Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
815a08defc
commit
a1b9a6464c
|
|
@ -87,6 +87,9 @@
|
|||
.if-merge-request-targeting-stable-branch: &if-merge-request-targeting-stable-branch
|
||||
if: '($CI_PIPELINE_SOURCE == "merge_request_event" && $CI_MERGE_REQUEST_EVENT_TYPE != "merge_train") && $CI_MERGE_REQUEST_TARGET_BRANCH_NAME =~ /^[\d-]+-stable(-ee|-jh)?$/'
|
||||
|
||||
.if-merge-request-targeting-stable-branch-is-tier-3: &if-merge-request-targeting-stable-branch-is-tier-3
|
||||
if: '($CI_PIPELINE_SOURCE == "merge_request_event" && $CI_MERGE_REQUEST_EVENT_TYPE != "merge_train") && $CI_MERGE_REQUEST_TARGET_BRANCH_NAME =~ /^[\d-]+-stable(-ee|-jh)?$/ && $CI_MERGE_REQUEST_LABELS =~ /pipeline::tier-3/'
|
||||
|
||||
.if-merge-request-labels-as-if-foss: &if-merge-request-labels-as-if-foss
|
||||
if: '($CI_PIPELINE_SOURCE == "merge_request_event" && $CI_MERGE_REQUEST_EVENT_TYPE != "merge_train") && $CI_MERGE_REQUEST_LABELS =~ /pipeline:run-as-if-foss/'
|
||||
|
||||
|
|
@ -1941,7 +1944,7 @@
|
|||
- !reference [".qa:rules:e2e-test-never-run", rules]
|
||||
- <<: *if-merge-request-labels-run-all-e2e
|
||||
# this rule needs to be synced with .notify:rules:notify-test-on-omnibus-failure to produce failure notification in stable branch merge requests
|
||||
- <<: *if-merge-request-targeting-stable-branch
|
||||
- <<: *if-merge-request-targeting-stable-branch-is-tier-3
|
||||
changes: *setup-test-env-patterns
|
||||
variables:
|
||||
MR_STABLE_BRANCH_CODE_PATTERNS: "true"
|
||||
|
|
|
|||
|
|
@ -9,10 +9,10 @@ import {
|
|||
GlLink,
|
||||
GlPopover,
|
||||
} from '@gitlab/ui';
|
||||
import ImmutableBadge from 'ee_component/packages_and_registries/container_registry/explorer/components/details_page/immutable_badge.vue';
|
||||
import { localeDateFormat, newDate } from '~/lib/utils/datetime_utility';
|
||||
import { numberToHumanSize } from '~/lib/utils/number_utils';
|
||||
import { n__ } from '~/locale';
|
||||
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
|
||||
import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
|
||||
import DetailsRow from '~/vue_shared/components/registry/details_row.vue';
|
||||
import ListItem from '~/vue_shared/components/registry/list_item.vue';
|
||||
|
|
@ -51,11 +51,11 @@ export default {
|
|||
DetailsRow,
|
||||
SignatureDetailsModal,
|
||||
GlPopover,
|
||||
ImmutableBadge,
|
||||
},
|
||||
directives: {
|
||||
GlTooltip: GlTooltipDirective,
|
||||
},
|
||||
mixins: [glFeatureFlagsMixin()],
|
||||
props: {
|
||||
tag: {
|
||||
type: Object,
|
||||
|
|
@ -168,9 +168,6 @@ export default {
|
|||
this.tag.protection?.minimumAccessLevelForPush != null
|
||||
);
|
||||
},
|
||||
isImmutable() {
|
||||
return this.glFeatures.containerRegistryImmutableTags && this.tag.protection?.immutable;
|
||||
},
|
||||
tagRowId() {
|
||||
return `${this.tag.name}_badge`;
|
||||
},
|
||||
|
|
@ -232,21 +229,7 @@ export default {
|
|||
</gl-popover>
|
||||
</template>
|
||||
|
||||
<template v-if="isImmutable">
|
||||
<gl-badge
|
||||
:id="tagRowId"
|
||||
boundary="viewport"
|
||||
class="gl-ml-4"
|
||||
data-testid="immutable-badge"
|
||||
>
|
||||
{{ s__('ContainerRegistry|immutable') }}
|
||||
</gl-badge>
|
||||
<gl-popover :target="tagRowId" data-testid="immutable-popover">
|
||||
{{
|
||||
s__('ContainerRegistry|This container image tag cannot be overwritten or deleted.')
|
||||
}}
|
||||
</gl-popover>
|
||||
</template>
|
||||
<immutable-badge :tag="tag" :tag-row-id="tagRowId" />
|
||||
|
||||
<clipboard-button
|
||||
v-if="tag.location"
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ import {
|
|||
GlFormGroup,
|
||||
GlForm,
|
||||
GlFormInput,
|
||||
GlFormRadio,
|
||||
GlFormSelect,
|
||||
GlLink,
|
||||
GlSprintf,
|
||||
|
|
@ -18,11 +17,6 @@ import {
|
|||
} from '~/packages_and_registries/settings/project/constants';
|
||||
import { __, s__ } from '~/locale';
|
||||
import * as Sentry from '~/sentry/sentry_browser_wrapper';
|
||||
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
|
||||
import glAbilitiesMixin from '~/vue_shared/mixins/gl_abilities_mixin';
|
||||
|
||||
const PROTECTED_RULE_TYPE = 'protected';
|
||||
const IMMUTABLE_RULE_TYPE = 'immutable';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
|
|
@ -34,9 +28,7 @@ export default {
|
|||
GlFormSelect,
|
||||
GlLink,
|
||||
GlSprintf,
|
||||
GlFormRadio,
|
||||
},
|
||||
mixins: [glFeatureFlagsMixin(), glAbilitiesMixin()],
|
||||
inject: ['projectPath'],
|
||||
props: {
|
||||
rule: {
|
||||
|
|
@ -44,6 +36,11 @@ export default {
|
|||
required: false,
|
||||
default: null,
|
||||
},
|
||||
isProtectedTagRuleType: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: true,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
|
@ -55,7 +52,6 @@ export default {
|
|||
minimumAccessLevelForDelete:
|
||||
this.rule?.minimumAccessLevelForDelete ?? GRAPHQL_ACCESS_LEVEL_VALUE_MAINTAINER,
|
||||
},
|
||||
tagRuleType: PROTECTED_RULE_TYPE,
|
||||
showValidation: false,
|
||||
updateInProgress: false,
|
||||
};
|
||||
|
|
@ -73,21 +69,6 @@ export default {
|
|||
tagNamePattern: this.protectionRuleFormData.tagNamePattern,
|
||||
};
|
||||
},
|
||||
isFeatureFlagEnabled() {
|
||||
return this.glFeatures.containerRegistryImmutableTags;
|
||||
},
|
||||
isProtectedTagRuleType() {
|
||||
return this.tagRuleType === PROTECTED_RULE_TYPE;
|
||||
},
|
||||
canCreateImmutableTagRule() {
|
||||
return (
|
||||
this.isFeatureFlagEnabled &&
|
||||
this.glAbilities.createContainerRegistryProtectionImmutableTagRule
|
||||
);
|
||||
},
|
||||
showProtectionType() {
|
||||
return this.canCreateImmutableTagRule && !this.rule;
|
||||
},
|
||||
isTagNamePatternValid() {
|
||||
if (this.showValidation) {
|
||||
return this.tagNamePattern.length > 0 && this.tagNamePattern.length < 100;
|
||||
|
|
@ -190,8 +171,6 @@ export default {
|
|||
}
|
||||
},
|
||||
},
|
||||
PROTECTED_RULE_TYPE,
|
||||
IMMUTABLE_RULE_TYPE,
|
||||
minimumAccessLevelOptions: MinimumAccessLevelOptions,
|
||||
};
|
||||
</script>
|
||||
|
|
@ -207,35 +186,7 @@ export default {
|
|||
<div v-for="error in alertErrorMessages" :key="error">{{ error }}</div>
|
||||
</gl-alert>
|
||||
|
||||
<template v-if="showProtectionType">
|
||||
<gl-form-group :label="s__('ContainerRegistry|Protection type')">
|
||||
<gl-form-radio
|
||||
v-model="tagRuleType"
|
||||
name="protection-type"
|
||||
:value="$options.PROTECTED_RULE_TYPE"
|
||||
autofocus
|
||||
>
|
||||
{{ s__('ContainerRegistry|Protected') }}
|
||||
<template #help>
|
||||
{{
|
||||
s__(
|
||||
'ContainerRegistry|Container image tags can be created, overwritten, or deleted by specific user roles.',
|
||||
)
|
||||
}}
|
||||
</template>
|
||||
</gl-form-radio>
|
||||
<gl-form-radio
|
||||
v-model="tagRuleType"
|
||||
name="protection-type"
|
||||
:value="$options.IMMUTABLE_RULE_TYPE"
|
||||
>
|
||||
{{ s__('ContainerRegistry|Immutable') }}
|
||||
<template #help>
|
||||
{{ s__('ContainerRegistry|Container image tags can never be overwritten or deleted.') }}
|
||||
</template>
|
||||
</gl-form-radio>
|
||||
</gl-form-group>
|
||||
</template>
|
||||
<slot name="protection-type" :rule="rule"></slot>
|
||||
|
||||
<gl-form-group
|
||||
:label="tagNamePatternLabel"
|
||||
|
|
@ -247,7 +198,7 @@ export default {
|
|||
id="input-tag-name-pattern"
|
||||
v-model.trim="protectionRuleFormData.tagNamePattern"
|
||||
type="text"
|
||||
:autofocus="!showProtectionType"
|
||||
:autofocus="!isProtectedTagRuleType"
|
||||
required
|
||||
trim
|
||||
:state="isTagNamePatternValid"
|
||||
|
|
|
|||
|
|
@ -13,12 +13,11 @@ import {
|
|||
GlSkeletonLoader,
|
||||
} from '@gitlab/ui';
|
||||
import CrudComponent from '~/vue_shared/components/crud_component.vue';
|
||||
import ContainerProtectionTagRuleForm from '~/packages_and_registries/settings/project/components/container_protection_tag_rule_form.vue';
|
||||
import ContainerProtectionTagRuleForm from 'ee_else_ce/packages_and_registries/settings/project/components/container_protection_tag_rule_form.vue';
|
||||
import getContainerProtectionTagRulesQuery from '~/packages_and_registries/settings/project/graphql/queries/get_container_protection_tag_rules.query.graphql';
|
||||
import deleteContainerProtectionTagRuleMutation from '~/packages_and_registries/settings/project/graphql/mutations/delete_container_protection_tag_rule.mutation.graphql';
|
||||
import { __, s__ } from '~/locale';
|
||||
import { getAccessLevelLabel } from '~/packages_and_registries/settings/project/utils';
|
||||
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
|
||||
|
||||
import * as Sentry from '~/sentry/sentry_browser_wrapper';
|
||||
|
||||
|
|
@ -44,7 +43,6 @@ export default {
|
|||
GlModal: GlModalDirective,
|
||||
GlTooltip: GlTooltipDirective,
|
||||
},
|
||||
mixins: [glFeatureFlagsMixin()],
|
||||
inject: ['projectPath'],
|
||||
apollo: {
|
||||
protectionRulesQueryPayload: {
|
||||
|
|
@ -86,13 +84,9 @@ export default {
|
|||
return this.tagProtectionRulesCount > 0;
|
||||
},
|
||||
description() {
|
||||
return this.isFeatureFlagEnabled
|
||||
? s__(
|
||||
'ContainerRegistry|Set up rules to protect container image tags from unauthorized changes or make them permanently immutable. Protection rules are checked first, followed by immutable rules. You can add up to 5 protection rules per project.',
|
||||
)
|
||||
: s__(
|
||||
'ContainerRegistry|When a container image tag is protected, only certain user roles can create, update, and delete the protected tag, which helps to prevent unauthorized changes. You can add up to 5 protection rules per project.',
|
||||
);
|
||||
return s__(
|
||||
'ContainerRegistry|When a container image tag is protected, only certain user roles can create, update, and delete the protected tag, which helps to prevent unauthorized changes. You can add up to 5 protection rules per project.',
|
||||
);
|
||||
},
|
||||
drawerTitle() {
|
||||
return this.protectionRuleMutationItem
|
||||
|
|
@ -102,9 +96,6 @@ export default {
|
|||
isLoadingProtectionRules() {
|
||||
return this.$apollo.queries.protectionRulesQueryPayload.loading;
|
||||
},
|
||||
isFeatureFlagEnabled() {
|
||||
return this.glFeatures.containerRegistryImmutableTags;
|
||||
},
|
||||
protectionRulesQueryResult() {
|
||||
return this.protectionRulesQueryPayload.nodes;
|
||||
},
|
||||
|
|
@ -180,9 +171,6 @@ export default {
|
|||
this.closeDrawer();
|
||||
this.refetchProtectionRules();
|
||||
},
|
||||
getBadgeText({ immutable }) {
|
||||
return immutable ? s__('ContainerRegistry|immutable') : s__('ContainerRegistry|protected');
|
||||
},
|
||||
isEditable({ immutable }) {
|
||||
return !immutable;
|
||||
},
|
||||
|
|
@ -264,6 +252,10 @@ export default {
|
|||
data-testid="project-container-protection-tag-rules-settings"
|
||||
@showForm="openNewFormDrawer"
|
||||
>
|
||||
<template #description>
|
||||
<slot name="description"></slot>
|
||||
</template>
|
||||
|
||||
<template v-if="containsTableItems" #count>
|
||||
<gl-badge>
|
||||
<gl-sprintf :message="s__('ContainerRegistry|%{count} of %{max}')">
|
||||
|
|
@ -320,11 +312,7 @@ export default {
|
|||
<span data-testid="tag-name-pattern" class="gl-break-all">
|
||||
{{ item.tagNamePattern }}
|
||||
</span>
|
||||
<span v-if="isFeatureFlagEnabled">
|
||||
<gl-badge data-testid="tag-rule-type-badge">
|
||||
{{ getBadgeText(item) }}
|
||||
</gl-badge>
|
||||
</span>
|
||||
<slot name="tag-badge" :item="item"></slot>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { helpPagePath } from '~/helpers/help_page_helper';
|
|||
import SettingsBlock from '~/vue_shared/components/settings/settings_block.vue';
|
||||
import ContainerExpirationPolicy from '~/packages_and_registries/settings/project/components/container_expiration_policy.vue';
|
||||
import ContainerProtectionRepositoryRules from '~/packages_and_registries/settings/project/components/container_protection_repository_rules.vue';
|
||||
import ContainerProtectionTagRules from '~/packages_and_registries/settings/project/components/container_protection_tag_rules.vue';
|
||||
import ContainerProtectionTagRules from 'ee_else_ce/packages_and_registries/settings/project/components/container_protection_tag_rules.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
|
|
|
|||
|
|
@ -1,4 +0,0 @@
|
|||
- page_title _("New Application")
|
||||
|
||||
= render ::Layouts::PageHeadingComponent.new(_('New application'))
|
||||
= render 'form', application: @application
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
= render ::Layouts::PageHeadingComponent.new(_("Your authorized applications"))
|
||||
%main{ :role => "main" }
|
||||
.table-holder
|
||||
%table.table.table-striped
|
||||
%thead
|
||||
%tr
|
||||
%th= _('Application')
|
||||
%th= _('Created At')
|
||||
%th
|
||||
%th
|
||||
%tbody
|
||||
- @applications.each do |application|
|
||||
%tr
|
||||
%td= application.name
|
||||
%td= application.created_at.strftime('%Y-%m-%d %H:%M:%S')
|
||||
%td= render 'delete_form', application: application
|
||||
|
|
@ -5,4 +5,4 @@ feature_category: value_stream_management
|
|||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/174502
|
||||
milestone: '17.7'
|
||||
queued_migration_version: 20241203074404
|
||||
finalized_by: # version of the migration that finalized this BBM
|
||||
finalized_by: '20250528232821'
|
||||
|
|
|
|||
|
|
@ -0,0 +1,22 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddIndexToProjectComplianceViolationsNamespaceCreatedId < Gitlab::Database::Migration[2.3]
|
||||
milestone '18.1'
|
||||
disable_ddl_transaction!
|
||||
|
||||
INDEX_NAMESPACE_ID = 'idx_project_compliance_violations_on_namespace_id'
|
||||
INDEX_NAMESPACE_CREATED_AT_ID_DESC = 'i_project_compliance_violations_on_namespace_id_created_at_id'
|
||||
|
||||
def up
|
||||
add_concurrent_index :project_compliance_violations, [:namespace_id, :created_at, :id],
|
||||
order: { created_at: :desc, id: :desc }, using: :btree, name: INDEX_NAMESPACE_CREATED_AT_ID_DESC
|
||||
|
||||
remove_concurrent_index_by_name :project_compliance_violations, INDEX_NAMESPACE_ID
|
||||
end
|
||||
|
||||
def down
|
||||
add_concurrent_index :project_compliance_violations, :namespace_id, name: INDEX_NAMESPACE_ID
|
||||
|
||||
remove_concurrent_index_by_name :project_compliance_violations, INDEX_NAMESPACE_CREATED_AT_ID_DESC
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class FinalizeHkBackfillIssueMetricsNamespaceId < Gitlab::Database::Migration[2.3]
|
||||
milestone '18.1'
|
||||
|
||||
disable_ddl_transaction!
|
||||
|
||||
restrict_gitlab_migration gitlab_schema: :gitlab_main_cell
|
||||
|
||||
def up
|
||||
ensure_batched_background_migration_is_finished(
|
||||
job_class_name: 'BackfillIssueMetricsNamespaceId',
|
||||
table_name: :issue_metrics,
|
||||
column_name: :id,
|
||||
job_arguments: [:namespace_id, :issues, :namespace_id, :issue_id],
|
||||
finalize: true
|
||||
)
|
||||
end
|
||||
|
||||
def down; end
|
||||
end
|
||||
|
|
@ -0,0 +1 @@
|
|||
cd712adf76694be2c439982836cfd66f49a04cdf1775ebc3a198d3fd8821ca79
|
||||
|
|
@ -0,0 +1 @@
|
|||
cfb1b2b01820de3f7e413c7e7b0615e406063ea6970baf95e08a5427592cf34f
|
||||
|
|
@ -33259,6 +33259,8 @@ CREATE UNIQUE INDEX i_pm_package_versions_on_package_id_and_version ON pm_packag
|
|||
|
||||
CREATE UNIQUE INDEX i_pm_packages_purl_type_and_name ON pm_packages USING btree (purl_type, name);
|
||||
|
||||
CREATE INDEX i_project_compliance_violations_on_namespace_id_created_at_id ON project_compliance_violations USING btree (namespace_id, created_at DESC, id DESC);
|
||||
|
||||
CREATE INDEX i_project_requirement_statuses_on_namespace_id_framework_id ON project_requirement_compliance_statuses USING btree (namespace_id, compliance_framework_id, id);
|
||||
|
||||
CREATE INDEX i_project_requirement_statuses_on_namespace_id_project_id ON project_requirement_compliance_statuses USING btree (namespace_id, project_id, id);
|
||||
|
|
@ -33599,8 +33601,6 @@ CREATE INDEX idx_project_audit_events_on_project_id_author_created_at_id ON ONLY
|
|||
|
||||
CREATE INDEX idx_project_compliance_violations_on_control_id ON project_compliance_violations USING btree (compliance_requirements_control_id);
|
||||
|
||||
CREATE INDEX idx_project_compliance_violations_on_namespace_id ON project_compliance_violations USING btree (namespace_id);
|
||||
|
||||
CREATE INDEX idx_project_compliance_violations_on_project_id ON project_compliance_violations USING btree (project_id);
|
||||
|
||||
CREATE INDEX idx_project_control_compliance_status_on_requirement_status_id ON project_control_compliance_statuses USING btree (requirement_status_id);
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ title: GitLab Docs
|
|||
<h3>Visit <a href="https://docs.gitlab.com/ee/">docs.gitlab.com</a> for the latest version
|
||||
of this help information with enhanced navigation, formatting, and search.</h3>
|
||||
</div>
|
||||
<!-- the div above will not display on the docs site but will display on /help -->
|
||||
<!-- the div tag will not display on the docs site but will display on /help -->
|
||||
|
||||
<!-- markdownlint-enable MD044 -->
|
||||
|
||||
|
|
|
|||
|
|
@ -12,10 +12,10 @@ title: Job Artifacts API
|
|||
|
||||
{{< /details >}}
|
||||
|
||||
Use the job artifacts API to download or delete job artifacts.
|
||||
Use this API to interact with [job artifacts](../ci/jobs/job_artifacts.md).
|
||||
|
||||
Authentication with a [CI/CD job token](../ci/jobs/job_artifacts.md#with-a-cicd-job-token)
|
||||
available in the Premium and Ultimate tier.
|
||||
is available in the Premium and Ultimate tier.
|
||||
|
||||
## Get job artifacts
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,8 @@ title: Jobs API
|
|||
|
||||
{{< /details >}}
|
||||
|
||||
Use this API to interact with [CI/CD jobs](../ci/jobs/_index.md).
|
||||
|
||||
## List project jobs
|
||||
|
||||
{{< history >}}
|
||||
|
|
|
|||
|
|
@ -12,18 +12,11 @@ title: Merge Trains API
|
|||
|
||||
{{< /details >}}
|
||||
|
||||
Every API call for [merge train](../ci/pipelines/merge_trains.md) must be authenticated with at least the Developer [role](../user/permissions.md).
|
||||
Use this API to interact with [merge trains](../ci/pipelines/merge_trains.md).
|
||||
|
||||
If a user is not a member of a project and the project is private, a `GET` request on that project returns a `404` status code.
|
||||
Prerequisites:
|
||||
|
||||
If Merge Trains is not available for the project, a `403` status code is returned.
|
||||
|
||||
## Merge Trains API pagination
|
||||
|
||||
By default, `GET` requests return 20 results at a time because the API results
|
||||
are paginated.
|
||||
|
||||
For more information, see [pagination](rest/_index.md#pagination).
|
||||
- You must have at least the Developer role.
|
||||
|
||||
## List Merge Trains for a project
|
||||
|
||||
|
|
@ -34,6 +27,9 @@ GET /projects/:id/merge_trains
|
|||
GET /projects/:id/merge_trains?scope=complete
|
||||
```
|
||||
|
||||
Use the `page` and `per_page` [pagination](rest/_index.md#offset-based-pagination) parameters to
|
||||
control the pagination of results.
|
||||
|
||||
| Attribute | Type | Required | Description |
|
||||
|-----------|----------------|----------|-------------|
|
||||
| `id` | integer/string | Yes | The ID or [URL-encoded path of the project](rest/_index.md#namespaced-paths). |
|
||||
|
|
@ -44,6 +40,11 @@ GET /projects/:id/merge_trains?scope=complete
|
|||
curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/1/merge_trains"
|
||||
```
|
||||
|
||||
Returns:
|
||||
|
||||
- `403: Forbidden` if Merge Trains are not available for the project
|
||||
- `404: Not Found` if the user is not a member of a private project
|
||||
|
||||
Example response:
|
||||
|
||||
```json
|
||||
|
|
@ -96,6 +97,9 @@ Get all merge requests added to a merge train for the requested target branch.
|
|||
GET /projects/:id/merge_trains/:target_branch
|
||||
```
|
||||
|
||||
Use the `page` and `per_page` [pagination](rest/_index.md#offset-based-pagination) parameters to
|
||||
control the pagination of results.
|
||||
|
||||
Supported attributes:
|
||||
|
||||
| Attribute | Type | Required | Description |
|
||||
|
|
@ -111,6 +115,11 @@ Example request:
|
|||
curl --request GET --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/597/merge_trains/main"
|
||||
```
|
||||
|
||||
Returns:
|
||||
|
||||
- `403: Forbidden` if Merge Trains are not available for the project
|
||||
- `404: Not Found` if the user is not a member of a private project
|
||||
|
||||
Example response:
|
||||
|
||||
```json
|
||||
|
|
@ -166,6 +175,9 @@ Get merge train information for the requested merge request.
|
|||
GET /projects/:id/merge_trains/merge_requests/:merge_request_iid
|
||||
```
|
||||
|
||||
Use the `page` and `per_page` [pagination](rest/_index.md#offset-based-pagination) parameters to
|
||||
control the pagination of results.
|
||||
|
||||
Supported attributes:
|
||||
|
||||
| Attribute | Type | Required | Description |
|
||||
|
|
@ -179,6 +191,11 @@ Example request:
|
|||
curl --request GET --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/597/merge_trains/merge_requests/1"
|
||||
```
|
||||
|
||||
Returns:
|
||||
|
||||
- `403: Forbidden` if Merge Trains are not available for the project
|
||||
- `404: Not Found` if the user is not a member of a private project
|
||||
|
||||
Example response:
|
||||
|
||||
```json
|
||||
|
|
@ -249,6 +266,10 @@ Example request:
|
|||
curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/597/merge_trains/merge_requests/1"
|
||||
```
|
||||
|
||||
Returns:
|
||||
|
||||
- `403: Forbidden` if Merge Trains are not available for the project
|
||||
|
||||
Example response:
|
||||
|
||||
```json
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ title: Pipeline schedules API
|
|||
|
||||
{{< /details >}}
|
||||
|
||||
You can read more about [pipeline schedules](../ci/pipelines/schedules.md).
|
||||
Use this API to interact with [pipeline schedules](../ci/pipelines/schedules.md).
|
||||
|
||||
## Get all pipeline schedules
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ title: Pipeline trigger tokens API
|
|||
|
||||
{{< /details >}}
|
||||
|
||||
You can read more about [triggering pipelines through the API](../ci/triggers/_index.md).
|
||||
Use this API to [trigger pipelines](../ci/triggers/_index.md).
|
||||
|
||||
## List project trigger tokens
|
||||
|
||||
|
|
|
|||
|
|
@ -12,12 +12,7 @@ title: Pipelines API
|
|||
|
||||
{{< /details >}}
|
||||
|
||||
## Pipelines pagination
|
||||
|
||||
By default, `GET` requests return 20 results at a time because the API results
|
||||
are paginated.
|
||||
|
||||
Read more on [pagination](rest/_index.md#pagination).
|
||||
Use this API to interact with [CI/CD pipelines](../ci/pipelines/_index.md).
|
||||
|
||||
## List project pipelines
|
||||
|
||||
|
|
@ -41,6 +36,9 @@ are not included in the results. To return child pipelines, set `source` to `par
|
|||
GET /projects/:id/pipelines
|
||||
```
|
||||
|
||||
Use the `page` and `per_page` [pagination](rest/_index.md#offset-based-pagination) parameters to
|
||||
control the pagination of results.
|
||||
|
||||
| Attribute | Type | Required | Description |
|
||||
|------------------|----------------|----------|-------------|
|
||||
| `id` | integer/string | Yes | The ID or [URL-encoded path of the project](rest/_index.md#namespaced-paths) |
|
||||
|
|
@ -114,6 +112,9 @@ You can also get a single [child pipeline](../ci/pipelines/downstream_pipelines.
|
|||
GET /projects/:id/pipelines/:pipeline_id
|
||||
```
|
||||
|
||||
Use the `page` and `per_page` [pagination](rest/_index.md#offset-based-pagination) parameters to
|
||||
control the pagination of results.
|
||||
|
||||
| Attribute | Type | Required | Description |
|
||||
|---------------|----------------|----------|-------------|
|
||||
| `id` | integer/string | Yes | The ID or [URL-encoded path of the project](rest/_index.md#namespaced-paths) |
|
||||
|
|
@ -169,7 +170,7 @@ Example of response
|
|||
}
|
||||
```
|
||||
|
||||
### Get the latest pipeline
|
||||
## Get the latest pipeline
|
||||
|
||||
{{< history >}}
|
||||
|
||||
|
|
@ -184,6 +185,9 @@ Get the latest pipeline for the most recent commit on a specific ref in a projec
|
|||
GET /projects/:id/pipelines/latest
|
||||
```
|
||||
|
||||
Use the `page` and `per_page` [pagination](rest/_index.md#offset-based-pagination) parameters to
|
||||
control the pagination of results.
|
||||
|
||||
| Attribute | Type | Required | Description |
|
||||
|-----------|--------|----------|-------------|
|
||||
| `ref` | string | No | The branch or tag to check for the latest pipeline. Defaults to the default branch when not specified. |
|
||||
|
|
@ -238,7 +242,7 @@ Example of response
|
|||
}
|
||||
```
|
||||
|
||||
### Get variables of a pipeline
|
||||
## Get variables for a pipeline
|
||||
|
||||
Get the variables of a pipeline. Does not include variables that come from a pipeline schedule.
|
||||
For more information, see [issue 250850](https://gitlab.com/gitlab-org/gitlab/-/issues/250850).
|
||||
|
|
@ -247,6 +251,9 @@ For more information, see [issue 250850](https://gitlab.com/gitlab-org/gitlab/-/
|
|||
GET /projects/:id/pipelines/:pipeline_id/variables
|
||||
```
|
||||
|
||||
Use the `page` and `per_page` [pagination](rest/_index.md#offset-based-pagination) parameters to
|
||||
control the pagination of results.
|
||||
|
||||
| Attribute | Type | Required | Description |
|
||||
|---------------|----------------|----------|-------------|
|
||||
| `id` | integer/string | Yes | The ID or [URL-encoded path of the project](rest/_index.md#namespaced-paths) |
|
||||
|
|
@ -272,7 +279,7 @@ Example of response
|
|||
]
|
||||
```
|
||||
|
||||
### Get a pipeline's test report
|
||||
## Get a test report for a pipeline
|
||||
|
||||
{{< alert type="note" >}}
|
||||
|
||||
|
|
@ -284,6 +291,9 @@ This API route is part of the [Unit test report](../ci/testing/unit_test_reports
|
|||
GET /projects/:id/pipelines/:pipeline_id/test_report
|
||||
```
|
||||
|
||||
Use the `page` and `per_page` [pagination](rest/_index.md#offset-based-pagination) parameters to
|
||||
control the pagination of results.
|
||||
|
||||
| Attribute | Type | Required | Description |
|
||||
|---------------|----------------|----------|-------------|
|
||||
| `id` | integer/string | Yes | The ID or [URL-encoded path of the project](rest/_index.md#namespaced-paths) |
|
||||
|
|
@ -329,7 +339,7 @@ Sample response:
|
|||
}
|
||||
```
|
||||
|
||||
### Get a pipeline's test report summary
|
||||
## Get a test report summary for a pipeline
|
||||
|
||||
{{< alert type="note" >}}
|
||||
|
||||
|
|
@ -341,6 +351,9 @@ This API route is part of the [Unit test report](../ci/testing/unit_test_reports
|
|||
GET /projects/:id/pipelines/:pipeline_id/test_report_summary
|
||||
```
|
||||
|
||||
Use the `page` and `per_page` [pagination](rest/_index.md#offset-based-pagination) parameters to
|
||||
control the pagination of results.
|
||||
|
||||
| Attribute | Type | Required | Description |
|
||||
|---------------|----------------|----------|-------------|
|
||||
| `id` | integer/string | Yes | The ID or [URL-encoded path of the project](rest/_index.md#namespaced-paths) |
|
||||
|
|
@ -509,7 +522,7 @@ Response:
|
|||
}
|
||||
```
|
||||
|
||||
## Cancel a pipeline's jobs
|
||||
## Cancel all jobs for a pipeline
|
||||
|
||||
```plaintext
|
||||
POST /projects/:id/pipelines/:pipeline_id/cancel
|
||||
|
|
|
|||
|
|
@ -768,7 +768,7 @@ To determine the IP address of an instance runner:
|
|||
1. Select **CI/CD > Runners**.
|
||||
1. Find the runner in the table and view the **IP Address** column.
|
||||
|
||||

|
||||

|
||||
|
||||
### Determine the IP address of a project runner
|
||||
|
||||
|
|
@ -779,7 +779,7 @@ project.
|
|||
1. Go to the project's **Settings > CI/CD** and expand the **Runners** section.
|
||||
1. Select the runner name and find the **IP Address** row.
|
||||
|
||||

|
||||

|
||||
|
||||
## Enable use of runner registration tokens in projects and groups
|
||||
|
||||
|
|
|
|||
|
|
@ -143,7 +143,7 @@ displays a list of test suites and cases reported from the XML file.
|
|||
You can view all the known test suites and select each of these to see further
|
||||
details, including the cases that make up the suite.
|
||||
|
||||
You can also retrieve the reports via the [GitLab API](../../api/pipelines.md#get-a-pipelines-test-report).
|
||||
You can also retrieve the reports via the [GitLab API](../../api/pipelines.md#get-a-test-report-for-a-pipeline).
|
||||
|
||||
### Unit test reports parsing errors
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
stage: Verify
|
||||
group: Pipeline Authoring
|
||||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://handbook.gitlab.com/handbook/product/ux/technical-writing/#assignments
|
||||
title: Trigger pipelines by using the API
|
||||
title: Trigger pipelines with the API
|
||||
---
|
||||
|
||||
{{< details >}}
|
||||
|
|
|
|||
|
|
@ -2052,10 +2052,6 @@ feedback: false
|
|||
The default is to leave it there. If you want to omit it from a document, you
|
||||
must check with a technical writer before doing so.
|
||||
|
||||
The click events in the feedback section are tracked with Google Tag Manager.
|
||||
The conversions can be viewed on Google Analytics by navigating to
|
||||
**Behavior > Events > Top events > docs**.
|
||||
|
||||
### GitLab restart
|
||||
|
||||
When a restart or reconfigure of GitLab is required, avoid duplication by linking
|
||||
|
|
|
|||
|
|
@ -204,7 +204,7 @@ To override the default values in the `values.yaml` file in the
|
|||
- Add a file with a different name or path to the repository. Set the
|
||||
`HELM_UPGRADE_VALUES_FILE` [CI/CD variable](cicd_variables.md) with the path and name of the file.
|
||||
|
||||
Some values cannot be overridden with the options above, but [this issue](https://gitlab.com/gitlab-org/cluster-integration/auto-deploy-image/-/issues/31) proposes to change this behavior.
|
||||
Some values cannot be overridden with the previous options, but [this issue](https://gitlab.com/gitlab-org/cluster-integration/auto-deploy-image/-/issues/31) proposes to change this behavior.
|
||||
To override settings like `replicaCount`, use the `REPLICAS` [build and deployment](cicd_variables.md#build-and-deployment-variables) CI/CD variable.
|
||||
|
||||
### Customize `helm upgrade`
|
||||
|
|
|
|||
|
|
@ -173,7 +173,7 @@ controller to external clients and, indirectly, to any application running insid
|
|||
Bare-metal environments lack this commodity, requiring a slightly different setup to offer the
|
||||
same kind of access to external consumers.
|
||||
|
||||
The docs linked above explain the issue and present possible solutions, for example:
|
||||
The documentation linked previously explains the issue and provides possible solutions, for example:
|
||||
|
||||
- Through [MetalLB](https://github.com/metallb/metallb).
|
||||
- Through [PorterLB](https://github.com/kubesphere/porterlb).
|
||||
|
|
|
|||
|
|
@ -161,7 +161,7 @@ The supported buildpacks are:
|
|||
- buildpack-nginx
|
||||
```
|
||||
|
||||
If your application needs a buildpack that is not in the above list, you
|
||||
If your application needs a buildpack that is not in the previous list, you
|
||||
might want to use a [custom buildpack](customize.md#custom-buildpacks).
|
||||
|
||||
## Auto Code Quality
|
||||
|
|
@ -190,7 +190,7 @@ out. The merge request widget also displays any
|
|||
|
||||
Static Application Security Testing (SAST) runs static
|
||||
analysis on the current code, and checks for potential security issues. The
|
||||
Auto SAST stage requires [GitLab Runner](https://docs.gitlab.com/runner/) 11.5 or above.
|
||||
Auto SAST stage requires [GitLab Runner](https://docs.gitlab.com/runner/) 11.5 or later.
|
||||
|
||||
After creating the report, it's uploaded as an artifact which you can later
|
||||
download and check out. The merge request widget also displays any security
|
||||
|
|
|
|||
|
|
@ -183,7 +183,7 @@ that works for this problem. Follow these steps to use the tool in Auto DevOps:
|
|||
- remote: https://gitlab.com/shinya.maeda/ci-templates/-/raw/master/map-deprecated-api.gitlab-ci.yml
|
||||
|
||||
variables:
|
||||
HELM_VERSION_FOR_MAPKUBEAPIS: "v2" # If you're using auto-depoy-image v2 or above, please specify "v3".
|
||||
HELM_VERSION_FOR_MAPKUBEAPIS: "v2" # If you're using auto-depoy-image v2 or later, please specify "v3".
|
||||
```
|
||||
|
||||
1. Run the job `<environment-name>:map-deprecated-api`. Ensure that this job succeeds before moving
|
||||
|
|
|
|||
|
|
@ -372,7 +372,7 @@ remove filtering:
|
|||
- Its corresponding `pack-<SHA1>.idx` file.
|
||||
- A `pack-<SHA1>.promisor` file.
|
||||
|
||||
1. Delete the `.promisor` file. The above step should have left only one
|
||||
1. Delete the `.promisor` file. The previous step should have left only one
|
||||
`pack-<SHA1>.promisor` file, which should be empty and should be deleted.
|
||||
|
||||
1. Remove partial clone configuration. The partial clone-related configuration
|
||||
|
|
|
|||
|
|
@ -397,7 +397,7 @@ If license or advisory data is missing from the dependency list or MR pages, one
|
|||
|
||||
`package_metadata` synchronization is triggered by using cron jobs ([advisory sync](https://gitlab.com/gitlab-org/gitlab/-/blob/16-3-stable-ee/config/initializers/1_settings.rb#L864-866) and [license sync](https://gitlab.com/gitlab-org/gitlab/-/blob/16-3-stable-ee/config/initializers/1_settings.rb#L855-857)) and imports only the package registry types enabled in [admin settings](../../administration/settings/security_and_compliance.md#choose-package-registry-metadata-to-sync).
|
||||
|
||||
The file structure in `vendor/package_metadata` must coincide with the package registry type enabled above. For example, to sync `maven` license or advisory data, the package metadata directory under the Rails directory must have the following structure:
|
||||
The file structure in `vendor/package_metadata` must coincide with the package registry type enabled previously. For example, to sync `maven` license or advisory data, the package metadata directory under the Rails directory must have the following structure:
|
||||
|
||||
- For licenses:`$GITLAB_RAILS_ROOT_DIR/vendor/package_metadata/licenses/v2/maven/**/*.ndjson`.
|
||||
- For advisories:`$GITLAB_RAILS_ROOT_DIR/vendor/package_metadata/advisories/v2/maven/**/*.ndjson`.
|
||||
|
|
|
|||
|
|
@ -155,13 +155,13 @@ Based on the metrics collected, for this job profile, you can limit the Kubernet
|
|||
|
||||
If you use a cluster with a node pool of three `e2-standard-4` nodes to run jobs, the `1 CPU` limit allows only **12 jobs** to run simultaneously (an `e2-standard-4` node has **4 vCPU** and **16 GB** of memory). Additional jobs wait for the running jobs to complete and free up the resources before starting.
|
||||
|
||||
The memory requested is critical because Kubernetes terminates any pod that uses more memory than the limit set or available on the cluster. However, the CPU limit is more flexible but impacts the job duration. A lower CPU limit set increases the time it takes for a job to complete. In the above example, setting the CPU limit to `250m` (or `0.25`) instead `1` increased the job duration by four times (from about two minutes to eight to ten minutes).
|
||||
The memory requested is critical because Kubernetes terminates any pod that uses more memory than the limit set or available on the cluster. However, the CPU limit is more flexible but impacts the job duration. A lower CPU limit set increases the time it takes for a job to complete. In the previous example, setting the CPU limit to `250m` (or `0.25`) instead `1` increased the job duration by four times (from about two minutes to eight to ten minutes).
|
||||
|
||||
As the metrics collection method uses a polling mechanism, you should round up the maximum usage identified. For example, instead of `303 Mi` for the memory usage, round it to `400 Mi`.
|
||||
|
||||
Important considerations for the above example:
|
||||
Important considerations for the previous example:
|
||||
|
||||
- The metrics above were collected on the local machine, which doesn't have the same CPU configuration than a Google Kubernetes Engine Cluster. However, these metrics were validated by monitoring them on a Kubernetes cluster with an `e2-standard-4` node.
|
||||
- The metrics were collected on the local machine, which doesn't have the same CPU configuration than a Google Kubernetes Engine Cluster. However, these metrics were validated by monitoring them on a Kubernetes cluster with an `e2-standard-4` node.
|
||||
- To get an accurate representation of those metrics, run the tests described in the [Assess phase](#assess-the-expected-cicd-workloads) on a Google Compute Engine VM.
|
||||
|
||||
## Plan the runner fleet configuration
|
||||
|
|
@ -294,7 +294,7 @@ node_pools = {
|
|||
}
|
||||
```
|
||||
|
||||
In the configuration above:
|
||||
In the previous configuration:
|
||||
|
||||
- The `runner-manager` block refers to the node pool where GitLab Runner is installed. In our example, a `e2-standard-2` is more than enough.
|
||||
- The labels sections in the `runner-manager` block is useful when installing GitLab Runner on GitLab. A node selector is configured through the operator configuration to make sure that GitLab Runner is installed on a node of this node pool.
|
||||
|
|
@ -351,7 +351,7 @@ EOT
|
|||
]
|
||||
```
|
||||
|
||||
In the configuration above:
|
||||
In the previous configuration:
|
||||
|
||||
- The `pod_spec` parameter allows us to set a node selector for the pod running GitLab Runner. In the configuration, the node selector is set to `"app" = "gitlab-runner"` to ensure that GitLab Runner is installed on the runner-manager node pool.
|
||||
- The `config_template` parameters provides a default limit for all jobs run by the GitLab Runner Manager. It also allows an overwrite of those limits as long as the value set is not greater than the default values.
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
stage: AI-powered
|
||||
group: AI Framework
|
||||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://handbook.gitlab.com/handbook/product/ux/technical-writing/#assignments
|
||||
description: Set up and manage GitLab Duo with Amazon Q on a Self-Managed instance using AWS integration.
|
||||
title: Set up GitLab Duo with Amazon Q
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ stage: Software Supply Chain Security
|
|||
group: Authentication
|
||||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://handbook.gitlab.com/handbook/product/ux/technical-writing/#assignments
|
||||
gitlab_dedicated: yes
|
||||
description: Two-factor authentication (2FA) adds an extra layer of security to your GitLab account by requiring a second form of verification in addition to your password.
|
||||
title: Two-factor authentication
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
stage: Software Supply Chain Security
|
||||
group: Authentication
|
||||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://handbook.gitlab.com/handbook/product/ux/technical-writing/#assignments
|
||||
description: Use personal access tokens to authenticate with the GitLab API or Git over HTTPS. Includes creation, rotation, revocation, scopes, and expiration settings.
|
||||
title: Personal access tokens
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -7885,9 +7885,6 @@ msgstr ""
|
|||
msgid "AppleAppStore|Use GitLab to build and release an app in the Apple App Store."
|
||||
msgstr ""
|
||||
|
||||
msgid "Application"
|
||||
msgstr ""
|
||||
|
||||
msgid "Application ID"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -18963,9 +18960,6 @@ msgstr ""
|
|||
msgid "Created %{timestamp}"
|
||||
msgstr ""
|
||||
|
||||
msgid "Created At"
|
||||
msgstr ""
|
||||
|
||||
msgid "Created Date"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -40374,9 +40368,6 @@ msgstr ""
|
|||
msgid "New %{type} in %{project}"
|
||||
msgstr ""
|
||||
|
||||
msgid "New Application"
|
||||
msgstr ""
|
||||
|
||||
msgid "New File"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -40404,9 +40395,6 @@ msgstr ""
|
|||
msgid "New Security findings"
|
||||
msgstr ""
|
||||
|
||||
msgid "New application"
|
||||
msgstr ""
|
||||
|
||||
msgid "New branch"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -71613,9 +71601,6 @@ msgstr ""
|
|||
msgid "Your applications"
|
||||
msgstr ""
|
||||
|
||||
msgid "Your authorized applications"
|
||||
msgstr ""
|
||||
|
||||
msgid "Your browser doesn't support WebAuthn. Please use a supported browser, e.g. Chrome (67+) or Firefox (60+)."
|
||||
msgstr ""
|
||||
|
||||
|
|
|
|||
|
|
@ -58,13 +58,8 @@ describe('tags list row', () => {
|
|||
const getTooltipFor = (component) => getBinding(component.element, 'gl-tooltip');
|
||||
const findProtectedBadge = () => wrapper.findByTestId('protected-badge');
|
||||
const findProtectedPopover = () => wrapper.findByTestId('protected-popover');
|
||||
const findImmutableBadge = () => wrapper.findByTestId('immutable-badge');
|
||||
const findImmutablePopover = () => wrapper.findByTestId('immutable-popover');
|
||||
|
||||
const mountComponent = (
|
||||
propsData = defaultProps,
|
||||
{ immutableTagsFeatureFlagState = false } = {},
|
||||
) => {
|
||||
const mountComponent = (propsData = defaultProps) => {
|
||||
wrapper = shallowMountExtended(TagsListRow, {
|
||||
stubs: {
|
||||
GlSprintf,
|
||||
|
|
@ -75,11 +70,6 @@ describe('tags list row', () => {
|
|||
},
|
||||
propsData,
|
||||
directives: { GlTooltip: createMockDirective('gl-tooltip') },
|
||||
provide: {
|
||||
glFeatures: {
|
||||
containerRegistryImmutableTags: immutableTagsFeatureFlagState,
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
|
|
@ -224,56 +214,6 @@ describe('tags list row', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('immutable tag', () => {
|
||||
const immutableProtection = {
|
||||
minimumAccessLevelForDelete: null,
|
||||
minimumAccessLevelForPush: null,
|
||||
immutable: true,
|
||||
};
|
||||
|
||||
it('hidden if tag.protection does not exists', () => {
|
||||
mountComponent(defaultProps, { immutableTagsFeatureFlagState: true });
|
||||
|
||||
expect(findImmutableBadge().exists()).toBe(false);
|
||||
});
|
||||
|
||||
it('displays if tag.protection.immutable exists', () => {
|
||||
mountComponent(
|
||||
{
|
||||
...defaultProps,
|
||||
tag: {
|
||||
...tag,
|
||||
protection: {
|
||||
...immutableProtection,
|
||||
},
|
||||
},
|
||||
},
|
||||
{ immutableTagsFeatureFlagState: true },
|
||||
);
|
||||
|
||||
expect(findImmutableBadge().text()).toBe('immutable');
|
||||
});
|
||||
|
||||
it('has the correct text for the popover', () => {
|
||||
mountComponent(
|
||||
{
|
||||
...defaultProps,
|
||||
tag: {
|
||||
...tag,
|
||||
protection: {
|
||||
...immutableProtection,
|
||||
},
|
||||
},
|
||||
},
|
||||
{ immutableTagsFeatureFlagState: true },
|
||||
);
|
||||
|
||||
const popoverText = findImmutablePopover().text();
|
||||
|
||||
expect(popoverText).toBe('This container image tag cannot be overwritten or deleted.');
|
||||
});
|
||||
});
|
||||
|
||||
describe('warning icon', () => {
|
||||
it('is normally hidden', () => {
|
||||
mountComponent();
|
||||
|
|
|
|||
|
|
@ -20,18 +20,12 @@ import { containerProtectionTagRuleMutationInput } from '../mock_data';
|
|||
|
||||
Vue.use(VueApollo);
|
||||
|
||||
describe('container Protection Rule Form', () => {
|
||||
describe('Container Protection Rule Form', () => {
|
||||
let wrapper;
|
||||
let fakeApollo;
|
||||
|
||||
const defaultProvidedValues = {
|
||||
projectPath: 'path',
|
||||
glFeatures: {
|
||||
containerRegistryImmutableTags: true,
|
||||
},
|
||||
glAbilities: {
|
||||
createContainerRegistryProtectionImmutableTagRule: true,
|
||||
},
|
||||
};
|
||||
|
||||
const rule =
|
||||
|
|
@ -39,14 +33,8 @@ describe('container Protection Rule Form', () => {
|
|||
.containerProtectionTagRule;
|
||||
|
||||
const findForm = () => wrapper.findComponent(GlForm);
|
||||
const findProtectionTypeProtectedRadio = () =>
|
||||
wrapper.findByRole('radio', { name: /protected/i });
|
||||
const findProtectionTypeImmutableRadio = () =>
|
||||
wrapper.findByRole('radio', { name: /immutable/i });
|
||||
const findTagNamePatternInput = () =>
|
||||
wrapper.findByRole('textbox', { name: /protect container tags matching/i });
|
||||
const findImmutableTagNamePatternInput = () =>
|
||||
wrapper.findByRole('textbox', { name: /apply immutability rule to tags matching/i });
|
||||
const findMinimumAccessLevelForPushSelect = () =>
|
||||
wrapper.findByRole('combobox', { name: /minimum role allowed to push/i });
|
||||
const findMinimumAccessLevelForDeleteSelect = () =>
|
||||
|
|
@ -89,24 +77,6 @@ describe('container Protection Rule Form', () => {
|
|||
};
|
||||
|
||||
describe('form fields', () => {
|
||||
describe('form field "protectionType"', () => {
|
||||
beforeEach(() => {
|
||||
mountComponent();
|
||||
});
|
||||
|
||||
describe('form field "protectionType"', () => {
|
||||
it('protected radio exists and is checked by default', () => {
|
||||
expect(findProtectionTypeProtectedRadio().element.value).toBe('protected');
|
||||
expect(findProtectionTypeProtectedRadio().element.checked).toBe(true);
|
||||
});
|
||||
|
||||
it('immutable radio exists and default value', () => {
|
||||
expect(findProtectionTypeImmutableRadio().element.value).toBe('immutable');
|
||||
expect(findProtectionTypeImmutableRadio().element.checked).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('form field "tagNamePattern"', () => {
|
||||
it('exists', () => {
|
||||
mountComponent();
|
||||
|
|
@ -194,65 +164,6 @@ describe('container Protection Rule Form', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('when form field "protectionType"', () => {
|
||||
beforeEach(() => {
|
||||
mountComponent();
|
||||
});
|
||||
|
||||
describe('is set to immutable', () => {
|
||||
beforeEach(() => {
|
||||
extendedWrapper(findProtectionTypeImmutableRadio()).setChecked();
|
||||
});
|
||||
|
||||
it('form field "tagNamePattern" exists', () => {
|
||||
expect(findImmutableTagNamePatternInput().exists()).toBe(true);
|
||||
});
|
||||
|
||||
it('form field "minimumAccessLevelForPush" is hidden', () => {
|
||||
expect(findMinimumAccessLevelForPushSelect().exists()).toBe(false);
|
||||
});
|
||||
|
||||
it('form field "minimumAccessLevelForDelete" is hidden', () => {
|
||||
expect(findMinimumAccessLevelForDeleteSelect().exists()).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('and form is submitted', () => {
|
||||
const createMutationResolver = jest
|
||||
.fn()
|
||||
.mockResolvedValue(createContainerProtectionTagRuleMutationPayload);
|
||||
const updateMutationResolver = jest
|
||||
.fn()
|
||||
.mockResolvedValue(updateContainerProtectionTagRuleMutationPayload);
|
||||
|
||||
const submitForm = () => {
|
||||
findImmutableTagNamePatternInput().setValue(
|
||||
containerProtectionTagRuleMutationInput.tagNamePattern,
|
||||
);
|
||||
findForm().trigger('submit');
|
||||
return waitForPromises();
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
mountComponentWithApollo({ createMutationResolver, updateMutationResolver });
|
||||
});
|
||||
|
||||
it('dispatches correct apollo mutation', async () => {
|
||||
await extendedWrapper(findProtectionTypeImmutableRadio()).setChecked();
|
||||
|
||||
await submitForm();
|
||||
|
||||
expect(createMutationResolver).toHaveBeenCalledWith({
|
||||
input: {
|
||||
projectPath: 'path',
|
||||
tagNamePattern: containerProtectionTagRuleMutationInput.tagNamePattern,
|
||||
},
|
||||
});
|
||||
expect(updateMutationResolver).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe.each`
|
||||
description | props | submitButtonText
|
||||
${'when form has no prop "rule"'} | ${{}} | ${'Add rule'}
|
||||
|
|
@ -443,39 +354,16 @@ describe('container Protection Rule Form', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('when user does not have ability to create immutable tag rule', () => {
|
||||
beforeEach(() => {
|
||||
mountComponent({
|
||||
provide: {
|
||||
...defaultProvidedValues,
|
||||
glAbilities: {
|
||||
createContainerRegistryProtectionImmutableTagRule: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
describe('when isProtectedTagRuleType prop is passed', () => {
|
||||
it.each`
|
||||
propValue | label | description
|
||||
${true} | ${'Protect container tags matching'} | ${'Tags with names that match this regex pattern are protected. Must be less than 100 characters. What regex patterns are supported?'}
|
||||
${false} | ${'Apply immutability rule to tags matching'} | ${'Tags with names that match this regex pattern are immutable. Must be less than 100 characters. What regex patterns are supported?'}
|
||||
`('test', ({ propValue, label, description }) => {
|
||||
mountComponent({ props: { isProtectedTagRuleType: propValue } });
|
||||
|
||||
it('form field "protectionType" does not exist', () => {
|
||||
expect(findProtectionTypeProtectedRadio().exists()).toBe(false);
|
||||
expect(findProtectionTypeImmutableRadio().exists()).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when `containerRegistryImmutableTags` feature flag is turned off', () => {
|
||||
beforeEach(() => {
|
||||
mountComponent({
|
||||
provide: {
|
||||
...defaultProvidedValues,
|
||||
glFeatures: {
|
||||
containerRegistryImmutableTags: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('form field "protectionType" does not exist', () => {
|
||||
expect(findProtectionTypeProtectedRadio().exists()).toBe(false);
|
||||
expect(findProtectionTypeImmutableRadio().exists()).toBe(false);
|
||||
expect(wrapper.text()).toContain(label);
|
||||
expect(wrapper.text()).toContain(description);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -14,8 +14,6 @@ import containerProtectionTagRuleEmptyRulesQueryPayload from 'test_fixtures/grap
|
|||
import containerProtectionTagRuleMaxRulesQueryPayload from 'test_fixtures/graphql/packages_and_registries/settings/project/graphql/queries/get_container_protection_tag_rules.query.graphql.max_rules.json';
|
||||
import containerProtectionTagRuleNullProjectQueryPayload from 'test_fixtures/graphql/packages_and_registries/settings/project/graphql/queries/get_container_protection_tag_rules.query.graphql.null_project.json';
|
||||
import containerProtectionTagRuleQueryPayload from 'test_fixtures/graphql/packages_and_registries/settings/project/graphql/queries/get_container_protection_tag_rules.query.graphql.json';
|
||||
import containerProtectionImmutableTagRuleQueryPayload from 'test_fixtures/graphql/packages_and_registries/settings/project/graphql/queries/get_container_protection_tag_rules.query.graphql.immutable_rules.json';
|
||||
import containerProtectionImmutableTagRuleMaintainerQueryPayload from 'test_fixtures/graphql/packages_and_registries/settings/project/graphql/queries/get_container_protection_tag_rules.query.graphql.immutable_rules_maintainer.json';
|
||||
import deleteContainerProtectionTagRuleMutationPayload from 'test_fixtures/graphql/packages_and_registries/settings/project/graphql/mutations/delete_container_protection_tag_rule.mutation.graphql.json';
|
||||
import deleteContainerProtectionTagRuleMutationErrorPayload from 'test_fixtures/graphql/packages_and_registries/settings/project/graphql/mutations/delete_container_protection_tag_rule.mutation.graphql.errors.json';
|
||||
|
||||
|
|
@ -56,9 +54,6 @@ describe('ContainerProtectionTagRules', () => {
|
|||
|
||||
const defaultProvidedValues = {
|
||||
projectPath: 'path',
|
||||
glFeatures: {
|
||||
containerRegistryImmutableTags: true,
|
||||
},
|
||||
};
|
||||
|
||||
const { nodes: tagRules } =
|
||||
|
|
@ -104,7 +99,7 @@ describe('ContainerProtectionTagRules', () => {
|
|||
expect(findCrudComponent().props('title')).toBe('Protected container image tags');
|
||||
expect(findCrudComponent().props('toggleText')).toBeNull();
|
||||
expect(findCrudComponent().props('description')).toBe(
|
||||
'Set up rules to protect container image tags from unauthorized changes or make them permanently immutable. Protection rules are checked first, followed by immutable rules. You can add up to 5 protection rules per project.',
|
||||
'When a container image tag is protected, only certain user roles can create, update, and delete the protected tag, which helps to prevent unauthorized changes. You can add up to 5 protection rules per project.',
|
||||
);
|
||||
});
|
||||
|
||||
|
|
@ -212,14 +207,9 @@ describe('ContainerProtectionTagRules', () => {
|
|||
await waitForPromises();
|
||||
});
|
||||
|
||||
it('contains container protection tag rules and `protected` badge', () => {
|
||||
it('contains container protection tag rules', () => {
|
||||
tagRules.forEach((protectionRule, i) => {
|
||||
expect(findTableRowCell(i, 0).findByTestId('tag-name-pattern').text()).toBe(
|
||||
protectionRule.tagNamePattern,
|
||||
);
|
||||
expect(findTableRowCell(i, 0).findByTestId('tag-rule-type-badge').text()).toBe(
|
||||
'protected',
|
||||
);
|
||||
expect(findTableRowCell(i, 0).text()).toBe(protectionRule.tagNamePattern);
|
||||
expect(findTableRowCell(i, 1).text()).toBe(
|
||||
MinimumAccessLevelText[protectionRule.minimumAccessLevelForPush],
|
||||
);
|
||||
|
|
@ -258,107 +248,6 @@ describe('ContainerProtectionTagRules', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('table rows for immutable rules', () => {
|
||||
beforeEach(async () => {
|
||||
createComponent({
|
||||
mountFn: mountExtended,
|
||||
containerProtectionTagRuleQueryResolver: jest
|
||||
.fn()
|
||||
.mockResolvedValue(containerProtectionImmutableTagRuleQueryPayload),
|
||||
});
|
||||
|
||||
await waitForPromises();
|
||||
});
|
||||
|
||||
it('contains immutable container protection tag rules and `immutable` badge', () => {
|
||||
tagRules.forEach((protectionRule, i) => {
|
||||
expect(findTableRowCell(i, 0).findByTestId('tag-name-pattern').text()).toBe(
|
||||
protectionRule.tagNamePattern,
|
||||
);
|
||||
expect(findTableRowCell(i, 0).findByTestId('tag-rule-type-badge').text()).toBe(
|
||||
'immutable',
|
||||
);
|
||||
expect(findTableRowCell(i, 1).text()).toBe('');
|
||||
expect(findTableRowCell(i, 2).text()).toBe('');
|
||||
});
|
||||
});
|
||||
|
||||
describe('column "rowActions"', () => {
|
||||
it('does not show Edit button', () => {
|
||||
expect(findTableRowButtonEdit(0).exists()).toBe(false);
|
||||
});
|
||||
|
||||
describe('Delete button', () => {
|
||||
it('exists in table', () => {
|
||||
expect(findTableRowButtonDelete(0).exists()).toBe(true);
|
||||
});
|
||||
|
||||
describe('when button is clicked', () => {
|
||||
beforeEach(async () => {
|
||||
await findTableRowButtonDelete(0).trigger('click');
|
||||
});
|
||||
|
||||
it('renders the "delete container protection rule" confirmation modal', () => {
|
||||
const modalId = getBinding(findTableRowButtonDelete(0).element, 'gl-modal');
|
||||
|
||||
expect(findModal().props('modal-id')).toBe(modalId);
|
||||
expect(findModal().props('title')).toBe('Delete protection rule');
|
||||
expect(findModal().text()).toBe(
|
||||
'Are you sure you want to delete the protected container tags rule v.+?',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when user does not have permission', () => {
|
||||
beforeEach(async () => {
|
||||
createComponent({
|
||||
mountFn: mountExtended,
|
||||
containerProtectionTagRuleQueryResolver: jest
|
||||
.fn()
|
||||
.mockResolvedValue(containerProtectionImmutableTagRuleMaintainerQueryPayload),
|
||||
});
|
||||
|
||||
await waitForPromises();
|
||||
});
|
||||
|
||||
it('does not exist in table', () => {
|
||||
expect(findTableRowButtonDelete(0).exists()).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when feature flag `containerRegistryImmutableTags` is disabled', () => {
|
||||
describe('table rows', () => {
|
||||
beforeEach(async () => {
|
||||
createComponent({
|
||||
mountFn: mountExtended,
|
||||
provide: {
|
||||
...defaultProvidedValues,
|
||||
glFeatures: {
|
||||
containerRegistryImmutableTags: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await waitForPromises();
|
||||
});
|
||||
|
||||
it('contains container protection tag rules', () => {
|
||||
tagRules.forEach((protectionRule, i) => {
|
||||
expect(findTableRowCell(i, 0).text()).toBe(protectionRule.tagNamePattern);
|
||||
expect(findTableRowCell(i, 1).text()).toBe(
|
||||
MinimumAccessLevelText[protectionRule.minimumAccessLevelForPush],
|
||||
);
|
||||
expect(findTableRowCell(i, 2).text()).toBe(
|
||||
MinimumAccessLevelText[protectionRule.minimumAccessLevelForDelete],
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('modal "confirmation for delete action"', () => {
|
||||
const createComponentAndClickButtonDeleteInTableRow = async ({
|
||||
tableRowIndex = 0,
|
||||
|
|
@ -637,25 +526,4 @@ describe('ContainerProtectionTagRules', () => {
|
|||
expect(findAlert().text()).toBe('error');
|
||||
});
|
||||
});
|
||||
|
||||
describe('when feature flag `containerRegistryImmutableTags` is disabled', () => {
|
||||
describe('layout', () => {
|
||||
beforeEach(() => {
|
||||
createComponent({
|
||||
provide: {
|
||||
...defaultProvidedValues,
|
||||
glFeatures: {
|
||||
containerRegistryImmutableTags: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('renders card component with different description', () => {
|
||||
expect(findCrudComponent().props('description')).toBe(
|
||||
'When a container image tag is protected, only certain user roles can create, update, and delete the protected tag, which helps to prevent unauthorized changes. You can add up to 5 protection rules per project.',
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import SettingsBlock from '~/vue_shared/components/settings/settings_block.vue';
|
|||
import ContainerRegistrySection from '~/packages_and_registries/settings/project/components/container_registry_section.vue';
|
||||
import ContainerExpirationPolicy from '~/packages_and_registries/settings/project/components/container_expiration_policy.vue';
|
||||
import ContainerProtectionRepositoryRules from '~/packages_and_registries/settings/project/components/container_protection_repository_rules.vue';
|
||||
import ContainerProtectionTagRules from '~/packages_and_registries/settings/project/components/container_protection_tag_rules.vue';
|
||||
import ContainerProtectionTagRules from 'ee_else_ce/packages_and_registries/settings/project/components/container_protection_tag_rules.vue';
|
||||
|
||||
describe('Container registry project settings section', () => {
|
||||
let wrapper;
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ GEM
|
|||
google-protobuf (3.25.7)
|
||||
googleapis-common-protos-types (1.20.0)
|
||||
google-protobuf (>= 3.18, < 5.a)
|
||||
grpc (1.71.0)
|
||||
grpc (1.72.0)
|
||||
google-protobuf (>= 3.25, < 5.0)
|
||||
googleapis-common-protos-types (~> 1.0)
|
||||
i18n (1.14.7)
|
||||
|
|
@ -63,7 +63,7 @@ GEM
|
|||
rouge
|
||||
unparser
|
||||
racc (1.8.1)
|
||||
rack (3.1.13)
|
||||
rack (3.1.14)
|
||||
rainbow (3.1.1)
|
||||
regexp_parser (2.10.0)
|
||||
rouge (4.5.2)
|
||||
|
|
@ -76,7 +76,7 @@ GEM
|
|||
rspec-expectations (3.13.4)
|
||||
diff-lcs (>= 1.2.0, < 2.0)
|
||||
rspec-support (~> 3.13.0)
|
||||
rspec-mocks (3.13.3)
|
||||
rspec-mocks (3.13.4)
|
||||
diff-lcs (>= 1.2.0, < 2.0)
|
||||
rspec-support (~> 3.13.0)
|
||||
rspec-parameterized (1.0.2)
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
81624de3633fa2fab3737bc438683966649e5568
|
||||
20a0104c6e8d836906e15a68f35667856f2ceb68
|
||||
|
|
|
|||
|
|
@ -4,5 +4,4 @@ require_relative "topology_service/version"
|
|||
|
||||
require "proto/claim_service_services_pb"
|
||||
require "proto/classify_service_services_pb"
|
||||
require "proto/health_service_services_pb"
|
||||
require "proto/cell_service_services_pb"
|
||||
|
|
|
|||
|
|
@ -1,30 +0,0 @@
|
|||
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
# source: proto/health_service.proto
|
||||
|
||||
require 'google/protobuf'
|
||||
|
||||
require 'google/api/annotations_pb'
|
||||
|
||||
Google::Protobuf::DescriptorPool.generated_pool.build do
|
||||
add_file("proto/health_service.proto", :syntax => :proto3) do
|
||||
add_message "gitlab.cells.topology_service.ReadinessProbeRequest" do
|
||||
end
|
||||
add_message "gitlab.cells.topology_service.ReadinessProbeResponse" do
|
||||
end
|
||||
add_message "gitlab.cells.topology_service.LivenessProbeRequest" do
|
||||
end
|
||||
add_message "gitlab.cells.topology_service.LivenessProbeResponse" do
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
module Gitlab
|
||||
module Cells
|
||||
module TopologyService
|
||||
ReadinessProbeRequest = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("gitlab.cells.topology_service.ReadinessProbeRequest").msgclass
|
||||
ReadinessProbeResponse = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("gitlab.cells.topology_service.ReadinessProbeResponse").msgclass
|
||||
LivenessProbeRequest = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("gitlab.cells.topology_service.LivenessProbeRequest").msgclass
|
||||
LivenessProbeResponse = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("gitlab.cells.topology_service.LivenessProbeResponse").msgclass
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
# Source: proto/health_service.proto for package 'Gitlab.Cells.TopologyService'
|
||||
|
||||
require 'grpc'
|
||||
require 'proto/health_service_pb'
|
||||
|
||||
module Gitlab
|
||||
module Cells
|
||||
module TopologyService
|
||||
module HealthService
|
||||
# Public read-only service
|
||||
class Service
|
||||
|
||||
include ::GRPC::GenericService
|
||||
|
||||
self.marshal_class_method = :encode
|
||||
self.unmarshal_class_method = :decode
|
||||
self.service_name = 'gitlab.cells.topology_service.HealthService'
|
||||
|
||||
# (Lightweight) Used for checking if application is ready to accept requests
|
||||
rpc :ReadinessProbe, ::Gitlab::Cells::TopologyService::ReadinessProbeRequest, ::Gitlab::Cells::TopologyService::ReadinessProbeResponse
|
||||
# (Lightweight) Used for checking if application is alive, or whether it should be restarted
|
||||
rpc :LivenessProbe, ::Gitlab::Cells::TopologyService::LivenessProbeRequest, ::Gitlab::Cells::TopologyService::LivenessProbeResponse
|
||||
end
|
||||
|
||||
Stub = Service.rpc_stub_class
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
Loading…
Reference in New Issue