Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2021-07-21 00:09:52 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2021-07-21 00:09:52 +0300
commit900c5cc840cccdce182aef5d5050a7950de9ad00 (patch)
tree08bf4419b68ce3e41abde6bc793da8c2c4527b78
parent1bf106b17275c87cf8baa199599f113f154a52fe (diff)
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--app/models/clusters/applications/runner.rb2
-rw-r--r--doc/api/packages/helm.md3
-rw-r--r--doc/development/ee_features.md30
-rw-r--r--doc/public_access/public_access.md2
-rw-r--r--doc/user/packages/helm_repository/index.md4
-rw-r--r--lib/api/api.rb8
-rw-r--r--lib/api/ci/helpers/runner.rb123
-rw-r--r--lib/api/ci/job_artifacts.rb143
-rw-r--r--lib/api/ci/jobs.rb206
-rw-r--r--lib/api/ci/runner.rb2
-rw-r--r--lib/api/ci/triggers.rb148
-rw-r--r--lib/api/ci/variables.rb126
-rw-r--r--lib/api/group_variables.rb2
-rw-r--r--lib/api/helpers/projects_helpers.rb3
-rw-r--r--lib/api/helpers/runner.rb121
-rw-r--r--lib/api/job_artifacts.rb141
-rw-r--r--lib/api/jobs.rb204
-rw-r--r--lib/api/triggers.rb146
-rw-r--r--lib/api/variables.rb124
-rw-r--r--spec/lib/api/ci/helpers/runner_helpers_spec.rb (renamed from spec/lib/api/helpers/runner_helpers_spec.rb)4
-rw-r--r--spec/lib/api/ci/helpers/runner_spec.rb (renamed from spec/lib/api/helpers/runner_spec.rb)4
-rw-r--r--spec/requests/api/ci/jobs_spec.rb (renamed from spec/requests/api/jobs_spec.rb)20
-rw-r--r--spec/requests/api/ci/runner/jobs_artifacts_spec.rb4
-rw-r--r--spec/requests/api/ci/runner/jobs_trace_spec.rb2
-rw-r--r--spec/requests/api/ci/triggers_spec.rb (renamed from spec/requests/api/triggers_spec.rb)2
-rw-r--r--spec/requests/api/ci/variables_spec.rb (renamed from spec/requests/api/variables_spec.rb)2
-rw-r--r--spec/requests/api/project_attributes.yml2
27 files changed, 794 insertions, 784 deletions
diff --git a/app/models/clusters/applications/runner.rb b/app/models/clusters/applications/runner.rb
index 3785023c9af..993ccb33655 100644
--- a/app/models/clusters/applications/runner.rb
+++ b/app/models/clusters/applications/runner.rb
@@ -3,7 +3,7 @@
module Clusters
module Applications
class Runner < ApplicationRecord
- VERSION = '0.30.0'
+ VERSION = '0.31.0'
self.table_name = 'clusters_applications_runners'
diff --git a/doc/api/packages/helm.md b/doc/api/packages/helm.md
index a76fa9d3755..f1d5f24cd99 100644
--- a/doc/api/packages/helm.md
+++ b/doc/api/packages/helm.md
@@ -11,8 +11,7 @@ This is the API documentation for [Helm](../../user/packages/helm_repository/ind
WARNING:
This API is used by the Helm-related package clients such as [Helm](https://helm.sh/)
and [`helm-push`](https://github.com/chartmuseum/helm-push/#readme),
-and is generally not meant for manual consumption. This API is under development and is not ready
-for production use due to limited functionality.
+and is generally not meant for manual consumption.
For instructions on how to upload and install Helm packages from the GitLab
Package Registry, see the [Helm registry documentation](../../user/packages/helm_repository/index.md).
diff --git a/doc/development/ee_features.md b/doc/development/ee_features.md
index fb00fe748d0..fc4180bede7 100644
--- a/doc/development/ee_features.md
+++ b/doc/development/ee_features.md
@@ -673,17 +673,19 @@ class definition to make it easy and clear:
```ruby
module API
- class JobArtifacts < Grape::API::Instance
- # EE::API::JobArtifacts would override the following helpers
- helpers do
- def authorize_download_artifacts!
- authorize_read_builds!
+ module Ci
+ class JobArtifacts < Grape::API::Instance
+ # EE::API::Ci::JobArtifacts would override the following helpers
+ helpers do
+ def authorize_download_artifacts!
+ authorize_read_builds!
+ end
end
end
end
end
-API::JobArtifacts.prepend_mod_with('API::JobArtifacts')
+API::Ci::JobArtifacts.prepend_mod_with('API::Ci::JobArtifacts')
```
And then we can follow regular object-oriented practices to override it:
@@ -691,14 +693,16 @@ And then we can follow regular object-oriented practices to override it:
```ruby
module EE
module API
- module JobArtifacts
- extend ActiveSupport::Concern
+ module Ci
+ module JobArtifacts
+ extend ActiveSupport::Concern
- prepended do
- helpers do
- def authorize_download_artifacts!
- super
- check_cross_project_pipelines_feature!
+ prepended do
+ helpers do
+ def authorize_download_artifacts!
+ super
+ check_cross_project_pipelines_feature!
+ end
end
end
end
diff --git a/doc/public_access/public_access.md b/doc/public_access/public_access.md
index 4a7e178072b..2522e69b4d1 100644
--- a/doc/public_access/public_access.md
+++ b/doc/public_access/public_access.md
@@ -15,6 +15,8 @@ GitLab allows [Owners](../user/permissions.md) to set a project's visibility as:
These visibility levels affect who can see the project in the public access directory (`/public`
for your GitLab instance). For example, <https://gitlab.com/public>.
+You can control the visibility of individual features with
+[project feature settings](../user/permissions.md#project-features).
## Public projects
diff --git a/doc/user/packages/helm_repository/index.md b/doc/user/packages/helm_repository/index.md
index 26d8bf76cd6..fcd1aa04ee3 100644
--- a/doc/user/packages/helm_repository/index.md
+++ b/doc/user/packages/helm_repository/index.md
@@ -8,10 +8,6 @@ info: To determine the technical writer assigned to the Stage/Group associated w
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/18997) in GitLab 14.1.
-WARNING:
-The Helm package registry for GitLab is under development and isn't ready for production use due to
-limited functionality.
-
Publish Helm packages in your project's Package Registry. Then install the
packages whenever you need to use them as a dependency.
diff --git a/lib/api/api.rb b/lib/api/api.rb
index f9e89191a36..0a8a9682f35 100644
--- a/lib/api/api.rb
+++ b/lib/api/api.rb
@@ -153,10 +153,14 @@ module API
mount ::API::Branches
mount ::API::BroadcastMessages
mount ::API::BulkImports
+ mount ::API::Ci::JobArtifacts
+ mount ::API::Ci::Jobs
mount ::API::Ci::Pipelines
mount ::API::Ci::PipelineSchedules
mount ::API::Ci::Runner
mount ::API::Ci::Runners
+ mount ::API::Ci::Triggers
+ mount ::API::Ci::Variables
mount ::API::Commits
mount ::API::CommitStatuses
mount ::API::ContainerRegistryEvent
@@ -190,8 +194,6 @@ module API
mount ::API::IssueLinks
mount ::API::Invitations
mount ::API::Issues
- mount ::API::JobArtifacts
- mount ::API::Jobs
mount ::API::Keys
mount ::API::Labels
mount ::API::Lint
@@ -268,14 +270,12 @@ module API
mount ::API::Tags
mount ::API::Templates
mount ::API::Todos
- mount ::API::Triggers
mount ::API::Unleash
mount ::API::UsageData
mount ::API::UsageDataQueries
mount ::API::UsageDataNonSqlMetrics
mount ::API::UserCounts
mount ::API::Users
- mount ::API::Variables
mount ::API::Version
mount ::API::Wikis
end
diff --git a/lib/api/ci/helpers/runner.rb b/lib/api/ci/helpers/runner.rb
new file mode 100644
index 00000000000..b9662b822fb
--- /dev/null
+++ b/lib/api/ci/helpers/runner.rb
@@ -0,0 +1,123 @@
+# frozen_string_literal: true
+
+module API
+ module Ci
+ module Helpers
+ module Runner
+ include Gitlab::Utils::StrongMemoize
+
+ prepend_mod_with('API::Ci::Helpers::Runner') # rubocop: disable Cop/InjectEnterpriseEditionModule
+
+ JOB_TOKEN_HEADER = 'HTTP_JOB_TOKEN'
+ JOB_TOKEN_PARAM = :token
+
+ def runner_registration_token_valid?
+ ActiveSupport::SecurityUtils.secure_compare(params[:token], Gitlab::CurrentSettings.runners_registration_token)
+ end
+
+ def runner_registrar_valid?(type)
+ Feature.disabled?(:runner_registration_control) || Gitlab::CurrentSettings.valid_runner_registrars.include?(type)
+ end
+
+ def authenticate_runner!
+ forbidden! unless current_runner
+
+ current_runner
+ .heartbeat(get_runner_details_from_request)
+ end
+
+ def get_runner_details_from_request
+ return get_runner_ip unless params['info'].present?
+
+ attributes_for_keys(%w(name version revision platform architecture), params['info'])
+ .merge(get_runner_config_from_request)
+ .merge(get_runner_ip)
+ end
+
+ def get_runner_ip
+ { ip_address: ip_address }
+ end
+
+ def current_runner
+ token = params[:token]
+
+ if token
+ ::Gitlab::Database::LoadBalancing::RackMiddleware
+ .stick_or_unstick(env, :runner, token)
+ end
+
+ strong_memoize(:current_runner) do
+ ::Ci::Runner.find_by_token(token.to_s)
+ end
+ end
+
+ # HTTP status codes to terminate the job on GitLab Runner:
+ # - 403
+ def authenticate_job!(require_running: true)
+ job = current_job
+
+ # 404 is not returned here because we want to terminate the job if it's
+ # running. A 404 can be returned from anywhere in the networking stack which is why
+ # we are explicit about a 403, we should improve this in
+ # https://gitlab.com/gitlab-org/gitlab/-/issues/327703
+ forbidden! unless job
+
+ forbidden! unless job_token_valid?(job)
+
+ forbidden!('Project has been deleted!') if job.project.nil? || job.project.pending_delete?
+ forbidden!('Job has been erased!') if job.erased?
+
+ if require_running
+ job_forbidden!(job, 'Job is not running') unless job.running?
+ end
+
+ job.runner&.heartbeat(get_runner_ip)
+
+ job
+ end
+
+ def current_job
+ id = params[:id]
+
+ if id
+ ::Gitlab::Database::LoadBalancing::RackMiddleware
+ .stick_or_unstick(env, :build, id)
+ end
+
+ strong_memoize(:current_job) do
+ ::Ci::Build.find_by_id(id)
+ end
+ end
+
+ def job_token_valid?(job)
+ token = (params[JOB_TOKEN_PARAM] || env[JOB_TOKEN_HEADER]).to_s
+ token && job.valid_token?(token)
+ end
+
+ def job_forbidden!(job, reason)
+ header 'Job-Status', job.status
+ forbidden!(reason)
+ end
+
+ def set_application_context
+ return unless current_job
+
+ Gitlab::ApplicationContext.push(
+ user: -> { current_job.user },
+ project: -> { current_job.project }
+ )
+ end
+
+ def track_ci_minutes_usage!(_build, _runner)
+ # noop: overridden in EE
+ end
+
+ private
+
+ def get_runner_config_from_request
+ { config: attributes_for_keys(%w(gpus), params.dig('info', 'config')) }
+ end
+ end
+ end
+ end
+end
diff --git a/lib/api/ci/job_artifacts.rb b/lib/api/ci/job_artifacts.rb
new file mode 100644
index 00000000000..6431436b50d
--- /dev/null
+++ b/lib/api/ci/job_artifacts.rb
@@ -0,0 +1,143 @@
+# frozen_string_literal: true
+
+module API
+ module Ci
+ class JobArtifacts < ::API::Base
+ before { authenticate_non_get! }
+
+ feature_category :build_artifacts
+
+ # EE::API::Ci::JobArtifacts would override the following helpers
+ helpers do
+ def authorize_download_artifacts!
+ authorize_read_builds!
+ end
+ end
+
+ prepend_mod_with('API::Ci::JobArtifacts') # rubocop: disable Cop/InjectEnterpriseEditionModule
+
+ params do
+ requires :id, type: String, desc: 'The ID of a project'
+ end
+ resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
+ desc 'Download the artifacts archive from a job' do
+ detail 'This feature was introduced in GitLab 8.10'
+ end
+ params do
+ requires :ref_name, type: String, desc: 'The ref from repository'
+ requires :job, type: String, desc: 'The name for the job'
+ end
+ route_setting :authentication, job_token_allowed: true
+ get ':id/jobs/artifacts/:ref_name/download',
+ requirements: { ref_name: /.+/ } do
+ authorize_download_artifacts!
+
+ latest_build = user_project.latest_successful_build_for_ref!(params[:job], params[:ref_name])
+ authorize_read_job_artifacts!(latest_build)
+
+ present_carrierwave_file!(latest_build.artifacts_file)
+ end
+
+ desc 'Download a specific file from artifacts archive from a ref' do
+ detail 'This feature was introduced in GitLab 11.5'
+ end
+ params do
+ requires :ref_name, type: String, desc: 'The ref from repository'
+ requires :job, type: String, desc: 'The name for the job'
+ requires :artifact_path, type: String, desc: 'Artifact path'
+ end
+ route_setting :authentication, job_token_allowed: true
+ get ':id/jobs/artifacts/:ref_name/raw/*artifact_path',
+ format: false,
+ requirements: { ref_name: /.+/ } do
+ authorize_download_artifacts!
+
+ build = user_project.latest_successful_build_for_ref!(params[:job], params[:ref_name])
+ authorize_read_job_artifacts!(build)
+
+ path = Gitlab::Ci::Build::Artifacts::Path
+ .new(params[:artifact_path])
+
+ bad_request! unless path.valid?
+
+ send_artifacts_entry(build.artifacts_file, path)
+ end
+
+ desc 'Download the artifacts archive from a job' do
+ detail 'This feature was introduced in GitLab 8.5'
+ end
+ params do
+ requires :job_id, type: Integer, desc: 'The ID of a job'
+ end
+ route_setting :authentication, job_token_allowed: true
+ get ':id/jobs/:job_id/artifacts' do
+ authorize_download_artifacts!
+
+ build = find_build!(params[:job_id])
+ authorize_read_job_artifacts!(build)
+
+ present_carrierwave_file!(build.artifacts_file)
+ end
+
+ desc 'Download a specific file from artifacts archive' do
+ detail 'This feature was introduced in GitLab 10.0'
+ end
+ params do
+ requires :job_id, type: Integer, desc: 'The ID of a job'
+ requires :artifact_path, type: String, desc: 'Artifact path'
+ end
+ route_setting :authentication, job_token_allowed: true
+ get ':id/jobs/:job_id/artifacts/*artifact_path', format: false do
+ authorize_download_artifacts!
+
+ build = find_build!(params[:job_id])
+ authorize_read_job_artifacts!(build)
+
+ not_found! unless build.available_artifacts?
+
+ path = Gitlab::Ci::Build::Artifacts::Path
+ .new(params[:artifact_path])
+
+ bad_request! unless path.valid?
+
+ send_artifacts_entry(build.artifacts_file, path)
+ end
+
+ desc 'Keep the artifacts to prevent them from being deleted' do
+ success ::API::Entities::Ci::Job
+ end
+ params do
+ requires :job_id, type: Integer, desc: 'The ID of a job'
+ end
+ post ':id/jobs/:job_id/artifacts/keep' do
+ authorize_update_builds!
+
+ build = find_build!(params[:job_id])
+ authorize!(:update_build, build)
+ break not_found!(build) unless build.artifacts?
+
+ build.keep_artifacts!
+
+ status 200
+ present build, with: ::API::Entities::Ci::Job
+ end
+
+ desc 'Delete the artifacts files from a job' do
+ detail 'This feature was introduced in GitLab 11.9'
+ end
+ params do
+ requires :job_id, type: Integer, desc: 'The ID of a job'
+ end
+ delete ':id/jobs/:job_id/artifacts' do
+ authorize_destroy_artifacts!
+ build = find_build!(params[:job_id])
+ authorize!(:destroy_artifacts, build)
+
+ build.erase_erasable_artifacts!
+
+ status :no_content
+ end
+ end
+ end
+ end
+end
diff --git a/lib/api/ci/jobs.rb b/lib/api/ci/jobs.rb
new file mode 100644
index 00000000000..eea1637c32a
--- /dev/null
+++ b/lib/api/ci/jobs.rb
@@ -0,0 +1,206 @@
+# frozen_string_literal: true
+
+module API
+ module Ci
+ class Jobs < ::API::Base
+ include PaginationParams
+ before { authenticate! }
+
+ resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
+ params do
+ requires :id, type: String, desc: 'The ID of a project'
+ end
+
+ helpers do
+ params :optional_scope do
+ optional :scope, types: [String, Array[String]], desc: 'The scope of builds to show',
+ values: ::CommitStatus::AVAILABLE_STATUSES,
+ coerce_with: ->(scope) {
+ case scope
+ when String
+ [scope]
+ when ::Hash
+ scope.values
+ when ::Array
+ scope
+ else
+ ['unknown']
+ end
+ }
+ end
+ end
+
+ desc 'Get a projects jobs' do
+ success Entities::Ci::Job
+ end
+ params do
+ use :optional_scope
+ use :pagination
+ end
+ # rubocop: disable CodeReuse/ActiveRecord
+ get ':id/jobs', feature_category: :continuous_integration do
+ authorize_read_builds!
+
+ builds = user_project.builds.order('id DESC')
+ builds = filter_builds(builds, params[:scope])
+
+ builds = builds.preload(:user, :job_artifacts_archive, :job_artifacts, :runner, :tags, pipeline: :project)
+ present paginate(builds), with: Entities::Ci::Job
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+
+ desc 'Get a specific job of a project' do
+ success Entities::Ci::Job
+ end
+ params do
+ requires :job_id, type: Integer, desc: 'The ID of a job'
+ end
+ get ':id/jobs/:job_id', feature_category: :continuous_integration do
+ authorize_read_builds!
+
+ build = find_build!(params[:job_id])
+
+ present build, with: Entities::Ci::Job
+ end
+
+ # TODO: We should use `present_disk_file!` and leave this implementation for backward compatibility (when build trace
+ # is saved in the DB instead of file). But before that, we need to consider how to replace the value of
+ # `runners_token` with some mask (like `xxxxxx`) when sending trace file directly by workhorse.
+ desc 'Get a trace of a specific job of a project'
+ params do
+ requires :job_id, type: Integer, desc: 'The ID of a job'
+ end
+ get ':id/jobs/:job_id/trace', feature_category: :continuous_integration do
+ authorize_read_builds!
+
+ build = find_build!(params[:job_id])
+
+ authorize_read_build_trace!(build) if build
+
+ header 'Content-Disposition', "infile; filename=\"#{build.id}.log\""
+ content_type 'text/plain'
+ env['api.format'] = :binary
+
+ # The trace can be nil bu body method expects a string as an argument.
+ trace = build.trace.raw || ''
+ body trace
+ end
+
+ desc 'Cancel a specific job of a project' do
+ success Entities::Ci::Job
+ end
+ params do
+ requires :job_id, type: Integer, desc: 'The ID of a job'
+ end
+ post ':id/jobs/:job_id/cancel', feature_category: :continuous_integration do
+ authorize_update_builds!
+
+ build = find_build!(params[:job_id])
+ authorize!(:update_build, build)
+
+ build.cancel
+
+ present build, with: Entities::Ci::Job
+ end
+
+ desc 'Retry a specific build of a project' do
+ success Entities::Ci::Job
+ end
+ params do
+ requires :job_id, type: Integer, desc: 'The ID of a build'
+ end
+ post ':id/jobs/:job_id/retry', feature_category: :continuous_integration do
+ authorize_update_builds!
+
+ build = find_build!(params[:job_id])
+ authorize!(:update_build, build)
+ break forbidden!('Job is not retryable') unless build.retryable?
+
+ build = ::Ci::Build.retry(build, current_user)
+
+ present build, with: Entities::Ci::Job
+ end
+
+ desc 'Erase job (remove artifacts and the trace)' do
+ success Entities::Ci::Job
+ end
+ params do
+ requires :job_id, type: Integer, desc: 'The ID of a build'
+ end
+ post ':id/jobs/:job_id/erase', feature_category: :continuous_integration do
+ authorize_update_builds!
+
+ build = find_build!(params[:job_id])
+ authorize!(:erase_build, build)
+ break forbidden!('Job is not erasable!') unless build.erasable?
+
+ build.erase(erased_by: current_user)
+ present build, with: Entities::Ci::Job
+ end
+
+ desc 'Trigger an actionable job (manual, delayed, etc)' do
+ success Entities::Ci::JobBasic
+ detail 'This feature was added in GitLab 8.11'
+ end
+ params do
+ requires :job_id, type: Integer, desc: 'The ID of a Job'
+ end
+
+ post ":id/jobs/:job_id/play", feature_category: :continuous_integration do
+ authorize_read_builds!
+
+ job = find_job!(params[:job_id])
+
+ authorize!(:play_job, job)
+
+ bad_request!("Unplayable Job") unless job.playable?
+
+ job.play(current_user)
+
+ status 200
+
+ if job.is_a?(::Ci::Build)
+ present job, with: Entities::Ci::Job
+ else
+ present job, with: Entities::Ci::Bridge
+ end
+ end
+ end
+
+ resource :job do
+ desc 'Get current project using job token' do
+ success Entities::Ci::Job
+ end
+ route_setting :authentication, job_token_allowed: true
+ get '', feature_category: :continuous_integration do
+ validate_current_authenticated_job
+
+ present current_authenticated_job, with: Entities::Ci::Job
+ end
+ end
+
+ helpers do
+ # rubocop: disable CodeReuse/ActiveRecord
+ def filter_builds(builds, scope)
+ return builds if scope.nil? || scope.empty?
+
+ available_statuses = ::CommitStatus::AVAILABLE_STATUSES
+
+ unknown = scope - available_statuses
+ render_api_error!('Scope contains invalid value(s)', 400) unless unknown.empty?
+
+ builds.where(status: available_statuses && scope)
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+
+ def validate_current_authenticated_job
+ # current_authenticated_job will be nil if user is using
+ # a valid authentication (like PRIVATE-TOKEN) that is not CI_JOB_TOKEN
+ not_found!('Job') unless current_authenticated_job
+ end
+ end
+ end
+ end
+end
+
+API::Ci::Jobs.prepend_mod_with('API::Ci::Jobs')
diff --git a/lib/api/ci/runner.rb b/lib/api/ci/runner.rb
index 0bac6fe2054..b931e16f811 100644
--- a/lib/api/ci/runner.rb
+++ b/lib/api/ci/runner.rb
@@ -3,7 +3,7 @@
module API
module Ci
class Runner < ::API::Base
- helpers ::API::Helpers::Runner
+ helpers ::API::Ci::Helpers::Runner
content_type :txt, 'text/plain'
diff --git a/lib/api/ci/triggers.rb b/lib/api/ci/triggers.rb
new file mode 100644
index 00000000000..6a2b16e1568
--- /dev/null
+++ b/lib/api/ci/triggers.rb
@@ -0,0 +1,148 @@
+# frozen_string_literal: true
+
+module API
+ module Ci
+ class Triggers < ::API::Base
+ include PaginationParams
+
+ HTTP_GITLAB_EVENT_HEADER = "HTTP_#{WebHookService::GITLAB_EVENT_HEADER}".underscore.upcase
+
+ feature_category :continuous_integration
+
+ params do
+ requires :id, type: String, desc: 'The ID of a project'
+ end
+ resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
+ desc 'Trigger a GitLab project pipeline' do
+ success Entities::Ci::Pipeline
+ end
+ params do
+ requires :ref, type: String, desc: 'The commit sha or name of a branch or tag', allow_blank: false
+ requires :token, type: String, desc: 'The unique token of trigger or job token'
+ optional :variables, type: Hash, desc: 'The list of variables to be injected into build'
+ end
+ post ":id/(ref/:ref/)trigger/pipeline", requirements: { ref: /.+/ } do
+ Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab/-/issues/20758')
+
+ forbidden! if gitlab_pipeline_hook_request?
+
+ # validate variables
+ params[:variables] = params[:variables].to_h
+ unless params[:variables].all? { |key, value| key.is_a?(String) && value.is_a?(String) }
+ render_api_error!('variables needs to be a map of key-valued strings', 400)
+ end
+
+ project = find_project(params[:id])
+ not_found! unless project
+
+ result = ::Ci::PipelineTriggerService.new(project, nil, params).execute
+ not_found! unless result
+
+ if result.error?
+ render_api_error!(result[:message], result[:http_status])
+ else
+ present result[:pipeline], with: Entities::Ci::Pipeline
+ end
+ end
+
+ desc 'Get triggers list' do
+ success Entities::Trigger
+ end
+ params do
+ use :pagination
+ end
+ # rubocop: disable CodeReuse/ActiveRecord
+ get ':id/triggers' do
+ authenticate!
+ authorize! :admin_build, user_project
+
+ triggers = user_project.triggers.includes(:trigger_requests)
+
+ present paginate(triggers), with: Entities::Trigger, current_user: current_user
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+
+ desc 'Get specific trigger of a project' do
+ success Entities::Trigger
+ end
+ params do
+ requires :trigger_id, type: Integer, desc: 'The trigger ID'
+ end
+ get ':id/triggers/:trigger_id' do
+ authenticate!
+ authorize! :admin_build, user_project
+
+ trigger = user_project.triggers.find(params.delete(:trigger_id))
+ break not_found!('Trigger') unless trigger
+
+ present trigger, with: Entities::Trigger, current_user: current_user
+ end
+
+ desc 'Create a trigger' do
+ success Entities::Trigger
+ end
+ params do
+ requires :description, type: String, desc: 'The trigger description'
+ end
+ post ':id/triggers' do
+ authenticate!
+ authorize! :admin_build, user_project
+
+ trigger = user_project.triggers.create(
+ declared_params(include_missing: false).merge(owner: current_user))
+
+ if trigger.valid?
+ present trigger, with: Entities::Trigger, current_user: current_user
+ else
+ render_validation_error!(trigger)
+ end
+ end
+
+ desc 'Update a trigger' do
+ success Entities::Trigger
+ end
+ params do
+ requires :trigger_id, type: Integer, desc: 'The trigger ID'
+ optional :description, type: String, desc: 'The trigger description'
+ end
+ put ':id/triggers/:trigger_id' do
+ authenticate!
+ authorize! :admin_build, user_project
+
+ trigger = user_project.triggers.find(params.delete(:trigger_id))
+ break not_found!('Trigger') unless trigger
+
+ authorize! :admin_trigger, trigger
+
+ if trigger.update(declared_params(include_missing: false))
+ present trigger, with: Entities::Trigger, current_user: current_user
+ else
+ render_validation_error!(trigger)
+ end
+ end
+
+ desc 'Delete a trigger' do
+ success Entities::Trigger
+ end
+ params do
+ requires :trigger_id, type: Integer, desc: 'The trigger ID'
+ end
+ delete ':id/triggers/:trigger_id' do
+ authenticate!
+ authorize! :admin_build, user_project
+
+ trigger = user_project.triggers.find(params.delete(:trigger_id))
+ break not_found!('Trigger') unless trigger
+
+ destroy_conditionally!(trigger)
+ end
+ end
+
+ helpers do
+ def gitlab_pipeline_hook_request?
+ request.get_header(HTTP_GITLAB_EVENT_HEADER) == WebHookService.hook_to_event(:pipeline_hooks)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/api/ci/variables.rb b/lib/api/ci/variables.rb
new file mode 100644
index 00000000000..9c04d5e9923
--- /dev/null
+++ b/lib/api/ci/variables.rb
@@ -0,0 +1,126 @@
+# frozen_string_literal: true
+
+module API
+ module Ci
+ class Variables < ::API::Base
+ include PaginationParams
+
+ before { authenticate! }
+ before { authorize! :admin_build, user_project }
+
+ feature_category :pipeline_authoring
+
+ helpers ::API::Helpers::VariablesHelpers
+
+ params do
+ requires :id, type: String, desc: 'The ID of a project'
+ end
+
+ resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
+ desc 'Get project variables' do
+ success Entities::Ci::Variable
+ end
+ params do
+ use :pagination
+ end
+ get ':id/variables' do
+ variables = user_project.variables
+ present paginate(variables), with: Entities::Ci::Variable
+ end
+
+ desc 'Get a specific variable from a project' do
+ success Entities::Ci::Variable
+ end
+ params do
+ requires :key, type: String, desc: 'The key of the variable'
+ end
+ # rubocop: disable CodeReuse/ActiveRecord
+ get ':id/variables/:key' do
+ variable = find_variable(user_project, params)
+ not_found!('Variable') unless variable
+
+ present variable, with: Entities::Ci::Variable
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+
+ desc 'Create a new variable in a project' do
+ success Entities::Ci::Variable
+ end
+ params do
+ requires :key, type: String, desc: 'The key of the variable'
+ requires :value, type: String, desc: 'The value of the variable'
+ optional :protected, type: Boolean, desc: 'Whether the variable is protected'
+ optional :masked, type: Boolean, desc: 'Whether the variable is masked'
+ optional :variable_type, type: String, values: ::Ci::Variable.variable_types.keys, desc: 'The type of variable, must be one of env_var or file. Defaults to env_var'
+ optional :environment_scope, type: String, desc: 'The environment_scope of the variable'
+ end
+ post ':id/variables' do
+ variable = ::Ci::ChangeVariableService.new(
+ container: user_project,
+ current_user: current_user,
+ params: { action: :create, variable_params: declared_params(include_missing: false) }
+ ).execute
+
+ if variable.valid?
+ present variable, with: Entities::Ci::Variable
+ else
+ render_validation_error!(variable)
+ end
+ end
+
+ desc 'Update an existing variable from a project' do
+ success Entities::Ci::Variable
+ end
+ params do
+ optional :key, type: String, desc: 'The key of the variable'
+ optional :value, type: String, desc: 'The value of the variable'
+ optional :protected, type: Boolean, desc: 'Whether the variable is protected'
+ optional :masked, type: Boolean, desc: 'Whether the variable is masked'
+ optional :variable_type, type: String, values: ::Ci::Variable.variable_types.keys, desc: 'The type of variable, must be one of env_var or file'
+ optional :environment_scope, type: String, desc: 'The environment_scope of the variable'
+ optional :filter, type: Hash, desc: 'Available filters: [environment_scope]. Example: filter[environment_scope]=production'
+ end
+ # rubocop: disable CodeReuse/ActiveRecord
+ put ':id/variables/:key' do
+ variable = find_variable(user_project, params)
+ not_found!('Variable') unless variable
+
+ variable = ::Ci::ChangeVariableService.new(
+ container: user_project,
+ current_user: current_user,
+ params: { action: :update, variable: variable, variable_params: declared_params(include_missing: false).except(:key, :filter) }
+ ).execute
+
+ if variable.valid?
+ present variable, with: Entities::Ci::Variable
+ else
+ render_validation_error!(variable)
+ end
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+
+ desc 'Delete an existing variable from a project' do
+ success Entities::Ci::Variable
+ end
+ params do
+ requires :key, type: String, desc: 'The key of the variable'
+ optional :filter, type: Hash, desc: 'Available filters: [environment_scope]. Example: filter[environment_scope]=production'
+ end
+ # rubocop: disable CodeReuse/ActiveRecord
+ delete ':id/variables/:key' do
+ variable = find_variable(user_project, params)
+ not_found!('Variable') unless variable
+
+ ::Ci::ChangeVariableService.new(
+ container: user_project,
+ current_user: current_user,
+ params: { action: :destroy, variable: variable }
+ ).execute
+
+ no_content!
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+ end
+ end
+ end
+end
diff --git a/lib/api/group_variables.rb b/lib/api/group_variables.rb
index 8d52a0a5b4e..13daf05fc78 100644
--- a/lib/api/group_variables.rb
+++ b/lib/api/group_variables.rb
@@ -8,7 +8,7 @@ module API
before { authorize! :admin_group, user_group }
feature_category :continuous_integration
- helpers Helpers::VariablesHelpers
+ helpers ::API::Helpers::VariablesHelpers
params do
requires :id, type: String, desc: 'The ID of a group'
diff --git a/lib/api/helpers/projects_helpers.rb b/lib/api/helpers/projects_helpers.rb
index 272452bd8db..0fdd6c141a9 100644
--- a/lib/api/helpers/projects_helpers.rb
+++ b/lib/api/helpers/projects_helpers.rb
@@ -132,7 +132,10 @@ module API
:forking_access_level,
:issues_access_level,
:lfs_enabled,
+ :merge_pipelines_enabled,
:merge_requests_access_level,
+ :merge_requests_template,
+ :merge_trains_enabled,
:merge_method,
:name,
:only_allow_merge_if_all_discussions_are_resolved,
diff --git a/lib/api/helpers/runner.rb b/lib/api/helpers/runner.rb
deleted file mode 100644
index a022d1a56ac..00000000000
--- a/lib/api/helpers/runner.rb
+++ /dev/null
@@ -1,121 +0,0 @@
-# frozen_string_literal: true
-
-module API
- module Helpers
- module Runner
- include Gitlab::Utils::StrongMemoize
-
- prepend_mod_with('API::Helpers::Runner') # rubocop: disable Cop/InjectEnterpriseEditionModule
-
- JOB_TOKEN_HEADER = 'HTTP_JOB_TOKEN'
- JOB_TOKEN_PARAM = :token
-
- def runner_registration_token_valid?
- ActiveSupport::SecurityUtils.secure_compare(params[:token], Gitlab::CurrentSettings.runners_registration_token)
- end
-
- def runner_registrar_valid?(type)
- Feature.disabled?(:runner_registration_control) || Gitlab::CurrentSettings.valid_runner_registrars.include?(type)
- end
-
- def authenticate_runner!
- forbidden! unless current_runner
-
- current_runner
- .heartbeat(get_runner_details_from_request)
- end
-
- def get_runner_details_from_request
- return get_runner_ip unless params['info'].present?
-
- attributes_for_keys(%w(name version revision platform architecture), params['info'])
- .merge(get_runner_config_from_request)
- .merge(get_runner_ip)
- end
-
- def get_runner_ip
- { ip_address: ip_address }
- end
-
- def current_runner
- token = params[:token]
-
- if token
- ::Gitlab::Database::LoadBalancing::RackMiddleware
- .stick_or_unstick(env, :runner, token)
- end
-
- strong_memoize(:current_runner) do
- ::Ci::Runner.find_by_token(token.to_s)
- end
- end
-
- # HTTP status codes to terminate the job on GitLab Runner:
- # - 403
- def authenticate_job!(require_running: true)
- job = current_job
-
- # 404 is not returned here because we want to terminate the job if it's
- # running. A 404 can be returned from anywhere in the networking stack which is why
- # we are explicit about a 403, we should improve this in
- # https://gitlab.com/gitlab-org/gitlab/-/issues/327703
- forbidden! unless job
-
- forbidden! unless job_token_valid?(job)
-
- forbidden!('Project has been deleted!') if job.project.nil? || job.project.pending_delete?
- forbidden!('Job has been erased!') if job.erased?
-
- if require_running
- job_forbidden!(job, 'Job is not running') unless job.running?
- end
-
- job.runner&.heartbeat(get_runner_ip)
-
- job
- end
-
- def current_job
- id = params[:id]
-
- if id
- ::Gitlab::Database::LoadBalancing::RackMiddleware
- .stick_or_unstick(env, :build, id)
- end
-
- strong_memoize(:current_job) do
- ::Ci::Build.find_by_id(id)
- end
- end
-
- def job_token_valid?(job)
- token = (params[JOB_TOKEN_PARAM] || env[JOB_TOKEN_HEADER]).to_s
- token && job.valid_token?(token)
- end
-
- def job_forbidden!(job, reason)
- header 'Job-Status', job.status
- forbidden!(reason)
- end
-
- def set_application_context
- return unless current_job
-
- Gitlab::ApplicationContext.push(
- user: -> { current_job.user },
- project: -> { current_job.project }
- )
- end
-
- def track_ci_minutes_usage!(_build, _runner)
- # noop: overridden in EE
- end
-
- private
-
- def get_runner_config_from_request
- { config: attributes_for_keys(%w(gpus), params.dig('info', 'config')) }
- end
- end
- end
-end
diff --git a/lib/api/job_artifacts.rb b/lib/api/job_artifacts.rb
deleted file mode 100644
index beda4433e4f..00000000000
--- a/lib/api/job_artifacts.rb
+++ /dev/null
@@ -1,141 +0,0 @@
-# frozen_string_literal: true
-
-module API
- class JobArtifacts < ::API::Base
- before { authenticate_non_get! }
-
- feature_category :build_artifacts
-
- # EE::API::JobArtifacts would override the following helpers
- helpers do
- def authorize_download_artifacts!
- authorize_read_builds!
- end
- end
-
- prepend_mod_with('API::JobArtifacts') # rubocop: disable Cop/InjectEnterpriseEditionModule
-
- params do
- requires :id, type: String, desc: 'The ID of a project'
- end
- resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
- desc 'Download the artifacts archive from a job' do
- detail 'This feature was introduced in GitLab 8.10'
- end
- params do
- requires :ref_name, type: String, desc: 'The ref from repository'
- requires :job, type: String, desc: 'The name for the job'
- end
- route_setting :authentication, job_token_allowed: true
- get ':id/jobs/artifacts/:ref_name/download',
- requirements: { ref_name: /.+/ } do
- authorize_download_artifacts!
-
- latest_build = user_project.latest_successful_build_for_ref!(params[:job], params[:ref_name])
- authorize_read_job_artifacts!(latest_build)
-
- present_carrierwave_file!(latest_build.artifacts_file)
- end
-
- desc 'Download a specific file from artifacts archive from a ref' do
- detail 'This feature was introduced in GitLab 11.5'
- end
- params do
- requires :ref_name, type: String, desc: 'The ref from repository'
- requires :job, type: String, desc: 'The name for the job'
- requires :artifact_path, type: String, desc: 'Artifact path'
- end
- route_setting :authentication, job_token_allowed: true
- get ':id/jobs/artifacts/:ref_name/raw/*artifact_path',
- format: false,
- requirements: { ref_name: /.+/ } do
- authorize_download_artifacts!
-
- build = user_project.latest_successful_build_for_ref!(params[:job], params[:ref_name])
- authorize_read_job_artifacts!(build)
-
- path = Gitlab::Ci::Build::Artifacts::Path
- .new(params[:artifact_path])
-
- bad_request! unless path.valid?
-
- send_artifacts_entry(build.artifacts_file, path)
- end
-
- desc 'Download the artifacts archive from a job' do
- detail 'This feature was introduced in GitLab 8.5'
- end
- params do
- requires :job_id, type: Integer, desc: 'The ID of a job'
- end
- route_setting :authentication, job_token_allowed: true
- get ':id/jobs/:job_id/artifacts' do
- authorize_download_artifacts!
-
- build = find_build!(params[:job_id])
- authorize_read_job_artifacts!(build)
-
- present_carrierwave_file!(build.artifacts_file)
- end
-
- desc 'Download a specific file from artifacts archive' do
- detail 'This feature was introduced in GitLab 10.0'
- end
- params do
- requires :job_id, type: Integer, desc: 'The ID of a job'
- requires :artifact_path, type: String, desc: 'Artifact path'
- end
- route_setting :authentication, job_token_allowed: true
- get ':id/jobs/:job_id/artifacts/*artifact_path', format: false do
- authorize_download_artifacts!
-
- build = find_build!(params[:job_id])
- authorize_read_job_artifacts!(build)
-
- not_found! unless build.available_artifacts?
-
- path = Gitlab::Ci::Build::Artifacts::Path
- .new(params[:artifact_path])
-
- bad_request! unless path.valid?
-
- send_artifacts_entry(build.artifacts_file, path)
- end
-
- desc 'Keep the artifacts to prevent them from being deleted' do
- success ::API::Entities::Ci::Job
- end
- params do
- requires :job_id, type: Integer, desc: 'The ID of a job'
- end
- post ':id/jobs/:job_id/artifacts/keep' do
- authorize_update_builds!
-
- build = find_build!(params[:job_id])
- authorize!(:update_build, build)
- break not_found!(build) unless build.artifacts?
-
- build.keep_artifacts!
-
- status 200
- present build, with: ::API::Entities::Ci::Job
- end
-
- desc 'Delete the artifacts files from a job' do
- detail 'This feature was introduced in GitLab 11.9'
- end
- params do
- requires :job_id, type: Integer, desc: 'The ID of a job'
- end
- delete ':id/jobs/:job_id/artifacts' do
- authorize_destroy_artifacts!
- build = find_build!(params[:job_id])
- authorize!(:destroy_artifacts, build)
-
- build.erase_erasable_artifacts!
-
- status :no_content
- end
- end
- end
-end
diff --git a/lib/api/jobs.rb b/lib/api/jobs.rb
deleted file mode 100644
index 723a5b0fa3a..00000000000
--- a/lib/api/jobs.rb
+++ /dev/null
@@ -1,204 +0,0 @@
-# frozen_string_literal: true
-
-module API
- class Jobs < ::API::Base
- include PaginationParams
- before { authenticate! }
-
- resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
- params do
- requires :id, type: String, desc: 'The ID of a project'
- end
-
- helpers do
- params :optional_scope do
- optional :scope, types: [String, Array[String]], desc: 'The scope of builds to show',
- values: ::CommitStatus::AVAILABLE_STATUSES,
- coerce_with: ->(scope) {
- case scope
- when String
- [scope]
- when ::Hash
- scope.values
- when ::Array
- scope
- else
- ['unknown']
- end
- }
- end
- end
-
- desc 'Get a projects jobs' do
- success Entities::Ci::Job
- end
- params do
- use :optional_scope
- use :pagination
- end
- # rubocop: disable CodeReuse/ActiveRecord
- get ':id/jobs', feature_category: :continuous_integration do
- authorize_read_builds!
-
- builds = user_project.builds.order('id DESC')
- builds = filter_builds(builds, params[:scope])
-
- builds = builds.preload(:user, :job_artifacts_archive, :job_artifacts, :runner, :tags, pipeline: :project)
- present paginate(builds), with: Entities::Ci::Job
- end
- # rubocop: enable CodeReuse/ActiveRecord
-
- desc 'Get a specific job of a project' do
- success Entities::Ci::Job
- end
- params do
- requires :job_id, type: Integer, desc: 'The ID of a job'
- end
- get ':id/jobs/:job_id', feature_category: :continuous_integration do
- authorize_read_builds!
-
- build = find_build!(params[:job_id])
-
- present build, with: Entities::Ci::Job
- end
-
- # TODO: We should use `present_disk_file!` and leave this implementation for backward compatibility (when build trace
- # is saved in the DB instead of file). But before that, we need to consider how to replace the value of
- # `runners_token` with some mask (like `xxxxxx`) when sending trace file directly by workhorse.
- desc 'Get a trace of a specific job of a project'
- params do
- requires :job_id, type: Integer, desc: 'The ID of a job'
- end
- get ':id/jobs/:job_id/trace', feature_category: :continuous_integration do
- authorize_read_builds!
-
- build = find_build!(params[:job_id])
-
- authorize_read_build_trace!(build) if build
-
- header 'Content-Disposition', "infile; filename=\"#{build.id}.log\""
- content_type 'text/plain'
- env['api.format'] = :binary
-
- # The trace can be nil bu body method expects a string as an argument.
- trace = build.trace.raw || ''
- body trace
- end
-
- desc 'Cancel a specific job of a project' do
- success Entities::Ci::Job
- end
- params do
- requires :job_id, type: Integer, desc: 'The ID of a job'
- end
- post ':id/jobs/:job_id/cancel', feature_category: :continuous_integration do
- authorize_update_builds!
-
- build = find_build!(params[:job_id])
- authorize!(:update_build, build)
-
- build.cancel
-
- present build, with: Entities::Ci::Job
- end
-
- desc 'Retry a specific build of a project' do
- success Entities::Ci::Job
- end
- params do
- requires :job_id, type: Integer, desc: 'The ID of a build'
- end
- post ':id/jobs/:job_id/retry', feature_category: :continuous_integration do
- authorize_update_builds!
-
- build = find_build!(params[:job_id])
- authorize!(:update_build, build)
- break forbidden!('Job is not retryable') unless build.retryable?
-
- build = ::Ci::Build.retry(build, current_user)
-
- present build, with: Entities::Ci::Job
- end
-
- desc 'Erase job (remove artifacts and the trace)' do
- success Entities::Ci::Job
- end
- params do
- requires :job_id, type: Integer, desc: 'The ID of a build'
- end
- post ':id/jobs/:job_id/erase', feature_category: :continuous_integration do
- authorize_update_builds!
-
- build = find_build!(params[:job_id])
- authorize!(:erase_build, build)
- break forbidden!('Job is not erasable!') unless build.erasable?
-
- build.erase(erased_by: current_user)
- present build, with: Entities::Ci::Job
- end
-
- desc 'Trigger an actionable job (manual, delayed, etc)' do
- success Entities::Ci::JobBasic
- detail 'This feature was added in GitLab 8.11'
- end
- params do
- requires :job_id, type: Integer, desc: 'The ID of a Job'
- end
-
- post ":id/jobs/:job_id/play", feature_category: :continuous_integration do
- authorize_read_builds!
-
- job = find_job!(params[:job_id])
-
- authorize!(:play_job, job)
-
- bad_request!("Unplayable Job") unless job.playable?
-
- job.play(current_user)
-
- status 200
-
- if job.is_a?(::Ci::Build)
- present job, with: Entities::Ci::Job
- else
- present job, with: Entities::Ci::Bridge
- end
- end
- end
-
- resource :job do
- desc 'Get current project using job token' do
- success Entities::Ci::Job
- end
- route_setting :authentication, job_token_allowed: true
- get '', feature_category: :continuous_integration do
- validate_current_authenticated_job
-
- present current_authenticated_job, with: Entities::Ci::Job
- end
- end
-
- helpers do
- # rubocop: disable CodeReuse/ActiveRecord
- def filter_builds(builds, scope)
- return builds if scope.nil? || scope.empty?
-
- available_statuses = ::CommitStatus::AVAILABLE_STATUSES
-
- unknown = scope - available_statuses
- render_api_error!('Scope contains invalid value(s)', 400) unless unknown.empty?
-
- builds.where(status: available_statuses && scope)
- end
- # rubocop: enable CodeReuse/ActiveRecord
-
- def validate_current_authenticated_job
- # current_authenticated_job will be nil if user is using
- # a valid authentication (like PRIVATE-TOKEN) that is not CI_JOB_TOKEN
- not_found!('Job') unless current_authenticated_job
- end
- end
- end
-end
-
-API::Jobs.prepend_mod_with('API::Jobs')
diff --git a/lib/api/triggers.rb b/lib/api/triggers.rb
deleted file mode 100644
index a359083a9d2..00000000000
--- a/lib/api/triggers.rb
+++ /dev/null
@@ -1,146 +0,0 @@
-# frozen_string_literal: true
-
-module API
- class Triggers < ::API::Base
- include PaginationParams
-
- HTTP_GITLAB_EVENT_HEADER = "HTTP_#{WebHookService::GITLAB_EVENT_HEADER}".underscore.upcase
-
- feature_category :continuous_integration
-
- params do
- requires :id, type: String, desc: 'The ID of a project'
- end
- resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
- desc 'Trigger a GitLab project pipeline' do
- success Entities::Ci::Pipeline
- end
- params do
- requires :ref, type: String, desc: 'The commit sha or name of a branch or tag', allow_blank: false
- requires :token, type: String, desc: 'The unique token of trigger or job token'
- optional :variables, type: Hash, desc: 'The list of variables to be injected into build'
- end
- post ":id/(ref/:ref/)trigger/pipeline", requirements: { ref: /.+/ } do
- Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab/-/issues/20758')
-
- forbidden! if gitlab_pipeline_hook_request?
-
- # validate variables
- params[:variables] = params[:variables].to_h
- unless params[:variables].all? { |key, value| key.is_a?(String) && value.is_a?(String) }
- render_api_error!('variables needs to be a map of key-valued strings', 400)
- end
-
- project = find_project(params[:id])
- not_found! unless project
-
- result = ::Ci::PipelineTriggerService.new(project, nil, params).execute
- not_found! unless result
-
- if result.error?
- render_api_error!(result[:message], result[:http_status])
- else
- present result[:pipeline], with: Entities::Ci::Pipeline
- end
- end
-
- desc 'Get triggers list' do
- success Entities::Trigger
- end
- params do
- use :pagination
- end
- # rubocop: disable CodeReuse/ActiveRecord
- get ':id/triggers' do
- authenticate!
- authorize! :admin_build, user_project
-
- triggers = user_project.triggers.includes(:trigger_requests)
-
- present paginate(triggers), with: Entities::Trigger, current_user: current_user
- end
- # rubocop: enable CodeReuse/ActiveRecord
-
- desc 'Get specific trigger of a project' do
- success Entities::Trigger
- end
- params do
- requires :trigger_id, type: Integer, desc: 'The trigger ID'
- end
- get ':id/triggers/:trigger_id' do
- authenticate!
- authorize! :admin_build, user_project
-
- trigger = user_project.triggers.find(params.delete(:trigger_id))
- break not_found!('Trigger') unless trigger
-
- present trigger, with: Entities::Trigger, current_user: current_user
- end
-
- desc 'Create a trigger' do
- success Entities::Trigger
- end
- params do
- requires :description, type: String, desc: 'The trigger description'
- end
- post ':id/triggers' do
- authenticate!
- authorize! :admin_build, user_project
-
- trigger = user_project.triggers.create(
- declared_params(include_missing: false).merge(owner: current_user))
-
- if trigger.valid?
- present trigger, with: Entities::Trigger, current_user: current_user
- else
- render_validation_error!(trigger)
- end
- end
-
- desc 'Update a trigger' do
- success Entities::Trigger
- end
- params do
- requires :trigger_id, type: Integer, desc: 'The trigger ID'
- optional :description, type: String, desc: 'The trigger description'
- end
- put ':id/triggers/:trigger_id' do
- authenticate!
- authorize! :admin_build, user_project
-
- trigger = user_project.triggers.find(params.delete(:trigger_id))
- break not_found!('Trigger') unless trigger
-
- authorize! :admin_trigger, trigger
-
- if trigger.update(declared_params(include_missing: false))
- present trigger, with: Entities::Trigger, current_user: current_user
- else
- render_validation_error!(trigger)
- end
- end
-
- desc 'Delete a trigger' do
- success Entities::Trigger
- end
- params do
- requires :trigger_id, type: Integer, desc: 'The trigger ID'
- end
- delete ':id/triggers/:trigger_id' do
- authenticate!
- authorize! :admin_build, user_project
-
- trigger = user_project.triggers.find(params.delete(:trigger_id))
- break not_found!('Trigger') unless trigger
-
- destroy_conditionally!(trigger)
- end
- end
-
- helpers do
- def gitlab_pipeline_hook_request?
- request.get_header(HTTP_GITLAB_EVENT_HEADER) == WebHookService.hook_to_event(:pipeline_hooks)
- end
- end
- end
-end
diff --git a/lib/api/variables.rb b/lib/api/variables.rb
deleted file mode 100644
index 75df0e050a6..00000000000
--- a/lib/api/variables.rb
+++ /dev/null
@@ -1,124 +0,0 @@
-# frozen_string_literal: true
-
-module API
- class Variables < ::API::Base
- include PaginationParams
-
- before { authenticate! }
- before { authorize! :admin_build, user_project }
-
- feature_category :pipeline_authoring
-
- helpers Helpers::VariablesHelpers
-
- params do
- requires :id, type: String, desc: 'The ID of a project'
- end
-
- resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
- desc 'Get project variables' do
- success Entities::Ci::Variable
- end
- params do
- use :pagination
- end
- get ':id/variables' do
- variables = user_project.variables
- present paginate(variables), with: Entities::Ci::Variable
- end
-
- desc 'Get a specific variable from a project' do
- success Entities::Ci::Variable
- end
- params do
- requires :key, type: String, desc: 'The key of the variable'
- end
- # rubocop: disable CodeReuse/ActiveRecord
- get ':id/variables/:key' do
- variable = find_variable(user_project, params)
- not_found!('Variable') unless variable
-
- present variable, with: Entities::Ci::Variable
- end
- # rubocop: enable CodeReuse/ActiveRecord
-
- desc 'Create a new variable in a project' do
- success Entities::Ci::Variable
- end
- params do
- requires :key, type: String, desc: 'The key of the variable'
- requires :value, type: String, desc: 'The value of the variable'
- optional :protected, type: Boolean, desc: 'Whether the variable is protected'
- optional :masked, type: Boolean, desc: 'Whether the variable is masked'
- optional :variable_type, type: String, values: ::Ci::Variable.variable_types.keys, desc: 'The type of variable, must be one of env_var or file. Defaults to env_var'
- optional :environment_scope, type: String, desc: 'The environment_scope of the variable'
- end
- post ':id/variables' do
- variable = ::Ci::ChangeVariableService.new(
- container: user_project,
- current_user: current_user,
- params: { action: :create, variable_params: declared_params(include_missing: false) }
- ).execute
-
- if variable.valid?
- present variable, with: Entities::Ci::Variable
- else
- render_validation_error!(variable)
- end
- end
-
- desc 'Update an existing variable from a project' do
- success Entities::Ci::Variable
- end
- params do
- optional :key, type: String, desc: 'The key of the variable'
- optional :value, type: String, desc: 'The value of the variable'
- optional :protected, type: Boolean, desc: 'Whether the variable is protected'
- optional :masked, type: Boolean, desc: 'Whether the variable is masked'
- optional :variable_type, type: String, values: ::Ci::Variable.variable_types.keys, desc: 'The type of variable, must be one of env_var or file'
- optional :environment_scope, type: String, desc: 'The environment_scope of the variable'
- optional :filter, type: Hash, desc: 'Available filters: [environment_scope]. Example: filter[environment_scope]=production'
- end
- # rubocop: disable CodeReuse/ActiveRecord
- put ':id/variables/:key' do
- variable = find_variable(user_project, params)
- not_found!('Variable') unless variable
-
- variable = ::Ci::ChangeVariableService.new(
- container: user_project,
- current_user: current_user,
- params: { action: :update, variable: variable, variable_params: declared_params(include_missing: false).except(:key, :filter) }
- ).execute
-
- if variable.valid?
- present variable, with: Entities::Ci::Variable
- else
- render_validation_error!(variable)
- end
- end
- # rubocop: enable CodeReuse/ActiveRecord
-
- desc 'Delete an existing variable from a project' do
- success Entities::Ci::Variable
- end
- params do
- requires :key, type: String, desc: 'The key of the variable'
- optional :filter, type: Hash, desc: 'Available filters: [environment_scope]. Example: filter[environment_scope]=production'
- end
- # rubocop: disable CodeReuse/ActiveRecord
- delete ':id/variables/:key' do
- variable = find_variable(user_project, params)
- not_found!('Variable') unless variable
-
- ::Ci::ChangeVariableService.new(
- container: user_project,
- current_user: current_user,
- params: { action: :destroy, variable: variable }
- ).execute
-
- no_content!
- end
- # rubocop: enable CodeReuse/ActiveRecord
- end
- end
-end
diff --git a/spec/lib/api/helpers/runner_helpers_spec.rb b/spec/lib/api/ci/helpers/runner_helpers_spec.rb
index 65b35845aab..c6638bea59e 100644
--- a/spec/lib/api/helpers/runner_helpers_spec.rb
+++ b/spec/lib/api/ci/helpers/runner_helpers_spec.rb
@@ -2,12 +2,12 @@
require 'spec_helper'
-RSpec.describe API::Helpers::Runner do
+RSpec.describe API::Ci::Helpers::Runner do
let(:ip_address) { '1.2.3.4' }
let(:runner_class) do
Class.new do
include API::Helpers
- include API::Helpers::Runner
+ include API::Ci::Helpers::Runner
attr_accessor :params
diff --git a/spec/lib/api/helpers/runner_spec.rb b/spec/lib/api/ci/helpers/runner_spec.rb
index e55c20b7ab6..99f2db544a5 100644
--- a/spec/lib/api/helpers/runner_spec.rb
+++ b/spec/lib/api/ci/helpers/runner_spec.rb
@@ -2,8 +2,8 @@
require 'spec_helper'
-RSpec.describe API::Helpers::Runner do
- let(:helper) { Class.new { include API::Helpers::Runner }.new }
+RSpec.describe API::Ci::Helpers::Runner do
+ let(:helper) { Class.new { include API::Ci::Helpers::Runner }.new }
before do
allow(helper).to receive(:env).and_return({})
diff --git a/spec/requests/api/jobs_spec.rb b/spec/requests/api/ci/jobs_spec.rb
index cff006bed94..b6ab9310471 100644
--- a/spec/requests/api/jobs_spec.rb
+++ b/spec/requests/api/ci/jobs_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::Jobs do
+RSpec.describe API::Ci::Jobs do
include HttpBasicAuthHelpers
include DependencyProxyHelpers
@@ -114,7 +114,7 @@ RSpec.describe API::Jobs do
context 'with job token authentication header' do
include_context 'with auth headers' do
- let(:header) { { API::Helpers::Runner::JOB_TOKEN_HEADER => running_job.token } }
+ let(:header) { { API::Ci::Helpers::Runner::JOB_TOKEN_HEADER => running_job.token } }
end
it_behaves_like 'returns common job data' do
@@ -150,7 +150,7 @@ RSpec.describe API::Jobs do
context 'with non running job' do
include_context 'with auth headers' do
- let(:header) { { API::Helpers::Runner::JOB_TOKEN_HEADER => job.token } }
+ let(:header) { { API::Ci::Helpers::Runner::JOB_TOKEN_HEADER => job.token } }
end
it_behaves_like 'returns unauthorized'
@@ -523,15 +523,13 @@ RSpec.describe API::Jobs do
context 'when artifacts are stored remotely' do
let(:proxy_download) { false }
+ let(:job) { create(:ci_build, pipeline: pipeline) }
+ let(:artifact) { create(:ci_job_artifact, :archive, :remote_store, job: job) }
before do
stub_artifacts_object_storage(proxy_download: proxy_download)
- end
-
- let(:job) { create(:ci_build, pipeline: pipeline) }
- let!(:artifact) { create(:ci_job_artifact, :archive, :remote_store, job: job) }
- before do
+ artifact
job.reload
get api("/projects/#{project.id}/jobs/#{job.id}/artifacts", api_user)
@@ -708,11 +706,7 @@ RSpec.describe API::Jobs do
context 'with branch name containing slash' do
before do
pipeline.reload
- pipeline.update!(ref: 'improve/awesome',
- sha: project.commit('improve/awesome').sha)
- end
-
- before do
+ pipeline.update!(ref: 'improve/awesome', sha: project.commit('improve/awesome').sha)
get_for_ref('improve/awesome')
end
diff --git a/spec/requests/api/ci/runner/jobs_artifacts_spec.rb b/spec/requests/api/ci/runner/jobs_artifacts_spec.rb
index 017a12a4a40..195aac2e5f0 100644
--- a/spec/requests/api/ci/runner/jobs_artifacts_spec.rb
+++ b/spec/requests/api/ci/runner/jobs_artifacts_spec.rb
@@ -32,7 +32,7 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
let(:job) { create(:ci_build, :pending, user: user, project: project, pipeline: pipeline, runner_id: runner.id) }
let(:jwt) { JWT.encode({ 'iss' => 'gitlab-workhorse' }, Gitlab::Workhorse.secret, 'HS256') }
let(:headers) { { 'GitLab-Workhorse' => '1.0', Gitlab::Workhorse::INTERNAL_API_REQUEST_HEADER => jwt } }
- let(:headers_with_token) { headers.merge(API::Helpers::Runner::JOB_TOKEN_HEADER => job.token) }
+ let(:headers_with_token) { headers.merge(API::Ci::Helpers::Runner::JOB_TOKEN_HEADER => job.token) }
let(:file_upload) { fixture_file_upload('spec/fixtures/banana_sample.gif', 'image/gif') }
let(:file_upload2) { fixture_file_upload('spec/fixtures/dk.png', 'image/gif') }
@@ -398,7 +398,7 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
context 'when using runners token' do
it 'responds with forbidden' do
- upload_artifacts(file_upload, headers.merge(API::Helpers::Runner::JOB_TOKEN_HEADER => job.project.runners_token))
+ upload_artifacts(file_upload, headers.merge(API::Ci::Helpers::Runner::JOB_TOKEN_HEADER => job.project.runners_token))
expect(response).to have_gitlab_http_status(:forbidden)
end
diff --git a/spec/requests/api/ci/runner/jobs_trace_spec.rb b/spec/requests/api/ci/runner/jobs_trace_spec.rb
index e20c7e36096..2760e306693 100644
--- a/spec/requests/api/ci/runner/jobs_trace_spec.rb
+++ b/spec/requests/api/ci/runner/jobs_trace_spec.rb
@@ -33,7 +33,7 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_trace_chunks do
project: project, user: user, runner_id: runner.id, pipeline: pipeline)
end
- let(:headers) { { API::Helpers::Runner::JOB_TOKEN_HEADER => job.token, 'Content-Type' => 'text/plain' } }
+ let(:headers) { { API::Ci::Helpers::Runner::JOB_TOKEN_HEADER => job.token, 'Content-Type' => 'text/plain' } }
let(:headers_with_range) { headers.merge({ 'Content-Range' => '11-20' }) }
let(:update_interval) { 10.seconds.to_i }
diff --git a/spec/requests/api/triggers_spec.rb b/spec/requests/api/ci/triggers_spec.rb
index 4318f106996..410e2ae405e 100644
--- a/spec/requests/api/triggers_spec.rb
+++ b/spec/requests/api/ci/triggers_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::Triggers do
+RSpec.describe API::Ci::Triggers do
let_it_be(:user) { create(:user) }
let_it_be(:user2) { create(:user) }
diff --git a/spec/requests/api/variables_spec.rb b/spec/requests/api/ci/variables_spec.rb
index 1ae9b0d548d..dc524e121d5 100644
--- a/spec/requests/api/variables_spec.rb
+++ b/spec/requests/api/ci/variables_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::Variables do
+RSpec.describe API::Ci::Variables do
let(:user) { create(:user) }
let(:user2) { create(:user) }
let!(:project) { create(:project, creator_id: user.id) }
diff --git a/spec/requests/api/project_attributes.yml b/spec/requests/api/project_attributes.yml
index 8341fac3191..244c053df4a 100644
--- a/spec/requests/api/project_attributes.yml
+++ b/spec/requests/api/project_attributes.yml
@@ -89,6 +89,8 @@ ci_cd_settings:
- group_runners_enabled
- merge_pipelines_enabled
- merge_trains_enabled
+ - merge_pipelines_enabled
+ - merge_trains_enabled
- auto_rollback_enabled
remapped_attributes:
default_git_depth: ci_default_git_depth