Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
efd6f06bfa
commit
e808a772e7
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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 = {
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
|
|
|
|||
|
|
@ -67,6 +67,7 @@
|
|||
],
|
||||
"MemberInterface": [
|
||||
"GroupMember",
|
||||
"PendingGroupMember",
|
||||
"ProjectMember"
|
||||
],
|
||||
"NoteableInterface": [
|
||||
|
|
|
|||
|
|
@ -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')
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -0,0 +1 @@
|
|||
d31386b36b5db29deb9041febc116915f94fa7c551f1d91d5f474671dccdc709
|
||||
|
|
@ -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))
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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. |
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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).
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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 |
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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!
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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) }
|
||||
|
|
|
|||
|
|
@ -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 ""
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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) }
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in New Issue