Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2025-03-19 15:07:45 +00:00
parent fa91163a5b
commit 5f5a1d09aa
67 changed files with 789 additions and 209 deletions

View File

@ -213,7 +213,7 @@ semgrep-appsec-custom-rules:
stage: lint
extends:
- .semgrep-appsec-custom-rules:rules
image: returntocorp/semgrep
image: returntocorp/semgrep:1.99.0
needs: []
script:
- git fetch origin master

View File

@ -5,7 +5,6 @@ Layout/LineLength:
- 'app/controllers/application_controller.rb'
- 'app/controllers/groups/milestones_controller.rb'
- 'app/controllers/projects/issues_controller.rb'
- 'app/controllers/projects/jobs_controller.rb'
- 'app/controllers/projects/labels_controller.rb'
- 'app/controllers/projects/milestones_controller.rb'
- 'app/controllers/projects/notes_controller.rb'

View File

@ -2,14 +2,6 @@
# Cop supports --autocorrect.
Style/StringConcatenation:
Exclude:
- 'app/controllers/projects/labels_controller.rb'
- 'app/controllers/projects/milestones_controller.rb'
- 'app/models/concerns/cross_database_modification.rb'
- 'app/models/concerns/from_set_operator.rb'
- 'app/models/concerns/routable.rb'
- 'app/models/integrations/chat_message/merge_message.rb'
- 'app/models/integrations/chat_message/note_message.rb'
- 'app/models/namespace.rb'
- 'app/models/packages/go/module_version.rb'
- 'app/models/pool_repository.rb'
- 'app/models/project_wiki.rb'

View File

@ -1 +1 @@
db3ec43472f41609c94a3f0cf5382b26360638e1
53cd0a7f9b29bb9c76e294e25e15c2597bcc8800

View File

@ -8,6 +8,16 @@
"type": "string",
"format": "uri"
},
"spec": {
"type": "object",
"markdownDescription": "Specification for pipeline configuration. Must be declared at the top of a configuration file, in a header section separated from the rest of the configuration with `---`. [Learn More](https://docs.gitlab.com/ee/ci/yaml/#spec).",
"properties": {
"inputs": {
"$ref": "#/definitions/inputParameters"
}
},
"additionalProperties": false
},
"image": {
"$ref": "#/definitions/image",
"markdownDescription": "Defining `image` globally is deprecated. Use [`default`](https://docs.gitlab.com/ee/ci/yaml/#default) instead. [Learn more](https://docs.gitlab.com/ee/ci/yaml/#globally-defined-image-services-cache-before_script-after_script)."
@ -389,6 +399,156 @@
}
]
},
"inputParameters": {
"type": "object",
"markdownDescription": "Define parameters that can be populated in reusable CI/CD configuration files when added to a pipeline. [Learn More](https://docs.gitlab.com/ee/ci/yaml/inputs).",
"patternProperties": {
".*": {
"markdownDescription": "**Input Configuration**\n\nAvailable properties:\n- `type`: string (default), array, boolean, or number\n- `description`: Human-readable explanation of the parameter (supports Markdown)\n- `options`: List of allowed values\n- `default`: Value to use when not specified (makes input optional)\n- `regex`: Pattern that string values must match",
"oneOf": [
{
"type": "object",
"properties": {
"type": {
"type": "string",
"markdownDescription": "Force a specific input type. Defaults to 'string' when not specified. [Learn More](https://docs.gitlab.com/ee/ci/yaml/inputs/#input-types).",
"enum": [
"array",
"boolean",
"number",
"string"
],
"default": "string"
},
"description": {
"type": "string",
"markdownDescription": "Give a description to a specific input. The description does not affect the input, but can help people understand the input details or expected values. Supports markdown.",
"maxLength": 1024
},
"options": {
"type": "array",
"markdownDescription": "Specify a list of allowed values for an input.",
"items": {
"oneOf": [
{
"type": "string"
},
{
"type": "number"
},
{
"type": "boolean"
}
]
}
},
"regex": {
"type": "string",
"markdownDescription": "Specify a regular expression that the input must match. Only impacts inputs with a `type` of `string`."
},
"default": {
"markdownDescription": "Define default values for inputs when not specified. When you specify a default, the inputs are no longer mandatory."
}
},
"allOf": [
{
"if": {
"properties": {
"type": {
"enum": [
"string"
]
}
}
},
"then": {
"properties": {
"default": {
"type": [
"string",
"null"
]
}
}
}
},
{
"if": {
"properties": {
"type": {
"enum": [
"number"
]
}
}
},
"then": {
"properties": {
"default": {
"type": [
"number",
"null"
]
}
}
}
},
{
"if": {
"properties": {
"type": {
"enum": [
"boolean"
]
}
}
},
"then": {
"properties": {
"default": {
"type": [
"boolean",
"null"
]
}
}
}
},
{
"if": {
"properties": {
"type": {
"enum": [
"array"
]
}
}
},
"then": {
"properties": {
"default": {
"oneOf": [
{
"type": "array"
},
{
"type": "null"
}
]
}
}
}
}
],
"additionalProperties": false
},
{
"type": "null"
}
]
}
}
},
"include_item": {
"oneOf": [
{
@ -1760,8 +1920,59 @@
"default": false
},
"inputs": {
"markdownDescription": "Used to pass input values to included templates or components. [Learn More](https://docs.gitlab.com/ee/ci/yaml/inputs.html#set-input-values-when-using-include).",
"type": "object"
"markdownDescription": "Used to pass input values to included templates, components, downstream pipelines, or child pipelines. [Learn More](https://docs.gitlab.com/ee/ci/yaml/inputs.html).",
"type": "object",
"patternProperties": {
"^[a-zA-Z0-9_-]+$": {
"description": "Input parameter value that matches parameter names defined in spec:inputs of the included configuration.",
"oneOf": [
{
"type": "string",
"maxLength": 1024
},
{
"type": "number"
},
{
"type": "boolean"
},
{
"type": "array",
"items": {
"oneOf": [
{
"type": "string"
},
{
"type": "number"
},
{
"type": "boolean"
},
{
"type": "object",
"additionalProperties": true
},
{
"type": "array",
"items": {
"additionalProperties": true
}
}
]
}
},
{
"type": "object",
"additionalProperties": true
},
{
"type": "null"
}
]
}
},
"additionalProperties": false
},
"job": {
"allOf": [

View File

@ -9,24 +9,20 @@ import {
I18N_ERROR_MESSAGE,
} from './constants';
export const initJwtCiCdJobTokenEnabledToggle = () => {
const toggle = () => {
const toggleButton = document.querySelector('.js-jwt-ci-cd-job-token-enabled-toggle button');
toggleButton.click();
};
export const initSettingsToggles = () => {
let toastMessage = {};
const displayToast = (message, options = {}) => {
toastMessage.hide?.();
toastMessage = toast(message, options);
};
const el = document.querySelector('.js-jwt-ci-cd-job-token-enabled-toggle');
const input = document.querySelector('.js-jwt-ci-cd-job-token-enabled-input');
const elements = document.querySelectorAll('.js-setting-toggle');
if (!elements.length) return null;
if (el && input) {
return Array.from(elements).map((el) => {
const form = el.closest('form');
const input = form.querySelector('.js-setting-input');
const toggleButton = el.querySelector('button');
const toggleElement = initToggle(el);
toggleElement.$on('change', async (isEnabled) => {
@ -43,7 +39,7 @@ export const initJwtCiCdJobTokenEnabledToggle = () => {
displayToast(I18N_SUCCESS_MESSAGE, {
action: {
text: I18N_UNDO_ACTION_TEXT,
onClick: toggle,
onClick: () => toggleButton.click(),
},
});
} catch (_) {
@ -53,7 +49,7 @@ export const initJwtCiCdJobTokenEnabledToggle = () => {
displayToast(I18N_ERROR_MESSAGE, {
action: {
text: I18N_RETRY_ACTION_TEXT,
onClick: toggle,
onClick: () => toggleButton.click(),
},
});
} finally {
@ -62,7 +58,5 @@ export const initJwtCiCdJobTokenEnabledToggle = () => {
});
return toggleElement;
}
return null;
});
};

View File

@ -1,6 +1,6 @@
import initStaleRunnerCleanupSetting from 'ee_else_ce/group_settings/stale_runner_cleanup';
import { initAllowRunnerRegistrationTokenToggle } from '~/group_settings/allow_runner_registration_token_toggle';
import { initJwtCiCdJobTokenEnabledToggle } from '~/group_settings/jwt_ci_cd_job_token_enabled_toggle';
import { initSettingsToggles } from '~/group_settings/settings_toggles';
import initPipelineVariablesDefaultRole from '~/group_settings/pipeline_variables_default_role';
import initVariableList from '~/ci/ci_variable_list';
@ -12,7 +12,7 @@ import initDeployTokens from '~/deploy_tokens';
initSettingsPanels();
initDeployTokens();
initAllowRunnerRegistrationTokenToggle();
initJwtCiCdJobTokenEnabledToggle();
initSettingsToggles();
initSharedRunnersForm();
initStaleRunnerCleanupSetting();
initVariableList();

View File

@ -98,7 +98,8 @@ module Groups
params.require(:group).permit(
:max_artifacts_size,
:allow_runner_registration_token,
:jwt_ci_cd_job_token_enabled
:jwt_ci_cd_job_token_enabled,
:job_token_policies_enabled
)
end

View File

@ -6,7 +6,8 @@ class Projects::JobsController < Projects::ApplicationController
include ContinueParams
include ProjectStatsRefreshConflictsGuard
urgency :low, [:index, :show, :trace, :retry, :play, :cancel, :unschedule, :erase, :viewer, :raw, :test_report_summary]
urgency :low,
[:index, :show, :trace, :retry, :play, :cancel, :unschedule, :erase, :viewer, :raw, :test_report_summary]
before_action :find_job_as_build, except: [:index, :play, :retry, :show]
before_action :find_job_as_processable, only: [:play, :retry, :show]
@ -149,7 +150,8 @@ class Projects::JobsController < Projects::ApplicationController
def raw
if @build.trace.archived?
workhorse_set_content_type!
send_upload(@build.job_artifacts_trace.file, send_params: raw_send_params, redirect_params: raw_redirect_params, proxy: params[:proxy])
send_upload(@build.job_artifacts_trace.file, send_params: raw_send_params, redirect_params: raw_redirect_params,
proxy: params[:proxy])
else
@build.trace.read do |stream|
if stream.file?
@ -161,7 +163,8 @@ class Projects::JobsController < Projects::ApplicationController
# to the user but, because we have the trace content, we can calculate
# the proper content type and disposition here.
raw_data = stream.raw
send_data raw_data, type: 'text/plain; charset=utf-8', disposition: raw_trace_content_disposition(raw_data), filename: 'job.log'
send_data raw_data, type: 'text/plain; charset=utf-8', disposition: raw_trace_content_disposition(raw_data),
filename: 'job.log'
end
end
end

View File

@ -148,7 +148,8 @@ class Projects::LabelsController < Projects::ApplicationController
end
def flash_notice_for(label, group)
''.html_safe + "#{label.title} promoted to " + view_context.link_to('<u>group label</u>'.html_safe, group_labels_path(group)) + '.'
safe_link = view_context.link_to('<u>group label</u>'.html_safe, group_labels_path(group))
view_context.safe_join([ERB::Util.html_escape(label.title), " promoted to ", safe_link, "."])
end
protected

View File

@ -131,7 +131,8 @@ class Projects::MilestonesController < Projects::ApplicationController
end
def flash_notice_for(milestone, group)
''.html_safe + "#{milestone.title} promoted to " + view_context.link_to('<u>group milestone</u>'.html_safe, group_milestone_path(group, milestone.iid)) + '.'
safe_link = view_context.link_to('<u>group milestone</u>'.html_safe, group_milestone_path(group, milestone.iid))
view_context.safe_join([ERB::Util.html_escape(milestone.title), " promoted to ", safe_link, "."])
end
def destroy

View File

@ -9,7 +9,8 @@ module Mutations
:extensions_marketplace_opt_in_status,
:organization_groups_projects_display,
:visibility_pipeline_id_type,
:use_work_items_view
:use_work_items_view,
:merge_request_dashboard_list_type
].freeze
argument :extensions_marketplace_opt_in_status, Types::ExtensionsMarketplaceOptInStatusEnum,
@ -18,6 +19,9 @@ module Mutations
argument :issues_sort, Types::IssueSortEnum,
required: false,
description: 'Sort order for issue lists.'
argument :merge_request_dashboard_list_type, Types::MergeRequests::DashboardListTypeEnum,
required: false,
description: 'Merge request dashboard list rendering type.'
argument :merge_requests_sort, Types::MergeRequestSortEnum,
required: false,
description: 'Sort order for issue lists.'

View File

@ -0,0 +1,13 @@
# frozen_string_literal: true
module Types
module MergeRequests
class DashboardListTypeEnum < BaseEnum
graphql_name 'MergeRequestsDashboardListType'
description 'Values for merge request dashboard list type'
value 'ACTION_BASED', 'Action based list rendering.', value: 'action_based'
value 'ROLE_BASED', 'Role based list rendering.', value: 'role_based'
end
end
end

View File

@ -25,6 +25,10 @@ module Types
description: 'Use work item view instead of legacy issue view.',
null: true
field :merge_request_dashboard_list_type, Types::MergeRequests::DashboardListTypeEnum,
description: 'Merge request dashboard list rendering type.',
null: true
field :projects_sort,
Types::Projects::ProjectSortEnum,
description: 'Sort order for projects.',

View File

@ -67,8 +67,9 @@ module Ci
origin_project_id = authorizations[:origin_project_id]
return unless accessed_project_id && origin_project_id
policies = authorizations.fetch(:policies, []).map(&:to_s)
Ci::JobToken::LogAuthorizationWorker # rubocop:disable CodeReuse/Worker -- This method is called from a middleware and it's better tested
.perform_in(CAPTURE_DELAY, accessed_project_id, origin_project_id)
.perform_in(CAPTURE_DELAY, accessed_project_id, origin_project_id, policies)
end
def self.log_captures!(accessed_project_id:, origin_project_id:, policies: [])

View File

@ -13,7 +13,7 @@ module CrossDatabaseModification
].freeze
def self.logger
@logger ||= Logger.new(LOG_FILENAME, formatter: ->(_, _, _, msg) { Gitlab::Json.dump(msg) + "\n" })
@logger ||= Logger.new(LOG_FILENAME, formatter: ->(_, _, _, msg) { "#{Gitlab::Json.dump(msg)}\n" })
end
def self.log_gitlab_transactions_stack(action: nil, example: nil)

View File

@ -5,7 +5,7 @@ module FromSetOperator
# of UNION, INTERSECT, and EXCEPT as defined by Gitlab::SQL::Union,
# Gitlab::SQL::Intersect, and Gitlab::SQL::Except respectively.
def define_set_operator(operator)
method_name = 'from_' + operator.name.demodulize.downcase
method_name = "from_#{operator.name.demodulize.downcase}"
method_name = method_name.to_sym
raise "Trying to redefine method '#{method(method_name)}'" if methods.include?(method_name)

View File

@ -136,7 +136,7 @@ module Routable
def build_full_path
if parent && path
parent.full_path + '/' + path
"#{parent.full_path}/#{path}"
else
path
end
@ -191,7 +191,7 @@ module Routable
def build_full_name
if parent && name
parent.human_name + ' / ' + name
"#{parent.human_name} / #{name}"
else
name
end

View File

@ -39,7 +39,7 @@ module Integrations
private
def format_title(title)
'*' + strip_markup(title.lines.first.chomp) + '*'
"*#{strip_markup(title.lines.first.chomp)}*"
end
def message

View File

@ -35,7 +35,7 @@ module Integrations
def activity
{
title: "#{strip_markup(user_combined_name)} #{link('commented on ' + target, note_url)}",
title: "#{strip_markup(user_combined_name)} #{link("commented on #{target}", note_url)}",
subtitle: "in #{project_link}",
text: strip_markup(formatted_title),
image: user_avatar
@ -45,7 +45,7 @@ module Integrations
private
def message
"#{strip_markup(user_combined_name)} #{link('commented on ' + target, note_url)} in #{project_link}: *#{strip_markup(formatted_title)}*"
"#{strip_markup(user_combined_name)} #{link("commented on #{target}", note_url)} in #{project_link}: *#{strip_markup(formatted_title)}*"
end
def format_title(title)

View File

@ -198,7 +198,9 @@ class Namespace < ApplicationRecord
:resource_access_token_notify_inherited_locked_by_ancestor?,
:resource_access_token_notify_inherited_locked_by_application_setting?,
to: :namespace_settings
delegate :jwt_ci_cd_job_token_enabled?, to: :namespace_settings
delegate :jwt_ci_cd_job_token_enabled?,
:job_token_policies_enabled?,
to: :namespace_settings
before_create :sync_share_with_group_lock_with_parent
before_update :sync_share_with_group_lock_with_parent, if: :parent_changed?
@ -571,7 +573,7 @@ class Namespace < ApplicationRecord
path_before_last_save
else
previous_parent = Group.find_by(id: parent_id_before_last_save)
previous_parent.full_path + '/' + path_before_last_save
"#{previous_parent.full_path}/#{path_before_last_save}"
end
end

View File

@ -55,6 +55,7 @@ class NamespaceSetting < ApplicationRecord
math_rendering_limits_enabled
lock_math_rendering_limits_enabled
jwt_ci_cd_job_token_enabled
job_token_policies_enabled
].freeze
# matches the size set in the database constraint

View File

@ -3486,7 +3486,8 @@ class Project < ApplicationRecord
end
def job_token_policies_enabled?
Feature.enabled?(:add_policies_to_ci_job_token, self)
Feature.enabled?(:add_policies_to_ci_job_token, self) ||
namespace.root_ancestor.namespace_settings&.job_token_policies_enabled?
end
strong_memoize_attr :job_token_policies_enabled?

View File

@ -63,6 +63,16 @@ class Upload < ApplicationRecord
store_class.new.delete_keys_async(keys)
end
end
def destroy_for_associations!(records, uploader = AttachmentUploader)
return if records.blank?
for_model_type_and_id(records.klass, records.pluck_primary_key)
.for_uploader(uploader)
.then { |uploads| [uploads, uploads.begin_fast_destroy] }
.tap { |uploads, _| uploads.delete_all }
.tap { |_, files| finalize_fast_destroy(files) }
end
end
def absolute_path

View File

@ -12,7 +12,7 @@ module Uploads
def delete_keys_async(keys_to_delete)
keys_to_delete.each_slice(BATCH_SIZE) do |batch|
DeleteStoredFilesWorker.perform_async(self.class, batch)
DeleteStoredFilesWorker.perform_async(self.class.name, batch)
end
end
end

View File

@ -463,6 +463,7 @@ class User < ApplicationRecord
:use_work_items_view, :use_work_items_view=,
:text_editor, :text_editor=,
:default_text_editor_enabled, :default_text_editor_enabled=,
:merge_request_dashboard_list_type, :merge_request_dashboard_list_type=,
to: :user_preference
delegate :path, to: :namespace, allow_nil: true, prefix: true

View File

@ -46,6 +46,8 @@ class UserPreference < ApplicationRecord
enum :extensions_marketplace_opt_in_status, Enums::WebIde::ExtensionsMarketplaceOptInStatus.statuses
enum :organization_groups_projects_display, { projects: 0, groups: 1 }
enum :merge_request_dashboard_list_type, { action_based: 0, role_based: 1 }
class << self
def notes_filters
{

View File

@ -39,6 +39,10 @@ module NamespaceSettings
param_key: :enabled_git_access_protocol,
user_policy: :update_git_access_protocol
)
validate_settings_param_for_root_group(
param_key: :job_token_policies_enabled,
user_policy: :admin_group
)
handle_default_branch_name
handle_default_branch_protection unless settings_params[:default_branch_protection].blank?

View File

@ -18,8 +18,19 @@
= link_to _('Learn more.'), help_page_path('ci/jobs/ci_job_token.md', anchor: 'use-legacy-format-for-cicd-tokens'), target: '_blank', rel: 'noopener noreferrer'
.gl-mb-5
= gitlab_ui_form_for group, url: group_settings_ci_cd_path(group, anchor: 'js-general-pipeline-settings') do |f|
= f.hidden_field :jwt_ci_cd_job_token_enabled, class: 'js-jwt-ci-cd-job-token-enabled-input', value: group.jwt_ci_cd_job_token_enabled?
= render Pajamas::ToggleComponent.new(classes: 'js-jwt-ci-cd-job-token-enabled-toggle',
= f.hidden_field :jwt_ci_cd_job_token_enabled, class: 'js-setting-input', value: group.jwt_ci_cd_job_token_enabled?
= render Pajamas::ToggleComponent.new(classes: 'js-setting-toggle',
label: s_('GroupSettings|Enable JWT format for CI/CD job tokens'),
is_checked: group.jwt_ci_cd_job_token_enabled?) do
= s_('GroupSettings|Enable JWT format for the CI_JOB_TOKEN variable. When disabled, it uses the legacy database format.')
.gl-mb-5
= gitlab_ui_form_for group, url: group_settings_ci_cd_path(group, anchor: 'js-general-pipeline-settings') do |f|
%span.gl-toggle-label.gl-shrink-0= s_('GroupSettings|Enable fine-grained permissions for CI/CD job tokens')
%span= render_if_exists 'shared/experimental_badge_tag'
= f.hidden_field :job_token_policies_enabled, class: 'js-setting-input', value: group.job_token_policies_enabled?
= render Pajamas::ToggleComponent.new(classes: 'js-setting-toggle gl-mt-3',
label: s_('GroupSettings|Enable fine-grained permissions for CI/CD job tokens'),
label_position: :hidden,
is_checked: group.job_token_policies_enabled?) do
= s_('GroupSettings|Enable fine-grained permissions for CI/CD job tokens.')
= link_to _('Learn more.'), help_page_path('ci/jobs/fine_grained_permissions.md'), target: '_blank', rel: 'noopener noreferrer'

View File

@ -1,9 +0,0 @@
---
name: label_keep_around_ref_metrics
feature_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/383814
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/145988
rollout_issue_url:
milestone: '16.10'
group: group::gitaly::git
type: ops
default_enabled: false

View File

@ -1000,6 +1000,9 @@ Gitlab.ee do
Settings.cron_jobs['analytics_dump_ai_user_metrics_database_write_buffer_cron_worker'] ||= {}
Settings.cron_jobs['analytics_dump_ai_user_metrics_database_write_buffer_cron_worker']['cron'] ||= "*/10 * * * *"
Settings.cron_jobs['analytics_dump_ai_user_metrics_database_write_buffer_cron_worker']['job_class'] = 'Analytics::DumpAiUserMetricsWriteBufferCronWorker'
Settings.cron_jobs['delete_expired_vulnerability_exports_worker'] ||= {}
Settings.cron_jobs['delete_expired_vulnerability_exports_worker']['cron'] ||= '0 4 * * *'
Settings.cron_jobs['delete_expired_vulnerability_exports_worker']['job_class'] = 'Vulnerabilities::DeleteExpiredExportsWorker'
Gitlab.com do
Settings.cron_jobs['disable_legacy_open_source_license_for_inactive_projects'] ||= {}

View File

@ -0,0 +1,9 @@
# frozen_string_literal: true
class AddMergeRequestDashboardListTypeToUserPreferences < Gitlab::Database::Migration[2.2]
milestone '17.11'
def change
add_column :user_preferences, :merge_request_dashboard_list_type, :smallint, default: 0, null: false
end
end

View File

@ -0,0 +1,10 @@
# frozen_string_literal: true
class AddJobTokenPoliciesEnabledColumnToNamespaceSettings < Gitlab::Database::Migration[2.2]
milestone '17.11'
enable_lock_retries!
def change
add_column :namespace_settings, :job_token_policies_enabled, :boolean, default: false, null: false
end
end

View File

@ -0,0 +1 @@
821adcb681f8c7618ce7560acb7dfbc36b0004ba6cd9144e76f761536bc574fa

View File

@ -0,0 +1 @@
968664b5b33cf194f36a9ba5b28490471405e94272c64668264ec0c11d22a672

View File

@ -17475,6 +17475,7 @@ CREATE TABLE namespace_settings (
jwt_ci_cd_job_token_enabled boolean DEFAULT false NOT NULL,
jwt_ci_cd_job_token_opted_out boolean DEFAULT false NOT NULL,
require_dpop_for_manage_api_endpoints boolean DEFAULT true NOT NULL,
job_token_policies_enabled boolean DEFAULT false NOT NULL,
CONSTRAINT check_0ba93c78c7 CHECK ((char_length(default_branch_name) <= 255)),
CONSTRAINT namespace_settings_unique_project_download_limit_alertlist_size CHECK ((cardinality(unique_project_download_limit_alertlist) <= 100)),
CONSTRAINT namespace_settings_unique_project_download_limit_allowlist_size CHECK ((cardinality(unique_project_download_limit_allowlist) <= 100))
@ -23498,6 +23499,7 @@ CREATE TABLE user_preferences (
dpop_enabled boolean DEFAULT false NOT NULL,
use_work_items_view boolean DEFAULT false NOT NULL,
text_editor_type smallint DEFAULT 0 NOT NULL,
merge_request_dashboard_list_type smallint DEFAULT 0 NOT NULL,
CONSTRAINT check_1d670edc68 CHECK ((time_display_relative IS NOT NULL)),
CONSTRAINT check_89bf269f41 CHECK ((char_length(diffs_deletion_color) <= 7)),
CONSTRAINT check_b1306f8875 CHECK ((char_length(organization_groups_projects_sort) <= 64)),

View File

@ -59,10 +59,10 @@ The following table lists the GitLab Duo features, and whether they are availabl
| [Refactor Code](../../user/gitlab_duo_chat/examples.md#refactor-code-in-the-ide) | {{< icon name="check-circle-filled" >}} Yes | GitLab 17.9 and later |
| [Fix Code](../../user/gitlab_duo_chat/examples.md#fix-code-in-the-ide) | {{< icon name="check-circle-filled" >}} Yes | GitLab 17.9 and later |
| [AI Impact Dashboard](../../user/analytics/ai_impact_analytics.md) | {{< icon name="check-circle-dashed" >}} Beta | GitLab 17.9 and later |
| [Root Cause Analysis](../../user/gitlab_duo_chat/examples.md#troubleshoot-failed-cicd-jobs-with-root-cause-analysis) | {{< icon name="check-circle-dashed" >}} Beta | GitLab 17.10 and later |
| [Discussion Summary](../../user/discussions/_index.md#summarize-issue-discussions-with-duo-chat) | {{< icon name="dash-circle" >}} No | Not applicable |
| [GitLab Duo for the CLI](../../editor_extensions/gitlab_cli/_index.md#gitlab-duo-for-the-cli) | {{< icon name="dash-circle" >}} No | Not applicable |
| [Merge Commit Message Generation](../../user/project/merge_requests/duo_in_merge_requests.md#generate-a-merge-commit-message) | {{< icon name="dash-circle" >}} No | Not applicable |
| [Root Cause Analysis](../../user/gitlab_duo_chat/examples.md#troubleshoot-failed-cicd-jobs-with-root-cause-analysis) | {{< icon name="dash-circle" >}} No | Not applicable |
| [Vulnerability Explanation](../../user/application_security/vulnerabilities/_index.md#explaining-a-vulnerability) | {{< icon name="dash-circle" >}} No | Not applicable |
| [Vulnerability Resolution](../../user/application_security/vulnerabilities/_index.md#vulnerability-resolution) | {{< icon name="dash-circle" >}} No | Not applicable |

View File

@ -132,7 +132,7 @@ Configure the GitLab Duo feature and sub-feature to send queries to the configur
1. Select the **AI-powered features** tab.
1. For the feature and sub-feature you want to configure, from the dropdown list, choose the self-hosted model you want to use.
For example, for the the code generation sub-feature under GitLab Duo Code Suggestions, you can select **claude sonnet on bedrock (Claude 3)**.
For example, for the code generation sub-feature under GitLab Duo Code Suggestions, you can select **claude sonnet on bedrock (Claude 3)**.
![GitLab Duo Self-Hosted Feature Configuration](../img/gitlab_duo_self_hosted_feature_configuration_v17_10.png)

View File

@ -11816,6 +11816,7 @@ Input type: `UserPreferencesUpdateInput`
| <a id="mutationuserpreferencesupdateclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationuserpreferencesupdateextensionsmarketplaceoptinstatus"></a>`extensionsMarketplaceOptInStatus` | [`ExtensionsMarketplaceOptInStatus`](#extensionsmarketplaceoptinstatus) | Status of the Web IDE Extension Marketplace opt-in for the user. |
| <a id="mutationuserpreferencesupdateissuessort"></a>`issuesSort` | [`IssueSort`](#issuesort) | Sort order for issue lists. |
| <a id="mutationuserpreferencesupdatemergerequestdashboardlisttype"></a>`mergeRequestDashboardListType` | [`MergeRequestsDashboardListType`](#mergerequestsdashboardlisttype) | Merge request dashboard list rendering type. |
| <a id="mutationuserpreferencesupdatemergerequestssort"></a>`mergeRequestsSort` | [`MergeRequestSort`](#mergerequestsort) | Sort order for issue lists. |
| <a id="mutationuserpreferencesupdateorganizationgroupsprojectsdisplay"></a>`organizationGroupsProjectsDisplay` {{< icon name="warning-solid" >}} | [`OrganizationGroupProjectDisplay`](#organizationgroupprojectdisplay) | **Deprecated:** **Status**: Experiment. Introduced in GitLab 17.2. |
| <a id="mutationuserpreferencesupdateorganizationgroupsprojectssort"></a>`organizationGroupsProjectsSort` {{< icon name="warning-solid" >}} | [`OrganizationGroupProjectSort`](#organizationgroupprojectsort) | **Deprecated:** **Status**: Experiment. Introduced in GitLab 17.2. |
@ -39103,6 +39104,7 @@ fields relate to interactions between the two entities.
| ---- | ---- | ----------- |
| <a id="userpreferencesextensionsmarketplaceoptinstatus"></a>`extensionsMarketplaceOptInStatus` | [`ExtensionsMarketplaceOptInStatus!`](#extensionsmarketplaceoptinstatus) | Status of the Web IDE Extension Marketplace opt-in for the user. |
| <a id="userpreferencesissuessort"></a>`issuesSort` | [`IssueSort`](#issuesort) | Sort order for issue lists. |
| <a id="userpreferencesmergerequestdashboardlisttype"></a>`mergeRequestDashboardListType` | [`MergeRequestsDashboardListType`](#mergerequestsdashboardlisttype) | Merge request dashboard list rendering type. |
| <a id="userpreferencesorganizationgroupsprojectsdisplay"></a>`organizationGroupsProjectsDisplay` {{< icon name="warning-solid" >}} | [`OrganizationGroupProjectDisplay!`](#organizationgroupprojectdisplay) | **Introduced** in GitLab 17.2. **Status**: Experiment. Default list view for organization groups and projects. |
| <a id="userpreferencesorganizationgroupsprojectssort"></a>`organizationGroupsProjectsSort` {{< icon name="warning-solid" >}} | [`OrganizationGroupProjectSort`](#organizationgroupprojectsort) | **Introduced** in GitLab 17.2. **Status**: Experiment. Sort order for organization groups and projects. |
| <a id="userpreferencesprojectssort"></a>`projectsSort` | [`ProjectSort`](#projectsort) | Sort order for projects. |
@ -43174,6 +43176,15 @@ State of a GitLab merge request.
| <a id="mergerequeststatemerged"></a>`merged` | Merge request has been merged. |
| <a id="mergerequeststateopened"></a>`opened` | Opened merge request. |
### `MergeRequestsDashboardListType`
Values for merge request dashboard list type.
| Value | Description |
| ----- | ----------- |
| <a id="mergerequestsdashboardlisttypeaction_based"></a>`ACTION_BASED` | Action based list rendering. |
| <a id="mergerequestsdashboardlisttyperole_based"></a>`ROLE_BASED` | Role based list rendering. |
### `MergeStatus`
Representation of whether a GitLab merge request can be merged.

View File

@ -29,33 +29,26 @@ Status: Experiment
{{< /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 >}}
You can use fine-grained permissions to explicitly allow access to a limited set of API endpoints.
These permissions are applied to the CI/CD job tokens in a specified project.
This feature is an [experiment](../../policy/development_stages_support.md#experiment).
## Enable fine-grained permissions
This feature is an [experiment](../../policy/development_stages_support.md#experiment) and subject to change without notice. This feature is not ready for production use. If you want to use this feature, you should test outside of production first.
### On GitLab Self-Managed
## Enable fine-grained permissions for projects
1. Start the GitLab Rails console. For information, see [Enable and disable GitLab features deployed behind feature flags](../../administration/feature_flags.md#enable-or-disable-the-feature)
1. Turn on the [feature flag](../../administration/feature_flags.md):
Prerequisites:
```ruby
# You must include a specific project ID with this command.
Feature.enable(:add_policies_to_ci_job_token, <project_id>)
```
- You must have the Owner role for a group.
### On GitLab.com
You must turn on fine-grained permissions at the group level. Then, each project in the group can
apply fine-grained permissions for CI/CD job tokens to grant access to individual resources.
Add a comment on this [issue](https://gitlab.com/gitlab-org/gitlab/-/issues/519575) with your project ID.
To enable fine-grained permissions for all projects in a group:
1. On the left sidebar, select **Search or go to** and find your group.
1. On the left sidebar, select **Settings > CI/CD**.
1. Expand **General pipelines**.
1. Turn on the **Enable fine-grained permissions for CI/CD job tokens** toggle.
## Available API endpoints

View File

@ -44,7 +44,7 @@ Please refer to [the documentation](../code_review.md#reviewer-roulette)
### Ask for help
If contributors have questions or need additional help with Python-specific reviews, direct them to the GitLab #python or #python_maintainers Slack channels for assistance.
If contributors have questions or need additional help with Python-specific reviews, direct them to the GitLab `#python` or `#python_maintainers` Slack channels for assistance.
## How to find a project to review

View File

@ -62,7 +62,8 @@ To auto-format this table, use the VS Code Markdown Table formatter: `https://do
| Command | Issue | Merge request | Epic | Action |
|:------------------------------------------------------------------------------------------------|:-----------------------|:-----------------------|:-----------------------|:-------|
| `/add_contacts [contact:email1@example.com] [contact:email2@example.com]` | {{< icon name="check-circle" >}} Yes | {{< icon name="dotted-circle" >}} No | {{< icon name="dotted-circle" >}} No | Add one or more active [CRM contacts](../crm/_index.md) ([introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/73413) in GitLab 14.6). |
| `/add_child <item>` | {{< icon name="check-circle" >}} Yes | {{< icon name="dotted-circle" >}} No | {{< icon name="check-circle" >}} Yes | Add `<item>` as a child item. The `<item>` value should be in the format of `#item`, `group/project#item`, or a URL to the item. For issues, you can add tasks and OKRs. Your administrator must have [enabled the new look for issues](../project/issues/issue_work_items.md). For epics, you can add issues, tasks, and OKRs. Multiple work items can be added as child items at the same time. Your administrator must have [enabled the new look for epics](../group/epics/epic_work_items.md). |
| `/add_contacts [contact:email1@example.com] [contact:email2@example.com]` | {{< icon name="check-circle" >}} Yes | {{< icon name="dotted-circle" >}} No | {{< icon name="dotted-circle" >}} No | Add one or more active [CRM contacts](../crm/_index.md). |
| `/add_email email1 email2` | {{< icon name="check-circle" >}} Yes | {{< icon name="dotted-circle" >}} No | {{< icon name="dotted-circle" >}} No | Add up to six [email participants](service_desk/external_participants.md). This action is behind the feature flag `issue_email_participants`. Not supported in [issue templates](description_templates.md). |
| `/approve` | {{< icon name="dotted-circle" >}} No | {{< icon name="check-circle" >}} Yes | {{< icon name="dotted-circle" >}} No | Approve the merge request. |
| `/assign @user1 @user2` | {{< icon name="check-circle" >}} Yes | {{< icon name="check-circle" >}} Yes | {{< icon name="dotted-circle" >}} No | Assign one or more users. |
@ -110,6 +111,7 @@ To auto-format this table, use the VS Code Markdown Table formatter: `https://do
| `/rebase` | {{< icon name="dotted-circle" >}} No | {{< icon name="check-circle" >}} Yes | {{< icon name="dotted-circle" >}} No | Rebase source branch on the latest commit of the target branch. For help, see [troubleshooting information](../../topics/git/troubleshooting_git.md). |
| `/relabel ~label1 ~label2` | {{< icon name="check-circle" >}} Yes | {{< icon name="check-circle" >}} Yes | {{< icon name="check-circle" >}} Yes | Replace current labels with those specified. |
| `/relate <item1> <item2>` | {{< icon name="check-circle" >}} Yes | {{< icon name="dotted-circle" >}} No | {{< icon name="dotted-circle" >}} No | Mark items as related. The `<item>` value should be in the format of `#item`, `group/project#item`, or the full URL. |
| `/remove_child <item>` | {{< icon name="check-circle" >}} Yes | {{< icon name="dotted-circle" >}} No | {{< icon name="check-circle" >}} Yes | Remove `<item>` as child. The `<item>` value should be in the format of `#item`, `group/project#item`, or a URL to the item. For issues, your administrator must have [enabled the new look for issues](../project/issues/issue_work_items.md). For epics, your administrator must have [enabled the new look for epics](../group/epics/epic_work_items.md). |
| `/remove_child_epic <epic>` | {{< icon name="dotted-circle" >}} No | {{< icon name="dotted-circle" >}} No | {{< icon name="check-circle" >}} Yes | Remove child epic from `<epic>`. The `<epic>` value should be in the format of `&epic`, `group&epic`, or a URL to an epic. |
| `/remove_contacts [contact:email1@example.com] [contact:email2@example.com]` | {{< icon name="check-circle" >}} Yes | {{< icon name="dotted-circle" >}} No | {{< icon name="dotted-circle" >}} No | Remove one or more [CRM contacts](../crm/_index.md) |
| `/remove_due_date` | {{< icon name="check-circle" >}} Yes | {{< icon name="dotted-circle" >}} No | {{< icon name="dotted-circle" >}} No | Remove due date. |
@ -118,6 +120,7 @@ To auto-format this table, use the VS Code Markdown Table formatter: `https://do
| `/remove_estimate` or `/remove_time_estimate` | {{< icon name="check-circle" >}} Yes | {{< icon name="check-circle" >}} Yes | {{< icon name="dotted-circle" >}} No | Remove time estimate. Alias `/remove_time_estimate` [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/16501) in GitLab 15.6. |
| `/remove_iteration` | {{< icon name="check-circle" >}} Yes | {{< icon name="dotted-circle" >}} No | {{< icon name="dotted-circle" >}} No | Remove iteration. |
| `/remove_milestone` | {{< icon name="check-circle" >}} Yes | {{< icon name="check-circle" >}} Yes | {{< icon name="dotted-circle" >}} No | Remove milestone. |
| `/remove_parent` | {{< icon name="check-circle" >}} Yes | {{< icon name="dotted-circle" >}} No | {{< icon name="check-circle" >}} Yes | Remove the parent from item. For issues, your administrator must have [enabled the new look for issues](../project/issues/issue_work_items.md). For epics, your administrator must have [enabled the new look for epics](../group/epics/epic_work_items.md). |
| `/remove_parent_epic` | {{< icon name="dotted-circle" >}} No | {{< icon name="dotted-circle" >}} No | {{< icon name="check-circle" >}} Yes | Remove parent epic from epic. |
| `/remove_time_spent` | {{< icon name="check-circle" >}} Yes | {{< icon name="check-circle" >}} Yes | {{< icon name="dotted-circle" >}} No | Remove time spent. |
| `/remove_zoom` | {{< icon name="check-circle" >}} Yes | {{< icon name="dotted-circle" >}} No | {{< icon name="dotted-circle" >}} No | Remove Zoom meeting from this issue. |
@ -169,7 +172,7 @@ To auto-format this table, use the VS Code Markdown Table formatter: `https://do
|:--------------------------------------------------------------|:-----------------------|:-----------------------|:-----------------------|:-------|
| `/assign @user1 @user2` | {{< icon name="check-circle" >}} Yes | {{< icon name="check-circle" >}} Yes | {{< icon name="check-circle" >}} Yes | Assign one or more users. |
| `/assign me` | {{< icon name="check-circle" >}} Yes | {{< icon name="check-circle" >}} Yes | {{< icon name="check-circle" >}} Yes | Assign yourself. |
| `/add_child <work_item>` | {{< icon name="dotted-circle" >}} No | {{< icon name="check-circle" >}} Yes | {{< icon name="dotted-circle" >}} No | Add child to `<work_item>`. The `<work_item>` value should be in the format of `#item`, `group/project#item`, or a URL to a work item. Multiple work items can be added as children at the same time. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/420797) in GitLab 16.5. |
| `/add_child <work_item>` | {{< icon name="dotted-circle" >}} No | {{< icon name="check-circle" >}} Yes | {{< icon name="dotted-circle" >}} No | Add child to `<work_item>`. The `<work_item>` value should be in the format of `#item`, `group/project#item`, or a URL to a work item. Multiple work items can be added as child items at the same time. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/420797) in GitLab 16.5. |
| `/award :emoji:` | {{< icon name="check-circle" >}} Yes | {{< icon name="check-circle" >}} Yes | {{< icon name="check-circle" >}} Yes | Toggle an emoji reaction. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/412275) in GitLab 16.5 |
| `/cc @user` | {{< icon name="check-circle" >}} Yes | {{< icon name="check-circle" >}} Yes | {{< icon name="check-circle" >}} Yes | Mention a user. In GitLab 15.0 and later, this command performs no action. You can instead type `CC @user` or only `@user`. |
| `/checkin_reminder <cadence>` | {{< icon name="dotted-circle" >}} No| {{< icon name="check-circle" >}} Yes | {{< icon name="dotted-circle" >}} No | Schedule [check-in reminders](../okrs.md#schedule-okr-check-in-reminders). Options are `weekly`, `twice-monthly`, `monthly`, or `never` (default). [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/422761) in GitLab 16.4 with flags named `okrs_mvc` and `okr_checkin_reminders`. |

View File

@ -161,6 +161,13 @@ For additional information, see [Approval rules](../../merge_requests/approvals/
### Edit squash commits option
{{< details >}}
- Tier: Premium, Ultimate
- Offering: GitLab.com, GitLab Self-Managed, GitLab Dedicated
{{< /details >}}
{{< history >}}
- [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/181370) in GitLab 17.9 with a flag named `branch_rule_squash_settings`. Disabled by default.

View File

@ -29,7 +29,7 @@ module Gitlab
def execute(shas, source:)
return if disabled?
labels = project_labels.merge(source: source)
labels = { source: source }
shas.uniq.each do |sha|
next unless sha.present? && commit_by(oid: sha)
@ -61,17 +61,6 @@ module Gitlab
private
def project_labels
return { full_path: '' } unless add_project_labels?
{ full_path: @repository.full_path }
end
def add_project_labels?
Feature.enabled?(:label_keep_around_ref_metrics, @repository, type: :ops) ||
(@repository.project && Feature.enabled?(:label_keep_around_ref_metrics, @repository.project, type: :ops))
end
def disabled?
Feature.enabled?(:disable_keep_around_refs, @repository, type: :ops) ||
(@repository.project && Feature.enabled?(:disable_keep_around_refs, @repository.project, type: :ops))

View File

@ -63,6 +63,7 @@ module Gitlab
provider_arguments.concat arguments
provider_arguments << defaults unless defaults.empty?
when Hash, GitlabSettings::Options
verify_saml_cert_arguments!(provider['name'], arguments)
hash_arguments = merge_hash_defaults_and_args(defaults, arguments)
normalized = normalize_hash_arguments(hash_arguments)
@ -104,6 +105,33 @@ module Gitlab
args
end
def verify_saml_cert_arguments!(provider_name, arguments)
return arguments unless AuthHelper.saml_providers.include?(provider_name.to_sym)
fingerprint = arguments['idp_cert_fingerprint']
algorithm = arguments['idp_cert_fingerprint_algorithm']
return arguments unless fingerprint.present? && algorithm.nil?
algorithm = detect_fingerprint_algorithm(fingerprint)
return arguments unless algorithm.present?
arguments['idp_cert_fingerprint_algorithm'] = algorithm
arguments
end
def detect_fingerprint_algorithm(fingerprint)
case fingerprint.scan(/[0-9A-Fa-f]{2}/i).length
when 20
# v2.x will change to RubySaml::XML::SHA1
XMLSecurity::Document::SHA1
when 32
# v2.x will change to RubySaml::XML::SHA256
XMLSecurity::Document::SHA256
end
end
def provider_defaults(provider)
self.class.default_arguments_for(provider['name'])
end

View File

@ -26213,6 +26213,9 @@ msgstr ""
msgid "Geo|Container repositories synchronization concurrency limit"
msgstr ""
msgid "Geo|Created: %{timeAgo}"
msgstr ""
msgid "Geo|Data replication lag"
msgstr ""
@ -26282,6 +26285,9 @@ msgstr ""
msgid "Geo|Go to the primary site"
msgstr ""
msgid "Geo|GraphQL ID: %{id}"
msgstr ""
msgid "Geo|Groups to synchronize"
msgstr ""
@ -26387,6 +26393,12 @@ msgstr ""
msgid "Geo|Re-verification interval"
msgstr ""
msgid "Geo|Registry ID: %{id}"
msgstr ""
msgid "Geo|Registry information"
msgstr ""
msgid "Geo|Remove %{siteType} site"
msgstr ""
@ -26396,6 +26408,9 @@ msgstr ""
msgid "Geo|Removing a Geo site stops the synchronization to and from that site. Are you sure?"
msgstr ""
msgid "Geo|Replicable ID: %{id}"
msgstr ""
msgid "Geo|Replicated data is verified with the secondary site(s) using checksums"
msgstr ""
@ -28816,6 +28831,12 @@ msgstr ""
msgid "GroupSettings|Enable extension marketplace"
msgstr ""
msgid "GroupSettings|Enable fine-grained permissions for CI/CD job tokens"
msgstr ""
msgid "GroupSettings|Enable fine-grained permissions for CI/CD job tokens."
msgstr ""
msgid "GroupSettings|Enable overview background aggregation for Value Streams Dashboard"
msgstr ""
@ -65328,6 +65349,9 @@ msgstr ""
msgid "Vulnerability|Scanner:"
msgstr ""
msgid "Vulnerability|Scanner: %{scannerName}"
msgstr ""
msgid "Vulnerability|Search or filter vulnerabilities..."
msgstr ""

View File

@ -12,14 +12,17 @@ class SemgrepResultProcessor
ALLOWED_PROJECT_DIRS = %w[/builds/gitlab-org/gitlab].freeze
ALLOWED_API_URLS = %w[https://gitlab.com/api/v4].freeze
UNIQUE_COMMENT_RULES_IDS = %w[builds.sast-custom-rules.appsec-pings.glappsec_ci-job-token builds.sast-custom-rules.secure-coding-guidelines.ruby.glappsec_insecure-regex].freeze
# Remove this when the feature is fully working
APPSEC_HANDLE = "@gitlab-com/gl-security/appsec"
MESSAGE_SCG_PING_APPSEC = "#{APPSEC_HANDLE} please review this finding, which is a potential violation of [GitLab's secure coding guidelines](https://docs.gitlab.com/development/secure_coding_guidelines/).".freeze
MESSAGE_S1_PING_APPSEC = "#{APPSEC_HANDLE} please review this finding. This MR potentially reintroduces code from a past S1 issue.".freeze
MESSAGE_FOOTER = <<~FOOTER
<small>
This AppSec automation is currently under testing.
This automation belongs to AppSec.
Use ~"appsec-sast::helpful" or ~"appsec-sast::unhelpful" for quick feedback.
To stop the bot from further commenting, you can use the ~"appsec-sast::stop" label.
For any detailed feedback, [add a comment here](https://gitlab.com/gitlab-com/gl-security/product-security/appsec/sast-custom-rules/-/issues/38).
@ -132,8 +135,18 @@ class SemgrepResultProcessor
message_header = "<!-- #{header_information} -->"
new_line = finding[:line]
message = finding[:message]
check_id = finding[:check_id]
uri = URI.parse("#{ENV['CI_API_V4_URL']}/projects/#{ENV['CI_MERGE_REQUEST_PROJECT_ID']}/merge_requests/#{ENV['CI_MERGE_REQUEST_IID']}/discussions")
message_from_bot = "#{message_header}\n#{message}\n#{MESSAGE_FOOTER}"
suffix = if check_id&.start_with?("builds.sast-custom-rules.secure-coding-guidelines")
"\n#{MESSAGE_SCG_PING_APPSEC}"
elsif check_id&.start_with?("builds.sast-custom-rules.s1")
"\n#{MESSAGE_S1_PING_APPSEC}"
else
""
end
message_from_bot = "#{message_header}\n#{message}#{suffix}\n#{MESSAGE_FOOTER}"
request = Net::HTTP::Post.new(uri)
request["PRIVATE-TOKEN"] = ENV['CUSTOM_SAST_RULES_BOT_PAT']
request.set_form_data(

View File

@ -543,27 +543,29 @@ function log_disk_usage() {
# all functions below are for customizing CI job exit code
function run_with_custom_exit_code() {
set -o pipefail # Take the exit status of the rightmost command that failed
set +e # temporarily disable exit on error to prevent premature exit
"$@"
local trace_file="/tmp/stdout_stderr_log.out"
# Run the command and tee output to both the terminal and the file
"$@" 2>&1 | tee "$trace_file"
initial_exit_code=$?
echo "initial_exit_code: $initial_exit_code"
# set -o pipefail # Take the exit status of the rightmost command that failed
# set +e # temporarily disable exit on error to prevent premature exit
find_custom_exit_code "$initial_exit_code" "$trace_file"
new_exit_code=$?
# local trace_file="/tmp/stdout_stderr_log.out"
echo "new_exit_code=$new_exit_code"
# # Run the command and tee output to both the terminal and the file
# "$@" 2>&1 | tee "$trace_file"
# initial_exit_code=$?
# Restore shell default behavior
set -e
set +o pipefail
# echo "initial_exit_code: $initial_exit_code"
exit "$new_exit_code"
# find_custom_exit_code "$initial_exit_code" "$trace_file"
# new_exit_code=$?
# echo "new_exit_code=$new_exit_code"
# # Restore shell default behavior
# set -e
# set +o pipefail
# exit "$new_exit_code"
}
function find_custom_exit_code() {

View File

@ -175,6 +175,15 @@ RSpec.describe Groups::Settings::CiCdController, feature_category: :continuous_i
.from(false).to(true)
end
end
context 'when updating `job_token_policies_enabled`' do
let(:params) { { group_id: group, group: { job_token_policies_enabled: true } } }
it 'can update `job_token_policies_enabled`' do
expect { perform_request }.to change { group.reload.namespace_settings.job_token_policies_enabled }
.from(false).to(true)
end
end
end
context 'when user is a group maintainer' do
@ -199,6 +208,16 @@ RSpec.describe Groups::Settings::CiCdController, feature_category: :continuous_i
expect(response).to have_gitlab_http_status(:not_found)
end
end
context 'when updating `job_token_policies_enabled`' do
let(:params) { { group_id: group, group: { job_token_policies_enabled: true } } }
it 'cannot update `job_token_policies_enabled`' do
expect { perform_request }.not_to change { group.reload.namespace_settings.job_token_policies_enabled }
expect(response).to have_gitlab_http_status(:not_found)
end
end
end
context 'when user is an admin' do
@ -227,6 +246,15 @@ RSpec.describe Groups::Settings::CiCdController, feature_category: :continuous_i
.from(false).to(true)
end
end
context 'when updating `job_token_policies_enabled`' do
let(:params) { { group_id: group, group: { job_token_policies_enabled: true } } }
it 'can update `job_token_policies_enabled`' do
expect { perform_request }.to change { group.reload.namespace_settings.job_token_policies_enabled }
.from(false).to(true)
end
end
end
context 'when admin mode is enabled', :enable_admin_mode do

View File

@ -31,6 +31,7 @@ import RulesYaml from './yaml_tests/positive_tests/rules.yml';
import RulesNeedsYaml from './yaml_tests/positive_tests/rules_needs.yml';
import RunYaml from './yaml_tests/positive_tests/run.yml';
import ProjectPathYaml from './yaml_tests/positive_tests/project_path.yml';
import SpecInputsYaml from './yaml_tests/positive_tests/spec_inputs.yml';
import VariablesYaml from './yaml_tests/positive_tests/variables.yml';
import JobWhenYaml from './yaml_tests/positive_tests/job_when.yml';
import IdTokensYaml from './yaml_tests/positive_tests/id_tokens.yml';
@ -63,6 +64,7 @@ import ProjectPathIncludeTailSlashYaml from './yaml_tests/negative_tests/project
import RulesNegativeYaml from './yaml_tests/negative_tests/rules.yml';
import RulesNeedsNegativeYaml from './yaml_tests/negative_tests/rules_needs.yml';
import RunNegativeYaml from './yaml_tests/negative_tests/run.yml';
import SpecInputsNegativeYaml from './yaml_tests/negative_tests/spec_inputs.yml';
import TriggerNegativeYaml from './yaml_tests/negative_tests/trigger.yml';
import VariablesInvalidOptionsYaml from './yaml_tests/negative_tests/variables/invalid_options.yml';
import VariablesInvalidSyntaxDescYaml from './yaml_tests/negative_tests/variables/invalid_syntax_desc.yml';
@ -113,25 +115,26 @@ describe('positive tests', () => {
CacheYaml,
MultipleCachesYaml,
FilterYaml,
HooksYaml,
IdTokensYaml,
IncludeYaml,
JobWhenYaml,
HooksYaml,
NeedsParallelMatrixYaml,
ParallelYaml,
ProjectPathYaml,
RetryYaml,
RulesYaml,
RulesNeedsYaml,
RunYaml,
VariablesYaml,
ProjectPathYaml,
IdTokensYaml,
ServicesYaml,
SecretsYaml,
NeedsParallelMatrixYaml,
ScriptYaml,
SecretsYaml,
ServicesYaml,
SpecInputsYaml,
StagesYaml,
TriggerYaml,
VariablesYaml,
WorkflowRulesAutoCancelOnJobFailureYaml,
WorkflowRulesAutoCancelOnNewCommitYaml,
StagesYaml,
RetryYaml,
ParallelYaml,
TriggerYaml,
}),
)('schema validates %s', (_, input) => {
// We construct a new "JSON" from each main key that is inside a
@ -170,43 +173,44 @@ describe('negative tests', () => {
Object.entries({
// JSON
DefaultNoAdditionalPropertiesJson,
JobVariablesMustNotContainObjectsJson,
InheritDefaultNoAdditionalPropertiesJson,
JobVariablesMustNotContainObjectsJson,
ReleaseAssetsLinksJson,
RetryUnknownWhenJson,
// YAML
ArtifactsNegativeYaml,
ImageNegativeYaml,
CacheKeyNeative,
MultipleCachesYamlNegative,
HooksNegative,
IdTokensNegativeYaml,
ImageNegativeYaml,
IncludeNegativeYaml,
JobWhenNegativeYaml,
RulesNegativeYaml,
RulesNeedsNegativeYaml,
RunNegativeYaml,
TriggerNegativeYaml,
VariablesInvalidOptionsYaml,
VariablesInvalidSyntaxDescYaml,
VariablesWrongSyntaxUsageExpand,
MultipleCachesYamlNegative,
NeedsParallelMatrixNumericYaml,
NeedsParallelMatrixWrongMatrixValueYaml,
NeedsParallelMatrixWrongParallelValueYaml,
ParallelNegativeYaml,
ProjectPathIncludeEmptyYaml,
ProjectPathIncludeInvalidVariableYaml,
ProjectPathIncludeLeadSlashYaml,
ProjectPathIncludeNoSlashYaml,
ProjectPathIncludeTailSlashYaml,
RetryNegativeYaml,
RulesNeedsNegativeYaml,
RulesNegativeYaml,
RunNegativeYaml,
ScriptNegativeYaml,
SecretsNegativeYaml,
ServicesNegativeYaml,
NeedsParallelMatrixNumericYaml,
NeedsParallelMatrixWrongParallelValueYaml,
NeedsParallelMatrixWrongMatrixValueYaml,
ScriptNegativeYaml,
SpecInputsNegativeYaml,
StagesNegativeYaml,
TriggerNegativeYaml,
VariablesInvalidOptionsYaml,
VariablesInvalidSyntaxDescYaml,
VariablesWrongSyntaxUsageExpand,
WorkflowRulesAutoCancelOnJobFailureNegativeYaml,
WorkflowRulesAutoCancelOnNewCommitNegativeYaml,
StagesNegativeYaml,
RetryNegativeYaml,
ParallelNegativeYaml,
}),
)('schema validates %s', (_, input) => {
// We construct a new "JSON" from each main key that is inside a

View File

@ -0,0 +1,63 @@
# Type mismatch: string type with number default
type_mismatch_string:
spec:
inputs:
string_with_number_default:
type: string
default: 123
# Type mismatch: number type with string default
type_mismatch_number:
spec:
inputs:
number_with_string_default:
type: number
default: "not a number"
# Type mismatch: boolean type with string default
type_mismatch_boolean:
spec:
inputs:
boolean_with_string_default:
type: boolean
default: "not a boolean"
# Type mismatch: array type with object default
type_mismatch_array:
spec:
inputs:
array_with_object_default:
type: array
default:
key: "value"
# Invalid input type
invalid_input_type:
spec:
inputs:
invalid_type_input:
type: "object"
default: {}
# Invalid property on input
invalid_property:
spec:
inputs:
input_with_invalid_property:
type: string
invalid_property: "value"
# Include with invalid input name
include_invalid_input_name:
include:
- local: "template.yml"
inputs:
invalid/name: "value"
# Trigger with invalid input value type
trigger_invalid_input:
trigger_job:
trigger:
project: "group/project"
inputs:
number_input: "not a number"

View File

@ -0,0 +1,51 @@
# Global component: spec section with inputs
spec:
inputs:
string_input:
type: string
description: "String input"
number_input:
type: number
description: "Number input"
boolean_input:
type: boolean
description: "Boolean input"
array_input:
type: array
description: "Array input"
# Global component: include with inputs
include:
- local: "template.yml"
inputs:
environment: "production"
version: "v2.0.0"
debug: false
workers: 4
# Job: trigger with downstream inputs
trigger_job:
trigger:
project: "group/project"
strategy: depend
inputs:
environment: "production"
version: "v2.1.0"
debug: false
matrix:
- browser: "chrome"
device: "desktop"
# Job: trigger with child pipeline inputs
child_pipeline_job:
trigger:
include:
- local: "child.yml"
inputs:
environment: "staging"
version: "v1.5.0"
- project: "group/project"
file: "template.yml"
inputs:
debug: true
workers: 8

View File

@ -5,11 +5,11 @@ import { setHTMLFixture, resetHTMLFixture } from 'helpers/fixtures';
import axios from '~/lib/utils/axios_utils';
import toast from '~/vue_shared/plugins/global_toast';
import { initJwtCiCdJobTokenEnabledToggle } from '~/group_settings/jwt_ci_cd_job_token_enabled_toggle';
import { initSettingsToggles } from '~/group_settings/settings_toggles';
jest.mock('~/vue_shared/plugins/global_toast');
describe('initJwtCiCdJobTokenEnabledToggle', () => {
describe('initSettingsToggles', () => {
let form;
let wrapper;
let requestSubmitMock;
@ -26,12 +26,12 @@ describe('initJwtCiCdJobTokenEnabledToggle', () => {
} = {}) => {
setHTMLFixture(`
<form action="${action}">
<input class="js-jwt-ci-cd-job-token-enabled-input" value="${hiddenInputValue}" type="hidden" name="group[jwt_ci_cd_job_token_enabled]"/>
<span class="js-jwt-ci-cd-job-token-enabled-toggle" data-disabled="${toggleDisabled}" data-is-checked="${toggleIsChecked}" data-is-loading="false" data-label="${toggleLabel}"></span>
<input class="js-setting-input" value="${hiddenInputValue}" type="hidden" name="group[setting]"/>
<span class="js-setting-toggle" data-disabled="${toggleDisabled}" data-is-checked="${toggleIsChecked}" data-is-loading="false" data-label="${toggleLabel}"></span>
</form>
`);
const toggle = initJwtCiCdJobTokenEnabledToggle();
const toggle = initSettingsToggles()[0];
form = document.querySelector('form');
wrapper = createWrapper(toggle);
@ -44,7 +44,7 @@ describe('initJwtCiCdJobTokenEnabledToggle', () => {
);
};
const findInput = () => form.querySelector('[name="group[jwt_ci_cd_job_token_enabled]"]');
const findInput = () => form.querySelector('[name="group[setting]"]');
const findToggle = () => wrapper.findComponent(GlToggle);
afterEach(() => {

View File

@ -15,6 +15,7 @@ RSpec.describe Types::UserPreferencesType, feature_category: :user_profile do
organization_groups_projects_sort
organization_groups_projects_display
timezone
merge_request_dashboard_list_type
]
expect(described_class).to have_graphql_fields(*expected_fields)

View File

@ -2,13 +2,13 @@
require 'spec_helper'
RSpec.describe Gitlab::Git::KeepAround do
RSpec.describe Gitlab::Git::KeepAround, feature_category: :gitaly do
include RepoHelpers
let(:repository) { create(:project, :repository).repository }
let(:service) { described_class.new(repository) }
let(:keep_around_ref_name) { "refs/#{::Repository::REF_KEEP_AROUND}/#{sample_commit.id}" }
let(:metric_labels) { { full_path: repository.full_path, source: 'keeparound_spec' } }
let(:metric_labels) { { source: 'keeparound_spec' } }
def expect_metrics_change(requested, created, &block)
requested_metric = Gitlab::Metrics.registry.get(:gitlab_keeparound_refs_requested_total)
@ -67,33 +67,4 @@ RSpec.describe Gitlab::Git::KeepAround do
expect(service.kept_around?(another_sample_commit.id)).to be_truthy
end
end
context 'when disable_keep_around_refs feature flag is enabled' do
before do
stub_feature_flags(disable_keep_around_refs: true)
end
it 'does not create keep-around refs' do
expect_metrics_change(0, 0) do
service.execute([sample_commit.id], source: 'keeparound_spec')
end
expect(service.kept_around?(sample_commit.id)).to be_truthy
expect(repository.list_refs([keep_around_ref_name])).to be_empty
end
end
context 'when label_keep_around_ref_metrics feature flag is disabled' do
let(:metric_labels) { { full_path: '', source: 'keeparound_spec' } }
before do
stub_feature_flags(label_keep_around_ref_metrics: false)
end
it 'does not label keep-around refs' do
expect_metrics_change(1, 1) do
service.execute([sample_commit.id], source: 'keeparound_spec')
end
end
end
end

View File

@ -281,6 +281,82 @@ RSpec.describe Gitlab::OmniauthInitializer, feature_category: :system_access do
end
end
context 'for SAML certificate settings' do
subject(:init) { initializer.execute(Gitlab.config.omniauth.providers) }
def stub_saml_config_fingerprint(fingerprint)
stub_omniauth_config(
providers: [
{
name: 'saml',
args: {
idp_cert_fingerprint: fingerprint
}
}
]
)
end
it 'sets SHA1 algorithm for SHA1 fingerprint' do
stub_saml_config_fingerprint("DD:80:B1:FA:A9:A7:8D:9D:41:7E:09:10:D8:6F:7D:0A:7E:58:4C:C4")
expect(devise_config).to receive(:omniauth).with(
:saml,
{
attribute_statements: ::Gitlab::Auth::Saml::Config.default_attribute_statements,
idp_cert_fingerprint: "DD:80:B1:FA:A9:A7:8D:9D:41:7E:09:10:D8:6F:7D:0A:7E:58:4C:C4",
idp_cert_fingerprint_algorithm: "http://www.w3.org/2000/09/xmldsig#sha1"
}
)
init
end
it 'sets SHA256 algoritihm for SHA256 fingerprint' do
stub_saml_config_fingerprint(
"73:2D:28:C2:D2:D0:34:9F:F8:9A:9C:74:23:BF:0A:CB:66:75:78:9B:01:4D:1F:7D:60:8F:AD:47:A2:30:D7:4A"
)
expect(devise_config).to receive(:omniauth).with(
:saml,
{
attribute_statements: ::Gitlab::Auth::Saml::Config.default_attribute_statements,
idp_cert_fingerprint:
"73:2D:28:C2:D2:D0:34:9F:F8:9A:9C:74:23:BF:0A:CB:66:75:78:9B:01:4D:1F:7D:60:8F:AD:47:A2:30:D7:4A",
idp_cert_fingerprint_algorithm: "http://www.w3.org/2001/04/xmlenc#sha256"
}
)
init
end
it 'does nothing for explicitly configured algorithm' do
stub_omniauth_config(
providers: [
{
name: 'saml',
args: {
# Mismatched fingerprint and algo to verify we do not mess with explicitly configured values
idp_cert_fingerprint: "DD:80:B1:FA:A9:A7:8D:9D:41:7E:09:10:D8:6F:7D:0A:7E:58:4C:C4",
idp_cert_fingerprint_algorithm: "http://www.w3.org/2001/04/xmlenc#sha256"
}
}
]
)
expect(devise_config).to receive(:omniauth).with(
:saml,
{
attribute_statements: ::Gitlab::Auth::Saml::Config.default_attribute_statements,
idp_cert_fingerprint: "DD:80:B1:FA:A9:A7:8D:9D:41:7E:09:10:D8:6F:7D:0A:7E:58:4C:C4",
idp_cert_fingerprint_algorithm: "http://www.w3.org/2001/04/xmlenc#sha256"
}
)
init
end
end
it 'configures defaults args for multiple SAML providers' do
stub_omniauth_config(
providers: [

View File

@ -164,7 +164,7 @@ RSpec.describe Ci::JobToken::Authorization, feature_category: :secrets_managemen
context 'when authorization is cross project' do
it 'schedules the log' do
expect(::Ci::JobToken::LogAuthorizationWorker)
.to receive(:perform_in).with(5.minutes, accessed_project.id, origin_project.id)
.to receive(:perform_in).with(5.minutes, accessed_project.id, origin_project.id, [])
log_captures_async
end

View File

@ -9993,4 +9993,28 @@ RSpec.describe Project, factory_default: :keep, feature_category: :groups_and_pr
expect(project.container_registry_protection_tag_rules).to have_received(:for_actions_and_access).with(%w[push], :maintainer).once
end
end
describe '#job_token_policies_enabled?' do
let_it_be(:project) { build_stubbed(:project) }
subject { project.job_token_policies_enabled? }
where(:flag_enabled, :setting_enabled, :result) do
true | true | true
true | false | true
false | true | true
false | false | false
end
before do
project.clear_memoization(:job_token_policies_enabled?)
stub_feature_flags(add_policies_to_ci_job_token: flag_enabled)
allow(project).to receive_message_chain(:namespace, :root_ancestor, :namespace_settings,
:job_token_policies_enabled?).and_return(setting_enabled)
end
with_them do
it { is_expected.to eq(result) }
end
end
end

View File

@ -114,6 +114,9 @@ RSpec.describe User, feature_category: :user_profile do
it { is_expected.to delegate_method(:use_work_items_view).to(:user_preference) }
it { is_expected.to delegate_method(:use_work_items_view=).to(:user_preference).with_arguments(:args) }
it { is_expected.to delegate_method(:merge_request_dashboard_list_type).to(:user_preference) }
it { is_expected.to delegate_method(:merge_request_dashboard_list_type=).to(:user_preference).with_arguments(:args) }
it { is_expected.to delegate_method(:text_editor).to(:user_preference) }
it { is_expected.to delegate_method(:text_editor=).to(:user_preference).with_arguments(:args) }

View File

@ -17,7 +17,8 @@ RSpec.describe Mutations::UserPreferences::Update, feature_category: :user_profi
'organizationGroupsProjectsDisplay' => 'GROUPS',
'organizationGroupsProjectsSort' => 'NAME_DESC',
'visibilityPipelineIdType' => 'IID',
'useWorkItemsView' => true
'useWorkItemsView' => true,
'mergeRequestDashboardListType' => 'ROLE_BASED'
}
end
@ -36,12 +37,14 @@ RSpec.describe Mutations::UserPreferences::Update, feature_category: :user_profi
expect(mutation_response['userPreferences']['organizationGroupsProjectsSort']).to eq('NAME_DESC')
expect(mutation_response['userPreferences']['visibilityPipelineIdType']).to eq('IID')
expect(mutation_response['userPreferences']['useWorkItemsView']).to eq(true)
expect(mutation_response['userPreferences']['mergeRequestDashboardListType']).to eq('ROLE_BASED')
expect(current_user.user_preference.persisted?).to eq(true)
expect(current_user.user_preference.extensions_marketplace_opt_in_status).to eq('enabled')
expect(current_user.user_preference.issues_sort).to eq(Types::IssueSortEnum.values[sort_value].value.to_s)
expect(current_user.user_preference.visibility_pipeline_id_type).to eq('iid')
expect(current_user.user_preference.use_work_items_view).to eq(true)
expect(current_user.user_preference.merge_request_dashboard_list_type).to eq('role_based')
end
end
@ -54,7 +57,8 @@ RSpec.describe Mutations::UserPreferences::Update, feature_category: :user_profi
organization_groups_projects_display: Types::Organizations::GroupsProjectsDisplayEnum.values['GROUPS'].value,
organization_groups_projects_sort: 'NAME_DESC',
visibility_pipeline_id_type: 'id',
use_work_items_view: false
use_work_items_view: false,
merge_request_dashboard_list_type: 'action_based'
}
end
@ -77,6 +81,7 @@ RSpec.describe Mutations::UserPreferences::Update, feature_category: :user_profi
expect(current_user.user_preference.issues_sort).to eq(Types::IssueSortEnum.values[sort_value].value.to_s)
expect(current_user.user_preference.visibility_pipeline_id_type).to eq('iid')
expect(current_user.user_preference.use_work_items_view).to eq(true)
expect(current_user.user_preference.merge_request_dashboard_list_type).to eq('role_based')
end
context 'when input has nil attributes' do

View File

@ -309,6 +309,7 @@ RSpec.describe NamespaceSettings::AssignAttributesService, feature_category: :gr
:new_user_signups_cap | nil | 100
:seat_control | 'off' | 'user_cap'
:enabled_git_access_protocol | 'all' | 'ssh'
:job_token_policies_enabled | false | true
end
with_them do

View File

@ -90,7 +90,7 @@ RSpec.describe Projects::ImportExport::PruneExpiredExportJobsService, feature_ca
it 'deletes stored upload files' do
old_upload_file_paths = Uploads::Local.new.keys(old_uploads)
expect(DeleteStoredFilesWorker).to receive(:perform_async).with(Uploads::Local, old_upload_file_paths)
expect(DeleteStoredFilesWorker).to receive(:perform_async).with(Uploads::Local.name, old_upload_file_paths)
described_class.execute
end

View File

@ -3,6 +3,15 @@
RSpec.shared_examples 'enforcing job token policies' do |policies, expected_success_status: :success,
allow_public_access_for_enabled_project_features: nil|
shared_examples 'capturing job token policies' do
it 'captures the policies' do
expect(::Ci::JobToken::Authorization).to receive(:capture_job_token_policies)
.with(Array(policies)).and_call_original
do_request
end
end
context 'when authenticating with a CI job token from another project' do
let(:source_project) { project }
let(:job_user) { user }
@ -58,8 +67,12 @@ RSpec.shared_examples 'enforcing job token policies' do |policies, expected_succ
end
end
it_behaves_like 'capturing job token policies'
context 'when the policies are not allowed' do
let(:allowed_policies) { [] }
let(:allowed_policies) do
(::Ci::JobToken::Policies::POLICIES - Array(policies)).take(1)
end
it { is_expected.to have_gitlab_http_status(:forbidden) }
@ -82,6 +95,8 @@ RSpec.shared_examples 'enforcing job token policies' do |policies, expected_succ
let(:default_permissions) { true }
it { is_expected.to have_gitlab_http_status(expected_success_status) }
it_behaves_like 'capturing job token policies'
end
context 'when job token policies are disabled' do
@ -92,6 +107,8 @@ RSpec.shared_examples 'enforcing job token policies' do |policies, expected_succ
end
it { is_expected.to have_gitlab_http_status(expected_success_status) }
it_behaves_like 'capturing job token policies'
end
end

View File

@ -10,7 +10,7 @@ RSpec.shared_examples 'logs inbound authorizations via job token' do |success_st
.and_call_original
expect(Ci::JobToken::LogAuthorizationWorker)
.to receive(:perform_in).with(5.minutes, accessed_project.id, origin_project.id)
.to receive(:perform_in).with(5.minutes, accessed_project.id, origin_project.id, anything)
perform_request

View File

@ -29,33 +29,26 @@ Status: Experiment
{{< /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 >}}
You can use fine-grained permissions to explicitly allow access to a limited set of API endpoints.
These permissions are applied to the CI/CD job tokens in a specified project.
This feature is an [experiment](../../policy/development_stages_support.md#experiment).
## Enable fine-grained permissions
This feature is an [experiment](../../policy/development_stages_support.md#experiment) and subject to change without notice. This feature is not ready for production use. If you want to use this feature, you should test outside of production first.
### On GitLab Self-Managed
## Enable fine-grained permissions for projects
1. Start the GitLab Rails console. For information, see [Enable and disable GitLab features deployed behind feature flags](../../administration/feature_flags.md#enable-or-disable-the-feature)
1. Turn on the [feature flag](../../administration/feature_flags.md):
Prerequisites:
```ruby
# You must include a specific project ID with this command.
Feature.enable(:add_policies_to_ci_job_token, <project_id>)
```
- You must have the Owner role for a group.
### On GitLab.com
You must turn on fine-grained permissions at the group level. Then, each project in the group can
apply fine-grained permissions for CI/CD job tokens to grant access to individual resources.
Add a comment on this [issue](https://gitlab.com/gitlab-org/gitlab/-/issues/519575) with your project ID.
To enable fine-grained permissions for all projects in a group:
1. On the left sidebar, select **Search or go to** and find your group.
1. On the left sidebar, select **Settings > CI/CD**.
1. Expand **General pipelines**.
1. Turn on the **Enable fine-grained permissions for CI/CD job tokens** toggle.
## Available API endpoints

View File

@ -29,7 +29,7 @@ require (
github.com/sirupsen/logrus v1.9.3
github.com/stretchr/testify v1.10.0
gitlab.com/gitlab-org/gitaly/v16 v16.11.0-rc1.0.20250313033925-4cbd999aaf5d
gitlab.com/gitlab-org/labkit v1.23.1
gitlab.com/gitlab-org/labkit v1.23.2
go.uber.org/goleak v1.3.0
gocloud.dev v0.40.1-0.20241107185025-56954848c3aa
golang.org/x/image v0.20.0

View File

@ -599,8 +599,8 @@ gitlab.com/gitlab-org/gitaly/v16 v16.11.0-rc1.0.20250313033925-4cbd999aaf5d h1:v
gitlab.com/gitlab-org/gitaly/v16 v16.11.0-rc1.0.20250313033925-4cbd999aaf5d/go.mod h1:i9SSlTe8PnTQoSTnLlx+FYHurZWg11BosyClhJHhdqI=
gitlab.com/gitlab-org/go/reopen v1.0.0 h1:6BujZ0lkkjGIejTUJdNO1w56mN1SI10qcVQyQlOPM+8=
gitlab.com/gitlab-org/go/reopen v1.0.0/go.mod h1:D6OID8YJDzEVZNYW02R/Pkj0v8gYFSIhXFTArAsBQw8=
gitlab.com/gitlab-org/labkit v1.23.1 h1:KvHn1lAykOjeYA3uO/luBDIO8gLmWQreKPvKrsh2PO4=
gitlab.com/gitlab-org/labkit v1.23.1/go.mod h1:pikea0zSNSfV3wuMrFST4REaM3yrSjnccIfMyCmlrkw=
gitlab.com/gitlab-org/labkit v1.23.2 h1:7KhXddq6+3pi7ozYEVGSQBpAs2SNFuw8wmh0RSgygO0=
gitlab.com/gitlab-org/labkit v1.23.2/go.mod h1:pikea0zSNSfV3wuMrFST4REaM3yrSjnccIfMyCmlrkw=
go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
go.etcd.io/etcd/raft/v3 v3.5.18 h1:gueCda+9U76Lvk6rINjNc/mXalUp0u8OK5CVESDZh4I=
go.etcd.io/etcd/raft/v3 v3.5.18/go.mod h1:XBaZHTJt3nLnpS8hMDR55Sxrq76cEC4xWYMBYSY3jcs=