Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2022-02-08 06:17:16 +00:00
parent b79a10eb5a
commit 2408d960e9
9 changed files with 85 additions and 17 deletions

View File

@ -1,9 +1,22 @@
<script>
import { GlAlert, GlButton, GlLoadingIcon, GlModal, GlModalDirective } from '@gitlab/ui';
import {
GlAlert,
GlButton,
GlLoadingIcon,
GlModal,
GlModalDirective,
GlTooltipDirective,
} from '@gitlab/ui';
import { setUrlFragment, redirectTo } from '~/lib/utils/url_utility';
import { __ } from '~/locale';
import ciHeader from '~/vue_shared/components/header_ci_component.vue';
import { LOAD_FAILURE, POST_FAILURE, DELETE_FAILURE, DEFAULT } from '../constants';
import {
LOAD_FAILURE,
POST_FAILURE,
DELETE_FAILURE,
DEFAULT,
BUTTON_TOOLTIP_RETRY,
} from '../constants';
import cancelPipelineMutation from '../graphql/mutations/cancel_pipeline.mutation.graphql';
import deletePipelineMutation from '../graphql/mutations/delete_pipeline.mutation.graphql';
import retryPipelineMutation from '../graphql/mutations/retry_pipeline.mutation.graphql';
@ -15,6 +28,7 @@ const POLL_INTERVAL = 10000;
export default {
name: 'PipelineHeaderSection',
BUTTON_TOOLTIP_RETRY,
pipelineCancel: 'pipelineCancel',
pipelineRetry: 'pipelineRetry',
finishedStatuses: ['FAILED', 'SUCCESS', 'CANCELED'],
@ -27,6 +41,7 @@ export default {
},
directives: {
GlModal: GlModalDirective,
GlTooltip: GlTooltipDirective,
},
errorTexts: {
[LOAD_FAILURE]: __('We are currently unable to fetch data for the pipeline header.'),
@ -225,6 +240,9 @@ export default {
>
<gl-button
v-if="canRetryPipeline"
v-gl-tooltip
:aria-label="$options.BUTTON_TOOLTIP_RETRY"
:title="$options.BUTTON_TOOLTIP_RETRY"
:loading="isRetrying"
:disabled="isRetrying"
category="secondary"

View File

@ -1,15 +1,13 @@
<script>
import { GlButton, GlTooltipDirective, GlModalDirective } from '@gitlab/ui';
import { __ } from '~/locale';
import eventHub from '../../event_hub';
import { BUTTON_TOOLTIP_RETRY, BUTTON_TOOLTIP_CANCEL } from '../../constants';
import PipelineMultiActions from './pipeline_multi_actions.vue';
import PipelinesManualActions from './pipelines_manual_actions.vue';
export default {
i18n: {
cancelTitle: __('Cancel'),
redeployTitle: __('Retry'),
},
BUTTON_TOOLTIP_RETRY,
BUTTON_TOOLTIP_CANCEL,
directives: {
GlTooltip: GlTooltipDirective,
GlModalDirective,
@ -75,12 +73,13 @@ export default {
<gl-button
v-if="pipeline.flags.retryable"
v-gl-tooltip.hover
:aria-label="$options.i18n.redeployTitle"
:title="$options.i18n.redeployTitle"
:aria-label="$options.BUTTON_TOOLTIP_RETRY"
:title="$options.BUTTON_TOOLTIP_RETRY"
:disabled="isRetrying"
:loading="isRetrying"
class="js-pipelines-retry-button"
data-qa-selector="pipeline_retry_button"
data-testid="pipelines-retry-button"
icon="repeat"
variant="default"
category="secondary"
@ -91,14 +90,15 @@ export default {
v-if="pipeline.flags.cancelable"
v-gl-tooltip.hover
v-gl-modal-directive="'confirmation-modal'"
:aria-label="$options.i18n.cancelTitle"
:title="$options.i18n.cancelTitle"
:aria-label="$options.BUTTON_TOOLTIP_CANCEL"
:title="$options.BUTTON_TOOLTIP_CANCEL"
:loading="isCancelling"
:disabled="isCancelling"
icon="cancel"
variant="danger"
category="primary"
class="js-pipelines-cancel-button gl-ml-1"
data-testid="pipelines-cancel-button"
@click="handleCancelClick"
/>

View File

@ -59,3 +59,6 @@ export const PipelineKeyOptions = [
];
export const TOAST_MESSAGE = s__('Pipeline|Creating pipeline.');
export const BUTTON_TOOLTIP_RETRY = __('Retry failed jobs');
export const BUTTON_TOOLTIP_CANCEL = __('Cancel');

View File

@ -120,7 +120,7 @@ class Groups::DependencyProxyForContainersController < ::Groups::DependencyProxy
end
def manifest_file_name
@manifest_file_name ||= "#{image}:#{tag}.json"
@manifest_file_name ||= Gitlab::Utils.check_path_traversal!("#{image}:#{tag}.json")
end
def group

View File

@ -10,11 +10,13 @@
- link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: help_page_path('user/project/settings/index', anchor: 'unarchiving-a-project') }
%p= _("Unarchiving the project will restore its members' ability to make changes to it. The repository can be committed to, and issues, comments, and other entities can be created. %{strong_start}Once active, this project shows up in the search and on the dashboard.%{strong_end} %{link_start}Learn more.%{link_end}").html_safe % { strong_start: '<strong>'.html_safe, strong_end: '</strong>'.html_safe, link_start: link_start, link_end: '</a>'.html_safe }
= link_to _('Unarchive project'), unarchive_project_path(@project),
aria: { label: _('Unarchive project') },
data: { confirm: _("Are you sure that you want to unarchive this project?"), qa_selector: 'unarchive_project_link' },
method: :post, class: "gl-button btn btn-confirm"
- else
- link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: help_page_path('user/project/settings/index', anchor: 'archiving-a-project') }
%p= _("Archiving the project will make it entirely read-only. It is hidden from the dashboard and doesn't show up in searches. %{strong_start}The repository cannot be committed to, and no issues, comments, or other entities can be created.%{strong_end} %{link_start}Learn more.%{link_end}").html_safe % { strong_start: '<strong>'.html_safe, strong_end: '</strong>'.html_safe, link_start: link_start, link_end: '</a>'.html_safe }
= link_to _('Archive project'), archive_project_path(@project),
data: { confirm: _("Are you sure that you want to archive this project?"), qa_selector: 'archive_project_link' },
aria: { label: _('Archive project') },
data: { confirm: _("Are you sure that you want to archive this project?"), qa_selector: 'archive_project_link', 'confirm-btn-variant': 'warning' },
method: :post, class: "gl-button btn btn-warning"

View File

@ -30864,6 +30864,9 @@ msgstr ""
msgid "Retry"
msgstr ""
msgid "Retry failed jobs"
msgstr ""
msgid "Retry job"
msgstr ""

View File

@ -47,6 +47,24 @@ RSpec.describe Groups::DependencyProxyForContainersController do
end
end
shared_examples 'with invalid path' do
context 'with invalid image' do
let(:image) { '../path_traversal' }
it 'raises an error' do
expect { subject }.to raise_error(Gitlab::Utils::PathTraversalAttackError, 'Invalid path')
end
end
context 'with invalid tag' do
let(:tag) { 'latest%2f..%2f..%2fpath_traversal' }
it 'raises an error' do
expect { subject }.to raise_error(Gitlab::Utils::PathTraversalAttackError, 'Invalid path')
end
end
end
shared_examples 'without permission' do
context 'with invalid user' do
before do
@ -164,8 +182,10 @@ RSpec.describe Groups::DependencyProxyForContainersController do
end
describe 'GET #manifest' do
let_it_be(:image) { 'alpine' }
let_it_be(:tag) { 'latest' }
let_it_be(:manifest) { create(:dependency_proxy_manifest, file_name: "alpine:#{tag}.json", group: group) }
let_it_be(:file_name) { "#{image}:#{tag}.json" }
let_it_be(:manifest) { create(:dependency_proxy_manifest, file_name: file_name, group: group) }
let(:pull_response) { { status: :success, manifest: manifest, from_cache: false } }
@ -235,6 +255,8 @@ RSpec.describe Groups::DependencyProxyForContainersController do
context 'with workhorse response' do
let(:pull_response) { { status: :success, manifest: nil, from_cache: false } }
it_behaves_like 'with invalid path'
it 'returns Workhorse send-dependency instructions', :aggregate_failures do
subject
@ -246,7 +268,7 @@ RSpec.describe Groups::DependencyProxyForContainersController do
"Authorization" => ["Bearer abcd1234"],
"Accept" => ::ContainerRegistry::Client::ACCEPTED_TYPES
)
expect(url).to eq(DependencyProxy::Registry.manifest_url('alpine', tag))
expect(url).to eq(DependencyProxy::Registry.manifest_url(image, tag))
expect(response.headers['Content-Type']).to eq('application/gzip')
expect(response.headers['Content-Disposition']).to eq(
ActionDispatch::Http::ContentDisposition.format(disposition: 'attachment', filename: manifest.file_name)
@ -277,7 +299,7 @@ RSpec.describe Groups::DependencyProxyForContainersController do
it_behaves_like 'not found when disabled'
def get_manifest(tag)
get :manifest, params: { group_id: group.to_param, image: 'alpine', tag: tag }
get :manifest, params: { group_id: group.to_param, image: image, tag: tag }
end
end
@ -440,6 +462,7 @@ RSpec.describe Groups::DependencyProxyForContainersController do
end
it_behaves_like 'a package tracking event', described_class.name, 'pull_manifest'
it_behaves_like 'with invalid path'
context 'with no existing manifest' do
it 'creates a manifest' do

View File

@ -4,6 +4,7 @@ import HeaderComponent from '~/pipelines/components/header_component.vue';
import cancelPipelineMutation from '~/pipelines/graphql/mutations/cancel_pipeline.mutation.graphql';
import deletePipelineMutation from '~/pipelines/graphql/mutations/delete_pipeline.mutation.graphql';
import retryPipelineMutation from '~/pipelines/graphql/mutations/retry_pipeline.mutation.graphql';
import { BUTTON_TOOLTIP_RETRY } from '~/pipelines/constants';
import {
mockCancelledPipelineHeader,
mockFailedPipelineHeader,
@ -113,6 +114,10 @@ describe('Pipeline details header', () => {
variables: { id: mockCancelledPipelineHeader.id },
});
});
it('should render retry action tooltip', () => {
expect(findRetryButton().attributes('title')).toBe(BUTTON_TOOLTIP_RETRY);
});
});
describe('Cancel action', () => {

View File

@ -9,7 +9,11 @@ import PipelineTriggerer from '~/pipelines/components/pipelines_list/pipeline_tr
import PipelineUrl from '~/pipelines/components/pipelines_list/pipeline_url.vue';
import PipelinesTable from '~/pipelines/components/pipelines_list/pipelines_table.vue';
import PipelinesTimeago from '~/pipelines/components/pipelines_list/time_ago.vue';
import { PipelineKeyOptions } from '~/pipelines/constants';
import {
PipelineKeyOptions,
BUTTON_TOOLTIP_RETRY,
BUTTON_TOOLTIP_CANCEL,
} from '~/pipelines/constants';
import eventHub from '~/pipelines/event_hub';
import CiBadge from '~/vue_shared/components/ci_badge_link.vue';
@ -66,6 +70,8 @@ describe('Pipelines Table', () => {
const findStagesTh = () => wrapper.findByTestId('stages-th');
const findTimeAgoTh = () => wrapper.findByTestId('timeago-th');
const findActionsTh = () => wrapper.findByTestId('actions-th');
const findRetryBtn = () => wrapper.findByTestId('pipelines-retry-button');
const findCancelBtn = () => wrapper.findByTestId('pipelines-cancel-button');
beforeEach(() => {
pipeline = createMockPipeline();
@ -192,6 +198,14 @@ describe('Pipelines Table', () => {
it('should render pipeline operations', () => {
expect(findActions().exists()).toBe(true);
});
it('should render retry action tooltip', () => {
expect(findRetryBtn().attributes('title')).toBe(BUTTON_TOOLTIP_RETRY);
});
it('should render cancel action tooltip', () => {
expect(findCancelBtn().attributes('title')).toBe(BUTTON_TOOLTIP_CANCEL);
});
});
});