Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'app/assets/javascripts/jobs')
-rw-r--r--app/assets/javascripts/jobs/components/job_app.vue15
-rw-r--r--app/assets/javascripts/jobs/components/log/line.vue65
-rw-r--r--app/assets/javascripts/jobs/components/unmet_prerequisites_block.vue27
-rw-r--r--app/assets/javascripts/jobs/mixins/delayed_job_mixin.js7
-rw-r--r--app/assets/javascripts/jobs/store/actions.js35
-rw-r--r--app/assets/javascripts/jobs/store/mutations.js1
-rw-r--r--app/assets/javascripts/jobs/utils.js14
7 files changed, 97 insertions, 67 deletions
diff --git a/app/assets/javascripts/jobs/components/job_app.vue b/app/assets/javascripts/jobs/components/job_app.vue
index c6adf2f231f..30093224631 100644
--- a/app/assets/javascripts/jobs/components/job_app.vue
+++ b/app/assets/javascripts/jobs/components/job_app.vue
@@ -1,12 +1,11 @@
<script>
import { throttle, isEmpty } from 'lodash';
import { mapGetters, mapState, mapActions } from 'vuex';
-import { GlLoadingIcon, GlIcon, GlSafeHtmlDirective as SafeHtml } from '@gitlab/ui';
+import { GlLoadingIcon, GlIcon, GlSafeHtmlDirective as SafeHtml, GlAlert } from '@gitlab/ui';
import { GlBreakpointInstance as bp } from '@gitlab/ui/dist/utils';
import { isScrolledToBottom } from '~/lib/utils/scroll_utils';
import { polyfillSticky } from '~/lib/utils/sticky';
import CiHeader from '~/vue_shared/components/header_ci_component.vue';
-import Callout from '~/vue_shared/components/callout.vue';
import EmptyState from './empty_state.vue';
import EnvironmentsBlock from './environments_block.vue';
import ErasedBlock from './erased_block.vue';
@@ -22,7 +21,6 @@ export default {
name: 'JobPageApp',
components: {
CiHeader,
- Callout,
EmptyState,
EnvironmentsBlock,
ErasedBlock,
@@ -34,6 +32,7 @@ export default {
Sidebar,
GlLoadingIcon,
SharedRunner: () => import('ee_component/jobs/components/shared_runner_limit_block.vue'),
+ GlAlert,
},
directives: {
SafeHtml,
@@ -223,10 +222,14 @@ export default {
@clickedSidebarButton="toggleSidebar"
/>
</div>
-
- <callout v-if="shouldRenderHeaderCallout">
+ <gl-alert
+ v-if="shouldRenderHeaderCallout"
+ variant="danger"
+ class="gl-mt-3"
+ :dismissible="false"
+ >
<div v-safe-html="job.callout_message"></div>
- </callout>
+ </gl-alert>
</header>
<!-- EO Header Section -->
diff --git a/app/assets/javascripts/jobs/components/log/line.vue b/app/assets/javascripts/jobs/components/log/line.vue
index affaddcdee2..87af387ca91 100644
--- a/app/assets/javascripts/jobs/components/log/line.vue
+++ b/app/assets/javascripts/jobs/components/log/line.vue
@@ -18,46 +18,33 @@ export default {
render(h, { props }) {
const { line, path } = props;
- let chars;
- if (gon?.features?.ciJobLineLinks) {
- chars = line.content.map(content => {
- return h(
- 'span',
- {
- class: ['gl-white-space-pre-wrap', content.style],
- },
- // Simple "tokenization": Split text in chunks of text
- // which alternate between text and urls.
- content.text.split(linkRegex).map(chunk => {
- // Return normal string for non-links
- if (!chunk.match(linkRegex)) {
- return chunk;
- }
- return h(
- 'a',
- {
- attrs: {
- href: chunk,
- class: 'gl-reset-color! gl-text-decoration-underline',
- rel: 'nofollow noopener noreferrer', // eslint-disable-line @gitlab/require-i18n-strings
- },
+ const chars = line.content.map(content => {
+ return h(
+ 'span',
+ {
+ class: ['gl-white-space-pre-wrap', content.style],
+ },
+ // Simple "tokenization": Split text in chunks of text
+ // which alternate between text and urls.
+ content.text.split(linkRegex).map(chunk => {
+ // Return normal string for non-links
+ if (!chunk.match(linkRegex)) {
+ return chunk;
+ }
+ return h(
+ 'a',
+ {
+ attrs: {
+ href: chunk,
+ class: 'gl-reset-color! gl-text-decoration-underline',
+ rel: 'nofollow noopener noreferrer', // eslint-disable-line @gitlab/require-i18n-strings
},
- chunk,
- );
- }),
- );
- });
- } else {
- chars = line.content.map(content => {
- return h(
- 'span',
- {
- class: ['gl-white-space-pre-wrap', content.style],
- },
- content.text,
- );
- });
- }
+ },
+ chunk,
+ );
+ }),
+ );
+ });
return h('div', { class: 'js-line log-line' }, [
h(LineNumber, {
diff --git a/app/assets/javascripts/jobs/components/unmet_prerequisites_block.vue b/app/assets/javascripts/jobs/components/unmet_prerequisites_block.vue
index 633561c879e..c9747ca9f02 100644
--- a/app/assets/javascripts/jobs/components/unmet_prerequisites_block.vue
+++ b/app/assets/javascripts/jobs/components/unmet_prerequisites_block.vue
@@ -1,11 +1,19 @@
<script>
-import { GlLink } from '@gitlab/ui';
+import { GlLink, GlAlert } from '@gitlab/ui';
+import { __, s__ } from '~/locale';
/**
* Renders Unmet Prerequisites block for job's view.
*/
export default {
+ i18n: {
+ failMessage: s__(
+ 'Job|This job failed because the necessary resources were not successfully created.',
+ ),
+ moreInformation: __('More information'),
+ },
components: {
GlLink,
+ GlAlert,
},
props: {
helpPath: {
@@ -16,15 +24,10 @@ export default {
};
</script>
<template>
- <div class="bs-callout bs-callout-danger">
- <p class="js-failed-unmet-prerequisites gl-mb-0">
- {{
- s__(`Job|This job failed because the necessary resources were not successfully created.`)
- }}
-
- <gl-link :href="helpPath" class="js-help-path">
- <strong> {{ __('More information') }} </strong>
- </gl-link>
- </p>
- </div>
+ <gl-alert variant="danger" class="gl-mt-3" :dismissible="false">
+ {{ $options.i18n.failMessage }}
+ <gl-link :href="helpPath">
+ {{ $options.i18n.moreInformation }}
+ </gl-link>
+ </gl-alert>
</template>
diff --git a/app/assets/javascripts/jobs/mixins/delayed_job_mixin.js b/app/assets/javascripts/jobs/mixins/delayed_job_mixin.js
index 8c7fb785a61..7b17dc7f693 100644
--- a/app/assets/javascripts/jobs/mixins/delayed_job_mixin.js
+++ b/app/assets/javascripts/jobs/mixins/delayed_job_mixin.js
@@ -20,7 +20,10 @@ export default {
computed: {
isDelayedJob() {
- return this.job && this.job.scheduled;
+ return this.job?.scheduled || this.job?.scheduledAt;
+ },
+ scheduledTime() {
+ return this.job.scheduled_at || this.job.scheduledAt;
},
},
@@ -43,7 +46,7 @@ export default {
},
updateRemainingTime() {
- const remainingMilliseconds = calculateRemainingMilliseconds(this.job.scheduled_at);
+ const remainingMilliseconds = calculateRemainingMilliseconds(this.scheduledTime);
this.remainingTime = formatTime(remainingMilliseconds);
},
},
diff --git a/app/assets/javascripts/jobs/store/actions.js b/app/assets/javascripts/jobs/store/actions.js
index 1e4b5e986db..cac9dc06284 100644
--- a/app/assets/javascripts/jobs/store/actions.js
+++ b/app/assets/javascripts/jobs/store/actions.js
@@ -13,6 +13,7 @@ import {
scrollDown,
scrollUp,
} from '~/lib/utils/scroll_utils';
+import httpStatusCodes from '~/lib/utils/http_status';
export const init = ({ dispatch }, { endpoint, logState, pagePath }) => {
dispatch('setJobEndpoint', endpoint);
@@ -20,8 +21,7 @@ export const init = ({ dispatch }, { endpoint, logState, pagePath }) => {
logState,
pagePath,
});
-
- return Promise.all([dispatch('fetchJob'), dispatch('fetchTrace')]);
+ dispatch('fetchJob');
};
export const setJobEndpoint = ({ commit }, endpoint) => commit(types.SET_JOB_ENDPOINT, endpoint);
@@ -39,6 +39,7 @@ export const toggleSidebar = ({ dispatch, state }) => {
};
let eTagPoll;
+let isTraceReadyForRender;
export const clearEtagPoll = () => {
eTagPoll = null;
@@ -70,7 +71,14 @@ export const fetchJob = ({ state, dispatch }) => {
});
if (!Visibility.hidden()) {
- eTagPoll.makeRequest();
+ // eslint-disable-next-line promise/catch-or-return
+ eTagPoll.makeRequest().then(() => {
+ // if a job is canceled we still need to dispatch
+ // fetchTrace to get the trace so we check for has_trace
+ if (state.job.started || state.job.has_trace) {
+ dispatch('fetchTrace');
+ }
+ });
} else {
axios
.get(state.jobEndpoint)
@@ -80,9 +88,15 @@ export const fetchJob = ({ state, dispatch }) => {
Visibility.change(() => {
if (!Visibility.hidden()) {
+ // This check is needed to ensure the loading icon
+ // is not shown for a finished job during a visibility change
+ if (!isTraceReadyForRender && state.job.started) {
+ dispatch('startPollingTrace');
+ }
dispatch('restartPolling');
} else {
dispatch('stopPolling');
+ dispatch('stopPollingTrace');
}
});
};
@@ -163,6 +177,8 @@ export const fetchTrace = ({ dispatch, state }) =>
params: { state: state.traceState },
})
.then(({ data }) => {
+ isTraceReadyForRender = data.complete;
+
dispatch('toggleScrollisInBottom', isScrolledToBottom());
dispatch('receiveTraceSuccess', data);
@@ -172,7 +188,11 @@ export const fetchTrace = ({ dispatch, state }) =>
dispatch('startPollingTrace');
}
})
- .catch(() => dispatch('receiveTraceError'));
+ .catch(e =>
+ e.response.status === httpStatusCodes.FORBIDDEN
+ ? dispatch('receiveTraceUnauthorizedError')
+ : dispatch('receiveTraceError'),
+ );
export const startPollingTrace = ({ dispatch, commit }) => {
const traceTimeout = setTimeout(() => {
@@ -194,6 +214,10 @@ export const receiveTraceError = ({ dispatch }) => {
dispatch('stopPollingTrace');
flash(__('An error occurred while fetching the job log.'));
};
+export const receiveTraceUnauthorizedError = ({ dispatch }) => {
+ dispatch('stopPollingTrace');
+ flash(__('The current user is not authorized to access the job log.'));
+};
/**
* When the user clicks a collapsible line in the job
* log, we commit a mutation to update the state
@@ -234,7 +258,7 @@ export const receiveJobsForStageError = ({ commit }) => {
flash(__('An error occurred while fetching the jobs.'));
};
-export const triggerManualJob = ({ state }, variables) => {
+export const triggerManualJob = ({ state, dispatch }, variables) => {
const parsedVariables = variables.map(variable => {
const copyVar = { ...variable };
delete copyVar.id;
@@ -245,5 +269,6 @@ export const triggerManualJob = ({ state }, variables) => {
.post(state.job.status.action.path, {
job_variables_attributes: parsedVariables,
})
+ .then(() => dispatch('fetchTrace'))
.catch(() => flash(__('An error occurred while triggering the job.')));
};
diff --git a/app/assets/javascripts/jobs/store/mutations.js b/app/assets/javascripts/jobs/store/mutations.js
index 924b811d0d6..dea53f715a7 100644
--- a/app/assets/javascripts/jobs/store/mutations.js
+++ b/app/assets/javascripts/jobs/store/mutations.js
@@ -49,6 +49,7 @@ export default {
[types.SET_TRACE_TIMEOUT](state, id) {
state.traceTimeout = id;
+ state.isTraceComplete = false;
},
/**
diff --git a/app/assets/javascripts/jobs/utils.js b/app/assets/javascripts/jobs/utils.js
index 28a125b2b8f..122f23a5bb5 100644
--- a/app/assets/javascripts/jobs/utils.js
+++ b/app/assets/javascripts/jobs/utils.js
@@ -1,4 +1,12 @@
-// capture anything starting with http:// or https://
-// up until a disallowed character or whitespace
-export const linkRegex = /(https?:\/\/[^"<>\\^`{|}\s]+)/g;
+/**
+ * capture anything starting with http:// or https://
+ * https?:\/\/
+ *
+ * up until a disallowed character or whitespace
+ * [^"<>\\^`{|}\s]+
+ *
+ * and a disallowed character or whitespace, including non-ending chars .,:;!?
+ * [^"<>\\^`{|}\s.,:;!?]
+ */
+export const linkRegex = /(https?:\/\/[^"<>\\^`{|}\s]+[^"<>\\^`{|}\s.,:;!?])/g;
export default { linkRegex };