Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2021-09-01 18:08:49 +00:00
parent 9c191c0b94
commit d551c55bb0
73 changed files with 1063 additions and 328 deletions

View file

@ -0,0 +1,56 @@
<!--
Triage of infradev Issues is desired to occur asynchronously.
For maximum efficiency, please ensure the following, so that your infradev issues can gain maximum traction.
https://about.gitlab.com/handbook/engineering/workflow/#a-guide-to-creating-effective-infradev-issues
-->
## Summary
<!--
Clearly state the scope of the problem, and how it affects GitLab.com
-->
## Impact
<!--
- Quantify the effect of the problem to help ensure that correct prioritization occurs.
- Include costs to availability. The Incident Budget Explorer dashboard can help here.
- Include the number of times alerts have fired owing to the problem, how much time was spent dealing with the problem, and how many people were involved.
- Link to affected incidents, and cross-reference them as related issues.
- Include screenshots of visualization from Grafana or Kibana.
- Always include a permalink to the source of the screenshot so that others can investigate further.
-->
## Recommendation
<!--
Provide a clear, unambiguous, self-contained solution to the problem.
-->
## Verification
<!--
Provide a method for validating that the original issue still exists.
Having a way of checking validity can save on a great deal of back-and-forth discussion between Infradev Triage participants including Engineering Managers, Directors and Product Managers and make space for other non-resolved issues to get scheduled sooner.
Ideally, provide a link to a Thanos query or an ELK query and clear instructions on how to interpret the results to determine whether the problem is still occurring.
-->
<!--
Workflow and other relevant labels
/label ~"severity::"
/label ~"priority::"
/label ~"group::"
/label ~"devops::"
See also:
- https://about.gitlab.com/handbook/engineering/quality/issue-triage/#availability
- https://about.gitlab.com/handbook/product/categories/
- https://gitlab.com/gitlab-com/www-gitlab-com/blob/master/data/stages.yml
-->
/label ~"infradev"
/label ~"bug"

View file

@ -23,28 +23,6 @@ Graphql/Descriptions:
- 'ee/app/graphql/types/vulnerability_severity_enum.rb'
- 'ee/app/graphql/types/vulnerability_state_enum.rb'
- 'ee/app/graphql/types/vulnerability_confidence_enum.rb'
- 'ee/app/graphql/mutations/app_sec/fuzzing/api/ci_configuration/create.rb'
- 'ee/app/graphql/mutations/boards/epic_boards/create.rb'
- 'ee/app/graphql/mutations/boards/epic_boards/epic_move_list.rb'
- 'ee/app/graphql/mutations/boards/epic_boards/update.rb'
- 'ee/app/graphql/mutations/boards/epic_lists/destroy.rb'
- 'ee/app/graphql/mutations/boards/lists/update_limit_metrics.rb'
- 'ee/app/graphql/mutations/boards/update_epic_user_preferences.rb'
- 'ee/app/graphql/mutations/compliance_management/frameworks/create.rb'
- 'ee/app/graphql/mutations/compliance_management/frameworks/destroy.rb'
- 'ee/app/graphql/mutations/compliance_management/frameworks/update.rb'
- 'ee/app/graphql/mutations/concerns/mutations/shared_epic_arguments.rb'
- 'ee/app/graphql/mutations/dast/profiles/create.rb'
- 'ee/app/graphql/mutations/dast/profiles/update.rb'
- 'ee/app/graphql/mutations/dast_on_demand_scans/create.rb'
- 'ee/app/graphql/mutations/dast_scanner_profiles/create.rb'
- 'ee/app/graphql/mutations/dast_scanner_profiles/update.rb'
- 'ee/app/graphql/mutations/dast_site_profiles/create.rb'
- 'ee/app/graphql/mutations/dast_site_profiles/delete.rb'
- 'ee/app/graphql/mutations/dast_site_profiles/update.rb'
- 'ee/app/graphql/mutations/dast_site_tokens/create.rb'
- 'ee/app/graphql/mutations/dast_site_validations/create.rb'
- 'ee/app/graphql/mutations/dast_site_validations/revoke.rb'
- 'ee/app/graphql/mutations/epic_tree/reorder.rb'
- 'ee/app/graphql/mutations/epics/add_issue.rb'
- 'ee/app/graphql/mutations/epics/base.rb'

View file

@ -0,0 +1,51 @@
import { Node, mergeAttributes } from '@tiptap/core';
export default Node.create({
name: 'descriptionItem',
content: 'block+',
defining: true,
addAttributes() {
return {
isTerm: {
default: true,
parseHTML: (element) => ({
isTerm: element.tagName.toLowerCase() === 'dt',
}),
},
};
},
parseHTML() {
return [{ tag: 'dt' }, { tag: 'dd' }];
},
renderHTML({ HTMLAttributes: { isTerm, ...HTMLAttributes } }) {
return [
'li',
mergeAttributes(HTMLAttributes, { class: isTerm ? 'dl-term' : 'dl-description' }),
0,
];
},
addKeyboardShortcuts() {
return {
Enter: () => {
return this.editor.commands.splitListItem('descriptionItem');
},
Tab: () => {
const { isTerm } = this.editor.getAttributes('descriptionItem');
if (isTerm)
return this.editor.commands.updateAttributes('descriptionItem', { isTerm: !isTerm });
return false;
},
'Shift-Tab': () => {
const { isTerm } = this.editor.getAttributes('descriptionItem');
if (isTerm) return this.editor.commands.liftListItem('descriptionItem');
return this.editor.commands.updateAttributes('descriptionItem', { isTerm: true });
},
};
},
});

View file

@ -0,0 +1,23 @@
import { Node, mergeAttributes } from '@tiptap/core';
import { wrappingInputRule } from 'prosemirror-inputrules';
export const inputRegex = /^\s*(<dl>)$/;
export default Node.create({
name: 'descriptionList',
// eslint-disable-next-line @gitlab/require-i18n-strings
group: 'block list',
content: 'descriptionItem+',
parseHTML() {
return [{ tag: 'dl' }];
},
renderHTML({ HTMLAttributes }) {
return ['ul', mergeAttributes(HTMLAttributes, { class: 'dl-content' }), 0];
},
addInputRules() {
return [wrappingInputRule(inputRegex, this.type)];
},
});

View file

@ -8,6 +8,8 @@ import Bold from '../extensions/bold';
import BulletList from '../extensions/bullet_list';
import Code from '../extensions/code';
import CodeBlockHighlight from '../extensions/code_block_highlight';
import DescriptionItem from '../extensions/description_item';
import DescriptionList from '../extensions/description_list';
import Division from '../extensions/division';
import Document from '../extensions/document';
import Dropcursor from '../extensions/dropcursor';
@ -74,6 +76,8 @@ export const createContentEditor = ({
BulletList,
Code,
CodeBlockHighlight,
DescriptionItem,
DescriptionList,
Document,
Division,
Dropcursor,

View file

@ -9,6 +9,8 @@ import Bold from '../extensions/bold';
import BulletList from '../extensions/bullet_list';
import Code from '../extensions/code';
import CodeBlockHighlight from '../extensions/code_block_highlight';
import DescriptionItem from '../extensions/description_item';
import DescriptionList from '../extensions/description_list';
import Division from '../extensions/division';
import Emoji from '../extensions/emoji';
import Figure from '../extensions/figure';
@ -122,6 +124,12 @@ const defaultSerializerConfig = {
state.closeBlock(node);
},
[Division.name]: renderHTMLNode('div'),
[DescriptionList.name]: renderHTMLNode('dl', true),
[DescriptionItem.name]: (state, node, parent, index) => {
if (index === 1) state.ensureNewLine();
renderHTMLNode(node.attrs.isTerm ? 'dt' : 'dd')(state, node);
if (index === parent.childCount - 1) state.ensureNewLine();
},
[Emoji.name]: (state, node) => {
const { name } = node.attrs;

View file

@ -6,6 +6,11 @@ const defaultAttrs = {
th: { colspan: 1, rowspan: 1, colwidth: null },
};
const ignoreAttrs = {
dd: ['isTerm'],
dt: ['isTerm'],
};
const tableMap = new WeakMap();
// Source taken from
@ -118,7 +123,8 @@ export function openTag(tagName, attrs) {
str += Object.entries(attrs || {})
.map(([key, value]) => {
if (defaultAttrs[tagName]?.[key] === value) return '';
if ((ignoreAttrs[tagName] || []).includes(key) || defaultAttrs[tagName]?.[key] === value)
return '';
return ` ${key}="${htmlEncode(value?.toString())}"`;
})

View file

@ -3,7 +3,6 @@ import { GlLoadingIcon, GlPagination, GlSprintf } from '@gitlab/ui';
import { GlBreakpointInstance as bp } from '@gitlab/ui/dist/utils';
import Mousetrap from 'mousetrap';
import { mapState, mapGetters, mapActions } from 'vuex';
import { DynamicScroller, DynamicScrollerItem } from 'vendor/vue-virtual-scroller';
import api from '~/api';
import {
keysFor,
@ -55,13 +54,18 @@ import CompareVersions from './compare_versions.vue';
import DiffFile from './diff_file.vue';
import HiddenFilesWarning from './hidden_files_warning.vue';
import NoChanges from './no_changes.vue';
import PreRenderer from './pre_renderer.vue';
import TreeList from './tree_list.vue';
import VirtualScrollerScrollSync from './virtual_scroller_scroll_sync';
export default {
name: 'DiffsApp',
components: {
DynamicScroller: () =>
import('vendor/vue-virtual-scroller').then(({ DynamicScroller }) => DynamicScroller),
DynamicScrollerItem: () =>
import('vendor/vue-virtual-scroller').then(({ DynamicScrollerItem }) => DynamicScrollerItem),
PreRenderer: () => import('./pre_renderer.vue').then((PreRenderer) => PreRenderer),
VirtualScrollerScrollSync: () =>
import('./virtual_scroller_scroll_sync').then((VSSSync) => VSSSync),
CompareVersions,
DiffFile,
NoChanges,
@ -73,10 +77,6 @@ export default {
PanelResizer,
GlPagination,
GlSprintf,
DynamicScroller,
DynamicScrollerItem,
PreRenderer,
VirtualScrollerScrollSync,
MrWidgetHowToMergeModal,
},
alerts: {
@ -189,25 +189,25 @@ export default {
treeWidth,
diffFilesLength: 0,
virtualScrollCurrentIndex: -1,
subscribedToVirtualScrollingEvents: false,
};
},
computed: {
...mapState({
isLoading: (state) => state.diffs.isLoading,
isBatchLoading: (state) => state.diffs.isBatchLoading,
diffFiles: (state) => state.diffs.diffFiles,
diffViewType: (state) => state.diffs.diffViewType,
commit: (state) => state.diffs.commit,
renderOverflowWarning: (state) => state.diffs.renderOverflowWarning,
numTotalFiles: (state) => state.diffs.realSize,
numVisibleFiles: (state) => state.diffs.size,
plainDiffPath: (state) => state.diffs.plainDiffPath,
emailPatchPath: (state) => state.diffs.emailPatchPath,
retrievingBatches: (state) => state.diffs.retrievingBatches,
...mapState('diffs', {
numTotalFiles: 'realSize',
numVisibleFiles: 'size',
}),
...mapState('diffs', [
'showTreeList',
'isLoading',
'isBatchLoading',
'diffFiles',
'diffViewType',
'commit',
'renderOverflowWarning',
'plainDiffPath',
'emailPatchPath',
'retrievingBatches',
'startVersion',
'latestDiff',
'currentDiffFileId',
@ -316,6 +316,7 @@ export default {
}
this.adjustView();
this.subscribeToVirtualScrollingEvents();
},
isLoading: 'adjustView',
renderFileTree: 'adjustView',
@ -349,11 +350,6 @@ export default {
this.setHighlightedRow(id.split('diff-content').pop().slice(1));
}
if (window.gon?.features?.diffsVirtualScrolling) {
diffsEventHub.$on('scrollToFileHash', this.scrollVirtualScrollerToFileHash);
diffsEventHub.$on('scrollToIndex', this.scrollVirtualScrollerToIndex);
}
if (window.gon?.features?.diffSettingsUsageData) {
const events = [];
@ -383,6 +379,8 @@ export default {
queueRedisHllEvents(events);
}
this.subscribeToVirtualScrollingEvents();
},
beforeCreate() {
diffsApp.instrument();
@ -611,6 +609,18 @@ export default {
}
}
},
subscribeToVirtualScrollingEvents() {
if (
window.gon?.features?.diffsVirtualScrolling &&
this.shouldShow &&
!this.subscribedToVirtualScrollingEvents
) {
diffsEventHub.$on('scrollToFileHash', this.scrollVirtualScrollerToFileHash);
diffsEventHub.$on('scrollToIndex', this.scrollVirtualScrollerToIndex);
this.subscribedToVirtualScrollingEvents = true;
}
},
},
minTreeWidth: MIN_TREE_WIDTH,
maxTreeWidth: MAX_TREE_WIDTH,
@ -672,7 +682,6 @@ export default {
<template v-else-if="renderDiffFiles">
<dynamic-scroller
v-if="isVirtualScrollingEnabled"
ref="virtualScroller"
:items="diffs"
:min-item-size="70"
:buffer="1000"

View file

@ -24,8 +24,13 @@ export default {
return isScrollable ? scrollableStyles : null;
},
},
userColorScheme: window.gon.user_color_scheme,
};
</script>
<template>
<pre class="code-block rounded" :style="styleObject"><code class="d-block">{{ code }}</code></pre>
<pre
class="code-block rounded code"
:class="$options.userColorScheme"
:style="styleObject"
><code class="d-block">{{ code }}</code></pre>
</template>

View file

@ -1,7 +1,9 @@
.ProseMirror {
td,
th,
li {
li,
dd,
dt {
:first-child {
margin-bottom: 0 !important;
}
@ -34,6 +36,20 @@
}
}
}
.dl-content {
width: 100%;
> li {
list-style-type: none;
margin-left: $gl-spacing-scale-5;
&.dl-term {
margin: 0;
font-weight: 600;
}
}
}
}
.table-creator-grid-item {

View file

@ -337,8 +337,6 @@
}
.code-block {
background: $black;
color: $gray-darkest;
white-space: pre;
overflow-x: auto;
font-size: 12px;

View file

@ -96,15 +96,10 @@
}
form {
width: 48%;
padding: 0;
border: 0;
background: none;
margin-bottom: $gl-padding;
@include media-breakpoint-down(md) {
width: 100%;
}
}
.omniauth-btn {

View file

@ -634,17 +634,11 @@ svg {
margin: 0;
}
.login-page .omniauth-container form {
width: 48%;
padding: 0;
border: 0;
background: none;
margin-bottom: 16px;
}
@media (max-width: 991.98px) {
.login-page .omniauth-container form {
width: 100%;
}
}
.login-page .omniauth-container .omniauth-btn {
width: 100%;
}

View file

@ -311,6 +311,9 @@ module ApplicationSettingsHelper
:throttle_authenticated_api_enabled,
:throttle_authenticated_api_period_in_seconds,
:throttle_authenticated_api_requests_per_period,
:throttle_authenticated_git_lfs_enabled,
:throttle_authenticated_git_lfs_period_in_seconds,
:throttle_authenticated_git_lfs_requests_per_period,
:throttle_authenticated_web_enabled,
:throttle_authenticated_web_period_in_seconds,
:throttle_authenticated_web_requests_per_period,

View file

@ -497,6 +497,14 @@ class ApplicationSetting < ApplicationRecord
presence: true,
numericality: { only_integer: true, greater_than: 0 }
validates :throttle_authenticated_git_lfs_requests_per_period,
presence: true,
numericality: { only_integer: true, greater_than: 0 }
validates :throttle_authenticated_git_lfs_period_in_seconds,
presence: true,
numericality: { only_integer: true, greater_than: 0 }
validates :throttle_authenticated_web_requests_per_period,
presence: true,
numericality: { only_integer: true, greater_than: 0 }

View file

@ -163,6 +163,9 @@ module ApplicationSettingImplementation
throttle_authenticated_api_enabled: false,
throttle_authenticated_api_period_in_seconds: 3600,
throttle_authenticated_api_requests_per_period: 7200,
throttle_authenticated_git_lfs_enabled: false,
throttle_authenticated_git_lfs_period_in_seconds: 60,
throttle_authenticated_git_lfs_requests_per_period: 1000,
throttle_authenticated_web_enabled: false,
throttle_authenticated_web_period_in_seconds: 3600,
throttle_authenticated_web_requests_per_period: 7200,

View file

@ -0,0 +1,21 @@
= form_for @application_setting, url: network_admin_application_settings_path(anchor: 'js-git-lfs-limits-settings'), html: { class: 'fieldset-form' } do |f|
= form_errors(@application_setting)
%fieldset
%h5
= _('Authenticated Git LFS request rate limit')
.form-group
.form-check
= f.check_box :throttle_authenticated_git_lfs_enabled, class: 'form-check-input', data: { qa_selector: 'throttle_authenticated_git_lfs_checkbox' }
= f.label :throttle_authenticated_git_lfs_enabled, class: 'form-check-label gl-font-weight-bold' do
= _('Enable authenticated Git LFS request rate limit')
%span.form-text.gl-text-gray-600
= _('Helps reduce request volume (for example, from crawlers or abusive bots)')
.form-group
= f.label :throttle_authenticated_git_lfs_requests_per_period, _('Max authenticated Git LFS requests per period per user'), class: 'gl-font-weight-bold'
= f.number_field :throttle_authenticated_git_lfs_requests_per_period, class: 'form-control gl-form-input'
.form-group
= f.label :throttle_authenticated_git_lfs_period_in_seconds, _('Authenticated Git LFS rate limit period in seconds'), class: 'gl-font-weight-bold'
= f.number_field :throttle_authenticated_git_lfs_period_in_seconds, class: 'form-control gl-form-input'
= f.submit _('Save changes'), class: "gl-button btn btn-confirm", data: { qa_selector: 'save_changes_button' }

View file

@ -35,6 +35,18 @@
.settings-content
= render 'package_registry_limits'
%section.settings.as-git-lfs-limits.no-animate#js-git-lfs-limits-settings{ class: ('expanded' if expanded_by_default?), data: { qa_selector: 'git_lfs_limits_content' } }
.settings-header
%h4
= _('Git LFS Rate Limits')
%button.btn.gl-button.btn-default.js-settings-toggle{ type: 'button' }
= expanded_by_default? ? _('Collapse') : _('Expand')
%p
= _('Configure specific limits for Git LFS requests that supersede the general user and IP rate limits.')
= link_to _('Learn more.'), help_page_path('user/admin_area/settings/git_lfs_rate_limits.md'), target: '_blank', rel: 'noopener noreferrer'
.settings-content
= render 'git_lfs_limits'
%section.settings.as-outbound.no-animate#js-outbound-settings{ class: ('expanded' if expanded_by_default?), data: { qa_selector: 'outbound_requests_content' } }
.settings-header
%h4

View file

@ -7,10 +7,10 @@
.d-flex.justify-content-between.flex-wrap
- providers.each do |provider|
- has_icon = provider_has_icon?(provider)
= button_to omniauth_authorize_path(:user, provider), id: "oauth-login-#{provider}", class: "btn gl-button btn-default omniauth-btn oauth-login #{qa_class_for_provider(provider)}" do
= button_to omniauth_authorize_path(:user, provider), id: "oauth-login-#{provider}", class: "btn gl-button btn-default omniauth-btn oauth-login #{qa_class_for_provider(provider)}", form: { class: 'gl-w-full' } do
- if has_icon
= provider_image_tag(provider)
%span
%span.gl-button-text
= label_for_provider(provider)
- unless hide_remember_me
%fieldset.remember-me

View file

@ -0,0 +1,23 @@
# frozen_string_literal: true
class AddThrottleAuthenticatedGitLfsColumns < ActiveRecord::Migration[6.1]
include Gitlab::Database::MigrationHelpers
disable_ddl_transaction!
def up
with_lock_retries do
add_column :application_settings, :throttle_authenticated_git_lfs_requests_per_period, :integer, default: 1000, null: false
add_column :application_settings, :throttle_authenticated_git_lfs_period_in_seconds, :integer, default: 60, null: false
add_column :application_settings, :throttle_authenticated_git_lfs_enabled, :boolean, default: false, null: false
end
end
def down
with_lock_retries do
remove_column :application_settings, :throttle_authenticated_git_lfs_requests_per_period
remove_column :application_settings, :throttle_authenticated_git_lfs_period_in_seconds
remove_column :application_settings, :throttle_authenticated_git_lfs_enabled, :boolean
end
end
end

View file

@ -0,0 +1 @@
cf1a51194961500cb63d848afb1d5ebbf2ef77f54d60957e92c9dd6a667913ea

View file

@ -9614,6 +9614,9 @@ CREATE TABLE application_settings (
throttle_authenticated_files_api_period_in_seconds integer DEFAULT 15 NOT NULL,
throttle_unauthenticated_files_api_enabled boolean DEFAULT false NOT NULL,
throttle_authenticated_files_api_enabled boolean DEFAULT false NOT NULL,
throttle_authenticated_git_lfs_requests_per_period integer DEFAULT 1000 NOT NULL,
throttle_authenticated_git_lfs_period_in_seconds integer DEFAULT 60 NOT NULL,
throttle_authenticated_git_lfs_enabled boolean DEFAULT false NOT NULL,
CONSTRAINT app_settings_container_reg_cleanup_tags_max_list_size_positive CHECK ((container_registry_cleanup_tags_service_max_list_size >= 0)),
CONSTRAINT app_settings_ext_pipeline_validation_service_url_text_limit CHECK ((char_length(external_pipeline_validation_service_url) <= 255)),
CONSTRAINT app_settings_registry_exp_policies_worker_capacity_positive CHECK ((container_registry_expiration_policies_worker_capacity >= 0)),

View file

@ -78,6 +78,16 @@ This setting limits the request rate on the Packages API per user or IP. For mor
- **Default rate limit**: Disabled by default.
### Git LFS
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/68642) in GitLab 14.3.
This setting limits the request rate on the [Git LFS](../topics/git/lfs/index.md)
requests per user. For more information, read
[GitLab Git Large File Storage (LFS) Administration](../administration/lfs/index.md).
- **Default rate limit**: Disabled by default.
### Import/Export
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/35728) in GitLab 13.2.

View file

@ -631,7 +631,7 @@ Input type: `ApiFuzzingCiConfigurationCreateInput`
| <a id="mutationapifuzzingciconfigurationcreateauthusername"></a>`authUsername` | [`String`](#string) | CI variable containing the username for authenticating with the target API. |
| <a id="mutationapifuzzingciconfigurationcreateclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationapifuzzingciconfigurationcreateprojectpath"></a>`projectPath` | [`ID!`](#id) | Full path of the project. |
| <a id="mutationapifuzzingciconfigurationcreatescanmode"></a>`scanMode` | [`ApiFuzzingScanMode!`](#apifuzzingscanmode) | The mode for API fuzzing scans. |
| <a id="mutationapifuzzingciconfigurationcreatescanmode"></a>`scanMode` | [`ApiFuzzingScanMode!`](#apifuzzingscanmode) | Mode for API fuzzing scans. |
| <a id="mutationapifuzzingciconfigurationcreatescanprofile"></a>`scanProfile` | [`String`](#string) | Name of a default profile to use for scanning. Ex: Quick-10. |
| <a id="mutationapifuzzingciconfigurationcreatetarget"></a>`target` | [`String!`](#string) | URL for the target of API fuzzing scans. |
@ -642,7 +642,7 @@ Input type: `ApiFuzzingCiConfigurationCreateInput`
| <a id="mutationapifuzzingciconfigurationcreateclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationapifuzzingciconfigurationcreateconfigurationyaml"></a>`configurationYaml` | [`String`](#string) | A YAML snippet that can be inserted into the project's `.gitlab-ci.yml` to set up API fuzzing scans. |
| <a id="mutationapifuzzingciconfigurationcreateerrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
| <a id="mutationapifuzzingciconfigurationcreategitlabciyamleditpath"></a>`gitlabCiYamlEditPath` | [`String`](#string) | The location at which the project's `.gitlab-ci.yml` file can be edited in the browser. |
| <a id="mutationapifuzzingciconfigurationcreategitlabciyamleditpath"></a>`gitlabCiYamlEditPath` | [`String`](#string) | Location at which the project's `.gitlab-ci.yml` file can be edited in the browser. |
### `Mutation.awardEmojiAdd`
@ -760,10 +760,10 @@ Input type: `BoardListUpdateLimitMetricsInput`
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="mutationboardlistupdatelimitmetricsclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationboardlistupdatelimitmetricslimitmetric"></a>`limitMetric` | [`ListLimitMetric`](#listlimitmetric) | The new limit metric type for the list. |
| <a id="mutationboardlistupdatelimitmetricslistid"></a>`listId` | [`ListID!`](#listid) | The global ID of the list. |
| <a id="mutationboardlistupdatelimitmetricsmaxissuecount"></a>`maxIssueCount` | [`Int`](#int) | The new maximum issue count limit. |
| <a id="mutationboardlistupdatelimitmetricsmaxissueweight"></a>`maxIssueWeight` | [`Int`](#int) | The new maximum issue weight limit. |
| <a id="mutationboardlistupdatelimitmetricslimitmetric"></a>`limitMetric` | [`ListLimitMetric`](#listlimitmetric) | New limit metric type for the list. |
| <a id="mutationboardlistupdatelimitmetricslistid"></a>`listId` | [`ListID!`](#listid) | Global ID of the list. |
| <a id="mutationboardlistupdatelimitmetricsmaxissuecount"></a>`maxIssueCount` | [`Int`](#int) | New maximum issue count limit. |
| <a id="mutationboardlistupdatelimitmetricsmaxissueweight"></a>`maxIssueWeight` | [`Int`](#int) | New maximum issue weight limit. |
#### Fields
@ -771,7 +771,7 @@ Input type: `BoardListUpdateLimitMetricsInput`
| ---- | ---- | ----------- |
| <a id="mutationboardlistupdatelimitmetricsclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationboardlistupdatelimitmetricserrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
| <a id="mutationboardlistupdatelimitmetricslist"></a>`list` | [`BoardList`](#boardlist) | The updated list. |
| <a id="mutationboardlistupdatelimitmetricslist"></a>`list` | [`BoardList`](#boardlist) | Updated list. |
### `Mutation.bulkEnableDevopsAdoptionNamespaces`
@ -1149,7 +1149,7 @@ Input type: `CreateComplianceFrameworkInput`
| ---- | ---- | ----------- |
| <a id="mutationcreatecomplianceframeworkclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationcreatecomplianceframeworkerrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
| <a id="mutationcreatecomplianceframeworkframework"></a>`framework` | [`ComplianceFramework`](#complianceframework) | The created compliance framework. |
| <a id="mutationcreatecomplianceframeworkframework"></a>`framework` | [`ComplianceFramework`](#complianceframework) | Created compliance framework. |
### `Mutation.createCustomEmoji`
@ -1204,17 +1204,17 @@ Input type: `CreateEpicInput`
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="mutationcreateepicaddlabelids"></a>`addLabelIds` | [`[ID!]`](#id) | The IDs of labels to be added to the epic. |
| <a id="mutationcreateepicaddlabelids"></a>`addLabelIds` | [`[ID!]`](#id) | IDs of labels to be added to the epic. |
| <a id="mutationcreateepicclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationcreateepicconfidential"></a>`confidential` | [`Boolean`](#boolean) | Indicates if the epic is confidential. |
| <a id="mutationcreateepicdescription"></a>`description` | [`String`](#string) | The description of the epic. |
| <a id="mutationcreateepicduedatefixed"></a>`dueDateFixed` | [`String`](#string) | The end date of the epic. |
| <a id="mutationcreateepicdescription"></a>`description` | [`String`](#string) | Description of the epic. |
| <a id="mutationcreateepicduedatefixed"></a>`dueDateFixed` | [`String`](#string) | End date of the epic. |
| <a id="mutationcreateepicduedateisfixed"></a>`dueDateIsFixed` | [`Boolean`](#boolean) | Indicates end date should be sourced from due_date_fixed field not the issue milestones. |
| <a id="mutationcreateepicgrouppath"></a>`groupPath` | [`ID!`](#id) | The group the epic to mutate is in. |
| <a id="mutationcreateepicremovelabelids"></a>`removeLabelIds` | [`[ID!]`](#id) | The IDs of labels to be removed from the epic. |
| <a id="mutationcreateepicstartdatefixed"></a>`startDateFixed` | [`String`](#string) | The start date of the epic. |
| <a id="mutationcreateepicgrouppath"></a>`groupPath` | [`ID!`](#id) | Group the epic to mutate is in. |
| <a id="mutationcreateepicremovelabelids"></a>`removeLabelIds` | [`[ID!]`](#id) | IDs of labels to be removed from the epic. |
| <a id="mutationcreateepicstartdatefixed"></a>`startDateFixed` | [`String`](#string) | Start date of the epic. |
| <a id="mutationcreateepicstartdateisfixed"></a>`startDateIsFixed` | [`Boolean`](#boolean) | Indicates start date should be sourced from start_date_fixed field not the issue milestones. |
| <a id="mutationcreateepictitle"></a>`title` | [`String`](#string) | The title of the epic. |
| <a id="mutationcreateepictitle"></a>`title` | [`String`](#string) | Title of the epic. |
#### Fields
@ -1417,7 +1417,7 @@ Input type: `DastOnDemandScanCreateInput`
| <a id="mutationdastondemandscancreateclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationdastondemandscancreatedastscannerprofileid"></a>`dastScannerProfileId` | [`DastScannerProfileID`](#dastscannerprofileid) | ID of the scanner profile to be used for the scan. |
| <a id="mutationdastondemandscancreatedastsiteprofileid"></a>`dastSiteProfileId` | [`DastSiteProfileID!`](#dastsiteprofileid) | ID of the site profile to be used for the scan. |
| <a id="mutationdastondemandscancreatefullpath"></a>`fullPath` | [`ID!`](#id) | The project the site profile belongs to. |
| <a id="mutationdastondemandscancreatefullpath"></a>`fullPath` | [`ID!`](#id) | Project the site profile belongs to. |
#### Fields
@ -1435,13 +1435,13 @@ Input type: `DastProfileCreateInput`
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="mutationdastprofilecreatebranchname"></a>`branchName` | [`String`](#string) | The associated branch. |
| <a id="mutationdastprofilecreatebranchname"></a>`branchName` | [`String`](#string) | Associated branch. |
| <a id="mutationdastprofilecreateclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationdastprofilecreatedastscannerprofileid"></a>`dastScannerProfileId` | [`DastScannerProfileID!`](#dastscannerprofileid) | ID of the scanner profile to be associated. |
| <a id="mutationdastprofilecreatedastsiteprofileid"></a>`dastSiteProfileId` | [`DastSiteProfileID!`](#dastsiteprofileid) | ID of the site profile to be associated. |
| <a id="mutationdastprofilecreatedescription"></a>`description` | [`String`](#string) | The description of the profile. Defaults to an empty string. |
| <a id="mutationdastprofilecreatefullpath"></a>`fullPath` | [`ID!`](#id) | The project the profile belongs to. |
| <a id="mutationdastprofilecreatename"></a>`name` | [`String!`](#string) | The name of the profile. |
| <a id="mutationdastprofilecreatedescription"></a>`description` | [`String`](#string) | Description of the profile. Defaults to an empty string. |
| <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. |
#### Fields
@ -1449,9 +1449,9 @@ Input type: `DastProfileCreateInput`
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="mutationdastprofilecreateclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationdastprofilecreatedastprofile"></a>`dastProfile` | [`DastProfile`](#dastprofile) | The created profile. |
| <a id="mutationdastprofilecreatedastprofile"></a>`dastProfile` | [`DastProfile`](#dastprofile) | Created profile. |
| <a id="mutationdastprofilecreateerrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
| <a id="mutationdastprofilecreatepipelineurl"></a>`pipelineUrl` | [`String`](#string) | The URL of the pipeline that was created. Requires `runAfterCreate` to be set to `true`. |
| <a id="mutationdastprofilecreatepipelineurl"></a>`pipelineUrl` | [`String`](#string) | URL of the pipeline that was created. Requires `runAfterCreate` to be set to `true`. |
### `Mutation.dastProfileDelete`
@ -1499,14 +1499,14 @@ Input type: `DastProfileUpdateInput`
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="mutationdastprofileupdatebranchname"></a>`branchName` | [`String`](#string) | The associated branch. |
| <a id="mutationdastprofileupdatebranchname"></a>`branchName` | [`String`](#string) | Associated branch. |
| <a id="mutationdastprofileupdateclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationdastprofileupdatedastscannerprofileid"></a>`dastScannerProfileId` | [`DastScannerProfileID`](#dastscannerprofileid) | ID of the scanner profile to be associated. |
| <a id="mutationdastprofileupdatedastsiteprofileid"></a>`dastSiteProfileId` | [`DastSiteProfileID`](#dastsiteprofileid) | ID of the site profile to be associated. |
| <a id="mutationdastprofileupdatedescription"></a>`description` | [`String`](#string) | The description of the profile. Defaults to an empty string. |
| <a id="mutationdastprofileupdatefullpath"></a>`fullPath` | [`ID!`](#id) | The project the profile belongs to. |
| <a id="mutationdastprofileupdatedescription"></a>`description` | [`String`](#string) | Description of the profile. Defaults to an empty string. |
| <a id="mutationdastprofileupdatefullpath"></a>`fullPath` | [`ID!`](#id) | Project the profile belongs to. |
| <a id="mutationdastprofileupdateid"></a>`id` | [`DastProfileID!`](#dastprofileid) | ID of the profile to be deleted. |
| <a id="mutationdastprofileupdatename"></a>`name` | [`String`](#string) | The name of the profile. |
| <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. |
#### Fields
@ -1514,7 +1514,7 @@ Input type: `DastProfileUpdateInput`
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="mutationdastprofileupdateclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationdastprofileupdatedastprofile"></a>`dastProfile` | [`DastProfile`](#dastprofile) | The updated profile. |
| <a id="mutationdastprofileupdatedastprofile"></a>`dastProfile` | [`DastProfile`](#dastprofile) | Updated profile. |
| <a id="mutationdastprofileupdateerrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
| <a id="mutationdastprofileupdatepipelineurl"></a>`pipelineUrl` | [`String`](#string) | The URL of the pipeline that was created. Requires the input argument `runAfterUpdate` to be set to `true` when calling the mutation, otherwise no pipeline will be created. |
@ -1527,12 +1527,12 @@ Input type: `DastScannerProfileCreateInput`
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="mutationdastscannerprofilecreateclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationdastscannerprofilecreatefullpath"></a>`fullPath` | [`ID!`](#id) | The project the scanner profile belongs to. |
| <a id="mutationdastscannerprofilecreateprofilename"></a>`profileName` | [`String!`](#string) | The name of the scanner profile. |
| <a id="mutationdastscannerprofilecreatefullpath"></a>`fullPath` | [`ID!`](#id) | Project the scanner profile belongs to. |
| <a id="mutationdastscannerprofilecreateprofilename"></a>`profileName` | [`String!`](#string) | Name of the scanner profile. |
| <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) | The maximum number of minutes allowed for the spider to traverse the site. |
| <a id="mutationdastscannerprofilecreatetargettimeout"></a>`targetTimeout` | [`Int`](#int) | The maximum number of seconds allowed for the site under test to respond to a request. |
| <a id="mutationdastscannerprofilecreatespidertimeout"></a>`spiderTimeout` | [`Int`](#int) | Maximum number of minutes allowed for the spider to traverse the site. |
| <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. |
#### Fields
@ -1571,13 +1571,13 @@ Input type: `DastScannerProfileUpdateInput`
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="mutationdastscannerprofileupdateclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationdastscannerprofileupdatefullpath"></a>`fullPath` | [`ID!`](#id) | The project the scanner profile belongs to. |
| <a id="mutationdastscannerprofileupdatefullpath"></a>`fullPath` | [`ID!`](#id) | Project the scanner profile belongs to. |
| <a id="mutationdastscannerprofileupdateid"></a>`id` | [`DastScannerProfileID!`](#dastscannerprofileid) | ID of the scanner profile to be updated. |
| <a id="mutationdastscannerprofileupdateprofilename"></a>`profileName` | [`String!`](#string) | The name of the scanner profile. |
| <a id="mutationdastscannerprofileupdateprofilename"></a>`profileName` | [`String!`](#string) | Name of the scanner profile. |
| <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) | The maximum number of minutes allowed for the spider to traverse the site. |
| <a id="mutationdastscannerprofileupdatetargettimeout"></a>`targetTimeout` | [`Int!`](#int) | The maximum number of seconds allowed for the site under test to respond to a request. |
| <a id="mutationdastscannerprofileupdatespidertimeout"></a>`spiderTimeout` | [`Int!`](#int) | Maximum number of minutes allowed for the spider to traverse the site. |
| <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. |
#### Fields
@ -1598,12 +1598,12 @@ Input type: `DastSiteProfileCreateInput`
| ---- | ---- | ----------- |
| <a id="mutationdastsiteprofilecreateauth"></a>`auth` | [`DastSiteProfileAuthInput`](#dastsiteprofileauthinput) | Parameters for authentication. |
| <a id="mutationdastsiteprofilecreateclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationdastsiteprofilecreateexcludedurls"></a>`excludedUrls` | [`[String!]`](#string) | The URLs to skip during an authenticated scan. Defaults to `[]`. |
| <a id="mutationdastsiteprofilecreatefullpath"></a>`fullPath` | [`ID!`](#id) | The project the site profile belongs to. |
| <a id="mutationdastsiteprofilecreateprofilename"></a>`profileName` | [`String!`](#string) | The name of the site profile. |
| <a id="mutationdastsiteprofilecreateexcludedurls"></a>`excludedUrls` | [`[String!]`](#string) | URLs to skip during an authenticated scan. Defaults to `[]`. |
| <a id="mutationdastsiteprofilecreatefullpath"></a>`fullPath` | [`ID!`](#id) | Project the site profile belongs to. |
| <a id="mutationdastsiteprofilecreateprofilename"></a>`profileName` | [`String!`](#string) | Name of the site profile. |
| <a id="mutationdastsiteprofilecreaterequestheaders"></a>`requestHeaders` | [`String`](#string) | Comma-separated list of request header names and values to be added to every request made by DAST. |
| <a id="mutationdastsiteprofilecreatetargettype"></a>`targetType` | [`DastTargetTypeEnum`](#dasttargettypeenum) | The type of target to be scanned. |
| <a id="mutationdastsiteprofilecreatetargeturl"></a>`targetUrl` | [`String`](#string) | The URL of the target to be scanned. |
| <a id="mutationdastsiteprofilecreatetargettype"></a>`targetType` | [`DastTargetTypeEnum`](#dasttargettypeenum) | Type of target to be scanned. |
| <a id="mutationdastsiteprofilecreatetargeturl"></a>`targetUrl` | [`String`](#string) | URL of the target to be scanned. |
#### Fields
@ -1622,7 +1622,7 @@ Input type: `DastSiteProfileDeleteInput`
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="mutationdastsiteprofiledeleteclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationdastsiteprofiledeletefullpath"></a>`fullPath` | [`ID!`](#id) | The project the site profile belongs to. |
| <a id="mutationdastsiteprofiledeletefullpath"></a>`fullPath` | [`ID!`](#id) | Project the site profile belongs to. |
| <a id="mutationdastsiteprofiledeleteid"></a>`id` | [`DastSiteProfileID!`](#dastsiteprofileid) | ID of the site profile to be deleted. |
#### Fields
@ -1642,13 +1642,13 @@ Input type: `DastSiteProfileUpdateInput`
| ---- | ---- | ----------- |
| <a id="mutationdastsiteprofileupdateauth"></a>`auth` | [`DastSiteProfileAuthInput`](#dastsiteprofileauthinput) | Parameters for authentication. |
| <a id="mutationdastsiteprofileupdateclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationdastsiteprofileupdateexcludedurls"></a>`excludedUrls` | [`[String!]`](#string) | The URLs to skip during an authenticated scan. |
| <a id="mutationdastsiteprofileupdatefullpath"></a>`fullPath` | [`ID!`](#id) | The project the site profile belongs to. |
| <a id="mutationdastsiteprofileupdateexcludedurls"></a>`excludedUrls` | [`[String!]`](#string) | URLs to skip during an authenticated scan. |
| <a id="mutationdastsiteprofileupdatefullpath"></a>`fullPath` | [`ID!`](#id) | Project the site profile belongs to. |
| <a id="mutationdastsiteprofileupdateid"></a>`id` | [`DastSiteProfileID!`](#dastsiteprofileid) | ID of the site profile to be updated. |
| <a id="mutationdastsiteprofileupdateprofilename"></a>`profileName` | [`String!`](#string) | The name of the site profile. |
| <a id="mutationdastsiteprofileupdateprofilename"></a>`profileName` | [`String!`](#string) | Name of the site profile. |
| <a id="mutationdastsiteprofileupdaterequestheaders"></a>`requestHeaders` | [`String`](#string) | Comma-separated list of request header names and values to be added to every request made by DAST. |
| <a id="mutationdastsiteprofileupdatetargettype"></a>`targetType` | [`DastTargetTypeEnum`](#dasttargettypeenum) | The type of target to be scanned. |
| <a id="mutationdastsiteprofileupdatetargeturl"></a>`targetUrl` | [`String`](#string) | The URL of the target to be scanned. |
| <a id="mutationdastsiteprofileupdatetargettype"></a>`targetType` | [`DastTargetTypeEnum`](#dasttargettypeenum) | Type of target to be scanned. |
| <a id="mutationdastsiteprofileupdatetargeturl"></a>`targetUrl` | [`String`](#string) | URL of the target to be scanned. |
#### Fields
@ -1667,8 +1667,8 @@ Input type: `DastSiteTokenCreateInput`
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="mutationdastsitetokencreateclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationdastsitetokencreatefullpath"></a>`fullPath` | [`ID!`](#id) | The project the site token belongs to. |
| <a id="mutationdastsitetokencreatetargeturl"></a>`targetUrl` | [`String`](#string) | The URL of the target to be validated. |
| <a id="mutationdastsitetokencreatefullpath"></a>`fullPath` | [`ID!`](#id) | Project the site token belongs to. |
| <a id="mutationdastsitetokencreatetargeturl"></a>`targetUrl` | [`String`](#string) | URL of the target to be validated. |
#### Fields
@ -1677,7 +1677,7 @@ Input type: `DastSiteTokenCreateInput`
| <a id="mutationdastsitetokencreateclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationdastsitetokencreateerrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
| <a id="mutationdastsitetokencreateid"></a>`id` | [`DastSiteTokenID`](#dastsitetokenid) | ID of the site token. |
| <a id="mutationdastsitetokencreatestatus"></a>`status` | [`DastSiteProfileValidationStatusEnum`](#dastsiteprofilevalidationstatusenum) | The current validation status of the target. |
| <a id="mutationdastsitetokencreatestatus"></a>`status` | [`DastSiteProfileValidationStatusEnum`](#dastsiteprofilevalidationstatusenum) | Current validation status of the target. |
| <a id="mutationdastsitetokencreatetoken"></a>`token` | [`String`](#string) | Token string. |
### `Mutation.dastSiteValidationCreate`
@ -1690,9 +1690,9 @@ Input type: `DastSiteValidationCreateInput`
| ---- | ---- | ----------- |
| <a id="mutationdastsitevalidationcreateclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationdastsitevalidationcreatedastsitetokenid"></a>`dastSiteTokenId` | [`DastSiteTokenID!`](#dastsitetokenid) | ID of the site token. |
| <a id="mutationdastsitevalidationcreatefullpath"></a>`fullPath` | [`ID!`](#id) | The project the site profile belongs to. |
| <a id="mutationdastsitevalidationcreatestrategy"></a>`strategy` | [`DastSiteValidationStrategyEnum`](#dastsitevalidationstrategyenum) | The validation strategy to be used. |
| <a id="mutationdastsitevalidationcreatevalidationpath"></a>`validationPath` | [`String!`](#string) | The path to be requested during validation. |
| <a id="mutationdastsitevalidationcreatefullpath"></a>`fullPath` | [`ID!`](#id) | Project the site profile belongs to. |
| <a id="mutationdastsitevalidationcreatestrategy"></a>`strategy` | [`DastSiteValidationStrategyEnum`](#dastsitevalidationstrategyenum) | Validation strategy to be used. |
| <a id="mutationdastsitevalidationcreatevalidationpath"></a>`validationPath` | [`String!`](#string) | Path to be requested during validation. |
#### Fields
@ -1701,7 +1701,7 @@ Input type: `DastSiteValidationCreateInput`
| <a id="mutationdastsitevalidationcreateclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationdastsitevalidationcreateerrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
| <a id="mutationdastsitevalidationcreateid"></a>`id` | [`DastSiteValidationID`](#dastsitevalidationid) | ID of the site validation. |
| <a id="mutationdastsitevalidationcreatestatus"></a>`status` | [`DastSiteProfileValidationStatusEnum`](#dastsiteprofilevalidationstatusenum) | The current validation status. |
| <a id="mutationdastsitevalidationcreatestatus"></a>`status` | [`DastSiteProfileValidationStatusEnum`](#dastsiteprofilevalidationstatusenum) | Current validation status. |
### `Mutation.dastSiteValidationRevoke`
@ -1712,7 +1712,7 @@ Input type: `DastSiteValidationRevokeInput`
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="mutationdastsitevalidationrevokeclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationdastsitevalidationrevokefullpath"></a>`fullPath` | [`ID!`](#id) | The project the site validation belongs to. |
| <a id="mutationdastsitevalidationrevokefullpath"></a>`fullPath` | [`ID!`](#id) | Project the site validation belongs to. |
| <a id="mutationdastsitevalidationrevokenormalizedtargeturl"></a>`normalizedTargetUrl` | [`String!`](#string) | Normalized URL of the target to be revoked. |
#### Fields
@ -1851,7 +1851,7 @@ Input type: `DestroyComplianceFrameworkInput`
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="mutationdestroycomplianceframeworkclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationdestroycomplianceframeworkid"></a>`id` | [`ComplianceManagementFrameworkID!`](#compliancemanagementframeworkid) | The global ID of the compliance framework to destroy. |
| <a id="mutationdestroycomplianceframeworkid"></a>`id` | [`ComplianceManagementFrameworkID!`](#compliancemanagementframeworkid) | Global ID of the compliance framework to destroy. |
#### Fields
@ -2144,7 +2144,7 @@ Input type: `EpicBoardCreateInput`
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="mutationepicboardcreateclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationepicboardcreateepicboard"></a>`epicBoard` | [`EpicBoard`](#epicboard) | The created epic board. |
| <a id="mutationepicboardcreateepicboard"></a>`epicBoard` | [`EpicBoard`](#epicboard) | Created epic board. |
| <a id="mutationepicboardcreateerrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
### `Mutation.epicBoardListCreate`
@ -2187,7 +2187,7 @@ Input type: `EpicBoardListDestroyInput`
| ---- | ---- | ----------- |
| <a id="mutationepicboardlistdestroyclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationepicboardlistdestroyerrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
| <a id="mutationepicboardlistdestroylist"></a>`list` | [`EpicList`](#epiclist) | The epic board list. `null` if the board was destroyed successfully. |
| <a id="mutationepicboardlistdestroylist"></a>`list` | [`EpicList`](#epiclist) | Epic board list. `null` if the board was destroyed successfully. |
### `Mutation.epicBoardUpdate`
@ -2200,7 +2200,7 @@ Input type: `EpicBoardUpdateInput`
| <a id="mutationepicboardupdateclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationepicboardupdatehidebackloglist"></a>`hideBacklogList` | [`Boolean`](#boolean) | Whether or not backlog list is hidden. |
| <a id="mutationepicboardupdatehideclosedlist"></a>`hideClosedList` | [`Boolean`](#boolean) | Whether or not closed list is hidden. |
| <a id="mutationepicboardupdateid"></a>`id` | [`BoardsEpicBoardID!`](#boardsepicboardid) | The epic board global ID. |
| <a id="mutationepicboardupdateid"></a>`id` | [`BoardsEpicBoardID!`](#boardsepicboardid) | Epic board global ID. |
| <a id="mutationepicboardupdatelabelids"></a>`labelIds` | [`[LabelID!]`](#labelid) | IDs of labels to be added to the board. |
| <a id="mutationepicboardupdatelabels"></a>`labels` | [`[String!]`](#string) | Labels of the issue. |
| <a id="mutationepicboardupdatename"></a>`name` | [`String`](#string) | Board name. |
@ -2210,7 +2210,7 @@ Input type: `EpicBoardUpdateInput`
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="mutationepicboardupdateclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationepicboardupdateepicboard"></a>`epicBoard` | [`EpicBoard`](#epicboard) | The updated epic board. |
| <a id="mutationepicboardupdateepicboard"></a>`epicBoard` | [`EpicBoard`](#epicboard) | Updated epic board. |
| <a id="mutationepicboardupdateerrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
### `Mutation.epicMoveList`
@ -2234,7 +2234,7 @@ Input type: `EpicMoveListInput`
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="mutationepicmovelistclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationepicmovelistepic"></a>`epic` | [`Epic`](#epic) | The epic after mutation. |
| <a id="mutationepicmovelistepic"></a>`epic` | [`Epic`](#epic) | Epic after mutation. |
| <a id="mutationepicmovelisterrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
### `Mutation.epicSetSubscription`
@ -4099,7 +4099,7 @@ Input type: `UpdateBoardEpicUserPreferencesInput`
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="mutationupdateboardepicuserpreferencesboardid"></a>`boardId` | [`BoardID!`](#boardid) | The board global ID. |
| <a id="mutationupdateboardepicuserpreferencesboardid"></a>`boardId` | [`BoardID!`](#boardid) | Board global ID. |
| <a id="mutationupdateboardepicuserpreferencesclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationupdateboardepicuserpreferencescollapsed"></a>`collapsed` | [`Boolean!`](#boolean) | Whether the epic should be collapsed in the board. |
| <a id="mutationupdateboardepicuserpreferencesepicid"></a>`epicId` | [`EpicID!`](#epicid) | ID of an epic to set preferences for. |
@ -4142,7 +4142,7 @@ Input type: `UpdateComplianceFrameworkInput`
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="mutationupdatecomplianceframeworkclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationupdatecomplianceframeworkid"></a>`id` | [`ComplianceManagementFrameworkID!`](#compliancemanagementframeworkid) | The global ID of the compliance framework to update. |
| <a id="mutationupdatecomplianceframeworkid"></a>`id` | [`ComplianceManagementFrameworkID!`](#compliancemanagementframeworkid) | Global ID of the compliance framework to update. |
| <a id="mutationupdatecomplianceframeworkparams"></a>`params` | [`ComplianceFrameworkInput!`](#complianceframeworkinput) | Parameters to update the compliance framework with. |
#### Fields
@ -4150,7 +4150,7 @@ Input type: `UpdateComplianceFrameworkInput`
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="mutationupdatecomplianceframeworkclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationupdatecomplianceframeworkcomplianceframework"></a>`complianceFramework` | [`ComplianceFramework`](#complianceframework) | The compliance framework after mutation. |
| <a id="mutationupdatecomplianceframeworkcomplianceframework"></a>`complianceFramework` | [`ComplianceFramework`](#complianceframework) | Compliance framework after mutation. |
| <a id="mutationupdatecomplianceframeworkerrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
### `Mutation.updateContainerExpirationPolicy`
@ -4186,19 +4186,19 @@ Input type: `UpdateEpicInput`
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="mutationupdateepicaddlabelids"></a>`addLabelIds` | [`[ID!]`](#id) | The IDs of labels to be added to the epic. |
| <a id="mutationupdateepicaddlabelids"></a>`addLabelIds` | [`[ID!]`](#id) | IDs of labels to be added to the epic. |
| <a id="mutationupdateepicclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationupdateepicconfidential"></a>`confidential` | [`Boolean`](#boolean) | Indicates if the epic is confidential. |
| <a id="mutationupdateepicdescription"></a>`description` | [`String`](#string) | The description of the epic. |
| <a id="mutationupdateepicduedatefixed"></a>`dueDateFixed` | [`String`](#string) | The end date of the epic. |
| <a id="mutationupdateepicdescription"></a>`description` | [`String`](#string) | Description of the epic. |
| <a id="mutationupdateepicduedatefixed"></a>`dueDateFixed` | [`String`](#string) | End date of the epic. |
| <a id="mutationupdateepicduedateisfixed"></a>`dueDateIsFixed` | [`Boolean`](#boolean) | Indicates end date should be sourced from due_date_fixed field not the issue milestones. |
| <a id="mutationupdateepicgrouppath"></a>`groupPath` | [`ID!`](#id) | The group the epic to mutate is in. |
| <a id="mutationupdateepicgrouppath"></a>`groupPath` | [`ID!`](#id) | Group the epic to mutate is in. |
| <a id="mutationupdateepiciid"></a>`iid` | [`ID!`](#id) | The IID of the epic to mutate. |
| <a id="mutationupdateepicremovelabelids"></a>`removeLabelIds` | [`[ID!]`](#id) | The IDs of labels to be removed from the epic. |
| <a id="mutationupdateepicstartdatefixed"></a>`startDateFixed` | [`String`](#string) | The start date of the epic. |
| <a id="mutationupdateepicremovelabelids"></a>`removeLabelIds` | [`[ID!]`](#id) | IDs of labels to be removed from the epic. |
| <a id="mutationupdateepicstartdatefixed"></a>`startDateFixed` | [`String`](#string) | Start date of the epic. |
| <a id="mutationupdateepicstartdateisfixed"></a>`startDateIsFixed` | [`Boolean`](#boolean) | Indicates start date should be sourced from start_date_fixed field not the issue milestones. |
| <a id="mutationupdateepicstateevent"></a>`stateEvent` | [`EpicStateEvent`](#epicstateevent) | State event for the epic. |
| <a id="mutationupdateepictitle"></a>`title` | [`String`](#string) | The title of the epic. |
| <a id="mutationupdateepictitle"></a>`title` | [`String`](#string) | Title of the epic. |
#### Fields

View file

@ -552,7 +552,7 @@ Example response:
"state": "active",
"avatar_url": "https://www.gravatar.com/avatar/c2525a7f58ae3776070e44c106c48e15?s=80&d=identicon",
"web_url": "http://192.168.1.8:3000/root",
"expires_at": "2012-10-22T14:13:35Z",
"expires_at": "2012-10-22",
"access_level": 40,
"email": "john@example.com",
"override": false

View file

@ -77,12 +77,12 @@ The single entrypoint for the registry is the [HTTP API](https://gitlab.com/gitl
| Operation | UI | Background | Observations |
| ------------------------------------------------------------ | ------------------ | ------------------------ | ------------------------------------------------------------ |
| [Check API version](https://gitlab.com/gitlab-org/container-registry/-/blob/master/docs/spec/api.md#api-version-check) | :heavy_check_mark: | :heavy_check_mark: | Used globally to ensure that the registry supports the Docker Distribution V2 API, as well as for identifying whether GitLab Rails is talking to the GitLab Container Registry or a third-party one (used to toggle features only available in the former). |
| [List repository tags](https://gitlab.com/gitlab-org/container-registry/-/blob/master/docs/spec/api.md#listing-image-tags) | :heavy_check_mark: | :heavy_check_mark: | Used to list and show tags in the UI. Used to list tags in the background for [cleanup policies](../../../user/packages/container_registry/#cleanup-policy) and [Geo replication](../../../administration/geo/replication/docker_registry.md). |
| [Check if manifest exists](https://gitlab.com/gitlab-org/container-registry/-/blob/master/docs/spec/api.md#existing-manifests) | :heavy_check_mark: | :heavy_multiplication_x: | Used to get the digest of a manifest by tag. This is then used to pull the manifest and show the tag details in the UI. |
| [Pull manifest](https://gitlab.com/gitlab-org/container-registry/-/blob/master/docs/spec/api.md#pulling-an-image-manifest) | :heavy_check_mark: | :heavy_multiplication_x: | Used to show the image size and the manifest digest in the tag details UI. |
| [Pull blob](https://gitlab.com/gitlab-org/container-registry/-/blob/master/docs/spec/api.md#pulling-a-layer) | :heavy_check_mark: | :heavy_multiplication_x: | Used to show the configuration digest and the creation date in the tag details UI. |
| [Delete tag](https://gitlab.com/gitlab-org/container-registry/-/blob/master/docs/spec/api.md#deleting-a-tag) | :heavy_check_mark: | :heavy_check_mark: | Used to delete a tag from the UI and in background (cleanup policies). |
| [Check API version](https://gitlab.com/gitlab-org/container-registry/-/blob/master/docs/spec/api.md#api-version-check) | **{check-circle}** Yes | **{check-circle}** Yes | Used globally to ensure that the registry supports the Docker Distribution V2 API, as well as for identifying whether GitLab Rails is talking to the GitLab Container Registry or a third-party one (used to toggle features only available in the former). |
| [List repository tags](https://gitlab.com/gitlab-org/container-registry/-/blob/master/docs/spec/api.md#listing-image-tags) | **{check-circle}** Yes | **{check-circle}** Yes | Used to list and show tags in the UI. Used to list tags in the background for [cleanup policies](../../../user/packages/container_registry/#cleanup-policy) and [Geo replication](../../../administration/geo/replication/docker_registry.md). |
| [Check if manifest exists](https://gitlab.com/gitlab-org/container-registry/-/blob/master/docs/spec/api.md#existing-manifests) | **{check-circle}** Yes | **{dotted-circle}** No | Used to get the digest of a manifest by tag. This is then used to pull the manifest and show the tag details in the UI. |
| [Pull manifest](https://gitlab.com/gitlab-org/container-registry/-/blob/master/docs/spec/api.md#pulling-an-image-manifest) | **{check-circle}** Yes | **{dotted-circle}** No | Used to show the image size and the manifest digest in the tag details UI. |
| [Pull blob](https://gitlab.com/gitlab-org/container-registry/-/blob/master/docs/spec/api.md#pulling-a-layer) | **{check-circle}** Yes | **{dotted-circle}** No | Used to show the configuration digest and the creation date in the tag details UI. |
| [Delete tag](https://gitlab.com/gitlab-org/container-registry/-/blob/master/docs/spec/api.md#deleting-a-tag) | **{check-circle}** Yes | **{check-circle}** Yes | Used to delete a tag from the UI and in background (cleanup policies). |
A valid authentication token is generated in GitLab Rails and embedded in all these requests before sending them to the registry.
@ -211,22 +211,22 @@ This is a list of all the registry HTTP API operations and how they depend on th
| Operation | Method | Path | Requires Database | Requires Storage | Used by GitLab Rails * |
|--------------------------------------------------------------------------------------------------------------------------------|----------|-----------------------------------------|-------------------|------------------|------------------------|
| [Check API version](https://gitlab.com/gitlab-org/container-registry/-/blob/master/docs/spec/api.md#api-version-check) | `GET` | `/v2/` | ✖ | ✖ | ✔ |
| [List repositories](https://gitlab.com/gitlab-org/container-registry/-/blob/master/docs/spec/api.md#listing-repositories) | `GET` | `/v2/_catalog` | ✔ | ✖ | ✖ |
| [List repository tags](https://gitlab.com/gitlab-org/container-registry/-/blob/master/docs/spec/api.md#listing-image-tags) | `GET` | `/v2/<name>/tags/list` | ✔ | ✖ | ✔ |
| [Delete tag](https://gitlab.com/gitlab-org/container-registry/-/blob/master/docs/spec/api.md#deleting-a-tag) | `DELETE` | `/v2/<name>/tags/reference/<reference>` | ✔ | ✖ | ✔ |
| [Check if manifest exists](https://gitlab.com/gitlab-org/container-registry/-/blob/master/docs/spec/api.md#existing-manifests) | `HEAD` | `/v2/<name>/manifests/<reference>` | ✔ | ✖ | ✔ |
| [Pull manifest](https://gitlab.com/gitlab-org/container-registry/-/blob/master/docs/spec/api.md#pulling-an-image-manifest) | `GET` | `/v2/<name>/manifests/<reference>` | ✔ | ✖ | ✔ |
| [Push manifest](https://gitlab.com/gitlab-org/container-registry/-/blob/master/docs/spec/api.md#pushing-an-image-manifest) | `PUT` | `/v2/<name>/manifests/<reference>` | ✔ | ✖ | ✖ |
| [Delete manifest](https://gitlab.com/gitlab-org/container-registry/-/blob/master/docs/spec/api.md#deleting-an-image) | `DELETE` | `/v2/<name>/manifests/<reference>` | ✔ | ✖ | ✖ |
| [Check if blob exists](https://gitlab.com/gitlab-org/container-registry/-/blob/master/docs/spec/api.md#existing-layers) | `HEAD` | `/v2/<name>/blobs/<digest>` | ✔ | ✖ | ✖ |
| [Pull blob](https://gitlab.com/gitlab-org/container-registry/-/blob/master/docs/spec/api.md#fetch-blob) | `GET` | `/v2/<name>/blobs/<digest>` | ✔ | ✔ | ✔ |
| [Delete blob](https://gitlab.com/gitlab-org/container-registry/-/blob/master/docs/spec/api.md#delete-blob) | `DELETE` | `/v2/<name>/blobs/<digest>` | ✔ | ✖ | ✖ |
| [Start blob upload](https://gitlab.com/gitlab-org/container-registry/-/blob/master/docs/spec/api.md#starting-an-upload) | `POST` | `/v2/<name>/blobs/uploads/` | ✔ | ✔ | ✖ |
| [Check blob upload status](https://gitlab.com/gitlab-org/container-registry/-/blob/master/docs/spec/api.md#get-blob-upload) | `GET` | `/v2/<name>/blobs/uploads/<uuid>` | ✔ | ✔ | ✖ |
| [Push blob chunk](https://gitlab.com/gitlab-org/container-registry/-/blob/master/docs/spec/api.md#chunked-upload-1) | `PATCH` | `/v2/<name>/blobs/uploads/<uuid>` | ✔ | ✔ | ✖ |
| [Complete blob upload](https://gitlab.com/gitlab-org/container-registry/-/blob/master/docs/spec/api.md#put-blob-upload) | `PUT` | `/v2/<name>/blobs/uploads/<uuid>` | ✔ | ✔ | ✖ |
| [Cancel blob upload](https://gitlab.com/gitlab-org/container-registry/-/blob/master/docs/spec/api.md#canceling-an-upload) | `DELETE` | `/v2/<name>/blobs/uploads/<uuid>` | ✔ | ✔ | ✖ |
| [Check API version](https://gitlab.com/gitlab-org/container-registry/-/blob/master/docs/spec/api.md#api-version-check) | `GET` | `/v2/` | **{dotted-circle}** No | **{dotted-circle}** No | **{check-circle}** Yes |
| [List repositories](https://gitlab.com/gitlab-org/container-registry/-/blob/master/docs/spec/api.md#listing-repositories) | `GET` | `/v2/_catalog` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No |
| [List repository tags](https://gitlab.com/gitlab-org/container-registry/-/blob/master/docs/spec/api.md#listing-image-tags) | `GET` | `/v2/<name>/tags/list` | **{check-circle}** Yes | **{dotted-circle}** No | **{check-circle}** Yes |
| [Delete tag](https://gitlab.com/gitlab-org/container-registry/-/blob/master/docs/spec/api.md#deleting-a-tag) | `DELETE` | `/v2/<name>/tags/reference/<reference>` | **{check-circle}** Yes | **{dotted-circle}** No | **{check-circle}** Yes |
| [Check if manifest exists](https://gitlab.com/gitlab-org/container-registry/-/blob/master/docs/spec/api.md#existing-manifests) | `HEAD` | `/v2/<name>/manifests/<reference>` | **{check-circle}** Yes | **{dotted-circle}** No | **{check-circle}** Yes |
| [Pull manifest](https://gitlab.com/gitlab-org/container-registry/-/blob/master/docs/spec/api.md#pulling-an-image-manifest) | `GET` | `/v2/<name>/manifests/<reference>` | **{check-circle}** Yes | **{dotted-circle}** No | **{check-circle}** Yes |
| [Push manifest](https://gitlab.com/gitlab-org/container-registry/-/blob/master/docs/spec/api.md#pushing-an-image-manifest) | `PUT` | `/v2/<name>/manifests/<reference>` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No |
| [Delete manifest](https://gitlab.com/gitlab-org/container-registry/-/blob/master/docs/spec/api.md#deleting-an-image) | `DELETE` | `/v2/<name>/manifests/<reference>` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No |
| [Check if blob exists](https://gitlab.com/gitlab-org/container-registry/-/blob/master/docs/spec/api.md#existing-layers) | `HEAD` | `/v2/<name>/blobs/<digest>` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No |
| [Pull blob](https://gitlab.com/gitlab-org/container-registry/-/blob/master/docs/spec/api.md#fetch-blob) | `GET` | `/v2/<name>/blobs/<digest>` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes |
| [Delete blob](https://gitlab.com/gitlab-org/container-registry/-/blob/master/docs/spec/api.md#delete-blob) | `DELETE` | `/v2/<name>/blobs/<digest>` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No |
| [Start blob upload](https://gitlab.com/gitlab-org/container-registry/-/blob/master/docs/spec/api.md#starting-an-upload) | `POST` | `/v2/<name>/blobs/uploads/` | **{check-circle}** Yes | **{check-circle}** Yes | **{dotted-circle}** No |
| [Check blob upload status](https://gitlab.com/gitlab-org/container-registry/-/blob/master/docs/spec/api.md#get-blob-upload) | `GET` | `/v2/<name>/blobs/uploads/<uuid>` | **{check-circle}** Yes | **{check-circle}** Yes | **{dotted-circle}** No |
| [Push blob chunk](https://gitlab.com/gitlab-org/container-registry/-/blob/master/docs/spec/api.md#chunked-upload-1) | `PATCH` | `/v2/<name>/blobs/uploads/<uuid>` | **{check-circle}** Yes | **{check-circle}** Yes | **{dotted-circle}** No |
| [Complete blob upload](https://gitlab.com/gitlab-org/container-registry/-/blob/master/docs/spec/api.md#put-blob-upload) | `PUT` | `/v2/<name>/blobs/uploads/<uuid>` | **{check-circle}** Yes | **{check-circle}** Yes | **{dotted-circle}** No |
| [Cancel blob upload](https://gitlab.com/gitlab-org/container-registry/-/blob/master/docs/spec/api.md#canceling-an-upload) | `DELETE` | `/v2/<name>/blobs/uploads/<uuid>` | **{check-circle}** Yes | **{check-circle}** Yes | **{dotted-circle}** No |
`*` Please refer to the [list of interactions between registry and Rails](#from-gitlab-rails-to-registry) to know why and how.

View file

@ -38,7 +38,7 @@ read the [Introduction to CI/CD with GitLab](introduction/index.md).
<iframe src="https://www.youtube.com/embed/1iXFbchozdY" frameborder="0" allowfullscreen="true"> </iframe>
</figure>
## Concepts
## GitLab CI/CD concepts
GitLab CI/CD uses a number of concepts to describe and run your build and deploy.
@ -53,7 +53,7 @@ GitLab CI/CD uses a number of concepts to describe and run your build and deploy
| [Pipeline efficiency](pipelines/pipeline_efficiency.md) | Configure your pipelines to run quickly and efficiently. |
| [Test cases](test_cases/index.md) | Create testing scenarios. |
## Configuration
## GitLab CI/CD configuration
GitLab CI/CD supports numerous configuration options:
@ -72,7 +72,7 @@ GitLab CI/CD supports numerous configuration options:
Certain operations can only be performed according to the
[user](../user/permissions.md#gitlab-cicd-permissions) and [job](../user/permissions.md#job-permissions) permissions.
## Features
## GitLab CI/CD features
GitLab CI/CD features, grouped by DevOps stage, include:
@ -109,7 +109,7 @@ GitLab CI/CD features, grouped by DevOps stage, include:
| [License Compliance](../user/compliance/license_compliance/index.md) **(ULTIMATE)** | Search your project dependencies for their licenses. |
| [Security Test reports](../user/application_security/index.md) **(ULTIMATE)** | Check for app vulnerabilities. |
## Examples
## GitLab CI/CD examples
See the [CI/CD examples](examples/README.md) page for example project code and tutorials for
using GitLab CI/CD with various:
@ -118,7 +118,7 @@ using GitLab CI/CD with various:
- Languages
- Platforms
## Administration
## GitLab CI/CD Administration
You can change the default behavior of GitLab CI/CD for:

View file

@ -199,7 +199,7 @@ GitLab takes advantage of our connected ecosystem to automatically pull these ki
your Merge Requests, pipeline details pages, and other locations. You may find that you actually don't
need to configure anything to have these appear.
If they aren't working as expected, or if you'd like to see what's available, our [CI feature index](../index.md#features) has the full list
If they aren't working as expected, or if you'd like to see what's available, our [CI feature index](../index.md#gitlab-cicd-features) has the full list
of bundled features and links to the documentation for each.
### Templates

View file

@ -40,9 +40,7 @@ It's recommended to create two separate migration script files.
desired limit using `create_or_update_plan_limit` migration helper, such as:
```ruby
class InsertProjectHooksPlanLimits < ActiveRecord::Migration[5.2]
include Gitlab::Database::MigrationHelpers
class InsertProjectHooksPlanLimits < Gitlab::Database::Migration[1.0]
def up
create_or_update_plan_limit('project_hooks', 'default', 0)
create_or_update_plan_limit('project_hooks', 'free', 10)

View file

@ -95,9 +95,7 @@ renaming. For example
```ruby
# A regular migration in db/migrate
class RenameUsersUpdatedAtToUpdatedAtTimestamp < ActiveRecord::Migration[4.2]
include Gitlab::Database::MigrationHelpers
class RenameUsersUpdatedAtToUpdatedAtTimestamp < Gitlab::Database::Migration[1.0]
disable_ddl_transaction!
def up
@ -125,9 +123,7 @@ We can perform this cleanup using
```ruby
# A post-deployment migration in db/post_migrate
class CleanupUsersUpdatedAtRename < ActiveRecord::Migration[4.2]
include Gitlab::Database::MigrationHelpers
class CleanupUsersUpdatedAtRename < Gitlab::Database::Migration[1.0]
disable_ddl_transaction!
def up
@ -174,9 +170,7 @@ as follows:
```ruby
# A regular migration in db/migrate
class ChangeUsersUsernameStringToText < ActiveRecord::Migration[4.2]
include Gitlab::Database::MigrationHelpers
class ChangeUsersUsernameStringToText < Gitlab::Database::Migration[1.0]
disable_ddl_transaction!
def up
@ -195,9 +189,7 @@ Next we need to clean up our changes using a post-deployment migration:
```ruby
# A post-deployment migration in db/post_migrate
class ChangeUsersUsernameStringToTextCleanup < ActiveRecord::Migration[4.2]
include Gitlab::Database::MigrationHelpers
class ChangeUsersUsernameStringToTextCleanup < Gitlab::Database::Migration[1.0]
disable_ddl_transaction!
def up
@ -245,9 +237,7 @@ the work / load over a longer time period, without slowing down deployments.
For example, to change the column type using a background migration:
```ruby
class ExampleMigration < ActiveRecord::Migration[4.2]
include Gitlab::Database::MigrationHelpers
class ExampleMigration < Gitlab::Database::Migration[1.0]
disable_ddl_transaction!
class Issue < ActiveRecord::Base
@ -289,9 +279,7 @@ release) by a cleanup migration, which should steal from the queue and handle
any remaining rows. For example:
```ruby
class MigrateRemainingIssuesClosedAt < ActiveRecord::Migration[4.2]
include Gitlab::Database::MigrationHelpers
class MigrateRemainingIssuesClosedAt < Gitlab::Database::Migration[1.0]
disable_ddl_transaction!
class Issue < ActiveRecord::Base

View file

@ -254,7 +254,7 @@ existing data. Since we're dealing with a lot of rows we'll schedule jobs in
batches instead of doing this one by one:
```ruby
class ScheduleExtractServicesUrl < ActiveRecord::Migration[4.2]
class ScheduleExtractServicesUrl < Gitlab::Database::Migration[1.0]
disable_ddl_transaction!
def up
@ -281,7 +281,7 @@ jobs and manually run on any un-migrated rows. Such a migration would look like
this:
```ruby
class ConsumeRemainingExtractServicesUrlJobs < ActiveRecord::Migration[4.2]
class ConsumeRemainingExtractServicesUrlJobs < Gitlab::Database::Migration[1.0]
disable_ddl_transaction!
def up
@ -463,8 +463,6 @@ end
```ruby
# Post deployment migration
include Gitlab::Database::MigrationHelpers
MIGRATION = 'YourBackgroundMigrationName'
DELAY_INTERVAL = 2.minutes.to_i # can be different
BATCH_SIZE = 10_000 # can be different
@ -494,8 +492,6 @@ You can reschedule pending migrations from the `background_migration_jobs` table
```ruby
# Post deployment migration
include Gitlab::Database::MigrationHelpers
MIGRATION = 'YourBackgroundMigrationName'
DELAY_INTERVAL = 2.minutes

View file

@ -38,7 +38,7 @@ Settings are not cascading by default. To define a cascading setting, take the f
`application_settings`.
```ruby
class AddDelayedProjectRemovalCascadingSetting < ActiveRecord::Migration[6.0]
class AddDelayedProjectRemovalCascadingSetting < Gitlab::Database::Migration[1.0]
include Gitlab::Database::MigrationHelpers::CascadingNamespaceSettings
def up

View file

@ -66,9 +66,7 @@ In the example above, you'd be still able to update records in the `emails` tabl
Migration file for adding `NOT VALID` foreign key:
```ruby
class AddNotValidForeignKeyToEmailsUser < ActiveRecord::Migration[5.2]
include Gitlab::Database::MigrationHelpers
class AddNotValidForeignKeyToEmailsUser < Gitlab::Database::Migration[1.0]
def up
# safe to use: it requires short lock on the table since we don't validate the foreign key
add_foreign_key :emails, :users, on_delete: :cascade, validate: false
@ -92,9 +90,7 @@ In case the data volume is higher (>1000 records), it's better to create a backg
Example for cleaning up records in the `emails` table within a database migration:
```ruby
class RemoveRecordsWithoutUserFromEmailsTable < ActiveRecord::Migration[5.2]
include Gitlab::Database::MigrationHelpers
class RemoveRecordsWithoutUserFromEmailsTable < Gitlab::Database::Migration[1.0]
disable_ddl_transaction!
class Email < ActiveRecord::Base
@ -126,9 +122,7 @@ Migration file for validating the foreign key:
```ruby
# frozen_string_literal: true
class ValidateForeignKeyOnEmailUsers < ActiveRecord::Migration[5.2]
include Gitlab::Database::MigrationHelpers
class ValidateForeignKeyOnEmailUsers < Gitlab::Database::Migration[1.0]
def up
validate_foreign_key :emails, :user_id
end

View file

@ -25,7 +25,7 @@ For example, consider a migration that creates a table with two `NOT NULL` colum
`db/migrate/20200401000001_create_db_guides.rb`:
```ruby
class CreateDbGuides < ActiveRecord::Migration[6.0]
class CreateDbGuides < Gitlab::Database::Migration[1.0]
def change
create_table :db_guides do |t|
t.bigint :stars, default: 0, null: false
@ -44,7 +44,7 @@ For example, consider a migration that adds a new `NOT NULL` column `active` to
`db/migrate/20200501000001_add_active_to_db_guides.rb`:
```ruby
class AddExtendedTitleToSprints < ActiveRecord::Migration[6.0]
class AddExtendedTitleToSprints < Gitlab::Database::Migration[1.0]
def change
add_column :db_guides, :active, :boolean, default: true, null: false
end
@ -111,9 +111,7 @@ with `validate: false` in a post-deployment migration,
`db/post_migrate/20200501000001_add_not_null_constraint_to_epics_description.rb`:
```ruby
class AddNotNullConstraintToEpicsDescription < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
class AddNotNullConstraintToEpicsDescription < Gitlab::Database::Migration[1.0]
disable_ddl_transaction!
def up
@ -144,9 +142,7 @@ so we are going to add a post-deployment migration for the 13.0 milestone (curre
`db/post_migrate/20200501000002_cleanup_epics_with_null_description.rb`:
```ruby
class CleanupEpicsWithNullDescription < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
class CleanupEpicsWithNullDescription < Gitlab::Database::Migration[1.0]
# With BATCH_SIZE=1000 and epics.count=29500 on GitLab.com
# - 30 iterations will be run
# - each requires on average ~150ms
@ -184,9 +180,7 @@ migration helper in a final post-deployment migration,
`db/post_migrate/20200601000001_validate_not_null_constraint_on_epics_description.rb`:
```ruby
class ValidateNotNullConstraintOnEpicsDescription < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
class ValidateNotNullConstraintOnEpicsDescription < Gitlab::Database::Migration[1.0]
disable_ddl_transaction!
def up

View file

@ -60,8 +60,6 @@ Consider the next release as "Release N.M".
Execute a standard migration (not a post-migration):
```ruby
include Gitlab::Database::MigrationHelpers
def up
rename_table_safely(:issues, :tickets)
end
@ -96,8 +94,6 @@ At this point, we don't have applications using the old database table name in t
1. Remove the database view through a post-migration:
```ruby
include Gitlab::Database::MigrationHelpers
def up
finalize_table_rename(:issues, :tickets)
end

View file

@ -47,9 +47,7 @@ For example, consider a migration that creates a table with two text columns,
`db/migrate/20200401000001_create_db_guides.rb`:
```ruby
class CreateDbGuides < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
class CreateDbGuides < Gitlab::Database::Migration[1.0]
def up
create_table_with_constraints :db_guides do |t|
t.bigint :stars, default: 0, null: false
@ -84,7 +82,7 @@ For example, consider a migration that adds a new text column `extended_title` t
`db/migrate/20200501000001_add_extended_title_to_sprints.rb`:
```ruby
class AddExtendedTitleToSprints < ActiveRecord::Migration[6.0]
class AddExtendedTitleToSprints < Gitlab::Database::Migration[1.0]
# rubocop:disable Migration/AddLimitToTextColumns
# limit is added in 20200501000002_add_text_limit_to_sprints_extended_title
@ -99,8 +97,7 @@ A second migration should follow the first one with a limit added to `extended_t
`db/migrate/20200501000002_add_text_limit_to_sprints_extended_title.rb`:
```ruby
class AddTextLimitToSprintsExtendedTitle < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
class AddTextLimitToSprintsExtendedTitle < Gitlab::Database::Migration[1.0]
disable_ddl_transaction!
def up
@ -175,9 +172,7 @@ in a post-deployment migration,
`db/post_migrate/20200501000001_add_text_limit_migration.rb`:
```ruby
class AddTextLimitMigration < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
class AddTextLimitMigration < Gitlab::Database::Migration[1.0]
disable_ddl_transaction!
def up
@ -208,9 +203,7 @@ to add a background migration for the 13.0 milestone (current),
`db/post_migrate/20200501000002_schedule_cap_title_length_on_issues.rb`:
```ruby
class ScheduleCapTitleLengthOnIssues < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
class ScheduleCapTitleLengthOnIssues < Gitlab::Database::Migration[1.0]
# Info on how many records will be affected on GitLab.com
# time each batch needs to run on average, etc ...
BATCH_SIZE = 5000
@ -255,9 +248,7 @@ helper in a final post-deployment migration,
`db/post_migrate/20200601000001_validate_text_limit_migration.rb`:
```ruby
class ValidateTextLimitMigration < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
class ValidateTextLimitMigration < Gitlab::Database::Migration[1.0]
disable_ddl_transaction!
def up

View file

@ -173,7 +173,7 @@ An example migration of partitioning the `audit_events` table by its
`created_at` column would look like:
```ruby
class PartitionAuditEvents < ActiveRecord::Migration[6.0]
class PartitionAuditEvents < Gitlab::Database::Migration[1.0]
include Gitlab::Database::PartitioningMigrationHelpers
def up
@ -200,7 +200,7 @@ into the partitioned copy.
Continuing the above example, the migration would look like:
```ruby
class BackfillPartitionAuditEvents < ActiveRecord::Migration[6.0]
class BackfillPartitionAuditEvents < Gitlab::Database::Migration[1.0]
include Gitlab::Database::PartitioningMigrationHelpers
def up
@ -233,7 +233,7 @@ failed jobs.
Once again, continuing the example, this migration would look like:
```ruby
class CleanupPartitionedAuditEventsBackfill < ActiveRecord::Migration[6.0]
class CleanupPartitionedAuditEventsBackfill < Gitlab::Database::Migration[1.0]
include Gitlab::Database::PartitioningMigrationHelpers
def up

View file

@ -171,6 +171,45 @@ If you need to translate strings in the Vue component's JavaScript, you can impo
To test Vue translations, learn about [manually testing translations from the UI](#manually-test-translations-from-the-ui).
### Test files
Test expectations against externalized contents should not be hard coded,
because we may need to run the tests with non-default locale, and tests with
hard coded contents will fail.
This means any expectations against externalized contents should call the
same externalizing method to match the translation.
Bad:
```ruby
click_button 'Submit review'
expect(rendered).to have_content('Thank you for your feedback!')
```
Good:
```ruby
click_button _('Submit review')
expect(rendered).to have_content(_('Thank you for your feedback!'))
```
This includes JavaScript tests:
Bad:
```javascript
expect(findUpdateIgnoreStatusButton().text()).toBe('Ignore');
```
Good:
```javascript
expect(findUpdateIgnoreStatusButton().text()).toBe(__('Ignore'));
```
#### Recommendations
If strings are reused throughout a component, it can be useful to define these strings as variables. We recommend defining an `i18n` property on the component's `$options` object. If there is a mixture of many-use and single-use strings in the component, consider using this approach to create a local [Single Source of Truth](https://about.gitlab.com/handbook/values/#single-source-of-truth) for externalized strings.

View file

@ -70,7 +70,7 @@ graph LR
H -->|Yes| E[Regular migration]
H -->|No| I[Post-deploy migration<br/>+ feature flag]
D -->|Yes| F[Post-deploy migration]
D -->|No| G[Background migration]
```
@ -217,6 +217,39 @@ In case you need to insert, update, or delete a significant amount of data, you:
- Must disable the single transaction with `disable_ddl_transaction!`.
- Should consider doing it in a [Background Migration](background_migrations.md).
## Migration helpers and versioning
Various helper methods are available for many common patterns in database migrations. Those
helpers can be found in `Gitlab::Database::MigrationHelpers` and related modules.
In order to allow changing a helper's behavior over time, we implement a versioning scheme
for migration helpers. This allows us to maintain the behavior of a helper for already
existing migrations but change the behavior for any new migrations.
For that purpose, all database migrations should inherit from `Gitlab::Database::Migration`,
which is a "versioned" class. For new migrations, the latest version should be used (which
can be looked up in `Gitlab::Database::Migration::MIGRATION_CLASSES`) to use the latest version
of migration helpers.
In this example, we use version 1.0 of the migration class:
```ruby
class TestMigration < Gitlab::Database::Migration[1.0]
def change
end
end
```
NOTE:
It is discouraged to include `Gitlab::Database::MigrationHelpers` directly into a
migration. Instead, the latest version of `Gitlab::Database::Migration` will expose the latest
version of migration helpers automatically.
NOTE:
Migration helpers and versioning are available starting from [14.3](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/68986).
For merge requests targeting previous stable branches, the old format needs to be used and we continue
to inherit from `ActiveRecord::Migration[6.1]` instead of `Gitlab::Database::Migration[1.0]` for those.
## Retry mechanism when acquiring database locks
When changing the database schema, we use helper methods to invoke DDL (Data Definition
@ -256,8 +289,6 @@ lock allow the database to process other statements.
**Removing a column:**
```ruby
include Gitlab::Database::MigrationHelpers
def up
with_lock_retries do
remove_column :users, :full_name
@ -278,8 +309,6 @@ you should do as much as possible inside the transaction rather than trying to g
Be careful about running long database statements within the block. The acquired locks are kept until the transaction (block) finishes and depending on the lock type, it might block other database operations.
```ruby
include Gitlab::Database::MigrationHelpers
def up
with_lock_retries do
add_column :users, :full_name, :string
@ -298,8 +327,6 @@ end
**Removing a foreign key:**
```ruby
include Gitlab::Database::MigrationHelpers
def up
with_lock_retries do
remove_foreign_key :issues, :projects
@ -316,8 +343,6 @@ end
**Changing default value for a column:**
```ruby
include Gitlab::Database::MigrationHelpers
def up
with_lock_retries do
change_column_default :merge_requests, :lock_version, from: nil, to: 0
@ -387,8 +412,6 @@ We can use the `add_concurrent_foreign_key` method in this case, as this helper
has the lock retries built into it.
```ruby
include Gitlab::Database::MigrationHelpers
disable_ddl_transaction!
def up
@ -405,8 +428,6 @@ end
Adding foreign key to `users`:
```ruby
include Gitlab::Database::MigrationHelpers
disable_ddl_transaction!
def up
@ -498,11 +519,11 @@ by calling the method `disable_ddl_transaction!` in the body of your migration
class like so:
```ruby
class MyMigration < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
class MyMigration < Gitlab::Database::Migration[1.0]
disable_ddl_transaction!
INDEX_NAME = 'index_name'
def up
remove_concurrent_index :table_name, :column_name, name: INDEX_NAME
end
@ -549,7 +570,7 @@ by calling the method `disable_ddl_transaction!` in the body of your migration
class like so:
```ruby
class MyMigration < ActiveRecord::Migration[6.0]
class MyMigration < Gitlab::Database::Migration[1.0]
include Gitlab::Database::MigrationHelpers
disable_ddl_transaction!
@ -594,7 +615,7 @@ The easiest way to test for existence of an index by name is to use the
be used with a name option. For example:
```ruby
class MyMigration < ActiveRecord::Migration[6.0]
class MyMigration < Gitlab::Database::Migration[1.0]
include Gitlab::Database::MigrationHelpers
INDEX_NAME = 'index_name'
@ -631,7 +652,7 @@ Here's an example where we add a new column with a foreign key
constraint. Note it includes `index: true` to create an index for it.
```ruby
class Migration < ActiveRecord::Migration[6.0]
class Migration < Gitlab::Database::Migration[1.0]
def change
add_reference :model, :other_model, index: true, foreign_key: { on_delete: :cascade }
@ -677,7 +698,7 @@ expensive and disruptive operation for larger tables, but in reality it's not.
Take the following migration as an example:
```ruby
class DefaultRequestAccessGroups < ActiveRecord::Migration[5.2]
class DefaultRequestAccessGroups < Gitlab::Database::Migration[1.0]
def change
change_column_default(:namespaces, :request_access_enabled, from: false, to: true)
end
@ -884,7 +905,7 @@ The Rails 5 natively supports `JSONB` (binary JSON) column type.
Example migration adding this column:
```ruby
class AddOptionsToBuildMetadata < ActiveRecord::Migration[5.0]
class AddOptionsToBuildMetadata < Gitlab::Database::Migration[1.0]
def change
add_column :ci_builds_metadata, :config_options, :jsonb
end
@ -916,7 +937,7 @@ Do not store `attr_encrypted` attributes as `:text` in the database; use
efficient:
```ruby
class AddSecretToSomething < ActiveRecord::Migration[5.0]
class AddSecretToSomething < Gitlab::Database::Migration[1.0]
def change
add_column :something, :encrypted_secret, :binary
add_column :something, :encrypted_secret_iv, :binary
@ -974,7 +995,7 @@ If you need more complex logic, you can define and use models local to a
migration. For example:
```ruby
class MyMigration < ActiveRecord::Migration[6.0]
class MyMigration < Gitlab::Database::Migration[1.0]
class Project < ActiveRecord::Base
self.table_name = 'projects'
end
@ -1073,7 +1094,7 @@ in a previous migration.
It is important not to leave out the `User.reset_column_information` command, in order to ensure that the old schema is dropped from the cache and ActiveRecord loads the updated schema information.
```ruby
class AddAndSeedMyColumn < ActiveRecord::Migration[6.0]
class AddAndSeedMyColumn < Gitlab::Database::Migration[1.0]
class User < ActiveRecord::Base
self.table_name = 'users'
end

View file

@ -36,9 +36,7 @@ After you add or change an existing common metric, you must [re-run the import s
Or, you can create a database migration:
```ruby
class ImportCommonMetrics < ActiveRecord::Migration[4.2]
include Gitlab::Database::MigrationHelpers
class ImportCommonMetrics < Gitlab::Database::Migration[1.0]
def up
::Gitlab::DatabaseImporters::CommonMetrics::Importer.new.execute
end

View file

@ -1002,9 +1002,7 @@ When renaming queues, use the `sidekiq_queue_migrate` helper migration method
in a **post-deployment migration**:
```ruby
class MigrateTheRenamedSidekiqQueue < ActiveRecord::Migration[5.0]
include Gitlab::Database::MigrationHelpers
class MigrateTheRenamedSidekiqQueue < Gitlab::Database::Migration[1.0]
def up
sidekiq_queue_migrate 'old_queue_name', to: 'new_queue_name'
end

View file

@ -31,7 +31,7 @@ could result in loading unexpected code or associations which may cause unintend
side effects or failures during upgrades.
```ruby
class SomeMigration < ActiveRecord::Migration[6.0]
class SomeMigration < Gitlab::Database::Migration[1.0]
class Services < ActiveRecord::Base
self.table_name = 'services'
self.inheritance_column = :_type_disabled
@ -47,7 +47,7 @@ This ensures that the migration loads the columns for the migration in isolation
and the helper disables STI by default.
```ruby
class EnqueueSomeBackgroundMigration < ActiveRecord::Migration[6.0]
class EnqueueSomeBackgroundMigration < Gitlab::Database::Migration[1.0]
disable_ddl_transaction!
def up

View file

@ -102,7 +102,7 @@ transaction. Transactions for migrations can be disabled using the following
pattern:
```ruby
class MigrationName < ActiveRecord::Migration[4.2]
class MigrationName < Gitlab::Database::Migration[1.0]
disable_ddl_transaction!
end
```
@ -110,7 +110,7 @@ end
For example:
```ruby
class AddUsersLowerUsernameEmailIndexes < ActiveRecord::Migration[4.2]
class AddUsersLowerUsernameEmailIndexes < Gitlab::Database::Migration[1.0]
disable_ddl_transaction!
def up

View file

@ -367,26 +367,34 @@ If needed, you can scope interactions within a specific area of the page by usin
As you will likely be scoping to an element such as a `div`, which typically does not have a label,
you may use a `data-testid` selector in this case.
##### Externalized contents
Test expectations against externalized contents should call the same
externalizing method to match the translation. For example, you should use the `_`
method in Ruby and `__` method in JavaScript.
See [Internationalization for GitLab - Test files](../i18n/externalization.md#test-files) for details.
##### Actions
Where possible, use more specific [actions](https://rubydoc.info/github/teamcapybara/capybara/master/Capybara/Node/Actions), such as the ones below.
```ruby
# good
click_button 'Submit review'
click_button _('Submit review')
click_link 'UI testing docs'
click_link _('UI testing docs')
fill_in 'Search projects', with: 'gitlab' # fill in text input with text
fill_in _('Search projects'), with: 'gitlab' # fill in text input with text
select 'Last updated', from: 'Sort by' # select an option from a select input
select _('Last updated'), from: 'Sort by' # select an option from a select input
check 'Checkbox label'
uncheck 'Checkbox label'
check _('Checkbox label')
uncheck _('Checkbox label')
choose 'Radio input label'
choose _('Radio input label')
attach_file('Attach a file', '/path/to/file.png')
attach_file(_('Attach a file'), '/path/to/file.png')
# bad - interactive elements must have accessible names, so
# we should be able to use one of the specific actions above
@ -403,17 +411,17 @@ Where possible, use more specific [finders](https://rubydoc.info/github/teamcapy
```ruby
# good
find_button 'Submit review'
find_button 'Submit review', disabled: true
find_button _('Submit review')
find_button _('Submit review'), disabled: true
find_link 'UI testing docs'
find_link 'UI testing docs', href: docs_url
find_link _('UI testing docs')
find_link _('UI testing docs'), href: docs_url
find_field 'Search projects'
find_field 'Search projects', with: 'gitlab' # find the input field with text
find_field 'Search projects', disabled: true
find_field 'Checkbox label', checked: true
find_field 'Checkbox label', unchecked: true
find_field _('Search projects')
find_field _('Search projects'), with: 'gitlab' # find the input field with text
find_field _('Search projects'), disabled: true
find_field _('Checkbox label'), checked: true
find_field _('Checkbox label'), unchecked: true
# acceptable when finding a element that is not a button, link, or field
find('[data-testid="element"]')
@ -425,31 +433,31 @@ Where possible, use more specific [matchers](https://rubydoc.info/github/teamcap
```ruby
# good
expect(page).to have_button 'Submit review'
expect(page).to have_button 'Submit review', disabled: true
expect(page).to have_button 'Notifications', class: 'is-checked' # assert the "Notifications" GlToggle is checked
expect(page).to have_button _('Submit review')
expect(page).to have_button _('Submit review'), disabled: true
expect(page).to have_button _('Notifications'), class: 'is-checked' # assert the "Notifications" GlToggle is checked
expect(page).to have_link 'UI testing docs'
expect(page).to have_link 'UI testing docs', href: docs_url # assert the link has an href
expect(page).to have_link _('UI testing docs')
expect(page).to have_link _('UI testing docs'), href: docs_url # assert the link has an href
expect(page).to have_field 'Search projects'
expect(page).to have_field 'Search projects', disabled: true
expect(page).to have_field 'Search projects', with: 'gitlab' # assert the input field has text
expect(page).to have_field _('Search projects')
expect(page).to have_field _('Search projects'), disabled: true
expect(page).to have_field _('Search projects'), with: 'gitlab' # assert the input field has text
expect(page).to have_checked_field 'Checkbox label'
expect(page).to have_unchecked_field 'Radio input label'
expect(page).to have_checked_field _('Checkbox label')
expect(page).to have_unchecked_field _('Radio input label')
expect(page).to have_select 'Sort by'
expect(page).to have_select 'Sort by', selected: 'Last updated' # assert the option is selected
expect(page).to have_select 'Sort by', options: ['Last updated', 'Created date', 'Due date'] # assert an exact list of options
expect(page).to have_select 'Sort by', with_options: ['Created date', 'Due date'] # assert a partial list of options
expect(page).to have_select _('Sort by')
expect(page).to have_select _('Sort by'), selected: 'Last updated' # assert the option is selected
expect(page).to have_select _('Sort by'), options: ['Last updated', 'Created date', 'Due date'] # assert an exact list of options
expect(page).to have_select _('Sort by'), with_options: ['Created date', 'Due date'] # assert a partial list of options
expect(page).to have_text 'Some paragraph text.'
expect(page).to have_text 'Some paragraph text.', exact: true # assert exact match
expect(page).to have_text _('Some paragraph text.')
expect(page).to have_text _('Some paragraph text.'), exact: true # assert exact match
expect(page).to have_current_path 'gitlab/gitlab-test/-/issues'
expect(page).to have_title 'Not Found'
expect(page).to have_title _('Not Found')
# acceptable when a more specific matcher above is not possible
expect(page).to have_css 'h2', text: 'Issue title'

View file

@ -34,6 +34,7 @@ These are rate limits you can set in the Admin Area of your instance:
- [Raw endpoints rate limits](../user/admin_area/settings/rate_limits_on_raw_endpoints.md)
- [User and IP rate limits](../user/admin_area/settings/user_and_ip_rate_limits.md)
- [Package registry rate limits](../user/admin_area/settings/package_registry_rate_limits.md)
- [Git LFS rate limits](../user/admin_area/settings/git_lfs_rate_limits.md)
## Non-configurable limits

View file

@ -0,0 +1,35 @@
---
stage: Create
group: Source Code
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
type: reference
---
# Git LFS Rate Limits **(FREE SELF)**
[Git LFS (Large File Storage)](../../../topics/git/lfs/index.md) is a Git extension
for handling large files. If you use Git LFS in your repository, common Git operations
can generate many Git LFS requests. You can enforce
[general user and IP rate limits](user_and_ip_rate_limits.md), but you can also
override the general setting to enforce additional limits on Git LFS requests. This
override can improve the security and durability of your web application. Aside from
precedence, this configuration provides the same features as the general user and IP
rate limits.
## Configure Git LFS rate limits
Git LFS rate limits are disabled by default. If enabled and configured, these limits
supersede the [general user and IP rate limits](user_and_ip_rate_limits.md):
1. On the top bar, select **Menu >** **{admin}** **Admin**.
1. On the left sidebar, select **Settings > Network**.
1. Expand **Git LFS Rate Limits**.
1. Select **Enable authenticated Git LFS request rate limit**.
1. Enter a value for **Max authenticated Git LFS requests per period per user**.
1. Enter a value for **Authenticated Git LFS rate limit period in seconds**.
1. Select **Save changes**.
## Resources
- [Rate limiting](../../../security/rate_limits.md)
- [User and IP rate limits](user_and_ip_rate_limits.md)

View file

@ -96,6 +96,7 @@ To access the default page for Admin Area settings:
| Performance optimization | [Write to "authorized_keys" file](../../../administration/operations/fast_ssh_key_lookup.md#setting-up-fast-lookup-via-gitlab-shell) and [Push event activities limit and bulk push events](push_event_activities_limit.md). Various settings that affect GitLab performance. |
| [User and IP rate limits](user_and_ip_rate_limits.md) | Configure limits for web and API requests. |
| [Package Registry Rate Limits](package_registry_rate_limits.md) | Configure specific limits for Packages API requests that supersede the user and IP rate limits. |
| [Git LFS Rate Limits](git_lfs_rate_limits.md) | Configure specific limits for Git LFS requests that supersede the user and IP rate limits. |
| [Outbound requests](../../../security/webhooks.md) | Allow requests to the local network from hooks and services. |
| [Protected Paths](protected_paths.md) | Configure paths to be protected by Rack Attack. |
| [Incident Management](../../../operations/incident_management/index.md) Limits | Limit the number of inbound alerts that can be sent to a project. |

View file

@ -136,6 +136,7 @@ The possible names are:
- `throttle_authenticated_protected_paths_web`
- `throttle_unauthenticated_packages_api`
- `throttle_authenticated_packages_api`
- `throttle_authenticated_git_lfs`
For example, to try out throttles for all authenticated requests to
non-protected paths can be done by setting

View file

@ -3,10 +3,7 @@
# See https://docs.gitlab.com/ee/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
class <%= migration_class_name %> < ActiveRecord::Migration[<%= ActiveRecord::Migration.current_version %>]
# Uncomment the following include if you require helper functions:
# include Gitlab::Database::MigrationHelpers
class <%= migration_class_name %> < Gitlab::Database::Migration[<%= Gitlab::Database::Migration.current_version %>]
# When using the methods "add_concurrent_index" or "remove_concurrent_index"
# you must disable the use of transactions
# as these methods can not run in an existing transaction.

View file

@ -3,10 +3,7 @@
# See https://docs.gitlab.com/ee/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
class <%= migration_class_name %> < ActiveRecord::Migration[<%= ActiveRecord::Migration.current_version %>]
# Uncomment the following include if you require helper functions:
# include Gitlab::Database::MigrationHelpers
class <%= migration_class_name %> < Gitlab::Database::Migration[<%= Gitlab::Database::Migration.current_version %>]
# When using the methods "add_concurrent_index" or "remove_concurrent_index"
# you must disable the use of transactions
# as these methods can not run in an existing transaction.
@ -20,6 +17,9 @@ class <%= migration_class_name %> < ActiveRecord::Migration[<%= ActiveRecord::Mi
# comments:
# disable_ddl_transaction!
def change
def up
end
def down
end
end

View file

@ -338,6 +338,10 @@ module Gitlab
Gitlab::PathRegex.repository_git_route_regex.match?(current_request.path)
end
def git_lfs_request?
Gitlab::PathRegex.repository_git_lfs_route_regex.match?(current_request.path)
end
def archive_request?
current_request.path.include?('/-/archive/')
end

View file

@ -0,0 +1,36 @@
# frozen_string_literal: true
module Gitlab
module Database
class Migration
# This implements a simple versioning scheme for migration helpers.
#
# We need to be able to version helpers, so we can change their behavior without
# altering the behavior of already existing migrations in incompatible ways.
#
# We can continue to change the behavior of helpers without bumping the version here,
# *if* the change is backwards-compatible.
#
# If not, we would typically override the helper method in a new MigrationHelpers::V[0-9]+
# class and create a new entry with a bumped version below.
#
# We use major version bumps to indicate significant changes and minor version bumps
# to indicate backwards-compatible or otherwise minor changes (e.g. a Rails version bump).
# However, this hasn't been strictly formalized yet.
MIGRATION_CLASSES = {
1.0 => Class.new(ActiveRecord::Migration[6.1]) do
include Gitlab::Database::MigrationHelpers::V2
end
}.freeze
def self.[](version)
MIGRATION_CLASSES[version] || raise(ArgumentError, "Unknown migration version: #{version}")
end
# The current version to be used in new migrations
def self.current_version
1.0
end
end
end
end

View file

@ -20,7 +20,8 @@ module Gitlab
:throttle_authenticated_web,
:throttle_authenticated_protected_paths_api,
:throttle_authenticated_protected_paths_web,
:throttle_authenticated_packages_api
:throttle_authenticated_packages_api,
:throttle_authenticated_git_lfs
].freeze
PAYLOAD_KEYS = [

View file

@ -184,6 +184,10 @@ module Gitlab
@repository_git_route_regex ||= /#{repository_route_regex}\.git/.freeze
end
def repository_git_lfs_route_regex
@repository_git_lfs_route_regex ||= %r{#{repository_git_route_regex}\/(info\/lfs|gitlab-lfs)\/}.freeze
end
def repository_wiki_git_route_regex
@repository_wiki_git_route_regex ||= /#{full_namespace_route_regex}\.*\.wiki\.git/.freeze
end

View file

@ -139,6 +139,12 @@ module Gitlab
end
end
throttle_or_track(rack_attack, 'throttle_authenticated_git_lfs', Gitlab::Throttle.throttle_authenticated_git_lfs_options) do |req|
if req.throttle_authenticated_git_lfs?
req.throttled_user_id([:api])
end
end
rack_attack.safelist('throttle_bypass_header') do |req|
Gitlab::Throttle.bypass_header.present? &&
req.get_header(Gitlab::Throttle.bypass_header) == '1'

View file

@ -73,6 +73,7 @@ module Gitlab
def throttle_authenticated_web?
web_request? &&
!throttle_authenticated_git_lfs? &&
Gitlab::Throttle.settings.throttle_authenticated_web_enabled
end
@ -109,6 +110,11 @@ module Gitlab
Gitlab::Throttle.settings.throttle_authenticated_packages_api_enabled
end
def throttle_authenticated_git_lfs?
git_lfs_path? &&
Gitlab::Throttle.settings.throttle_authenticated_git_lfs_enabled
end
private
def authenticated_user_id(request_formats)
@ -130,6 +136,10 @@ module Gitlab
def packages_api_path?
path =~ ::Gitlab::Regex::Packages::API_PATH_REGEX
end
def git_lfs_path?
path =~ Gitlab::PathRegex.repository_git_lfs_route_regex
end
end
end
end

View file

@ -63,6 +63,13 @@ module Gitlab
{ limit: limit_proc, period: period_proc }
end
def self.throttle_authenticated_git_lfs_options
limit_proc = proc { |req| settings.throttle_authenticated_git_lfs_requests_per_period }
period_proc = proc { |req| settings.throttle_authenticated_git_lfs_period_in_seconds.seconds }
{ limit: limit_proc, period: period_proc }
end
def self.rate_limiting_response_text
(settings.rate_limiting_response_text.presence || DEFAULT_RATE_LIMITING_RESPONSE_TEXT) + "\n"
end

View file

@ -4769,6 +4769,12 @@ msgstr ""
msgid "Authenticated API requests"
msgstr ""
msgid "Authenticated Git LFS rate limit period in seconds"
msgstr ""
msgid "Authenticated Git LFS request rate limit"
msgstr ""
msgid "Authenticated web rate limit period in seconds"
msgstr ""
@ -8506,6 +8512,9 @@ msgstr ""
msgid "Configure settings for Advanced Search with Elasticsearch."
msgstr ""
msgid "Configure specific limits for Git LFS requests that supersede the general user and IP rate limits."
msgstr ""
msgid "Configure specific limits for Packages API requests that supersede the general user and IP rate limits."
msgstr ""
@ -12413,6 +12422,9 @@ msgstr ""
msgid "Enable authenticated API request rate limit"
msgstr ""
msgid "Enable authenticated Git LFS request rate limit"
msgstr ""
msgid "Enable authentication"
msgstr ""
@ -15203,6 +15215,9 @@ msgstr ""
msgid "Git GC period"
msgstr ""
msgid "Git LFS Rate Limits"
msgstr ""
msgid "Git LFS is not enabled on this GitLab server, contact your admin."
msgstr ""
@ -16526,6 +16541,9 @@ msgstr ""
msgid "Helps reduce request volume (e.g. from crawlers or abusive bots)"
msgstr ""
msgid "Helps reduce request volume (for example, from crawlers or abusive bots)"
msgstr ""
msgid "Helps reduce request volume for protected paths"
msgstr ""
@ -20604,6 +20622,9 @@ msgstr ""
msgid "Max authenticated API requests per period per user"
msgstr ""
msgid "Max authenticated Git LFS requests per period per user"
msgstr ""
msgid "Max authenticated web requests per period per user"
msgstr ""

View file

@ -0,0 +1,55 @@
# frozen_string_literal: true
require_relative '../../migration_helpers'
module RuboCop
module Cop
module Migration
class VersionedMigrationClass < RuboCop::Cop::Cop
include MigrationHelpers
ENFORCED_SINCE = 2021_09_02_00_00_00
MSG_INHERIT = 'Don\'t inherit from ActiveRecord::Migration but use Gitlab::Database::Migration[1.0] instead. See https://docs.gitlab.com/ee/development/migration_style_guide.html#migration-helpers-and-versioning.'
MSG_INCLUDE = 'Don\'t include migration helper modules directly. Inherit from Gitlab::Database::Migration[1.0] instead. See https://docs.gitlab.com/ee/development/migration_style_guide.html#migration-helpers-and-versioning.'
MIGRATION_CLASS = 'Gitlab::Database::Migration'
def_node_search :includes_helpers?, <<~PATTERN
(send nil? :include
(const
(const
(const nil? :Gitlab) :Database) :MigrationHelpers))
PATTERN
def on_class(node)
return unless relevant_migration?(node)
add_offense(node, location: :expression, message: MSG_INHERIT) unless gitlab_migration_class?(node)
end
def on_send(node)
return unless relevant_migration?(node)
add_offense(node, location: :expression, message: MSG_INCLUDE) if includes_helpers?(node)
end
private
def relevant_migration?(node)
in_migration?(node) && version(node) >= ENFORCED_SINCE
end
def gitlab_migration_class?(node)
superclass(node) == MIGRATION_CLASS
end
def superclass(class_node)
_, *others = class_node.descendants
others.find { |node| node.const_type? && node&.const_name != 'Types' }&.const_name
end
end
end
end
end

View file

@ -3,6 +3,8 @@ import Bold from '~/content_editor/extensions/bold';
import BulletList from '~/content_editor/extensions/bullet_list';
import Code from '~/content_editor/extensions/code';
import CodeBlockHighlight from '~/content_editor/extensions/code_block_highlight';
import DescriptionItem from '~/content_editor/extensions/description_item';
import DescriptionList from '~/content_editor/extensions/description_list';
import Division from '~/content_editor/extensions/division';
import Emoji from '~/content_editor/extensions/emoji';
import Figure from '~/content_editor/extensions/figure';
@ -41,6 +43,8 @@ const tiptapEditor = createTestEditor({
BulletList,
Code,
CodeBlockHighlight,
DescriptionItem,
DescriptionList,
Division,
Emoji,
Figure,
@ -75,6 +79,8 @@ const {
code,
codeBlock,
division,
descriptionItem,
descriptionList,
emoji,
figure,
figureCaption,
@ -105,6 +111,8 @@ const {
code: { markType: Code.name },
codeBlock: { nodeType: CodeBlockHighlight.name },
division: { nodeType: Division.name },
descriptionItem: { nodeType: DescriptionItem.name },
descriptionList: { nodeType: DescriptionList.name },
emoji: { markType: Emoji.name },
figure: { nodeType: Figure.name },
figureCaption: { nodeType: FigureCaption.name },
@ -545,6 +553,41 @@ this is not really json but just trying out whether this case works or not
);
});
it('correctly renders a description list', () => {
expect(
serialize(
descriptionList(
descriptionItem(paragraph('Beast of Bodmin')),
descriptionItem({ isTerm: false }, paragraph('A large feline inhabiting Bodmin Moor.')),
descriptionItem(paragraph('Morgawr')),
descriptionItem({ isTerm: false }, paragraph('A sea serpent.')),
descriptionItem(paragraph('Owlman')),
descriptionItem(
{ isTerm: false },
paragraph('A giant ', italic('owl-like'), ' creature.'),
),
),
),
).toBe(
`
<dl>
<dt>Beast of Bodmin</dt>
<dd>A large feline inhabiting Bodmin Moor.</dd>
<dt>Morgawr</dt>
<dd>A sea serpent.</dd>
<dt>Owlman</dt>
<dd>
A giant _owl-like_ creature.
</dd>
</dl>
`.trim(),
);
});
it('correctly renders div', () => {
expect(
serialize(

View file

@ -1,7 +1,7 @@
import { secondsToHours } from '~/lib/utils/datetime_utility';
export const freezePeriodsFixture = getJSONFixture('/api/freeze-periods/freeze_periods.json');
export const timezoneDataFixture = getJSONFixture('/api/freeze-periods/timezone_data.json');
export const timezoneDataFixture = getJSONFixture('/timezones/short.json');
export const findTzByName = (identifier = '') =>
timezoneDataFixture.find(({ name }) => name.toLowerCase() === identifier.toLowerCase());

View file

@ -9,6 +9,12 @@ Vue.use(Vuex);
export default function createDiffsStore() {
return new Vuex.Store({
modules: {
page: {
namespaced: true,
state: {
activeTab: 'notes',
},
},
diffs: diffsModule(),
notes: notesModule(),
batchComments: batchCommentsModule(),

View file

@ -59,6 +59,24 @@
</figcaption>
</figure>
- name: description_list
markdown: |-
<dl>
<dt>Frog</dt>
<dd>Wet green thing</dd>
<dt>Rabbit</dt>
<dd>Warm fluffy thing</dd>
<dt>Punt</dt>
<dd>Kick a ball</dd>
<dd>Take a bet</dd>
<dt>Color</dt>
<dt>Colour</dt>
<dd>
Any hue except _white_ or **black**
</dd>
</dl>
- name: link
markdown: '[GitLab](https://gitlab.com)'
- name: attachment_link

View file

@ -39,13 +39,4 @@ RSpec.describe 'Freeze Periods (JavaScript fixtures)' do
expect(response).to be_successful
end
end
describe TimeZoneHelper, '(JavaScript fixtures)' do
let(:response) { timezone_data.to_json }
it 'api/freeze-periods/timezone_data.json' do
# Looks empty but does things
# More info: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/38525/diffs#note_391048415
end
end
end

View file

@ -0,0 +1,22 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe TimeZoneHelper, '(JavaScript fixtures)' do
include JavaScriptFixturesHelpers
include TimeZoneHelper
let(:response) { @timezones.sort_by! { |tz| tz[:name] }.to_json }
before(:all) do
clean_frontend_fixtures('timezones/')
end
it 'timezones/short.json' do
@timezones = timezone_data(format: :short)
end
it 'timezones/full.json' do
@timezones = timezone_data(format: :full)
end
end

View file

@ -2,7 +2,7 @@
exports[`Code Block with default props renders correctly 1`] = `
<pre
class="code-block rounded"
class="code-block rounded code"
>
<code
class="d-block"
@ -14,7 +14,7 @@ exports[`Code Block with default props renders correctly 1`] = `
exports[`Code Block with maxHeight set to "200px" renders correctly 1`] = `
<pre
class="code-block rounded"
class="code-block rounded code"
style="max-height: 200px; overflow-y: auto;"
>
<code

View file

@ -0,0 +1,34 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Gitlab::Database::Migration do
describe '.[]' do
context 'version: 1.0' do
subject { described_class[1.0] }
it 'inherits from ActiveRecord::Migration[6.1]' do
expect(subject.superclass).to eq(ActiveRecord::Migration[6.1])
end
it 'includes migration helpers version 2' do
expect(subject.included_modules).to include(Gitlab::Database::MigrationHelpers::V2)
end
end
context 'unknown version' do
it 'raises an error' do
expect { described_class[0] }.to raise_error(ArgumentError, /Unknown migration version/)
end
end
end
describe '.current_version' do
it 'includes current ActiveRecord migration class' do
# This breaks upon Rails upgrade. In that case, we'll add a new version in Gitlab::Database::Migration::MIGRATION_CLASSES,
# bump .current_version and leave existing migrations and already defined versions of Gitlab::Database::Migration
# untouched.
expect(described_class[described_class.current_version].superclass).to eq(ActiveRecord::Migration::Current)
end
end
end

View file

@ -468,6 +468,7 @@ RSpec.describe Gitlab::PathRegex do
end
let_it_be(:git_paths) { container_paths.map { |path| path + '.git' } }
let_it_be(:git_lfs_paths) { git_paths.flat_map { |path| [path + '/info/lfs/', path + '/gitlab-lfs/'] } }
let_it_be(:snippet_paths) { container_paths.grep(%r{snippets/\d}) }
let_it_be(:wiki_git_paths) { (container_paths - snippet_paths).map { |path| path + '.wiki.git' } }
let_it_be(:invalid_git_paths) { invalid_paths.map { |path| path + '.git' } }
@ -498,6 +499,15 @@ RSpec.describe Gitlab::PathRegex do
end
end
describe '.repository_git_lfs_route_regex' do
subject { %r{\A#{described_class.repository_git_lfs_route_regex}\z} }
it 'matches the expected paths' do
expect_route_match(git_lfs_paths)
expect_no_route_match(container_paths + invalid_paths + git_paths + invalid_git_paths)
end
end
describe '.repository_wiki_git_route_regex' do
subject { %r{\A#{described_class.repository_wiki_git_route_regex}\z} }

View file

@ -939,6 +939,8 @@ RSpec.describe ApplicationSetting do
throttle_unauthenticated_files_api_period_in_seconds
throttle_authenticated_files_api_requests_per_period
throttle_authenticated_files_api_period_in_seconds
throttle_authenticated_git_lfs_requests_per_period
throttle_authenticated_git_lfs_period_in_seconds
]
end

View file

@ -22,7 +22,9 @@ RSpec.describe 'Rack Attack global throttles', :use_clean_rails_memory_store_cac
throttle_unauthenticated_packages_api_requests_per_period: 100,
throttle_unauthenticated_packages_api_period_in_seconds: 1,
throttle_authenticated_packages_api_requests_per_period: 100,
throttle_authenticated_packages_api_period_in_seconds: 1
throttle_authenticated_packages_api_period_in_seconds: 1,
throttle_authenticated_git_lfs_requests_per_period: 100,
throttle_authenticated_git_lfs_period_in_seconds: 1
}
end
@ -620,6 +622,95 @@ RSpec.describe 'Rack Attack global throttles', :use_clean_rails_memory_store_cac
end
end
describe 'authenticated git lfs requests', :api do
let_it_be(:project) { create(:project, :internal) }
let_it_be(:user) { create(:user) }
let_it_be(:token) { create(:personal_access_token, user: user) }
let_it_be(:other_user) { create(:user) }
let_it_be(:other_user_token) { create(:personal_access_token, user: other_user) }
let(:request_method) { 'GET' }
let(:throttle_setting_prefix) { 'throttle_authenticated_git_lfs' }
let(:git_lfs_url) { "/#{project.full_path}.git/info/lfs/locks" }
before do
allow(Gitlab.config.lfs).to receive(:enabled).and_return(true)
stub_application_setting(settings_to_set)
end
context 'with regular login' do
let(:url_that_requires_authentication) { git_lfs_url }
it_behaves_like 'rate-limited web authenticated requests'
end
context 'with the token in the headers' do
let(:request_args) { [git_lfs_url, { headers: basic_auth_headers(user, token) }] }
let(:other_user_request_args) { [git_lfs_url, { headers: basic_auth_headers(other_user, other_user_token) }] }
it_behaves_like 'rate-limited token-authenticated requests'
end
context 'precedence over authenticated web throttle' do
before do
settings_to_set[:throttle_authenticated_git_lfs_requests_per_period] = requests_per_period
settings_to_set[:throttle_authenticated_git_lfs_period_in_seconds] = period_in_seconds
end
def do_request
get git_lfs_url, headers: basic_auth_headers(user, token)
end
context 'when authenticated git lfs throttle is enabled' do
before do
settings_to_set[:throttle_authenticated_git_lfs_enabled] = true
end
context 'when authenticated web throttle is lower' do
before do
settings_to_set[:throttle_authenticated_web_requests_per_period] = 0
settings_to_set[:throttle_authenticated_web_period_in_seconds] = period_in_seconds
settings_to_set[:throttle_authenticated_web_enabled] = true
stub_application_setting(settings_to_set)
end
it 'ignores authenticated web throttle' do
requests_per_period.times do
do_request
expect(response).to have_gitlab_http_status(:ok)
end
expect_rejection { do_request }
end
end
end
context 'when authenticated git lfs throttle is disabled' do
before do
settings_to_set[:throttle_authenticated_git_lfs_enabled] = false
end
context 'when authenticated web throttle is enabled' do
before do
settings_to_set[:throttle_authenticated_web_requests_per_period] = requests_per_period
settings_to_set[:throttle_authenticated_web_period_in_seconds] = period_in_seconds
settings_to_set[:throttle_authenticated_web_enabled] = true
stub_application_setting(settings_to_set)
end
it 'rejects requests over the authenticated web rate limit' do
requests_per_period.times do
do_request
expect(response).to have_gitlab_http_status(:ok)
end
expect_rejection { do_request }
end
end
end
end
end
describe 'throttle bypass header' do
let(:headers) { {} }
let(:bypass_header) { 'gitlab-bypass-rate-limiting' }

View file

@ -0,0 +1,69 @@
# frozen_string_literal: true
require 'fast_spec_helper'
require_relative '../../../../rubocop/cop/migration/versioned_migration_class'
RSpec.describe RuboCop::Cop::Migration::VersionedMigrationClass do
subject(:cop) { described_class.new }
let(:migration) do
<<~SOURCE
class TestMigration < Gitlab::Database::Migration[1.0]
def up
execute 'select 1'
end
def down
execute 'select 1'
end
end
SOURCE
end
shared_examples 'a disabled cop' do
it 'does not register any offenses' do
expect_no_offenses(migration)
end
end
context 'outside of a migration' do
it_behaves_like 'a disabled cop'
end
context 'in migration' do
before do
allow(cop).to receive(:in_migration?).and_return(true)
end
context 'in an old migration' do
before do
allow(cop).to receive(:version).and_return(described_class::ENFORCED_SINCE - 5)
end
it_behaves_like 'a disabled cop'
end
context 'that is recent' do
before do
allow(cop).to receive(:version).and_return(described_class::ENFORCED_SINCE + 5)
end
it 'adds an offence if inheriting from ActiveRecord::Migration' do
expect_offense(<<~RUBY)
class MyMigration < ActiveRecord::Migration[6.1]
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Don't inherit from ActiveRecord::Migration but use Gitlab::Database::Migration[1.0] instead. See https://docs.gitlab.com/ee/development/migration_style_guide.html#migration-helpers-and-versioning.
end
RUBY
end
it 'adds an offence if including Gitlab::Database::MigrationHelpers directly' do
expect_offense(<<~RUBY)
class MyMigration < Gitlab::Database::Migration[1.0]
include Gitlab::Database::MigrationHelpers
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Don't include migration helper modules directly. Inherit from Gitlab::Database::Migration[1.0] instead. See https://docs.gitlab.com/ee/development/migration_style_guide.html#migration-helpers-and-versioning.
end
RUBY
end
end
end
end

View file

@ -388,6 +388,26 @@ RSpec.describe ApplicationSettings::UpdateService do
end
end
context 'when git lfs rate limits are passed' do
let(:params) do
{
throttle_authenticated_git_lfs_enabled: 1,
throttle_authenticated_git_lfs_period_in_seconds: 600,
throttle_authenticated_git_lfs_requests_per_period: 10
}
end
it 'updates git lfs throttle settings' do
subject.execute
application_settings.reload
expect(application_settings.throttle_authenticated_git_lfs_enabled).to be_truthy
expect(application_settings.throttle_authenticated_git_lfs_period_in_seconds).to eq(600)
expect(application_settings.throttle_authenticated_git_lfs_requests_per_period).to eq(10)
end
end
context 'when issues_create_limit is passed' do
let(:params) do
{

View file

@ -1,7 +1,7 @@
# frozen_string_literal: true
#
# Requires let variables:
# * throttle_setting_prefix: "throttle_authenticated_api", "throttle_authenticated_web", "throttle_protected_paths", "throttle_authenticated_packages_api"
# * throttle_setting_prefix: "throttle_authenticated_api", "throttle_authenticated_web", "throttle_protected_paths", "throttle_authenticated_packages_api", "throttle_authenticated_git_lfs"
# * request_method
# * request_args
# * other_user_request_args
@ -14,7 +14,8 @@ RSpec.shared_examples 'rate-limited token-authenticated requests' do
"throttle_protected_paths" => "throttle_authenticated_protected_paths_api",
"throttle_authenticated_api" => "throttle_authenticated_api",
"throttle_authenticated_web" => "throttle_authenticated_web",
"throttle_authenticated_packages_api" => "throttle_authenticated_packages_api"
"throttle_authenticated_packages_api" => "throttle_authenticated_packages_api",
"throttle_authenticated_git_lfs" => "throttle_authenticated_git_lfs"
}
end
@ -165,7 +166,7 @@ RSpec.shared_examples 'rate-limited token-authenticated requests' do
end
# Requires let variables:
# * throttle_setting_prefix: "throttle_authenticated_web" or "throttle_protected_paths"
# * throttle_setting_prefix: "throttle_authenticated_web", "throttle_protected_paths", "throttle_authenticated_git_lfs"
# * user
# * url_that_requires_authentication
# * request_method
@ -176,7 +177,8 @@ RSpec.shared_examples 'rate-limited web authenticated requests' do
let(:throttle_types) do
{
"throttle_protected_paths" => "throttle_authenticated_protected_paths_web",
"throttle_authenticated_web" => "throttle_authenticated_web"
"throttle_authenticated_web" => "throttle_authenticated_web",
"throttle_authenticated_git_lfs" => "throttle_authenticated_git_lfs"
}
end