Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
bc89c8dcb1
commit
8da0fac362
8
Gemfile
8
Gemfile
|
|
@ -277,7 +277,7 @@ gem 're2', '2.7.0' # rubocop:todo Gemfile/MissingFeatureCategory
|
|||
|
||||
# Misc
|
||||
|
||||
gem 'semver_dialects', '~> 2.0', feature_category: :static_application_security_testing
|
||||
gem 'semver_dialects', '~> 2.0', '>= 2.0.2', feature_category: :static_application_security_testing
|
||||
gem 'version_sorter', '~> 2.3' # rubocop:todo Gemfile/MissingFeatureCategory
|
||||
gem 'csv_builder', path: 'gems/csv_builder' # rubocop:todo Gemfile/MissingFeatureCategory
|
||||
|
||||
|
|
@ -418,7 +418,7 @@ group :development do
|
|||
|
||||
gem 'ruby-lsp', "~> 0.14.6", require: false, feature_category: :tooling
|
||||
|
||||
gem 'ruby-lsp-rails', "~> 0.3.0", feature_category: :tooling
|
||||
gem 'ruby-lsp-rails', "~> 0.3.3", feature_category: :tooling
|
||||
|
||||
gem 'ruby-lsp-rspec', "~> 0.1.10", require: false, feature_category: :tooling
|
||||
|
||||
|
|
@ -563,7 +563,7 @@ gem 'ssh_data', '~> 1.3' # rubocop:todo Gemfile/MissingFeatureCategory
|
|||
gem 'spamcheck', '~> 1.3.0' # rubocop:todo Gemfile/MissingFeatureCategory
|
||||
|
||||
# Gitaly GRPC protocol definitions
|
||||
gem 'gitaly', '~> 16.10.1', feature_category: :gitaly
|
||||
gem 'gitaly', '~> 16.11.0.pre.rc1', feature_category: :gitaly
|
||||
|
||||
# KAS GRPC protocol definitions
|
||||
gem 'kas-grpc', '~> 0.4.0', feature_category: :deployment_management
|
||||
|
|
@ -645,7 +645,7 @@ gem 'cvss-suite', '~> 3.0.1', require: 'cvss_suite' # rubocop:todo Gemfile/Missi
|
|||
gem 'arr-pm', '~> 0.0.12' # rubocop:todo Gemfile/MissingFeatureCategory
|
||||
|
||||
# Remote Development
|
||||
gem 'devfile', '~> 0.0.25.pre.alpha1', feature_category: :remote_development
|
||||
gem 'devfile', '~> 0.0.26.pre.alpha1', feature_category: :remote_development
|
||||
|
||||
# Apple plist parsing
|
||||
gem 'CFPropertyList', '~> 3.0.0' # rubocop:todo Gemfile/MissingFeatureCategory
|
||||
|
|
|
|||
|
|
@ -112,9 +112,9 @@
|
|||
{"name":"deprecation_toolkit","version":"1.5.1","platform":"ruby","checksum":"a8a1ab1a19ae40ea12560b65010e099f3459ebde390b76621ef0c21c516a04ba"},
|
||||
{"name":"derailed_benchmarks","version":"2.1.2","platform":"ruby","checksum":"eaadc6206ceeb5538ff8f5e04a0023d54ebdd95d04f33e8960fb95a5f189a14f"},
|
||||
{"name":"descendants_tracker","version":"0.0.4","platform":"ruby","checksum":"e9c41dd4cfbb85829a9301ea7e7c48c2a03b26f09319db230e6479ccdc780897"},
|
||||
{"name":"devfile","version":"0.0.25.pre.alpha1","platform":"arm64-darwin","checksum":"cf8d5d16ad1ff1ff9ef6f086e58fbad17bdc95dad7fc0c46af3a833c659dd10e"},
|
||||
{"name":"devfile","version":"0.0.25.pre.alpha1","platform":"ruby","checksum":"0ff071727acc6561766e115bd9aa63c4a52cb910ff64bf77129568a1c08621dc"},
|
||||
{"name":"devfile","version":"0.0.25.pre.alpha1","platform":"x86_64-linux","checksum":"b448240d1cfeb62fbe32e447d62a69efc1d099beac6f6b1778926f72dcf5672f"},
|
||||
{"name":"devfile","version":"0.0.26.pre.alpha1","platform":"arm64-darwin","checksum":"9865844fb87a616d3fa8f6bdc4b2c3fc7c8d67b7126b39afc7049ffa3dd419ba"},
|
||||
{"name":"devfile","version":"0.0.26.pre.alpha1","platform":"ruby","checksum":"2012962fa924e51b5444f22c68055a6f02c72cb129c5180876468a0361dec9e4"},
|
||||
{"name":"devfile","version":"0.0.26.pre.alpha1","platform":"x86_64-linux","checksum":"050e1a996fbae10ad6b9db00201b683bf9a11987d2869672172e7342003636ef"},
|
||||
{"name":"device_detector","version":"1.0.0","platform":"ruby","checksum":"b800fb3150b00c23e87b6768011808ac1771fffaae74c3238ebaf2b782947a7d"},
|
||||
{"name":"devise","version":"4.9.3","platform":"ruby","checksum":"480638d6c51b97f56da6e28d4f3e2a1b8e606681b316aa594b87c6ab94923488"},
|
||||
{"name":"devise-two-factor","version":"4.1.1","platform":"ruby","checksum":"c95f5b07533e62217aaed3c386874d94e2d472fb5f2b6598afe8600fc17a8b95"},
|
||||
|
|
@ -206,7 +206,7 @@
|
|||
{"name":"gettext","version":"3.4.9","platform":"ruby","checksum":"292864fe6a15c224cee4125a4a72fab426fdbb280e4cff3cfe44935f549b009a"},
|
||||
{"name":"gettext_i18n_rails","version":"1.12.0","platform":"ruby","checksum":"6ac4817731a9e2ce47e1e83381ac34f9142263bc2911aaaafb2526d2f1afc1be"},
|
||||
{"name":"git","version":"1.18.0","platform":"ruby","checksum":"c9b80462e4565cd3d7a9ba8440c41d2c52244b17b0dad0bfddb46de70630c465"},
|
||||
{"name":"gitaly","version":"16.10.1","platform":"ruby","checksum":"8f416e71d70fcea89941ca02ddd74b0938ec31e11d5d297bc99e8e3a8861df5a"},
|
||||
{"name":"gitaly","version":"16.11.0.pre.rc1","platform":"ruby","checksum":"24334f5f3fd5b6c3d278eea9fe2b6732dd08e87a4146cd4374615506b1a6e7ae"},
|
||||
{"name":"gitlab","version":"4.19.0","platform":"ruby","checksum":"3f645e3e195dbc24f0834fbf83e8ccfb2056d8e9712b01a640aad418a6949679"},
|
||||
{"name":"gitlab-chronic","version":"0.10.5","platform":"ruby","checksum":"f80f18dc699b708870a80685243331290bc10cfeedb6b99c92219722f729c875"},
|
||||
{"name":"gitlab-dangerfiles","version":"4.7.0","platform":"ruby","checksum":"2576876a8dcb7290853fc3aef8048001cfe593b87318dd0016959d42e0e145ca"},
|
||||
|
|
@ -571,7 +571,7 @@
|
|||
{"name":"rubocop-rspec","version":"2.25.0","platform":"ruby","checksum":"083f8a0481dbb9969b2a9eae85670a454fe91d46812e6ec97b34e7f6227b99f3"},
|
||||
{"name":"ruby-fogbugz","version":"0.3.0","platform":"ruby","checksum":"5e04cde474648f498a71cf1e1a7ab42c66b953862fbe224f793ec0a7a1d5f657"},
|
||||
{"name":"ruby-lsp","version":"0.14.6","platform":"ruby","checksum":"543bedcbe2f073b78285a88a46c9282ab6fd50eac2e922dcd21d7180c5534349"},
|
||||
{"name":"ruby-lsp-rails","version":"0.3.1","platform":"ruby","checksum":"81e0aff084c54933cc150d8f544752da9a64b714cb312c196196fb5b8aec730c"},
|
||||
{"name":"ruby-lsp-rails","version":"0.3.3","platform":"ruby","checksum":"03bc63aea84d6dc9a000396d2019121f646cfcd2dfefd1ec361b35bb751a2bc3"},
|
||||
{"name":"ruby-lsp-rspec","version":"0.1.10","platform":"ruby","checksum":"71b638b46b08bfda425477aff705bcc0f48d703983afd1fde50e7b43425afd5a"},
|
||||
{"name":"ruby-magic","version":"0.6.0","platform":"ruby","checksum":"7b2138877b7d23aff812c95564eba6473b74b815ef85beb0eb792e729a2b6101"},
|
||||
{"name":"ruby-openai","version":"3.7.0","platform":"ruby","checksum":"fb735d4c055e282ade264cab9864944c05a8a10e0cddd45a0551e8a9851b1850"},
|
||||
|
|
@ -591,7 +591,7 @@
|
|||
{"name":"sd_notify","version":"0.1.1","platform":"ruby","checksum":"cbc7ac6caa7cedd26b30a72b5eeb6f36050dc0752df263452ea24fb5a4ad3131"},
|
||||
{"name":"seed-fu","version":"2.3.7","platform":"ruby","checksum":"f19673443e9af799b730e3d4eca6a89b39e5a36825015dffd00d02ea3365cf74"},
|
||||
{"name":"selenium-webdriver","version":"4.19.0","platform":"ruby","checksum":"4c8bd1d6016a456154b4ba71a3bb4d532a0ae185a38acf9cec0acbd38b4e5066"},
|
||||
{"name":"semver_dialects","version":"2.0.0","platform":"ruby","checksum":"2199413894d88635f15f4ba55835af5b846db78e123ffa84a31207b44bb64587"},
|
||||
{"name":"semver_dialects","version":"2.0.2","platform":"ruby","checksum":"60059c9f416f931b5212d862fad2879d6b9affb8e0b9afb0d91b793639c116fe"},
|
||||
{"name":"sentry-rails","version":"5.10.0","platform":"ruby","checksum":"99aa2fac136c26942eb1897c65de65dac88ad43ac5eb183ff20711287a137ebd"},
|
||||
{"name":"sentry-raven","version":"3.1.2","platform":"ruby","checksum":"103d3b122958810d34898ce2e705bcf549ddb9d855a70ce9a3970ee2484f364a"},
|
||||
{"name":"sentry-ruby","version":"5.10.0","platform":"ruby","checksum":"115c24c0aee1309210f3a2988fb118e2bec1f11609feeda90e694388b1183619"},
|
||||
|
|
|
|||
16
Gemfile.lock
16
Gemfile.lock
|
|
@ -482,7 +482,7 @@ GEM
|
|||
thor (>= 0.19, < 2)
|
||||
descendants_tracker (0.0.4)
|
||||
thread_safe (~> 0.3, >= 0.3.1)
|
||||
devfile (0.0.25.pre.alpha1)
|
||||
devfile (0.0.26.pre.alpha1)
|
||||
device_detector (1.0.0)
|
||||
devise (4.9.3)
|
||||
bcrypt (~> 3.0)
|
||||
|
|
@ -687,7 +687,7 @@ GEM
|
|||
git (1.18.0)
|
||||
addressable (~> 2.8)
|
||||
rchardet (~> 1.8)
|
||||
gitaly (16.10.1)
|
||||
gitaly (16.11.0.pre.rc1)
|
||||
grpc (~> 1.0)
|
||||
gitlab (4.19.0)
|
||||
httparty (~> 0.20)
|
||||
|
|
@ -1517,7 +1517,7 @@ GEM
|
|||
language_server-protocol (~> 3.17.0)
|
||||
prism (>= 0.22.0, < 0.25)
|
||||
sorbet-runtime (>= 0.5.10782)
|
||||
ruby-lsp-rails (0.3.1)
|
||||
ruby-lsp-rails (0.3.3)
|
||||
actionpack (>= 6.0)
|
||||
activerecord (>= 6.0)
|
||||
railties (>= 6.0)
|
||||
|
|
@ -1560,7 +1560,7 @@ GEM
|
|||
rexml (~> 3.2, >= 3.2.5)
|
||||
rubyzip (>= 1.2.2, < 3.0)
|
||||
websocket (~> 1.0)
|
||||
semver_dialects (2.0.0)
|
||||
semver_dialects (2.0.2)
|
||||
deb_version (~> 1.0.1)
|
||||
pastel (~> 0.8.0)
|
||||
thor (~> 1.3)
|
||||
|
|
@ -1871,7 +1871,7 @@ DEPENDENCIES
|
|||
declarative_policy (~> 1.1.0)
|
||||
deprecation_toolkit (~> 1.5.1)
|
||||
derailed_benchmarks
|
||||
devfile (~> 0.0.25.pre.alpha1)
|
||||
devfile (~> 0.0.26.pre.alpha1)
|
||||
device_detector
|
||||
devise (~> 4.9.3)
|
||||
devise-pbkdf2-encryptable (~> 0.0.0)!
|
||||
|
|
@ -1907,7 +1907,7 @@ DEPENDENCIES
|
|||
gdk-toogle (~> 0.9)
|
||||
gettext (~> 3.4, >= 3.4.9)
|
||||
gettext_i18n_rails (~> 1.12.0)
|
||||
gitaly (~> 16.10.1)
|
||||
gitaly (~> 16.11.0.pre.rc1)
|
||||
gitlab-backup-cli!
|
||||
gitlab-chronic (~> 0.10.5)
|
||||
gitlab-dangerfiles (~> 4.7.0)
|
||||
|
|
@ -2086,7 +2086,7 @@ DEPENDENCIES
|
|||
rubocop
|
||||
ruby-fogbugz (~> 0.3.0)
|
||||
ruby-lsp (~> 0.14.6)
|
||||
ruby-lsp-rails (~> 0.3.0)
|
||||
ruby-lsp-rails (~> 0.3.3)
|
||||
ruby-lsp-rspec (~> 0.1.10)
|
||||
ruby-magic (~> 0.6)
|
||||
ruby-openai (~> 3.7)
|
||||
|
|
@ -2098,7 +2098,7 @@ DEPENDENCIES
|
|||
sd_notify (~> 0.1.0)
|
||||
seed-fu (~> 2.3.7)
|
||||
selenium-webdriver (~> 4.19)
|
||||
semver_dialects (~> 2.0)
|
||||
semver_dialects (~> 2.0, >= 2.0.2)
|
||||
sentry-rails (~> 5.10.0)
|
||||
sentry-raven (~> 3.1)
|
||||
sentry-ruby (~> 5.10.0)
|
||||
|
|
|
|||
|
|
@ -44,6 +44,12 @@ export function createProject(projectData) {
|
|||
});
|
||||
}
|
||||
|
||||
export function updateProject(projectId, data) {
|
||||
const url = buildApiUrl(PROJECT_PATH).replace(':id', projectId);
|
||||
|
||||
return axios.put(url, data);
|
||||
}
|
||||
|
||||
export function deleteProject(projectId, params) {
|
||||
const url = buildApiUrl(PROJECT_PATH).replace(':id', projectId);
|
||||
|
||||
|
|
|
|||
|
|
@ -1175,11 +1175,60 @@
|
|||
]
|
||||
},
|
||||
"exists": {
|
||||
"type": "array",
|
||||
"markdownDescription": "Additional attributes will be provided to job if any of the provided paths matches an existing file in the repository. [Learn More](https://docs.gitlab.com/ee/ci/yaml/#rulesexists).",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"paths"
|
||||
],
|
||||
"properties": {
|
||||
"paths": {
|
||||
"type": "array",
|
||||
"description": "List of file paths.",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"project": {
|
||||
"type": "string",
|
||||
"description": "Path of the project to search in."
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"paths",
|
||||
"project"
|
||||
],
|
||||
"properties": {
|
||||
"paths": {
|
||||
"type": "array",
|
||||
"description": "List of file paths.",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"project": {
|
||||
"type": "string",
|
||||
"description": "Path of the project to search in."
|
||||
},
|
||||
"ref": {
|
||||
"type": "string",
|
||||
"description": "Ref of the project to search in."
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"timeout": {
|
||||
"type": "string",
|
||||
|
|
|
|||
|
|
@ -1,23 +1,74 @@
|
|||
<script>
|
||||
import { GlSprintf } from '@gitlab/ui';
|
||||
import { __ } from '~/locale';
|
||||
import { __, s__ } from '~/locale';
|
||||
import NewEditForm from '~/projects/components/new_edit_form.vue';
|
||||
import { FORM_FIELD_NAME, FORM_FIELD_DESCRIPTION } from '~/projects/components/constants';
|
||||
import { updateProject } from '~/rest_api';
|
||||
import { visitUrlWithAlerts } from '~/lib/utils/url_utility';
|
||||
import { createAlert } from '~/alert';
|
||||
|
||||
export default {
|
||||
name: 'OrganizationProjectsEditApp',
|
||||
components: { GlSprintf, NewEditForm },
|
||||
i18n: {
|
||||
pageTitle: __('Edit project: %{project_name}'),
|
||||
errorMessage: s__('ProjectsEdit|An error occurred updating this project. Please try again.'),
|
||||
successMessage: s__('ProjectsEdit|Project was successfully updated.'),
|
||||
},
|
||||
inject: ['projectsOrganizationPath', 'previewMarkdownPath', 'project'],
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
serverValidations: {},
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
async onSubmit() {
|
||||
// Make API call. Coming in future MR
|
||||
async onSubmit({ [FORM_FIELD_NAME]: name, [FORM_FIELD_DESCRIPTION]: description }) {
|
||||
this.loading = true;
|
||||
|
||||
try {
|
||||
await updateProject(this.project.id, {
|
||||
name,
|
||||
description,
|
||||
});
|
||||
|
||||
visitUrlWithAlerts(window.location.href, [
|
||||
{
|
||||
id: 'organization-project-successfully-updated',
|
||||
message: this.$options.i18n.successMessage,
|
||||
variant: 'info',
|
||||
},
|
||||
]);
|
||||
} catch (error) {
|
||||
this.loading = false;
|
||||
|
||||
const fieldsToValidate = {
|
||||
[FORM_FIELD_NAME]: s__('ProjectsNew|Project name'),
|
||||
[FORM_FIELD_DESCRIPTION]: s__('ProjectsNewEdit|Project description'),
|
||||
};
|
||||
this.serverValidations = Object.entries(error?.response?.data?.message || {}).reduce(
|
||||
(accumulator, [fieldName, errorMessages]) => {
|
||||
const fieldLabel = fieldsToValidate[fieldName];
|
||||
|
||||
if (!fieldLabel || !errorMessages.length) {
|
||||
return accumulator;
|
||||
}
|
||||
|
||||
return {
|
||||
...accumulator,
|
||||
[fieldName]: `${fieldLabel} ${errorMessages[0]}`,
|
||||
};
|
||||
},
|
||||
{},
|
||||
);
|
||||
|
||||
if (!Object.keys(this.serverValidations).length) {
|
||||
createAlert({ message: this.$options.i18n.errorMessage, error, captureError: true });
|
||||
}
|
||||
}
|
||||
},
|
||||
onInputField({ name }) {
|
||||
this.$delete(this.serverValidations, name);
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
@ -33,9 +84,11 @@ export default {
|
|||
<new-edit-form
|
||||
:loading="loading"
|
||||
:initial-form-values="project"
|
||||
:server-validations="serverValidations"
|
||||
:preview-markdown-path="previewMarkdownPath"
|
||||
:cancel-button-href="projectsOrganizationPath"
|
||||
@submit="onSubmit"
|
||||
@input-field="onInputField"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -45,6 +45,13 @@ export default {
|
|||
};
|
||||
},
|
||||
},
|
||||
serverValidations: {
|
||||
type: Object,
|
||||
required: false,
|
||||
default() {
|
||||
return {};
|
||||
},
|
||||
},
|
||||
submitButtonText: {
|
||||
type: String,
|
||||
required: false,
|
||||
|
|
@ -128,8 +135,10 @@ export default {
|
|||
v-model="formValues"
|
||||
:form-id="$options.formId"
|
||||
:fields="fields"
|
||||
:server-validations="serverValidations"
|
||||
class="gl-display-flex gl-column-gap-5 gl-flex-wrap"
|
||||
@submit="$emit('submit', formValues)"
|
||||
@input-field="$emit('input-field', $event)"
|
||||
>
|
||||
<template #input(description)="{ id, value, input, blur }">
|
||||
<div class="gl-md-form-input-xl">
|
||||
|
|
|
|||
|
|
@ -898,17 +898,6 @@ $system-note-icon-m-left: $avatar-m-left + $icon-size-diff / $avatar-m-ratio;
|
|||
min-width: 180px;
|
||||
}
|
||||
|
||||
.note-actions-item {
|
||||
margin-left: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
&.more-actions {
|
||||
// compensate for narrow icon
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.discussion-toggle-button {
|
||||
padding: 0 $gl-padding-8 0 0;
|
||||
background-color: transparent;
|
||||
|
|
|
|||
|
|
@ -21,9 +21,9 @@
|
|||
.gl-display-flex.gl-flex-wrap.gl-mt-5.gl-gap-3{ class: button_wrapper_class }
|
||||
|
||||
- if @primary_button_text.present?
|
||||
= render Pajamas::ButtonComponent.new(variant: :confirm, href: @primary_button_link, button_options: { class: 'gl-ml-0!' }) do
|
||||
= render Pajamas::ButtonComponent.new(variant: :confirm, href: @primary_button_link, button_options: { class: 'gl-ml-0!', **@primary_button_options }) do
|
||||
= @primary_button_text
|
||||
|
||||
- if @secondary_button_text.present?
|
||||
= render Pajamas::ButtonComponent.new(variant: :default, href: @secondary_button_link, button_options: { class: ('gl-ml-0!' unless @primary_button_text.present?) }) do
|
||||
= render Pajamas::ButtonComponent.new(variant: :default, href: @secondary_button_link, button_options: { class: ('gl-ml-0!' unless @primary_button_text.present?), **@secondary_button_options }) do
|
||||
= @secondary_button_text
|
||||
|
|
|
|||
|
|
@ -9,15 +9,19 @@ module Pajamas
|
|||
# @param [String] primary_button_link
|
||||
# @param [String] secondary_button_text
|
||||
# @param [String] secondary_button_link
|
||||
# @param [Hash] primary_button_options
|
||||
# @param [Hash] secondary_button_options
|
||||
# @param [Hash] empty_state_options
|
||||
def initialize(
|
||||
compact: false,
|
||||
title: nil,
|
||||
svg_path: nil,
|
||||
primary_button_text: nil,
|
||||
primary_button_options: {},
|
||||
primary_button_link: nil,
|
||||
secondary_button_text: nil,
|
||||
secondary_button_link: nil,
|
||||
secondary_button_options: {},
|
||||
empty_state_options: {}
|
||||
)
|
||||
@compact = compact
|
||||
|
|
@ -27,6 +31,8 @@ module Pajamas
|
|||
@primary_button_link = primary_button_link
|
||||
@secondary_button_text = secondary_button_text
|
||||
@secondary_button_link = secondary_button_link
|
||||
@primary_button_options = primary_button_options
|
||||
@secondary_button_options = secondary_button_options
|
||||
@empty_state_options = empty_state_options
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -33,12 +33,8 @@ class Projects::DiscussionsController < Projects::ApplicationController
|
|||
private
|
||||
|
||||
def render_discussion
|
||||
if serialize_notes?
|
||||
prepare_notes_for_rendering(discussion.notes)
|
||||
render_json_with_discussions_serializer
|
||||
else
|
||||
render_json_with_html
|
||||
end
|
||||
prepare_notes_for_rendering(discussion.notes)
|
||||
render_json_with_discussions_serializer
|
||||
end
|
||||
|
||||
def render_json_with_discussions_serializer
|
||||
|
|
@ -47,14 +43,6 @@ class Projects::DiscussionsController < Projects::ApplicationController
|
|||
.represent(discussion, context: self, render_truncated_diff_lines: true)
|
||||
end
|
||||
|
||||
# Legacy method used to render discussions notes when not using Vue on views.
|
||||
def render_json_with_html
|
||||
render json: {
|
||||
resolved_by: discussion.resolved_by.try(:name),
|
||||
discussion_headline_html: view_to_html_string('discussions/_headline', discussion: discussion)
|
||||
}
|
||||
end
|
||||
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
def noteable
|
||||
@noteable ||= noteable_finder_class.new(current_user, project_id: @project.id).find_by!(iid: params[:noteable_id])
|
||||
|
|
|
|||
|
|
@ -207,11 +207,11 @@ module ApplicationHelper
|
|||
def edited_time_ago_with_tooltip(editable_object, placement: 'top', html_class: 'time_ago', exclude_author: false)
|
||||
return unless editable_object.edited?
|
||||
|
||||
content_tag :small, class: 'edited-text' do
|
||||
content_tag :div, class: 'edited-text gl-mt-4 gl-text-gray-500 gl-font-sm' do
|
||||
timeago = time_ago_with_tooltip(editable_object.last_edited_at, placement: placement, html_class: html_class)
|
||||
|
||||
if !exclude_author && editable_object.last_edited_by
|
||||
author_link = link_to_member(editable_object.project, editable_object.last_edited_by, avatar: false, extra_class: 'gl-hover-text-decoration-underline', author_class: nil)
|
||||
author_link = link_to_member(editable_object.project, editable_object.last_edited_by, avatar: false, extra_class: 'gl-hover-text-decoration-underline gl-text-gray-700', author_class: nil)
|
||||
output = safe_format(_("Edited %{timeago} by %{author}"), timeago: timeago, author: author_link)
|
||||
else
|
||||
output = safe_format(_("Edited %{timeago}"), timeago: timeago)
|
||||
|
|
|
|||
|
|
@ -197,10 +197,6 @@ module NotesHelper
|
|||
data
|
||||
end
|
||||
|
||||
def discussion_resolved_intro(discussion)
|
||||
discussion.resolved_by_push? ? 'Automatically resolved' : 'Resolved'
|
||||
end
|
||||
|
||||
def rendered_for_merge_request?
|
||||
params[:from_merge_request].present?
|
||||
end
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ module Ci
|
|||
validates :version, :catalog_resource, :project, :name, presence: true
|
||||
|
||||
def include_path
|
||||
"#{Gitlab.config.gitlab_ci.server_fqdn}/#{project.full_path}/#{name}@#{version.version}"
|
||||
"#{Gitlab.config.gitlab_ci.server_fqdn}/#{project.full_path}/#{name}@#{version.name}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -9,9 +9,6 @@ module Ci
|
|||
include BulkInsertableAssociations
|
||||
include SemanticVersionable
|
||||
|
||||
semver_method :version
|
||||
validate_semver
|
||||
|
||||
self.table_name = 'catalog_resource_versions'
|
||||
|
||||
belongs_to :release, inverse_of: :catalog_resource_version
|
||||
|
|
@ -53,7 +50,7 @@ module Ci
|
|||
end
|
||||
|
||||
def name
|
||||
release.tag
|
||||
semver.to_s
|
||||
end
|
||||
|
||||
def commit
|
||||
|
|
|
|||
|
|
@ -42,6 +42,8 @@ class Commit
|
|||
MAX_DIFF_FILES_SETTING_UPPER_BOUND = 3_000
|
||||
DIFF_SAFE_LIMIT_FACTOR = 10
|
||||
|
||||
CO_AUTHORED_TRAILER = "Co-authored-by"
|
||||
|
||||
cache_markdown_field :title, pipeline: :single_line
|
||||
cache_markdown_field :full_title, pipeline: :single_line, limit: 1.kilobyte
|
||||
cache_markdown_field :description, pipeline: :commit_description, limit: 1.megabyte
|
||||
|
|
|
|||
|
|
@ -156,8 +156,22 @@ class CommitCollection
|
|||
private
|
||||
|
||||
def committers_emails(with_merge_commits: false)
|
||||
return commits.filter_map(&:committer_email).uniq if with_merge_commits
|
||||
return committer_emails_for(commits) if with_merge_commits
|
||||
|
||||
without_merge_commits.filter_map(&:committer_email).uniq
|
||||
committer_emails_for(without_merge_commits)
|
||||
end
|
||||
|
||||
def committer_emails_for(commits)
|
||||
if ::Feature.enabled?(:web_ui_commit_author_change, project)
|
||||
commits.each(&:signature) # preload signatures
|
||||
end
|
||||
|
||||
commits.filter_map do |commit|
|
||||
if ::Feature.enabled?(:web_ui_commit_author_change, project) && commit.signature&.verified_system?
|
||||
commit.author_email
|
||||
else
|
||||
commit.committer_email
|
||||
end
|
||||
end.uniq
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -4,50 +4,32 @@ module SemanticVersionable
|
|||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
self.require_valid_semver = false
|
||||
|
||||
validate :semver_format, if: :require_valid_semver?
|
||||
validates :semver,
|
||||
format: { with: ::Gitlab::Regex::SemVer.optional_prefixed, message: 'must follow semantic version' }
|
||||
|
||||
scope :order_by_semantic_version_desc, -> { order(semver_major: :desc, semver_minor: :desc, semver_patch: :desc) }
|
||||
scope :order_by_semantic_version_asc, -> { order(semver_major: :asc, semver_minor: :asc, semver_patch: :asc) }
|
||||
|
||||
private
|
||||
def semver
|
||||
return if [semver_major, semver_minor, semver_patch].any?(&:nil?)
|
||||
|
||||
def semver_format
|
||||
return unless [semver_major, semver_minor, semver_patch].any?(&:nil?)
|
||||
prefixed = respond_to?(:semver_prefixed) && semver_prefixed
|
||||
|
||||
errors.add(:base, _('must follow semantic version'))
|
||||
Packages::SemVer.new(semver_major, semver_minor, semver_patch, semver_prerelease, prefixed: prefixed)
|
||||
end
|
||||
|
||||
def require_valid_semver?
|
||||
self.class.require_valid_semver
|
||||
end
|
||||
end
|
||||
def semver=(version)
|
||||
prefixed = version.start_with?('v')
|
||||
|
||||
class_methods do
|
||||
attr_accessor :require_valid_semver
|
||||
parsed_version = Packages::SemVer.parse(version, prefixed: prefixed)
|
||||
|
||||
def semver_method(name)
|
||||
define_method(name) do
|
||||
return if [semver_major, semver_minor, semver_patch].any?(&:nil?)
|
||||
return if parsed_version.nil?
|
||||
|
||||
Packages::SemVer.new(semver_major, semver_minor, semver_patch, semver_prerelease)
|
||||
end
|
||||
|
||||
define_method("#{name}=") do |version|
|
||||
parsed = Packages::SemVer.parse(version)
|
||||
|
||||
return if parsed.nil?
|
||||
|
||||
self.semver_major = parsed.major
|
||||
self.semver_minor = parsed.minor
|
||||
self.semver_patch = parsed.patch
|
||||
self.semver_prerelease = parsed.prerelease
|
||||
end
|
||||
end
|
||||
|
||||
def validate_semver
|
||||
self.require_valid_semver = true
|
||||
self.semver_major = parsed_version.major
|
||||
self.semver_minor = parsed_version.minor
|
||||
self.semver_patch = parsed_version.patch
|
||||
self.semver_prerelease = parsed_version.prerelease
|
||||
self.semver_prefixed = prefixed if respond_to?(:semver_prefixed)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -6,9 +6,6 @@ module Ml
|
|||
include Sortable
|
||||
include SemanticVersionable
|
||||
|
||||
semver_method :semver
|
||||
validate_semver
|
||||
|
||||
validates :project, :model, presence: true
|
||||
|
||||
validates :version,
|
||||
|
|
|
|||
|
|
@ -3,6 +3,9 @@
|
|||
class Packages::SemVer
|
||||
attr_accessor :major, :minor, :patch, :prerelease, :build
|
||||
|
||||
# TODO: Move logic into the SemanticVersionable concern
|
||||
# https://gitlab.com/gitlab-org/gitlab/-/issues/455670
|
||||
|
||||
def initialize(major = 0, minor = 0, patch = 0, prerelease = nil, build = nil, prefixed: false)
|
||||
@major = major
|
||||
@minor = minor
|
||||
|
|
|
|||
|
|
@ -956,7 +956,8 @@ class Repository
|
|||
|
||||
def cherry_pick(
|
||||
user, commit, branch_name, message,
|
||||
start_branch_name: nil, start_project: project, dry_run: false)
|
||||
start_branch_name: nil, start_project: project,
|
||||
author_name: nil, author_email: nil, dry_run: false)
|
||||
|
||||
with_cache_hooks do
|
||||
raw_repository.cherry_pick(
|
||||
|
|
@ -966,6 +967,8 @@ class Repository
|
|||
message: message,
|
||||
start_branch_name: start_branch_name,
|
||||
start_repository: start_project.repository.raw_repository,
|
||||
author_name: author_name,
|
||||
author_email: author_email,
|
||||
dry_run: dry_run
|
||||
)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ module Ci
|
|||
release: release,
|
||||
catalog_resource: project.catalog_resource,
|
||||
project: project,
|
||||
version: release.tag
|
||||
semver: release.tag
|
||||
)
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -9,25 +9,18 @@ module Commits
|
|||
@message = params[:message]
|
||||
end
|
||||
|
||||
def commit_message
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
attr_reader :commit
|
||||
|
||||
def commit_change(action)
|
||||
raise NotImplementedError unless repository.respond_to?(action)
|
||||
message = @message || commit_message
|
||||
|
||||
# rubocop:disable GitlabSecurity/PublicSend
|
||||
message =
|
||||
@message || @commit.public_send(:"#{action}_message", current_user)
|
||||
|
||||
repository.public_send(
|
||||
action,
|
||||
current_user,
|
||||
@commit,
|
||||
@branch_name,
|
||||
message,
|
||||
start_project: @start_project,
|
||||
start_branch_name: @start_branch,
|
||||
dry_run: @dry_run
|
||||
)
|
||||
yield message
|
||||
rescue Gitlab::Git::Repository::CreateTreeError => ex
|
||||
type = @commit.change_type_title(current_user)
|
||||
|
||||
|
|
|
|||
|
|
@ -11,14 +11,28 @@ module Commits
|
|||
|
||||
def create_commit!
|
||||
Gitlab::Git::CrossRepo.new(@project.repository, @source_project.repository).execute(@commit.id) do
|
||||
commit_change(:cherry_pick).tap do |sha|
|
||||
track_mr_picking(sha)
|
||||
commit_sha = commit_change(:cherry_pick) do |message|
|
||||
perform_cherry_pick(message)
|
||||
end
|
||||
|
||||
track_mr_picking(commit_sha)
|
||||
|
||||
commit_sha
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def commit_message
|
||||
message = commit.cherry_pick_message(current_user)
|
||||
|
||||
return message unless ::Feature.enabled?(:web_ui_commit_author_change, project)
|
||||
|
||||
co_authored_trailer = "#{Commit::CO_AUTHORED_TRAILER}: #{commit.author_name} <#{commit.author_email}>"
|
||||
|
||||
"#{message}\n\n#{co_authored_trailer}"
|
||||
end
|
||||
|
||||
def track_mr_picking(pick_sha)
|
||||
merge_request = project.merge_requests.by_merge_commit_sha(@commit.sha).first
|
||||
return unless merge_request
|
||||
|
|
@ -29,5 +43,19 @@ module Commits
|
|||
author: current_user
|
||||
).picked_into_branch(@branch_name, pick_sha)
|
||||
end
|
||||
|
||||
def perform_cherry_pick(message)
|
||||
author_kwargs =
|
||||
if Feature.enabled?(:web_ui_commit_author_change, project)
|
||||
{ author_name: current_user.name, author_email: current_user.email }
|
||||
else
|
||||
{}
|
||||
end
|
||||
|
||||
repository.cherry_pick(current_user, @commit, @branch_name, message,
|
||||
start_project: @start_project, start_branch_name: @start_branch, dry_run: @dry_run,
|
||||
**author_kwargs
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -3,7 +3,17 @@
|
|||
module Commits
|
||||
class RevertService < ChangeService
|
||||
def create_commit!
|
||||
commit_change(:revert)
|
||||
commit_change(:revert) do |message|
|
||||
repository.revert(current_user, @commit, @branch_name, message,
|
||||
start_project: @start_project, start_branch_name: @start_branch, dry_run: @dry_run
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def commit_message
|
||||
commit.revert_message(current_user)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -54,6 +54,13 @@ module Suggestions
|
|||
author_email: author&.email
|
||||
}
|
||||
|
||||
if ::Feature.enabled?(:web_ui_commit_author_change, project)
|
||||
params.merge!({
|
||||
author_name: current_user.name,
|
||||
author_email: current_user.commit_email_or_default
|
||||
})
|
||||
end
|
||||
|
||||
::Files::MultiService.new(suggestion_set.source_project, current_user, params)
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
= render Pajamas::EmptyStateComponent.new(svg_path: 'illustrations/empty-state/empty-admin-apps-md.svg',
|
||||
title: s_('AdminArea|No applications found'),
|
||||
primary_button_text: _('Add new application'),
|
||||
primary_button_options: { data: {testid: 'new-application-button'} },
|
||||
primary_button_link: new_admin_application_path) do |c|
|
||||
|
||||
- c.with_description do
|
||||
|
|
|
|||
|
|
@ -3,52 +3,9 @@
|
|||
.timeline-entry-inner
|
||||
.timeline-content
|
||||
.discussion.js-toggle-container{ data: { discussion_id: discussion.id, is_expanded: expanded.to_s } }
|
||||
.discussion-header.gl-display-flex.gl--flex-center
|
||||
.timeline-icon.gl-flex-shrink-0
|
||||
= link_to user_path(discussion.author) do
|
||||
= render Pajamas::AvatarComponent.new(discussion.author, size: 32, class: 'gl-mr-3')
|
||||
= link_to_member(@project, discussion.author, avatar: false)
|
||||
|
||||
.inline.discussion-headline-light.gl-mx-3
|
||||
= discussion.author.to_reference
|
||||
started a thread
|
||||
|
||||
- url = discussion_path(discussion)
|
||||
- if discussion.for_commit? && @noteable != discussion.noteable
|
||||
on
|
||||
- commit = discussion.noteable
|
||||
- if commit
|
||||
commit
|
||||
= link_to commit.short_id, url, class: 'commit-sha'
|
||||
- else
|
||||
a deleted commit
|
||||
- elsif discussion.diff_discussion?
|
||||
on
|
||||
= conditional_link_to url.present?, url do
|
||||
- if discussion.on_merge_request_commit?
|
||||
- unless discussion.active?
|
||||
an outdated change in
|
||||
commit
|
||||
|
||||
%span.commit-sha= truncate_sha(discussion.commit_id)
|
||||
- else
|
||||
- unless discussion.active?
|
||||
an old version of
|
||||
the diff
|
||||
|
||||
= time_ago_with_tooltip(discussion.created_at, placement: "bottom", html_class: "note-created-ago")
|
||||
= render "discussions/headline", discussion: discussion
|
||||
|
||||
.discussion-actions.gl-ml-auto
|
||||
%button.note-action-button.discussion-toggle-button.js-toggle-button{ type: "button", class: ("js-toggle-lazy-diff" unless expanded) }
|
||||
= sprite_icon('chevron-up', css_class: "js-sidebar-collapse #{'hidden' unless expanded}")
|
||||
= sprite_icon('chevron-down', css_class: "js-sidebar-expand #{'hidden' if expanded}")
|
||||
%span.js-sidebar-collapse{ class: "#{'hidden' unless expanded}" }= _('Hide thread')
|
||||
%span.js-sidebar-expand{ class: "#{'hidden' if expanded}" }= _('Show thread')
|
||||
|
||||
.discussion-body.js-toggle-content{ class: ("hide" unless expanded) }
|
||||
- if discussion.diff_discussion? && discussion.diff_file
|
||||
= render "discussions/diff_with_notes", discussion: discussion
|
||||
- else
|
||||
.card
|
||||
.card.discussion-wrapper
|
||||
= render partial: "discussions/notes", locals: { discussion: discussion, disable_collapse_class: true }
|
||||
|
|
|
|||
|
|
@ -1,16 +0,0 @@
|
|||
- if discussion.resolved?
|
||||
.discussion-headline-light.js-discussion-headline
|
||||
= discussion_resolved_intro(discussion)
|
||||
- if discussion.resolved_by
|
||||
by
|
||||
= link_to_member(@project, discussion.resolved_by, avatar: false)
|
||||
- if discussion.resolved_by_push?
|
||||
with a push
|
||||
= time_ago_with_tooltip(discussion.resolved_at, placement: "bottom")
|
||||
- elsif discussion.updated?
|
||||
.discussion-headline-light.js-discussion-headline
|
||||
Last updated
|
||||
- if discussion.last_updated_by
|
||||
by
|
||||
= link_to_member(@project, discussion.last_updated_by, avatar: false)
|
||||
= time_ago_with_tooltip(discussion.last_updated_at, placement: "bottom")
|
||||
|
|
@ -1,24 +1,24 @@
|
|||
- access = note_human_max_access(note)
|
||||
- if note.noteable_author?(@noteable)
|
||||
= render Pajamas::BadgeComponent.new(_("Author"), variant: 'neutral', class: 'gl-bg-transparent! gl-inset-border-1-gray-100! note-role user-access-role has-tooltip gl-display-none gl-md-display-inline-block', title: _("This user is the author of this %{noteable}.") % { noteable: @noteable.human_class_name })
|
||||
= render Pajamas::BadgeComponent.new(_("Author"), variant: 'neutral', class: 'has-tooltip gl-bg-transparent! gl-inset-border-1-gray-100! gl-display-none gl-md-display-inline-block gl-mr-3', title: _("This user is the author of this %{noteable}.") % { noteable: @noteable.human_class_name })
|
||||
- if access
|
||||
= render Pajamas::BadgeComponent.new(access, variant: 'neutral', class: 'gl-bg-transparent! gl-inset-border-1-gray-100! note-role user-access-role has-tooltip', title: _("This user has the %{access} role in the %{name} project.") % { access: access.downcase, name: note.project_name })
|
||||
= render Pajamas::BadgeComponent.new(access, variant: 'neutral', class: 'has-tooltip gl-bg-transparent! gl-inset-border-1-gray-100! gl-display-none gl-md-display-inline-block gl-mr-3', title: _("This user has the %{access} role in the %{name} project.") % { access: access.downcase, name: note.project_name })
|
||||
- elsif note.contributor?
|
||||
= render Pajamas::BadgeComponent.new(_("Contributor"), variant: 'neutral', class: 'gl-bg-transparent! gl-inset-border-1-gray-100! note-role user-access-role has-tooltip', title: _("This user has previously committed to the %{name} project.") % { name: note.project_name })
|
||||
= render Pajamas::BadgeComponent.new(_("Contributor"), variant: 'neutral', class: 'has-tooltip gl-bg-transparent! gl-inset-border-1-gray-100! gl-display-none gl-md-display-inline-block gl-mr-3', title: _("This user has previously committed to the %{name} project.") % { name: note.project_name })
|
||||
|
||||
- if can?(current_user, :award_emoji, note)
|
||||
- if note.emoji_awardable?
|
||||
.note-actions-item
|
||||
= render Pajamas::ButtonComponent.new(category: :tertiary,
|
||||
button_options: { title: _('Add reaction'), class: 'btn-icon add-reaction-button note-action-button note-emoji-button js-add-award js-note-emoji has-tooltip', data: { position: 'right', container: 'body' }, 'aria-label': _('Add reaction') }) do
|
||||
= sprite_icon('slight-smile', css_class: 'reaction-control-icon-neutral award-control-icon-neutral gl-button-icon gl-icon')
|
||||
= sprite_icon('smiley', css_class: 'reaction-control-icon-positive award-control-icon-positive gl-button-icon gl-icon !gl-left-3')
|
||||
= sprite_icon('smile', css_class: 'reaction-control-icon-super-positive award-control-icon-super-positive gl-button-icon gl-icon !gl-left-3 ')
|
||||
= render Pajamas::ButtonComponent.new(category: :tertiary,
|
||||
button_options: { title: _('Add reaction'), class: 'btn-icon add-reaction-button note-action-button note-emoji-button js-add-award js-note-emoji has-tooltip', data: { position: 'right', container: 'body' }, 'aria-label': _('Add reaction') }) do
|
||||
= sprite_icon('slight-smile', css_class: 'reaction-control-icon-neutral award-control-icon-neutral gl-button-icon gl-icon')
|
||||
= sprite_icon('smiley', css_class: 'reaction-control-icon-positive award-control-icon-positive gl-button-icon gl-icon !gl-left-3')
|
||||
= sprite_icon('smile', css_class: 'reaction-control-icon-super-positive award-control-icon-super-positive gl-button-icon gl-icon !gl-left-3 ')
|
||||
|
||||
- if note_editable
|
||||
.note-actions-item.gl-ml-0
|
||||
.gl-ml-0
|
||||
= render Pajamas::ButtonComponent.new(category: :tertiary,
|
||||
icon: 'pencil',
|
||||
button_options: { class: 'note-action-button js-note-edit has-tooltip', data: { container: 'body', testid: 'edit-comment-button' }, title: _('Edit comment'), 'aria-label': _('Edit comment') })
|
||||
button_options: { class: 'gl-display-none gl-sm-display-inline-block note-action-button js-note-edit has-tooltip', data: { testid: 'edit-comment-button' }, title: _('Edit comment'), 'aria-label': _('Edit comment') })
|
||||
|
||||
= render 'projects/notes/more_actions_dropdown', note: note, note_editable: note_editable
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,13 @@
|
|||
- is_current_user = current_user == note.author
|
||||
|
||||
- if note_editable || !is_current_user
|
||||
%div{ class: "dropdown more-actions note-actions-item gl-ml-0!" }
|
||||
= render Pajamas::ButtonComponent.new(icon: 'ellipsis_v', category: :tertiary, button_options: { class: 'note-action-button more-actions-toggle has-tooltip', data: { title: 'More actions', toggle: 'dropdown', container: 'body', testid: 'more-actions-dropdown' }})
|
||||
.dropdown{ class: "more-actions gl-ml-0!" }
|
||||
= render Pajamas::ButtonComponent.new(icon: 'ellipsis_v', category: :tertiary, button_options: { class: 'note-action-button more-actions-toggle', data: { title: 'More actions', toggle: 'dropdown', container: 'body', testid: 'more-actions-dropdown' }})
|
||||
%ul.dropdown-menu.more-actions-dropdown.dropdown-open-left
|
||||
- if note_editable
|
||||
%li{ class: "gl-sm-display-none!" }
|
||||
= render Pajamas::ButtonComponent.new(category: :tertiary, button_options: { class: 'menu-item note-action-button js-note-edit', data: { container: 'body', testid: 'edit-comment-button' } }) do
|
||||
= _("Edit comment")
|
||||
%li
|
||||
= clipboard_button(text: noteable_note_url(note), title: _('Copy reference'), button_text: _('Copy link'), class: 'gl-rounded-0!', size: :medium, hide_tooltip: true, hide_button_icon: true)
|
||||
- unless is_current_user
|
||||
|
|
|
|||
|
|
@ -32,17 +32,17 @@
|
|||
.note-header
|
||||
.note-header-info
|
||||
%a{ href: user_path(note.author) }
|
||||
%span.note-header-author-name.bold
|
||||
%span.note-header-author-name.gl-font-weight-bold
|
||||
= note.author.name
|
||||
= user_status(note.author)
|
||||
%spannote-headline-light{ data: { testid: 'note-author-content' } }
|
||||
= note.author.to_reference
|
||||
%span.note-headline-ligh.note-headline-meta
|
||||
%span.note-headline-light{ data: { testid: 'note-author-content' } }
|
||||
= note.author.to_reference
|
||||
%span.note-headline-light.note-headline-meta
|
||||
- if note.system
|
||||
%span.system-note-message
|
||||
= markdown_field(note, :note)
|
||||
- if note.created_at
|
||||
%span.system-note-separator
|
||||
%span.system-note-separator.gl-display-none.gl-sm-display-inline
|
||||
·
|
||||
%a.system-note-separator{ href: "##{dom_id(note)}" }= time_ago_with_tooltip(note.created_at, placement: 'bottom', html_class: 'note-created-ago')
|
||||
- unless note.system?
|
||||
|
|
@ -51,30 +51,31 @@
|
|||
= render 'snippets/notes/actions', note: note, note_editable: note_editable
|
||||
- else
|
||||
= render 'projects/notes/actions', note: note, note_editable: note_editable
|
||||
.note-body{ class: note_editable ? 'js-task-list-container' : '' }
|
||||
.note-text.md{ data: { testid: 'note-content' } }
|
||||
= markdown_field(note, :note)
|
||||
= edited_time_ago_with_tooltip(note, placement: 'bottom', html_class: 'note_edited_ago')
|
||||
.original-note-content.hidden{ data: { post_url: note_url(note), target_id: note.noteable.id, target_type: note.noteable.class.name.underscore } }
|
||||
#{note.note}
|
||||
- if note_editable
|
||||
= render 'shared/notes/edit', note: note
|
||||
.note-awards
|
||||
= render 'award_emoji/awards_block', awardable: note, inline: false
|
||||
- if note.system
|
||||
.system-note-commit-list-toggler.hide
|
||||
= _("Toggle commit list")
|
||||
= sprite_icon('chevron-down', css_class: 'js-chevron-down gl-ml-1 gl-vertical-align-text-bottom')
|
||||
= sprite_icon('chevron-up', css_class: 'js-chevron-up gl-ml-1 gl-vertical-align-text-bottom gl-display-none')
|
||||
- if note.attachment.url
|
||||
.note-attachment
|
||||
- if note.attachment.image?
|
||||
= link_to note.attachment.url, target: '_blank' do
|
||||
= image_tag note.attachment.url, class: 'note-image-attach col-lg-4'
|
||||
.attachment
|
||||
= link_to note.attachment.url, target: '_blank' do
|
||||
= sprite_icon('paperclip')
|
||||
= note.attachment_identifier
|
||||
= link_to delete_attachment_project_note_path(note.project, note),
|
||||
title: _('Delete this attachment'), method: :delete, remote: true, data: { confirm: _('Are you sure you want to remove the attachment?') }, class: 'danger js-note-attachment-delete' do
|
||||
= sprite_icon('remove', css_class: 'cred')
|
||||
.timeline-discussion-body
|
||||
.note-body{ class: note_editable ? 'js-task-list-container' : '' }
|
||||
.note-text.md{ data: { testid: 'note-content' } }
|
||||
= markdown_field(note, :note)
|
||||
= edited_time_ago_with_tooltip(note, placement: 'bottom', html_class: 'note_edited_ago')
|
||||
.original-note-content.hidden{ data: { post_url: note_url(note), target_id: note.noteable.id, target_type: note.noteable.class.name.underscore } }
|
||||
#{note.note}
|
||||
- if note_editable
|
||||
= render 'shared/notes/edit', note: note
|
||||
.note-awards
|
||||
= render 'award_emoji/awards_block', awardable: note, inline: false
|
||||
- if note.system
|
||||
.system-note-commit-list-toggler.hide
|
||||
= _("Toggle commit list")
|
||||
= sprite_icon('chevron-down', css_class: 'js-chevron-down gl-ml-1 gl-vertical-align-text-bottom')
|
||||
= sprite_icon('chevron-up', css_class: 'js-chevron-up gl-ml-1 gl-vertical-align-text-bottom gl-display-none')
|
||||
- if note.attachment.url
|
||||
.note-attachment
|
||||
- if note.attachment.image?
|
||||
= link_to note.attachment.url, target: '_blank' do
|
||||
= image_tag note.attachment.url, class: 'note-image-attach col-lg-4'
|
||||
.attachment
|
||||
= link_to note.attachment.url, target: '_blank' do
|
||||
= sprite_icon('paperclip')
|
||||
= note.attachment_identifier
|
||||
= link_to delete_attachment_project_note_path(note.project, note),
|
||||
title: _('Delete this attachment'), method: :delete, remote: true, data: { confirm: _('Are you sure you want to remove the attachment?') }, class: 'danger js-note-attachment-delete' do
|
||||
= sprite_icon('remove', css_class: 'cred')
|
||||
|
|
|
|||
|
|
@ -1,13 +1,12 @@
|
|||
- if current_user
|
||||
- if note.emoji_awardable?
|
||||
.note-actions-item
|
||||
= render Pajamas::ButtonComponent.new(category: :tertiary, button_options: { title: _('Add reaction'), class: 'btn-icon add-reaction-button note-action-button note-emoji-button js-add-award js-note-emoji has-tooltip' }) do
|
||||
= sprite_icon('slight-smile', css_class: 'reaction-control-icon-neutral award-control-icon-neutral gl-button-icon gl-icon')
|
||||
= sprite_icon('smiley', css_class: 'reaction-control-icon-positive award-control-icon-positive gl-button-icon gl-icon !gl-left-3')
|
||||
= sprite_icon('smile', css_class: 'reaction-control-icon-super-positive award-control-icon-super-positive gl-button-icon gl-icon !gl-left-3 ')
|
||||
= render Pajamas::ButtonComponent.new(category: :tertiary, button_options: { title: _('Add reaction'), class: 'btn-icon add-reaction-button note-action-button note-emoji-button js-add-award js-note-emoji has-tooltip' }) do
|
||||
= sprite_icon('slight-smile', css_class: 'reaction-control-icon-neutral award-control-icon-neutral gl-button-icon gl-icon')
|
||||
= sprite_icon('smiley', css_class: 'reaction-control-icon-positive award-control-icon-positive gl-button-icon gl-icon !gl-left-3')
|
||||
= sprite_icon('smile', css_class: 'reaction-control-icon-super-positive award-control-icon-super-positive gl-button-icon gl-icon !gl-left-3 ')
|
||||
|
||||
- if note_editable
|
||||
.note-actions-item.gl-ml-0
|
||||
= render Pajamas::ButtonComponent.new(category: :tertiary, icon: 'pencil', button_options: { title: _('Edit comment'), class: 'note-action-button js-note-edit has-tooltip', data: { container: 'body', testid: 'edit-comment-button' } })
|
||||
.gl-ml-0
|
||||
= render Pajamas::ButtonComponent.new(category: :tertiary, icon: 'pencil', button_options: { title: _('Edit comment'), class: 'note-action-button js-note-edit has-tooltip gl-display-none gl-sm-display-block', data: { container: 'body', testid: 'edit-comment-button' } })
|
||||
|
||||
= render 'projects/notes/more_actions_dropdown', note: note, note_editable: note_editable
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ module ClickHouse
|
|||
end
|
||||
|
||||
def enabled?
|
||||
Gitlab::ClickHouse.globally_enabled_for_analytics? && Feature.enabled?(:event_sync_worker_for_click_house)
|
||||
Gitlab::ClickHouse.globally_enabled_for_analytics?
|
||||
end
|
||||
|
||||
def runtime_limiter
|
||||
|
|
|
|||
|
|
@ -1,8 +0,0 @@
|
|||
---
|
||||
name: event_sync_worker_for_click_house
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/128628
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/421184
|
||||
milestone: '16.3'
|
||||
type: development
|
||||
group: group::optimize
|
||||
default_enabled: false
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
---
|
||||
name: ci_support_rules_exists_paths_and_project
|
||||
feature_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/386040
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/149326
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/453983
|
||||
milestone: '16.11'
|
||||
group: group::pipeline authoring
|
||||
type: gitlab_com_derisk
|
||||
default_enabled: false
|
||||
|
|
@ -2,8 +2,8 @@
|
|||
name: jira_multiple_project_keys
|
||||
feature_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/440430
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/146087
|
||||
rollout_issue_url:
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/455259
|
||||
milestone: '16.10'
|
||||
group: group::import and integrate
|
||||
type: wip
|
||||
type: gitlab_com_derisk
|
||||
default_enabled: false
|
||||
|
|
@ -1,8 +1,9 @@
|
|||
---
|
||||
name: clickhouse_data_collection
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/127435
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/420257
|
||||
milestone: '16.3'
|
||||
type: development
|
||||
group: group::optimize
|
||||
name: web_ui_commit_author_change
|
||||
feature_issue_url:
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/148889
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/455288
|
||||
milestone: '16.11'
|
||||
group: group::source code
|
||||
type: gitlab_com_derisk
|
||||
default_enabled: false
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
---
|
||||
table_name: external_status_checks_protected_branches
|
||||
classes: []
|
||||
classes:
|
||||
- MergeRequests::ExternalStatusChecksProtectedBranch
|
||||
feature_categories:
|
||||
- compliance_management
|
||||
description: Keeps relation between protected branches and external status checks
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ DETAILS:
|
|||
**Offering:** GitLab.com, Self-managed
|
||||
|
||||
> - ClickHouse data collector [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/414610) in GitLab 16.3 [with a flag](../administration/feature_flags.md) named `clickhouse_data_collection`. Disabled by default.
|
||||
> - Feature flag `clickhouse_data_collection` removed in GitLab 17.0 and replaced with an application setting.
|
||||
|
||||
The [contribution analytics](../user/group/contribution_analytics/index.md) report and [Value Streams Dashboard](../user/analytics/value_streams_dashboard.md#dashboard-metrics-and-drill-down-reports) contributors count metric can use ClickHouse as a data source.
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ This document describes how to:
|
|||
|
||||
This document is intended for environments using:
|
||||
|
||||
- [Linux package (Omnibus) and cloud-native hybrid reference architectures 3,000 users and up](../reference_architectures/index.md)
|
||||
- [Linux package (Omnibus) and cloud-native hybrid reference architectures 60 RPS / 3,000 users and up](../reference_architectures/index.md)
|
||||
- [Amazon RDS](https://aws.amazon.com/rds/) for PostgreSQL data
|
||||
- [Amazon S3](https://aws.amazon.com/s3/) for object storage
|
||||
- [Object storage](../object_storage.md) to store everything possible, including [blobs](backup_gitlab.md#blobs) and [container registry](backup_gitlab.md#container-registry)
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ DETAILS:
|
|||
| Secondaries | One |
|
||||
|
||||
This runbook guides you through a planned failover of a multi-node Geo site
|
||||
with one secondary. The following [2000 user reference architecture](../../../../administration/reference_architectures/2k_users.md) is assumed:
|
||||
with one secondary. The following [40 RPS / 2,000 user reference architecture](../../../../administration/reference_architectures/2k_users.md) is assumed:
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ these definitions yet.
|
|||
| Primary site | A GitLab site whose data is being replicated by at least one secondary site. There can only be a single primary site. | Geo-specific | Geo deployment, Primary node |
|
||||
| Secondary site | A GitLab site that is configured to replicate the data of a primary site. There can be one or more secondary sites. | Geo-specific | Geo deployment, Secondary node |
|
||||
| Geo deployment | A collection of two or more GitLab sites with exactly one primary site being replicated by one or more secondary sites. | Geo-specific | |
|
||||
| Reference architecture | A [specified configuration of GitLab for a number of users](../reference_architectures/index.md), possibly including multiple nodes and multiple sites. | GitLab | |
|
||||
| Reference architecture | A [specified configuration of GitLab based on Requests per Second or user count](../reference_architectures/index.md), possibly including multiple nodes and multiple sites. | GitLab | |
|
||||
| Promoting | Changing the role of a site from secondary to primary. | Geo-specific | |
|
||||
| Demoting | Changing the role of a site from primary to secondary. | Geo-specific | |
|
||||
| Failover | The entire process that shifts users from a primary Site to a secondary site. This includes promoting a secondary, but contains other parts as well. For example, scheduling maintenance. | Geo-specific | |
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ Depending on your GitLab deployment, [additional configuration](#additional-conf
|
|||
|
||||
### Multi-node Geo sites
|
||||
|
||||
If one or more of your sites is using the [2K reference architecture](../../reference_architectures/2k_users.md) or larger, see
|
||||
If one or more of your sites is using the [40 RPS / 2,000 user reference architecture](../../reference_architectures/2k_users.md) or larger, see
|
||||
[Configure Geo for multiple nodes](../replication/multiple_servers.md).
|
||||
|
||||
Depending on your GitLab deployment, [additional configuration](#additional-configuration) for LDAP, object storage, and the container registry might be required.
|
||||
|
|
|
|||
|
|
@ -167,9 +167,9 @@ E --> F
|
|||
### Configure Gitaly
|
||||
|
||||
Gitaly comes pre-configured with a Linux package installation, which is a configuration
|
||||
[suitable for up to 1000 users](../reference_architectures/1k_users.md). For:
|
||||
[suitable for up to 20 RPS / 1,000 users](../reference_architectures/1k_users.md). For:
|
||||
|
||||
- Linux package installations for up to 2000 users, see [specific Gitaly configuration instructions](../reference_architectures/2k_users.md#configure-gitaly).
|
||||
- Linux package installations for up to 40 RPS / 2,000 users, see [specific Gitaly configuration instructions](../reference_architectures/2k_users.md#configure-gitaly).
|
||||
- Self-compiled installations or custom Gitaly installations, see [Configure Gitaly](configure_gitaly.md).
|
||||
|
||||
GitLab installations for more than 2000 active users performing daily Git write operation may be
|
||||
|
|
|
|||
|
|
@ -14,11 +14,11 @@ Configure Gitaly Cluster using either:
|
|||
|
||||
- Gitaly Cluster configuration instructions available as part of
|
||||
[reference architectures](../reference_architectures/index.md) for installations of up to:
|
||||
- [3000 users](../reference_architectures/3k_users.md#configure-gitaly-cluster).
|
||||
- [5000 users](../reference_architectures/5k_users.md#configure-gitaly-cluster).
|
||||
- [10,000 users](../reference_architectures/10k_users.md#configure-gitaly-cluster).
|
||||
- [25,000 users](../reference_architectures/25k_users.md#configure-gitaly-cluster).
|
||||
- [50,000 users](../reference_architectures/50k_users.md#configure-gitaly-cluster).
|
||||
- [60 RPS or 3,000 users](../reference_architectures/3k_users.md#configure-gitaly-cluster).
|
||||
- [100 RPS or 5,000 users](../reference_architectures/5k_users.md#configure-gitaly-cluster).
|
||||
- [200 RPS or 10,000 users](../reference_architectures/10k_users.md#configure-gitaly-cluster).
|
||||
- [500 RPS or 25,000 users](../reference_architectures/25k_users.md#configure-gitaly-cluster).
|
||||
- [1000 RPS or 50,000 users](../reference_architectures/50k_users.md#configure-gitaly-cluster).
|
||||
- The custom configuration instructions that follow on this page.
|
||||
|
||||
Smaller GitLab installations may need only [Gitaly itself](index.md).
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ DETAILS:
|
|||
If you wish to have your database service hosted separately from your GitLab
|
||||
application servers, you can do this using the PostgreSQL binaries packaged
|
||||
together with the Linux package. This is recommended as part of our
|
||||
[reference architecture for up to 2,000 users](../reference_architectures/2k_users.md).
|
||||
[reference architecture for up to 40 RPS or 2,000 users](../reference_architectures/2k_users.md).
|
||||
|
||||
## Setting it up
|
||||
|
||||
|
|
|
|||
|
|
@ -4,14 +4,13 @@ group: Distribution
|
|||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://handbook.gitlab.com/handbook/product/ux/technical-writing/#assignments
|
||||
---
|
||||
|
||||
# Reference architecture: up to 10,000 users
|
||||
# Reference architecture: 200 RPS or up to 10,000 users
|
||||
|
||||
DETAILS:
|
||||
**Tier:** Premium, Ultimate
|
||||
**Offering:** Self-managed
|
||||
|
||||
This page describes the GitLab reference architecture designed for the load of up to 10,000 users
|
||||
with notable headroom.
|
||||
This page describes the GitLab reference architecture designed to target a peak load of 200 requests per second (RPS), the typical peak load of up to 10,000 users, both manual and automated, based on real data with headroom added.
|
||||
|
||||
For a full list of reference architectures, see
|
||||
[Available reference architectures](index.md#available-reference-architectures).
|
||||
|
|
@ -157,7 +156,7 @@ Before starting, see the [requirements](index.md#requirements) for reference arc
|
|||
## Testing methodology
|
||||
|
||||
The 10k architecture is designed to cover a large majority of workflows and is regularly
|
||||
[smoke and performance tested](index.md#validation-and-test-results) by the Quality Engineering team
|
||||
[smoke and performance tested](index.md#validation-and-test-results) by the Test Platform team
|
||||
against the following endpoint throughput targets:
|
||||
|
||||
- API: 200 RPS
|
||||
|
|
@ -179,7 +178,7 @@ The load balancers used for testing were HAProxy for Linux package environments
|
|||
|
||||
## Set up components
|
||||
|
||||
To set up GitLab and its components to accommodate up to 10,000 users:
|
||||
To set up GitLab and its components to accommodate up to 200 RPS or 10,000 users:
|
||||
|
||||
1. [Configure the external load balancer](#configure-the-external-load-balancer)
|
||||
to handle the load balancing of the GitLab application services nodes.
|
||||
|
|
@ -2398,7 +2397,7 @@ Each Webservice pod consumes roughly 4 CPUs and 5 GB of memory using
|
|||
the [recommended topology](#cluster-topology) because four worker processes
|
||||
are created by default and each pod has other small processes running.
|
||||
|
||||
For 10,000 users we recommend a total Puma worker count of around 80.
|
||||
For 200 RPS or 10,000 users we recommend a total Puma worker count of around 80.
|
||||
With the [provided recommendations](#cluster-topology) this allows the deployment of up to 20
|
||||
Webservice pods with 4 workers per pod and 5 pods per node. Expand available resources using
|
||||
the ratio of 1 CPU to 1.25 GB of memory _per each worker process_ for each additional
|
||||
|
|
|
|||
|
|
@ -4,14 +4,13 @@ group: Distribution
|
|||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://handbook.gitlab.com/handbook/product/ux/technical-writing/#assignments
|
||||
---
|
||||
|
||||
# Reference architecture: up to 1,000 users
|
||||
# Reference architecture: 20 RPS or up to 1,000 users
|
||||
|
||||
DETAILS:
|
||||
**Tier:** Free, Premium, Ultimate
|
||||
**Offering:** Self-managed
|
||||
|
||||
This page describes the GitLab reference architecture designed for the load of up to 1,000 users
|
||||
with notable headroom (non-HA standalone).
|
||||
This page describes the GitLab reference architecture designed to target a peak load of 20 requests per second (RPS), the typical peak load of up to 1,000 users, both manual and automated, based on real data with headroom added.
|
||||
|
||||
For a full list of reference architectures, see
|
||||
[Available reference architectures](index.md#available-reference-architectures).
|
||||
|
|
@ -26,7 +25,7 @@ For a full list of reference architectures, see
|
|||
|
||||
| Users | Configuration | GCP | AWS | Azure |
|
||||
|--------------|-------------------------|----------------|--------------|----------|
|
||||
| Up to 1,000 | 8 vCPU, 7.2 GB memory | `n1-highcpu-8` | `c5.2xlarge` | `F8s v2` |
|
||||
| Up to 1,000 or 20 RPS | 8 vCPU, 7.2 GB memory | `n1-highcpu-8` | `c5.2xlarge` | `F8s v2` |
|
||||
|
||||
```plantuml
|
||||
@startuml 1k
|
||||
|
|
@ -74,7 +73,7 @@ If this applies to you, we strongly recommended referring to the linked document
|
|||
## Testing methodology
|
||||
|
||||
The 1k architecture is designed to cover a large majority of workflows and is regularly
|
||||
[smoke and performance tested](index.md#validation-and-test-results) by the Quality Engineering team
|
||||
[smoke and performance tested](index.md#validation-and-test-results) by the Test Platform team
|
||||
against the following endpoint throughput targets:
|
||||
|
||||
- API: 20 RPS
|
||||
|
|
@ -121,5 +120,5 @@ Cloud Native Hybrid Reference Architecture is an alternative approach where sele
|
|||
components are deployed in Kubernetes via our official [Helm Charts](https://docs.gitlab.com/charts/),
|
||||
and _stateful_ components are deployed in compute VMs with the Linux package.
|
||||
|
||||
The [2k GitLab Cloud Native Hybrid](2k_users.md#cloud-native-hybrid-reference-architecture-with-helm-charts-alternative) (non HA) and [3k GitLab Cloud Native Hybrid](3k_users.md#cloud-native-hybrid-reference-architecture-with-helm-charts-alternative) (HA) reference architectures are the smallest we recommend in Kubernetes.
|
||||
For environments that serve fewer users, you can lower the node specs. Depending on your user count, you can lower all suggested node specs as desired. However, it's recommended that you don't go lower than the [general requirements](../../install/requirements.md).
|
||||
The [2k or 40 RPS GitLab Cloud Native Hybrid](2k_users.md#cloud-native-hybrid-reference-architecture-with-helm-charts-alternative) (non HA) and [3k or 60 RPS GitLab Cloud Native Hybrid](3k_users.md#cloud-native-hybrid-reference-architecture-with-helm-charts-alternative) (HA) reference architectures are the smallest we recommend in Kubernetes.
|
||||
For environments that serve fewer users or a lower RPS, you can lower the node specs. Depending on your user count, you can lower all suggested node specs as desired. However, it's recommended that you don't go lower than the [general requirements](../../install/requirements.md).
|
||||
|
|
|
|||
|
|
@ -4,14 +4,13 @@ group: Distribution
|
|||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://handbook.gitlab.com/handbook/product/ux/technical-writing/#assignments
|
||||
---
|
||||
|
||||
# Reference architecture: up to 25,000 users
|
||||
# Reference architecture: 500 RPS or up to 25,000 users
|
||||
|
||||
DETAILS:
|
||||
**Tier:** Premium, Ultimate
|
||||
**Offering:** Self-managed
|
||||
|
||||
This page describes the GitLab reference architecture designed for the load of up to 25,000 users
|
||||
with notable headroom.
|
||||
This page describes the GitLab reference architecture designed to target a peak load of 500 requests per second (RPS) - The typical peak load of up to 25,000 users, both manual and automated, based on real data with headroom added.
|
||||
|
||||
For a full list of reference architectures, see
|
||||
[Available reference architectures](index.md#available-reference-architectures).
|
||||
|
|
@ -157,7 +156,7 @@ Before starting, see the [requirements](index.md#requirements) for reference arc
|
|||
## Testing methodology
|
||||
|
||||
The 25k architecture is designed to cover a large majority of workflows and is regularly
|
||||
[smoke and performance tested](index.md#validation-and-test-results) by the Quality Engineering team
|
||||
[smoke and performance tested](index.md#validation-and-test-results) by the Test Platform team
|
||||
against the following endpoint throughput targets:
|
||||
|
||||
- API: 500 RPS
|
||||
|
|
@ -179,7 +178,7 @@ The load balancers used for testing were HAProxy for Linux package environments
|
|||
|
||||
## Set up components
|
||||
|
||||
To set up GitLab and its components to accommodate up to 25,000 users:
|
||||
To set up GitLab and its components to accommodate up to 500 RPS or 25,000 users:
|
||||
|
||||
1. [Configure the external load balancer](#configure-the-external-load-balancer)
|
||||
to handle the load balancing of the GitLab application services nodes.
|
||||
|
|
@ -2403,7 +2402,7 @@ Each Webservice pod consumes roughly 4 CPUs and 5 GB of memory using
|
|||
the [recommended topology](#cluster-topology) because four worker processes
|
||||
are created by default and each pod has other small processes running.
|
||||
|
||||
For 25,000 users we recommend a total Puma worker count of around 140.
|
||||
For 500 RPS or 25,000 users we recommend a total Puma worker count of around 140.
|
||||
With the [provided recommendations](#cluster-topology) this allows the deployment of up to 35
|
||||
Webservice pods with 4 workers per pod and 5 pods per node. Expand available resources using
|
||||
the ratio of 1 CPU to 1.25 GB of memory _per each worker process_ for each additional
|
||||
|
|
|
|||
|
|
@ -4,21 +4,20 @@ group: Distribution
|
|||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://handbook.gitlab.com/handbook/product/ux/technical-writing/#assignments
|
||||
---
|
||||
|
||||
# Reference architecture: up to 2,000 users
|
||||
# Reference architecture: 40 RPS or up to 2,000 users
|
||||
|
||||
DETAILS:
|
||||
**Tier:** Free, Premium, Ultimate
|
||||
**Offering:** Self-managed
|
||||
|
||||
This page describes the GitLab reference architecture designed for the load of up to 2,000 users
|
||||
with notable headroom (non-HA).
|
||||
This page describes the GitLab reference architecture designed to target a peak load of 40 requests per second (RPS), the typical peak load of up to 2,000 users, both manual and automated, based on real data with headroom added.
|
||||
|
||||
For a full list of reference architectures, see
|
||||
[Available reference architectures](index.md#available-reference-architectures).
|
||||
|
||||
> - **Target Load:** API: 40 RPS, Web: 4 RPS, Git (Pull): 4 RPS, Git (Push): 1 RPS
|
||||
> - **High Availability:** No. For a highly-available environment, you can
|
||||
> follow a modified [3K reference architecture](3k_users.md#supported-modifications-for-lower-user-counts-ha).
|
||||
> follow a modified [3K or 60 RPS reference architecture](3k_users.md#supported-modifications-for-lower-user-counts-ha).
|
||||
> - **Estimated Costs:** [See cost table](index.md#cost-to-run)
|
||||
> - **Cloud Native Hybrid:** [Yes](#cloud-native-hybrid-reference-architecture-with-helm-charts-alternative)
|
||||
> - **Unsure which Reference Architecture to use?** [Go to this guide for more info](index.md#deciding-which-architecture-to-use).
|
||||
|
|
@ -100,7 +99,7 @@ Before starting, see the [requirements](index.md#requirements) for reference arc
|
|||
## Testing methodology
|
||||
|
||||
The 2k architecture is designed to cover a large majority of workflows and is regularly
|
||||
[smoke and performance tested](index.md#validation-and-test-results) by the Quality Engineering team
|
||||
[smoke and performance tested](index.md#validation-and-test-results) by the Test Platform team
|
||||
against the following endpoint throughput targets:
|
||||
|
||||
- API: 40 RPS
|
||||
|
|
@ -122,7 +121,7 @@ The load balancers used for testing were HAProxy for Linux package environments
|
|||
|
||||
## Set up components
|
||||
|
||||
To set up GitLab and its components to accommodate up to 2,000 users:
|
||||
To set up GitLab and its components to accommodate up to 40 RPS or 2,000 users:
|
||||
|
||||
1. [Configure the external load balancing node](#configure-the-external-load-balancer)
|
||||
to handle the load balancing of the GitLab application services nodes.
|
||||
|
|
@ -1105,7 +1104,7 @@ section assumes this.
|
|||
|
||||
NOTE:
|
||||
The 2,000 reference architecture is not a highly-available setup. To achieve HA,
|
||||
you can follow a modified [3K reference architecture](3k_users.md#cloud-native-hybrid-reference-architecture-with-helm-charts-alternative).
|
||||
you can follow a modified [3K or 60 RPS reference architecture](3k_users.md#cloud-native-hybrid-reference-architecture-with-helm-charts-alternative).
|
||||
|
||||
WARNING:
|
||||
**Gitaly Cluster is not supported to be run in Kubernetes**.
|
||||
|
|
@ -1200,7 +1199,7 @@ Each Webservice pod consumes roughly 4 CPUs and 5 GB of memory using
|
|||
the [recommended topology](#cluster-topology) because two worker processes
|
||||
are created by default and each pod has other small processes running.
|
||||
|
||||
For 2,000 users we recommend a total Puma worker count of around 12.
|
||||
For 40 RPS or 2,000 users we recommend a total Puma worker count of around 12.
|
||||
With the [provided recommendations](#cluster-topology) this allows the deployment of up to 3
|
||||
Webservice pods with 4 workers per pod and 1 pod per node. Expand available resources using
|
||||
the ratio of 1 CPU to 1.25 GB of memory _per each worker process_ for each additional
|
||||
|
|
|
|||
|
|
@ -4,14 +4,13 @@ group: Distribution
|
|||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://handbook.gitlab.com/handbook/product/ux/technical-writing/#assignments
|
||||
---
|
||||
|
||||
# Reference architecture: up to 3,000 users
|
||||
# Reference architecture: 60 RPS or up to 3,000 users
|
||||
|
||||
DETAILS:
|
||||
**Tier:** Premium, Ultimate
|
||||
**Offering:** Self-managed
|
||||
|
||||
This page describes the GitLab reference architecture designed for the load of up to 3,000 users
|
||||
with notable headroom.
|
||||
This page describes the GitLab reference architecture designed to target a peak load of 60 requests per second (RPS), the typical peak load of up to 3,000 users, both manual and automated, based on real data with headroom added.
|
||||
|
||||
This architecture is the smallest one available with HA built in. If you require HA but
|
||||
have a lower user count or total load the [Supported Modifications for lower user counts](#supported-modifications-for-lower-user-counts-ha)
|
||||
|
|
@ -152,7 +151,7 @@ Before starting, see the [requirements](index.md#requirements) for reference arc
|
|||
## Testing methodology
|
||||
|
||||
The 3k architecture is designed to cover a large majority of workflows and is regularly
|
||||
[smoke and performance tested](index.md#validation-and-test-results) by the Quality Engineering team
|
||||
[smoke and performance tested](index.md#validation-and-test-results) by the Test Platform team
|
||||
against the following endpoint throughput targets:
|
||||
|
||||
- API: 60 RPS
|
||||
|
|
@ -174,7 +173,7 @@ The load balancers used for testing were HAProxy for Linux package environments
|
|||
|
||||
## Set up components
|
||||
|
||||
To set up GitLab and its components to accommodate up to 3,000 users:
|
||||
To set up GitLab and its components to accommodate up to 60 RPS or 3,000 users:
|
||||
|
||||
1. [Configure the external load balancer](#configure-the-external-load-balancer)
|
||||
to handle the load balancing of the GitLab application services nodes.
|
||||
|
|
@ -2202,18 +2201,18 @@ cluster alongside your instance, read how to
|
|||
|
||||
## Supported modifications for lower user counts (HA)
|
||||
|
||||
The 3,000 user GitLab reference architecture is the smallest we recommend that achieves High Availability (HA).
|
||||
However, for environments that need to serve fewer users but maintain HA, there are several
|
||||
The 60 RPS or 3,000 users GitLab reference architecture is the smallest we recommend that achieves High Availability (HA).
|
||||
However, for environments that need to serve fewer users or a lower RPS but maintain HA, there are several
|
||||
supported modifications you can make to this architecture to reduce complexity and cost.
|
||||
|
||||
It should be noted that to achieve HA with GitLab, the 3,000 user architecture's makeup is ultimately what is
|
||||
required. Each component has various considerations and rules to follow, and the 3,000 user architecture
|
||||
It should be noted that to achieve HA with GitLab, the 60 RPS or 3,000 users architecture's makeup is ultimately what is
|
||||
required. Each component has various considerations and rules to follow, and the architecture
|
||||
meets all of these. Smaller versions of this architecture will be fundamentally the same,
|
||||
but with smaller performance requirements, several modifications can be considered as follows:
|
||||
|
||||
- Lowering node specs: Depending on your user count, you can lower all suggested node specs as desired. However, it's recommended that you don't go lower than the [general requirements](../../install/requirements.md).
|
||||
- Combining select nodes: Some nodes can be combined to reduce complexity at the cost of some performance:
|
||||
- GitLab Rails and Sidekiq: Sidekiq nodes can be removed and the component instead enabled on the GitLab Rails nodes.
|
||||
- GitLab Rails and Sidekiq: Sidekiq nodes can be removed, and the component instead enabled on the GitLab Rails nodes.
|
||||
- PostgreSQL and PgBouncer: PgBouncer nodes could be removed and instead be enabled on PostgreSQL nodes with the Internal Load Balancer pointing to them. However, to enable [Database Load Balancing](../postgresql/database_load_balancing.md), a separate PgBouncer array is still required.
|
||||
- Reducing the node counts: Some node types do not need consensus and can run with fewer nodes (but more than one for redundancy). This will also lead to reduced performance.
|
||||
- GitLab Rails and Sidekiq: Stateless services don't have a minimum node count. Two are enough for redundancy.
|
||||
|
|
@ -2381,7 +2380,7 @@ Each Webservice pod consumes roughly 4 CPUs and 5 GB of memory using
|
|||
the [recommended topology](#cluster-topology) because four worker processes
|
||||
are created by default and each pod has other small processes running.
|
||||
|
||||
For 3,000 users we recommend a total Puma worker count of around 16.
|
||||
For 60 RPS or 3,000 users we recommend a total Puma worker count of around 16.
|
||||
With the [provided recommendations](#cluster-topology) this allows the deployment of up to 4
|
||||
Webservice pods with 4 workers per pod and 2 pods per node. Expand available resources using
|
||||
the ratio of 1 CPU to 1.25 GB of memory _per each worker process_ for each additional
|
||||
|
|
|
|||
|
|
@ -4,14 +4,13 @@ group: Distribution
|
|||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://handbook.gitlab.com/handbook/product/ux/technical-writing/#assignments
|
||||
---
|
||||
|
||||
# Reference architecture: up to 50,000 users
|
||||
# Reference architecture: 1000 RPS or up to 50,000 users
|
||||
|
||||
DETAILS:
|
||||
**Tier:** Premium, Ultimate
|
||||
**Offering:** Self-managed
|
||||
|
||||
This page describes the GitLab reference architecture designed for the load of up to 50,000 users
|
||||
with notable headroom.
|
||||
This page describes the GitLab reference architecture designed to target a peak load of 1000 requests per second (RPS), the typical peak load of up to 50,000 users, both manual and automated, based on real data with headroom added.
|
||||
|
||||
For a full list of reference architectures, see
|
||||
[Available reference architectures](index.md#available-reference-architectures).
|
||||
|
|
@ -156,7 +155,7 @@ Before starting, see the [requirements](index.md#requirements) for reference arc
|
|||
## Testing methodology
|
||||
|
||||
The 50k architecture is designed to cover a large majority of workflows and is regularly
|
||||
[smoke and performance tested](index.md#validation-and-test-results) by the Quality Engineering team
|
||||
[smoke and performance tested](index.md#validation-and-test-results) by the Test Platform team
|
||||
against the following endpoint throughput targets:
|
||||
|
||||
- API: 1000 RPS
|
||||
|
|
@ -178,7 +177,7 @@ The load balancers used for testing were HAProxy for Linux package environments
|
|||
|
||||
## Set up components
|
||||
|
||||
To set up GitLab and its components to accommodate up to 50,000 users:
|
||||
To set up GitLab and its components to accommodate up to 1000 RPS or 50,000 users:
|
||||
|
||||
1. [Configure the external load balancer](#configure-the-external-load-balancer)
|
||||
to handle the load balancing of the GitLab application services nodes.
|
||||
|
|
@ -2417,7 +2416,7 @@ Each Webservice pod consumes roughly 4 CPUs and 5 GB of memory using
|
|||
the [recommended topology](#cluster-topology) because four worker processes
|
||||
are created by default and each pod has other small processes running.
|
||||
|
||||
For 50,000 users we recommend a total Puma worker count of around 320.
|
||||
For 1000 RPS or 50,000 users we recommend a total Puma worker count of around 320.
|
||||
With the [provided recommendations](#cluster-topology) this allows the deployment of up to 80
|
||||
Webservice pods with 4 workers per pod and 5 pods per node. Expand available resources using
|
||||
the ratio of 1 CPU to 1.25 GB of memory _per each worker process_ for each additional
|
||||
|
|
|
|||
|
|
@ -4,14 +4,13 @@ group: Distribution
|
|||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://handbook.gitlab.com/handbook/product/ux/technical-writing/#assignments
|
||||
---
|
||||
|
||||
# Reference architecture: up to 5,000 users
|
||||
# Reference architecture: 100 RPS or up to 5,000 users
|
||||
|
||||
DETAILS:
|
||||
**Tier:** Premium, Ultimate
|
||||
**Offering:** Self-managed
|
||||
|
||||
This page describes the GitLab reference architecture designed for the load of up to 5,000 users
|
||||
with notable headroom.
|
||||
This page describes the GitLab reference architecture designed to target a peak load of 100 requests per second (RPS) - The typical peak load of up to 5,000 users, both manual and automated, based on real data with headroom added.
|
||||
|
||||
For a full list of reference architectures, see
|
||||
[Available reference architectures](index.md#available-reference-architectures).
|
||||
|
|
@ -152,7 +151,7 @@ Before starting, see the [requirements](index.md#requirements) for reference arc
|
|||
## Testing methodology
|
||||
|
||||
The 5k architecture is designed to cover a large majority of workflows and is regularly
|
||||
[smoke and performance tested](index.md#validation-and-test-results) by the Quality Engineering team
|
||||
[smoke and performance tested](index.md#validation-and-test-results) by the Test Platform team
|
||||
against the following endpoint throughput targets:
|
||||
|
||||
- API: 100 RPS
|
||||
|
|
@ -174,7 +173,7 @@ The load balancers used for testing were HAProxy for Linux package environments
|
|||
|
||||
## Set up components
|
||||
|
||||
To set up GitLab and its components to accommodate up to 5,000 users:
|
||||
To set up GitLab and its components to accommodate up to 100 RPS or 5,000 users:
|
||||
|
||||
1. [Configure the external load balancer](#configure-the-external-load-balancer)
|
||||
to handle the load balancing of the GitLab application services nodes.
|
||||
|
|
@ -2356,7 +2355,7 @@ Each Webservice pod consumes roughly 4 CPUs and 5 GB of memory using
|
|||
the [recommended topology](#cluster-topology) because four worker processes
|
||||
are created by default and each pod has other small processes running.
|
||||
|
||||
For 5,000 users we recommend a total Puma worker count of around 40.
|
||||
For 100 RPS or 5,000 users we recommend a total Puma worker count of around 40.
|
||||
With the [provided recommendations](#cluster-topology) this allows the deployment of up to 10
|
||||
Webservice pods with 4 workers per pod and 2 pods per node. Expand available resources using
|
||||
the ratio of 1 CPU to 1.25 GB of memory _per each worker process_ for each additional
|
||||
|
|
|
|||
|
|
@ -12,40 +12,41 @@ DETAILS:
|
|||
**Offering:** Self-managed
|
||||
|
||||
The GitLab Reference Architectures have been designed and tested by the
|
||||
GitLab Quality Engineering and Support teams to provide recommended deployments at scale.
|
||||
GitLab Test Platform and Support teams to provide scalable recommended deployments for target loads.
|
||||
|
||||
## Available reference architectures
|
||||
|
||||
The following Reference Architectures are available as recommended starting points for your environment.
|
||||
|
||||
The architectures are named in terms of _total_ load, both manual and automated, correlated to user count and based on real data along with substantial headroom added to add additional coverage for most scenarios.
|
||||
The architectures are named in terms of peak load, based on user count or Requests per Second (RPS). Where the latter has been calculated based on average real data of the former with headroom added.
|
||||
|
||||
However, it should be noted that in some cases, known heavy scenarios such as [large monorepos](#large-monorepos) or notable [additional workloads](#additional-workloads) may require adjustments to be made.
|
||||
NOTE:
|
||||
Each architecture has been designed to be [scalable and can be adjusted accordingly if required](#scaling-an-environment) by your specific workload. This may be likely in known heavy scenarios such as using [large monorepos](#large-monorepos) or notable [additional workloads](#additional-workloads).
|
||||
|
||||
For details about what each Reference Architecture has been tested against, see the "Testing Methodology" section of each page.
|
||||
|
||||
### GitLab package (Omnibus)
|
||||
|
||||
Below is a list of Linux package based architectures:
|
||||
Below is the list of Linux package based reference architectures:
|
||||
|
||||
- [Up to 1,000 users](1k_users.md) <span style="color: darkgrey;">_API: 20 RPS, Web: 2 RPS, Git (Pull): 2 RPS, Git (Push): 1 RPS_</span>
|
||||
- [Up to 2,000 users](2k_users.md) <span style="color: darkgrey;">_API: 40 RPS, Web: 4 RPS, Git (Pull): 4 RPS, Git (Push): 1 RPS_</span>
|
||||
- [Up to 3,000 users](3k_users.md) <span style="color: darkgrey;">_API: 60 RPS, Web: 6 RPS, Git (Pull): 6 RPS, Git (Push): 1 RPS_</span>
|
||||
- [Up to 5,000 users](5k_users.md) <span style="color: darkgrey;">_API: 100 RPS, Web: 10 RPS, Git (Pull): 10 RPS, Git (Push): 2 RPS_</span>
|
||||
- [Up to 10,000 users](10k_users.md) <span style="color: darkgrey;">_API: 200 RPS, Web: 20 RPS, Git (Pull): 20 RPS, Git (Push): 4 RPS_</span>
|
||||
- [Up to 25,000 users](25k_users.md) <span style="color: darkgrey;">_API: 500 RPS, Web: 50 RPS, Git (Pull): 50 RPS, Git (Push): 10 RPS_</span>
|
||||
- [Up to 50,000 users](50k_users.md) <span style="color: darkgrey;">_API: 1000 RPS, Web: 100 RPS, Git (Pull): 100 RPS, Git (Push): 20 RPS_</span>
|
||||
- [Up to 20 RPS or 1,000 users](1k_users.md) <span style="color: darkgrey;">_API: 20 RPS, Web: 2 RPS, Git (Pull): 2 RPS, Git (Push): 1 RPS_</span>
|
||||
- [Up to 40 RPS or 2,000 users](2k_users.md) <span style="color: darkgrey;">_API: 40 RPS, Web: 4 RPS, Git (Pull): 4 RPS, Git (Push): 1 RPS_</span>
|
||||
- [Up to 60 RPS or 3,000 users](3k_users.md) <span style="color: darkgrey;">_API: 60 RPS, Web: 6 RPS, Git (Pull): 6 RPS, Git (Push): 1 RPS_</span>
|
||||
- [Up to 100 RPS or 5,000 users](5k_users.md) <span style="color: darkgrey;">_API: 100 RPS, Web: 10 RPS, Git (Pull): 10 RPS, Git (Push): 2 RPS_</span>
|
||||
- [Up to 200 RPS or 10,000 users](10k_users.md) <span style="color: darkgrey;">_API: 200 RPS, Web: 20 RPS, Git (Pull): 20 RPS, Git (Push): 4 RPS_</span>
|
||||
- [Up to 500 RPS or 25,000 users](25k_users.md) <span style="color: darkgrey;">_API: 500 RPS, Web: 50 RPS, Git (Pull): 50 RPS, Git (Push): 10 RPS_</span>
|
||||
- [Up to 1000 RPS or 50,000 users](50k_users.md) <span style="color: darkgrey;">_API: 1000 RPS, Web: 100 RPS, Git (Pull): 100 RPS, Git (Push): 20 RPS_</span>
|
||||
|
||||
### Cloud native hybrid
|
||||
|
||||
Below is a list of Cloud Native Hybrid reference architectures, where select recommended components can be run in Kubernetes:
|
||||
|
||||
- [Up to 2,000 users](2k_users.md#cloud-native-hybrid-reference-architecture-with-helm-charts-alternative) <span style="color: darkgrey;">_API: 40 RPS, Web: 4 RPS, Git (Pull): 4 RPS, Git (Push): 1 RPS_</span>
|
||||
- [Up to 3,000 users](3k_users.md#cloud-native-hybrid-reference-architecture-with-helm-charts-alternative) <span style="color: darkgrey;">_API: 60 RPS, Web: 6 RPS, Git (Pull): 6 RPS, Git (Push): 1 RPS_</span>
|
||||
- [Up to 5,000 users](5k_users.md#cloud-native-hybrid-reference-architecture-with-helm-charts-alternative) <span style="color: darkgrey;">_API: 100 RPS, Web: 10 RPS, Git (Pull): 10 RPS, Git (Push): 2 RPS_</span>
|
||||
- [Up to 10,000 users](10k_users.md#cloud-native-hybrid-reference-architecture-with-helm-charts-alternative) <span style="color: darkgrey;">_API: 200 RPS, Web: 20 RPS, Git (Pull): 20 RPS, Git (Push): 4 RPS_</span>
|
||||
- [Up to 25,000 users](25k_users.md#cloud-native-hybrid-reference-architecture-with-helm-charts-alternative) <span style="color: darkgrey;">_API: 500 RPS, Web: 50 RPS, Git (Pull): 50 RPS, Git (Push): 10 RPS_</span>
|
||||
- [Up to 50,000 users](50k_users.md#cloud-native-hybrid-reference-architecture-with-helm-charts-alternative) <span style="color: darkgrey;">_API: 1000 RPS, Web: 100 RPS, Git (Pull): 100 RPS, Git (Push): 20 RPS_</span>
|
||||
- [40 RPS or Up to 2,000 users](2k_users.md#cloud-native-hybrid-reference-architecture-with-helm-charts-alternative) <span style="color: darkgrey;">_API: 40 RPS, Web: 4 RPS, Git (Pull): 4 RPS, Git (Push): 1 RPS_</span>
|
||||
- [60 RPS or Up to 3,000 users](3k_users.md#cloud-native-hybrid-reference-architecture-with-helm-charts-alternative) <span style="color: darkgrey;">_API: 60 RPS, Web: 6 RPS, Git (Pull): 6 RPS, Git (Push): 1 RPS_</span>
|
||||
- [100 RPS or Up to 5,000 users](5k_users.md#cloud-native-hybrid-reference-architecture-with-helm-charts-alternative) <span style="color: darkgrey;">_API: 100 RPS, Web: 10 RPS, Git (Pull): 10 RPS, Git (Push): 2 RPS_</span>
|
||||
- [200 RPS or Up to 10,000 users](10k_users.md#cloud-native-hybrid-reference-architecture-with-helm-charts-alternative) <span style="color: darkgrey;">_API: 200 RPS, Web: 20 RPS, Git (Pull): 20 RPS, Git (Push): 4 RPS_</span>
|
||||
- [500 RPS or Up to 25,000 users](25k_users.md#cloud-native-hybrid-reference-architecture-with-helm-charts-alternative) <span style="color: darkgrey;">_API: 500 RPS, Web: 50 RPS, Git (Pull): 50 RPS, Git (Push): 10 RPS_</span>
|
||||
- [1000 RPS or Up to 50,000 users](50k_users.md#cloud-native-hybrid-reference-architecture-with-helm-charts-alternative) <span style="color: darkgrey;">_API: 1000 RPS, Web: 100 RPS, Git (Pull): 100 RPS, Git (Push): 20 RPS_</span>
|
||||
|
||||
## Before you start
|
||||
|
||||
|
|
@ -67,18 +68,18 @@ As a general guide, **the more performant and/or resilient you want your environ
|
|||
|
||||
This section explains the designs you can choose from. It begins with the least complexity, goes to the most, and ends with a decision tree.
|
||||
|
||||
### Expected Load (RPS)
|
||||
### Expected Load (RPS or user count)
|
||||
|
||||
The first thing to check is what the expected load is your environment would be expected to serve.
|
||||
The first thing to check is what the expected peak load is your environment would be expected to serve.
|
||||
|
||||
The Reference Architectures have been designed with substantial headroom by default, but it's recommended to also check the
|
||||
load of what each architecture has been tested against under the "Testing Methodology" section found on each page,
|
||||
comparing those values with what load you are expecting against your existing GitLab environment to help select the right Reference Architecture
|
||||
size.
|
||||
Each architecture is described in terms of peak Requests per Second (RPS) or user count load. As detailed under the "Testing Methodology" section on each page, each architecture is tested
|
||||
against its listed RPS for each endpoint type (API, Web, Git), which is the typical peak load of the given user count, both manual and automated, with headroom.
|
||||
|
||||
Load is given in terms of Requests per Second (RPS) for each endpoint type (API, Web, Git). This information on your existing infrastructure
|
||||
can typically be surfaced by most reputable monitoring solutions or in some other ways such as load balancer metrics. For example, on existing GitLab environments,
|
||||
[Prometheus metrics](../monitoring/prometheus/gitlab_metrics.md) such as `gitlab_transaction_duration_seconds` can be used to see this data.
|
||||
It's strongly recommended finding out what peak RPS your environment will be expected to handle across endpoint types, through existing metrics (such as [Prometheus](../monitoring/prometheus/gitlab_metrics.md))
|
||||
or estimates, and to select the corresponding architecture as this is the most objective.
|
||||
|
||||
If it's not possible for you to find out the expected peak RPS then it's recommended to select based on user count to start and then monitor the environment
|
||||
closely to confirm the RPS, whether the architecture is performing and adjust accordingly is necessary.
|
||||
|
||||
### Standalone (non-HA)
|
||||
|
||||
|
|
@ -170,10 +171,10 @@ graph TD
|
|||
L3A("<a href=#do-you-need-high-availability-ha>Do you need HA?</a><br>(or Zero-Downtime Upgrades)")
|
||||
L3B[Do you have experience with<br/>and want additional resilience<br/>with select components in Kubernetes?]
|
||||
|
||||
L4A><b>Recommendation</b><br><br>3K architecture with HA<br>and supported reductions]
|
||||
L4A><b>Recommendation</b><br><br>60 RPS / 3K users architecture with HA<br>and supported reductions]
|
||||
L4B><b>Recommendation</b><br><br>Architecture closest to user<br>count with HA]
|
||||
L4C><b>Recommendation</b><br><br>Cloud Native Hybrid architecture<br>closest to user count]
|
||||
L4D>"<b>Recommendation</b><br><br>Standalone 1K or 2K<br/>architecture with Backups"]
|
||||
L4D>"<b>Recommendation</b><br><br>Standalone 20 RPS / 1K users or 40 RPS / 2K users<br/>architecture with Backups"]
|
||||
|
||||
L0A --> L1A
|
||||
L1A --> L2A
|
||||
|
|
@ -412,7 +413,7 @@ If you choose to use a third party external service:
|
|||
|
||||
[When selecting to use an external Redis service](../redis/replication_and_failover_external.md#redis-as-a-managed-service-in-a-cloud-provider), it should run a standard, performant, and supported version. Note that this specifically must not be run in [Cluster mode](../../install/requirements.md#redis) as this is unsupported by GitLab.
|
||||
|
||||
Redis is primarily single threaded. For the 10,000 user and above Reference Architectures, separate out the instances as specified into Cache and Persistent data to achieve optimum performance at this scale.
|
||||
Redis is primarily single threaded. For environments targeting up to 200 RPS / 10,000 users or higher, separate out the instances as specified into Cache and Persistent data to achieve optimum performance at this scale.
|
||||
|
||||
### Recommendation notes for Object Storage
|
||||
|
||||
|
|
@ -477,7 +478,7 @@ For deploying GitLab over multiple data centers or regions we offer [GitLab Geo]
|
|||
|
||||
## Validation and test results
|
||||
|
||||
The [Quality Engineering team](https://handbook.gitlab.com/handbook/engineering/quality/)
|
||||
The [Test Platform team](https://handbook.gitlab.com/handbook/engineering/quality/)
|
||||
does regular smoke and performance tests for the reference architectures to ensure they
|
||||
remain compliant.
|
||||
|
||||
|
|
@ -517,7 +518,7 @@ per 1,000 users:
|
|||
- Git (Pull): 2 RPS
|
||||
- Git (Push): 0.4 RPS (rounded to the nearest integer)
|
||||
|
||||
The above targets were selected based on real customer data of total environmental loads corresponding to the user count, including CI and other workloads along with additional substantial headroom added.
|
||||
The above RPS targets were selected based on real customer data of total environmental loads corresponding to the user count, including CI and other workloads along with additional substantial headroom added.
|
||||
|
||||
### How to interpret the results
|
||||
|
||||
|
|
|
|||
|
|
@ -371,7 +371,7 @@ Cluster-wide features are strongly discouraged because:
|
|||
Services that might initially be cluster-wide are still expected to be split in the future to achieve full service isolation.
|
||||
No feature should be built to depend on such a service (like Elasticsearch).
|
||||
|
||||
### Will Cells use the [reference architecture for 50,000 users](../../../administration/reference_architectures/50k_users.md)?
|
||||
### Will Cells use the [reference architecture for up to 1000 RPS or 50,000 users](../../../administration/reference_architectures/50k_users.md)?
|
||||
|
||||
The infrastructure team will properly size Cells depending on the load.
|
||||
The Tenant Scale team sees an opportunity to use GitLab Dedicated as a base for Cells deployment.
|
||||
|
|
|
|||
|
|
@ -40,13 +40,13 @@ GitLab Reference Architecture sizes and [costs](../../../../administration/refer
|
|||
|
||||
| Reference Architecture | ClickHouse type | ClickHouse cost / (GitLab cost + ClickHouse cost) |
|
||||
|-------------|-----------------|-----------------------------------|
|
||||
| [1k - non HA](https://cloud.google.com/products/calculator#id=a6d6a94a-c7dc-4c22-85c4-7c5747f272ed) | [non-HA](https://cloud.google.com/products/calculator#id=9af5359e-b155-451c-b090-5f0879bb591e) | 78.01% |
|
||||
| [2k - non HA](https://cloud.google.com/products/calculator#id=0d3aff1f-ea3d-43f9-aa59-df49d27c35ca) | [non-HA](https://cloud.google.com/products/calculator#id=9af5359e-b155-451c-b090-5f0879bb591e) | 44.50% |
|
||||
| [3k - HA](https://cloud.google.com/products/calculator/#id=15fc2bd9-5b1c-479d-bc46-d5ce096b8107) | [HA](https://cloud.google.com/products/calculator#id=9909f5af-d41a-4da2-b8cc-a0347702a823) | 37.87% |
|
||||
| [5k - HA](https://cloud.google.com/products/calculator/#id=9a798136-53f2-4c35-be43-8e1e975a6663) | [HA](https://cloud.google.com/products/calculator#id=9909f5af-d41a-4da2-b8cc-a0347702a823) | 30.92% |
|
||||
| [10k - HA](https://cloud.google.com/products/calculator#id=cbe61840-31a1-487f-88fa-631251c2fde5) | [HA](https://cloud.google.com/products/calculator#id=9909f5af-d41a-4da2-b8cc-a0347702a823) | 20.47% |
|
||||
| [25k - HA](https://cloud.google.com/products/calculator#id=b4b8b587-508a-4433-adc8-dc506bbe924f) | [HA](https://cloud.google.com/products/calculator#id=9909f5af-d41a-4da2-b8cc-a0347702a823) | 14.30% |
|
||||
| [50k - HA](https://cloud.google.com/products/calculator/#id=48b4d817-d6cd-44b8-b069-0ba9a5d123ea) | [HA](https://cloud.google.com/products/calculator#id=9909f5af-d41a-4da2-b8cc-a0347702a823) | 8.16% |
|
||||
| [20 RPS / 1k users - non HA](https://cloud.google.com/products/calculator#id=a6d6a94a-c7dc-4c22-85c4-7c5747f272ed) | [non-HA](https://cloud.google.com/products/calculator#id=9af5359e-b155-451c-b090-5f0879bb591e) | 78.01% |
|
||||
| [40 RPS / 2k users- non HA](https://cloud.google.com/products/calculator#id=0d3aff1f-ea3d-43f9-aa59-df49d27c35ca) | [non-HA](https://cloud.google.com/products/calculator#id=9af5359e-b155-451c-b090-5f0879bb591e) | 44.50% |
|
||||
| [60 RPS / 3k users - HA](https://cloud.google.com/products/calculator/#id=15fc2bd9-5b1c-479d-bc46-d5ce096b8107) | [HA](https://cloud.google.com/products/calculator#id=9909f5af-d41a-4da2-b8cc-a0347702a823) | 37.87% |
|
||||
| [100 RPS / 5k users - HA](https://cloud.google.com/products/calculator/#id=9a798136-53f2-4c35-be43-8e1e975a6663) | [HA](https://cloud.google.com/products/calculator#id=9909f5af-d41a-4da2-b8cc-a0347702a823) | 30.92% |
|
||||
| [200 RPS / 10k users - HA](https://cloud.google.com/products/calculator#id=cbe61840-31a1-487f-88fa-631251c2fde5) | [HA](https://cloud.google.com/products/calculator#id=9909f5af-d41a-4da2-b8cc-a0347702a823) | 20.47% |
|
||||
| [500 RPS / 25k users - HA](https://cloud.google.com/products/calculator#id=b4b8b587-508a-4433-adc8-dc506bbe924f) | [HA](https://cloud.google.com/products/calculator#id=9909f5af-d41a-4da2-b8cc-a0347702a823) | 14.30% |
|
||||
| [1000 RPS / 50k users - HA](https://cloud.google.com/products/calculator/#id=48b4d817-d6cd-44b8-b069-0ba9a5d123ea) | [HA](https://cloud.google.com/products/calculator#id=9909f5af-d41a-4da2-b8cc-a0347702a823) | 8.16% |
|
||||
|
||||
NOTE:
|
||||
The ClickHouse Self-Managed component evaluation is the minimum estimation for the costs
|
||||
|
|
@ -57,7 +57,7 @@ The following components increase the cost, and were not considered in the minim
|
|||
- Disk size - depends on data size, hard to estimate.
|
||||
- Disk types - ClickHouse recommends [fast SSDs](https://clickhouse.com/docs/ru/operations/tips#storage-subsystem).
|
||||
- Network usage - ClickHouse recommends using [10 GB network, if possible](https://clickhouse.com/docs/en/operations/tips#network).
|
||||
- For HA we sum minimum cost across all reference architectures from 3k to 50k users, but HA specs tend to increase with user count.
|
||||
- For HA we sum minimum cost across all reference architectures from 60 RPS / 3k users to 1000 RPS / 50k users, but HA specs tend to increase with user count.
|
||||
|
||||
### Resources
|
||||
|
||||
|
|
|
|||
|
|
@ -273,12 +273,33 @@ first check that the resource group is working correctly:
|
|||
1. If **View job currently using resource** is not available, the resource is not assigned to a job. Instead, check the resource's upcoming jobs.
|
||||
|
||||
1. Get the resource's upcoming jobs with the [REST API](../../api/resource_groups.md#list-upcoming-jobs-for-a-specific-resource-group).
|
||||
1. Verify that the job's [process mode](#process-modes) is **Oldest first**.
|
||||
1. Verify that the resource group's [process mode](#process-modes) is **Oldest first**.
|
||||
1. Find the first job in the list of upcoming jobs, and get the job details [with GraphQL](#get-job-details-through-graphql).
|
||||
1. If the first job's pipeline is an older pipeline, try to cancel the pipeline or the job itself.
|
||||
1. Optional. Repeat this process if the next upcoming job is still in an older pipeline that should no longer run.
|
||||
1. If the problem persists, [report the issue to GitLab](#report-an-issue).
|
||||
|
||||
#### Race conditions in complex or busy pipelines
|
||||
|
||||
If you can't resolve your issue with the solutions above, you might be encountering a known race condition issue. The race condition happens in complex or busy pipelines.
|
||||
For example, you might encounter the race condition if you have:
|
||||
|
||||
- A pipeline with multiple child pipelines.
|
||||
- A single project with multiple pipelines running simultaneously.
|
||||
|
||||
If you think you are running into this problem, [report the issue to GitLab](#report-an-issue) and leave a comment on [issue 436988](https://gitlab.com/gitlab-org/gitlab/-/issues/436988) with a link to your new issue.
|
||||
To confirm the problem, GitLab might ask for additional details such
|
||||
as your full pipeline configuration.
|
||||
|
||||
As a temporary workaround, you can:
|
||||
|
||||
- Start a new pipeline.
|
||||
- Re-run a finished job that has the same resource group as the stuck job.
|
||||
|
||||
For example, if you have a `setup_job` and a `deploy_job` with the same resource group,
|
||||
the `setup_job` might finish while the `deploy_job` is stuck at "waiting for resource".
|
||||
Re-run the `setup_job` to restart the whole process and allow `deploy_job` to finish.
|
||||
|
||||
#### Get job details through GraphQL
|
||||
|
||||
You can get job information from the GraphQL API. You should use the GraphQL API if you use [pipeline-level concurrency control with cross-project/parent-child pipelines](#pipeline-level-concurrency-control-with-cross-projectparent-child-pipelines) because the trigger jobs are not accessible from the UI.
|
||||
|
|
|
|||
|
|
@ -468,27 +468,49 @@ test:
|
|||
|
||||
In this example, GitLab checks for the existence of `file.md` in the current project.
|
||||
|
||||
There is a known issue if you configure `include` with `rules:exists` to add a configuration file
|
||||
There is a known issue if you configure `include` with `rules:exists` in an include file
|
||||
from a different project. GitLab checks for the existence of the file in the _other_ project.
|
||||
For example:
|
||||
|
||||
```yaml
|
||||
# Pipeline configuration in my-group/my-project
|
||||
include:
|
||||
- project: my-group/my-project-2
|
||||
ref: main
|
||||
file: test-file.yml
|
||||
rules:
|
||||
- exists:
|
||||
- file.md
|
||||
- project: my-group/other-project
|
||||
ref: other_branch
|
||||
file: other-file.yml
|
||||
|
||||
test:
|
||||
stage: test
|
||||
script: exit 0
|
||||
|
||||
# other-file.yml in my-group/other-project on ref other_branch
|
||||
include:
|
||||
- project: my-group/my-project
|
||||
ref: main
|
||||
file: my-file.yml
|
||||
rules:
|
||||
- exists:
|
||||
- file.md
|
||||
```
|
||||
|
||||
In this example, GitLab checks for the existence of `test-file.yml` in `my-group/my-project-2`,
|
||||
not the current project. Follow [issue 386040](https://gitlab.com/gitlab-org/gitlab/-/issues/386040)
|
||||
for information about work to improve this behavior.
|
||||
In this example, GitLab searches for the existence of `file.md` in `my-group/other-project`
|
||||
on commit ref `other_branch`, not the project/ref in which the pipeline runs.
|
||||
|
||||
To change the search context you can use [`rules:exists:paths`](index.md#rulesexistspaths)
|
||||
with [`rules:exists:project`](index.md#rulesexistsproject).
|
||||
For example:
|
||||
|
||||
```yaml
|
||||
include:
|
||||
- project: my-group/my-project
|
||||
ref: main
|
||||
file: my-file.yml
|
||||
rules:
|
||||
- exists:
|
||||
paths:
|
||||
- file.md
|
||||
project: my-group/my-project
|
||||
ref: main
|
||||
```
|
||||
|
||||
### `include` with `rules:changes`
|
||||
|
||||
|
|
|
|||
|
|
@ -4182,7 +4182,7 @@ relative to `refs/heads/branch1` and the pipeline source is a merge request even
|
|||
|
||||
Use `exists` to run a job when certain files exist in the repository.
|
||||
|
||||
**Keyword type**: Job keyword. You can use it only as part of a job.
|
||||
**Keyword type**: Job keyword. You can use it as part of a job or an [`include`](#include).
|
||||
|
||||
**Possible inputs**:
|
||||
|
||||
|
|
@ -4213,6 +4213,88 @@ job:
|
|||
- A maximum of 50 patterns or file paths can be defined per `rules:exists` section.
|
||||
- `exists` resolves to `true` if any of the listed files are found (an `OR` operation).
|
||||
|
||||
- With job-level `rules:exists`, GitLab searches for the files in the project and
|
||||
ref that runs the pipeline. When using [`include` with `rules:exists`](includes.md#include-with-rulesexists),
|
||||
GitLab searches for the files in the project and ref of the file that contains the `include`
|
||||
section. The project containing the `include` section can be different than the project
|
||||
running the pipeline when using:
|
||||
- [Nested includes](includes.md#use-nested-includes).
|
||||
- [Compliance pipelines](../../user/group/compliance_pipelines.md).
|
||||
|
||||
##### `rules:exists:paths`
|
||||
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/386040) in GitLab 16.11 [with a flag](../../administration/feature_flags.md) named `ci_support_rules_exists_paths_and_project`. Disabled by default.
|
||||
|
||||
FLAG:
|
||||
On self-managed GitLab, by default this feature is not available.
|
||||
To make it available, an administrator can [enable the feature flag](../../administration/feature_flags.md) named `ci_support_rules_exists_paths_and_project`.
|
||||
On GitLab.com and GitLab Dedicated, this feature is not available.
|
||||
|
||||
`rules:exists:paths` is the same as using [`rules:exists`](#rulesexists) without
|
||||
any subkeys. All additional details are the same.
|
||||
|
||||
**Keyword type**: Job keyword. You can use it as part of a job or an [`include`](#include).
|
||||
|
||||
**Possible inputs**:
|
||||
|
||||
- An array of file paths.
|
||||
|
||||
**Example of `rules:exists:paths`**:
|
||||
|
||||
```yaml
|
||||
docker-build-1:
|
||||
script: docker build -t my-image:$CI_COMMIT_REF_SLUG .
|
||||
rules:
|
||||
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
|
||||
exists:
|
||||
- Dockerfile
|
||||
|
||||
docker-build-2:
|
||||
script: docker build -t my-image:$CI_COMMIT_REF_SLUG .
|
||||
rules:
|
||||
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
|
||||
exists:
|
||||
paths:
|
||||
- Dockerfile
|
||||
```
|
||||
|
||||
In this example, both jobs have the same behavior.
|
||||
|
||||
##### `rules:exists:project`
|
||||
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/386040) in GitLab 16.11 [with a flag](../../administration/feature_flags.md) named `ci_support_rules_exists_paths_and_project`. Disabled by default.
|
||||
|
||||
FLAG:
|
||||
On self-managed GitLab, by default this feature is not available.
|
||||
To make it available, an administrator can [enable the feature flag](../../administration/feature_flags.md) named `ci_support_rules_exists_paths_and_project`.
|
||||
On GitLab.com and GitLab Dedicated, this feature is not available.
|
||||
|
||||
Use `rules:exists:project` to specify the location in which to search for the files
|
||||
listed under [`rules:exists:paths`](#rulesexistspaths). Must be used with `rules:exists:paths`.
|
||||
|
||||
**Keyword type**: Job keyword. You can use it as part of a job or an [`include`](#include), and it must be combined with `rules:exists:paths`.
|
||||
|
||||
**Possible inputs**:
|
||||
|
||||
- `exists:project`: A full project path, including namespace and group.
|
||||
- `exists:ref`: Optional. The commit ref to use to search for the file. The ref can be a tag, branch name, or SHA. Defaults to the `HEAD` of the project when not specified.
|
||||
|
||||
**Example of `rules:exists:project`**:
|
||||
|
||||
```yaml
|
||||
docker build:
|
||||
script: docker build -t my-image:$CI_COMMIT_REF_SLUG .
|
||||
rules:
|
||||
- exists:
|
||||
paths:
|
||||
- Dockerfile
|
||||
project: my-group/my-project
|
||||
ref: v1.0.0
|
||||
```
|
||||
|
||||
In this example, the `docker build` job is only included when the `Dockerfile` exists in
|
||||
the project `my-group/my-project` on the commit tagged with `v1.0.0`.
|
||||
|
||||
#### `rules:allow_failure`
|
||||
|
||||
Use [`allow_failure: true`](#allow_failure) in `rules` to allow a job to fail
|
||||
|
|
|
|||
|
|
@ -689,7 +689,7 @@ After triggering a successful [e2e:package-and-test-ee](testing_guide/end_to_end
|
|||
1. The `GET:Geo` job can be found and triggered under the `trigger-qa` stage.
|
||||
|
||||
This pipeline uses [GET](https://gitlab.com/gitlab-org/gitlab-environment-toolkit) to spin up a
|
||||
[1k](../administration/reference_architectures/1k_users.md) Geo installation,
|
||||
[20 RPS / 1k users](../administration/reference_architectures/1k_users.md) Geo installation,
|
||||
and run the [`gitlab-qa`](https://gitlab.com/gitlab-org/gitlab-qa) Geo scenario against the instance.
|
||||
When working on Geo features, it is a good idea to ensure the `qa-geo` job passes in a triggered `GET:Geo pipeline`.
|
||||
|
||||
|
|
|
|||
|
|
@ -93,7 +93,7 @@ These users accept some downtime during the update. Unfortunately we can't ignor
|
|||
|
||||
## What kind of components can GitLab be broken down into?
|
||||
|
||||
The [50,000 reference architecture](../administration/reference_architectures/50k_users.md) runs GitLab on 48+ nodes. GitLab.com is [bigger than that](https://handbook.gitlab.com/handbook/engineering/infrastructure/production/architecture/), plus a portion of the [infrastructure runs on Kubernetes](https://handbook.gitlab.com/handbook/engineering/infrastructure/production/architecture/#gitlab-com-architecture), plus there is a ["canary" stage which receives updates first](https://handbook.gitlab.com/handbook/engineering/infrastructure/environments/canary-stage/).
|
||||
The [1000 RPS or 50,000 user reference architecture](../administration/reference_architectures/50k_users.md) runs GitLab on 48+ nodes. GitLab.com is [bigger than that](https://handbook.gitlab.com/handbook/engineering/infrastructure/production/architecture/), plus a portion of the [infrastructure runs on Kubernetes](https://handbook.gitlab.com/handbook/engineering/infrastructure/production/architecture/#gitlab-com-architecture), plus there is a ["canary" stage which receives updates first](https://handbook.gitlab.com/handbook/engineering/infrastructure/environments/canary-stage/).
|
||||
|
||||
But the problem isn't just that there are many nodes. The bigger problem is that a deployment can be divided into different contexts. And GitLab.com is not the only one that does this. Some possible divisions:
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ info: Any user with at least the Maintainer role can merge updates to this conte
|
|||
|
||||
## Setup Instructions
|
||||
|
||||
In order to use SemanticVersionable you must first create a database migration to add the required columns to your table. The required columns are `semver_major`, `semver_minor`, `semver_patch`, and `semver_prerelease`. An example migration would look like this:
|
||||
In order to use SemanticVersionable you must first create a database migration to add the required columns to your table. The required columns are `semver_major`, `semver_minor`, `semver_patch`, and `semver_prerelease`. A `v` prefix can be added to the version by including a column `semver_prefixed`. An example migration would look like this:
|
||||
|
||||
```ruby
|
||||
class AddVersionPartsToModelVersions < Gitlab::Database::Migration[2.2]
|
||||
|
|
@ -23,6 +23,7 @@ class AddVersionPartsToModelVersions < Gitlab::Database::Migration[2.2]
|
|||
add_column :ml_model_versions, :semver_minor, :integer
|
||||
add_column :ml_model_versions, :semver_patch, :integer
|
||||
add_column :ml_model_versions, :semver_prerelease, :text
|
||||
add_column :ml_model_versions, :semver_prefixed, :boolean, default: false
|
||||
end
|
||||
|
||||
def down
|
||||
|
|
@ -30,32 +31,23 @@ class AddVersionPartsToModelVersions < Gitlab::Database::Migration[2.2]
|
|||
remove_column :ml_model_versions, :semver_minor, :integer
|
||||
remove_column :ml_model_versions, :semver_patch, :integer
|
||||
remove_column :ml_model_versions, :semver_prerelease, :text
|
||||
remove_column :ml_model_versions, :semver_prefixed, :boolean
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
Once the columns are in the database, you can enable the module by including it in your model and configuring it by setting the name of the semver accessor method. For example:
|
||||
Once the columns are in the database, you can enable the module by including it in your model. For example:
|
||||
|
||||
```ruby
|
||||
module Ml
|
||||
class ModelVersion < ApplicationRecord
|
||||
include SemanticVersionable
|
||||
|
||||
semver_method :semver
|
||||
|
||||
...
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
The module has two configuation options:
|
||||
|
||||
- `semver_method` specifies the name of accessor method that will be added to the objecct
|
||||
- `validate_semver` is `true` or `false` (defaults to `false`). If true it will throw a validation error if the provided semver string is not in a valid semver format.
|
||||
|
||||
Depending on the use case, you may want to disable the validation during the rollout or backfill process.
|
||||
|
||||
Please refer to [this MR](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/142228) as a reference.
|
||||
The module is configured to validate a semantic version by default.
|
||||
|
||||
## Sorting
|
||||
|
||||
|
|
|
|||
|
|
@ -16,16 +16,16 @@ DETAILS:
|
|||
This page offers a walkthrough of a common configuration for GitLab on AWS using the official Linux package. You should customize it to accommodate your needs.
|
||||
|
||||
NOTE:
|
||||
For organizations with 1,000 users or less, the recommended AWS installation method is to launch an EC2 single box [Linux package installation](https://about.gitlab.com/install/) and implement a snapshot strategy for backing up the data. See the [1,000 user reference architecture](../../administration/reference_architectures/1k_users.md) for more information.
|
||||
For organizations with 1,000 users or less, the recommended AWS installation method is to launch an EC2 single box [Linux package installation](https://about.gitlab.com/install/) and implement a snapshot strategy for backing up the data. See the [20 RPS or 1,000 user reference architecture](../../administration/reference_architectures/1k_users.md) for more information.
|
||||
|
||||
## Getting started for production-grade GitLab
|
||||
|
||||
NOTE:
|
||||
This document is an installation guide for a proof of concept instance. It is not a reference architecture and it does not result in a highly available configuration.
|
||||
|
||||
Following this guide exactly results in a proof of concept instance that roughly equates to a **scaled down** version of a **two availability zone implementation** of the **Non-HA** [2000 User Reference Architecture](../../administration/reference_architectures/2k_users.md). The 2K reference architecture is not HA because it is primarily intended to provide some scaling while keeping costs and complexity low. The [3000 User Reference Architecture](../../administration/reference_architectures/3k_users.md) is the smallest size that is GitLab HA. It has additional service roles to achieve HA, most notably it uses Gitaly Cluster to achieve HA for Git repository storage and specifies triple redundancy.
|
||||
Following this guide exactly results in a proof of concept instance that roughly equates to a **scaled down** version of a **two availability zone implementation** of the **Non-HA** [40 RPS or 2,000 User Reference Architecture](../../administration/reference_architectures/2k_users.md). The 2K reference architecture is not HA because it is primarily intended to provide some scaling while keeping costs and complexity low. The [60 RPS or 3,000 User Reference Architecture](../../administration/reference_architectures/3k_users.md) is the smallest size that is GitLab HA. It has additional service roles to achieve HA, most notably it uses Gitaly Cluster to achieve HA for Git repository storage and specifies triple redundancy.
|
||||
|
||||
GitLab maintains and tests two main types of Reference Architectures. The **Linux package architectures** are implemented on instance compute while **Cloud Native Hybrid architectures** maximize the use of a Kubernetes cluster. Cloud Native Hybrid reference architecture specifications are addendum sections to the Reference Architecture size pages that start by describing the Linux package architecture. For example, the 3000 User Cloud Native Reference Architecture is in the subsection titled [Cloud Native Hybrid reference architecture with Helm Charts (alternative)](../../administration/reference_architectures/3k_users.md#cloud-native-hybrid-reference-architecture-with-helm-charts-alternative) in the 3000 User Reference Architecture page.
|
||||
GitLab maintains and tests two main types of Reference Architectures. The **Linux package architectures** are implemented on instance compute while **Cloud Native Hybrid architectures** maximize the use of a Kubernetes cluster. Cloud Native Hybrid reference architecture specifications are addendum sections to the Reference Architecture size pages that start by describing the Linux package architecture. For example, the 60 RPS or 3,000 User Cloud Native Reference Architecture is in the subsection titled [Cloud Native Hybrid reference architecture with Helm Charts (alternative)](../../administration/reference_architectures/3k_users.md#cloud-native-hybrid-reference-architecture-with-helm-charts-alternative) in the 60 RPS or 3,000 User Reference Architecture page.
|
||||
|
||||
### Getting started for production-grade Linux package installations
|
||||
|
||||
|
|
|
|||
|
|
@ -97,7 +97,6 @@ To configure your project settings in GitLab:
|
|||
|
||||
DETAILS:
|
||||
**Tier:** Premium, Ultimate
|
||||
**Offering:** GitLab.com, Self-managed, GitLab Dedicated
|
||||
|
||||
> - Ability to enable Jira issues at the group level [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/325715) in GitLab 16.9.
|
||||
|
||||
|
|
@ -126,7 +125,6 @@ Issues are grouped into the following tabs based on their
|
|||
|
||||
DETAILS:
|
||||
**Tier:** Premium, Ultimate
|
||||
**Offering:** GitLab.com, Self-managed, GitLab Dedicated
|
||||
|
||||
Prerequisites:
|
||||
|
||||
|
|
@ -146,12 +144,33 @@ You can also filter the issues by:
|
|||
(for example, `/-/integrations/jira/issues?author_username=John Smith`).
|
||||
- **Assignee**: specify the Jira display name of the `assignee_username` parameter in the URL
|
||||
(for example, `/-/integrations/jira/issues?assignee_username=John Smith`).
|
||||
- **Project**: specify the [Jira project key](#multiple-jira-project-keys) in the `project` parameter in the URL
|
||||
(for example, `/-/integrations/jira/issues?project=GTL`).
|
||||
|
||||
### Multiple Jira project keys
|
||||
|
||||
DETAILS:
|
||||
**Tier:** Premium, Ultimate
|
||||
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/440430) in GitLab 16.11 [with a flag](../../administration/feature_flags.md) named `jira_multiple_project_keys`. Disabled by default.
|
||||
|
||||
FLAG:
|
||||
On self-managed GitLab, by default this feature is not available.
|
||||
To make it available, an administrator can enable the feature flag named `jira_multiple_project_keys`.
|
||||
On GitLab.com and GitLab Dedicated, this feature is not available.
|
||||
|
||||
When you enable `jira_multiple_project_keys`, you can:
|
||||
|
||||
- [View issues](#view-jira-issues) from multiple Jira projects in a GitLab project.
|
||||
- [Filter Jira issues](#filter-jira-issues) by project.
|
||||
|
||||
In **Jira project keys**, you can enter up to 100 project keys separated by commas.
|
||||
Leave blank to include all available keys.
|
||||
|
||||
## Create a Jira issue for a vulnerability
|
||||
|
||||
DETAILS:
|
||||
**Tier:** Ultimate
|
||||
**Offering:** GitLab.com, Self-managed, GitLab Dedicated
|
||||
|
||||
Prerequisites:
|
||||
|
||||
|
|
|
|||
|
|
@ -221,12 +221,16 @@ The following operational features are not available:
|
|||
|
||||
The following is a list of AWS regions verified for use in GitLab Dedicated. Regions must support io2 volumes and meet other requirements. If there is a region you are interested in that is not on this list, reach out through your account representative or [GitLab Support](https://about.gitlab.com/support/) to inquire about its availability. This list will be updated from time to time as additional regions are verified.
|
||||
|
||||
- Asia Pacific (Mumbai)
|
||||
- Asia Pacific (Seoul)
|
||||
- Asia Pacific (Singapore)
|
||||
- Asia Pacific (Sydney)
|
||||
- Asia Pacific (Tokyo)
|
||||
- Canada (Central)
|
||||
- Europe (Frankfurt)
|
||||
- Europe (Ireland)
|
||||
- Europe (London)
|
||||
- Europe (Stockholm)
|
||||
- US East (Ohio)
|
||||
- US East (N. Virginia)
|
||||
- US West (N. California)
|
||||
|
|
|
|||
|
|
@ -489,6 +489,54 @@ git revert <rev commit hash>
|
|||
# reverted commit is back (new commit created again)
|
||||
```
|
||||
|
||||
## Unstage changes
|
||||
|
||||
When you _stage_ a file in Git, you instruct Git to track changes to the file in
|
||||
preparation for a commit. To disregard changes to a file, and not
|
||||
include it in your next commit, _unstage_ the file.
|
||||
|
||||
### Unstage a file
|
||||
|
||||
- To remove files from staging, but keep your changes:
|
||||
|
||||
```shell
|
||||
git reset HEAD <file>
|
||||
```
|
||||
|
||||
- To unstage the last three commits:
|
||||
|
||||
```shell
|
||||
git reset HEAD^3
|
||||
```
|
||||
|
||||
- To unstage changes to a certain file from HEAD:
|
||||
|
||||
```shell
|
||||
git reset <filename>
|
||||
```
|
||||
|
||||
After you unstage the file, to revert the file back to the state it was in before the changes:
|
||||
|
||||
```shell
|
||||
git checkout -- <file>
|
||||
```
|
||||
|
||||
### Remove a file
|
||||
|
||||
- To remove a file from disk and repository, use `git rm`. To remove a directory, use the `-r` flag:
|
||||
|
||||
```shell
|
||||
git rm '*.txt'
|
||||
git rm -r <dirname>
|
||||
```
|
||||
|
||||
- To keep a file on disk but remove it from the repository (such as a file you want
|
||||
to add to `.gitignore`), use the `rm` command with the `--cache` flag:
|
||||
|
||||
```shell
|
||||
git rm <filename> --cache
|
||||
```
|
||||
|
||||
<!-- ## Troubleshooting
|
||||
|
||||
Include any troubleshooting steps that you can foresee. If you know beforehand what issues
|
||||
|
|
|
|||
|
|
@ -1,54 +1,11 @@
|
|||
---
|
||||
stage: Create
|
||||
group: Source Code
|
||||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://handbook.gitlab.com/handbook/product/ux/technical-writing/#assignments
|
||||
description: "Use the unstage command in Git to stop tracking your changes to a file."
|
||||
redirect_to: 'undo.md'
|
||||
remove_date: '2024-07-12'
|
||||
---
|
||||
|
||||
# Unstage files by using Git
|
||||
This document was moved to [another location](undo.md).
|
||||
|
||||
When you _stage_ a file in Git, you instruct Git to track changes to the file in
|
||||
preparation for a commit. To disregard changes to a file, and not
|
||||
include it in your next commit, _unstage_ the file.
|
||||
|
||||
## Unstage a file
|
||||
|
||||
- To remove files from staging, but keep your changes:
|
||||
|
||||
```shell
|
||||
git reset HEAD <file>
|
||||
```
|
||||
|
||||
- To unstage the last three commits:
|
||||
|
||||
```shell
|
||||
git reset HEAD^3
|
||||
```
|
||||
|
||||
- To unstage changes to a certain file from HEAD:
|
||||
|
||||
```shell
|
||||
git reset <filename>
|
||||
```
|
||||
|
||||
After you unstage the file, to revert the file back to the state it was in before the changes:
|
||||
|
||||
```shell
|
||||
git checkout -- <file>
|
||||
```
|
||||
|
||||
## Remove a file
|
||||
|
||||
- To remove a file from disk and repository, use `git rm`. To remove a directory, use the `-r` flag:
|
||||
|
||||
```shell
|
||||
git rm '*.txt'
|
||||
git rm -r <dirname>
|
||||
```
|
||||
|
||||
- To keep a file on disk but remove it from the repository (such as a file you want
|
||||
to add to `.gitignore`), use the `rm` command with the `--cache` flag:
|
||||
|
||||
```shell
|
||||
git rm <filename> --cache
|
||||
```
|
||||
<!-- This redirect file can be deleted after <2024-07-12>. -->
|
||||
<!-- Redirects that point to other docs in the same project expire in three months. -->
|
||||
<!-- Redirects that point to docs in a different project or site (for example, link is not relative and starts with `https:`) expire in one year. -->
|
||||
<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html -->
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ DETAILS:
|
|||
|
||||
In this tutorial you will learn how to install and securely configure a single
|
||||
node GitLab instance that can accommodate up to
|
||||
[1,000 users](../../administration/reference_architectures/1k_users.md).
|
||||
[20 RPS or 1,000 users](../../administration/reference_architectures/1k_users.md).
|
||||
|
||||
To install a single node GitLab instance and configure it to be secure:
|
||||
|
||||
|
|
|
|||
|
|
@ -80,7 +80,7 @@ As such, we generally recommend the following order:
|
|||
In this section we'll go through the core process of upgrading a multi-node GitLab environment by
|
||||
sequentially going through each as per the [upgrade order](#upgrade-order) and load balancers / HA mechanisms handle each node going down accordingly.
|
||||
|
||||
For the purposes of this guide we'll upgrade a [10,000 Reference Architecture](../administration/reference_architectures/10k_users.md) built with the Linux package.
|
||||
For the purposes of this guide we'll upgrade a [200 RPS or 10,000 Reference Architecture](../administration/reference_architectures/10k_users.md) built with the Linux package.
|
||||
|
||||
### Consul, PostgreSQL, PgBouncer and Redis
|
||||
|
||||
|
|
|
|||
|
|
@ -34,17 +34,35 @@ Some features are still in development. View details about [support for each sta
|
|||
| Assists you in determining the root cause for a pipeline failure and failed CI/CD build. | [Root cause analysis](#root-cause-analysis) | **Tier:** Ultimate <br>**Offering:** GitLab.com <br>**Status:** Experiment |
|
||||
| Assists you with predicting productivity metrics and identifying anomalies across your software development lifecycle. | [Value stream forecasting](#forecast-deployment-frequency-with-value-stream-forecasting) | **Tier:** Ultimate <br>**Offering:** GitLab.com, Self-managed, GitLab Dedicated <br>**Status:** Experiment |
|
||||
|
||||
## Enable AI/ML features
|
||||
## Controlling GitLab Duo features
|
||||
|
||||
For features listed as Experiment and Beta:
|
||||
There are two different levels at which GitLab Duo features can be controlled. The user level and the content level.
|
||||
|
||||
- These features are disabled by default.
|
||||
- To enable, a user with the Owner role for the group must [turn on this setting](group/manage.md#enable-experiment-and-beta-features).
|
||||
On GitLab.com, this setting is available for Ultimate subscriptions only.
|
||||
- These features are subject to the
|
||||
[Testing Terms of Use](https://handbook.gitlab.com/handbook/legal/testing-agreement/).
|
||||
GitLab Duo features that are generally available are always enabled for all users that have access to these features. Whether they have access depends on the tier or the add-on as stated in the previous table. [Experimental](../policy/experiment-beta-support.md#experiment) and [Beta](../policy/experiment-beta-support.md#beta) GitLab Duo features need to be enabled as follows.
|
||||
|
||||
For all self-managed features:
|
||||
Owners of projects and groups as well as administrators of self-managed instances can control if GitLab Duo features can be used with their content such as code files.
|
||||
|
||||
### Giving access to users
|
||||
|
||||
If a feature is dependent on an add-on seat such as [Code Suggestions](project/repository/code_suggestions/index.md), access to the feature can be controlled through [GitLab Duo seat assignment](../subscriptions/subscription-add-ons.md#assign-gitlab-duo-pro-seats).
|
||||
|
||||
### Enabling Beta and Experimental AI-powered features
|
||||
|
||||
Features listed as Experiment and Beta are disabled by default. These features are subject to the [Testing Agreement](https://handbook.gitlab.com/handbook/legal/testing-agreement/).
|
||||
|
||||
#### GitLab.com
|
||||
|
||||
Prerequisites
|
||||
|
||||
- You must have the Owner role in the top-level group.
|
||||
|
||||
To enable Beta and Experimental AI-powered features, use the [Experiment and Beta features checkbox](group/manage.md#enable-experiment-and-beta-features).
|
||||
|
||||
#### GitLab self-managed
|
||||
|
||||
To enable Beta and Experimental AI-powered features for GitLab versions where GitLab Duo Chat is not yet generally available, see the [GitLab Duo Chat documentation](gitlab_duo_chat.md#for-self-managed-and-gitlab-dedicated).
|
||||
|
||||
### Enable outbound connections to enable GitLab Duo features on Self-managed instances
|
||||
|
||||
- Your firewalls and HTTP/S proxy servers must allow outbound connections
|
||||
to `gitlab.com` and `cloud.gitlab.com` on port `443`.
|
||||
|
|
@ -56,15 +74,7 @@ For all self-managed features:
|
|||
Network policy restrictions on `wss://` traffic can cause issues with some GitLab Duo
|
||||
chat services. Consider policy updates to allow these services.
|
||||
|
||||
For other features:
|
||||
|
||||
- [Code Suggestions](project/repository/code_suggestions/index.md) is enabled when you purchase the
|
||||
GitLab Duo Pro add-on and assign seats to users.
|
||||
- [Chat](gitlab_duo_chat.md)
|
||||
- View [how to enable for self-managed](gitlab_duo_chat.md#for-self-managed-and-gitlab-dedicated).
|
||||
- View [how to enable for GitLab.com](gitlab_duo_chat.md#for-gitlabcom).
|
||||
|
||||
### Disable GitLab Duo features
|
||||
### Disable GitLab Duo features for specific groups or projects or an entire instance
|
||||
|
||||
DETAILS:
|
||||
**Tier:** Premium, Ultimate
|
||||
|
|
|
|||
|
|
@ -87,7 +87,7 @@ The sparkline color ranges from blue to green, where green indicates a positive
|
|||
Sparklines help you identify patterns in metric trends (such as seasonal changes) over time.
|
||||
|
||||
NOTE:
|
||||
The contributor count metric is available only on GitLab.com at the group-level. To view this metric in the comparison panel, you must [set up ClickHouse](../../integration/clickhouse.md), and enable the [feature flags](../../administration/feature_flags.md) `clickhouse_data_collection` and `event_sync_worker_for_click_house`.
|
||||
The contributor count metric is available only on GitLab.com at the group-level. To view this metric in the comparison panel, you must [set up ClickHouse](../../integration/clickhouse.md).
|
||||
|
||||
### DORA Performers score panel
|
||||
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ Prerequisites:
|
|||
- You must be a member of the project.
|
||||
- The vulnerability must be a SAST finding.
|
||||
|
||||
Learn more about [how to enable all GitLab Duo features](../../ai_features.md#enable-aiml-features).
|
||||
Learn more about [how to enable all GitLab Duo features](../../ai_features.md#enabling-beta-and-experimental-ai-powered-features).
|
||||
|
||||
To explain the vulnerability:
|
||||
|
||||
|
|
@ -110,7 +110,7 @@ Prerequisites:
|
|||
- You must be a member of the project.
|
||||
- The vulnerability must be a SAST finding.
|
||||
|
||||
Learn more about [how to enable all GitLab Duo features](../../ai_features.md#enable-aiml-features).
|
||||
Learn more about [how to enable all GitLab Duo features](../../ai_features.md#enabling-beta-and-experimental-ai-powered-features).
|
||||
|
||||
To resolve the vulnerability:
|
||||
|
||||
|
|
|
|||
|
|
@ -207,6 +207,38 @@ include: # Execute individual project's configuration (if project contains .git
|
|||
- if: $CI_PIPELINE_SOURCE != 'merge_request_event'
|
||||
```
|
||||
|
||||
#### Compliance pipelines in projects with no configuration file
|
||||
|
||||
The [example configuration](#example-configuration) above assumes that all projects contain
|
||||
a pipeline configuration file (`.gitlab-ci.yml` by default). However, in projects
|
||||
with no configuration file (and therefore no pipelines by default), the compliance pipeline
|
||||
fails because the file specified in `include:project` is required.
|
||||
|
||||
To only include a configuration file if it exists in a target project, use
|
||||
[`rules:exists:project`](../../ci/yaml/index.md#rulesexistsproject):
|
||||
|
||||
```yaml
|
||||
include: # Execute individual project's configuration
|
||||
- project: '$CI_PROJECT_PATH'
|
||||
file: '$CI_CONFIG_PATH'
|
||||
ref: '$CI_COMMIT_SHA'
|
||||
rules:
|
||||
- exists:
|
||||
paths:
|
||||
- '$CI_CONFIG_PATH'
|
||||
project: '$CI_PROJECT_PATH'
|
||||
ref: '$CI_COMMIT_SHA'
|
||||
```
|
||||
|
||||
In this example, a configuration file is only included if it exists for the given `ref`
|
||||
in the project in `exists:project: $CI_PROJECT_PATH'`.
|
||||
|
||||
You cannot use [`rules:exists` with `include`](../../ci/yaml/includes.md#include-with-rulesexists)
|
||||
to solve this problem because `include:rules:exists` searches for files in the project
|
||||
in which the `include` is defined. In compliance pipelines, the `include` from the example above
|
||||
is defined in the project hosting the compliance pipeline configuration file, not the project
|
||||
running the pipeline.
|
||||
|
||||
## Ensure compliance jobs are always run
|
||||
|
||||
Compliance pipelines [use GitLab CI/CD](../../ci/index.md) to give you an incredible amount of flexibility
|
||||
|
|
|
|||
|
|
@ -208,7 +208,8 @@ to change their user notification settings to **Watch** instead.
|
|||
|
||||
### Edit notification settings for issues, merge requests, and epics
|
||||
|
||||
To toggle notifications on an issue, merge request, or epic: on the right sidebar, turn on or off the **Notifications** toggle.
|
||||
To toggle notifications on an issue, merge request, or epic: on the right sidebar,
|
||||
turn on or off the **Notifications** (**{notifications}**) toggle.
|
||||
|
||||
When you **turn on** notifications, you start receiving notifications on each update, even if you
|
||||
haven't participated in the discussion.
|
||||
|
|
@ -241,9 +242,9 @@ epics:
|
|||
| Issue | Reopened | Subscribers and participants. |
|
||||
| Merge Request | Closed | Subscribers and participants. |
|
||||
| Merge Request | Conflict | Author and any user that has set the merge request to auto-merge. |
|
||||
| Merge Request | [Marked as ready](../project/merge_requests/drafts.md) | Watchers and participants. _[Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/15332) in GitLab 13.10._ |
|
||||
| Merge Request | [Marked as ready](../project/merge_requests/drafts.md) | Watchers and participants. |
|
||||
| Merge Request | Merged | Subscribers and participants. |
|
||||
| Merge Request | Merged when pipeline succeeds | Author, Participants, Watchers, Subscribers, and Custom notification level with this event selected. Custom notification level is ignored for Author, Watchers and Subscribers. _[Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/211961) in GitLab 13.4._ |
|
||||
| Merge Request | Merged when pipeline succeeds | Author, Participants, Watchers, Subscribers, and Custom notification level with this event selected. Custom notification level is ignored for Author, Watchers and Subscribers. |
|
||||
| Merge Request | Milestone changed | Subscribers and participants. |
|
||||
| Merge Request | Milestone removed | Subscribers and participants. |
|
||||
| Merge Request | New | Anyone mentioned by username in the description, with notification level "Mention" or higher. |
|
||||
|
|
@ -254,7 +255,7 @@ epics:
|
|||
| Merge Request | Reopened | Subscribers and participants. |
|
||||
| Merge Request | Title or description changed | Any new mentions by username. |
|
||||
| Pipeline | Failed | The author of the pipeline. |
|
||||
| Pipeline | Fixed | The author of the pipeline. Enabled by default. _[Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/24309) in GitLab 13.1._ |
|
||||
| Pipeline | Fixed | The author of the pipeline. Enabled by default. |
|
||||
| Pipeline | Successful | The author of the pipeline, with Custom notification level for successful pipelines. If the pipeline failed previously, a "Fixed pipeline" message is sent for the first successful pipeline after the failure, and then a "Successful pipeline" message for any further successful pipelines. |
|
||||
|
||||
By default, you don't receive notifications for issues, merge requests, or epics created by yourself.
|
||||
|
|
@ -263,7 +264,6 @@ To always receive notifications on your own issues, merge requests, and so on, t
|
|||
|
||||
## Notifications for unknown sign-ins
|
||||
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/27211) in GitLab 13.0.
|
||||
> - Listing the full name and username of the signed-in user [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/225183) in GitLab 15.10.
|
||||
|
||||
NOTE:
|
||||
|
|
@ -299,8 +299,6 @@ to brute force 2FA.
|
|||
|
||||
## Notifications on designs
|
||||
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/217095) in GitLab 13.6.
|
||||
|
||||
Email notifications are sent to the participants when someone comments on a design.
|
||||
|
||||
The participants are:
|
||||
|
|
@ -342,21 +340,21 @@ a merge request or an issue.
|
|||
|
||||
The following table lists all GitLab-specific email headers:
|
||||
|
||||
| Header | Description |
|
||||
| ----------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `List-Id` | The path of the project in an RFC 2919 mailing list identifier. You can use it for email organization with filters. |
|
||||
| `X-GitLab-(Resource)-ID` | The ID of the resource the notification is for. The resource, for example, can be `Issue`, `MergeRequest`, `Commit`, or another such resource. |
|
||||
| Header | Description |
|
||||
|-------------------------------|-------------|
|
||||
| `List-Id` | The path of the project in an RFC 2919 mailing list identifier. You can use it for email organization with filters. |
|
||||
| `X-GitLab-(Resource)-ID` | The ID of the resource the notification is for. The resource, for example, can be `Issue`, `MergeRequest`, `Commit`, or another such resource. |
|
||||
| `X-GitLab-(Resource)-State` | The state of the resource the notification is for. The resource can be, for example, `Issue` or `MergeRequest`. The value can be `opened`, `closed`, `merged`, or `locked`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/130967) in GitLab 16.4. |
|
||||
| `X-GitLab-ConfidentialIssue` | The boolean value indicating issue confidentiality for notifications. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/222908) in GitLab 16.0. |
|
||||
| `X-GitLab-Discussion-ID` | The ID of the thread the comment belongs to, in notification emails for comments. |
|
||||
| `X-GitLab-Group-Id` | The group's ID. Only present on notification emails for [epics](../group/epics/index.md). |
|
||||
| `X-GitLab-Group-Path` | The group's path. Only present on notification emails for [epics](../group/epics/index.md) |
|
||||
| `X-GitLab-NotificationReason` | The reason for the notification. [See possible values.](#x-gitlab-notificationreason). |
|
||||
| `X-GitLab-Pipeline-Id` | The ID of the pipeline the notification is for, in notification emails for pipelines. |
|
||||
| `X-GitLab-Project-Id` | The project's ID. |
|
||||
| `X-GitLab-Project-Path` | The project's path. |
|
||||
| `X-GitLab-Project` | The name of the project the notification belongs to. |
|
||||
| `X-GitLab-Reply-Key` | A unique token to support reply by email. |
|
||||
| `X-GitLab-ConfidentialIssue` | The boolean value indicating issue confidentiality for notifications. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/222908) in GitLab 16.0. |
|
||||
| `X-GitLab-Discussion-ID` | The ID of the thread the comment belongs to, in notification emails for comments. |
|
||||
| `X-GitLab-Group-Id` | The group's ID. Only present on notification emails for [epics](../group/epics/index.md). |
|
||||
| `X-GitLab-Group-Path` | The group's path. Only present on notification emails for [epics](../group/epics/index.md) |
|
||||
| `X-GitLab-NotificationReason` | The reason for the notification. [See possible values.](#x-gitlab-notificationreason). |
|
||||
| `X-GitLab-Pipeline-Id` | The ID of the pipeline the notification is for, in notification emails for pipelines. |
|
||||
| `X-GitLab-Project-Id` | The project's ID. |
|
||||
| `X-GitLab-Project-Path` | The project's path. |
|
||||
| `X-GitLab-Project` | The name of the project the notification belongs to. |
|
||||
| `X-GitLab-Reply-Key` | A unique token to support reply by email. |
|
||||
|
||||
### X-GitLab-NotificationReason
|
||||
|
||||
|
|
|
|||
|
|
@ -24,7 +24,8 @@ cannot merge until the **Draft** flag is removed, even if all other merge criter
|
|||
|
||||
You can flag a merge request as a draft in several ways:
|
||||
|
||||
- **Viewing a merge request**: In the upper-right corner of the merge request, select **Mark as draft**.
|
||||
- **Viewing a merge request**: In the upper-right corner of the merge request,
|
||||
select **Merge request actions** (**{ellipsis_v}**), then **Mark as draft**.
|
||||
- **Creating or editing a merge request**: Add `[Draft]`, `Draft:` or `(Draft)` to
|
||||
the beginning of the merge request's title, or select **Mark as draft**
|
||||
below the **Title** field.
|
||||
|
|
|
|||
|
|
@ -11,6 +11,10 @@ DETAILS:
|
|||
**Tier:** Free, Premium, Ultimate
|
||||
**Offering:** GitLab.com, Self-managed, GitLab Dedicated
|
||||
|
||||
> - Sidebar actions menu [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/85584) in GitLab 14.10 [with a flag](../../../administration/feature_flags.md) named `moved_mr_sidebar`. Enabled by default.
|
||||
> - Sidebar actions menu [changed](https://gitlab.com/gitlab-org/gitlab/-/issues/373757) to also move actions on issues, incidents, and epics in GitLab 16.0.
|
||||
> - [Generally available](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/127001) in GitLab 16.9. Feature flag `moved_mr_sidebar` removed.
|
||||
|
||||
A merge request (MR) is a proposal to incorporate changes from a source branch to a target branch.
|
||||
|
||||
When you open a merge request, you can visualize and collaborate on the changes before merge.
|
||||
|
|
@ -171,8 +175,6 @@ DETAILS:
|
|||
**Tier:** Premium, Ultimate
|
||||
**Offering:** GitLab.com, Self-managed, GitLab Dedicated
|
||||
|
||||
> - Moved to GitLab Premium in 13.9.
|
||||
|
||||
GitLab enables multiple assignees for merge requests, if multiple people are
|
||||
accountable for it:
|
||||
|
||||
|
|
@ -256,27 +258,6 @@ This feature works only when a merge request is merged. Selecting **Remove sourc
|
|||
after merging does not retarget open merge requests. This improvement is
|
||||
[proposed as a follow-up](https://gitlab.com/gitlab-org/gitlab/-/issues/321559).
|
||||
|
||||
## Move sidebar actions
|
||||
|
||||
<!-- When the `moved_mr_sidebar` feature flag is removed, delete this topic and update the steps for these actions
|
||||
like in https://gitlab.com/gitlab-org/gitlab/-/merge_requests/87727/diffs?diff_id=522279685#5d9afba799c4af9920dab533571d7abb8b9e9163 -->
|
||||
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/85584) in GitLab 14.10 [with a flag](../../../administration/feature_flags.md) named `moved_mr_sidebar`. Enabled by default.
|
||||
> - [Changed](https://gitlab.com/gitlab-org/gitlab/-/issues/373757) to also move actions on issues, incidents, and epics in GitLab 16.0.
|
||||
|
||||
When this feature flag is enabled, in the upper-right corner,
|
||||
**Merge request actions** (**{ellipsis_v}**) contains the following actions:
|
||||
|
||||
- The [notifications](../../profile/notifications.md#edit-notification-settings-for-issues-merge-requests-and-epics) toggle
|
||||
- Mark merge request as ready or [draft](../merge_requests/drafts.md)
|
||||
- Close merge request
|
||||
- [Lock discussion](../../discussions/index.md#prevent-comments-by-locking-the-discussion)
|
||||
- Copy reference
|
||||
|
||||
In GitLab 16.0 and later, similar action menus are available on issues, incidents, and epics.
|
||||
|
||||
When this feature flag is disabled, these actions are in the right sidebar.
|
||||
|
||||
## Merge request workflows
|
||||
|
||||
For a software developer working in a team:
|
||||
|
|
|
|||
|
|
@ -19,16 +19,16 @@ GitLab does not limit the number of private projects you can create.
|
|||
- [Manage projects](working_with_projects.md)
|
||||
- [Project visibility](../public_access.md)
|
||||
- [Project settings](working_with_projects.md)
|
||||
- [Description templates](../../user/project/description_templates.md)
|
||||
- [Project access tokens](../project/settings/project_access_tokens.md)
|
||||
- [Deploy keys](../../user/project/deploy_keys/index.md)
|
||||
- [Deploy tokens](../../user/project/deploy_tokens/index.md)
|
||||
- [Share projects](../project/members/share_project_with_groups.md)
|
||||
- [Reserved project and group names](../../user/reserved_names.md)
|
||||
- [Search](../../user/search/index.md)
|
||||
- [Badges](../../user/project/badges.md)
|
||||
- [Code intelligence](../../user/project/code_intelligence.md)
|
||||
- [Compliance](../../user/compliance/index.md)
|
||||
- [Description templates](../../user/project/description_templates.md)
|
||||
- [Deploy keys](../../user/project/deploy_keys/index.md)
|
||||
- [Deploy tokens](../../user/project/deploy_tokens/index.md)
|
||||
- [File finder](../../user/project/repository/file_finder.md)
|
||||
- [Migrating projects](../../user/project/import/index.md)
|
||||
- [Migrate projects by using file exports](../../user/project/settings/import_export.md)
|
||||
- [System notes](../../user/project/system_notes.md)
|
||||
- [Transfer a project to another namespace](../../user/project/import/index.md)
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ module Gitlab
|
|||
include ParallelScheduling
|
||||
|
||||
def execute
|
||||
page = 1
|
||||
page = page_counter.current
|
||||
|
||||
loop do
|
||||
log_info(
|
||||
|
|
@ -21,21 +21,6 @@ module Gitlab
|
|||
|
||||
break if pull_requests.empty?
|
||||
|
||||
commits_to_fetch = pull_requests.filter_map do |pull_request|
|
||||
next if already_processed?(pull_request)
|
||||
next unless pull_request.merged? || pull_request.closed?
|
||||
|
||||
[].tap do |commits|
|
||||
source_sha = pull_request.source_branch_sha
|
||||
target_sha = pull_request.target_branch_sha
|
||||
|
||||
existing_commits = repo.commits_by(oids: [source_sha, target_sha]).map(&:sha)
|
||||
|
||||
commits << source_branch_commit(source_sha, pull_request) unless existing_commits.include?(source_sha)
|
||||
commits << target_branch_commit(target_sha) unless existing_commits.include?(target_sha)
|
||||
end
|
||||
end.flatten
|
||||
|
||||
# Bitbucket Server keeps tracks of references for open pull requests in
|
||||
# refs/heads/pull-requests, but closed and merged requests get moved
|
||||
# into hidden internal refs under stash-refs/pull-requests. As a result,
|
||||
|
|
@ -43,12 +28,10 @@ module Gitlab
|
|||
#
|
||||
# This method call explicitly fetches head and start commits for affected pull requests.
|
||||
# That allows us to correctly assign diffs and commits to merge requests.
|
||||
fetch_missing_commits(commits_to_fetch)
|
||||
fetch_missing_commits(pull_requests)
|
||||
|
||||
pull_requests.each do |pull_request|
|
||||
# Needs to come before `already_processed?` as `jobs_remaining` resets to zero when the job restarts and
|
||||
# jobs_remaining needs to be the total amount of enqueued jobs
|
||||
job_waiter.jobs_remaining += 1
|
||||
job_waiter.jobs_remaining = Gitlab::Cache::Import::Caching.increment(job_waiter_remaining_cache_key)
|
||||
|
||||
next if already_processed?(pull_request)
|
||||
|
||||
|
|
@ -60,14 +43,32 @@ module Gitlab
|
|||
end
|
||||
|
||||
page += 1
|
||||
page_counter.set(page)
|
||||
end
|
||||
|
||||
page_counter.expire!
|
||||
|
||||
job_waiter
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def fetch_missing_commits(commits_to_fetch)
|
||||
def fetch_missing_commits(pull_requests)
|
||||
commits_to_fetch = pull_requests.filter_map do |pull_request|
|
||||
next if already_processed?(pull_request)
|
||||
next unless pull_request.merged? || pull_request.closed?
|
||||
|
||||
[].tap do |commits|
|
||||
source_sha = pull_request.source_branch_sha
|
||||
target_sha = pull_request.target_branch_sha
|
||||
|
||||
existing_commits = repo.commits_by(oids: [source_sha, target_sha]).map(&:sha)
|
||||
|
||||
commits << source_branch_commit(source_sha, pull_request) unless existing_commits.include?(source_sha)
|
||||
commits << target_branch_commit(target_sha) unless existing_commits.include?(target_sha)
|
||||
end
|
||||
end.flatten
|
||||
|
||||
return if commits_to_fetch.blank?
|
||||
|
||||
project.repository.fetch_remote(project.import_url, refmap: commits_to_fetch, prune: false)
|
||||
|
|
|
|||
|
|
@ -5,7 +5,8 @@ module Gitlab
|
|||
module ParallelScheduling
|
||||
include Loggable
|
||||
|
||||
attr_reader :project, :already_processed_cache_key, :job_waiter_cache_key
|
||||
attr_reader :project, :page_counter, :already_processed_cache_key,
|
||||
:job_waiter_cache_key, :job_waiter_remaining_cache_key
|
||||
|
||||
# The base cache key to use for tracking already processed objects.
|
||||
ALREADY_PROCESSED_CACHE_KEY =
|
||||
|
|
@ -15,14 +16,21 @@ module Gitlab
|
|||
JOB_WAITER_CACHE_KEY =
|
||||
'bitbucket-server-importer/job-waiter/%{project}/%{collection}'
|
||||
|
||||
# The base cache key to use for storing job waiter remaining jobs
|
||||
JOB_WAITER_REMAINING_CACHE_KEY =
|
||||
'bitbucket-server-importer/job-waiter-remaining/%{project}/%{collection}'
|
||||
|
||||
# project - An instance of `Project`.
|
||||
def initialize(project)
|
||||
@project = project
|
||||
|
||||
@page_counter = Gitlab::Import::PageCounter.new(project, collection_method, 'bitbucket-server-importer')
|
||||
@already_processed_cache_key =
|
||||
format(ALREADY_PROCESSED_CACHE_KEY, project: project.id, collection: collection_method)
|
||||
@job_waiter_cache_key =
|
||||
format(JOB_WAITER_CACHE_KEY, project: project.id, collection: collection_method)
|
||||
@job_waiter_remaining_cache_key = format(JOB_WAITER_REMAINING_CACHE_KEY, project: project.id,
|
||||
collection: collection_method)
|
||||
end
|
||||
|
||||
private
|
||||
|
|
@ -62,8 +70,9 @@ module Gitlab
|
|||
@job_waiter ||= begin
|
||||
key = Gitlab::Cache::Import::Caching.read(job_waiter_cache_key)
|
||||
key ||= Gitlab::Cache::Import::Caching.write(job_waiter_cache_key, JobWaiter.generate_key)
|
||||
jobs_remaining = Gitlab::Cache::Import::Caching.read(job_waiter_remaining_cache_key).to_i || 0
|
||||
|
||||
JobWaiter.new(0, key)
|
||||
JobWaiter.new(jobs_remaining, key)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -4,18 +4,37 @@ module Gitlab
|
|||
module Ci
|
||||
module Build
|
||||
class Rules::Rule::Clause::Exists < Rules::Rule::Clause
|
||||
include Gitlab::Utils::StrongMemoize
|
||||
|
||||
# The maximum number of patterned glob comparisons that will be
|
||||
# performed before the rule assumes that it has a match
|
||||
MAX_PATTERN_COMPARISONS = 10_000
|
||||
|
||||
WILDCARD_NESTED_PATTERN = "**/*"
|
||||
|
||||
def initialize(globs)
|
||||
@globs = Array(globs)
|
||||
def initialize(clause)
|
||||
# Remove this variable when FF `ci_support_rules_exists_paths_and_project` is removed
|
||||
@clause = clause
|
||||
|
||||
if complex_exists_enabled?
|
||||
@globs = Array(clause[:paths])
|
||||
@project_path = clause[:project]
|
||||
@ref = clause[:ref]
|
||||
else
|
||||
@globs = Array(clause)
|
||||
end
|
||||
|
||||
@top_level_only = @globs.all?(&method(:top_level_glob?))
|
||||
end
|
||||
|
||||
def satisfied_by?(_pipeline, context)
|
||||
if complex_exists_enabled? && @project_path
|
||||
# Return early to avoid redundant Gitaly calls
|
||||
return false unless @globs.any?
|
||||
|
||||
context = change_context(context)
|
||||
end
|
||||
|
||||
paths = worktree_paths(context)
|
||||
exact_globs, extension_globs, pattern_globs = separate_globs(context)
|
||||
|
||||
|
|
@ -35,6 +54,7 @@ module Gitlab
|
|||
|
||||
def expand_globs(context)
|
||||
@globs.map do |glob|
|
||||
# TODO: Replace w/ `expand_value(glob, context)` when FF `ci_support_rules_exists_paths_and_project` removed
|
||||
ExpandVariables.expand_existing(glob, -> { context.variables_hash })
|
||||
end
|
||||
end
|
||||
|
|
@ -110,6 +130,79 @@ module Gitlab
|
|||
def without_wildcard_nested_pattern(glob)
|
||||
glob.delete_prefix(WILDCARD_NESTED_PATTERN)
|
||||
end
|
||||
|
||||
def change_context(old_context)
|
||||
user = find_context_user(old_context)
|
||||
new_project = find_context_project(user, old_context)
|
||||
new_sha = find_context_sha(new_project, old_context)
|
||||
|
||||
Gitlab::Ci::Config::External::Context.new(
|
||||
project: new_project,
|
||||
user: user,
|
||||
sha: new_sha,
|
||||
variables: old_context.variables
|
||||
)
|
||||
end
|
||||
|
||||
def find_context_user(context)
|
||||
context.is_a?(Gitlab::Ci::Config::External::Context) ? context.user : context.pipeline.user
|
||||
end
|
||||
|
||||
def find_context_project(user, context)
|
||||
full_path = expand_value(@project_path, context)
|
||||
project = Project.find_by_full_path(full_path)
|
||||
|
||||
unless project
|
||||
raise Rules::Rule::Clause::ParseError,
|
||||
"rules:exists:project `#{mask_context_variables_from(context, full_path)}` is not a valid project path"
|
||||
end
|
||||
|
||||
unless Ability.allowed?(user, :read_code, project)
|
||||
raise Rules::Rule::Clause::ParseError,
|
||||
"rules:exists:project access denied to project " \
|
||||
"`#{mask_context_variables_from(context, project.full_path)}`"
|
||||
end
|
||||
|
||||
project
|
||||
end
|
||||
|
||||
def find_context_sha(project, context)
|
||||
return project.commit&.sha unless @ref
|
||||
|
||||
ref = expand_value(@ref, context)
|
||||
commit = project.commit(ref)
|
||||
|
||||
unless commit
|
||||
raise Rules::Rule::Clause::ParseError,
|
||||
"rules:exists:ref `#{mask_context_variables_from(context, ref)}` is not a valid ref " \
|
||||
"in project `#{mask_context_variables_from(context, project.full_path)}`"
|
||||
end
|
||||
|
||||
commit.sha
|
||||
end
|
||||
|
||||
def mask_context_variables_from(context, string)
|
||||
context.variables.reduce(string.dup) do |str, variable|
|
||||
if variable[:masked]
|
||||
Gitlab::Ci::MaskSecret.mask!(str, variable[:value])
|
||||
else
|
||||
str
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def expand_value(value, context)
|
||||
ExpandVariables.expand_existing(value, -> { context.variables_hash })
|
||||
end
|
||||
|
||||
def complex_exists_enabled?
|
||||
# We do not need to check the FF `ci_support_rules_exists_paths_and_project` here.
|
||||
# Instead, we can simply check if the value is a Hash because it can only be a Hash
|
||||
# if the FF was on when `Entry::Rules::Rule::Exists` was composed. The entry is
|
||||
# always composed before we reach this point. This also ensures we have the correct
|
||||
# value type before processing, which is safer.
|
||||
@clause.is_a?(Hash)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -13,11 +13,15 @@ module Gitlab
|
|||
ALLOWED_KEYS = %i[if exists when changes].freeze
|
||||
ALLOWED_WHEN = %w[never always].freeze
|
||||
|
||||
# Remove `exists` when FF `ci_support_rules_exists_paths_and_project` is removed
|
||||
attributes :if, :exists, :when
|
||||
|
||||
entry :changes, Entry::Rules::Rule::Changes,
|
||||
description: 'File change condition rule.'
|
||||
|
||||
entry :exists, Entry::Rules::Rule::Exists,
|
||||
description: 'File exists condition rule.'
|
||||
|
||||
validations do
|
||||
validates :config, presence: true
|
||||
validates :config, type: { with: Hash }
|
||||
|
|
@ -25,15 +29,26 @@ module Gitlab
|
|||
|
||||
with_options allow_nil: true do
|
||||
validates :if, expression: true
|
||||
validates :exists, array_of_strings_or_string: true, allow_blank: true
|
||||
validates :exists, array_of_strings_or_string: true, allow_blank: true, unless: :complex_exists_enabled?
|
||||
validates :when, allowed_values: { in: ALLOWED_WHEN }
|
||||
end
|
||||
end
|
||||
|
||||
def value
|
||||
config.merge(
|
||||
changes: (changes_value if changes_defined?)
|
||||
).compact
|
||||
if complex_exists_enabled?
|
||||
config.merge(
|
||||
changes: (changes_value if changes_defined?),
|
||||
exists: (exists_value if exists_defined?)
|
||||
).compact
|
||||
else
|
||||
config.merge(
|
||||
changes: (changes_value if changes_defined?)
|
||||
).compact
|
||||
end
|
||||
end
|
||||
|
||||
def complex_exists_enabled?
|
||||
::Feature.enabled?(:ci_support_rules_exists_paths_and_project, ::Feature.current_request)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -14,11 +14,15 @@ module Gitlab
|
|||
include ::Gitlab::Config::Entry::Configurable
|
||||
include ::Gitlab::Config::Entry::Attributable
|
||||
|
||||
# Remove `exists` when FF `ci_support_rules_exists_paths_and_project` is removed
|
||||
attributes :if, :exists, :when, :start_in, :allow_failure, :interruptible
|
||||
|
||||
entry :changes, Entry::Rules::Rule::Changes,
|
||||
description: 'File change condition rule.'
|
||||
|
||||
entry :exists, Entry::Rules::Rule::Exists,
|
||||
description: 'File exists condition rule.'
|
||||
|
||||
entry :variables, Entry::Variables,
|
||||
description: 'Environment variables to define for rule conditions.'
|
||||
|
||||
|
|
@ -39,7 +43,7 @@ module Gitlab
|
|||
|
||||
with_options allow_nil: true do
|
||||
validates :if, expression: true
|
||||
validates :exists, array_of_strings: true, length: { maximum: 50 }
|
||||
validates :exists, array_of_strings: true, length: { maximum: 50 }, unless: :complex_exists_enabled?
|
||||
validates :allow_failure, boolean: true
|
||||
validates :interruptible, boolean: true
|
||||
end
|
||||
|
|
@ -61,12 +65,22 @@ module Gitlab
|
|||
end
|
||||
|
||||
def value
|
||||
config.merge(
|
||||
changes: (changes_value if changes_defined?),
|
||||
variables: (variables_value if variables_defined?),
|
||||
needs: (needs_value if needs_defined?),
|
||||
auto_cancel: (auto_cancel_value if auto_cancel_defined?)
|
||||
).compact
|
||||
if complex_exists_enabled?
|
||||
config.merge(
|
||||
changes: (changes_value if changes_defined?),
|
||||
exists: (exists_value if exists_defined?),
|
||||
variables: (variables_value if variables_defined?),
|
||||
needs: (needs_value if needs_defined?),
|
||||
auto_cancel: (auto_cancel_value if auto_cancel_defined?)
|
||||
).compact
|
||||
else
|
||||
config.merge(
|
||||
changes: (changes_value if changes_defined?),
|
||||
variables: (variables_value if variables_defined?),
|
||||
needs: (needs_value if needs_defined?),
|
||||
auto_cancel: (auto_cancel_value if auto_cancel_defined?)
|
||||
).compact
|
||||
end
|
||||
end
|
||||
|
||||
def specifies_delay?
|
||||
|
|
@ -75,6 +89,10 @@ module Gitlab
|
|||
|
||||
def default
|
||||
end
|
||||
|
||||
def complex_exists_enabled?
|
||||
::Feature.enabled?(:ci_support_rules_exists_paths_and_project, ::Feature.current_request)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,69 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Gitlab
|
||||
module Ci
|
||||
class Config
|
||||
module Entry
|
||||
class Rules
|
||||
class Rule
|
||||
class Exists < ::Gitlab::Config::Entry::Simplifiable
|
||||
# TODO: We should not have the String support for `exists`.
|
||||
# Issue to remove: https://gitlab.com/gitlab-org/gitlab/-/issues/455040
|
||||
strategy :SimpleExists, if: ->(config) { config.is_a?(String) || config.is_a?(Array) || config.blank? }
|
||||
strategy :ComplexExists, if: ->(config) { config.is_a?(Hash) }
|
||||
|
||||
class SimpleExists < ::Gitlab::Config::Entry::Node
|
||||
include ::Gitlab::Config::Entry::Validatable
|
||||
|
||||
validations do
|
||||
validates :config, array_of_strings: true,
|
||||
length: { maximum: 50, too_long: 'has too many entries (maximum %{count})' },
|
||||
if: -> { config.is_a?(Array) }
|
||||
end
|
||||
|
||||
def value
|
||||
{ paths: Array(config) }
|
||||
end
|
||||
end
|
||||
|
||||
class ComplexExists < ::Gitlab::Config::Entry::Node
|
||||
include ::Gitlab::Config::Entry::Validatable
|
||||
include ::Gitlab::Config::Entry::Attributable
|
||||
|
||||
ALLOWED_KEYS = %i[paths project ref].freeze
|
||||
REQUIRED_KEYS = %i[paths].freeze
|
||||
|
||||
attributes ALLOWED_KEYS
|
||||
|
||||
validations do
|
||||
validates :config, allowed_keys: ALLOWED_KEYS
|
||||
validates :config, required_keys: REQUIRED_KEYS
|
||||
validates :config, required_keys: %i[project], if: :has_ref_value?
|
||||
|
||||
with_options allow_nil: true do
|
||||
validates :paths, array_of_strings: true,
|
||||
length: { maximum: 50, too_long: 'has too many entries (maximum %{count})' }
|
||||
validates :project, type: String
|
||||
validates :ref, type: String
|
||||
end
|
||||
end
|
||||
|
||||
def value
|
||||
config.merge(
|
||||
paths: Array(paths)
|
||||
).compact
|
||||
end
|
||||
end
|
||||
|
||||
class UnknownStrategy < ::Gitlab::Config::Entry::Node
|
||||
def errors
|
||||
["#{location} should be a string, an array of strings, or a hash"]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -34,6 +34,10 @@ module Gitlab
|
|||
# we can end with different config types like String
|
||||
next unless config.is_a?(Hash)
|
||||
|
||||
if key == :exists
|
||||
next unless ::Feature.enabled?(:ci_support_rules_exists_paths_and_project, ::Feature.current_request)
|
||||
end
|
||||
|
||||
entry_create!(key, config[key])
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -57,6 +57,7 @@ module Gitlab
|
|||
config.before_send = method(:before_send_sentry)
|
||||
config.background_worker_threads = 0
|
||||
config.send_default_pii = true
|
||||
config.send_modules = false
|
||||
config.traces_sample_rate = 0.2 if Gitlab::Utils.to_boolean(ENV['ENABLE_SENTRY_PERFORMANCE_MONITORING'])
|
||||
|
||||
yield config if block_given?
|
||||
|
|
|
|||
|
|
@ -701,19 +701,9 @@ module Gitlab
|
|||
end
|
||||
end
|
||||
|
||||
def cherry_pick(user:, commit:, branch_name:, message:, start_branch_name:, start_repository:, dry_run: false)
|
||||
args = {
|
||||
user: user,
|
||||
commit: commit,
|
||||
branch_name: branch_name,
|
||||
message: message,
|
||||
start_branch_name: start_branch_name,
|
||||
start_repository: start_repository,
|
||||
dry_run: dry_run
|
||||
}
|
||||
|
||||
def cherry_pick(...)
|
||||
wrapped_gitaly_errors do
|
||||
gitaly_operation_client.user_cherry_pick(**args)
|
||||
gitaly_operation_client.user_cherry_pick(...)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -235,15 +235,33 @@ module Gitlab
|
|||
raise Gitlab::Git::CommitError, e
|
||||
end
|
||||
|
||||
def user_cherry_pick(user:, commit:, branch_name:, message:, start_branch_name:, start_repository:, dry_run: false)
|
||||
response = call_cherry_pick_or_revert(:cherry_pick,
|
||||
user: user,
|
||||
commit: commit,
|
||||
branch_name: branch_name,
|
||||
message: message,
|
||||
start_branch_name: start_branch_name,
|
||||
start_repository: start_repository,
|
||||
dry_run: dry_run)
|
||||
# rubocop:disable Metrics/ParameterLists
|
||||
def user_cherry_pick(
|
||||
user:, commit:, branch_name:, message:,
|
||||
start_branch_name:, start_repository:, author_name: nil, author_email: nil, dry_run: false)
|
||||
|
||||
request = Gitaly::UserCherryPickRequest.new(
|
||||
repository: @gitaly_repo,
|
||||
user: Gitlab::Git::User.from_gitlab(user).to_gitaly,
|
||||
commit: commit.to_gitaly_commit,
|
||||
branch_name: encode_binary(branch_name),
|
||||
message: encode_binary(message),
|
||||
start_branch_name: encode_binary(start_branch_name.to_s),
|
||||
start_repository: start_repository.gitaly_repository,
|
||||
commit_author_name: encode_binary(author_name),
|
||||
commit_author_email: encode_binary(author_email),
|
||||
dry_run: dry_run,
|
||||
timestamp: Google::Protobuf::Timestamp.new(seconds: Time.now.utc.to_i)
|
||||
)
|
||||
|
||||
response = gitaly_client_call(
|
||||
@repository.storage,
|
||||
:operation_service,
|
||||
:user_cherry_pick,
|
||||
request,
|
||||
remote_storage: start_repository.storage,
|
||||
timeout: GitalyClient.long_timeout
|
||||
)
|
||||
|
||||
Gitlab::Git::OperationService::BranchUpdate.from_gitaly(response.branch_update)
|
||||
rescue GRPC::BadStatus => e
|
||||
|
|
@ -264,16 +282,29 @@ module Gitlab
|
|||
raise e
|
||||
end
|
||||
end
|
||||
# rubocop:enable Metrics/ParameterLists
|
||||
|
||||
def user_revert(user:, commit:, branch_name:, message:, start_branch_name:, start_repository:, dry_run: false)
|
||||
response = call_cherry_pick_or_revert(:revert,
|
||||
user: user,
|
||||
commit: commit,
|
||||
branch_name: branch_name,
|
||||
message: message,
|
||||
start_branch_name: start_branch_name,
|
||||
start_repository: start_repository,
|
||||
dry_run: dry_run)
|
||||
request = Gitaly::UserRevertRequest.new(
|
||||
repository: @gitaly_repo,
|
||||
user: Gitlab::Git::User.from_gitlab(user).to_gitaly,
|
||||
commit: commit.to_gitaly_commit,
|
||||
branch_name: encode_binary(branch_name),
|
||||
message: encode_binary(message),
|
||||
start_branch_name: encode_binary(start_branch_name.to_s),
|
||||
start_repository: start_repository.gitaly_repository,
|
||||
dry_run: dry_run,
|
||||
timestamp: Google::Protobuf::Timestamp.new(seconds: Time.now.utc.to_i)
|
||||
)
|
||||
|
||||
response = gitaly_client_call(
|
||||
@repository.storage,
|
||||
:operation_service,
|
||||
:user_revert,
|
||||
request,
|
||||
remote_storage: start_repository.storage,
|
||||
timeout: GitalyClient.long_timeout
|
||||
)
|
||||
|
||||
if response.pre_receive_error.presence
|
||||
raise Gitlab::Git::PreReceiveError, response.pre_receive_error
|
||||
|
|
@ -541,31 +572,6 @@ module Gitlab
|
|||
|
||||
private
|
||||
|
||||
def call_cherry_pick_or_revert(rpc, user:, commit:, branch_name:, message:, start_branch_name:, start_repository:, dry_run:)
|
||||
request_class = "Gitaly::User#{rpc.to_s.camelcase}Request".constantize
|
||||
|
||||
request = request_class.new(
|
||||
repository: @gitaly_repo,
|
||||
user: Gitlab::Git::User.from_gitlab(user).to_gitaly,
|
||||
commit: commit.to_gitaly_commit,
|
||||
branch_name: encode_binary(branch_name),
|
||||
message: encode_binary(message),
|
||||
start_branch_name: encode_binary(start_branch_name.to_s),
|
||||
start_repository: start_repository.gitaly_repository,
|
||||
dry_run: dry_run,
|
||||
timestamp: Google::Protobuf::Timestamp.new(seconds: Time.now.utc.to_i)
|
||||
)
|
||||
|
||||
gitaly_client_call(
|
||||
@repository.storage,
|
||||
:operation_service,
|
||||
:"user_#{rpc}",
|
||||
request,
|
||||
remote_storage: start_repository.storage,
|
||||
timeout: GitalyClient.long_timeout
|
||||
)
|
||||
end
|
||||
|
||||
# rubocop:disable Metrics/ParameterLists
|
||||
def user_commit_files_request_header(
|
||||
user, branch_name, commit_message, actions, author_email, author_name,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,14 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Gitlab
|
||||
module Regex
|
||||
module SemVer
|
||||
extend self
|
||||
|
||||
def optional_prefixed
|
||||
Regexp.new("\\Av?#{::Gitlab::Regex.unbounded_semver_regex.source}\\z",
|
||||
::Gitlab::Regex.unbounded_semver_regex.options)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -44,7 +44,7 @@ module Gitlab
|
|||
'suggestions_count' => ->(user, suggestion_set) { suggestion_set.suggestions.size },
|
||||
'co_authored_by' => ->(user, suggestions_set) {
|
||||
suggestions_set.authors.without(user).map do |author|
|
||||
"Co-authored-by: #{author.name} <#{author.commit_email_or_default}>"
|
||||
"#{Commit::CO_AUTHORED_TRAILER}: #{author.name} <#{author.commit_email_or_default}>"
|
||||
end.join("\n")
|
||||
}
|
||||
}.freeze
|
||||
|
|
|
|||
|
|
@ -40671,12 +40671,21 @@ msgstr ""
|
|||
msgid "Projects with write access"
|
||||
msgstr ""
|
||||
|
||||
msgid "ProjectsEdit|An error occurred updating this project. Please try again."
|
||||
msgstr ""
|
||||
|
||||
msgid "ProjectsEdit|Project was successfully updated."
|
||||
msgstr ""
|
||||
|
||||
msgid "ProjectsNewEdit|Must start with a letter, digit, emoji, or underscore. Can also contain periods, dashes, spaces, and parentheses."
|
||||
msgstr ""
|
||||
|
||||
msgid "ProjectsNewEdit|My awesome project"
|
||||
msgstr ""
|
||||
|
||||
msgid "ProjectsNewEdit|Project description"
|
||||
msgstr ""
|
||||
|
||||
msgid "ProjectsNewEdit|Project description (optional)"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -61414,9 +61423,6 @@ msgstr ""
|
|||
msgid "must contain only a mastodon username."
|
||||
msgstr ""
|
||||
|
||||
msgid "must follow semantic version"
|
||||
msgstr ""
|
||||
|
||||
msgid "must have a repository"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
|||
|
|
@ -77,10 +77,7 @@ module QA
|
|||
end
|
||||
end
|
||||
|
||||
describe 'OIDC', quarantine: {
|
||||
issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/429723',
|
||||
type: :flaky
|
||||
} do
|
||||
describe 'OIDC', :orchestrated do
|
||||
let(:consumer_name) { 'gitlab-oidc-consumer' }
|
||||
let(:redirect_uri) { "#{consumer_host}/users/auth/openid_connect/callback" }
|
||||
let(:scopes) { %w[openid profile email] }
|
||||
|
|
@ -125,11 +122,7 @@ module QA
|
|||
it_behaves_like 'Instance OAuth Application', :oidc, 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/405137'
|
||||
end
|
||||
|
||||
describe 'OAuth',
|
||||
quarantine: {
|
||||
issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/415011',
|
||||
type: :flaky
|
||||
} do
|
||||
describe 'OAuth', :orchestrated do
|
||||
let(:consumer_name) { 'gitlab-oauth-consumer' }
|
||||
let(:redirect_uri) { "#{consumer_host}/users/auth/gitlab/callback" }
|
||||
let(:scopes) { %w[read_user] }
|
||||
|
|
|
|||
|
|
@ -1040,125 +1040,6 @@ RSpec.describe Projects::NotesController, type: :controller, feature_category: :
|
|||
end
|
||||
end
|
||||
|
||||
describe "resolving and unresolving" do
|
||||
let(:project) { create(:project, :repository) }
|
||||
let(:merge_request) { create(:merge_request, source_project: project) }
|
||||
let(:note) { create(:diff_note_on_merge_request, noteable: merge_request, project: project) }
|
||||
|
||||
describe 'POST resolve' do
|
||||
before do
|
||||
sign_in user
|
||||
end
|
||||
|
||||
specify { expect(post(:resolve, params: request_params)).to have_request_urgency(:low) }
|
||||
|
||||
context "when the user is not authorized to resolve the note" do
|
||||
it "returns status 404" do
|
||||
post :resolve, params: request_params
|
||||
|
||||
expect(response).to have_gitlab_http_status(:not_found)
|
||||
end
|
||||
end
|
||||
|
||||
context "when the user is authorized to resolve the note" do
|
||||
before do
|
||||
project.add_developer(user)
|
||||
end
|
||||
|
||||
context "when the note is not resolvable" do
|
||||
before do
|
||||
note.update!(system: true)
|
||||
end
|
||||
|
||||
it "returns status 404" do
|
||||
post :resolve, params: request_params
|
||||
|
||||
expect(response).to have_gitlab_http_status(:not_found)
|
||||
end
|
||||
end
|
||||
|
||||
context "when the note is resolvable" do
|
||||
it "resolves the note" do
|
||||
post :resolve, params: request_params
|
||||
|
||||
expect(note.reload.resolved?).to be true
|
||||
expect(note.reload.resolved_by).to eq(user)
|
||||
end
|
||||
|
||||
it "sends notifications if all discussions are resolved" do
|
||||
expect_next_instance_of(MergeRequests::ResolvedDiscussionNotificationService) do |instance|
|
||||
expect(instance).to receive(:execute).with(merge_request)
|
||||
end
|
||||
|
||||
post :resolve, params: request_params
|
||||
end
|
||||
|
||||
it "returns the name of the resolving user" do
|
||||
post :resolve, params: request_params.merge(html: true)
|
||||
|
||||
expect(json_response["resolved_by"]).to eq(user.name)
|
||||
end
|
||||
|
||||
it "returns status 200" do
|
||||
post :resolve, params: request_params
|
||||
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'DELETE unresolve' do
|
||||
before do
|
||||
sign_in user
|
||||
|
||||
note.resolve!(user)
|
||||
end
|
||||
|
||||
specify { expect(delete(:unresolve, params: request_params)).to have_request_urgency(:low) }
|
||||
|
||||
context "when the user is not authorized to resolve the note" do
|
||||
it "returns status 404" do
|
||||
delete :unresolve, params: request_params
|
||||
|
||||
expect(response).to have_gitlab_http_status(:not_found)
|
||||
end
|
||||
end
|
||||
|
||||
context "when the user is authorized to resolve the note" do
|
||||
before do
|
||||
project.add_developer(user)
|
||||
end
|
||||
|
||||
context "when the note is not resolvable" do
|
||||
before do
|
||||
note.update!(system: true)
|
||||
end
|
||||
|
||||
it "returns status 404" do
|
||||
delete :unresolve, params: request_params
|
||||
|
||||
expect(response).to have_gitlab_http_status(:not_found)
|
||||
end
|
||||
end
|
||||
|
||||
context "when the note is resolvable" do
|
||||
it "unresolves the note" do
|
||||
delete :unresolve, params: request_params
|
||||
|
||||
expect(note.reload.resolved?).to be false
|
||||
end
|
||||
|
||||
it "returns status 200" do
|
||||
delete :unresolve, params: request_params
|
||||
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'GET outdated_line_change' do
|
||||
let(:request_params) do
|
||||
{
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
FactoryBot.define do
|
||||
factory :ci_catalog_resource_version, class: 'Ci::Catalog::Resources::Version' do
|
||||
version { '1.0.0' }
|
||||
semver { '1.0.0' }
|
||||
|
||||
catalog_resource factory: :ci_catalog_resource
|
||||
project { catalog_resource.project }
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe 'CI/CD Catalog settings', :js, feature_category: :pipeline_composition do
|
||||
RSpec.describe 'CI/CD Catalog settings', :js, feature_category: :pipeline_composition, quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/455829' do
|
||||
let_it_be(:user) { create(:user) }
|
||||
let_it_be_with_reload(:namespace) { create(:group) }
|
||||
let_it_be_with_reload(:project_with_ci_components) do
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import MockAdapter from 'axios-mock-adapter';
|
||||
import getTransferLocationsResponse from 'test_fixtures/api/projects/transfer_locations_page_1.json';
|
||||
import project from 'test_fixtures/api/projects/put.json';
|
||||
import * as projectsApi from '~/api/projects_api';
|
||||
import { DEFAULT_PER_PAGE } from '~/api';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
|
|
@ -81,6 +82,19 @@ describe('~/api/projects_api.js', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('updateProject', () => {
|
||||
it('posts to the correct URL and returns the data', async () => {
|
||||
const data = { name: 'Foo bar', description: 'Mock description' };
|
||||
const expectedUrl = `/api/v7/projects/${projectId}`;
|
||||
|
||||
mock.onPut(expectedUrl, data).replyOnce(HTTP_STATUS_OK, project);
|
||||
|
||||
await expect(projectsApi.updateProject(projectId, data)).resolves.toMatchObject({
|
||||
data: project,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('deleteProject', () => {
|
||||
beforeEach(() => {
|
||||
jest.spyOn(axios, 'delete');
|
||||
|
|
|
|||
|
|
@ -23,4 +23,29 @@ rules:interruptible as integer:
|
|||
script: exit 0
|
||||
rules:
|
||||
- if: $TEST
|
||||
interruptible: 1
|
||||
interruptible: 1
|
||||
|
||||
# invalid rules:exists
|
||||
rules:exists as string:
|
||||
rules:
|
||||
- exists: abc.md
|
||||
|
||||
rules:exists as integer:
|
||||
rules:
|
||||
- exists: 1
|
||||
|
||||
rules:exists:paths as string:
|
||||
rules:
|
||||
- exists:
|
||||
paths: abc.md
|
||||
|
||||
rules:exists:project without paths:
|
||||
rules:
|
||||
- exists:
|
||||
project: my-group/my-project
|
||||
|
||||
rules:exists:ref without project:
|
||||
rules:
|
||||
- exists:
|
||||
paths: [abc.md]
|
||||
ref: main
|
||||
|
|
|
|||
|
|
@ -38,4 +38,27 @@ rules:interruptible as boolean:
|
|||
script: exit 0
|
||||
rules:
|
||||
- if: $TEST
|
||||
interruptible: true
|
||||
interruptible: true
|
||||
|
||||
# valid rules:exists
|
||||
rules:exists as an array:
|
||||
rules:
|
||||
- exists: [abc.md, def.md]
|
||||
|
||||
rules:exists:paths:
|
||||
rules:
|
||||
- exists:
|
||||
paths: [abc.md]
|
||||
|
||||
rules:exists:project:
|
||||
rules:
|
||||
- exists:
|
||||
paths: [abc.md]
|
||||
project: my-group/my-project
|
||||
|
||||
rules:exists:project with ref:
|
||||
rules:
|
||||
- exists:
|
||||
paths: [abc.md]
|
||||
project: my-group/my-project
|
||||
ref: main
|
||||
|
|
|
|||
|
|
@ -124,3 +124,31 @@ RSpec.describe 'Projects (JavaScript fixtures)', type: :controller, feature_cate
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
RSpec.describe API::Projects, '(JavaScript fixtures)', type: :request, feature_category: :groups_and_projects do
|
||||
include ApiHelpers
|
||||
include JavaScriptFixturesHelpers
|
||||
|
||||
let_it_be(:user) { create(:user) }
|
||||
let_it_be(:project) { create(:project) }
|
||||
|
||||
before_all do
|
||||
project.add_maintainer(user)
|
||||
end
|
||||
|
||||
before do
|
||||
sign_in(user)
|
||||
end
|
||||
|
||||
it 'api/projects/put.json' do
|
||||
put api("/projects/#{project.id}", user), params: { name: "#{project.name} updated" }
|
||||
|
||||
expect(response).to be_successful
|
||||
end
|
||||
|
||||
it 'api/projects/put_validation_error.json' do
|
||||
put api("/projects/#{project.id}", user), params: { name: ".", description: 'a' * 2001 }
|
||||
|
||||
expect(response).to have_gitlab_http_status(:bad_request)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,7 +1,22 @@
|
|||
import { nextTick } from 'vue';
|
||||
import { GlSprintf } from '@gitlab/ui';
|
||||
import project from 'test_fixtures/api/projects/put.json';
|
||||
import projectValidationError from 'test_fixtures/api/projects/put_validation_error.json';
|
||||
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
|
||||
import App from '~/organizations/projects/edit/components/app.vue';
|
||||
import NewEditForm from '~/projects/components/new_edit_form.vue';
|
||||
import { updateProject } from '~/api/projects_api';
|
||||
import { visitUrlWithAlerts } from '~/lib/utils/url_utility';
|
||||
import { FORM_FIELD_NAME, FORM_FIELD_DESCRIPTION } from '~/projects/components/constants';
|
||||
import { createAlert } from '~/alert';
|
||||
import waitForPromises from 'helpers/wait_for_promises';
|
||||
import { useMockLocationHelper } from 'helpers/mock_window_location_helper';
|
||||
|
||||
jest.mock('~/api/projects_api');
|
||||
jest.mock('~/lib/utils/url_utility');
|
||||
jest.mock('~/alert');
|
||||
|
||||
useMockLocationHelper();
|
||||
|
||||
describe('OrganizationProjectsEditApp', () => {
|
||||
let wrapper;
|
||||
|
|
@ -28,6 +43,14 @@ describe('OrganizationProjectsEditApp', () => {
|
|||
|
||||
const findForm = () => wrapper.findComponent(NewEditForm);
|
||||
|
||||
const submitForm = async () => {
|
||||
findForm().vm.$emit('submit', {
|
||||
name: 'Foo bar updated',
|
||||
description: 'Mock description updated',
|
||||
});
|
||||
await nextTick();
|
||||
};
|
||||
|
||||
it('renders page title', () => {
|
||||
createComponent();
|
||||
|
||||
|
|
@ -46,4 +69,106 @@ describe('OrganizationProjectsEditApp', () => {
|
|||
cancelButtonHref: defaultProvide.projectsOrganizationPath,
|
||||
});
|
||||
});
|
||||
|
||||
describe('when form is submitted', () => {
|
||||
describe('when API is loading', () => {
|
||||
beforeEach(async () => {
|
||||
updateProject.mockResolvedValueOnce({ data: project });
|
||||
createComponent();
|
||||
|
||||
await submitForm();
|
||||
});
|
||||
|
||||
it('sets `NewEditForm` `loading` prop to `true`', () => {
|
||||
expect(findForm().props('loading')).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when API request is successful', () => {
|
||||
beforeEach(async () => {
|
||||
updateProject.mockResolvedValueOnce({ data: project });
|
||||
createComponent();
|
||||
await submitForm();
|
||||
await waitForPromises();
|
||||
});
|
||||
|
||||
it('calls API with correct variables and reloads the page with success alert', () => {
|
||||
expect(updateProject).toHaveBeenCalledWith(defaultProvide.project.id, {
|
||||
name: 'Foo bar updated',
|
||||
description: 'Mock description updated',
|
||||
});
|
||||
expect(visitUrlWithAlerts).toHaveBeenCalledWith(window.location.href, [
|
||||
{
|
||||
id: 'organization-project-successfully-updated',
|
||||
message: 'Project was successfully updated.',
|
||||
variant: 'info',
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when API request is not successful', () => {
|
||||
describe('when error is a server error', () => {
|
||||
const error = new Error();
|
||||
|
||||
beforeEach(async () => {
|
||||
updateProject.mockRejectedValueOnce(error);
|
||||
createComponent();
|
||||
await submitForm();
|
||||
await waitForPromises();
|
||||
});
|
||||
|
||||
it('displays error alert', () => {
|
||||
expect(createAlert).toHaveBeenCalledWith({
|
||||
message: 'An error occurred updating this project. Please try again.',
|
||||
error,
|
||||
captureError: true,
|
||||
});
|
||||
});
|
||||
|
||||
it('sets `loading` prop to `false`', () => {
|
||||
expect(findForm().props('loading')).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when error is a validation error', () => {
|
||||
const error = { response: { data: projectValidationError } };
|
||||
|
||||
beforeEach(async () => {
|
||||
updateProject.mockRejectedValueOnce(error);
|
||||
createComponent();
|
||||
await submitForm();
|
||||
await waitForPromises();
|
||||
});
|
||||
|
||||
it('sets `loading` prop to `false`', () => {
|
||||
expect(findForm().props('loading')).toBe(false);
|
||||
});
|
||||
|
||||
it('shows validation error for `Project name` field', () => {
|
||||
expect(findForm().props('serverValidations')).toMatchObject({
|
||||
[FORM_FIELD_NAME]:
|
||||
"Project name can contain only letters, digits, emoji, '_', '.', '+', dashes, or spaces. It must start with a letter, digit, emoji, or '_'.",
|
||||
});
|
||||
});
|
||||
|
||||
it('shows validation error for `Project description` field', () => {
|
||||
expect(findForm().props('serverValidations')).toMatchObject({
|
||||
[FORM_FIELD_DESCRIPTION]:
|
||||
'Project description is too long (maximum is 2000 characters)',
|
||||
});
|
||||
});
|
||||
|
||||
describe('when `input-field` event is fired', () => {
|
||||
beforeEach(() => {
|
||||
findForm().vm.$emit('input-field', { name: FORM_FIELD_NAME, value: 'foo' });
|
||||
});
|
||||
|
||||
it('clears server validation for that field', () => {
|
||||
expect(findForm().props('serverValidations')[FORM_FIELD_NAME]).toBeUndefined();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue