diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2023-02-22 00:12:50 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2023-02-22 00:12:50 +0300 |
commit | f4d7e9a45a72f0a12a06ad6131afcd77428a0f66 (patch) | |
tree | 406253c6da999b0b40fc50b014395c8b0c1239de | |
parent | 10c562403c5e2b842e1e833db80f890295fc2b40 (diff) |
Add latest changes from gitlab-org/gitlab@master
30 files changed, 319 insertions, 147 deletions
diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION index 2577208bfe5..3017301fdeb 100644 --- a/GITALY_SERVER_VERSION +++ b/GITALY_SERVER_VERSION @@ -1 +1 @@ -87304fdbc72c480c9173b9e181eab885069e5399 +3c8e91098f09d1f896d4ca9f4e74b3faa1768bbf diff --git a/app/assets/javascripts/issues/constants.js b/app/assets/javascripts/issues/constants.js index ba05dd731f7..9f8b876a629 100644 --- a/app/assets/javascripts/issues/constants.js +++ b/app/assets/javascripts/issues/constants.js @@ -7,6 +7,7 @@ export const STATUS_REOPENED = 'reopened'; export const TITLE_LENGTH_MAX = 255; export const TYPE_EPIC = 'epic'; +export const TYPE_INCIDENT = 'incident'; export const TYPE_ISSUE = 'issue'; export const IssuableStatusText = { diff --git a/app/assets/javascripts/issues/index.js b/app/assets/javascripts/issues/index.js index 5b5f1d273d0..83387d3ac29 100644 --- a/app/assets/javascripts/issues/index.js +++ b/app/assets/javascripts/issues/index.js @@ -6,7 +6,7 @@ import ShortcutsNavigation from '~/behaviors/shortcuts/shortcuts_navigation'; import GLForm from '~/gl_form'; import { initIssuableHeaderWarnings, initIssuableSidebar } from '~/issuable'; import IssuableTemplateSelectors from '~/issuable/issuable_template_selectors'; -import { IssueType } from '~/issues/constants'; +import { TYPE_INCIDENT } from '~/issues/constants'; import Issue from '~/issues/issue'; import { initTitleSuggestions, initTypePopover } from '~/issues/new'; import { initRelatedMergeRequests } from '~/issues/related_merge_requests'; @@ -59,11 +59,11 @@ export function initShow() { const { issueType, ...issuableData } = parseIssuableData(el); - if (issueType === IssueType.Incident) { + if (issueType === TYPE_INCIDENT) { initIncidentApp({ ...issuableData, issuableId: el.dataset.issuableId }, store); - initHeaderActions(store, IssueType.Incident); + initHeaderActions(store, TYPE_INCIDENT); initLinkedResources(); - initRelatedIssues(IssueType.Incident); + initRelatedIssues(TYPE_INCIDENT); } else { initIssueApp(issuableData, store); initHeaderActions(store); diff --git a/app/assets/javascripts/issues/show/components/app.vue b/app/assets/javascripts/issues/show/components/app.vue index ba1ffaf874d..ffa6607a355 100644 --- a/app/assets/javascripts/issues/show/components/app.vue +++ b/app/assets/javascripts/issues/show/components/app.vue @@ -6,6 +6,7 @@ import { IssuableStatusText, STATUS_CLOSED, TYPE_EPIC, + TYPE_INCIDENT, TYPE_ISSUE, WorkspaceType, } from '~/issues/constants'; @@ -13,7 +14,7 @@ import Poll from '~/lib/utils/poll'; import { visitUrl } from '~/lib/utils/url_utility'; import { __, sprintf } from '~/locale'; import ConfidentialityBadge from '~/vue_shared/components/confidentiality_badge.vue'; -import { ISSUE_TYPE_PATH, INCIDENT_TYPE_PATH, INCIDENT_TYPE, POLLING_DELAY } from '../constants'; +import { ISSUE_TYPE_PATH, INCIDENT_TYPE_PATH, POLLING_DELAY } from '../constants'; import eventHub from '../event_hub'; import getIssueStateQuery from '../queries/get_issue_state.query.graphql'; import Service from '../services/index'; @@ -402,14 +403,14 @@ export default { .then((data) => { if ( !window.location.pathname.includes(data.web_url) && - issueState.issueType !== INCIDENT_TYPE + issueState.issueType !== TYPE_INCIDENT ) { visitUrl(data.web_url); } if (issueState.isDirty) { const URI = - issueState.issueType === INCIDENT_TYPE + issueState.issueType === TYPE_INCIDENT ? data.web_url.replace(ISSUE_TYPE_PATH, INCIDENT_TYPE_PATH) : data.web_url; visitUrl(URI); diff --git a/app/assets/javascripts/issues/show/components/fields/type.vue b/app/assets/javascripts/issues/show/components/fields/type.vue index 5ade1a86d30..eb3e91ff1ae 100644 --- a/app/assets/javascripts/issues/show/components/fields/type.vue +++ b/app/assets/javascripts/issues/show/components/fields/type.vue @@ -1,8 +1,8 @@ <script> import { GlFormGroup, GlIcon, GlListbox } from '@gitlab/ui'; -import { TYPE_ISSUE } from '~/issues/constants'; +import { TYPE_INCIDENT, TYPE_ISSUE } from '~/issues/constants'; import { __ } from '~/locale'; -import { issuableTypes, INCIDENT_TYPE } from '../../constants'; +import { issuableTypes } from '../../constants'; import getIssueStateQuery from '../../queries/get_issue_state.query.graphql'; import updateIssueStateMutation from '../../queries/update_issue_state.mutation.graphql'; @@ -46,7 +46,7 @@ export default { }, computed: { shouldShowIncident() { - return this.issueType === INCIDENT_TYPE || this.canCreateIncident; + return this.issueType === TYPE_INCIDENT || this.canCreateIncident; }, }, methods: { @@ -60,7 +60,7 @@ export default { }); }, isShown(type) { - return type.value !== INCIDENT_TYPE || this.shouldShowIncident; + return type.value !== TYPE_INCIDENT || this.shouldShowIncident; }, }, }; diff --git a/app/assets/javascripts/issues/show/components/header_actions.vue b/app/assets/javascripts/issues/show/components/header_actions.vue index 9d92b5cf954..ee2468e6c40 100644 --- a/app/assets/javascripts/issues/show/components/header_actions.vue +++ b/app/assets/javascripts/issues/show/components/header_actions.vue @@ -12,7 +12,7 @@ import { import { mapActions, mapGetters, mapState } from 'vuex'; import { createAlert, VARIANT_SUCCESS } from '~/flash'; import { EVENT_ISSUABLE_VUE_APP_CHANGE } from '~/issuable/constants'; -import { IssueType, STATUS_CLOSED } from '~/issues/constants'; +import { IssueType, STATUS_CLOSED, TYPE_INCIDENT } from '~/issues/constants'; import { ISSUE_STATE_EVENT_CLOSE, ISSUE_STATE_EVENT_REOPEN } from '~/issues/show/constants'; import { capitalizeFirstCharacter } from '~/lib/utils/text_utility'; import { visitUrl } from '~/lib/utils/url_utility'; @@ -119,7 +119,7 @@ export default { issueTypeText() { const issueTypeTexts = { [IssueType.Issue]: s__('HeaderAction|issue'), - [IssueType.Incident]: s__('HeaderAction|incident'), + [TYPE_INCIDENT]: s__('HeaderAction|incident'), }; return issueTypeTexts[this.issueType] ?? this.issueType; diff --git a/app/assets/javascripts/issues/show/constants.js b/app/assets/javascripts/issues/show/constants.js index a100aaf88ad..4d8c11f9669 100644 --- a/app/assets/javascripts/issues/show/constants.js +++ b/app/assets/javascripts/issues/show/constants.js @@ -1,6 +1,5 @@ import { __ } from '~/locale'; -export const INCIDENT_TYPE = 'incident'; export const INCIDENT_TYPE_PATH = 'issues/incident'; export const ISSUE_STATE_EVENT_CLOSE = 'CLOSE'; export const ISSUE_STATE_EVENT_REOPEN = 'REOPEN'; diff --git a/app/assets/javascripts/issues/show/index.js b/app/assets/javascripts/issues/show/index.js index c4a5c882097..b89ee3cf22e 100644 --- a/app/assets/javascripts/issues/show/index.js +++ b/app/assets/javascripts/issues/show/index.js @@ -2,13 +2,14 @@ import Vue from 'vue'; import { mapGetters } from 'vuex'; import errorTrackingStore from '~/error_tracking/store'; import { apolloProvider } from '~/graphql_shared/issuable_client'; +import { TYPE_INCIDENT } from '~/issues/constants'; import { parseBoolean } from '~/lib/utils/common_utils'; import { scrollToTargetOnResize } from '~/lib/utils/resize_observer'; import IssueApp from './components/app.vue'; import HeaderActions from './components/header_actions.vue'; import IncidentTabs from './components/incidents/incident_tabs.vue'; import SentryErrorStackTrace from './components/sentry_error_stack_trace.vue'; -import { INCIDENT_TYPE, issueState } from './constants'; +import { issueState } from './constants'; import getIssueStateQuery from './queries/get_issue_state.query.graphql'; const bootstrapApollo = (state = {}) => { @@ -51,7 +52,7 @@ export function initIncidentApp(issueData = {}, store) { apolloProvider, store, provide: { - issueType: INCIDENT_TYPE, + issueType: TYPE_INCIDENT, canCreateIncident, canUpdateTimelineEvent, canUpdate, @@ -141,7 +142,7 @@ export function initHeaderActions(store, type = '') { bootstrapApollo({ ...issueState, issueType: el.dataset.issueType }); const canCreate = - type === INCIDENT_TYPE ? el.dataset.canCreateIncident : el.dataset.canCreateIssue; + type === TYPE_INCIDENT ? el.dataset.canCreateIncident : el.dataset.canCreateIssue; return new Vue({ el, diff --git a/app/assets/javascripts/jobs/components/job/graphql/fragments/ci_job.fragment.graphql b/app/assets/javascripts/jobs/components/job/graphql/fragments/ci_job.fragment.graphql new file mode 100644 index 00000000000..f4a0b10672e --- /dev/null +++ b/app/assets/javascripts/jobs/components/job/graphql/fragments/ci_job.fragment.graphql @@ -0,0 +1,11 @@ +#import "~/jobs/components/job/graphql/fragments/ci_variable.fragment.graphql" + +fragment BaseCiJob on CiJob { + id + manualVariables { + nodes { + ...ManualCiVariable + } + } + __typename +} diff --git a/app/assets/javascripts/jobs/components/job/graphql/fragments/ci_variable.fragment.graphql b/app/assets/javascripts/jobs/components/job/graphql/fragments/ci_variable.fragment.graphql new file mode 100644 index 00000000000..0479df7bc4c --- /dev/null +++ b/app/assets/javascripts/jobs/components/job/graphql/fragments/ci_variable.fragment.graphql @@ -0,0 +1,6 @@ +fragment ManualCiVariable on CiVariable { + __typename + id + key + value +} diff --git a/app/assets/javascripts/jobs/components/job/graphql/mutations/job_play_with_variables.mutation.graphql b/app/assets/javascripts/jobs/components/job/graphql/mutations/job_play_with_variables.mutation.graphql new file mode 100644 index 00000000000..520deef5136 --- /dev/null +++ b/app/assets/javascripts/jobs/components/job/graphql/mutations/job_play_with_variables.mutation.graphql @@ -0,0 +1,11 @@ +#import "~/jobs/components/job/graphql/fragments/ci_job.fragment.graphql" + +mutation playJobWithVariables($id: CiBuildID!, $variables: [CiVariableInput!]) { + jobPlay(input: { id: $id, variables: $variables }) { + job { + ...BaseCiJob + webPath + } + errors + } +} diff --git a/app/assets/javascripts/jobs/components/job/graphql/mutations/job_retry_with_variables.mutation.graphql b/app/assets/javascripts/jobs/components/job/graphql/mutations/job_retry_with_variables.mutation.graphql index 2b79892a072..e35d603ea71 100644 --- a/app/assets/javascripts/jobs/components/job/graphql/mutations/job_retry_with_variables.mutation.graphql +++ b/app/assets/javascripts/jobs/components/job/graphql/mutations/job_retry_with_variables.mutation.graphql @@ -1,14 +1,9 @@ +#import "~/jobs/components/job/graphql/fragments/ci_job.fragment.graphql" + mutation retryJobWithVariables($id: CiBuildID!, $variables: [CiVariableInput!]) { jobRetry(input: { id: $id, variables: $variables }) { job { - id - manualVariables { - nodes { - id - key - value - } - } + ...BaseCiJob webPath } errors diff --git a/app/assets/javascripts/jobs/components/job/graphql/queries/get_job.query.graphql b/app/assets/javascripts/jobs/components/job/graphql/queries/get_job.query.graphql index aaf1dec8e0f..95e3521091d 100644 --- a/app/assets/javascripts/jobs/components/job/graphql/queries/get_job.query.graphql +++ b/app/assets/javascripts/jobs/components/job/graphql/queries/get_job.query.graphql @@ -1,16 +1,11 @@ +#import "~/jobs/components/job/graphql/fragments/ci_job.fragment.graphql" + query getJob($fullPath: ID!, $id: JobID!) { project(fullPath: $fullPath) { id job(id: $id) { - id + ...BaseCiJob manualJob - manualVariables { - nodes { - id - key - value - } - } name } } diff --git a/app/assets/javascripts/jobs/components/job/manual_variables_form.vue b/app/assets/javascripts/jobs/components/job/manual_variables_form.vue index 763eb6705aa..df37cd1f585 100644 --- a/app/assets/javascripts/jobs/components/job/manual_variables_form.vue +++ b/app/assets/javascripts/jobs/components/job/manual_variables_form.vue @@ -10,7 +10,6 @@ import { GlTooltipDirective, } from '@gitlab/ui'; import { cloneDeep, uniqueId } from 'lodash'; -import { mapActions } from 'vuex'; import { fetchPolicies } from '~/lib/graphql'; import { createAlert } from '~/flash'; import { TYPENAME_CI_BUILD, TYPENAME_COMMIT_STATUS } from '~/graphql_shared/constants'; @@ -19,7 +18,9 @@ import { JOB_GRAPHQL_ERRORS } from '~/jobs/constants'; import { helpPagePath } from '~/helpers/help_page_helper'; import { redirectTo } from '~/lib/utils/url_utility'; import { s__ } from '~/locale'; +import { reportMessageToSentry } from '~/jobs/utils'; import GetJob from './graphql/queries/get_job.query.graphql'; +import playJobWithVariablesMutation from './graphql/mutations/job_play_with_variables.mutation.graphql'; import retryJobWithVariablesMutation from './graphql/mutations/job_retry_with_variables.mutation.graphql'; // This component is a port of ~/jobs/components/job/legacy_manual_variables_form.vue @@ -54,8 +55,9 @@ export default { const jobVariables = cloneDeep(data?.project?.job?.manualVariables?.nodes); return [...jobVariables.reverse(), ...this.variables]; }, - error() { + error(error) { createAlert({ message: JOB_GRAPHQL_ERRORS.jobQueryErrorText }); + reportMessageToSentry(this.$options.name, error, {}); }, }, }, @@ -75,6 +77,7 @@ export default { value: 'value', }, i18n: { + cancel: s__('CiVariables|Cancel'), clearInputs: s__('CiVariables|Clear inputs'), formHelpText: s__( 'CiVariables|Specify variable values to be used in this run. The variables specified in the configuration file and %{linkStart}CI/CD settings%{linkEnd} are used by default.', @@ -86,14 +89,10 @@ export default { keyLabel: s__('CiVariables|Key'), keyPlaceholder: s__('CiVariables|Input variable key'), runAgainButtonText: s__('CiVariables|Run job again'), - triggerButtonText: s__('CiVariables|Run job'), + runButtonText: s__('CiVariables|Run job'), valueLabel: s__('CiVariables|Value'), valuePlaceholder: s__('CiVariables|Input variable value'), }, - variableValueKeys: { - rest: 'secret_value', - gql: 'value', - }, data() { return { job: {}, @@ -104,30 +103,63 @@ export default { value: '', }, ], - runAgainBtnDisabled: false, - triggerBtnDisabled: false, + runBtnDisabled: false, }; }, computed: { + mutationVariables() { + return { + id: convertToGraphQLId(TYPENAME_CI_BUILD, this.jobId), + variables: this.preparedVariables, + }; + }, preparedVariables() { - // filtering out 'id' along with empty variables to send only key, value in the mutation. - // This will be removed in: https://gitlab.com/gitlab-org/gitlab/-/issues/377268 - return this.variables .filter((variable) => variable.key !== '') - .map(({ key, value }) => ({ key, [this.valueKey]: value })); + .map(({ key, value }) => ({ key, value })); }, - valueKey() { + runBtnText() { return this.isRetryable - ? this.$options.variableValueKeys.gql - : this.$options.variableValueKeys.rest; + ? this.$options.i18n.runAgainButtonText + : this.$options.i18n.runButtonText; }, variableSettings() { return helpPagePath('ci/variables/index', { anchor: 'add-a-cicd-variable-to-a-project' }); }, }, methods: { - ...mapActions(['triggerManualJob']), + async playJob() { + try { + const { data } = await this.$apollo.mutate({ + mutation: playJobWithVariablesMutation, + variables: this.mutationVariables, + }); + if (data.jobPlay?.errors?.length) { + createAlert({ message: data.jobPlay.errors[0] }); + } else { + this.navigateToJob(data.jobPlay?.job?.webPath); + } + } catch (error) { + createAlert({ message: JOB_GRAPHQL_ERRORS.jobMutationErrorText }); + reportMessageToSentry(this.$options.name, error, {}); + } + }, + async retryJob() { + try { + const { data } = await this.$apollo.mutate({ + mutation: retryJobWithVariablesMutation, + variables: this.mutationVariables, + }); + if (data.jobRetry?.errors?.length) { + createAlert({ message: data.jobRetry.errors[0] }); + } else { + this.navigateToJob(data.jobRetry?.job?.webPath); + } + } catch (error) { + createAlert({ message: JOB_GRAPHQL_ERRORS.jobMutationErrorText }); + reportMessageToSentry(this.$options.name, error, {}); + } + }, addEmptyVariable() { const lastVar = this.variables[this.variables.length - 1]; @@ -153,37 +185,17 @@ export default { inputRef(type, id) { return `${this.$options.inputTypes[type]}-${id}`; }, - navigateToRetriedJob(retryPath) { - redirectTo(retryPath); + navigateToJob(path) { + redirectTo(path); }, - async retryJob() { - try { - const { data } = await this.$apollo.mutate({ - mutation: retryJobWithVariablesMutation, - variables: { - id: convertToGraphQLId(TYPENAME_CI_BUILD, this.jobId), - // we need to ensure no empty variables are passed to the API - variables: this.preparedVariables, - }, - }); - if (data.jobRetry?.errors?.length) { - createAlert({ message: data.jobRetry.errors[0] }); - } else { - this.navigateToRetriedJob(data.jobRetry?.job?.webPath); - } - } catch (error) { - createAlert({ message: JOB_GRAPHQL_ERRORS.retryMutationErrorText }); - } - }, - runAgain() { - this.runAgainBtnDisabled = true; - - this.retryJob(); - }, - triggerJob() { - this.triggerBtnDisabled = true; + runJob() { + this.runBtnDisabled = true; - this.triggerManualJob(this.preparedVariables); + if (this.isRetryable) { + this.retryJob(); + } else { + this.playJob(); + } }, }, }; @@ -271,37 +283,23 @@ export default { </template> </gl-sprintf> </div> - <div v-if="isRetryable" class="gl-display-flex gl-justify-content-center gl-mt-5"> + <div class="gl-display-flex gl-justify-content-center gl-mt-5"> <gl-button + v-if="isRetryable" class="gl-mt-5" - :aria-label="__('Cancel')" data-testid="cancel-btn" @click="$emit('hideManualVariablesForm')" - >{{ __('Cancel') }}</gl-button + >{{ $options.i18n.cancel }}</gl-button > <gl-button class="gl-mt-5" variant="confirm" category="primary" - :aria-label="__('Run manual job again')" - :disabled="runAgainBtnDisabled" + :disabled="runBtnDisabled" data-testid="run-manual-job-btn" - @click="runAgain" - > - {{ $options.i18n.runAgainButtonText }} - </gl-button> - </div> - <div v-else class="gl-display-flex gl-justify-content-center gl-mt-5"> - <gl-button - class="gl-mt-5" - variant="confirm" - category="primary" - :aria-label="__('Trigger manual job')" - :disabled="triggerBtnDisabled" - data-testid="trigger-manual-job-btn" - @click="triggerJob" + @click="runJob" > - {{ $options.i18n.triggerButtonText }} + {{ runBtnText }} </gl-button> </div> </div> diff --git a/app/assets/javascripts/jobs/constants.js b/app/assets/javascripts/jobs/constants.js index 027d896ba0e..40b3de7edd9 100644 --- a/app/assets/javascripts/jobs/constants.js +++ b/app/assets/javascripts/jobs/constants.js @@ -19,7 +19,7 @@ export const JOB_SIDEBAR_COPY = { }; export const JOB_GRAPHQL_ERRORS = { - retryMutationErrorText: __('There was an error running the job. Please try again.'), + jobMutationErrorText: __('There was an error running the job. Please try again.'), jobQueryErrorText: __('There was an error fetching the job.'), }; diff --git a/app/assets/javascripts/jobs/store/actions.js b/app/assets/javascripts/jobs/store/actions.js index af2d720643f..3e9d6ae188a 100644 --- a/app/assets/javascripts/jobs/store/actions.js +++ b/app/assets/javascripts/jobs/store/actions.js @@ -22,7 +22,7 @@ export const init = ({ dispatch }, { endpoint, logState, pagePath }) => { pagePath, }); - return Promise.all([dispatch('fetchJob')]); + return dispatch('fetchJob'); }; export const setJobEndpoint = ({ commit }, endpoint) => commit(types.SET_JOB_ENDPOINT, endpoint); diff --git a/app/assets/javascripts/related_issues/constants.js b/app/assets/javascripts/related_issues/constants.js index 2a4ce70511b..2e7fbde78b5 100644 --- a/app/assets/javascripts/related_issues/constants.js +++ b/app/assets/javascripts/related_issues/constants.js @@ -1,5 +1,5 @@ import { __, sprintf } from '~/locale'; -import { TYPE_EPIC, TYPE_ISSUE } from '~/issues/constants'; +import { TYPE_EPIC, TYPE_INCIDENT, TYPE_ISSUE } from '~/issues/constants'; export const issuableTypesMap = { ISSUE: 'issue', @@ -27,7 +27,7 @@ export const autoCompleteTextMap = { { emphasisStart: '<', emphasisEnd: '>' }, false, ), - [issuableTypesMap.INCIDENT]: sprintf( + [TYPE_INCIDENT]: sprintf( __(' or %{emphasisStart}#id%{emphasisEnd}'), { emphasisStart: '<', emphasisEnd: '>' }, false, @@ -52,7 +52,7 @@ export const autoCompleteTextMap = { export const inputPlaceholderTextMap = { [TYPE_ISSUE]: __('Paste issue link'), - [issuableTypesMap.INCIDENT]: __('Paste link'), + [TYPE_INCIDENT]: __('Paste link'), [TYPE_EPIC]: __('Paste epic link'), [issuableTypesMap.MERGE_REQUEST]: __('Enter merge request URLs'), }; @@ -96,7 +96,7 @@ export const addRelatedItemErrorMap = { */ export const issuableIconMap = { [TYPE_ISSUE]: 'issues', - [issuableTypesMap.INCIDENT]: 'issues', + [TYPE_INCIDENT]: 'issues', [TYPE_EPIC]: 'epic', }; @@ -107,13 +107,13 @@ export const PathIdSeparator = { export const issuablesBlockHeaderTextMap = { [TYPE_ISSUE]: __('Linked items'), - [issuableTypesMap.INCIDENT]: __('Linked incidents or issues'), + [TYPE_INCIDENT]: __('Linked incidents or issues'), [TYPE_EPIC]: __('Linked epics'), }; export const issuablesBlockHelpTextMap = { [TYPE_ISSUE]: __('Learn more about linking issues'), - [issuableTypesMap.INCIDENT]: __('Learn more about linking issues and incidents'), + [TYPE_INCIDENT]: __('Learn more about linking issues and incidents'), [TYPE_EPIC]: __('Learn more about linking epics'), }; @@ -124,12 +124,12 @@ export const issuablesBlockAddButtonTextMap = { export const issuablesFormCategoryHeaderTextMap = { [TYPE_ISSUE]: __('The current issue'), - [issuableTypesMap.INCIDENT]: __('The current incident'), + [TYPE_INCIDENT]: __('The current incident'), [TYPE_EPIC]: __('The current epic'), }; export const issuablesFormInputTextMap = { [TYPE_ISSUE]: __('the following issues'), - [issuableTypesMap.INCIDENT]: __('the following incidents or issues'), + [TYPE_INCIDENT]: __('the following incidents or issues'), [TYPE_EPIC]: __('the following epics'), }; diff --git a/app/assets/javascripts/sidebar/components/severity/sidebar_severity_widget.vue b/app/assets/javascripts/sidebar/components/severity/sidebar_severity_widget.vue index ecb9a2809a0..17e0debe71f 100644 --- a/app/assets/javascripts/sidebar/components/severity/sidebar_severity_widget.vue +++ b/app/assets/javascripts/sidebar/components/severity/sidebar_severity_widget.vue @@ -1,9 +1,10 @@ <script> import { GlDropdown, GlDropdownItem, GlTooltip, GlSprintf } from '@gitlab/ui'; import { createAlert } from '~/flash'; +import { TYPE_INCIDENT } from '~/issues/constants'; import SidebarEditableItem from '~/sidebar/components/sidebar_editable_item.vue'; import updateIssuableSeverity from '../../queries/update_issuable_severity.mutation.graphql'; -import { INCIDENT_SEVERITY, ISSUABLE_TYPES, SEVERITY_I18N as I18N } from '../../constants'; +import { INCIDENT_SEVERITY, SEVERITY_I18N as I18N } from '../../constants'; import SeverityToken from './severity.vue'; export default { @@ -34,10 +35,10 @@ export default { issuableType: { type: String, required: false, - default: ISSUABLE_TYPES.INCIDENT, + default: TYPE_INCIDENT, validator: (value) => { // currently severity is supported only for incidents, but this list might be extended - return [ISSUABLE_TYPES.INCIDENT].includes(value); + return [TYPE_INCIDENT].includes(value); }, }, }, @@ -50,7 +51,7 @@ export default { computed: { severitiesList() { switch (this.issuableType) { - case ISSUABLE_TYPES.INCIDENT: + case TYPE_INCIDENT: return Object.values(INCIDENT_SEVERITY); default: return []; diff --git a/app/assets/javascripts/sidebar/constants.js b/app/assets/javascripts/sidebar/constants.js index 14491226b15..c9a50696fe5 100644 --- a/app/assets/javascripts/sidebar/constants.js +++ b/app/assets/javascripts/sidebar/constants.js @@ -407,10 +407,6 @@ export const INCIDENT_SEVERITY = { }, }; -export const ISSUABLE_TYPES = { - INCIDENT: 'incident', -}; - export const MILESTONE_STATE = { ACTIVE: 'active', CLOSED: 'closed', diff --git a/data/deprecations/15-9-deprecate-ci-pre-clone-script.yml b/data/deprecations/15-9-deprecate-ci-pre-clone-script.yml new file mode 100644 index 00000000000..72c33915ae9 --- /dev/null +++ b/data/deprecations/15-9-deprecate-ci-pre-clone-script.yml @@ -0,0 +1,23 @@ +- title: "Deprecation and planned removal for `CI_PRE_CLONE_SCRIPT` variable on GitLab SaaS" # (required) Clearly explain the change, or planned change. For example, "The `confidential` field for a `Note` is deprecated" or "CI/CD job names will be limited to 250 characters." + announcement_milestone: "15.9" # (required) The milestone when this feature was first announced as deprecated. + removal_milestone: "15.11" # (required) The milestone when this feature is planned to be removed + breaking_change: true # (required) Change to false if this is not a breaking change. + reporter: DarrenEastman # (required) GitLab username of the person reporting the change + stage: Verify # (required) String value of the stage that the feature was created in. e.g., Growth + issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/391896 # (required) Link to the deprecation issue in GitLab + body: | # (required) Do not modify this line, instead modify the lines below. + The [`CI_PRE_CLONE_SCRIPT` variable](https://docs.gitlab.com/ee/ci/runners/saas/linux_saas_runner.html#pre-clone-script) supported by GitLab SaaS Runners is deprecated as of GitLab 15.9 and will be removed in 15.11. The `CI_PRE_CLONE_SCRIPT` variable enables you to run commands in your CI/CD job prior to the runner executing Git init and get fetch. For more information about how this feature works, see [Pre-clone script](https://docs.gitlab.com/ee/ci/runners/saas/linux_saas_runner.html#pre-clone-script). As an alternative, you can use the [`pre_get_sources_script`](https://docs.gitlab.com/ee/ci/yaml/#hookspre_get_sources_script). + # + # OPTIONAL END OF SUPPORT FIELDS + # + # If an End of Support period applies, the announcement should be shared with GitLab Support + # in the `#spt_managers` channel in Slack, and mention `@gitlab-com/support` in this MR. + # + end_of_support_milestone: 15.11 # (optional) Use "XX.YY" format. The milestone when support for this feature will end. + # + # OTHER OPTIONAL FIELDS + # + tiers: Free, Premium, Ultimate # (optional - may be required in the future) An array of tiers that the feature is available in currently. e.g., [Free, Silver, Gold, Core, Premium, Ultimate] + documentation_url: https://docs.gitlab.com/ee/ci/runners/saas/linux_saas_runner.html#pre-clone-script # (optional) This is a link to the current documentation page + image_url: # (optional) This is a link to a thumbnail image depicting the feature + video_url: # (optional) Use the youtube thumbnail URL with the structure of https://img.youtube.com/vi/UNIQUEID/hqdefault.jpg diff --git a/data/removals/15_9/15-9-live-preview.yml b/data/removals/15_9/15-9-live-preview.yml new file mode 100644 index 00000000000..139d7829d72 --- /dev/null +++ b/data/removals/15_9/15-9-live-preview.yml @@ -0,0 +1,11 @@ +- title: "Live Preview no longer available in the Web IDE" # (required) Clearly explain the change. For example, "The `confidential` field for a `Note` is removed" or "CI/CD job names are limited to 250 characters." + announcement_milestone: "15.8" # (required) The milestone when this feature was deprecated. + announcement_date: "2023-01-22" # (required) The date of the milestone release when this feature was deprecated. This should almost always be the 22nd of a month (YYYY-MM-DD), unless you did an out of band blog post. + removal_milestone: "15.9" # (required) The milestone when this feature is being removed. + removal_date: "2023-02-22" # (required) This should almost always be the 22nd of a month (YYYY-MM-DD), the date of the milestone release when this feature will be removed. + breaking_change: true # (required) Change to false if this is not a breaking change. + reporter: ericschurter # (required) GitLab username of the person reporting the removal + stage: create # (required) String value of the stage that the feature was created in. e.g., Growth + issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/383889 # (required) Link to the deprecation issue in GitLab + body: | # (required) Do not modify this line, instead modify the lines below. + The Live Preview feature of the Web IDE was intended to provide a client-side preview of static web applications. However, complex configuration steps and a narrow set of supported project types have limited its utility. With the introduction of the Web IDE Beta in GitLab 15.7, you can now connect to a full server-side runtime environment. With upcoming support for installing extensions in the Web IDE, we’ll also support more advanced workflows than those available with Live Preview. As of GitLab 15.9, Live Preview is no longer available in the Web IDE. diff --git a/doc/development/migration_style_guide.md b/doc/development/migration_style_guide.md index 20861c3e604..fa5eca8ce06 100644 --- a/doc/development/migration_style_guide.md +++ b/doc/development/migration_style_guide.md @@ -1,6 +1,6 @@ --- -stage: none -group: unassigned +stage: Data Stores +group: Database info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments --- diff --git a/doc/update/deprecations.md b/doc/update/deprecations.md index 93fbc3615c1..7293fc05410 100644 --- a/doc/update/deprecations.md +++ b/doc/update/deprecations.md @@ -110,6 +110,21 @@ In 16.0, this inbound scope limit will be the only option available for all proj </div> +<div class="deprecation removal-1511 breaking-change"> + +### Deprecation and planned removal for `CI_PRE_CLONE_SCRIPT` variable on GitLab SaaS + +End of Support: GitLab <span class="removal-milestone">15.11</span> <span class="support-end-date"></span><br /> +Planned removal: GitLab <span class="removal-milestone">15.11</span> <span class="removal-date"></span> + +WARNING: +This is a [breaking change](https://docs.gitlab.com/ee/development/deprecation_guidelines/). +Review the details carefully before upgrading. + +The [`CI_PRE_CLONE_SCRIPT` variable](https://docs.gitlab.com/ee/ci/runners/saas/linux_saas_runner.html#pre-clone-script) supported by GitLab SaaS Runners is deprecated as of GitLab 15.9 and will be removed in 15.11. The `CI_PRE_CLONE_SCRIPT` variable enables you to run commands in your CI/CD job prior to the runner executing Git init and get fetch. For more information about how this feature works, see [Pre-clone script](https://docs.gitlab.com/ee/ci/runners/saas/linux_saas_runner.html#pre-clone-script). As an alternative, you can use the [`pre_get_sources_script`](https://docs.gitlab.com/ee/ci/yaml/#hookspre_get_sources_script). + +</div> + <div class="deprecation removal-160 breaking-change"> ### Development dependencies reported for PHP and Python diff --git a/doc/update/removals.md b/doc/update/removals.md index ae42ba53fd5..110a3ee29e8 100644 --- a/doc/update/removals.md +++ b/doc/update/removals.md @@ -34,6 +34,16 @@ For removal reviewers (Technical Writers only): https://about.gitlab.com/handbook/marketing/blog/release-posts/#update-the-removals-doc --> +## Removed in 15.9 + +### Live Preview no longer available in the Web IDE + +WARNING: +This is a [breaking change](https://docs.gitlab.com/ee/development/deprecation_guidelines/). +Review the details carefully before upgrading. + +The Live Preview feature of the Web IDE was intended to provide a client-side preview of static web applications. However, complex configuration steps and a narrow set of supported project types have limited its utility. With the introduction of the Web IDE Beta in GitLab 15.7, you can now connect to a full server-side runtime environment. With upcoming support for installing extensions in the Web IDE, we’ll also support more advanced workflows than those available with Live Preview. As of GitLab 15.9, Live Preview is no longer available in the Web IDE. + ## Removed in 15.8 ### CiliumNetworkPolicy within the auto deploy Helm chart is removed diff --git a/locale/gitlab.pot b/locale/gitlab.pot index d22d5f9b12f..08990452cb0 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -8827,6 +8827,9 @@ msgstr "" msgid "CiStatus|running" msgstr "" +msgid "CiVariables|Cancel" +msgstr "" + msgid "CiVariables|Cannot use Masked Variable with current value" msgstr "" @@ -36811,9 +36814,6 @@ msgstr "" msgid "Run job" msgstr "" -msgid "Run manual job again" -msgstr "" - msgid "Run manual or delayed jobs" msgstr "" @@ -45313,9 +45313,6 @@ msgstr "" msgid "Trigger job" msgstr "" -msgid "Trigger manual job" -msgstr "" - msgid "Trigger pipelines for mirror updates" msgstr "" diff --git a/spec/features/projects/jobs/user_triggers_manual_job_with_variables_spec.rb b/spec/features/projects/jobs/user_triggers_manual_job_with_variables_spec.rb index a9e0fce1a1c..e4394010e8c 100644 --- a/spec/features/projects/jobs/user_triggers_manual_job_with_variables_spec.rb +++ b/spec/features/projects/jobs/user_triggers_manual_job_with_variables_spec.rb @@ -24,7 +24,7 @@ RSpec.describe 'User triggers manual job with variables', :js, feature_category: find("[data-testid='ci-variable-value']").set('key_value') end - find("[data-testid='trigger-manual-job-btn']").click + find("[data-testid='run-manual-job-btn']").click wait_for_requests diff --git a/spec/frontend/issues/show/components/header_actions_spec.js b/spec/frontend/issues/show/components/header_actions_spec.js index 3d9dad3a721..54d8cceb523 100644 --- a/spec/frontend/issues/show/components/header_actions_spec.js +++ b/spec/frontend/issues/show/components/header_actions_spec.js @@ -4,7 +4,7 @@ import { shallowMount } from '@vue/test-utils'; import Vuex from 'vuex'; import { mockTracking } from 'helpers/tracking_helper'; import { createAlert, VARIANT_SUCCESS } from '~/flash'; -import { IssueType, STATUS_CLOSED, STATUS_OPEN } from '~/issues/constants'; +import { IssueType, STATUS_CLOSED, STATUS_OPEN, TYPE_INCIDENT } from '~/issues/constants'; import DeleteIssueModal from '~/issues/show/components/delete_issue_modal.vue'; import AbuseCategorySelector from '~/abuse_reports/components/abuse_category_selector.vue'; import HeaderActions from '~/issues/show/components/header_actions.vue'; @@ -119,7 +119,7 @@ describe('HeaderActions component', () => { describe.each` issueType ${IssueType.Issue} - ${IssueType.Incident} + ${TYPE_INCIDENT} `('when issue type is $issueType', ({ issueType }) => { describe('close/reopen button', () => { describe.each` diff --git a/spec/frontend/jobs/components/job/manual_variables_form_spec.js b/spec/frontend/jobs/components/job/manual_variables_form_spec.js index a5b3b0e3b47..4b2bf6d3a75 100644 --- a/spec/frontend/jobs/components/job/manual_variables_form_spec.js +++ b/spec/frontend/jobs/components/job/manual_variables_form_spec.js @@ -2,24 +2,28 @@ import { GlSprintf, GlLink } from '@gitlab/ui'; import { createLocalVue } from '@vue/test-utils'; import VueApollo from 'vue-apollo'; import { nextTick } from 'vue'; +import { createAlert } from '~/flash'; import { mountExtended } from 'helpers/vue_test_utils_helper'; import createMockApollo from 'helpers/mock_apollo_helper'; import { TYPENAME_CI_BUILD } from '~/graphql_shared/constants'; +import { JOB_GRAPHQL_ERRORS } from '~/jobs/constants'; import { convertToGraphQLId } from '~/graphql_shared/utils'; import waitForPromises from 'helpers/wait_for_promises'; import { redirectTo } from '~/lib/utils/url_utility'; import ManualVariablesForm from '~/jobs/components/job/manual_variables_form.vue'; import getJobQuery from '~/jobs/components/job/graphql/queries/get_job.query.graphql'; -import retryJobMutation from '~/jobs/components/job/graphql/mutations/job_retry_with_variables.mutation.graphql'; +import playJobMutation from '~/jobs/components/job/graphql/mutations/job_play_with_variables.mutation.graphql'; import { mockFullPath, mockId, mockJobResponse, mockJobWithVariablesResponse, - mockJobMutationData, + mockJobPlayMutationData, + mockJobRetryMutationData, } from './mock_data'; const localVue = createLocalVue(); +jest.mock('~/flash'); localVue.use(VueApollo); jest.mock('~/lib/utils/url_utility', () => ({ @@ -39,9 +43,9 @@ describe('Manual Variables Form', () => { const createComponent = ({ options = {}, props = {} } = {}) => { wrapper = mountExtended(ManualVariablesForm, { propsData: { - ...props, jobId: mockId, - isRetryable: true, + isRetryable: false, + ...props, }, provide: { ...defaultProvide, @@ -71,7 +75,7 @@ describe('Manual Variables Form', () => { const findHelpText = () => wrapper.findComponent(GlSprintf); const findHelpLink = () => wrapper.findComponent(GlLink); const findCancelBtn = () => wrapper.findByTestId('cancel-btn'); - const findRerunBtn = () => wrapper.findByTestId('run-manual-job-btn'); + const findRunBtn = () => wrapper.findByTestId('run-manual-job-btn'); const findDeleteVarBtn = () => wrapper.findByTestId('delete-variable-btn'); const findAllDeleteVarBtns = () => wrapper.findAllByTestId('delete-variable-btn'); const findDeleteVarBtnPlaceholder = () => wrapper.findByTestId('delete-variable-btn-placeholder'); @@ -97,6 +101,7 @@ describe('Manual Variables Form', () => { }); afterEach(() => { + createAlert.mockClear(); wrapper.destroy(); }); @@ -112,10 +117,30 @@ describe('Manual Variables Form', () => { '/help/ci/variables/index#add-a-cicd-variable-to-a-project', ); }); + }); - it('renders buttons', () => { - expect(findCancelBtn().exists()).toBe(true); - expect(findRerunBtn().exists()).toBe(true); + describe('when query is unsuccessful', () => { + beforeEach(async () => { + getJobQueryResponse.mockRejectedValue({}); + await createComponentWithApollo(); + }); + + it('shows an alert with error', () => { + expect(createAlert).toHaveBeenCalledWith({ + message: JOB_GRAPHQL_ERRORS.jobQueryErrorText, + }); + }); + }); + + describe('when job has not been retried', () => { + beforeEach(async () => { + getJobQueryResponse.mockResolvedValue(mockJobWithVariablesResponse); + await createComponentWithApollo(); + }); + + it('does not render the cancel button', () => { + expect(findCancelBtn().exists()).toBe(false); + expect(findRunBtn().exists()).toBe(true); }); }); @@ -135,10 +160,10 @@ describe('Manual Variables Form', () => { }); }); - describe('when mutation fires', () => { + describe('when play mutation fires', () => { beforeEach(async () => { await createComponentWithApollo(); - jest.spyOn(wrapper.vm.$apollo, 'mutate').mockResolvedValue(mockJobMutationData); + jest.spyOn(wrapper.vm.$apollo, 'mutate').mockResolvedValue(mockJobPlayMutationData); }); it('passes variables in correct format', async () => { @@ -146,11 +171,11 @@ describe('Manual Variables Form', () => { await findCiVariableValue().setValue('new value'); - await findRerunBtn().vm.$emit('click'); + await findRunBtn().vm.$emit('click'); expect(wrapper.vm.$apollo.mutate).toHaveBeenCalledTimes(1); expect(wrapper.vm.$apollo.mutate).toHaveBeenCalledWith({ - mutation: retryJobMutation, + mutation: playJobMutation, variables: { id: convertToGraphQLId(TYPENAME_CI_BUILD, mockId), variables: [ @@ -163,13 +188,63 @@ describe('Manual Variables Form', () => { }); }); - // redirect to job after initial trigger assertion will be added in https://gitlab.com/gitlab-org/gitlab/-/issues/377268 + it('redirects to job properly after job is run', async () => { + findRunBtn().vm.$emit('click'); + await waitForPromises(); + + expect(wrapper.vm.$apollo.mutate).toHaveBeenCalledTimes(1); + expect(redirectTo).toHaveBeenCalledWith(mockJobPlayMutationData.data.jobPlay.job.webPath); + }); + }); + + describe('when play mutation is unsuccessful', () => { + beforeEach(async () => { + jest.spyOn(wrapper.vm.$apollo, 'mutate').mockRejectedValue({}); + await createComponentWithApollo(); + }); + + it('shows an alert with error', async () => { + findRunBtn().vm.$emit('click'); + await waitForPromises(); + + expect(createAlert).toHaveBeenCalledWith({ + message: JOB_GRAPHQL_ERRORS.jobMutationErrorText, + }); + }); + }); + + describe('when job is retryable', () => { + beforeEach(async () => { + await createComponentWithApollo({ props: { isRetryable: true } }); + jest.spyOn(wrapper.vm.$apollo, 'mutate').mockResolvedValue(mockJobRetryMutationData); + }); + + it('renders cancel button', () => { + expect(findCancelBtn().exists()).toBe(true); + }); + it('redirects to job properly after rerun', async () => { - findRerunBtn().vm.$emit('click'); + findRunBtn().vm.$emit('click'); await waitForPromises(); expect(wrapper.vm.$apollo.mutate).toHaveBeenCalledTimes(1); - expect(redirectTo).toHaveBeenCalledWith(mockJobMutationData.data.jobRetry.job.webPath); + expect(redirectTo).toHaveBeenCalledWith(mockJobRetryMutationData.data.jobRetry.job.webPath); + }); + }); + + describe('when retry mutation is unsuccessful', () => { + beforeEach(async () => { + jest.spyOn(wrapper.vm.$apollo, 'mutate').mockRejectedValue({}); + await createComponentWithApollo({ props: { isRetryable: true } }); + }); + + it('shows an alert with error', async () => { + findRunBtn().vm.$emit('click'); + await waitForPromises(); + + expect(createAlert).toHaveBeenCalledWith({ + message: JOB_GRAPHQL_ERRORS.jobMutationErrorText, + }); }); }); diff --git a/spec/frontend/jobs/components/job/mock_data.js b/spec/frontend/jobs/components/job/mock_data.js index 8a838acca7a..fb3a361c9c9 100644 --- a/spec/frontend/jobs/components/job/mock_data.js +++ b/spec/frontend/jobs/components/job/mock_data.js @@ -50,7 +50,32 @@ export const mockJobWithVariablesResponse = { }, }; -export const mockJobMutationData = { +export const mockJobPlayMutationData = { + data: { + jobPlay: { + job: { + id: 'gid://gitlab/Ci::Build/401', + manualVariables: { + nodes: [ + { + id: 'gid://gitlab/Ci::JobVariable/151', + key: 'new key', + value: 'new value', + __typename: 'CiManualVariable', + }, + ], + __typename: 'CiManualVariableConnection', + }, + webPath: '/Commit451/lab-coat/-/jobs/401', + __typename: 'CiJob', + }, + errors: [], + __typename: 'JobPlayPayload', + }, + }, +}; + +export const mockJobRetryMutationData = { data: { jobRetry: { job: { diff --git a/spec/frontend/sidebar/components/severity/sidebar_severity_spec.js b/spec/frontend/sidebar/components/severity/sidebar_severity_spec.js index 71c6c259c32..7580dc7307b 100644 --- a/spec/frontend/sidebar/components/severity/sidebar_severity_spec.js +++ b/spec/frontend/sidebar/components/severity/sidebar_severity_spec.js @@ -3,7 +3,8 @@ import { nextTick } from 'vue'; import { mountExtended } from 'helpers/vue_test_utils_helper'; import waitForPromises from 'helpers/wait_for_promises'; import { createAlert } from '~/flash'; -import { INCIDENT_SEVERITY, ISSUABLE_TYPES } from '~/sidebar/constants'; +import { TYPE_INCIDENT } from '~/issues/constants'; +import { INCIDENT_SEVERITY } from '~/sidebar/constants'; import updateIssuableSeverity from '~/sidebar/queries/update_issuable_severity.mutation.graphql'; import SeverityToken from '~/sidebar/components/severity/severity.vue'; import SidebarSeverityWidget from '~/sidebar/components/severity/sidebar_severity_widget.vue'; @@ -22,7 +23,7 @@ describe('SidebarSeverity', () => { const propsData = { projectPath, iid, - issuableType: ISSUABLE_TYPES.INCIDENT, + issuableType: TYPE_INCIDENT, initialSeverity: severity, ...props, }; |