Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2024-08-29 21:12:14 +00:00
parent 30df34b9ce
commit 7cc896c6f7
14 changed files with 261 additions and 46 deletions

View File

@ -48,8 +48,6 @@ compile-production-assets:
- .compile-assets-base
- .production
- .frontend:rules:compile-production-assets
tags:
- saas-linux-large-amd64
artifacts:
name: webpack-report
expire_in: 31d

View File

@ -4,7 +4,6 @@ import { scrollToElement, backOff } from '~/lib/utils/common_utils';
import { numberToHumanSize } from '~/lib/utils/number_utils';
import { s__, sprintf } from '~/locale';
import { compactJobLog } from '~/ci/job_details/utils';
import HelpPopover from '~/vue_shared/components/help_popover.vue';
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
export default {
@ -13,12 +12,8 @@ export default {
scrollToTopButtonLabel: s__('Job|Scroll to top'),
scrollToNextFailureButtonLabel: s__('Job|Scroll to next failure'),
showRawButtonLabel: s__('Job|Show complete raw'),
searchPlaceholder: s__('Job|Search job log'),
searchPlaceholder: s__('Job|Search visible log output'),
noResults: s__('Job|No search results found'),
searchPopoverTitle: s__('Job|Job log search'),
searchPopoverDescription: s__(
'Job|Search for substrings in your job log output. Currently search is only supported for the visible job log output, not for any log output that is truncated due to size.',
),
logLineNumberNotFound: s__('Job|We could not find this element'),
enterFullscreen: s__('Job|Show full screen'),
exitFullScreen: s__('Job|Exit full screen'),
@ -29,7 +24,6 @@ export default {
GlButton,
GlSearchBoxByClick,
GlSprintf,
HelpPopover,
},
directives: {
GlTooltip: GlTooltipDirective,
@ -200,7 +194,7 @@ export default {
v-if="rawPath && isComplete && logViewerPath"
:message="
s__(
'Job|%{rawLinkStart}View raw%{rawLinkEnd} or %{fullLinkStart}view full log%{fullLinkEnd}.',
'Job|%{rawLinkStart}View raw%{rawLinkEnd} or %{fullLinkStart}full log%{fullLinkEnd}.',
)
"
>
@ -221,21 +215,12 @@ export default {
<gl-search-box-by-click
v-model="searchTerm"
class="gl-mr-3 gl-flex-nowrap"
class="gl-mr-3 gl-w-30 gl-flex-nowrap"
:placeholder="$options.i18n.searchPlaceholder"
data-testid="job-log-search-box"
@clear="$emit('searchResults', [])"
@submit="searchJobLog"
/>
<help-popover class="gl-mr-3">
<template #title>{{ $options.i18n.searchPopoverTitle }}</template>
<p class="gl-mb-0">
{{ $options.i18n.searchPopoverDescription }}
</p>
</help-popover>
<!-- links -->
<gl-button
v-if="rawPath"

View File

@ -73,7 +73,7 @@ export default {
},
data() {
return {
retryBtnDisabled: false,
retryingJob: false,
cancelBtnDisabled: false,
playManualBtnDisabled: false,
unscheduleBtnDisabled: false,
@ -168,22 +168,23 @@ export default {
this.postJobAction(this.$options.jobCancel, cancelJobMutation);
},
async retryJob() {
if (this.job.detailedStatus.action.confirmationMessage !== null) {
if (this.job?.detailedStatus?.action?.confirmationMessage) {
const confirmed = await confirmJobConfirmationMessage(
this.job.name,
this.job.detailedStatus.action.confirmationMessage,
);
if (!confirmed) {
return;
}
}
this.retryBtnDisabled = true;
this.retryingJob = true;
this.postJobAction(this.$options.jobRetry, retryJobMutation, true);
},
async playJob() {
if (this.job.detailedStatus.action.confirmationMessage !== null) {
if (this.job?.detailedStatus?.action?.confirmationMessage) {
const confirmed = await confirmJobConfirmationMessage(
this.job.name,
this.job.detailedStatus.action.confirmationMessage,
@ -270,8 +271,7 @@ export default {
:title="retryButtonTitle"
:aria-label="retryButtonTitle"
:method="currentJobMethod"
:disabled="retryBtnDisabled"
:loading="retryBtnDisabled"
:loading="retryingJob"
data-testid="retry"
@click="retryJob()"
/>

View File

@ -5,12 +5,19 @@
- if closed_tab_selected
- title = s_('Milestones|There are no closed milestones')
- if params[:search_title]
= render Pajamas::EmptyStateComponent.new(svg_path: 'illustrations/empty-state/empty-search-md.svg',
title: _("No results found")) do |c|
= render Pajamas::EmptyStateComponent.new(svg_path: 'illustrations/empty-state/empty-milestone-md.svg',
title: title) do |c|
- c.with_description do
= _("Edit your criteria and try again.")
- c.with_description do
= s_('Milestones|Create a milestone to better track your issues and merge requests. %{learn_more_link}').html_safe % { learn_more_link: learn_more_link }
- if yield.present?
.gl-flex.gl-flex-wrap.gl-mt-5.gl-justify-center
= yield
- else
= render Pajamas::EmptyStateComponent.new(svg_path: 'illustrations/empty-state/empty-milestone-md.svg',
title: title) do |c|
- c.with_description do
= s_('Milestones|Create a milestone to better track your issues and merge requests. %{learn_more_link}').html_safe % { learn_more_link: learn_more_link }
- if yield.present?
.gl-flex.gl-flex-wrap.gl-mt-5.gl-justify-center
= yield

View File

@ -0,0 +1,11 @@
---
migration_job_name: BackfillProjectIdToSecurityScans
description: Backfills `ci_builds.project_id` to be used as the sharding key. The association
with `ci_builds` initially did not have a foreign key, so we need to check for rows that do
not have a corresponding `ci_builds` record and delete them.
feature_category: vulnerability_management
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/164110
milestone: '17.4'
queued_migration_version: 20240827165545
finalize_after: '2024-10-12' # 17.6 milestone start
finalized_by: # version of the migration that finalized this BBM

View File

@ -0,0 +1,27 @@
# frozen_string_literal: true
class QueueBackfillProjectIdToSecurityScans < Gitlab::Database::Migration[2.2]
milestone '17.4'
restrict_gitlab_migration gitlab_schema: :gitlab_sec
MIGRATION = "BackfillProjectIdToSecurityScans"
DELAY_INTERVAL = 2.minutes
BATCH_SIZE = 10_000
SUB_BATCH_SIZE = 100
def up
queue_batched_background_migration(
MIGRATION,
:security_scans,
:id,
job_interval: DELAY_INTERVAL,
batch_size: BATCH_SIZE,
sub_batch_size: SUB_BATCH_SIZE
)
end
def down
delete_batched_background_migration(MIGRATION, :security_scans, :id, [])
end
end

View File

@ -0,0 +1 @@
d807356d7c3156e0f6a74a80111abab84883828292ff815d9f9d426aa3986c8a

View File

@ -0,0 +1,61 @@
# frozen_string_literal: true
module Gitlab
module BackgroundMigration
class BackfillProjectIdToSecurityScans < BatchedMigrationJob
feature_category :vulnerability_management
operation_name :backfill_project_id_to_security_scans
class Scan < ::Gitlab::Database::SecApplicationRecord
self.table_name = 'security_scans'
end
class Build < ::Ci::ApplicationRecord
self.table_name = 'p_ci_builds'
self.inheritance_column = :_type_disabled
end
def perform
each_sub_batch do |sub_batch|
scans = sub_batch
builds = Build.id_in(scans.map(&:build_id))
missing_build_ids = []
tuples_to_update = scans.filter_map do |scan|
build = builds.find { |build| build.id == scan.build_id }
if build.blank?
missing_build_ids.push(scan.id)
next
end
[scan.id, build.project_id] if build.project_id != scan.project_id
end
Scan.id_in(missing_build_ids).delete_all if missing_build_ids.present?
bulk_update!(tuples_to_update)
end
end
def bulk_update!(tuples)
return if tuples.blank?
values_sql = Arel::Nodes::ValuesList.new(tuples).to_sql
sql = <<~SQL
UPDATE
security_scans
SET
project_id = tuples.project_id,
updated_at = NOW()
FROM
(#{values_sql}) AS tuples(scan_id, project_id)
WHERE security_scans.id = tuples.scan_id;
SQL
Scan.connection.execute(sql)
end
end
end
end

View File

@ -30705,7 +30705,7 @@ msgstr ""
msgid "Job|%{boldStart}Pipeline%{boldEnd} %{id} for %{mrId} with %{source} into %{target}"
msgstr ""
msgid "Job|%{rawLinkStart}View raw%{rawLinkEnd} or %{fullLinkStart}view full log%{fullLinkEnd}."
msgid "Job|%{rawLinkStart}View raw%{rawLinkEnd} or %{fullLinkStart}full log%{fullLinkEnd}."
msgstr ""
msgid "Job|%{searchLength} results found for %{searchTerm}"
@ -30768,9 +30768,6 @@ msgstr ""
msgid "Job|Job has been erased by %{userLink}"
msgstr ""
msgid "Job|Job log search"
msgstr ""
msgid "Job|Keep"
msgstr ""
@ -30819,10 +30816,7 @@ msgstr ""
msgid "Job|Scroll to top"
msgstr ""
msgid "Job|Search for substrings in your job log output. Currently search is only supported for the visible job log output, not for any log output that is truncated due to size."
msgstr ""
msgid "Job|Search job log"
msgid "Job|Search visible log output"
msgstr ""
msgid "Job|Show complete raw"
@ -60058,6 +60052,9 @@ msgstr ""
msgid "Vulnerability|Identifiers"
msgstr ""
msgid "Vulnerability|Image and tag"
msgstr ""
msgid "Vulnerability|Image:"
msgstr ""

View File

@ -1,4 +1,4 @@
ARG GDK_SHA=9d28846535331fae67117bd94aa541e51a154b0d
ARG GDK_SHA=2b0bf1f634ea2e58586470ba114b507d8f51cb16
# Use tag prefix when running on 'stable' branch to make sure 'protected' image is used which is not deleted by registry cleanup
ARG GDK_BASE_TAG_PREFIX

View File

@ -1,7 +1,6 @@
import { GlLink, GlSearchBoxByClick } from '@gitlab/ui';
import { mount } from '@vue/test-utils';
import JobLogTopBar from '~/ci/job_details/components/job_log_top_bar.vue';
import HelpPopover from '~/vue_shared/components/help_popover.vue';
import { backoffMockImplementation } from 'helpers/backoff_helper';
import * as commonUtils from '~/lib/utils/common_utils';
import { mockJobLog } from 'jest/ci/jobs_mock_data';
@ -56,7 +55,6 @@ describe('JobLogTopBar', () => {
const findScrollTop = () => wrapper.find('[data-testid="job-top-bar-scroll-top"]');
const findScrollBottom = () => wrapper.find('[data-testid="job-top-bar-scroll-bottom"]');
const findJobLogSearch = () => wrapper.findComponent(GlSearchBoxByClick);
const findSearchHelp = () => wrapper.findComponent(HelpPopover);
const findScrollFailure = () => wrapper.find('[data-testid="job-top-bar-scroll-to-failure"]');
const findShowFullScreenButton = () =>
wrapper.find('[data-testid="job-top-bar-enter-fullscreen"]');
@ -310,7 +308,6 @@ describe('JobLogTopBar', () => {
it('displays job log search', () => {
expect(findJobLogSearch().exists()).toBe(true);
expect(findSearchHelp().exists()).toBe(true);
});
it('emits search results', () => {

View File

@ -182,10 +182,21 @@ describe('Job actions cell', () => {
},
);
it('retry job button goes into loading state after click', async () => {
createComponent(retryableJob);
expect(findRetryButton().props('loading')).toBe(false);
findRetryButton().vm.$emit('click');
await waitForPromises();
expect(findRetryButton().props('loading')).toBe(true);
});
it.each`
button | action | jobType
${findPlayButton} | ${'play'} | ${playableJob}
${findRetryButton} | ${'retry'} | ${retryableJob}
${findCancelButton} | ${'cancel'} | ${cancelableJob}
${findUnscheduleButton} | ${'unschedule'} | ${scheduledJob}
`('disables the $action button after first request', async ({ button, jobType }) => {

View File

@ -0,0 +1,93 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Gitlab::BackgroundMigration::BackfillProjectIdToSecurityScans, feature_category: :vulnerability_management do
let(:security_scans) { table(:security_scans, database: :sec) }
let(:ci_builds) { partitioned_table(:p_ci_builds, database: :ci) }
let(:projects) { table(:projects) }
let(:namespaces) { table(:namespaces) }
let!(:ci_build) { create_ci_build('build-1') }
let(:args) do
min, max = security_scans.pick('MIN(id)', 'MAX(id)')
{
start_id: min,
end_id: max,
batch_table: 'security_scans',
batch_column: 'id',
sub_batch_size: 1,
pause_ms: 0,
connection: Gitlab::Database::SecApplicationRecord.connection
}
end
subject(:perform_migration) { described_class.new(**args).perform }
context 'when security_scan.build_id does not exist' do
let!(:scan) do
security_scans.create!(
build_id: non_existing_record_id,
scan_type: 1
)
end
it 'deletes the security_scan' do
expect { perform_migration }.to change { security_scans.count }.from(1).to(0)
end
end
context 'when security_scan is missing project_id' do
let!(:scan) do
security_scans.create!(
build_id: ci_build.id,
scan_type: 1
)
end
let!(:other_build) { create_ci_build('build-2') }
let!(:other_scan) do
security_scans.create!(
build_id: other_build.id,
scan_type: 1
)
end
it 'sets the project_id to build.project_id' do
expect { perform_migration }.to change { scan.reload.project_id }.from(nil).to(ci_build.project_id)
.and change { other_scan.reload.project_id }.from(nil).to(other_build.project_id)
end
it 'touches updated_at' do
expect { perform_migration }.to change { scan.reload.updated_at }
end
end
context 'when security_scan does not need to be changed' do
let!(:scan) do
security_scans.create!(
build_id: ci_build.id,
project_id: ci_build.project_id,
scan_type: 1
)
end
it 'does not update the scan' do
expect { perform_migration }.not_to change { scan.reload.updated_at }
end
end
def create_ci_build(name)
namespace = namespaces.create!(name: "group-#{name}", path: "group-#{name}")
project_namespace = namespaces.create!(name: "project-#{name}", path: "project-#{name}")
project = projects.create!(
namespace_id: namespace.id,
project_namespace_id: project_namespace.id,
name: "project-#{name}",
path: "project-#{name}"
)
ci_builds.create!(project_id: project.id, partition_id: 100, type: 'Ci::Build')
end
end

View File

@ -0,0 +1,27 @@
# frozen_string_literal: true
require 'spec_helper'
require_migration!
RSpec.describe QueueBackfillProjectIdToSecurityScans, feature_category: :vulnerability_management 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(
gitlab_schema: :gitlab_sec,
table_name: :security_scans,
column_name: :id,
interval: described_class::DELAY_INTERVAL,
batch_size: described_class::BATCH_SIZE,
sub_batch_size: described_class::SUB_BATCH_SIZE
)
}
end
end
end