Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2023-08-03 18:10:18 +00:00
parent 388e0fbbd0
commit 9be457ffc1
160 changed files with 1736 additions and 962 deletions

View File

@ -63,10 +63,17 @@ GITALY_SERVER_VERSION @project_278964_bot6 @gitlab-org/maintainers/rails-backend
# This entry must occur before `/scripts/` in order to be matched first
/scripts/remote_development/
[Engineering Productivity] @gl-quality/eng-prod
# This entry must occur before `/scripts/glfm/**/*`/`/scripts/lib/glfm/**/*` in order to be matched first
/scripts/glfm/**/*.js @gitlab-org/maintainers/frontend
/scripts/lib/glfm/**/*.js @gitlab-org/maintainers/frontend
/scripts/glfm/**/* @gitlab-org/maintainers/rails-backend
/scripts/lib/glfm/**/* @gitlab-org/maintainers/rails-backend
[Pipeline configuration] @gl-quality/eng-prod
/.gitlab-ci.yml
/.gitlab/ci/
/.gitlab/ci/docs.gitlab-ci.yml @gl-quality/eng-prod @gl-docsteam
/.gitlab/ci/frontend.gitlab-ci.yml @gl-quality/eng-prod @gitlab-org/maintainers/frontend
/.gitlab/ci/package-and-test/ @gl-quality/eng-prod @gl-quality/qe-maintainers
/.gitlab/ci/qa.gitlab-ci.yml @gl-quality/eng-prod @gl-quality/qe-maintainers
/.gitlab/ci/qa-common/ @gl-quality/eng-prod @gl-quality/qe-maintainers
@ -74,14 +81,29 @@ GITALY_SERVER_VERSION @project_278964_bot6 @gitlab-org/maintainers/rails-backend
/.gitlab/ci/reports.gitlab-ci.yml @gl-quality/eng-prod @gitlab-com/gl-security/appsec
/.gitlab/ci/review-apps/qa.gitlab-ci.yml @gl-quality/eng-prod @gl-quality/qe-maintainers
/.gitlab/ci/test-on-gdk/ @gl-quality/eng-prod @gl-quality/qe-maintainers
/gems/gem.gitlab-ci.yml
[Tooling] @gl-quality/eng-prod
/.gitlab/CODEOWNERS
Dangerfile
/danger/
/gems/gem.gitlab-ci.yml
/tooling/danger/
/scripts/
/scripts/**/*.rb @gl-quality/eng-prod @gitlab-org/maintainers/rails-backend
/scripts/**/*.js @gl-quality/eng-prod @gitlab-org/maintainers/frontend
/scripts/frontend/ @gl-quality/eng-prod @gitlab-org/maintainers/frontend
/scripts/review_apps/seed-dast-test-data.sh @gl-quality/eng-prod @dappelt @ngeorge1
.editorconfig
/.codeclimate.yml
/.dockerignore
/.editorconfig
/.gitpod.yml
/.haml-lint_todo.yml
/.haml-lint.yml
/.nvmrc
/.ruby-version
/.tool-versions
/lefthook.yml
/tests.yml
^[Backend Static Code Analysis] @gl-quality/eng-prod @dstull @splattael
.rubocop*.yml

View File

@ -760,6 +760,10 @@ No changes.
- [Add schema_version in the commits index mapping](gitlab-org/gitlab@e75b94903b69e1e1588e251217926882875555a8) ([merge request](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/123435)) **GitLab Enterprise Edition**
- [Allow to set labels for Redis calls](gitlab-org/gitlab@8ccfff9e2d250eb22afaa7d0243e707b536a5436) ([merge request](gitlab-org/gitlab!122340))
## 16.1.4 (2023-08-03)
No changes.
## 16.1.3 (2023-08-01)
### Added (1 change)

View File

@ -404,7 +404,7 @@ group :development, :test do
gem 'parser', '~> 3.2', '>= 3.2.2.3'
gem 'pry-byebug'
gem 'pry-rails', '~> 0.3.9'
gem 'pry-shell', '~> 0.6.1'
gem 'pry-shell', '~> 0.6.4'
gem 'awesome_print', require: false

View File

@ -461,7 +461,7 @@
{"name":"pry","version":"0.14.2","platform":"ruby","checksum":"c4fe54efedaca1d351280b45b8849af363184696fcac1c72e0415f9bdac4334d"},
{"name":"pry-byebug","version":"3.10.1","platform":"ruby","checksum":"c8f975c32255bfdb29e151f5532130be64ff3d0042dc858d0907e849125581f8"},
{"name":"pry-rails","version":"0.3.9","platform":"ruby","checksum":"468662575abb6b67f4a9831219f99290d5eae7bf186e64dd810d0a3e4a8cc4b1"},
{"name":"pry-shell","version":"0.6.1","platform":"ruby","checksum":"a99a6b3dffe4df274ea1751866816906861a23851f13346e10a8e8f61b53360c"},
{"name":"pry-shell","version":"0.6.4","platform":"ruby","checksum":"ad024882d29912b071a7de65ebea538b242d2dc1498c60c7c2352ef94769f208"},
{"name":"public_suffix","version":"5.0.0","platform":"ruby","checksum":"26ee4fbce33ada25eb117ac71f2c24bf4d8b3414ab6b34f05b4708a3e90f1c6b"},
{"name":"puma","version":"6.3.0","platform":"java","checksum":"5e2ff95953608d1ba0350b80a3961a43e9bbb78ec60ebd5e4db1940c2921d5d8"},
{"name":"puma","version":"6.3.0","platform":"ruby","checksum":"b0e35b4fe7ae440237a9ff1647c6bb252a1c0951ff356020670d2e62c1aeeeec"},

View File

@ -1217,7 +1217,7 @@ GEM
pry (>= 0.13, < 0.15)
pry-rails (0.3.9)
pry (>= 0.10.4)
pry-shell (0.6.1)
pry-shell (0.6.4)
pry (>= 0.13.0)
tty-markdown
tty-prompt
@ -1941,7 +1941,7 @@ DEPENDENCIES
prometheus-client-mmap (~> 0.27)
pry-byebug
pry-rails (~> 0.3.9)
pry-shell (~> 0.6.1)
pry-shell (~> 0.6.4)
puma (~> 6.3)
rack (~> 2.2.7)
rack-attack (~> 6.6.1)

View File

@ -687,11 +687,23 @@ export function redirectTo(url) {
}
/**
* Navigates to a URL
* @param {*} url - url to navigate to
* Navigates to a URL.
*
* If destination is a querystring, it will be automatically transformed into a fully qualified URL.
* If the URL is not a safe URL (see isSafeURL implementation), this function will log an exception into Sentry.
*
* @param {*} destination - url to navigate to. This can be a fully qualified URL or a querystring.
* @param {*} external - if true, open a new page or tab
*/
export function visitUrl(url, external = false) {
export function visitUrl(destination, external = false) {
let url = destination;
if (destination.startsWith('?')) {
const currentUrl = new URL(window.location.href);
currentUrl.search = destination;
url = currentUrl.toString();
}
if (!isSafeURL(url)) {
// For now log this to Sentry and do not block the execution.
// See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/121551#note_1408873600

View File

@ -1,6 +1,7 @@
import $ from 'jquery';
import Visibility from 'visibilityjs';
import Vue from 'vue';
import actionCable from '~/actioncable_consumer';
import Api from '~/api';
import { createAlert, VARIANT_INFO } from '~/alert';
import { EVENT_ISSUABLE_VUE_APP_CHANGE } from '~/issuable/constants';
@ -151,7 +152,30 @@ export const initPolling = ({ state, dispatch, getters, commit }) => {
dispatch('setLastFetchedAt', getters.getNotesDataByProp('lastFetchedAt'));
dispatch('poll');
if (gon.features?.actionCableNotes) {
actionCable.subscriptions.create(
{
channel: 'Noteable::NotesChannel',
project_id: state.notesData.projectId,
group_id: state.notesData.groupId,
noteable_type: state.notesData.noteableType,
noteable_id: state.notesData.noteableId,
},
{
connected() {
dispatch('fetchUpdatedNotes');
},
received(data) {
if (data.event === 'updated') {
dispatch('fetchUpdatedNotes');
}
},
},
);
} else {
dispatch('poll');
}
commit(types.SET_IS_POLLING_INITIALIZED, true);
};
@ -491,7 +515,7 @@ export const saveNote = ({ commit, dispatch }, noteData) => {
{"commands_changes":{},"valid":false,"errors":{"commands_only":["Commands applied"]}}
*/
if (hasQuickActions && message) {
eTagPoll.makeRequest();
if (eTagPoll) eTagPoll.makeRequest();
// synchronizing the quick action with the sidebar widget
// this is a temporary solution until we have confidentiality real-time updates
@ -592,6 +616,21 @@ const getFetchDataParams = (state) => {
return { endpoint, options };
};
export const fetchUpdatedNotes = ({ commit, state, getters, dispatch }) => {
const { endpoint, options } = getFetchDataParams(state);
return axios
.get(endpoint, options)
.then(({ data }) => {
pollSuccessCallBack(data, commit, state, getters, dispatch);
})
.catch(() => {
createAlert({
message: __('Something went wrong while fetching latest comments.'),
});
});
};
export const poll = ({ commit, state, getters, dispatch }) => {
const notePollOccurrenceTracking = create();
let alert;

View File

@ -0,0 +1,23 @@
# frozen_string_literal: true
module Noteable
class NotesChannel < ApplicationCable::Channel
def subscribed
project = Project.find(params[:project_id]) if params[:project_id].present?
noteable = NotesFinder.new(current_user, {
project: project,
group_id: params[:group_id],
target_type: params[:noteable_type],
target_id: params[:noteable_id]
}).target
return reject if noteable.nil?
return reject if Feature.disabled?(:action_cable_notes, project || noteable.try(:group))
stream_for noteable
rescue ActiveRecord::RecordNotFound
reject
end
end
end

View File

@ -21,7 +21,7 @@ module WebIdeCSP
default_src = Array(request.content_security_policy.directives['default-src'] || [])
request.content_security_policy.directives['frame-src'] ||= default_src
request.content_security_policy.directives['frame-src'].concat([webpack_url, 'https://*.vscode-cdn.net/'])
request.content_security_policy.directives['frame-src'].concat([webpack_url, 'https://*.web-ide.gitlab-static.net/'])
request.content_security_policy.directives['worker-src'] ||= default_src
request.content_security_policy.directives['worker-src'].concat([webpack_url])

View File

@ -72,6 +72,7 @@ class Projects::IssuesController < Projects::ApplicationController
push_frontend_feature_flag(:epic_widget_edit_confirmation, project)
push_frontend_feature_flag(:moved_mr_sidebar, project)
push_frontend_feature_flag(:move_close_into_dropdown, project)
push_frontend_feature_flag(:action_cable_notes, project)
end
around_action :allow_gitaly_ref_name_caching, only: [:discussions]

View File

@ -50,6 +50,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
push_frontend_feature_flag(:mr_activity_filters, current_user)
push_frontend_feature_flag(:review_apps_redeploy_mr_widget, project)
push_frontend_feature_flag(:ci_job_failures_in_mr, project)
push_frontend_feature_flag(:action_cable_notes, project)
end
before_action only: [:edit] do

View File

@ -318,6 +318,7 @@ module ApplicationSettingsHelper
:max_export_size,
:max_import_size,
:max_import_remote_file_size,
:max_decompressed_archive_size,
:max_pages_size,
:max_pages_custom_domains_per_project,
:max_terraform_state_size_bytes,

View File

@ -178,6 +178,10 @@ module NotesHelper
def notes_data(issuable)
data = {
noteableType: @noteable.class.underscore,
noteableId: @noteable.id,
projectId: @project&.id,
groupId: @group&.id,
discussionsPath: discussions_path(issuable),
registerPath: new_session_path(:user, redirect_to_referer: 'yes', anchor: 'register-pane'),
newSessionPath: new_session_path(:user, redirect_to_referer: 'yes'),

View File

@ -8,6 +8,7 @@ class DeviseMailer < Devise::Mailer
helper EmailsHelper
helper ApplicationHelper
helper RegistrationsHelper
def password_change_by_admin(record, opts = {})
devise_mail(record, :password_change_by_admin, opts)

View File

@ -39,6 +39,7 @@ class Notify < ApplicationMailer
helper GitlabRoutingHelper
helper IssuablesHelper
helper InProductMarketingHelper
helper RegistrationsHelper
def test_email(recipient_email, subject, body)
mail_with_locale(

View File

@ -263,6 +263,10 @@ class ApplicationSetting < MainClusterwide::ApplicationRecord
presence: true,
numericality: { only_integer: true, greater_than_or_equal_to: 0 }
validates :max_decompressed_archive_size,
presence: true,
numericality: { only_integer: true, greater_than_or_equal_to: 0 }
validates :max_pages_size,
presence: true,
numericality: {

View File

@ -120,6 +120,7 @@ module ApplicationSettingImplementation
max_export_size: 0,
max_import_size: 0,
max_import_remote_file_size: 10240,
max_decompressed_archive_size: 25600,
max_terraform_state_size_bytes: 0,
max_yaml_size_bytes: 1.megabyte,
max_yaml_depth: 100,

View File

@ -171,9 +171,9 @@ module Noteable
return unless etag_caching_enabled?
# TODO: We need to figure out a way to make ETag caching work for group-level work items
return if is_a?(Issue) && project.nil?
Gitlab::EtagCaching::Store.new.touch(note_etag_key) unless is_a?(Issue) && project.nil?
Gitlab::EtagCaching::Store.new.touch(note_etag_key)
Noteable::NotesChannel.broadcast_to(self, event: 'updated') if Feature.enabled?(:action_cable_notes, project || try(:group))
end
def note_etag_key

View File

@ -3,6 +3,7 @@
module WebHooks
module AutoDisabling
extend ActiveSupport::Concern
include ::Gitlab::Loggable
ENABLED_HOOK_TYPES = %w[ProjectHook].freeze
MAX_FAILURES = 100
@ -86,17 +87,14 @@ module WebHooks
recent_failures > FAILURE_THRESHOLD && disabled_until.blank?
end
def disable!
return if !auto_disabling_enabled? || permanently_disabled?
update_attribute(:recent_failures, EXCEEDED_FAILURE_THRESHOLD)
end
def enable!
return unless auto_disabling_enabled?
return if recent_failures == 0 && disabled_until.nil? && backoff_count == 0
assign_attributes(recent_failures: 0, disabled_until: nil, backoff_count: 0)
attrs = { recent_failures: 0, disabled_until: nil, backoff_count: 0 }
assign_attributes(attrs)
logger.info(hook_id: id, action: 'enable', **attrs)
save(validate: false)
end
@ -114,14 +112,21 @@ module WebHooks
end
assign_attributes(attrs)
save(validate: false) if changed?
return unless changed?
logger.info(hook_id: id, action: 'backoff', **attrs)
save(validate: false)
end
def failed!
return unless auto_disabling_enabled?
return unless recent_failures < MAX_FAILURES
assign_attributes(disabled_until: nil, backoff_count: 0, recent_failures: next_failure_count)
attrs = { disabled_until: nil, backoff_count: 0, recent_failures: next_failure_count }
assign_attributes(**attrs)
logger.info(hook_id: id, action: 'disable', **attrs)
save(validate: false)
end
@ -147,6 +152,10 @@ module WebHooks
private
def logger
@logger ||= Gitlab::WebHooks::Logger.build
end
def next_failure_count
recent_failures.succ.clamp(1, MAX_FAILURES)
end

View File

@ -167,7 +167,7 @@ class Integration < ApplicationRecord
raise ArgumentError, "Unknown field storage: #{storage}"
end
boolean_accessor(name) if attrs[:type] == 'checkbox' && storage != :attribute
boolean_accessor(name) if attrs[:type] == :checkbox && storage != :attribute
end
# :nocov:
@ -472,7 +472,7 @@ class Integration < ApplicationRecord
# use `#secret?` here.
# See: https://gitlab.com/groups/gitlab-org/-/epics/7652
def secret_fields
fields.select { |f| f[:type] == 'password' }.pluck(:name)
fields.select { |f| f[:type] == :password }.pluck(:name)
end
# Expose a list of fields in the JSON endpoint.
@ -517,7 +517,7 @@ class Integration < ApplicationRecord
end
def api_field_names
fields.reject { _1[:type] == 'password' || _1[:name] == 'webhook' }.pluck(:name)
fields.reject { _1[:type] == :password || _1[:name] == 'webhook' }.pluck(:name)
end
def form_fields

View File

@ -32,7 +32,7 @@ module Integrations
field :app_store_private_key, api_only: true
field :app_store_protected_refs,
type: 'checkbox',
type: :checkbox,
section: SECTION_TYPE_CONFIGURATION,
title: -> { s_('AppleAppStore|Protected branches and tags only') },
checkbox_label: -> { s_('AppleAppStore|Only set variables on protected branches and tags') }

View File

@ -7,7 +7,7 @@ module Integrations
validates :api_key, presence: true, if: :activated?
field :api_key,
type: 'password',
type: :password,
title: 'API key',
help: -> { s_('AsanaService|User Personal Access Token. User must have access to the task. All comments are attributed to this user.') },
non_empty_password_title: -> { s_('ProjectService|Enter new API key') },

View File

@ -5,7 +5,7 @@ module Integrations
validates :token, presence: true, if: :activated?
field :token,
type: 'password',
type: :password,
non_empty_password_title: -> { s_('ProjectService|Enter new token') },
non_empty_password_help: -> { s_('ProjectService|Leave blank to use your current token.') },
placeholder: '',

View File

@ -24,7 +24,7 @@ module Integrations
help: -> { s_('BambooService|The user with API access to the Bamboo server.') }
field :password,
type: 'password',
type: :password,
non_empty_password_title: -> { s_('ProjectService|Enter new password') },
non_empty_password_help: -> { s_('ProjectService|Leave blank to use your current password') }

View File

@ -78,27 +78,27 @@ module Integrations
def default_fields
[
{
type: 'checkbox',
type: :checkbox,
section: SECTION_TYPE_CONFIGURATION,
name: 'notify_only_broken_pipelines',
help: 'Do not send notifications for successful pipelines.'
}.freeze,
{
type: 'select',
type: :select,
section: SECTION_TYPE_CONFIGURATION,
name: 'branches_to_be_notified',
title: s_('Integrations|Branches for which notifications are to be sent'),
choices: self.class.branch_choices
}.freeze,
{
type: 'text',
type: :text,
section: SECTION_TYPE_CONFIGURATION,
name: 'labels_to_be_notified',
placeholder: '~backend,~frontend',
help: 'Send notifications for issue, merge request, and comment events with the listed labels only. Leave blank to receive notifications for all events.'
}.freeze,
{
type: 'select',
type: :select,
section: SECTION_TYPE_CONFIGURATION,
name: 'labels_to_be_notified_behavior',
choices: [
@ -110,8 +110,8 @@ module Integrations
next unless requires_webhook?
fields.unshift(
{ type: 'text', name: 'webhook', help: webhook_help, required: true }.freeze,
{ type: 'text', name: 'username', placeholder: 'GitLab-integration' }.freeze
{ type: :text, name: 'webhook', help: webhook_help, required: true }.freeze,
{ type: :text, name: 'username', placeholder: 'GitLab-integration' }.freeze
)
end.freeze
end
@ -264,7 +264,7 @@ module Integrations
def build_event_channels
event_channel_names.map do |channel_field|
{ type: 'text', name: channel_field, placeholder: default_channel_placeholder }
{ type: :text, name: channel_field, placeholder: default_channel_placeholder }
end
end

View File

@ -16,7 +16,7 @@ module Integrations
required: true
field :token,
type: 'password',
type: :password,
title: -> { _('Token') },
help: -> do
s_('ProjectService|The token you get after you create a Buildkite pipeline with a GitLab repository.')

View File

@ -13,7 +13,7 @@ module Integrations
format: { with: SUBDOMAIN_REGEXP }, length: { in: 1..63 }
field :token,
type: 'password',
type: :password,
title: -> { _('Campfire token') },
help: -> { s_('CampfireService|API authentication token from Campfire.') },
non_empty_password_title: -> { s_('ProjectService|Enter new token') },

View File

@ -32,7 +32,7 @@ module Integrations
help: -> { s_('DatadogIntegration|(Advanced) The full URL for your Datadog site.') }
field :api_key,
type: 'password',
type: :password,
title: -> { _('API key') },
non_empty_password_title: -> { s_('ProjectService|Enter new API key') },
non_empty_password_help: -> { s_('ProjectService|Leave blank to use your current API key') },
@ -48,7 +48,7 @@ module Integrations
field :archive_trace_events,
storage: :attribute,
type: 'checkbox',
type: :checkbox,
title: -> { _('Logs') },
checkbox_label: -> { _('Enable logs collection') },
help: -> { s_('When enabled, job logs are collected by Datadog and displayed along with pipeline execution traces.') }
@ -73,7 +73,7 @@ module Integrations
end
field :datadog_tags,
type: 'textarea',
type: :textarea,
title: -> { s_('DatadogIntegration|Tags') },
placeholder: "tag:value\nanother_tag:value",
help: -> do

View File

@ -14,11 +14,11 @@ module Integrations
required: true
field :notify_only_broken_pipelines,
type: 'checkbox',
type: :checkbox,
section: SECTION_TYPE_CONFIGURATION
field :branches_to_be_notified,
type: 'select',
type: :select,
section: SECTION_TYPE_CONFIGURATION,
title: -> { s_('Integrations|Branches for which notifications are to be sent') },
choices: -> { branch_choices }

View File

@ -16,7 +16,7 @@ module Integrations
required: true
field :token,
type: 'password',
type: :password,
help: -> { s_('ProjectService|Token for the Drone project.') },
non_empty_password_title: -> { s_('ProjectService|Enter new token') },
non_empty_password_help: -> { s_('ProjectService|Leave blank to use your current token.') },

View File

@ -10,7 +10,7 @@ module Integrations
validate :number_of_recipients_within_limit, if: :validate_recipients?
field :send_from_committer_email,
type: 'checkbox',
type: :checkbox,
title: -> { s_("EmailsOnPushService|Send from committer") },
help: -> do
@help ||= begin
@ -21,17 +21,17 @@ module Integrations
end
field :disable_diffs,
type: 'checkbox',
type: :checkbox,
title: -> { s_("EmailsOnPushService|Disable code diffs") },
help: -> { s_("EmailsOnPushService|Don't include possibly sensitive code diffs in notification body.") }
field :branches_to_be_notified,
type: 'select',
type: :select,
title: -> { s_('Integrations|Branches for which notifications are to be sent') },
choices: branch_choices
field :recipients,
type: 'textarea',
type: :textarea,
placeholder: -> { s_('EmailsOnPushService|tanuki@example.com gitlab@example.com') },
help: -> { s_('EmailsOnPushService|Emails separated by whitespace.') }

View File

@ -11,15 +11,15 @@ module Integrations
non_empty_password_title
].concat(BOOLEAN_ATTRIBUTES).freeze
TYPES = %w[text textarea password checkbox select].freeze
TYPES = %i[text textarea password checkbox select].freeze
attr_reader :name, :integration_class
def initialize(name:, integration_class:, type: 'text', is_secret: false, api_only: false, **attributes)
def initialize(name:, integration_class:, type: :text, is_secret: false, api_only: false, **attributes)
@name = name.to_s.freeze
@integration_class = integration_class
attributes[:type] = is_secret ? 'password' : type
attributes[:type] = is_secret ? :password : type
attributes[:api_only] = api_only
attributes[:is_secret] = is_secret
@attributes = attributes.freeze
@ -42,7 +42,7 @@ module Integrations
end
def secret?
self[:type] == 'password'
self[:type] == :password
end
ATTRIBUTES.each do |name|

View File

@ -10,11 +10,11 @@ module Integrations
required: true
field :notify_only_broken_pipelines,
type: 'checkbox',
type: :checkbox,
section: SECTION_TYPE_CONFIGURATION
field :branches_to_be_notified,
type: 'select',
type: :select,
section: SECTION_TYPE_CONFIGURATION,
title: -> { s_('Integrations|Branches for which notifications are to be sent') },
choices: -> { branch_choices }

View File

@ -25,7 +25,7 @@ module Integrations
required: true
field :password,
type: 'password',
type: :password,
title: -> { s_('HarborIntegration|Harbor password') },
help: -> { s_('HarborIntegration|Password for your Harbor username.') },
non_empty_password_title: -> { s_('HarborIntegration|Enter new Harbor password') },

View File

@ -23,7 +23,7 @@ module Integrations
placeholder: 'irc://irc.network.net:6697/'
field :recipients,
type: 'textarea',
type: :textarea,
title: -> { s_('IrkerService|Recipients') },
placeholder: 'irc[s]://irc.network.net[:port]/#channel',
required: true,
@ -45,7 +45,7 @@ module Integrations
end
field :colorize_messages,
type: 'checkbox',
type: :checkbox,
title: -> { _('Colorize messages') }
# NOTE: This field is only used internally to store the parsed

View File

@ -22,7 +22,7 @@ module Integrations
help: -> { s_('The username for the Jenkins server.') }
field :password,
type: 'password',
type: :password,
help: -> { s_('The password for the Jenkins server.') },
non_empty_password_title: -> { s_('ProjectService|Enter new password.') },
non_empty_password_help: -> { s_('ProjectService|Leave blank to use your current password.') }

View File

@ -74,7 +74,7 @@ module Integrations
exposes_secrets: true
field :jira_auth_type,
type: 'select',
type: :select,
required: true,
section: SECTION_TYPE_CONNECTION,
title: -> { s_('JiraService|Authentication type') },

View File

@ -5,7 +5,7 @@ module Integrations
include Ci::TriggersHelper
field :token,
type: 'password',
type: :password,
non_empty_password_title: -> { s_('ProjectService|Enter new token') },
non_empty_password_help: -> { s_('ProjectService|Leave blank to use your current token.') },
placeholder: ''

View File

@ -10,12 +10,12 @@ module Integrations
required: true
field :notify_only_broken_pipelines,
type: 'checkbox',
type: :checkbox,
section: SECTION_TYPE_CONFIGURATION,
help: 'If selected, successful pipelines do not trigger a notification event.'
field :branches_to_be_notified,
type: 'select',
type: :select,
section: SECTION_TYPE_CONFIGURATION,
title: -> { s_('Integrations|Branches for which notifications are to be sent') },
choices: -> { branch_choices }

View File

@ -11,7 +11,7 @@ module Integrations
required: true
field :token,
type: 'password',
type: :password,
title: -> { _('Token') },
help: -> { _('Enter your Packagist token.') },
non_empty_password_title: -> { s_('ProjectService|Enter new token') },

View File

@ -10,19 +10,19 @@ module Integrations
validate :number_of_recipients_within_limit, if: :validate_recipients?
field :recipients,
type: 'textarea',
type: :textarea,
help: -> { _('Comma-separated list of email addresses.') },
required: true
field :notify_only_broken_pipelines,
type: 'checkbox'
type: :checkbox
field :notify_only_default_branch,
type: 'checkbox',
type: :checkbox,
api_only: true
field :branches_to_be_notified,
type: 'select',
type: :select,
title: -> { s_('Integrations|Branches for which notifications are to be sent') },
choices: branch_choices

View File

@ -7,7 +7,7 @@ module Integrations
validates :token, presence: true, if: :activated?
field :token,
type: 'password',
type: :password,
help: -> { s_('PivotalTrackerService|Pivotal Tracker API token. User must have access to the story. All comments are attributed to this user.') },
non_empty_password_title: -> { s_('ProjectService|Enter new token') },
non_empty_password_help: -> { s_('ProjectService|Leave blank to use your current token.') },

View File

@ -6,7 +6,7 @@ module Integrations
include Gitlab::Utils::StrongMemoize
field :manual_configuration,
type: 'checkbox',
type: :checkbox,
title: -> { s_('PrometheusService|Active') },
help: -> { s_('PrometheusService|Select this checkbox to override the auto configuration settings with your own settings.') },
required: true
@ -24,7 +24,7 @@ module Integrations
required: false
field :google_iap_service_account_json,
type: 'textarea',
type: :textarea,
title: 'Google IAP Service Account JSON',
placeholder: -> { s_('PrometheusService|{ "type": "service_account", "project_id": ... }') },
help: -> { s_('PrometheusService|The contents of the credentials.json file of your service account.') },

View File

@ -10,12 +10,12 @@ module Integrations
required: true
field :notify_only_broken_pipelines,
type: 'checkbox',
type: :checkbox,
section: SECTION_TYPE_CONFIGURATION,
help: 'If selected, successful pipelines do not trigger a notification event.'
field :branches_to_be_notified,
type: 'select',
type: :select,
section: SECTION_TYPE_CONFIGURATION,
title: -> { s_('Integrations|Branches for which notifications are to be sent') },
choices: -> { branch_choices }

View File

@ -7,7 +7,7 @@ module Integrations
validates :api_key, :user_key, :priority, presence: true, if: :activated?
field :api_key,
type: 'password',
type: :password,
title: -> { _('API key') },
help: -> { s_('PushoverService|Enter your application key.') },
non_empty_password_title: -> { s_('ProjectService|Enter new API key') },
@ -16,7 +16,7 @@ module Integrations
required: true
field :user_key,
type: 'password',
type: :password,
title: -> { _('User key') },
help: -> { s_('PushoverService|Enter your user key.') },
non_empty_password_title: -> { s_('PushoverService|Enter new user key') },
@ -30,7 +30,7 @@ module Integrations
placeholder: ''
field :priority,
type: 'select',
type: :select,
required: true,
choices: -> do
[
@ -42,7 +42,7 @@ module Integrations
end
field :sound,
type: 'select',
type: :select,
choices: -> do
[
['Device default sound', nil],

View File

@ -5,7 +5,7 @@ module Integrations
include Ci::TriggersHelper
field :token,
type: 'password',
type: :password,
non_empty_password_title: -> { s_('ProjectService|Enter new token') },
non_empty_password_help: -> { s_('ProjectService|Leave blank to use your current token.') },
placeholder: ''

View File

@ -11,7 +11,7 @@ module Integrations
required: true
field :token,
type: 'password',
type: :password,
title: -> { s_('SquashTmIntegration|Secret token (optional)') },
non_empty_password_title: -> { s_('ProjectService|Enter new token') },
non_empty_password_help: -> { s_('ProjectService|Leave blank to use your current token.') },

View File

@ -22,7 +22,7 @@ module Integrations
help: -> { s_('ProjectService|Must have permission to trigger a manual build in TeamCity.') }
field :password,
type: 'password',
type: :password,
non_empty_password_title: -> { s_('ProjectService|Enter new password') },
non_empty_password_help: -> { s_('ProjectService|Leave blank to use your current password') }

View File

@ -10,11 +10,11 @@ module Integrations
required: true
field :notify_only_broken_pipelines,
type: 'checkbox',
type: :checkbox,
section: SECTION_TYPE_CONFIGURATION
field :branches_to_be_notified,
type: 'select',
type: :select,
section: SECTION_TYPE_CONFIGURATION,
title: -> { s_('Integrations|Branches for which notifications are to be sent') },
choices: -> { branch_choices }

View File

@ -10,11 +10,11 @@ module Integrations
required: true
field :notify_only_broken_pipelines,
type: 'checkbox',
type: :checkbox,
section: SECTION_TYPE_CONFIGURATION
field :branches_to_be_notified,
type: 'select',
type: :select,
section: SECTION_TYPE_CONFIGURATION,
title: -> { s_('Integrations|Branches for which notifications are to be sent') },
choices: -> { branch_choices }

View File

@ -19,7 +19,7 @@ module Integrations
exposes_secrets: true
field :api_token,
type: 'password',
type: :password,
title: -> { s_('ZentaoIntegration|ZenTao API token') },
non_empty_password_title: -> { s_('ZentaoIntegration|Enter new ZenTao API token') },
non_empty_password_help: -> { s_('ProjectService|Leave blank to use your current token.') },

View File

@ -197,9 +197,7 @@ class Note < ApplicationRecord
# Syncs `confidential` with `internal` as we rename the column.
# https://gitlab.com/gitlab-org/gitlab/-/issues/367923
before_create :set_internal_flag
after_destroy :expire_etag_cache
after_save :keep_around_commit, if: :for_project_noteable?, unless: -> { importing? || skip_keep_around_commits }
after_save :expire_etag_cache, unless: :importing?
after_save :touch_noteable, unless: :importing?
after_commit :notify_after_create, on: :create
after_commit :notify_after_destroy, on: :destroy
@ -207,6 +205,7 @@ class Note < ApplicationRecord
after_commit :trigger_note_subscription_create, on: :create
after_commit :trigger_note_subscription_update, on: :update
after_commit :trigger_note_subscription_destroy, on: :destroy
after_commit :expire_etag_cache, unless: :importing?
def trigger_note_subscription_create
return unless trigger_note_subscription?

View File

@ -1,9 +1,6 @@
# frozen_string_literal: true
class ProjectAuthorization < ApplicationRecord
BATCH_SIZE = 1000
SLEEP_DELAY = 0.1
extend SuppressCompositePrimaryKeyWarning
include FromUnion
@ -28,57 +25,6 @@ class ProjectAuthorization < ApplicationRecord
def self.insert_all(attributes)
super(attributes, unique_by: connection.schema_cache.primary_keys(table_name))
end
def self.insert_all_in_batches(attributes, per_batch = BATCH_SIZE)
add_delay = add_delay_between_batches?(entire_size: attributes.size, batch_size: per_batch)
log_details(entire_size: attributes.size, batch_size: per_batch) if add_delay
attributes.each_slice(per_batch) do |attributes_batch|
insert_all(attributes_batch)
perform_delay if add_delay
end
end
def self.delete_all_in_batches_for_project(project:, user_ids:, per_batch: BATCH_SIZE)
add_delay = add_delay_between_batches?(entire_size: user_ids.size, batch_size: per_batch)
log_details(entire_size: user_ids.size, batch_size: per_batch) if add_delay
user_ids.each_slice(per_batch) do |user_ids_batch|
project.project_authorizations.where(user_id: user_ids_batch).delete_all
perform_delay if add_delay
end
end
def self.delete_all_in_batches_for_user(user:, project_ids:, per_batch: BATCH_SIZE)
add_delay = add_delay_between_batches?(entire_size: project_ids.size, batch_size: per_batch)
log_details(entire_size: project_ids.size, batch_size: per_batch) if add_delay
project_ids.each_slice(per_batch) do |project_ids_batch|
user.project_authorizations.where(project_id: project_ids_batch).delete_all
perform_delay if add_delay
end
end
private_class_method def self.add_delay_between_batches?(entire_size:, batch_size:)
# The reason for adding a delay is to give the replica database enough time to
# catch up with the primary when large batches of records are being added/removed.
# Hance, we add a delay only if the GitLab installation has a replica database configured.
entire_size > batch_size &&
!::Gitlab::Database::LoadBalancing.primary_only?
end
private_class_method def self.log_details(entire_size:, batch_size:)
Gitlab::AppLogger.info(
entire_size: entire_size,
total_delay: (entire_size / batch_size.to_f).ceil * SLEEP_DELAY,
message: 'Project authorizations refresh performed with delay',
**Gitlab::ApplicationContext.current
)
end
private_class_method def self.perform_delay
sleep(SLEEP_DELAY)
end
end
ProjectAuthorization.prepend_mod_with('ProjectAuthorization')

View File

@ -0,0 +1,129 @@
# frozen_string_literal: true
module ProjectAuthorizations
# How to use this class
# authorizations_to_add:
# Rows to insert in the form `[{ user_id: user_id, project_id: project_id, access_level: access_level}, ...]
#
# ProjectAuthorizations::Changes.new do |changes|
# changes.add(authorizations_to_add)
# changes.remove_users_in_project(project, user_ids)
# changes.remove_projects_for_user(user, project_ids)
# end.apply!
class Changes
attr_reader :projects_to_remove, :users_to_remove, :authorizations_to_add
BATCH_SIZE = 1000
SLEEP_DELAY = 0.1
def initialize
@authorizations_to_add = []
yield self
end
def add(authorizations_to_add)
@authorizations_to_add += authorizations_to_add
end
def remove_users_in_project(project, user_ids)
@users_to_remove = { user_ids: user_ids, scope: project }
end
def remove_projects_for_user(user, project_ids)
@projects_to_remove = { project_ids: project_ids, scope: user }
end
def apply!
delete_authorizations_for_user if should_delete_authorizations_for_user?
delete_authorizations_for_project if should_delete_authorizations_for_project?
add_authorizations if should_add_authorization?
end
private
def should_add_authorization?
authorizations_to_add.present?
end
def should_delete_authorizations_for_user?
user && project_ids.present?
end
def should_delete_authorizations_for_project?
project && user_ids.present?
end
def add_authorizations
insert_all_in_batches(authorizations_to_add)
end
def delete_authorizations_for_user
delete_all_in_batches(resource: user,
ids_to_remove: project_ids,
column_name_of_ids_to_remove: :project_id)
end
def delete_authorizations_for_project
delete_all_in_batches(resource: project,
ids_to_remove: user_ids,
column_name_of_ids_to_remove: :user_id)
end
def delete_all_in_batches(resource:, ids_to_remove:, column_name_of_ids_to_remove:)
add_delay = add_delay_between_batches?(entire_size: ids_to_remove.size, batch_size: BATCH_SIZE)
log_details(entire_size: ids_to_remove.size, batch_size: BATCH_SIZE) if add_delay
ids_to_remove.each_slice(BATCH_SIZE) do |ids_batch|
resource.project_authorizations.where(column_name_of_ids_to_remove => ids_batch).delete_all
perform_delay if add_delay
end
end
def insert_all_in_batches(attributes)
add_delay = add_delay_between_batches?(entire_size: attributes.size, batch_size: BATCH_SIZE)
log_details(entire_size: attributes.size, batch_size: BATCH_SIZE) if add_delay
attributes.each_slice(BATCH_SIZE) do |attributes_batch|
ProjectAuthorization.insert_all(attributes_batch)
perform_delay if add_delay
end
end
def add_delay_between_batches?(entire_size:, batch_size:)
# The reason for adding a delay is to give the replica database enough time to
# catch up with the primary when large batches of records are being added/removed.
# Hence, we add a delay only if the GitLab installation has a replica database configured.
entire_size > batch_size &&
!::Gitlab::Database::LoadBalancing.primary_only?
end
def log_details(entire_size:, batch_size:)
Gitlab::AppLogger.info(
entire_size: entire_size,
total_delay: (entire_size / batch_size.to_f).ceil * SLEEP_DELAY,
message: 'Project authorizations refresh performed with delay',
**Gitlab::ApplicationContext.current
)
end
def perform_delay
sleep(SLEEP_DELAY)
end
def user
projects_to_remove&.[](:scope)
end
def project_ids
projects_to_remove&.[](:project_ids)
end
def project
users_to_remove&.[](:scope)
end
def user_ids
users_to_remove&.[](:user_ids)
end
end
end

View File

@ -67,7 +67,13 @@ class ProjectStatistics < ApplicationRecord
end
def update_repository_size
self.repository_size = project.repository.size * 1.megabyte
size = if Feature.enabled?(:recent_objects_for_project_statistics, project)
project.repository.recent_objects_size
else
project.repository.size
end
self.repository_size = size.megabytes
end
def update_wiki_size

View File

@ -47,7 +47,7 @@ class Repository
#
# For example, for entry `:commit_count` there's a method called `commit_count` which
# stores its data in the `commit_count` cache key.
CACHED_METHODS = %i(size commit_count readme_path contribution_guide
CACHED_METHODS = %i(size recent_objects_size commit_count readme_path contribution_guide
changelog license_blob license_gitaly gitignore
gitlab_ci_yml branch_names tag_names branch_count
tag_count avatar exists? root_ref merged_branch_names
@ -363,7 +363,7 @@ class Repository
end
def expire_statistics_caches
expire_method_caches(%i(size commit_count))
expire_method_caches(%i(size recent_objects_size commit_count))
end
def expire_all_method_caches
@ -579,6 +579,12 @@ class Repository
end
cache_method :size, fallback: 0.0
# The recent objects size of this repository in mebibytes.
def recent_objects_size
exists? ? raw_repository.recent_objects_size : 0.0
end
cache_method :recent_objects_size, fallback: 0.0
def commit_count
root_ref ? raw_repository.commit_count(root_ref) : 0
end

View File

@ -2,6 +2,8 @@
module Ci
class BridgePolicy < CommitStatusPolicy
include Ci::DeployablePolicy
condition(:can_update_downstream_branch) do
::Gitlab::UserAccess.new(@user, container: @subject.downstream_project)
.can_update_branch?(@subject.target_revision_ref)

View File

@ -2,6 +2,8 @@
module Ci
class BuildPolicy < CommitStatusPolicy
include Ci::DeployablePolicy
delegate { @subject.project }
condition(:protected_ref) do
@ -22,15 +24,6 @@ module Ci
end
end
# overridden in EE
condition(:protected_environment) do
false
end
condition(:outdated_deployment) do
@subject.outdated_deployment?
end
condition(:owner_of_job) do
@subject.triggered_by?(@user)
end
@ -83,17 +76,15 @@ module Ci
# Authorizing the user to access to protected entities.
# There is a "jailbreak" mode to exceptionally bypass the authorization,
# however, you should NEVER allow it, rather suspect it's a wrong feature/product design.
rule { ~can?(:jailbreak) & (archived | (protected_ref & ~admin) | protected_environment) }.policy do
rule { ~can?(:jailbreak) & (archived | (protected_ref & ~admin)) }.policy do
prevent :update_commit_status
end
rule { ~can?(:jailbreak) & (archived | protected_ref | protected_environment) }.policy do
rule { ~can?(:jailbreak) & (archived | protected_ref) }.policy do
prevent :update_build
prevent :erase_build
end
rule { outdated_deployment }.prevent :update_build
rule { can?(:admin_build) | (can?(:update_build) & owner_of_job & unprotected_ref) }.enable :erase_build
rule { can?(:public_access) & branch_allows_collaboration }.policy do

View File

@ -0,0 +1,17 @@
# frozen_string_literal: true
module Ci
module DeployablePolicy
extend ActiveSupport::Concern
included do
prepend_mod_with('Ci::DeployablePolicy') # rubocop: disable Cop/InjectEnterpriseEditionModule
condition(:outdated_deployment) do
@subject.outdated_deployment?
end
rule { outdated_deployment }.prevent :update_build
end
end
end

View File

@ -5,12 +5,16 @@ module Integrations
include RequestAwareEntity
include Gitlab::Utils::StrongMemoize
expose :section, :type, :name, :placeholder, :required, :choices, :checkbox_label
expose :section, :name, :placeholder, :required, :choices, :checkbox_label
expose :title do |field|
non_empty_password?(field) ? field[:non_empty_password_title] : field[:title]
end
expose :type do |field|
field[:type].to_s
end
expose :help do |field|
non_empty_password?(field) ? field[:non_empty_password_help] : field[:help]
end
@ -20,7 +24,7 @@ module Integrations
if non_empty_password?(field)
'true'
elsif field[:type] == 'checkbox'
elsif field[:type] == :checkbox
ActiveRecord::Type::Boolean.new.deserialize(value).to_s
elsif field[:name] == 'webhook' && integration.chat?
BaseChatNotification::SECRET_MASK if value.present?
@ -44,7 +48,7 @@ module Integrations
def non_empty_password?(field)
strong_memoize(:non_empty_password) do
field[:type] == 'password' && value_for(field).present?
field[:type] == :password && value_for(field).present?
end
end
end

View File

@ -15,6 +15,12 @@ module Admin
add_history_to_params!
plan_limits.assign_attributes(parsed_params)
validate_storage_limits
return error(plan_limits.errors.full_messages, :bad_request) if plan_limits.errors.any?
if plan_limits.update(parsed_params)
success
else
@ -26,6 +32,8 @@ module Admin
attr_accessor :current_user, :params, :plan, :plan_limits
delegate :notification_limit, :storage_size_limit, :enforcement_limit, to: :plan_limits
def can_update?
current_user.can_admin_all_resources?
end
@ -35,6 +43,39 @@ module Admin
parsed_params.merge!(limits_history: formatted_limits_history) unless formatted_limits_history.empty?
end
def validate_storage_limits
validate_notification_limit
validate_enforcement_limit
validate_storage_size_limit
end
def validate_notification_limit
return unless parsed_params.include?(:notification_limit)
return if notification_limit >= storage_size_limit && notification_limit <= enforcement_limit
plan_limits.errors.add(:notification_limit, "must be greater than or equal to " \
"storage_size_limit (Dashboard limit): #{storage_size_limit} " \
"and less than or equal to enforcement_limit: #{enforcement_limit}")
end
def validate_enforcement_limit
return unless parsed_params.include?(:enforcement_limit)
return if enforcement_limit >= storage_size_limit && enforcement_limit >= notification_limit
plan_limits.errors.add(:enforcement_limit, "must be greater than or equal to " \
"storage_size_limit (Dashboard limit): #{storage_size_limit} and " \
"greater than or equal to notification_limit: #{notification_limit}")
end
def validate_storage_size_limit
return unless parsed_params.include?(:storage_size_limit)
return if storage_size_limit <= enforcement_limit && storage_size_limit <= notification_limit
plan_limits.errors.add(:storage_size_limit, "(Dashboard limit) must be less than or equal to " \
"enforcement_limit: #{enforcement_limit} " \
"and notification_limit: #{notification_limit}")
end
# Overridden in EE
def parsed_params
params

View File

@ -64,13 +64,10 @@ module AuthorizedProjectUpdate
end
def refresh_authorizations
if user_ids_to_remove.any?
ProjectAuthorization.delete_all_in_batches_for_project(
project: project,
user_ids: user_ids_to_remove)
end
ProjectAuthorization.insert_all_in_batches(authorizations_to_create) if authorizations_to_create.any?
ProjectAuthorizations::Changes.new do |changes|
changes.add(authorizations_to_create)
changes.remove_users_in_project(project, user_ids_to_remove)
end.apply!
end
def apply_scopes(project_authorizations)

View File

@ -10,7 +10,6 @@ module Metrics
STAGES = ::Gitlab::Metrics::Dashboard::Stages
SEQUENCE = [
STAGES::CommonMetricsInserter,
STAGES::VariableEndpointInserter,
STAGES::PanelIdsInserter,
STAGES::TrackPanelType,
STAGES::UrlValidator

View File

@ -1,69 +0,0 @@
# frozen_string_literal: true
# Responsible for returning a filtered system dashboard
# containing only the default embedded metrics. This class
# operates by selecting metrics directly from the system
# dashboard.
#
# Why isn't this filtering in a processing stage? By filtering
# here, we ensure the dynamically-determined dashboard is cached.
#
# Use Gitlab::Metrics::Dashboard::Finder to retrive dashboards.
module Metrics
module Dashboard
class DefaultEmbedService < ::Metrics::Dashboard::BaseEmbedService
# For the default filtering for embedded metrics,
# uses the 'id' key in dashboard-yml definition for
# identification.
DEFAULT_EMBEDDED_METRICS_IDENTIFIERS = %w(
system_metrics_kubernetes_container_memory_total
system_metrics_kubernetes_container_cores_total
).freeze
class << self
def valid_params?(params)
embedded?(params[:embedded])
end
end
# Returns a new dashboard with only the matching
# metrics from the system dashboard, stripped of groups.
# @return [Hash]
def get_raw_dashboard
panels = panel_groups.each_with_object([]) do |group, panels|
matched_panels = group['panels'].select { |panel| matching_panel?(panel) }
panels.concat(matched_panels)
end
{ 'panel_groups' => [{ 'panels' => panels }] }
end
private
# Returns an array of the panels groups on the
# system dashboard
def panel_groups
::Metrics::Dashboard::SystemDashboardService
.new(project, nil)
.raw_dashboard['panel_groups']
end
# Identifies a panel as "matching" if any metric ids in
# the panel is in the list of identifiers to collect.
def matching_panel?(panel)
panel['metrics'].any? do |metric|
metric_identifiers.include?(metric['id'])
end
end
def metric_identifiers
DEFAULT_EMBEDDED_METRICS_IDENTIFIERS
end
def identifiers
metric_identifiers.join('|')
end
end
end
end

View File

@ -10,7 +10,6 @@ module Metrics
DASHBOARD_NAME = nil
SEQUENCE = [
STAGES::VariableEndpointInserter,
STAGES::PanelIdsInserter
].freeze

View File

@ -15,7 +15,6 @@ module Metrics
STAGES::CommonMetricsInserter,
STAGES::CustomMetricsInserter,
STAGES::CustomMetricsDetailsInserter,
STAGES::VariableEndpointInserter,
STAGES::PanelIdsInserter
].freeze

View File

@ -5,7 +5,7 @@ module Projects
include ::Gitlab::Utils::StrongMemoize
STAT_TO_CACHED_METHOD = {
repository_size: :size,
repository_size: [:size, :recent_objects_size],
commit_count: :commit_count
}.freeze
@ -37,7 +37,7 @@ module Projects
def method_caches_to_expire
strong_memoize(:method_caches_to_expire) do
statistics.map { |stat| STAT_TO_CACHED_METHOD[stat] }.compact
statistics.flat_map { |stat| STAT_TO_CACHED_METHOD[stat] }.compact
end
end

View File

@ -67,8 +67,10 @@ module Users
def update_authorizations(remove = [], add = [])
log_refresh_details(remove, add)
ProjectAuthorization.delete_all_in_batches_for_user(user: user, project_ids: remove) if remove.any?
ProjectAuthorization.insert_all_in_batches(add) if add.any?
ProjectAuthorizations::Changes.new do |changes|
changes.add(add)
changes.remove_projects_for_user(user, remove)
end.apply!
# Since we batch insert authorization rows, Rails' associations may get
# out of sync. As such we force a reload of the User object.

View File

@ -33,6 +33,9 @@
.form-group
= f.label :bulk_import_max_download_file_size, s_('BulkImport|Direct transfer maximum download file size (MB)'), class: 'label-light'
= f.number_field :bulk_import_max_download_file_size, class: 'form-control gl-form-input', title: s_('BulkImport|Maximum download file size when importing from source GitLab instances by direct transfer.'), data: { toggle: 'tooltip', container: 'body' }
.form-group
= f.label :max_decompressed_archive_size, s_('Import|Maximum decompressed size (MiB)'), class: 'label-light'
= f.number_field :max_decompressed_archive_size, class: 'form-control gl-form-input', title: s_('Import|Maximum size of decompressed archive.'), data: { toggle: 'tooltip', container: 'body' }
%span.form-text.text-muted= _('Set to 0 for no size limit.')
.form-group
= f.label :session_expire_delay, _('Session duration (minutes)'), class: 'label-light'

View File

@ -8,6 +8,8 @@
= render "layouts/bizible"
= render "layouts/google_tag_manager_body"
= render_if_exists 'devise/shared/delete_unconfirmed_users_flash'
.well-confirmation.gl-text-center.gl-mb-6
%h1.gl-mt-0
= _("Almost there...")

View File

@ -1,9 +1,11 @@
- confirmation_link = confirmation_url(@resource, confirmation_token: @token)
- if @resource.unconfirmed_email.present? || !@resource.created_recently?
#content
= email_default_heading(@email)
%p= _('Click the link below to confirm your email address.')
#cta
= render_if_exists 'devise/shared/delete_unconfirmed_users'
= link_to _('Confirm your email address'), confirmation_link
- else
#content
@ -13,4 +15,5 @@
= email_default_heading(_("Welcome, %{name}!") % { name: @resource.name })
%p= _("To get started, click the link below to confirm your account.")
#cta
= render_if_exists 'devise/shared/delete_unconfirmed_users'
= link_to _('Confirm your account'), confirmation_link

View File

@ -10,4 +10,6 @@
<%= _('To get started, use the link below to confirm your account.') %>
<% end %>
<%= render_if_exists 'devise/shared/delete_unconfirmed_users_text' %>
<%= confirmation_url(@resource, confirmation_token: @token) %>

View File

@ -24,7 +24,10 @@ class WebHookWorker
# present in the request header so the hook can pass this same header value in its request.
Gitlab::WebHooks::RecursionDetection.set_request_uuid(params[:recursion_detection_request_uuid])
WebHookService.new(hook, data, hook_name, jid).execute
WebHookService.new(hook, data, hook_name, jid).execute.tap do |response|
log_extra_metadata_on_done(:response_status, response.status)
log_extra_metadata_on_done(:http_status, response[:http_status])
end
end
end
# rubocop:enable Scalability/IdempotentWorker

View File

@ -0,0 +1,8 @@
---
name: action_cable_notes
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/127964
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/412823
milestone: '16.3'
type: development
group: group::project management
default_enabled: false

View File

@ -0,0 +1,8 @@
---
name: recent_objects_for_project_statistics
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/127867
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/420125
milestone: '16.3'
type: development
group: group::utilization
default_enabled: false

View File

@ -0,0 +1,7 @@
# frozen_string_literal: true
class AddMaxDecompressionArchiveSizeToApplicationSettings < Gitlab::Database::Migration[2.1]
def change
add_column :application_settings, :max_decompressed_archive_size, :integer, default: 25600, null: false
end
end

View File

@ -0,0 +1,15 @@
# frozen_string_literal: true
class AddApplicationSettingsMaxDecompressionSizeConstraint < Gitlab::Database::Migration[2.1]
CONSTRAINT_NAME = 'app_settings_max_decompressed_archive_size_positive'
disable_ddl_transaction!
def up
add_check_constraint :application_settings, 'max_decompressed_archive_size >= 0', CONSTRAINT_NAME
end
def down
remove_check_constraint :application_settings, CONSTRAINT_NAME
end
end

View File

@ -0,0 +1 @@
1bc56bc33ef336f4c97d15c8b726dad319a5775b8f8efa759bb14bb9a4db44eb

View File

@ -0,0 +1 @@
1b158b3191749032084d69f68e02f8aaddeb640ee10db4f1bb43041e4fd2d4ac

View File

@ -11861,12 +11861,14 @@ CREATE TABLE application_settings (
bulk_import_max_download_file_size bigint DEFAULT 5120 NOT NULL,
max_import_remote_file_size bigint DEFAULT 10240 NOT NULL,
protected_paths_for_get_request text[] DEFAULT '{}'::text[] NOT NULL,
max_decompressed_archive_size integer DEFAULT 25600 NOT NULL,
CONSTRAINT app_settings_container_reg_cleanup_tags_max_list_size_positive CHECK ((container_registry_cleanup_tags_service_max_list_size >= 0)),
CONSTRAINT app_settings_container_registry_pre_import_tags_rate_positive CHECK ((container_registry_pre_import_tags_rate >= (0)::numeric)),
CONSTRAINT app_settings_dep_proxy_ttl_policies_worker_capacity_positive CHECK ((dependency_proxy_ttl_group_policy_worker_capacity >= 0)),
CONSTRAINT app_settings_ext_pipeline_validation_service_url_text_limit CHECK ((char_length(external_pipeline_validation_service_url) <= 255)),
CONSTRAINT app_settings_git_rate_limit_users_alertlist_max_usernames CHECK ((cardinality(git_rate_limit_users_alertlist) <= 100)),
CONSTRAINT app_settings_git_rate_limit_users_allowlist_max_usernames CHECK ((cardinality(git_rate_limit_users_allowlist) <= 100)),
CONSTRAINT app_settings_max_decompressed_archive_size_positive CHECK ((max_decompressed_archive_size >= 0)),
CONSTRAINT app_settings_max_pages_custom_domains_per_project_check CHECK ((max_pages_custom_domains_per_project >= 0)),
CONSTRAINT app_settings_max_terraform_state_size_bytes_check CHECK ((max_terraform_state_size_bytes >= 0)),
CONSTRAINT app_settings_p_cleanup_package_file_worker_capacity_positive CHECK ((packages_cleanup_package_file_worker_capacity >= 0)),

View File

@ -820,6 +820,23 @@ This file is located at:
This structured log file records internal activity in the `mail_room` gem.
Its name and path are configurable, so the name and path may not match the above.
## `web_hooks.log`
> Introduced in GitLab 16.3.
This file is located at:
- `/var/log/gitlab/gitlab-rails/web_hooks.log` on Linux package installations.
- `/home/git/gitlab/log/web_hooks.log` on self-compiled installations.
The back-off, disablement, and re-enablement events for Webhook are recorded in this file. For example:
```json
{"severity":"INFO","time":"2020-11-24T02:30:59.860Z","hook_id":12,"action":"backoff","disabled_until":"2020-11-24T04:30:59.860Z","backoff_count":2,"recent_failures":2}
{"severity":"INFO","time":"2020-11-24T02:30:59.860Z","hook_id":12,"action":"disable","disabled_until":null,"backoff_count":5,"recent_failures":100}
{"severity":"INFO","time":"2020-11-24T02:30:59.860Z","hook_id":12,"action":"enable","disabled_until":null,"backoff_count":0,"recent_failures":0}
```
## Reconfigure logs
Reconfigure log files are in `/var/log/gitlab/reconfigure` for Linux package installations. Self-compiled installations

View File

@ -135,6 +135,26 @@ To modify the maximum download file size for imports by direct transfer:
1. Expand **Account and limit**.
1. Increase or decrease by changing the value in **Direct transfer maximum download file size (MB)**. Set to `0` to set no download file size limit.
## Maximum decompressed file size for imported archives
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/128218) in GitLab 16.3.
When you [import a project](../../user/project/settings/import_export.md), you can specify the maximum decompressed file size for imported archives. The default value is 25 GB.
When you import a compressed file, the decompressed size cannot exceed the maximum decompressed file size limit. If the decompressed size exceeds the configured limit, the following error is returned:
```plaintext
Decompressed archive size validation failed.
```
To modify the maximum decompressed file size for imports in GitLab:
1. On the left sidebar, expand the top-most chevron (**{chevron-down}**).
1. Select **Admin Area**.
1. Select **Settings > General**.
1. Expand **Account and limit**.
1. Set another value for **Maximum decompressed size (MiB)**.
## Personal access token prefix
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/20968) in GitLab 13.7.

View File

@ -6888,6 +6888,7 @@ Input type: `UserAddOnAssignmentCreateInput`
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="mutationuseraddonassignmentcreateaddonpurchase"></a>`addOnPurchase` | [`AddOnPurchase`](#addonpurchase) | AddOnPurchase state after mutation. |
| <a id="mutationuseraddonassignmentcreateclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationuseraddonassignmentcreateerrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |

View File

@ -324,7 +324,8 @@ Example response:
"version": "1.3.0.17",
"tags": "",
"packageContent": "https://gitlab.example.com/api/v4/projects/1/packages/nuget/download/MyNuGetPkg/1.3.0.17/helloworld.1.3.0.17.nupkg",
"summary": "Summary of the package",
"description": "Description of the package",
"summary": "Description of the package",
"published": "2023-05-08T17:23:25Z",
}
}
@ -367,7 +368,8 @@ Example response:
"version": "1.3.0.17",
"tags": "",
"packageContent": "https://gitlab.example.com/api/v4/projects/1/packages/nuget/download/MyNuGetPkg/1.3.0.17/helloworld.1.3.0.17.nupkg",
"summary": "Summary of the package",
"description": "Description of the package",
"summary": "Description of the package",
"published": "2023-05-08T17:23:25Z",
}
}
@ -405,7 +407,8 @@ Example response:
"authors": "Author1, Author2",
"id": "MyNuGetPkg",
"title": "MyNuGetPkg",
"summary": "Summary of the package",
"description": "Description of the package",
"summary": "Description of the package",
"totalDownloads": 0,
"verified": true,
"version": "1.3.0.17",

View File

@ -44,6 +44,7 @@ Example response:
"max_export_size": 50,
"max_import_size": 50,
"max_import_remote_file_size": 10240,
"max_decompressed_archive_size": 25600,
"user_oauth_applications" : true,
"updated_at" : "2016-01-04T15:44:55.176Z",
"session_expire_delay" : 10080,
@ -185,6 +186,7 @@ Example response:
"max_export_size": 50,
"max_import_size": 50,
"max_import_remote_file_size": 10240,
"max_decompressed_archive_size": 25600,
"session_expire_delay": 10080,
"default_ci_config_path" : null,
"default_project_visibility": "internal",
@ -456,9 +458,10 @@ listed in the descriptions of the relevant settings.
| `maintenance_mode` **(PREMIUM)** | boolean | no | When instance is in maintenance mode, non-administrative users can sign in with read-only access and make read-only API requests. |
| `max_artifacts_size` | integer | no | Maximum artifacts size in MB. |
| `max_attachment_size` | integer | no | Limit attachment size in MB. |
| `max_decompressed_archive_size` | integer | no | Maximum decompressed file size for imported archives in MB. Set to `0` for unlimited. Default is `25600`. |
| `max_export_size` | integer | no | Maximum export size in MB. 0 for unlimited. Default = 0 (unlimited). |
| `max_import_size` | integer | no | Maximum import size in MB. 0 for unlimited. Default = 0 (unlimited). [Changed](https://gitlab.com/gitlab-org/gitlab/-/issues/251106) from 50 MB to 0 in GitLab 13.8. |
| `max_import_remote_file_size` | integer | no | Maximum remote file size for imports from external object storages. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/384976) in GitLab 16.3. |
| `max_import_remote_file_size` | integer | no | Maximum remote file size for imports from external object storages. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/384976) in GitLab 16.3. |
| `max_pages_size` | integer | no | Maximum size of pages repositories in MB. |
| `max_personal_access_token_lifetime` **(ULTIMATE SELF)** | integer | no | Maximum allowable lifetime for access tokens in days. When left blank, default value of 365 is applied. When set, value must be 365 or less. When changed, existing access tokens with an expiration date beyond the maximum allowable lifetime are revoked.|
| `max_ssh_key_lifetime` **(ULTIMATE SELF)** | integer | no | Maximum allowable lifetime for SSH keys in days. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/1007) in GitLab 14.6. |

View File

@ -15,7 +15,7 @@ AI is moving very quickly, and we need to be able to keep pace with changes in t
The following diagram from the [architecture blueprint](../architecture/blueprints/ai_gateway/index.md) shows a simplified view of how the different components in GitLab interact. The abstraction layer helps avoid code duplication within the REST APIs within the `AI API` block.
![architecture diagram](../architecture/blueprints/ai_gateway/img/architecture.png)
![architecture diagram](img/architecture.png)
## SaaS-based AI abstraction layer

View File

@ -1,6 +1,6 @@
---
stage: none
group: none
stage: ModelOps
group: AI Framework
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
---
@ -421,6 +421,52 @@ module EE
end
```
### Pairing requests with responses
Because multiple users' requests can be processed in parallel, when receiving responses,
it can be difficult to pair a response with its original request. The `requestId`
field can be used for this purpose, because both the request and response are assured
to have the same `requestId` UUID.
### Caching
AI requests and responses can be cached. Cached conversation is being used to
display user interaction with AI features. In the current implementation, this cache
is not used to skip consecutive calls to the AI service when a user repeats
their requests.
```graphql
query {
aiMessages {
nodes {
id
requestId
content
role
errors
timestamp
}
}
}
```
This cache is especially useful for chat functionality. For other services,
caching is disabled. (It can be enabled for a service by using `skip_cache: false`
option.)
Caching has following limitations:
- Messages are stored in Redis stream.
- There is a single stream of messages per user. This means that all services
currently share the same cache. If needed, this could be extended to multiple
streams per user (after checking with the infrastructure team that Redis can handle
the estimated amount of messages).
- Only the last 50 messages (requests + responses) are kept.
- Expiration time of the stream is 3 days since adding last message.
- User can access only their own messages. There is no authorization on the caching
level, and any authorization (if accessed by not current user) is expected on
the service layer.
### Check if feature is allowed for this resource based on namespace settings
There are two settings allowed on root namespace level that restrict the use of AI features:

View File

@ -1298,6 +1298,12 @@ Use lowercase for **runner managers**. These are a type of runner that can creat
Use lowercase for **runner workers**. This is the process created by the runner on the host computing platform to run jobs. See also [GitLab Runner](#gitlab-runner).
## runner authentication token
Use **runner authentication token** instead of variations like **runner token**, **authentication token**, or **token**.
Runners are assigned runner authentication tokens when they are created, and use them to authenticate with GitLab when
they execute jobs.
## (s)
Do not use **(s)** to make a word optionally plural. It can slow down comprehension. For example:

Binary file not shown.

After

Width:  |  Height:  |  Size: 369 KiB

View File

@ -24,7 +24,7 @@ type: index
- [Proxying images](asset_proxy.md)
- [CI/CD variables](../ci/variables/index.md#cicd-variable-security)
- [Token overview](token_overview.md)
- [Project Import decompressed archive size limits](project_import_decompressed_archive_size_limits.md)
- [Maximum decompressed file size for imported archives](../administration/settings/account_and_limit_settings.md#maximum-decompressed-file-size-for-imported-archives)
- [Responding to security incidents](responding_to_security_incidents.md)
To harden your GitLab instance and minimize the risk of unwanted user account creation, consider access control features like [Sign up restrictions](../administration/settings/sign_up_restrictions.md) and [Authentication options](../topics/authentication/index.md). For more detailed information, refer to [Hardening](hardening.md).

View File

@ -1,31 +1,11 @@
---
stage: Manage
group: Authentication and Authorization
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
type: reference, howto
redirect_to: '../administration/settings/account_and_limit_settings.md#maximum-decompressed-file-size-for-imported-archives'
remove_date: '2023-11-02'
---
# Project import decompressed archive size limits **(FREE SELF)**
This document was moved to [another location](../administration/settings/account_and_limit_settings.md#maximum-decompressed-file-size-for-imported-archives).
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/31564) in GitLab 13.2.
> - [Enabled by default](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/63025) in GitLab 14.0.
When using [Project Import](../user/project/settings/import_export.md), the size of the decompressed project archive is limited to 10 Gb.
If decompressed size exceeds this limit, `Decompressed archive size validation failed` error is returned.
## Enable/disable size validation
If you have a project with decompressed size exceeding this limit,
it is possible to disable the validation by turning off the
`validate_import_decompressed_archive_size` feature flag.
Start a [Rails console](../administration/operations/rails_console.md#starting-a-rails-console-session).
```ruby
# Disable
Feature.disable(:validate_import_decompressed_archive_size)
# Enable
Feature.enable(:validate_import_decompressed_archive_size)
```
<!-- This redirect file can be deleted after <2023-11-02>. -->
<!-- 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 -->

View File

@ -21,14 +21,7 @@ they confirm their email address.
By default, a user can confirm their account within 24 hours after the confirmation email was sent.
After 24 hours, the confirmation token becomes invalid.
<!-- ## Troubleshooting
## Automatically delete unconfirmed users **(PREMIUM SELF)**
Include any troubleshooting steps that you can foresee. If you know beforehand what issues
one might have when setting this up, or when something is changed, or on upgrading, it's
important to describe those, too. Think of things that may go wrong and include them here.
This is important to minimize requests for support, and to avoid doc comments with
questions that you know someone might ask.
Each scenario can be a third-level heading, for example `### Getting error message X`.
If you have none to add when creating a doc, leave this section in place
but commented out to help encourage others to add to it in the future. -->
When email confirmation is turned on, administrators can enable the setting to
[automatically delete unconfirmed users](../administration/moderate_users.md#automatically-delete-unconfirmed-users).

View File

@ -212,6 +212,7 @@ the default value [is the same as for self-managed instances](../../administrati
| Maximum remote file size for imports from external object storages | 10 GB |
| Maximum download file size when importing from source GitLab instances by direct transfer | 5 GB |
| Maximum attachment size | 100 MB |
| [Maximum decompressed file size for imported archives](../../administration/settings/account_and_limit_settings.md#maximum-decompressed-file-size-for-imported-archives) | 25 GB |
If you are near or over the repository size limit, you can either
[reduce your repository size with Git](../project/repository/reducing_the_repo_size_using_git.md)

View File

@ -101,6 +101,7 @@ Keep in mind that restricting group access by IP address has the following impli
- IP access restrictions for Git operations via SSH are supported on GitLab SaaS.
IP access restrictions applied to self-managed instances are possible with [`gitlab-sshd`](../../administration/operations/gitlab_sshd.md)
with [PROXY protocol](../../administration/operations/gitlab_sshd.md#proxy-protocol-support) enabled.
- IP restriction is not applicable to shared resources belonging to a group. Any shared resource is accessible to a user even if that user is not able to access the group.
## Restrict group access by domain **(PREMIUM)**

View File

@ -38,7 +38,7 @@ Prerequisites:
the [Composer specification](https://getcomposer.org/doc/04-schema.md#version).
If the version is not valid, for example, it has three dots (`1.0.0.0`), an
error (`Validation failed: Version is invalid`) occurs when you publish.
- A valid `composer.json` file.
- A valid `composer.json` file at the project root directory.
- The Packages feature is enabled in a GitLab repository.
- The project ID, which is on the project's home page.
- One of the following token types:
@ -324,6 +324,14 @@ If you committed your `composer.lock`, you could do a `composer install` in CI w
In GitLab 14.10 and later, authorization is required for the [downloading a package archive](../../../api/packages/composer.md#download-a-package-archive) endpoint.
If you encounter a credentials prompt when you are using `composer install`, follow the instructions in the [install a composer package](#install-a-composer-package) section to create an `auth.json` file.
### Publish fails with `The file composer.json was not found`
You might see an error that says `The file composer.json was not found`.
This issue occurs when [configuration requirements for publishing a package](#publish-a-composer-package-by-using-the-api) are not met.
To resolve the error, commit a `composer.json` file to the project root directory.
## Supported CLI commands
The GitLab Composer repository supports the following Composer CLI commands:

View File

@ -284,6 +284,7 @@ More details about the permissions for some project-level features follow.
| Run CI/CD pipeline | | | | ✓ | ✓ | ✓ |
| Run CI/CD pipeline for a protected branch | | | | ✓ (5) | ✓ (5) | ✓ |
| Stop [environments](../ci/environments/index.md) | | | | ✓ | ✓ | ✓ |
| Run deployment job for a protected environment | | | ✓ (5) | ✓ (6) | ✓ (6) | ✓ |
| View a job with [debug logging](../ci/variables/index.md#enable-debug-logging) | | | | ✓ | ✓ | ✓ |
| Use pipeline editor | | | | ✓ | ✓ | ✓ |
| Run [interactive web terminals](../ci/interactive_web_terminal/index.md) | | | | ✓ | ✓ | ✓ |
@ -307,6 +308,7 @@ More details about the permissions for some project-level features follow.
- [In GitLab 13.0](https://gitlab.com/gitlab-org/gitlab/-/issues/35069) and later,
run for a non-protected branch.
5. If the user is [allowed to merge or push to the protected branch](../ci/pipelines/index.md#pipeline-security-on-protected-branches).
6. If the user if [part of a group with at least the Reporter role](../ci/environments/protected_environments.md#deployment-only-access-to-protected-environments)
<!-- markdownlint-enable MD029 -->

View File

@ -7,8 +7,10 @@ module API
expose :authors, documentation: { type: 'string', example: 'Authors' } do |metadatum|
metadatum[:authors] || ''
end
expose :description, as: :summary, documentation: { type: 'string', example: 'Description' } do |metadatum|
metadatum[:description] || ''
with_options documentation: { type: 'string', example: 'Description' } do
set_default = ->(metadatum) { metadatum[:description] || '' }
expose :description, &set_default
expose :description, as: :summary, &set_default
end
expose :project_url, as: :projectUrl, expose_nil: false, documentation: { type: 'string', example: 'http://sandbox.com/project' }
expose :license_url, as: :licenseUrl, expose_nil: false, documentation: { type: 'string', example: 'http://sandbox.com/license' }

View File

@ -106,6 +106,7 @@ module API
optional :max_export_size, type: Integer, desc: 'Maximum export size in MB'
optional :max_import_size, type: Integer, desc: 'Maximum import size in MB'
optional :max_import_remote_file_size, type: Integer, desc: 'Maximum remote file size in MB for imports from external object storages'
optional :max_decompressed_archive_size, type: Integer, desc: 'Maximum decompressed size in MB'
optional :max_pages_size, type: Integer, desc: 'Maximum size of pages in MB'
optional :max_pages_custom_domains_per_project, type: Integer, desc: 'Maximum number of GitLab Pages custom domains per project'
optional :max_terraform_state_size_bytes, type: Integer, desc: "Maximum size in bytes of the Terraform state file. Set this to 0 for unlimited file size."

View File

@ -11,12 +11,13 @@ module Gitlab
include ::Gitlab::Config::Entry::Validatable
include ::Gitlab::Config::Entry::Attributable
attributes :default, prefix: :input
attributes :default, :type, prefix: :input
validations do
validates :config, type: Hash, allowed_keys: [:default]
validates :config, type: Hash, allowed_keys: [:default, :type]
validates :key, alphanumeric: true
validates :input_default, alphanumeric: true, allow_nil: true
validates :input_type, allow_nil: true, allowed_values: ::Gitlab::Ci::Interpolation::Inputs.input_types
end
end
end

View File

@ -8,9 +8,15 @@ module Gitlab
UnknownInputTypeError = Class.new(StandardError)
TYPES = [
BooleanInput,
NumberInput,
StringInput
].freeze
def self.input_types
TYPES.map(&:type_name)
end
def initialize(specs, args)
@specs = specs.to_h
@args = args.to_h

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