Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
b79a10eb5a
commit
2408d960e9
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
/>
|
||||
|
||||
|
|
|
@ -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');
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -30864,6 +30864,9 @@ msgstr ""
|
|||
msgid "Retry"
|
||||
msgstr ""
|
||||
|
||||
msgid "Retry failed jobs"
|
||||
msgstr ""
|
||||
|
||||
msgid "Retry job"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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', () => {
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
|
Loading…
Reference in New Issue