diff options
Diffstat (limited to 'lib')
27 files changed, 272 insertions, 186 deletions
diff --git a/lib/api/entities.rb b/lib/api/entities.rb index c13cb389947..18f15632f2b 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -485,6 +485,12 @@ module API expose(:project_id) { |entity| entity&.project.try(:id) } expose :title, :description expose :state, :created_at, :updated_at + + # Avoids an N+1 query when metadata is included + def issuable_metadata(subject, options, method) + cached_subject = options.dig(:issuable_metadata, subject.id) + (cached_subject || subject).public_send(method) # rubocop: disable GitlabSecurity/PublicSend + end end class Diff < Grape::Entity @@ -530,54 +536,32 @@ module API class IssueBasic < ProjectEntity expose :closed_at expose :closed_by, using: Entities::UserBasic - expose :labels do |issue, options| + expose :labels do |issue| # Avoids an N+1 query since labels are preloaded issue.labels.map(&:title).sort end expose :milestone, using: Entities::Milestone expose :assignees, :author, using: Entities::UserBasic - expose :assignee, using: ::API::Entities::UserBasic do |issue, options| + expose :assignee, using: ::API::Entities::UserBasic do |issue| issue.assignees.first end - expose :user_notes_count - expose :upvotes do |issue, options| - if options[:issuable_metadata] - # Avoids an N+1 query when metadata is included - options[:issuable_metadata][issue.id].upvotes - else - issue.upvotes - end - end - expose :downvotes do |issue, options| - if options[:issuable_metadata] - # Avoids an N+1 query when metadata is included - options[:issuable_metadata][issue.id].downvotes - else - issue.downvotes - end - end + expose(:user_notes_count) { |issue, options| issuable_metadata(issue, options, :user_notes_count) } + expose(:merge_requests_count) { |issue, options| issuable_metadata(issue, options, :merge_requests_count) } + expose(:upvotes) { |issue, options| issuable_metadata(issue, options, :upvotes) } + expose(:downvotes) { |issue, options| issuable_metadata(issue, options, :downvotes) } expose :due_date expose :confidential expose :discussion_locked - expose :web_url do |issue, options| + expose :web_url do |issue| Gitlab::UrlBuilder.build(issue) end expose :time_stats, using: 'API::Entities::IssuableTimeStats' do |issue| issue end - - expose :merge_requests_count do |issue, options| - if options[:issuable_metadata] - # Avoids an N+1 query when metadata is included - options[:issuable_metadata][issue.id].merge_requests_count - else - issue.merge_requests_closing_issues.count - end - end end class Issue < IssueBasic @@ -671,23 +655,12 @@ module API MarkupHelper.markdown_field(entity, :description) end expose :target_branch, :source_branch - expose :upvotes do |merge_request, options| - if options[:issuable_metadata] - options[:issuable_metadata][merge_request.id].upvotes - else - merge_request.upvotes - end - end - expose :downvotes do |merge_request, options| - if options[:issuable_metadata] - options[:issuable_metadata][merge_request.id].downvotes - else - merge_request.downvotes - end - end + expose(:user_notes_count) { |merge_request, options| issuable_metadata(merge_request, options, :user_notes_count) } + expose(:upvotes) { |merge_request, options| issuable_metadata(merge_request, options, :upvotes) } + expose(:downvotes) { |merge_request, options| issuable_metadata(merge_request, options, :downvotes) } expose :author, :assignee, using: Entities::UserBasic expose :source_project_id, :target_project_id - expose :labels do |merge_request, options| + expose :labels do |merge_request| # Avoids an N+1 query since labels are preloaded merge_request.labels.map(&:title).sort end @@ -705,7 +678,6 @@ module API end expose :diff_head_sha, as: :sha expose :merge_commit_sha - expose :user_notes_count expose :discussion_locked expose :should_remove_source_branch?, as: :should_remove_source_branch expose :force_remove_source_branch?, as: :force_remove_source_branch @@ -713,7 +685,7 @@ module API # Deprecated expose :allow_collaboration, as: :allow_maintainer_to_push, if: -> (merge_request, _) { merge_request.for_fork? } - expose :web_url do |merge_request, options| + expose :web_url do |merge_request| Gitlab::UrlBuilder.build(merge_request) end @@ -1397,13 +1369,9 @@ module API class GitInfo < Grape::Entity expose :repo_url, :ref, :sha, :before_sha - expose :ref_type do |model| - if model.tag - 'tag' - else - 'branch' - end - end + expose :ref_type + expose :refspecs + expose :git_depth, as: :depth end class RunnerInfo < Grape::Entity diff --git a/lib/api/helpers/pagination.rb b/lib/api/helpers/pagination.rb index de59c915d66..d00e61678b5 100644 --- a/lib/api/helpers/pagination.rb +++ b/lib/api/helpers/pagination.rb @@ -13,6 +13,33 @@ module API strategy.new(self).paginate(relation) end + class Base + private + + def per_page + @per_page ||= params[:per_page] + end + + def base_request_uri + @base_request_uri ||= URI.parse(request.url).tap do |uri| + uri.host = Gitlab.config.gitlab.host + uri.port = nil + end + end + + def build_page_url(query_params:) + base_request_uri.tap do |uri| + uri.query = query_params + end.to_s + end + + def page_href(next_page_params = {}) + query_params = params.merge(**next_page_params, per_page: per_page).to_query + + build_page_url(query_params: query_params) + end + end + class KeysetPaginationInfo attr_reader :relation, :request_context @@ -85,7 +112,7 @@ module API end end - class KeysetPaginationStrategy + class KeysetPaginationStrategy < Base attr_reader :request_context delegate :params, :header, :request, to: :request_context @@ -141,10 +168,6 @@ module API ] end - def per_page - params[:per_page] - end - def add_default_pagination_headers header 'X-Per-Page', per_page.to_s end @@ -154,22 +177,12 @@ module API header 'Link', link_for('next', next_page_params) end - def page_href(next_page_params) - request_url = request.url.split('?').first - request_params = params.dup - request_params[:per_page] = per_page - - request_params.merge!(next_page_params) if next_page_params - - "#{request_url}?#{request_params.to_query}" - end - def link_for(rel, next_page_params) %(<#{page_href(next_page_params)}>; rel="#{rel}") end end - class DefaultPaginationStrategy + class DefaultPaginationStrategy < Base attr_reader :request_context delegate :params, :header, :request, to: :request_context @@ -198,15 +211,13 @@ module API end end - # rubocop: disable CodeReuse/ActiveRecord def add_default_order(relation) if relation.is_a?(ActiveRecord::Relation) && relation.order_values.empty? - relation = relation.order(:id) + relation = relation.order(:id) # rubocop: disable CodeReuse/ActiveRecord end relation end - # rubocop: enable CodeReuse/ActiveRecord def add_pagination_headers(paginated_data) header 'X-Per-Page', paginated_data.limit_value.to_s @@ -222,27 +233,13 @@ module API end def pagination_links(paginated_data) - request_url = request.url.split('?').first - request_params = params.clone - request_params[:per_page] = paginated_data.limit_value - - links = [] - - request_params[:page] = paginated_data.prev_page - links << %(<#{request_url}?#{request_params.to_query}>; rel="prev") if request_params[:page] - - request_params[:page] = paginated_data.next_page - links << %(<#{request_url}?#{request_params.to_query}>; rel="next") if request_params[:page] - - request_params[:page] = 1 - links << %(<#{request_url}?#{request_params.to_query}>; rel="first") - - unless data_without_counts?(paginated_data) - request_params[:page] = total_pages(paginated_data) - links << %(<#{request_url}?#{request_params.to_query}>; rel="last") - end + [].tap do |links| + links << %(<#{page_href(page: paginated_data.prev_page)}>; rel="prev") if paginated_data.prev_page + links << %(<#{page_href(page: paginated_data.next_page)}>; rel="next") if paginated_data.next_page + links << %(<#{page_href(page: 1)}>; rel="first") - links.join(', ') + links << %(<#{page_href(page: total_pages(paginated_data))}>; rel="last") unless data_without_counts?(paginated_data) + end.join(', ') end def total_pages(paginated_data) diff --git a/lib/api/helpers/runner.rb b/lib/api/helpers/runner.rb index 16df8e830e1..ff73a49d5e8 100644 --- a/lib/api/helpers/runner.rb +++ b/lib/api/helpers/runner.rb @@ -26,7 +26,7 @@ module API end def get_runner_ip - { ip_address: request.env["HTTP_X_FORWARDED_FOR"] || request.ip } + { ip_address: env["action_dispatch.remote_ip"].to_s || request.ip } end def current_runner diff --git a/lib/api/issues.rb b/lib/api/issues.rb index 94ed9ac6fb1..f43f4d961d6 100644 --- a/lib/api/issues.rb +++ b/lib/api/issues.rb @@ -54,6 +54,7 @@ module API optional :scope, type: String, values: %w[created-by-me assigned-to-me created_by_me assigned_to_me all], desc: 'Return issues for the given scope: `created_by_me`, `assigned_to_me` or `all`' optional :my_reaction_emoji, type: String, desc: 'Return issues reacted by the authenticated user by the given emoji' + optional :confidential, type: Boolean, desc: 'Filter confidential or public issues' use :pagination use :issues_params_ee diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb index f8d2ba49d2f..44f1e81caf2 100644 --- a/lib/api/merge_requests.rb +++ b/lib/api/merge_requests.rb @@ -369,11 +369,11 @@ module API merge_request.update(squash: params[:squash]) if params[:squash] - merge_params = { + merge_params = HashWithIndifferentAccess.new( commit_message: params[:merge_commit_message], squash_commit_message: params[:squash_commit_message], should_remove_source_branch: params[:should_remove_source_branch] - } + ) if merge_when_pipeline_succeeds && merge_request.head_pipeline && merge_request.head_pipeline.active? ::MergeRequests::MergeWhenPipelineSucceedsService @@ -388,6 +388,31 @@ module API present merge_request, with: Entities::MergeRequest, current_user: current_user, project: user_project end + desc 'Merge a merge request to its default temporary merge ref path' + params do + optional :merge_commit_message, type: String, desc: 'Custom merge commit message' + end + put ':id/merge_requests/:merge_request_iid/merge_to_ref' do + merge_request = find_project_merge_request(params[:merge_request_iid]) + + authorize! :admin_merge_request, user_project + + merge_params = { + commit_message: params[:merge_commit_message] + } + + result = ::MergeRequests::MergeToRefService + .new(merge_request.target_project, current_user, merge_params) + .execute(merge_request) + + if result[:status] == :success + present result.slice(:commit_id), 200 + else + http_status = result[:http_status] || 400 + render_api_error!(result[:message], http_status) + end + end + desc 'Cancel merge if "Merge When Pipeline Succeeds" is enabled' do success Entities::MergeRequest end diff --git a/lib/api/project_milestones.rb b/lib/api/project_milestones.rb index da31bcb8dac..ca24742b7a3 100644 --- a/lib/api/project_milestones.rb +++ b/lib/api/project_milestones.rb @@ -98,6 +98,23 @@ module API milestone_issuables_for(user_project, :merge_request) end + + desc 'Promote a milestone to group milestone' do + detail 'This feature was introduced in GitLab 11.9' + end + post ':id/milestones/:milestone_id/promote' do + begin + authorize! :admin_milestone, user_project + authorize! :admin_milestone, user_project.group + + milestone = user_project.milestones.find(params[:milestone_id]) + Milestones::PromoteService.new(user_project, current_user).execute(milestone) + + status(200) + rescue Milestones::PromoteService::PromoteMilestoneError => error + render_api_error!(error.message, 400) + end + end end end end diff --git a/lib/api/project_templates.rb b/lib/api/project_templates.rb index d05ddad7466..119902a189c 100644 --- a/lib/api/project_templates.rb +++ b/lib/api/project_templates.rb @@ -36,7 +36,10 @@ module API optional :project, type: String, desc: 'The project name to use when expanding placeholders in the template. Only affects licenses' optional :fullname, type: String, desc: 'The full name of the copyright holder to use when expanding placeholders in the template. Only affects licenses' end - get ':id/templates/:type/:name', requirements: { name: /[\w\.-]+/ } do + # The regex is needed to ensure a period (e.g. agpl-3.0) + # isn't confused with a format type. We also need to allow encoded + # values (e.g. C%2B%2B for C++), so allow % and + as well. + get ':id/templates/:type/:name', requirements: { name: /[\w%.+-]+/ } do template = TemplateFinder .build(params[:type], user_project, name: params[:name]) .execute diff --git a/lib/api/projects.rb b/lib/api/projects.rb index 6a93ef9f3ad..b23fe6cd4e7 100644 --- a/lib/api/projects.rb +++ b/lib/api/projects.rb @@ -258,6 +258,8 @@ module API end params do optional :namespace, type: String, desc: 'The ID or name of the namespace that the project will be forked into' + optional :path, type: String, desc: 'The path that will be assigned to the fork' + optional :name, type: String, desc: 'The name that will be assigned to the fork' end post ':id/fork' do Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-ce/issues/42284') @@ -386,7 +388,11 @@ module API desc 'Get languages in project repository' get ':id/languages' do - user_project.repository.languages.map { |language| language.values_at(:label, :value) }.to_h + if user_project.repository_languages.present? + user_project.repository_languages.map { |l| [l.name, l.share] }.to_h + else + user_project.repository.languages.map { |language| language.values_at(:label, :value) }.to_h + end end desc 'Remove a project' diff --git a/lib/api/services.rb b/lib/api/services.rb index 145897516a0..bda6be51553 100644 --- a/lib/api/services.rb +++ b/lib/api/services.rb @@ -592,6 +592,26 @@ module API desc: 'The description of the tracker' } ], + 'youtrack' => [ + { + required: true, + name: :project_url, + type: String, + desc: 'The project URL' + }, + { + required: true, + name: :issues_url, + type: String, + desc: 'The issues URL' + }, + { + required: false, + name: :description, + type: String, + desc: 'The description of the tracker' + } + ], 'slack' => [ CHAT_NOTIFICATION_SETTINGS, CHAT_NOTIFICATION_FLAGS, @@ -665,6 +685,7 @@ module API PrometheusService, PushoverService, RedmineService, + YoutrackService, SlackService, MattermostService, MicrosoftTeamsService, diff --git a/lib/banzai/filter/relative_link_filter.rb b/lib/banzai/filter/relative_link_filter.rb index 93e6d6470f1..2745905c5ff 100644 --- a/lib/banzai/filter/relative_link_filter.rb +++ b/lib/banzai/filter/relative_link_filter.rb @@ -150,7 +150,10 @@ module Banzai end def uri_type(path) - @uri_types[path] ||= current_commit.uri_type(path) + # https://gitlab.com/gitlab-org/gitlab-ce/issues/58011 + Gitlab::GitalyClient.allow_n_plus_1_calls do + @uri_types[path] ||= current_commit.uri_type(path) + end end def current_commit diff --git a/lib/gitlab/ci/config/entry/global.rb b/lib/gitlab/ci/config/entry/global.rb index 09ecb5fdb99..2b5a59c078e 100644 --- a/lib/gitlab/ci/config/entry/global.rb +++ b/lib/gitlab/ci/config/entry/global.rb @@ -17,6 +17,9 @@ module Gitlab entry :image, Entry::Image, description: 'Docker image that will be used to execute jobs.' + entry :include, Entry::Includes, + description: 'List of external YAML files to include.' + entry :services, Entry::Services, description: 'Docker images that will be linked to the container.' diff --git a/lib/gitlab/ci/config/entry/include.rb b/lib/gitlab/ci/config/entry/include.rb new file mode 100644 index 00000000000..f2f3dd84eda --- /dev/null +++ b/lib/gitlab/ci/config/entry/include.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +module Gitlab + module Ci + class Config + module Entry + ## + # Entry that represents a single include. + # + class Include < ::Gitlab::Config::Entry::Node + include ::Gitlab::Config::Entry::Validatable + + ALLOWED_KEYS = %i[local file remote template].freeze + + validations do + validates :config, hash_or_string: true + validates :config, allowed_keys: ALLOWED_KEYS + end + end + end + end + end +end diff --git a/lib/gitlab/ci/config/entry/includes.rb b/lib/gitlab/ci/config/entry/includes.rb new file mode 100644 index 00000000000..82b2b1ccf4b --- /dev/null +++ b/lib/gitlab/ci/config/entry/includes.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true + +module Gitlab + module Ci + class Config + module Entry + ## + # Entry that represents a list of include. + # + class Includes < ::Gitlab::Config::Entry::Node + include ::Gitlab::Config::Entry::Validatable + + validations do + validates :config, type: Array + end + + def self.aspects + super.append -> do + @config = Array.wrap(@config) + + @config.each_with_index do |config, i| + @entries[i] = ::Gitlab::Config::Entry::Factory.new(Entry::Include) + .value(config || {}) + .create! + end + end + end + end + end + end + end +end diff --git a/lib/gitlab/ci/pipeline/chain/build.rb b/lib/gitlab/ci/pipeline/chain/build.rb index 41632211374..164a4634d84 100644 --- a/lib/gitlab/ci/pipeline/chain/build.rb +++ b/lib/gitlab/ci/pipeline/chain/build.rb @@ -12,6 +12,8 @@ module Gitlab ref: @command.ref, sha: @command.sha, before_sha: @command.before_sha, + source_sha: @command.source_sha, + target_sha: @command.target_sha, tag: @command.tag_exists?, trigger_requests: Array(@command.trigger_request), user: @command.current_user, diff --git a/lib/gitlab/ci/pipeline/chain/command.rb b/lib/gitlab/ci/pipeline/chain/command.rb index e4ed1424865..7b77e86feae 100644 --- a/lib/gitlab/ci/pipeline/chain/command.rb +++ b/lib/gitlab/ci/pipeline/chain/command.rb @@ -7,7 +7,7 @@ module Gitlab module Chain Command = Struct.new( :source, :project, :current_user, - :origin_ref, :checkout_sha, :after_sha, :before_sha, + :origin_ref, :checkout_sha, :after_sha, :before_sha, :source_sha, :target_sha, :trigger_request, :schedule, :merge_request, :ignore_skip_ci, :save_incompleted, :seeds_block, :variables_attributes, :push_options, diff --git a/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml b/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml index 7c1182d4293..c0d4d4400b3 100644 --- a/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml @@ -48,6 +48,7 @@ variables: POSTGRES_PASSWORD: testing-password POSTGRES_ENABLED: "true" POSTGRES_DB: $CI_ENVIRONMENT_SLUG + POSTGRES_VERSION: 9.6.2 KUBERNETES_VERSION: 1.11.7 HELM_VERSION: 2.12.3 @@ -73,12 +74,11 @@ stages: build: stage: build - image: docker:stable-git + image: "registry.gitlab.com/gitlab-org/cluster-integration/auto-build-image/master:stable" services: - - docker:stable-dind + - docker:stable-dind script: - - setup_docker - - build + - /build/build.sh only: - branches @@ -490,7 +490,6 @@ rollout 100%: export DATABASE_URL=${DATABASE_URL-$auto_database_url} export CI_APPLICATION_REPOSITORY=$CI_REGISTRY_IMAGE/$CI_COMMIT_REF_SLUG export CI_APPLICATION_TAG=$CI_COMMIT_SHA - export CI_CONTAINER_NAME=ci_job_build_${CI_JOB_ID} export TILLER_NAMESPACE=$KUBE_NAMESPACE # Extract "MAJOR.MINOR" from CI_SERVER_VERSION and generate "MAJOR-MINOR-stable" for Security Products export SP_VERSION=$(echo "$CI_SERVER_VERSION" | sed 's/^\([0-9]*\)\.\([0-9]*\).*/\1-\2-stable/') @@ -700,6 +699,7 @@ rollout 100%: --set postgresql.postgresUser="$POSTGRES_USER" \ --set postgresql.postgresPassword="$POSTGRES_PASSWORD" \ --set postgresql.postgresDatabase="$POSTGRES_DB" \ + --set postgresql.imageTag="$POSTGRES_VERSION" \ --set application.initializeCommand="$DB_INITIALIZE" \ --namespace="$KUBE_NAMESPACE" \ "$name" \ @@ -849,50 +849,6 @@ rollout 100%: fi } - function build() { - registry_login - - if [[ -f Dockerfile ]]; then - echo "Building Dockerfile-based application..." - docker build \ - --build-arg HTTP_PROXY="$HTTP_PROXY" \ - --build-arg http_proxy="$http_proxy" \ - --build-arg HTTPS_PROXY="$HTTPS_PROXY" \ - --build-arg https_proxy="$https_proxy" \ - --build-arg FTP_PROXY="$FTP_PROXY" \ - --build-arg ftp_proxy="$ftp_proxy" \ - --build-arg NO_PROXY="$NO_PROXY" \ - --build-arg no_proxy="$no_proxy" \ - -t "$CI_APPLICATION_REPOSITORY:$CI_APPLICATION_TAG" . - else - echo "Building Heroku-based application using gliderlabs/herokuish docker image..." - docker run -i \ - -e BUILDPACK_URL \ - -e HTTP_PROXY \ - -e http_proxy \ - -e HTTPS_PROXY \ - -e https_proxy \ - -e FTP_PROXY \ - -e ftp_proxy \ - -e NO_PROXY \ - -e no_proxy \ - --name="$CI_CONTAINER_NAME" -v "$(pwd):/tmp/app:ro" gliderlabs/herokuish /bin/herokuish buildpack build - docker commit "$CI_CONTAINER_NAME" "$CI_APPLICATION_REPOSITORY:$CI_APPLICATION_TAG" - docker rm "$CI_CONTAINER_NAME" >/dev/null - echo "" - - echo "Configuring $CI_APPLICATION_REPOSITORY:$CI_APPLICATION_TAG docker image..." - docker create --expose 5000 --env PORT=5000 --name="$CI_CONTAINER_NAME" "$CI_APPLICATION_REPOSITORY:$CI_APPLICATION_TAG" /bin/herokuish procfile start web - docker commit "$CI_CONTAINER_NAME" "$CI_APPLICATION_REPOSITORY:$CI_APPLICATION_TAG" - docker rm "$CI_CONTAINER_NAME" >/dev/null - echo "" - fi - - echo "Pushing to GitLab Container Registry..." - docker push "$CI_APPLICATION_REPOSITORY:$CI_APPLICATION_TAG" - echo "" - } - function initialize_tiller() { echo "Checking Tiller..." diff --git a/lib/gitlab/git/repository.rb b/lib/gitlab/git/repository.rb index 593a3676519..aea132a3dd9 100644 --- a/lib/gitlab/git/repository.rb +++ b/lib/gitlab/git/repository.rb @@ -556,6 +556,12 @@ module Gitlab tags.find { |tag| tag.name == name } end + def merge_to_ref(user, source_sha, branch, target_ref, message) + wrapped_gitaly_errors do + gitaly_operation_client.user_merge_to_ref(user, source_sha, branch, target_ref, message) + end + end + def merge(user, source_sha, target_branch, message, &block) wrapped_gitaly_errors do gitaly_operation_client.user_merge_branch(user, source_sha, target_branch, message, &block) diff --git a/lib/gitlab/gitaly_client.rb b/lib/gitlab/gitaly_client.rb index 0ab53f8f706..5aeedb0f50d 100644 --- a/lib/gitlab/gitaly_client.rb +++ b/lib/gitlab/gitaly_client.rb @@ -28,7 +28,7 @@ module Gitlab PEM_REGEX = /\-+BEGIN CERTIFICATE\-+.+?\-+END CERTIFICATE\-+/m SERVER_VERSION_FILE = 'GITALY_SERVER_VERSION' - MAXIMUM_GITALY_CALLS = 35 + MAXIMUM_GITALY_CALLS = 30 CLIENT_NAME = (Sidekiq.server? ? 'gitlab-sidekiq' : 'gitlab-web').freeze MUTEX = Mutex.new diff --git a/lib/gitlab/gitaly_client/operation_service.rb b/lib/gitlab/gitaly_client/operation_service.rb index 22d2d149e65..d172c798da2 100644 --- a/lib/gitlab/gitaly_client/operation_service.rb +++ b/lib/gitlab/gitaly_client/operation_service.rb @@ -100,6 +100,25 @@ module Gitlab end end + def user_merge_to_ref(user, source_sha, branch, target_ref, message) + request = Gitaly::UserMergeToRefRequest.new( + repository: @gitaly_repo, + source_sha: source_sha, + branch: encode_binary(branch), + target_ref: encode_binary(target_ref), + user: Gitlab::Git::User.from_gitlab(user).to_gitaly, + message: message + ) + + response = GitalyClient.call(@repository.storage, :operation_service, :user_merge_to_ref, request) + + if pre_receive_error = response.pre_receive_error.presence + raise Gitlab::Git::PreReceiveError, pre_receive_error + end + + response.commit_id + end + def user_merge_branch(user, source_sha, target_branch, message) request_enum = QueueEnumerator.new response_enum = GitalyClient.call( diff --git a/lib/gitlab/graphql/authorize.rb b/lib/gitlab/graphql/authorize.rb index 5e48bf9043d..f62813db82c 100644 --- a/lib/gitlab/graphql/authorize.rb +++ b/lib/gitlab/graphql/authorize.rb @@ -10,21 +10,6 @@ module Gitlab def self.use(schema_definition) schema_definition.instrument(:field, Instrumentation.new) end - - def required_permissions - # If the `#authorize` call is used on multiple classes, we add the - # permissions specified on a subclass, to the ones that were specified - # on it's superclass. - @required_permissions ||= if self.respond_to?(:superclass) && superclass.respond_to?(:required_permissions) - superclass.required_permissions.dup - else - [] - end - end - - def authorize(*permissions) - required_permissions.concat(permissions) - end end end end diff --git a/lib/gitlab/graphql/authorize/authorize_resource.rb b/lib/gitlab/graphql/authorize/authorize_resource.rb index a56c4f6368d..b367a97105c 100644 --- a/lib/gitlab/graphql/authorize/authorize_resource.rb +++ b/lib/gitlab/graphql/authorize/authorize_resource.rb @@ -6,8 +6,21 @@ module Gitlab module AuthorizeResource extend ActiveSupport::Concern - included do - extend Gitlab::Graphql::Authorize + class_methods do + def required_permissions + # If the `#authorize` call is used on multiple classes, we add the + # permissions specified on a subclass, to the ones that were specified + # on it's superclass. + @required_permissions ||= if self.respond_to?(:superclass) && superclass.respond_to?(:required_permissions) + superclass.required_permissions.dup + else + [] + end + end + + def authorize(*permissions) + required_permissions.concat(permissions) + end end def find_object(*args) diff --git a/lib/gitlab/graphql/authorize/instrumentation.rb b/lib/gitlab/graphql/authorize/instrumentation.rb index 2a3d790d67b..593da8471dd 100644 --- a/lib/gitlab/graphql/authorize/instrumentation.rb +++ b/lib/gitlab/graphql/authorize/instrumentation.rb @@ -6,19 +6,15 @@ module Gitlab class Instrumentation # Replace the resolver for the field with one that will only return the # resolved object if the permissions check is successful. - # - # Collections are not supported. Apply permissions checks for those at the - # database level instead, to avoid loading superfluous data from the DB def instrument(_type, field) - field_definition = field.metadata[:type_class] - return field unless field_definition.respond_to?(:required_permissions) - return field if field_definition.required_permissions.empty? + required_permissions = Array.wrap(field.metadata[:authorize]) + return field if required_permissions.empty? old_resolver = field.resolve_proc new_resolver = -> (obj, args, ctx) do resolved_obj = old_resolver.call(obj, args, ctx) - checker = build_checker(ctx[:current_user], field_definition.required_permissions) + checker = build_checker(ctx[:current_user], required_permissions) if resolved_obj.respond_to?(:then) resolved_obj.then(&checker) diff --git a/lib/gitlab/import_export/import_export.yml b/lib/gitlab/import_export/import_export.yml index 7f8c6d56627..fa54fc17d95 100644 --- a/lib/gitlab/import_export/import_export.yml +++ b/lib/gitlab/import_export/import_export.yml @@ -28,7 +28,6 @@ project_tree: - notes: :author - releases: - - :author - :links - project_members: - :user diff --git a/lib/gitlab/project_template.rb b/lib/gitlab/project_template.rb index ef656e5b2ce..9b6ff602fcd 100644 --- a/lib/gitlab/project_template.rb +++ b/lib/gitlab/project_template.rb @@ -28,11 +28,18 @@ module Gitlab ProjectTemplate.new('rails', 'Ruby on Rails', _('Includes an MVC structure, Gemfile, Rakefile, along with many others, to help you get started.'), 'https://gitlab.com/gitlab-org/project-templates/rails', 'illustrations/logos/rails.svg'), ProjectTemplate.new('spring', 'Spring', _('Includes an MVC structure, mvnw and pom.xml to help you get started.'), 'https://gitlab.com/gitlab-org/project-templates/spring', 'illustrations/logos/spring.svg'), ProjectTemplate.new('express', 'NodeJS Express', _('Includes an MVC structure to help you get started.'), 'https://gitlab.com/gitlab-org/project-templates/express', 'illustrations/logos/express.svg'), + ProjectTemplate.new('dotnetcore', '.NET Core', _('A .NET Core console application template, customizable for any .NET Core project'), 'https://gitlab.com/gitlab-org/project-templates/dotnetcore', 'illustrations/logos/dotnet.svg'), + ProjectTemplate.new('gomicro', 'Go Micro', _('Go Micro is a framework for micro service development.'), 'https://gitlab.com/gitlab-org/project-templates/go-micro'), ProjectTemplate.new('hugo', 'Pages/Hugo', _('Everything you need to create a GitLab Pages site using Hugo.'), 'https://gitlab.com/pages/hugo'), ProjectTemplate.new('jekyll', 'Pages/Jekyll', _('Everything you need to create a GitLab Pages site using Jekyll.'), 'https://gitlab.com/pages/jekyll'), ProjectTemplate.new('plainhtml', 'Pages/Plain HTML', _('Everything you need to create a GitLab Pages site using plain HTML.'), 'https://gitlab.com/pages/plain-html'), ProjectTemplate.new('gitbook', 'Pages/GitBook', _('Everything you need to create a GitLab Pages site using GitBook.'), 'https://gitlab.com/pages/gitbook'), - ProjectTemplate.new('hexo', 'Pages/Hexo', _('Everything you need to create a GitLab Pages site using Hexo.'), 'https://gitlab.com/pages/hexo') + ProjectTemplate.new('hexo', 'Pages/Hexo', _('Everything you need to create a GitLab Pages site using Hexo.'), 'https://gitlab.com/pages/hexo'), + ProjectTemplate.new('nfhugo', 'Netlify/Hugo', _('A Hugo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features.'), 'https://gitlab.com/pages/nfhugo', 'illustrations/logos/netlify.svg'), + ProjectTemplate.new('nfjekyll', 'Netlify/Jekyll', _('A Jekyll site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features.'), 'https://gitlab.com/pages/nfjekyll', 'illustrations/logos/netlify.svg'), + ProjectTemplate.new('nfplainhtml', 'Netlify/Plain HTML', _('A plain HTML site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features.'), 'https://gitlab.com/pages/nfplain-html', 'illustrations/logos/netlify.svg'), + ProjectTemplate.new('nfgitbook', 'Netlify/GitBook', _('A GitBook site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features.'), 'https://gitlab.com/pages/nfgitbook', 'illustrations/logos/netlify.svg'), + ProjectTemplate.new('nfhexo', 'Netlify/Hexo', _('A Hexo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features.'), 'https://gitlab.com/pages/nfhexo', 'illustrations/logos/netlify.svg') ].freeze class << self diff --git a/lib/gitlab/shell.rb b/lib/gitlab/shell.rb index 1153e69d3de..40b641b8317 100644 --- a/lib/gitlab/shell.rb +++ b/lib/gitlab/shell.rb @@ -280,7 +280,10 @@ module Gitlab # add_namespace("default", "gitlab") # def add_namespace(storage, name) - Gitlab::GitalyClient::NamespaceService.new(storage).add(name) + # https://gitlab.com/gitlab-org/gitlab-ce/issues/58012 + Gitlab::GitalyClient.allow_n_plus_1_calls do + Gitlab::GitalyClient::NamespaceService.new(storage).add(name) + end rescue GRPC::InvalidArgument => e raise ArgumentError, e.message end @@ -337,16 +340,16 @@ module Gitlab end # rubocop: enable CodeReuse/ActiveRecord + def hooks_path + File.join(gitlab_shell_path, 'hooks') + end + protected def gitlab_shell_path File.expand_path(Gitlab.config.gitlab_shell.path) end - def gitlab_shell_hooks_path - File.expand_path(Gitlab.config.gitlab_shell.hooks_path) - end - def gitlab_shell_user_home File.expand_path("~#{Gitlab.config.gitlab_shell.ssh_user}") end diff --git a/lib/gitlab/tracing.rb b/lib/gitlab/tracing.rb index 0d9b0be1c8e..29517591c51 100644 --- a/lib/gitlab/tracing.rb +++ b/lib/gitlab/tracing.rb @@ -27,10 +27,11 @@ module Gitlab def self.tracing_url return unless tracing_url_enabled? - tracing_url_template % { - correlation_id: Gitlab::CorrelationId.current_id.to_s, - service: Gitlab.process_name - } + # Avoid using `format` since it can throw TypeErrors + # which we want to avoid on unsanitised env var input + tracing_url_template.to_s + .gsub(/\{\{\s*correlation_id\s*\}\}/, Gitlab::CorrelationId.current_id.to_s) + .gsub(/\{\{\s*service\s*\}\}/, Gitlab.process_name) end end end diff --git a/lib/tasks/gitlab/info.rake b/lib/tasks/gitlab/info.rake index e97d77d20e0..b8798fb3cfd 100644 --- a/lib/tasks/gitlab/info.rake +++ b/lib/tasks/gitlab/info.rake @@ -58,7 +58,7 @@ namespace :gitlab do puts "Omniauth Providers: #{omniauth_providers.join(', ')}" if Gitlab::Auth.omniauth_enabled? # check Gitolite version - gitlab_shell_version_file = "#{Gitlab.config.gitlab_shell.hooks_path}/../VERSION" + gitlab_shell_version_file = "#{Gitlab.config.gitlab_shell.path}/VERSION" if File.readable?(gitlab_shell_version_file) gitlab_shell_version = File.read(gitlab_shell_version_file) end @@ -72,7 +72,7 @@ namespace :gitlab do puts "- #{name}: \t#{repository_storage.legacy_disk_path}" end end - puts "Hooks:\t\t#{Gitlab.config.gitlab_shell.hooks_path}" + puts "GitLab Shell path:\t\t#{Gitlab.config.gitlab_shell.path}" puts "Git:\t\t#{Gitlab.config.git.bin_path}" end end |