Add latest changes from gitlab-org/gitlab@master
|
@ -355,7 +355,7 @@ rspec foss-impact:
|
|||
- run_timed_command "scripts/gitaly-test-spawn"
|
||||
- source scripts/rspec_helpers.sh
|
||||
- tooling/bin/find_foss_tests tmp/matching_foss_tests.txt
|
||||
- 'if [[ -n "$(cat tmp/matching_foss_tests.txt)" ]]; then rspec_simple_job "--tag ~quarantine --tag ~geo --tag ~level:migration $(cat tmp/matching_foss_tests.txt)"; fi'
|
||||
- rspec_matched_tests tmp/matching_foss_tests.txt "--tag ~quarantine --tag ~geo --tag ~level:migration"
|
||||
artifacts:
|
||||
expire_in: 7d
|
||||
paths:
|
||||
|
|
|
@ -422,6 +422,8 @@
|
|||
rules:
|
||||
- <<: *if-not-ee
|
||||
when: never
|
||||
- <<: *if-merge-request-title-as-if-foss
|
||||
when: never
|
||||
- <<: *if-security-merge-request
|
||||
changes: *code-backstage-patterns
|
||||
- <<: *if-dot-com-gitlab-org-merge-request
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
import $ from 'jquery';
|
||||
|
||||
export default function initAvatarPicker() {
|
||||
$('.js-choose-avatar-button').on('click', function onClickAvatar() {
|
||||
const form = $(this).closest('form');
|
||||
return form.find('.js-avatar-input').click();
|
||||
});
|
||||
|
||||
$('.js-avatar-input').on('change', function onChangeAvatarInput() {
|
||||
const form = $(this).closest('form');
|
||||
const filename = $(this)
|
||||
.val()
|
||||
.replace(/^.*[\\\/]/, ''); // eslint-disable-line no-useless-escape
|
||||
return form.find('.js-avatar-filename').text(filename);
|
||||
});
|
||||
}
|
|
@ -66,9 +66,7 @@ class ListIssue {
|
|||
}
|
||||
|
||||
removeMilestone(removeMilestone) {
|
||||
if (IS_EE && removeMilestone && removeMilestone.id === this.milestone.id) {
|
||||
this.milestone = {};
|
||||
}
|
||||
boardsStore.removeIssueMilestone(this, removeMilestone);
|
||||
}
|
||||
|
||||
getLists() {
|
||||
|
|
|
@ -817,6 +817,12 @@ const boardsStore = {
|
|||
issue.isFetching[key] = value;
|
||||
},
|
||||
|
||||
removeIssueMilestone(issue, removeMilestone) {
|
||||
if (IS_EE && removeMilestone && removeMilestone.id === issue.milestone.id) {
|
||||
issue.milestone = {};
|
||||
}
|
||||
},
|
||||
|
||||
refreshIssueData(issue, obj) {
|
||||
issue.id = obj.id;
|
||||
issue.iid = obj.iid;
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
export default function initFilePickers() {
|
||||
const filePickers = document.querySelectorAll('.js-filepicker');
|
||||
|
||||
filePickers.forEach(filePicker => {
|
||||
const button = filePicker.querySelector('.js-filepicker-button');
|
||||
|
||||
button.addEventListener('click', () => {
|
||||
const form = button.closest('form');
|
||||
form.querySelector('.js-filepicker-input').click();
|
||||
});
|
||||
|
||||
const input = filePicker.querySelector('.js-filepicker-input');
|
||||
|
||||
input.addEventListener('change', () => {
|
||||
const form = input.closest('form');
|
||||
const filename = input.value.replace(/^.*[\\\/]/, ''); // eslint-disable-line no-useless-escape
|
||||
|
||||
form.querySelector('.js-filepicker-filename').textContent = filename;
|
||||
});
|
||||
});
|
||||
}
|
|
@ -1,4 +1,3 @@
|
|||
import $ from 'jquery';
|
||||
import { slugify } from './lib/utils/text_utility';
|
||||
import fetchGroupPathAvailability from '~/pages/groups/new/fetch_group_path_availability';
|
||||
import flash from '~/flash';
|
||||
|
@ -6,44 +5,69 @@ import { __ } from '~/locale';
|
|||
|
||||
export default class Group {
|
||||
constructor() {
|
||||
this.groupPath = $('#group_path');
|
||||
this.groupName = $('#group_name');
|
||||
this.parentId = $('#group_parent_id');
|
||||
this.groupPaths = Array.from(document.querySelectorAll('.js-autofill-group-path'));
|
||||
this.groupNames = Array.from(document.querySelectorAll('.js-autofill-group-name'));
|
||||
this.parentId = document.getElementById('group_parent_id');
|
||||
this.updateHandler = this.update.bind(this);
|
||||
this.resetHandler = this.reset.bind(this);
|
||||
this.updateGroupPathSlugHandler = this.updateGroupPathSlug.bind(this);
|
||||
if (this.groupName.val() === '') {
|
||||
this.groupName.on('keyup', this.updateHandler);
|
||||
this.groupPath.on('keydown', this.resetHandler);
|
||||
if (!this.parentId.val()) {
|
||||
this.groupName.on('blur', this.updateGroupPathSlugHandler);
|
||||
|
||||
this.groupNames.forEach(groupName => {
|
||||
if (groupName.value === '') {
|
||||
groupName.addEventListener('keyup', this.updateHandler);
|
||||
|
||||
if (!this.parentId.value) {
|
||||
groupName.addEventListener('blur', this.updateGroupPathSlugHandler);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this.groupPaths.forEach(groupPath => {
|
||||
groupPath.addEventListener('keydown', this.resetHandler);
|
||||
});
|
||||
}
|
||||
|
||||
update() {
|
||||
const slug = slugify(this.groupName.val());
|
||||
this.groupPath.val(slug);
|
||||
update({ currentTarget: { value: updatedValue } }) {
|
||||
const slug = slugify(updatedValue);
|
||||
|
||||
this.groupNames.forEach(element => {
|
||||
element.value = updatedValue;
|
||||
});
|
||||
this.groupPaths.forEach(element => {
|
||||
element.value = slug;
|
||||
});
|
||||
}
|
||||
|
||||
reset() {
|
||||
this.groupName.off('keyup', this.updateHandler);
|
||||
this.groupPath.off('keydown', this.resetHandler);
|
||||
this.groupName.off('blur', this.checkPathHandler);
|
||||
this.groupNames.forEach(groupName => {
|
||||
groupName.removeEventListener('keyup', this.updateHandler);
|
||||
groupName.removeEventListener('blur', this.checkPathHandler);
|
||||
});
|
||||
|
||||
this.groupPaths.forEach(groupPath => {
|
||||
groupPath.removeEventListener('keydown', this.resetHandler);
|
||||
});
|
||||
}
|
||||
|
||||
updateGroupPathSlug() {
|
||||
const slug = this.groupPath.val() || slugify(this.groupName.val());
|
||||
updateGroupPathSlug({ currentTarget: { value } = '' } = {}) {
|
||||
const slug = this.groupPaths[0]?.value || slugify(value);
|
||||
if (!slug) return;
|
||||
|
||||
fetchGroupPathAvailability(slug)
|
||||
.then(({ data }) => data)
|
||||
.then(data => {
|
||||
if (data.exists && data.suggests.length > 0) {
|
||||
const suggestedSlug = data.suggests[0];
|
||||
this.groupPath.val(suggestedSlug);
|
||||
.then(({ exists, suggests }) => {
|
||||
if (exists && suggests.length) {
|
||||
const [suggestedSlug] = suggests;
|
||||
|
||||
this.groupPaths.forEach(element => {
|
||||
element.value = suggestedSlug;
|
||||
});
|
||||
} else if (exists && !suggests.length) {
|
||||
flash(__('Unable to suggest a path. Please refresh and try again.'));
|
||||
}
|
||||
})
|
||||
.catch(() => flash(__('An error occurred while checking group path')));
|
||||
.catch(() =>
|
||||
flash(__('An error occurred while checking group path. Please refresh and try again.')),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import $ from 'jquery';
|
||||
import { parseBoolean, getCookie, setCookie, removeCookie } from '~/lib/utils/common_utils';
|
||||
import { __ } from '~/locale';
|
||||
import Tracking from '~/tracking';
|
||||
|
||||
const COOKIE_NAME = 'onboarding_issues_settings';
|
||||
|
||||
|
@ -18,6 +19,7 @@ function disposePopover(event) {
|
|||
event.preventDefault();
|
||||
this.popover('dispose');
|
||||
removeLearnGitLabCookie();
|
||||
Tracking.event('Growth::Conversion::Experiment::OnboardingIssues', 'dismiss_popover');
|
||||
}
|
||||
|
||||
const showPopover = (el, path, footer, options) => {
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
import initAvatarPicker from '~/avatar_picker';
|
||||
import initFilePickers from '~/file_pickers';
|
||||
|
||||
document.addEventListener('DOMContentLoaded', initAvatarPicker);
|
||||
document.addEventListener('DOMContentLoaded', initFilePickers);
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
import BindInOut from '../../../../behaviors/bind_in_out';
|
||||
import Group from '../../../../group';
|
||||
import initAvatarPicker from '~/avatar_picker';
|
||||
import initFilePickers from '~/file_pickers';
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
BindInOut.initAll();
|
||||
new Group(); // eslint-disable-line no-new
|
||||
initAvatarPicker();
|
||||
initFilePickers();
|
||||
|
||||
return new Group();
|
||||
});
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import initAvatarPicker from '~/avatar_picker';
|
||||
import initFilePickers from '~/file_pickers';
|
||||
import TransferDropdown from '~/groups/transfer_dropdown';
|
||||
import initConfirmDangerModal from '~/confirm_danger_modal';
|
||||
import initSettingsPanels from '~/settings_panels';
|
||||
|
@ -10,8 +10,7 @@ import groupsSelect from '~/groups_select';
|
|||
import projectSelect from '~/project_select';
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
initAvatarPicker();
|
||||
new TransferDropdown(); // eslint-disable-line no-new
|
||||
initFilePickers();
|
||||
initConfirmDangerModal();
|
||||
initSettingsPanels();
|
||||
dirtySubmitFactory(
|
||||
|
@ -24,4 +23,6 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||
groupsSelect();
|
||||
|
||||
projectSelect();
|
||||
|
||||
return new TransferDropdown();
|
||||
});
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import $ from 'jquery';
|
||||
import BindInOut from '~/behaviors/bind_in_out';
|
||||
import Group from '~/group';
|
||||
import initAvatarPicker from '~/avatar_picker';
|
||||
import GroupPathValidator from './group_path_validator';
|
||||
import initFilePickers from '~/file_pickers';
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const parentId = $('#group_parent_id');
|
||||
|
@ -10,6 +10,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||
new GroupPathValidator(); // eslint-disable-line no-new
|
||||
}
|
||||
BindInOut.initAll();
|
||||
new Group(); // eslint-disable-line no-new
|
||||
initAvatarPicker();
|
||||
initFilePickers();
|
||||
|
||||
return new Group();
|
||||
});
|
||||
|
|
|
@ -4,12 +4,12 @@ import setupTransferEdit from '~/transfer_edit';
|
|||
import initConfirmDangerModal from '~/confirm_danger_modal';
|
||||
import mountBadgeSettings from '~/pages/shared/mount_badge_settings';
|
||||
import dirtySubmitFactory from '~/dirty_submit/dirty_submit_factory';
|
||||
import initAvatarPicker from '~/avatar_picker';
|
||||
import initFilePickers from '~/file_pickers';
|
||||
import initProjectLoadingSpinner from '../shared/save_project_loader';
|
||||
import initProjectPermissionsSettings from '../shared/permissions';
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
initAvatarPicker();
|
||||
initFilePickers();
|
||||
initConfirmDangerModal();
|
||||
initSettingsPanels();
|
||||
mountBadgeSettings(PROJECT_BADGE);
|
||||
|
|
|
@ -55,7 +55,7 @@ export default {
|
|||
{
|
||||
metric: 'redis',
|
||||
header: s__('PerformanceBar|Redis calls'),
|
||||
keys: ['cmd'],
|
||||
keys: ['cmd', 'instance'],
|
||||
},
|
||||
{
|
||||
metric: 'es',
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Types
|
||||
module Snippets
|
||||
class FileInputActionEnum < BaseEnum
|
||||
graphql_name 'SnippetFileInputActionEnum'
|
||||
description 'Type of a snippet file input action'
|
||||
|
||||
value 'create', value: :create
|
||||
value 'update', value: :update
|
||||
value 'delete', value: :delete
|
||||
value 'move', value: :move
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,26 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Types
|
||||
module Snippets
|
||||
class FileInputType < BaseInputObject # rubocop:disable Graphql/AuthorizeTypes
|
||||
graphql_name 'SnippetFileInputType'
|
||||
description 'Represents an action to perform over a snippet file'
|
||||
|
||||
argument :action, Types::Snippets::FileInputActionEnum,
|
||||
description: 'Type of input action',
|
||||
required: true
|
||||
|
||||
argument :previous_path, GraphQL::STRING_TYPE,
|
||||
description: 'Previous path of the snippet file',
|
||||
required: false
|
||||
|
||||
argument :file_path, GraphQL::STRING_TYPE,
|
||||
description: 'Path of the snippet file',
|
||||
required: true
|
||||
|
||||
argument :content, GraphQL::STRING_TYPE,
|
||||
description: 'Snippet file content',
|
||||
required: false
|
||||
end
|
||||
end
|
||||
end
|
|
@ -26,6 +26,17 @@ module ApplicationSettingsHelper
|
|||
end
|
||||
end
|
||||
|
||||
def storage_weights
|
||||
ApplicationSetting.repository_storages_weighted_attributes.map do |attribute|
|
||||
storage = attribute.to_s.delete_prefix('repository_storages_weighted_')
|
||||
{
|
||||
name: attribute,
|
||||
label: storage,
|
||||
value: @application_setting.repository_storages_weighted[storage] || 0
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
def all_protocols_enabled?
|
||||
Gitlab::CurrentSettings.enabled_git_access_protocol.blank?
|
||||
end
|
||||
|
|
|
@ -734,7 +734,7 @@ module ProjectsHelper
|
|||
end
|
||||
|
||||
def native_code_navigation_enabled?(project)
|
||||
Feature.enabled?(:code_navigation, project)
|
||||
Feature.enabled?(:code_navigation, project, default_enabled: true)
|
||||
end
|
||||
|
||||
def show_visibility_confirm_modal?(project)
|
||||
|
|
|
@ -156,7 +156,6 @@ module AlertManagement
|
|||
end
|
||||
|
||||
def execute_services
|
||||
return unless Feature.enabled?(:alert_slack_event, project)
|
||||
return unless project.has_active_services?(:alert_hooks)
|
||||
|
||||
project.execute_services(hook_data, :alert_hooks)
|
||||
|
|
|
@ -16,6 +16,9 @@ module Ci
|
|||
has_many :sourced_pipelines, class_name: "::Ci::Sources::Pipeline",
|
||||
foreign_key: :source_job_id
|
||||
|
||||
has_one :sourced_pipeline, class_name: "::Ci::Sources::Pipeline", foreign_key: :source_job_id
|
||||
has_one :downstream_pipeline, through: :sourced_pipeline, source: :pipeline
|
||||
|
||||
validates :ref, presence: true
|
||||
|
||||
# rubocop:disable Cop/ActiveRecordSerialize
|
||||
|
|
|
@ -41,6 +41,7 @@ module Ci
|
|||
has_many :statuses, class_name: 'CommitStatus', foreign_key: :commit_id, inverse_of: :pipeline
|
||||
has_many :latest_statuses_ordered_by_stage, -> { latest.order(:stage_idx, :stage) }, class_name: 'CommitStatus', foreign_key: :commit_id, inverse_of: :pipeline
|
||||
has_many :processables, class_name: 'Ci::Processable', foreign_key: :commit_id, inverse_of: :pipeline
|
||||
has_many :bridges, class_name: 'Ci::Bridge', foreign_key: :commit_id, inverse_of: :pipeline
|
||||
has_many :builds, foreign_key: :commit_id, inverse_of: :pipeline
|
||||
has_many :job_artifacts, through: :builds
|
||||
has_many :trigger_requests, dependent: :destroy, foreign_key: :commit_id # rubocop:disable Cop/ActiveRecordDependent
|
||||
|
|
|
@ -30,7 +30,8 @@ module Ci
|
|||
webide: 9,
|
||||
merge_request_event: 10,
|
||||
external_pull_request_event: 11,
|
||||
parent_pipeline: 12
|
||||
parent_pipeline: 12,
|
||||
ondemand_scan: 13
|
||||
}
|
||||
end
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@ module Ci
|
|||
variables.concat(deployment_variables(environment: environment))
|
||||
variables.concat(yaml_variables)
|
||||
variables.concat(user_variables)
|
||||
variables.concat(dependency_variables) if Feature.enabled?(:ci_dependency_variables, project)
|
||||
variables.concat(dependency_variables)
|
||||
variables.concat(secret_instance_variables)
|
||||
variables.concat(secret_group_variables)
|
||||
variables.concat(secret_project_variables(environment: environment))
|
||||
|
|
|
@ -1198,14 +1198,6 @@ class Project < ApplicationRecord
|
|||
get_issue(issue_id)
|
||||
end
|
||||
|
||||
def default_issue_tracker
|
||||
gitlab_issue_tracker_service || create_gitlab_issue_tracker_service
|
||||
end
|
||||
|
||||
def issues_tracker
|
||||
external_issue_tracker || default_issue_tracker
|
||||
end
|
||||
|
||||
def external_issue_reference_pattern
|
||||
external_issue_tracker.class.reference_pattern(only_long: issues_enabled?)
|
||||
end
|
||||
|
|
|
@ -414,7 +414,7 @@ class JiraService < IssueTrackerService
|
|||
# Handle errors when doing Jira API calls
|
||||
def jira_request
|
||||
yield
|
||||
rescue Timeout::Error, Errno::EINVAL, Errno::ECONNRESET, Errno::ECONNREFUSED, URI::InvalidURIError, JIRA::HTTPError, OpenSSL::SSL::SSLError => error
|
||||
rescue => error
|
||||
@error = error
|
||||
log_error("Error sending message", client_url: client_url, error: @error.message)
|
||||
nil
|
||||
|
|
|
@ -25,7 +25,7 @@ class SlackService < ChatNotificationService
|
|||
|
||||
def supported_events
|
||||
additional = []
|
||||
additional << 'alert' if Feature.enabled?(:alert_slack_event, project)
|
||||
additional << 'alert'
|
||||
|
||||
super + additional
|
||||
end
|
||||
|
|
|
@ -78,7 +78,7 @@ class DiffsEntity < Grape::Entity
|
|||
options[:merge_request_diffs]
|
||||
end
|
||||
|
||||
expose :definition_path_prefix, if: -> (diff_file) { Feature.enabled?(:code_navigation, merge_request.project) } do |diffs|
|
||||
expose :definition_path_prefix, if: -> (diff_file) { Feature.enabled?(:code_navigation, merge_request.project, default_enabled: true) } do |diffs|
|
||||
project_blob_path(merge_request.project, diffs.diff_refs&.head_sha)
|
||||
end
|
||||
|
||||
|
@ -89,7 +89,7 @@ class DiffsEntity < Grape::Entity
|
|||
private
|
||||
|
||||
def code_navigation_path(diffs)
|
||||
return unless Feature.enabled?(:code_navigation, merge_request.project)
|
||||
return unless Feature.enabled?(:code_navigation, merge_request.project, default_enabled: true)
|
||||
|
||||
Gitlab::CodeNavigationPath.new(merge_request.project, diffs.diff_refs&.head_sha)
|
||||
end
|
||||
|
|
|
@ -37,7 +37,7 @@ class PaginatedDiffEntity < Grape::Entity
|
|||
private
|
||||
|
||||
def code_navigation_path(diffs)
|
||||
return unless Feature.enabled?(:code_navigation, merge_request.project)
|
||||
return unless Feature.enabled?(:code_navigation, merge_request.project, default_enabled: true)
|
||||
|
||||
Gitlab::CodeNavigationPath.new(merge_request.project, diffs.diff_refs&.head_sha)
|
||||
end
|
||||
|
|
|
@ -36,7 +36,7 @@ module Ci
|
|||
|
||||
def code_navigation_enabled?
|
||||
strong_memoize(:code_navigation_enabled) do
|
||||
Feature.enabled?(:code_navigation, job.project)
|
||||
Feature.enabled?(:code_navigation, job.project, default_enabled: true)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
%fieldset
|
||||
.form-group
|
||||
= f.label :issues_create_limit, 'Max requests per second per user', class: 'label-bold'
|
||||
= f.label :issues_create_limit, 'Max requests per minute per user', class: 'label-bold'
|
||||
= f.number_field :issues_create_limit, class: 'form-control'
|
||||
|
||||
= f.submit 'Save changes', class: "btn btn-success", data: { qa_selector: 'save_changes_button' }
|
||||
|
|
|
@ -15,12 +15,12 @@
|
|||
.form-group
|
||||
.form-text
|
||||
%p.text-secondary
|
||||
= _('Select the configured storage available for new repositories to be placed on.')
|
||||
= _('Select a weight for the storage new repositories will be placed on.')
|
||||
= link_to icon('question-circle'), help_page_path('administration/repository_storage_paths')
|
||||
.form-check
|
||||
- @application_setting.repository_storages_weighted.each_key do |storage|
|
||||
= f.text_field "repository_storages_weighted_#{storage}".to_sym, class: 'form-text-input'
|
||||
= f.label storage, storage, class: 'label-bold form-check-label'
|
||||
- storage_weights.each do |attribute|
|
||||
= f.text_field attribute[:name], class: 'form-text-input', value: attribute[:value]
|
||||
= f.label attribute[:label], attribute[:label], class: 'label-bold form-check-label'
|
||||
%br
|
||||
|
||||
= f.submit _('Save changes'), class: "btn btn-success qa-save-changes-button"
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
- parent = @group.parent
|
||||
- group_path = root_url
|
||||
- group_path << parent.full_path + '/' if parent
|
||||
|
||||
= form_with url: import_gitlab_group_path, class: 'group-form gl-show-field-errors', multipart: true do |f|
|
||||
= form_errors(@group)
|
||||
|
||||
.row
|
||||
.form-group.group-name.col-sm-12
|
||||
= f.label :name, _('Group name'), class: 'label-bold'
|
||||
= f.text_field :name, placeholder: s_('GroupsNew|My Awesome Group'), class: 'js-autofill-group-name form-control input-lg',
|
||||
required: true,
|
||||
title: _('Please fill in a descriptive name for your group.'),
|
||||
autofocus: true
|
||||
|
||||
.row
|
||||
.form-group.col-xs-12.col-sm-8
|
||||
= f.label :path, _('Group URL'), class: 'label-bold'
|
||||
.input-group.gl-field-error-anchor
|
||||
.group-root-path.input-group-prepend.has-tooltip{ title: group_path, :'data-placement' => 'bottom' }
|
||||
.input-group-text
|
||||
%span
|
||||
= root_url
|
||||
- if parent
|
||||
%strong= parent.full_path + '/'
|
||||
= f.hidden_field :parent_id, value: parent&.id
|
||||
= f.text_field :path, placeholder: 'my-awesome-group', class: 'form-control js-validate-group-path js-autofill-group-path',
|
||||
id: 'import_group_path',
|
||||
required: true,
|
||||
pattern: Gitlab::PathRegex::NAMESPACE_FORMAT_REGEX_JS,
|
||||
title: _('Please choose a group URL with no special characters.'),
|
||||
"data-bind-in" => "#{'create_chat_team' if Gitlab.config.mattermost.enabled}"
|
||||
%p.validation-error.gl-field-error.field-validation.hide
|
||||
= _('Group path is already taken. Suggestions: ')
|
||||
%span.gl-path-suggestions
|
||||
%p.validation-success.gl-field-success.field-validation.hide= _('Group path is available.')
|
||||
%p.validation-pending.gl-field-error-ignore.field-validation.hide= _('Checking group path availability...')
|
||||
|
||||
.row
|
||||
.form-group.col-md-12
|
||||
= s_('GroupsNew|To copy a GitLab group between installations, navigate to the group settings page for the original installation, generate an export file, and upload it here.')
|
||||
.row
|
||||
.form-group.col-sm-12
|
||||
= f.label :file, s_('GroupsNew|GitLab group export'), class: 'label-bold'
|
||||
%div
|
||||
= render 'shared/file_picker_button', f: f, field: :file, help_text: nil
|
||||
|
||||
.row
|
||||
.form-actions.col-sm-12
|
||||
= f.submit s_('GroupsNew|Import group'), class: 'btn btn-success'
|
||||
= link_to _('Cancel'), new_group_path, class: 'btn btn-cancel'
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
= form_errors(@group)
|
||||
= render 'shared/group_form', f: f, autofocus: true
|
||||
|
||||
.row
|
||||
.form-group.group-description-holder.col-sm-12
|
||||
= f.label :avatar, _("Group avatar"), class: 'label-bold'
|
||||
%div
|
||||
= render 'shared/choose_avatar_button', f: f
|
||||
|
||||
.form-group.col-sm-12
|
||||
%label.label-bold
|
||||
= _('Visibility level')
|
||||
%p
|
||||
= _('Who will be able to see this group?')
|
||||
= link_to _('View the documentation'), help_page_path("public_access/public_access"), target: '_blank'
|
||||
= render 'shared/visibility_level', f: f, visibility_level: default_group_visibility, can_change_visibility_level: true, form_model: @group, with_label: false
|
||||
|
||||
= render 'create_chat_team', f: f if Gitlab.config.mattermost.enabled
|
||||
|
||||
.form-actions.col-sm-12
|
||||
= f.submit _('Create group'), class: "btn btn-success"
|
||||
= link_to _('Cancel'), dashboard_groups_path, class: 'btn btn-cancel'
|
|
@ -2,43 +2,44 @@
|
|||
- @hide_top_links = true
|
||||
- page_title _('New Group')
|
||||
- header_title _("Groups"), dashboard_groups_path
|
||||
- active_tab = local_assigns.fetch(:active_tab, 'create')
|
||||
|
||||
.page-title-holder.d-flex.align-items-center
|
||||
%h1.page-title= _('New group')
|
||||
.row.prepend-top-default
|
||||
.col-lg-3.profile-settings-sidebar
|
||||
%p
|
||||
- group_docs_path = help_page_path('user/group/index')
|
||||
- group_docs_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: group_docs_path }
|
||||
= s_('%{group_docs_link_start}Groups%{group_docs_link_end} allow you to manage and collaborate across multiple projects. Members of a group have access to all of its projects.').html_safe % { group_docs_link_start: group_docs_link_start, group_docs_link_end: '</a>'.html_safe }
|
||||
%p
|
||||
- subgroup_docs_path = help_page_path('user/group/subgroups/index')
|
||||
- subgroup_docs_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: subgroup_docs_path }
|
||||
= s_('Groups can also be nested by creating %{subgroup_docs_link_start}subgroups%{subgroup_docs_link_end}.').html_safe % { subgroup_docs_link_start: subgroup_docs_link_start, subgroup_docs_link_end: '</a>'.html_safe }
|
||||
%p
|
||||
= _('Projects that belong to a group are prefixed with the group namespace. Existing projects may be moved into a group.')
|
||||
.group-edit-container.prepend-top-default
|
||||
.row
|
||||
.col-lg-3.group-settings-sidebar
|
||||
%h4.prepend-top-0
|
||||
= _('New group')
|
||||
%p
|
||||
- group_docs_path = help_page_path('user/group/index')
|
||||
- group_docs_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: group_docs_path }
|
||||
= s_('%{group_docs_link_start}Groups%{group_docs_link_end} allow you to manage and collaborate across multiple projects. Members of a group have access to all of its projects.').html_safe % { group_docs_link_start: group_docs_link_start, group_docs_link_end: '</a>'.html_safe }
|
||||
%p
|
||||
- subgroup_docs_path = help_page_path('user/group/subgroups/index')
|
||||
- subgroup_docs_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: subgroup_docs_path }
|
||||
= s_('Groups can also be nested by creating %{subgroup_docs_link_start}subgroups%{subgroup_docs_link_end}.').html_safe % { subgroup_docs_link_start: subgroup_docs_link_start, subgroup_docs_link_end: '</a>'.html_safe }
|
||||
%p
|
||||
= _('Projects that belong to a group are prefixed with the group namespace. Existing projects may be moved into a group.')
|
||||
|
||||
.col-lg-9
|
||||
= form_for @group, html: { class: 'group-form gl-show-field-errors' } do |f|
|
||||
= form_errors(@group)
|
||||
= render 'shared/group_form', f: f, autofocus: true
|
||||
.col-lg-9.js-toggle-container
|
||||
%ul.nav.nav-tabs.nav-links.gitlab-tabs{ role: 'tablist' }
|
||||
%li.nav-item{ role: 'presentation' }
|
||||
%a.nav-link.active{ href: '#create-group-pane', id: 'create-group-tab', role: 'tab', data: { toggle: 'tab', track_label: 'create_group', track_event: 'click_tab', track_value: '' } }
|
||||
%span.d-none.d-sm-block= s_('GroupsNew|Create group')
|
||||
%span.d-block.d-sm-none= s_('GroupsNew|Create')
|
||||
%li.nav-item{ role: 'presentation' }
|
||||
%a.nav-link{ href: '#import-group-pane', id: 'import-group-tab', role: 'tab', data: { toggle: 'tab', track_label: 'import_group', track_event: 'click_tab', track_value: '' } }
|
||||
%span.d-none.d-sm-block= s_('GroupsNew|Import group')
|
||||
%span.d-block.d-sm-none= s_('GroupsNew|Import')
|
||||
|
||||
.row
|
||||
.form-group.group-description-holder.col-sm-12
|
||||
= f.label :avatar, _("Group avatar"), class: 'label-bold'
|
||||
%div
|
||||
= render 'shared/choose_avatar_button', f: f
|
||||
.tab-content.gitlab-tab-content
|
||||
.tab-pane.js-toggle-container{ id: 'create-group-pane', class: active_when(active_tab == 'create'), role: 'tabpanel' }
|
||||
= form_for @group, html: { class: 'group-form gl-show-field-errors' } do |f|
|
||||
= render 'new_group_fields', f: f, group_name_id: 'create-group-name'
|
||||
|
||||
.form-group.col-sm-12
|
||||
%label.label-bold
|
||||
= _('Visibility level')
|
||||
%p
|
||||
= _('Who will be able to see this group?')
|
||||
= link_to _('View the documentation'), help_page_path("public_access/public_access"), target: '_blank'
|
||||
= render 'shared/visibility_level', f: f, visibility_level: default_group_visibility, can_change_visibility_level: true, form_model: @group, with_label: false
|
||||
|
||||
= render 'create_chat_team', f: f if Gitlab.config.mattermost.enabled
|
||||
|
||||
.form-actions
|
||||
= f.submit _('Create group'), class: "btn btn-success"
|
||||
= link_to _('Cancel'), dashboard_groups_path, class: 'btn btn-cancel'
|
||||
.tab-pane.js-toggle-container{ id: 'import-group-pane', class: active_when(active_tab) == 'import', role: 'tabpanel' }
|
||||
- if import_sources_enabled?
|
||||
= render 'import_group_pane', active_tab: active_tab, autofocus: true
|
||||
- else
|
||||
.nothing-here-block
|
||||
%h4= s_('GroupsNew|No import options available')
|
||||
%p= s_('GroupsNew|Contact an administrator to enable options for importing your group.')
|
||||
|
|
|
@ -1,4 +1 @@
|
|||
%button.btn.js-choose-avatar-button{ type: 'button' }= _("Choose file…")
|
||||
%span.file_name.js-avatar-filename= _("No file chosen")
|
||||
= f.file_field :avatar, class: "js-avatar-input hidden"
|
||||
.form-text.text-muted= _("The maximum file size allowed is 200KB.")
|
||||
= render 'shared/file_picker_button', f: f, field: :avatar, help_text: _("The maximum file size allowed is 200KB.")
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
%span.js-filepicker
|
||||
%button.btn.js-filepicker-button{ type: 'button' }= _("Choose file…")
|
||||
%span.file_name.js-filepicker-filename= _("No file chosen")
|
||||
= f.file_field field, class: "js-filepicker-input hidden"
|
||||
- if help_text.present?
|
||||
.form-text.text-muted= help_text
|
|
@ -6,7 +6,7 @@
|
|||
.form-group.group-name-holder.col-sm-12
|
||||
= f.label :name, class: 'label-bold' do
|
||||
= _("Group name")
|
||||
= f.text_field :name, placeholder: _('My Awesome Group'), class: 'form-control input-lg',
|
||||
= f.text_field :name, placeholder: _('My Awesome Group'), class: 'js-autofill-group-name form-control input-lg',
|
||||
required: true,
|
||||
title: _('Please fill in a descriptive name for your group.'),
|
||||
autofocus: true
|
||||
|
@ -22,7 +22,7 @@
|
|||
- if parent
|
||||
%strong= parent.full_path + '/'
|
||||
= f.hidden_field :parent_id
|
||||
= f.text_field :path, placeholder: _('my-awesome-group'), class: 'form-control js-validate-group-path',
|
||||
= f.text_field :path, placeholder: _('my-awesome-group'), class: 'form-control js-validate-group-path js-autofill-group-path',
|
||||
autofocus: local_assigns[:autofocus] || false, required: true,
|
||||
pattern: Gitlab::PathRegex::NAMESPACE_FORMAT_REGEX_JS,
|
||||
title: _('Please choose a group URL with no special characters.'),
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add system check for CI JWT signing key
|
||||
merge_request: 33920
|
||||
author:
|
||||
type: added
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Enable CI Inheriting Env Variables feature
|
||||
merge_request: 34495
|
||||
author:
|
||||
type: added
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add route for the lost-and-found group and update the route of orphaned projects
|
||||
merge_request: 34285
|
||||
author:
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Enable Slack notifications for alerts
|
||||
merge_request: 34038
|
||||
author:
|
||||
type: added
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Remove removeMilestone logic from issue model
|
||||
merge_request: 32253
|
||||
author: nuwe1
|
||||
type: other
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add API endpoint for listing bridge jobs.
|
||||
merge_request: 31370
|
||||
author: Abhijith Sivarajan
|
||||
type: added
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Update issue limits template to use minutes
|
||||
merge_request: 34254
|
||||
author:
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add GraphQL snippet FileInputType
|
||||
merge_request: 34442
|
||||
author:
|
||||
type: other
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add native code intelligence
|
||||
merge_request: 34542
|
||||
author:
|
||||
type: added
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Show all storages in settings
|
||||
merge_request: 34093
|
||||
author:
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Create Group import UI for creating new Groups
|
||||
merge_request: 29271
|
||||
author:
|
||||
type: added
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
title: Add detailed logs of each Redis instance usage during job execution and web
|
||||
requests
|
||||
merge_request: 34110
|
||||
author:
|
||||
type: added
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add Verify/FailFast CI template
|
||||
merge_request: 31812
|
||||
author:
|
||||
type: added
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Show Redis instance in performance bar
|
||||
merge_request: 34377
|
||||
author:
|
||||
type: changed
|
|
@ -17,6 +17,8 @@ class CreateMergeRequestAssigneesTable < ActiveRecord::Migration[5.0]
|
|||
end
|
||||
|
||||
def down
|
||||
# rubocop:disable Migration/DropTable
|
||||
drop_table :merge_request_assignees
|
||||
# rubocop:enable Migration/DropTable
|
||||
end
|
||||
end
|
||||
|
|
|
@ -193,7 +193,9 @@ class BackportEnterpriseSchema < ActiveRecord::Migration[5.0]
|
|||
end
|
||||
|
||||
def drop_table_if_exists(table)
|
||||
# rubocop:disable Migration/DropTable
|
||||
drop_table(table) if table_exists?(table)
|
||||
# rubocop:enable Migration/DropTable
|
||||
end
|
||||
|
||||
def add_column_with_default_if_not_exists(table, name, *args)
|
||||
|
|
|
@ -15,6 +15,8 @@ class CreateMilestoneReleasesTable < ActiveRecord::Migration[5.2]
|
|||
end
|
||||
|
||||
def down
|
||||
# rubocop:disable Migration/DropTable
|
||||
drop_table :milestone_releases
|
||||
# rubocop:enable Migration/DropTable
|
||||
end
|
||||
end
|
||||
|
|
|
@ -24,6 +24,8 @@ class CreateDescriptionVersions < ActiveRecord::Migration[5.2]
|
|||
def down
|
||||
remove_column :system_note_metadata, :description_version_id
|
||||
|
||||
# rubocop:disable Migration/DropTable
|
||||
drop_table :description_versions
|
||||
# rubocop:enable Migration/DropTable
|
||||
end
|
||||
end
|
||||
|
|
|
@ -23,6 +23,8 @@ class AddGroupDeletionSchedules < ActiveRecord::Migration[5.2]
|
|||
end
|
||||
|
||||
def down
|
||||
# rubocop:disable Migration/DropTable
|
||||
drop_table :group_deletion_schedules
|
||||
# rubocop:enable Migration/DropTable
|
||||
end
|
||||
end
|
||||
|
|
|
@ -23,6 +23,8 @@ class CreateGitlabSubscriptionHistories < ActiveRecord::Migration[5.2]
|
|||
end
|
||||
|
||||
def down
|
||||
# rubocop:disable Migration/DropTable
|
||||
drop_table :gitlab_subscription_histories
|
||||
# rubocop:enable Migration/DropTable
|
||||
end
|
||||
end
|
||||
|
|
|
@ -7,7 +7,9 @@ class DropAnalyticsRepositoryFilesTable < ActiveRecord::Migration[5.2]
|
|||
|
||||
def up
|
||||
# Requires ExclusiveLock on the table. Not in use, no records, no FKs.
|
||||
# rubocop:disable Migration/DropTable
|
||||
drop_table :analytics_repository_files
|
||||
# rubocop:enable Migration/DropTable
|
||||
end
|
||||
|
||||
def down
|
||||
|
|
|
@ -7,7 +7,9 @@ class DropAnalyticsRepositoryFileCommitsTable < ActiveRecord::Migration[5.2]
|
|||
|
||||
def up
|
||||
# Requires ExclusiveLock on the table. Not in use, no records, no FKs.
|
||||
# rubocop:disable Migration/DropTable
|
||||
drop_table :analytics_repository_file_commits
|
||||
# rubocop:enable Migration/DropTable
|
||||
end
|
||||
|
||||
def down
|
||||
|
|
|
@ -7,7 +7,9 @@ class DropAnalyticsRepositoryFileEditsTable < ActiveRecord::Migration[5.2]
|
|||
|
||||
def up
|
||||
# Requires ExclusiveLock on the table. Not in use, no records, no FKs.
|
||||
# rubocop:disable Migration/DropTable
|
||||
drop_table :analytics_repository_file_edits if table_exists?(:analytics_repository_file_edits) # this table might be already dropped on development environment
|
||||
# rubocop:enable Migration/DropTable
|
||||
end
|
||||
|
||||
def down
|
||||
|
|
|
@ -20,7 +20,9 @@ class AddCanonicalEmails < ActiveRecord::Migration[6.0]
|
|||
|
||||
def down
|
||||
with_lock_retries do
|
||||
# rubocop:disable Migration/DropTable
|
||||
drop_table(:user_canonical_emails)
|
||||
# rubocop:enable Migration/DropTable
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -8,6 +8,7 @@ class DropForkedProjectLinksTable < ActiveRecord::Migration[6.0]
|
|||
DOWNTIME = false
|
||||
|
||||
def change
|
||||
# rubocop:disable Migration/DropTable
|
||||
drop_table "forked_project_links", id: :serial do |t|
|
||||
t.integer "forked_to_project_id", null: false
|
||||
t.integer "forked_from_project_id", null: false
|
||||
|
@ -15,5 +16,6 @@ class DropForkedProjectLinksTable < ActiveRecord::Migration[6.0]
|
|||
t.datetime "updated_at"
|
||||
t.index ["forked_to_project_id"], name: "index_forked_project_links_on_forked_to_project_id", unique: true
|
||||
end
|
||||
# rubocop:enable Migration/DropTable
|
||||
end
|
||||
end
|
||||
|
|
|
@ -20,7 +20,9 @@ class CreateUserDetails < ActiveRecord::Migration[6.0]
|
|||
|
||||
def down
|
||||
with_lock_retries do
|
||||
# rubocop:disable Migration/DropTable
|
||||
drop_table :user_details
|
||||
# rubocop:enable Migration/DropTable
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -19,7 +19,9 @@ class CreateUserHighestRoles < ActiveRecord::Migration[6.0]
|
|||
|
||||
def down
|
||||
with_lock_retries do
|
||||
# rubocop:disable Migration/DropTable
|
||||
drop_table :user_highest_roles
|
||||
# rubocop:enable Migration/DropTable
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -30,6 +30,8 @@ class CreateDiffNotePositions < ActiveRecord::Migration[6.0]
|
|||
# rubocop:enable Migration/AddLimitToTextColumns
|
||||
|
||||
def down
|
||||
# rubocop:disable Migration/DropTable
|
||||
drop_table :diff_note_positions
|
||||
# rubocop:enable Migration/DropTable
|
||||
end
|
||||
end
|
||||
|
|
|
@ -9,7 +9,9 @@ class RecreateCiRef < ActiveRecord::Migration[6.0]
|
|||
|
||||
def up
|
||||
with_lock_retries do
|
||||
# rubocop:disable Migration/DropTable
|
||||
drop_table :ci_refs
|
||||
# rubocop:enable Migration/DropTable
|
||||
|
||||
create_table :ci_refs do |t|
|
||||
t.references :project, null: false, index: false, foreign_key: { on_delete: :cascade }, type: :bigint
|
||||
|
@ -23,7 +25,9 @@ class RecreateCiRef < ActiveRecord::Migration[6.0]
|
|||
|
||||
def down
|
||||
with_lock_retries do
|
||||
# rubocop:disable Migration/DropTable
|
||||
drop_table :ci_refs
|
||||
# rubocop:enable Migration/DropTable
|
||||
|
||||
create_table :ci_refs do |t|
|
||||
t.references :project, null: false, index: false, foreign_key: { on_delete: :cascade }, type: :integer
|
||||
|
|
|
@ -16,7 +16,9 @@ class AddProjectComplianceFrameworkSettingsTable < ActiveRecord::Migration[6.0]
|
|||
|
||||
def down
|
||||
with_lock_retries do
|
||||
# rubocop:disable Migration/DropTable
|
||||
drop_table :project_compliance_framework_settings
|
||||
# rubocop:enable Migration/DropTable
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -26,6 +26,8 @@ class CreatePartitionedForeignKeys < ActiveRecord::Migration[6.0]
|
|||
end
|
||||
|
||||
def down
|
||||
# rubocop:disable Migration/DropTable
|
||||
drop_table :partitioned_foreign_keys
|
||||
# rubocop:enable Migration/DropTable
|
||||
end
|
||||
end
|
||||
|
|
|
@ -26,6 +26,8 @@ class CreateProjectRepositoryStorageMoves < ActiveRecord::Migration[6.0]
|
|||
remove_check_constraint(:project_repository_storage_moves, 'project_repository_storage_moves_source_storage_name')
|
||||
remove_check_constraint(:project_repository_storage_moves, 'project_repository_storage_moves_destination_storage_name')
|
||||
|
||||
# rubocop:disable Migration/DropTable
|
||||
drop_table :project_repository_storage_moves
|
||||
# rubocop:enable Migration/DropTable
|
||||
end
|
||||
end
|
||||
|
|
|
@ -25,6 +25,8 @@ class CreateCiFreezePeriods < ActiveRecord::Migration[6.0]
|
|||
end
|
||||
|
||||
def down
|
||||
# rubocop:disable Migration/DropTable
|
||||
drop_table :ci_freeze_periods
|
||||
# rubocop:enable Migration/DropTable
|
||||
end
|
||||
end
|
||||
|
|
|
@ -15,6 +15,8 @@ class CreateStatusPagePublishedIncidents < ActiveRecord::Migration[6.0]
|
|||
end
|
||||
|
||||
def down
|
||||
# rubocop:disable Migration/DropTable
|
||||
drop_table :status_page_published_incidents
|
||||
# rubocop:enable Migration/DropTable
|
||||
end
|
||||
end
|
||||
|
|
|
@ -39,6 +39,8 @@ class CreateAlertManagementAlerts < ActiveRecord::Migration[6.0]
|
|||
end
|
||||
|
||||
def down
|
||||
# rubocop:disable Migration/DropTable
|
||||
drop_table :alert_management_alerts
|
||||
# rubocop:enable Migration/DropTable
|
||||
end
|
||||
end
|
||||
|
|
|
@ -20,6 +20,8 @@ class AddGroupImportStatesTable < ActiveRecord::Migration[6.0]
|
|||
# rubocop:enable Migration/AddLimitToTextColumns
|
||||
|
||||
def down
|
||||
# rubocop:disable Migration/DropTable
|
||||
drop_table :group_import_states
|
||||
# rubocop:enable Migration/DropTable
|
||||
end
|
||||
end
|
||||
|
|
|
@ -20,6 +20,8 @@ class CreateMetricsUsersStarredDashboard < ActiveRecord::Migration[6.0]
|
|||
# rubocop: enable Migration/AddLimitToTextColumns
|
||||
|
||||
def down
|
||||
# rubocop:disable Migration/DropTable
|
||||
drop_table :metrics_users_starred_dashboards
|
||||
# rubocop:enable Migration/DropTable
|
||||
end
|
||||
end
|
||||
|
|
|
@ -26,6 +26,8 @@ class CreateCiInstanceVariables < ActiveRecord::Migration[6.0]
|
|||
end
|
||||
|
||||
def down
|
||||
# rubocop:disable Migration/DropTable
|
||||
drop_table :ci_instance_variables
|
||||
# rubocop:enable Migration/DropTable
|
||||
end
|
||||
end
|
||||
|
|
|
@ -21,6 +21,8 @@ class CreateNugetDependencyLinkMetadata < ActiveRecord::Migration[6.0]
|
|||
end
|
||||
|
||||
def down
|
||||
# rubocop:disable Migration/DropTable
|
||||
drop_table :packages_nuget_dependency_link_metadata
|
||||
# rubocop:enable Migration/DropTable
|
||||
end
|
||||
end
|
||||
|
|
|
@ -29,6 +29,8 @@ class CreatePackagesNugetMetadata < ActiveRecord::Migration[6.0]
|
|||
end
|
||||
|
||||
def down
|
||||
# rubocop:disable Migration/DropTable
|
||||
drop_table :packages_nuget_metadata
|
||||
# rubocop:enable Migration/DropTable
|
||||
end
|
||||
end
|
||||
|
|
|
@ -31,6 +31,8 @@ class CreateGroupDeployKeys < ActiveRecord::Migration[6.0]
|
|||
end
|
||||
|
||||
def down
|
||||
# rubocop:disable Migration/DropTable
|
||||
drop_table :group_deploy_keys
|
||||
# rubocop:enable Migration/DropTable
|
||||
end
|
||||
end
|
||||
|
|
|
@ -17,6 +17,8 @@ class CreateAlertManagementAlertAssignees < ActiveRecord::Migration[6.0]
|
|||
end
|
||||
|
||||
def down
|
||||
# rubocop:disable Migration/DropTable
|
||||
drop_table :alert_management_alert_assignees
|
||||
# rubocop:enable Migration/DropTable
|
||||
end
|
||||
end
|
||||
|
|
|
@ -21,7 +21,9 @@ class CreateProjectSecuritySettings < ActiveRecord::Migration[6.0]
|
|||
|
||||
def down
|
||||
with_lock_retries do
|
||||
# rubocop:disable Migration/DropTable
|
||||
drop_table :project_security_settings
|
||||
# rubocop:enable Migration/DropTable
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -15,6 +15,8 @@ class CreateBoardUserPreferences < ActiveRecord::Migration[6.0]
|
|||
end
|
||||
|
||||
def down
|
||||
# rubocop:disable Migration/DropTable
|
||||
drop_table :board_user_preferences
|
||||
# rubocop:enable Migration/DropTable
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,178 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# This migration adds or updates the routes for all the entities affected by
|
||||
# post-migration '20200511083541_cleanup_projects_with_missing_namespace'
|
||||
# - A route is added for the 'lost-and-found' group
|
||||
# - A route is added for the Ghost user (if not already defined)
|
||||
# - The routes for all the orphaned projects that were moved under the 'lost-and-found'
|
||||
# group are updated to reflect the new path
|
||||
class UpdateRoutesForLostAndFoundGroupAndOrphanedProjects < ActiveRecord::Migration[6.0]
|
||||
DOWNTIME = false
|
||||
|
||||
class User < ActiveRecord::Base
|
||||
self.table_name = 'users'
|
||||
|
||||
LOST_AND_FOUND_GROUP = 'lost-and-found'
|
||||
USER_TYPE_GHOST = 5
|
||||
ACCESS_LEVEL_OWNER = 50
|
||||
|
||||
has_one :namespace, -> { where(type: nil) },
|
||||
foreign_key: :owner_id, inverse_of: :owner, autosave: true,
|
||||
class_name: 'UpdateRoutesForLostAndFoundGroupAndOrphanedProjects::Namespace'
|
||||
|
||||
def lost_and_found_group
|
||||
# Find the 'lost-and-found' group
|
||||
# There should only be one Group owned by the Ghost user starting with 'lost-and-found'
|
||||
Group
|
||||
.joins('INNER JOIN members ON namespaces.id = members.source_id')
|
||||
.where('namespaces.type = ?', 'Group')
|
||||
.where('members.type = ?', 'GroupMember')
|
||||
.where('members.source_type = ?', 'Namespace')
|
||||
.where('members.user_id = ?', self.id)
|
||||
.where('members.access_level = ?', ACCESS_LEVEL_OWNER)
|
||||
.find_by(Group.arel_table[:name].matches("#{LOST_AND_FOUND_GROUP}%"))
|
||||
end
|
||||
|
||||
class << self
|
||||
# Return the ghost user
|
||||
def ghost
|
||||
User.find_by(user_type: USER_TYPE_GHOST)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Temporary Concern to not repeat the same methods twice
|
||||
module HasPath
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
def full_path
|
||||
if parent && path
|
||||
parent.full_path + '/' + path
|
||||
else
|
||||
path
|
||||
end
|
||||
end
|
||||
|
||||
def full_name
|
||||
if parent && name
|
||||
parent.full_name + ' / ' + name
|
||||
else
|
||||
name
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class Namespace < ActiveRecord::Base
|
||||
include HasPath
|
||||
|
||||
self.table_name = 'namespaces'
|
||||
|
||||
belongs_to :owner, class_name: 'UpdateRoutesForLostAndFoundGroupAndOrphanedProjects::User'
|
||||
belongs_to :parent, class_name: "UpdateRoutesForLostAndFoundGroupAndOrphanedProjects::Namespace"
|
||||
has_many :children, foreign_key: :parent_id,
|
||||
class_name: "UpdateRoutesForLostAndFoundGroupAndOrphanedProjects::Namespace"
|
||||
has_many :projects, class_name: "UpdateRoutesForLostAndFoundGroupAndOrphanedProjects::Project"
|
||||
|
||||
def ensure_route!
|
||||
unless Route.for_source('Namespace', self.id)
|
||||
Route.create!(
|
||||
source_id: self.id,
|
||||
source_type: 'Namespace',
|
||||
path: self.full_path,
|
||||
name: self.full_name
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
def generate_unique_path
|
||||
# Generate a unique path if there is no route for the namespace
|
||||
# (an existing route guarantees that the path is already unique)
|
||||
unless Route.for_source('Namespace', self.id)
|
||||
self.path = Uniquify.new.string(self.path) do |str|
|
||||
Route.where(path: str).exists?
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class Group < Namespace
|
||||
# Disable STI to allow us to manually set "type = 'Group'"
|
||||
# Otherwise rails forces "type = CleanupProjectsWithMissingNamespace::Group"
|
||||
self.inheritance_column = :_type_disabled
|
||||
end
|
||||
|
||||
class Route < ActiveRecord::Base
|
||||
self.table_name = 'routes'
|
||||
|
||||
def self.for_source(source_type, source_id)
|
||||
Route.find_by(source_type: source_type, source_id: source_id)
|
||||
end
|
||||
end
|
||||
|
||||
class Project < ActiveRecord::Base
|
||||
include HasPath
|
||||
|
||||
self.table_name = 'projects'
|
||||
|
||||
belongs_to :group, -> { where(type: 'Group') }, foreign_key: 'namespace_id',
|
||||
class_name: "UpdateRoutesForLostAndFoundGroupAndOrphanedProjects::Group"
|
||||
belongs_to :namespace,
|
||||
class_name: "UpdateRoutesForLostAndFoundGroupAndOrphanedProjects::Namespace"
|
||||
|
||||
alias_method :parent, :namespace
|
||||
alias_attribute :parent_id, :namespace_id
|
||||
|
||||
def ensure_route!
|
||||
Route.find_or_initialize_by(source_type: 'Project', source_id: self.id).tap do |record|
|
||||
record.path = self.full_path
|
||||
record.name = self.full_name
|
||||
record.save!
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def up
|
||||
# Reset the column information of all the models that update the database
|
||||
# to ensure the Active Record's knowledge of the table structure is current
|
||||
Namespace.reset_column_information
|
||||
Route.reset_column_information
|
||||
|
||||
# Find the ghost user, its namespace and the "lost and found" group
|
||||
ghost_user = User.ghost
|
||||
return unless ghost_user # No reason to continue if there is no Ghost user
|
||||
|
||||
ghost_namespace = ghost_user.namespace
|
||||
lost_and_found_group = ghost_user.lost_and_found_group
|
||||
|
||||
# No reason to continue if there is no 'lost-and-found' group
|
||||
# 1. No orphaned projects were found in this instance, or
|
||||
# 2. The 'lost-and-found' group and the orphaned projects have been already deleted
|
||||
return unless lost_and_found_group
|
||||
|
||||
# Update the 'lost-and-found' group description to be more self-explanatory
|
||||
lost_and_found_group.generate_unique_path
|
||||
lost_and_found_group.description =
|
||||
'Group for storing projects that were not properly deleted. '\
|
||||
'It should be considered as a system level Group with non-working '\
|
||||
'projects inside it. The contents may be deleted with a future update. '\
|
||||
'More info: gitlab.com/gitlab-org/gitlab/-/issues/198603'
|
||||
lost_and_found_group.save!
|
||||
|
||||
# Update the routes for the Ghost user, the "lost and found" group
|
||||
# and all the orphaned projects
|
||||
ghost_namespace.ensure_route!
|
||||
lost_and_found_group.ensure_route!
|
||||
|
||||
# The following does a fast index scan by namespace_id
|
||||
# No reason to process in batches:
|
||||
# - 66 projects in GitLab.com, less than 1ms execution time to fetch them
|
||||
# with a constant update time for each
|
||||
lost_and_found_group.projects.each do |project|
|
||||
project.ensure_route!
|
||||
end
|
||||
end
|
||||
|
||||
def down
|
||||
# no-op
|
||||
end
|
||||
end
|
|
@ -13974,6 +13974,7 @@ COPY "schema_migrations" (version) FROM STDIN;
|
|||
20200601210148
|
||||
20200602013900
|
||||
20200602013901
|
||||
20200602143020
|
||||
20200603073101
|
||||
20200603180338
|
||||
20200604143628
|
||||
|
|
|
@ -64,13 +64,17 @@ seconds:
|
|||
1. `queue_duration_s`: total time that the request was queued inside GitLab Workhorse
|
||||
1. `view_duration_s`: total time taken inside the Rails views
|
||||
1. `db_duration_s`: total time to retrieve data from PostgreSQL
|
||||
1. `redis_duration_s`: total time to retrieve data from Redis
|
||||
1. `cpu_s`: total time spent on CPU
|
||||
1. `gitaly_duration_s`: total time taken by Gitaly calls
|
||||
1. `gitaly_calls`: total number of calls made to Gitaly
|
||||
1. `redis_calls`: total number of calls made to Redis
|
||||
1. `redis_duration_s`: total time to retrieve data from Redis
|
||||
1. `redis_read_bytes`: total bytes read from Redis
|
||||
1. `redis_write_bytes`: total bytes written to Redis
|
||||
1. `redis_<instance>_calls`: total number of calls made to a Redis instance
|
||||
1. `redis_<instance>_duration_s`: total time to retrieve data from a Redis instance
|
||||
1. `redis_<instance>_read_bytes`: total bytes read from a Redis instance
|
||||
1. `redis_<instance>_write_bytes`: total bytes written to a Redis instance
|
||||
|
||||
User clone and fetch activity using HTTP transport appears in this log as `action: git_upload_pack`.
|
||||
|
||||
|
|
|
@ -208,7 +208,6 @@ control over how the Pages daemon runs and serves content in your environment.
|
|||
| `gitlab_secret` | The OAuth application secret. Leave blank to automatically fill when Pages authenticates with GitLab.
|
||||
| `gitlab_server` | Server to use for authentication when access control is enabled; defaults to GitLab `external_url`.
|
||||
| `headers` | Specify any additional http headers that should be sent to the client with each response.
|
||||
| `http_proxy` | Configure GitLab Pages to use an HTTP Proxy to mediate traffic between Pages and GitLab. Sets an environment variable `http_proxy` when starting Pages daemon.
|
||||
| `inplace_chroot` | On [systems that don't support bind-mounts](index.md#additional-configuration-for-docker-container), this instructs GitLab Pages to chroot into its `pages_path` directory. Some caveats exist when using inplace chroot; refer to the GitLab Pages [README](https://gitlab.com/gitlab-org/gitlab-pages/blob/master/README.md#caveats) for more information.
|
||||
| `insecure_ciphers` | Use default list of cipher suites, may contain insecure ones like 3DES and RC4.
|
||||
| `internal_gitlab_server` | Internal GitLab server address used exclusively for API requests. Useful if you want to send that traffic over an internal load balancer. Defaults to GitLab `external_url`.
|
||||
|
@ -226,6 +225,8 @@ control over how the Pages daemon runs and serves content in your environment.
|
|||
| `tls_max_version` | Specifies the maximum SSL/TLS version ("ssl3", "tls1.0", "tls1.1" or "tls1.2").
|
||||
| `tls_min_version` | Specifies the minimum SSL/TLS version ("ssl3", "tls1.0", "tls1.1" or "tls1.2").
|
||||
| `use_http2` | Enable HTTP2 support.
|
||||
| **gitlab_pages['env'][]** | |
|
||||
| `http_proxy` | Configure GitLab Pages to use an HTTP Proxy to mediate traffic between Pages and GitLab. Sets an environment variable `http_proxy` when starting Pages daemon.
|
||||
| **gitlab_rails[]** | |
|
||||
| `pages_domain_verification_cron_worker` | Schedule for verifying custom GitLab Pages domains.
|
||||
| `pages_domain_ssl_renewal_cron_worker` | Schedule for obtaining and renewing SSL certificates through Let's Encrypt for GitLab Pages domains.
|
||||
|
@ -400,7 +401,7 @@ pages:
|
|||
1. Configure in `/etc/gitlab/gitlab.rb`:
|
||||
|
||||
```ruby
|
||||
gitlab_pages['http_proxy'] = 'http://example:8080'
|
||||
gitlab_pages['env']['http_proxy'] = 'http://example:8080'
|
||||
```
|
||||
|
||||
1. [Reconfigure GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect.
|
||||
|
|
|
@ -51,6 +51,40 @@ Hooks: /home/git/gitlab-shell/hooks/
|
|||
Git: /usr/bin/git
|
||||
```
|
||||
|
||||
## Show GitLab license information **(STARTER ONLY)**
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/20501) in GitLab Starter 12.6.
|
||||
|
||||
This command shows information about your [GitLab license](../../user/admin_area/license.md) and
|
||||
how many seats are used. It is only available on GitLab Enterprise
|
||||
installations: a license cannot be installed into GitLab Community Edition.
|
||||
|
||||
These may be useful when raising tickets with Support, or for programmatically
|
||||
checking your license parameters.
|
||||
|
||||
**Omnibus Installation**
|
||||
|
||||
```shell
|
||||
sudo gitlab-rake gitlab:license:info
|
||||
```
|
||||
|
||||
**Source Installation**
|
||||
|
||||
```shell
|
||||
bundle exec rake gitlab:license:info RAILS_ENV=production
|
||||
```
|
||||
|
||||
Example output:
|
||||
|
||||
```plaintext
|
||||
Today's Date: 2020-02-29
|
||||
Current User Count: 30
|
||||
Max Historical Count: 30
|
||||
Max Users in License: 40
|
||||
License valid from: 2019-11-29 to 2020-11-28
|
||||
Email associated with license: user@example.com
|
||||
```
|
||||
|
||||
## Check GitLab configuration
|
||||
|
||||
The `gitlab:check` Rake task runs the following Rake tasks:
|
||||
|
|
|
@ -7,11 +7,18 @@ For a full list of reference architectures, see
|
|||
> - **Supported users (approximate):** 1,000
|
||||
> - **High Availability:** False
|
||||
|
||||
| Users | Configuration([8](#footnotes)) | GCP | AWS | Azure |
|
||||
|-------|--------------------------------|-----------------|----------------------|------------------------|
|
||||
| 100 | 2 vCPU, 7.2GB Memory | `n1-standard-2` | `m5.large` | D2s v3 |
|
||||
| 500 | 4 vCPU, 15GB Memory | `n1-standard-4` | `m5.xlarge` | D4s v3 |
|
||||
| 1000 | 8 vCPU, 30GB Memory | `n1-standard-8` | `m5.2xlarge` | D8s v3 |
|
||||
| Users | Configuration([8](#footnotes)) | GCP | AWS | Azure |
|
||||
|-------|------------------------------------|----------------|---------------------|------------------------|
|
||||
| 500 | 4 vCPU, 3.6GB Memory | `n1-highcpu-4` | `c5.xlarge` | F4s v2 |
|
||||
| 1000 | 8 vCPU, 7.2GB Memory | `n1-highcpu-8` | `c5.2xlarge` | F8s v2 |
|
||||
|
||||
In addition to the above, we recommend having at least
|
||||
2GB of swap on your server, even if you currently have
|
||||
enough available RAM. Having swap will help reduce the chance of errors occurring
|
||||
if your available memory changes. We also recommend
|
||||
configuring the kernel's swappiness setting
|
||||
to a low value like `10` to make the most of your RAM while still having the swap
|
||||
available when needed.
|
||||
|
||||
For situations where you need to serve up to 1,000 users, a single-node
|
||||
solution with [frequent backups](index.md#automated-backups-core-only) is appropriate
|
||||
|
|
|
@ -171,6 +171,25 @@ column.
|
|||
| [GitLab application services](../../development/architecture.md#unicorn)([1](#footnotes)) | Puma/Unicorn, Workhorse, GitLab Shell - serves front-end requests (UI, API, Git over HTTP/SSH) | [GitLab app scaling configuration](../high_availability/gitlab.md) | Yes |
|
||||
| [Prometheus](../../development/architecture.md#prometheus) and [Grafana](../../development/architecture.md#grafana) | GitLab environment monitoring | [Monitoring node for scaling](../high_availability/monitoring_node.md) | Yes |
|
||||
|
||||
### Configuring select components with Cloud Native Helm
|
||||
|
||||
We also provide [Helm charts](https://docs.gitlab.com/charts/) as a Cloud Native installation
|
||||
method for GitLab. For the reference architectures, select components can be set up in this
|
||||
way as an alternative if so desired.
|
||||
|
||||
For these kind of setups we support using the charts in an [advanced configuration](https://docs.gitlab.com/charts/#advanced-configuration)
|
||||
where stateful backend components, such as the database or Gitaly, are run externally - either
|
||||
via Omnibus or reputable third party services. Note that we don't currently support running the
|
||||
stateful components via Helm _at large scales_.
|
||||
|
||||
When designing these environments you should refer to the respective [Reference Architecture](#available-reference-architectures)
|
||||
above for guidance on sizing. Components run via Helm would be similarly scaled to their Omnibus
|
||||
specs, only translated into Kubernetes resources.
|
||||
|
||||
For example, if you were to set up a 50k installation with the Rails nodes being run in Helm,
|
||||
then the same amount of resources as given for Omnibus should be given to the Kubernetes
|
||||
cluster with the Rails nodes broken down into a number of smaller Pods across that cluster.
|
||||
|
||||
## Footnotes
|
||||
|
||||
1. In our architectures we run each GitLab Rails node using the Puma webserver
|
||||
|
|
|
@ -269,6 +269,90 @@ Example of response
|
|||
]
|
||||
```
|
||||
|
||||
## List pipeline bridges
|
||||
|
||||
Get a list of bridge jobs for a pipeline.
|
||||
|
||||
```plaintext
|
||||
GET /projects/:id/pipelines/:pipeline_id/bridges
|
||||
```
|
||||
|
||||
| Attribute | Type | Required | Description |
|
||||
|---------------|--------------------------------|----------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `id` | integer/string | yes | ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user. |
|
||||
| `pipeline_id` | integer | yes | ID of a pipeline. |
|
||||
| `scope` | string **or** array of strings | no | Scope of jobs to show. Either one of or an array of the following: `created`, `pending`, `running`, `failed`, `success`, `canceled`, `skipped`, or `manual`. All jobs are returned if `scope` is not provided. |
|
||||
|
||||
```shell
|
||||
curl --header "PRIVATE-TOKEN: <your_access_token>" 'https://gitlab.example.com/api/v4/projects/1/pipelines/6/bridges?scope[]=pending&scope[]=running'
|
||||
```
|
||||
|
||||
Example of response
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"commit": {
|
||||
"author_email": "admin@example.com",
|
||||
"author_name": "Administrator",
|
||||
"created_at": "2015-12-24T16:51:14.000+01:00",
|
||||
"id": "0ff3ae198f8601a285adcf5c0fff204ee6fba5fd",
|
||||
"message": "Test the CI integration.",
|
||||
"short_id": "0ff3ae19",
|
||||
"title": "Test the CI integration."
|
||||
},
|
||||
"coverage": null,
|
||||
"allow_failure": false,
|
||||
"created_at": "2015-12-24T15:51:21.802Z",
|
||||
"started_at": "2015-12-24T17:54:27.722Z",
|
||||
"finished_at": "2015-12-24T17:58:27.895Z",
|
||||
"duration": 240,
|
||||
"id": 7,
|
||||
"name": "teaspoon",
|
||||
"pipeline": {
|
||||
"id": 6,
|
||||
"ref": "master",
|
||||
"sha": "0ff3ae198f8601a285adcf5c0fff204ee6fba5fd",
|
||||
"status": "pending",
|
||||
"created_at": "2015-12-24T15:50:16.123Z",
|
||||
"updated_at": "2015-12-24T18:00:44.432Z",
|
||||
"web_url": "https://example.com/foo/bar/pipelines/6"
|
||||
},
|
||||
"ref": "master",
|
||||
"stage": "test",
|
||||
"status": "pending",
|
||||
"tag": false,
|
||||
"web_url": "https://example.com/foo/bar/-/jobs/7",
|
||||
"user": {
|
||||
"id": 1,
|
||||
"name": "Administrator",
|
||||
"username": "root",
|
||||
"state": "active",
|
||||
"avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
|
||||
"web_url": "http://gitlab.dev/root",
|
||||
"created_at": "2015-12-21T13:14:24.077Z",
|
||||
"bio": null,
|
||||
"location": null,
|
||||
"public_email": "",
|
||||
"skype": "",
|
||||
"linkedin": "",
|
||||
"twitter": "",
|
||||
"website_url": "",
|
||||
"organization": ""
|
||||
},
|
||||
"downstream_pipeline": {
|
||||
"id": 5,
|
||||
"sha": "f62a4b2fb89754372a346f24659212eb8da13601",
|
||||
"ref": "master",
|
||||
"status": "pending",
|
||||
"created_at": "2015-12-24T17:54:27.722Z",
|
||||
"updated_at": "2015-12-24T17:58:27.896Z",
|
||||
"web_url": "https://example.com/diaspora/diaspora-client/pipelines/5"
|
||||
}
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
## Get a single job
|
||||
|
||||
Get a single job of a project
|
||||
|
|
|
@ -474,8 +474,8 @@ Feature.enable(:instance_variables_ui)
|
|||
|
||||
## Inherit environment variables
|
||||
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/22638) in GitLab 13.0.
|
||||
> - It's deployed behind a feature flag (`ci_dependency_variables`), disabled by default.
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/22638) in GitLab 13.0 behind a disabled [feature flag](../../administration/feature_flags.md): `ci_dependency_variables`.
|
||||
> - [Feature flag removed](https://gitlab.com/gitlab-org/gitlab/-/issues/217834) in GitLab 13.1.
|
||||
|
||||
You can inherit environment variables from dependent jobs.
|
||||
|
||||
|
@ -520,25 +520,6 @@ deploy:
|
|||
artifacts: true
|
||||
```
|
||||
|
||||
### Enable inherited environment variables **(CORE ONLY)**
|
||||
|
||||
The Inherited Environment Variables feature is under development and not ready for production use. It is
|
||||
deployed behind a feature flag that is **disabled by default**.
|
||||
[GitLab administrators with access to the GitLab Rails console](../../administration/feature_flags.md)
|
||||
can enable it for your instance.
|
||||
|
||||
To enable it:
|
||||
|
||||
```ruby
|
||||
Feature.enable(:ci_dependency_variables)
|
||||
```
|
||||
|
||||
To disable it:
|
||||
|
||||
```ruby
|
||||
Feature.disable(:ci_dependency_variables)
|
||||
```
|
||||
|
||||
## Priority of environment variables
|
||||
|
||||
Variables of different types can take precedence over other
|
||||
|
|
|
@ -92,42 +92,31 @@ NOTE: **Note:** Since file system performance may affect GitLab's overall perfor
|
|||
|
||||
### CPU
|
||||
|
||||
This is the recommended minimum hardware for a handful of example GitLab user base sizes. Your exact needs may be more, depending on your workload. Your workload is influenced by factors such as - but not limited to - how active your users are, how much automation you use, mirroring, and repository/change size.
|
||||
CPU requirements are dependent on the number of users and expected workload. Your exact needs may be more, depending on your workload. Your workload is influenced by factors such as - but not limited to - how active your users are, how much automation you use, mirroring, and repo/change size.
|
||||
|
||||
- 1 core supports up to 100 users but the application can be a bit slower due to having all workers and background jobs running on the same core
|
||||
- **2 cores** is the **recommended** minimum number of cores and supports up to 100 users
|
||||
- 4 cores support up to 500 users
|
||||
- 8 cores support up to 1,000 users
|
||||
- 32 cores support up to 5,000 users
|
||||
The following is the recommended minimum CPU hardware guidance for a handful of example GitLab user base sizes.
|
||||
|
||||
- **4 cores** is the **recommended** minimum number of cores and supports up to 500 users
|
||||
- 8 cores supports up to 1000 users
|
||||
- More users? Consult the [reference architectures page](../administration/reference_architectures/index.md)
|
||||
|
||||
### Memory
|
||||
|
||||
This is the recommended minimum hardware for a handful of example GitLab user base sizes. Your exact needs may be more, depending on your workload. Your workload is influenced by factors such as - but not limited to - how active your users are, how much automation you use, mirroring, and the size of repositories as well as changes/commits.
|
||||
Memory requirements are dependent on the number of users and expected workload. Your exact needs may be more, depending on your workload. Your workload is influenced by factors such as - but not limited to - how active your users are, how much automation you use, mirroring, and repo/change size.
|
||||
|
||||
You need at least 8GB of addressable memory (RAM + swap) to install and use GitLab!
|
||||
The operating system and any other running applications will also be using memory
|
||||
so keep in mind that you need at least 4GB available before running GitLab. With
|
||||
less memory GitLab will give strange errors during the reconfigure run and 500
|
||||
errors during usage.
|
||||
The following is the recommended minimum Memory hardware guidance for a handful of example GitLab user base sizes.
|
||||
|
||||
- 4GB RAM + 4GB swap supports up to 100 users but it will be very slow
|
||||
- **8GB RAM** is the **recommended** minimum memory size for all installations and supports up to 100 users
|
||||
- 16GB RAM supports up to 500 users
|
||||
- 32GB RAM supports up to 1,000 users
|
||||
- 128GB RAM supports up to 5,000 users
|
||||
- **4GB RAM** is the **required** minimum memory size and supports up to 500 users
|
||||
- Our [Memory Team](https://about.gitlab.com/handbook/engineering/development/enablement/memory/) is working to reduce the memory requirement.
|
||||
- 8GB RAM supports up to 1000 users
|
||||
- More users? Consult the [reference architectures page](../administration/reference_architectures/index.md)
|
||||
|
||||
We recommend having at least [2GB of swap on your server](https://askubuntu.com/a/505344/310789), even if you currently have
|
||||
enough available RAM. Having swap will help reduce the chance of errors occurring
|
||||
if your available memory changes. We also recommend [configuring the kernel's swappiness setting](https://askubuntu.com/a/103916)
|
||||
In addition to the above, we generally recommend having at least 2GB of swap on your server,
|
||||
even if you currently have enough available RAM. Having swap will help reduce the chance of errors occurring
|
||||
if your available memory changes. We also recommend configuring the kernel's swappiness setting
|
||||
to a low value like `10` to make the most of your RAM while still having the swap
|
||||
available when needed.
|
||||
|
||||
Our [Memory Team](https://about.gitlab.com/handbook/engineering/development/enablement/memory/) is actively working to reduce the memory requirement.
|
||||
|
||||
NOTE: **Note:** The 25 workers of Sidekiq will show up as separate processes in your process overview (such as `top` or `htop`). However, they share the same RAM allocation, as Sidekiq is a multi-threaded application. See the section below about Unicorn workers for information about how many you need for those.
|
||||
|
||||
## Database
|
||||
|
||||
PostgreSQL is the only supported database, which is bundled with the Omnibus GitLab package.
|
||||
|
@ -168,22 +157,6 @@ If you're using [GitLab Geo](../administration/geo/replication/index.md):
|
|||
[can be enabled](https://www.postgresql.org/docs/11/sql-createextension.html)
|
||||
using a PostgreSQL super user.
|
||||
|
||||
## Unicorn Workers
|
||||
|
||||
For most instances we recommend using: (CPU cores * 1.5) + 1 = Unicorn workers.
|
||||
For example a node with 4 cores would have 7 Unicorn workers.
|
||||
|
||||
For all machines that have 2GB and up we recommend a minimum of three Unicorn workers.
|
||||
If you have a 1GB machine we recommend to configure only two Unicorn workers to prevent excessive
|
||||
swapping.
|
||||
|
||||
As long as you have enough available CPU and memory capacity, it's okay to increase the number of
|
||||
Unicorn workers and this will usually help to reduce the response time of the applications and
|
||||
increase the ability to handle parallel requests.
|
||||
|
||||
To change the Unicorn workers when you have the Omnibus package (which defaults to the
|
||||
recommendation above) please see [the Unicorn settings in the Omnibus GitLab documentation](https://docs.gitlab.com/omnibus/settings/unicorn.html).
|
||||
|
||||
## Puma settings
|
||||
|
||||
The recommended settings for Puma are determined by the infrastructure on which it's running.
|
||||
|
@ -219,6 +192,22 @@ of [legacy Rugged code](../development/gitaly.md#legacy-rugged-code).
|
|||
higher, due to how [Ruby MRI multi-threading](https://en.wikipedia.org/wiki/Global_interpreter_lock)
|
||||
works.
|
||||
|
||||
## Unicorn Workers
|
||||
|
||||
For most instances we recommend using: (CPU cores * 1.5) + 1 = Unicorn workers.
|
||||
For example a node with 4 cores would have 7 Unicorn workers.
|
||||
|
||||
For all machines that have 2GB and up we recommend a minimum of three Unicorn workers.
|
||||
If you have a 1GB machine we recommend to configure only two Unicorn workers to prevent excessive
|
||||
swapping.
|
||||
|
||||
As long as you have enough available CPU and memory capacity, it's okay to increase the number of
|
||||
Unicorn workers and this will usually help to reduce the response time of the applications and
|
||||
increase the ability to handle parallel requests.
|
||||
|
||||
To change the Unicorn workers when you have the Omnibus package (which defaults to the
|
||||
recommendation above) please see [the Unicorn settings in the Omnibus GitLab documentation](https://docs.gitlab.com/omnibus/settings/unicorn.html).
|
||||
|
||||
## Redis and Sidekiq
|
||||
|
||||
Redis stores all user sessions and the background task queue.
|
||||
|
|
Before Width: | Height: | Size: 11 KiB |
After Width: | Height: | Size: 13 KiB |
|
@ -1,5 +1,8 @@
|
|||
---
|
||||
type: reference
|
||||
stage: Plan
|
||||
group: Project Management
|
||||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
|
||||
---
|
||||
|
||||
# Rate limits on issue creation
|
||||
|
@ -14,7 +17,7 @@ For example, requests using the
|
|||
[Projects::IssuesController#create](https://gitlab.com/gitlab-org/gitlab/raw/master/app/controllers/projects/issues_controller.rb)
|
||||
action exceeding a rate of 300 per minute are blocked. Access to the endpoint is allowed after one minute.
|
||||
|
||||
![Rate limits on issues creation](img/rate_limit_on_issues_creation.png)
|
||||
![Rate limits on issues creation](img/rate_limit_on_issues_creation_v13_1.png)
|
||||
|
||||
This limit is:
|
||||
|
||||
|
|
After Width: | Height: | Size: 8.8 KiB |
After Width: | Height: | Size: 141 KiB |
After Width: | Height: | Size: 39 KiB |
|
@ -55,14 +55,19 @@ generates for you. GitLab supports the following scanners:
|
|||
is only available for Node.js projects managed with `yarn`.
|
||||
- [Container Scanning](../container_scanning/index.md).
|
||||
|
||||
When an automatic solution is available, the button in the header will show "Resolve with merge request":
|
||||
|
||||
![Resolve with Merge Request button](img/standalone_vulnerability_page_merge_request_button_v13_1.png)
|
||||
|
||||
Selecting the button will create a merge request with the automatic solution.
|
||||
|
||||
### Manually applying a suggested patch
|
||||
|
||||
To apply a patch automatically generated by GitLab to fix a vulnerability:
|
||||
To manually apply the patch that was generated by GitLab for a vulnerability, select the dropdown arrow on the "Resolve
|
||||
with merge request" button, then select the "Download patch to resolve" option:
|
||||
|
||||
1. Open the issue created in [Create issue](#creating-an-issue-for-a-vulnerability).
|
||||
1. In the **Issue description**, scroll to **Solution** and download the linked patch file.
|
||||
1. Ensure your local project has the same commit checked out that was used to generate the patch.
|
||||
1. Run `git apply remediation.patch` to apply the patch.
|
||||
1. Verify and commit the changes to your branch.
|
||||
![Resolve with Merge Request button dropdown](img/standalone_vulnerability_page_merge_request_button_dropdown_v13_1.png)
|
||||
|
||||
![Apply patch for dependency scanning](../img/vulnerability_solution.png)
|
||||
This will change the button text to "Download patch to resolve". Click on it to download the patch:
|
||||
|
||||
![Download patch button](img/standalone_vulnerability_page_download_patch_button_v13_1.png)
|
||||
|
|
Before Width: | Height: | Size: 196 KiB |
After Width: | Height: | Size: 47 KiB |