Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2024-07-22 15:33:30 +00:00
parent 1007a13cc1
commit c0fe5a7e5b
114 changed files with 924 additions and 695 deletions

View File

@ -1 +1 @@
80395fced1ecfa757d2e72c3f648dc52f0f343c1
v17.3.0-rc3

View File

@ -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';

View File

@ -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();
});
});

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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
>

View File

@ -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"

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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"

View File

@ -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>

View File

@ -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>

View File

@ -40,6 +40,11 @@ fragment WorkItemHierarchy on WorkItem {
name
iconName
}
namespace {
id
fullPath
name
}
title
state
createdAt

View File

@ -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 {

View File

@ -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(

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,11 @@
# frozen_string_literal: true
module AntiAbuse
module Reports
class IndividualNoteDiscussion < Discussion
def individual_note?
true
end
end
end
end

View File

@ -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

View File

@ -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

View File

@ -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,

View File

@ -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 }

View File

@ -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

View File

@ -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,

View File

@ -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

View File

@ -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')

View File

@ -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

View File

@ -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

View File

@ -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"

View 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'

View File

@ -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

View File

@ -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

View File

@ -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'

View File

@ -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

View File

@ -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) }

View File

@ -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

View File

@ -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

View File

@ -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')

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -1,6 +1,7 @@
---
table_name: abuse_report_notes
classes:
- AntiAbuse::Reports::DiscussionNote
- AntiAbuse::Reports::Note
feature_categories:
- insider_threat

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -0,0 +1 @@
261ce451c12f68a089622b880be600a1b67ae0ae2d4108b1041355551cd7a733

View File

@ -0,0 +1 @@
074fb0d5586e7792884ab75a43d84c51678eb174ca79462683bd061fd9c91c25

View File

@ -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))

View File

@ -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`

View File

@ -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.

View File

@ -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.

View File

@ -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:

View File

@ -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.

View File

@ -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).

View File

@ -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).

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 ""

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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();
});
});

View File

@ -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">

View File

@ -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,

View File

@ -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'

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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