Commit 5688dd2b authored by GitLab Bot's avatar GitLab Bot

Automatic merge of gitlab-org/gitlab master

parents 5013a6b2 f375c51b
......@@ -54,7 +54,7 @@ export default {
<ide-tree-list>
<template #header>
{{ __('Edit') }}
<div class="ide-tree-actions ml-auto d-flex">
<div class="ide-tree-actions ml-auto d-flex" data-testid="ide-root-actions">
<new-entry-button
:label="__('New file')"
:show-label="false"
......
---
title: Fix IDE issues with special characters
merge_request: 46398
author:
type: fixed
---
title: Show tar warning message when file/folder changed during backup instead of
failing whole backup operation
merge_request: 42197
author:
type: fixed
---
name: coverage_fuzzing_mr_widget
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/43545
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/257839
type: development
group: group::fuzz testing
default_enabled: false
......@@ -231,14 +231,8 @@ export default {
return this.enabledReports.dast;
},
hasCoverageFuzzingReports() {
/*
* Fixes bug https://gitlab.com/gitlab-org/gitlab/-/issues/255183
* For https://gitlab.com/gitlab-org/gitlab/-/issues/210343 change to:
* return this.enabledReports.coverageFuzzing;
*/
return (
gl?.mrWidgetData?.coverage_fuzzing_comparison_path && this.enabledReports.coverageFuzzing
);
// TODO: Remove feature flag in https://gitlab.com/gitlab-org/gitlab/-/issues/257839
return this.enabledReports.coverageFuzzing && this.glFeatures.coverageFuzzingMrWidget;
},
hasSastReports() {
return this.enabledReports.sast;
......
......@@ -88,7 +88,7 @@ export default {
},
coverageFuzzingPopover() {
return {
title: s__('ciReport|Coverage Fuzzing Title'),
title: s__('ciReport|Coverage Fuzzing'),
content: sprintf(
s__('ciReport|%{linkStartTag}Learn more about Coverage Fuzzing %{linkEndTag}'),
{
......
......@@ -11,6 +11,7 @@ module EE
before_action only: [:show] do
push_frontend_feature_flag(:anonymous_visual_review_feedback)
push_frontend_feature_flag(:missing_mr_security_scan_types, @project)
push_frontend_feature_flag(:coverage_fuzzing_mr_widget, @project)
end
before_action :whitelist_query_limiting_ee_merge, only: [:merge]
......
......@@ -20,3 +20,4 @@
window.gl.mrWidgetData.sast_comparison_path = '#{sast_reports_project_merge_request_path(@project, @merge_request) if @project.feature_available?(:sast)}'
window.gl.mrWidgetData.dast_comparison_path = '#{dast_reports_project_merge_request_path(@project, @merge_request) if @project.feature_available?(:dast)}'
window.gl.mrWidgetData.secret_scanning_comparison_path = '#{secret_detection_reports_project_merge_request_path(@project, @merge_request) if @project.feature_available?(:secret_detection)}'
window.gl.mrWidgetData.coverage_fuzzing_comparison_path = '#{coverage_fuzzing_reports_project_merge_request_path(@project, @merge_request) if @project.feature_available?(:coverage_fuzzing) && Feature.enabled?(:coverage_fuzzing_mr_widget, @project)}'
......@@ -5,7 +5,7 @@ module Gitlab
class Ingress
include Gitlab::Utils::StrongMemoize
# Canry Ingress Annotations https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/annotations/#canary
# Canary Ingress Annotations https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/annotations/#canary
ANNOTATION_KEY_CANARY = 'nginx.ingress.kubernetes.io/canary'
ANNOTATION_KEY_CANARY_WEIGHT = 'nginx.ingress.kubernetes.io/canary-weight'
......
......@@ -743,6 +743,14 @@ describe('ee merge request widget options', () => {
describe('Coverage Fuzzing', () => {
const COVERAGE_FUZZING_ENDPOINT = 'coverage_fuzzing_report';
const mountWithFeatureFlag = () =>
new Component({
propsData: { mrData: gl.mrWidgetData },
provide: {
glFeatures: { coverageFuzzingMrWidget: true },
},
}).$mount();
beforeEach(() => {
gl.mrWidgetData = {
...mockData,
......@@ -758,8 +766,7 @@ describe('ee merge request widget options', () => {
it('should render loading indicator', () => {
mock.onGet(COVERAGE_FUZZING_ENDPOINT).reply(200, coverageFuzzingDiffSuccessMock);
mock.onGet(VULNERABILITY_FEEDBACK_ENDPOINT).reply(200, []);
vm = mountComponent(Component, { mrData: gl.mrWidgetData });
vm = mountWithFeatureFlag();
expect(
findSecurityWidget()
......@@ -773,8 +780,7 @@ describe('ee merge request widget options', () => {
beforeEach(() => {
mock.onGet(COVERAGE_FUZZING_ENDPOINT).reply(200, coverageFuzzingDiffSuccessMock);
mock.onGet(VULNERABILITY_FEEDBACK_ENDPOINT).reply(200, []);
vm = mountComponent(Component, { mrData: gl.mrWidgetData });
vm = mountWithFeatureFlag();
});
it('should render provided data', done => {
......@@ -793,8 +799,7 @@ describe('ee merge request widget options', () => {
beforeEach(() => {
mock.onGet(COVERAGE_FUZZING_ENDPOINT).reply(500, {});
mock.onGet(VULNERABILITY_FEEDBACK_ENDPOINT).reply(500, {});
vm = mountComponent(Component, { mrData: gl.mrWidgetData });
vm = mountWithFeatureFlag();
});
it('should render error indicator', done => {
......
......@@ -63,7 +63,7 @@ describe('Grouped security reports app', () => {
const glModalDirective = jest.fn();
const createWrapper = (propsData, options) => {
const createWrapper = (propsData, options, provide) => {
wrapper = mount(GroupedSecurityReportsApp, {
propsData,
data() {
......@@ -80,6 +80,10 @@ describe('Grouped security reports app', () => {
},
},
store: appStore(),
provide: {
glFeatures: { coverageFuzzingMrWidget: true },
...provide,
},
});
};
......@@ -368,6 +372,37 @@ describe('Grouped security reports app', () => {
});
});
describe('coverage fuzzing reports', () => {
describe.each([true, false])(
'given coverage fuzzing comparison endpoint is /fuzzing and featureEnabled is %s',
shouldShowFuzzing => {
beforeEach(() => {
gl.mrWidgetData = gl.mrWidgetData || {};
gl.mrWidgetData.coverage_fuzzing_comparison_path = '/fuzzing';
createWrapper(
{
...props,
enabledReports: {
coverageFuzzing: true,
},
},
{},
{
glFeatures: { coverageFuzzingMrWidget: shouldShowFuzzing },
},
);
});
it(`${shouldShowFuzzing ? 'renders' : 'does not render'}`, () => {
expect(wrapper.find('[data-qa-selector="coverage_fuzzing_report"]').exists()).toBe(
shouldShowFuzzing,
);
});
},
);
});
describe('container scanning reports', () => {
beforeEach(() => {
gl.mrWidgetData = gl.mrWidgetData || {};
......@@ -580,40 +615,6 @@ describe('Grouped security reports app', () => {
});
});
describe('coverage fuzzing reports', () => {
/*
* Fixes bug https://gitlab.com/gitlab-org/gitlab/-/issues/255183
* For https://gitlab.com/gitlab-org/gitlab/-/issues/210343
* replace with updated tests
*/
describe.each`
endpoint | shouldShowFuzzing
${'/fuzzing'} | ${true}
${undefined} | ${false}
`(
'given coverage fuzzing comparision enpoint is $endpoint',
({ endpoint, shouldShowFuzzing }) => {
beforeEach(() => {
gl.mrWidgetData = gl.mrWidgetData || {};
gl.mrWidgetData.coverage_fuzzing_comparison_path = endpoint;
createWrapper({
...props,
enabledReports: {
coverageFuzzing: true,
},
});
});
it(`${shouldShowFuzzing ? 'renders' : 'does not render'} security row`, () => {
expect(wrapper.find('[data-qa-selector="coverage_fuzzing_report"]').exists()).toBe(
shouldShowFuzzing,
);
});
},
);
});
describe('Out of date report', () => {
const createComponent = (extraProp, done) => {
gl.mrWidgetData = gl.mrWidgetData || {};
......
......@@ -26,7 +26,7 @@ module Backup
FileUtils.rm_f(backup_tarball)
if ENV['STRATEGY'] == 'copy'
cmd = [%w(rsync -a), exclude_dirs(:rsync), %W(#{app_files_dir} #{Gitlab.config.backup.path})].flatten
cmd = [%w[rsync -a], exclude_dirs(:rsync), %W[#{app_files_dir} #{Gitlab.config.backup.path}]].flatten
output, status = Gitlab::Popen.popen(cmd)
unless status == 0
......@@ -34,19 +34,27 @@ module Backup
raise Backup::Error, 'Backup failed'
end
tar_cmd = [tar, exclude_dirs(:tar), %W(-C #{@backup_files_dir} -cf - .)].flatten
run_pipeline!([tar_cmd, gzip_cmd], out: [backup_tarball, 'w', 0600])
tar_cmd = [tar, exclude_dirs(:tar), %W[-C #{@backup_files_dir} -cf - .]].flatten
status_list, output = run_pipeline!([tar_cmd, gzip_cmd], out: [backup_tarball, 'w', 0600])
FileUtils.rm_rf(@backup_files_dir)
else
tar_cmd = [tar, exclude_dirs(:tar), %W(-C #{app_files_dir} -cf - .)].flatten
run_pipeline!([tar_cmd, gzip_cmd], out: [backup_tarball, 'w', 0600])
tar_cmd = [tar, exclude_dirs(:tar), %W[-C #{app_files_dir} -cf - .]].flatten
status_list, output = run_pipeline!([tar_cmd, gzip_cmd], out: [backup_tarball, 'w', 0600])
end
unless pipeline_succeeded?(tar_status: status_list[0], gzip_status: status_list[1], output: output)
raise Backup::Error, "Backup operation failed: #{output}"
end
end
def restore
backup_existing_files_dir
run_pipeline!([%w(gzip -cd), %W(#{tar} --unlink-first --recursive-unlink -C #{app_files_dir} -xf -)], in: backup_tarball)
cmd_list = [%w[gzip -cd], %W[#{tar} --unlink-first --recursive-unlink -C #{app_files_dir} -xf -]]
status_list, output = run_pipeline!(cmd_list, in: backup_tarball)
unless pipeline_succeeded?(gzip_status: status_list[0], tar_status: status_list[1], output: output)
raise Backup::Error, "Restore operation failed: #{output}"
end
end
def tar
......@@ -78,13 +86,44 @@ module Backup
def run_pipeline!(cmd_list, options = {})
err_r, err_w = IO.pipe
options[:err] = err_w
status = Open3.pipeline(*cmd_list, options)
status_list = Open3.pipeline(*cmd_list, options)
err_w.close
return if status.compact.all?(&:success?)
regex = /^g?tar: \.: Cannot mkdir: No such file or directory$/
error = err_r.read
raise Backup::Error, "Backup failed. #{error}" unless error =~ regex
[status_list, err_r.read]
end
def noncritical_warning?(warning)
noncritical_warnings = [
/^g?tar: \.: Cannot mkdir: No such file or directory$/
]
noncritical_warnings.map { |w| warning =~ w }.any?
end
def pipeline_succeeded?(tar_status:, gzip_status:, output:)
return false unless gzip_status&.success?
tar_status&.success? || tar_ignore_non_success?(tar_status.exitstatus, output)
end
def tar_ignore_non_success?(exitstatus, output)
# tar can exit with nonzero code:
# 1 - if some files changed (i.e. a CI job is currently writes to log)
# 2 - if it cannot create `.` directory (see issue https://gitlab.com/gitlab-org/gitlab/-/issues/22442)
# http://www.gnu.org/software/tar/manual/html_section/tar_19.html#Synopsis
# so check tar status 1 or stderr output against some non-critical warnings
if exitstatus == 1
$stdout.puts "Ignoring tar exit status 1 'Some files differ': #{output}"
return true
end
# allow tar to fail with other non-success status if output contain non-critical warning
if noncritical_warning?(output)
$stdout.puts "Ignoring non-success exit status #{exitstatus} due to output of non-critical warning(s): #{output}"
return true
end
false
end
def exclude_dirs(fmt)
......
......@@ -31139,9 +31139,6 @@ msgstr ""
msgid "ciReport|Coverage Fuzzing"
msgstr ""
msgid "ciReport|Coverage Fuzzing Title"
msgstr ""
msgid "ciReport|Coverage fuzzing"
msgstr ""
......
......@@ -105,7 +105,8 @@ describe('Monitoring router', () => {
path | currentDashboard
${'/panel/new'} | ${undefined}
${'/dashboard.yml/panel/new'} | ${'dashboard.yml'}
${'/config%2Fprometheus%2Fcommon_metrics.yml/panel/new'} | ${'config%2Fprometheus%2Fcommon_metrics.yml'}
${'/config/prometheus/common_metrics.yml/panel/new'} | ${'config/prometheus/common_metrics.yml'}
${'/config%2Fprometheus%2Fcommon_metrics.yml/panel/new'} | ${'config/prometheus/common_metrics.yml'}
`('"$path" renders page with dashboard "$currentDashboard"', ({ path, currentDashboard }) => {
const wrapper = createWrapper(BASE_PATH, path);
......
import { findAllByText, fireEvent, getByLabelText, screen } from '@testing-library/dom';
const isFileRowOpen = row => row.matches('.is-open');
const isFolderRowOpen = row => row.matches('.folder.is-open');
const getLeftSidebar = () => screen.getByTestId('left-sidebar');
......@@ -24,6 +24,8 @@ const findAndSetEditorValue = async value => {
const findTreeBody = () => screen.findByTestId('ide-tree-body', {}, { timeout: 5000 });
const findRootActions = () => screen.findByTestId('ide-root-actions', {}, { timeout: 7000 });
const findFileRowContainer = (row = null) =>
row ? Promise.resolve(row.parentElement) : findTreeBody();
......@@ -35,7 +37,7 @@ const findFileChild = async (row, name, index = 0) => {
};
const openFileRow = row => {
if (!row || isFileRowOpen(row)) {
if (!row || isFolderRowOpen(row)) {
return;
}
......@@ -74,6 +76,19 @@ const findAndSetFileName = async value => {
createButton.click();
};
const findAndClickRootAction = async name => {
const container = await findRootActions();
const button = getByLabelText(container, name);
button.click();
};
export const openFile = async path => {
const row = await findAndTraverseToPath(path);
openFileRow(row);
};
export const createFile = async (path, content) => {
const parentPath = path
.split('/')
......@@ -81,7 +96,12 @@ export const createFile = async (path, content) => {
.join('/');
const parentRow = await findAndTraverseToPath(parentPath);
clickFileRowAction(parentRow, 'New file');
if (parentRow) {
clickFileRowAction(parentRow, 'New file');
} else {
await findAndClickRootAction('New file');
}
await findAndSetFileName(path);
await findAndSetEditorValue(content);
......
import { TEST_HOST } from 'helpers/test_constants';
import { waitForText } from 'helpers/wait_for_text';
import waitForPromises from 'helpers/wait_for_promises';
import { useOverclockTimers } from 'test_helpers/utils/overclock_timers';
import { createCommitId } from 'test_helpers/factories/commit_id';
import { initIde } from '~/ide';
......@@ -86,4 +87,18 @@ describe('WebIDE', () => {
],
});
});
it('user adds file that starts with +', async () => {
createComponent();
await ideHelper.createFile('+test', 'Hello world!');
await ideHelper.openFile('+test');
// Wait for monaco things
await waitForPromises();
// Assert that +test is the only open tab
const tabs = Array.from(document.querySelectorAll('.multi-file-tab'));
expect(tabs.map(x => x.textContent.trim())).toEqual(['+test']);
});
});
......@@ -6,7 +6,7 @@ beforeEach(() => {
relative_url_root: '',
};
setTestTimeout(5000);
setTestTimeout(7000);
jest.useRealTimers();
});
......
......@@ -30,7 +30,8 @@ RSpec.describe Backup::Artifacts do
it 'excludes tmp from backup tar' do
expect(backup).to receive(:tar).and_return('blabla-tar')
expect(backup).to receive(:run_pipeline!).with([%w(blabla-tar --exclude=lost+found --exclude=./tmp -C /var/gitlab-artifacts -cf - .), 'gzip -c -1'], any_args)
expect(backup).to receive(:run_pipeline!).with([%w(blabla-tar --exclude=lost+found --exclude=./tmp -C /var/gitlab-artifacts -cf - .), 'gzip -c -1'], any_args).and_return([[true, true], ''])
expect(backup).to receive(:pipeline_succeeded?).and_return(true)
backup.dump
end
end
......
This diff is collapsed.
......@@ -23,7 +23,8 @@ RSpec.describe Backup::Pages do
allow(Gitlab.config.pages).to receive(:path) { '/var/gitlab-pages' }
expect(subject).to receive(:tar).and_return('blabla-tar')
expect(subject).to receive(:run_pipeline!).with([%w(blabla-tar --exclude=lost+found --exclude=./@pages.tmp -C /var/gitlab-pages -cf - .), 'gzip -c -1'], any_args)
expect(subject).to receive(:run_pipeline!).with([%w(blabla-tar --exclude=lost+found --exclude=./@pages.tmp -C /var/gitlab-pages -cf - .), 'gzip -c -1'], any_args).and_return([[true, true], ''])
expect(subject).to receive(:pipeline_succeeded?).and_return(true)
subject.dump
end
end
......
......@@ -32,7 +32,8 @@ RSpec.describe Backup::Uploads do
it 'excludes tmp from backup tar' do
expect(backup).to receive(:tar).and_return('blabla-tar')
expect(backup).to receive(:run_pipeline!).with([%w(blabla-tar --exclude=lost+found --exclude=./tmp -C /var/uploads -cf - .), 'gzip -c -1'], any_args)
expect(backup).to receive(:run_pipeline!).with([%w(blabla-tar --exclude=lost+found --exclude=./tmp -C /var/uploads -cf - .), 'gzip -c -1'], any_args).and_return([[true, true], ''])
expect(backup).to receive(:pipeline_succeeded?).and_return(true)
backup.dump
end
end
......
......@@ -12363,10 +12363,10 @@ vue-loader@^15.9.3:
vue-hot-reload-api "^2.3.0"
vue-style-loader "^4.1.0"
vue-router@^3.4.7:
version "3.4.7"
resolved "https://registry.yarnpkg.com/vue-router/-/vue-router-3.4.7.tgz#bf189bafd16f4e4ef783c4a6250a3090f2c1fa1b"
integrity sha512-CbHXue5BLrDivOk5O4eZ0WT4Yj8XwdXa4kCnsEIOzYUPF/07ZukayA2jGxDCJxLc9SgVQX9QX0OuGOwGlVB4Qg==
vue-router@3.4.5:
version "3.4.5"
resolved "https://registry.yarnpkg.com/vue-router/-/vue-router-3.4.5.tgz#d396ec037b35931bdd1e9b7edd86f9788dc15175"
integrity sha512-ioRY5QyDpXM9TDjOX6hX79gtaMXSVDDzSlbIlyAmbHNteIL81WIVB2e+jbzV23vzxtoV0krdS2XHm+GxFg+Nxg==
vue-runtime-helpers@^1.1.2:
version "1.1.2"
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment