Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
1007a13cc1
commit
c0fe5a7e5b
|
|
@ -1 +1 @@
|
|||
80395fced1ecfa757d2e72c3f648dc52f0f343c1
|
||||
v17.3.0-rc3
|
||||
|
|
@ -5,7 +5,6 @@ import initCopyToClipboard from './copy_to_clipboard';
|
|||
import installGlEmojiElement from './gl_emoji';
|
||||
import initCopyAsGFM from './markdown/copy_as_gfm';
|
||||
import './quick_submit';
|
||||
import './requires_input';
|
||||
import { initToastMessages } from './toasts';
|
||||
import { initGlobalAlerts } from './global_alerts';
|
||||
import './shortcuts';
|
||||
|
|
|
|||
|
|
@ -1,46 +0,0 @@
|
|||
import $ from 'jquery';
|
||||
import { isEmpty } from 'lodash';
|
||||
import '../commons/bootstrap';
|
||||
|
||||
// Requires Input behavior
|
||||
//
|
||||
// When called on a form with input fields with the `required` attribute, the
|
||||
// form's submit button will be disabled until all required fields have values.
|
||||
//
|
||||
// ### Example Markup
|
||||
//
|
||||
// <form class="js-requires-input">
|
||||
// <input type="text" required="required">
|
||||
// <input type="submit" value="Submit">
|
||||
// </form>
|
||||
//
|
||||
|
||||
$.fn.requiresInput = function requiresInput() {
|
||||
const $form = $(this);
|
||||
const $button = $('button[type=submit], input[type=submit]', $form);
|
||||
const fieldSelector =
|
||||
'input[required=required], select[required=required], textarea[required=required]';
|
||||
|
||||
function requireInput() {
|
||||
// Collect the input values of *all* required fields
|
||||
const values = Array.from($(fieldSelector, $form)).map((field) => field.value);
|
||||
|
||||
// Disable the button if any required fields are empty
|
||||
if (values.length && values.some(isEmpty)) {
|
||||
$button.disable();
|
||||
} else {
|
||||
$button.enable();
|
||||
}
|
||||
}
|
||||
|
||||
// Set initial button state
|
||||
requireInput();
|
||||
$form.on('change input', fieldSelector, requireInput);
|
||||
};
|
||||
|
||||
$(() => {
|
||||
$('form.js-requires-input').each((i, el) => {
|
||||
const $form = $(el);
|
||||
$form.requiresInput();
|
||||
});
|
||||
});
|
||||
|
|
@ -230,7 +230,7 @@ export default {
|
|||
@ok="onSubmit"
|
||||
>
|
||||
{{ deleteModalMessage }}
|
||||
<form ref="form" :action="deleteFeatureFlagUrl" method="post" class="js-requires-input">
|
||||
<form ref="form" :action="deleteFeatureFlagUrl" method="post">
|
||||
<input ref="method" type="hidden" name="_method" value="delete" />
|
||||
<input :value="csrfToken" type="hidden" name="authenticity_token" />
|
||||
</form>
|
||||
|
|
|
|||
|
|
@ -137,7 +137,7 @@ export default {
|
|||
@ok="onSubmit"
|
||||
>
|
||||
{{ modalBody }}
|
||||
<form ref="form" :action="wikiUrl" method="post" class="js-requires-input">
|
||||
<form ref="form" :action="wikiUrl" method="post">
|
||||
<input ref="method" type="hidden" name="_method" value="delete" />
|
||||
<input :value="csrfToken" type="hidden" name="authenticity_token" />
|
||||
</form>
|
||||
|
|
|
|||
|
|
@ -121,12 +121,7 @@ export default {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<form
|
||||
ref="form"
|
||||
class="js-requires-input js-signature-container"
|
||||
method="POST"
|
||||
:action="projectCompareIndexPath"
|
||||
>
|
||||
<form ref="form" class="js-signature-container" method="POST" :action="projectCompareIndexPath">
|
||||
<input :value="$options.csrf.token" type="hidden" name="authenticity_token" />
|
||||
<h1 class="gl-font-size-h1 gl-mt-4">{{ $options.i18n.title }}</h1>
|
||||
<p>
|
||||
|
|
|
|||
|
|
@ -349,7 +349,7 @@ export default {
|
|||
|
||||
<template>
|
||||
<div>
|
||||
<div class="gl-display-flex gl-justify-content-space-between gl-align-items-center">
|
||||
<div class="gl-flex gl-items-center gl-justify-between">
|
||||
<h1 class="h3 gl-mb-5">{{ $options.i18n.pageTitle }}</h1>
|
||||
<gl-button
|
||||
v-if="glFeatures.editBranchRules && branchRule"
|
||||
|
|
@ -383,10 +383,10 @@ export default {
|
|||
<div v-if="allBranches" class="gl-mt-2" data-testid="all-branches">
|
||||
{{ $options.i18n.allBranches }}
|
||||
</div>
|
||||
<code v-else class="gl-bg-transparent p-0 gl-font-base" data-testid="branch">{{
|
||||
<code v-else class="gl-p-0 gl-bg-transparent gl-text-base" data-testid="branch">{{
|
||||
branch
|
||||
}}</code>
|
||||
<p v-if="matchingBranchesCount" class="gl-mt-3 gl-mb-0">
|
||||
<p v-if="matchingBranchesCount" class="gl-mb-0 gl-mt-3">
|
||||
<gl-link :href="matchingBranchesLinkHref">{{ matchingBranchesLinkTitle }}</gl-link>
|
||||
</p>
|
||||
</gl-card>
|
||||
|
|
|
|||
|
|
@ -105,7 +105,7 @@ export default {
|
|||
>{{ __('Edit') }}</gl-button
|
||||
>
|
||||
<gl-link v-else :href="headerLinkHref">{{ headerLinkTitle }}</gl-link>
|
||||
<p v-if="showHelpText" class="gl-flex-basis-full gl-mb-0 gl-text-secondary">
|
||||
<p v-if="showHelpText" class="gl-mb-0 gl-basis-full gl-text-secondary">
|
||||
{{ helpText }}
|
||||
</p></template
|
||||
>
|
||||
|
|
|
|||
|
|
@ -66,13 +66,13 @@ export default {
|
|||
|
||||
<template>
|
||||
<div
|
||||
class="gl-display-flex gl-align-items-center gl-gap-7 gl-border-gray-100 gl-mb-4 gl-border-t-1"
|
||||
:class="{ 'gl-border-t-solid gl-pt-4': showDivider }"
|
||||
class="gl-mb-4 gl-flex gl-items-center gl-gap-7 gl-border-t-1 gl-border-gray-100"
|
||||
:class="{ 'gl-pt-4 gl-border-t-solid': showDivider }"
|
||||
>
|
||||
<div class="gl-display-flex gl-w-full gl-align-items-center">
|
||||
<div class="gl-flex-basis-quarter">{{ title }}</div>
|
||||
<div class="gl-flex gl-w-full gl-items-center">
|
||||
<div class="gl-basis-1/4">{{ title }}</div>
|
||||
|
||||
<div v-if="statusCheckUrl" class="gl-flex-grow-1">{{ statusCheckUrl }}</div>
|
||||
<div v-if="statusCheckUrl" class="gl-grow">{{ statusCheckUrl }}</div>
|
||||
|
||||
<gl-avatars-inline
|
||||
v-if="usersAndGroups.length"
|
||||
|
|
|
|||
|
|
@ -95,7 +95,7 @@ export default {
|
|||
</gl-toggle>
|
||||
</div>
|
||||
<div v-else class="gl-mb-5">
|
||||
<div class="gl-display-flex gl-align-items-center">
|
||||
<div class="gl-flex gl-items-center">
|
||||
<gl-icon :data-testid="iconDataTestId" :size="14" :name="iconName" :class="iconClass" />
|
||||
<strong class="gl-ml-2">{{ iconTitle }}</strong>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -137,7 +137,7 @@ export default {
|
|||
v-on="$listeners"
|
||||
>
|
||||
<template #title>
|
||||
<h2 class="gl-font-size-h2 gl-mt-0">{{ title }}</h2>
|
||||
<h2 class="gl-mt-0 gl-text-size-h2">{{ title }}</h2>
|
||||
</template>
|
||||
|
||||
<template #header>
|
||||
|
|
|
|||
|
|
@ -30,12 +30,12 @@ export default {
|
|||
|
||||
<template>
|
||||
<span
|
||||
class="gl-display-flex gl-align-items-center gl-gap-3"
|
||||
class="gl-flex gl-items-center gl-gap-3"
|
||||
data-testid="deploy-key-wrapper"
|
||||
@click="$emit('select', id)"
|
||||
>
|
||||
<gl-icon name="key" />
|
||||
<span class="gl-display-flex gl-flex-direction-column gl-flex-grow-1">
|
||||
<span class="gl-flex gl-grow gl-flex-col">
|
||||
<span class="gl-font-bold">{{ title }}</span>
|
||||
<span class="gl-text-gray-600">@{{ owner }}</span>
|
||||
</span>
|
||||
|
|
|
|||
|
|
@ -240,16 +240,16 @@ export default {
|
|||
<template #header
|
||||
><strong data-testid="list-selector-title"
|
||||
>{{ config.title }}
|
||||
<span class="gl-text-gray-700 gl-ml-3"
|
||||
<span class="gl-ml-3 gl-text-gray-700"
|
||||
><gl-icon :name="config.icon" /> {{ selectedItems.length }}</span
|
||||
></strong
|
||||
></template
|
||||
>
|
||||
|
||||
<div class="gl-display-flex gl-gap-3" :class="{ 'gl-mb-4': selectedItems.length }">
|
||||
<div class="gl-flex gl-gap-3" :class="{ 'gl-mb-4': selectedItems.length }">
|
||||
<gl-collapsible-listbox
|
||||
ref="results"
|
||||
class="list-selector gl-block gl-flex-grow-1"
|
||||
class="list-selector gl-block gl-grow"
|
||||
:items="filteredItems"
|
||||
@select="handleSelectItem"
|
||||
@shown="handleSearchInput"
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ export default {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<span class="gl-display-flex gl-align-items-center gl-gap-3" @click="$emit('select', name)">
|
||||
<span class="gl-flex gl-items-center gl-gap-3" @click="$emit('select', name)">
|
||||
<gl-avatar
|
||||
:alt="name"
|
||||
:entity-name="name"
|
||||
|
|
@ -43,7 +43,7 @@ export default {
|
|||
:src="data.avatarUrl"
|
||||
fallback-on-error
|
||||
/>
|
||||
<span class="gl-display-flex gl-flex-direction-column gl-flex-grow-1 gl-max-w-30">
|
||||
<span class="gl-flex gl-max-w-30 gl-grow gl-flex-col">
|
||||
<span class="gl-font-bold">{{ name }}</span>
|
||||
<span class="gl-text-gray-600">{{ data.nameWithNamespace }}</span>
|
||||
</span>
|
||||
|
|
|
|||
|
|
@ -40,9 +40,9 @@ export default {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<span class="gl-display-flex gl-align-items-center gl-gap-3">
|
||||
<span class="gl-flex gl-items-center gl-gap-3">
|
||||
<gl-avatar :alt="name" :entity-name="name" :size="32" :src="avatarUrl" fallback-on-error />
|
||||
<span class="gl-display-flex gl-flex-direction-column gl-flex-grow-1">
|
||||
<span class="gl-flex gl-grow gl-flex-col">
|
||||
<span class="gl-font-bold">{{ name }}</span>
|
||||
<span class="gl-text-gray-600">@{{ username }}</span>
|
||||
</span>
|
||||
|
|
|
|||
|
|
@ -40,6 +40,11 @@ fragment WorkItemHierarchy on WorkItem {
|
|||
name
|
||||
iconName
|
||||
}
|
||||
namespace {
|
||||
id
|
||||
fullPath
|
||||
name
|
||||
}
|
||||
title
|
||||
state
|
||||
createdAt
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
.detail-page-header a.link,
|
||||
.detail-page-header .title a {
|
||||
color: $blue-600;
|
||||
color: var(--gl-text-color-link);
|
||||
}
|
||||
|
||||
.detail-page-header-body {
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ module Resolvers
|
|||
alias_method :container_repository, :object
|
||||
|
||||
def resolve(sort:, **filters)
|
||||
if container_repository.migrated?
|
||||
if container_repository.gitlab_api_client.supports_gitlab_api?
|
||||
page_size = [filters[:first], filters[:last]].map(&:to_i).max
|
||||
|
||||
result = container_repository.tags_page(
|
||||
|
|
|
|||
|
|
@ -22,6 +22,9 @@ module Resolvers
|
|||
end
|
||||
|
||||
def resolve_with_lookahead(**args)
|
||||
# TODO: Implement as part of completion https://gitlab.com/gitlab-org/gitlab/-/issues/458264
|
||||
return [] if object.is_a?(AbuseReport)
|
||||
|
||||
notes = NotesFinder.new(current_user, build_params(args)).execute
|
||||
apply_lookahead(notes)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -29,6 +29,9 @@ module Types
|
|||
end
|
||||
|
||||
def commenters
|
||||
# TODO: Implement as part of completion https://gitlab.com/gitlab-org/gitlab/-/issues/458264
|
||||
return [] if object.is_a?(AbuseReport)
|
||||
|
||||
object.commenters(user: current_user)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ class AbuseReport < MainClusterwide::ApplicationRecord
|
|||
|
||||
has_many :abuse_events, class_name: 'AntiAbuse::Event', inverse_of: :abuse_report
|
||||
|
||||
has_many :notes, as: :noteable
|
||||
has_many :notes, class_name: 'AntiAbuse::Reports::Note'
|
||||
has_many :user_mentions, class_name: 'AntiAbuse::Reports::UserMention'
|
||||
|
||||
validates :reporter, presence: true, on: :create
|
||||
|
|
|
|||
|
|
@ -0,0 +1,23 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module AntiAbuse
|
||||
module Reports
|
||||
class Discussion < ::Discussion
|
||||
def self.base_discussion_id(_note)
|
||||
[:discussion, :abuse_report_id]
|
||||
end
|
||||
|
||||
def self.note_class
|
||||
DiscussionNote
|
||||
end
|
||||
|
||||
def reply_attributes
|
||||
first_note.slice(:abuse_report_id, :discussion_id)
|
||||
end
|
||||
|
||||
def individual_note?
|
||||
false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# A note in a discussion on an abuse report.
|
||||
#
|
||||
# A note of this type can be resolvable.
|
||||
module AntiAbuse
|
||||
module Reports
|
||||
class DiscussionNote < Note
|
||||
# we are re-using existing discussions functionality from notes model and its classes
|
||||
self.allow_legacy_sti_class = true
|
||||
|
||||
def discussion_class(*)
|
||||
AntiAbuse::Reports::Discussion
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module AntiAbuse
|
||||
module Reports
|
||||
class IndividualNoteDiscussion < Discussion
|
||||
def individual_note?
|
||||
true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -4,8 +4,31 @@ module AntiAbuse
|
|||
module Reports
|
||||
class Note < ApplicationRecord
|
||||
include Notes::ActiveRecord
|
||||
include Notes::Discussion
|
||||
|
||||
include Awardable
|
||||
include CacheMarkdownField
|
||||
include Editable
|
||||
include Mentionable
|
||||
include Participable
|
||||
include Redactable
|
||||
include ResolvableNote
|
||||
include Sortable
|
||||
|
||||
self.table_name = 'abuse_report_notes'
|
||||
|
||||
belongs_to :abuse_report
|
||||
|
||||
alias_attribute :noteable_id, :abuse_report_id
|
||||
alias_method :noteable, :abuse_report
|
||||
|
||||
validates :abuse_report, presence: true
|
||||
|
||||
scope :fresh, -> { order_created_asc.with_order_id_asc }
|
||||
|
||||
def discussion_class(_noteable = nil)
|
||||
AntiAbuse::Reports::IndividualNoteDiscussion
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -6,8 +6,6 @@ module Notes
|
|||
include Gitlab::Utils::StrongMemoize
|
||||
|
||||
included do
|
||||
validate :ensure_confidentiality_discussion_compliance
|
||||
|
||||
scope :with_discussion_ids, ->(discussion_ids) { where(discussion_id: discussion_ids) }
|
||||
end
|
||||
|
||||
|
|
@ -83,13 +81,13 @@ module Notes
|
|||
|
||||
def in_reply_to?(other)
|
||||
case other
|
||||
when Note
|
||||
when Note, AntiAbuse::Reports::Note
|
||||
if part_of_discussion?
|
||||
in_reply_to?(other.noteable) && in_reply_to?(other.to_discussion)
|
||||
else
|
||||
in_reply_to?(other.noteable)
|
||||
end
|
||||
when ::Discussion
|
||||
when ::Discussion, AntiAbuse::Reports::Discussion
|
||||
discussion_id == other.id
|
||||
when Noteable
|
||||
noteable == other
|
||||
|
|
|
|||
|
|
@ -195,7 +195,7 @@ class ContainerRepository < ApplicationRecord
|
|||
end
|
||||
|
||||
def tags_page(before: nil, last: nil, sort: nil, name: nil, page_size: 100, referrers: nil, referrer_type: nil)
|
||||
raise ArgumentError, 'not a migrated repository' unless migrated?
|
||||
raise ArgumentError, _('GitLab container registry API not supported') unless gitlab_api_client.supports_gitlab_api?
|
||||
|
||||
page = gitlab_api_client.tags(
|
||||
self.path,
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ class DiscussionNote < Note
|
|||
|
||||
# Names of all implementers of `Noteable` that support discussions.
|
||||
def self.noteable_types
|
||||
%w[MergeRequest Issue Commit Snippet AbuseReport]
|
||||
%w[MergeRequest Issue Commit Snippet]
|
||||
end
|
||||
|
||||
validates :noteable_type, inclusion: { in: noteable_types }
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ class Note < ApplicationRecord
|
|||
|
||||
include Notes::ActiveRecord
|
||||
include Notes::Discussion
|
||||
|
||||
include Gitlab::Utils::StrongMemoize
|
||||
include Participable
|
||||
include Mentionable
|
||||
|
|
@ -105,6 +106,7 @@ class Note < ApplicationRecord
|
|||
validate :ensure_noteable_can_have_confidential_note
|
||||
validate :ensure_note_type_can_be_confidential
|
||||
validate :ensure_confidentiality_not_changed, on: :update
|
||||
validate :ensure_confidentiality_discussion_compliance
|
||||
|
||||
validate unless: [:for_commit?, :importing?, :skip_project_check?] do |note|
|
||||
unless note.noteable.try(:project) == note.project
|
||||
|
|
|
|||
|
|
@ -28,8 +28,7 @@ module Ci
|
|||
|
||||
return {} unless config.exists?
|
||||
|
||||
ref_name = Gitlab::Ci::RefFinder.new(project).find_by_sha(sha) if Feature.enabled?(
|
||||
:project_ref_name_in_variables, project)
|
||||
ref_name = Gitlab::Ci::RefFinder.new(project).find_by_sha(sha)
|
||||
|
||||
result = Gitlab::Ci::YamlProcessor.new(
|
||||
config.content,
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
module Packages
|
||||
class CreateEventService < BaseService
|
||||
INTERNAL_EVENTS_NAMES = {
|
||||
'delete_package' => 'delete_package_from_registry',
|
||||
'pull_package' => 'pull_package_from_registry'
|
||||
}.freeze
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
= gitlab_ui_form_for @topic, url: url, html: { multipart: true, class: 'js-project-topic-form gl-show-field-errors common-note-form js-quick-submit js-requires-input' }, authenticity_token: true do |f|
|
||||
= gitlab_ui_form_for @topic, url: url, html: { multipart: true, class: 'js-project-topic-form gl-show-field-errors common-note-form js-quick-submit' }, authenticity_token: true do |f|
|
||||
= form_errors(@topic)
|
||||
|
||||
.form-group
|
||||
|
|
@ -46,4 +46,3 @@
|
|||
= f.submit _('Save changes'), pajamas_button: true
|
||||
= render Pajamas::ButtonComponent.new(href: admin_topics_path) do
|
||||
= _('Cancel')
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
= gitlab_ui_form_for [@group, @milestone], html: { class: 'milestone-form common-note-form js-quick-submit js-requires-input' } do |f|
|
||||
= gitlab_ui_form_for [@group, @milestone], html: { class: 'milestone-form common-note-form js-quick-submit' } do |f|
|
||||
= form_errors(@milestone)
|
||||
|
||||
- if @conflict
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@
|
|||
|
||||
= gl_tab_link_to editing_preview_title(@blob.name), '#preview', { data: { 'preview-url': project_preview_blob_path(@project, @id) } }
|
||||
|
||||
= form_tag(project_update_blob_path(@project, @id), method: :put, class: 'js-quick-submit js-requires-input js-edit-blob-form', data: blob_editor_paths(@project, 'put')) do
|
||||
= form_tag(project_update_blob_path(@project, @id), method: :put, class: 'js-quick-submit js-edit-blob-form', data: blob_editor_paths(@project, 'put')) do
|
||||
= render 'projects/blob/editor', ref: @ref, path: @path, blob_data: @blob.data
|
||||
= render 'shared/new_commit_form', placeholder: "Update #{@blob.name}"
|
||||
= hidden_field_tag 'last_commit_sha', @last_commit_sha
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
%h1.page-title.blob-new-page-title.gl-font-size-h-display
|
||||
= _('New file')
|
||||
.file-editor
|
||||
= form_tag(project_create_blob_path(@project, @id), method: :post, class: 'js-edit-blob-form js-new-blob-form js-quick-submit js-requires-input', data: blob_editor_paths(@project, 'post')) do
|
||||
= form_tag(project_create_blob_path(@project, @id), method: :post, class: 'js-edit-blob-form js-new-blob-form js-quick-submit', data: blob_editor_paths(@project, 'post')) do
|
||||
= render 'projects/blob/editor', ref: @ref, path: @path
|
||||
= render 'shared/new_commit_form', placeholder: "Add new file"
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
%h1.page-title.gl-font-size-h-display
|
||||
= _('New Branch')
|
||||
|
||||
= form_tag namespace_project_branches_path, method: :post, id: "new-branch-form", class: "js-create-branch-form js-requires-input" do
|
||||
= form_tag namespace_project_branches_path, method: :post, id: "new-branch-form", class: "js-create-branch-form" do
|
||||
.form-group.gl-max-w-80
|
||||
= label_tag :branch_name, _('Branch name')
|
||||
= text_field_tag :branch_name, params[:branch_name], required: true, autofocus: true, class: 'form-control js-branch-name monospace'
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
- page_title _('Edit deploy key')
|
||||
%h1.page-title.gl-font-size-h-display= _('Edit deploy key')
|
||||
|
||||
= gitlab_ui_form_for [@project, @deploy_key], include_id: false, html: { class: 'js-requires-input' } do |f|
|
||||
= gitlab_ui_form_for [@project, @deploy_key], include_id: false do |f|
|
||||
= render partial: 'shared/deploy_keys/form', locals: { form: f, deploy_key: @deploy_key }
|
||||
.gl-display-flex.gl-gap-3
|
||||
= f.submit _('Save changes'), pajamas_button: true
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
rel: 'noopener noreferrer'
|
||||
|
||||
- url = cleanup_namespace_project_settings_repository_path(@project.namespace, @project)
|
||||
= gitlab_ui_form_for @project, url: url, method: :post, authenticity_token: true, html: { class: 'js-requires-input' } do |f|
|
||||
= gitlab_ui_form_for @project, url: url, method: :post, authenticity_token: true do |f|
|
||||
%fieldset.gl-mt-0.gl-mb-3
|
||||
.gl-mb-3
|
||||
%h6.gl-mt-0
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
This service will be installed on the Mattermost instance at
|
||||
%strong= link_to Gitlab.config.mattermost.host, Gitlab.config.mattermost.host
|
||||
%hr
|
||||
= gitlab_ui_form_for(:mattermost, method: :post, url: project_mattermost_path(@project), html: { class: 'js-requires-input' }) do |f|
|
||||
= gitlab_ui_form_for(:mattermost, method: :post, url: project_mattermost_path(@project)) do |f|
|
||||
%h4 Team
|
||||
%p
|
||||
= @teams.one? ? 'The team' : 'Select the team'
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
= gitlab_ui_form_for [@project, @merge_request],
|
||||
html: { class: 'merge-request-form common-note-form js-requires-input js-quick-submit' } do |f|
|
||||
html: { class: 'merge-request-form common-note-form js-quick-submit' } do |f|
|
||||
= render 'shared/issuable/form', f: f, issuable: @merge_request, presenter: @mr_presenter
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
= render Pajamas::ButtonComponent.new(href: sso_group_saml_providers_path(group, { token: group.saml_discovery_token, redirect: project_new_merge_request_branch_from_path(@source_project) }), button_options: { class: "gl-mr-3 gl-mb-3" }) do
|
||||
= group.path
|
||||
|
||||
= gitlab_ui_form_for [@project, @merge_request], url: project_new_merge_request_path(@project), method: :get, html: { class: "merge-request-form js-requires-input" } do |f|
|
||||
= gitlab_ui_form_for [@project, @merge_request], url: project_new_merge_request_path(@project), method: :get, html: { class: "merge-request-form" } do |f|
|
||||
- if params[:nav_source].present?
|
||||
= hidden_field_tag(:nav_source, params[:nav_source])
|
||||
.js-merge-request-new-compare.row{ data: mr_compare_form_data(current_user, @merge_request) }
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
%h1.page-title.gl-font-size-h-display
|
||||
= _('New merge request')
|
||||
= gitlab_ui_form_for [@project, @merge_request], html: { class: 'merge-request-form common-note-form js-requires-input js-quick-submit' } do |f|
|
||||
= gitlab_ui_form_for [@project, @merge_request], html: { class: 'merge-request-form common-note-form js-quick-submit' } do |f|
|
||||
= render 'shared/issuable/form', f: f, issuable: @merge_request, commits: @commits, presenter: @mr_presenter
|
||||
= f.hidden_field :source_project_id
|
||||
= f.hidden_field :source_branch
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
= gitlab_ui_form_for [@project, @milestone], html: { class: 'milestone-form common-note-form js-quick-submit js-requires-input' } do |f|
|
||||
= gitlab_ui_form_for [@project, @milestone], html: { class: 'milestone-form common-note-form js-quick-submit' } do |f|
|
||||
= form_errors(@milestone)
|
||||
|
||||
- if @conflict
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
- link = link_to('', new_namespace_project_release_path)
|
||||
= safe_format(s_('TagsPage|Do you want to create a release with the new tag? You can do that in the %{link_start}New release page%{link_end}.'), tag_pair(link, :link_start, :link_end))
|
||||
|
||||
= form_tag namespace_project_tags_path, method: :post, id: "new-tag-form", class: "common-note-form tag-form js-quick-submit js-requires-input" do
|
||||
= form_tag namespace_project_tags_path, method: :post, id: "new-tag-form", class: "common-note-form tag-form js-quick-submit" do
|
||||
.form-group.row
|
||||
.col-sm-12
|
||||
= label_tag :tag_name, _('Tag name')
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
%h4.gl-mt-0
|
||||
= title
|
||||
|
||||
= gitlab_ui_form_for token, as: prefix, url: path, method: :post, html: { id: 'js-new-access-token-form', class: 'js-requires-input' }, remote: ajax do |f|
|
||||
= gitlab_ui_form_for token, as: prefix, url: path, method: :post, html: { id: 'js-new-access-token-form' }, remote: ajax do |f|
|
||||
= form_errors(token)
|
||||
|
||||
.form-group
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
= gitlab_ui_form_for [@project.namespace, @project, @deploy_keys.new_key], url: namespace_project_deploy_keys_path, html: { class: "js-requires-input" } do |f|
|
||||
= gitlab_ui_form_for [@project.namespace, @project, @deploy_keys.new_key], url: namespace_project_deploy_keys_path do |f|
|
||||
= form_errors(@deploy_keys.new_key)
|
||||
|
||||
.form-group
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
- add_page_specific_style 'page_bundles/labels'
|
||||
- show_lock_on_merge = local_assigns.fetch(:show_lock_on_merge, false)
|
||||
|
||||
= gitlab_ui_form_for @label, as: :label, url: url, html: { class: 'label-form js-quick-submit js-requires-input' } do |f|
|
||||
= gitlab_ui_form_for @label, as: :label, url: url, html: { class: 'label-form js-quick-submit' } do |f|
|
||||
= form_errors(@label)
|
||||
|
||||
.form-group.row
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
%div
|
||||
= gitlab_ui_form_for [:user_settings, @gpg_key], html: { class: 'js-requires-input' } do |f|
|
||||
= gitlab_ui_form_for [:user_settings, @gpg_key] do |f|
|
||||
= form_errors(@gpg_key)
|
||||
|
||||
.form-group
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
- max_date = ::Gitlab::CurrentSettings.max_ssh_key_lifetime_from_now.to_date if ssh_key_expiration_policy_enabled?
|
||||
%div
|
||||
= gitlab_ui_form_for [:user_settings_ssh, @key], html: { class: 'js-requires-input' } do |f|
|
||||
= gitlab_ui_form_for [:user_settings_ssh, @key] do |f|
|
||||
= form_errors(@key)
|
||||
|
||||
.form-group
|
||||
|
|
|
|||
|
|
@ -0,0 +1,23 @@
|
|||
---
|
||||
description: Packaged deleted from the registry
|
||||
internal_events: true
|
||||
action: delete_package_from_registry
|
||||
identifiers:
|
||||
- project
|
||||
- namespace
|
||||
- user
|
||||
additional_properties:
|
||||
label:
|
||||
description: The name of the package type
|
||||
property:
|
||||
description: The auth type. Either 'guest', 'user' or 'deploy_token'
|
||||
product_group: package_registry
|
||||
milestone: '17.3'
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/153159
|
||||
distributions:
|
||||
- ce
|
||||
- ee
|
||||
tiers:
|
||||
- free
|
||||
- premium
|
||||
- ultimate
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
---
|
||||
name: observability_tracing
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/124966
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/opstrace/opstrace/-/issues/2252
|
||||
milestone: '16.2'
|
||||
type: development
|
||||
group: group::observability
|
||||
default_enabled: false
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
---
|
||||
name: project_ref_name_in_variables
|
||||
feature_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/430786
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/155868
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/466427
|
||||
milestone: '17.2'
|
||||
group: group::pipeline authoring
|
||||
type: gitlab_com_derisk
|
||||
default_enabled: false
|
||||
|
|
@ -6,11 +6,11 @@ product_group: package_registry
|
|||
value_type: number
|
||||
status: active
|
||||
time_frame: all
|
||||
data_source: redis
|
||||
instrumentation_class: RedisMetric
|
||||
options:
|
||||
prefix: package_events
|
||||
event: i_package_composer_delete_package
|
||||
data_source: internal_events
|
||||
events:
|
||||
- name: delete_package_from_registry
|
||||
filter:
|
||||
label: composer
|
||||
distribution:
|
||||
- ee
|
||||
- ce
|
||||
|
|
|
|||
|
|
@ -6,11 +6,11 @@ product_group: package_registry
|
|||
value_type: number
|
||||
status: active
|
||||
time_frame: all
|
||||
data_source: redis
|
||||
instrumentation_class: RedisMetric
|
||||
options:
|
||||
prefix: package_events
|
||||
event: i_package_conan_delete_package
|
||||
data_source: internal_events
|
||||
events:
|
||||
- name: delete_package_from_registry
|
||||
filter:
|
||||
label: conan
|
||||
distribution:
|
||||
- ee
|
||||
- ce
|
||||
|
|
|
|||
|
|
@ -6,11 +6,11 @@ product_group: package_registry
|
|||
value_type: number
|
||||
status: active
|
||||
time_frame: all
|
||||
data_source: redis
|
||||
instrumentation_class: RedisMetric
|
||||
options:
|
||||
prefix: package_events
|
||||
event: i_package_debian_delete_package
|
||||
data_source: internal_events
|
||||
events:
|
||||
- name: delete_package_from_registry
|
||||
filter:
|
||||
label: debian
|
||||
distribution:
|
||||
- ee
|
||||
- ce
|
||||
|
|
|
|||
|
|
@ -6,11 +6,9 @@ product_group: package_registry
|
|||
value_type: number
|
||||
status: active
|
||||
time_frame: all
|
||||
data_source: redis
|
||||
instrumentation_class: RedisMetric
|
||||
options:
|
||||
prefix: package_events
|
||||
event: i_package_delete_package
|
||||
data_source: internal_events
|
||||
events:
|
||||
- name: delete_package_from_registry
|
||||
distribution:
|
||||
- ee
|
||||
- ce
|
||||
|
|
|
|||
|
|
@ -6,11 +6,11 @@ product_group: package_registry
|
|||
value_type: number
|
||||
status: active
|
||||
time_frame: all
|
||||
data_source: redis
|
||||
instrumentation_class: RedisMetric
|
||||
options:
|
||||
prefix: package_events
|
||||
event: i_package_delete_package_by_deploy_token
|
||||
data_source: internal_events
|
||||
events:
|
||||
- name: delete_package_from_registry
|
||||
filter:
|
||||
property: deploy_token
|
||||
distribution:
|
||||
- ee
|
||||
- ce
|
||||
|
|
|
|||
|
|
@ -6,11 +6,11 @@ product_group: package_registry
|
|||
value_type: number
|
||||
status: active
|
||||
time_frame: all
|
||||
data_source: redis
|
||||
instrumentation_class: RedisMetric
|
||||
options:
|
||||
prefix: package_events
|
||||
event: i_package_delete_package_by_guest
|
||||
data_source: internal_events
|
||||
events:
|
||||
- name: delete_package_from_registry
|
||||
filter:
|
||||
property: guest
|
||||
distribution:
|
||||
- ee
|
||||
- ce
|
||||
|
|
|
|||
|
|
@ -6,11 +6,11 @@ product_group: package_registry
|
|||
value_type: number
|
||||
status: active
|
||||
time_frame: all
|
||||
data_source: redis
|
||||
instrumentation_class: RedisMetric
|
||||
options:
|
||||
prefix: package_events
|
||||
event: i_package_delete_package_by_user
|
||||
data_source: internal_events
|
||||
events:
|
||||
- name: delete_package_from_registry
|
||||
filter:
|
||||
property: user
|
||||
distribution:
|
||||
- ee
|
||||
- ce
|
||||
|
|
|
|||
|
|
@ -6,11 +6,11 @@ product_group: package_registry
|
|||
value_type: number
|
||||
status: active
|
||||
time_frame: all
|
||||
data_source: redis
|
||||
instrumentation_class: RedisMetric
|
||||
options:
|
||||
prefix: package_events
|
||||
event: i_package_generic_delete_package
|
||||
data_source: internal_events
|
||||
events:
|
||||
- name: delete_package_from_registry
|
||||
filter:
|
||||
label: generic
|
||||
distribution:
|
||||
- ee
|
||||
- ce
|
||||
|
|
|
|||
|
|
@ -6,11 +6,11 @@ product_group: package_registry
|
|||
value_type: number
|
||||
status: active
|
||||
time_frame: all
|
||||
data_source: redis
|
||||
instrumentation_class: RedisMetric
|
||||
options:
|
||||
prefix: package_events
|
||||
event: i_package_golang_delete_package
|
||||
data_source: internal_events
|
||||
events:
|
||||
- name: delete_package_from_registry
|
||||
filter:
|
||||
label: golang
|
||||
distribution:
|
||||
- ee
|
||||
- ce
|
||||
|
|
|
|||
|
|
@ -6,11 +6,11 @@ product_group: package_registry
|
|||
value_type: number
|
||||
status: active
|
||||
time_frame: all
|
||||
data_source: redis
|
||||
instrumentation_class: RedisMetric
|
||||
options:
|
||||
prefix: package_events
|
||||
event: i_package_maven_delete_package
|
||||
data_source: internal_events
|
||||
events:
|
||||
- name: delete_package_from_registry
|
||||
filter:
|
||||
label: maven
|
||||
distribution:
|
||||
- ee
|
||||
- ce
|
||||
|
|
|
|||
|
|
@ -6,11 +6,11 @@ product_group: package_registry
|
|||
value_type: number
|
||||
status: active
|
||||
time_frame: all
|
||||
data_source: redis
|
||||
instrumentation_class: RedisMetric
|
||||
options:
|
||||
prefix: package_events
|
||||
event: i_package_npm_delete_package
|
||||
data_source: internal_events
|
||||
events:
|
||||
- name: delete_package_from_registry
|
||||
filter:
|
||||
label: npm
|
||||
distribution:
|
||||
- ee
|
||||
- ce
|
||||
|
|
|
|||
|
|
@ -6,11 +6,11 @@ product_group: package_registry
|
|||
value_type: number
|
||||
status: active
|
||||
time_frame: all
|
||||
data_source: redis
|
||||
instrumentation_class: RedisMetric
|
||||
options:
|
||||
prefix: package_events
|
||||
event: i_package_nuget_delete_package
|
||||
data_source: internal_events
|
||||
events:
|
||||
- name: delete_package_from_registry
|
||||
filter:
|
||||
label: nuget
|
||||
distribution:
|
||||
- ee
|
||||
- ce
|
||||
|
|
|
|||
|
|
@ -6,11 +6,11 @@ product_group: package_registry
|
|||
value_type: number
|
||||
status: active
|
||||
time_frame: all
|
||||
data_source: redis
|
||||
instrumentation_class: RedisMetric
|
||||
options:
|
||||
prefix: package_events
|
||||
event: i_package_pypi_delete_package
|
||||
data_source: internal_events
|
||||
events:
|
||||
- name: delete_package_from_registry
|
||||
filter:
|
||||
label: pypi
|
||||
distribution:
|
||||
- ee
|
||||
- ce
|
||||
|
|
|
|||
|
|
@ -8,11 +8,11 @@ status: active
|
|||
milestone: '13.10'
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/53480
|
||||
time_frame: all
|
||||
data_source: redis
|
||||
instrumentation_class: RedisMetric
|
||||
options:
|
||||
prefix: package_events
|
||||
event: i_package_rubygems_delete_package
|
||||
data_source: internal_events
|
||||
events:
|
||||
- name: delete_package_from_registry
|
||||
filter:
|
||||
label: rubygems
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
|
|
|
|||
|
|
@ -8,11 +8,11 @@ status: active
|
|||
milestone: '13.11'
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/55018
|
||||
time_frame: all
|
||||
data_source: redis
|
||||
instrumentation_class: RedisMetric
|
||||
options:
|
||||
prefix: package_events
|
||||
event: i_package_terraform_module_delete_package
|
||||
data_source: internal_events
|
||||
events:
|
||||
- name: delete_package_from_registry
|
||||
filter:
|
||||
label: terraform_module
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
---
|
||||
table_name: abuse_report_notes
|
||||
classes:
|
||||
- AntiAbuse::Reports::DiscussionNote
|
||||
- AntiAbuse::Reports::Note
|
||||
feature_categories:
|
||||
- insider_threat
|
||||
|
|
|
|||
|
|
@ -0,0 +1,10 @@
|
|||
---
|
||||
migration_job_name: BackfillUserDetails
|
||||
description: Create user_details records for users without one.
|
||||
feature_category: acquisition
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/159632
|
||||
milestone: '17.3'
|
||||
queued_migration_version: 20240716191121
|
||||
# Replace with the approximate date you think it's best to ensure the completion of this BBM.
|
||||
finalize_after: '2024-08-16'
|
||||
finalized_by: # version of the migration that finalized this BBM
|
||||
|
|
@ -8,5 +8,5 @@ feature_categories:
|
|||
description: Represents an Advanced Search index
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/113612/
|
||||
milestone: '15.11'
|
||||
gitlab_schema: gitlab_main
|
||||
sharding_key_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/442663
|
||||
gitlab_schema: gitlab_main_cell
|
||||
exempt_from_sharding: true # table scheduled for deletion https://gitlab.com/gitlab-org/gitlab/-/issues/473381
|
||||
|
|
|
|||
|
|
@ -0,0 +1,20 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddTypeToAbuseReportNotes < Gitlab::Database::Migration[2.2]
|
||||
disable_ddl_transaction!
|
||||
milestone '17.3'
|
||||
|
||||
def up
|
||||
with_lock_retries do
|
||||
add_column :abuse_report_notes, :type, :text, if_not_exists: true
|
||||
end
|
||||
|
||||
add_text_limit :abuse_report_notes, :type, 40
|
||||
end
|
||||
|
||||
def down
|
||||
with_lock_retries do
|
||||
remove_column :abuse_report_notes, :type, if_exists: true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class QueueBackfillUserDetails < Gitlab::Database::Migration[2.2]
|
||||
milestone '17.3'
|
||||
|
||||
restrict_gitlab_migration gitlab_schema: :gitlab_main
|
||||
|
||||
MIGRATION = "BackfillUserDetails"
|
||||
DELAY_INTERVAL = 2.minutes
|
||||
BATCH_SIZE = 3_000
|
||||
SUB_BATCH_SIZE = 250
|
||||
MAX_BATCH_SIZE = 10_000
|
||||
|
||||
def up
|
||||
queue_batched_background_migration(
|
||||
MIGRATION,
|
||||
:users,
|
||||
:id,
|
||||
job_interval: DELAY_INTERVAL,
|
||||
batch_size: BATCH_SIZE,
|
||||
sub_batch_size: SUB_BATCH_SIZE,
|
||||
max_batch_size: MAX_BATCH_SIZE
|
||||
)
|
||||
end
|
||||
|
||||
def down
|
||||
delete_batched_background_migration(MIGRATION, :users, :id, [])
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1 @@
|
|||
261ce451c12f68a089622b880be600a1b67ae0ae2d4108b1041355551cd7a733
|
||||
|
|
@ -0,0 +1 @@
|
|||
074fb0d5586e7792884ab75a43d84c51678eb174ca79462683bd061fd9c91c25
|
||||
|
|
@ -4509,6 +4509,8 @@ CREATE TABLE abuse_report_notes (
|
|||
discussion_id text,
|
||||
note text,
|
||||
note_html text,
|
||||
type text,
|
||||
CONSTRAINT check_096e19df7f CHECK ((char_length(type) <= 40)),
|
||||
CONSTRAINT check_0de721e87e CHECK ((char_length(note) <= 10000)),
|
||||
CONSTRAINT check_13235633b5 CHECK ((char_length(discussion_id) <= 255)),
|
||||
CONSTRAINT check_21b51956e3 CHECK ((char_length(note_html) <= 20000))
|
||||
|
|
|
|||
|
|
@ -204,7 +204,7 @@ To troubleshoot [project imports](../../administration/raketasks/project_import_
|
|||
jq 'select(.project_path == "<namespace>/<project>").error_messages' importer.log
|
||||
```
|
||||
|
||||
For common issues, see [troubleshooting](../../administration/raketasks/project_import_export.md#troubleshooting).
|
||||
For common issues, see [troubleshooting](../../administration/raketasks/import_export_rake_tasks_troubleshooting.md).
|
||||
|
||||
### Parsing `gitlab-workhorse/current`
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,100 @@
|
|||
---
|
||||
stage: Systems
|
||||
group: Distribution
|
||||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://handbook.gitlab.com/handbook/product/ux/technical-writing/#assignments
|
||||
---
|
||||
|
||||
## Troubleshooting project import and export
|
||||
|
||||
If you are having trouble with import or export, use a Rake task to enable debug mode:
|
||||
|
||||
```shell
|
||||
# Import
|
||||
IMPORT_DEBUG=true gitlab-rake "gitlab:import_export:import[root, group/subgroup, testingprojectimport, /path/to/file_to_import.tar.gz]"
|
||||
|
||||
# Export
|
||||
EXPORT_DEBUG=true gitlab-rake "gitlab:import_export:export[root, group/subgroup, projectnametoexport, /tmp/export_file.tar.gz]"
|
||||
```
|
||||
|
||||
Then, review the following details on specific error messages.
|
||||
|
||||
### `Exception: undefined method 'name' for nil:NilClass`
|
||||
|
||||
The `username` is not valid.
|
||||
|
||||
### `Exception: undefined method 'full_path' for nil:NilClass`
|
||||
|
||||
The `namespace_path` does not exist.
|
||||
For example, one of the groups or subgroups is mistyped or missing,
|
||||
or you've specified the project name in the path.
|
||||
|
||||
The task only creates the project.
|
||||
If you want to import it to a new group or subgroup, create it first.
|
||||
|
||||
### `Exception: No such file or directory @ rb_sysopen - (filename)`
|
||||
|
||||
The specified project export file in `archive_path` is missing.
|
||||
|
||||
### `Exception: Permission denied @ rb_sysopen - (filename)`
|
||||
|
||||
The specified project export file cannot be accessed by the `git` user.
|
||||
|
||||
To fix the issue:
|
||||
|
||||
1. Set the file owner to `git:git`.
|
||||
1. Change the file permissions to `0400`.
|
||||
1. Move the file to a public folder (for example `/tmp/`).
|
||||
|
||||
### `Name can contain only letters, digits, emoji ...`
|
||||
|
||||
```plaintext
|
||||
Name can contain only letters, digits, emoji, '_', '.', '+', dashes, or spaces. It must start with a letter,
|
||||
digit, emoji, or '_', and Path can contain only letters, digits, '_', '-', or '.'. It cannot start
|
||||
with '-', end in '.git', or end in '.atom'.
|
||||
```
|
||||
|
||||
The project name specified in `project_path` is not valid for one of the specified reasons.
|
||||
|
||||
Only put the project name in `project_path`. For example, if you provide a path of subgroups
|
||||
it fails with this error as `/` is not a valid character in a project name.
|
||||
|
||||
### `Name has already been taken and Path has already been taken`
|
||||
|
||||
A project with that name already exists.
|
||||
|
||||
### `Exception: Error importing repository into (namespace) - No space left on device`
|
||||
|
||||
The disk has insufficient space to complete the import.
|
||||
|
||||
During import, the tarball is cached in your configured `shared_path` directory. Verify the
|
||||
disk has enough free space to accommodate both the cached tarball and the unpacked
|
||||
project files on disk.
|
||||
|
||||
### Import succeeds with `Total number of not imported relations: XX` message
|
||||
|
||||
If you receive a `Total number of not imported relations: XX` message, and issues
|
||||
aren't created during the import, check [exceptions_json.log](../logs/index.md#exceptions_jsonlog).
|
||||
You might see an error like `N is out of range for ActiveModel::Type::Integer with limit 4 bytes`,
|
||||
where `N` is the integer exceeding the 4-byte integer limit. If that's the case, you
|
||||
are likely hitting the issue with rebalancing of `relative_position` field of the issues.
|
||||
|
||||
```ruby
|
||||
# Check the current maximum value of relative_position
|
||||
Issue.where(project_id: Project.find(ID).root_namespace.all_projects).maximum(:relative_position)
|
||||
|
||||
# Run the rebalancing process and check if the maximum value of relative_position has changed
|
||||
Issues::RelativePositionRebalancingService.new(Project.find(ID).root_namespace.all_projects).execute
|
||||
Issue.where(project_id: Project.find(ID).root_namespace.all_projects).maximum(:relative_position)
|
||||
```
|
||||
|
||||
Repeat the import attempt and check if the issues are imported successfully.
|
||||
|
||||
### Gitaly calls error when importing
|
||||
|
||||
If you're attempting to import a large project into a development environment, Gitaly might throw an error about too many calls or invocations. For example:
|
||||
|
||||
```plaintext
|
||||
Error importing repository into qa-perf-testing/gitlabhq - GitalyClient#call called 31 times from single request. Potential n+1?
|
||||
```
|
||||
|
||||
This error is due to a [n+1 calls limit for development setups](../../development/gitaly.md#toomanyinvocationserror-errors). To resolve this error, set `GITALY_DISABLE_REQUEST_LIMITS=1` as an environment variable. Then restart your development environment and import again.
|
||||
|
|
@ -57,98 +57,3 @@ Parameters:
|
|||
```shell
|
||||
gitlab-rake "gitlab:import_export:export[username, namespace_path, project_path, archive_path]"
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
If you are having trouble with import/export, you can enable debug mode using the same Rake task:
|
||||
|
||||
```shell
|
||||
# Import
|
||||
IMPORT_DEBUG=true gitlab-rake "gitlab:import_export:import[root, group/subgroup, testingprojectimport, /path/to/file_to_import.tar.gz]"
|
||||
|
||||
# Export
|
||||
EXPORT_DEBUG=true gitlab-rake "gitlab:import_export:export[root, group/subgroup, projectnametoexport, /tmp/export_file.tar.gz]"
|
||||
```
|
||||
|
||||
Check the common errors listed below, what they mean, and how to fix them.
|
||||
|
||||
### `Exception: undefined method 'name' for nil:NilClass`
|
||||
|
||||
The `username` is not valid.
|
||||
|
||||
### `Exception: undefined method 'full_path' for nil:NilClass`
|
||||
|
||||
The `namespace_path` does not exist.
|
||||
For example, one of the groups or subgroups is mistyped or missing,
|
||||
or you've specified the project name in the path.
|
||||
|
||||
The task only creates the project.
|
||||
If you want to import it to a new group or subgroup, create it first.
|
||||
|
||||
### `Exception: No such file or directory @ rb_sysopen - (filename)`
|
||||
|
||||
The specified project export file in `archive_path` is missing.
|
||||
|
||||
### `Exception: Permission denied @ rb_sysopen - (filename)`
|
||||
|
||||
The specified project export file cannot be accessed by the `git` user.
|
||||
|
||||
To fix the issue:
|
||||
|
||||
1. Set the file owner to `git:git`.
|
||||
1. Change the file permissions to `0400`.
|
||||
1. Move the file to a public folder (for example `/tmp/`).
|
||||
|
||||
### `Name can contain only letters, digits, emoji ...`
|
||||
|
||||
```plaintext
|
||||
Name can contain only letters, digits, emoji, '_', '.', '+', dashes, or spaces. It must start with a letter,
|
||||
digit, emoji, or '_', and Path can contain only letters, digits, '_', '-', or '.'. It cannot start
|
||||
with '-', end in '.git', or end in '.atom'.
|
||||
```
|
||||
|
||||
The project name specified in `project_path` is not valid for one of the specified reasons.
|
||||
|
||||
Only put the project name in `project_path`. For example, if you provide a path of subgroups
|
||||
it fails with this error as `/` is not a valid character in a project name.
|
||||
|
||||
### `Name has already been taken and Path has already been taken`
|
||||
|
||||
A project with that name already exists.
|
||||
|
||||
### `Exception: Error importing repository into (namespace) - No space left on device`
|
||||
|
||||
The disk has insufficient space to complete the import.
|
||||
|
||||
During import, the tarball is cached in your configured `shared_path` directory. Verify the
|
||||
disk has enough free space to accommodate both the cached tarball and the unpacked
|
||||
project files on disk.
|
||||
|
||||
### Import succeeds with `Total number of not imported relations: XX` message
|
||||
|
||||
If you receive a `Total number of not imported relations: XX` message, and issues
|
||||
aren't created during the import, check [exceptions_json.log](../logs/index.md#exceptions_jsonlog).
|
||||
You might see an error like `N is out of range for ActiveModel::Type::Integer with limit 4 bytes`,
|
||||
where `N` is the integer exceeding the 4-byte integer limit. If that's the case, you
|
||||
are likely hitting the issue with rebalancing of `relative_position` field of the issues.
|
||||
|
||||
```ruby
|
||||
# Check the current maximum value of relative_position
|
||||
Issue.where(project_id: Project.find(ID).root_namespace.all_projects).maximum(:relative_position)
|
||||
|
||||
# Run the rebalancing process and check if the maximum value of relative_position has changed
|
||||
Issues::RelativePositionRebalancingService.new(Project.find(ID).root_namespace.all_projects).execute
|
||||
Issue.where(project_id: Project.find(ID).root_namespace.all_projects).maximum(:relative_position)
|
||||
```
|
||||
|
||||
Repeat the import attempt and check if the issues are imported successfully.
|
||||
|
||||
### Gitaly calls error when importing
|
||||
|
||||
If you're attempting to import a large project into a development environment, Gitaly might throw an error about too many calls or invocations. For example:
|
||||
|
||||
```plaintext
|
||||
Error importing repository into qa-perf-testing/gitlabhq - GitalyClient#call called 31 times from single request. Potential n+1?
|
||||
```
|
||||
|
||||
This error is due to a [n+1 calls limit for development setups](../../development/gitaly.md#toomanyinvocationserror-errors). To resolve this error, set `GITALY_DISABLE_REQUEST_LIMITS=1` as an environment variable. Then restart your development environment and import again.
|
||||
|
|
|
|||
|
|
@ -832,15 +832,15 @@ Below is a table with the demonstration projects and their associated workflows:
|
|||
| Extend default ruleset | File Passthrough | [Local Ruleset](https://gitlab.com/gitlab-org/security-products/demos/analyzer-configurations/secret-detection/extend-default-ruleset/file-passthrough/-/blob/main/config/extended-gitleaks-config.toml) / [Project](https://gitlab.com/gitlab-org/security-products/demos/analyzer-configurations/secret-detection/extend-default-ruleset/file-passthrough) | Not applicable |
|
||||
| Extend default ruleset | Git Passthrough | Not applicable | [Remote Ruleset](https://gitlab.com/gitlab-org/security-products/tests/secrets-passthrough-git-and-url-test/-/blob/config-demos-extend/config/gitleaks.toml) / [Project](https://gitlab.com/gitlab-org/security-products/demos/analyzer-configurations/secret-detection/extend-default-ruleset/git-passthrough) |
|
||||
| Extend default ruleset | URL Passthrough | Not applicable | [Remote Ruleset](https://gitlab.com/gitlab-org/security-products/tests/secrets-passthrough-git-and-url-test/-/blob/config-demos-extend/config/gitleaks.toml) / [Project](https://gitlab.com/gitlab-org/security-products/demos/analyzer-configurations/secret-detection/extend-default-ruleset/url-passthrough) |
|
||||
| Ignore paths | File Passthrough | Demo not available yet | Not applicable |
|
||||
| Ignore paths | Git Passthrough | Not applicable | Demo not available yet |
|
||||
| Ignore paths | URL Passthrough | Not applicable | Demo not available yet |
|
||||
| Ignore patterns | File Passthrough | Demo not available yet | Not applicable |
|
||||
| Ignore patterns | Git Passthrough | Not applicable | Demo not available yet |
|
||||
| Ignore patterns | URL Passthrough | Not applicable | Demo not available yet |
|
||||
| Ignore values | File Passthrough | Demo not available yet | Not applicable |
|
||||
| Ignore values | Git Passthrough | Not applicable | Demo not available yet |
|
||||
| Ignore values | URL Passthrough | Not applicable | Demo not available yet |
|
||||
| Ignore paths | File Passthrough | [Local Ruleset](https://gitlab.com/gitlab-org/security-products/demos/analyzer-configurations/secret-detection/ignore-paths/file-passthrough/-/blob/main/config/extended-gitleaks-config.toml) / [Project](https://gitlab.com/gitlab-org/security-products/demos/analyzer-configurations/secret-detection/ignore-paths/file-passthrough) | Not applicable |
|
||||
| Ignore paths | Git Passthrough | Not applicable | [Remote Ruleset](https://gitlab.com/gitlab-org/security-products/tests/secrets-passthrough-git-and-url-test/-/blob/config-demos-ignore-paths/config/gitleaks.toml) / [Project](https://gitlab.com/gitlab-org/security-products/demos/analyzer-configurations/secret-detection/ignore-paths/git-passthrough) |
|
||||
| Ignore paths | URL Passthrough | Not applicable | [Remote Ruleset](https://gitlab.com/gitlab-org/security-products/tests/secrets-passthrough-git-and-url-test/-/blob/config-demos-ignore-paths/config/gitleaks.toml) / [Project](https://gitlab.com/gitlab-org/security-products/demos/analyzer-configurations/secret-detection/ignore-paths/url-passthrough) |
|
||||
| Ignore patterns | File Passthrough | [Local Ruleset](https://gitlab.com/gitlab-org/security-products/demos/analyzer-configurations/secret-detection/ignore-patterns/file-passthrough/-/blob/main/config/extended-gitleaks-config.toml) / [Project](https://gitlab.com/gitlab-org/security-products/demos/analyzer-configurations/secret-detection/ignore-patterns/file-passthrough) | Not applicable |
|
||||
| Ignore patterns | Git Passthrough | Not applicable | [Remote Ruleset](https://gitlab.com/gitlab-org/security-products/tests/secrets-passthrough-git-and-url-test/-/blob/config-demos-ignore-patterns/config/gitleaks.toml) / [Project](https://gitlab.com/gitlab-org/security-products/demos/analyzer-configurations/secret-detection/ignore-patterns/git-passthrough) |
|
||||
| Ignore patterns | URL Passthrough | Not applicable | [Remote Ruleset](https://gitlab.com/gitlab-org/security-products/tests/secrets-passthrough-git-and-url-test/-/blob/config-demos-ignore-patterns/config/gitleaks.toml) / [Project](https://gitlab.com/gitlab-org/security-products/demos/analyzer-configurations/secret-detection/ignore-patterns/url-passthrough) |
|
||||
| Ignore values | File Passthrough | [Local Ruleset](https://gitlab.com/gitlab-org/security-products/demos/analyzer-configurations/secret-detection/ignore-values/file-passthrough/-/blob/main/config/extended-gitleaks-config.toml) / [Project](https://gitlab.com/gitlab-org/security-products/demos/analyzer-configurations/secret-detection/ignore-values/file-passthrough) | Not applicable |
|
||||
| Ignore values | Git Passthrough | Not applicable | [Remote Ruleset](https://gitlab.com/gitlab-org/security-products/tests/secrets-passthrough-git-and-url-test/-/blob/config-demos-ignore-values/config/gitleaks.toml) / [Project](https://gitlab.com/gitlab-org/security-products/demos/analyzer-configurations/secret-detection/ignore-values/git-passthrough) |
|
||||
| Ignore values | URL Passthrough | Not applicable | [Remote Ruleset](https://gitlab.com/gitlab-org/security-products/tests/secrets-passthrough-git-and-url-test/-/blob/config-demos-ignore-values/config/gitleaks.toml) / [Project](https://gitlab.com/gitlab-org/security-products/demos/analyzer-configurations/secret-detection/ignore-values/url-passthrough) |
|
||||
|
||||
There are also some video demonstrations walking through setting up remote rulesets:
|
||||
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ With the rise of DevOps and SRE approaches, infrastructure management has become
|
|||
codified and automatable. You can now employ software development best practices
|
||||
in your infrastructure management.
|
||||
|
||||
The daily tasks of classical a classical operations team
|
||||
The daily tasks of a classical operations team
|
||||
have changed and are more similar to traditional software development. At the same time, software engineers
|
||||
are more likely to control their whole DevOps lifecycle, including deployments and delivery.
|
||||
|
||||
|
|
|
|||
|
|
@ -183,14 +183,14 @@ When the **Organization** tab is selected, you can further narrow down your sear
|
|||
|
||||
To make imports as fast as possible, the following items aren't imported from GitHub by default:
|
||||
|
||||
- More than approximately 30,000 comments because of a [limitation of the GitHub API](#missing-comments).
|
||||
- More than approximately 30,000 comments because of a [limitation of the GitHub API](troubleshooting_github_import.md#missing-comments).
|
||||
- Markdown attachments from repository comments, release posts, issue descriptions, and pull request descriptions. These can include
|
||||
images, text, or binary attachments. If not imported, links in Markdown to attachments break after you remove the attachments from GitHub.
|
||||
|
||||
You can choose to import these items, but this could significantly increase import time. To import these items, select the appropriate fields in the UI:
|
||||
|
||||
- **Use alternative comments import method**. If importing GitHub projects with more than approximately 30,000 comments across all issues and pull requests, you should enable this method because of a
|
||||
[limitation of the GitHub API](#missing-comments).
|
||||
[limitation of the GitHub API](troubleshooting_github_import.md#missing-comments).
|
||||
- **Import Markdown attachments**.
|
||||
- **Import collaborators** (selected by default). Leaving it selected might result in new users using a seat in the group or namespace,
|
||||
and being granted permissions [as high as project owner](#collaborators-members). Only direct collaborators are imported.
|
||||
|
|
@ -485,109 +485,3 @@ LoadModule ssl_module lib/httpd/modules/mod_ssl.so
|
|||
Header edit* Link "https://GITHUB_ENTERPRISE_HOSTNAME" "https://PROXY_HOSTNAME"
|
||||
</VirtualHost>
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Manually continue a previously failed import process
|
||||
|
||||
In some cases, the GitHub import process can fail to import the repository. This causes GitLab to abort the project import process and requires the
|
||||
repository to be imported manually. Administrators can manually import the repository for a failed import process:
|
||||
|
||||
1. Open a Rails console.
|
||||
1. Run the following series of commands in the console:
|
||||
|
||||
```ruby
|
||||
project_id = <PROJECT_ID>
|
||||
github_access_token = <GITHUB_ACCESS_TOKEN>
|
||||
github_repository_path = '<GROUP>/<REPOSITORY>'
|
||||
|
||||
github_repository_url = "https://#{github_access_token}@github.com/#{github_repository_path}.git"
|
||||
|
||||
# Find project by ID
|
||||
project = Project.find(project_id)
|
||||
# Set import URL and credentials
|
||||
project.import_url = github_repository_url
|
||||
project.import_type = 'github'
|
||||
project.import_source = github_repository_path
|
||||
project.save!
|
||||
# Create an import state if the project was created manually and not from a failed import
|
||||
project.create_import_state if project.import_state.blank?
|
||||
# Set state to start
|
||||
project.import_state.force_start
|
||||
|
||||
# Optional: If your import had certain optional stages selected or a timeout strategy
|
||||
# set, you can reset them here. Below is an example.
|
||||
# The params follow the format documented in the API:
|
||||
# https://docs.gitlab.com/ee/api/import.html#import-repository-from-github
|
||||
Gitlab::GithubImport::Settings
|
||||
.new(project)
|
||||
.write(
|
||||
timeout_strategy: "optimistic",
|
||||
optional_stages: {
|
||||
single_endpoint_issue_events_import: true,
|
||||
single_endpoint_notes_import: true,
|
||||
attachments_import: true,
|
||||
collaborators_import: true
|
||||
}
|
||||
)
|
||||
|
||||
# Trigger import from second step
|
||||
Gitlab::GithubImport::Stage::ImportRepositoryWorker.perform_async(project.id)
|
||||
```
|
||||
|
||||
### Import fails due to missing prefix
|
||||
|
||||
In GitLab 16.5 and later, you might get an error that states `Import failed due to a GitHub error: (HTTP 406)`.
|
||||
|
||||
This issue occurs because, in GitLab 16.5, the path prefix `api/v3` was removed from the GitHub importer. This happened because the importer stopped using the `Gitlab::LegacyGithubImport::Client`. This client automatically added the `api/v3` prefix on imports from a GitHub Enterprise URL.
|
||||
|
||||
To work around this error, [add the `api/v3` prefix](https://gitlab.com/gitlab-org/gitlab/-/issues/438358#note_1978902725) when importing from a GitHub Enterprise URL.
|
||||
|
||||
### Errors when importing large projects
|
||||
|
||||
The GitHub importer might encounter some errors when importing large projects.
|
||||
|
||||
#### Missing comments
|
||||
|
||||
The GitHub API has a limit that prevents more than approximately 30,000 notes or diff notes from being imported.
|
||||
When this limit is reached, the GitHub API instead returns the following error:
|
||||
|
||||
```plaintext
|
||||
In order to keep the API fast for everyone, pagination is limited for this resource. Check the rel=last link relation in the Link response header to see how far back you can traverse.
|
||||
```
|
||||
|
||||
If you are importing GitHub projects with a large number of comments, you should select the **Use alternative comments import method**
|
||||
[additional item to import](#select-additional-items-to-import) checkbox. This setting makes the import process take longer because it increases the number of network requests
|
||||
required to perform the import.
|
||||
|
||||
#### Reduce GitHub API request objects per page
|
||||
|
||||
Some GitHub API endpoints might return a `500` or `502` error for project imports from large repositories.
|
||||
To reduce the chance of these errors, in the group project importing the data, enable the
|
||||
`github_importer_lower_per_page_limit` feature flag. When enabled, the flag reduces the
|
||||
page size from `100` to `50`.
|
||||
|
||||
To enable this feature flag:
|
||||
|
||||
1. Start a [Rails console](../../../administration/operations/rails_console.md#starting-a-rails-console-session).
|
||||
1. Run the following `enable` command:
|
||||
|
||||
```ruby
|
||||
group = Group.find_by_full_path('my/group/fullpath')
|
||||
|
||||
# Enable
|
||||
Feature.enable(:github_importer_lower_per_page_limit, group)
|
||||
```
|
||||
|
||||
To disable the feature flag, run this command:
|
||||
|
||||
```ruby
|
||||
# Disable
|
||||
Feature.disable(:github_importer_lower_per_page_limit, group)
|
||||
```
|
||||
|
||||
### GitLab instance cannot connect to GitHub
|
||||
|
||||
Self-managed instances that run GitLab 15.10 or earlier, and are behind proxies, cannot resolve DNS for `github.com` or `api.github.com`.
|
||||
In this situation, the GitLab instance fails to connect to GitHub during the import and you must add `github.com` and `api.github.com`
|
||||
entries in the [allowlist for local requests](../../../security/webhooks.md#allow-outbound-requests-to-certain-ip-addresses-and-domains).
|
||||
|
|
|
|||
|
|
@ -0,0 +1,113 @@
|
|||
---
|
||||
stage: Manage
|
||||
group: Import and Integrate
|
||||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://handbook.gitlab.com/handbook/product/ux/technical-writing/#assignments
|
||||
---
|
||||
|
||||
## Troubleshooting importing a project from GitHub to GitLab
|
||||
|
||||
When importing a project from GitHub to GitLab, you might encounter the following issues.
|
||||
|
||||
### Manually continue a previously failed import process
|
||||
|
||||
In some cases, the GitHub import process can fail to import the repository. This causes GitLab to abort the project import process and requires the
|
||||
repository to be imported manually. Administrators can manually import the repository for a failed import process:
|
||||
|
||||
1. Open a Rails console.
|
||||
1. Run the following series of commands in the console:
|
||||
|
||||
```ruby
|
||||
project_id = <PROJECT_ID>
|
||||
github_access_token = <GITHUB_ACCESS_TOKEN>
|
||||
github_repository_path = '<GROUP>/<REPOSITORY>'
|
||||
|
||||
github_repository_url = "https://#{github_access_token}@github.com/#{github_repository_path}.git"
|
||||
|
||||
# Find project by ID
|
||||
project = Project.find(project_id)
|
||||
# Set import URL and credentials
|
||||
project.import_url = github_repository_url
|
||||
project.import_type = 'github'
|
||||
project.import_source = github_repository_path
|
||||
project.save!
|
||||
# Create an import state if the project was created manually and not from a failed import
|
||||
project.create_import_state if project.import_state.blank?
|
||||
# Set state to start
|
||||
project.import_state.force_start
|
||||
|
||||
# Optional: If your import had certain optional stages selected or a timeout strategy
|
||||
# set, you can reset them here. Below is an example.
|
||||
# The params follow the format documented in the API:
|
||||
# https://docs.gitlab.com/ee/api/import.html#import-repository-from-github
|
||||
Gitlab::GithubImport::Settings
|
||||
.new(project)
|
||||
.write(
|
||||
timeout_strategy: "optimistic",
|
||||
optional_stages: {
|
||||
single_endpoint_issue_events_import: true,
|
||||
single_endpoint_notes_import: true,
|
||||
attachments_import: true,
|
||||
collaborators_import: true
|
||||
}
|
||||
)
|
||||
|
||||
# Trigger import from second step
|
||||
Gitlab::GithubImport::Stage::ImportRepositoryWorker.perform_async(project.id)
|
||||
```
|
||||
|
||||
### Import fails due to missing prefix
|
||||
|
||||
In GitLab 16.5 and later, you might get an error that states `Import failed due to a GitHub error: (HTTP 406)`.
|
||||
|
||||
This issue occurs because, in GitLab 16.5, the path prefix `api/v3` was removed from the GitHub importer. This happened because the importer stopped using the `Gitlab::LegacyGithubImport::Client`. This client automatically added the `api/v3` prefix on imports from a GitHub Enterprise URL.
|
||||
|
||||
To work around this error, [add the `api/v3` prefix](https://gitlab.com/gitlab-org/gitlab/-/issues/438358#note_1978902725) when importing from a GitHub Enterprise URL.
|
||||
|
||||
### Errors when importing large projects
|
||||
|
||||
The GitHub importer might encounter some errors when importing large projects.
|
||||
|
||||
#### Missing comments
|
||||
|
||||
The GitHub API has a limit that prevents more than approximately 30,000 notes or diff notes from being imported.
|
||||
When this limit is reached, the GitHub API instead returns the following error:
|
||||
|
||||
```plaintext
|
||||
In order to keep the API fast for everyone, pagination is limited for this resource. Check the rel=last link relation in the Link response header to see how far back you can traverse.
|
||||
```
|
||||
|
||||
When importing GitHub projects with a large number of comments, select the **Use alternative comments import method**
|
||||
[additional item to import](github.md#select-additional-items-to-import) checkbox. This setting makes the import process take longer because it increases the number of network requests
|
||||
required to perform the import.
|
||||
|
||||
#### Reduce GitHub API request objects per page
|
||||
|
||||
Some GitHub API endpoints might return a `500` or `502` error for project imports from large repositories.
|
||||
To reduce the chance of these errors, in the group project importing the data, enable the
|
||||
`github_importer_lower_per_page_limit` feature flag. When enabled, the flag reduces the
|
||||
page size from `100` to `50`.
|
||||
|
||||
To enable this feature flag:
|
||||
|
||||
1. Start a [Rails console](../../../administration/operations/rails_console.md#starting-a-rails-console-session).
|
||||
1. Run the following `enable` command:
|
||||
|
||||
```ruby
|
||||
group = Group.find_by_full_path('my/group/fullpath')
|
||||
|
||||
# Enable
|
||||
Feature.enable(:github_importer_lower_per_page_limit, group)
|
||||
```
|
||||
|
||||
To disable the feature flag, run this command:
|
||||
|
||||
```ruby
|
||||
# Disable
|
||||
Feature.disable(:github_importer_lower_per_page_limit, group)
|
||||
```
|
||||
|
||||
### GitLab instance cannot connect to GitHub
|
||||
|
||||
Self-managed instances that run GitLab 15.10 or earlier, and are behind proxies, cannot resolve DNS for `github.com` or `api.github.com`.
|
||||
The GitLab instance fails to connect to GitHub during the import and you must add `github.com` and `api.github.com`
|
||||
entries in the [allowlist for local requests](../../../security/webhooks.md#allow-outbound-requests-to-certain-ip-addresses-and-domains).
|
||||
|
|
@ -46,11 +46,11 @@ module Gitlab
|
|||
output =
|
||||
case status
|
||||
when :success
|
||||
"[DONE]\n".color(:green)
|
||||
Rainbow("[DONE]\n").green
|
||||
when :failure
|
||||
"[FAILED]\n".color(:red)
|
||||
Rainbow("[FAILED]\n").red
|
||||
when :skipped
|
||||
"[SKIPPED]\n".color(:yellow)
|
||||
Rainbow("[SKIPPED]\n").yellow
|
||||
else
|
||||
raise ArgumentError, "State must be one of the following: #{STATES.join(', ')}"
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,52 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Gitlab
|
||||
module BackgroundMigration
|
||||
class BackfillUserDetails < BatchedMigrationJob
|
||||
operation_name :backfill_user_details # This is used as the key on collecting metrics
|
||||
feature_category :acquisition
|
||||
|
||||
class UserDetail < ApplicationRecord
|
||||
self.table_name = :user_details
|
||||
end
|
||||
|
||||
def perform
|
||||
each_sub_batch do |sub_batch|
|
||||
records_need_created_for_user_ids = sub_batch
|
||||
.joins("LEFT JOIN user_details ON (users.id = user_details.user_id)")
|
||||
.where(user_details: { user_id: nil })
|
||||
.ids
|
||||
|
||||
next if records_need_created_for_user_ids.empty?
|
||||
|
||||
user_details_attributes = records_need_created_for_user_ids.map do |user_id|
|
||||
{
|
||||
user_id: user_id
|
||||
}
|
||||
end
|
||||
|
||||
# This should be safe so we do not hit the fk_rails_12e0b3043d constraint
|
||||
# since we already prequalified in the query above that no user_details
|
||||
# record exists for that user.
|
||||
# However, to be sure, we'll rescue here in case there is some extreme
|
||||
# edge case.
|
||||
UserDetail.upsert_all(user_details_attributes, returning: false)
|
||||
rescue Exception => e # rubocop:disable Lint/RescueException -- following https://docs.gitlab.com/ee/development/database/batched_background_migrations.html#best-practices re-raise
|
||||
logger.error(
|
||||
class: e.class,
|
||||
message: "BackfillUserDetails Migration: error inserting. Reason: #{e.message}",
|
||||
user_ids: records_need_created_for_user_ids
|
||||
)
|
||||
|
||||
raise
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def logger
|
||||
@logger ||= Gitlab::BackgroundMigration::Logger.build
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -25,8 +25,7 @@ module Gitlab
|
|||
def should_enable_observability_auth_scopes?(resource)
|
||||
# Enable the needed oauth scopes if tracing is enabled.
|
||||
if resource.is_a?(Group) || resource.is_a?(Project)
|
||||
return Feature.enabled?(:observability_tracing, resource.root_ancestor) ||
|
||||
Feature.enabled?(:observability_features, resource.root_ancestor)
|
||||
return Feature.enabled?(:observability_features, resource.root_ancestor)
|
||||
end
|
||||
|
||||
false
|
||||
|
|
|
|||
|
|
@ -1,24 +1,12 @@
|
|||
---
|
||||
- i_package_composer_delete_package
|
||||
- i_package_composer_push_package
|
||||
- i_package_conan_delete_package
|
||||
- i_package_conan_push_package
|
||||
- i_package_debian_delete_package
|
||||
- i_package_debian_push_package
|
||||
- i_package_delete_package
|
||||
- i_package_delete_package_by_deploy_token
|
||||
- i_package_delete_package_by_guest
|
||||
- i_package_delete_package_by_user
|
||||
- i_package_generic_delete_package
|
||||
- i_package_generic_push_package
|
||||
- i_package_golang_delete_package
|
||||
- i_package_golang_push_package
|
||||
- i_package_helm_push_package
|
||||
- i_package_maven_delete_package
|
||||
- i_package_maven_push_package
|
||||
- i_package_npm_delete_package
|
||||
- i_package_npm_push_package
|
||||
- i_package_nuget_delete_package
|
||||
- i_package_nuget_push_package
|
||||
- i_package_nuget_pull_symbol_package
|
||||
- i_package_nuget_push_symbol_package
|
||||
|
|
@ -34,10 +22,7 @@
|
|||
- i_package_push_symbol_package_by_deploy_token
|
||||
- i_package_push_symbol_package_by_guest
|
||||
- i_package_push_symbol_package_by_user
|
||||
- i_package_pypi_delete_package
|
||||
- i_package_pypi_push_package
|
||||
- i_package_rubygems_delete_package
|
||||
- i_package_rubygems_push_package
|
||||
- i_package_terraform_module_delete_package
|
||||
- i_package_terraform_module_push_package
|
||||
- i_package_rpm_push_package
|
||||
|
|
|
|||
|
|
@ -28,6 +28,12 @@
|
|||
'{event_counters}_create_snippet_note': USAGE_NOTE_CREATE_SNIPPET
|
||||
'{event_counters}_create_wiki_page': USAGE_WIKI_PAGES_CREATE
|
||||
'{event_counters}_delete_design_management_design': USAGE_DESIGN_MANAGEMENT_DESIGNS_DELETE
|
||||
'{event_counters}_delete_package_from_registry-filter': USAGE_PACKAGE_EVENTS_I_PACKAGE_DELETE_PACKAGE
|
||||
'{event_counters}_delete_package_from_registry-filter:[label:conan]': USAGE_PACKAGE_EVENTS_I_PACKAGE_CONAN_DELETE_PACKAGE
|
||||
'{event_counters}_delete_package_from_registry-filter:[label:nuget]': USAGE_PACKAGE_EVENTS_I_PACKAGE_NUGET_DELETE_PACKAGE
|
||||
'{event_counters}_delete_package_from_registry-filter:[property:deploy_token]': USAGE_PACKAGE_EVENTS_I_PACKAGE_DELETE_PACKAGE_BY_DEPLOY_TOKEN
|
||||
'{event_counters}_delete_package_from_registry-filter:[property:guest]': USAGE_PACKAGE_EVENTS_I_PACKAGE_DELETE_PACKAGE_BY_GUEST
|
||||
'{event_counters}_delete_package_from_registry-filter:[property:user]': USAGE_PACKAGE_EVENTS_I_PACKAGE_DELETE_PACKAGE_BY_USER
|
||||
'{event_counters}_delete_wiki_page': USAGE_WIKI_PAGES_DELETE
|
||||
'{event_counters}_expand_merge_request_widget-filter:[label:accessibility]': USAGE_I_CODE_REVIEW_MERGE_REQUEST_WIDGET_ACCESSIBILITY_COUNT_EXPAND
|
||||
'{event_counters}_expand_merge_request_widget-filter:[label:accessibility,property:failed]': USAGE_I_CODE_REVIEW_MERGE_REQUEST_WIDGET_ACCESSIBILITY_COUNT_EXPAND_FAILED
|
||||
|
|
|
|||
|
|
@ -23765,6 +23765,9 @@ msgstr ""
|
|||
msgid "GitLab commit"
|
||||
msgstr ""
|
||||
|
||||
msgid "GitLab container registry API not supported"
|
||||
msgstr ""
|
||||
|
||||
msgid "GitLab detected an attempt to sign in to your %{host} account using an incorrect verification code"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
|||
|
|
@ -134,6 +134,8 @@ end
|
|||
require 'spec_helper'
|
||||
require File.expand_path('#{file_path}')
|
||||
|
||||
# See https://docs.gitlab.com/ee/development/testing_guide/best_practices.html#elasticsearch-specs
|
||||
# for more information on how to write search migration specs for GitLab.
|
||||
RSpec.describe #{options.name}, feature_category: :#{options.group.parameterize.underscore} do
|
||||
let(:version) { #{timestamp} }
|
||||
end
|
||||
|
|
@ -164,5 +166,3 @@ end
|
|||
end
|
||||
|
||||
ElasticMigrationCreator.new.execute if $PROGRAM_NAME == __FILE__
|
||||
|
||||
# vim: ft=ruby
|
||||
|
|
|
|||
|
|
@ -1,10 +1,26 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
FactoryBot.define do
|
||||
factory :abuse_report_notes, class: 'AntiAbuse::Reports::Note' do
|
||||
factory :abuse_report_note, class: 'AntiAbuse::Reports::Note' do
|
||||
abuse_report { association(:abuse_report) }
|
||||
note { generate(:title) }
|
||||
author { association(:user) }
|
||||
updated_by { author }
|
||||
|
||||
factory :abuse_report_discussion_note, class: 'AntiAbuse::Reports::DiscussionNote'
|
||||
|
||||
transient do
|
||||
in_reply_to { nil }
|
||||
end
|
||||
|
||||
before(:create) do |note, evaluator|
|
||||
discussion = evaluator.in_reply_to
|
||||
next unless discussion
|
||||
|
||||
discussion = discussion.to_discussion if discussion.is_a?(AntiAbuse::Reports::Note)
|
||||
next unless discussion
|
||||
|
||||
note.assign_attributes(discussion.reply_attributes)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -94,24 +94,6 @@ RSpec.describe 'Set up Mattermost slash commands', :js, feature_category: :integ
|
|||
expect(page).to have_content('test mattermost error message')
|
||||
end
|
||||
|
||||
it 'enables the submit button if the required fields are provided', :js do
|
||||
stub_teams(count: 1)
|
||||
|
||||
click_link 'Add to Mattermost'
|
||||
|
||||
expect(find('button[type="submit"]')['disabled']).not_to eq("true")
|
||||
end
|
||||
|
||||
it 'disables the submit button if the required fields are not provided', :js do
|
||||
stub_teams(count: 1)
|
||||
|
||||
click_link 'Add to Mattermost'
|
||||
|
||||
fill_in('mattermost_trigger', with: '')
|
||||
|
||||
expect(find('button[type="submit"]')['disabled']).to eq("true")
|
||||
end
|
||||
|
||||
def stub_teams(count: 0)
|
||||
teams = create_teams(count)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,55 +0,0 @@
|
|||
import $ from 'jquery';
|
||||
import htmlNewBranch from 'test_fixtures/branches/new_branch.html';
|
||||
import { setHTMLFixture, resetHTMLFixture } from 'helpers/fixtures';
|
||||
import '~/behaviors/requires_input';
|
||||
|
||||
describe('requiresInput', () => {
|
||||
let submitButton;
|
||||
|
||||
beforeEach(() => {
|
||||
setHTMLFixture(htmlNewBranch);
|
||||
submitButton = $('button[type="submit"]');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
resetHTMLFixture();
|
||||
});
|
||||
|
||||
it('disables submit when any field is required', () => {
|
||||
$('.js-requires-input').requiresInput();
|
||||
|
||||
expect(submitButton).toBeDisabled();
|
||||
});
|
||||
|
||||
it('enables submit when no field is required', () => {
|
||||
$('*[required=required]').prop('required', false);
|
||||
$('.js-requires-input').requiresInput();
|
||||
|
||||
expect(submitButton).not.toBeDisabled();
|
||||
});
|
||||
|
||||
it('enables submit when all required fields are pre-filled', () => {
|
||||
$('*[required=required]').remove();
|
||||
$('.js-requires-input').requiresInput();
|
||||
|
||||
expect($('.submit')).not.toBeDisabled();
|
||||
});
|
||||
|
||||
it('enables submit when all required fields receive input', () => {
|
||||
$('.js-requires-input').requiresInput();
|
||||
$('#required1').val('input1').change();
|
||||
|
||||
expect(submitButton).toBeDisabled();
|
||||
|
||||
$('#optional1').val('input1').change();
|
||||
|
||||
expect(submitButton).toBeDisabled();
|
||||
|
||||
$('#required2').val('input2').change();
|
||||
$('#required3').val('input3').change();
|
||||
$('#required4').val('input4').change();
|
||||
$('#required5').val('1').change();
|
||||
|
||||
expect($('.submit')).not.toBeDisabled();
|
||||
});
|
||||
});
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
<body>
|
||||
<meta charset="utf-8">
|
||||
<title>Document with Textarea</title>
|
||||
<form class="milestone-form common-note-form js-quick-submit js-requires-input" id="new_milestone"
|
||||
<form class="milestone-form common-note-form js-quick-submit" id="new_milestone"
|
||||
action="http://test.host/frontend-fixtures/milestones-project/-/milestones"
|
||||
accept-charset="UTF-8" method="post">
|
||||
<div class="form-group">
|
||||
|
|
|
|||
|
|
@ -1846,6 +1846,12 @@ export const mockHierarchyChildren = [
|
|||
iconName: 'issue-type-objective',
|
||||
__typename: 'WorkItemType',
|
||||
},
|
||||
namespace: {
|
||||
__typename: 'Project',
|
||||
id: '1',
|
||||
fullPath: 'test-objective-project-path',
|
||||
name: 'Project name',
|
||||
},
|
||||
title: 'Objective 2',
|
||||
state: 'OPEN',
|
||||
confidential: false,
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ RSpec.describe Resolvers::ContainerRepositoryTagsResolver, feature_category: :co
|
|||
stub_container_registry_config(enabled: true)
|
||||
end
|
||||
|
||||
context 'when Gitlab API is supported', :saas do
|
||||
context 'when Gitlab API is supported' do
|
||||
before do
|
||||
allow(repository).to receive(:tags_page).and_return({
|
||||
tags: [],
|
||||
|
|
@ -61,7 +61,7 @@ RSpec.describe Resolvers::ContainerRepositoryTagsResolver, feature_category: :co
|
|||
}
|
||||
})
|
||||
|
||||
allow(ContainerRegistry::GitlabApiClient).to receive(:supports_gitlab_api?).and_return(true)
|
||||
allow(repository.gitlab_api_client).to receive(:supports_gitlab_api?).and_return(true)
|
||||
end
|
||||
|
||||
context 'get the page size based on first and last param' do
|
||||
|
|
@ -133,7 +133,7 @@ RSpec.describe Resolvers::ContainerRepositoryTagsResolver, feature_category: :co
|
|||
|
||||
context 'when Gitlab API is not supported' do
|
||||
before do
|
||||
allow(ContainerRegistry::GitlabApiClient).to receive(:supports_gitlab_api?).and_return(false)
|
||||
allow(repository.gitlab_api_client).to receive(:supports_gitlab_api?).and_return(false)
|
||||
end
|
||||
|
||||
it_behaves_like 'fetching via tags and filter in place'
|
||||
|
|
|
|||
|
|
@ -95,11 +95,9 @@ RSpec.describe Gitlab::Auth, :use_clean_rails_memory_store_caching, feature_cate
|
|||
end
|
||||
|
||||
context 'with observability feature flags' do
|
||||
feature_flags = [:observability_tracing, :observability_features]
|
||||
|
||||
context 'when all disabled' do
|
||||
before do
|
||||
stub_feature_flags(feature_flags.index_with { false })
|
||||
stub_feature_flags(observability_features: false)
|
||||
end
|
||||
|
||||
it 'contains for group all resource bot scopes without observability scopes' do
|
||||
|
|
@ -126,70 +124,64 @@ RSpec.describe Gitlab::Auth, :use_clean_rails_memory_store_caching, feature_cate
|
|||
end
|
||||
end
|
||||
|
||||
flag_states = [true, false].repeated_permutation(feature_flags.length)
|
||||
flag_tests = flag_states.filter(&:any?).map { |flags| Hash[feature_flags.zip(flags)] }
|
||||
context "with feature flag enabled for specific root group" do
|
||||
let(:parent) { build_stubbed(:group) }
|
||||
let(:group) do
|
||||
build_stubbed(:group, parent: parent).tap { |g| g.namespace_settings = build_stubbed(:namespace_settings, namespace: g) }
|
||||
end
|
||||
|
||||
flag_tests.each do |flags|
|
||||
context "with flags #{flags} enabled for specific root group" do
|
||||
let(:parent) { build_stubbed(:group) }
|
||||
let(:group) do
|
||||
build_stubbed(:group, parent: parent).tap { |g| g.namespace_settings = build_stubbed(:namespace_settings, namespace: g) }
|
||||
let(:project) { build_stubbed(:project, namespace: group) }
|
||||
|
||||
before do
|
||||
stub_feature_flags(observability_features: parent)
|
||||
end
|
||||
|
||||
it 'contains for group all resource bot scopes including observability scopes' do
|
||||
expect(subject.available_scopes_for(group)).to match_array %i[
|
||||
api read_api read_repository write_repository read_registry write_registry
|
||||
read_observability write_observability create_runner manage_runner k8s_proxy ai_features
|
||||
]
|
||||
end
|
||||
|
||||
it 'contains for admin user all non-default scopes with ADMIN access and without observability scopes' do
|
||||
user = build_stubbed(:user, admin: true)
|
||||
|
||||
expect(subject.available_scopes_for(user)).to match_array %i[
|
||||
api read_user read_api read_repository write_repository read_registry write_registry read_service_ping
|
||||
sudo admin_mode create_runner manage_runner k8s_proxy ai_features
|
||||
]
|
||||
end
|
||||
|
||||
it 'contains for project all resource bot scopes including observability scopes' do
|
||||
expect(subject.available_scopes_for(project)).to match_array %i[
|
||||
api read_api read_repository write_repository read_registry write_registry
|
||||
read_observability write_observability create_runner manage_runner k8s_proxy ai_features
|
||||
]
|
||||
end
|
||||
|
||||
it 'contains for other group all resource bot scopes without observability scopes' do
|
||||
other_parent = build_stubbed(:group)
|
||||
other_group = build_stubbed(:group, parent: other_parent).tap do |g|
|
||||
g.namespace_settings = build_stubbed(:namespace_settings, namespace: g)
|
||||
end
|
||||
|
||||
let(:project) { build_stubbed(:project, namespace: group) }
|
||||
expect(subject.available_scopes_for(other_group)).to match_array %i[
|
||||
api read_api read_repository write_repository read_registry write_registry
|
||||
create_runner manage_runner k8s_proxy ai_features
|
||||
]
|
||||
end
|
||||
|
||||
before do
|
||||
flags.transform_values! { |v| v ? parent : false }
|
||||
stub_feature_flags(flags)
|
||||
it 'contains for other project all resource bot scopes without observability scopes' do
|
||||
other_parent = build_stubbed(:group)
|
||||
other_group = build_stubbed(:group, parent: other_parent).tap do |g|
|
||||
g.namespace_settings = build_stubbed(:namespace_settings, namespace: g)
|
||||
end
|
||||
other_project = build_stubbed(:project, namespace: other_group)
|
||||
|
||||
it 'contains for group all resource bot scopes including observability scopes' do
|
||||
expect(subject.available_scopes_for(group)).to match_array %i[
|
||||
api read_api read_repository write_repository read_registry write_registry
|
||||
read_observability write_observability create_runner manage_runner k8s_proxy ai_features
|
||||
]
|
||||
end
|
||||
|
||||
it 'contains for admin user all non-default scopes with ADMIN access and without observability scopes' do
|
||||
user = build_stubbed(:user, admin: true)
|
||||
|
||||
expect(subject.available_scopes_for(user)).to match_array %i[
|
||||
api read_user read_api read_repository write_repository read_registry write_registry read_service_ping
|
||||
sudo admin_mode create_runner manage_runner k8s_proxy ai_features
|
||||
]
|
||||
end
|
||||
|
||||
it 'contains for project all resource bot scopes including observability scopes' do
|
||||
expect(subject.available_scopes_for(project)).to match_array %i[
|
||||
api read_api read_repository write_repository read_registry write_registry
|
||||
read_observability write_observability create_runner manage_runner k8s_proxy ai_features
|
||||
]
|
||||
end
|
||||
|
||||
it 'contains for other group all resource bot scopes without observability scopes' do
|
||||
other_parent = build_stubbed(:group)
|
||||
other_group = build_stubbed(:group, parent: other_parent).tap do |g|
|
||||
g.namespace_settings = build_stubbed(:namespace_settings, namespace: g)
|
||||
end
|
||||
|
||||
expect(subject.available_scopes_for(other_group)).to match_array %i[
|
||||
api read_api read_repository write_repository read_registry write_registry
|
||||
create_runner manage_runner k8s_proxy ai_features
|
||||
]
|
||||
end
|
||||
|
||||
it 'contains for other project all resource bot scopes without observability scopes' do
|
||||
other_parent = build_stubbed(:group)
|
||||
other_group = build_stubbed(:group, parent: other_parent).tap do |g|
|
||||
g.namespace_settings = build_stubbed(:namespace_settings, namespace: g)
|
||||
end
|
||||
other_project = build_stubbed(:project, namespace: other_group)
|
||||
|
||||
expect(subject.available_scopes_for(other_project)).to match_array %i[
|
||||
api read_api read_repository write_repository read_registry write_registry
|
||||
create_runner manage_runner k8s_proxy ai_features
|
||||
]
|
||||
end
|
||||
expect(subject.available_scopes_for(other_project)).to match_array %i[
|
||||
api read_api read_repository write_repository read_registry write_registry
|
||||
create_runner manage_runner k8s_proxy ai_features
|
||||
]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,75 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Gitlab::BackgroundMigration::BackfillUserDetails, schema: 20240716191121, feature_category: :acquisition do
|
||||
let(:users) { table(:users) }
|
||||
let(:user_details) { table(:user_details) }
|
||||
|
||||
let!(:first_user) do
|
||||
users.create!(name: 'bob', email: 'bob@example.com', projects_limit: 1).tap do |record|
|
||||
user_details.create!(user_id: record.id)
|
||||
end
|
||||
end
|
||||
|
||||
let!(:user_without_details) { users.create!(name: 'foo', email: 'foo@example.com', projects_limit: 1) }
|
||||
let!(:multiple_user_without_details) { users.create!(name: 'foo2', email: 'foo2@example.com', projects_limit: 1) }
|
||||
|
||||
subject(:migration) do
|
||||
described_class.new(
|
||||
start_id: first_user.id,
|
||||
end_id: multiple_user_without_details.id,
|
||||
batch_table: :users,
|
||||
batch_column: :id,
|
||||
sub_batch_size: 100,
|
||||
pause_ms: 0,
|
||||
connection: ApplicationRecord.connection
|
||||
)
|
||||
end
|
||||
|
||||
describe '#perform' do
|
||||
it 'creates only the needed user_details entries' do
|
||||
expect(user_details.count).to eq(1)
|
||||
expect(user_details.exists?(user_id: first_user.id)).to be(true)
|
||||
expect(user_details.exists?(user_id: user_without_details.id)).to be(false)
|
||||
expect(user_details.exists?(user_id: multiple_user_without_details.id)).to be(false)
|
||||
|
||||
expect { migration.perform }.to change { user_details.count }.by(2)
|
||||
|
||||
expect(user_details.exists?(user_id: user_without_details.id)).to be(true)
|
||||
expect(user_details.exists?(user_id: multiple_user_without_details.id)).to be(true)
|
||||
end
|
||||
|
||||
context 'when there are no user_details that are missing for user records' do
|
||||
before do
|
||||
user_details.create!(user_id: user_without_details.id)
|
||||
user_details.create!(user_id: multiple_user_without_details.id)
|
||||
end
|
||||
|
||||
it 'creates only the needed user_details entries' do
|
||||
expect(user_details.count).to eq(3)
|
||||
|
||||
expect { migration.perform }.to change { user_details.count }.by(0)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when upsert raises an error' do
|
||||
before do
|
||||
allow(described_class::UserDetail).to receive(:upsert_all).and_raise(Exception, '_error_')
|
||||
end
|
||||
|
||||
it 'logs the error' do
|
||||
expect_next_instance_of(Gitlab::BackgroundMigration::Logger) do |logger|
|
||||
details = {
|
||||
class: Exception,
|
||||
message: "BackfillUserDetails Migration: error inserting. Reason: _error_",
|
||||
user_ids: [user_without_details.id, multiple_user_without_details.id]
|
||||
}
|
||||
expect(logger).to receive(:error).with(details)
|
||||
end
|
||||
|
||||
expect { migration.perform }.to raise_error(Exception)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -56,34 +56,47 @@ RSpec.describe Gitlab::Observability, feature_category: :error_tracking do
|
|||
end
|
||||
end
|
||||
|
||||
feature_flags = [:observability_tracing, :observability_features]
|
||||
flag_states = [true, false].repeated_permutation(feature_flags.length)
|
||||
flag_tests = flag_states.map { |flags| Hash[feature_flags.zip(flags)] }
|
||||
context "when feature flag is enabled" do
|
||||
before do
|
||||
stub_feature_flags(observability_features: true)
|
||||
end
|
||||
|
||||
flag_tests.each do |flags|
|
||||
context "with feature flag state #{flags}" do
|
||||
before do
|
||||
flags.transform_values! { |v| v ? parent : false }
|
||||
stub_feature_flags(flags)
|
||||
end
|
||||
describe 'when resource is group' do
|
||||
it { is_expected.to be true }
|
||||
end
|
||||
|
||||
let(:expected_enabled) { flags.values.any? }
|
||||
describe 'when resource is project' do
|
||||
let(:resource) { build_stubbed(:project, namespace: parent) }
|
||||
|
||||
describe 'when resource is group' do
|
||||
it { is_expected.to be expected_enabled }
|
||||
end
|
||||
it { is_expected.to be true }
|
||||
end
|
||||
|
||||
describe 'when resource is project' do
|
||||
let(:resource) { build_stubbed(:project, namespace: parent) }
|
||||
describe 'when resource is not a group or project' do
|
||||
let(:resource) { build_stubbed(:user) }
|
||||
|
||||
it { is_expected.to be expected_enabled }
|
||||
end
|
||||
it { is_expected.to be false }
|
||||
end
|
||||
end
|
||||
|
||||
describe 'when resource is not a group or project' do
|
||||
let(:resource) { build_stubbed(:user) }
|
||||
context "when feature flag is disabled" do
|
||||
before do
|
||||
stub_feature_flags(observability_features: false)
|
||||
end
|
||||
|
||||
it { is_expected.to be false }
|
||||
end
|
||||
describe 'when resource is group' do
|
||||
it { is_expected.to be false }
|
||||
end
|
||||
|
||||
describe 'when resource is project' do
|
||||
let(:resource) { build_stubbed(:project, namespace: parent) }
|
||||
|
||||
it { is_expected.to be false }
|
||||
end
|
||||
|
||||
describe 'when resource is not a group or project' do
|
||||
let(:resource) { build_stubbed(:user) }
|
||||
|
||||
it { is_expected.to be false }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,27 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
require_migration!
|
||||
|
||||
RSpec.describe QueueBackfillUserDetails, feature_category: :acquisition do
|
||||
let!(:batched_migration) { described_class::MIGRATION }
|
||||
|
||||
it 'schedules a new batched migration' do
|
||||
reversible_migration do |migration|
|
||||
migration.before -> {
|
||||
expect(batched_migration).not_to have_scheduled_batched_migration
|
||||
}
|
||||
|
||||
migration.after -> {
|
||||
expect(batched_migration).to have_scheduled_batched_migration(
|
||||
table_name: :users,
|
||||
column_name: :id,
|
||||
interval: described_class::DELAY_INTERVAL,
|
||||
batch_size: described_class::BATCH_SIZE,
|
||||
sub_batch_size: described_class::SUB_BATCH_SIZE,
|
||||
max_batch_size: described_class::MAX_BATCH_SIZE
|
||||
)
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe AntiAbuse::Reports::Discussion, feature_category: :insider_threat do
|
||||
describe '.base_discussion_id' do
|
||||
it 'returns the correct value' do
|
||||
expect(described_class.base_discussion_id(nil)).to eq([:discussion, :abuse_report_id])
|
||||
end
|
||||
end
|
||||
|
||||
describe '.note_class' do
|
||||
it 'returns the correct value' do
|
||||
expect(described_class.note_class).to eq(AntiAbuse::Reports::DiscussionNote)
|
||||
end
|
||||
end
|
||||
end
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue