diff --git a/app/assets/javascripts/jobs/components/job_app.vue b/app/assets/javascripts/jobs/components/job_app.vue
index 6e95e3d16f8..d23915966de 100644
--- a/app/assets/javascripts/jobs/components/job_app.vue
+++ b/app/assets/javascripts/jobs/components/job_app.vue
@@ -3,9 +3,11 @@ import _ from 'underscore';
import { mapGetters, mapState, mapActions } from 'vuex';
import { GlLoadingIcon } from '@gitlab-org/gitlab-ui';
import { isScrolledToBottom } from '~/lib/utils/scroll_utils';
+import { polyfillSticky } from '~/lib/utils/sticky';
import bp from '~/breakpoints';
import CiHeader from '~/vue_shared/components/header_ci_component.vue';
import Callout from '~/vue_shared/components/callout.vue';
+import Icon from '~/vue_shared/components/icon.vue';
import createStore from '../store';
import EmptyState from './empty_state.vue';
import EnvironmentsBlock from './environments_block.vue';
@@ -25,6 +27,7 @@ export default {
EnvironmentsBlock,
ErasedBlock,
GlLoadingIcon,
+ Icon,
Log,
LogTopBar,
StuckBlock,
@@ -97,6 +100,14 @@ export default {
if (_.isEmpty(oldVal) && !_.isEmpty(newVal.pipeline)) {
this.fetchStages();
}
+
+ if (newVal.archived) {
+ this.$nextTick(() => {
+ if (this.$refs.sticky) {
+ polyfillSticky(this.$refs.sticky);
+ }
+ });
+ }
},
},
created() {
@@ -114,16 +125,13 @@ export default {
window.addEventListener('resize', this.onResize);
window.addEventListener('scroll', this.updateScroll);
},
-
mounted() {
this.updateSidebar();
},
-
destroyed() {
window.removeEventListener('resize', this.onResize);
window.removeEventListener('scroll', this.updateScroll);
},
-
methods: {
...mapActions([
'setJobEndpoint',
@@ -218,14 +226,28 @@ export default {
:erased-at="job.erased_at"
/>
+
+
+
+ {{ __('This job is archived. Only the complete pipeline can be retried.') }}
+
+ class="build-trace-container"
+ >
-
+
diff --git a/app/assets/stylesheets/pages/builds.scss b/app/assets/stylesheets/pages/builds.scss
index 1449723de52..81cb519883b 100644
--- a/app/assets/stylesheets/pages/builds.scss
+++ b/app/assets/stylesheets/pages/builds.scss
@@ -55,9 +55,29 @@
@include build-trace();
}
+ .archived-sticky {
+ top: $header-height;
+ border-radius: 2px 2px 0 0;
+ color: $orange-600;
+ background-color: $orange-100;
+ border: 1px solid $border-gray-normal;
+ border-bottom: 0;
+ padding: 3px 12px;
+ margin: auto;
+ align-items: center;
+
+ .with-performance-bar & {
+ top: $header-height + $performance-bar-height;
+ }
+ }
+
.top-bar {
@include build-trace-top-bar(35px);
+ &.has-archived-block {
+ top: $header-height + $performance-bar-height + 28px;
+ }
+
&.affix {
top: $header-height;
diff --git a/changelogs/unreleased/53535-sticky-archived.yml b/changelogs/unreleased/53535-sticky-archived.yml
new file mode 100644
index 00000000000..8d452d84871
--- /dev/null
+++ b/changelogs/unreleased/53535-sticky-archived.yml
@@ -0,0 +1,5 @@
+---
+title: Renders warning info when job is archieved
+merge_request:
+author:
+type: added
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 3e35a5dbdf4..45fc072900a 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -6230,6 +6230,9 @@ msgstr ""
msgid "This job is an out-of-date deployment to %{environmentLink}. View the most recent deployment %{deploymentLink}."
msgstr ""
+msgid "This job is archived. Only the complete pipeline can be retried."
+msgstr ""
+
msgid "This job is creating a deployment to %{environmentLink} and will overwrite the %{deploymentLink}."
msgstr ""
diff --git a/spec/javascripts/jobs/components/job_app_spec.js b/spec/javascripts/jobs/components/job_app_spec.js
index ba1889c4dcd..f8ca43fc150 100644
--- a/spec/javascripts/jobs/components/job_app_spec.js
+++ b/spec/javascripts/jobs/components/job_app_spec.js
@@ -423,6 +423,40 @@ describe('Job App ', () => {
});
});
+ describe('archived job', () => {
+ beforeEach(() => {
+ mock.onGet(props.endpoint).reply(200, Object.assign({}, job, { archived: true }), {});
+ vm = mountComponentWithStore(Component, {
+ props,
+ store,
+ });
+ });
+
+ it('renders warning about job being archived', done => {
+ setTimeout(() => {
+ expect(vm.$el.querySelector('.js-archived-job ')).not.toBeNull();
+ done();
+ }, 0);
+ });
+ });
+
+ describe('non-archived job', () => {
+ beforeEach(() => {
+ mock.onGet(props.endpoint).reply(200, job, {});
+ vm = mountComponentWithStore(Component, {
+ props,
+ store,
+ });
+ });
+
+ it('does not warning about job being archived', done => {
+ setTimeout(() => {
+ expect(vm.$el.querySelector('.js-archived-job ')).toBeNull();
+ done();
+ }, 0);
+ });
+ });
+
describe('trace output', () => {
beforeEach(() => {
mock.onGet(props.endpoint).reply(200, job, {});