Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
85209dc3e4
commit
4df10dee37
|
|
@ -99,10 +99,15 @@ workflow:
|
|||
# Example of project bot usernames (the format changed over time):
|
||||
# - project_278964_bot2
|
||||
# - project_278964_bot_7fb4d1cca8242cb399a0b8f483783120
|
||||
#
|
||||
# We set the DOCKER_AUTH_CONFIG variable to authenticate to Docker Hub to not be impacted by rate limitations
|
||||
# We don't set DOCKER_AUTH_CONFIG as a CI/CD group/project variable, because it would then have higher precedence than .gitlab-ci.yml,
|
||||
# and we would always reconfigure the docker deamon for any CI/CD pipeline.
|
||||
- if: '$CI_MERGE_REQUEST_IID && $GITLAB_USER_LOGIN =~ /project_\d+_bot/'
|
||||
variables:
|
||||
<<: [*next-ruby-variables, *default-merge-request-variables]
|
||||
GITLAB_DEPENDENCY_PROXY_ADDRESS: ""
|
||||
DOCKER_AUTH_CONFIG: "${DOCKER_AUTH_CONFIG_OVERRIDE}"
|
||||
PIPELINE_NAME: 'Ruby $RUBY_VERSION MR (triggered by a project token)'
|
||||
- <<: *if-merge-request-security-canonical-sync
|
||||
variables:
|
||||
|
|
@ -145,10 +150,15 @@ workflow:
|
|||
# Example of project bot usernames (the format changed over time):
|
||||
# - project_278964_bot2
|
||||
# - project_278964_bot_7fb4d1cca8242cb399a0b8f483783120
|
||||
#
|
||||
# We set the DOCKER_AUTH_CONFIG variable to authenticate to Docker Hub to not be impacted by rate limitations
|
||||
# We don't set DOCKER_AUTH_CONFIG as a CI/CD group/project variable, because it would then have higher precedence than .gitlab-ci.yml,
|
||||
# and we would always reconfigure the docker deamon for any CI/CD pipeline.
|
||||
- if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH && $GITLAB_USER_LOGIN =~ /project_\d+_bot/'
|
||||
variables:
|
||||
<<: [*default-ruby-variables, *default-branch-pipeline-failure-variables]
|
||||
GITLAB_DEPENDENCY_PROXY_ADDRESS: ""
|
||||
DOCKER_AUTH_CONFIG: "${DOCKER_AUTH_CONFIG_OVERRIDE}"
|
||||
PIPELINE_NAME: 'Ruby $RUBY_VERSION $CI_COMMIT_BRANCH branch (triggered by a project token)'
|
||||
# For `$CI_DEFAULT_BRANCH` from wider community contributors, we don't want to run any pipelines on pushes,
|
||||
# because normally we want to run merge request pipelines and scheduled pipelines, not for repository synchronization.
|
||||
|
|
|
|||
|
|
@ -11,4 +11,3 @@ InternalAffairs/NumblockHandler:
|
|||
- 'rubocop/cop/migration/with_lock_retries_disallowed_method.rb'
|
||||
- 'rubocop/cop/qa/ambiguous_page_object_name.rb'
|
||||
- 'rubocop/cop/rspec/feature_category.rb'
|
||||
- 'rubocop/cop/rspec/shared_groups_metadata.rb'
|
||||
|
|
|
|||
|
|
@ -15,6 +15,5 @@ InternalAffairs/RedundantMessageArgument:
|
|||
- 'rubocop/cop/migration/prevent_single_statement_with_disable_ddl_transaction.rb'
|
||||
- 'rubocop/cop/migration/schema_addition_methods_no_post.rb'
|
||||
- 'rubocop/cop/redis_queue_usage.rb'
|
||||
- 'rubocop/cop/rspec/shared_groups_metadata.rb'
|
||||
- 'rubocop/cop/sidekiq_api_usage.rb'
|
||||
- 'rubocop/cop/sidekiq_redis_call.rb'
|
||||
|
|
|
|||
|
|
@ -1,5 +0,0 @@
|
|||
---
|
||||
RSpec/SharedGroupsMetadata:
|
||||
Exclude:
|
||||
- 'spec/lib/gitlab/ci/config/entry/retry_spec.rb'
|
||||
- 'spec/lib/gitlab/git/merge_base_spec.rb'
|
||||
|
|
@ -11,6 +11,81 @@ export const FEED_TOKEN = 'feedToken';
|
|||
export const INCOMING_EMAIL_TOKEN = 'incomingEmailToken';
|
||||
export const STATIC_OBJECT_TOKEN = 'staticObjectToken';
|
||||
|
||||
export const FILTER_OPTIONS = [
|
||||
{
|
||||
icon: 'status',
|
||||
title: s__('AccessTokens|State'),
|
||||
type: 'state',
|
||||
token: GlFilteredSearchToken,
|
||||
operators: OPERATORS_IS,
|
||||
unique: true,
|
||||
options: [
|
||||
{ value: 'active', title: s__('AccessTokens|Active') },
|
||||
{ value: 'inactive', title: s__('AccessTokens|Inactive') },
|
||||
],
|
||||
},
|
||||
{
|
||||
icon: 'remove',
|
||||
title: s__('AccessTokens|Revoked'),
|
||||
type: 'revoked',
|
||||
token: GlFilteredSearchToken,
|
||||
operators: OPERATORS_IS,
|
||||
unique: true,
|
||||
options: [
|
||||
{ value: 'true', title: __('Yes') },
|
||||
{ value: 'false', title: __('No') },
|
||||
],
|
||||
},
|
||||
{
|
||||
icon: 'history',
|
||||
title: __('Created date'),
|
||||
type: 'created',
|
||||
token: DateToken,
|
||||
operators: OPERATORS_AFTER_BEFORE,
|
||||
unique: true,
|
||||
},
|
||||
{
|
||||
icon: 'history',
|
||||
title: __('Expiration date'),
|
||||
type: 'expires',
|
||||
token: DateToken,
|
||||
operators: OPERATORS_AFTER_BEFORE,
|
||||
unique: true,
|
||||
},
|
||||
{
|
||||
icon: 'history',
|
||||
title: __('Last used date'),
|
||||
type: 'last_used',
|
||||
token: DateToken,
|
||||
operators: OPERATORS_AFTER_BEFORE,
|
||||
unique: true,
|
||||
},
|
||||
];
|
||||
|
||||
export const FILTER_OPTIONS_CREDENTIALS_INVENTORY = [
|
||||
{
|
||||
icon: 'key',
|
||||
title: s__('CredentialsInventory|Type'),
|
||||
type: 'filter',
|
||||
token: GlFilteredSearchToken,
|
||||
operators: OPERATORS_IS,
|
||||
unique: true,
|
||||
options: [
|
||||
{
|
||||
value: 'personal_access_tokens',
|
||||
title: s__('CredentialsInventory|Personal access tokens'),
|
||||
},
|
||||
{ value: 'ssh_keys', title: s__('CredentialsInventory|SSH keys') },
|
||||
{
|
||||
value: 'resource_access_tokens',
|
||||
title: s__('CredentialsInventory|Project and group access tokens'),
|
||||
},
|
||||
{ value: 'gpg_keys', title: s__('CredentialsInventory|GPG keys') },
|
||||
],
|
||||
},
|
||||
...FILTER_OPTIONS,
|
||||
];
|
||||
|
||||
export const DEFAULT_SORT = { value: 'expires', isAsc: true };
|
||||
|
||||
export const SORT_OPTIONS = [
|
||||
|
|
@ -48,77 +123,6 @@ export const SORT_OPTIONS = [
|
|||
},
|
||||
];
|
||||
|
||||
export const TOKENS = [
|
||||
{
|
||||
icon: 'key',
|
||||
title: s__('CredentialsInventory|Type'),
|
||||
type: 'filter',
|
||||
token: GlFilteredSearchToken,
|
||||
operators: OPERATORS_IS,
|
||||
unique: true,
|
||||
options: [
|
||||
{
|
||||
value: 'personal_access_tokens',
|
||||
title: s__('CredentialsInventory|Personal access tokens'),
|
||||
},
|
||||
{ value: 'ssh_keys', title: s__('CredentialsInventory|SSH keys') },
|
||||
{
|
||||
value: 'resource_access_tokens',
|
||||
title: s__('CredentialsInventory|Project and group access tokens'),
|
||||
},
|
||||
{ value: 'gpg_keys', title: s__('CredentialsInventory|GPG keys') },
|
||||
],
|
||||
},
|
||||
{
|
||||
icon: 'status',
|
||||
title: s__('CredentialsInventory|State'),
|
||||
type: 'state',
|
||||
token: GlFilteredSearchToken,
|
||||
operators: OPERATORS_IS,
|
||||
unique: true,
|
||||
options: [
|
||||
{ value: 'active', title: s__('CredentialsInventory|Active') },
|
||||
{ value: 'inactive', title: s__('CredentialsInventory|Inactive') },
|
||||
],
|
||||
},
|
||||
{
|
||||
icon: 'remove',
|
||||
title: s__('CredentialsInventory|Revoked'),
|
||||
type: 'revoked',
|
||||
token: GlFilteredSearchToken,
|
||||
operators: OPERATORS_IS,
|
||||
unique: true,
|
||||
options: [
|
||||
{ value: 'true', title: __('Yes') },
|
||||
{ value: 'false', title: __('No') },
|
||||
],
|
||||
},
|
||||
{
|
||||
icon: 'history',
|
||||
title: s__('CredentialsInventory|Created date'),
|
||||
type: 'created',
|
||||
token: DateToken,
|
||||
operators: OPERATORS_AFTER_BEFORE,
|
||||
unique: true,
|
||||
},
|
||||
{
|
||||
icon: 'history',
|
||||
title: s__('CredentialsInventory|Expiration date'),
|
||||
type: 'expires',
|
||||
token: DateToken,
|
||||
operators: OPERATORS_AFTER_BEFORE,
|
||||
unique: true,
|
||||
},
|
||||
{
|
||||
icon: 'history',
|
||||
title: s__('CredentialsInventory|Last used date'),
|
||||
type: 'last_used',
|
||||
token: DateToken,
|
||||
operators: OPERATORS_AFTER_BEFORE,
|
||||
unique: true,
|
||||
},
|
||||
];
|
||||
|
||||
export const STATISTICS_CONFIG = [
|
||||
{
|
||||
title: s__('AccessTokens|Active tokens'),
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<script>
|
||||
import { GlFilteredSearch, GlSorting } from '@gitlab/ui';
|
||||
import { SORT_OPTIONS, TOKENS } from '~/access_tokens/constants';
|
||||
import { SORT_OPTIONS, FILTER_OPTIONS_CREDENTIALS_INVENTORY } from '~/access_tokens/constants';
|
||||
import { initializeValuesFromQuery, goTo } from '../utils';
|
||||
|
||||
export default {
|
||||
|
|
@ -19,10 +19,10 @@ export default {
|
|||
availableTokens() {
|
||||
// Once SSH or GPG key is selected, discard the rest of the tokens
|
||||
if (this.hasKey) {
|
||||
return TOKENS.filter(({ type }) => type === 'filter');
|
||||
return FILTER_OPTIONS_CREDENTIALS_INVENTORY.filter(({ type }) => type === 'filter');
|
||||
}
|
||||
|
||||
return TOKENS;
|
||||
return FILTER_OPTIONS_CREDENTIALS_INVENTORY;
|
||||
},
|
||||
hasKey() {
|
||||
return this.tokens.some(
|
||||
|
|
|
|||
|
|
@ -3,7 +3,11 @@ import {
|
|||
OPERATORS_BEFORE,
|
||||
OPERATORS_AFTER,
|
||||
} from '~/vue_shared/components/filtered_search_bar/constants';
|
||||
import { DEFAULT_SORT, SORT_OPTIONS, TOKENS } from '~/access_tokens/constants';
|
||||
import {
|
||||
DEFAULT_SORT,
|
||||
SORT_OPTIONS,
|
||||
FILTER_OPTIONS_CREDENTIALS_INVENTORY,
|
||||
} from '~/access_tokens/constants';
|
||||
|
||||
/**
|
||||
* @param {Object<string, string>} filters
|
||||
|
|
@ -26,7 +30,7 @@ function initializeFilters(filters, search) {
|
|||
});
|
||||
} else {
|
||||
try {
|
||||
const { operators } = TOKENS.find(({ options }) =>
|
||||
const { operators } = FILTER_OPTIONS_CREDENTIALS_INVENTORY.find(({ options }) =>
|
||||
options.some((option) => option.value === value),
|
||||
);
|
||||
tokens.push({
|
||||
|
|
|
|||
|
|
@ -2,16 +2,12 @@
|
|||
|
||||
module Ci
|
||||
class BuildNameFinder
|
||||
MAX_PER_PAGE = 100
|
||||
|
||||
def initialize(relation:, name:, project:, params: {})
|
||||
def initialize(relation:, name:, project:)
|
||||
raise ArgumentError, 'Only Ci::Builds are name searchable' unless relation.klass == Ci::Build
|
||||
raise ArgumentError, "Offset Pagination is not supported" if relation.offset_value.present?
|
||||
|
||||
@relation = relation
|
||||
@name = name
|
||||
@project = project
|
||||
@params = params
|
||||
end
|
||||
|
||||
def execute
|
||||
|
|
@ -22,51 +18,19 @@ module Ci
|
|||
|
||||
private
|
||||
|
||||
attr_reader :relation, :name, :project, :params
|
||||
attr_reader :relation, :name, :project
|
||||
|
||||
def limited_name_search_terms
|
||||
name.truncate_words(5, omission: '')
|
||||
end
|
||||
|
||||
# rubocop: disable CodeReuse/ActiveRecord -- Need specialized queries for database optimizations
|
||||
def filter_by_name(build_relation)
|
||||
build_name_relation = generate_build_name_relation(apply_pagination_cursor(build_relation))
|
||||
|
||||
main_build_relation =
|
||||
Ci::Build.where("(id, partition_id) IN (?)", build_name_relation.select(:build_id, :partition_id))
|
||||
|
||||
# Some callers (graphQL) will invert the ordering based on the relation and the params (asc)
|
||||
if params[:invert_ordering]
|
||||
main_build_relation.reorder(id: :desc)
|
||||
else
|
||||
apply_pagination_order(main_build_relation, :id)
|
||||
end
|
||||
end
|
||||
|
||||
def generate_build_name_relation(build_subrelation)
|
||||
build_name_relation = Ci::BuildName
|
||||
.where(project_id: project.id)
|
||||
.pg_full_text_search_in_model(name)
|
||||
.pg_full_text_search_in_model(limited_name_search_terms)
|
||||
|
||||
build_name_relation = apply_pagination_order(build_name_relation, :build_id)
|
||||
build_name_relation
|
||||
.where("(build_id, partition_id) IN (?)", build_subrelation.select(:id, :partition_id))
|
||||
.limit(MAX_PER_PAGE + 1)
|
||||
end
|
||||
|
||||
# Ci::Builds main ordering is ID DESC which makes ordering reversed
|
||||
def apply_pagination_cursor(relation)
|
||||
return relation if params[:after].blank? && params[:before].blank?
|
||||
|
||||
if params[:after]
|
||||
relation.id_before(Integer(params[:after]))
|
||||
else
|
||||
relation.id_after(Integer(params[:before]))
|
||||
end
|
||||
end
|
||||
|
||||
def apply_pagination_order(relation, column)
|
||||
if params[:asc].present?
|
||||
relation.reorder(column => :asc)
|
||||
else
|
||||
relation.reorder(column => :desc)
|
||||
end
|
||||
build_relation.where("(id, partition_id) IN (?)", build_name_relation.select(:build_id, :partition_id))
|
||||
end
|
||||
# rubocop: enable CodeReuse/ActiveRecord
|
||||
end
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ module Resolvers
|
|||
|
||||
argument :name, GraphQL::Types::String,
|
||||
required: false,
|
||||
experiment: { milestone: '17.1' },
|
||||
experiment: { milestone: '17.11' },
|
||||
description: 'Filter jobs by name.'
|
||||
|
||||
argument :sources, [::Types::Ci::JobSourceEnum],
|
||||
|
|
@ -47,11 +47,7 @@ module Resolvers
|
|||
jobs = ::Ci::BuildNameFinder.new(
|
||||
relation: jobs,
|
||||
name: args[:name],
|
||||
project: project,
|
||||
params: {
|
||||
before: decode_cursor(args[:before]), after: decode_cursor(args[:after]),
|
||||
asc: args[:last].present?, invert_ordering: true
|
||||
}
|
||||
project: project
|
||||
).execute
|
||||
elsif filter_by_sources
|
||||
jobs = ::Ci::BuildSourceFinder.new(
|
||||
|
|
@ -68,14 +64,6 @@ module Resolvers
|
|||
|
||||
private
|
||||
|
||||
def decode_cursor(encoded)
|
||||
return unless encoded.present?
|
||||
|
||||
Gitlab::Json.parse(context.schema.cursor_encoder.decode(encoded, nonce: true))&.fetch('id')
|
||||
rescue JSON::ParserError
|
||||
raise Gitlab::Graphql::Errors::ArgumentError, "Please provide a valid cursor"
|
||||
end
|
||||
|
||||
def preloads
|
||||
{
|
||||
previous_stage_jobs_or_needs: [:needs, :pipeline],
|
||||
|
|
|
|||
|
|
@ -23,6 +23,13 @@ module Types
|
|||
'Minimum GitLab access level required to push container image tags to the container repository. ' \
|
||||
'Valid values include `MAINTAINER`, `OWNER`, or `ADMIN`. ' \
|
||||
'If the value is `nil`, no access level can push tags. '
|
||||
|
||||
field :immutable,
|
||||
GraphQL::Types::Boolean,
|
||||
null: false,
|
||||
method: :immutable?,
|
||||
experiment: { milestone: '17.11' },
|
||||
description: 'Returns true when tag rule is for tag immutability. Otherwise, false.'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,35 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Namespaces
|
||||
module AdjournedDeletable
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
def adjourned_deletion?
|
||||
adjourned_deletion_configured?
|
||||
end
|
||||
|
||||
def adjourned_deletion_configured?
|
||||
return false unless Feature.enabled?(:downtier_delayed_deletion, :instance, type: :wip)
|
||||
return false if try(:personal?)
|
||||
|
||||
::Gitlab::CurrentSettings.deletion_adjourned_period > 0
|
||||
end
|
||||
|
||||
def marked_for_deletion?
|
||||
return false unless Feature.enabled?(:downtier_delayed_deletion, :instance, type: :wip)
|
||||
|
||||
marked_for_deletion_on.present?
|
||||
end
|
||||
|
||||
def self_or_ancestor_marked_for_deletion
|
||||
return unless Feature.enabled?(:downtier_delayed_deletion, :instance, type: :wip)
|
||||
return self if marked_for_deletion?
|
||||
|
||||
ancestors(hierarchy_order: :asc).joins(:deletion_schedule).first
|
||||
end
|
||||
|
||||
def permanent_deletion_date(date)
|
||||
date + ::Gitlab::CurrentSettings.deletion_adjourned_period.days
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -22,6 +22,7 @@ class Group < Namespace
|
|||
include Importable
|
||||
include IdInOrdered
|
||||
include Members::Enumerable
|
||||
include Namespaces::AdjournedDeletable
|
||||
|
||||
extend ::Gitlab::Utils::Override
|
||||
|
||||
|
|
@ -935,10 +936,6 @@ class Group < Namespace
|
|||
import_export_upload_by_user(user)&.export_archive_exists?
|
||||
end
|
||||
|
||||
def adjourned_deletion?
|
||||
false
|
||||
end
|
||||
|
||||
def execute_hooks(data, hooks_scope)
|
||||
# NOOP
|
||||
# TODO: group hooks https://gitlab.com/gitlab-org/gitlab/-/issues/216904
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@ class Project < ApplicationRecord
|
|||
include Importable
|
||||
include SafelyChangeColumnDefault
|
||||
include Todoable
|
||||
include Namespaces::AdjournedDeletable
|
||||
|
||||
columns_changing_default :organization_id
|
||||
|
||||
|
|
|
|||
|
|
@ -135,7 +135,7 @@ class WorkItem < Issue
|
|||
end
|
||||
|
||||
def non_widgets
|
||||
[:related_vulnerabilities, :pending_escalations]
|
||||
[:pending_escalations]
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
---
|
||||
name: downtier_delayed_deletion
|
||||
feature_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/526403
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/185850
|
||||
rollout_issue_url:
|
||||
milestone: '17.11'
|
||||
group: group::authorization
|
||||
type: wip
|
||||
default_enabled: false
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
---
|
||||
data_category: optional
|
||||
key_path: product_usage_data_enabled
|
||||
description: Whether product usage data is enabled
|
||||
product_group: analytics_instrumentation
|
||||
value_type: boolean
|
||||
status: active
|
||||
time_frame: none
|
||||
data_source: system
|
||||
instrumentation_class: GitlabSettingsMetric
|
||||
options:
|
||||
setting_method: product_usage_data_enabled?
|
||||
tiers:
|
||||
- free
|
||||
- premium
|
||||
- ultimate
|
||||
performance_indicator_type: []
|
||||
milestone: "17.11"
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/184899
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
- title: "Pipelines API cancel endpoint returns error for non-cancelable pipelines"
|
||||
# The milestones for the deprecation announcement, and the removal.
|
||||
removal_milestone: "18.0"
|
||||
announcement_milestone: "17.6"
|
||||
# Change breaking_change to false if needed.
|
||||
breaking_change: true
|
||||
window: 1 # Can be [1, 2, or 3] - The window when the breaking change will be deployed on GitLab.com
|
||||
# The stage and GitLab username of the person reporting the change,
|
||||
# and a link to the deprecation issue
|
||||
reporter: rutshah
|
||||
stage: verify
|
||||
issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/414963
|
||||
# Use the impact calculator https://gitlab-com.gitlab.io/gl-infra/breaking-change-impact-calculator/?
|
||||
impact: high # Can be one of: [critical, high, medium, low]
|
||||
scope: instance, group, project # Can be one or a combination of: [instance, group, project]
|
||||
resolution_role: Owner # Can be one of: [Admin, Owner, Maintainer, Developer]
|
||||
manual_task: true # Can be true or false. Use this to denote whether a resolution action must be performed manually (true), or if it can be automated by using the API or other automation (false).
|
||||
body: | # (required) Don't change this line.
|
||||
The Pipelines API cancel endpoint [`POST /projects/:id/pipelines/:pipeline_id/cancel`](https://docs.gitlab.com/api/pipelines/#cancel-a-pipelines-jobs)
|
||||
returns a `200` success response regardless of whether a pipeline can be canceled.
|
||||
Starting in GitLab 18.0, the endpoint will return a `422 Unprocessable Entity` error when a pipeline cannot be canceled.
|
||||
Update your API integration to handle the `422` status code when making pipeline cancellation requests.
|
||||
|
||||
# ==============================
|
||||
# OPTIONAL END-OF-SUPPORT FIELDS
|
||||
# ==============================
|
||||
#
|
||||
# If an End of Support period applies:
|
||||
# 1) Share this announcement in the `#spt_managers` Support channel in Slack
|
||||
# 2) Mention `@gitlab-com/support` in this merge request.
|
||||
#
|
||||
# When support for this feature ends, in XX.YY milestone format.
|
||||
end_of_support_milestone:
|
||||
# Array of tiers the feature is currently available to,
|
||||
# like [Free, Silver, Gold, Core, Premium, Ultimate]
|
||||
tiers:
|
||||
# Links to documentation and thumbnail image
|
||||
documentation_url:
|
||||
image_url:
|
||||
# Use the youtube thumbnail URL with the structure of https://img.youtube.com/vi/UNIQUEID/hqdefault.jpg
|
||||
video_url:
|
||||
|
|
@ -17,8 +17,8 @@
|
|||
|
||||
To migrate:
|
||||
|
||||
1. Check your S3 storage backend configuration in the GitLab container registry settings.
|
||||
1. Remove the `v4auth: false` option if it's set.
|
||||
1. Check your [S3 storage backend configuration in the GitLab container registry settings](https://docs.gitlab.com/administration/packages/container_registry/#use-object-storage).
|
||||
1. If `v4auth` is set to `false`, remove the option.
|
||||
1. Verify your existing credentials work with v4 authentication.
|
||||
|
||||
If you encounter any issues after making these changes, try regenerating your AWS credentials.
|
||||
|
|
|
|||
|
|
@ -2,15 +2,15 @@
|
|||
announcement_milestone: "17.9"
|
||||
removal_milestone: "18.0"
|
||||
breaking_change: true
|
||||
impact: # Can be one of: [critical, high, medium, low]
|
||||
scope: # Can be one or a combination of: [instance, group, project]
|
||||
impact: low
|
||||
scope: project
|
||||
reporter: jayswain
|
||||
stage: Software Supply Chain Security
|
||||
issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/509578
|
||||
body: | # (required) Do not modify this line, instead modify the lines below.
|
||||
In GitLab 18.0, CI/CD job tokens are moving to the JWT standard by default. All new projects will use this standard, but existing projects will continue to use the legacy format. Existing projects can switch to the JWT standard before the GitLab 18.0 release. If you experience issues, you can still [use the legacy format for your CI/CD tokens](https://docs.gitlab.com/ci/jobs/ci_job_token#use-legacy-format-for-cicd-tokens) until the GitLab 18.3 release.
|
||||
In GitLab 18.0, CI/CD job tokens will switch from a string token format to the JWT token format. This changes impacts new and existing CI/CD job tokens in all projects. If you experience issues, you can still [use the legacy format for your CI/CD tokens](https://docs.gitlab.com/ci/jobs/ci_job_token#use-legacy-format-for-cicd-tokens) until the GitLab 19.0 release.
|
||||
|
||||
In GitLab 18.3, all CI/CD job tokens must use the JWT standard. Before this release, you can temporarily revert your tokens back to the legacy job token format.
|
||||
In GitLab 19.0, all CI/CD job tokens must use the JWT standard. Before this release, you can temporarily revert your tokens back to the legacy job token format.
|
||||
|
||||
Known issues:
|
||||
|
||||
|
|
|
|||
|
|
@ -23597,6 +23597,7 @@ Represents the most restrictive permissions for a container image tag.
|
|||
|
||||
| Name | Type | Description |
|
||||
| ---- | ---- | ----------- |
|
||||
| <a id="containerprotectionaccesslevelimmutable"></a>`immutable` {{< icon name="warning-solid" >}} | [`Boolean!`](#boolean) | **Introduced** in GitLab 17.11. **Status**: Experiment. Returns true when tag rule is for tag immutability. Otherwise, false. |
|
||||
| <a id="containerprotectionaccesslevelminimumaccesslevelfordelete"></a>`minimumAccessLevelForDelete` {{< icon name="warning-solid" >}} | [`ContainerProtectionTagRuleAccessLevel`](#containerprotectiontagruleaccesslevel) | **Introduced** in GitLab 17.8. **Status**: Experiment. Minimum GitLab access level required to delete container image tags from the container repository. Valid values include `MAINTAINER`, `OWNER`, or `ADMIN`. If the value is `nil`, no access level can delete tags. |
|
||||
| <a id="containerprotectionaccesslevelminimumaccesslevelforpush"></a>`minimumAccessLevelForPush` {{< icon name="warning-solid" >}} | [`ContainerProtectionTagRuleAccessLevel`](#containerprotectiontagruleaccesslevel) | **Introduced** in GitLab 17.8. **Status**: Experiment. Minimum GitLab access level required to push container image tags to the container repository. Valid values include `MAINTAINER`, `OWNER`, or `ADMIN`. If the value is `nil`, no access level can push tags. |
|
||||
|
||||
|
|
@ -23622,6 +23623,7 @@ A container repository tag protection rule designed to prevent users with a cert
|
|||
| Name | Type | Description |
|
||||
| ---- | ---- | ----------- |
|
||||
| <a id="containerprotectiontagruleid"></a>`id` {{< icon name="warning-solid" >}} | [`ContainerRegistryProtectionTagRuleID!`](#containerregistryprotectiontagruleid) | **Introduced** in GitLab 17.8. **Status**: Experiment. ID of the container repository tag protection rule. |
|
||||
| <a id="containerprotectiontagruleimmutable"></a>`immutable` {{< icon name="warning-solid" >}} | [`Boolean!`](#boolean) | **Introduced** in GitLab 17.11. **Status**: Experiment. Returns true when tag rule is for tag immutability. Otherwise, false. |
|
||||
| <a id="containerprotectiontagruleminimumaccesslevelfordelete"></a>`minimumAccessLevelForDelete` {{< icon name="warning-solid" >}} | [`ContainerProtectionTagRuleAccessLevel`](#containerprotectiontagruleaccesslevel) | **Introduced** in GitLab 17.8. **Status**: Experiment. Minimum GitLab access level required to delete container image tags from the container repository. Valid values include `MAINTAINER`, `OWNER`, or `ADMIN`. If the value is `nil`, no access level can delete tags. |
|
||||
| <a id="containerprotectiontagruleminimumaccesslevelforpush"></a>`minimumAccessLevelForPush` {{< icon name="warning-solid" >}} | [`ContainerProtectionTagRuleAccessLevel`](#containerprotectiontagruleaccesslevel) | **Introduced** in GitLab 17.8. **Status**: Experiment. Minimum GitLab access level required to push container image tags to the container repository. Valid values include `MAINTAINER`, `OWNER`, or `ADMIN`. If the value is `nil`, no access level can push tags. |
|
||||
| <a id="containerprotectiontagruletagnamepattern"></a>`tagNamePattern` {{< icon name="warning-solid" >}} | [`String!`](#string) | **Introduced** in GitLab 17.8. **Status**: Experiment. The pattern that matches container image tags to protect. For example, `v1.*`. Wildcard character `*` allowed. |
|
||||
|
|
@ -35416,7 +35418,7 @@ four standard [pagination arguments](#pagination-arguments):
|
|||
|
||||
| Name | Type | Description |
|
||||
| ---- | ---- | ----------- |
|
||||
| <a id="projectjobsname"></a>`name` {{< icon name="warning-solid" >}} | [`String`](#string) | **Introduced** in GitLab 17.1. **Status**: Experiment. Filter jobs by name. |
|
||||
| <a id="projectjobsname"></a>`name` {{< icon name="warning-solid" >}} | [`String`](#string) | **Introduced** in GitLab 17.11. **Status**: Experiment. Filter jobs by name. |
|
||||
| <a id="projectjobssources"></a>`sources` {{< icon name="warning-solid" >}} | [`[CiJobSource!]`](#cijobsource) | **Introduced** in GitLab 17.7. **Status**: Experiment. Filter jobs by source. Ignored if 'populate_and_use_build_source_table' feature flag is disabled. |
|
||||
| <a id="projectjobsstatuses"></a>`statuses` | [`[CiJobStatus!]`](#cijobstatus) | Filter jobs by status. |
|
||||
| <a id="projectjobswithartifacts"></a>`withArtifacts` | [`Boolean`](#boolean) | Filter by artifacts presence. |
|
||||
|
|
@ -46567,6 +46569,7 @@ Implementations:
|
|||
|
||||
| Name | Type | Description |
|
||||
| ---- | ---- | ----------- |
|
||||
| <a id="accesslevelinterfaceimmutable"></a>`immutable` {{< icon name="warning-solid" >}} | [`Boolean!`](#boolean) | **Introduced** in GitLab 17.11. **Status**: Experiment. Returns true when tag rule is for tag immutability. Otherwise, false. |
|
||||
| <a id="accesslevelinterfaceminimumaccesslevelfordelete"></a>`minimumAccessLevelForDelete` {{< icon name="warning-solid" >}} | [`ContainerProtectionTagRuleAccessLevel`](#containerprotectiontagruleaccesslevel) | **Introduced** in GitLab 17.8. **Status**: Experiment. Minimum GitLab access level required to delete container image tags from the container repository. Valid values include `MAINTAINER`, `OWNER`, or `ADMIN`. If the value is `nil`, no access level can delete tags. |
|
||||
| <a id="accesslevelinterfaceminimumaccesslevelforpush"></a>`minimumAccessLevelForPush` {{< icon name="warning-solid" >}} | [`ContainerProtectionTagRuleAccessLevel`](#containerprotectiontagruleaccesslevel) | **Introduced** in GitLab 17.8. **Status**: Experiment. Minimum GitLab access level required to push container image tags to the container repository. Valid values include `MAINTAINER`, `OWNER`, or `ADMIN`. If the value is `nil`, no access level can push tags. |
|
||||
|
||||
|
|
|
|||
|
|
@ -775,7 +775,7 @@ POST /projects/:id/jobs/:job_id/cancel
|
|||
|-----------|----------------|----------|-------------|
|
||||
| `id` | integer/string | Yes | ID or [URL-encoded path of the project](rest/_index.md#namespaced-paths). |
|
||||
| `job_id` | integer | Yes | ID of a job. |
|
||||
| `force` | boolean | No | [Forces cancellation](../ci/jobs/job_logs.md#force-cancel-a-job) of a job in `canceling` state when set to `true`. |
|
||||
| `force` | boolean | No | [Forces cancellation](../ci/jobs/_index.md#force-cancel-a-job) of a job in `canceling` state when set to `true`. |
|
||||
|
||||
```shell
|
||||
curl --request POST \
|
||||
|
|
|
|||
|
|
@ -297,6 +297,152 @@ One or more `: [...]`, `X Y`, `X/Y`, or `X\Y` sequences are removed from the **e
|
|||
of job names only. Matching substrings found at the beginning or in the middle of
|
||||
job names are not removed.
|
||||
|
||||
## Retry a job
|
||||
|
||||
You can retry a job after it completes, regardless of its final state (failed, success, or canceled).
|
||||
|
||||
When you retry a job:
|
||||
|
||||
- A new job instance is created with a new job ID.
|
||||
- The job runs with the same parameters and variables as the original job.
|
||||
- If the job produces artifacts, new artifacts are created and stored.
|
||||
|
||||
Prerequisites:
|
||||
|
||||
- You must have at least the Developer role for the project.
|
||||
|
||||
To retry a job from a merge request:
|
||||
|
||||
1. On the left sidebar, select **Search or go to** and find your project.
|
||||
1. From your merge request, do one of the following:
|
||||
- In the pipeline widget, next to the job you want to retry, select **Run again** ({{< icon name="retry" >}}).
|
||||
- Select the **Pipelines** tab, next to the job you want to retry, select **Run again** ({{< icon name="retry" >}}).
|
||||
|
||||
To retry a job from the job log:
|
||||
|
||||
1. Go to the job's log page.
|
||||
1. In the upper-right corner, select **Run again** ({{< icon name="retry" >}}).
|
||||
|
||||
To retry a job from a pipeline:
|
||||
|
||||
1. On the left sidebar, select **Search or go to** and find your project.
|
||||
1. Select **Build > Pipelines**.
|
||||
1. Find the pipeline that contains the job you want to retry.
|
||||
1. From the pipeline graph, next to the job you want to retry, select **Run again** ({{< icon name="retry" >}}).
|
||||
|
||||
### Retry all failed or canceled jobs in a pipeline
|
||||
|
||||
If a pipeline has multiple failed or canceled jobs, you can retry all of them at once:
|
||||
|
||||
1. On the left sidebar, select **Search or go to** and find your project.
|
||||
1. Do one of the following:
|
||||
- Select **Build > Pipelines**.
|
||||
- Go to a merge request and select the **Pipelines** tab.
|
||||
1. For the pipeline with failed or canceled jobs, select **Retry all failed or canceled jobs** ({{< icon name="retry" >}}).
|
||||
|
||||
## Cancel a job
|
||||
|
||||
You can cancel a CI/CD job depending on its current state and the runner's capabilities.
|
||||
|
||||
When you cancel a job, what happens next depends on the job state and runner capabilities:
|
||||
|
||||
- For a `pending` job (not yet executing), the job is canceled immediately.
|
||||
- For a `running` job:
|
||||
- If the runner supports graceful cancellation, the job enters the `canceling` state.
|
||||
The runner can complete its [`after_script`](../yaml/_index.md#after_script) before the job is marked as `canceled`.
|
||||
- If the runner doesn't support graceful cancellation, the job moves to the `canceled` state immediately.
|
||||
|
||||
```mermaid
|
||||
%%{init: { "fontFamily": "GitLab Sans" }}%%
|
||||
stateDiagram-v2
|
||||
accTitle: CI/CD job state transitions
|
||||
accDescr: Shows possible state transitions for CI/CD jobs, including cancellation paths.
|
||||
|
||||
direction TB
|
||||
state if_graceful <>
|
||||
[*] --> pending: Job created
|
||||
pending --> canceled: Cancel requested
|
||||
canceled --> [*]
|
||||
pending --> running: Runner picks up job
|
||||
running --> success: Job succeeds
|
||||
success --> [*]
|
||||
running --> failed: Job fails
|
||||
failed --> [*]
|
||||
running --> if_graceful: Cancel requested
|
||||
if_graceful --> canceling: Runner supports graceful cancellation
|
||||
if_graceful --> canceled: Runner doesn't support graceful cancellation
|
||||
canceling --> canceled: Graceful cancellation complete
|
||||
note right of if_graceful: Does the runner support graceful cancellation?
|
||||
```
|
||||
|
||||
Prerequisites:
|
||||
|
||||
- You must have at least the Developer role for the project,
|
||||
or the [minimum role required to cancel a pipeline or job](../pipelines/settings.md#restrict-roles-that-can-cancel-pipelines-or-jobs).
|
||||
|
||||
To cancel a job from a merge request:
|
||||
|
||||
1. On the left sidebar, select **Search or go to** and find your project.
|
||||
1. From your merge request, do one of the following:
|
||||
- In the pipeline widget, next to the job you want to cancel, select **Cancel** ({{< icon name="cancel" >}}).
|
||||
- Select the **Pipelines** tab, next to the job you want to cancel, select **Cancel** ({{< icon name="cancel" >}}).
|
||||
|
||||
To cancel a job from the job log:
|
||||
|
||||
1. Go to the job's log page.
|
||||
1. In the upper-right corner, select **Cancel** ({{< icon name="cancel" >}}).
|
||||
|
||||
To cancel a job from a pipeline:
|
||||
|
||||
1. On the left sidebar, select **Search or go to** and find your project.
|
||||
1. Select **Build > Pipelines**.
|
||||
1. Find the pipeline that contains the job you want to cancel.
|
||||
1. From the pipeline graph, next to the job you want to cancel, select **Cancel** ({{< icon name="cancel" >}}).
|
||||
|
||||
### Cancel all running jobs in a pipeline
|
||||
|
||||
You can cancel all jobs in a running pipeline at once.
|
||||
|
||||
1. On the left sidebar, select **Search or go to** and find your project.
|
||||
1. Do one of the following:
|
||||
- Select **Build > Pipelines**.
|
||||
- Go to a merge request and select the **Pipelines** tab.
|
||||
1. For the pipeline you want to cancel, select **Cancel the running pipeline** ({{< icon name="cancel" >}}).
|
||||
|
||||
### Force cancel a job
|
||||
|
||||
{{< history >}}
|
||||
|
||||
- [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/467107) in GitLab 17.10 [with a flag](../../administration/feature_flags.md) named `force_cancel_build`. Disabled by default. This feature is an [experiment](../../policy/development_stages_support.md).
|
||||
|
||||
{{< /history >}}
|
||||
|
||||
{{< alert type="flag">}}
|
||||
|
||||
The availability of this feature is controlled by a feature flag.
|
||||
For more information, see the history.
|
||||
This feature is available for testing, but not ready for production use.
|
||||
|
||||
{{< /alert >}}
|
||||
|
||||
If a job is stuck in the `canceling` state, you can force it to the `canceled` state.
|
||||
|
||||
Prerequisites:
|
||||
|
||||
- You must have at least the Maintainer role for the project.
|
||||
|
||||
To force cancel a job:
|
||||
|
||||
- From the job log, select **Force cancel**.
|
||||
|
||||
{{< alert type="warning" >}}
|
||||
|
||||
When you force cancel a job, the [job token](ci_job_token.md) is revoked.
|
||||
If the runner is still trying to execute the job, it loses access to GitLab.
|
||||
The runner aborts the job without waiting for `after_script` to complete.
|
||||
|
||||
{{< /alert >}}
|
||||
|
||||
## Troubleshoot a failed job
|
||||
|
||||
When a pipeline fails or is allowed to fail, there are several places where you
|
||||
|
|
|
|||
|
|
@ -164,70 +164,6 @@ job1:
|
|||
- echo -e "\e[0Ksection_end:`date +%s`:my_first_section\r\e[0K"
|
||||
```
|
||||
|
||||
## Cancel a job
|
||||
|
||||
You can cancel a CI/CD job depending on its current state and the runner's capabilities.
|
||||
|
||||
```mermaid
|
||||
%%{init: { "fontFamily": "GitLab Sans" }}%%
|
||||
stateDiagram-v2
|
||||
accTitle: CI/CD job state transitions
|
||||
accDescr: Shows possible state transitions for CI/CD jobs, including cancellation paths.
|
||||
|
||||
direction LR
|
||||
state if_graceful <<choice>>
|
||||
[*] --> pending: Job created
|
||||
pending --> canceled: Cancel requested
|
||||
canceled --> [*]
|
||||
pending --> running: Runner picks up job
|
||||
running --> success: Job succeeds
|
||||
success --> [*]
|
||||
running --> failed: Job fails
|
||||
failed --> [*]
|
||||
running --> if_graceful: Cancel requested
|
||||
if_graceful --> canceling: Runner supports graceful cancellation
|
||||
if_graceful --> canceled: Runner doesn't support graceful cancellation
|
||||
canceling --> canceled: Graceful cancellation complete
|
||||
note right of if_graceful: Runner supports graceful cancellation?
|
||||
```
|
||||
|
||||
Prerequisites:
|
||||
|
||||
- You must have at least the Developer role for the project, or the [minimum role required to cancel a pipeline or job](../pipelines/settings.md#restrict-roles-that-can-cancel-pipelines-or-jobs).
|
||||
|
||||
To cancel a job from the job log:
|
||||
|
||||
1. Select **Cancel**.
|
||||
1. What happens next depends on the job state and runner capabilities:
|
||||
- For a `pending` job (not yet executing), the job is canceled immediately.
|
||||
- For a `running` job:
|
||||
- If the runner supports graceful cancellation, the job enters the `canceling` state. The runner can complete its [`after_script`](../yaml/_index.md#after_script) before the job is marked as `canceled`.
|
||||
- If the runner doesn't support graceful cancellation, the job moves to the `canceled` state immediately.
|
||||
|
||||
### Force cancel a job
|
||||
|
||||
{{< history >}}
|
||||
|
||||
- [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/467107) in GitLab 17.10.
|
||||
|
||||
{{< /history >}}
|
||||
|
||||
If a job is stuck in the `canceling` state due to runner issues (for example, due to a runner hardware failure), you can force cancel it to complete its pipeline.
|
||||
|
||||
Prerequisites:
|
||||
|
||||
- You must have at least the Maintainer role for the project.
|
||||
|
||||
To force cancel a job:
|
||||
|
||||
- From the job log, select **Force cancel**.
|
||||
|
||||
{{< alert type="warning" >}}
|
||||
|
||||
When you force cancel a job, the [job token](ci_job_token.md) is revoked. If the runner is still trying to execute the job, it loses access to GitLab. The runner aborts the job without waiting for `after_script` to complete.
|
||||
|
||||
{{< /alert >}}
|
||||
|
||||
## Delete job logs
|
||||
|
||||
When you delete a job log you also [erase the entire job](../../api/jobs.md#erase-a-job).
|
||||
|
|
|
|||
|
|
@ -33,7 +33,6 @@ This window takes place on April 21 - 23, 2025 from 09:00 UTC to 22:00 UTC.
|
|||
| [Replace namespace `add_on_purchase` GraphQL field with `add_on_purchases`](https://gitlab.com/gitlab-org/gitlab/-/issues/489850) | Low | Fulfillment | Instance, group |
|
||||
| [Public use of Secure container registries is deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/470641) | Low | Secure | Instance |
|
||||
| [Limit number of scan execution policy actions allowed per policy](https://gitlab.com/gitlab-org/gitlab/-/issues/510897) | Low | Security risk management | Instance, group, project |
|
||||
| [Pipelines API cancel endpoint returns error for non-cancelable pipelines](https://gitlab.com/gitlab-org/gitlab/-/issues/414963) | High | Verify | Instance, group, project |
|
||||
| [Deprecate CI job implementation of Repository X-Ray](https://gitlab.com/gitlab-org/gitlab/-/issues/500146) | Low | Create | Project |
|
||||
| [Pipeline job limits extended to the Commits API](https://gitlab.com/gitlab-org/gitlab/-/issues/436361) | Low | Verify | Project |
|
||||
| [Deprecation of `name` field in `ProjectMonthlyUsageType` GraphQL API](https://gitlab.com/gitlab-org/gitlab/-/issues/381894) | Low | Fulfillment | Project |
|
||||
|
|
@ -65,7 +64,7 @@ This window takes place on April 28 - 30, 2025 from 09:00 UTC to 22:00 UTC.
|
|||
| [`mergeTrainIndex` and `mergeTrainsCount` GraphQL fields deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/473759) | Low | Verify | Project |
|
||||
| [Behavior change for Upcoming and Started milestone filters](https://gitlab.com/gitlab-org/gitlab/-/issues/501294) | Low | Plan | Group, project |
|
||||
| [`kpt`-based `agentk` is deprecated](https://gitlab.com/gitlab-org/cluster-integration/gitlab-agent/-/issues/656) | Low | Deploy | Project |
|
||||
| [Updating CI/CD job tokens to JWT standard](https://gitlab.com/gitlab-org/gitlab/-/issues/509578) | | Software supply chain security | |
|
||||
| [Updating CI/CD job tokens to JWT standard](https://gitlab.com/gitlab-org/gitlab/-/issues/509578) | Low | Software supply chain security | Project |
|
||||
|
||||
## Window 3
|
||||
|
||||
|
|
|
|||
|
|
@ -785,8 +785,8 @@ To ensure continued compatibility and security, migrate to Signature Version 4.
|
|||
|
||||
To migrate:
|
||||
|
||||
1. Check your S3 storage backend configuration in the GitLab container registry settings.
|
||||
1. Remove the `v4auth: false` option if it's set.
|
||||
1. Check your [S3 storage backend configuration in the GitLab container registry settings](https://docs.gitlab.com/administration/packages/container_registry/#use-object-storage).
|
||||
1. If `v4auth` is set to `false`, remove the option.
|
||||
1. Verify your existing credentials work with v4 authentication.
|
||||
|
||||
If you encounter any issues after making these changes, try regenerating your AWS credentials.
|
||||
|
|
@ -1610,25 +1610,6 @@ Starting in GitLab 18.0, the maximum [number of jobs in active pipelines](https:
|
|||
|
||||
<div class="deprecation breaking-change" data-milestone="18.0">
|
||||
|
||||
### Pipelines API cancel endpoint returns error for non-cancelable pipelines
|
||||
|
||||
<div class="deprecation-notes">
|
||||
|
||||
- Announced in GitLab <span class="milestone">17.6</span>
|
||||
- Removal in GitLab <span class="milestone">18.0</span> ([breaking change](https://docs.gitlab.com/update/terminology/#breaking-change))
|
||||
- To discuss this change or learn more, see the [deprecation issue](https://gitlab.com/gitlab-org/gitlab/-/issues/414963).
|
||||
|
||||
</div>
|
||||
|
||||
The Pipelines API cancel endpoint [`POST /projects/:id/pipelines/:pipeline_id/cancel`](https://docs.gitlab.com/api/pipelines/#cancel-a-pipelines-jobs)
|
||||
returns a `200` success response regardless of whether a pipeline can be canceled.
|
||||
Starting in GitLab 18.0, the endpoint will return a `422 Unprocessable Entity` error when a pipeline cannot be canceled.
|
||||
Update your API integration to handle the `422` status code when making pipeline cancellation requests.
|
||||
|
||||
</div>
|
||||
|
||||
<div class="deprecation breaking-change" data-milestone="18.0">
|
||||
|
||||
### PostgreSQL 14 and 15 no longer supported
|
||||
|
||||
<div class="deprecation-notes">
|
||||
|
|
@ -2234,9 +2215,9 @@ In other cases:
|
|||
|
||||
</div>
|
||||
|
||||
In GitLab 18.0, CI/CD job tokens are moving to the JWT standard by default. All new projects will use this standard, but existing projects will continue to use the legacy format. Existing projects can switch to the JWT standard before the GitLab 18.0 release. If you experience issues, you can still [use the legacy format for your CI/CD tokens](https://docs.gitlab.com/ci/jobs/ci_job_token#use-legacy-format-for-cicd-tokens) until the GitLab 18.3 release.
|
||||
In GitLab 18.0, CI/CD job tokens will switch from a string token format to the JWT token format. This changes impacts new and existing CI/CD job tokens in all projects. If you experience issues, you can still [use the legacy format for your CI/CD tokens](https://docs.gitlab.com/ci/jobs/ci_job_token#use-legacy-format-for-cicd-tokens) until the GitLab 19.0 release.
|
||||
|
||||
In GitLab 18.3, all CI/CD job tokens must use the JWT standard. Before this release, you can temporarily revert your tokens back to the legacy job token format.
|
||||
In GitLab 19.0, all CI/CD job tokens must use the JWT standard. Before this release, you can temporarily revert your tokens back to the legacy job token format.
|
||||
|
||||
Known issues:
|
||||
|
||||
|
|
|
|||
|
|
@ -153,6 +153,23 @@ To create test coverage for selected lines:
|
|||
- If the merge request includes a test file, it is updated with the suggested tests.
|
||||
- If the merge request does not include a test file, Amazon Q populates a comment with the suggested tests.
|
||||
|
||||
## Additional supported features
|
||||
|
||||
In addition, these features are available on GitLab Duo with Amazon Q.
|
||||
|
||||
| Feature | GitLab version |
|
||||
|----------------------------------------------------------------------------------------------------------------------------------------|----------------|
|
||||
| [GitLab Duo Chat](../../user/gitlab_duo_chat/_index.md) | GitLab 17.11 and later |
|
||||
| [Code Suggestions](../../user/project/repository/code_suggestions/_index.md) | GitLab 17.11 and later |
|
||||
| [Code Explanation](../../user/project/repository/code_explain.md) | GitLab 17.11 and later |
|
||||
| [Test Generation](../../user/gitlab_duo_chat/examples.md#write-tests-in-the-ide) | GitLab 17.11 and later |
|
||||
| [Refactor Code](../../user/gitlab_duo_chat/examples.md#refactor-code-in-the-ide) | GitLab 17.11 and later |
|
||||
| [Fix Code](../../user/gitlab_duo_chat/examples.md#fix-code-in-the-ide) | GitLab 17.11 and later |
|
||||
| [Root Cause Analysis](../../user/gitlab_duo_chat/examples.md#troubleshoot-failed-cicd-jobs-with-root-cause-analysis) | GitLab 17.11 and later |
|
||||
| [Discussion Summary](../../user/discussions/_index.md#summarize-issue-discussions-with-duo-chat) | GitLab 17.11 and later |
|
||||
| [Vulnerability Explanation](../../user/application_security/vulnerabilities/_index.md#explaining-a-vulnerability) | GitLab 17.11 and later |
|
||||
| [Vulnerability Resolution](../../user/application_security/vulnerabilities/_index.md#vulnerability-resolution) | GitLab 17.11 and later |
|
||||
|
||||
## Related topics
|
||||
|
||||
- [Set up GitLab Duo with Amazon Q](setup.md)
|
||||
|
|
|
|||
|
|
@ -82,6 +82,8 @@ To use CI/CD to authenticate with the container registry, you can use:
|
|||
|
||||
- A [CI job token](../../../ci/jobs/ci_job_token.md).
|
||||
|
||||
This token can only be used for read (pull) access. It has the `read_registry` scope but not the `write_registry` scope needed for push operations.
|
||||
|
||||
```shell
|
||||
echo "$CI_JOB_TOKEN" | docker login $CI_REGISTRY -u $CI_REGISTRY_USER --password-stdin
|
||||
```
|
||||
|
|
|
|||
|
|
@ -590,6 +590,53 @@ npm-deploy-job:
|
|||
- npm dist-tag add @scope/package@version my-tag
|
||||
```
|
||||
|
||||
### Audit npm packages
|
||||
|
||||
GitLab supports `npm audit` commands, allowing you to check your packages for known vulnerabilities.
|
||||
|
||||
#### Use `npm audit`
|
||||
|
||||
Prerequisites:
|
||||
|
||||
- [Configure authentication](#authenticate-to-the-package-registry) to the package registry.
|
||||
- [Set up the registry URL](#set-up-the-registry-url).
|
||||
|
||||
To run security audits, you can run the following command:
|
||||
|
||||
```shell
|
||||
npm audit --registry=https://gitlab.example.com/api/v4/packages/npm/
|
||||
```
|
||||
|
||||
Or, if you've already set your registry configuration:
|
||||
|
||||
```shell
|
||||
npm audit
|
||||
```
|
||||
|
||||
The `npm audit` command checks your dependencies for known vulnerabilities and provides a report.
|
||||
|
||||
#### `npm audit` workflow
|
||||
|
||||
When you run `npm audit` against the GitLab package registry, one of two scenarios occurs:
|
||||
|
||||
1. If package forwarding is enabled (default), GitLab forwards the audit request to `npmjs.com` to retrieve vulnerability information for both public and private packages.
|
||||
1. If package forwarding is disabled, GitLab returns an empty result set. GitLab does not scan packages for vulnerabilities independently.
|
||||
|
||||
To learn more about the package forwarding setting, see [Package forwarding to npmjs.com](#package-forwarding-to-npmjscom).
|
||||
|
||||
#### Important security considerations
|
||||
|
||||
If you do not specify GitLab as your package registry (either with the `--registry` flag or by setting it as your default registry in the `.npmrc` file), the audit request goes to the public [npm registry](https://registry.npmjs.org) instead.
|
||||
|
||||
In this case, the request body contains information about all packages in your project, including your private GitLab packages.
|
||||
|
||||
To ensure your private package information stays within GitLab, always make sure to specify the GitLab registry when running `npm audit` commands.
|
||||
|
||||
#### Limitations
|
||||
|
||||
- Audit results depend on [package forwarding](#package-forwarding-to-npmjscom) being enabled. If forwarding is disabled by an administrator or group Owner, `npm audit` does not return vulnerability information.
|
||||
- The audit request includes information about all packages in your project, including private packages.
|
||||
|
||||
### Supported CLI commands
|
||||
|
||||
The GitLab npm repository supports the following commands for the npm CLI (`npm`) and yarn CLI
|
||||
|
|
@ -604,6 +651,7 @@ The GitLab npm repository supports the following commands for the npm CLI (`npm`
|
|||
- `npm view`: Show package metadata.
|
||||
- `npm pack`: Create a tarball from a package.
|
||||
- `npm deprecate`: Deprecate a version of a package.
|
||||
- `npm audit`: Check for vulnerabilities in your project dependencies.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
|
|
|
|||
|
|
@ -35,6 +35,15 @@ You can configure merge request status checks for each individual project. These
|
|||
|
||||
Status checks fail if they stay in the pending state for more than two minutes.
|
||||
|
||||
## Access permissions
|
||||
|
||||
External status check responses can be viewed by:
|
||||
|
||||
- Users with Reporter role or higher permissions in the project
|
||||
- Any authenticated user who can view the merge request when the project has internal visibility
|
||||
|
||||
This means that if you have an internal project, any logged-in user who can access the merge request can view the external status check responses.
|
||||
|
||||
For more information about use cases, feature discovery, and development timelines,
|
||||
see [epic 3869](https://gitlab.com/groups/gitlab-org/-/epics/3869).
|
||||
|
||||
|
|
|
|||
|
|
@ -346,6 +346,7 @@ module API
|
|||
authorize! :cancel_pipeline, pipeline
|
||||
|
||||
# TODO: inconsistent behavior: when pipeline is not cancelable we should return an error
|
||||
# Set to be fixed on V5 to avoid breaking changes: https://gitlab.com/gitlab-org/gitlab/-/issues/519143
|
||||
::Ci::CancelPipelineService.new(pipeline: pipeline, current_user: current_user).execute
|
||||
|
||||
status 200
|
||||
|
|
|
|||
|
|
@ -14,6 +14,15 @@ module Authn
|
|||
|
||||
Gitlab::CurrentSettings.current_application_settings.instance_token_prefix
|
||||
end
|
||||
|
||||
def self.default_instance_prefix(prefix)
|
||||
return prefix unless Feature.enabled?(:custom_prefix_for_all_token_types, :instance)
|
||||
|
||||
return prefix unless prefix.starts_with?(instance_prefix)
|
||||
|
||||
# remove the configured instance prefix and add the default:
|
||||
"#{ApplicationSetting.defaults[:instance_token_prefix]}#{prefix.delete_prefix(instance_prefix)}"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -4,7 +4,8 @@ module Authn
|
|||
module Tokens
|
||||
class FeedToken
|
||||
def self.prefix?(plaintext)
|
||||
plaintext.start_with?(::User.prefix_for_feed_token)
|
||||
plaintext.start_with?(::User.prefix_for_feed_token,
|
||||
Authn::TokenField::PrefixHelper.default_instance_prefix(::User.prefix_for_feed_token))
|
||||
end
|
||||
|
||||
attr_reader :revocable, :source
|
||||
|
|
|
|||
|
|
@ -13,6 +13,9 @@ module Gitlab
|
|||
license = data['license']
|
||||
return unless license
|
||||
|
||||
# A license must have either id or name
|
||||
return unless license['id'].present? || license['name'].present?
|
||||
|
||||
::Gitlab::Ci::Reports::Sbom::License.new(
|
||||
spdx_identifier: license['id'],
|
||||
name: license['name'],
|
||||
|
|
|
|||
|
|
@ -227,24 +227,11 @@ dependency-scanning:
|
|||
variables:
|
||||
ANALYZER_SUPPORTED_FILES: "packages.lock.json,conan.lock,conda-lock.yml,pubspec.lock,go.mod,go.graph,ivy-report.xml,maven.graph.json,dependencies.lock,package-lock.json,npm-shrinkwrap.json,pnpm-lock.yaml,yarn.lock,Podfile.lock,composer.lock,pipdeptree.json,requirements.txt,Pipfile.lock,pipenv.graph.json,poetry.lock,uv.lock,Gemfile.lock,gems.locked,Cargo.lock,dependencies-compile.dot,Package.resolved"
|
||||
ADDITIONAL_SUPPORTED_FILES: "pom.xml,build.gradle,build.gradle.kts,build.sbt,requirements.pip,Pipfile,requires.txt,setup.py"
|
||||
SCA_TO_SARIF_MATCHER_VERSION: "v2.0.1"
|
||||
stage: test
|
||||
image:
|
||||
name: "$CI_TEMPLATE_REGISTRY_HOST/security-products/dependency-scanning:v0"
|
||||
needs:
|
||||
- job: gitlab-static-reachability
|
||||
optional: true
|
||||
artifacts: true
|
||||
script:
|
||||
- |
|
||||
/analyzer run || exit $?
|
||||
if [ -f "reachable_packages.json" ]; then
|
||||
echo "Found reachable_packages.json"
|
||||
echo "Downloading SCA-to-sarif-matcher ${SCA_TO_SARIF_MATCHER_VERSION}"
|
||||
curl -L "gitlab.com/api/v4/projects/60962090/packages/generic/sca-to-sarif-matcher/${SCA_TO_SARIF_MATCHER_VERSION}/matcher" -o /home/gitlab/sbom-enricher
|
||||
chmod +x /home/gitlab/sbom-enricher
|
||||
/home/gitlab/sbom-enricher process --glas_report="reachable_packages.json"
|
||||
fi
|
||||
- /analyzer run
|
||||
allow_failure: true
|
||||
artifacts:
|
||||
access: "developer"
|
||||
|
|
@ -315,42 +302,3 @@ dependency-scanning:
|
|||
- '**/{conda-lock.yml,pubspec.lock,Podfile.lock,Cargo.lock,Package.resolved}'
|
||||
variables:
|
||||
DS_EXCLUDED_PATHS: 'spec, test, tests, tmp, **/build.gradle, **/build.gradle.kts, **/build.sbt, **/pom.xml, **/requirements.txt, **/requirements.pip, **/Pipfile, **/Pipfile.lock, **/requires.txt, **/setup.py, **/poetry.lock, **/uv.lock, **/packages.lock.json, **/conan.lock, **/package-lock.json, **/npm-shrinkwrap.json, **/pnpm-lock.yaml, **/yarn.lock, **/composer.lock, **/Gemfile.lock, **/gems.locked, **/go.graph, **/ivy-report.xml, **/maven.graph.json, **/dependencies.lock, **/pipdeptree.json, **/pipenv.graph.json, **/dependencies-compile.dot'
|
||||
|
||||
gitlab-static-reachability:
|
||||
stage: test
|
||||
variables:
|
||||
SEARCH_MAX_DEPTH: 20
|
||||
STATIC_REACHABILITY_ANALYZER_IMAGE_TAG: 1
|
||||
# For now we are using GLAS as our static reachability analyzer
|
||||
STATIC_REACHABILITY_ANALYZER_IMAGE: "$SECURE_ANALYZERS_PREFIX/gitlab-advanced-sast:$STATIC_REACHABILITY_ANALYZER_IMAGE_TAG"
|
||||
image:
|
||||
name: "$STATIC_REACHABILITY_ANALYZER_IMAGE"
|
||||
cache: []
|
||||
script:
|
||||
- |
|
||||
export SAST_SCANNER_ALLOWED_CLI_OPTS="--sca-output-path reachable_packages.json"
|
||||
echo keep-builtin-rules: false >> /lightz-aio_default_config.yaml
|
||||
/analyzer run
|
||||
chmod 644 reachable_packages.json
|
||||
artifacts:
|
||||
access: 'developer'
|
||||
paths:
|
||||
- reachable_packages.json
|
||||
rules:
|
||||
- if: $GITLAB_STATIC_REACHABILITY_ENABLED != 'true' || $DS_ENFORCE_NEW_ANALYZER != 'true'
|
||||
when: never
|
||||
# if DS is disabled then static reachability cannot execute
|
||||
- if: $DEPENDENCY_SCANNING_DISABLED == 'true' || $DEPENDENCY_SCANNING_DISABLED == '1'
|
||||
when: never
|
||||
# Add the job to merge request pipelines if there's an open merge request.
|
||||
- if: $CI_PIPELINE_SOURCE == "merge_request_event" &&
|
||||
$GITLAB_FEATURES =~ /\bsast_advanced\b/
|
||||
exists:
|
||||
- '**/*.py'
|
||||
- if: $CI_OPEN_MERGE_REQUESTS # Don't add it to a *branch* pipeline if it's already in a merge request pipeline.
|
||||
when: never
|
||||
# If there's no open merge request, add it to a *branch* pipeline instead.
|
||||
- if: $CI_COMMIT_BRANCH &&
|
||||
$GITLAB_FEATURES =~ /\bsast_advanced\b/
|
||||
exists:
|
||||
- '**/*.py'
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ variables:
|
|||
DEFAULT_SAST_EXCLUDED_PATHS: "spec, test, tests, tmp"
|
||||
SAST_EXCLUDED_PATHS: "$DEFAULT_SAST_EXCLUDED_PATHS"
|
||||
SCAN_KUBERNETES_MANIFESTS: "false"
|
||||
GITLAB_ADVANCED_SAST_SCA_FILENAME: "GLAS_SCA.json"
|
||||
|
||||
sast:
|
||||
stage: test
|
||||
|
|
@ -87,6 +88,90 @@ gitlab-advanced-sast:
|
|||
- '**/*.cs'
|
||||
- '**/*.rb'
|
||||
|
||||
.static-reachability-rules:
|
||||
rules:
|
||||
- if: $SAST_DISABLED == 'true' || $SAST_DISABLED == '1'
|
||||
when: never
|
||||
- if: $SAST_EXCLUDED_ANALYZERS =~ /gitlab-advanced-sast/
|
||||
when: never
|
||||
- if: $GITLAB_STATIC_REACHABILITY_ENABLED != 'true'
|
||||
when: never
|
||||
# Add the job to merge request pipelines if there's an open merge request.
|
||||
- if: $CI_PIPELINE_SOURCE == "merge_request_event" &&
|
||||
$GITLAB_FEATURES =~ /\bsast_advanced\b/
|
||||
exists:
|
||||
- '**/*.py'
|
||||
- '**/*.java'
|
||||
- '**/*.jsp'
|
||||
- if: $CI_OPEN_MERGE_REQUESTS # Don't add it to a *branch* pipeline if it's already in a merge request pipeline.
|
||||
when: never
|
||||
# If there's no open merge request, add it to a *branch* pipeline instead.
|
||||
- if: $CI_COMMIT_BRANCH &&
|
||||
$GITLAB_FEATURES =~ /\bsast_advanced\b/
|
||||
exists:
|
||||
- '**/*.py'
|
||||
- '**/*.java'
|
||||
- '**/*.jsp'
|
||||
|
||||
gitlab-static-reachability:
|
||||
extends:
|
||||
- gitlab-advanced-sast
|
||||
variables:
|
||||
SAST_SCANNER_ALLOWED_CLI_OPTS: --sca-output-path ${GITLAB_ADVANCED_SAST_SCA_FILENAME}
|
||||
before_script:
|
||||
- |
|
||||
echo keep-builtin-rules: false >> /lightz-aio_default_config.yaml
|
||||
artifacts:
|
||||
paths:
|
||||
- $GITLAB_ADVANCED_SAST_SCA_FILENAME
|
||||
rules:
|
||||
- if: $SAST_DISABLED == 'true' || $SAST_DISABLED == '1'
|
||||
when: never
|
||||
- if: $SAST_EXCLUDED_ANALYZERS =~ /gitlab-advanced-sast/
|
||||
when: never
|
||||
- if: $GITLAB_STATIC_REACHABILITY_ENABLED != 'true'
|
||||
when: never
|
||||
# Add the job to merge request pipelines if there's an open merge request.
|
||||
- if: $CI_PIPELINE_SOURCE == "merge_request_event" &&
|
||||
$GITLAB_FEATURES =~ /\bsast_advanced\b/
|
||||
exists:
|
||||
- '**/*.py'
|
||||
- '**/*.java'
|
||||
- '**/*.jsp'
|
||||
- if: $CI_OPEN_MERGE_REQUESTS # Don't add it to a *branch* pipeline if it's already in a merge request pipeline.
|
||||
when: never
|
||||
# If there's no open merge request, add it to a *branch* pipeline instead.
|
||||
- if: $CI_COMMIT_BRANCH &&
|
||||
$GITLAB_FEATURES =~ /\bsast_advanced\b/
|
||||
exists:
|
||||
- '**/*.py'
|
||||
- '**/*.java'
|
||||
- '**/*.jsp'
|
||||
|
||||
gitlab-enrich-cdx-results:
|
||||
stage: .post
|
||||
extends: .static-reachability-rules
|
||||
variables:
|
||||
GLAS_STATIC_REACHABILITY_MATCHER_VERSION: "v1.0.3"
|
||||
GLAS_REPORT: $GITLAB_ADVANCED_SAST_SCA_FILENAME
|
||||
SCA_TO_SARIF_PROJECT_ID: 60962090
|
||||
DEPENDENCY_SCANNING_PATTERN: "**/gl-sbom-*-*.cdx.json"
|
||||
image:
|
||||
name: "$CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG"
|
||||
entrypoint: [""]
|
||||
before_script:
|
||||
- |
|
||||
wget "gitlab.com/api/v4/projects/${SCA_TO_SARIF_PROJECT_ID}/packages/generic/sca-to-sarif-matcher/${GLAS_STATIC_REACHABILITY_MATCHER_VERSION}/matcher" \
|
||||
--no-verbose -O /matcher
|
||||
- chmod +x /matcher
|
||||
script:
|
||||
- /matcher process
|
||||
artifacts:
|
||||
paths:
|
||||
- "**/gl-sbom-*.cdx.json"
|
||||
reports:
|
||||
cyclonedx: "**/gl-sbom-*.cdx.json"
|
||||
|
||||
kubesec-sast:
|
||||
extends: .sast-analyzer
|
||||
image:
|
||||
|
|
|
|||
|
|
@ -2990,9 +2990,6 @@ msgstr ""
|
|||
msgid "AccessTokens|Created"
|
||||
msgstr ""
|
||||
|
||||
msgid "AccessTokens|Created date"
|
||||
msgstr ""
|
||||
|
||||
msgid "AccessTokens|Description"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -3112,9 +3109,6 @@ msgstr ""
|
|||
msgid "AccessTokens|Keep this token secret. Anyone who has it can read activity and issue RSS feeds or your calendar feed as if they were you. If that happens, %{linkStart}reset this token%{linkEnd}."
|
||||
msgstr ""
|
||||
|
||||
msgid "AccessTokens|Last used date"
|
||||
msgstr ""
|
||||
|
||||
msgid "AccessTokens|Last used:"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -4992,6 +4986,12 @@ msgstr ""
|
|||
msgid "AdminUsers|Could not load user group counts. Please refresh the page to try again."
|
||||
msgstr ""
|
||||
|
||||
msgid "AdminUsers|Create"
|
||||
msgstr ""
|
||||
|
||||
msgid "AdminUsers|Create service account"
|
||||
msgstr ""
|
||||
|
||||
msgid "AdminUsers|Deactivate"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -5022,6 +5022,12 @@ msgstr ""
|
|||
msgid "AdminUsers|Delete user and contributions"
|
||||
msgstr ""
|
||||
|
||||
msgid "AdminUsers|Edit"
|
||||
msgstr ""
|
||||
|
||||
msgid "AdminUsers|Edit service account"
|
||||
msgstr ""
|
||||
|
||||
msgid "AdminUsers|Export permissions as CSV (max 100,000 users)"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -5214,6 +5220,9 @@ msgstr ""
|
|||
msgid "AdminUsers|Unblock user %{username}?"
|
||||
msgstr ""
|
||||
|
||||
msgid "AdminUsers|Unique username that can be called for usage across GitLab"
|
||||
msgstr ""
|
||||
|
||||
msgid "AdminUsers|Unlock user %{username}?"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -17939,18 +17948,9 @@ msgstr ""
|
|||
msgid "Credentials"
|
||||
msgstr ""
|
||||
|
||||
msgid "CredentialsInventory|Active"
|
||||
msgstr ""
|
||||
|
||||
msgid "CredentialsInventory|Created date"
|
||||
msgstr ""
|
||||
|
||||
msgid "CredentialsInventory|Deleted user"
|
||||
msgstr ""
|
||||
|
||||
msgid "CredentialsInventory|Expiration date"
|
||||
msgstr ""
|
||||
|
||||
msgid "CredentialsInventory|Expired"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -17960,12 +17960,6 @@ msgstr ""
|
|||
msgid "CredentialsInventory|GPG keys"
|
||||
msgstr ""
|
||||
|
||||
msgid "CredentialsInventory|Inactive"
|
||||
msgstr ""
|
||||
|
||||
msgid "CredentialsInventory|Last used date"
|
||||
msgstr ""
|
||||
|
||||
msgid "CredentialsInventory|No credentials found"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -17990,9 +17984,6 @@ msgstr ""
|
|||
msgid "CredentialsInventory|Search or filter credentials…"
|
||||
msgstr ""
|
||||
|
||||
msgid "CredentialsInventory|State"
|
||||
msgstr ""
|
||||
|
||||
msgid "CredentialsInventory|Type"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -55238,6 +55229,12 @@ msgstr ""
|
|||
msgid "ServiceAccounts|Add Service Account"
|
||||
msgstr ""
|
||||
|
||||
msgid "ServiceAccounts|An error occurred creating the service account."
|
||||
msgstr ""
|
||||
|
||||
msgid "ServiceAccounts|An error occurred updating the service account."
|
||||
msgstr ""
|
||||
|
||||
msgid "ServiceAccounts|An error occurred while deleting the service account."
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -55268,6 +55265,12 @@ msgstr ""
|
|||
msgid "ServiceAccounts|The service account is being deleted."
|
||||
msgstr ""
|
||||
|
||||
msgid "ServiceAccounts|The service account was created."
|
||||
msgstr ""
|
||||
|
||||
msgid "ServiceAccounts|The service account was updated."
|
||||
msgstr ""
|
||||
|
||||
msgid "ServiceAccount|No more seats are available to create Service Account User"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
|||
|
|
@ -1,52 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rubocop/cop/rspec/base'
|
||||
|
||||
module RuboCop
|
||||
module Cop
|
||||
module RSpec
|
||||
# Ensures that shared examples and shared context don't have any metadata.
|
||||
#
|
||||
# @example
|
||||
#
|
||||
# # bad
|
||||
# RSpec.shared_examples 'an external link with rel attribute', feature_category: :team_planning do
|
||||
# end
|
||||
#
|
||||
# RSpec.shared_examples 'an external link with rel attribute', :aggregate_failures do
|
||||
# end
|
||||
#
|
||||
# RSpec.shared_context 'an external link with rel attribute', :aggregate_failures do
|
||||
# end
|
||||
#
|
||||
# # good
|
||||
# RSpec.shared_examples 'an external link with rel attribute' do
|
||||
# end
|
||||
#
|
||||
# shared_examples 'an external link with rel attribute' do
|
||||
# end
|
||||
#
|
||||
# it 'adds rel="nofollow" to external links', feature_category: :team_planning do
|
||||
# end
|
||||
class SharedGroupsMetadata < RuboCop::Cop::RSpec::Base
|
||||
MSG = 'Avoid using metadata on shared examples and shared context. They might cause flaky tests. See https://gitlab.com/gitlab-org/gitlab/-/issues/404388'
|
||||
|
||||
# @!method metadata_value(node)
|
||||
def_node_matcher :metadata_value, <<~PATTERN
|
||||
(block
|
||||
(send #rspec? {#SharedGroups.all} _description $_ ...)
|
||||
...
|
||||
)
|
||||
PATTERN
|
||||
|
||||
def on_block(node)
|
||||
value_node = metadata_value(node)
|
||||
|
||||
return unless value_node
|
||||
|
||||
add_offense(value_node, message: MSG)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -170,7 +170,6 @@ spec/frontend/projects/new/components/new_project_url_select_spec.js
|
|||
spec/frontend/projects/report_abuse/components/report_abuse_dropdown_item_spec.js
|
||||
spec/frontend/projects/settings/components/branch_rule_modal_spec.js
|
||||
spec/frontend/projects/settings/topics/components/topics_token_selector_spec.js
|
||||
spec/frontend/projects/settings_service_desk/components/custom_email_form_spec.js
|
||||
spec/frontend/projects/settings_service_desk/components/service_desk_root_spec.js
|
||||
spec/frontend/projects/settings_service_desk/components/service_desk_setting_spec.js
|
||||
spec/frontend/ref/init_ambiguous_ref_modal_spec.js
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ RSpec.describe Admin::ProjectsController, feature_category: :groups_and_projects
|
|||
|
||||
before do
|
||||
sign_in(create(:admin))
|
||||
stub_feature_flags(downtier_delayed_deletion: false)
|
||||
end
|
||||
|
||||
describe 'GET /projects' do
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ RSpec.describe GroupsController, :with_current_organization, factory_default: :k
|
|||
|
||||
before do
|
||||
enable_admin_mode!(admin_with_admin_mode)
|
||||
stub_feature_flags(downtier_delayed_deletion: false)
|
||||
end
|
||||
|
||||
shared_examples 'member with ability to create subgroups' do
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ RSpec.describe 'Group', :with_current_organization, feature_category: :groups_an
|
|||
|
||||
before do
|
||||
sign_in(user)
|
||||
stub_feature_flags(downtier_delayed_deletion: false)
|
||||
end
|
||||
|
||||
matcher :have_namespace_error_message do
|
||||
|
|
|
|||
|
|
@ -7,6 +7,10 @@ RSpec.describe 'Copy as GFM', :js, feature_category: :markdown do
|
|||
include RepoHelpers
|
||||
include ActionView::Helpers::JavaScriptHelper
|
||||
|
||||
before do
|
||||
stub_feature_flags(downtier_delayed_deletion: false)
|
||||
end
|
||||
|
||||
describe 'Copying rendered GFM' do
|
||||
before do
|
||||
@feat = MarkdownFeature.new
|
||||
|
|
|
|||
|
|
@ -5,42 +5,40 @@ require 'spec_helper'
|
|||
RSpec.describe Ci::BuildNameFinder, feature_category: :continuous_integration do
|
||||
let_it_be(:pipeline) { create(:ci_pipeline) }
|
||||
let_it_be(:build_non_relevant) { create(:ci_build, :with_build_name, pipeline: pipeline, name: "unique-name") }
|
||||
let_it_be(:old_build) { create(:ci_build, :with_build_name, pipeline: pipeline, name: "build1") }
|
||||
let_it_be(:old_middle_build) { create(:ci_build, :with_build_name, pipeline: pipeline, name: "build2") }
|
||||
let_it_be(:middle_build) { create(:ci_build, :with_build_name, pipeline: pipeline, name: "build3") }
|
||||
let_it_be(:middle_new_build) { create(:ci_build, :with_build_name, pipeline: pipeline, name: "build4") }
|
||||
let_it_be(:new_build) { create(:ci_build, :with_build_name, pipeline: pipeline, name: "build5") }
|
||||
let_it_be(:build_test) { create(:ci_build, :with_build_name, pipeline: pipeline, name: "build test") }
|
||||
let_it_be(:build_deploy) { create(:ci_build, :with_build_name, pipeline: pipeline, name: "build deploy") }
|
||||
let_it_be(:build_test_deploy) { create(:ci_build, :with_build_name, pipeline: pipeline, name: "build test deploy") }
|
||||
let_it_be(:multi_search_term) { create(:ci_build, :with_build_name, pipeline: pipeline, name: "a b c d e f") }
|
||||
|
||||
describe "#execute" do
|
||||
let(:main_relation) { Ci::Build.all }
|
||||
let(:name) { "build" }
|
||||
let(:before) { nil }
|
||||
let(:after) { nil }
|
||||
let(:asc) { nil }
|
||||
let(:invert_ordering) { false }
|
||||
|
||||
subject(:build_name_finder) do
|
||||
described_class.new(
|
||||
relation: main_relation,
|
||||
name: name,
|
||||
project: pipeline.project,
|
||||
params: {
|
||||
before: before, after: after, asc: asc,
|
||||
invert_ordering: invert_ordering
|
||||
}
|
||||
project: pipeline.project
|
||||
).execute
|
||||
end
|
||||
|
||||
it 'filters by name in desc order' do
|
||||
expect(build_name_finder)
|
||||
.to eq([new_build, middle_new_build, middle_build, old_middle_build, old_build])
|
||||
it 'filters by name' do
|
||||
expect(build_name_finder).to match_array([build_test, build_deploy, build_test_deploy])
|
||||
end
|
||||
|
||||
context 'when a multi-term name is passed in' do
|
||||
let(:name) { "a b c d e z z z" }
|
||||
|
||||
it 'filters and restricts search term' do
|
||||
expect(build_name_finder).to eq([multi_search_term])
|
||||
end
|
||||
end
|
||||
|
||||
context 'when no name is passed in' do
|
||||
let(:name) { nil }
|
||||
|
||||
it 'does not filter by name' do
|
||||
expect(build_name_finder.count).to eq(6)
|
||||
expect(build_name_finder.count).to eq(5)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -52,64 +50,6 @@ RSpec.describe Ci::BuildNameFinder, feature_category: :continuous_integration do
|
|||
expect { build_name_finder.execute }.to raise_error(ArgumentError, 'Only Ci::Builds are name searchable')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when relation is using offset' do
|
||||
it 'raises argument error for params' do
|
||||
expect do
|
||||
described_class.new(
|
||||
relation: main_relation.offset(1),
|
||||
name: name,
|
||||
project: pipeline.project,
|
||||
params: {}
|
||||
)
|
||||
.execute
|
||||
end.to raise_error(ArgumentError, 'Offset Pagination is not supported')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with before param' do
|
||||
let(:before) { middle_build.id }
|
||||
|
||||
it 'returns builds newer than middle build' do
|
||||
expect(build_name_finder)
|
||||
.to eq([new_build, middle_new_build])
|
||||
end
|
||||
|
||||
context 'with asc param' do
|
||||
let(:asc) { true }
|
||||
|
||||
it 'returns only the builds in asc order' do
|
||||
expect(build_name_finder)
|
||||
.to eq([middle_new_build, new_build])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with after param' do
|
||||
let(:after) { middle_build.id }
|
||||
|
||||
it 'returns builds older than middle build' do
|
||||
expect(build_name_finder)
|
||||
.to eq([old_middle_build, old_build])
|
||||
end
|
||||
|
||||
context 'with asc param' do
|
||||
let(:asc) { true }
|
||||
|
||||
it 'returns build before cursor in asc order' do
|
||||
expect(build_name_finder)
|
||||
.to eq([old_build, old_middle_build])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with asc param' do
|
||||
let(:asc) { true }
|
||||
|
||||
it 'returns the records in ascending order' do
|
||||
expect(build_name_finder).to eq([old_build, old_middle_build, middle_build, middle_new_build, new_build])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ describe('Daterange component', () => {
|
|||
factory({ show: true, startDate, endDate, minDate }, mountExtended);
|
||||
const input = findDaterangePicker().find('input');
|
||||
|
||||
input.setValue('2019-01-01');
|
||||
input.element.value = '2019-01-01';
|
||||
await input.trigger('change');
|
||||
|
||||
expect(wrapper.emitted().change).toEqual([[{ startDate: minDate, endDate }]]);
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import { GlDatepicker } from '@gitlab/ui';
|
||||
import { mount } from '@vue/test-utils';
|
||||
import Vue, { nextTick } from 'vue';
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import Vuex from 'vuex';
|
||||
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
|
||||
import { useFakeDate } from 'helpers/fake_date';
|
||||
import waitForPromises from 'helpers/wait_for_promises';
|
||||
import ExpirationDatepicker from '~/members/components/table/expiration_datepicker.vue';
|
||||
|
|
@ -40,7 +40,7 @@ describe('ExpirationDatepicker', () => {
|
|||
};
|
||||
|
||||
const createComponent = (propsData = {}) => {
|
||||
wrapper = mount(ExpirationDatepicker, {
|
||||
wrapper = shallowMountExtended(ExpirationDatepicker, {
|
||||
propsData: {
|
||||
member,
|
||||
permissions: { canUpdate: true },
|
||||
|
|
@ -56,7 +56,6 @@ describe('ExpirationDatepicker', () => {
|
|||
});
|
||||
};
|
||||
|
||||
const findInput = () => wrapper.find('input');
|
||||
const findDatepicker = () => wrapper.findComponent(GlDatepicker);
|
||||
|
||||
describe('datepicker input', () => {
|
||||
|
|
@ -65,7 +64,7 @@ describe('ExpirationDatepicker', () => {
|
|||
|
||||
await nextTick();
|
||||
|
||||
expect(findInput().element.value).toBe('2020-03-17');
|
||||
expect(findDatepicker().props('value')).toEqual(new Date('2020-03-17'));
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -128,9 +127,8 @@ describe('ExpirationDatepicker', () => {
|
|||
beforeEach(async () => {
|
||||
createComponent();
|
||||
|
||||
findInput().setValue('2020-03-17');
|
||||
await nextTick();
|
||||
wrapper.find('[data-testid="clear-button"]').trigger('click');
|
||||
await findDatepicker().vm.$emit('input', new Date('2020-03-17'));
|
||||
await findDatepicker().vm.$emit('clear');
|
||||
});
|
||||
|
||||
it('calls `updateMemberExpiration` Vuex action', () => {
|
||||
|
|
|
|||
|
|
@ -132,7 +132,9 @@ describe('search box by type component', () => {
|
|||
|
||||
createComponent({ debounce }, mount);
|
||||
|
||||
findInput().setValue(newValue);
|
||||
const input = findInput();
|
||||
input.element.value = newValue;
|
||||
input.trigger('input');
|
||||
});
|
||||
|
||||
it(`emits a ${modelEvent} after the debounce delay`, () => {
|
||||
|
|
@ -151,7 +153,9 @@ describe('search box by type component', () => {
|
|||
beforeEach(() => {
|
||||
createComponent({ lazy: true }, mount);
|
||||
|
||||
findInput().setValue(newValue);
|
||||
const input = findInput();
|
||||
input.element.value = newValue;
|
||||
input.trigger('input');
|
||||
});
|
||||
|
||||
it.each(['change', 'blur'])(`emits ${modelEvent} event after input's %s event`, (event) => {
|
||||
|
|
|
|||
|
|
@ -82,7 +82,14 @@ if (global.document) {
|
|||
}).mount(document.createElement('div'));
|
||||
|
||||
Vue.configureCompat(compatConfig);
|
||||
installVTUCompat(VTU, fullCompatConfig, compatH);
|
||||
installVTUCompat(
|
||||
VTU,
|
||||
{
|
||||
...fullCompatConfig,
|
||||
WRAPPER_SET_VALUE_DOES_NOT_TRIGGER_CHANGE: false,
|
||||
},
|
||||
compatH,
|
||||
);
|
||||
|
||||
jest.mock('vue', () => {
|
||||
const actualVue = jest.requireActual('vue');
|
||||
|
|
|
|||
|
|
@ -399,7 +399,9 @@ describe('vue_shared/component/markdown/markdown_editor', () => {
|
|||
buildWrapper();
|
||||
const newValue = 'new value';
|
||||
|
||||
await findTextarea().setValue(newValue);
|
||||
const textArea = findTextarea();
|
||||
textArea.element.value = newValue;
|
||||
await findTextarea().trigger('input');
|
||||
|
||||
expect(wrapper.emitted('input')).toEqual([[value], [newValue]]);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -0,0 +1,27 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe GitlabSchema.types['ContainerProtectionAccessLevel'], feature_category: :container_registry do
|
||||
specify { expect(described_class.graphql_name).to eq('ContainerProtectionAccessLevel') }
|
||||
|
||||
specify { expect(described_class.description).to be_present }
|
||||
|
||||
describe 'minimum_access_level_for_push' do
|
||||
subject { described_class.fields['minimumAccessLevelForPush'] }
|
||||
|
||||
it { is_expected.to have_nullable_graphql_type(Types::ContainerRegistry::Protection::TagRuleAccessLevelEnum) }
|
||||
end
|
||||
|
||||
describe 'minimum_access_level_for_delete' do
|
||||
subject { described_class.fields['minimumAccessLevelForDelete'] }
|
||||
|
||||
it { is_expected.to have_nullable_graphql_type(Types::ContainerRegistry::Protection::TagRuleAccessLevelEnum) }
|
||||
end
|
||||
|
||||
describe 'immutable' do
|
||||
subject { described_class.fields['immutable'] }
|
||||
|
||||
it { is_expected.to have_non_null_graphql_type(GraphQL::Types::Boolean) }
|
||||
end
|
||||
end
|
||||
|
|
@ -32,4 +32,10 @@ RSpec.describe GitlabSchema.types['ContainerProtectionTagRule'], feature_categor
|
|||
|
||||
it { is_expected.to have_nullable_graphql_type(Types::ContainerRegistry::Protection::TagRuleAccessLevelEnum) }
|
||||
end
|
||||
|
||||
describe 'immutable' do
|
||||
subject { described_class.fields['immutable'] }
|
||||
|
||||
it { is_expected.to have_non_null_graphql_type(GraphQL::Types::Boolean) }
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -45,7 +45,29 @@ RSpec.describe Authn::AgnosticTokenIdentifier, feature_category: :system_access
|
|||
end
|
||||
|
||||
with_them do
|
||||
it_behaves_like 'supported token type'
|
||||
context 'with default instance prefix' do
|
||||
it_behaves_like 'supported token type'
|
||||
end
|
||||
|
||||
context 'with custom instance prefix' do
|
||||
let_it_be(:instance_prefix) { 'instance-prefix-' }
|
||||
|
||||
before do
|
||||
stub_application_setting(instance_token_prefix: instance_prefix)
|
||||
end
|
||||
|
||||
# this will make sure that we find old tokens with the default instance prefix,
|
||||
# even if we have configured a custom one:
|
||||
it_behaves_like 'supported token type'
|
||||
end
|
||||
|
||||
context 'with feature flag custom_prefix_for_all_token_types disabled' do
|
||||
before do
|
||||
stub_feature_flags(custom_prefix_for_all_token_types: false)
|
||||
end
|
||||
|
||||
it_behaves_like 'supported token type'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -3,35 +3,89 @@
|
|||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Authn::TokenField::PrefixHelper, feature_category: :system_access do
|
||||
let(:prefix) { 'prefix' }
|
||||
describe '.prepend_instance_prefix' do
|
||||
let(:prefix) { 'prefix' }
|
||||
|
||||
subject(:prepend_instance_prefix) { described_class.prepend_instance_prefix(prefix) }
|
||||
subject(:prepend_instance_prefix) { described_class.prepend_instance_prefix(prefix) }
|
||||
|
||||
context 'with application config default value' do
|
||||
it 'prepends the instance wide token prefix' do
|
||||
expect(prepend_instance_prefix).to eq("gl#{prefix}")
|
||||
context 'with application config default value' do
|
||||
it 'prepends the instance wide token prefix' do
|
||||
expect(prepend_instance_prefix).to eq("gl#{prefix}")
|
||||
end
|
||||
end
|
||||
|
||||
context 'with application config set to custom value' do
|
||||
let(:instance_prefix) { 'instance-prefix-' }
|
||||
|
||||
before do
|
||||
stub_application_setting(instance_token_prefix: instance_prefix)
|
||||
end
|
||||
|
||||
it 'prepends the instance wide token prefix' do
|
||||
expect(prepend_instance_prefix).to eq("#{instance_prefix}#{prefix}")
|
||||
end
|
||||
end
|
||||
|
||||
context 'with feature flag custom_prefix_for_all_token_types disabled' do
|
||||
before do
|
||||
stub_feature_flags(custom_prefix_for_all_token_types: false)
|
||||
end
|
||||
|
||||
it 'does not prepend the instance wide token prefix' do
|
||||
expect(prepend_instance_prefix).to eq(prefix)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with application config set to custom value' do
|
||||
let(:instance_prefix) { 'instance-prefix-' }
|
||||
describe '.default_instance_prefix' do
|
||||
let(:prefix) { 'glprefix' }
|
||||
|
||||
before do
|
||||
stub_application_setting(instance_token_prefix: instance_prefix)
|
||||
subject(:default_instance_prefix) { described_class.default_instance_prefix(prefix) }
|
||||
|
||||
context 'with application config default value' do
|
||||
it 'returns the default prefix' do
|
||||
expect(default_instance_prefix).to eq(prefix)
|
||||
end
|
||||
end
|
||||
|
||||
it 'prepends the instance wide token prefix' do
|
||||
expect(prepend_instance_prefix).to eq("#{instance_prefix}#{prefix}")
|
||||
end
|
||||
end
|
||||
context 'with application config set to custom value' do
|
||||
let(:instance_prefix) { 'instance-prefix-' }
|
||||
|
||||
context 'with feature flag custom_prefix_for_all_token_types disabled' do
|
||||
before do
|
||||
stub_feature_flags(custom_prefix_for_all_token_types: false)
|
||||
before do
|
||||
stub_application_setting(instance_token_prefix: instance_prefix)
|
||||
end
|
||||
|
||||
context 'for the default prefix' do
|
||||
it 'still returns the default prefix' do
|
||||
expect(default_instance_prefix).to eq(prefix)
|
||||
end
|
||||
end
|
||||
|
||||
context 'for the current instance prefix' do
|
||||
let(:prefix) { "#{instance_prefix}token-prefix" }
|
||||
|
||||
it 'changes the instance prefix to the default prefix' do
|
||||
expect(default_instance_prefix).to eq('gltoken-prefix')
|
||||
end
|
||||
end
|
||||
|
||||
context 'for a non-matching prefix' do
|
||||
let(:prefix) { 'different-token-prefix' }
|
||||
|
||||
it 'keeps the prefix unchanged' do
|
||||
expect(default_instance_prefix).to eq(prefix)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it 'does not prepend the instance wide token prefix' do
|
||||
expect(prepend_instance_prefix).to eq(prefix)
|
||||
context 'with feature flag custom_prefix_for_all_token_types disabled' do
|
||||
before do
|
||||
stub_feature_flags(custom_prefix_for_all_token_types: false)
|
||||
end
|
||||
|
||||
it 'keeps the prefix unchanged' do
|
||||
expect(default_instance_prefix).to eq(prefix)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -5,12 +5,12 @@ require 'spec_helper'
|
|||
RSpec.describe Gitlab::Ci::Config::Entry::Retry do
|
||||
let(:entry) { described_class.new(config) }
|
||||
|
||||
shared_context 'when retry value is a numeric', :numeric do
|
||||
shared_context 'when retry value is a numeric' do
|
||||
let(:config) { max }
|
||||
let(:max) {}
|
||||
end
|
||||
|
||||
shared_context 'when retry value is a hash', :hash do
|
||||
shared_context 'when retry value is a hash' do
|
||||
let(:config) { { max: max, when: public_send(:when), exit_codes: exit_codes }.compact }
|
||||
let(:when) {}
|
||||
let(:exit_codes) {}
|
||||
|
|
@ -20,7 +20,9 @@ RSpec.describe Gitlab::Ci::Config::Entry::Retry do
|
|||
describe '#value' do
|
||||
subject(:value) { entry.value }
|
||||
|
||||
context 'when retry value is a numeric', :numeric do
|
||||
context 'when retry value is a numeric' do
|
||||
include_context 'when retry value is a numeric'
|
||||
|
||||
let(:max) { 2 }
|
||||
|
||||
it 'is returned as a hash with max key' do
|
||||
|
|
@ -28,7 +30,9 @@ RSpec.describe Gitlab::Ci::Config::Entry::Retry do
|
|||
end
|
||||
end
|
||||
|
||||
context 'when retry value is a hash', :hash do
|
||||
context 'when retry value is a hash' do
|
||||
include_context 'when retry value is a hash'
|
||||
|
||||
context 'and `when` is a string' do
|
||||
let(:when) { 'unknown_failure' }
|
||||
|
||||
|
|
@ -65,7 +69,9 @@ RSpec.describe Gitlab::Ci::Config::Entry::Retry do
|
|||
|
||||
describe 'validation' do
|
||||
context 'when retry value is correct' do
|
||||
context 'when it is a numeric', :numeric do
|
||||
context 'when it is a numeric' do
|
||||
include_context 'when retry value is a numeric'
|
||||
|
||||
let(:max) { 2 }
|
||||
|
||||
it 'is valid' do
|
||||
|
|
@ -73,7 +79,9 @@ RSpec.describe Gitlab::Ci::Config::Entry::Retry do
|
|||
end
|
||||
end
|
||||
|
||||
context 'when it is a hash', :hash do
|
||||
context 'when it is a hash' do
|
||||
include_context 'when retry value is a hash'
|
||||
|
||||
context 'with max' do
|
||||
let(:max) { 2 }
|
||||
|
||||
|
|
@ -175,7 +183,9 @@ RSpec.describe Gitlab::Ci::Config::Entry::Retry do
|
|||
end
|
||||
end
|
||||
|
||||
context 'when it is a numeric', :numeric do
|
||||
context 'when it is a numeric' do
|
||||
include_context 'when retry value is a numeric'
|
||||
|
||||
context 'when it is lower than zero' do
|
||||
let(:max) { -1 }
|
||||
|
||||
|
|
@ -205,7 +215,9 @@ RSpec.describe Gitlab::Ci::Config::Entry::Retry do
|
|||
end
|
||||
end
|
||||
|
||||
context 'when it is a hash', :hash do
|
||||
context 'when it is a hash' do
|
||||
include_context 'when retry value is a hash'
|
||||
|
||||
context 'with unknown keys' do
|
||||
let(:config) { { max: 2, unknown_key: :something, one_more: :key } }
|
||||
|
||||
|
|
|
|||
|
|
@ -40,6 +40,20 @@ RSpec.describe Gitlab::Ci::Parsers::Sbom::License, feature_category: :dependency
|
|||
end
|
||||
end
|
||||
|
||||
context "when the license has neither id nor name" do
|
||||
let(:data) do
|
||||
{
|
||||
"license" => {
|
||||
"url" => "https://example.com/license.txt"
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
it "returns nil" do
|
||||
is_expected.to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context "when the license is defined using an expression" do
|
||||
let(:data) do
|
||||
{
|
||||
|
|
|
|||
|
|
@ -9,24 +9,26 @@ RSpec.describe Gitlab::Git::MergeBase do
|
|||
|
||||
subject(:merge_base) { described_class.new(repository, refs) }
|
||||
|
||||
shared_context 'existing refs with a merge base', :existing_refs do
|
||||
shared_context 'existing refs with a merge base' do
|
||||
let(:refs) do
|
||||
%w[304d257dcb821665ab5110318fc58a007bd104ed 0031876facac3f2b2702a0e53a26e89939a42209]
|
||||
end
|
||||
end
|
||||
|
||||
shared_context 'when passing a missing ref', :missing_ref do
|
||||
shared_context 'when passing a missing ref' do
|
||||
let(:refs) do
|
||||
%w[304d257dcb821665ab5110318fc58a007bd104ed aaaa]
|
||||
end
|
||||
end
|
||||
|
||||
shared_context 'when passing refs that do not have a common ancestor', :no_common_ancestor do
|
||||
shared_context 'when passing refs that do not have a common ancestor' do
|
||||
let(:refs) { ['304d257dcb821665ab5110318fc58a007bd104ed', TestEnv::BRANCH_SHA['orphaned-branch']] }
|
||||
end
|
||||
|
||||
describe '#sha' do
|
||||
context 'when the refs exist', :existing_refs do
|
||||
context 'when the refs exist' do
|
||||
include_context 'existing refs with a merge base'
|
||||
|
||||
it 'returns the SHA of the merge base' do
|
||||
expect(merge_base.sha).not_to be_nil
|
||||
end
|
||||
|
|
@ -38,7 +40,9 @@ RSpec.describe Gitlab::Git::MergeBase do
|
|||
end
|
||||
end
|
||||
|
||||
context 'when passing a missing ref', :missing_ref do
|
||||
context 'when passing a missing ref' do
|
||||
include_context 'when passing a missing ref'
|
||||
|
||||
it 'does not call merge_base on the repository but raises an error' do
|
||||
expect(repository).not_to receive(:merge_base)
|
||||
|
||||
|
|
@ -46,8 +50,12 @@ RSpec.describe Gitlab::Git::MergeBase do
|
|||
end
|
||||
end
|
||||
|
||||
it 'returns `nil` when the refs do not have a common ancestor', :no_common_ancestor do
|
||||
expect(merge_base.sha).to be_nil
|
||||
context 'when the refs do not have a common ancestor' do
|
||||
include_context 'when passing refs that do not have a common ancestor'
|
||||
|
||||
it 'returns `nil`' do
|
||||
expect(merge_base.sha).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
it 'returns a merge base when passing 2 branch names' do
|
||||
|
|
@ -64,7 +72,9 @@ RSpec.describe Gitlab::Git::MergeBase do
|
|||
end
|
||||
|
||||
describe '#commit' do
|
||||
context 'for existing refs with a merge base', :existing_refs do
|
||||
context 'for existing refs with a merge base' do
|
||||
include_context 'existing refs with a merge base'
|
||||
|
||||
it 'finds the commit for the merge base' do
|
||||
expect(merge_base.commit).to be_a(Commit)
|
||||
end
|
||||
|
|
@ -76,14 +86,20 @@ RSpec.describe Gitlab::Git::MergeBase do
|
|||
end
|
||||
end
|
||||
|
||||
it 'does not try to find the commit when there is no sha', :no_common_ancestor do
|
||||
expect(repository).not_to receive(:commit_by)
|
||||
context 'when there is no sha' do
|
||||
include_context 'when passing refs that do not have a common ancestor'
|
||||
|
||||
merge_base.commit
|
||||
it 'does not try to find the commit' do
|
||||
expect(repository).not_to receive(:commit_by)
|
||||
|
||||
merge_base.commit
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#unknown_refs', :missing_ref do
|
||||
describe '#unknown_refs' do
|
||||
include_context 'when passing a missing ref'
|
||||
|
||||
it 'returns the refs passed that are not part of the repository' do
|
||||
expect(merge_base.unknown_refs).to contain_exactly('aaaa')
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,171 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Namespaces::AdjournedDeletable, feature_category: :groups_and_projects do
|
||||
let(:project) { build(:project) }
|
||||
|
||||
describe '#adjourned_deletion?' do
|
||||
it 'returns the result of adjourned_deletion_configured?', :aggregate_failures do
|
||||
expect(project).to receive(:adjourned_deletion_configured?).and_return(true)
|
||||
expect(project.adjourned_deletion?).to be true
|
||||
|
||||
expect(project).to receive(:adjourned_deletion_configured?).and_return(false)
|
||||
expect(project.adjourned_deletion?).to be false
|
||||
end
|
||||
end
|
||||
|
||||
describe '#adjourned_deletion_configured?' do
|
||||
context 'when deletion_adjourned_period is zero' do
|
||||
before do
|
||||
stub_application_setting(deletion_adjourned_period: 0)
|
||||
end
|
||||
|
||||
it 'returns false' do
|
||||
expect(project.adjourned_deletion_configured?).to be false
|
||||
end
|
||||
end
|
||||
|
||||
context 'when deletion_adjourned_period is positive' do
|
||||
before do
|
||||
stub_application_setting(deletion_adjourned_period: 7)
|
||||
end
|
||||
|
||||
context 'for groups' do
|
||||
let(:group) { build(:group) }
|
||||
|
||||
it 'returns true' do
|
||||
expect(group.adjourned_deletion_configured?).to be true
|
||||
end
|
||||
end
|
||||
|
||||
context 'when it is a personal project' do
|
||||
before do
|
||||
allow(project).to receive(:personal?).and_return(true)
|
||||
end
|
||||
|
||||
it 'returns false' do
|
||||
expect(project.adjourned_deletion_configured?).to be false
|
||||
end
|
||||
end
|
||||
|
||||
context 'when it is not a personal project' do
|
||||
before do
|
||||
allow(project).to receive(:personal?).and_return(false)
|
||||
end
|
||||
|
||||
it 'returns true' do
|
||||
expect(project.adjourned_deletion_configured?).to be true
|
||||
end
|
||||
|
||||
context 'when downtier_delayed_deletion feature flag is disabled' do
|
||||
before do
|
||||
stub_feature_flags(downtier_delayed_deletion: false)
|
||||
end
|
||||
|
||||
it 'returns false' do
|
||||
expect(project.adjourned_deletion_configured?).to be false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#marked_for_deletion?' do
|
||||
context 'when marked_for_deletion_at is present' do
|
||||
before do
|
||||
project.marked_for_deletion_at = Time.current
|
||||
end
|
||||
|
||||
it 'returns true' do
|
||||
expect(project.marked_for_deletion?).to be true
|
||||
end
|
||||
|
||||
context 'when downtier_delayed_deletion feature flag is disabled' do
|
||||
before do
|
||||
stub_feature_flags(downtier_delayed_deletion: false)
|
||||
end
|
||||
|
||||
it 'returns false' do
|
||||
expect(project.marked_for_deletion?).to be false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when marked_for_deletion_at is nil' do
|
||||
before do
|
||||
project.marked_for_deletion_at = nil
|
||||
end
|
||||
|
||||
it 'returns false' do
|
||||
expect(project.marked_for_deletion?).to be false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#self_or_ancestor_marked_for_deletion' do
|
||||
let_it_be(:group) { create(:group) }
|
||||
let_it_be_with_reload(:project) { create(:project, group: group) }
|
||||
|
||||
context 'when the project is marked for deletion' do
|
||||
before do
|
||||
project.update!(marked_for_deletion_on: Time.current)
|
||||
end
|
||||
|
||||
it 'returns self' do
|
||||
expect(project.self_or_ancestor_marked_for_deletion).to eq(project)
|
||||
end
|
||||
|
||||
context 'when downtier_delayed_deletion feature flag is disabled' do
|
||||
before do
|
||||
stub_feature_flags(downtier_delayed_deletion: false)
|
||||
end
|
||||
|
||||
it 'returns nil' do
|
||||
expect(project.self_or_ancestor_marked_for_deletion).to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the project is not marked for deletion' do
|
||||
context 'when an ancestor is marked for deletion' do
|
||||
let_it_be(:group) { create(:group_with_deletion_schedule) }
|
||||
let_it_be(:project) { create(:project, group: group) }
|
||||
|
||||
it 'returns the first ancestor marked for deletion' do
|
||||
expect(project.self_or_ancestor_marked_for_deletion).to eq(group)
|
||||
end
|
||||
|
||||
context 'when downtier_delayed_deletion feature flag is disabled' do
|
||||
before do
|
||||
stub_feature_flags(downtier_delayed_deletion: false)
|
||||
end
|
||||
|
||||
it 'returns nil' do
|
||||
expect(project.self_or_ancestor_marked_for_deletion).to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when no ancestor is marked for deletion' do
|
||||
it 'returns nil' do
|
||||
expect(project.self_or_ancestor_marked_for_deletion).to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#permanent_deletion_date' do
|
||||
let(:date) { Time.current }
|
||||
let(:adjourned_period) { 7 }
|
||||
|
||||
before do
|
||||
stub_application_setting(deletion_adjourned_period: adjourned_period)
|
||||
end
|
||||
|
||||
it 'returns the date plus the configured adjourned period in days', :freeze_time do
|
||||
expected_date = date + adjourned_period.days
|
||||
expect(project.permanent_deletion_date(date)).to eq(expected_date)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -44,7 +44,8 @@ RSpec.describe 'Deleting a container registry tag protection rule', :aggregate_f
|
|||
'id' => container_protection_rule.to_global_id.to_s,
|
||||
'tagNamePattern' => container_protection_rule.tag_name_pattern,
|
||||
'minimumAccessLevelForDelete' => container_protection_rule.minimum_access_level_for_delete.upcase,
|
||||
'minimumAccessLevelForPush' => container_protection_rule.minimum_access_level_for_push.upcase
|
||||
'minimumAccessLevelForPush' => container_protection_rule.minimum_access_level_for_push.upcase,
|
||||
'immutable' => container_protection_rule.immutable?
|
||||
}
|
||||
)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -18,6 +18,10 @@ RSpec.describe API::Groups, :with_current_organization, feature_category: :group
|
|||
let_it_be(:project3) { create(:project, namespace: group1, path: 'test', visibility_level: Gitlab::VisibilityLevel::PRIVATE) }
|
||||
let_it_be(:archived_project) { create(:project, namespace: group1, archived: true) }
|
||||
|
||||
before do
|
||||
stub_feature_flags(downtier_delayed_deletion: false)
|
||||
end
|
||||
|
||||
def expect_log_keys(caller_id:, route:, root_namespace:)
|
||||
expect(API::API::LOG_FORMATTER).to receive(:call) do |_severity, _datetime, _, data|
|
||||
expect(data.stringify_keys).to include(
|
||||
|
|
|
|||
|
|
@ -5,6 +5,10 @@ require 'spec_helper'
|
|||
RSpec.describe Organizations::GroupsController, feature_category: :cell do
|
||||
let_it_be(:organization) { create(:organization) }
|
||||
|
||||
before do
|
||||
stub_feature_flags(downtier_delayed_deletion: false)
|
||||
end
|
||||
|
||||
describe 'GET #new' do
|
||||
subject(:gitlab_request) { get new_groups_organization_path(organization) }
|
||||
|
||||
|
|
|
|||
|
|
@ -1,70 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rubocop_spec_helper'
|
||||
require 'rspec-parameterized'
|
||||
|
||||
require_relative '../../../../rubocop/cop/rspec/shared_groups_metadata'
|
||||
|
||||
RSpec.describe RuboCop::Cop::RSpec::SharedGroupsMetadata, feature_category: :tooling do
|
||||
context 'with hash metadata' do
|
||||
it 'flags metadata in shared example' do
|
||||
expect_offense(<<~RUBY)
|
||||
RSpec.shared_examples 'foo', feature_category: :shared do
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^ Avoid using metadata on shared examples and shared context. They might cause flaky tests. See https://gitlab.com/gitlab-org/gitlab/-/issues/404388
|
||||
end
|
||||
|
||||
shared_examples 'foo', feature_category: :shared do
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^ Avoid using metadata on shared examples and shared context. They might cause flaky tests. See https://gitlab.com/gitlab-org/gitlab/-/issues/404388
|
||||
end
|
||||
RUBY
|
||||
end
|
||||
|
||||
it 'flags metadata in shared context' do
|
||||
expect_offense(<<~RUBY)
|
||||
RSpec.shared_context 'foo', feature_category: :shared do
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^ Avoid using metadata on shared examples and shared context. They might cause flaky tests. See https://gitlab.com/gitlab-org/gitlab/-/issues/404388
|
||||
end
|
||||
|
||||
shared_context 'foo', feature_category: :shared do
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^ Avoid using metadata on shared examples and shared context. They might cause flaky tests. See https://gitlab.com/gitlab-org/gitlab/-/issues/404388
|
||||
end
|
||||
RUBY
|
||||
end
|
||||
end
|
||||
|
||||
context 'with symbol metadata' do
|
||||
it 'flags metadata in shared example' do
|
||||
expect_offense(<<~RUBY)
|
||||
RSpec.shared_examples 'foo', :aggregate_failures do
|
||||
^^^^^^^^^^^^^^^^^^^ Avoid using metadata on shared examples and shared context. They might cause flaky tests. See https://gitlab.com/gitlab-org/gitlab/-/issues/404388
|
||||
end
|
||||
|
||||
shared_examples 'foo', :aggregate_failures do
|
||||
^^^^^^^^^^^^^^^^^^^ Avoid using metadata on shared examples and shared context. They might cause flaky tests. See https://gitlab.com/gitlab-org/gitlab/-/issues/404388
|
||||
end
|
||||
RUBY
|
||||
end
|
||||
|
||||
it 'flags metadata in shared context' do
|
||||
expect_offense(<<~RUBY)
|
||||
RSpec.shared_context 'foo', :aggregate_failures do
|
||||
^^^^^^^^^^^^^^^^^^^ Avoid using metadata on shared examples and shared context. They might cause flaky tests. See https://gitlab.com/gitlab-org/gitlab/-/issues/404388
|
||||
end
|
||||
|
||||
shared_context 'foo', :aggregate_failures do
|
||||
^^^^^^^^^^^^^^^^^^^ Avoid using metadata on shared examples and shared context. They might cause flaky tests. See https://gitlab.com/gitlab-org/gitlab/-/issues/404388
|
||||
end
|
||||
RUBY
|
||||
end
|
||||
end
|
||||
|
||||
it 'does not flag if feature category is missing' do
|
||||
expect_no_offenses(<<~RUBY)
|
||||
RSpec.shared_examples 'foo' do
|
||||
end
|
||||
|
||||
shared_examples 'foo' do
|
||||
end
|
||||
RUBY
|
||||
end
|
||||
end
|
||||
|
|
@ -225,7 +225,9 @@ RSpec.configure do |config|
|
|||
config.include ClickHouseHelpers, :click_house
|
||||
config.include WorkItems::DataSync::AssociationsHelpers
|
||||
|
||||
config.shared_context_metadata_behavior = :apply_to_host_groups
|
||||
config.include_context 'when rendered has no HTML escapes', type: :view
|
||||
config.include_context 'with STI disabled', type: :model
|
||||
|
||||
include StubFeatureFlags
|
||||
include StubSnowplow
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
# goal of this context: provide an easy process for setting and using the current organization that is set
|
||||
# in the middleware for non-feature spec level specs.
|
||||
RSpec.shared_context 'with current_organization setting', shared_context: :metadata do # rubocop:disable RSpec/SharedGroupsMetadata -- We are actually using this for easy metadata setting
|
||||
RSpec.shared_context 'with current_organization setting' do
|
||||
unless method_defined?(:current_organization)
|
||||
let_it_be(:current_organization, reload: true) { create(:organization, name: 'Current Organization') }
|
||||
end
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ RSpec.shared_examples 'Model disables STI' do
|
|||
end
|
||||
end
|
||||
|
||||
RSpec.shared_examples 'STI disabled', type: :model do # rubocop:disable RSpec/SharedGroupsMetadata -- Shared example is run within every spec tagged `type: :model`
|
||||
RSpec.shared_context 'with STI disabled' do
|
||||
include_examples 'Model disables STI' do
|
||||
let(:models) { [described_class] }
|
||||
end
|
||||
|
|
|
|||
|
|
@ -3,6 +3,10 @@
|
|||
require 'spec_helper'
|
||||
|
||||
RSpec.describe 'groups/settings/_remove.html.haml' do
|
||||
before do
|
||||
stub_feature_flags(downtier_delayed_deletion: false)
|
||||
end
|
||||
|
||||
describe 'render' do
|
||||
it 'enables the Remove group button for a group' do
|
||||
group = build(:group)
|
||||
|
|
|
|||
Loading…
Reference in New Issue