Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2022-11-02 21:09:03 +00:00
parent 983f6954d1
commit 03cd2a56f3
58 changed files with 897 additions and 344 deletions

View File

@ -171,7 +171,7 @@ graphql-schema-dump:
graphql-schema-dump as-if-foss: graphql-schema-dump as-if-foss:
extends: extends:
- graphql-schema-dump - graphql-schema-dump
- .frontend:rules:eslint-as-if-foss - .frontend:rules:default-frontend-jobs-as-if-foss
- .as-if-foss - .as-if-foss
.frontend-test-base: .frontend-test-base:

View File

@ -46,7 +46,7 @@
files: files:
- GITALY_SERVER_VERSION - GITALY_SERVER_VERSION
- lib/gitlab/setup_helper.rb - lib/gitlab/setup_helper.rb
prefix: "gitaly-binaries-debian-${DEBIAN_VERSION}-ruby-${RUBY_VERSION}" prefix: "gitaly-binaries-${GITALY_SERVER_VERSION}-debian-${DEBIAN_VERSION}-ruby-${RUBY_VERSION}"
paths: paths:
- ${TMP_TEST_FOLDER}/gitaly/_build/bin/ - ${TMP_TEST_FOLDER}/gitaly/_build/bin/
- ${TMP_TEST_FOLDER}/gitaly/_build/deps/git/install/ - ${TMP_TEST_FOLDER}/gitaly/_build/deps/git/install/

View File

@ -9,6 +9,10 @@
"CiManualVariable", "CiManualVariable",
"CiProjectVariable" "CiProjectVariable"
], ],
"CommitSignature": [
"GpgSignature",
"X509Signature"
],
"CurrentUserTodos": [ "CurrentUserTodos": [
"BoardEpic", "BoardEpic",
"Design", "Design",

View File

@ -86,6 +86,7 @@ export default {
:target="openInNewTab ? '_blank' : '_self'" :target="openInNewTab ? '_blank' : '_self'"
:href="value.url" :href="value.url"
data-testid="uncompleted-learn-gitlab-link" data-testid="uncompleted-learn-gitlab-link"
data-qa-selector="uncompleted_learn_gitlab_link"
data-track-action="click_link" data-track-action="click_link"
:data-track-label="actionLabelValue('trackLabel')" :data-track-label="actionLabelValue('trackLabel')"
>{{ actionLabelValue('title') }}</gl-link >{{ actionLabelValue('title') }}</gl-link

View File

@ -41,6 +41,8 @@ export default {
featureFlagsHelpText: s__( featureFlagsHelpText: s__(
'ProjectSettings|Roll out new features without redeploying with feature flags.', 'ProjectSettings|Roll out new features without redeploying with feature flags.',
), ),
infrastructureLabel: s__('ProjectSettings|Infrastructure'),
infrastructureHelpText: s__('ProjectSettings|Configure your infrastructure.'),
monitorLabel: s__('ProjectSettings|Monitor'), monitorLabel: s__('ProjectSettings|Monitor'),
packagesHelpText: s__( packagesHelpText: s__(
'ProjectSettings|Every project can have its own space to store its packages. Note: The Package Registry is always visible when a project is public.', 'ProjectSettings|Every project can have its own space to store its packages. Note: The Package Registry is always visible when a project is public.',
@ -157,6 +159,11 @@ export default {
required: false, required: false,
default: '', default: '',
}, },
infrastructureHelpPath: {
type: String,
required: false,
default: '',
},
releasesHelpPath: { releasesHelpPath: {
type: String, type: String,
required: false, required: false,
@ -245,6 +252,7 @@ export default {
operationsAccessLevel: featureAccessLevel.EVERYONE, operationsAccessLevel: featureAccessLevel.EVERYONE,
environmentsAccessLevel: featureAccessLevel.EVERYONE, environmentsAccessLevel: featureAccessLevel.EVERYONE,
featureFlagsAccessLevel: featureAccessLevel.PROJECT_MEMBERS, featureFlagsAccessLevel: featureAccessLevel.PROJECT_MEMBERS,
infrastructureAccessLevel: featureAccessLevel.PROJECT_MEMBERS,
releasesAccessLevel: featureAccessLevel.EVERYONE, releasesAccessLevel: featureAccessLevel.EVERYONE,
monitorAccessLevel: featureAccessLevel.EVERYONE, monitorAccessLevel: featureAccessLevel.EVERYONE,
containerRegistryAccessLevel: featureAccessLevel.EVERYONE, containerRegistryAccessLevel: featureAccessLevel.EVERYONE,
@ -433,6 +441,10 @@ export default {
featureAccessLevel.PROJECT_MEMBERS, featureAccessLevel.PROJECT_MEMBERS,
this.featureFlagsAccessLevel, this.featureFlagsAccessLevel,
); );
this.infrastructureAccessLevel = Math.min(
featureAccessLevel.PROJECT_MEMBERS,
this.infrastructureAccessLevel,
);
this.releasesAccessLevel = Math.min( this.releasesAccessLevel = Math.min(
featureAccessLevel.PROJECT_MEMBERS, featureAccessLevel.PROJECT_MEMBERS,
this.releasesAccessLevel, this.releasesAccessLevel,
@ -981,6 +993,19 @@ export default {
name="project[project_feature_attributes][feature_flags_access_level]" name="project[project_feature_attributes][feature_flags_access_level]"
/> />
</project-setting-row> </project-setting-row>
<project-setting-row
ref="infrastructure-settings"
:label="$options.i18n.infrastructureLabel"
:help-text="$options.i18n.infrastructureHelpText"
:help-path="infrastructureHelpPath"
>
<project-feature-setting
v-model="infrastructureAccessLevel"
:label="$options.i18n.infrastructureLabel"
:options="featureAccessLevelOptions"
name="project[project_feature_attributes][infrastructure_access_level]"
/>
</project-setting-row>
</template> </template>
<project-setting-row <project-setting-row
ref="releases-settings" ref="releases-settings"

View File

@ -55,6 +55,7 @@ export default {
class="js-sidebar-dropdown-toggle edit-link btn gl-text-gray-900! gl-ml-auto hide-collapsed btn-default btn-sm gl-button btn-default-tertiary float-right" class="js-sidebar-dropdown-toggle edit-link btn gl-text-gray-900! gl-ml-auto hide-collapsed btn-default btn-sm gl-button btn-default-tertiary float-right"
href="#" href="#"
data-test-id="edit-link" data-test-id="edit-link"
data-qa-selector="edit_link"
data-track-action="click_edit_button" data-track-action="click_edit_button"
data-track-label="right_sidebar" data-track-label="right_sidebar"
data-track-property="assignee" data-track-property="assignee"

View File

@ -56,6 +56,7 @@ export default {
type="button" type="button"
class="gl-button btn-link gl-reset-color!" class="gl-button btn-link gl-reset-color!"
data-testid="assign-yourself" data-testid="assign-yourself"
data-qa-selector="assign_yourself_button"
@click="assignSelf" @click="assignSelf"
> >
{{ __('assign yourself') }} {{ __('assign yourself') }}

View File

@ -91,6 +91,7 @@ export default {
<div <div
class="gl-ml-3 gl-line-height-normal gl-display-grid gl-align-items-center" class="gl-ml-3 gl-line-height-normal gl-display-grid gl-align-items-center"
data-testid="username" data-testid="username"
data-qa-selector="username"
> >
<user-name-with-status :name="user.name" :availability="userAvailability(user)" /> <user-name-with-status :name="user.name" :availability="userAvailability(user)" />
</div> </div>

View File

@ -0,0 +1,37 @@
# frozen_string_literal: true
module Types
module CommitSignatureInterface
include Types::BaseInterface
graphql_name 'CommitSignature'
description 'Represents signing information for a commit'
field :verification_status, CommitSignatures::VerificationStatusEnum,
null: true,
description: 'Indicates verification status of the associated key or certificate.'
field :commit_sha, GraphQL::Types::String,
null: true,
description: 'SHA of the associated commit.'
field :project, Types::ProjectType,
null: true,
description: 'Project of the associated commit.'
orphan_types Types::CommitSignatures::GpgSignatureType,
Types::CommitSignatures::X509SignatureType
def self.resolve_type(object, context)
case object
when ::CommitSignatures::GpgSignature
Types::CommitSignatures::GpgSignatureType
when ::CommitSignatures::X509CommitSignature
Types::CommitSignatures::X509SignatureType
else
raise 'Unsupported commit signature type'
end
end
end
end

View File

@ -0,0 +1,29 @@
# frozen_string_literal: true
module Types
module CommitSignatures
class GpgSignatureType < Types::BaseObject
graphql_name 'GpgSignature'
description 'GPG signature for a signed commit'
implements Types::CommitSignatureInterface
authorize :download_code
field :user, Types::UserType, null: true,
description: 'User associated with the key.'
field :gpg_key_user_name, GraphQL::Types::String,
null: true,
description: 'User name associated with the GPG key.'
field :gpg_key_user_email, GraphQL::Types::String,
null: true,
description: 'User email associated with the GPG key.'
field :gpg_key_primary_keyid, GraphQL::Types::String,
null: true,
description: 'ID of the GPG key.'
end
end
end

View File

@ -0,0 +1,18 @@
# frozen_string_literal: true
# rubocop:disable Graphql/AuthorizeTypes
module Types
module CommitSignatures
class VerificationStatusEnum < BaseEnum
graphql_name 'VerificationStatus'
description 'Verification status of a GPG or X.509 signature for a commit.'
::CommitSignatures::GpgSignature.verification_statuses.each do |status, _|
value status.upcase, value: status, description: "#{status} verification status."
end
end
end
end
# rubocop:enable Graphql/AuthorizeTypes

View File

@ -0,0 +1,22 @@
# frozen_string_literal: true
module Types
module CommitSignatures
class X509SignatureType < Types::BaseObject
graphql_name 'X509Signature'
description 'X.509 signature for a signed commit'
implements Types::CommitSignatureInterface
authorize :download_code
field :user, Types::UserType, null: true,
calls_gitaly: true,
description: 'User associated with the key.'
field :x509_certificate, Types::X509CertificateType,
null: true,
description: 'Certificate used for the signature.'
end
end
end

View File

@ -40,6 +40,11 @@ module Types
field :web_path, type: GraphQL::Types::String, null: false, field :web_path, type: GraphQL::Types::String, null: false,
description: 'Web path of the commit.' description: 'Web path of the commit.'
field :signature, type: Types::CommitSignatureInterface,
null: true,
calls_gitaly: true,
description: 'Signature of the commit.'
field :signature_html, type: GraphQL::Types::String, null: true, calls_gitaly: true, field :signature_html, type: GraphQL::Types::String, null: true, calls_gitaly: true,
description: 'Rendered HTML of the commit signature.' description: 'Rendered HTML of the commit signature.'

View File

@ -0,0 +1,39 @@
# frozen_string_literal: true
# rubocop:disable Graphql/AuthorizeTypes
module Types
class X509CertificateType < Types::BaseObject
graphql_name 'X509Certificate'
description 'Represents an X.509 certificate.'
field :certificate_status, GraphQL::Types::String,
null: false,
description: 'Indicates if the certificate is good or revoked.'
field :created_at, Types::TimeType, null: false,
description: 'Timestamp of when the certificate was saved.'
field :email, GraphQL::Types::String, null: false,
description: 'Email associated with the cerificate.'
field :id, GraphQL::Types::ID, null: false, description: 'ID of the certificate.'
field :serial_number, GraphQL::Types::String, null: false,
description: 'Serial number of the certificate.'
field :subject, GraphQL::Types::String, null: false, description: 'Subject of the certificate.'
field :subject_key_identifier, GraphQL::Types::String,
null: false,
description: 'Subject key identifier of the certificate.'
field :updated_at, Types::TimeType, null: false,
description: 'Timestamp of when the certificate was last updated.'
field :x509_issuer, Types::X509IssuerType, null: false,
description: 'Issuer of the certificate.'
end
end
# rubocop:enable Graphql/AuthorizeTypes

View File

@ -0,0 +1,29 @@
# frozen_string_literal: true
# rubocop:disable Graphql/AuthorizeTypes
module Types
class X509IssuerType < Types::BaseObject
graphql_name 'X509Issuer'
description 'Issuer of an X.509 certificate.'
field :created_at, Types::TimeType, null: true,
description: 'Timestamp of when the issuer was created.'
field :crl_url, GraphQL::Types::String, null: true,
description: 'Certificate revokation list of the issuer.'
field :id, GraphQL::Types::ID, null: true, description: 'ID of the issuer.'
field :subject, GraphQL::Types::String, null: true, description: 'Subject of the issuer.'
field :subject_key_identifier, GraphQL::Types::String,
null: true,
description: 'Subject key identifier of the issuer.'
field :updated_at, Types::TimeType, null: true,
description: 'Timestamp of when the issuer was last updated.'
end
end
# rubocop:enable Graphql/AuthorizeTypes

View File

@ -395,7 +395,8 @@ module ProjectsHelper
membersPagePath: project_project_members_path(project), membersPagePath: project_project_members_path(project),
environmentsHelpPath: help_page_path('ci/environments/index'), environmentsHelpPath: help_page_path('ci/environments/index'),
featureFlagsHelpPath: help_page_path('operations/feature_flags'), featureFlagsHelpPath: help_page_path('operations/feature_flags'),
releasesHelpPath: help_page_path('user/project/releases/index') releasesHelpPath: help_page_path('user/project/releases/index'),
infrastructureHelpPath: help_page_path('user/infrastructure/index')
} }
end end
@ -664,7 +665,8 @@ module ProjectsHelper
containerRegistryAccessLevel: feature.container_registry_access_level, containerRegistryAccessLevel: feature.container_registry_access_level,
environmentsAccessLevel: feature.environments_access_level, environmentsAccessLevel: feature.environments_access_level,
featureFlagsAccessLevel: feature.feature_flags_access_level, featureFlagsAccessLevel: feature.feature_flags_access_level,
releasesAccessLevel: feature.releases_access_level releasesAccessLevel: feature.releases_access_level,
infrastructureAccessLevel: feature.infrastructure_access_level
} }
end end

View File

@ -49,5 +49,9 @@ module CommitSignatures
Gitlab::Gpg::Commit.new(commit) Gitlab::Gpg::Commit.new(commit)
end end
def user
gpg_key&.user
end
end end
end end

View File

@ -0,0 +1,7 @@
# frozen_string_literal: true
module CommitSignatures
class GpgSignaturePolicy < BasePolicy
delegate { @subject.project }
end
end

View File

@ -0,0 +1,7 @@
# frozen_string_literal: true
module CommitSignatures
class X509CommitSignaturePolicy < BasePolicy
delegate { @subject.project }
end
end

View File

@ -12,8 +12,8 @@ module ObjectStorage
UrlResult = Struct.new(:url, :used_cdn) UrlResult = Struct.new(:url, :used_cdn)
def cdn_enabled_url(project, ip_address) def cdn_enabled_url(ip_address)
if Feature.enabled?(:ci_job_artifacts_cdn, project) && use_cdn?(ip_address) if use_cdn?(ip_address)
UrlResult.new(cdn_signed_url, true) UrlResult.new(cdn_signed_url, true)
else else
UrlResult.new(url, false) UrlResult.new(url, false)

View File

@ -1,8 +0,0 @@
---
name: ci_job_artifacts_cdn
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/98010
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/373860
milestone: '15.5'
type: development
group: group::pipeline execution
default_enabled: false

View File

@ -48,9 +48,9 @@ settings and automation to ensure that whatever a compliance team has configured
stays configured and working correctly. These features can help you automate stays configured and working correctly. These features can help you automate
compliance: compliance:
- [**Compliance frameworks**](../user/group/manage.md#compliance-frameworks) (for groups): Create a custom - [**Compliance frameworks**](../user/group/compliance_frameworks.md) (for groups): Create a custom
compliance framework at the group level to describe the type of compliance requirements any child project needs to follow. compliance framework at the group level to describe the type of compliance requirements any child project needs to follow.
- [**Compliance pipelines**](../user/group/manage.md#configure-a-compliance-pipeline) (for groups): Define a - [**Compliance pipelines**](../user/group/compliance_frameworks.md#configure-a-compliance-pipeline) (for groups): Define a
pipeline configuration to run for any projects with a given compliance framework. pipeline configuration to run for any projects with a given compliance framework.
## Audit management ## Audit management

View File

@ -10939,6 +10939,7 @@ Represents a code quality degradation on the pipeline.
| <a id="commitmessage"></a>`message` | [`String`](#string) | Raw commit message. | | <a id="commitmessage"></a>`message` | [`String`](#string) | Raw commit message. |
| <a id="commitsha"></a>`sha` | [`String!`](#string) | SHA1 ID of the commit. | | <a id="commitsha"></a>`sha` | [`String!`](#string) | SHA1 ID of the commit. |
| <a id="commitshortid"></a>`shortId` | [`String!`](#string) | Short SHA1 ID of the commit. | | <a id="commitshortid"></a>`shortId` | [`String!`](#string) | Short SHA1 ID of the commit. |
| <a id="commitsignature"></a>`signature` | [`CommitSignature`](#commitsignature) | Signature of the commit. |
| <a id="commitsignaturehtml"></a>`signatureHtml` | [`String`](#string) | Rendered HTML of the commit signature. | | <a id="commitsignaturehtml"></a>`signatureHtml` | [`String`](#string) | Rendered HTML of the commit signature. |
| <a id="committitle"></a>`title` | [`String`](#string) | Title of the commit message. | | <a id="committitle"></a>`title` | [`String`](#string) | Title of the commit message. |
| <a id="committitlehtml"></a>`titleHtml` | [`String`](#string) | The GitLab Flavored Markdown rendering of `title`. | | <a id="committitlehtml"></a>`titleHtml` | [`String`](#string) | The GitLab Flavored Markdown rendering of `title`. |
@ -12799,6 +12800,22 @@ four standard [pagination arguments](#connection-pagination-arguments):
| <a id="geonodeuploadregistriesreplicationstate"></a>`replicationState` | [`ReplicationStateEnum`](#replicationstateenum) | Filters registries by their replication state. | | <a id="geonodeuploadregistriesreplicationstate"></a>`replicationState` | [`ReplicationStateEnum`](#replicationstateenum) | Filters registries by their replication state. |
| <a id="geonodeuploadregistriesverificationstate"></a>`verificationState` | [`VerificationStateEnum`](#verificationstateenum) | Filters registries by their verification state. | | <a id="geonodeuploadregistriesverificationstate"></a>`verificationState` | [`VerificationStateEnum`](#verificationstateenum) | Filters registries by their verification state. |
### `GpgSignature`
GPG signature for a signed commit.
#### Fields
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="gpgsignaturecommitsha"></a>`commitSha` | [`String`](#string) | SHA of the associated commit. |
| <a id="gpgsignaturegpgkeyprimarykeyid"></a>`gpgKeyPrimaryKeyid` | [`String`](#string) | ID of the GPG key. |
| <a id="gpgsignaturegpgkeyuseremail"></a>`gpgKeyUserEmail` | [`String`](#string) | User email associated with the GPG key. |
| <a id="gpgsignaturegpgkeyusername"></a>`gpgKeyUserName` | [`String`](#string) | User name associated with the GPG key. |
| <a id="gpgsignatureproject"></a>`project` | [`Project`](#project) | Project of the associated commit. |
| <a id="gpgsignatureuser"></a>`user` | [`UserCore`](#usercore) | User associated with the key. |
| <a id="gpgsignatureverificationstatus"></a>`verificationStatus` | [`VerificationStatus`](#verificationstatus) | Indicates verification status of the associated key or certificate. |
### `GrafanaIntegration` ### `GrafanaIntegration`
#### Fields #### Fields
@ -20078,6 +20095,53 @@ Represents a weight widget.
| <a id="workitemwidgetweighttype"></a>`type` | [`WorkItemWidgetType`](#workitemwidgettype) | Widget type. | | <a id="workitemwidgetweighttype"></a>`type` | [`WorkItemWidgetType`](#workitemwidgettype) | Widget type. |
| <a id="workitemwidgetweightweight"></a>`weight` | [`Int`](#int) | Weight of the work item. | | <a id="workitemwidgetweightweight"></a>`weight` | [`Int`](#int) | Weight of the work item. |
### `X509Certificate`
Represents an X.509 certificate.
#### Fields
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="x509certificatecertificatestatus"></a>`certificateStatus` | [`String!`](#string) | Indicates if the certificate is good or revoked. |
| <a id="x509certificatecreatedat"></a>`createdAt` | [`Time!`](#time) | Timestamp of when the certificate was saved. |
| <a id="x509certificateemail"></a>`email` | [`String!`](#string) | Email associated with the cerificate. |
| <a id="x509certificateid"></a>`id` | [`ID!`](#id) | ID of the certificate. |
| <a id="x509certificateserialnumber"></a>`serialNumber` | [`String!`](#string) | Serial number of the certificate. |
| <a id="x509certificatesubject"></a>`subject` | [`String!`](#string) | Subject of the certificate. |
| <a id="x509certificatesubjectkeyidentifier"></a>`subjectKeyIdentifier` | [`String!`](#string) | Subject key identifier of the certificate. |
| <a id="x509certificateupdatedat"></a>`updatedAt` | [`Time!`](#time) | Timestamp of when the certificate was last updated. |
| <a id="x509certificatex509issuer"></a>`x509Issuer` | [`X509Issuer!`](#x509issuer) | Issuer of the certificate. |
### `X509Issuer`
Issuer of an X.509 certificate.
#### Fields
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="x509issuercreatedat"></a>`createdAt` | [`Time`](#time) | Timestamp of when the issuer was created. |
| <a id="x509issuercrlurl"></a>`crlUrl` | [`String`](#string) | Certificate revokation list of the issuer. |
| <a id="x509issuerid"></a>`id` | [`ID`](#id) | ID of the issuer. |
| <a id="x509issuersubject"></a>`subject` | [`String`](#string) | Subject of the issuer. |
| <a id="x509issuersubjectkeyidentifier"></a>`subjectKeyIdentifier` | [`String`](#string) | Subject key identifier of the issuer. |
| <a id="x509issuerupdatedat"></a>`updatedAt` | [`Time`](#time) | Timestamp of when the issuer was last updated. |
### `X509Signature`
X.509 signature for a signed commit.
#### Fields
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="x509signaturecommitsha"></a>`commitSha` | [`String`](#string) | SHA of the associated commit. |
| <a id="x509signatureproject"></a>`project` | [`Project`](#project) | Project of the associated commit. |
| <a id="x509signatureuser"></a>`user` | [`UserCore`](#usercore) | User associated with the key. |
| <a id="x509signatureverificationstatus"></a>`verificationStatus` | [`VerificationStatus`](#verificationstatus) | Indicates verification status of the associated key or certificate. |
| <a id="x509signaturex509certificate"></a>`x509Certificate` | [`X509Certificate`](#x509certificate) | Certificate used for the signature. |
## Enumeration types ## Enumeration types
Also called _Enums_, enumeration types are a special kind of scalar that Also called _Enums_, enumeration types are a special kind of scalar that
@ -21935,6 +21999,20 @@ Possible states of a user.
| <a id="verificationstateenumstarted"></a>`STARTED` | Verification process is in progress. | | <a id="verificationstateenumstarted"></a>`STARTED` | Verification process is in progress. |
| <a id="verificationstateenumsucceeded"></a>`SUCCEEDED` | Verification process finished successfully. | | <a id="verificationstateenumsucceeded"></a>`SUCCEEDED` | Verification process finished successfully. |
### `VerificationStatus`
Verification status of a GPG or X.509 signature for a commit.
| Value | Description |
| ----- | ----------- |
| <a id="verificationstatusmultiple_signatures"></a>`MULTIPLE_SIGNATURES` | multiple_signatures verification status. |
| <a id="verificationstatusother_user"></a>`OTHER_USER` | other_user verification status. |
| <a id="verificationstatussame_user_different_email"></a>`SAME_USER_DIFFERENT_EMAIL` | same_user_different_email verification status. |
| <a id="verificationstatusunknown_key"></a>`UNKNOWN_KEY` | unknown_key verification status. |
| <a id="verificationstatusunverified"></a>`UNVERIFIED` | unverified verification status. |
| <a id="verificationstatusunverified_key"></a>`UNVERIFIED_KEY` | unverified_key verification status. |
| <a id="verificationstatusverified"></a>`VERIFIED` | verified verification status. |
### `VisibilityLevelsEnum` ### `VisibilityLevelsEnum`
| Value | Description | | Value | Description |
@ -22903,6 +22981,23 @@ Implementations:
| <a id="civariablevalue"></a>`value` | [`String`](#string) | Value of the variable. | | <a id="civariablevalue"></a>`value` | [`String`](#string) | Value of the variable. |
| <a id="civariablevariabletype"></a>`variableType` | [`CiVariableType`](#civariabletype) | Type of the variable. | | <a id="civariablevariabletype"></a>`variableType` | [`CiVariableType`](#civariabletype) | Type of the variable. |
#### `CommitSignature`
Represents signing information for a commit.
Implementations:
- [`GpgSignature`](#gpgsignature)
- [`X509Signature`](#x509signature)
##### Fields
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="commitsignaturecommitsha"></a>`commitSha` | [`String`](#string) | SHA of the associated commit. |
| <a id="commitsignatureproject"></a>`project` | [`Project`](#project) | Project of the associated commit. |
| <a id="commitsignatureverificationstatus"></a>`verificationStatus` | [`VerificationStatus`](#verificationstatus) | Indicates verification status of the associated key or certificate. |
#### `CurrentUserTodos` #### `CurrentUserTodos`
Implementations: Implementations:

View File

@ -379,7 +379,7 @@ start. Jobs in the current stage are not stopped and continue to run.
- If a job does not specify a [`stage`](#stage), the job is assigned the `test` stage. - If a job does not specify a [`stage`](#stage), the job is assigned the `test` stage.
- If a stage is defined but no jobs use it, the stage is not visible in the pipeline, - If a stage is defined but no jobs use it, the stage is not visible in the pipeline,
which can help [compliance pipeline configurations](../../user/group/manage.md#configure-a-compliance-pipeline): which can help [compliance pipeline configurations](../../user/group/compliance_frameworks.md#configure-a-compliance-pipeline):
- Stages can be defined in the compliance configuration but remain hidden if not used. - Stages can be defined in the compliance configuration but remain hidden if not used.
- The defined stages become visible when developers use them in job definitions. - The defined stages become visible when developers use them in job definitions.

View File

@ -244,7 +244,7 @@ To enable or disable the banner:
> [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/352316) from GitLab Premium to GitLab Ultimate in 15.0. > [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/352316) from GitLab Premium to GitLab Ultimate in 15.0.
NOTE: NOTE:
An alternative [compliance solution](../../group/manage.md#configure-a-compliance-pipeline) An alternative [compliance solution](../../group/compliance_frameworks.md#configure-a-compliance-pipeline)
is available. We recommend this alternative solution because it provides greater flexibility, is available. We recommend this alternative solution because it provides greater flexibility,
allowing required pipelines to be assigned to specific compliance framework labels. allowing required pipelines to be assigned to specific compliance framework labels.

View File

@ -36,7 +36,7 @@ The following steps will help you get the most from GitLab application security
remediating existing vulnerabilities and preventing the introduction of new ones. remediating existing vulnerabilities and preventing the introduction of new ones.
1. Enable other scan types such as [SAST](sast/index.md), [DAST](dast/index.md), 1. Enable other scan types such as [SAST](sast/index.md), [DAST](dast/index.md),
[Fuzz testing](coverage_fuzzing/index.md), or [Container Scanning](container_scanning/index.md). [Fuzz testing](coverage_fuzzing/index.md), or [Container Scanning](container_scanning/index.md).
1. Use [Compliance Pipelines](../group/manage.md#configure-a-compliance-pipeline) 1. Use [Compliance Pipelines](../group/compliance_frameworks.md#configure-a-compliance-pipeline)
or [Scan Execution Policies](policies/scan-execution-policies.md) to enforce required scan types or [Scan Execution Policies](policies/scan-execution-policies.md) to enforce required scan types
and ensure separation of duties between security and engineering. and ensure separation of duties between security and engineering.
1. Consider enabling [Review Apps](../../development/testing_guide/review_apps.md) to allow for DAST 1. Consider enabling [Review Apps](../../development/testing_guide/review_apps.md) to allow for DAST

View File

@ -487,7 +487,7 @@ Security and compliance teams must ensure that security scans:
GitLab provides two methods of accomplishing this, each with advantages and disadvantages. GitLab provides two methods of accomplishing this, each with advantages and disadvantages.
- [Compliance framework pipelines](../group/manage.md#configure-a-compliance-pipeline) - [Compliance framework pipelines](../group/compliance_frameworks.md#configure-a-compliance-pipeline)
are recommended when: are recommended when:
- Scan execution enforcement is required for any scanner that uses a GitLab template, such as SAST IaC, DAST, Dependency Scanning, - Scan execution enforcement is required for any scanner that uses a GitLab template, such as SAST IaC, DAST, Dependency Scanning,

View File

@ -15,7 +15,7 @@ with a long, random job name. In the unlikely event of a job name collision, the
any pre-existing job in the pipeline. If a policy is created at the group-level, it will apply to every child any pre-existing job in the pipeline. If a policy is created at the group-level, it will apply to every child
project or subgroup. A group-level policy cannot be edited from a child project or subgroup. project or subgroup. A group-level policy cannot be edited from a child project or subgroup.
This feature has some overlap with [compliance framework pipelines](../../group/manage.md#configure-a-compliance-pipeline), This feature has some overlap with [compliance framework pipelines](../../group/compliance_frameworks.md#configure-a-compliance-pipeline),
as we have not [unified the user experience for these two features](https://gitlab.com/groups/gitlab-org/-/epics/7312). as we have not [unified the user experience for these two features](https://gitlab.com/groups/gitlab-org/-/epics/7312).
For details on the similarities and differences between these features, see For details on the similarities and differences between these features, see
[Enforce scan execution](../index.md#enforce-scan-execution). [Enforce scan execution](../index.md#enforce-scan-execution).

View File

@ -0,0 +1,262 @@
---
stage: Govern
group: Compliance
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
---
# Compliance frameworks **(PREMIUM)**
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/276221) in GitLab 13.9.
> - [Feature flag removed](https://gitlab.com/gitlab-org/gitlab/-/issues/287779) in GitLab 13.12.
You can create a compliance framework that is a label to identify that your project has certain compliance
requirements or needs additional oversight. The label can optionally enforce
[compliance pipeline configuration](#configure-a-compliance-pipeline) to the projects on which it is
[applied](../project/settings/index.md#add-a-compliance-framework-to-a-project).
Group owners can create, edit, and delete compliance frameworks:
1. On the top bar, select **Main menu > Groups > View all groups** and find your group.
1. On the left sidebar, select **Settings** > **General**.
1. Expand the **Compliance frameworks** section.
1. Create, edit, or delete compliance frameworks.
## Set a default compliance framework
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/375036) in GitLab 15.6.
Group owners can set a default compliance framework. The default framework is applied to all the new projects
that are created within that group. It does not affect the framework applied to the existing projects. The default
framework cannot be deleted.
### Example GraphQL mutations for setting a default compliance framework
Creating a new compliance framework and setting it as the default framework for the group.
```graphql
mutation {
createComplianceFramework(
input: {params: {name: "SOX", description: "Sarbanes-Oxley Act", color: "#87CEEB", default: true}, namespacePath: "gitlab-org"}
) {
framework {
id
name
default
description
color
pipelineConfigurationFullPath
}
errors
}
}
```
Setting an existing compliance framework as the default framework the group.
```graphql
mutation {
updateComplianceFramework(
input: {id: "gid://gitlab/ComplianceManagement::Framework/<id>", params: {default: true}}
) {
complianceFramework {
id
name
default
description
color
pipelineConfigurationFullPath
}
}
}
```
## Configure a compliance pipeline **(ULTIMATE)**
> - [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/3156) in GitLab 13.9, disabled behind `ff_evaluate_group_level_compliance_pipeline` [feature flag](../../administration/feature_flags.md).
> - [Enabled by default](https://gitlab.com/gitlab-org/gitlab/-/issues/300324) in GitLab 13.11.
> - [Feature flag removed](https://gitlab.com/gitlab-org/gitlab/-/issues/331231) in GitLab 14.2.
Group owners can configure a compliance pipeline in a project separate to other projects. By default, the compliance
pipeline configuration (`.gitlab-ci.yml` file) is run instead of the pipeline configuration of labeled projects.
However, the compliance pipeline configuration can reference the `.gitlab-ci.yml` file of the labeled projects so that:
- The compliance pipeline can also run jobs of labeled project pipelines. This allows for centralized control of
pipeline configuration.
- Jobs and variables defined in the compliance pipeline can't be changed by variables in the labeled project's
`.gitlab-ci.yml` file.
See [example configuration](#example-configuration) for help configuring a compliance pipeline that runs jobs from
labeled project pipeline configuration.
To configure a compliance pipeline:
1. On the top bar, select **Main menu > Groups > View all groups** and find your group.
1. On the left sidebar, select **Settings** > **General**.
1. Expand the **Compliance frameworks** section.
1. In **Compliance pipeline configuration (optional)**, add the path to the compliance framework configuration. Use the
`path/file.y[a]ml@group-name/project-name` format. For example:
- `.compliance-ci.yml@gitlab-org/gitlab`.
- `.compliance-ci.yaml@gitlab-org/gitlab`.
This configuration is inherited by projects where the compliance framework label is
[applied](../project/settings/index.md#add-a-compliance-framework-to-a-project). In projects with the applied compliance
framework label, the compliance pipeline configuration is run instead of the labeled project's own pipeline configuration.
The user running the pipeline in the labeled project must at least have the Reporter role on the compliance project.
When used to enforce scan execution, this feature has some overlap with
[scan execution policies](../application_security/policies/scan-execution-policies.md). We have not
[unified the user experience for these two features](https://gitlab.com/groups/gitlab-org/-/epics/7312). For details on
the similarities and differences between these features, see [Enforce scan execution](../application_security/index.md#enforce-scan-execution).
### Example configuration
The following example `.compliance-gitlab-ci.yml` includes the `include` keyword to ensure labeled project pipeline
configuration is also executed.
```yaml
# Allows compliance team to control the ordering and interweaving of stages/jobs.
# Stages without jobs defined will remain hidden.
stages:
- pre-compliance
- build
- test
- pre-deploy-compliance
- deploy
- post-compliance
variables: # Can be overridden by setting a job-specific variable in project's local .gitlab-ci.yml
FOO: sast
sast: # None of these attributes can be overridden by a project's local .gitlab-ci.yml
variables:
FOO: sast
image: ruby:2.6
stage: pre-compliance
rules:
- if: $CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS && $CI_PIPELINE_SOURCE == "push"
when: never
- when: always # or when: on_success
allow_failure: false
before_script:
- "# No before scripts."
script:
- echo "running $FOO"
after_script:
- "# No after scripts."
sanity check:
image: ruby:2.6
stage: pre-deploy-compliance
rules:
- if: $CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS && $CI_PIPELINE_SOURCE == "push"
when: never
- when: always # or when: on_success
allow_failure: false
before_script:
- "# No before scripts."
script:
- echo "running $FOO"
after_script:
- "# No after scripts."
audit trail:
image: ruby:2.7
stage: post-compliance
rules:
- if: $CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS && $CI_PIPELINE_SOURCE == "push"
when: never
- when: always # or when: on_success
allow_failure: false
before_script:
- "# No before scripts."
script:
- echo "running $FOO"
after_script:
- "# No after scripts."
include: # Execute individual project's configuration (if project contains .gitlab-ci.yml)
project: '$CI_PROJECT_PATH'
file: '$CI_CONFIG_PATH'
ref: '$CI_COMMIT_REF_NAME' # Must be defined or MR pipelines always use the use default branch
```
#### CF pipelines in Merge Requests originating in project forks
When an MR originates in a fork, the branch to be merged usually only exists in the fork.
When creating such an MR against a project with CF pipelines, the above snippet will fail with a
`Project <project-name> reference <branch-name> does not exist!` error message.
This is because in the context of the target project, `$CI_COMMIT_REF_NAME` evaluates to a non-existing branch name.
To get the correct context, use `$CI_MERGE_REQUEST_SOURCE_PROJECT_PATH` instead of `$CI_PROJECT_PATH`.
This variable is only availabe in
[merge request pipelines](../../ci/pipelines/merge_request_pipelines.md).
For example, for a configuration that supports both merge request pipelines originating in project forks and branch pipelines,
you need to [combine both `include` directives with `rules:if`](../../ci/yaml/includes.md#use-rules-with-include):
```yaml
include: # Execute individual project's configuration (if project contains .gitlab-ci.yml)
- project: '$CI_MERGE_REQUEST_SOURCE_PROJECT_PATH'
file: '$CI_CONFIG_PATH'
ref: '$CI_COMMIT_REF_NAME'
rules:
- if: $CI_PIPELINE_SOURCE == 'merge_request_event'
- project: '$CI_PROJECT_PATH'
file: '$CI_CONFIG_PATH'
ref: '$CI_COMMIT_REF_NAME'
rules:
- if: $CI_PIPELINE_SOURCE != 'merge_request_event'
```
## Ensure compliance jobs are always run
Compliance pipelines [use GitLab CI/CD](../../ci/index.md) to give you an incredible amount of flexibility
for defining any sort of compliance jobs you like. Depending on your goals, these jobs
can be configured to be:
- Modified by users.
- Non-modifiable.
Generally, if a value in a compliance job:
- Is set, it cannot be changed or overridden by project-level configurations.
- Is not set, a project-level configuration may set.
Either might be wanted or not depending on your use case.
There are a few best practices for ensuring that these jobs are always run exactly
as you define them and that downstream, project-level pipeline configurations
cannot change them:
- Add [a `rules:when:always` block](../../ci/yaml/index.md#when) to each of your compliance jobs. This ensures they are
non-modifiable and are always run.
- Explicitly set any [variables](../../ci/yaml/index.md#variables) the job references. This:
- Ensures that project-level pipeline configurations do not set them and alter their
behavior.
- Includes any jobs that drive the logic of your job.
- Explicitly set the [container image](../../ci/yaml/index.md#image) to run the job in. This ensures that your script
steps execute in the correct environment.
- Explicitly set any relevant GitLab pre-defined [job keywords](../../ci/yaml/index.md#job-keywords).
This ensures that your job uses the settings you intend and that they are not overridden by
project-level pipelines.
## Avoid parent and child pipelines in GitLab 14.7 and earlier
NOTE:
This advice does not apply to GitLab 14.8 and later because [a fix](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/78878) added
compatibility for combining compliance pipelines, and parent and child pipelines.
Compliance pipelines start on the run of _every_ pipeline in a labeled project. This means that if a pipeline in the labeled project
triggers a child pipeline, the compliance pipeline runs first. This can trigger the parent pipeline, instead of the child pipeline.
Therefore, in projects with compliance frameworks, we recommend replacing
[parent-child pipelines](../../ci/pipelines/downstream_pipelines.md#parent-child-pipelines) with the following:
- Direct [`include`](../../ci/yaml/index.md#include) statements that provide the parent pipeline with child pipeline configuration.
- Child pipelines placed in another project that are run using the [trigger API](../../ci/triggers/index.md) rather than the parent-child
pipeline feature.
This alternative ensures the compliance pipeline does not re-start the parent pipeline.

View File

@ -397,263 +397,6 @@ To enable delayed deletion of projects in a group:
NOTE: NOTE:
In GitLab 13.11 and above the group setting for delayed project deletion is inherited by subgroups. As discussed in [Cascading settings](../../development/cascading_settings.md) inheritance can be overridden, unless enforced by an ancestor. In GitLab 13.11 and above the group setting for delayed project deletion is inherited by subgroups. As discussed in [Cascading settings](../../development/cascading_settings.md) inheritance can be overridden, unless enforced by an ancestor.
## Compliance frameworks **(PREMIUM)**
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/276221) in GitLab 13.9.
> - [Feature flag removed](https://gitlab.com/gitlab-org/gitlab/-/issues/287779) in GitLab 13.12.
You can create a compliance framework that is a label to identify that your project has certain compliance
requirements or needs additional oversight. The label can optionally enforce
[compliance pipeline configuration](#configure-a-compliance-pipeline) to the projects on which it is
[applied](../project/settings/index.md#add-a-compliance-framework-to-a-project).
Group owners can create, edit, and delete compliance frameworks:
1. On the top bar, select **Main menu > Groups > View all groups** and find your group.
1. On the left sidebar, select **Settings** > **General**.
1. Expand the **Compliance frameworks** section.
1. Create, edit, or delete compliance frameworks.
### Set a default compliance framework
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/375036) in GitLab 15.6.
Group owners can set a default compliance framework. The default framework is applied to all the new projects
that are created within that group. It does not affect the framework applied to the existing projects. The default
framework cannot be deleted.
#### Example GraphQL mutations for setting a default compliance framework
Creating a new compliance framework and setting it as the default framework for the group.
```graphql
mutation {
createComplianceFramework(
input: {params: {name: "SOX", description: "Sarbanes-Oxley Act", color: "#87CEEB", default: true}, namespacePath: "gitlab-org"}
) {
framework {
id
name
default
description
color
pipelineConfigurationFullPath
}
errors
}
}
```
Setting an existing compliance framework as the default framework the group.
```graphql
mutation {
updateComplianceFramework(
input: {id: "gid://gitlab/ComplianceManagement::Framework/<id>", params: {default: true}}
) {
complianceFramework {
id
name
default
description
color
pipelineConfigurationFullPath
}
}
}
```
### Configure a compliance pipeline **(ULTIMATE)**
> - [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/3156) in GitLab 13.9, disabled behind `ff_evaluate_group_level_compliance_pipeline` [feature flag](../../administration/feature_flags.md).
> - [Enabled by default](https://gitlab.com/gitlab-org/gitlab/-/issues/300324) in GitLab 13.11.
> - [Feature flag removed](https://gitlab.com/gitlab-org/gitlab/-/issues/331231) in GitLab 14.2.
Group owners can configure a compliance pipeline in a project separate to other projects. By default, the compliance
pipeline configuration (`.gitlab-ci.yml` file) is run instead of the pipeline configuration of labeled projects.
However, the compliance pipeline configuration can reference the `.gitlab-ci.yml` file of the labeled projects so that:
- The compliance pipeline can also run jobs of labeled project pipelines. This allows for centralized control of
pipeline configuration.
- Jobs and variables defined in the compliance pipeline can't be changed by variables in the labeled project's
`.gitlab-ci.yml` file.
See [example configuration](#example-configuration) for help configuring a compliance pipeline that runs jobs from
labeled project pipeline configuration.
To configure a compliance pipeline:
1. On the top bar, select **Main menu > Groups > View all groups** and find your group.
1. On the left sidebar, select **Settings** > **General**.
1. Expand the **Compliance frameworks** section.
1. In **Compliance pipeline configuration (optional)**, add the path to the compliance framework configuration. Use the
`path/file.y[a]ml@group-name/project-name` format. For example:
- `.compliance-ci.yml@gitlab-org/gitlab`.
- `.compliance-ci.yaml@gitlab-org/gitlab`.
This configuration is inherited by projects where the compliance framework label is
[applied](../project/settings/index.md#add-a-compliance-framework-to-a-project). In projects with the applied compliance
framework label, the compliance pipeline configuration is run instead of the labeled project's own pipeline configuration.
The user running the pipeline in the labeled project must at least have the Reporter role on the compliance project.
When used to enforce scan execution, this feature has some overlap with
[scan execution policies](../application_security/policies/scan-execution-policies.md). We have not
[unified the user experience for these two features](https://gitlab.com/groups/gitlab-org/-/epics/7312). For details on
the similarities and differences between these features, see [Enforce scan execution](../application_security/index.md#enforce-scan-execution).
#### Example configuration
The following example `.compliance-gitlab-ci.yml` includes the `include` keyword to ensure labeled project pipeline
configuration is also executed.
```yaml
# Allows compliance team to control the ordering and interweaving of stages/jobs.
# Stages without jobs defined will remain hidden.
stages:
- pre-compliance
- build
- test
- pre-deploy-compliance
- deploy
- post-compliance
variables: # Can be overridden by setting a job-specific variable in project's local .gitlab-ci.yml
FOO: sast
sast: # None of these attributes can be overridden by a project's local .gitlab-ci.yml
variables:
FOO: sast
image: ruby:2.6
stage: pre-compliance
rules:
- if: $CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS && $CI_PIPELINE_SOURCE == "push"
when: never
- when: always # or when: on_success
allow_failure: false
before_script:
- "# No before scripts."
script:
- echo "running $FOO"
after_script:
- "# No after scripts."
sanity check:
image: ruby:2.6
stage: pre-deploy-compliance
rules:
- if: $CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS && $CI_PIPELINE_SOURCE == "push"
when: never
- when: always # or when: on_success
allow_failure: false
before_script:
- "# No before scripts."
script:
- echo "running $FOO"
after_script:
- "# No after scripts."
audit trail:
image: ruby:2.7
stage: post-compliance
rules:
- if: $CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS && $CI_PIPELINE_SOURCE == "push"
when: never
- when: always # or when: on_success
allow_failure: false
before_script:
- "# No before scripts."
script:
- echo "running $FOO"
after_script:
- "# No after scripts."
include: # Execute individual project's configuration (if project contains .gitlab-ci.yml)
project: '$CI_PROJECT_PATH'
file: '$CI_CONFIG_PATH'
ref: '$CI_COMMIT_REF_NAME' # Must be defined or MR pipelines always use the use default branch
```
##### CF pipelines in Merge Requests originating in project forks
When an MR originates in a fork, the branch to be merged usually only exists in the fork.
When creating such an MR against a project with CF pipelines, the above snippet will fail with a
`Project <project-name> reference <branch-name> does not exist!` error message.
This is because in the context of the target project, `$CI_COMMIT_REF_NAME` evaluates to a non-existing branch name.
To get the correct context, use `$CI_MERGE_REQUEST_SOURCE_PROJECT_PATH` instead of `$CI_PROJECT_PATH`.
This variable is only availabe in
[merge request pipelines](../../ci/pipelines/merge_request_pipelines.md).
For example, for a configuration that supports both merge request pipelines originating in project forks and branch pipelines,
you need to [combine both `include` directives with `rules:if`](../../ci/yaml/includes.md#use-rules-with-include):
```yaml
include: # Execute individual project's configuration (if project contains .gitlab-ci.yml)
- project: '$CI_MERGE_REQUEST_SOURCE_PROJECT_PATH'
file: '$CI_CONFIG_PATH'
ref: '$CI_COMMIT_REF_NAME'
rules:
- if: $CI_PIPELINE_SOURCE == 'merge_request_event'
- project: '$CI_PROJECT_PATH'
file: '$CI_CONFIG_PATH'
ref: '$CI_COMMIT_REF_NAME'
rules:
- if: $CI_PIPELINE_SOURCE != 'merge_request_event'
```
### Ensure compliance jobs are always run
Compliance pipelines [use GitLab CI/CD](../../ci/index.md) to give you an incredible amount of flexibility
for defining any sort of compliance jobs you like. Depending on your goals, these jobs
can be configured to be:
- Modified by users.
- Non-modifiable.
Generally, if a value in a compliance job:
- Is set, it cannot be changed or overridden by project-level configurations.
- Is not set, a project-level configuration may set.
Either might be wanted or not depending on your use case.
There are a few best practices for ensuring that these jobs are always run exactly
as you define them and that downstream, project-level pipeline configurations
cannot change them:
- Add [a `rules:when:always` block](../../ci/yaml/index.md#when) to each of your compliance jobs. This ensures they are
non-modifiable and are always run.
- Explicitly set any [variables](../../ci/yaml/index.md#variables) the job references. This:
- Ensures that project-level pipeline configurations do not set them and alter their
behavior.
- Includes any jobs that drive the logic of your job.
- Explicitly set the [container image](../../ci/yaml/index.md#image) to run the job in. This ensures that your script
steps execute in the correct environment.
- Explicitly set any relevant GitLab pre-defined [job keywords](../../ci/yaml/index.md#job-keywords).
This ensures that your job uses the settings you intend and that they are not overridden by
project-level pipelines.
### Avoid parent and child pipelines in GitLab 14.7 and earlier
NOTE:
This advice does not apply to GitLab 14.8 and later because [a fix](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/78878) added
compatibility for combining compliance pipelines, and parent and child pipelines.
Compliance pipelines start on the run of _every_ pipeline in a labeled project. This means that if a pipeline in the labeled project
triggers a child pipeline, the compliance pipeline runs first. This can trigger the parent pipeline, instead of the child pipeline.
Therefore, in projects with compliance frameworks, we recommend replacing
[parent-child pipelines](../../ci/pipelines/downstream_pipelines.md#parent-child-pipelines) with the following:
- Direct [`include`](../../ci/yaml/index.md#include) statements that provide the parent pipeline with child pipeline configuration.
- Child pipelines placed in another project that are run using the [trigger API](../../ci/triggers/index.md) rather than the parent-child
pipeline feature.
This alternative ensures the compliance pipeline does not re-start the parent pipeline.
## Disable email notifications ## Disable email notifications
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/23585) in GitLab 12.2. > [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/23585) in GitLab 12.2.

View File

@ -45,7 +45,7 @@ If you're an instance administrator, you can administer all project topics from
## Add a compliance framework to a project **(PREMIUM)** ## Add a compliance framework to a project **(PREMIUM)**
[Compliance frameworks](../../group/manage.md#compliance-frameworks) can be assigned to projects within group that has a [Compliance frameworks](../../group/compliance_frameworks.md) can be assigned to projects within group that has a
compliance framework using either: compliance framework using either:
- The GitLab UI: - The GitLab UI:

View File

@ -188,6 +188,7 @@ module API
mount ::API::ProjectRepositoryStorageMoves mount ::API::ProjectRepositoryStorageMoves
mount ::API::Release::Links mount ::API::Release::Links
mount ::API::ResourceAccessTokens mount ::API::ResourceAccessTokens
mount ::API::ProtectedTags
mount ::API::SnippetRepositoryStorageMoves mount ::API::SnippetRepositoryStorageMoves
mount ::API::ProtectedBranches mount ::API::ProtectedBranches
mount ::API::Statistics mount ::API::Statistics

View File

@ -38,7 +38,7 @@ module API
latest_build = user_project.latest_successful_build_for_ref!(params[:job], params[:ref_name]) latest_build = user_project.latest_successful_build_for_ref!(params[:job], params[:ref_name])
authorize_read_job_artifacts!(latest_build) authorize_read_job_artifacts!(latest_build)
present_artifacts_file!(latest_build.artifacts_file, project: latest_build.project) present_artifacts_file!(latest_build.artifacts_file)
end end
desc 'Download a specific file from artifacts archive from a ref' do desc 'Download a specific file from artifacts archive from a ref' do
@ -80,7 +80,7 @@ module API
build = find_build!(params[:job_id]) build = find_build!(params[:job_id])
authorize_read_job_artifacts!(build) authorize_read_job_artifacts!(build)
present_artifacts_file!(build.artifacts_file, project: build.project) present_artifacts_file!(build.artifacts_file)
end end
desc 'Download a specific file from artifacts archive' do desc 'Download a specific file from artifacts archive' do

View File

@ -349,7 +349,7 @@ module API
authenticate_job!(require_running: false) authenticate_job!(require_running: false)
end end
present_artifacts_file!(current_job.artifacts_file, project: current_job.project, supports_direct_download: params[:direct_download]) present_artifacts_file!(current_job.artifacts_file, supports_direct_download: params[:direct_download])
end end
end end
end end

View File

@ -3,7 +3,7 @@
module API module API
module Entities module Entities
class ProtectedTag < Grape::Entity class ProtectedTag < Grape::Entity
expose :name expose :name, documentation: { type: 'string', example: 'release-1-0' }
expose :create_access_levels, using: Entities::ProtectedRefAccess expose :create_access_levels, using: Entities::ProtectedRefAccess
end end
end end

View File

@ -592,19 +592,19 @@ module API
end end
end end
def present_artifacts_file!(file, project:, **args) def present_artifacts_file!(file, **args)
log_artifacts_filesize(file&.model) log_artifacts_filesize(file&.model)
present_carrierwave_file!(file, project: project, **args) present_carrierwave_file!(file, **args)
end end
def present_carrierwave_file!(file, project: nil, supports_direct_download: true) def present_carrierwave_file!(file, supports_direct_download: true)
return not_found! unless file&.exists? return not_found! unless file&.exists?
if file.file_storage? if file.file_storage?
present_disk_file!(file.path, file.filename) present_disk_file!(file.path, file.filename)
elsif supports_direct_download && file.class.direct_download_enabled? elsif supports_direct_download && file.class.direct_download_enabled?
redirect(cdn_fronted_url(file, project)) redirect(cdn_fronted_url(file))
else else
header(*Gitlab::Workhorse.send_url(file.url)) header(*Gitlab::Workhorse.send_url(file.url))
status :ok status :ok
@ -612,9 +612,9 @@ module API
end end
end end
def cdn_fronted_url(file, project) def cdn_fronted_url(file)
if file.respond_to?(:cdn_enabled_url) if file.respond_to?(:cdn_enabled_url)
result = file.cdn_enabled_url(project, ip_address) result = file.cdn_enabled_url(ip_address)
Gitlab::ApplicationContext.push(artifact_used_cdn: result.used_cdn) Gitlab::ApplicationContext.push(artifact_used_cdn: result.used_cdn)
result.url result.url
else else

View File

@ -18,7 +18,13 @@ module API
resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
desc "Get a project's protected tags" do desc "Get a project's protected tags" do
detail 'This feature was introduced in GitLab 11.3.' detail 'This feature was introduced in GitLab 11.3.'
success Entities::ProtectedTag is_array true
success code: 200, model: Entities::ProtectedTag
failure [
{ code: 403, message: 'Unauthenticated' },
{ code: 404, message: 'Not found' }
]
tags %w[protected_tags]
end end
params do params do
use :pagination use :pagination
@ -33,10 +39,15 @@ module API
desc 'Get a single protected tag' do desc 'Get a single protected tag' do
detail 'This feature was introduced in GitLab 11.3.' detail 'This feature was introduced in GitLab 11.3.'
success Entities::ProtectedTag success code: 200, model: Entities::ProtectedTag
failure [
{ code: 403, message: 'Unauthenticated' },
{ code: 404, message: 'Not found' }
]
tags %w[protected_tags]
end end
params do params do
requires :name, type: String, desc: 'The name of the tag or wildcard' requires :name, type: String, desc: 'The name of the tag or wildcard', documentation: { example: 'release*' }
end end
# rubocop: disable CodeReuse/ActiveRecord # rubocop: disable CodeReuse/ActiveRecord
get ':id/protected_tags/:name', requirements: TAG_ENDPOINT_REQUIREMENTS do get ':id/protected_tags/:name', requirements: TAG_ENDPOINT_REQUIREMENTS do
@ -48,13 +59,21 @@ module API
desc 'Protect a single tag or wildcard' do desc 'Protect a single tag or wildcard' do
detail 'This feature was introduced in GitLab 11.3.' detail 'This feature was introduced in GitLab 11.3.'
success Entities::ProtectedTag success code: 201, model: Entities::ProtectedTag
failure [
{ code: 403, message: 'Unauthenticated' },
{ code: 404, message: 'Not found' },
{ code: 422, message: 'Unprocessable entity' }
]
tags %w[protected_tags]
end end
params do params do
requires :name, type: String, desc: 'The name of the protected tag' requires :name, type: String, desc: 'The name of the protected tag', documentation: { example: 'release-1-0' }
optional :create_access_level, type: Integer, optional :create_access_level,
values: ProtectedTag::CreateAccessLevel.allowed_access_levels, type: Integer,
desc: 'Access levels allowed to create (defaults: `40`, maintainer access level)' values: ProtectedTag::CreateAccessLevel.allowed_access_levels,
desc: 'Access levels allowed to create (defaults: `40`, maintainer access level)',
documentation: { example: 30 }
use :optional_params_ee use :optional_params_ee
end end
post ':id/protected_tags' do post ':id/protected_tags' do
@ -76,9 +95,16 @@ module API
desc 'Unprotect a single tag' do desc 'Unprotect a single tag' do
detail 'This feature was introduced in GitLab 11.3.' detail 'This feature was introduced in GitLab 11.3.'
success code: 204
failure [
{ code: 403, message: 'Unauthenticated' },
{ code: 404, message: 'Not found' },
{ code: 412, message: 'Precondition Failed' }
]
tags %w[protected_tags]
end end
params do params do
requires :name, type: String, desc: 'The name of the protected tag' requires :name, type: String, desc: 'The name of the protected tag', documentation: { example: 'release-1-0' }
end end
# rubocop: disable CodeReuse/ActiveRecord # rubocop: disable CodeReuse/ActiveRecord
delete ':id/protected_tags/:name', requirements: TAG_ENDPOINT_REQUIREMENTS do delete ':id/protected_tags/:name', requirements: TAG_ENDPOINT_REQUIREMENTS do

View File

@ -352,7 +352,7 @@ module Gitlab
end end
def cookie_key def cookie_key
"#{idempotency_key}:cookie" "#{idempotency_key}:cookie:v2"
end end
def get_cookie def get_cookie

View File

@ -31814,6 +31814,9 @@ msgstr ""
msgid "ProjectSettings|Combine git tags with release notes, release evidence, and assets to create a release." msgid "ProjectSettings|Combine git tags with release notes, release evidence, and assets to create a release."
msgstr "" msgstr ""
msgid "ProjectSettings|Configure your infrastructure."
msgstr ""
msgid "ProjectSettings|Configure your project resources and monitor their health." msgid "ProjectSettings|Configure your project resources and monitor their health."
msgstr "" msgstr ""
@ -31913,6 +31916,9 @@ msgstr ""
msgid "ProjectSettings|If merge trains are enabled, merging is only possible if the branch can be rebased without conflicts." msgid "ProjectSettings|If merge trains are enabled, merging is only possible if the branch can be rebased without conflicts."
msgstr "" msgstr ""
msgid "ProjectSettings|Infrastructure"
msgstr ""
msgid "ProjectSettings|Internal" msgid "ProjectSettings|Internal"
msgstr "" msgstr ""

View File

@ -103,6 +103,9 @@ RSpec.describe 'User changes public project visibility', :js do
sign_in(project.first_owner) sign_in(project.first_owner)
visit edit_project_path(project) visit edit_project_path(project)
# https://gitlab.com/gitlab-org/gitlab/-/issues/381259
allow(Gitlab::QueryLimiting::Transaction).to receive(:threshold).and_return(110)
end end
it_behaves_like 'does not require confirmation' it_behaves_like 'does not require confirmation'

View File

@ -147,6 +147,7 @@ exports[`Learn GitLab renders correctly 1`] = `
<div> <div>
<a <a
class="gl-link" class="gl-link"
data-qa-selector="uncompleted_learn_gitlab_link"
data-testid="uncompleted-learn-gitlab-link" data-testid="uncompleted-learn-gitlab-link"
data-track-action="click_link" data-track-action="click_link"
data-track-label="set_up_your_first_project_s_ci_cd" data-track-label="set_up_your_first_project_s_ci_cd"
@ -171,6 +172,7 @@ exports[`Learn GitLab renders correctly 1`] = `
<div> <div>
<a <a
class="gl-link" class="gl-link"
data-qa-selector="uncompleted_learn_gitlab_link"
data-testid="uncompleted-learn-gitlab-link" data-testid="uncompleted-learn-gitlab-link"
data-track-action="click_link" data-track-action="click_link"
data-track-label="start_a_free_trial_of_gitlab_ultimate" data-track-label="start_a_free_trial_of_gitlab_ultimate"
@ -196,6 +198,7 @@ exports[`Learn GitLab renders correctly 1`] = `
<div> <div>
<a <a
class="gl-link" class="gl-link"
data-qa-selector="uncompleted_learn_gitlab_link"
data-testid="uncompleted-learn-gitlab-link" data-testid="uncompleted-learn-gitlab-link"
data-track-action="click_link" data-track-action="click_link"
data-track-label="add_code_owners" data-track-label="add_code_owners"
@ -228,6 +231,7 @@ exports[`Learn GitLab renders correctly 1`] = `
<div> <div>
<a <a
class="gl-link" class="gl-link"
data-qa-selector="uncompleted_learn_gitlab_link"
data-testid="uncompleted-learn-gitlab-link" data-testid="uncompleted-learn-gitlab-link"
data-track-action="click_link" data-track-action="click_link"
data-track-label="enable_require_merge_approvals" data-track-label="enable_require_merge_approvals"
@ -294,6 +298,7 @@ exports[`Learn GitLab renders correctly 1`] = `
<div> <div>
<a <a
class="gl-link" class="gl-link"
data-qa-selector="uncompleted_learn_gitlab_link"
data-testid="uncompleted-learn-gitlab-link" data-testid="uncompleted-learn-gitlab-link"
data-track-action="click_link" data-track-action="click_link"
data-track-label="create_an_issue" data-track-label="create_an_issue"
@ -318,6 +323,7 @@ exports[`Learn GitLab renders correctly 1`] = `
<div> <div>
<a <a
class="gl-link" class="gl-link"
data-qa-selector="uncompleted_learn_gitlab_link"
data-testid="uncompleted-learn-gitlab-link" data-testid="uncompleted-learn-gitlab-link"
data-track-action="click_link" data-track-action="click_link"
data-track-label="submit_a_merge_request_mr" data-track-label="submit_a_merge_request_mr"
@ -376,6 +382,7 @@ exports[`Learn GitLab renders correctly 1`] = `
<div> <div>
<a <a
class="gl-link" class="gl-link"
data-qa-selector="uncompleted_learn_gitlab_link"
data-testid="uncompleted-learn-gitlab-link" data-testid="uncompleted-learn-gitlab-link"
data-track-action="click_link" data-track-action="click_link"
data-track-label="run_a_security_scan_using_ci_cd" data-track-label="run_a_security_scan_using_ci_cd"

View File

@ -137,6 +137,8 @@ describe('Settings Panel', () => {
const findConfirmDangerButton = () => wrapper.findComponent(ConfirmDanger); const findConfirmDangerButton = () => wrapper.findComponent(ConfirmDanger);
const findEnvironmentsSettings = () => wrapper.findComponent({ ref: 'environments-settings' }); const findEnvironmentsSettings = () => wrapper.findComponent({ ref: 'environments-settings' });
const findFeatureFlagsSettings = () => wrapper.findComponent({ ref: 'feature-flags-settings' }); const findFeatureFlagsSettings = () => wrapper.findComponent({ ref: 'feature-flags-settings' });
const findInfrastructureSettings = () =>
wrapper.findComponent({ ref: 'infrastructure-settings' });
const findReleasesSettings = () => wrapper.findComponent({ ref: 'environments-settings' }); const findReleasesSettings = () => wrapper.findComponent({ ref: 'environments-settings' });
const findMonitorSettings = () => wrapper.findComponent({ ref: 'monitor-settings' }); const findMonitorSettings = () => wrapper.findComponent({ ref: 'monitor-settings' });
@ -841,6 +843,24 @@ describe('Settings Panel', () => {
}); });
}); });
}); });
describe('Infrastructure', () => {
describe('with feature flag', () => {
it('should show the infrastructure toggle', () => {
wrapper = mountComponent({
glFeatures: { splitOperationsVisibilityPermissions: true },
});
expect(findInfrastructureSettings().exists()).toBe(true);
});
});
describe('without feature flag', () => {
it('should not show the infrastructure toggle', () => {
wrapper = mountComponent({});
expect(findInfrastructureSettings().exists()).toBe(false);
});
});
});
describe('Releases', () => { describe('Releases', () => {
describe('with feature flag', () => { describe('with feature flag', () => {
it('should show the releases toggle', () => { it('should show the releases toggle', () => {

View File

@ -0,0 +1,25 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe GitlabSchema.types['CommitSignature'] do
it 'exposes the expected fields' do
expect(described_class).to have_graphql_fields(:verification_status, :commit_sha, :project)
end
describe '.resolve_type' do
it 'resolves gpg signatures' do
expect(described_class.resolve_type(build(:gpg_signature), {})).to eq(
Types::CommitSignatures::GpgSignatureType)
end
it 'resolves x509 signatures' do
expect(described_class.resolve_type(build(:x509_commit_signature), {})).to eq(
Types::CommitSignatures::X509SignatureType)
end
it 'raises an error when type is not known' do
expect { described_class.resolve_type(Class, {}) }.to raise_error('Unsupported commit signature type')
end
end
end

View File

@ -0,0 +1,18 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe GitlabSchema.types['GpgSignature'] do
specify { expect(described_class.graphql_name).to eq('GpgSignature') }
specify { expect(described_class).to require_graphql_authorizations(:download_code) }
specify { expect(described_class).to include(Types::CommitSignatureInterface) }
it 'contains attributes related to GPG signatures' do
expect(described_class).to have_graphql_fields(
:user, :verification_status, :commit_sha, :project,
:gpg_key_user_name, :gpg_key_user_email, :gpg_key_primary_keyid
)
end
end

View File

@ -0,0 +1,16 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe GitlabSchema.types['VerificationStatus'] do
specify { expect(described_class.graphql_name).to eq('VerificationStatus') }
it 'exposes all signature verification states' do
expect(described_class.values.keys)
.to match_array(%w[
UNVERIFIED UNVERIFIED_KEY VERIFIED
SAME_USER_DIFFERENT_EMAIL OTHER_USER UNKNOWN_KEY
MULTIPLE_SIGNATURES
])
end
end

View File

@ -0,0 +1,18 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe GitlabSchema.types['X509Signature'] do
specify { expect(described_class.graphql_name).to eq('X509Signature') }
specify { expect(described_class).to require_graphql_authorizations(:download_code) }
specify { expect(described_class).to include(Types::CommitSignatureInterface) }
it 'contains attributes related to X.509 signatures' do
expect(described_class).to have_graphql_fields(
:user, :verification_status, :commit_sha, :project,
:x509_certificate
)
end
end

View File

@ -13,7 +13,7 @@ RSpec.describe GitlabSchema.types['Commit'] do
expect(described_class).to have_graphql_fields( expect(described_class).to have_graphql_fields(
:id, :sha, :short_id, :title, :full_title, :full_title_html, :description, :description_html, :message, :title_html, :authored_date, :id, :sha, :short_id, :title, :full_title, :full_title_html, :description, :description_html, :message, :title_html, :authored_date,
:author_name, :author_email, :author_gravatar, :author, :web_url, :web_path, :author_name, :author_email, :author_gravatar, :author, :web_url, :web_path,
:pipelines, :signature_html :pipelines, :signature_html, :signature
) )
end end
end end

View File

@ -0,0 +1,14 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe GitlabSchema.types['X509Certificate'] do
specify { expect(described_class.graphql_name).to eq('X509Certificate') }
it 'contains attributes for X.509 certifcates' do
expect(described_class).to have_graphql_fields(
:certificate_status, :created_at, :email, :id, :serial_number, :subject,
:subject_key_identifier, :updated_at, :x509_issuer
)
end
end

View File

@ -0,0 +1,13 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe GitlabSchema.types['X509Issuer'] do
specify { expect(described_class.graphql_name).to eq('X509Issuer') }
it 'contains attributes for X.509 issuers' do
expect(described_class).to have_graphql_fields(
:created_at, :crl_url, :id, :subject, :subject_key_identifier, :updated_at
)
end
end

View File

@ -969,7 +969,8 @@ RSpec.describe ProjectsHelper do
containerRegistryAccessLevel: project.project_feature.container_registry_access_level, containerRegistryAccessLevel: project.project_feature.container_registry_access_level,
environmentsAccessLevel: project.project_feature.environments_access_level, environmentsAccessLevel: project.project_feature.environments_access_level,
featureFlagsAccessLevel: project.project_feature.feature_flags_access_level, featureFlagsAccessLevel: project.project_feature.feature_flags_access_level,
releasesAccessLevel: project.project_feature.releases_access_level releasesAccessLevel: project.project_feature.releases_access_level,
infrastructureAccessLevel: project.project_feature.infrastructure_access_level
) )
end end

View File

@ -798,7 +798,7 @@ RSpec.describe API::Helpers do
context 'with object storage' do context 'with object storage' do
let(:artifact) { create(:ci_job_artifact, :zip, :remote_store) } let(:artifact) { create(:ci_job_artifact, :zip, :remote_store) }
subject { helper.present_artifacts_file!(artifact.file, project: artifact.job.project) } subject { helper.present_artifacts_file!(artifact.file) }
before do before do
allow(helper).to receive(:env).and_return({}) allow(helper).to receive(:env).and_return({})
@ -830,7 +830,7 @@ RSpec.describe API::Helpers do
it 'retrieves a CDN-fronted URL' do it 'retrieves a CDN-fronted URL' do
expect(artifact.file).to receive(:cdn_enabled_url).and_call_original expect(artifact.file).to receive(:cdn_enabled_url).and_call_original
expect(Gitlab::ApplicationContext).to receive(:push).with(artifact_used_cdn: false).and_call_original expect(Gitlab::ApplicationContext).to receive(:push).with(artifact_used_cdn: false).and_call_original
expect(helper.cdn_fronted_url(artifact.file, artifact.job.project)).to be_a(String) expect(helper.cdn_fronted_url(artifact.file)).to be_a(String)
end end
end end
@ -841,7 +841,7 @@ RSpec.describe API::Helpers do
file = double(url: url) file = double(url: url)
expect(Gitlab::ApplicationContext).not_to receive(:push) expect(Gitlab::ApplicationContext).not_to receive(:push)
expect(helper.cdn_fronted_url(file, nil)).to eq(url) expect(helper.cdn_fronted_url(file)).to eq(url)
end end
end end
end end

View File

@ -527,7 +527,7 @@ RSpec.describe Gitlab::SidekiqMiddleware::DuplicateJobs::DuplicateJob, :clean_gi
end end
context 'with Redis cookies' do context 'with Redis cookies' do
let(:cookie_key) { "#{idempotency_key}:cookie" } let(:cookie_key) { "#{idempotency_key}:cookie:v2" }
let(:cookie) { get_redis_msgpack(cookie_key) } let(:cookie) { get_redis_msgpack(cookie_key) }
def with_redis(&block) def with_redis(&block)

View File

@ -85,4 +85,10 @@ RSpec.describe CommitSignatures::GpgSignature do
end end
end end
end end
describe '#user' do
it 'retrieves the gpg_key user' do
expect(signature.user).to eq(gpg_key.user)
end
end
end end

View File

@ -389,8 +389,7 @@ RSpec.describe API::Ci::JobArtifacts do
end end
end end
context 'when Google CDN is enabled' do context 'when Google CDN is configured' do
let(:cdn_enabled) { true }
let(:cdn_config) do let(:cdn_config) do
{ {
'provider' => 'Google', 'provider' => 'Google',
@ -401,7 +400,6 @@ RSpec.describe API::Ci::JobArtifacts do
end end
before do before do
stub_feature_flags(ci_job_artifacts_cdn: cdn_enabled)
stub_object_storage_uploader(config: Gitlab.config.artifacts.object_store, stub_object_storage_uploader(config: Gitlab.config.artifacts.object_store,
uploader: JobArtifactUploader, uploader: JobArtifactUploader,
proxy_download: proxy_download, proxy_download: proxy_download,
@ -418,18 +416,6 @@ RSpec.describe API::Ci::JobArtifacts do
expect(response.redirect_url).to start_with("https://cdn.example.org/#{artifact.file.path}") expect(response.redirect_url).to start_with("https://cdn.example.org/#{artifact.file.path}")
end end
context 'when ci_job_artifacts_cdn feature flag is disabled' do
let(:cdn_enabled) { false }
it 'returns the file remote URL' do
expect(Gitlab::ApplicationContext).to receive(:push).with(artifact_used_cdn: false).and_call_original
subject
expect(response).to redirect_to(artifact.file.url)
end
end
end end
context 'authorized user' do context 'authorized user' do

View File

@ -4,7 +4,8 @@ require 'spec_helper'
RSpec.describe 'getting a tree in a project' do RSpec.describe 'getting a tree in a project' do
include GraphqlHelpers include GraphqlHelpers
let(:project) { create(:project, :repository) } let_it_be(:project) { create(:project, :repository) }
let(:current_user) { project.first_owner } let(:current_user) { project.first_owner }
let(:path) { "" } let(:path) { "" }
let(:ref) { "master" } let(:ref) { "master" }
@ -82,6 +83,89 @@ RSpec.describe 'getting a tree in a project' do
end end
end end
context 'when the ref points to a gpg-signed commit with a user' do
let_it_be(:name) { GpgHelpers::User1.names.first }
let_it_be(:email) { GpgHelpers::User1.emails.first }
let_it_be(:current_user) { create(:user, name: name, email: email).tap { |user| project.add_owner(user) } }
let_it_be(:gpg_key) { create(:gpg_key, user: current_user, key: GpgHelpers::User1.public_key) }
let(:ref) { GpgHelpers::SIGNED_AND_AUTHORED_SHA }
let(:fields) do
<<~QUERY
tree(path:"#{path}", ref:"#{ref}") {
lastCommit {
signature {
... on GpgSignature {
#{all_graphql_fields_for('GpgSignature'.classify, max_depth: 2)}
}
}
}
}
QUERY
end
before do
post_graphql(query, current_user: current_user)
end
it 'returns the expected signature data' do
signature = graphql_data['project']['repository']['tree']['lastCommit']['signature']
expect(signature['commitSha']).to eq(ref)
expect(signature['user']['id']).to eq("gid://gitlab/User/#{current_user.id}")
expect(signature['gpgKeyUserName']).to eq(name)
expect(signature['gpgKeyUserEmail']).to eq(email)
expect(signature['verificationStatus']).to eq('VERIFIED')
expect(signature['project']['id']).to eq("gid://gitlab/Project/#{project.id}")
end
end
context 'when the ref points to a X.509-signed commit' do
let_it_be(:email) { X509Helpers::User1.certificate_email }
let_it_be(:current_user) { create(:user, email: email).tap { |user| project.add_owner(user) } }
let(:ref) { X509Helpers::User1.commit }
let(:fields) do
<<~QUERY
tree(path:"#{path}", ref:"#{ref}") {
lastCommit {
signature {
... on X509Signature {
#{all_graphql_fields_for('X509Signature'.classify, max_depth: 2)}
}
}
}
}
QUERY
end
before do
store = OpenSSL::X509::Store.new
store.add_cert(OpenSSL::X509::Certificate.new(X509Helpers::User1.trust_cert))
allow(OpenSSL::X509::Store).to receive(:new).and_return(store)
post_graphql(query, current_user: current_user)
end
it 'returns the expected signature data' do
signature = graphql_data['project']['repository']['tree']['lastCommit']['signature']
expect(signature['commitSha']).to eq(ref)
expect(signature['verificationStatus']).to eq('VERIFIED')
expect(signature['project']['id']).to eq("gid://gitlab/Project/#{project.id}")
end
it 'returns expected certificate data' do
signature = graphql_data['project']['repository']['tree']['lastCommit']['signature']
certificate = signature['x509Certificate']
expect(certificate['certificateStatus']).to eq('good')
expect(certificate['email']).to eq(X509Helpers::User1.certificate_email)
expect(certificate['id']).to be_present
expect(certificate['serialNumber']).to eq(X509Helpers::User1.certificate_serial.to_s)
expect(certificate['subject']).to eq(X509Helpers::User1.certificate_subject)
expect(certificate['subjectKeyIdentifier']).to eq(X509Helpers::User1.certificate_subject_key_identifier)
expect(certificate['createdAt']).to be_present
expect(certificate['updatedAt']).to be_present
end
end
context 'when current user is nil' do context 'when current user is nil' do
it 'returns empty project' do it 'returns empty project' do
post_graphql(query, current_user: nil) post_graphql(query, current_user: nil)

View File

@ -11,6 +11,8 @@ module TestEnv
# When developing the seed repository, comment out the branch you will modify. # When developing the seed repository, comment out the branch you will modify.
BRANCH_SHA = { BRANCH_SHA = {
'signed-commits' => 'c7794c1', 'signed-commits' => 'c7794c1',
'gpg-signed' => '8a852d5',
'x509-signed' => 'a4df3c8',
'not-merged-branch' => 'b83d6e3', 'not-merged-branch' => 'b83d6e3',
'branch-merged' => '498214d', 'branch-merged' => '498214d',
'empty-branch' => '7efb185', 'empty-branch' => '7efb185',

View File

@ -26,7 +26,7 @@ RSpec.describe JobArtifactUploader do
describe '#cdn_enabled_url' do describe '#cdn_enabled_url' do
it 'returns URL and false' do it 'returns URL and false' do
result = uploader.cdn_enabled_url(nil, '127.0.0.1') result = uploader.cdn_enabled_url('127.0.0.1')
expect(result.used_cdn).to be false expect(result.used_cdn).to be false
end end

View File

@ -44,30 +44,13 @@ RSpec.describe ObjectStorage::CDN do
end end
describe '#cdn_enabled_url' do describe '#cdn_enabled_url' do
context 'with ci_job_artifacts_cdn feature flag disabled' do it 'calls #cdn_signed_url' do
before do expect(subject).not_to receive(:url)
stub_feature_flags(ci_job_artifacts_cdn: false) expect(subject).to receive(:cdn_signed_url).and_call_original
end
it 'calls #url' do result = subject.cdn_enabled_url(public_ip)
expect(subject).to receive(:url).and_call_original
expect(subject).not_to receive(:cdn_signed_url)
result = subject.cdn_enabled_url(project, public_ip) expect(result.used_cdn).to be true
expect(result.used_cdn).to be false
end
end
context 'with ci_job_artifacts_cdn feature flag enabled' do
it 'calls #cdn_signed_url' do
expect(subject).not_to receive(:url)
expect(subject).to receive(:cdn_signed_url).and_call_original
result = subject.cdn_enabled_url(project, public_ip)
expect(result.used_cdn).to be true
end
end end
end end