Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2023-11-08 06:07:06 +00:00
parent efd6f06bfa
commit e808a772e7
45 changed files with 817 additions and 263 deletions

View File

@ -97,7 +97,7 @@ variables:
- echo -e "\e[0Ksection_end:`date +%s`:launch_gdk\r\e[0K"
- echo -e "\e[0Ksection_start:`date +%s`:install_gems[collapsed=true]\r\e[0KInstall gems"
- source scripts/utils.sh
- cd qa && bundle install
- cd qa && bundle config set --local without 'development' && bundle install
- echo -e "\e[0Ksection_end:`date +%s`:install_gems\r\e[0K"
script:
- echo -e "\e[0Ksection_start:`date +%s`:healthcheck[collapsed=true]\r\e[0KWait for gdk to start"

View File

@ -271,7 +271,6 @@ Layout/SpaceInLambdaLiteral:
- 'ee/app/services/vulnerability_exports/exporters/csv_service.rb'
- 'ee/app/workers/update_all_mirrors_worker.rb'
- 'ee/lib/api/entities/pending_member.rb'
- 'ee/lib/api/ml/ai_assist.rb'
- 'ee/lib/ee/api/entities/ci/job_request/response.rb'
- 'ee/lib/ee/api/entities/epic.rb'
- 'ee/lib/ee/api/entities/issue.rb'

View File

@ -5,9 +5,10 @@ import {
TOKEN_TYPE_APPROVED_BY,
TOKEN_TYPE_REVIEWER,
TOKEN_TYPE_TARGET_BRANCH,
TOKEN_TYPE_SOURCE_BRANCH,
} from '~/vue_shared/components/filtered_search_bar/constants';
export default (IssuableTokenKeys, disableTargetBranchFilter = false) => {
export default (IssuableTokenKeys, disableBranchFilter = false) => {
const reviewerToken = {
formattedKey: TOKEN_TITLE_REVIEWER,
key: TOKEN_TYPE_REVIEWER,
@ -57,7 +58,7 @@ export default (IssuableTokenKeys, disableTargetBranchFilter = false) => {
IssuableTokenKeys.tokenKeysWithAlternative.push(draftToken.token);
IssuableTokenKeys.conditions.push(...draftToken.conditions);
if (!disableTargetBranchFilter) {
if (!disableBranchFilter) {
const targetBranchToken = {
formattedKey: __('Target-Branch'),
key: TOKEN_TYPE_TARGET_BRANCH,
@ -68,8 +69,18 @@ export default (IssuableTokenKeys, disableTargetBranchFilter = false) => {
tag: 'branch',
};
IssuableTokenKeys.tokenKeys.push(targetBranchToken);
IssuableTokenKeys.tokenKeysWithAlternative.push(targetBranchToken);
const sourceBranchToken = {
formattedKey: __('Source-Branch'),
key: TOKEN_TYPE_SOURCE_BRANCH,
type: 'string',
param: '',
symbol: '',
icon: 'branch',
tag: 'branch',
};
IssuableTokenKeys.tokenKeys.push(targetBranchToken, sourceBranchToken);
IssuableTokenKeys.tokenKeysWithAlternative.push(targetBranchToken, sourceBranchToken);
}
const approvedToken = {

View File

@ -11,6 +11,7 @@ import {
TOKEN_TYPE_RELEASE,
TOKEN_TYPE_REVIEWER,
TOKEN_TYPE_TARGET_BRANCH,
TOKEN_TYPE_SOURCE_BRANCH,
} from '~/vue_shared/components/filtered_search_bar/constants';
import DropdownEmoji from './dropdown_emoji';
import DropdownHint from './dropdown_hint';
@ -157,6 +158,15 @@ export default class AvailableDropdownMappings {
},
element: this.container.querySelector('#js-dropdown-target-branch'),
},
[TOKEN_TYPE_SOURCE_BRANCH]: {
reference: null,
gl: DropdownNonUser,
extraArguments: {
endpoint: this.getMergeRequestSourceBranchesEndpoint(),
symbol: '',
},
element: this.container.querySelector('#js-dropdown-source-branch'),
},
environment: {
reference: null,
gl: DropdownNonUser,
@ -197,10 +207,17 @@ export default class AvailableDropdownMappings {
}
getMergeRequestTargetBranchesEndpoint() {
const endpoint = `${
gon.relative_url_root || ''
}/-/autocomplete/merge_request_target_branches.json`;
const targetBranchEndpointPath = '/-/autocomplete/merge_request_target_branches.json';
return this.getMergeRequestBranchesEndpoint(targetBranchEndpointPath);
}
getMergeRequestSourceBranchesEndpoint() {
const sourceBranchEndpointPath = '/-/autocomplete/merge_request_source_branches.json';
return this.getMergeRequestBranchesEndpoint(sourceBranchEndpointPath);
}
getMergeRequestBranchesEndpoint(endpointPath = '') {
const endpoint = `${gon.relative_url_root || ''}${endpointPath}`;
const params = {
group_id: this.getGroupId(),
project_id: this.getProjectId(),

View File

@ -67,6 +67,7 @@
],
"MemberInterface": [
"GroupMember",
"PendingGroupMember",
"ProjectMember"
],
"NoteableInterface": [

View File

@ -3,16 +3,18 @@
class AutocompleteController < ApplicationController
include SearchRateLimitable
skip_before_action :authenticate_user!, only: [:users, :award_emojis, :merge_request_target_branches]
skip_before_action :authenticate_user!, only: [
:users, :award_emojis, :merge_request_target_branches, :merge_request_source_branches
]
before_action :check_search_rate_limit!, only: [:users, :projects]
feature_category :user_profile, [:users, :user]
feature_category :groups_and_projects, [:projects]
feature_category :team_planning, [:award_emojis]
feature_category :code_review_workflow, [:merge_request_target_branches]
feature_category :code_review_workflow, [:merge_request_target_branches, :merge_request_source_branches]
feature_category :continuous_delivery, [:deploy_keys_with_owners]
urgency :low, [:merge_request_target_branches, :deploy_keys_with_owners, :users]
urgency :low, [:merge_request_target_branches, :merge_request_source_branches, :deploy_keys_with_owners, :users]
urgency :low, [:award_emojis]
urgency :medium, [:projects]
@ -62,14 +64,11 @@ class AutocompleteController < ApplicationController
end
def merge_request_target_branches
if target_branch_params.present?
merge_requests = MergeRequestsFinder.new(current_user, target_branch_params).execute
target_branches = merge_requests.recent_target_branches
merge_request_branches(target: true)
end
render json: target_branches.map { |target_branch| { title: target_branch } }
else
render json: { error: _('At least one of group_id or project_id must be specified') }, status: :bad_request
end
def merge_request_source_branches
merge_request_branches(source: true)
end
def deploy_keys_with_owners
@ -90,7 +89,7 @@ class AutocompleteController < ApplicationController
.execute
end
def target_branch_params
def branch_params
params.permit(:group_id, :project_id).select { |_, v| v.present? }
end
@ -98,6 +97,21 @@ class AutocompleteController < ApplicationController
def presented_suggested_users
[]
end
def merge_request_branches(source: false, target: false)
if branch_params.present?
merge_requests = MergeRequestsFinder.new(current_user, branch_params).execute
branches = []
branches.concat(merge_requests.recent_source_branches) if source
branches.concat(merge_requests.recent_target_branches) if target
render json: branches.map { |branch| { title: branch } }
else
render json: { error: _('At least one of group_id or project_id must be specified') }, status: :bad_request
end
end
end
AutocompleteController.prepend_mod_with('AutocompleteController')

View File

@ -46,6 +46,7 @@ class MergeRequestsFinder < IssuableFinder
:merged_before,
:reviewer_id,
:reviewer_username,
:source_branch,
:target_branch,
:wip
]
@ -81,7 +82,8 @@ class MergeRequestsFinder < IssuableFinder
items = super(items)
items = by_negated_reviewer(items)
items = by_negated_approved_by(items)
by_negated_target_branch(items)
items = by_negated_target_branch(items)
by_negated_source_branch(items)
end
private
@ -132,6 +134,12 @@ class MergeRequestsFinder < IssuableFinder
items.where.not(target_branch: not_params[:target_branch])
end
def by_negated_source_branch(items)
return items unless not_params[:source_branch]
items.where.not(source_branch: not_params[:source_branch])
end
# rubocop: enable CodeReuse/ActiveRecord
def by_negated_approved_by(items)

View File

@ -11,11 +11,11 @@ module Types
implements MemberInterface
field :group, Types::GroupType, null: true,
description: 'Group that a User is a member of.'
description: 'Group that a user is a member of.'
field :notification_email,
resolver: Resolvers::GroupMembers::NotificationEmailResolver,
description: "Group notification email for User. Only available for admins."
description: "Group notification email for user. Only available for admins."
def group
Gitlab::Graphql::Loaders::BatchModelLoader.new(Group, object.source_id).find

View File

@ -529,6 +529,14 @@ class MergeRequest < ApplicationRecord
.pluck(:target_branch)
end
def self.recent_source_branches(limit: 100)
group(:source_branch)
.select(:source_branch)
.reorder(arel_table[:updated_at].maximum.desc)
.limit(limit)
.pluck(:source_branch)
end
def self.sort_by_attribute(method, excluded_labels: [])
case method.to_s
when 'merged_at', 'merged_at_asc' then order_merged_at_asc

View File

@ -0,0 +1,25 @@
# frozen_string_literal: true
# IpCidrArrayValidator
#
# Validates that an array of IP are a valid IPv4 or IPv6 CIDR address.
#
# Example:
#
# class Group < ActiveRecord::Base
# validates :ip_array, presence: true, ip_cidr_array: true
# end
class IpCidrArrayValidator < ActiveModel::EachValidator # rubocop:disable Gitlab/NamespacedClass -- This is a globally shareable validator, but it's unclear what namespace it should belong in
def validate_each(record, attribute, value)
unless value.is_a?(Array)
record.errors.add(attribute, _("must be an array of CIDR values"))
return
end
value.each do |cidr|
single_validator = IpCidrValidator.new(attributes: attribute)
single_validator.validate_each(record, attribute, cidr)
end
end
end

View File

@ -0,0 +1,45 @@
# frozen_string_literal: true
# IpCidrValidator
#
# Validates that an IP is a valid IPv4 or IPv6 CIDR address.
#
# Example:
#
# class Group < ActiveRecord::Base
# validates :ip, presence: true, ip_cidr: true
# end
class IpCidrValidator < ActiveModel::EachValidator # rubocop:disable Gitlab/NamespacedClass -- This is a globally shareable validator, but it's unclear what namespace it should belong in
def validate_each(record, attribute, value)
# NOTE: We want this to be usable for nullable fields, so we don't validate presence.
# Use a separate `presence` validation for the field if needed.
return true if value.blank?
# rubocop:disable Layout/LineLength -- The error message is bigger than the line limit
unless valid_cidr_format?(value)
record.errors.add(
attribute,
format(_(
"IP '%{value}' is not a valid CIDR: IP should be followed by a slash followed by an integer subnet mask (for example: '192.168.1.0/24')"),
value: value
)
)
return
end
# rubocop:enable Layout/LineLength
IPAddress.parse(value)
rescue ArgumentError => e
record.errors.add(
attribute,
format(_("IP '%{value}' is not a valid CIDR: %{message}"), value: value, message: e.message)
)
end
private
def valid_cidr_format?(cidr)
cidr.count('/') == 1 && cidr.split('/').last =~ /^\d+$/
end
end

View File

@ -38,7 +38,6 @@
= render 'groups/settings/lfs', f: f
= render_if_exists 'groups/settings/code_suggestions', f: f, group: @group
= render_if_exists 'groups/settings/experimental_settings', f: f, group: @group
= render_if_exists 'groups/settings/ai_third_party_settings', f: f, group: @group
= render 'groups/settings/git_access_protocols', f: f, group: @group
= render 'groups/settings/project_creation_level', f: f, group: @group
= render 'groups/settings/subgroup_creation_level', f: f, group: @group

View File

@ -185,6 +185,11 @@
%li.filter-dropdown-item
%button.btn.btn-link.js-data-value.monospace
{{title}}
#js-dropdown-source-branch.filtered-search-input-dropdown-menu.dropdown-menu
%ul.filter-dropdown{ data: { dynamic: true, dropdown: true } }
%li.filter-dropdown-item
%button.btn.btn-link.js-data-value.monospace
{{title}}
#js-dropdown-environment.filtered-search-input-dropdown-menu.dropdown-menu
%ul.filter-dropdown{ data: { dynamic: true, dropdown: true } }
%li.filter-dropdown-item

View File

@ -1,8 +0,0 @@
---
name: ai_assist_api
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/100500
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/378470
milestone: '15.6'
type: development
group: group::incubation
default_enabled: false

View File

@ -88,6 +88,7 @@ InitializerConnections.raise_if_new_database_connection do
get '/autocomplete/projects' => 'autocomplete#projects'
get '/autocomplete/award_emojis' => 'autocomplete#award_emojis'
get '/autocomplete/merge_request_target_branches' => 'autocomplete#merge_request_target_branches'
get '/autocomplete/merge_request_source_branches' => 'autocomplete#merge_request_source_branches'
get '/autocomplete/deploy_keys_with_owners' => 'autocomplete#deploy_keys_with_owners'
Gitlab.ee do

View File

@ -0,0 +1,22 @@
# frozen_string_literal: true
class AddNetworkPolicyEgressToAgent < Gitlab::Database::Migration[2.2]
milestone '16.6'
NETWORK_POLICY_EGRESS_DEFAULT = [{
allow: "0.0.0.0/0",
except: [
- "10.0.0.0/8",
- "172.16.0.0/12",
- "192.168.0.0/16"
]
}]
def change
add_column :remote_development_agent_configs,
:network_policy_egress,
:jsonb,
null: false,
default: NETWORK_POLICY_EGRESS_DEFAULT
end
end

View File

@ -0,0 +1 @@
d31386b36b5db29deb9041febc116915f94fa7c551f1d91d5f474671dccdc709

View File

@ -22466,6 +22466,7 @@ CREATE TABLE remote_development_agent_configs (
dns_zone text NOT NULL,
network_policy_enabled boolean DEFAULT true NOT NULL,
gitlab_workspaces_proxy_namespace text DEFAULT 'gitlab-workspaces'::text NOT NULL,
network_policy_egress jsonb DEFAULT '[{"allow": "0.0.0.0/0", "except": ["10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"]}]'::jsonb NOT NULL,
CONSTRAINT check_72947a4495 CHECK ((char_length(gitlab_workspaces_proxy_namespace) <= 63)),
CONSTRAINT check_9f5cd54d1c CHECK ((char_length(dns_zone) <= 256))
);

View File

@ -37,6 +37,9 @@ To create an OAuth application on your self-managed instance:
- If you're installing the app from the official marketplace listing, enter `https://gitlab.com/-/jira_connect/oauth_callbacks`.
- If you're installing the app manually, enter `<instance_url>/-/jira_connect/oauth_callbacks` and replace `<instance_url>` with the URL of your instance.
1. Clear the **Trusted** and **Confidential** checkboxes.
NOTE:
You must clear these checkboxes to avoid errors.
1. In **Scopes**, select the `api` checkbox only.
1. Select **Save application**.
1. Copy the **Application ID** value.

View File

@ -11669,6 +11669,29 @@ The edge type for [`PathLock`](#pathlock).
| <a id="pathlockedgecursor"></a>`cursor` | [`String!`](#string) | A cursor for use in pagination. |
| <a id="pathlockedgenode"></a>`node` | [`PathLock`](#pathlock) | The item at the end of the edge. |
#### `PendingGroupMemberConnection`
The connection type for [`PendingGroupMember`](#pendinggroupmember).
##### Fields
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="pendinggroupmemberconnectionedges"></a>`edges` | [`[PendingGroupMemberEdge]`](#pendinggroupmemberedge) | A list of edges. |
| <a id="pendinggroupmemberconnectionnodes"></a>`nodes` | [`[PendingGroupMember]`](#pendinggroupmember) | A list of nodes. |
| <a id="pendinggroupmemberconnectionpageinfo"></a>`pageInfo` | [`PageInfo!`](#pageinfo) | Information to aid in pagination. |
#### `PendingGroupMemberEdge`
The edge type for [`PendingGroupMember`](#pendinggroupmember).
##### Fields
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="pendinggroupmemberedgecursor"></a>`cursor` | [`String!`](#string) | A cursor for use in pagination. |
| <a id="pendinggroupmemberedgenode"></a>`node` | [`PendingGroupMember`](#pendinggroupmember) | The item at the end of the edge. |
#### `PipelineArtifactRegistryConnection`
The connection type for [`PipelineArtifactRegistry`](#pipelineartifactregistry).
@ -18197,6 +18220,7 @@ GPG signature for a signed commit.
| <a id="grouppackagesettings"></a>`packageSettings` | [`PackageSettings`](#packagesettings) | Package settings for the namespace. |
| <a id="groupparent"></a>`parent` | [`Group`](#group) | Parent group. |
| <a id="grouppath"></a>`path` | [`String!`](#string) | Path of the namespace. |
| <a id="grouppendingmembers"></a>`pendingMembers` **{warning-solid}** | [`PendingGroupMemberConnection`](#pendinggroupmemberconnection) | **Introduced** in 16.6. This feature is an Experiment. It can be changed or removed at any time. A pending membership of a user within this group. |
| <a id="groupprojectcreationlevel"></a>`projectCreationLevel` | [`String`](#string) | Permission level required to create projects in the group. |
| <a id="grouprecentissueboards"></a>`recentIssueBoards` | [`BoardConnection`](#boardconnection) | List of recently visited boards of the group. Maximum size is 4. (see [Connections](#connections)) |
| <a id="grouprepositorysizeexcessprojectcount"></a>`repositorySizeExcessProjectCount` | [`Int!`](#int) | Number of projects in the root namespace where the repository size exceeds the limit. This only applies to namespaces under Project limit enforcement. |
@ -19280,9 +19304,9 @@ Represents a Group Membership.
| <a id="groupmembercreatedat"></a>`createdAt` | [`Time`](#time) | Date and time the membership was created. |
| <a id="groupmembercreatedby"></a>`createdBy` | [`UserCore`](#usercore) | User that authorized membership. |
| <a id="groupmemberexpiresat"></a>`expiresAt` | [`Time`](#time) | Date and time the membership expires. |
| <a id="groupmembergroup"></a>`group` | [`Group`](#group) | Group that a User is a member of. |
| <a id="groupmembergroup"></a>`group` | [`Group`](#group) | Group that a user is a member of. |
| <a id="groupmemberid"></a>`id` | [`ID!`](#id) | ID of the member. |
| <a id="groupmembernotificationemail"></a>`notificationEmail` | [`String`](#string) | Group notification email for User. Only available for admins. |
| <a id="groupmembernotificationemail"></a>`notificationEmail` | [`String`](#string) | Group notification email for user. Only available for admins. |
| <a id="groupmemberupdatedat"></a>`updatedAt` | [`Time`](#time) | Date and time the membership was last updated. |
| <a id="groupmemberuser"></a>`user` | [`UserCore`](#usercore) | User that is associated with the member object. |
| <a id="groupmemberuserpermissions"></a>`userPermissions` | [`GroupPermissions!`](#grouppermissions) | Permissions for the current user on the resource. |
@ -22497,6 +22521,46 @@ Represents a file or directory in the project repository that has been locked.
| <a id="pathlockpath"></a>`path` | [`String`](#string) | Locked path. |
| <a id="pathlockuser"></a>`user` | [`UserCore`](#usercore) | User that has locked this path. |
### `PendingGroupMember`
Represents a Pending Group Membership.
#### Fields
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="pendinggroupmemberaccesslevel"></a>`accessLevel` | [`AccessLevel`](#accesslevel) | GitLab::Access level. |
| <a id="pendinggroupmemberapproved"></a>`approved` | [`Boolean`](#boolean) | Whether the pending group member has been approved. |
| <a id="pendinggroupmemberavatarurl"></a>`avatarUrl` | [`String`](#string) | URL to avatar image file of the pending group member. |
| <a id="pendinggroupmembercreatedat"></a>`createdAt` | [`Time`](#time) | Date and time the membership was created. |
| <a id="pendinggroupmembercreatedby"></a>`createdBy` | [`UserCore`](#usercore) | User that authorized membership. |
| <a id="pendinggroupmemberemail"></a>`email` | [`String`](#string) | Public email of the pending group member. |
| <a id="pendinggroupmemberexpiresat"></a>`expiresAt` | [`Time`](#time) | Date and time the membership expires. |
| <a id="pendinggroupmembergroup"></a>`group` | [`Group`](#group) | Group that a user is a member of. |
| <a id="pendinggroupmemberid"></a>`id` | [`ID!`](#id) | ID of the member. |
| <a id="pendinggroupmemberinvited"></a>`invited` | [`Boolean`](#boolean) | Whether the pending group member has been invited. |
| <a id="pendinggroupmembername"></a>`name` | [`String`](#string) | Name of the pending group member. |
| <a id="pendinggroupmembernotificationemail"></a>`notificationEmail` | [`String`](#string) | Group notification email for user. Only available for admins. |
| <a id="pendinggroupmemberupdatedat"></a>`updatedAt` | [`Time`](#time) | Date and time the membership was last updated. |
| <a id="pendinggroupmemberuser"></a>`user` | [`UserCore`](#usercore) | User that is associated with the member object. |
| <a id="pendinggroupmemberuserpermissions"></a>`userPermissions` | [`GroupPermissions!`](#grouppermissions) | Permissions for the current user on the resource. |
| <a id="pendinggroupmemberusername"></a>`username` | [`String`](#string) | Username of the pending group member. |
| <a id="pendinggroupmemberweburl"></a>`webUrl` | [`String`](#string) | Web URL of the pending group member. |
#### Fields with arguments
##### `PendingGroupMember.mergeRequestInteraction`
Find a merge request.
Returns [`UserMergeRequestInteraction`](#usermergerequestinteraction).
###### Arguments
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="pendinggroupmembermergerequestinteractionid"></a>`id` | [`MergeRequestID!`](#mergerequestid) | Global ID of the merge request. |
### `Pipeline`
#### Fields
@ -31633,6 +31697,7 @@ Implementations:
Implementations:
- [`GroupMember`](#groupmember)
- [`PendingGroupMember`](#pendinggroupmember)
- [`ProjectMember`](#projectmember)
##### Fields

View File

@ -140,9 +140,9 @@ as it can cause the pipeline to behave unexpectedly.
| `GITLAB_CI` | all | all | Available for all jobs executed in CI/CD. `true` when available. |
| `GITLAB_FEATURES` | 10.6 | all | The comma-separated list of licensed features available for the GitLab instance and license. |
| `GITLAB_USER_EMAIL` | 8.12 | all | The email of the user who started the pipeline, unless the job is a manual job. In manual jobs, the value is the email of the user who started the job. |
| `GITLAB_USER_ID` | 8.12 | all | The ID of the user who started the pipeline, unless the job is a manual job. In manual jobs, the value is the ID of the user who started the job. |
| `GITLAB_USER_ID` | 8.12 | all | The numeric ID of the user who started the pipeline, unless the job is a manual job. In manual jobs, the value is the ID of the user who started the job. |
| `GITLAB_USER_LOGIN` | 10.0 | all | The username of the user who started the pipeline, unless the job is a manual job. In manual jobs, the value is the username of the user who started the job. |
| `GITLAB_USER_NAME` | 10.0 | all | The name of the user who started the pipeline, unless the job is a manual job. In manual jobs, the value is the name of the user who started the job. |
| `GITLAB_USER_NAME` | 10.0 | all | The display name of the user who started the pipeline, unless the job is a manual job. In manual jobs, the value is the name of the user who started the job. |
| `KUBECONFIG` | 14.2 | all | The path to the `kubeconfig` file with contexts for every shared agent connection. Only available when a [GitLab agent is authorized to access the project](../../user/clusters/agent/ci_cd_workflow.md#authorize-the-agent). |
| `TRIGGER_PAYLOAD` | 13.9 | all | The webhook payload. Only available when a pipeline is [triggered with a webhook](../triggers/index.md#access-webhook-payload). |
@ -157,8 +157,8 @@ These variables are available when:
|---------------------------------------------|--------|--------|-------------|
| `CI_MERGE_REQUEST_APPROVED` | 14.1 | all | Approval status of the merge request. `true` when [merge request approvals](../../user/project/merge_requests/approvals/index.md) is available and the merge request has been approved. |
| `CI_MERGE_REQUEST_ASSIGNEES` | 11.9 | all | Comma-separated list of usernames of assignees for the merge request. |
| `CI_MERGE_REQUEST_ID` | 11.6 | all | The instance-level ID of the merge request. This is a unique ID across all projects on GitLab. |
| `CI_MERGE_REQUEST_IID` | 11.6 | all | The project-level IID (internal ID) of the merge request. This ID is unique for the current project. |
| `CI_MERGE_REQUEST_ID` | 11.6 | all | The instance-level ID of the merge request. This is a unique ID across all projects on the GitLab instance. |
| `CI_MERGE_REQUEST_IID` | 11.6 | all | The project-level IID (internal ID) of the merge request. This ID is unique for the current project, and is the number used in the merge request URL, page title, and other visible locations. |
| `CI_MERGE_REQUEST_LABELS` | 11.9 | all | Comma-separated label names of the merge request. |
| `CI_MERGE_REQUEST_MILESTONE` | 11.9 | all | The milestone title of the merge request. |
| `CI_MERGE_REQUEST_PROJECT_ID` | 11.6 | all | The ID of the project of the merge request. |

View File

@ -36,7 +36,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
Apply the following two feature flags to any AI feature work:
- A general that applies to all AI features.
- A general flag (`ai_global_switch`) that applies to all AI features.
- A flag specific to that feature. The feature flag name [must be different](../feature_flags/index.md#feature-flags-for-licensed-features) than the licensed feature name.
See the [feature flag tracker](https://gitlab.com/gitlab-org/gitlab/-/issues/405161) for the list of all feature flags and how to use them.
@ -63,11 +63,10 @@ Use [this snippet](https://gitlab.com/gitlab-org/gitlab/-/snippets/2554994) for
1. Ensure you have followed [the process to obtain an EE license](https://about.gitlab.com/handbook/developer-onboarding/#working-on-gitlab-ee-developer-licenses) for your local instance
1. Simulate the GDK to [simulate SaaS](../ee_features.md#simulate-a-saas-instance) and ensure the group you want to test has an Ultimate license
1. Enable `Experimental features` and `Third-party AI services`
1. Enable `Experimental features`:
1. Go to the group with the Ultimate license
1. **Group Settings** > **General** -> **Permissions and group features**
1. Enable **Experiment features**
1. Enable **Third-party AI services**
1. Enable the specific feature flag for the feature you want to test
1. Set the required access token. To receive an access token:
1. For Vertex, follow the [instructions below](#configure-gcp-vertex-access).
@ -392,11 +391,11 @@ end
We recommend to use [policies](../policies.md) to deal with authorization for a feature. Currently we need to make sure to cover the following checks:
1. General AI feature flag is enabled
1. General AI feature flag (`ai_global_switch`) is enabled
1. Feature specific feature flag is enabled
1. The namespace has the required license for the feature
1. User is a member of the group/project
1. `experiment_features_enabled` and `third_party_ai_features_enabled` flags are set on the `Namespace`
1. `experiment_features_enabled` settings are set on the `Namespace`
For our example, we need to implement the `allowed?(:amazing_new_ai_feature)` call. As an example, you can look at the [Issue Policy for the summarize comments feature](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/app/policies/ee/issue_policy.rb). In our example case, we want to implement the feature for Issues as well:
@ -474,10 +473,9 @@ Caching has following limitations:
### Check if feature is allowed for this resource based on namespace settings
There are two settings allowed on root namespace level that restrict the use of AI features:
There is one setting allowed on root namespace level that restrict the use of AI features:
- `experiment_features_enabled`
- `third_party_ai_features_enabled`.
To check if that feature is allowed for a given namespace, call:
@ -485,13 +483,12 @@ To check if that feature is allowed for a given namespace, call:
Gitlab::Llm::StageCheck.available?(namespace, :name_of_the_feature)
```
Add the name of the feature to the `Gitlab::Llm::StageCheck` class. There are arrays there that differentiate
between experimental and beta features.
Add the name of the feature to the `Gitlab::Llm::StageCheck` class. There are
arrays there that differentiate between experimental and beta features.
This way we are ready for the following different cases:
- If the feature is not in any array, the check will return `true`. For example, the feature was moved to GA and does not use a third-party setting.
- If feature is in GA, but uses a third-party setting, the class will return a proper answer based on the namespace third-party setting.
- If the feature is not in any array, the check will return `true`. For example, the feature was moved to GA.
To move the feature from the experimental phase to the beta phase, move the name of the feature from the `EXPERIMENTAL_FEATURES` array to the `BETA_FEATURES` array.

View File

@ -28,22 +28,12 @@ GitLab is creating AI-assisted features across our DevSecOps platform. These fea
## Enable AI/ML features
- Third-party AI features
- All features built on large language models (LLM) from Google,
Anthropic or OpenAI (besides Code Suggestions) require that this setting is
enabled at the group level.
- [Generally Available](../policy/experiment-beta-support.md#generally-available-ga)
features are available when third-party AI features are enabled.
- Third-party AI features are enabled by default.
- This setting is available to Ultimate groups on SaaS and can be
set by a user who has the Owner role in the group.
- View [how to enable this setting](group/manage.md#enable-third-party-ai-features).
- Experiment and Beta features
- All features categorized as
[Experiment features](../policy/experiment-beta-support.md#experiment) or
[Beta features](../policy/experiment-beta-support.md#beta)
(besides Code Suggestions) require that this setting is enabled at the group
level. This is in addition to the Third-party AI features setting.
level.
- Their usage is subject to the
[Testing Terms of Use](https://about.gitlab.com/handbook/legal/testing-agreement/).
- Experiment and Beta features are disabled by default.
@ -65,7 +55,6 @@ The following subsections describe the experimental AI features in more detail.
To use this feature:
- The parent group of the project must:
- Enable the [third-party AI features setting](group/manage.md#enable-third-party-ai-features).
- Enable the [experiment and beta features setting](group/manage.md#enable-experiment-and-beta-features).
- You must be a member of the project with sufficient permissions to view the repository.
@ -111,7 +100,6 @@ We cannot guarantee that the large language model produces results that are corr
To use this feature:
- The parent group of the issue must:
- Enable the [third-party AI features setting](group/manage.md#enable-third-party-ai-features).
- Enable the [experiment and beta features setting](group/manage.md#enable-experiment-and-beta-features).
- You must be a member of the project with sufficient permissions to view the issue.
@ -135,7 +123,6 @@ language model referenced above.
To use this feature:
- The parent group of the project must:
- Enable the [third-party AI features setting](group/manage.md#enable-third-party-ai-features).
- Enable the [experiment and beta features setting](group/manage.md#enable-experiment-and-beta-features).
- You must be a member of the project with sufficient permissions to view the CI/CD analytics.
@ -161,7 +148,6 @@ Provide feedback on this experimental feature in [issue 416833](https://gitlab.c
To use this feature:
- The parent group of the project must:
- Enable the [third-party AI features setting](group/manage.md#enable-third-party-ai-features).
- Enable the [experiment and beta features setting](group/manage.md#enable-experiment-and-beta-features).
- You must be a member of the project with sufficient permissions to view the CI/CD job.
@ -176,7 +162,6 @@ reason for the failure.
To use this feature:
- The parent group of the project must:
- Enable the [third-party AI features setting](group/manage.md#enable-third-party-ai-features).
- Enable the [experiment and beta features setting](group/manage.md#enable-experiment-and-beta-features).
- You must be a member of the project with sufficient permissions to view the issue.
@ -215,8 +200,6 @@ These features are in a variety of [feature support levels](../policy/experiment
Some AI features require the use of third-party AI services models and APIs from: Google AI and OpenAI. The processing of any personal data is in accordance with our [Privacy Statement](https://about.gitlab.com/privacy/). You may also visit the [Sub-Processors page](https://about.gitlab.com/privacy/subprocessors/#third-party-sub-processors) to see the list of our Sub-Processors that we use to provide these features.
Group owners can control which top-level groups have access to third-party AI features by using the [group level third-party AI features setting](group/manage.md#enable-third-party-ai-features).
### Model accuracy and quality
Generative AI may produce unexpected results that may be:

View File

@ -44,7 +44,6 @@ This is an experimental feature and we're continuously extending the capabilitie
To use this feature, at least one group you're a member of must:
- Have the [third-party AI features setting](group/manage.md#enable-third-party-ai-features) enabled.
- Have the [experiment and beta features setting](group/manage.md#enable-experiment-and-beta-features) enabled.
## Use GitLab Duo Chat

View File

@ -492,29 +492,6 @@ To enable Experiment features for a top-level group:
1. Under **Experiment and Beta features**, select the **Use Experiment and Beta features** checkbox.
1. Select **Save changes**.
## Enable third-party AI features **(ULTIMATE SAAS)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/118222) in GitLab 16.0.
WARNING:
These AI features use [third-party services](../ai_features.md#data-usage)
and require transmission of data, including personal data.
All users in the group have third-party AI features enabled by default.
This setting [cascades to all projects](../project/merge_requests/approvals/settings.md#settings-cascading)
that belong to the group.
To disable third-party AI features for a group:
1. On the left sidebar, select **Search or go to** and find your group.
1. Select **Settings > General**.
1. Expand **Permissions and group features**.
1. Under **Third-party AI services**, uncheck the **Use third-party AI services** checkbox.
1. Select **Save changes**.
When Code Suggestions are enabled and disabled, an
[audit event](../../administration/audit_events.md) is created.
## Group activity analytics **(PREMIUM ALL)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/207164) in GitLab 12.10 as a [Beta feature](../../policy/experiment-beta-support.md#beta).

View File

@ -128,6 +128,15 @@ time is set to 15 minutes.
If you are using self-managed GitLab, an administrator can
[increase the token duration](../../../administration/packages/container_registry.md#increase-token-duration).
## `insufficient_scope: authorization failed` when pulling an image
GitLab CI/CD jobs that set [`image`](../../../ci/yaml/index.md#image) to pull an image
from a project's container registry automatically authenticate with a [CI/CD job token](../../../ci/jobs/ci_job_token.md).
All projects with CI/CD jobs that fetch images from the container registry must be listed
in the registry project's [job token allowlist](../../../ci/jobs/ci_job_token.md#allow-access-to-your-project-with-a-job-token).
Otherwise, the job fails with an `insufficient_scope: authorization failed` error.
## Slow uploads when using `kaniko` to push large images
When you push large images with `kaniko`, you might experience uncharacteristically long delays.

View File

@ -32,6 +32,10 @@ Do not use authentication methods other than the methods documented here. Undocu
#### Edit the client configuration
Update your configuration to authenticate to the Maven repository with HTTP.
##### Custom HTTP header
You must add the authentication details to the configuration file
for your client.
@ -127,6 +131,97 @@ file:
}
```
::EndTabs
##### Basic HTTP Authentication
You can also use basic HTTP authentication to authenticate to the Maven Package Registry.
::Tabs
:::TabTitle `mvn`
| Token type | Name must be | Token |
| --------------------- | ---------------------------- | ---------------------------------------------------------------------- |
| Personal access token | The username of the user | Paste token as-is, or define an environment variable to hold the token |
| Deploy token | The username of deploy token | Paste token as-is, or define an environment variable to hold the token |
| CI Job token | `gitlab-ci-token` | `${CI_JOB_TOKEN}` |
Add the following section to your
[`settings.xml`](https://maven.apache.org/settings.html) file.
```xml
<settings>
<servers>
<server>
<id>gitlab-maven</id>
<username>REPLACE_WITH_NAME</username>
<password>REPLACE_WITH_TOKEN</password>
<configuration>
<authenticationInfo>
<userName>REPLACE_WITH_NAME</userName>
<password>REPLACE_WITH_TOKEN</password>
</authenticationInfo>
</configuration>
</server>
</servers>
</settings>
```
:::TabTitle `gradle`
| Token type | Name must be | Token |
| --------------------- | ---------------------------- | ---------------------------------------------------------------------- |
| Personal access token | The username of the user | Paste token as-is, or define an environment variable to hold the token |
| Deploy token | The username of deploy token | Paste token as-is, or define an environment variable to hold the token |
| CI Job token | `gitlab-ci-token` | `System.getenv("CI_JOB_TOKEN")` |
In [your `GRADLE_USER_HOME` directory](https://docs.gradle.org/current/userguide/directory_layout.html#dir:gradle_user_home),
create a file `gradle.properties` with the following content:
```properties
gitLabPrivateToken=REPLACE_WITH_YOUR_TOKEN
```
Add a `repositories` section to your
[`build.gradle`](https://docs.gradle.org/current/userguide/tutorial_using_tasks.html).
- In Groovy DSL:
```groovy
repositories {
maven {
url "https://gitlab.example.com/api/v4/groups/<group>/-/packages/maven"
name "GitLab"
credentials(PasswordCredentials) {
username = 'REPLACE_WITH_NAME'
password = gitLabPrivateToken
}
authentication {
basic(BasicAuthentication)
}
}
}
```
- In Kotlin DSL:
```kotlin
repositories {
maven {
url = uri("https://gitlab.example.com/api/v4/groups/<group>/-/packages/maven")
name = "GitLab"
credentials(BasicAuthentication::class) {
username = "REPLACE_WITH_NAME"
password = findProperty("gitLabPrivateToken") as String?
}
authentication {
create("basic", BasicAuthentication::class)
}
}
}
```
:::TabTitle `sbt`
| Token type | Name must be | Token |

View File

@ -14,7 +14,7 @@ Additional information on enabling these features and maturity can be found in o
> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/10591) in GitLab 16.3 as an [Experiment](../../../policy/experiment-beta-support.md#experiment).
This feature is an [Experiment](../../../policy/experiment-beta-support.md) on GitLab.com that is using Google's Vertex service and the `text-bison` model. It requires the [group-level third-party AI features setting](../../group/manage.md#enable-third-party-ai-features) to be enabled.
This feature is an [Experiment](../../../policy/experiment-beta-support.md) on GitLab.com.
Merge requests in projects often have [templates](../description_templates.md#create-a-merge-request-template) defined that need to be filled out. This helps reviewers and other users understand the purpose and changes a merge request might propose.
@ -40,7 +40,7 @@ Provide feedback on this experimental feature in [issue 416537](https://gitlab.c
> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/10401) in GitLab 16.2 as an [Experiment](../../../policy/experiment-beta-support.md#experiment).
This feature is an [Experiment](../../../policy/experiment-beta-support.md) on GitLab.com that is using Google's Vertex service and the `text-bison` model. It requires the [group-level third-party AI features setting](../../group/manage.md#enable-third-party-ai-features) to be enabled.
This feature is an [Experiment](../../../policy/experiment-beta-support.md) on GitLab.com.
GitLab Duo Merge request summaries are available on the merge request page in:
@ -56,7 +56,7 @@ Provide feedback on this experimental feature in [issue 408726](https://gitlab.c
> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/10466) in GitLab 16.0 as an [Experiment](../../../policy/experiment-beta-support.md#experiment).
This feature is an [Experiment](../../../policy/experiment-beta-support.md) on GitLab.com that is using Google's Vertex service and the `text-bison` model. It requires the [group-level third-party AI features setting](../../group/manage.md#enable-third-party-ai-features) to be enabled.
This feature is an [Experiment](../../../policy/experiment-beta-support.md) on GitLab.com.
When you've completed your review of a merge request and are ready to [submit your review](reviews/index.md#submit-a-review), generate a GitLab Duo Code review summary:
@ -78,7 +78,7 @@ Provide feedback on this experimental feature in [issue 408991](https://gitlab.c
> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/10453) in GitLab 16.2 as an [Experiment](../../../policy/experiment-beta-support.md#experiment).
This feature is an [Experiment](../../../policy/experiment-beta-support.md) on GitLab.com that is using Google's Vertex service and the `text-bison` model. It requires the [group-level third-party AI features setting](../../group/manage.md#enable-third-party-ai-features) to be enabled.
This feature is an [Experiment](../../../policy/experiment-beta-support.md) on GitLab.com.
When preparing to merge your merge request you may wish to edit the proposed squash or merge commit message.
@ -99,7 +99,7 @@ Provide feedback on this experimental feature in [issue 408994](https://gitlab.c
> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/10366) in GitLab 16.0 as an [Experiment](../../../policy/experiment-beta-support.md#experiment).
This feature is an [Experiment](../../../policy/experiment-beta-support.md) on GitLab.com that is using Google's Vertex service and the `code-bison` model. It requires the [group-level third-party AI features setting](../../group/manage.md#enable-third-party-ai-features) to be enabled.
This feature is an [Experiment](../../../policy/experiment-beta-support.md) on GitLab.com.
Use GitLab Duo Test generation in a merge request to see a list of suggested tests for the file you are reviewing. This functionality can help determine if appropriate test coverage has been provided, or if you need more coverage for your project.

View File

@ -18,9 +18,6 @@ In GitLab, ensure Code Suggestions is enabled:
- [For your user account](../../../profile/preferences.md#enable-code-suggestions).
- [For *all* top-level groups your account belongs to](../../../group/manage.md#enable-code-suggestions). If you don't have a role that lets you view the top-level group's settings, contact a group owner.
To confirm that your account is enabled, go to [https://gitlab.com/api/v4/ml/ai-assist](https://gitlab.com/api/v4/ml/ai-assist). The `user_is_allowed` key should have should have a value of `true`.
A `404 Not Found` result is returned if either of the previous conditions is not met.
### Code Suggestions not displayed in VS Code or GitLab WebIDE
Check all the steps in [Code Suggestions aren't displayed](#code-suggestions-arent-displayed) first.

View File

@ -228,7 +228,7 @@ module API
requires :path, type: String, desc: 'Package path', documentation: { example: 'foo/bar/mypkg/1.0-SNAPSHOT' }
requires :file_name, type: String, desc: 'Package file name', regexp: Gitlab::Regex.maven_file_name_regex, documentation: { example: 'mypkg-1.0-SNAPSHOT.pom' }
end
route_setting :authentication, job_token_allowed: true, deploy_token_allowed: true
route_setting :authentication, job_token_allowed: true, deploy_token_allowed: true, basic_auth_personal_access_token: true
put ':id/packages/maven/*path/:file_name/authorize', requirements: MAVEN_ENDPOINT_REQUIREMENTS do
authorize_upload!
@ -254,7 +254,7 @@ module API
requires :file_name, type: String, desc: 'Package file name', regexp: Gitlab::Regex.maven_file_name_regex, documentation: { example: 'mypkg-1.0-SNAPSHOT.pom' }
requires :file, type: ::API::Validations::Types::WorkhorseFile, desc: 'The package file to be published (generated by Multipart middleware)', documentation: { type: 'file' }
end
route_setting :authentication, job_token_allowed: true, deploy_token_allowed: true
route_setting :authentication, job_token_allowed: true, deploy_token_allowed: true, basic_auth_personal_access_token: true
put ':id/packages/maven/*path/:file_name', requirements: MAVEN_ENDPOINT_REQUIREMENTS do
unprocessable_entity! if Gitlab::FIPS.enabled? && params[:file].md5
authorize_upload!

View File

@ -277,10 +277,13 @@ module Gitlab
#
# Let's define it explicitly instead of propagating it to method_missing
def close
if use_primary_and_secondary_stores?
[primary_store, secondary_store].map(&:close).first
if same_redis_store?
# if same_redis_store?, `use_primary_store_as_default?` returns false
# but we should avoid a feature-flag check in `.close` to avoid checking out
# an ActiveRecord connection during clean up.
secondary_store.close
else
default_store.close
[primary_store, secondary_store].map(&:close).first
end
end

View File

@ -25,11 +25,6 @@ module Gitlab
def metrics
metrics = {
sidekiq_jobs_cpu_seconds: ::Gitlab::Metrics.histogram(:sidekiq_jobs_cpu_seconds, 'Seconds this Sidekiq job spent on the CPU', {}, SIDEKIQ_LATENCY_BUCKETS),
sidekiq_jobs_db_seconds: ::Gitlab::Metrics.histogram(:sidekiq_jobs_db_seconds, 'Seconds of database time to run Sidekiq job', {}, SIDEKIQ_LATENCY_BUCKETS),
sidekiq_jobs_gitaly_seconds: ::Gitlab::Metrics.histogram(:sidekiq_jobs_gitaly_seconds, 'Seconds of Gitaly time to run Sidekiq job', {}, SIDEKIQ_LATENCY_BUCKETS),
sidekiq_redis_requests_duration_seconds: ::Gitlab::Metrics.histogram(:sidekiq_redis_requests_duration_seconds, 'Duration in seconds that a Sidekiq job spent requests a Redis server', {}, Gitlab::Instrumentation::Redis::QUERY_TIME_BUCKETS),
sidekiq_elasticsearch_requests_duration_seconds: ::Gitlab::Metrics.histogram(:sidekiq_elasticsearch_requests_duration_seconds, 'Duration in seconds that a Sidekiq job spent in requests to an Elasticsearch server', {}, SIDEKIQ_LATENCY_BUCKETS),
sidekiq_jobs_retried_total: ::Gitlab::Metrics.counter(:sidekiq_jobs_retried_total, 'Sidekiq jobs retried'),
sidekiq_jobs_interrupted_total: ::Gitlab::Metrics.counter(:sidekiq_jobs_interrupted_total, 'Sidekiq jobs interrupted'),
sidekiq_redis_requests_total: ::Gitlab::Metrics.counter(:sidekiq_redis_requests_total, 'Redis requests during a Sidekiq job execution'),
@ -43,9 +38,24 @@ module Gitlab
metrics[:sidekiq_jobs_completion_seconds] = ::Gitlab::Metrics.histogram(:sidekiq_jobs_completion_seconds, 'Seconds to complete Sidekiq job', {}, SIDEKIQ_JOB_DURATION_BUCKETS)
metrics[:sidekiq_jobs_queue_duration_seconds] = ::Gitlab::Metrics.histogram(:sidekiq_jobs_queue_duration_seconds, 'Duration in seconds that a Sidekiq job was queued before being executed', {}, SIDEKIQ_QUEUE_DURATION_BUCKETS)
metrics[:sidekiq_jobs_failed_total] = ::Gitlab::Metrics.counter(:sidekiq_jobs_failed_total, 'Sidekiq jobs failed')
# resource usage
metrics[:sidekiq_jobs_cpu_seconds] = ::Gitlab::Metrics.histogram(:sidekiq_jobs_cpu_seconds, 'Seconds this Sidekiq job spent on the CPU', {}, SIDEKIQ_LATENCY_BUCKETS)
metrics[:sidekiq_jobs_db_seconds] = ::Gitlab::Metrics.histogram(:sidekiq_jobs_db_seconds, 'Seconds of database time to run Sidekiq job', {}, SIDEKIQ_LATENCY_BUCKETS)
metrics[:sidekiq_jobs_gitaly_seconds] = ::Gitlab::Metrics.histogram(:sidekiq_jobs_gitaly_seconds, 'Seconds of Gitaly time to run Sidekiq job', {}, SIDEKIQ_LATENCY_BUCKETS)
metrics[:sidekiq_redis_requests_duration_seconds] = ::Gitlab::Metrics.histogram(:sidekiq_redis_requests_duration_seconds, 'Duration in seconds that a Sidekiq job spent in requests to a Redis server', {}, Gitlab::Instrumentation::Redis::QUERY_TIME_BUCKETS)
metrics[:sidekiq_elasticsearch_requests_duration_seconds] = ::Gitlab::Metrics.histogram(:sidekiq_elasticsearch_requests_duration_seconds, 'Duration in seconds that a Sidekiq job spent in requests to an Elasticsearch server', {}, SIDEKIQ_LATENCY_BUCKETS)
else
# The sum metric is still used in GitLab.com for dashboards
# These metrics are used in GitLab.com dashboards
metrics[:sidekiq_jobs_completion_seconds_sum] = ::Gitlab::Metrics.counter(:sidekiq_jobs_completion_seconds_sum, 'Total of seconds to complete Sidekiq job')
metrics[:sidekiq_jobs_completion_count] = ::Gitlab::Metrics.counter(:sidekiq_jobs_completion_count, 'Number of Sidekiq jobs completed')
# resource usage sums
metrics[:sidekiq_jobs_cpu_seconds_sum] = ::Gitlab::Metrics.counter(:sidekiq_jobs_cpu_seconds_sum, 'Total seconds this Sidekiq job spent on the CPU')
metrics[:sidekiq_jobs_db_seconds_sum] = ::Gitlab::Metrics.counter(:sidekiq_jobs_db_seconds_sum, 'Total seconds of database time to run Sidekiq job')
metrics[:sidekiq_jobs_gitaly_seconds_sum] = ::Gitlab::Metrics.counter(:sidekiq_jobs_gitaly_seconds_sum, 'Total seconds Gitaly time to run Sidekiq job')
metrics[:sidekiq_redis_requests_duration_seconds_sum] = ::Gitlab::Metrics.counter(:sidekiq_redis_requests_duration_seconds_sum, 'Total duration in seconds that a Sidekiq job spent in requests to a Redis server')
metrics[:sidekiq_elasticsearch_requests_duration_seconds_sum] = ::Gitlab::Metrics.counter(:sidekiq_elasticsearch_requests_duration_seconds_sum, 'Total duration in seconds that a Sidekiq job spent in requests to an Elasticsearch server')
end
metrics
@ -89,8 +99,9 @@ module Gitlab
# in metrics and can use them in the `ThreadsSampler` for setting a label
Thread.current.name ||= Gitlab::Metrics::Samplers::ThreadsSampler::SIDEKIQ_WORKER_THREAD_NAME
labels = create_labels(worker.class, queue, job)
instrument(job, labels) do
@job = job
@labels = create_labels(worker.class, queue, job)
instrument do
yield
end
end
@ -99,8 +110,8 @@ module Gitlab
attr_reader :metrics
def instrument(job, labels)
queue_duration = ::Gitlab::InstrumentationHelper.queue_duration_for_job(job)
def instrument
@queue_duration = ::Gitlab::InstrumentationHelper.queue_duration_for_job(job)
@metrics[:sidekiq_jobs_queue_duration_seconds]&.observe(labels, queue_duration) if queue_duration
@ -114,43 +125,33 @@ module Gitlab
@metrics[:sidekiq_jobs_interrupted_total].increment(labels, 1)
end
job_succeeded = false
@job_succeeded = false
monotonic_time_start = Gitlab::Metrics::System.monotonic_time
job_thread_cputime_start = get_thread_cputime
begin
transaction = Gitlab::Metrics::BackgroundTransaction.new
transaction.run { yield }
job_succeeded = true
@job_succeeded = true
ensure
monotonic_time_end = Gitlab::Metrics::System.monotonic_time
job_thread_cputime_end = get_thread_cputime
monotonic_time = monotonic_time_end - monotonic_time_start
job_thread_cputime = job_thread_cputime_end - job_thread_cputime_start
@monotonic_time = monotonic_time_end - monotonic_time_start
@job_thread_cputime = job_thread_cputime_end - job_thread_cputime_start
# sidekiq_running_jobs, sidekiq_jobs_failed_total should not include the job_status label
@metrics[:sidekiq_running_jobs].increment(labels, -1)
if Feature.enabled?(:emit_sidekiq_histogram_metrics, type: :ops)
@metrics[:sidekiq_jobs_failed_total].increment(labels, 1) unless job_succeeded
else
# we don't need job_status label here
@metrics[:sidekiq_jobs_completion_seconds_sum].increment(labels, monotonic_time)
end
@instrumentation = job[:instrumentation] || {}
record_resource_usage_counters
# job_status: done, fail match the job_status attribute in structured logging
labels[:job_status] = job_succeeded ? "done" : "fail"
instrumentation = job[:instrumentation] || {}
@metrics[:sidekiq_jobs_cpu_seconds].observe(labels, job_thread_cputime)
@metrics[:sidekiq_jobs_completion_seconds]&.observe(labels, monotonic_time)
record_histograms
@metrics[:sidekiq_jobs_db_seconds].observe(labels, ActiveRecord::LogSubscriber.runtime / 1000)
@metrics[:sidekiq_jobs_gitaly_seconds].observe(labels, get_gitaly_time(instrumentation))
@metrics[:sidekiq_redis_requests_total].increment(labels, get_redis_calls(instrumentation))
@metrics[:sidekiq_redis_requests_duration_seconds].observe(labels, get_redis_time(instrumentation))
@metrics[:sidekiq_elasticsearch_requests_total].increment(labels, get_elasticsearch_calls(instrumentation))
@metrics[:sidekiq_elasticsearch_requests_duration_seconds].observe(labels, get_elasticsearch_time(instrumentation))
@metrics[:sidekiq_mem_total_bytes].set(labels, get_thread_memory_total_allocations(instrumentation))
with_load_balancing_settings(job) do |settings|
@ -162,15 +163,50 @@ module Gitlab
@metrics[:sidekiq_load_balancing_count].increment(labels.merge(load_balancing_labels), 1)
end
sli_labels = labels.slice(*SIDEKIQ_SLI_LABELS)
Gitlab::Metrics::SidekiqSlis.record_execution_apdex(sli_labels, monotonic_time) if job_succeeded
Gitlab::Metrics::SidekiqSlis.record_execution_error(sli_labels, !job_succeeded)
Gitlab::Metrics::SidekiqSlis.record_queueing_apdex(sli_labels, queue_duration) if queue_duration
@sli_labels = labels.slice(*SIDEKIQ_SLI_LABELS)
record_execution_sli
record_queueing_sli
end
end
private
attr_reader :labels, :job, :queue_duration, :job_succeeded, :monotonic_time, :job_thread_cputime, :instrumentation, :sli_labels
def record_resource_usage_counters
if Feature.enabled?(:emit_sidekiq_histogram_metrics, type: :ops)
@metrics[:sidekiq_jobs_failed_total].increment(labels, 1) unless job_succeeded
else
@metrics[:sidekiq_jobs_completion_seconds_sum].increment(labels, monotonic_time)
@metrics[:sidekiq_jobs_completion_count].increment(labels, 1)
@metrics[:sidekiq_jobs_cpu_seconds_sum].increment(labels, job_thread_cputime)
@metrics[:sidekiq_jobs_db_seconds_sum].increment(labels, ActiveRecord::LogSubscriber.runtime / 1000)
@metrics[:sidekiq_jobs_gitaly_seconds_sum].increment(labels, get_gitaly_time(instrumentation))
@metrics[:sidekiq_redis_requests_duration_seconds_sum].increment(labels, get_redis_time(instrumentation))
@metrics[:sidekiq_elasticsearch_requests_duration_seconds_sum].increment(labels, get_elasticsearch_time(instrumentation))
end
end
def record_histograms
@metrics[:sidekiq_jobs_cpu_seconds]&.observe(labels, job_thread_cputime)
@metrics[:sidekiq_jobs_completion_seconds]&.observe(labels, monotonic_time)
@metrics[:sidekiq_jobs_db_seconds]&.observe(labels, ActiveRecord::LogSubscriber.runtime / 1000)
@metrics[:sidekiq_jobs_gitaly_seconds]&.observe(labels, get_gitaly_time(instrumentation))
@metrics[:sidekiq_redis_requests_duration_seconds]&.observe(labels, get_redis_time(instrumentation))
@metrics[:sidekiq_elasticsearch_requests_duration_seconds]&.observe(labels, get_elasticsearch_time(instrumentation))
end
def record_queueing_sli
Gitlab::Metrics::SidekiqSlis.record_queueing_apdex(sli_labels, queue_duration) if queue_duration
end
def record_execution_sli
Gitlab::Metrics::SidekiqSlis.record_execution_apdex(sli_labels, monotonic_time) if job_succeeded
Gitlab::Metrics::SidekiqSlis.record_execution_error(sli_labels, !job_succeeded)
end
def with_load_balancing_settings(job)
keys = %w[load_balancing_strategy worker_data_consistency]
return unless keys.all? { |k| job.key?(k) }

View File

@ -1375,6 +1375,12 @@ msgstr ""
msgid "'%{value}' days of inactivity must be greater than or equal to 90"
msgstr ""
msgid "'allow: %{allow}' must be a string"
msgstr ""
msgid "'except: %{except}' must be an array of string"
msgstr ""
msgid "'projects' is not yet supported"
msgstr ""
@ -1915,9 +1921,6 @@ msgstr ""
msgid "AISummary|View summary"
msgstr ""
msgid "AI| %{link_start}How is my data used?%{link_end}"
msgstr ""
msgid "AI|%{tool} is %{transition} an answer"
msgstr ""
@ -1960,9 +1963,6 @@ msgstr ""
msgid "AI|Explain your rating to help us improve! (optional)"
msgstr ""
msgid "AI|Features that use third-party AI services require transmission of data, including personal data."
msgstr ""
msgid "AI|For example: Organizations should be able to forecast into the future by using value stream analytics charts. This feature would help them understand how their metrics are trending."
msgstr ""
@ -2037,18 +2037,12 @@ msgstr ""
msgid "AI|There is too much text in the chat. Please try again with a shorter text."
msgstr ""
msgid "AI|Third-party AI services"
msgstr ""
msgid "AI|To help improve the quality of the content, send your feedback to GitLab team members."
msgstr ""
msgid "AI|Unhelpful"
msgstr ""
msgid "AI|Use third-party AI services"
msgstr ""
msgid "AI|What does the selected code mean?"
msgstr ""
@ -11922,7 +11916,7 @@ msgstr ""
msgid "CodeSuggestions|Projects in this group can use Code Suggestions"
msgstr ""
msgid "CodeSuggestions|Subject to the %{terms_link_start}Testing Terms of Use%{link_end}. Code Suggestions currently uses third-party AI services unless those are %{third_party_features_link_start}disabled%{link_end}."
msgid "CodeSuggestions|Subject to the %{terms_link_start}Testing Terms of Use%{link_end}. Code Suggestions uses third-party AI services."
msgstr ""
msgid "CodeownersValidation|An error occurred while loading the validation errors. Please try again later."
@ -24209,6 +24203,12 @@ msgstr ""
msgid "INFO: Your SSH key is expiring soon. Please generate a new key."
msgstr ""
msgid "IP '%{value}' is not a valid CIDR: %{message}"
msgstr ""
msgid "IP '%{value}' is not a valid CIDR: IP should be followed by a slash followed by an integer subnet mask (for example: '192.168.1.0/24')"
msgstr ""
msgid "IP Address"
msgstr ""
@ -45951,6 +45951,9 @@ msgstr ""
msgid "Source project cannot be found."
msgstr ""
msgid "Source-Branch"
msgstr ""
msgid "SourceEditor|\"el\" parameter is required for createInstance()"
msgstr ""
@ -48816,9 +48819,6 @@ msgstr ""
msgid "Third Party Advisory Link"
msgstr ""
msgid "Third party AI settings not allowed."
msgstr ""
msgid "This %{issuableDisplayName} is locked. Only project members can comment."
msgstr ""
@ -57594,6 +57594,18 @@ msgstr ""
msgid "must be after start"
msgstr ""
msgid "must be an array"
msgstr ""
msgid "must be an array of CIDR values"
msgstr ""
msgid "must be an array of hash"
msgstr ""
msgid "must be an array of hash containing 'allow' attribute of type string"
msgstr ""
msgid "must be an email you have verified"
msgstr ""

View File

@ -3,7 +3,13 @@
module QA
RSpec.describe 'Create', product_group: :source_code do
describe 'Push mirror a repository over HTTP' do
it 'configures and syncs LFS objects for a (push) mirrored repository', :aggregate_failures, testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347847' do
it 'configures and syncs LFS objects for a (push) mirrored repository', :aggregate_failures,
testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347847',
quarantine: {
only: { condition: -> { ENV['QA_RUN_TYPE'] == 'e2e-package-and-test-ce' } },
issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/412268',
type: :investigating
} do
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.perform(&:sign_in_using_credentials)

View File

@ -3,7 +3,14 @@
module QA
RSpec.describe 'Create' do
describe 'Push mirror a repository over HTTP', product_group: :source_code do
it 'configures and syncs a (push) mirrored repository', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347741' do
it('configures and syncs a (push) mirrored repository',
testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347741',
quarantine: {
only: { condition: -> { ENV['QA_RUN_TYPE'] == 'e2e-package-and-test-ce' } },
issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/412611',
type: :investigating
}
) do
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.perform(&:sign_in_using_credentials)

View File

@ -454,77 +454,85 @@ RSpec.describe AutocompleteController do
end
end
context 'Get merge_request_target_branches', feature_category: :code_review_workflow do
let!(:merge_request) { create(:merge_request, source_project: project, target_branch: 'feature') }
context 'anonymous user' do
it 'returns empty json' do
get :merge_request_target_branches, params: { project_id: project.id }
expect(response).to have_gitlab_http_status(:ok)
expect(json_response).to be_empty
end
context 'GET branches', feature_category: :code_review_workflow do
let_it_be(:merge_request) do
create(:merge_request, source_project: project,
source_branch: 'test_source_branch', target_branch: 'test_target_branch')
end
context 'user without any accessible merge requests' do
it 'returns empty json' do
sign_in(create(:user))
shared_examples 'Get merge_request_{}_branches' do |path, expected_result|
context 'anonymous user' do
it 'returns empty json' do
get path, params: { project_id: project.id }
get :merge_request_target_branches, params: { project_id: project.id }
expect(response).to have_gitlab_http_status(:ok)
expect(json_response).to be_empty
expect(response).to have_gitlab_http_status(:ok)
expect(json_response).to be_empty
end
end
end
context 'user with an accessible merge request but no scope' do
where(
params: [
{},
{ group_id: ' ' },
{ project_id: ' ' },
{ group_id: ' ', project_id: ' ' }
]
)
context 'user without any accessible merge requests' do
it 'returns empty json' do
sign_in(create(:user))
with_them do
it 'returns an error' do
get path, params: { project_id: project.id }
expect(response).to have_gitlab_http_status(:ok)
expect(json_response).to be_empty
end
end
context 'user with an accessible merge request but no scope' do
where(
params: [
{},
{ group_id: ' ' },
{ project_id: ' ' },
{ group_id: ' ', project_id: ' ' }
]
)
with_them do
it 'returns an error' do
sign_in(user)
get path, params: params
expect(response).to have_gitlab_http_status(:bad_request)
expect(json_response).to eq({ 'error' => 'At least one of group_id or project_id must be specified' })
end
end
end
context 'user with an accessible merge request by project' do
it 'returns json' do
sign_in(user)
get :merge_request_target_branches, params: params
get path, params: { project_id: project.id }
expect(response).to have_gitlab_http_status(:bad_request)
expect(json_response).to eq({ 'error' => 'At least one of group_id or project_id must be specified' })
expect(response).to have_gitlab_http_status(:ok)
expect(json_response).to contain_exactly(expected_result)
end
end
context 'user with an accessible merge request by group' do
let(:group) { create(:group) }
let(:user) { create(:user) }
it 'returns json' do
project.update!(namespace: group)
group.add_owner(user)
sign_in(user)
get path, params: { group_id: group.id }
expect(response).to have_gitlab_http_status(:ok)
expect(json_response).to contain_exactly(expected_result)
end
end
end
context 'user with an accessible merge request by project' do
it 'returns json' do
sign_in(user)
get :merge_request_target_branches, params: { project_id: project.id }
expect(response).to have_gitlab_http_status(:ok)
expect(json_response).to contain_exactly({ 'title' => 'feature' })
end
end
context 'user with an accessible merge request by group' do
let(:group) { create(:group) }
let(:project) { create(:project, namespace: group) }
let(:user) { create(:user) }
it 'returns json' do
group.add_owner(user)
sign_in(user)
get :merge_request_target_branches, params: { group_id: group.id }
expect(response).to have_gitlab_http_status(:ok)
expect(json_response).to contain_exactly({ 'title' => 'feature' })
end
end
it_behaves_like 'Get merge_request_{}_branches', :merge_request_target_branches, { 'title' => 'test_target_branch' }
it_behaves_like 'Get merge_request_{}_branches', :merge_request_source_branches, { 'title' => 'test_source_branch' }
end
end

View File

@ -0,0 +1,65 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe 'Merge Requests > User filters by source branch', :js, feature_category: :code_review_workflow do
include FilteredSearchHelpers
def create_mr(source_branch, target_branch, status)
create(:merge_request, status, source_project: project,
target_branch: target_branch, source_branch: source_branch)
end
let_it_be(:project) { create(:project, :public, :repository) }
let_it_be(:user) { project.creator }
let_it_be(:mr1) { create_mr('source1', 'target1', :opened) }
let_it_be(:mr2) { create_mr('source2', 'target1', :opened) }
let_it_be(:mr3) { create_mr('source1', 'target2', :merged) }
let_it_be(:mr4) { create_mr('source1', 'target2', :closed) }
before do
sign_in(user)
visit project_merge_requests_path(project)
end
context 'when filtering by source-branch:source1' do
it 'applies the filter' do
input_filtered_search('source-branch:=source1')
expect(page).to have_issuable_counts(open: 1, merged: 1, closed: 1, all: 3)
expect(page).to have_content mr1.title
expect(page).not_to have_content mr2.title
end
end
context 'when filtering by source-branch:source2' do
it 'applies the filter' do
input_filtered_search('source-branch:=source2')
expect(page).to have_issuable_counts(open: 1, merged: 0, closed: 0, all: 1)
expect(page).not_to have_content mr1.title
expect(page).to have_content mr2.title
end
end
context 'when filtering by source-branch:non-exists-branch' do
it 'applies the filter' do
input_filtered_search('source-branch:=non-exists-branch')
expect(page).to have_issuable_counts(open: 0, merged: 0, closed: 0, all: 0)
expect(page).not_to have_content mr1.title
expect(page).not_to have_content mr2.title
end
end
context 'when filtering by source-branch:!=source1' do
it 'applies the filter' do
input_filtered_search('source-branch:!=source1')
expect(page).to have_issuable_counts(open: 1, merged: 0, closed: 0, all: 1)
expect(page).not_to have_content mr1.title
expect(page).to have_content mr2.title
end
end
end

View File

@ -1011,11 +1011,7 @@ RSpec.describe Gitlab::Redis::MultiStore, feature_category: :redis do
describe '#close' do
subject { multi_store.close }
context 'when using both stores' do
before do
allow(multi_store).to receive(:use_primary_and_secondary_stores?).and_return(true)
end
context 'when using both stores are different' do
it 'closes both stores' do
expect(primary_store).to receive(:close)
expect(secondary_store).to receive(:close)
@ -1024,35 +1020,16 @@ RSpec.describe Gitlab::Redis::MultiStore, feature_category: :redis do
end
end
context 'when using only one store' do
context 'when using identical stores' do
before do
allow(multi_store).to receive(:use_primary_and_secondary_stores?).and_return(false)
allow(multi_store).to receive(:same_redis_store?).and_return(true)
end
context 'when using primary_store as default store' do
before do
allow(multi_store).to receive(:use_primary_store_as_default?).and_return(true)
end
it 'closes secondary store' do
expect(secondary_store).to receive(:close)
expect(primary_store).not_to receive(:close)
it 'closes primary store' do
expect(primary_store).to receive(:close)
expect(secondary_store).not_to receive(:close)
subject
end
end
context 'when using secondary_store as default store' do
before do
allow(multi_store).to receive(:use_primary_store_as_default?).and_return(false)
end
it 'closes secondary store' do
expect(primary_store).not_to receive(:close)
expect(secondary_store).to receive(:close)
subject
end
subject
end
end
end

View File

@ -3,7 +3,7 @@
require 'spec_helper'
# rubocop: disable RSpec/MultipleMemoizedHelpers
RSpec.describe Gitlab::SidekiqMiddleware::ServerMetrics do
RSpec.describe Gitlab::SidekiqMiddleware::ServerMetrics, feature_category: :shared do
shared_examples "a metrics middleware" do
context "with mocked prometheus" do
include_context 'server metrics with mocked prometheus'
@ -452,11 +452,6 @@ RSpec.describe Gitlab::SidekiqMiddleware::ServerMetrics do
end
context 'when emit_sidekiq_histogram_metrics FF is disabled' do
include_context 'server metrics with mocked prometheus'
include_context 'server metrics call' do
let(:stub_subject) { false }
end
subject(:middleware) { described_class.new }
let(:job) { {} }
@ -484,16 +479,38 @@ RSpec.describe Gitlab::SidekiqMiddleware::ServerMetrics do
stub_feature_flags(emit_sidekiq_histogram_metrics: false)
end
# include_context below must run after stubbing FF above because
# the middleware initialization depends on the FF and it's being initialized
# in the 'server metrics call' shared_context
include_context 'server metrics with mocked prometheus'
include_context 'server metrics call'
it 'does not emit histogram metrics' do
expect(completion_seconds_metric).not_to receive(:observe)
expect(queue_duration_seconds).not_to receive(:observe)
expect(failed_total_metric).not_to receive(:increment)
expect(user_execution_seconds_metric).not_to receive(:observe)
expect(db_seconds_metric).not_to receive(:observe)
expect(gitaly_seconds_metric).not_to receive(:observe)
expect(redis_seconds_metric).not_to receive(:observe)
expect(elasticsearch_seconds_metric).not_to receive(:observe)
middleware.call(worker, job, queue) { nil }
end
it 'emits sidekiq_jobs_completion_seconds_sum metric' do
it 'emits sidekiq_jobs_completion_seconds sum and count metric' do
expect(completion_seconds_sum_metric).to receive(:increment).with(labels, monotonic_time_duration)
expect(completion_count_metric).to receive(:increment).with(labels, 1)
middleware.call(worker, job, queue) { nil }
end
it 'emits resource usage sum metrics' do
expect(cpu_seconds_sum_metric).to receive(:increment).with(labels, thread_cputime_duration)
expect(db_seconds_sum_metric).to receive(:increment).with(labels, db_duration)
expect(gitaly_seconds_sum_metric).to receive(:increment).with(labels, gitaly_duration)
expect(redis_seconds_sum_metric).to receive(:increment).with(labels, redis_duration)
expect(elasticsearch_seconds_sum_metric).to receive(:increment).with(labels, elasticsearch_duration)
middleware.call(worker, job, queue) { nil }
end

View File

@ -725,22 +725,35 @@ RSpec.describe MergeRequest, factory_default: :keep, feature_category: :code_rev
end
end
describe '.recent_target_branches' do
describe '.recent_target_branches and .recent_source_branches' do
def create_mr(source_branch, target_branch, status, remove_source_branch = false)
if remove_source_branch
create(:merge_request, status, :remove_source_branch, source_project: project,
target_branch: target_branch, source_branch: source_branch)
else
create(:merge_request, status, source_project: project,
target_branch: target_branch, source_branch: source_branch)
end
end
let(:project) { create(:project) }
let!(:merge_request1) { create(:merge_request, :opened, source_project: project, target_branch: 'feature') }
let!(:merge_request2) { create(:merge_request, :closed, source_project: project, target_branch: 'merge-test') }
let!(:merge_request3) { create(:merge_request, :opened, source_project: project, target_branch: 'fix') }
let!(:merge_request4) { create(:merge_request, :closed, source_project: project, target_branch: 'feature') }
let!(:merge_request1) { create_mr('source1', 'target1', :opened) }
let!(:merge_request2) { create_mr('source2', 'target2', :closed) }
let!(:merge_request3) { create_mr('source3', 'target3', :opened) }
let!(:merge_request4) { create_mr('source4', 'target1', :closed) }
let!(:merge_request5) { create_mr('source5', 'target4', :merged, true) }
before do
merge_request1.update_columns(updated_at: 1.day.since)
merge_request2.update_columns(updated_at: 2.days.since)
merge_request3.update_columns(updated_at: 3.days.since)
merge_request4.update_columns(updated_at: 4.days.since)
merge_request5.update_columns(updated_at: 5.days.since)
end
it 'returns target branches sort by updated at desc' do
expect(described_class.recent_target_branches).to match_array(%w[feature merge-test fix])
it 'returns branches sort by updated at desc' do
expect(described_class.recent_target_branches).to match_array(%w[target1 target2 target3 target4])
expect(described_class.recent_source_branches).to match_array(%w[source1 source2 source3 source4 source5])
end
end

View File

@ -946,6 +946,22 @@ RSpec.describe API::MavenPackages, feature_category: :package_registry do
expect(response).to have_gitlab_http_status(:forbidden)
end
context 'with basic auth' do
where(:token_type) do
%i[personal_access_token deploy_token job]
end
with_them do
let(:token) { send(token_type).token }
it "authorizes upload with #{params[:token_type]} token" do
authorize_upload({}, headers.merge(basic_auth_header(token_type == :job ? ::Gitlab::Auth::CI_JOB_USER : user.username, token)))
expect(response).to have_gitlab_http_status(:ok)
end
end
end
def authorize_upload(params = {}, request_headers = headers)
put api("/projects/#{project.id}/packages/maven/com/example/my-app/#{version}/maven-metadata.xml/authorize"), params: params, headers: request_headers
end
@ -1083,6 +1099,22 @@ RSpec.describe API::MavenPackages, feature_category: :package_registry do
expect(response).to have_gitlab_http_status(:forbidden)
end
context 'with basic auth' do
where(:token_type) do
%i[personal_access_token deploy_token job]
end
with_them do
let(:token) { send(token_type).token }
it "allows upload with #{params[:token_type]} token" do
upload_file(params: params, request_headers: headers.merge(basic_auth_header(token_type == :job ? ::Gitlab::Auth::CI_JOB_USER : user.username, token)))
expect(response).to have_gitlab_http_status(:ok)
end
end
end
context 'file name is too long' do
let(:file_name) { 'a' * (Packages::Maven::FindOrCreatePackageService::MAX_FILE_NAME_LENGTH + 1) }

View File

@ -19,6 +19,12 @@ RSpec.shared_context 'server metrics with mocked prometheus' do
let(:load_balancing_metric) { double('load balancing metric') }
let(:sidekiq_mem_total_bytes) { double('sidekiq mem total bytes') }
let(:completion_seconds_sum_metric) { double('sidekiq completion seconds sum metric') }
let(:completion_count_metric) { double('sidekiq completion seconds count metric') }
let(:cpu_seconds_sum_metric) { double('cpu seconds sum metric') }
let(:db_seconds_sum_metric) { double('db seconds sum metric') }
let(:gitaly_seconds_sum_metric) { double('gitaly seconds sum metric') }
let(:redis_seconds_sum_metric) { double('redis seconds sum metric') }
let(:elasticsearch_seconds_sum_metric) { double('elasticsearch seconds sum metric') }
before do
allow(Gitlab::Metrics).to receive(:histogram).and_call_original
@ -38,6 +44,12 @@ RSpec.shared_context 'server metrics with mocked prometheus' do
allow(Gitlab::Metrics).to receive(:counter).with(:sidekiq_elasticsearch_requests_total, anything).and_return(elasticsearch_requests_total)
allow(Gitlab::Metrics).to receive(:counter).with(:sidekiq_load_balancing_count, anything).and_return(load_balancing_metric)
allow(Gitlab::Metrics).to receive(:counter).with(:sidekiq_jobs_completion_seconds_sum, anything).and_return(completion_seconds_sum_metric)
allow(Gitlab::Metrics).to receive(:counter).with(:sidekiq_jobs_completion_count, anything).and_return(completion_count_metric)
allow(Gitlab::Metrics).to receive(:counter).with(:sidekiq_jobs_cpu_seconds_sum, anything).and_return(cpu_seconds_sum_metric)
allow(Gitlab::Metrics).to receive(:counter).with(:sidekiq_jobs_db_seconds_sum, anything).and_return(db_seconds_sum_metric)
allow(Gitlab::Metrics).to receive(:counter).with(:sidekiq_jobs_gitaly_seconds_sum, anything).and_return(gitaly_seconds_sum_metric)
allow(Gitlab::Metrics).to receive(:counter).with(:sidekiq_redis_requests_duration_seconds_sum, anything).and_return(redis_seconds_sum_metric)
allow(Gitlab::Metrics).to receive(:counter).with(:sidekiq_elasticsearch_requests_duration_seconds_sum, anything).and_return(elasticsearch_seconds_sum_metric)
allow(Gitlab::Metrics).to receive(:gauge).with(:sidekiq_running_jobs, anything, {}, :all).and_return(running_jobs_metric)
allow(Gitlab::Metrics).to receive(:gauge).with(:sidekiq_concurrency, anything, {}, :all).and_return(concurrency_metric)
allow(Gitlab::Metrics).to receive(:gauge).with(:sidekiq_mem_total_bytes, anything, {}, :all).and_return(sidekiq_mem_total_bytes)
@ -78,12 +90,8 @@ RSpec.shared_context 'server metrics call' do
}
end
let(:stub_subject) { true }
before do
if stub_subject
allow(subject).to receive(:get_thread_cputime).and_return(thread_cputime_before, thread_cputime_after)
end
allow(subject).to receive(:get_thread_cputime).and_return(thread_cputime_before, thread_cputime_after)
allow(Gitlab::Metrics::System).to receive(:monotonic_time).and_return(monotonic_time_before, monotonic_time_after)
allow(Gitlab::InstrumentationHelper).to receive(:queue_duration_for_job).with(job).and_return(queue_duration_for_job)
@ -101,9 +109,16 @@ RSpec.shared_context 'server metrics call' do
allow(redis_requests_total).to receive(:increment)
allow(elasticsearch_requests_total).to receive(:increment)
allow(completion_seconds_sum_metric).to receive(:increment)
allow(completion_count_metric).to receive(:increment)
allow(cpu_seconds_sum_metric).to receive(:increment)
allow(db_seconds_sum_metric).to receive(:increment)
allow(gitaly_seconds_sum_metric).to receive(:increment)
allow(redis_seconds_sum_metric).to receive(:increment)
allow(elasticsearch_seconds_sum_metric).to receive(:increment)
allow(queue_duration_seconds).to receive(:observe)
allow(user_execution_seconds_metric).to receive(:observe)
allow(db_seconds_metric).to receive(:observe)
allow(db_seconds_sum_metric).to receive(:increment)
allow(gitaly_seconds_metric).to receive(:observe)
allow(completion_seconds_metric).to receive(:observe)
allow(redis_seconds_metric).to receive(:observe)

View File

@ -0,0 +1,45 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe IpCidrArrayValidator, feature_category: :shared do
let(:model) do
Class.new do
include ActiveModel::Model
include ActiveModel::Validations
attr_accessor :cidr_array
alias_method :cidr_array_before_type_cast, :cidr_array
validates :cidr_array, ip_cidr_array: true
end.new
end
using RSpec::Parameterized::TableSyntax
# noinspection RubyMismatchedArgumentType - RubyMine is resolving `#|` from Array, instead of Rspec::Parameterized
where(:cidr_array, :validity, :errors) do
# rubocop:disable Layout/LineLength -- The RSpec table syntax often requires long lines for errors
nil | false | { cidr_array: ["must be an array of CIDR values"] }
'' | false | { cidr_array: ["must be an array of CIDR values"] }
['172.0.0.1/256'] | false | { cidr_array: ["IP '172.0.0.1/256' is not a valid CIDR: Invalid netmask 256"] }
%w[172.0.0.1/24 invalid-CIDR] | false | { cidr_array: ["IP 'invalid-CIDR' is not a valid CIDR: IP should be followed by a slash followed by an integer subnet mask (for example: '192.168.1.0/24')"] }
%w[172.0.0.1/256 invalid-CIDR] | false | { cidr_array: ["IP '172.0.0.1/256' is not a valid CIDR: Invalid netmask 256", "IP 'invalid-CIDR' is not a valid CIDR: IP should be followed by a slash followed by an integer subnet mask (for example: '192.168.1.0/24')"] }
['172.0.0.1/24', nil] | true | {}
%w[172.0.0.1/24 2001:db8::8:800:200c:417a/128] | true | {}
[] | true | {}
[nil] | true | {}
[''] | true | {}
# rubocop:enable Layout/LineLength
end
with_them do
before do
model.cidr_array = cidr_array
model.validate
end
it { expect(model.valid?).to eq(validity) }
it { expect(model.errors.messages).to eq(errors) }
end
end

View File

@ -0,0 +1,45 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe IpCidrValidator, feature_category: :shared do
let(:model) do
Class.new do
include ActiveModel::Model
include ActiveModel::Validations
attr_accessor :cidr
alias_method :cidr_before_type_cast, :cidr
validates :cidr, ip_cidr: true
end.new
end
using RSpec::Parameterized::TableSyntax
where(:cidr, :validity, :errors) do
# rubocop:disable Layout/LineLength -- The RSpec table syntax often requires long lines for errors'
'invalid-CIDR' | false | { cidr: ["IP 'invalid-CIDR' is not a valid CIDR: IP should be followed by a slash followed by an integer subnet mask (for example: '192.168.1.0/24')"] }
'172.0.0.1|256' | false | { cidr: ["IP '172.0.0.1|256' is not a valid CIDR: IP should be followed by a slash followed by an integer subnet mask (for example: '192.168.1.0/24')"] }
'172.0.0.1' | false | { cidr: ["IP '172.0.0.1' is not a valid CIDR: IP should be followed by a slash followed by an integer subnet mask (for example: '192.168.1.0/24')"] }
'172.0.0.1/2/12' | false | { cidr: ["IP '172.0.0.1/2/12' is not a valid CIDR: IP should be followed by a slash followed by an integer subnet mask (for example: '192.168.1.0/24')"] }
'172.0.0.1/256' | false | { cidr: ["IP '172.0.0.1/256' is not a valid CIDR: Invalid netmask 256"] }
'2001:db8::8:800:200c:417a/129' | false | { cidr: ["IP '2001:db8::8:800:200c:417a/129' is not a valid CIDR: Prefix must be in range 0..128, got: 129"] }
'2001:db8::8:800:200c:417a' | false | { cidr: ["IP '2001:db8::8:800:200c:417a' is not a valid CIDR: IP should be followed by a slash followed by an integer subnet mask (for example: '192.168.1.0/24')"] }
'2001:db8::8:800:200c:417a/128' | true | {}
'172.0.0.1/32' | true | {}
'' | true | {}
nil | true | {}
# rubocop:enable Layout/LineLength
end
with_them do
before do
model.cidr = cidr
model.validate
end
it { expect(model.valid?).to eq(validity) }
it { expect(model.errors.messages).to eq(errors) }
end
end

View File

@ -7,7 +7,6 @@ RSpec.describe 'groups/edit.html.haml', feature_category: :groups_and_projects d
before do
stub_template 'groups/settings/_code_suggestions' => ''
stub_template 'groups/settings/_ai_third_party_settings' => ''
end
describe '"Share with group lock" setting' do