Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
30df34b9ce
commit
7cc896c6f7
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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()"
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -0,0 +1 @@
|
|||
d807356d7c3156e0f6a74a80111abab84883828292ff815d9f9d426aa3986c8a
|
||||
|
|
@ -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
|
||||
|
|
@ -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 ""
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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', () => {
|
||||
|
|
|
|||
|
|
@ -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 }) => {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
Loading…
Reference in New Issue