Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2023-01-20 18:10:05 +00:00
parent 709948b7a6
commit cc4e1c884c
177 changed files with 1254 additions and 847 deletions

View File

@ -629,7 +629,6 @@ RSpec/FactoryBot/AvoidCreate:
- 'spec/views/projects/pipeline_schedules/_pipeline_schedule.html.haml_spec.rb'
- 'spec/views/projects/pipelines/show.html.haml_spec.rb'
- 'spec/views/projects/project_members/index.html.haml_spec.rb'
- 'spec/views/projects/runners/_specific_runners.html.haml_spec.rb'
- 'spec/views/projects/settings/ci_cd/_autodevops_form.html.haml_spec.rb'
- 'spec/views/projects/settings/integrations/edit.html.haml_spec.rb'
- 'spec/views/projects/settings/merge_requests/show.html.haml_spec.rb'

View File

@ -544,7 +544,7 @@ gem 'lockbox', '~> 1.1.1'
gem 'valid_email', '~> 0.1'
# JSON
gem 'json', '~> 2.5.1'
gem 'json', '~> 2.6.3'
gem 'json_schemer', '~> 0.2.18'
gem 'oj', '~> 3.13.21'
gem 'oj-introspect', '~> 0.7'

View File

@ -295,8 +295,8 @@
{"name":"jira-ruby","version":"2.1.4","platform":"ruby","checksum":"4267c095cac8323b9eef3ba866eb28bb1388b7623a5abb60c1e7caf12d4adb9e"},
{"name":"jmespath","version":"1.6.2","platform":"ruby","checksum":"238d774a58723d6c090494c8879b5e9918c19485f7e840f2c1c7532cf84ebcb1"},
{"name":"js_regex","version":"3.8.0","platform":"ruby","checksum":"7934bcdd5a0e6d5af4a520288fd4684a02a472ae55831d9178ccaf82356344b5"},
{"name":"json","version":"2.5.1","platform":"java","checksum":"be284a0c4a9d0373e81b0d5dfe71ed5b18d0479f05970e60a77be89a2978ce6c"},
{"name":"json","version":"2.5.1","platform":"ruby","checksum":"918d8c41dacb7cfdbe0c7bbd6014a5372f0cf1c454ca150e9f4010fe80cc3153"},
{"name":"json","version":"2.6.3","platform":"java","checksum":"ea8c47427a2c876121b9a0ab53043ca390013a76374330eabd923bd81914e563"},
{"name":"json","version":"2.6.3","platform":"ruby","checksum":"86aaea16adf346a2b22743d88f8dcceeb1038843989ab93cda44b5176c845459"},
{"name":"json-jwt","version":"1.15.3","platform":"ruby","checksum":"66db4f14e538a774c15502a5b5b26b1f3e7585481bbb96df490aa74b5c2d6110"},
{"name":"json_schemer","version":"0.2.18","platform":"ruby","checksum":"3362c21efbefdd12ce994e541a1e7fdb86fd267a6541dd8715e8a580fe3b6be6"},
{"name":"jsonpath","version":"1.1.2","platform":"ruby","checksum":"6804124c244d04418218acb85b15c7caa79c592d7d6970195300428458946d3a"},

View File

@ -799,7 +799,7 @@ GEM
character_set (~> 1.4)
regexp_parser (~> 2.5)
regexp_property_values (~> 1.0)
json (2.5.1)
json (2.6.3)
json-jwt (1.15.3)
activesupport (>= 4.2)
aes_key_wrap
@ -1712,7 +1712,7 @@ DEPENDENCIES
ipynbdiff!
jira-ruby (~> 2.1.4)
js_regex (~> 3.8)
json (~> 2.5.1)
json (~> 2.6.3)
json_schemer (~> 0.2.18)
jwt (~> 2.1.0)
kaminari (~> 1.2.2)

View File

@ -116,7 +116,7 @@ export default {
category="tertiary"
:aria-label="__('Remove')"
:title="__('Remove')"
class="gl-align-self-center gl-p-1! gl-absolute! gl-w-auto! gl-top-4 gl-right-4"
class="gl-align-self-center gl-p-1! gl-absolute! gl-w-auto! gl-right-4 gl-top-half gl-translate-y-n50"
data-testid="item-remove"
@click.stop.prevent="removeFrequentItem(itemId)"
>

View File

@ -1,6 +1,7 @@
<script>
import { GlLoadingIcon, GlModal, GlEmptyState } from '@gitlab/ui';
import { createAlert } from '~/flash';
import { HTTP_STATUS_FORBIDDEN } from '~/lib/utils/http_status';
import { mergeUrlParams, getParameterByName } from '~/lib/utils/url_utility';
import { __, s__, sprintf } from '~/locale';
@ -225,7 +226,7 @@ export default {
})
.catch((err) => {
let message = COMMON_STR.FAILURE;
if (err.status === 403) {
if (err.status === HTTP_STATUS_FORBIDDEN) {
message = COMMON_STR.LEAVE_FORBIDDEN;
}
createAlert({ message });

View File

@ -1,34 +1,31 @@
<script>
import {
GlDropdown,
GlDropdownItem,
GlDropdownDivider,
GlSearchBoxByType,
GlLoadingIcon,
} from '@gitlab/ui';
import { debounce } from 'lodash';
import { GlCollapsibleListbox } from '@gitlab/ui';
import Api from '~/api';
import { DEFAULT_DEBOUNCE_AND_THROTTLE_MS } from '~/lib/utils/constants';
import { __ } from '~/locale';
export default {
i18n: {
dropdownHeader: __('Namespaces'),
headerText: __('Namespaces'),
searchPlaceholder: __('Search for Namespace'),
anyNamespace: __('Any namespace'),
reset: __('Clear'),
},
components: {
GlDropdown,
GlDropdownItem,
GlDropdownDivider,
GlLoadingIcon,
GlSearchBoxByType,
GlCollapsibleListbox,
},
props: {
showAny: {
type: Boolean,
origSelectedId: {
type: String,
required: false,
default: false,
default: '',
},
placeholder: {
origSelectedText: {
type: String,
required: false,
default: '',
},
toggleTextPlaceholder: {
type: String,
required: false,
default: __('Namespace'),
@ -42,50 +39,66 @@ export default {
data() {
return {
namespaceOptions: [],
selectedNamespaceId: null,
selectedNamespace: null,
selectedNamespaceId: this.origSelectedId,
selectedNamespaceText: this.origSelectedText,
searchTerm: '',
isLoading: false,
};
},
computed: {
selectedNamespaceName() {
if (this.selectedNamespaceId === null) {
return this.placeholder;
}
return this.selectedNamespace;
toggleText() {
return this.selectedNamespaceText || this.toggleTextPlaceholder;
},
},
watch: {
searchTerm() {
this.fetchNamespaces(this.searchTerm);
selectedNamespaceId(val) {
if (!val) {
this.selectedNamespaceText = null;
}
this.selectedNamespaceText = this.namespaceOptions.find(({ value }) => value === val)?.text;
},
},
mounted() {
this.fetchNamespaces();
},
methods: {
fetchNamespaces(filter) {
fetchNamespaces() {
this.isLoading = true;
this.namespaceOptions = [];
return Api.namespaces(filter, (namespaces) => {
this.namespaceOptions = namespaces;
return Api.namespaces(this.searchTerm, (namespaces) => {
this.namespaceOptions = this.formatNamespaceOptions(namespaces);
this.isLoading = false;
});
},
selectNamespace(key) {
this.selectedNamespaceId = this.namespaceOptions[key].id;
this.selectedNamespace = this.getNamespaceString(this.namespaceOptions[key]);
this.$emit('setNamespace', this.selectedNamespaceId);
formatNamespaceOptions(namespaces) {
if (!namespaces) {
return [];
}
return namespaces.map((namespace) => {
return {
value: String(namespace.id),
text: this.getNamespaceString(namespace),
};
});
},
selectAnyNamespace() {
this.selectedNamespaceId = null;
this.selectedNamespace = null;
this.$emit('setNamespace', null);
selectNamespace(value) {
this.selectedNamespaceId = value;
this.$emit('setNamespace', this.selectedNamespaceId);
},
getNamespaceString(namespace) {
return `${namespace.kind}: ${namespace.full_path}`;
},
search: debounce(function debouncedSearch(searchQuery) {
this.searchTerm = searchQuery?.trim();
this.fetchNamespaces();
}, DEFAULT_DEBOUNCE_AND_THROTTLE_MS),
onReset() {
this.selectedNamespaceId = null;
this.$emit('setNamespace', null);
},
},
};
</script>
@ -99,45 +112,19 @@ export default {
type="hidden"
data-testid="hidden-input"
/>
<gl-dropdown
:text="selectedNamespaceName"
:header-text="$options.i18n.dropdownHeader"
toggle-class="dropdown-menu-toggle large"
data-testid="namespace-dropdown"
:right="true"
>
<template #header>
<gl-search-box-by-type
v-model.trim="searchTerm"
class="namespace-search-box"
debounce="250"
:placeholder="$options.i18n.searchPlaceholder"
/>
</template>
<template v-if="showAny">
<gl-dropdown-item @click="selectAnyNamespace">
{{ $options.i18n.anyNamespace }}
</gl-dropdown-item>
<gl-dropdown-divider />
</template>
<gl-loading-icon v-if="isLoading" />
<gl-dropdown-item
v-for="(namespace, key) in namespaceOptions"
:key="namespace.id"
@click="selectNamespace(key)"
>
{{ getNamespaceString(namespace) }}
</gl-dropdown-item>
</gl-dropdown>
<gl-collapsible-listbox
:items="namespaceOptions"
:header-text="$options.i18n.headerText"
:reset-button-label="$options.i18n.reset"
:toggle-text="toggleText"
:search-placeholder="$options.i18n.searchPlaceholder"
:searching="isLoading"
:selected="selectedNamespaceId"
toggle-class="gl-w-full gl-flex-direction-column gl-align-items-stretch!"
searchable
@reset="onReset"
@search="search"
@select="selectNamespace"
/>
</div>
</template>
<style scoped>
/* workaround position: relative imposed by .top-area .nav-controls */
.namespace-search-box >>> input {
position: static;
}
</style>

View File

@ -1,5 +1,4 @@
import Vue from 'vue';
import { parseBoolean } from '~/lib/utils/common_utils';
import { mergeUrlParams } from '~/lib/utils/url_utility';
import ProjectsList from '~/projects_list';
import NamespaceSelect from './components/namespace_select.vue';
@ -12,16 +11,17 @@ function mountNamespaceSelect() {
return false;
}
const { showAny, fieldName, placeholder, updateLocation } = el.dataset;
const { fieldName, toggleTextPlaceholder, selectedId, selectedText, updateLocation } = el.dataset;
return new Vue({
el,
render(createComponent) {
return createComponent(NamespaceSelect, {
props: {
showAny: parseBoolean(showAny),
fieldName,
placeholder,
toggleTextPlaceholder,
origSelectedId: selectedId,
origSelectedText: selectedText,
},
on: {
setNamespace(newNamespace) {

View File

@ -115,6 +115,7 @@ class RegistrationsController < Devise::RegistrationsController
def after_request_hook(user)
return unless user.persisted?
track_creation user: user
Gitlab::Tracking.event(self.class.name, 'successfully_submitted_form', user: user)
end
@ -145,6 +146,11 @@ class RegistrationsController < Devise::RegistrationsController
users_sign_up_welcome_path(glm_tracking_params)
end
def track_creation(user:)
label = user_invited? ? 'invited' : 'signup'
Gitlab::Tracking.event(self.class.name, 'create_user', label: label, user: user)
end
def ensure_destroy_prerequisites_met
if current_user.solo_owned_groups.present?
redirect_to profile_account_path,
@ -252,9 +258,15 @@ class RegistrationsController < Devise::RegistrationsController
end
end
def after_pending_invitations_hook
member_id = session.delete(:originating_member_id)
def user_invited?
!!member_id
end
def member_id
@member_id ||= session.delete(:originating_member_id)
end
def after_pending_invitations_hook
return unless member_id
# if invited multiple times to different projects, only the email clicked will be counted as accepted

View File

@ -0,0 +1,52 @@
# frozen_string_literal: true
# AcceptingProjectSharesFinder
#
# Used to filter Shareable Groups by a set of params
#
# Arguments:
# current_user - which user is requesting groups
# params:
# search: string
module Groups
class AcceptingProjectSharesFinder < Base
def initialize(current_user, project_to_be_shared, params = {})
@current_user = current_user
@params = params
@project_to_be_shared = project_to_be_shared
end
def execute
return Group.none unless can_share_project?
groups = if has_admin_access?
Group.all
else
groups_with_guest_access_plus
end
groups = groups.search(params[:search]) if params[:search].present?
sort(groups).with_route
end
private
attr_reader :current_user, :project_to_be_shared, :params
def has_admin_access?
current_user&.can_read_all_resources?
end
# rubocop: disable CodeReuse/Finder
def groups_with_guest_access_plus
GroupsFinder.new(current_user, min_access_level: Gitlab::Access::GUEST).execute
end
# rubocop: enable CodeReuse/Finder
def can_share_project?
Ability.allowed?(current_user, :admin_project, project_to_be_shared) &&
project_to_be_shared.allowed_to_share_with_group?
end
end
end

View File

@ -5,7 +5,7 @@ module Groups
private
def sort(items)
items.order(Group.arel_table[:path].asc, Group.arel_table[:id].asc) # rubocop: disable CodeReuse/ActiveRecord
items.reorder(Group.arel_table[:path].asc, Group.arel_table[:id].asc) # rubocop: disable CodeReuse/ActiveRecord
end
def by_search(items)

View File

@ -30,6 +30,7 @@ module Resolvers
previous_stage_jobs_or_needs: [:needs, :pipeline],
artifacts: [:job_artifacts],
pipeline: [:user],
project: [{ project: [:route, { namespace: [:route] }] }],
detailed_status: [
:metadata,
{ pipeline: [:merge_request] },

View File

@ -97,6 +97,8 @@ module Types
field :web_path, GraphQL::Types::String, null: true,
description: 'Web path of the job.'
field :project, Types::ProjectType, null: true, description: 'Project that the job belongs to.'
def kind
return ::Ci::Build unless [::Ci::Build, ::Ci::Bridge].include?(object.class)

View File

@ -13,10 +13,11 @@ class Packages::Debian::FileMetadatum < ApplicationRecord
}
validates :file_type, presence: true
validates :file_type, inclusion: { in: %w[unknown] }, if: -> { package_file&.package&.debian_incoming? }
validates :file_type, inclusion: { in: %w[unknown] },
if: -> { package_file&.package&.debian_incoming? || package_file&.package&.processing? }
validates :file_type,
inclusion: { in: %w[source dsc deb udeb buildinfo changes] },
if: -> { package_file&.package&.debian_package? }
if: -> { package_file&.package&.debian_package? && !package_file&.package&.processing? }
validates :component,
presence: true,

View File

@ -43,14 +43,12 @@ class ProtectedBranch < ApplicationRecord
end
def self.new_cache(project, ref_name, dry_run: true)
if Feature.enabled?(:hash_based_cache_for_protected_branches, project)
ProtectedBranches::CacheService.new(project).fetch(ref_name, dry_run: dry_run) do # rubocop: disable CodeReuse/ServiceClass
self.matching(ref_name, protected_refs: protected_refs(project)).present?
end
ProtectedBranches::CacheService.new(project).fetch(ref_name, dry_run: dry_run) do # rubocop: disable CodeReuse/ServiceClass
self.matching(ref_name, protected_refs: protected_refs(project)).present?
end
end
# Deprecated: https://gitlab.com/gitlab-org/gitlab/-/issues/368279
# Deprecated: https://gitlab.com/gitlab-org/gitlab/-/issues/370608
# ----------------------------------------------------------------
CACHE_EXPIRE_IN = 1.hour

View File

@ -46,10 +46,10 @@ module Ci
# Create shared runner. Requires admin access
{ runner_type: :instance_type }
elsif runner_registrar_valid?('project') && project = ::Project.find_by_runners_token(registration_token)
# Create a specific runner for the project
# Create a project runner
{ runner_type: :project_type, scope: project }
elsif runner_registrar_valid?('group') && group = ::Group.find_by_runners_token(registration_token)
# Create a specific runner for the group
# Create a group runner
{ runner_type: :group_type, scope: group }
end
end

View File

@ -10,19 +10,23 @@ module Packages
# used by ExclusiveLeaseGuard
DEFAULT_LEASE_TIMEOUT = 1.hour.to_i.freeze
def initialize(package_file, creator, distribution_name, component_name)
def initialize(package_file, distribution_name, component_name)
@package_file = package_file
@creator = creator
@distribution_name = distribution_name
@component_name = component_name
end
def execute
return if @package_file.package.pending_destruction?
try_obtain_lease do
validate!
@package_file.transaction do
package.transaction do
rename_package_and_set_version
update_package
update_file_metadata
cleanup_temp_package
end
::Packages::Debian::GenerateDistributionWorker.perform_async(:project, package.debian_distribution.id)
@ -40,6 +44,80 @@ module Packages
raise ArgumentError, "invalid package file type: #{file_metadata[:file_type]}"
end
def file_metadata
::Packages::Debian::ExtractMetadataService.new(@package_file).execute
end
strong_memoize_attr :file_metadata
def package
package = temp_package.project
.packages
.debian
.with_name(package_name)
.with_version(package_version)
.with_debian_codename(@distribution_name)
.not_pending_destruction
.last
package || temp_package
end
strong_memoize_attr :package
def temp_package
@package_file.package
end
strong_memoize_attr :temp_package
def package_name
package_name_and_version[0]
end
def package_version
package_name_and_version[1]
end
def package_name_and_version
package_name = file_metadata[:fields]['Package']
package_version = file_metadata[:fields]['Version']
if file_metadata[:fields]['Source']
# "sample" or "sample (1.2.3~alpha2)"
source_field_parts = file_metadata[:fields]['Source'].split(SOURCE_FIELD_SPLIT_REGEX)
package_name = source_field_parts[0]
package_version = source_field_parts[2] || package_version
end
[package_name, package_version]
end
strong_memoize_attr :package_name_and_version
def rename_package_and_set_version
package.update!(
name: package_name,
version: package_version,
status: :default
)
end
def update_package
return unless using_temporary_package?
package.update!(
debian_publication_attributes: { distribution_id: distribution.id }
)
end
def using_temporary_package?
package.id == temp_package.id
end
def distribution
Packages::Debian::DistributionsFinder.new(
@package_file.package.project,
codename: @distribution_name
).execute.last!
end
strong_memoize_attr :distribution
def update_file_metadata
::Packages::UpdatePackageFileService.new(@package_file, package_id: package.id)
.execute
@ -55,36 +133,8 @@ module Packages
)
end
def package
strong_memoize(:package) do
package_name = file_metadata[:fields]['Package']
package_version = file_metadata[:fields]['Version']
if file_metadata[:fields]['Source']
# "sample" or "sample (1.2.3~alpha2)"
source_field_parts = file_metadata[:fields]['Source'].split(SOURCE_FIELD_SPLIT_REGEX)
package_name = source_field_parts[0]
package_version = source_field_parts[2] || package_version
end
params = {
'name': package_name,
'version': package_version,
'distribution_name': @distribution_name
}
response = Packages::Debian::FindOrCreatePackageService.new(project, @creator, params).execute
response.payload[:package]
end
end
def file_metadata
strong_memoize(:metadata) do
::Packages::Debian::ExtractMetadataService.new(@package_file).execute
end
end
def project
@package_file.package.project
def cleanup_temp_package
temp_package.destroy unless using_temporary_package?
end
# used by ExclusiveLeaseGuard

View File

@ -16,13 +16,10 @@
.nav-controls.gl-pl-2
.search-holder
= render 'shared/projects/search_form', autofocus: true, admin_view: true
- current_namespace = _('Namespace')
- if params[:namespace_id].present?
- namespace = Namespace.find(params[:namespace_id])
- current_namespace = "#{namespace.kind}: #{namespace.full_path}"
%button.dropdown-menu-toggle.btn.btn-default.btn-md.gl-button.js-namespace-select{ data: { show_any: 'true', field_name: 'namespace_id', placeholder: current_namespace, update_location: 'true' }, type: 'button' }
%span.gl-dropdown-button-text
= current_namespace
- selected_text = "#{namespace.kind}: #{namespace.full_path}" if namespace
.js-namespace-select{ data: { field_name: 'namespace_id', selected_id: namespace&.id, selected_text: selected_text, update_location: 'true' } }
= link_to new_project_path, class: 'gl-button btn btn-confirm' do
= _('New Project')

View File

@ -140,10 +140,7 @@
.col-sm-3.col-form-label
= f.label :new_namespace_id, _("Namespace")
.col-sm-9
- placeholder = _('Search for Namespace')
%button.dropdown-menu-toggle.btn.btn-default.btn-md.gl-button.js-namespace-select{ data: { field_name: 'new_namespace_id', placeholder: placeholder }, type: 'button' }
%span.gl-dropdown-button-text
= placeholder
.js-namespace-select{ data: { field_name: 'new_namespace_id', toggle_text_placeholder: _('Search for Namespace') } }
.form-group.row
.offset-sm-3.col-sm-9

View File

@ -30,6 +30,7 @@
= render_if_exists 'groups/merge_request_approval_settings', expanded: expanded, group: @group, user: current_user
= render_if_exists 'groups/insights', expanded: expanded
= render_if_exists 'groups/analytics_dashboards', expanded: expanded
%section.settings.no-animate#js-badge-settings{ class: ('expanded' if expanded) }
.settings-header

View File

@ -1,9 +1,9 @@
%h4
= _('Specific runners')
= s_('Runners|Project runners')
.bs-callout.help-callout
- if can?(current_user, :register_project_runners, @project)
= _('These runners are specific to this project.')
= s_('Runners|These runners are assigned to this project.')
- if params[:ci_runner_templates]
%hr
= render partial: 'ci/runner/setup_runner_in_aws',
@ -11,7 +11,7 @@
%hr
= render partial: 'ci/runner/how_to_setup_runner',
locals: { registration_token: @project.runners_token,
type: s_('Runners|specific'),
type: _('project'),
reset_token_url: reset_registration_token_namespace_project_settings_ci_cd_path,
project_path: @project.path_with_namespace,
group_path: '' }
@ -22,13 +22,13 @@
%hr
- if @project_runners.any?
%h4.underlined-title= _('Available specific runners')
%ul.bordered-list.activated-specific-runners
%h4.underlined-title= s_('Runners|Assigned project runners')
%ul.bordered-list{ data: { testid: 'assigned_project_runners' } }
= render partial: 'projects/runners/runner', collection: @project_runners, as: :runner
= paginate @project_runners, theme: "gitlab", param_name: "project_page", params: { expand_runners: true, anchor: 'js-runners-settings' }
- if @assignable_runners.any?
%h4.underlined-title= _('Other available runners')
%ul.bordered-list.available-specific-runners
%ul.bordered-list{ data: { testid: 'available_project_runners' } }
= render partial: 'projects/runners/runner', collection: @assignable_runners, as: :runner
= paginate @assignable_runners, theme: "gitlab", param_name: "specific_page", :params => { :anchor => 'js-runners-settings'}

View File

@ -2,7 +2,7 @@
.row
.col-sm-6
= render 'projects/runners/specific_runners'
= render 'projects/runners/project_runners'
.col-sm-6
= render 'projects/runners/shared_runners'
= render 'projects/runners/group_runners'

View File

@ -5,5 +5,4 @@
= render partial: 'projects/issues/issue', collection: @issues
= paginate @issues, theme: "gitlab"
- else
- project_select_button = local_assigns.fetch(:project_select_button, false)
= render 'shared/empty_states/issues', project_select_button: project_select_button
= render 'shared/empty_states/issues'

View File

@ -1,7 +1,5 @@
- button_path = local_assigns.fetch(:new_project_issue_button_path, false)
- project_select_button = local_assigns.fetch(:project_select_button, false)
- show_import_button = local_assigns.fetch(:show_import_button, false) && can?(current_user, :import_issues, @project)
- has_button = button_path || project_select_button
- closed_issues_count = issuables_count_for_state(:issues, :closed)
- opened_issues_count = issuables_count_for_state(:issues, :opened)
- is_opened_state = params[:state] == 'opened'
@ -39,11 +37,9 @@
= _("The Issue Tracker is the place to add things that need to be improved or solved in a project")
%p
= _("Issues can be bugs, tasks or ideas to be discussed. Also, issues are searchable and filterable.")
- if has_button
- if button_path
.text-center
- if project_select_button
= render 'shared/new_project_item_select', path: 'issues/new', label: _('issue'), type: :issues, with_feature_enabled: 'issues'
- elsif show_new_issue_link?(@project)
- if show_new_issue_link?(@project)
= link_to _('New issue'), button_path, class: 'gl-button btn btn-confirm', id: 'new_issue_link'
- if show_import_button

View File

@ -1,6 +1,4 @@
- button_path = local_assigns.fetch(:button_path, false)
- project_select_button = local_assigns.fetch(:project_select_button, false)
- has_button = button_path || project_select_button
- closed_merged_count = issuables_count_for_state(:merged, :closed)
- opened_merged_count = issuables_count_for_state(:merged, :opened)
- is_opened_state = params[:state] == 'opened'
@ -37,9 +35,6 @@
= _("Merge requests are a place to propose changes you've made to a project and discuss those changes with others")
%p
= _("Interested parties can even contribute by pushing commits if they want to.")
- if has_button
- if button_path
.text-center
- if project_select_button
= render 'shared/new_project_item_select', path: 'merge_requests/new', label: _('merge request'), type: :merge_requests, with_feature_enabled: 'merge_requests'
- else
= link_to _('New merge request'), button_path, class: 'gl-button btn btn-confirm', title: _('New merge request'), id: 'new_merge_request_link', data: { qa_selector: "new_merge_request_button" }
= link_to _('New merge request'), button_path, class: 'gl-button btn btn-confirm', title: _('New merge request'), id: 'new_merge_request_link', data: { qa_selector: "new_merge_request_button" }

View File

@ -12,5 +12,5 @@
title: s_('Runners|This runner is associated with specific projects.'),
dismissible: false) do |c|
= c.body do
= s_('Runners|You can set up a specific runner to be used by multiple projects but you cannot make this a shared or group runner.')
= link_to _('Learn more.'), help_page_path('ci/runners/runners_scope', anchor: 'specific-runners'), target: '_blank', rel: 'noopener noreferrer'
= s_('Runners|You can set up a project runner to be used by multiple projects but you cannot make this a shared or group runner.')
= link_to _('Learn more.'), help_page_path('ci/runners/runners_scope', anchor: 'project-runners'), target: '_blank', rel: 'noopener noreferrer'

View File

@ -4,4 +4,4 @@
- elsif runner.group_type?
= gl_badge_tag s_('Runners|group'), variant: :success
- else
= gl_badge_tag s_('Runners|specific'), variant: :info
= gl_badge_tag s_('Runners|project'), variant: :info

View File

@ -14,21 +14,20 @@ module Packages
queue_namespace :package_repositories
feature_category :package_registry
def perform(package_file_id, user_id, distribution_name, component_name)
def perform(package_file_id, distribution_name, component_name)
@package_file_id = package_file_id
@user_id = user_id
@distribution_name = distribution_name
@component_name = component_name
return unless package_file && user && distribution_name && component_name
return unless package_file && distribution_name && component_name
# return if file has already been processed
return unless package_file.debian_file_metadatum&.unknown?
::Packages::Debian::ProcessPackageFileService.new(package_file, user, distribution_name, component_name).execute
::Packages::Debian::ProcessPackageFileService.new(package_file, distribution_name, component_name).execute
rescue StandardError => e
Gitlab::ErrorTracking.log_exception(e, package_file_id: @package_file_id, user_id: @user_id,
Gitlab::ErrorTracking.log_exception(e, package_file_id: @package_file_id,
distribution_name: @distribution_name, component_name: @component_name)
package_file.destroy!
package_file.package.update_column(:status, :error)
end
private
@ -37,11 +36,6 @@ module Packages
::Packages::PackageFile.find_by_id(@package_file_id)
end
strong_memoize_attr :package_file
def user
::User.find_by_id(@user_id)
end
strong_memoize_attr :user
end
end
end

View File

@ -1,8 +0,0 @@
---
name: hash_based_cache_for_protected_branches
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/92934
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/368279
milestone: '15.3'
type: development
group: group::source code
default_enabled: true

View File

@ -1,8 +0,0 @@
---
name: use_primary_and_secondary_stores_for_duplicate_jobs
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/85740
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/364381
milestone:
type: development
group: group::scalability
default_enabled: false

View File

@ -1,8 +0,0 @@
---
name: use_primary_store_as_default_for_duplicate_jobs
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/85740
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/364381
milestone:
type: development
group: group::scalability
default_enabled: false

View File

@ -196,6 +196,10 @@ dast_profiles_pipelines:
- table: ci_pipelines
column: ci_pipeline_id
on_delete: async_delete
dast_profiles_tags:
- table: tags
column: tag_id
on_delete: async_delete
dast_scanner_profiles_builds:
- table: ci_builds
column: ci_build_id

View File

@ -0,0 +1,10 @@
---
table_name: dast_profiles_tags
classes:
- Dast::ScannerProfileTag
feature_categories:
- dynamic_application_security_testing
description: Join Table for Runner tags and DAST Profiles
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/108371
milestone: '15.8'
gitlab_schema: gitlab_main

View File

@ -0,0 +1,18 @@
# frozen_string_literal: true
class CreateDastProfilesTags < Gitlab::Database::Migration[2.1]
def up
create_table :dast_profiles_tags do |t|
t.references :dast_profile, null: false, foreign_key: { on_delete: :cascade },
index: { name: 'i_dast_profiles_tags_on_scanner_profiles_id' }
t.bigint :tag_id, null: false
t.index :tag_id, name: :index_dast_profiles_tags_on_tag_id
end
end
def down
drop_table :dast_profiles_tags
end
end

View File

@ -0,0 +1 @@
dad6e8972db3829dc6c02013ee87b08aa9bf4c50e58b35b0dbd67935ee4c266a

View File

@ -14573,6 +14573,21 @@ CREATE TABLE dast_profiles_pipelines (
COMMENT ON TABLE dast_profiles_pipelines IS '{"owner":"group::dynamic analysis","description":"Join table between DAST Profiles and CI Pipelines"}';
CREATE TABLE dast_profiles_tags (
id bigint NOT NULL,
dast_profile_id bigint NOT NULL,
tag_id bigint NOT NULL
);
CREATE SEQUENCE dast_profiles_tags_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE dast_profiles_tags_id_seq OWNED BY dast_profiles_tags.id;
CREATE TABLE dast_scanner_profiles (
id bigint NOT NULL,
created_at timestamp with time zone NOT NULL,
@ -24104,6 +24119,8 @@ ALTER TABLE ONLY dast_profile_schedules ALTER COLUMN id SET DEFAULT nextval('das
ALTER TABLE ONLY dast_profiles ALTER COLUMN id SET DEFAULT nextval('dast_profiles_id_seq'::regclass);
ALTER TABLE ONLY dast_profiles_tags ALTER COLUMN id SET DEFAULT nextval('dast_profiles_tags_id_seq'::regclass);
ALTER TABLE ONLY dast_scanner_profiles ALTER COLUMN id SET DEFAULT nextval('dast_scanner_profiles_id_seq'::regclass);
ALTER TABLE ONLY dast_scanner_profiles_tags ALTER COLUMN id SET DEFAULT nextval('dast_scanner_profiles_tags_id_seq'::regclass);
@ -25979,6 +25996,9 @@ ALTER TABLE ONLY dast_profiles_pipelines
ALTER TABLE ONLY dast_profiles
ADD CONSTRAINT dast_profiles_pkey PRIMARY KEY (id);
ALTER TABLE ONLY dast_profiles_tags
ADD CONSTRAINT dast_profiles_tags_pkey PRIMARY KEY (id);
ALTER TABLE ONLY dast_scanner_profiles_builds
ADD CONSTRAINT dast_scanner_profiles_builds_pkey PRIMARY KEY (dast_scanner_profile_id, ci_build_id);
@ -28305,6 +28325,8 @@ CREATE INDEX i_compliance_frameworks_on_id_and_created_at ON compliance_manageme
CREATE INDEX i_dast_pre_scan_verification_steps_on_pre_scan_verification_id ON dast_pre_scan_verification_steps USING btree (dast_pre_scan_verification_id);
CREATE INDEX i_dast_profiles_tags_on_scanner_profiles_id ON dast_profiles_tags USING btree (dast_profile_id);
CREATE INDEX i_dast_scanner_profiles_tags_on_scanner_profiles_id ON dast_scanner_profiles_tags USING btree (dast_scanner_profile_id);
CREATE UNIQUE INDEX i_pm_licenses_on_spdx_identifier ON pm_licenses USING btree (spdx_identifier);
@ -29287,6 +29309,8 @@ CREATE UNIQUE INDEX index_dast_profiles_on_project_id_and_name ON dast_profiles
CREATE UNIQUE INDEX index_dast_profiles_pipelines_on_ci_pipeline_id ON dast_profiles_pipelines USING btree (ci_pipeline_id);
CREATE INDEX index_dast_profiles_tags_on_tag_id ON dast_profiles_tags USING btree (tag_id);
CREATE UNIQUE INDEX index_dast_scanner_profiles_on_project_id_and_name ON dast_scanner_profiles USING btree (project_id, name);
CREATE INDEX index_dast_scanner_profiles_tags_on_tag_id ON dast_scanner_profiles_tags USING btree (tag_id);
@ -35332,6 +35356,9 @@ ALTER TABLE ONLY merge_request_user_mentions
ALTER TABLE ONLY x509_commit_signatures
ADD CONSTRAINT fk_rails_ab07452314 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
ALTER TABLE ONLY dast_profiles_tags
ADD CONSTRAINT fk_rails_ab9e643cd8 FOREIGN KEY (dast_profile_id) REFERENCES dast_profiles(id) ON DELETE CASCADE;
ALTER TABLE ONLY resource_iteration_events
ADD CONSTRAINT fk_rails_abf5d4affa FOREIGN KEY (issue_id) REFERENCES issues(id) ON DELETE CASCADE;

View File

@ -1961,6 +1961,7 @@ Input type: `DastProfileCreateInput`
| <a id="mutationdastprofilecreatefullpath"></a>`fullPath` | [`ID!`](#id) | Project the profile belongs to. |
| <a id="mutationdastprofilecreatename"></a>`name` | [`String!`](#string) | Name of the profile. |
| <a id="mutationdastprofilecreaterunaftercreate"></a>`runAfterCreate` | [`Boolean`](#boolean) | Run scan using profile after creation. Defaults to false. |
| <a id="mutationdastprofilecreatetaglist"></a>`tagList` | [`[String!]`](#string) | Indicates the runner tags associated with the profile. |
#### Fields
@ -2027,6 +2028,7 @@ Input type: `DastProfileUpdateInput`
| <a id="mutationdastprofileupdateid"></a>`id` | [`DastProfileID!`](#dastprofileid) | ID of the profile to be deleted. |
| <a id="mutationdastprofileupdatename"></a>`name` | [`String`](#string) | Name of the profile. |
| <a id="mutationdastprofileupdaterunafterupdate"></a>`runAfterUpdate` | [`Boolean`](#boolean) | Run scan using profile after update. Defaults to false. |
| <a id="mutationdastprofileupdatetaglist"></a>`tagList` | [`[String!]`](#string) | Indicates the runner tags associated with the profile. |
#### Fields
@ -2051,7 +2053,7 @@ Input type: `DastScannerProfileCreateInput`
| <a id="mutationdastscannerprofilecreatescantype"></a>`scanType` | [`DastScanTypeEnum`](#dastscantypeenum) | Indicates the type of DAST scan that will run. Either a Passive Scan or an Active Scan. |
| <a id="mutationdastscannerprofilecreateshowdebugmessages"></a>`showDebugMessages` | [`Boolean`](#boolean) | Indicates if debug messages should be included in DAST console output. True to include the debug messages. |
| <a id="mutationdastscannerprofilecreatespidertimeout"></a>`spiderTimeout` | [`Int`](#int) | Maximum number of minutes allowed for the spider to traverse the site. |
| <a id="mutationdastscannerprofilecreatetaglist"></a>`tagList` | [`[String!]`](#string) | Indicates the runner tags associated with the scanner profile. |
| <a id="mutationdastscannerprofilecreatetaglist"></a>`tagList` **{warning-solid}** | [`[String!]`](#string) | **Deprecated:** Moved to DastProfile. Deprecated in 15.8. |
| <a id="mutationdastscannerprofilecreatetargettimeout"></a>`targetTimeout` | [`Int`](#int) | Maximum number of seconds allowed for the site under test to respond to a request. |
| <a id="mutationdastscannerprofilecreateuseajaxspider"></a>`useAjaxSpider` | [`Boolean`](#boolean) | Indicates if the AJAX spider should be used to crawl the target site. True to run the AJAX spider in addition to the traditional spider, and false to run only the traditional spider. |
@ -2098,7 +2100,7 @@ Input type: `DastScannerProfileUpdateInput`
| <a id="mutationdastscannerprofileupdatescantype"></a>`scanType` | [`DastScanTypeEnum`](#dastscantypeenum) | Indicates the type of DAST scan that will run. Either a Passive Scan or an Active Scan. |
| <a id="mutationdastscannerprofileupdateshowdebugmessages"></a>`showDebugMessages` | [`Boolean`](#boolean) | Indicates if debug messages should be included in DAST console output. True to include the debug messages. |
| <a id="mutationdastscannerprofileupdatespidertimeout"></a>`spiderTimeout` | [`Int!`](#int) | Maximum number of minutes allowed for the spider to traverse the site. |
| <a id="mutationdastscannerprofileupdatetaglist"></a>`tagList` | [`[String!]`](#string) | Indicates the runner tags associated with the scanner profile. |
| <a id="mutationdastscannerprofileupdatetaglist"></a>`tagList` **{warning-solid}** | [`[String!]`](#string) | **Deprecated:** Moved to DastProfile. Deprecated in 15.8. |
| <a id="mutationdastscannerprofileupdatetargettimeout"></a>`targetTimeout` | [`Int!`](#int) | Maximum number of seconds allowed for the site under test to respond to a request. |
| <a id="mutationdastscannerprofileupdateuseajaxspider"></a>`useAjaxSpider` | [`Boolean`](#boolean) | Indicates if the AJAX spider should be used to crawl the target site. True to run the AJAX spider in addition to the traditional spider, and false to run only the traditional spider. |
@ -11241,6 +11243,7 @@ CI/CD variables for a GitLab instance.
| <a id="cijobpipeline"></a>`pipeline` | [`Pipeline`](#pipeline) | Pipeline the job belongs to. |
| <a id="cijobplayable"></a>`playable` | [`Boolean!`](#boolean) | Indicates the job can be played. |
| <a id="cijobpreviousstagejobsorneeds"></a>`previousStageJobsOrNeeds` | [`JobNeedUnionConnection`](#jobneedunionconnection) | Jobs that must complete before the job runs. Returns `BuildNeed`, which is the needed jobs if the job uses the `needs` keyword, or the previous stage jobs otherwise. (see [Connections](#connections)) |
| <a id="cijobproject"></a>`project` | [`Project`](#project) | Project that the job belongs to. |
| <a id="cijobqueuedat"></a>`queuedAt` | [`Time`](#time) | When the job was enqueued and marked as pending. |
| <a id="cijobqueuedduration"></a>`queuedDuration` | [`Duration`](#duration) | How long the job was enqueued before starting. |
| <a id="cijobrefname"></a>`refName` | [`String`](#string) | Ref name of the job. |
@ -12001,6 +12004,7 @@ Represents a DAST Profile.
| <a id="dastprofileeditpath"></a>`editPath` | [`String`](#string) | Relative web path to the edit page of a profile. |
| <a id="dastprofileid"></a>`id` | [`DastProfileID!`](#dastprofileid) | ID of the profile. |
| <a id="dastprofilename"></a>`name` | [`String`](#string) | Name of the profile. |
| <a id="dastprofiletaglist"></a>`tagList` | [`[String!]`](#string) | Runner tags associated with the profile. |
### `DastProfileBranch`
@ -12055,7 +12059,7 @@ Represents a DAST scanner profile.
| <a id="dastscannerprofilescantype"></a>`scanType` | [`DastScanTypeEnum`](#dastscantypeenum) | Indicates the type of DAST scan that will run. Either a Passive Scan or an Active Scan. |
| <a id="dastscannerprofileshowdebugmessages"></a>`showDebugMessages` | [`Boolean!`](#boolean) | Indicates if debug messages should be included in DAST console output. True to include the debug messages. |
| <a id="dastscannerprofilespidertimeout"></a>`spiderTimeout` | [`Int`](#int) | Maximum number of minutes allowed for the spider to traverse the site. |
| <a id="dastscannerprofiletaglist"></a>`tagList` | [`[String!]`](#string) | Runner tags associated with the scanner profile. |
| <a id="dastscannerprofiletaglist"></a>`tagList` **{warning-solid}** | [`[String!]`](#string) | **Deprecated** in 15.8. Moved to DastProfile. |
| <a id="dastscannerprofiletargettimeout"></a>`targetTimeout` | [`Int`](#int) | Maximum number of seconds allowed for the site under test to respond to a request. |
| <a id="dastscannerprofileuseajaxspider"></a>`useAjaxSpider` | [`Boolean!`](#boolean) | Indicates if the AJAX spider should be used to crawl the target site. True to run the AJAX spider in addition to the traditional spider, and false to run only the traditional spider. |

View File

@ -1135,6 +1135,7 @@ curl --request PUT --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab
> - Immediately deleting subgroups was [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/360008) in GitLab 15.3 [with a flag](../administration/feature_flags.md) named `immediate_delete_subgroup_api`. Disabled by default.
> - Immediately deleting subgroups was [enabled on GitLab.com and self-managed](https://gitlab.com/gitlab-org/gitlab/-/issues/368276) in GitLab 15.4.
> - Immediately deleting subgroups was [enabled](https://gitlab.com/gitlab-org/gitlab/-/issues/368276) by default in GitLab 15.4.
> - The flag `immediate_delete_subgroup_api` for immediately deleting subgroups was [removed](https://gitlab.com/gitlab-org/gitlab/-/issues/374069) in GitLab 15.9.
Only available to group owners and administrators.

View File

@ -1173,6 +1173,40 @@ GET /projects/:id/groups
]
```
## List a project's shareable groups
Get a list of groups that can be shared with a project
```plaintext
GET /projects/:id/share_locations
```
| Attribute | Type | Required | Description |
|-----------------------------|-------------------|------------------------|-------------|
| `id` | integer or string | **{check-circle}** Yes | The ID or [URL-encoded path of the project](rest/index.md#namespaced-path-encoding). |
| `search` | string | **{dotted-circle}** No | Search for specific groups. |
```json
[
{
"id": 22,
"web_url": "http://127.0.0.1:3000/groups/gitlab-org",
"name": "Gitlab Org",
"avatar_url": null,
"full_name": "Gitlab Org",
"full_path": "gitlab-org"
},
{
"id": 25,
"web_url": "http://127.0.0.1:3000/groups/gnuwget",
"name": "Gnuwget",
"avatar_url": null,
"full_name": "Gnuwget",
"full_path": "gnuwget"
}
]
```
## Get project events
Refer to the [Events API documentation](events.md#list-a-projects-visible-events).
@ -2586,20 +2620,6 @@ GET /projects/:id/push_rule
}
```
Users of [GitLab Premium or higher](https://about.gitlab.com/pricing/)
can also see the `commit_committer_check` and `reject_unsigned_commits`
parameters:
```json
{
"id": 1,
"project_id": 3,
"commit_committer_check": false,
"reject_unsigned_commits": false
...
}
```
### Add project push rule
Adds a push rule to a specified project.

View File

@ -33,7 +33,7 @@ GitLab and the runner are then connected.
## List owned runners
Get a list of specific runners available to the user.
Get a list of runners available to the user.
```plaintext
GET /runners
@ -46,7 +46,7 @@ GET /runners?tag_list=tag1,tag2
| Attribute | Type | Required | Description |
|------------|--------------|----------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `scope` | string | no | Deprecated: Use `type` or `status` instead. The scope of specific runners to return, one of: `active`, `paused`, `online` and `offline`; showing all runners if none provided |
| `scope` | string | no | Deprecated: Use `type` or `status` instead. The scope of runners to return, one of: `active`, `paused`, `online` and `offline`; showing all runners if none provided |
| `type` | string | no | The type of runners to return, one of: `instance_type`, `group_type`, `project_type` |
| `status` | string | no | The status of runners to return, one of: `online`, `offline`, `stale`, and `never_contacted`. `active` and `paused` are also possible values which were deprecated in GitLab 14.8 and will be removed in GitLab 16.0 |
| `paused` | boolean | no | Whether to include only runners that are accepting or ignoring new jobs |
@ -97,7 +97,7 @@ Example response:
## List all runners **(FREE SELF)**
Get a list of all runners in the GitLab instance (specific and shared). Access
Get a list of all runners in the GitLab instance (project and shared). Access
is restricted to users with administrator access.
```plaintext
@ -325,7 +325,7 @@ Example response:
### Pause a runner
Pause a specific runner.
Pause a runner.
```plaintext
PUT --form "paused=true" /runners/:runner_id
@ -463,7 +463,7 @@ GET /projects/:id/runners?tag_list=tag1,tag2
| Attribute | Type | Required | Description |
|------------|----------------|----------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](rest/index.md#namespaced-path-encoding) owned by the authenticated user |
| `scope` | string | no | Deprecated: Use `type` or `status` instead. The scope of specific runners to return, one of: `active`, `paused`, `online` and `offline`; showing all runners if none provided |
| `scope` | string | no | Deprecated: Use `type` or `status` instead. The scope of runners to return, one of: `active`, `paused`, `online` and `offline`; showing all runners if none provided |
| `type` | string | no | The type of runners to return, one of: `instance_type`, `group_type`, `project_type` |
| `status` | string | no | The status of runners to return, one of: `online`, `offline`, `stale`, and `never_contacted`. `active` and `paused` are also possible values which were deprecated in GitLab 14.8 and will be removed in GitLab 16.0 |
| `paused` | boolean | no | Whether to include only runners that are accepting or ignoring new jobs |
@ -514,7 +514,7 @@ Example response:
## Enable a runner in project
Enable an available specific runner in the project.
Enable an available project runner in the project.
```plaintext
POST /projects/:id/runners
@ -548,7 +548,7 @@ Example response:
## Disable a runner from project
Disable a specific runner from the project. It works only if the project isn't
Disable a project runner from the project. It works only if the project isn't
the only project associated with the specified runner. If so, an error is
returned. Use the call to [delete a runner](#delete-a-runner) instead.

View File

@ -47,7 +47,7 @@ To ensure maximum availability of the cache, do one or more of the following:
- [Tag your runners](../runners/configure_runners.md#use-tags-to-control-which-jobs-a-runner-can-run) and use the tag on jobs
that share the cache.
- [Use runners that are only available to a particular project](../runners/runners_scope.md#prevent-a-specific-runner-from-being-enabled-for-other-projects).
- [Use runners that are only available to a particular project](../runners/runners_scope.md#prevent-a-project-runner-from-being-enabled-for-other-projects).
- [Use a `key`](../yaml/index.md#cachekey) that fits your workflow. For example,
you can configure a different cache for each branch.

View File

@ -8,15 +8,30 @@ info: To determine the technical writer assigned to the Stage/Group associated w
> - `CI_JOB_JWT` variable for reading secrets from Vault [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/207125) in GitLab 12.10.
> - `CI_JOB_JWT_V2` variable to support additional OIDC providers [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/346737) in GitLab 14.7.
> - [ID tokens](../yaml/index.md) to support any OIDC provider, including HashiCorp Vault, [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/356986) in GitLab 15.7.
GitLab CI/CD supports [OpenID Connect (OIDC)](https://openid.net/connect/faq/) that allows your build and deployment job access to cloud credentials and services. Historically, teams stored secrets in projects or applied permissions on the GitLab Runner instance to build and deploy. To support this, a predefined variable named `CI_JOB_JWT_V2` is included in the CI/CD job allowing you to follow a scalable and least-privilege security approach.
GitLab CI/CD supports [OpenID Connect (OIDC)](https://openid.net/connect/faq/) to
give your build and deployment jobs access to cloud credentials and services.
Historically, teams stored secrets in projects or applied permissions on the GitLab Runner
instance to build and deploy. OIDC capable [ID tokens](../yaml/index.md#id_tokens) are configurable
in the CI/CD job allowing you to follow a scalable and least-privilege security approach.
In GitLab 15.6 and earlier, you must use `CI_JOB_JWT_V2` instead of an ID token,
but it is not customizable. In GitLab 14.6 an earlier you must use the `CI_JOB_JWT`, which has limited support.
## Requirements
- Account on GitLab.
- Access to a cloud provider that supports OIDC to configure authorization and create roles.
The original implementation of `CI_JOB_JWT` supports [HashiCorp Vault integration](../examples/authenticating-with-hashicorp-vault/index.md). The updated implementation of `CI_JOB_JWT_V2` supports additional cloud providers with OIDC including AWS, Azure, GCP, and Vault.
ID tokens and `CI_JOB_JWT_V2` support cloud providers with OIDC, including:
- AWS
- Azure
- GCP
- HashiCorp Vault
The `CI_JOB_JWT` only supports the [HashiCorp Vault integration](../examples/authenticating-with-hashicorp-vault/index.md).
NOTE:
Configuring OIDC enables JWT token access to the target environments for all pipelines.
@ -25,10 +40,6 @@ review for the pipeline, focusing on the additional access. You can use the [sof
as a starting point, and for more information about supply chain attacks, see
[How a DevOps Platform helps protect against supply chain attacks](https://about.gitlab.com/blog/2021/04/28/devops-platform-supply-chain-attacks/).
The `CI_JOB_JWT_V2` variable is available for testing, but the full feature is planned
to be generally available when [issue 360657](https://gitlab.com/gitlab-org/gitlab/-/issues/360657)
is complete.
## Use cases
- Removes the need to store secrets in your GitLab group or project. Temporary credentials can be retrieved from your cloud provider through OIDC.
@ -39,18 +50,18 @@ is complete.
## How it works
Each job has a JSON web token (JWT) provided as a CI/CD [predefined variable](../variables/predefined_variables.md) named `CI_JOB_JWT` or `CI_JOB_JWT_V2`. This JWT can be used to authenticate with the OIDC-supported cloud provider such as AWS, Azure, GCP, or Vault.
Each job can be configured with ID tokens, which are provided as a CI/CD variable. These JWTs can be used to authenticate with the OIDC-supported cloud provider such as AWS, Azure, GCP, or Vault.
The following fields are included in the JWT:
| Field | When | Description |
| ----------------------- | ------ | ----------- |
| `aud` | Always | Specified in the [ID tokens](../yaml/index.md#id_tokens) configuration |
| `jti` | Always | Unique identifier for this token |
| `iss` | Always | Issuer, the domain of your GitLab instance |
| `iat` | Always | Issued at |
| `nbf` | Always | Not valid before |
| `exp` | Always | Expires at |
| `aud` | Always | Issuer, the domain of your GitLab instance |
| `sub` | Always |`project_path:{group}/{project}:ref_type:{type}:ref:{branch_name}` |
| `namespace_id` | Always | Use this to scope to group or user level namespace by ID |
| `namespace_path` | Always | Use this to scope to group or user level namespace by path |
@ -72,7 +83,7 @@ The following fields are included in the JWT:
{
"jti": "c82eeb0c-5c6f-4a33-abf5-4c474b92b558",
"iss": "https://gitlab.example.com",
"aud": "https://gitlab.example.com",
"aud": "https://vault.example.com",
"iat": 1585710286,
"nbf": 1585798372,
"exp": 1585713886,
@ -102,8 +113,8 @@ sequenceDiagram
participant GitLab
Note right of Cloud: Create OIDC identity provider
Note right of Cloud: Create role with conditionals
Note left of GitLab: CI/CD job with CI_JOB_JWT_V2
GitLab->>+Cloud: Call cloud API with CI_JOB_JWT_V2
Note left of GitLab: CI/CD job with ID token
GitLab->>+Cloud: Call cloud API with ID token
Note right of Cloud: Decode & verify JWT with public key (https://gitlab/-/jwks)
Note right of Cloud: Validate audience defined in OIDC
Note right of Cloud: Validate conditional (sub, aud) role
@ -115,14 +126,25 @@ sequenceDiagram
1. Create an OIDC identity provider in the cloud (for example, AWS, Azure, GCP, Vault).
1. Create a conditional role in the cloud service that filters to a group, project, branch, or tag.
1. The CI/CD job includes a predefined variable `CI_JOB_JWT_V2` that is a JWT token. You can use this token for authorization with your cloud API.
1. The CI/CD job includes an ID token which is a JWT token. You can use this token for authorization with your cloud API.
1. The cloud verifies the token, validates the conditional role from the payload, and returns a temporary credential.
## Configure a conditional role with OIDC claims
To configure the trust between GitLab and OIDC, you must create a conditional role in the cloud provider that checks against the JWT token. The condition is validated against the JWT to create a trust specifically against two claims, the audience and subject.
To configure the trust between GitLab and OIDC, you must create a conditional role in the cloud provider that checks against the JWT.
The condition is validated against the JWT to create a trust specifically against two claims, the audience and subject.
- Audience or `aud`: Configured as part of the ID token:
```yaml
job_needing_oidc_auth:
id_tokens:
OIDC_TOKEN:
aud: https://oidc.provider.com
script:
- echo $OIDC_TOKEN
```
- Audience or `aud`: The URL of the GitLab instance. This is defined when the identity provider is first configured in your cloud provider.
- Subject or `sub`: A concatenation of metadata describing the GitLab CI/CD workflow including the group, project, branch, and tag. The `sub` field is in the following format:
- `project_path:{group}/{project}:ref_type:{type}:ref:{branch_name}`

View File

@ -33,7 +33,7 @@ On self-managed GitLab instances:
- Administrators can [assign more CI/CD minutes](#set-the-quota-of-cicd-minutes-for-a-specific-namespace)
if a namespace uses all the CI/CD minutes in its monthly quota.
[Specific runners](../runners/runners_scope.md#specific-runners) are not subject to a quota of CI/CD minutes.
[Project runners](../runners/runners_scope.md#project-runners) are not subject to a quota of CI/CD minutes.
## Set the quota of CI/CD minutes for all namespaces
@ -216,9 +216,9 @@ The cost factors on self-managed instances are:
#### Cost factor for community contributions to GitLab projects
Community contributors can use up to 300,000 minutes on shared runners when contributing to open source projects
Community contributors can use up to 300,000 minutes on shared runners when contributing to open source projects
maintained by GitLab. The maximum of 300,000 minutes would only be possible if contributing exclusively to projects [part of the GitLab product](https://about.gitlab.com/handbook/engineering/metrics/#projects-that-are-part-of-the-product). The total number of minutes available on shared runners
is reduced by the CI/CD minutes used by pipelines from other projects.
is reduced by the CI/CD minutes used by pipelines from other projects.
The 300,000 minutes applies to all SaaS tiers, and the cost factor calculation is:
- `Monthly minute quota / 300,000 job duration minutes = Cost factor`

View File

@ -473,6 +473,7 @@ a few different methods, based on where the variable is created or defined.
### Pass YAML-defined CI/CD variables
You can use the `variables` keyword to pass CI/CD variables to a downstream pipeline.
These variables are "trigger variables" for [variable precedence](../variables/index.md#cicd-variable-precedence).
For example:

View File

@ -28,7 +28,7 @@ On GitLab.com, you cannot override the job timeout for shared runners and must u
To set the maximum job timeout:
1. In a project, go to **Settings > CI/CD > Runners**.
1. Select your specific runner to edit the settings.
1. Select your project runner to edit the settings.
1. Enter a value under **Maximum job timeout**. Must be 10 minutes or more. If not
defined, the [project's job timeout setting](../pipelines/settings.md#set-a-limit-for-how-long-jobs-can-run)
is used.
@ -89,7 +89,7 @@ To protect or unprotect a runner:
1. Check the **Protected** option.
1. Select **Save changes**.
![specific runners edit icon](img/protected_runners_check_box_v14_1.png)
![Protect project runners checkbox](img/protected_runners_check_box_v14_1.png)
### Forks
@ -150,7 +150,7 @@ the source of the HTTP requests it makes to GitLab when polling for jobs. The
IP address is always kept up to date so if the runner IP changes it
automatically updates in GitLab.
The IP address for shared runners and specific runners can be found in
The IP address for shared runners and project runners can be found in
different places.
### Determine the IP address of a shared runner
@ -164,16 +164,16 @@ the GitLab instance. To determine this:
![shared runner IP address](img/shared_runner_ip_address_14_5.png)
### Determine the IP address of a specific runner
### Determine the IP address of a project runner
To can find the IP address of a runner for a specific project,
To can find the IP address of a runner for a project project,
you must have the Owner role for the
project.
1. Go to the project's **Settings > CI/CD** and expand the **Runners** section.
1. On the details page you should see a row for **IP Address**.
![specific runner IP address](img/specific_runner_ip_address.png)
![Project runner IP address](img/project_runner_ip_address.png)
## Use tags to control which jobs a runner can run

View File

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -11,8 +11,8 @@ Runners are available based on who you want to have access:
- [Shared runners](#shared-runners) are available to all groups and projects in a GitLab instance.
- [Group runners](#group-runners) are available to all projects and subgroups in a group.
- [Specific runners](#specific-runners) are associated with specific projects.
Typically, specific runners are used for one project at a time.
- [Project runners](#project-runners) are associated with specific projects.
Typically, project runners are used by one project at a time.
## Shared runners
@ -241,78 +241,78 @@ You must have the Owner role for the group.
You must remove it from each project first.
1. On the confirmation dialog, select **OK**.
## Specific runners
## Project runners
Use _specific runners_ when you want to use runners for specific projects. For example,
Use _project runners_ when you want to use runners for specific projects. For example,
when you have:
- Jobs with specific requirements, like a deploy job that requires credentials.
- Projects with a lot of CI activity that can benefit from being separate from other runners.
You can set up a specific runner to be used by multiple projects. Specific runners
You can set up a project runner to be used by multiple projects. Project runners
must be enabled for each project explicitly.
Specific runners process jobs by using a first in, first out ([FIFO](https://en.wikipedia.org/wiki/FIFO_(computing_and_electronics))) queue.
Project runners process jobs by using a first in, first out ([FIFO](https://en.wikipedia.org/wiki/FIFO_(computing_and_electronics))) queue.
NOTE:
Specific runners do not get shared with forked projects automatically.
Project runners do not get shared with forked projects automatically.
A fork *does* copy the CI/CD settings of the cloned repository.
### Create a specific runner
### Create a project runner
You can create a specific runner for your self-managed GitLab instance or for GitLab.com.
You can create a project runner for your self-managed GitLab instance or for GitLab.com.
Prerequisite:
- You must have at least the Maintainer role for the project.
To create a specific runner:
To create a project runner:
1. [Install GitLab Runner](https://docs.gitlab.com/runner/install/).
1. On the top bar, select **Main menu > Projects** and find the project where you want to use the runner.
1. On the left sidebar, select **Settings > CI/CD**.
1. Expand **Runners**.
1. In the **Specific runners** section, note the URL and token.
1. In the **Project runners** section, note the URL and token.
1. [Register the runner](https://docs.gitlab.com/runner/register/).
The runner is now enabled for the project.
### Enable a specific runner for a different project
### Enable a project runner for a different project
After a specific runner is created, you can enable it for other projects.
After a project runner is created, you can enable it for other projects.
Prerequisites:
You must have at least the Maintainer role for:
- The project where the runner is already enabled.
- The project where you want to enable the runner.
- The specific runner must not be [locked](#prevent-a-specific-runner-from-being-enabled-for-other-projects).
- The project runner must not be [locked](#prevent-a-project-runner-from-being-enabled-for-other-projects).
To enable a specific runner for a project:
To enable a project runner for a project:
1. On the top bar, select **Main menu > Projects** and find the project where you want to enable the runner.
1. On the left sidebar, select **Settings > CI/CD**.
1. Expand **Runners**.
1. In the **Specific runners** area, by the runner you want, select **Enable for this project**.
1. In the **Project runners** area, by the runner you want, select **Enable for this project**.
You can edit a specific runner from any of the projects it's enabled for.
You can edit a project runner from any of the projects it's enabled for.
The modifications, which include unlocking and editing tags and the description,
affect all projects that use the runner.
An administrator can [enable the runner for multiple projects](../../user/admin_area/settings/continuous_integration.md#enable-a-specific-runner-for-multiple-projects).
An administrator can [enable the runner for multiple projects](../../user/admin_area/settings/continuous_integration.md#enable-a-project-runner-for-multiple-projects).
### Prevent a specific runner from being enabled for other projects
### Prevent a project runner from being enabled for other projects
You can configure a specific runner so it is "locked" and cannot be enabled for other projects.
You can configure a project runner so it is "locked" and cannot be enabled for other projects.
This setting can be enabled when you first [register a runner](https://docs.gitlab.com/runner/register/),
but can also be changed later.
To lock or unlock a specific runner:
To lock or unlock a project runner:
1. On the top bar, select **Main menu > Projects** and find the project where you want to enable the runner.
1. On the left sidebar, select **Settings > CI/CD**.
1. Expand **Runners**.
1. Find the specific runner you want to lock or unlock. Make sure it's enabled. You cannot lock shared or group runners.
1. Find the project runner you want to lock or unlock. Make sure it's enabled. You cannot lock shared or group runners.
1. Select **Edit** (**{pencil}**).
1. Select the **Lock to current projects** checkbox.
1. Select **Save changes**.

View File

@ -0,0 +1,103 @@
---
stage: Verify
group: Pipeline Authoring
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
type: tutorial
---
# OpenID Connect (OIDC) Authentication Using ID Tokens **(FREE)**
You can authenticate with third party services using GitLab CI/CD's
[ID tokens](../yaml/index.md#id_tokens).
## ID Tokens
[ID tokens](../yaml/index.md#id_tokens) are JSON Web Tokens (JWTs) that can be added to a GitLab CI/CD job. They can be used for OIDC
authentication with third-party services, and are used by the [`secrets`](../yaml/index.md#secrets) keyword to authenticate with HashiCorp Vault.
ID tokens are configured in the `.gitlab-ci.yml`. For example:
```yaml
job_with_id_tokens:
id_tokens:
FIRST_ID_TOKEN:
aud: https://first.service.com
SECOND_ID_TOKEN:
aud: https://second.service.com
script:
- first-service-authentication-script.sh $FIRST_ID_TOKEN
- second-service-authentication-script.sh $SECOND_ID_TOKEN
```
In this example, the two tokens have different `aud` claims. Third party services can be configured to reject tokens
that do not have an `aud` claim matching their bound audience. Use this functionality to reduce the number of
services with which a token can authenticate. This reduces the severity of having a token compromised.
## Manual ID Token authentication
You can use ID tokens for OIDC authentication with a third party service. For example:
```yaml
manual_authentication:
variables:
VAULT_ADDR: http://vault.example.com:8200
image: vault:latest
id_tokens:
VAULT_ID_TOKEN:
aud: http://vault.example.com:8200
script:
- export VAULT_TOKEN="$(vault write -field=token auth/jwt/login role=myproject-example jwt=$VAULT_ID_TOKEN)"
- export PASSWORD="$(vault kv get -field=password secret/myproject/example/db)"
- my-authentication-script.sh $VAULT_TOKEN $PASSWORD
```
## Automatic ID Token authentication with HashiCorp Vault **(PREMIUM)**
You can use ID tokens to automatically fetch secrets from HashiCorp Vault with the
[`secrets`](../yaml/index.md#secrets) keyword.
### Enable automatic ID token authentication
To enable automatic ID token authentication:
1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > CI/CD**.
1. Expand **Token Access**.
1. Toggle **Limit JSON Web Token (JWT) access** to enabled.
### Configure automatic ID Token authentication
If one ID token is defined, the `secrets` keyword automatically uses it to authenticate with Vault. For example:
```yaml
job_with_secrets:
id_tokens:
VAULT_ID_TOKEN:
aud: https://example.vault.com
secrets:
PROD_DB_PASSWORD:
vault: example/db/password # authenticates using $VAULT_ID_TOKEN
script:
- access-prod-db.sh --token $PROD_DB_PASSWORD
```
If more than one ID token is defined, use the `token` keyword to specify which token should be used. For example:
```yaml
job_with_secrets:
id_tokens:
FIRST_ID_TOKEN:
aud: https://first.service.com
SECOND_ID_TOKEN:
aud: https://second.service.com
secrets:
FIRST_DB_PASSWORD:
vault: first/db/password
token: $FIRST_ID_TOKEN
SECOND_DB_PASSWORD:
vault: second/db/password
token: $SECOND_ID_TOKEN
script:
- access-first-db.sh --token $FIRST_DB_PASSWORD
- access-second-db.sh --token $SECOND_DB_PASSWORD
```

View File

@ -23,10 +23,16 @@ GitLab has selected [Vault by HashiCorp](https://www.vaultproject.io) as the
first supported provider, and [KV-V2](https://developer.hashicorp.com/vault/docs/secrets/kv/kv-v2)
as the first supported secrets engine.
GitLab authenticates using Vault's
By default, GitLab authenticates using Vault's
[JSON Web Token (JWT) authentication method](https://developer.hashicorp.com/vault/docs/auth/jwt#jwt-authentication), using
the [JSON Web Token](https://gitlab.com/gitlab-org/gitlab/-/issues/207125) (`CI_JOB_JWT`)
introduced in GitLab 12.10.
the [JSON Web Token](https://gitlab.com/gitlab-org/gitlab/-/issues/207125) (`CI_JOB_JWT`).
[ID tokens](../yaml/index.md#id_tokens) is the preferred secure way to authenticate with Vault,
because ID tokens are defined per-job. GitLab can also authenticate with Vault by using the `CI_JOB_JWT`,
but that token is provided to every job, which can be a security risk.
The [Authenticating and Reading Secrets With HashiCorp Vault](../examples/authenticating-with-hashicorp-vault/index.md)
tutorial has more details about authenticating with ID tokens.
You must [configure your Vault server](#configure-your-vault-server) before you
can use [use Vault secrets in a CI job](#use-vault-secrets-in-a-ci-job).
@ -106,9 +112,14 @@ After [configuring your Vault server](#configure-your-vault-server), you can use
the secrets stored in Vault by defining them with the `vault` keyword:
```yaml
secrets:
DATABASE_PASSWORD:
vault: production/db/password@ops # translates to secret `ops/data/production/db`, field `password`
job_using_vault:
id_tokens:
VAULT_ID_TOKEN:
aud: https://gitlab.com
secrets:
DATABASE_PASSWORD:
vault: production/db/password@ops # translates to secret `ops/data/production/db`, field `password`
token: $VAULT_ID_TOKEN
```
In this example:
@ -125,9 +136,13 @@ To overwrite the default behavior, set the `file` option explicitly:
```yaml
secrets:
id_tokens:
VAULT_ID_TOKEN:
aud: https://gitlab.com
DATABASE_PASSWORD:
vault: production/db/password@ops
file: false
token: $VAULT_ID_TOKEN
```
In this example, the secret value is put directly in the `DATABASE_PASSWORD` variable
@ -177,8 +192,8 @@ Always restrict your roles to a project or namespace by using one of the provide
claims like `project_id` or `namespace_id`. Without these restrictions, any JWT
generated by this GitLab instance may be allowed to authenticate using this role.
For a full list of `CI_JOB_JWT` claims, read the
[How it works](../examples/authenticating-with-hashicorp-vault/index.md#how-it-works) section of the
For a full list of ID token JWT claims, read the
[How It Works](../examples/authenticating-with-hashicorp-vault/index.md#how-it-works) section of the
[Authenticating and Reading Secrets With HashiCorp Vault](../examples/authenticating-with-hashicorp-vault/index.md) tutorial.
You can also specify some attributes for the resulting Vault tokens, such as time-to-live,
@ -188,7 +203,7 @@ for the JSON web token method.
## Using a self-signed Vault server
When the Vault server is using a self-signed certificate, you will see the following error in the job logs:
When the Vault server is using a self-signed certificate, you see the following error in the job logs:
```plaintext
ERROR: Job failed (system failure): resolving secrets: initializing Vault service: preparing authenticated client: checking Vault server health: Get https://vault.example.com:8000/v1/sys/health?drsecondarycode=299&performancestandbycode=299&sealedcode=299&standbycode=299&uninitcode=299: x509: certificate signed by unknown authority
@ -197,7 +212,7 @@ ERROR: Job failed (system failure): resolving secrets: initializing Vault servic
You have two options to solve this error:
- Add the self-signed certificate to the GitLab Runner server's CA store.
If you deployed GitLab Runner using the [Helm chart](https://docs.gitlab.com/runner/install/kubernetes.html), you will have to create your own GitLab Runner image.
If you deployed GitLab Runner using the [Helm chart](https://docs.gitlab.com/runner/install/kubernetes.html), you have to create your own GitLab Runner image.
- Use the `VAULT_CACERT` environment variable to configure GitLab Runner to trust the certificate:
- If you are using systemd to manage GitLab Runner, see [how to add an environment variable for GitLab Runner](https://docs.gitlab.com/runner/configuration/init.html#setting-custom-environment-variables).
- If you deployed GitLab Runner using the [Helm chart](https://docs.gitlab.com/runner/install/kubernetes.html):

View File

@ -3794,6 +3794,43 @@ job:
- The `file` keyword is a setting for the CI/CD variable and must be nested under
the CI/CD variable name, not in the `vault` section.
#### `secrets:token`
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/356986) in GitLab 15.8.
Use `secrets:token` to explicitly select a token to use when authenticating with Vault by referencing the token's CI/CD variable.
This keyword has no effect if [**Limit JSON Web Token (JWT) access**](../secrets/id_token_authentication.md#enable-automatic-id-token-authentication)
is disabled.
**Keyword type**: Job keyword. You can use it only as part of a job.
**Possible inputs**:
- The name of an ID token
**Example of `secrets:token`**:
```yaml
job:
id_tokens:
AWS_TOKEN:
aud: https://aws.example.com
VAULT_TOKEN:
aud: https://vault.example.com
secrets:
DB_PASSWORD:
vault: gitlab/production/db
token: $VAULT_TOKEN
```
**Additional details**:
- When the `token` keyword is not set and **Limit JSON Web Token (JWT) access** enabled, the first ID token
is used to authenticate.
- When **Limit JSON Web Token (JWT) access** is disabled, the `token` keyword is ignored and the `CI_JOB_JWT`
CI/CD variable is used to authenticate.
### `services`
Use `services` to specify any additional Docker images that your scripts require to run successfully. The [`services` image](../services/index.md) is linked

View File

@ -231,15 +231,7 @@ end
### Error budget attribution and ownership
This SLI is used for service level monitoring. It feeds into the
[error budget for stage groups](../stage_group_observability/index.md#error-budget). For this
particular SLI, we have opted everyone out by default to give time to
set the correct urgencies on endpoints before it affects a group's
error budget.
To include this SLI in the error budget, remove the `rails_requests`
from the `ignored_components` array in the entry for your group. Read
more about what is configurable in the
[runbooks documentation](https://gitlab.com/gitlab-com/runbooks/-/tree/master/services#teamsyml).
[error budget for stage groups](../stage_group_observability/index.md#error-budget).
For more information, read the epic for
[defining custom SLIs and incorporating them into error budgets](https://gitlab.com/groups/gitlab-com/gl-infra/-/epics/525)).

View File

@ -69,7 +69,7 @@ looks for the next jobs to be transitioned towards completion. While doing that,
updates the status of jobs, stages and the overall pipeline.
On the right side of the diagram we have a list of [runners](../../ci/runners/index.md)
connected to the GitLab instance. These can be shared runners, group runners, or project-specific runners.
connected to the GitLab instance. These can be shared runners, group runners, or project runners.
The communication between runners and the Rails server occurs through a set of API endpoints, grouped as
the `Runner API Gateway`.
@ -131,7 +131,7 @@ After the runner is [registered](https://docs.gitlab.com/runner/register/) using
- The type of runner it is registered as:
- a shared runner
- a group runner
- a project specific runner
- a project runner
- Any associated tags.
The runner initiates the communication by requesting jobs to execute with `POST /api/v4/jobs/request`. Although polling happens every few seconds, we leverage caching through HTTP headers to reduce the server-side work load if the job queue doesn't change.

View File

@ -320,7 +320,7 @@ You can use these fake tokens as examples:
| Application ID | `2fcb195768c39e9a94cec2c2e32c59c0aad7a3365c10892e8116b5d83d4096b6` |
| Application secret | `04f294d1eaca42b8692017b426d53bbc8fe75f827734f0260710b83a556082df` |
| CI/CD variable | `Li8j-mLUVA3eZYjPfd_H` |
| Specific runner token | `yrnZW46BrtBFqM7xDzE7dddd` |
| Project runner token | `yrnZW46BrtBFqM7xDzE7dddd` |
| Shared runner token | `6Vk7ZsosqQyfreAxXTZr` |
| Trigger token | `be20d8dcc028677c931e04f3871a9b` |
| Webhook secret token | `6XhDroRcYPM5by_h-HLY` |

View File

@ -176,7 +176,7 @@ Or the feature category can be specified in the action itself:
```ruby
module API
class Users < ::API::Base
get ':id', feature_category: :users do
get ':id', feature_category: :user_profile do
end
end
end

View File

@ -66,7 +66,6 @@ memory than it costs to have Workhorse look after it.
- Workhorse does not connect to PostgreSQL, only to Rails and (optionally) Redis.
- We assume that all requests that reach Workhorse pass through an
upstream proxy such as NGINX or Apache first.
- Workhorse does not accept HTTPS connections.
- Workhorse does not clean up idle client connections.
- We assume that all requests to Rails pass through Workhorse.

View File

@ -139,7 +139,7 @@ To make full use of Auto DevOps with Kubernetes, you need:
[Docker Machine](https://docs.gitlab.com/runner/executors/docker_machine.html).
Runners should be registered as [shared runners](../../ci/runners/runners_scope.md#shared-runners)
for the entire GitLab instance, or [specific runners](../../ci/runners/runners_scope.md#specific-runners)
for the entire GitLab instance, or [project runners](../../ci/runners/runners_scope.md#project-runners)
that are assigned to specific projects.
- **Prometheus** (for [Auto Monitoring](stages.md#auto-monitoring))

View File

@ -44,12 +44,12 @@ Any time a new project is created, the shared runners are available.
As an administrator you can set either a global or namespace-specific
limit on the number of [CI/CD minutes](../../../ci/pipelines/cicd_minutes.md) you can use.
## Enable a specific runner for multiple projects
## Enable a project runner for multiple projects
If you have already registered a [specific runner](../../../ci/runners/runners_scope.md#specific-runners)
If you have already registered a [project runner](../../../ci/runners/runners_scope.md#project-runners)
you can assign that runner to other projects.
To enable a specific runner for more than one project:
To enable a project runner for more than one project:
1. On the top bar, select **Main menu > Admin**.
1. From the left sidebar, select **CI/CD > Runners**.

View File

@ -8,7 +8,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
> Introduced in GitLab 11.0.
This page describes SAML for groups. For instance-wide SAML on self-managed GitLab instances, see [SAML OmniAuth Provider](../../../integration/saml.md).
This page describes SAML for groups. For instance-wide SAML on self-managed GitLab instances, see [SAML SSO for self-managed GitLab instances](../../../integration/saml.md).
[View the differences between SaaS and Self-Managed Authentication and Authorization Options](../../../administration/auth/index.md#saas-vs-self-managed-comparison).
SAML on GitLab.com allows users to sign in through their SAML identity provider. If the user is not already a member, the sign-in process automatically adds the user to the appropriate group.

View File

@ -222,8 +222,8 @@ The following table lists project permissions available for each role:
1. On self-managed GitLab instances, guest users are able to perform this action only on
public and internal projects (not on private projects). [External users](admin_area/external_users.md)
must be given explicit access even if the project is internal. Users with the Guest role on GitLab.com are
only able to perform this action on public projects because internal visibility is not available.
must be given explicit access even if the project is internal. Users with the Guest role on GitLab.com are
only able to perform this action on public projects because internal visibility is not available.
2. Guest users can only view the [confidential issues](project/issues/confidential_issues.md) they created themselves or are assigned to.
3. Not allowed for Guest, Reporter, Developer, Maintainer, or Owner. See [protected branches](project/protected_branches.md).
4. If the [branch is protected](project/protected_branches.md), this depends on the access given to Developers and Maintainers.
@ -287,7 +287,7 @@ More details about the permissions for some project-level features follow.
| View a job with [debug logging](../ci/variables/index.md#enable-debug-logging) | | | | ✓ | ✓ | ✓ |
| Use pipeline editor | | | | ✓ | ✓ | ✓ |
| Run [interactive web terminals](../ci/interactive_web_terminal/index.md) | | | | ✓ | ✓ | ✓ |
| Add specific runners to project | | | | | ✓ | ✓ |
| Add project runners to project | | | | | ✓ | ✓ |
| Clear runner caches manually | | | | | ✓ | ✓ |
| Enable shared runners in project | | | | | ✓ | ✓ |
| Manage CI/CD settings | | | | | ✓ | ✓ |

View File

@ -117,17 +117,27 @@ Hi, please message @frank :incoming_envelope:
## Actions that mark a to-do item as done
Any action to an issue, merge request, or epic marks its
Various actions on the to-do item object (like issue, merge request, or epic) mark its
corresponding to-do item as done.
Actions that dismiss to-do items include:
To-do items are marked as done if you:
- Changing the assignee
- Changing the milestone
- Closing the issue or merge request
- Adding or removing a label
- Commenting on the issue
- Resolving a [design discussion thread](project/issues/design_management.md#resolve-a-discussion-thread-on-a-design)
- Add an award emoji to the description or comment.
- Add or remove a label.
- Change the assignee.
- Change the milestone.
- Close the to-do item's object.
- Create a comment.
- Edit the description.
- Resolve a [design discussion thread](project/issues/design_management.md#resolve-a-discussion-thread-on-a-design).
To-do items are **not** marked as done if you:
- Add a linked item (like a [linked issue](project/issues/related_issues.md)).
- Add a child item (like [child epic](group/epics/manage_epics.md#multi-level-child-epics) or [task](tasks.md)).
- Add a [time entry](project/time_tracking.md).
- Assign yourself.
- Change the [health status](project/issues/managing_issues.md#health-status).
If someone else closes, merges, or takes action on an issue, merge request, or
epic, your to-do item remains pending.

View File

@ -13,7 +13,7 @@ module API
helpers do
params :deprecated_filter_params do
optional :scope, type: String, values: ::Ci::Runner::AVAILABLE_SCOPES,
desc: 'Deprecated: Use `type` or `status` instead. The scope of specific runners to return'
desc: 'Deprecated: Use `type` or `status` instead. The scope of runners to return'
end
params :filter_params do
@ -111,9 +111,9 @@ module API
present paginate(runners), with: Entities::Ci::Runner
end
desc 'Get all runners - shared and specific' do
desc 'Get all runners - shared and project' do
summary 'List all runners'
detail 'Get a list of all runners in the GitLab instance (specific and shared). ' \
detail 'Get a list of all runners in the GitLab instance (shared and project). ' \
'Access is restricted to users with administrator access.'
success Entities::Ci::Runner
failure [[400, 'Scope contains invalid value'], [401, 'Unauthorized']]
@ -286,7 +286,7 @@ module API
end
desc 'Enable a runner in project' do
detail "Enable an available specific runner in the project."
detail "Enable an available project runner in the project."
success Entities::Ci::Runner
failure [[400, 'Bad Request'],
[403, 'No access granted'], [403, 'Runner is a group runner'], [403, 'Runner is locked'],
@ -308,7 +308,7 @@ module API
end
desc "Disable project's runner" do
summary "Disable a specific runner from the project"
summary "Disable a project runner from the project"
detail "It works only if the project isn't the only project associated with the specified runner. " \
"If so, an error is returned. Use the call to delete a runner instead."
success Entities::Ci::Runner

View File

@ -9,7 +9,11 @@ module API
before { authenticate_non_get! }
feature_category :projects, ['/projects/:id/custom_attributes', '/projects/:id/custom_attributes/:key']
feature_category :projects, %w[
/projects/:id/custom_attributes
/projects/:id/custom_attributes/:key
/projects/:id/share_locations
]
PROJECT_ATTACHMENT_SIZE_EXEMPT = 1.gigabyte
@ -351,6 +355,20 @@ module API
render_validation_error!(project)
end
end
desc 'Returns group that can be shared with the given project' do
success Entities::Group
end
params do
requires :id, type: Integer, desc: 'The id of the project'
optional :search, type: String, desc: 'Return list of groups matching the search criteria'
end
get ':id/share_locations' do
groups = ::Groups::AcceptingProjectSharesFinder.new(current_user, user_project, declared_params(include_missing: false)).execute
present_groups groups
end
# rubocop: enable CodeReuse/ActiveRecord
end

View File

@ -68,7 +68,7 @@ module Gitlab
def valid_json?(metadata)
Oj.load(metadata)
true
rescue Oj::ParseError, EncodingError, JSON::ParserError, Encoding::UndefinedConversionError
rescue Oj::ParseError, EncodingError, JSON::ParserError, JSON::GeneratorError, Encoding::UndefinedConversionError
false
end

View File

@ -26,7 +26,7 @@ module Gitlab
# Without forcing the encoding to UTF-8 and then replacing
# invalid UTF-8 sequences we can get an error when serializing
# the Hash to JSON.
# Encoding::UndefinedConversionError:
# Encoding::UndefinedConversionError (or possibly JSON::GeneratorError in json 2.6.1+):
# "\xE2" from ASCII-8BIT to UTF-8
{ text: encode_utf8_no_detect(text) }.tap do |result|
result[:style] = style.to_s if style.set?

View File

@ -65,13 +65,15 @@ module Gitlab
status_code = Gitlab::PollingInterval.polling_enabled? ? 304 : 429
add_instrument_for_cache_hit(status_code, route, request)
Gitlab::ApplicationContext.push(
feature_category: route.feature_category,
caller_id: route.caller_id
caller_id: route.caller_id,
remote_ip: request.remote_ip
)
request.env[Gitlab::Metrics::RequestsRackMiddleware::REQUEST_URGENCY_KEY] = route.urgency
add_instrument_for_cache_hit(status_code, route, request)
new_headers = {
'ETag' => etag,
'X-Gitlab-From-Cache' => 'true'
@ -102,7 +104,10 @@ module Gitlab
format: request.format.ref,
method: request.request_method,
path: request.filtered_path,
status: status
status: status,
metadata: Gitlab::ApplicationContext.current,
request_urgency: route.urgency.name,
target_duration_s: route.urgency.duration
}
ActiveSupport::Notifications.instrument(

View File

@ -3,24 +3,34 @@
module Gitlab
module EtagCaching
module Router
Route = Struct.new(:router, :regexp, :name, :feature_category, :caller_id) do
Route = Struct.new(:router, :regexp, :name, :feature_category, :caller_id, :urgency, keyword_init: true) do
delegate :match, to: :regexp
delegate :cache_key, to: :router
end
module Helpers
def build_route(attrs)
EtagCaching::Router::Route.new(self, *attrs)
def build_graphql_route(regexp, name, feature_category)
EtagCaching::Router::Route.new(
router: self,
regexp: regexp,
name: name,
feature_category: feature_category,
# This information can be loaded from the graphql query, but is not
# included yet
# https://gitlab.com/groups/gitlab-com/gl-infra/-/epics/665
caller_id: nil,
urgency: Gitlab::EndpointAttributes::DEFAULT_URGENCY
)
end
def build_rails_route(attrs)
regexp, name, controller, action_name = *attrs
def build_rails_route(regexp, name, controller, action_name)
EtagCaching::Router::Route.new(
self,
regexp,
name,
controller.feature_category_for_action(action_name).to_s,
controller.endpoint_id_for_action(action_name).to_s
router: self,
regexp: regexp,
name: name,
feature_category: controller.feature_category_for_action(action_name).to_s,
caller_id: controller.endpoint_id_for_action(action_name).to_s,
urgency: controller.urgency_for_action(action_name)
)
end
end

View File

@ -23,7 +23,7 @@ module Gitlab
'on_demand_scans',
'dynamic_application_security_testing'
]
].map(&method(:build_route)).freeze
].map { |attrs| build_graphql_route(*attrs) }.freeze
def self.match(request)
return unless request.path_info == graphql_api_path

View File

@ -104,7 +104,7 @@ module Gitlab
::Projects::MergeRequests::ContentController,
:cached_widget
]
].map(&method(:build_rails_route)).freeze
].map { |attrs| build_rails_route(*attrs) }.freeze
# Overridden in EE to add more routes
def self.all_routes

View File

@ -103,7 +103,7 @@ module Gitlab
opts = standardize_opts(opts)
Oj.load(string, opts)
rescue Oj::ParseError, EncodingError, Encoding::UndefinedConversionError => ex
rescue Oj::ParseError, EncodingError, Encoding::UndefinedConversionError, JSON::GeneratorError => ex
raise parser_error, ex
end

View File

@ -27,6 +27,8 @@ module Gitlab
'not_owned', 'source_code_management',
FEATURE_CATEGORY_DEFAULT].freeze
REQUEST_URGENCY_KEY = 'gitlab.request_urgency'
def initialize(app)
@app = app
end
@ -142,7 +144,9 @@ module Gitlab
def urgency_for_env(env)
endpoint_urgency =
if env['api.endpoint'].present?
if env[REQUEST_URGENCY_KEY].present?
env[REQUEST_URGENCY_KEY]
elsif env['api.endpoint'].present?
env['api.endpoint'].options[:for].try(:urgency_for_app, env['api.endpoint'])
elsif env['action_controller.instance'].present? && env['action_controller.instance'].respond_to?(:urgency)
env['action_controller.instance'].urgency

View File

@ -1,32 +0,0 @@
# frozen_string_literal: true
module Gitlab
module Redis
# Pseudo-store to transition `Gitlab::SidekiqMiddleware::DuplicateJobs` from
# using `Sidekiq.redis` to using the `SharedState` redis store.
class DuplicateJobs < ::Gitlab::Redis::Wrapper
class << self
def store_name
'SharedState'
end
private
def redis
primary_store = ::Redis.new(Gitlab::Redis::SharedState.params)
# `Sidekiq.redis` is a namespaced redis connection. This means keys are actually being stored under
# "resque:gitlab:resque:gitlab:duplicate:". For backwards compatibility, we make the secondary store
# namespaced in the same way, but omit it from the primary so keys have proper format there.
# rubocop:disable Cop/RedisQueueUsage
secondary_store = ::Redis::Namespace.new(
Gitlab::Redis::Queues::SIDEKIQ_NAMESPACE, redis: ::Redis.new(Gitlab::Redis::Queues.params)
)
# rubocop:enable Cop/RedisQueueUsage
MultiStore.new(primary_store, secondary_store, name.demodulize)
end
end
end
end
end

View File

@ -120,7 +120,8 @@ module Gitlab
@debian_version_regex ||= %r{
\A(?:
(?:([0-9]{1,9}):)? (?# epoch)
([0-9][0-9a-z\.+~]*-?){1,15} (?# version-revision)
([0-9][0-9a-z\.+~]*) (?# version)
(-[0-9a-z\.+~]+){0,14} (?# -revision)
(?<!-)
)\z}xi.freeze
end

View File

@ -251,16 +251,8 @@ module Gitlab
!scheduled? && options[:if_deduplicated] == :reschedule_once
end
def with_redis
if Feature.enabled?(:use_primary_and_secondary_stores_for_duplicate_jobs) ||
Feature.enabled?(:use_primary_store_as_default_for_duplicate_jobs)
# TODO: Swap for Gitlab::Redis::SharedState after store transition
# https://gitlab.com/gitlab-com/gl-infra/scalability/-/issues/923
Gitlab::Redis::DuplicateJobs.with { |redis| yield redis }
else
# Keep the old behavior intact if neither feature flag is turned on
Sidekiq.redis { |redis| yield redis } # rubocop:disable Cop/SidekiqRedisCall
end
def with_redis(&block)
Sidekiq.redis(&block) # rubocop:disable Cop/SidekiqRedisCall
end
end
end

View File

@ -4713,9 +4713,6 @@ msgstr ""
msgid "Any milestone"
msgstr ""
msgid "Any namespace"
msgstr ""
msgid "App ID"
msgstr ""
@ -5980,9 +5977,6 @@ msgstr ""
msgid "Available on-demand"
msgstr ""
msgid "Available specific runners"
msgstr ""
msgid "Avatar for %{assigneeName}"
msgstr ""
@ -19867,6 +19861,9 @@ msgstr ""
msgid "GroupSelect|Select a group"
msgstr ""
msgid "GroupSettings|Analytics Dashboards"
msgstr ""
msgid "GroupSettings|Applied to all subgroups unless overridden by a group owner. Groups already added to the project lose access."
msgstr ""
@ -19969,6 +19966,12 @@ msgstr ""
msgid "GroupSettings|Select parent group"
msgstr ""
msgid "GroupSettings|Select the project containing Analytics Dashboards configuration files"
msgstr ""
msgid "GroupSettings|Select the project containing Analytics Dashboards configuration files."
msgstr ""
msgid "GroupSettings|Select the project containing the %{code_start}.gitlab/insights.yml%{code_end} file"
msgstr ""
@ -20008,6 +20011,9 @@ msgstr ""
msgid "GroupSettings|What are badges?"
msgstr ""
msgid "GroupSettings|What is Analytics Dashboards?"
msgstr ""
msgid "GroupSettings|What is Insights?"
msgstr ""
@ -22847,6 +22853,9 @@ msgstr ""
msgid "Invalid status"
msgstr ""
msgid "Invalid tags"
msgstr ""
msgid "Invalid two-factor code."
msgstr ""
@ -27142,6 +27151,9 @@ msgstr ""
msgid "MlExperimentsEmptyState|No Experiments to Show"
msgstr ""
msgid "Modal updated"
msgstr ""
msgid "ModalButton|Add projects"
msgstr ""
@ -36272,6 +36284,9 @@ msgstr ""
msgid "Runners|Assigned Projects (%{projectCount})"
msgstr ""
msgid "Runners|Assigned project runners"
msgstr ""
msgid "Runners|Associated with one or more projects"
msgstr ""
@ -36486,6 +36501,9 @@ msgstr ""
msgid "Runners|Project"
msgstr ""
msgid "Runners|Project runners"
msgstr ""
msgid "Runners|Property Name"
msgstr ""
@ -36666,6 +36684,9 @@ msgstr ""
msgid "Runners|The runner will be permanently deleted and no longer available for projects or groups in the instance. Are you sure you want to continue?"
msgstr ""
msgid "Runners|These runners are assigned to this project."
msgstr ""
msgid "Runners|This group currently has 1 stale runner."
msgid_plural "Runners|This group currently has %d stale runners."
msgstr[0] ""
@ -36755,7 +36776,7 @@ msgstr ""
msgid "Runners|Yes, start deleting stale runners"
msgstr ""
msgid "Runners|You can set up a specific runner to be used by multiple projects but you cannot make this a shared or group runner."
msgid "Runners|You can set up a project runner to be used by multiple projects but you cannot make this a shared or group runner."
msgstr ""
msgid "Runners|You have used %{quotaUsed} out of %{quotaLimit} of your shared Runners pipeline minutes."
@ -36770,10 +36791,10 @@ msgstr ""
msgid "Runners|paused"
msgstr ""
msgid "Runners|shared"
msgid "Runners|project"
msgstr ""
msgid "Runners|specific"
msgid "Runners|shared"
msgstr ""
msgid "Runner|Owner"
@ -39544,6 +39565,15 @@ msgstr ""
msgid "SlackIntegration|You may need to reinstall the GitLab for Slack app when we %{linkStart}make updates or change permissions%{linkEnd}."
msgstr ""
msgid "SlackModal|Are you sure you want to change the project?"
msgstr ""
msgid "SlackModal|If you change the project, you'll lose any text entered in the form."
msgstr ""
msgid "SlackModal|If you've entered some text, consider saving it somewhere to avoid losing any content."
msgstr ""
msgid "SlackService|1. %{slash_command_link_start}Add a slash command%{slash_command_link_end} in your Slack team using this information:"
msgstr ""
@ -39859,6 +39889,9 @@ msgstr ""
msgid "Something went wrong while updating assignees"
msgstr ""
msgid "Something went wrong while updating the modal."
msgstr ""
msgid "Something went wrong while updating your list settings"
msgstr ""
@ -40195,9 +40228,6 @@ msgstr ""
msgid "Spam log successfully submitted as ham."
msgstr ""
msgid "Specific runners"
msgstr ""
msgid "Specified URL cannot be used: \"%{reason}\""
msgstr ""
@ -42815,9 +42845,6 @@ msgstr ""
msgid "These runners are shared across projects in this group."
msgstr ""
msgid "These runners are specific to this project."
msgstr ""
msgid "These variables are inherited from the parent group."
msgstr ""

View File

@ -2,7 +2,7 @@
require 'spec_helper'
RSpec.describe RegistrationsController do
RSpec.describe RegistrationsController, feature_category: :users do
include TermsHelper
include FullNameHelper
@ -215,11 +215,18 @@ RSpec.describe RegistrationsController do
property: member.id.to_s,
user: member.reload.user
)
expect_snowplow_event(
category: 'RegistrationsController',
action: 'create_user',
label: 'invited',
user: member.reload.user
)
end
end
context 'when member does not exist from the session key value' do
let(:originating_member_id) { -1 }
let(:originating_member_id) { nil }
it 'does not track invite acceptance' do
subject
@ -229,6 +236,13 @@ RSpec.describe RegistrationsController do
action: 'accepted',
label: 'invite_email'
)
expect_snowplow_event(
category: 'RegistrationsController',
action: 'create_user',
label: 'signup',
user: member.reload.user
)
end
end
end

View File

@ -72,7 +72,7 @@ FactoryBot.define do
transient do
without_package_files { false }
file_metadatum_trait { :keep }
file_metadatum_trait { processing? ? :unknown : :keep }
published_in { :create }
end

View File

@ -651,7 +651,7 @@ RSpec.describe "Admin Runners", feature_category: :runner_fleet do
visit edit_admin_runner_path(runner)
end
it 'removed specific runner from project' do
it 'removed project runner from project' do
within '[data-testid="assigned-projects"]' do
click_on 'Disable'
end

View File

@ -2,7 +2,7 @@
require 'spec_helper'
RSpec.describe 'Contributions Calendar', :js, feature_category: :users do
RSpec.describe 'Contributions Calendar', :js, feature_category: :user_profile do
include MobileHelpers
let(:user) { create(:user) }

View File

@ -2,7 +2,7 @@
require 'spec_helper'
RSpec.describe 'Dashboard > Activity', feature_category: :users do
RSpec.describe 'Dashboard > Activity', feature_category: :user_profile do
let(:user) { create(:user) }
before do

View File

@ -2,7 +2,7 @@
require 'spec_helper'
RSpec.describe 'Tooltips on .timeago dates', :js, feature_category: :users do
RSpec.describe 'Tooltips on .timeago dates', :js, feature_category: :user_profile do
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project, name: 'test', namespace: user.namespace) }

View File

@ -2,7 +2,7 @@
require 'spec_helper'
RSpec.describe 'Project member activity', :js, feature_category: :users do
RSpec.describe 'Project member activity', :js, feature_category: :user_profile do
let(:user) { create(:user) }
let(:project) { create(:project, :public, name: 'x', namespace: user.namespace) }

View File

@ -2,7 +2,7 @@
require 'spec_helper'
RSpec.describe 'Explore Topics', feature_category: :users do
RSpec.describe 'Explore Topics', feature_category: :user_profile do
context 'when no topics exist' do
it 'renders empty message', :aggregate_failures do
visit topics_explore_projects_path

View File

@ -2,7 +2,7 @@
require 'spec_helper'
RSpec.describe 'User explores projects', feature_category: :users do
RSpec.describe 'User explores projects', feature_category: :user_profile do
context 'when some projects exist' do
let_it_be(:archived_project) { create(:project, :archived) }
let_it_be(:internal_project) { create(:project, :internal) }

View File

@ -2,7 +2,7 @@
require 'spec_helper'
RSpec.describe 'Upload a user avatar', :js, feature_category: :users do
RSpec.describe 'Upload a user avatar', :js, feature_category: :user_profile do
let_it_be(:user, reload: true) { create(:user) }
let(:file) { fixture_file_upload('spec/fixtures/banana_sample.gif') }

View File

@ -2,7 +2,7 @@
require 'spec_helper'
RSpec.describe 'User Cluster', :js, feature_category: :users do
RSpec.describe 'User Cluster', :js, feature_category: :user_profile do
include GoogleApi::CloudPlatformHelpers
let(:group) { create(:group) }

View File

@ -2,7 +2,7 @@
require 'spec_helper'
RSpec.describe 'Profile account page', :js, feature_category: :users do
RSpec.describe 'Profile account page', :js, feature_category: :user_profile do
include Spec::Support::Helpers::ModalHelpers
let(:user) { create(:user) }

View File

@ -2,7 +2,7 @@
require 'spec_helper'
RSpec.describe 'Profile > Account', :js, feature_category: :users do
RSpec.describe 'Profile > Account', :js, feature_category: :user_profile do
let(:user) { create(:user, username: 'foo') }
before do

View File

@ -2,7 +2,7 @@
require 'spec_helper'
RSpec.describe 'Profile > Active Sessions', :clean_gitlab_redis_shared_state, feature_category: :users do
RSpec.describe 'Profile > Active Sessions', :clean_gitlab_redis_shared_state, feature_category: :user_profile do
include Spec::Support::Helpers::ModalHelpers
let(:user) do

View File

@ -2,7 +2,7 @@
require 'spec_helper'
RSpec.describe 'Profile > Chat', feature_category: :users do
RSpec.describe 'Profile > Chat', feature_category: :user_profile do
let(:user) { create(:user) }
let(:integration) { create(:integration) }

View File

@ -2,7 +2,7 @@
require 'spec_helper'
RSpec.describe 'Profile > Emails', feature_category: :users do
RSpec.describe 'Profile > Emails', feature_category: :user_profile do
let(:user) { create(:user) }
let(:other_user) { create(:user) }

View File

@ -2,7 +2,7 @@
require 'spec_helper'
RSpec.describe 'Profile > GPG Keys', feature_category: :users do
RSpec.describe 'Profile > GPG Keys', feature_category: :user_profile do
let(:user) { create(:user, email: GpgHelpers::User2.emails.first) }
before do

View File

@ -2,7 +2,7 @@
require 'spec_helper'
RSpec.describe 'Profile > SSH Keys', feature_category: :users do
RSpec.describe 'Profile > SSH Keys', feature_category: :user_profile do
let(:user) { create(:user) }
before do

View File

@ -2,7 +2,7 @@
require 'spec_helper'
RSpec.describe 'Profile > Applications', feature_category: :users do
RSpec.describe 'Profile > Applications', feature_category: :user_profile do
include Spec::Support::Helpers::ModalHelpers
let(:user) { create(:user) }

View File

@ -2,7 +2,7 @@
require 'spec_helper'
RSpec.describe 'Profile > Password', feature_category: :users do
RSpec.describe 'Profile > Password', feature_category: :user_profile do
let(:user) { create(:user) }
def fill_passwords(password, confirmation)

View File

@ -2,7 +2,7 @@
require 'spec_helper'
RSpec.describe 'Profile > Personal Access Tokens', :js, feature_category: :users do
RSpec.describe 'Profile > Personal Access Tokens', :js, feature_category: :user_profile do
include Spec::Support::Helpers::ModalHelpers
include Spec::Support::Helpers::AccessTokenHelpers

View File

@ -2,7 +2,7 @@
require 'spec_helper'
RSpec.describe 'Two factor auths', feature_category: :users do
RSpec.describe 'Two factor auths', feature_category: :user_profile do
include Spec::Support::Helpers::ModalHelpers
context 'when signed in' do

View File

@ -3,7 +3,7 @@
require 'spec_helper'
RSpec.describe 'Profile > Notifications > User changes notified_of_own_activity setting', :js,
feature_category: :users do
feature_category: :user_profile do
let(:user) { create(:user) }
before do

View File

@ -2,7 +2,7 @@
require 'spec_helper'
RSpec.describe 'User edit preferences profile', :js, feature_category: :users do
RSpec.describe 'User edit preferences profile', :js, feature_category: :user_profile do
include StubLanguagesTranslationPercentage
# Empty value doesn't change the levels

View File

@ -2,7 +2,7 @@
require 'spec_helper'
RSpec.describe 'User edit profile', feature_category: :users do
RSpec.describe 'User edit profile', feature_category: :user_profile do
include Spec::Support::Helpers::Features::NotesHelpers
let_it_be(:user) { create(:user) }

View File

@ -2,7 +2,7 @@
require 'spec_helper'
RSpec.describe 'User manages applications', feature_category: :users do
RSpec.describe 'User manages applications', feature_category: :user_profile do
let_it_be(:user) { create(:user) }
let_it_be(:new_application_path) { applications_profile_path }
let_it_be(:index_path) { oauth_applications_path }

Some files were not shown because too many files have changed in this diff Show More