diff --git a/packages/components/src/components/PipelineRun/PipelineRun.js b/packages/components/src/components/PipelineRun/PipelineRun.js index d8dd8ab51..3904f5eba 100644 --- a/packages/components/src/components/PipelineRun/PipelineRun.js +++ b/packages/components/src/components/PipelineRun/PipelineRun.js @@ -216,7 +216,7 @@ export /* istanbul ignore next */ class PipelineRunContainer extends Component { onViewChange, pipelineRun, pod, - runaction, + runActions, selectedStepId, selectedTaskId, showIO, @@ -349,7 +349,7 @@ export /* istanbul ignore next */ class PipelineRunContainer extends Component { status={pipelineRunStatus} triggerHeader={triggerHeader} > - {runaction} + {runActions} {customNotification} {taskRuns.length > 0 && ( diff --git a/packages/components/src/components/RunAction/RunAction.js b/packages/components/src/components/RunAction/RunAction.js deleted file mode 100644 index c438dd0fa..000000000 --- a/packages/components/src/components/RunAction/RunAction.js +++ /dev/null @@ -1,139 +0,0 @@ -/* -Copyright 2019-2021 The Tekton Authors -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -import React, { Component } from 'react'; -import { injectIntl } from 'react-intl'; -import { Button } from 'carbon-components-react'; -import { - PlayOutline32 as Play, - Restart32 as Restart -} from '@carbon/icons-react'; - -class RunAction extends Component { - handleAction = event => { - event.preventDefault(); - const { action, getURL, intl, run, runaction, showNotification } = - this.props; - const { namespace } = run.metadata; - let triggerMsg = null; - switch (action) { - case 'rerun': - triggerMsg = intl.formatMessage({ - id: 'dashboard.rerun.triggered', - defaultMessage: 'Triggered rerun' - }); - break; - case 'start': - triggerMsg = intl.formatMessage({ - id: 'dashboard.start.triggered', - defaultMessage: 'Triggered start' - }); - break; - default: - break; - } - - runaction(run) - .then(newRun => { - let finalURL = null; - if (newRun) { - finalURL = getURL({ - namespace, - name: newRun.metadata.name - }); - } - showNotification({ - message: triggerMsg, - kind: 'success', - logsURL: finalURL - }); - }) - .catch(error => { - let msg = null; - switch (action) { - case 'rerun': - msg = intl.formatMessage( - { - id: 'dashboard.rerun.error', - defaultMessage: - 'An error occurred when rerunning {runName}: check the dashboard logs for details. Status code: {statusCode}' - }, - { - action, - runName: run.metadata.name, - statusCode: error.response.status - } - ); - break; - case 'start': - msg = intl.formatMessage( - { - id: 'dashboard.start.error', - defaultMessage: - 'An error occurred when starting {runName}: check the dashboard logs for details. Status code: {statusCode}' - }, - { - action, - runName: run.metadata.name, - statusCode: error.response.status - } - ); - break; - default: - break; - } - showNotification({ - message: msg, - kind: 'error' - }); - }); - }; - - render() { - const { action, intl } = this.props; - - let icon = null; - let msg = ''; - switch (action) { - case 'rerun': - icon = Restart; - msg = intl.formatMessage({ - id: 'dashboard.rerun.button', - defaultMessage: 'Rerun' - }); - break; - case 'start': - icon = Play; - msg = intl.formatMessage({ - id: 'dashboard.start.button', - defaultMessage: 'Start' - }); - break; - default: - break; - } - - return ( - - ); - } -} - -export default injectIntl(RunAction); diff --git a/packages/components/src/components/RunAction/RunAction.scss b/packages/components/src/components/RunAction/RunAction.scss deleted file mode 100644 index 179a32a17..000000000 --- a/packages/components/src/components/RunAction/RunAction.scss +++ /dev/null @@ -1,16 +0,0 @@ -/* -Copyright 2019-2020 The Tekton Authors -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -.bx--btn.tkn--runaction-btn.bx--btn--ghost { - border-width: 1px; -} diff --git a/packages/components/src/components/RunAction/RunAction.test.js b/packages/components/src/components/RunAction/RunAction.test.js deleted file mode 100644 index a40a1447e..000000000 --- a/packages/components/src/components/RunAction/RunAction.test.js +++ /dev/null @@ -1,79 +0,0 @@ -/* -Copyright 2019-2022 The Tekton Authors -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -import React from 'react'; -import { fireEvent, waitFor } from '@testing-library/react'; -import { renderWithRouter } from '../../utils/test'; -import RunAction from './RunAction'; - -/* RunAction should sit on the PipelineRun page and display notifications there -It would be useful to have tests at the container level too, but for now just do it at the component level */ - -const logsURL = '/fake/url'; -const fakeRunName = 'fake_runName'; -const fakeNamespace = 'fake_namespace'; -const props = { - getURL() { - return logsURL; - }, - run: { - metadata: { - namespace: fakeNamespace, - name: fakeRunName - }, - spec: { - pipelineRef: { - name: 'thepipeline' - } - } - }, - showNotification() {} -}; - -it('Rerun button creates API call with correct parameters', async () => { - const response = Promise.resolve({ metadata: { name: fakeRunName } }); - const rerunMock = jest.fn().mockImplementation(() => response); - jest.spyOn(props, 'getURL'); - jest.spyOn(props, 'showNotification'); - const { getByText } = renderWithRouter( - - ); - fireEvent.click(getByText('Rerun')); - await waitFor(() => expect(rerunMock).toHaveBeenCalled()); - expect(rerunMock).toHaveBeenCalledWith(props.run); - expect(props.getURL).toHaveBeenCalledWith({ - name: fakeRunName, - namespace: fakeNamespace - }); - expect(props.showNotification).toHaveBeenCalledWith( - expect.objectContaining({ kind: 'success', logsURL }) - ); -}); - -it('Rerun button handles API error', async () => { - const error = { response: { status: 500 } }; - const response = Promise.reject(error); - const rerunMock = jest.fn().mockImplementation(() => response); - jest.spyOn(props, 'getURL'); - jest.spyOn(props, 'showNotification'); - const { getByText } = renderWithRouter( - - ); - fireEvent.click(getByText('Rerun')); - await waitFor(() => expect(rerunMock).toHaveBeenCalled()); - expect(rerunMock).toHaveBeenCalledWith(props.run); - expect(props.getURL).not.toHaveBeenCalled(); - expect(props.showNotification).toHaveBeenCalledWith( - expect.objectContaining({ kind: 'error' }) - ); -}); diff --git a/packages/components/src/components/RunAction/index.js b/packages/components/src/components/RunAction/index.js deleted file mode 100644 index fb6b6d3ca..000000000 --- a/packages/components/src/components/RunAction/index.js +++ /dev/null @@ -1,15 +0,0 @@ -/* -Copyright 2019-2021 The Tekton Authors -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -/* istanbul ignore file */ - -export { default } from './RunAction'; diff --git a/packages/components/src/components/RunHeader/RunHeader.js b/packages/components/src/components/RunHeader/RunHeader.js index 5454dcfb0..1846e0c36 100644 --- a/packages/components/src/components/RunHeader/RunHeader.js +++ b/packages/components/src/components/RunHeader/RunHeader.js @@ -26,6 +26,7 @@ class RunHeader extends Component { render() { const { + children, icon, intl, lastTransitionTime, @@ -81,7 +82,7 @@ class RunHeader extends Component { ) : null} - {this.props.children} + {children}
{reason} diff --git a/packages/components/src/components/RunHeader/RunHeader.scss b/packages/components/src/components/RunHeader/RunHeader.scss index 0a19d3564..104f23946 100644 --- a/packages/components/src/components/RunHeader/RunHeader.scss +++ b/packages/components/src/components/RunHeader/RunHeader.scss @@ -47,8 +47,9 @@ header.tkn--pipeline-run-header { white-space: nowrap; } - .tkn--runaction-btn { + .tkn--actions-dropdown { margin-left: auto; + margin-right: 0.5rem; } } diff --git a/packages/components/src/components/index.js b/packages/components/src/components/index.js index b2d65bfcd..d7b9c7bb3 100644 --- a/packages/components/src/components/index.js +++ b/packages/components/src/components/index.js @@ -36,7 +36,6 @@ export { default as PipelineResources } from './PipelineResources'; export { default as PipelineRun } from './PipelineRun'; export { default as PipelineRuns } from './PipelineRuns'; export { default as Portal } from './Portal'; -export { default as RunAction } from './RunAction'; export { default as ResourceDetails } from './ResourceDetails'; export { default as ResourceTable } from './ResourceTable'; export { default as RunHeader } from './RunHeader'; diff --git a/packages/utils/src/utils/constants.js b/packages/utils/src/utils/constants.js index 90c716ca9..5a319bdd4 100644 --- a/packages/utils/src/utils/constants.js +++ b/packages/utils/src/utils/constants.js @@ -27,6 +27,10 @@ export const pipelineRunStatuses = { PENDING: 'PipelineRunPending' }; +export const preferences = { + CANCEL_STATUS_KEY: 'tkn-pipelinerun-cancel-status' +}; + export const queryParams = { PIPELINE_TASK: 'pipelineTask', RETRY: 'retry', diff --git a/src/containers/PipelineRun/PipelineRun.js b/src/containers/PipelineRun/PipelineRun.js index 344a3bfc1..9dbb07f17 100644 --- a/src/containers/PipelineRun/PipelineRun.js +++ b/src/containers/PipelineRun/PipelineRun.js @@ -1,5 +1,5 @@ /* -Copyright 2019-2021 The Tekton Authors +Copyright 2019-2022 The Tekton Authors Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at @@ -11,21 +11,30 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React, { useRef, useState } from 'react'; -import { PipelineRun, RunAction } from '@tektoncd/dashboard-components'; +import React, { useEffect, useRef, useState } from 'react'; +import { Actions, PipelineRun } from '@tektoncd/dashboard-components'; import { + getStatus, getTaskRunsWithPlaceholders, + isPending, + isRunning, labels as labelConstants, - pipelineRunStatuses, + preferences, queryParams as queryParamConstants, urls, useTitleSync } from '@tektoncd/dashboard-utils'; -import { InlineNotification } from 'carbon-components-react'; +import { + InlineNotification, + RadioTile, + TileGroup +} from 'carbon-components-react'; import { Link, useHistory, useLocation, useParams } from 'react-router-dom'; import { injectIntl } from 'react-intl'; import { + cancelPipelineRun, + deletePipelineRun, rerunPipelineRun, startPipelineRun, useClusterTasks, @@ -69,12 +78,26 @@ export /* istanbul ignore next */ function PipelineRunContainer({ intl }) { const isLogStreamingEnabled = useIsLogStreamingEnabled(); const isReadOnly = useIsReadOnly(); const [isUsingExternalLogs, setIsUsingExternalLogs] = useState(false); + const [cancelStatus, setCancelStatus] = useState('Cancelled'); useTitleSync({ page: 'PipelineRun', resourceName: pipelineRunName }); + useEffect(() => { + const savedCancelStatus = localStorage.getItem( + preferences.CANCEL_STATUS_KEY + ); + if (savedCancelStatus) { + setCancelStatus(savedCancelStatus); + } + }, []); + + useEffect(() => { + localStorage.setItem(preferences.CANCEL_STATUS_KEY, cancelStatus); + }, [cancelStatus]); + const { data: pipelineRun, error: pipelineRunError, @@ -201,6 +224,228 @@ export /* istanbul ignore next */ function PipelineRunContainer({ intl }) { history.push(browserURL); } + function cancel() { + // use value from localStorage to avoid stale value from closure + const savedCancelStatus = localStorage.getItem( + preferences.CANCEL_STATUS_KEY + ); + const { name, namespace: resourceNamespace } = pipelineRun.metadata; + cancelPipelineRun({ + name, + namespace: resourceNamespace, + status: savedCancelStatus + }).catch(err => { + err.response.text().then(text => { + const statusCode = err.response.status; + let errorMessage = `error code ${statusCode}`; + if (text) { + errorMessage = `${text} (error code ${statusCode})`; + } + setShowRunActionNotification({ kind: 'error', message: errorMessage }); + }); + }); + } + + function deleteRun() { + const { name, namespace: resourceNamespace } = pipelineRun.metadata; + deletePipelineRun({ name, namespace: resourceNamespace }) + .then(() => { + history.push(urls.pipelineRuns.byNamespace({ namespace })); + }) + .catch(err => { + err.response.text().then(text => { + const statusCode = err.response.status; + let errorMessage = `error code ${statusCode}`; + if (text) { + errorMessage = `${text} (error code ${statusCode})`; + } + setShowRunActionNotification({ + kind: 'error', + message: errorMessage + }); + }); + }); + } + + function rerun() { + rerunPipelineRun(pipelineRun) + .then(newRun => { + setShowRunActionNotification({ + kind: 'success', + logsURL: urls.pipelineRuns.byName({ + namespace, + pipelineRunName: newRun.metadata.name + }), + message: intl.formatMessage({ + id: 'dashboard.rerun.triggered', + defaultMessage: 'Triggered rerun' + }) + }); + }) + .catch(rerunError => { + setShowRunActionNotification({ + kind: 'error', + message: intl.formatMessage( + { + id: 'dashboard.rerun.error', + defaultMessage: + 'An error occurred when rerunning {runName}: check the dashboard logs for details. Status code: {statusCode}' + }, + { + runName: pipelineRun.metadata.name, + statusCode: rerunError.response.status + } + ) + }); + }); + } + + function pipelineRunActions() { + if (isReadOnly) { + return []; + } + + return [ + { + actionText: intl.formatMessage({ + id: 'dashboard.rerun.actionText', + defaultMessage: 'Rerun' + }), + action: rerun, + disable: resource => { + const { reason, status } = getStatus(resource); + return isPending(reason, status); + } + }, + { + actionText: intl.formatMessage({ + id: 'dashboard.startPipelineRun.actionText', + defaultMessage: 'Start' + }), + action: startPipelineRun, + disable: resource => { + const { reason, status } = getStatus(resource); + return !isPending(reason, status); + } + }, + { + actionText: intl.formatMessage({ + id: 'dashboard.cancelPipelineRun.actionText', + defaultMessage: 'Stop' + }), + action: cancel, + disable: resource => { + const { reason, status } = getStatus(resource); + return !isRunning(reason, status) && !isPending(reason, status); + }, + modalProperties: { + heading: intl.formatMessage({ + id: 'dashboard.cancelPipelineRun.heading', + defaultMessage: 'Stop PipelineRun' + }), + primaryButtonText: intl.formatMessage({ + id: 'dashboard.cancelPipelineRun.primaryText', + defaultMessage: 'Stop PipelineRun' + }), + body: resource => ( + <> +

+ {intl.formatMessage( + { + id: 'dashboard.cancelPipelineRun.body', + defaultMessage: + 'Are you sure you would like to stop PipelineRun {name}?' + }, + { name: resource.metadata.name } + )} +

+ setCancelStatus(status)} + > + + Cancelled +

+ {intl.formatMessage({ + id: 'dashboard.cancelPipelineRun.cancelled.description', + defaultMessage: + 'Interrupt any currently executing tasks and skip finally tasks' + })} +

+
+ + CancelledRunFinally +

+ {intl.formatMessage({ + id: 'dashboard.cancelPipelineRun.cancelledRunFinally.description', + defaultMessage: + 'Interrupt any currently executing non-finally tasks, then execute finally tasks' + })} +

+
+ + StoppedRunFinally +

+ {intl.formatMessage({ + id: 'dashboard.cancelPipelineRun.stoppedRunFinally.description', + defaultMessage: + 'Allow any currently executing tasks to complete but do not schedule any new non-finally tasks, then execute finally tasks' + })} +

+
+
+ + ) + } + }, + { + actionText: intl.formatMessage({ + id: 'dashboard.actions.deleteButton', + defaultMessage: 'Delete' + }), + action: deleteRun, + danger: true, + disable: resource => { + const { reason, status } = getStatus(resource); + return isRunning(reason, status); + }, + hasDivider: true, + modalProperties: { + danger: true, + heading: intl.formatMessage( + { + id: 'dashboard.deleteResources.heading', + defaultMessage: 'Delete {kind}' + }, + { kind: 'PipelineRun' } + ), + primaryButtonText: intl.formatMessage({ + id: 'dashboard.actions.deleteButton', + defaultMessage: 'Delete' + }), + body: resource => + intl.formatMessage( + { + id: 'dashboard.deletePipelineRun.body', + defaultMessage: + 'Are you sure you would like to delete PipelineRun {name}?' + }, + { name: resource.metadata.name } + ) + } + } + ]; + } + const selectedTaskId = getSelectedTaskId( currentPipelineTaskName, currentRetry @@ -229,34 +474,7 @@ export /* istanbul ignore next */ function PipelineRunContainer({ intl }) { podDetails = (events || pod) && { events, resource: pod }; } - let runAction = null; - if (pipelineRun && !isReadOnly) { - if (pipelineRun.spec.status !== pipelineRunStatuses.PENDING) { - runAction = ( - - urls.pipelineRuns.byName({ - namespace: resourceNamespace, - pipelineRunName: name - }) - } - run={pipelineRun} - runaction={rerunPipelineRun} - showNotification={value => setShowRunActionNotification(value)} - /> - ); - } else { - runAction = ( - setShowRunActionNotification(value)} - /> - ); - } - } + const runActions = pipelineRunActions(); return ( <> @@ -312,7 +530,11 @@ export /* istanbul ignore next */ function PipelineRunContainer({ intl }) { onViewChange={getViewChangeHandler({ history, location })} pipelineRun={pipelineRun} pod={podDetails} - runaction={runAction} + runActions={ + runActions.length ? ( + + ) : null + } selectedStepId={currentSelectedStepId} selectedTaskId={selectedTaskId} showIO diff --git a/src/containers/PipelineRuns/PipelineRuns.js b/src/containers/PipelineRuns/PipelineRuns.js index b5f7a4a8c..0a72eb739 100644 --- a/src/containers/PipelineRuns/PipelineRuns.js +++ b/src/containers/PipelineRuns/PipelineRuns.js @@ -32,6 +32,7 @@ import { isPending, isRunning, labels, + preferences, runMatchesStatusFilter, urls, useTitleSync @@ -50,8 +51,6 @@ import { useSelectedNamespace } from '../../api'; -const CANCEL_STATUS_KEY = 'tkn-pipelinerun-cancel-status'; - export function PipelineRuns({ intl }) { const history = useHistory(); const location = useLocation(); @@ -79,14 +78,16 @@ export function PipelineRuns({ intl }) { useTitleSync({ page: 'PipelineRuns' }); useEffect(() => { - const savedCancelStatus = localStorage.getItem(CANCEL_STATUS_KEY); + const savedCancelStatus = localStorage.getItem( + preferences.CANCEL_STATUS_KEY + ); if (savedCancelStatus) { setCancelStatus(savedCancelStatus); } }, []); useEffect(() => { - localStorage.setItem(CANCEL_STATUS_KEY, cancelStatus); + localStorage.setItem(preferences.CANCEL_STATUS_KEY, cancelStatus); }, [cancelStatus]); useEffect(() => { @@ -127,7 +128,9 @@ export function PipelineRuns({ intl }) { function cancel(pipelineRun) { // use value from localStorage to avoid stale value from closure - const savedCancelStatus = localStorage.getItem(CANCEL_STATUS_KEY); + const savedCancelStatus = localStorage.getItem( + preferences.CANCEL_STATUS_KEY + ); const { name, namespace: resourceNamespace } = pipelineRun.metadata; cancelPipelineRun({ name, diff --git a/src/containers/TaskRun/TaskRun.js b/src/containers/TaskRun/TaskRun.js index 27e631635..80040d43d 100644 --- a/src/containers/TaskRun/TaskRun.js +++ b/src/containers/TaskRun/TaskRun.js @@ -17,9 +17,9 @@ import { injectIntl } from 'react-intl'; import { Link, useHistory, useLocation, useParams } from 'react-router-dom'; import { InlineNotification, SkeletonText } from 'carbon-components-react'; import { + Actions, Log, Portal, - RunAction, RunHeader, StepDetails, TaskRunDetails, @@ -29,6 +29,7 @@ import { getStatus, getStepDefinition, getStepStatus, + isRunning, queryParams as queryParamConstants, urls, useTitleSync @@ -41,6 +42,8 @@ import { } from '../../utils'; import { + cancelTaskRun, + deleteTaskRun, rerunTaskRun, useEvents, useExternalLogsURL, @@ -93,7 +96,7 @@ export function TaskRunContainer({ intl }) { const maximizedLogsContainer = useRef(); const [isLogsMaximized, setIsLogsMaximized] = useState(false); - const [rerunNotification, setRerunNotification] = useState(null); + const [showNotification, setShowNotification] = useState(null); const [isUsingExternalLogs, setIsUsingExternalLogs] = useState(false); const externalLogsURL = useExternalLogsURL(); @@ -201,6 +204,151 @@ export function TaskRunContainer({ intl }) { history.push(browserURL); } + function cancel() { + cancelTaskRun({ + name: taskRun.metadata.name, + namespace: taskRun.metadata.namespace + }); + } + + function deleteTask() { + deleteTaskRun({ + name: taskRun.metadata.name, + namespace: taskRun.metadata.namespace + }) + .then(() => { + history.push(urls.taskRuns.byNamespace({ namespace })); + }) + .catch(err => { + err.response.text().then(text => { + const statusCode = err.response.status; + let errorMessage = `error code ${statusCode}`; + if (text) { + errorMessage = `${text} (error code ${statusCode})`; + } + setShowNotification({ + kind: 'error', + message: errorMessage + }); + }); + }); + } + + function rerun() { + rerunTaskRun(taskRun) + .then(newRun => { + setShowNotification({ + kind: 'success', + logsURL: urls.taskRuns.byName({ + namespace, + taskRunName: newRun.metadata.name + }), + message: intl.formatMessage({ + id: 'dashboard.rerun.triggered', + defaultMessage: 'Triggered rerun' + }) + }); + }) + .catch(rerunError => { + setShowNotification({ + kind: 'error', + message: intl.formatMessage( + { + id: 'dashboard.rerun.error', + defaultMessage: + 'An error occurred when rerunning {runName}: check the dashboard logs for details. Status code: {statusCode}' + }, + { + runName: taskRun.metadata.name, + statusCode: rerunError.response.status + } + ) + }); + }); + } + + function taskRunActions() { + if (isReadOnly) { + return []; + } + return [ + { + action: rerun, + actionText: intl.formatMessage({ + id: 'dashboard.rerun.actionText', + defaultMessage: 'Rerun' + }), + disable: resource => !!resource.metadata.labels?.['tekton.dev/pipeline'] + }, + { + actionText: intl.formatMessage({ + id: 'dashboard.cancelTaskRun.actionText', + defaultMessage: 'Stop' + }), + action: cancel, + disable: resource => { + const { reason, status } = getStatus(resource); + return !isRunning(reason, status); + }, + modalProperties: { + heading: intl.formatMessage({ + id: 'dashboard.cancelTaskRun.heading', + defaultMessage: 'Stop TaskRun' + }), + primaryButtonText: intl.formatMessage({ + id: 'dashboard.cancelTaskRun.primaryText', + defaultMessage: 'Stop TaskRun' + }), + body: resource => + intl.formatMessage( + { + id: 'dashboard.cancelTaskRun.body', + defaultMessage: + 'Are you sure you would like to stop TaskRun {name}?' + }, + { name: resource.metadata.name } + ) + } + }, + { + actionText: intl.formatMessage({ + id: 'dashboard.actions.deleteButton', + defaultMessage: 'Delete' + }), + action: deleteTask, + danger: true, + disable: resource => { + const { reason, status } = getStatus(resource); + return isRunning(reason, status); + }, + hasDivider: true, + modalProperties: { + danger: true, + heading: intl.formatMessage( + { + id: 'dashboard.deleteResources.heading', + defaultMessage: 'Delete {kind}' + }, + { kind: 'TaskRun' } + ), + primaryButtonText: intl.formatMessage({ + id: 'dashboard.actions.deleteButton', + defaultMessage: 'Delete' + }), + body: resource => + intl.formatMessage( + { + id: 'dashboard.deleteTaskRun.body', + defaultMessage: + 'Are you sure you would like to delete TaskRun {name}?' + }, + { name: resource.metadata.name } + ) + } + } + ]; + } + if (isLoadingTaskRun || isLoadingTask) { return ; } @@ -252,21 +400,7 @@ export function TaskRunContainer({ intl }) { const onViewChange = getViewChangeHandler({ history, location }); - const rerun = !isReadOnly && - !taskRun.metadata?.labels?.['tekton.dev/pipeline'] && ( - - urls.taskRuns.byName({ - namespace: currentNamespace, - taskRunName: name - }) - } - run={taskRun} - runaction={rerunTaskRun} - showNotification={setRerunNotification} - /> - ); + const runActions = taskRunActions(); let podDetails; if (!selectedStepId) { @@ -276,14 +410,14 @@ export function TaskRunContainer({ intl }) { return ( <>
- {rerunNotification && ( + {showNotification && ( {intl.formatMessage({ id: 'dashboard.run.rerunStatusMessage', @@ -294,8 +428,8 @@ export function TaskRunContainer({ intl }) { '' ) } - title={rerunNotification.message} - kind={rerunNotification.kind} + title={showNotification.message} + kind={showNotification.kind} caption="" /> )} @@ -306,7 +440,9 @@ export function TaskRunContainer({ intl }) { runName={taskRun.metadata.name} status={succeeded} > - {rerun} + {runActions.length ? ( + + ) : null}
handleCancelSelection); } - function rerun(taskRun) { - rerunTaskRun(taskRun); - } - function taskRunActions() { if (isReadOnly) { return []; } return [ { - action: rerun, + action: rerunTaskRun, actionText: intl.formatMessage({ id: 'dashboard.rerun.actionText', defaultMessage: 'Rerun' diff --git a/src/nls/messages_de.json b/src/nls/messages_de.json index c135ff878..bd468fceb 100644 --- a/src/nls/messages_de.json +++ b/src/nls/messages_de.json @@ -206,7 +206,6 @@ "dashboard.pod.resource": "", "dashboard.pod.resource.empty": "", "dashboard.rerun.actionText": "", - "dashboard.rerun.button": "", "dashboard.rerun.error": "", "dashboard.rerun.triggered": "", "dashboard.resource.apiVersion": "", @@ -233,9 +232,6 @@ "dashboard.sideNav.kubernetesResources": "", "dashboard.sideNav.tektonResources": "", "dashboard.skipToContent": "", - "dashboard.start.button": "", - "dashboard.start.error": "", - "dashboard.start.triggered": "", "dashboard.startPipelineRun.actionText": "", "dashboard.step.definitionNotAvailable": "Beschreibung: Die Schrittdefinition ist nicht verfügbar", "dashboard.step.statusNotAvailable": "Kein Status verfügbar", diff --git a/src/nls/messages_en.json b/src/nls/messages_en.json index 492c2ad51..8b7f26f3c 100644 --- a/src/nls/messages_en.json +++ b/src/nls/messages_en.json @@ -206,7 +206,6 @@ "dashboard.pod.resource": "Resource", "dashboard.pod.resource.empty": "Waiting for Pod resource", "dashboard.rerun.actionText": "Rerun", - "dashboard.rerun.button": "Rerun", "dashboard.rerun.error": "An error occurred when rerunning {runName}: check the dashboard logs for details. Status code: {statusCode}", "dashboard.rerun.triggered": "Triggered rerun", "dashboard.resource.apiVersion": "API version:", @@ -233,9 +232,6 @@ "dashboard.sideNav.kubernetesResources": "Kubernetes resources", "dashboard.sideNav.tektonResources": "Tekton resources", "dashboard.skipToContent": "Skip to main content", - "dashboard.start.button": "Start", - "dashboard.start.error": "An error occurred when starting {runName}: check the dashboard logs for details. Status code: {statusCode}", - "dashboard.start.triggered": "Triggered start", "dashboard.startPipelineRun.actionText": "Start", "dashboard.step.definitionNotAvailable": "Step definition not available", "dashboard.step.statusNotAvailable": "Status not available", diff --git a/src/nls/messages_es.json b/src/nls/messages_es.json index 48c6198d6..073eb0e8f 100644 --- a/src/nls/messages_es.json +++ b/src/nls/messages_es.json @@ -206,7 +206,6 @@ "dashboard.pod.resource": "", "dashboard.pod.resource.empty": "", "dashboard.rerun.actionText": "", - "dashboard.rerun.button": "", "dashboard.rerun.error": "", "dashboard.rerun.triggered": "", "dashboard.resource.apiVersion": "", @@ -233,9 +232,6 @@ "dashboard.sideNav.kubernetesResources": "", "dashboard.sideNav.tektonResources": "", "dashboard.skipToContent": "", - "dashboard.start.button": "", - "dashboard.start.error": "", - "dashboard.start.triggered": "", "dashboard.startPipelineRun.actionText": "", "dashboard.step.definitionNotAvailable": "descripción: la definición del paso no está disponible", "dashboard.step.statusNotAvailable": "Ningún estado disponible", diff --git a/src/nls/messages_fr.json b/src/nls/messages_fr.json index 5c9819d02..36fb5fb24 100644 --- a/src/nls/messages_fr.json +++ b/src/nls/messages_fr.json @@ -206,7 +206,6 @@ "dashboard.pod.resource": "", "dashboard.pod.resource.empty": "", "dashboard.rerun.actionText": "", - "dashboard.rerun.button": "", "dashboard.rerun.error": "", "dashboard.rerun.triggered": "", "dashboard.resource.apiVersion": "", @@ -233,9 +232,6 @@ "dashboard.sideNav.kubernetesResources": "", "dashboard.sideNav.tektonResources": "", "dashboard.skipToContent": "", - "dashboard.start.button": "", - "dashboard.start.error": "", - "dashboard.start.triggered": "", "dashboard.startPipelineRun.actionText": "", "dashboard.step.definitionNotAvailable": "description : définition d'étape non disponible", "dashboard.step.statusNotAvailable": "Aucun statut disponible", diff --git a/src/nls/messages_it.json b/src/nls/messages_it.json index 3d72c7cec..4eaef9dd9 100644 --- a/src/nls/messages_it.json +++ b/src/nls/messages_it.json @@ -206,7 +206,6 @@ "dashboard.pod.resource": "", "dashboard.pod.resource.empty": "", "dashboard.rerun.actionText": "", - "dashboard.rerun.button": "", "dashboard.rerun.error": "", "dashboard.rerun.triggered": "", "dashboard.resource.apiVersion": "", @@ -233,9 +232,6 @@ "dashboard.sideNav.kubernetesResources": "", "dashboard.sideNav.tektonResources": "", "dashboard.skipToContent": "", - "dashboard.start.button": "", - "dashboard.start.error": "", - "dashboard.start.triggered": "", "dashboard.startPipelineRun.actionText": "", "dashboard.step.definitionNotAvailable": "descrizione: definizione passo non disponibile", "dashboard.step.statusNotAvailable": "Nessuno stato disponibile", diff --git a/src/nls/messages_ja.json b/src/nls/messages_ja.json index ad3424b22..feacf096d 100644 --- a/src/nls/messages_ja.json +++ b/src/nls/messages_ja.json @@ -206,7 +206,6 @@ "dashboard.pod.resource": "", "dashboard.pod.resource.empty": "", "dashboard.rerun.actionText": "再実行", - "dashboard.rerun.button": "再実行", "dashboard.rerun.error": "{runName}の再実行中にエラーが発生しました:詳細については、ダッシュボードログを確認してください。ステータスコード:{statusCode}", "dashboard.rerun.triggered": "トリガーされた再実行", "dashboard.resource.apiVersion": "", @@ -233,9 +232,6 @@ "dashboard.sideNav.kubernetesResources": "Kubernetesリソース", "dashboard.sideNav.tektonResources": "Tektonリソース", "dashboard.skipToContent": "メインコンテンツへ", - "dashboard.start.button": "", - "dashboard.start.error": "", - "dashboard.start.triggered": "", "dashboard.startPipelineRun.actionText": "", "dashboard.step.definitionNotAvailable": "利用可能な定義がありません", "dashboard.step.statusNotAvailable": "ステータスは使用できません", diff --git a/src/nls/messages_ko.json b/src/nls/messages_ko.json index 03dff12a6..eaf91d53a 100644 --- a/src/nls/messages_ko.json +++ b/src/nls/messages_ko.json @@ -206,7 +206,6 @@ "dashboard.pod.resource": "", "dashboard.pod.resource.empty": "", "dashboard.rerun.actionText": "", - "dashboard.rerun.button": "", "dashboard.rerun.error": "", "dashboard.rerun.triggered": "", "dashboard.resource.apiVersion": "", @@ -233,9 +232,6 @@ "dashboard.sideNav.kubernetesResources": "", "dashboard.sideNav.tektonResources": "", "dashboard.skipToContent": "", - "dashboard.start.button": "", - "dashboard.start.error": "", - "dashboard.start.triggered": "", "dashboard.startPipelineRun.actionText": "", "dashboard.step.definitionNotAvailable": "설명: 단계 정의를 사용할 수 없음", "dashboard.step.statusNotAvailable": "사용 가능한 상태가 없음", diff --git a/src/nls/messages_pt.json b/src/nls/messages_pt.json index 3357ca909..e4cfde202 100644 --- a/src/nls/messages_pt.json +++ b/src/nls/messages_pt.json @@ -206,7 +206,6 @@ "dashboard.pod.resource": "", "dashboard.pod.resource.empty": "", "dashboard.rerun.actionText": "", - "dashboard.rerun.button": "", "dashboard.rerun.error": "", "dashboard.rerun.triggered": "", "dashboard.resource.apiVersion": "", @@ -233,9 +232,6 @@ "dashboard.sideNav.kubernetesResources": "", "dashboard.sideNav.tektonResources": "", "dashboard.skipToContent": "", - "dashboard.start.button": "", - "dashboard.start.error": "", - "dashboard.start.triggered": "", "dashboard.startPipelineRun.actionText": "", "dashboard.step.definitionNotAvailable": "Descrição: definição de etapa não disponível", "dashboard.step.statusNotAvailable": "Nenhum status disponível", diff --git a/src/nls/messages_zh-Hans.json b/src/nls/messages_zh-Hans.json index e6b5d7df1..0b384776a 100644 --- a/src/nls/messages_zh-Hans.json +++ b/src/nls/messages_zh-Hans.json @@ -206,7 +206,6 @@ "dashboard.pod.resource": "资源", "dashboard.pod.resource.empty": "等待pod资源", "dashboard.rerun.actionText": "重新运行", - "dashboard.rerun.button": "重新运行", "dashboard.rerun.error": "重新运行 {runName} 时发生错误:检查 Dashboard 日志以了解详情。状态代码:{statusCode}", "dashboard.rerun.triggered": "已触发的重新运行", "dashboard.resource.apiVersion": "", @@ -233,9 +232,6 @@ "dashboard.sideNav.kubernetesResources": "Kubernetes 资源", "dashboard.sideNav.tektonResources": "Tekton 资源", "dashboard.skipToContent": "跳转到主要内容", - "dashboard.start.button": "", - "dashboard.start.error": "", - "dashboard.start.triggered": "", "dashboard.startPipelineRun.actionText": "", "dashboard.step.definitionNotAvailable": "步骤定义不可用", "dashboard.step.statusNotAvailable": "状态不可用", diff --git a/src/nls/messages_zh-Hant.json b/src/nls/messages_zh-Hant.json index c22d776ad..0b436d17a 100644 --- a/src/nls/messages_zh-Hant.json +++ b/src/nls/messages_zh-Hant.json @@ -206,7 +206,6 @@ "dashboard.pod.resource": "", "dashboard.pod.resource.empty": "", "dashboard.rerun.actionText": "", - "dashboard.rerun.button": "", "dashboard.rerun.error": "", "dashboard.rerun.triggered": "", "dashboard.resource.apiVersion": "", @@ -233,9 +232,6 @@ "dashboard.sideNav.kubernetesResources": "", "dashboard.sideNav.tektonResources": "", "dashboard.skipToContent": "", - "dashboard.start.button": "", - "dashboard.start.error": "", - "dashboard.start.triggered": "", "dashboard.startPipelineRun.actionText": "", "dashboard.step.definitionNotAvailable": "說明:步驟定義無法使用", "dashboard.step.statusNotAvailable": "狀態無法使用", diff --git a/src/scss/App.scss b/src/scss/App.scss index bb5fa4cc5..fb7646aed 100644 --- a/src/scss/App.scss +++ b/src/scss/App.scss @@ -41,7 +41,6 @@ limitations under the License. @import '@tektoncd/dashboard-components/src/components/Param/Param.scss'; @import '@tektoncd/dashboard-components/src/components/PipelineRuns/PipelineRuns.scss'; @import '@tektoncd/dashboard-components/src/components/ResourceTable/ResourceTable.scss'; -@import '@tektoncd/dashboard-components/src/components/RunAction/RunAction.scss'; @import '@tektoncd/dashboard-components/src/components/RunHeader/RunHeader.scss'; @import '@tektoncd/dashboard-components/src/components/Spinner/Spinner.scss'; @import '@tektoncd/dashboard-components/src/components/StatusIcon/StatusIcon.scss';