Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2024-05-24 21:19:13 +00:00
parent d64d52a860
commit cd0b43eb25
101 changed files with 584 additions and 200 deletions

View File

@ -13,19 +13,6 @@ Layout/FirstHashElementIndentation:
- 'ee/app/services/timebox_report_service.rb'
- 'ee/lib/ee/gitlab/ci/parsers.rb'
- 'ee/lib/ee/gitlab/usage_data.rb'
- 'ee/lib/elastic/latest/issue_class_proxy.rb'
- 'ee/lib/gitlab/analytics/type_of_work/tasks_by_type.rb'
- 'ee/lib/gitlab/ci/parsers/security/formatters/dependency_list.rb'
- 'ee/lib/gitlab/graphql/aggregations/epics/lazy_epic_aggregate.rb'
- 'ee/spec/controllers/admin/application_settings_controller_spec.rb'
- 'ee/spec/controllers/ee/projects/jobs_controller_spec.rb'
- 'ee/spec/controllers/ee/projects/variables_controller_spec.rb'
- 'ee/spec/controllers/groups/epic_boards_controller_spec.rb'
- 'ee/spec/controllers/groups/issues_controller_spec.rb'
- 'ee/spec/controllers/projects/integrations/jira/issues_controller_spec.rb'
- 'ee/spec/controllers/projects/repositories_controller_spec.rb'
- 'ee/spec/controllers/projects/vulnerability_feedback_controller_spec.rb'
- 'ee/spec/controllers/projects_controller_spec.rb'
- 'ee/spec/factories/dependencies.rb'
- 'ee/spec/finders/epics_finder_spec.rb'
- 'ee/spec/finders/namespaces/free_user_cap/users_finder_spec.rb'

View File

@ -49,16 +49,3 @@ Lint/AmbiguousOperatorPrecedence:
- 'spec/lib/gitlab/regex_spec.rb'
- 'spec/lib/gitlab/search/abuse_validators/no_abusive_term_length_validator_spec.rb'
- 'spec/lib/gitlab/slash_commands/deploy_spec.rb'
- 'spec/mailers/notify_spec.rb'
- 'spec/models/appearance_spec.rb'
- 'spec/models/ci/build_spec.rb'
- 'spec/models/ci/runner_spec.rb'
- 'spec/models/commit_spec.rb'
- 'spec/models/concerns/pg_full_text_searchable_spec.rb'
- 'spec/models/custom_emoji_spec.rb'
- 'spec/models/environment_spec.rb'
- 'spec/models/grafana_integration_spec.rb'
- 'spec/models/integrations/chat_message/push_message_spec.rb'
- 'spec/models/merge_request_diff_spec.rb'
- 'spec/models/packages/package_file_spec.rb'
- 'spec/models/prometheus_alert_spec.rb'

View File

@ -104,7 +104,7 @@ export default {
:note-id="note.id"
:note-url="note.url"
>
<span v-if="note.createdAt" class="d-none d-sm-inline">&middot;</span>
<span v-if="note.createdAt" class="gl-hidden sm:gl-inline">&middot;</span>
</note-header>
<div class="gl-display-inline-flex">
<abuse-report-note-actions

View File

@ -59,7 +59,7 @@ export default {
</script>
<template>
<div :title="title" class="divergence-graph px-2 d-none d-md-block">
<div :title="title" class="divergence-graph px-2 gl-hidden md:gl-block">
<template v-if="distance">
<graph-bar :count="distance" :max-commits="maxCommits" position="full" />
</template>

View File

@ -21,6 +21,7 @@ import { visitUrl } from '~/lib/utils/url_utility';
import { s__, __, n__ } from '~/locale';
import {
CC_VALIDATION_REQUIRED_ERROR,
IDENTITY_VERIFICATION_REQUIRED_ERROR,
CONFIG_VARIABLES_TIMEOUT,
FILE_TYPE,
VARIABLE_TYPE,
@ -71,6 +72,8 @@ export default {
VariableValuesListbox,
CcValidationRequiredAlert: () =>
import('ee_component/billings/components/cc_validation_required_alert.vue'),
PipelineAccountVerificationAlert: () =>
import('ee_component/vue_shared/components/pipeline_account_verification_alert.vue'),
},
directives: { SafeHtml },
props: {
@ -223,6 +226,9 @@ export default {
ccRequiredError() {
return this.error === CC_VALIDATION_REQUIRED_ERROR && !this.ccAlertDismissed;
},
identityVerificationRequiredError() {
return this.error === IDENTITY_VERIFICATION_REQUIRED_ERROR;
},
variableTypeListboxItems() {
return [
{
@ -390,6 +396,10 @@ export default {
<template>
<gl-form @submit.prevent="createPipeline">
<cc-validation-required-alert v-if="ccRequiredError" class="gl-pb-5" @dismiss="dismissError" />
<pipeline-account-verification-alert
v-else-if="identityVerificationRequiredError"
class="gl-mb-4"
/>
<gl-alert
v-else-if="error"
:title="errorTitle"

View File

@ -12,3 +12,6 @@ export const TAG_REF_TYPE = 'tag';
export const CC_VALIDATION_REQUIRED_ERROR = __(
'Credit card required to be on file in order to run CI jobs',
);
export const IDENTITY_VERIFICATION_REQUIRED_ERROR = __(
'Identity verification is required in order to run CI jobs',
);

View File

@ -8,6 +8,7 @@ const mountPipelineNewForm = (el) => {
const {
// provide/inject
projectRefsEndpoint,
identityVerificationPath,
// props
defaultBranch,
@ -37,6 +38,12 @@ const mountPipelineNewForm = (el) => {
apolloProvider,
provide: {
projectRefsEndpoint,
identityVerificationPath,
// Normally this will have a value from a helper. In this case, this is
// set to true because the alert that uses this field is dynamically
// rendered if a specific error is returned from the backend after
// the create pipeline XHR request completes
identityVerificationRequired: true,
},
render(createElement) {
return createElement(PipelineNewForm, {

View File

@ -94,7 +94,7 @@ export default {
class="d-block d-sm-flex flex-row-reverse justify-content-between align-items-start flex-lg-row-reverse"
>
<div
class="commit-actions flex-row d-none d-sm-flex gl-align-items-center gl-flex-wrap justify-content-end"
class="commit-actions flex-row gl-hidden sm:gl-flex gl-align-items-center gl-flex-wrap justify-content-end"
>
<div
v-if="commit.signature_html"
@ -129,7 +129,7 @@ export default {
:img-src="authorAvatar"
:img-alt="authorName"
:img-size="32"
class="avatar-cell d-none d-sm-block gl-my-2 gl-mr-4"
class="avatar-cell gl-hidden sm:gl-block gl-my-2 gl-mr-4"
/>
</div>
<div

View File

@ -216,7 +216,7 @@ export default {
>
{{ __('Show latest version') }}
</gl-button>
<div v-if="hasChanges" class="inline-parallel-buttons d-none gl-md-display-flex! ml-auto">
<div v-if="hasChanges" class="inline-parallel-buttons gl-hidden md:gl-flex ml-auto">
<diff-stats
:diff-files-count-text="diffFilesCountText"
:added-lines="addedLines"

View File

@ -228,7 +228,7 @@ export default {
:img-src="author.avatar_url"
:img-alt="author.name"
:img-size="48"
class="d-none d-sm-block new-comment"
class="!gl-hidden sm:!gl-block new-comment"
/>
<diff-discussions
v-if="imageDiscussions.length"

View File

@ -508,7 +508,7 @@ export default {
<div
v-if="diffFile.submodule_compare"
class="file-actions d-none d-sm-flex gl-align-items-center gl-flex-wrap"
class="file-actions gl-hidden sm:gl-flex gl-align-items-center gl-flex-wrap"
>
<gl-button
v-gl-tooltip.hover

View File

@ -550,7 +550,7 @@ export default {
upcomingDeploymentCellClasses() {
return [
this.tableData.upcoming.spacing,
{ 'gl-display-none gl-md-display-block': !this.upcomingDeployment },
{ '!gl-hidden md:!gl-block': !this.upcomingDeployment },
];
},
tableNameSpacingClass() {
@ -643,7 +643,7 @@ export default {
<div
v-if="!isFolder"
class="table-section deployment-column d-none d-md-block"
class="table-section deployment-column gl-hidden md:gl-block"
:class="tableData.deploy.spacing"
role="gridcell"
data-testid="environment-deployment-id-cell"
@ -678,7 +678,7 @@ export default {
<div
v-if="!isFolder"
class="table-section d-none d-md-block"
class="table-section gl-hidden md:gl-block"
:class="tableData.build.spacing"
role="gridcell"
data-testid="environment-build-cell"

View File

@ -177,11 +177,11 @@ export default {
<gl-loading-icon
v-if="group.isChildrenLoading"
size="lg"
class="d-none d-sm-inline-flex flex-shrink-0 gl-mr-3"
class="gl-hidden sm:gl-inline-flex flex-shrink-0 gl-mr-3"
/>
<a
:class="{ 'gl-sm-display-flex': !group.isChildrenLoading }"
class="gl-display-none gl-text-decoration-none! gl-mr-3"
class="gl-hidden gl-text-decoration-none! gl-mr-3"
:href="group.relativePath"
:aria-label="group.name"
>
@ -270,7 +270,7 @@ export default {
>
<item-stats
:item="group"
class="group-stats gl-display-none gl-md-display-flex gl-align-items-center"
class="group-stats gl-hidden md:gl-flex gl-align-items-center"
/>
<item-actions
v-if="showActionsMenu"

View File

@ -22,7 +22,7 @@ export default {
<template>
<div class="d-flex-center gl-flex-nowrap text-nowrap js-ide-status-mr">
<gl-icon name="merge-request" />
<span class="ml-1 d-none d-sm-block">{{ s__('WebIDE|Merge request') }}</span>
<span class="ml-1 gl-hidden sm:gl-block">{{ s__('WebIDE|Merge request') }}</span>
<gl-link class="ml-1" :href="url">{{ text }}</gl-link>
</div>
</template>

View File

@ -427,7 +427,7 @@ export default {
<template #pipeline-status="{ issuable = {} }">
<li
v-if="issuable.headPipeline && issuable.headPipeline.detailedStatus"
class="issuable-pipeline-status d-none d-sm-flex"
class="issuable-pipeline-status gl-hidden sm:gl-flex"
>
<ci-icon :status="issuable.headPipeline.detailedStatus" use-link show-tooltip />
</li>

View File

@ -494,7 +494,7 @@ export default {
<slot name="note-header-info"></slot>
</template>
<span v-if="commit" v-safe-html="actionText"></span>
<span v-else-if="note.created_at" class="d-none d-sm-inline">&middot;</span>
<span v-else-if="note.created_at" class="gl-hidden sm:gl-inline">&middot;</span>
</note-header>
<note-actions
:author="author"

View File

@ -12,7 +12,7 @@ import {
import { getDraft, clearDraft, updateDraft } from '~/lib/utils/autosave';
import csrf from '~/lib/utils/csrf';
import { setUrlFragment } from '~/lib/utils/url_utility';
import { s__, sprintf } from '~/locale';
import { __, s__, sprintf } from '~/locale';
import Tracking from '~/tracking';
import MarkdownEditor from '~/vue_shared/components/markdown/markdown_editor.vue';
import { trackSavedUsingEditor } from '~/vue_shared/components/markdown/tracking';
@ -79,6 +79,9 @@ export default {
format: {
label: s__('WikiPage|Format'),
},
template: {
label: __('Template'),
},
content: {
label: s__('WikiPage|Content'),
placeholder: s__('WikiPage|Write your content or drag files here…'),
@ -335,7 +338,7 @@ export default {
<div class="col-12">
<gl-form-group :label="$options.i18n.title.label" label-for="wiki_title">
<template v-if="!isTemplate" #description>
<gl-icon class="-gl-mr-1" name="bulb" />
<gl-icon name="bulb" />
{{ titleHelpText }}
<gl-link :href="helpPath" target="_blank">
{{ $options.i18n.title.helpText.learnMore }}
@ -354,15 +357,16 @@ export default {
<input v-model="title" type="hidden" name="wiki[title]" />
</gl-form-group>
</div>
</div>
<div class="col-sm-3 row-sm-10">
<div class="row">
<div class="col-sm-6">
<gl-form-group :label="$options.i18n.format.label" label-for="wiki_format">
<gl-form-select
id="wiki_format"
v-model="format"
name="wiki[format]"
:disabled="isContentEditorActive"
class="form-control"
:value="formatOptions.Markdown"
>
<option v-for="(key, label) of formatOptions" :key="key" :value="key">
@ -371,18 +375,16 @@ export default {
</gl-form-select>
</gl-form-group>
</div>
<div v-if="!isTemplate && templates.length" class="col-sm-6">
<gl-form-group :label="$options.i18n.template.label" label-for="wiki_template">
<wiki-template :format="format" :templates="templates" @input="setTemplate" />
</gl-form-group>
</div>
</div>
<div class="row">
<div class="col-sm-12 row-sm-5">
<gl-form-group :label="$options.i18n.content.label" label-for="wiki_content">
<wiki-template
v-if="!isTemplate && templates.length"
:format="format"
:templates="templates"
class="gl-mb-4"
@input="setTemplate"
/>
<markdown-editor
ref="markdownEditor"
v-model="content"
@ -402,7 +404,7 @@ export default {
@keydown.ctrl.enter="submitFormWithShortcut"
@keydown.meta.enter="submitFormWithShortcut"
/>
<div class="form-text gl-text-gray-600">
<template #description>
<gl-sprintf
v-if="displayWikiSpecificMarkdownHelp && !isTemplate"
:message="$options.i18n.linksHelpText"
@ -421,7 +423,7 @@ export default {
></template
>
</gl-sprintf>
</div>
</template>
</gl-form-group>
</div>
</div>

View File

@ -79,6 +79,7 @@ export default {
v-model="selectedTemplate"
:items="templatesList"
searchable
block
:toggle-text="toggleText"
:search-placeholder="$options.i18n.searchTemplates"
:no-results-text="$options.i18n.noMatchingTemplates"

View File

@ -154,7 +154,7 @@ export default {
{{ metricDetailsLabel }}
</span>
</gl-button>
<gl-modal :modal-id="modalId" :title="header" size="lg" footer-class="d-none" scrollable>
<gl-modal :modal-id="modalId" :title="header" size="lg" footer-class="!gl-hidden" scrollable>
<div class="gl-display-flex gl-align-items-center gl-justify-content-space-between">
<div class="gl-display-flex gl-align-items-center" data-testid="performance-bar-summary">
<div v-for="(value, name) in metricDetailsSummary" :key="name" class="gl-pr-8">

View File

@ -3,7 +3,9 @@
<thead>
<tr>
<th id="name" scope="col">{{ s__('ProjectFileTree|Name') }}</th>
<th id="last-commit" scope="col" class="d-none d-sm-table-cell">{{ __('Last commit') }}</th>
<th id="last-commit" scope="col" class="gl-hidden sm:gl-table-cell">
{{ __('Last commit') }}
</th>
<th id="last-update" scope="col" class="text-right">{{ __('Last update') }}</th>
</tr>
</thead>

View File

@ -248,7 +248,7 @@ export default {
class="ml-1"
/>
</td>
<td class="d-none d-sm-table-cell tree-commit cursor-default">
<td class="gl-hidden sm:gl-table-cell tree-commit cursor-default">
<gl-link
v-if="commitData"
v-safe-html:[$options.safeHtmlConfig]="commitData.titleHtml"

View File

@ -1,5 +1,5 @@
const hide = (el) => el.classList.add('d-none');
const show = (el) => el.classList.remove('d-none');
const hide = (el) => el.classList.add('!gl-hidden');
const show = (el) => el.classList.remove('!gl-hidden');
const setupCollapsibleInput = (el) => {
const collapsedEl = el.querySelector('.js-collapsed');
@ -35,9 +35,9 @@ const setupCollapsibleInput = (el) => {
* Usage in HAML
*
* .js-collapsible-input
* .js-collapsed{ class: ('d-none' if is_expanded) }
* .js-collapsed{ class: ('!gl-hidden' if is_expanded) }
* = input
* .js-expanded{ class: ('d-none' if !is_expanded) }
* .js-expanded{ class: ('!gl-hidden' if !is_expanded) }
* = big_input
*/
export default () => {

View File

@ -32,7 +32,7 @@ export default {
<div class="form-group js-description-input">
<label for="snippet-description">{{ s__('Snippets|Description (optional)') }}</label>
<div class="js-collapsible-input">
<div class="js-collapsed" :class="{ 'd-none': value }">
<div class="js-collapsed" :class="{ '!gl-hidden': value }">
<gl-form-textarea
class="form-control"
rows="2"
@ -43,7 +43,7 @@ export default {
</div>
<markdown-field
class="js-expanded"
:class="{ 'd-none': !value }"
:class="{ '!gl-hidden': !value }"
:add-spacing-classes="false"
:markdown-preview-path="markdownPreviewPath"
:markdown-docs-path="markdownDocsPath"

View File

@ -17,7 +17,7 @@ export default {
<div class="gl-pl-7">
<table class="table m-0">
<thead class="thead-white text-nowrap">
<tr class="d-none d-sm-table-row">
<tr class="gl-hidden sm:gl-table-row">
<th>{{ __('Artifact') }}</th>
<th>{{ __('Job') }}</th>
</tr>

View File

@ -307,7 +307,7 @@ export default {
/>
</div>
<h4 class="gl-display-block d-md-none my-3">
<h4 class="gl-block md:gl-hidden my-3">
<slot name="title"></slot>
</h4>

View File

@ -343,7 +343,7 @@ export default {
:note-url="note.url"
:is-internal-note="note.internal"
>
<span v-if="note.createdAt" class="d-none d-sm-inline">&middot;</span>
<span v-if="note.createdAt" class="gl-hidden sm:gl-inline">&middot;</span>
</note-header>
<div class="gl-display-inline-flex">
<note-actions

View File

@ -143,7 +143,9 @@ ul.wiki-pages-list.content-list {
}
}
.wiki-form .markdown-area {
.wiki-form .markdown-area,
.wiki-form .ProseMirror {
min-height: min(20vh, 320px);
max-height: 55vh;
}
@ -211,4 +213,4 @@ ul.wiki-pages-list.content-list {
.gl-disclosure-dropdown {
display: none !important;
}
}
}

View File

@ -76,6 +76,23 @@ module Ci
current_user.user_preference.visibility_pipeline_id_type
end
def new_pipeline_data(project)
{
project_id: project.id,
pipelines_path: project_pipelines_path(project),
default_branch: project.default_branch,
pipelines_editor_path: project_ci_pipeline_editor_path(project),
can_view_pipeline_editor: can_view_pipeline_editor?(project),
ref_param: params[:ref] || project.default_branch,
var_param: params[:var].to_json,
file_param: params[:file_var].to_json,
project_path: project.full_path,
project_refs_endpoint: refs_project_path(project, sort: 'updated_desc'),
settings_link: project_settings_ci_cd_path(project),
max_warnings: ::Gitlab::Ci::Warnings::MAX_LIMIT
}
end
private
def show_jenkins_ci_prompt(project)

View File

@ -2,6 +2,8 @@
module Ci
class DailyBuildGroupReportResult < Ci::ApplicationRecord
include Ci::Partitionable
PARAM_TYPES = %w[coverage].freeze
belongs_to :last_pipeline, class_name: 'Ci::Pipeline', foreign_key: :last_pipeline_id,
@ -11,6 +13,8 @@ module Ci
validates :data, json_schema: { filename: "daily_build_group_report_result_data" }
partitionable scope: :last_pipeline
scope :by_ref_path, ->(ref_path) { where(ref_path: ref_path) }
scope :by_projects, ->(ids) { where(project_id: ids) }
scope :by_group, ->(group_id) { where(group_id: group_id) }

View File

@ -16,6 +16,7 @@ module Ci
Ci::BuildTraceChunk
Ci::BuildTraceMetadata
Ci::BuildPendingState
Ci::DailyBuildGroupReportResult
Ci::JobAnnotation
Ci::JobArtifact
Ci::JobVariable

View File

@ -1,6 +1,6 @@
- only_key_value = local_assigns.fetch(:only_key_value, false)
%li.ci-variable-row.m-0.d-none.d-sm-block
%li.ci-variable-row.m-0.gl-hidden.sm:gl-block
.d-flex.gl-w-full.gl-align-items-center.pb-2
.bold.table-section.section-15.gl-mr-3
= s_('CiVariables|Type')

View File

@ -26,6 +26,6 @@
.nav-controls
= render 'shared/projects/search_form'
= render 'filter'
= link_button_to nil, topic_explore_projects_path(@topic.name, rss_url_options), title: s_("Topics|Subscribe to the new projects feed"), class: 'd-none d-sm-inline-flex has-tooltip', icon: 'rss'
= link_button_to nil, topic_explore_projects_path(@topic.name, rss_url_options), title: s_("Topics|Subscribe to the new projects feed"), class: 'gl-hidden sm:gl-inline-flex has-tooltip', icon: 'rss'
= render 'projects', projects: @projects

View File

@ -1,7 +1,7 @@
.nav-block.activities
= render 'shared/event_filter', show_group_events: @group.supports_events?
.controls
= render Pajamas::ButtonComponent.new(href: group_path(@group, rss_url_options), icon: 'rss', button_options: { class: 'd-none d-sm-inline-flex has-tooltip', title: _('Subscribe') })
= render Pajamas::ButtonComponent.new(href: group_path(@group, rss_url_options), icon: 'rss', button_options: { class: 'gl-hidden sm:gl-inline-flex has-tooltip', title: _('Subscribe') })
.content_list
.loading

View File

@ -4,5 +4,5 @@
-# total_pages: total number of pages
-# per_page: number of items to fetch per page
-# remote: data-remote
%li.page-item.disabled.d-none.d-md-block
%li.page-item.disabled.gl-hidden.md:gl-block
= link_to raw(t('views.pagination.truncate')), '#', class: 'page-link'

View File

@ -10,5 +10,5 @@
('sibling' if page.next? || page.prev?),
('js-first-button' if page.first?),
('js-last-button' if page.last?),
('d-none d-md-block' if !page.current?)] }
('!gl-hidden md:!gl-block' if !page.current?)] }
= link_to page, url, { remote: remote, rel: page.next? ? 'next' : page.prev? ? 'prev' : nil, class: ['page-link', active_when(page.current?)] }

View File

@ -1,9 +1,9 @@
- is_project_overview = local_assigns.fetch(:is_project_overview, false)
.nav-block.d-none.d-sm-flex.activities.gl-static
.nav-block.activities.gl-static
= render 'shared/event_filter'
.controls.gl-display-flex
= link_button_to nil, project_path(@project, rss_url_options), title: s_("ProjectActivityRSS|Subscribe"), class: 'd-none d-sm-inline-flex has-tooltip', icon: 'rss'
.controls.gl-flex
= link_button_to nil, project_path(@project, rss_url_options), title: s_("ProjectActivityRSS|Subscribe"), class: '!gl-hidden sm:!gl-inline-flex has-tooltip', icon: 'rss'
- if is_project_overview && can?(current_user, :download_code, @project)
.project-code-holder.gl-hidden.md:gl-inline-flex.gl-ml-2
= render "projects/buttons/code", dropdown_class: 'dropdown-menu-right', ref: @ref

View File

@ -11,7 +11,7 @@
.js-file-title.file-title-flex-parent
= render 'projects/blob/header_content', blob: blob
.file-actions.d-none.d-sm-block
.file-actions.gl-hidden.sm:gl-block
.btn-group{ role: "group" }<
= copy_blob_source_button(blob)
= download_blob_button(blob)

View File

@ -7,7 +7,7 @@
- if blob.rich_viewer && blob.extension != 'geojson'
- add_page_startup_api_call local_assigns.fetch(:viewer_url) { url_for(safe_params.merge(viewer: blob.rich_viewer.type, format: :json)) }
.info-well.d-none.d-sm-block
.info-well.gl-hidden.sm:gl-block
.well-segment
%ul.blob-commit-info
= render 'projects/commits/commit', commit: @last_commit, project: @project, ref: @ref

View File

@ -26,7 +26,7 @@
- if branch.name != @repository.root_ref
.js-branch-divergence-graph
.pipeline-status.d-none.d-md-block<
.pipeline-status.gl-hidden.md:gl-block<
- if commit_status
= render 'ci/status/icon', status: commit_status
- elsif show_commit_status

View File

@ -7,7 +7,7 @@
#{ s_('CommitBoxTitle|Commit') }
%span.commit-sha{ data: { testid: 'commit-sha-content' } }= @commit.short_id
= clipboard_button(text: @commit.id, title: _('Copy commit SHA'))
%span.d-none.d-sm-inline= _('authored')
%span.gl-hidden.sm:gl-inline= _('authored')
#{time_ago_with_tooltip(@commit.authored_date)}
%span= s_('ByAuthor|by')
= author_avatar(@commit, size: 24, has_tooltip: false)

View File

@ -24,7 +24,7 @@
- add_page_specific_style 'page_bundles/commit_description'
%li{ class: ["commit flex-row", ("js-toggle-container" if collapsible)], id: "commit-#{commit.short_id}" }
.avatar-cell.d-none.d-sm-block
.avatar-cell.gl-hidden.sm:gl-block
= author_avatar(commit, size: 40, has_tooltip: false)
.commit-detail.flex-list.gl-display-flex.gl-justify-content-space-between.gl-align-items-center.gl-flex-grow-1.gl-min-w-0
@ -75,7 +75,7 @@
.js-commit-pipeline-status{ data: { endpoint: pipelines_project_commit_path(project, commit.id, ref: ref) } }
.commit-sha-group.btn-group.d-none.d-sm-flex
.commit-sha-group.btn-group.gl-hidden.sm:gl-flex
.label.label-monospace.monospace
= commit.short_id
= clipboard_button(text: commit.id, category: :primary, size: :medium, title: _("Copy commit SHA"))

View File

@ -19,17 +19,17 @@
#js-author-dropdown{ data: { 'commits_path': project_commits_path(@project), 'project_id': @project.id } }
.tree-controls
- if @merge_request.present?
.control.d-none.d-md-block
.control.gl-hidden.md:gl-block
= link_button_to _("View open merge request"), project_merge_request_path(@project, @merge_request)
- elsif create_mr_button?(from: @ref, source_project: @project)
.control.d-none.d-md-block
.control.gl-hidden.md:gl-block
= render Pajamas::ButtonComponent.new(variant: :confirm, href: create_mr_path(from: @ref, source_project: @project)) do
= _("Create merge request")
.control
= form_tag(project_commits_path(@project, @id, ref_type: @ref_type), method: :get, class: 'commits-search-form js-signature-container', data: { 'signatures-path' => namespace_project_signatures_path(ref_type: @ref_type)}) do
= search_field_tag :search, params[:search], { placeholder: _('Search by message'), id: 'commits-search', class: 'form-control gl-form-input input-short gl-mt-3 gl-sm-mt-0 gl-min-w-full', spellcheck: false }
.control.d-none.d-md-block
.control.gl-hidden.md:gl-block
= link_button_to nil, project_commits_path(@project, @id, rss_url_options), title: _("Commits feed"), icon: 'rss'
= render_if_exists 'projects/commits/mirror_status'

View File

@ -10,11 +10,11 @@
= render "projects/diffs/file_header", diff_file: diff_file, url: "##{file_hash}"
- if diff_file.submodule?
.file-actions.d-none.d-sm-block
.file-actions.gl-hidden.sm:gl-block
= submodule_diff_compare_link(diff_file)
- unless diff_file.submodule?
.file-actions.gl-display-none.gl-sm-display-flex
.file-actions.gl-hidden.sm:gl-flex
#js-diff-stats{ data: diff_file_stats_data(diff_file) }
- if diff_file.blob&.readable_text?
- unless @diff_notes_disabled

View File

@ -1,6 +1,6 @@
by
%a{ href: user_path(@build.user) }
%span.d-none.d-sm-inline
%span.gl-hidden.sm:gl-inline
= render Pajamas::AvatarComponent.new(@build.user, size: 24, alt: "")
%strong{ data: { toggle: 'tooltip', placement: 'top', title: @build.user.to_reference } }
= @build.user.name

View File

@ -5,7 +5,7 @@
.gl-new-card-empty.gl-px-5.gl-py-4= _('There are currently no mirrored repositories.')
- else
%table.table.b-table.gl-table.b-table-stacked-md
%thead.d-none.d-md-table-header-group
%thead.gl-hidden.md:gl-table-header-group
%tr
%th= _('Repository')
%th= _('Direction')

View File

@ -5,15 +5,4 @@
%h1.page-title.gl-font-size-h-display
= s_('Pipeline|Run pipeline')
#js-new-pipeline{ data: { project_id: @project.id,
pipelines_path: project_pipelines_path(@project),
default_branch: @project.default_branch,
pipelines_editor_path: project_ci_pipeline_editor_path(@project),
can_view_pipeline_editor: can_view_pipeline_editor?(@project),
ref_param: params[:ref] || @project.default_branch,
var_param: params[:var].to_json,
file_param: params[:file_var].to_json,
project_path: @project.full_path,
project_refs_endpoint: refs_project_path(@project, sort: 'updated_desc'),
settings_link: project_settings_ci_cd_path(@project),
max_warnings: ::Gitlab::Ci::Warnings::MAX_LIMIT } }
#js-new-pipeline{ data: new_pipeline_data(@project) }

View File

@ -12,7 +12,7 @@
%col{ width: "50%" }
- if can_admin_project
%col
%thead.d-none.d-md-table-header-group
%thead.gl-hidden.md:gl-table-header-group
%tr
%th
= s_('ProtectedBranch|Tag')

View File

@ -1,6 +1,6 @@
- @force_desktop_expanded_sidebar = true
- add_form_class = 'gl-display-none' if !form_errors(@application)
- hide_class = 'gl-display-none' if form_errors(@application)
- add_form_class = 'gl-hidden' if !form_errors(@application)
- hide_class = 'gl-hidden' if form_errors(@application)
.settings-section.js-search-settings-section
.settings-sticky-header
@ -36,7 +36,7 @@
- if @applications.any?
.table-holder
%table.table.b-table.gl-table.b-table-stacked-sm.-gl-mt-1.gl-mb-n2
%thead.d-none.d-md-table-header-group
%thead.gl-hidden.md:gl-table-header-group
%tr
%th= _('Name')
%th= _('Callback URL')
@ -78,7 +78,7 @@
- if @authorized_tokens.any?
.table-holder
%table.table.b-table.gl-table.b-table-stacked-sm.-gl-mt-1.gl-mb-n2
%thead.d-none.d-md-table-header-group
%thead.gl-hidden.md:gl-table-header-group
%tr
%th= _('Name')
%th= _('Authorized At')

View File

@ -2,7 +2,7 @@
%thead
%tr
%th= _('Status')
%th.d-none.d-sm-table-cell= _('Trigger')
%th.gl-hidden.sm:gl-table-cell= _('Trigger')
%th= _('Elapsed time')
%th= _('Request time')
%th
@ -12,7 +12,7 @@
%tr
%td
= render partial: 'shared/hook_logs/status_label', locals: { hook_log: hook_log }
%td.d-none.d-sm-table-cell
%td.gl-hidden.sm:gl-table-cell
= gl_badge_tag hook_log.trigger.singularize.titleize, { size: :sm }
%td
#{number_with_precision(hook_log.execution_duration, precision: 2)} sec

View File

@ -12,7 +12,7 @@
%a.gitlab-logo-wrapper{ href: url_for(only_path: false, overwrite_params: nil), title: 'View on GitLab', target: '_blank', rel: 'noopener noreferrer' }
%img.gitlab-logo{ src: image_url('ext_snippet_icons/logo.svg'), alt: "GitLab logo" }
.file-actions.d-none.d-sm-block
.file-actions.gl-hidden.sm:gl-block
.btn-group{ role: "group" }<
= embedded_copy_snippet_button(blob)
= embedded_raw_snippet_button(@snippet, blob)

View File

@ -3,7 +3,7 @@
%aside.right-sidebar.right-sidebar-expanded.wiki-sidebar.js-wiki-sidebar.js-right-sidebar{ data: { "offset-top" => "50", "spy" => "affix" }, 'aria-label': _('Wiki') }
.sidebar-container
.block.gl-mb-3.gl-mx-5.gl-block.sm:gl-hidden{ class: '!gl-pt-0' }
%a.gutter-toggle.gl-float-right.d-block.d-md-none.js-sidebar-wiki-toggle{ href: "#" }
%a.gutter-toggle.gl-float-right.gl-block.md:gl-hidden.js-sidebar-wiki-toggle{ href: "#" }
= sprite_icon('chevron-double-lg-right', css_class: 'gl-icon')
- if @sidebar_error.present?

View File

@ -19,7 +19,7 @@
.page-content-header
.header-main-content
%strong= markdown_field(commit, :title)
%span.d-none.d-sm-inline= _('authored')
%span.gl-hidden.sm:gl-inline= _('authored')
#{time_ago_with_tooltip(commit.authored_date)}
%span= s_('ByAuthor|by')
= author_avatar(commit, size: 24, has_tooltip: false)

View File

@ -1,7 +1,7 @@
%tr.key-list-item
%td{ data: { label: s_('Profiles|Key') } }
%div{ class: 'gl-display-flex! gl-pl-0!' }
= sprite_icon('key', css_class: "settings-list-icon d-none d-sm-inline gl-mr-2")
= sprite_icon('key', css_class: "settings-list-icon gl-hidden sm:gl-inline gl-mr-2")
.gl-display-flex.gl-flex-direction-column.gl-text-truncate
%p.gl-text-truncate.gl-m-0
%code= key.fingerprint

View File

@ -4,7 +4,7 @@
- if @gpg_keys.any?
.table-holder
%table.table.b-table.gl-table.b-table-stacked-md.-gl-mt-1.gl-mb-n2.ssh-keys-list
%thead.d-none.d-md-table-header-group
%thead.gl-hidden.md:gl-table-header-group
%tr
%th= s_('Profiles|Key')
%th= _('Status')

View File

@ -4,7 +4,7 @@
- if @keys.any?
.table-holder
%table.table.b-table.gl-table.b-table-stacked-md.-gl-mt-1.gl-mb-n2.ssh-keys-list{ data: { testid: 'ssh-keys-list' } }
%thead.d-none.d-md-table-header-group
%thead.gl-hidden.md:gl-table-header-group
%tr
%th= _('Title')
%th= s_('Profiles|Key')

View File

@ -6,9 +6,9 @@ property_description: ""
value_description: ""
extra_properties:
identifiers:
product_section: dev
product_stage: manage
product_group: foundations
product_section: ci
product_stage: package
product_group: package_registry
milestone: "13.4"
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/41668
distributions:

View File

@ -8,7 +8,7 @@ identifiers:
- user
product_section: core_platform
product_stage: manage
product_group: foundations
product_group: personal_productivity
milestone: '16.11'
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/issues/451458
distributions:

View File

@ -5,4 +5,4 @@ rollout_issue_url:
milestone: '14.8'
type: ops
group: group::environments
default_enabled: false
default_enabled: true

View File

@ -3,7 +3,7 @@ key_path: redis_hll_counters.count_distinct_user_id_from_click_expand_panel_on_s
description: Monthly count of unique users who expanded a settings panel
product_section: core_platform
product_stage: manage
product_group: foundations
product_group: personal_productivity
performance_indicator_type: []
value_type: number
status: active

View File

@ -3,7 +3,7 @@ key_path: redis_hll_counters.count_distinct_user_id_from_click_expand_panel_on_s
description: Weekly count of unique users who expanded a settings panel
product_section: core_platform
product_stage: manage
product_group: foundations
product_group: personal_productivity
performance_indicator_type: []
value_type: number
status: active

View File

@ -30,7 +30,6 @@
"engineering_analytics",
"engineering_productivity",
"environments",
"foundations",
"fulfillment_platform",
"gdk",
"geo",
@ -47,6 +46,7 @@
"observability",
"optimize",
"package_registry",
"personal_productivity",
"pipeline_authoring",
"pipeline_execution",
"pipeline_security",

View File

@ -0,0 +1,9 @@
---
migration_job_name: BackfillPartitionIdCiDailyBuildGroupReportResult
description: Fixes incorrect values for ci_daily_build_group_report_results being in the wrong partition
feature_category: ci_scaling
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/153236
milestone: '17.1'
queued_migration_version: 20240516123512
finalize_after: '2024-06-01'
finalized_by: # version of the migration that finalized this BBM

View File

@ -0,0 +1,9 @@
# frozen_string_literal: true
class AddPartitionIdToCiDailyBuildGroupReportResult < Gitlab::Database::Migration[2.2]
milestone '17.1'
def change
add_column(:ci_daily_build_group_report_results, :partition_id, :bigint, default: 100, null: false)
end
end

View File

@ -0,0 +1,26 @@
# frozen_string_literal: true
class QueueBackfillPartitionIdCiDailyBuildGroupReportResult < Gitlab::Database::Migration[2.2]
milestone '17.1'
restrict_gitlab_migration gitlab_schema: :gitlab_ci
MIGRATION = 'BackfillPartitionIdCiDailyBuildGroupReportResult'
DELAY_INTERVAL = 2.minutes
BATCH_SIZE = 5000
SUB_BATCH_SIZE = 200
def up
queue_batched_background_migration(
MIGRATION,
:ci_daily_build_group_report_results,
:id,
job_interval: DELAY_INTERVAL,
batch_size: BATCH_SIZE,
sub_batch_size: SUB_BATCH_SIZE
)
end
def down
delete_batched_background_migration(MIGRATION, :ci_daily_build_group_report_results, :id, [])
end
end

View File

@ -0,0 +1 @@
15c4257ca5b38f5db83f0660ace238a71a6dae0ed4f136ba0874ae3bd613c4ca

View File

@ -0,0 +1 @@
3f0a6af062f151c3e0744a0babec4a1a9742d1a3a1a078c9596243b59bdd6419

View File

@ -6531,7 +6531,8 @@ CREATE TABLE ci_daily_build_group_report_results (
group_name text NOT NULL,
data jsonb NOT NULL,
default_branch boolean DEFAULT false NOT NULL,
group_id bigint
group_id bigint,
partition_id bigint DEFAULT 100 NOT NULL
);
CREATE SEQUENCE ci_daily_build_group_report_results_id_seq

View File

@ -8268,7 +8268,7 @@ Input type: `SecurityTrainingUpdateInput`
### `Mutation.setContainerScanningForRegistry`
Enable/disable Container Scanning on Container Registry for the given project or group.
Enable/disable Container Scanning on Container Registry for the given project.
Input type: `SetContainerScanningForRegistryInput`
@ -8278,7 +8278,7 @@ Input type: `SetContainerScanningForRegistryInput`
| ---- | ---- | ----------- |
| <a id="mutationsetcontainerscanningforregistryclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationsetcontainerscanningforregistryenable"></a>`enable` | [`Boolean!`](#boolean) | Desired status for Container Scanning on Container Registry feature. |
| <a id="mutationsetcontainerscanningforregistrynamespacepath"></a>`namespacePath` | [`ID!`](#id) | Full path of the namespace (project or group). |
| <a id="mutationsetcontainerscanningforregistrynamespacepath"></a>`namespacePath` | [`ID!`](#id) | Full path of the namespace (project). |
#### Fields

View File

@ -306,10 +306,10 @@ Resources:
### Answer questions about GitLab
In this example, the challenge is exploring the GitLab Duo Chat beta to solve problems.
In this example, the challenge is to use GitLab Duo Chat to solve problems.
<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
Watch the recording here: [GitLab Duo Coffee Chat: Solve problems with GitLab Duo Chat Beta Challenge](https://www.youtube.com/watch?v=Ypwx4lFnHP0)
Watch the recording here: [GitLab Duo Coffee Chat: Solve problems with GitLab Duo Chat Challenge](https://www.youtube.com/watch?v=Ypwx4lFnHP0)
<!-- Video published on 2024-02-02 -->
- You can use GitLab Duo Chat to explain CI/CD errors.

View File

@ -0,0 +1,18 @@
# frozen_string_literal: true
module Gitlab
module BackgroundMigration
class BackfillPartitionIdCiDailyBuildGroupReportResult < BatchedMigrationJob
operation_name :update_all
feature_category :ci_scaling
def perform
each_sub_batch do |sub_batch|
sub_batch
.where('ci_daily_build_group_report_results.last_pipeline_id = ci_pipelines.id')
.update_all('partition_id = ci_pipelines.partition_id FROM ci_pipelines')
end
end
end
end
end

View File

@ -86,29 +86,25 @@ module Gitlab
end
def pattern_matches?(paths, pattern_globs, context)
return true if (paths.size * pattern_globs.size) > MAX_PATTERN_COMPARISONS
if ::Feature.disabled?(:ci_rules_exists_pattern_matches_cache, context.project)
return legacy_pattern_matches?(paths, pattern_globs)
end
comparisons = 0
pattern_globs.any? do |glob|
Gitlab::SafeRequestStore.fetch("ci_rules_exists_pattern_matches_#{context.project&.id}_#{glob}") do
paths.any? do |path|
comparisons += 1
comparisons > MAX_PATTERN_COMPARISONS || pattern_match?(glob, path)
pattern_match?(glob, path)
end
end
end
end
def legacy_pattern_matches?(paths, pattern_globs)
comparisons = 0
pattern_globs.any? do |glob|
paths.any? do |path|
comparisons += 1
comparisons > MAX_PATTERN_COMPARISONS || pattern_match?(glob, path)
pattern_match?(glob, path)
end
end
end

View File

@ -26276,6 +26276,9 @@ msgstr ""
msgid "Identity verification exemption has been removed."
msgstr ""
msgid "Identity verification is required in order to run CI jobs"
msgstr ""
msgid "IdentityVerification|%d country found"
msgid_plural "IdentityVerification|%d countries found"
msgstr[0] ""

View File

@ -53,6 +53,7 @@ RSpec.describe 'Database schema', feature_category: :database do
chat_names: %w[chat_id team_id user_id],
chat_teams: %w[team_id],
ci_builds: %w[project_id runner_id user_id erased_by_id trigger_request_id partition_id auto_canceled_by_partition_id execution_config_id],
ci_daily_build_group_report_results: %w[partition_id],
ci_job_artifacts: %w[partition_id project_id job_id],
ci_namespace_monthly_usages: %w[namespace_id],
ci_pipeline_artifacts: %w[partition_id],

View File

@ -753,12 +753,14 @@ RSpec.describe 'Pipelines', :js, feature_category: :continuous_integration do
stub_ci_pipeline_to_return_yaml_file
end
subject(:run_pipeline) do
find_by_testid('run-pipeline-button', text: 'Run pipeline').click
wait_for_requests
end
it 'creates a new pipeline' do
expect do
find_by_testid('run-pipeline-button', text: 'Run pipeline').click
wait_for_requests
end
.to change { Ci::Pipeline.count }.by(1)
expect { run_pipeline }.to change { Ci::Pipeline.count }.by(1)
expect(Ci::Pipeline.last).to be_web
end

View File

@ -2,7 +2,7 @@
exports[`Branch divergence graph component renders ahead and behind count 1`] = `
<div
class="d-md-block d-none divergence-graph px-2"
class="divergence-graph gl-hidden md:gl-block px-2"
title="10 commits behind main, 10 commits ahead"
>
<graph-bar-stub
@ -23,7 +23,7 @@ exports[`Branch divergence graph component renders ahead and behind count 1`] =
exports[`Branch divergence graph component renders distance count 1`] = `
<div
class="d-md-block d-none divergence-graph px-2"
class="divergence-graph gl-hidden md:gl-block px-2"
title="More than 900 commits different with main"
>
<graph-bar-stub

View File

@ -0,0 +1,98 @@
import Vue from 'vue';
import VueApollo from 'vue-apollo';
import { GlForm } from '@gitlab/ui';
import MockAdapter from 'axios-mock-adapter';
import PipelineAccountVerificationAlert from 'ee_component/vue_shared/components/pipeline_account_verification_alert.vue';
import createMockApollo from 'helpers/mock_apollo_helper';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import waitForPromises from 'helpers/wait_for_promises';
import axios from '~/lib/utils/axios_utils';
import { HTTP_STATUS_BAD_REQUEST } from '~/lib/utils/http_status';
import PipelineNewForm from '~/ci/pipeline_new/components/pipeline_new_form.vue';
import ciConfigVariablesQuery from '~/ci/pipeline_new/graphql/queries/ci_config_variables.graphql';
import { resolvers } from '~/ci/pipeline_new/graphql/resolvers';
import {
mockIdentityVerificationRequiredError,
mockEmptyCiConfigVariablesResponse,
mockProjectId,
} from '../mock_data';
Vue.use(VueApollo);
const pipelinesPath = '/root/project/-/pipelines';
const pipelinesEditorPath = '/root/project/-/ci/editor';
const projectPath = '/root/project/-/pipelines/config_variables';
const defaultBranch = 'main';
describe('Pipeline New Form', () => {
let wrapper;
let mock;
let mockApollo;
let mockCiConfigVariables;
let dummySubmitEvent;
const findForm = () => wrapper.findComponent(GlForm);
const findErrorAlert = () => wrapper.findByTestId('run-pipeline-error-alert');
const findIdentityVerificationRequiredAlert = () =>
wrapper.findComponent(PipelineAccountVerificationAlert);
const createComponentWithApollo = ({ props = {} } = {}) => {
const handlers = [[ciConfigVariablesQuery, mockCiConfigVariables]];
mockApollo = createMockApollo(handlers, resolvers);
wrapper = shallowMountExtended(PipelineNewForm, {
apolloProvider: mockApollo,
provide: {
identityVerificationRequired: true,
identityVerificationPath: '/test',
},
propsData: {
projectId: mockProjectId,
pipelinesPath,
pipelinesEditorPath,
canViewPipelineEditor: true,
projectPath,
defaultBranch,
refParam: defaultBranch,
settingsLink: '',
maxWarnings: 25,
...props,
},
});
};
beforeEach(() => {
mock = new MockAdapter(axios);
mockCiConfigVariables = jest.fn();
dummySubmitEvent = {
preventDefault: jest.fn(),
};
});
afterEach(() => {
mock.restore();
});
describe('Form errors and warnings', () => {
describe('when the error response is identity verification required', () => {
beforeEach(async () => {
mockCiConfigVariables.mockResolvedValue(mockEmptyCiConfigVariablesResponse);
createComponentWithApollo();
mock
.onPost(pipelinesPath)
.reply(HTTP_STATUS_BAD_REQUEST, mockIdentityVerificationRequiredError);
findForm().vm.$emit('submit', dummySubmitEvent);
await waitForPromises();
});
it('shows identity verification required alert', () => {
expect(findErrorAlert().exists()).toBe(false);
expect(findIdentityVerificationRequiredAlert().exists()).toBe(true);
});
});
});
});

View File

@ -101,6 +101,10 @@ describe('Pipeline New Form', () => {
wrapper = shallowMountExtended(PipelineNewForm, {
apolloProvider: mockApollo,
provide: {
identityVerificationRequired: true,
identityVerificationPath: '/test',
},
propsData: {
projectId: mockProjectId,
pipelinesPath,

View File

@ -43,6 +43,12 @@ export const mockCreditCardValidationRequiredError = {
total_warnings: 0,
};
export const mockIdentityVerificationRequiredError = {
errors: ['Identity verification is required in order to run CI jobs'],
warnings: [],
total_warnings: 0,
};
export const mockBranchRefs = ['main', 'dev', 'release'];
export const mockTagRefs = ['1.0.0', '1.1.0', '1.2.0'];

View File

@ -6,7 +6,7 @@ const SSL_TOGGLE_CLASS = 'js-enable-ssl-gl-toggle';
const SSL_TOGGLE_INPUT_CLASS = 'js-project-feature-toggle-input';
const SHOW_IF_AUTO_SSL_CLASS = 'js-shown-if-auto-ssl';
const SHOW_UNLESS_AUTO_SSL_CLASS = 'js-shown-unless-auto-ssl';
const D_NONE_CLASS = '!gl-hidden';
const HIDDEN_CLASS = '!gl-hidden';
describe('Page domains form', () => {
let toggle;
@ -51,8 +51,8 @@ describe('Page domains form', () => {
});
it('sets the correct classes', () => {
expect(Array.from(findIfAutoSsl().classList)).not.toContain(D_NONE_CLASS);
expect(Array.from(findUnlessAutoSsl().classList)).toContain(D_NONE_CLASS);
expect(Array.from(findIfAutoSsl().classList)).not.toContain(HIDDEN_CLASS);
expect(Array.from(findUnlessAutoSsl().classList)).toContain(HIDDEN_CLASS);
});
it('sets the correct disabled value', () => {
@ -72,8 +72,8 @@ describe('Page domains form', () => {
});
it('sets the correct classes', () => {
expect(Array.from(findIfAutoSsl().classList)).toContain(D_NONE_CLASS);
expect(Array.from(findUnlessAutoSsl().classList)).not.toContain(D_NONE_CLASS);
expect(Array.from(findIfAutoSsl().classList)).toContain(HIDDEN_CLASS);
expect(Array.from(findUnlessAutoSsl().classList)).not.toContain(HIDDEN_CLASS);
});
it('sets the correct disabled value', () => {

View File

@ -34,7 +34,7 @@ exports[`Repository table row component renders a symlink table row 1`] = `
/>
</td>
<td
class="cursor-default d-none d-sm-table-cell tree-commit"
class="cursor-default gl-hidden sm:gl-table-cell tree-commit"
>
<gl-link-stub
class="gl-text-gray-600 str-truncated-100 tree-commit-link"
@ -90,7 +90,7 @@ exports[`Repository table row component renders table row 1`] = `
/>
</td>
<td
class="cursor-default d-none d-sm-table-cell tree-commit"
class="cursor-default gl-hidden sm:gl-table-cell tree-commit"
>
<gl-link-stub
class="gl-text-gray-600 str-truncated-100 tree-commit-link"
@ -146,7 +146,7 @@ exports[`Repository table row component renders table row for path with special
/>
</td>
<td
class="cursor-default d-none d-sm-table-cell tree-commit"
class="cursor-default gl-hidden sm:gl-table-cell tree-commit"
>
<gl-link-stub
class="gl-text-gray-600 str-truncated-100 tree-commit-link"

View File

@ -11,7 +11,7 @@ describe('~/snippet/collapsible_input', () => {
setHTMLFixture(`
<form>
<div class="js-collapsible-input js-title">
<div class="js-collapsed d-none">
<div class="js-collapsed !gl-hidden">
<input type="text" />
</div>
<div class="js-expanded">
@ -22,7 +22,7 @@ describe('~/snippet/collapsible_input', () => {
<div class="js-collapsed">
<input type="text" />
</div>
<div class="js-expanded d-none">
<div class="js-expanded !gl-hidden">
<textarea></textarea>
</div>
</div>
@ -49,8 +49,8 @@ describe('~/snippet/collapsible_input', () => {
const findExpandedInput = (el) => findInput(findExpanded(el));
const focusIn = (target) => target.dispatchEvent(new Event('focusin', { bubbles: true }));
const expectIsCollapsed = (el, isCollapsed) => {
expect(findCollapsed(el).classList.contains('d-none')).toEqual(!isCollapsed);
expect(findExpanded(el).classList.contains('d-none')).toEqual(isCollapsed);
expect(findCollapsed(el).classList.contains('!gl-hidden')).toEqual(!isCollapsed);
expect(findExpanded(el).classList.contains('!gl-hidden')).toEqual(isCollapsed);
};
describe('when collapsed', () => {

View File

@ -13,7 +13,7 @@ exports[`Snippet Description Edit component rendering matches the snapshot 1`] =
class="js-collapsible-input"
>
<div
class="d-none js-collapsed"
class="!gl-hidden js-collapsed"
>
<gl-form-textarea-stub
class="form-control"

View File

@ -23,7 +23,7 @@ describe('Snippet Description Edit component', () => {
}
function isHidden(sel) {
return wrapper.find(sel).classes('d-none');
return wrapper.find(sel).classes('!gl-hidden');
}
beforeEach(() => {
@ -36,8 +36,8 @@ describe('Snippet Description Edit component', () => {
});
it('renders the field expanded when description exists', () => {
expect(wrapper.find('.js-collapsed').classes('d-none')).toBe(true);
expect(wrapper.find('.js-expanded').classes('d-none')).toBe(false);
expect(wrapper.find('.js-collapsed').classes('!gl-hidden')).toBe(true);
expect(wrapper.find('.js-expanded').classes('!gl-hidden')).toBe(false);
expect(isHidden('.js-collapsed')).toBe(true);
expect(isHidden('.js-expanded')).toBe(false);

View File

@ -5,6 +5,8 @@ require 'spec_helper'
RSpec.describe Ci::PipelinesHelper, feature_category: :continuous_integration do
include Devise::Test::ControllerHelpers
let_it_be(:project) { create(:project) }
describe 'has_gitlab_ci?' do
using RSpec::Parameterized::TableSyntax
@ -25,8 +27,6 @@ RSpec.describe Ci::PipelinesHelper, feature_category: :continuous_integration do
end
describe '#pipelines_list_data' do
let_it_be(:project) { create(:project) }
subject(:data) { helper.pipelines_list_data(project, 'list_url') }
before do
@ -111,4 +111,25 @@ RSpec.describe Ci::PipelinesHelper, feature_category: :continuous_integration do
it { expect(subject).to eq(result) }
end
end
describe '#new_pipeline_data' do
subject(:data) { helper.new_pipeline_data(project) }
it 'has the expected keys' do
expect(subject.keys).to include(
:project_id,
:pipelines_path,
:default_branch,
:pipelines_editor_path,
:can_view_pipeline_editor,
:ref_param,
:var_param,
:file_param,
:project_path,
:project_refs_endpoint,
:settings_link,
:max_warnings
)
end
end
end

View File

@ -0,0 +1,87 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Gitlab::BackgroundMigration::BackfillPartitionIdCiDailyBuildGroupReportResult, feature_category: :ci_scaling do
let(:ci_pipelines_table) { table(:ci_pipelines, database: :ci) }
let(:ci_daily_build_group_report_results_table) { table(:ci_daily_build_group_report_results, database: :ci) }
let!(:pipeline_1) { ci_pipelines_table.create!(id: 1, partition_id: 100) }
let!(:pipeline_2) { ci_pipelines_table.create!(id: 2, partition_id: 101) }
let!(:pipeline_3) { ci_pipelines_table.create!(id: 3, partition_id: 101) }
let!(:ci_daily_build_group_report_results_100) do
ci_daily_build_group_report_results_table.create!(
date: Date.yesterday,
project_id: 1,
ref_path: 'master',
group_name: 'rspec',
data: { 'coverage' => 77.0 },
default_branch: true,
last_pipeline_id: pipeline_1.id,
partition_id: pipeline_1.partition_id
)
end
let!(:ci_daily_build_group_report_results_101) do
ci_daily_build_group_report_results_table.create!(
date: Date.today,
project_id: 1,
ref_path: 'master',
group_name: 'rspec',
data: { 'coverage' => 77.0 },
default_branch: true,
last_pipeline_id: pipeline_2.id,
partition_id: pipeline_2.partition_id
)
end
let!(:invalid_ci_daily_build_group_report_results) do
ci_daily_build_group_report_results_table.create!(
date: Date.tomorrow,
project_id: 1,
ref_path: 'master',
group_name: 'rspec',
data: { 'coverage' => 77.0 },
default_branch: true,
last_pipeline_id: pipeline_3.id,
partition_id: pipeline_1.partition_id
)
end
let(:migration_attrs) do
{
start_id: ci_daily_build_group_report_results_table.minimum(:id),
end_id: ci_daily_build_group_report_results_table.maximum(:id),
batch_table: :ci_daily_build_group_report_results,
batch_column: :id,
sub_batch_size: 1,
pause_ms: 0,
connection: Ci::ApplicationRecord.connection
}
end
let!(:migration) { described_class.new(**migration_attrs) }
describe '#perform' do
context 'when second partition does not exist' do
before do
pipeline_3.update!(partition_id: 100)
end
it 'does not execute the migration' do
expect { migration.perform }
.not_to change { invalid_ci_daily_build_group_report_results.reload.partition_id }
end
end
context 'when second partition exists' do
it 'fixes invalid records in the wrong the partition' do
expect { migration.perform }
.to not_change { ci_daily_build_group_report_results_100.reload.partition_id }
.and not_change { ci_daily_build_group_report_results_101.reload.partition_id }
.and change { invalid_ci_daily_build_group_report_results.reload.partition_id }
.from(100)
.to(101)
end
end
end
end

View File

@ -71,7 +71,7 @@ RSpec.describe Gitlab::Ci::Build::Rules::Rule::Clause::Exists, feature_category:
before do
stub_const('Gitlab::Ci::Build::Rules::Rule::Clause::Exists::MAX_PATTERN_COMPARISONS', 2)
expect(File).to receive(:fnmatch?).twice.and_call_original
expect(File).not_to receive(:fnmatch?)
end
it { is_expected.to be_truthy }

View File

@ -740,7 +740,7 @@ RSpec.describe Notify, feature_category: :code_review_workflow do
describe 'that have new commits on top of more than two existing ones' do
let(:existing_commits) do
[merge_request.commits.first] + [double(:commit)] * 3 + [merge_request.commits.second]
[merge_request.commits.first] + ([double(:commit)] * 3) + [merge_request.commits.second]
end
subject do

View File

@ -0,0 +1,56 @@
# frozen_string_literal: true
require 'spec_helper'
require_migration!
RSpec.describe QueueBackfillPartitionIdCiDailyBuildGroupReportResult, migration: :gitlab_ci, feature_category: :ci_scaling do
let!(:batched_migrations) { table(:batched_background_migrations) }
let!(:migration) { described_class::MIGRATION }
describe '#up' do
context 'with migration present' do
let!(:ci_backfill_partition_id_ci_daily_build_group_report_results_migration) do
batched_migrations.create!(
job_class_name: 'QueueBackfillPartitionIdCiDailyBuildGroupReportResult',
table_name: :ci_daily_build_group_report_results,
column_name: :id,
job_arguments: [],
interval: 2.minutes,
min_value: 1,
max_value: 2,
batch_size: 1000,
sub_batch_size: 100,
gitlab_schema: :gitlab_ci,
status: 3 # finished
)
end
context 'when migration finished successfully' do
it 'does not raise exception' do
expect { migrate! }.not_to raise_error
end
it 'schedules background jobs for each batch of ci_daily_build_group_report_results' do
migrate!
expect(migration).to have_scheduled_batched_migration(
gitlab_schema: :gitlab_ci,
table_name: :ci_daily_build_group_report_results,
column_name: :id,
batch_size: described_class::BATCH_SIZE,
sub_batch_size: described_class::SUB_BATCH_SIZE
)
end
end
end
end
describe '#down' do
it 'deletes all batched migration records' do
migrate!
schema_migrate_down!
expect(migration).not_to have_scheduled_batched_migration
end
end
end

View File

@ -125,11 +125,11 @@ RSpec.describe Appearance do
context 'valid pwa attributes' do
where(:attribute, :value) do
:pwa_name | nil
:pwa_name | "G" * 255
:pwa_name | ("G" * 255)
:pwa_short_name | nil
:pwa_short_name | "S" * 255
:pwa_short_name | ("S" * 255)
:pwa_description | nil
:pwa_description | "T" * 2048
:pwa_description | ("T" * 2048)
end
with_them do
@ -139,9 +139,9 @@ RSpec.describe Appearance do
context 'invalid pwa attributes' do
where(:attribute, :value, :message) do
:pwa_name | "G" * 256 | 'is too long (maximum is 255 characters)'
:pwa_short_name | "S" * 256 | 'is too long (maximum is 255 characters)'
:pwa_description | "T" * 2049 | 'is too long (maximum is 2048 characters)'
:pwa_name | ("G" * 256) | 'is too long (maximum is 255 characters)'
:pwa_short_name | ("S" * 256) | 'is too long (maximum is 255 characters)'
:pwa_description | ("T" * 2049) | 'is too long (maximum is 2048 characters)'
end
with_them do

View File

@ -2298,13 +2298,13 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration, factory_def
'1-foo' | '1-foo'
'fix/1-foo' | 'fix-1-foo'
'fix-1-foo' | 'fix-1-foo'
'a' * 63 | 'a' * 63
'a' * 64 | 'a' * 63
'FOO' | 'foo'
'-' + 'a' * 61 + '-' | 'a' * 61
'-' + 'a' * 62 + '-' | 'a' * 62
'-' + 'a' * 63 + '-' | 'a' * 62
'a' * 62 + ' ' | 'a' * 62
('a' * 63) | ('a' * 63)
('a' * 64) | ('a' * 63)
'FOO' | 'foo'
('-' + ('a' * 61) + '-') | ('a' * 61)
('-' + ('a' * 62) + '-') | ('a' * 62)
('-' + ('a' * 63) + '-') | ('a' * 62)
(('a' * 62) + ' ') | ('a' * 62)
end
with_them do

View File

@ -181,4 +181,19 @@ RSpec.describe Ci::DailyBuildGroupReportResult, feature_category: :continuous_in
let!(:parent) { model.project }
end
describe 'partitioning', :ci_partitionable do
include Ci::PartitioningHelpers
let(:pipeline) { create(:ci_pipeline) }
let(:daily_build_group_report_result) { create(:ci_daily_build_group_report_result, last_pipeline: pipeline) }
before do
stub_current_partition_id
end
it 'assigns the same partition id as the one that pipeline has' do
expect(daily_build_group_report_result.partition_id).to eq(ci_testing_partition_id)
end
end
end

View File

@ -335,7 +335,7 @@ RSpec.describe Commit do
'1234567' | true
'123456' | false
'1' | false
'0' * 40 | true
('0' * 40) | true
'c1acaa58bbcbc3eafe538cb8274ba387047b69f8' | true
'H1acaa58bbcbc3eafe538cb8274ba387047b69f8' | false
nil | false

View File

@ -209,7 +209,7 @@ RSpec.describe PgFullTextSearchable, feature_category: :global_search do
end
it 'strips words containing @ with length >= 500' do
model = model_class.create!(project: project, namespace: project.project_namespace, title: 'title', description: 'description ' + '@user1' * 100)
model = model_class.create!(project: project, namespace: project.project_namespace, title: 'title', description: 'description ' + ('@user1' * 100))
model.update_search_data!
expect(model.search_data.search_vector).to match(/'titl':1A/)
@ -218,8 +218,8 @@ RSpec.describe PgFullTextSearchable, feature_category: :global_search do
end
context 'with long words' do
let(:long_word) { 'long/sequence' * 5 + ' ' }
let(:model) { model_class.create!(project: project, namespace: project.project_namespace, title: 'title', description: 'description ' + long_word * 51) }
let(:long_word) { ('long/sequence' * 5) + ' ' }
let(:model) { model_class.create!(project: project, namespace: project.project_namespace, title: 'title', description: 'description ' + (long_word * 51)) }
it 'strips words with length >= 50 when there are more than 50 instances' do
model.update_search_data!
@ -231,7 +231,7 @@ RSpec.describe PgFullTextSearchable, feature_category: :global_search do
end
it 'does not strip long words when there are less than 51 instances' do
model.update!(description: 'description ' + long_word * 50)
model.update!(description: 'description ' + (long_word * 50))
model.update_search_data!
expect(model.search_data.search_vector).to match(/'titl':1A/)

View File

@ -25,7 +25,7 @@ RSpec.describe CustomEmoji do
end
it 'disallows very long invalid emoji name without regular expression backtracking issues' do
new_emoji = build(:custom_emoji, name: 'a' * 10000 + '!', group: group)
new_emoji = build(:custom_emoji, name: ('a' * 10000) + '!', group: group)
Timeout.timeout(1) do
expect(new_emoji).not_to be_valid

View File

@ -1848,7 +1848,7 @@ RSpec.describe Environment, :use_clean_rails_memory_store_caching, feature_categ
where(:value, :expected_result) do
'2 days' | 2.days.to_i
'1 week' | 1.week.to_i
'2h20min' | 2.hours.to_i + 20.minutes.to_i
'2h20min' | (2.hours.to_i + 20.minutes.to_i)
'abcdef' | ChronicDuration::DurationParseError
'' | nil
nil | nil

View File

@ -15,7 +15,7 @@ RSpec.describe GrafanaIntegration do
unsafe_url = %{https://replaceme.com/'><script>alert(document.cookie)</script>}
non_ascii_url = 'http://gitlab.com/api/0/projects/project1/something€'
blank_url = ''
excessively_long_url = 'https://grafan' + 'a' * 1024 + '.com'
excessively_long_url = 'https://grafan' + ('a' * 1024) + '.com'
is_expected.not_to allow_values(
unsafe_url,

View File

@ -26,7 +26,7 @@ RSpec.describe Integrations::ChatMessage::PushMessage do
args[:commits] = [
{ message: 'message1', title: 'message1', url: 'http://url1.com', id: 'abcdefghijkl', author: { name: 'author1' } },
{
message: 'message2' + ' w' * 100 + "\nsecondline",
message: 'message2' + (' w' * 100) + "\nsecondline",
title: 'message2 w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w ...',
url: 'http://url2.com',
id: '123456789012',

View File

@ -944,7 +944,7 @@ RSpec.describe MergeRequestDiff, feature_category: :code_review_workflow do
context 'with diffs that contain a null byte' do
let(:filename) { 'test-null.txt' }
let(:content) { "a" * 10000 + "\x00" }
let(:content) { ("a" * 10000) + "\x00" }
let(:project) { create(:project, :repository) }
let(:branch) { 'null-data' }
let(:target_branch) { 'master' }

View File

@ -55,12 +55,12 @@ RSpec.describe Packages::PackageFile, type: :model, feature_category: :package_r
context 'file_sha256' do
where(:sha256_value, :expected_success) do
'a' * 64 | true
nil | true
'a' * 63 | false
'a' * 65 | false
'a' * 63 + '%' | false
'' | false
('a' * 64) | true
nil | true
('a' * 63) | false
('a' * 65) | false
(('a' * 63) + '%') | false
'' | false
end
with_them do

View File

@ -81,7 +81,7 @@ RSpec.describe PrometheusAlert do
it 'disallow invalid urls' do
unsafe_url = %{https://replaceme.com/'><script>alert(document.cookie)</script>}
non_ascii_url = 'http://gitlab.com/user/project1/wiki/something€'
excessively_long_url = 'https://gitla' + 'b' * 1024 + '.com'
excessively_long_url = 'https://gitla' + ('b' * 1024) + '.com'
is_expected.not_to allow_values(
unsafe_url,

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