summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPhil Hughes <me@iamphill.com>2018-05-31 15:50:24 +0100
committerPhil Hughes <me@iamphill.com>2018-06-06 08:33:02 +0100
commit90216b206659b78c802ef287726552275d87daf1 (patch)
tree65ae10be2bc4f5b986445cf7a50da095a8e47b72
parentaf07c490b2a32ed4c88e387d1133e7882f79abc5 (diff)
downloadgitlab-ce-90216b206659b78c802ef287726552275d87daf1.tar.gz
Show job logs in web IDE
[ci skip] Closes #46245
-rw-r--r--app/assets/javascripts/ide/components/jobs/detail.vue125
-rw-r--r--app/assets/javascripts/ide/components/jobs/item.vue12
-rw-r--r--app/assets/javascripts/ide/components/panes/right.vue2
-rw-r--r--app/assets/javascripts/ide/constants.js1
-rw-r--r--app/assets/javascripts/ide/stores/modules/pipelines/actions.js2
-rw-r--r--app/assets/javascripts/ide/stores/modules/pipelines/mutation_types.js2
-rw-r--r--app/assets/javascripts/ide/stores/modules/pipelines/mutations.js3
-rw-r--r--app/assets/javascripts/ide/stores/modules/pipelines/state.js1
-rw-r--r--app/assets/javascripts/ide/stores/modules/pipelines/utils.js1
-rw-r--r--app/assets/javascripts/job.js52
-rw-r--r--app/assets/stylesheets/pages/repo.scss4
11 files changed, 175 insertions, 30 deletions
diff --git a/app/assets/javascripts/ide/components/jobs/detail.vue b/app/assets/javascripts/ide/components/jobs/detail.vue
new file mode 100644
index 00000000000..28d858cf793
--- /dev/null
+++ b/app/assets/javascripts/ide/components/jobs/detail.vue
@@ -0,0 +1,125 @@
+<script>
+import { mapState } from 'vuex';
+import tooltip from '../../../vue_shared/directives/tooltip';
+import Icon from '../../../vue_shared/components/icon.vue';
+import Job from '../../../job';
+
+export default {
+ directives: {
+ tooltip,
+ },
+ components: {
+ Icon,
+ },
+ computed: {
+ ...mapState('pipelines', ['detailJob']),
+ rawUrl() {
+ return `${this.detailJob.path}/raw`;
+ },
+ },
+ mounted() {
+ this.job = new Job({
+ buildStage: 'a',
+ buildState: this.detailJob.status.text,
+ pagePath: this.detailJob.path,
+ redirectToJob: false,
+ });
+ },
+ beforeDestroy() {
+ this.job.destroy();
+ },
+};
+</script>
+
+<template>
+ <div class="ide-pipeline build-page">
+ <header
+ class="ide-tree-header ide-pipeline-header"
+ >
+ <button
+ class="btn btn-default btn-sm"
+ @click="() => { $store.state.pipelines.detailJob = null; $store.dispatch('setRightPane', 'pipelines-list') }"
+ >
+ <icon
+ name="chevron-left"
+ />
+ {{ __('View jobs') }}
+ </button>
+ </header>
+ <div class="build-trace-container prepend-top-default">
+ <div
+ v-once
+ class="top-bar js-top-bar"
+ >
+ <div class="controllers float-right">
+ <a
+ v-tooltip
+ :title="__('Show complete raw')"
+ data-placement="top"
+ data-container="body"
+ class="js-raw-link-controller controllers-buttons"
+ :href="rawUrl"
+ >
+ <i
+ aria-hidden="true"
+ class="fa fa-file-text-o"
+ ></i>
+ </a>
+ <div
+ v-tooltip
+ class="controllers-buttons"
+ data-container="body"
+ data-placement="top"
+ :title="__('Scroll to top')"
+ >
+ <button
+ class="js-scroll-up btn-scroll btn-transparent btn-blank"
+ disabled
+ type="button"
+ >
+ <icon
+ name="scroll_up"
+ />
+ </button>
+ </div>
+ <div
+ v-tooltip
+ class="controllers-buttons"
+ data-container="body"
+ data-placement="top"
+ :title="__('Scroll to top')"
+ >
+ <button
+ class="js-scroll-up btn-scroll btn-transparent btn-blank"
+ disabled
+ type="button"
+ >
+ <icon
+ name="scroll_down"
+ />
+ </button>
+ </div>
+ </div>
+ </div>
+ <pre
+ class="build-trace"
+ id="build-trace"
+ >
+ <code class="bash js-build-output">
+ </code>
+ </pre>
+ </div>
+ </div>
+</template>
+
+<style scoped>
+.build-trace-container {
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+}
+
+.ide-tree-header .btn {
+ display: flex;
+}
+</style> \ No newline at end of file
diff --git a/app/assets/javascripts/ide/components/jobs/item.vue b/app/assets/javascripts/ide/components/jobs/item.vue
index c33936021d4..0baeb4bb04f 100644
--- a/app/assets/javascripts/ide/components/jobs/item.vue
+++ b/app/assets/javascripts/ide/components/jobs/item.vue
@@ -42,5 +42,17 @@ export default {
/>
</a>
</span>
+ <button
+ class="btn btn-default btn-sm"
+ @click="() => { $store.state.pipelines.detailJob = job; $store.dispatch('setRightPane', 'jobs-detail') }"
+ >
+ {{ __('View log') }}
+ </button>
</div>
</template>
+
+<style scoped>
+.btn {
+ margin-left: auto;
+}
+</style>
diff --git a/app/assets/javascripts/ide/components/panes/right.vue b/app/assets/javascripts/ide/components/panes/right.vue
index 703c4a70cfa..7f4650cce1a 100644
--- a/app/assets/javascripts/ide/components/panes/right.vue
+++ b/app/assets/javascripts/ide/components/panes/right.vue
@@ -4,6 +4,7 @@ import tooltip from '../../../vue_shared/directives/tooltip';
import Icon from '../../../vue_shared/components/icon.vue';
import { rightSidebarViews } from '../../constants';
import PipelinesList from '../pipelines/list.vue';
+import JobsDetail from '../jobs/detail.vue';
export default {
directives: {
@@ -12,6 +13,7 @@ export default {
components: {
Icon,
PipelinesList,
+ JobsDetail,
},
computed: {
...mapState(['rightPane']),
diff --git a/app/assets/javascripts/ide/constants.js b/app/assets/javascripts/ide/constants.js
index 33cd20caf52..65886c02b92 100644
--- a/app/assets/javascripts/ide/constants.js
+++ b/app/assets/javascripts/ide/constants.js
@@ -23,4 +23,5 @@ export const viewerTypes = {
export const rightSidebarViews = {
pipelines: 'pipelines-list',
+ jobsDetail: 'jobs-detail',
};
diff --git a/app/assets/javascripts/ide/stores/modules/pipelines/actions.js b/app/assets/javascripts/ide/stores/modules/pipelines/actions.js
index 1ebe487263b..b112bc0e1ac 100644
--- a/app/assets/javascripts/ide/stores/modules/pipelines/actions.js
+++ b/app/assets/javascripts/ide/stores/modules/pipelines/actions.js
@@ -77,4 +77,6 @@ export const fetchJobs = ({ dispatch }, stage) => {
export const toggleStageCollapsed = ({ commit }, stageId) =>
commit(types.TOGGLE_STAGE_COLLAPSE, stageId);
+export const setDetailJob = ({ commit }, job) => commit(types.SET_DETAIL_JOB, job);
+
export default () => {};
diff --git a/app/assets/javascripts/ide/stores/modules/pipelines/mutation_types.js b/app/assets/javascripts/ide/stores/modules/pipelines/mutation_types.js
index 3ddc8409c5b..d95ce7c7440 100644
--- a/app/assets/javascripts/ide/stores/modules/pipelines/mutation_types.js
+++ b/app/assets/javascripts/ide/stores/modules/pipelines/mutation_types.js
@@ -7,3 +7,5 @@ export const RECEIVE_JOBS_ERROR = 'RECEIVE_JOBS_ERROR';
export const RECEIVE_JOBS_SUCCESS = 'RECEIVE_JOBS_SUCCESS';
export const TOGGLE_STAGE_COLLAPSE = 'TOGGLE_STAGE_COLLAPSE';
+
+export const SET_DETAIL_JOB = 'SET_DETAIL_JOB';
diff --git a/app/assets/javascripts/ide/stores/modules/pipelines/mutations.js b/app/assets/javascripts/ide/stores/modules/pipelines/mutations.js
index 745797e1ee5..bb49af1f45b 100644
--- a/app/assets/javascripts/ide/stores/modules/pipelines/mutations.js
+++ b/app/assets/javascripts/ide/stores/modules/pipelines/mutations.js
@@ -63,4 +63,7 @@ export default {
isCollapsed: stage.id === id ? !stage.isCollapsed : stage.isCollapsed,
}));
},
+ [types.SET_DETAIL_JOB](state, job) {
+ state.detailJob = job;
+ },
};
diff --git a/app/assets/javascripts/ide/stores/modules/pipelines/state.js b/app/assets/javascripts/ide/stores/modules/pipelines/state.js
index 0f83b315fff..8651e267b53 100644
--- a/app/assets/javascripts/ide/stores/modules/pipelines/state.js
+++ b/app/assets/javascripts/ide/stores/modules/pipelines/state.js
@@ -3,4 +3,5 @@ export default () => ({
isLoadingJobs: false,
latestPipeline: null,
stages: [],
+ detailJob: null,
});
diff --git a/app/assets/javascripts/ide/stores/modules/pipelines/utils.js b/app/assets/javascripts/ide/stores/modules/pipelines/utils.js
index 9f4b0d7d726..4f40e8766e0 100644
--- a/app/assets/javascripts/ide/stores/modules/pipelines/utils.js
+++ b/app/assets/javascripts/ide/stores/modules/pipelines/utils.js
@@ -4,4 +4,5 @@ export const normalizeJob = job => ({
name: job.name,
status: job.status,
path: job.build_path,
+ started: job.started,
});
diff --git a/app/assets/javascripts/job.js b/app/assets/javascripts/job.js
index 611e8200b4d..095d2ff9942 100644
--- a/app/assets/javascripts/job.js
+++ b/app/assets/javascripts/job.js
@@ -22,6 +22,8 @@ export default class Job {
this.$window = $(window);
this.logBytes = 0;
this.updateDropdown = this.updateDropdown.bind(this);
+ this.redirectToJob =
+ this.options.redirectToJob !== undefined ? this.options.redirectToJob : true;
this.$buildTrace = $('#build-trace');
this.$buildRefreshAnimation = $('.js-build-refresh');
@@ -44,31 +46,23 @@ export default class Job {
.off('click', '.js-sidebar-build-toggle')
.on('click', '.js-sidebar-build-toggle', this.sidebarOnClick.bind(this));
- this.$document
- .off('click', '.stage-item')
- .on('click', '.stage-item', this.updateDropdown);
+ this.$document.off('click', '.stage-item').on('click', '.stage-item', this.updateDropdown);
// add event listeners to the scroll buttons
- this.$scrollTopBtn
- .off('click')
- .on('click', this.scrollToTop.bind(this));
+ this.$scrollTopBtn.off('click').on('click', this.scrollToTop.bind(this));
- this.$scrollBottomBtn
- .off('click')
- .on('click', this.scrollToBottom.bind(this));
+ this.$scrollBottomBtn.off('click').on('click', this.scrollToBottom.bind(this));
this.scrollThrottled = _.throttle(this.toggleScroll.bind(this), 100);
- this.$window
- .off('scroll')
- .on('scroll', () => {
- if (!this.isScrolledToBottom()) {
- this.toggleScrollAnimation(false);
- } else if (this.isScrolledToBottom() && !this.isLogComplete) {
- this.toggleScrollAnimation(true);
- }
- this.scrollThrottled();
- });
+ this.$window.off('scroll').on('scroll', () => {
+ if (!this.isScrolledToBottom()) {
+ this.toggleScrollAnimation(false);
+ } else if (this.isScrolledToBottom() && !this.isLogComplete) {
+ this.toggleScrollAnimation(true);
+ }
+ this.scrollThrottled();
+ });
this.$window
.off('resize.build')
@@ -79,6 +73,10 @@ export default class Job {
this.getBuildTrace();
}
+ destroy() {
+ clearTimeout(this.timeout);
+ }
+
initAffixTopArea() {
/**
If the browser does not support position sticky, it returns the position as static.
@@ -102,9 +100,8 @@ export default class Job {
const windowHeight = $(window).height();
if (this.canScroll()) {
- if (currentPosition > 0 &&
- (scrollHeight - currentPosition !== windowHeight)) {
- // User is in the middle of the log
+ if (currentPosition > 0 && scrollHeight - currentPosition !== windowHeight) {
+ // User is in the middle of the log
this.toggleDisableButton(this.$scrollTopBtn, false);
this.toggleDisableButton(this.$scrollBottomBtn, false);
@@ -169,10 +166,11 @@ export default class Job {
}
getBuildTrace() {
- return axios.get(`${this.pagePath}/trace.json`, {
- params: { state: this.state },
- })
- .then((res) => {
+ return axios
+ .get(`${this.pagePath}/trace.json`, {
+ params: { state: this.state },
+ })
+ .then(res => {
const log = res.data;
if (!this.fetchingStatusFavicon) {
@@ -222,7 +220,7 @@ export default class Job {
this.toggleScrollAnimation(false);
}
- if (log.status !== this.buildStatus) {
+ if (log.status !== this.buildStatus && this.redirectToJob) {
visitUrl(this.pagePath);
}
})
diff --git a/app/assets/stylesheets/pages/repo.scss b/app/assets/stylesheets/pages/repo.scss
index 2b3cc33c8ae..2419fa08b73 100644
--- a/app/assets/stylesheets/pages/repo.scss
+++ b/app/assets/stylesheets/pages/repo.scss
@@ -1202,7 +1202,7 @@
}
.ide-pipeline-header {
- min-height: 50px;
+ min-height: 55px;
padding-left: $gl-padding;
padding-right: $gl-padding;
@@ -1222,8 +1222,6 @@
.ci-status-icon {
display: flex;
justify-content: center;
- height: 20px;
- margin-top: -2px;
overflow: hidden;
}
}