From 135f012565a963a493d5890da44b4fbb3d9df093 Mon Sep 17 00:00:00 2001 From: Tomasz Maczukin Date: Mon, 7 May 2018 10:52:57 +0200 Subject: [PATCH 001/153] Add missing migration for minimal Project build_timeout --- ...83701_set_minimal_project_build_timeout.rb | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 db/post_migrate/20180507083701_set_minimal_project_build_timeout.rb diff --git a/db/post_migrate/20180507083701_set_minimal_project_build_timeout.rb b/db/post_migrate/20180507083701_set_minimal_project_build_timeout.rb new file mode 100644 index 00000000000..d9d9e93f5a3 --- /dev/null +++ b/db/post_migrate/20180507083701_set_minimal_project_build_timeout.rb @@ -0,0 +1,19 @@ +class SetMinimalProjectBuildTimeout < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + MINIMUM_TIMEOUT = 600 + + # Allow this migration to resume if it fails partway through + disable_ddl_transaction! + + def up + update_column_in_batches(:projects, :build_timeout, MINIMUM_TIMEOUT) do |table, query| + query.where(table[:build_timeout].lt(MINIMUM_TIMEOUT)) + end + end + + def down + # no-op + end +end From ac5fef676e5392bf5f4157fad1bad17c648706c8 Mon Sep 17 00:00:00 2001 From: Tomasz Maczukin Date: Tue, 8 May 2018 19:05:00 +0200 Subject: [PATCH 002/153] Add CHANGELOG entry --- changelogs/unreleased/46019-add-missing-migration.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 changelogs/unreleased/46019-add-missing-migration.yml diff --git a/changelogs/unreleased/46019-add-missing-migration.yml b/changelogs/unreleased/46019-add-missing-migration.yml new file mode 100644 index 00000000000..e9c6c317de2 --- /dev/null +++ b/changelogs/unreleased/46019-add-missing-migration.yml @@ -0,0 +1,5 @@ +--- +title: Add missing migration for minimal Project build_timeout +merge_request: 18775 +author: +type: fixed From 32f965b244f38b8f94aff0d0f7bb952de7593127 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Tue, 22 May 2018 12:32:37 +0100 Subject: [PATCH 003/153] Added right sidebar components --- app/assets/javascripts/ide/components/ide.vue | 6 ++ .../ide/components/right_sidebar/index.vue | 61 +++++++++++++++++++ .../components/right_sidebar/pipelines.vue | 46 ++++++++++++++ 3 files changed, 113 insertions(+) create mode 100644 app/assets/javascripts/ide/components/right_sidebar/index.vue create mode 100644 app/assets/javascripts/ide/components/right_sidebar/pipelines.vue diff --git a/app/assets/javascripts/ide/components/ide.vue b/app/assets/javascripts/ide/components/ide.vue index 1ec69adce09..d61ed36a757 100644 --- a/app/assets/javascripts/ide/components/ide.vue +++ b/app/assets/javascripts/ide/components/ide.vue @@ -6,6 +6,7 @@ import RepoTabs from './repo_tabs.vue'; import IdeStatusBar from './ide_status_bar.vue'; import RepoEditor from './repo_editor.vue'; import FindFile from './file_finder/index.vue'; +import RightSidebar from './right_sidebar/index.vue'; const originalStopCallback = Mousetrap.stopCallback; @@ -16,6 +17,7 @@ export default { IdeStatusBar, RepoEditor, FindFile, + RightSidebar, }, computed: { ...mapState([ @@ -25,6 +27,7 @@ export default { 'currentMergeRequestId', 'fileFindVisible', 'emptyStateSvgPath', + 'currentProjectId', ]), ...mapGetters(['activeFile', 'hasChanges']), }, @@ -122,6 +125,9 @@ export default { + +import tooltip from '../../../vue_shared/directives/tooltip'; +import Icon from '../../../vue_shared/components/icon.vue'; +import Pipelines from './pipelines.vue'; + +export default { + directives: { + tooltip, + }, + components: { + Icon, + Pipelines, + }, +}; + + + + + diff --git a/app/assets/javascripts/ide/components/right_sidebar/pipelines.vue b/app/assets/javascripts/ide/components/right_sidebar/pipelines.vue new file mode 100644 index 00000000000..0ff78242e6a --- /dev/null +++ b/app/assets/javascripts/ide/components/right_sidebar/pipelines.vue @@ -0,0 +1,46 @@ + + + + + From cfe4d2f29dcdcfad96ae7ba5a5eb822fbe46a9a7 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Wed, 23 May 2018 11:44:47 +0100 Subject: [PATCH 004/153] added tab component --- app/assets/javascripts/ide/components/ide.vue | 6 +- .../index.vue => panes/right.vue} | 32 ++++++--- .../ide/components/pipelines/jobs.vue | 40 +++++++++++ .../pipelines.vue => pipelines/list.vue} | 31 +++++++-- app/assets/javascripts/ide/constants.js | 4 ++ app/assets/javascripts/ide/stores/actions.js | 4 ++ .../ide/stores/modules/pipelines/getters.js | 2 + .../ide/stores/modules/pipelines/mutations.js | 1 + .../javascripts/ide/stores/mutation_types.js | 2 + .../javascripts/ide/stores/mutations.js | 3 + app/assets/javascripts/ide/stores/state.js | 1 + .../vue_shared/components/tabs/tab.vue | 42 ++++++++++++ .../vue_shared/components/tabs/tabs.js | 62 +++++++++++++++++ .../vue_shared/components/tabs/tab_spec.js | 32 +++++++++ .../vue_shared/components/tabs/tabs_spec.js | 68 +++++++++++++++++++ 15 files changed, 314 insertions(+), 16 deletions(-) rename app/assets/javascripts/ide/components/{right_sidebar/index.vue => panes/right.vue} (60%) create mode 100644 app/assets/javascripts/ide/components/pipelines/jobs.vue rename app/assets/javascripts/ide/components/{right_sidebar/pipelines.vue => pipelines/list.vue} (54%) create mode 100644 app/assets/javascripts/vue_shared/components/tabs/tab.vue create mode 100644 app/assets/javascripts/vue_shared/components/tabs/tabs.js create mode 100644 spec/javascripts/vue_shared/components/tabs/tab_spec.js create mode 100644 spec/javascripts/vue_shared/components/tabs/tabs_spec.js diff --git a/app/assets/javascripts/ide/components/ide.vue b/app/assets/javascripts/ide/components/ide.vue index d61ed36a757..318e7aa5716 100644 --- a/app/assets/javascripts/ide/components/ide.vue +++ b/app/assets/javascripts/ide/components/ide.vue @@ -6,7 +6,7 @@ import RepoTabs from './repo_tabs.vue'; import IdeStatusBar from './ide_status_bar.vue'; import RepoEditor from './repo_editor.vue'; import FindFile from './file_finder/index.vue'; -import RightSidebar from './right_sidebar/index.vue'; +import RightPane from './panes/right.vue'; const originalStopCallback = Mousetrap.stopCallback; @@ -17,7 +17,7 @@ export default { IdeStatusBar, RepoEditor, FindFile, - RightSidebar, + RightPane, }, computed: { ...mapState([ @@ -125,7 +125,7 @@ export default { - diff --git a/app/assets/javascripts/ide/components/right_sidebar/index.vue b/app/assets/javascripts/ide/components/panes/right.vue similarity index 60% rename from app/assets/javascripts/ide/components/right_sidebar/index.vue rename to app/assets/javascripts/ide/components/panes/right.vue index 2417e3976aa..7ac79347225 100644 --- a/app/assets/javascripts/ide/components/right_sidebar/index.vue +++ b/app/assets/javascripts/ide/components/panes/right.vue @@ -1,7 +1,9 @@ @@ -18,25 +27,31 @@ export default {
-
- +
+ + +
@@ -55,6 +70,7 @@ export default { .ide-right-sidebar .multi-file-commit-panel-inner { width: 300px; + padding: 8px 16px; background-color: #fff; border-left: 1px solid #eaeaea; } diff --git a/app/assets/javascripts/ide/components/pipelines/jobs.vue b/app/assets/javascripts/ide/components/pipelines/jobs.vue new file mode 100644 index 00000000000..d69945b617c --- /dev/null +++ b/app/assets/javascripts/ide/components/pipelines/jobs.vue @@ -0,0 +1,40 @@ + + + diff --git a/app/assets/javascripts/ide/components/right_sidebar/pipelines.vue b/app/assets/javascripts/ide/components/pipelines/list.vue similarity index 54% rename from app/assets/javascripts/ide/components/right_sidebar/pipelines.vue rename to app/assets/javascripts/ide/components/pipelines/list.vue index 0ff78242e6a..e76ea0b50af 100644 --- a/app/assets/javascripts/ide/components/right_sidebar/pipelines.vue +++ b/app/assets/javascripts/ide/components/pipelines/list.vue @@ -1,14 +1,17 @@ + + diff --git a/app/assets/javascripts/vue_shared/components/tabs/tabs.js b/app/assets/javascripts/vue_shared/components/tabs/tabs.js new file mode 100644 index 00000000000..3dff37b1c84 --- /dev/null +++ b/app/assets/javascripts/vue_shared/components/tabs/tabs.js @@ -0,0 +1,62 @@ +export default { + data() { + return { + currentIndex: 0, + tabs: [], + }; + }, + mounted() { + this.updateTabs(); + }, + methods: { + updateTabs() { + this.tabs = this.$children.filter(child => child.isTab); + this.currentIndex = this.tabs.findIndex(tab => tab.localActive); + }, + setTab(index) { + this.tabs[this.currentIndex].localActive = false; + this.tabs[index].localActive = true; + + this.currentIndex = index; + }, + }, + render(h) { + const navItems = this.tabs.map((tab, i) => + h( + 'li', + { + key: i, + class: tab.localActive ? 'active' : null, + }, + [ + h( + 'a', + { + href: '#', + on: { + click: () => this.setTab(i), + }, + }, + tab.$slots.title || tab.title, + ), + ], + ), + ); + const nav = h( + 'ul', + { + class: 'nav-links tab-links', + }, + [navItems], + ); + const content = h( + 'div', + { + class: ['tab-content'], + }, + [this.$slots.default], + ); + + return h('div', {}, [[nav], content]); + }, +}; diff --git a/spec/javascripts/vue_shared/components/tabs/tab_spec.js b/spec/javascripts/vue_shared/components/tabs/tab_spec.js new file mode 100644 index 00000000000..8437fe37738 --- /dev/null +++ b/spec/javascripts/vue_shared/components/tabs/tab_spec.js @@ -0,0 +1,32 @@ +import Vue from 'vue'; +import mountComponent from 'spec/helpers/vue_mount_component_helper'; +import Tab from '~/vue_shared/components/tabs/tab.vue'; + +describe('Tab component', () => { + const Component = Vue.extend(Tab); + let vm; + + beforeEach(() => { + vm = mountComponent(Component); + }); + + it('sets localActive to equal active', done => { + vm.active = true; + + vm.$nextTick(() => { + expect(vm.localActive).toBe(true); + + done(); + }); + }); + + it('sets active class', done => { + vm.active = true; + + vm.$nextTick(() => { + expect(vm.$el.classList).toContain('active'); + + done(); + }); + }); +}); diff --git a/spec/javascripts/vue_shared/components/tabs/tabs_spec.js b/spec/javascripts/vue_shared/components/tabs/tabs_spec.js new file mode 100644 index 00000000000..07752329965 --- /dev/null +++ b/spec/javascripts/vue_shared/components/tabs/tabs_spec.js @@ -0,0 +1,68 @@ +import Vue from 'vue'; +import Tabs from '~/vue_shared/components/tabs/tabs'; +import Tab from '~/vue_shared/components/tabs/tab.vue'; + +describe('Tabs component', () => { + let vm; + + beforeEach(done => { + vm = new Vue({ + components: { + Tabs, + Tab, + }, + template: ` +
+ + + First tab + + + + Second tab + + +
+ `, + }).$mount(); + + setTimeout(done); + }); + + describe('tab links', () => { + it('renders links for tabs', () => { + expect(vm.$el.querySelectorAll('a').length).toBe(2); + }); + + it('renders link titles from props', () => { + expect(vm.$el.querySelector('a').textContent).toContain('Testing'); + }); + + it('renders link titles from slot', () => { + expect(vm.$el.querySelectorAll('a')[1].textContent).toContain('Test slot'); + }); + + it('renders active class', () => { + expect(vm.$el.querySelector('li').classList).toContain('active'); + }); + + it('updates active class on click', done => { + vm.$el.querySelectorAll('a')[1].click(); + + setTimeout(() => { + expect(vm.$el.querySelector('li').classList).not.toContain('active'); + expect(vm.$el.querySelectorAll('li')[1].classList).toContain('active'); + + done(); + }); + }); + }); + + describe('content', () => { + it('renders content panes', () => { + expect(vm.$el.querySelectorAll('.tab-pane').length).toBe(2); + expect(vm.$el.querySelectorAll('.tab-pane')[0].textContent).toContain('First tab'); + expect(vm.$el.querySelectorAll('.tab-pane')[1].textContent).toContain('Second tab'); + }); + }); +}); From 5e79276b53f61cbd727411ed33e71d5b6fa5ca54 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Wed, 23 May 2018 14:22:38 +0100 Subject: [PATCH 005/153] improve API calls by calling internal API to get data render job items (needs improvements to components) --- app/assets/javascripts/api.js | 6 +- .../ide/components/pipelines/jobs.vue | 50 ++++++++++++-- .../ide/stores/modules/pipelines/actions.js | 46 ++++++++----- .../ide/stores/modules/pipelines/getters.js | 6 +- .../modules/pipelines/mutation_types.js | 4 ++ .../ide/stores/modules/pipelines/mutations.js | 67 ++++++++++++------- .../vue_shared/components/tabs/tab.vue | 3 + .../projects/pipelines_controller.rb | 11 ++- 8 files changed, 137 insertions(+), 56 deletions(-) diff --git a/app/assets/javascripts/api.js b/app/assets/javascripts/api.js index eb919241318..06fdc8fe65b 100644 --- a/app/assets/javascripts/api.js +++ b/app/assets/javascripts/api.js @@ -24,7 +24,7 @@ const Api = { branchSinglePath: '/api/:version/projects/:id/repository/branches/:branch', createBranchPath: '/api/:version/projects/:id/repository/branches', pipelinesPath: '/api/:version/projects/:id/pipelines', - pipelineJobsPath: '/api/:version/projects/:id/pipelines/:pipeline_id/jobs', + pipelineJobsPath: '/:project_path/pipelines/:id/builds.json', group(groupId, callback) { const url = Api.buildUrl(Api.groupPath).replace(':id', groupId); @@ -232,8 +232,8 @@ const Api = { pipelineJobs(projectPath, pipelineId, params = {}) { const url = Api.buildUrl(this.pipelineJobsPath) - .replace(':id', encodeURIComponent(projectPath)) - .replace(':pipeline_id', pipelineId); + .replace(':project_path', projectPath) + .replace(':id', pipelineId); return axios.get(url, { params }); }, diff --git a/app/assets/javascripts/ide/components/pipelines/jobs.vue b/app/assets/javascripts/ide/components/pipelines/jobs.vue index d69945b617c..a3a99ad457c 100644 --- a/app/assets/javascripts/ide/components/pipelines/jobs.vue +++ b/app/assets/javascripts/ide/components/pipelines/jobs.vue @@ -1,5 +1,7 @@ @@ -27,11 +32,44 @@ export default { - List all jobs here +
+
+
+ + {{ stage.title }} + + {{ stage.jobs.length }} + + +
+
+
+ + {{ job.name }} #{{ job.id }} +
+
+
+
List all failed jobs here diff --git a/app/assets/javascripts/ide/stores/modules/pipelines/actions.js b/app/assets/javascripts/ide/stores/modules/pipelines/actions.js index 07f7b201f2e..994edd74aef 100644 --- a/app/assets/javascripts/ide/stores/modules/pipelines/actions.js +++ b/app/assets/javascripts/ide/stores/modules/pipelines/actions.js @@ -1,3 +1,4 @@ +import axios from 'axios'; import { __ } from '../../../../locale'; import Api from '../../../../api'; import flash from '../../../../flash'; @@ -21,29 +22,40 @@ export const fetchLatestPipeline = ({ dispatch, rootState }, sha) => { .catch(() => dispatch('receiveLatestPipelineError')); }; -export const requestJobs = ({ commit }) => commit(types.REQUEST_JOBS); -export const receiveJobsError = ({ commit }) => { - flash(__('There was an error loading jobs')); - commit(types.RECEIVE_JOBS_ERROR); +export const requestStages = ({ commit }) => commit(types.REQUEST_STAGES); +export const receiveStagesError = ({ commit }) => { + flash(__('There was an error loading job stages')); + commit(types.RECEIVE_STAGES_ERROR); }; -export const receiveJobsSuccess = ({ commit }, data) => commit(types.RECEIVE_JOBS_SUCCESS, data); +export const receiveStagesSuccess = ({ commit }, data) => + commit(types.RECEIVE_STAGES_SUCCESS, data); -export const fetchJobs = ({ dispatch, state, rootState }, page = '1') => { - dispatch('requestJobs'); +export const fetchStages = ({ dispatch, state, rootState }) => { + dispatch('requestStages'); - Api.pipelineJobs(rootState.currentProjectId, state.latestPipeline.id, { - page, - }) - .then(({ data, headers }) => { - const nextPage = headers && headers['x-next-page']; + Api.pipelineJobs(rootState.currentProjectId, state.latestPipeline.id) + .then(({ data }) => dispatch('receiveStagesSuccess', data)) + .then(() => state.stages.forEach(stage => dispatch('fetchJobs', stage))) + .catch(() => dispatch('receiveStagesError')); +}; - dispatch('receiveJobsSuccess', data); +export const requestJobs = ({ commit }, id) => commit(types.REQUEST_JOBS, id); +export const receiveJobsError = ({ commit }, id) => { + flash(__('There was an error loading jobs')); + commit(types.RECEIVE_JOBS_ERROR, id); +}; +export const receiveJobsSuccess = ({ commit }, { id, data }) => + commit(types.RECEIVE_JOBS_SUCCESS, { id, data }); - if (nextPage) { - dispatch('fetchJobs', nextPage); - } +export const fetchJobs = ({ dispatch }, stage) => { + dispatch('requestJobs', stage.id); + + axios + .get(stage.dropdown_path) + .then(({ data }) => { + dispatch('receiveJobsSuccess', { id: stage.id, data }); }) - .catch(() => dispatch('receiveJobsError')); + .catch(() => dispatch('receiveJobsError', stage.id)); }; export default () => {}; diff --git a/app/assets/javascripts/ide/stores/modules/pipelines/getters.js b/app/assets/javascripts/ide/stores/modules/pipelines/getters.js index e1f55bcd933..99b4554a96e 100644 --- a/app/assets/javascripts/ide/stores/modules/pipelines/getters.js +++ b/app/assets/javascripts/ide/stores/modules/pipelines/getters.js @@ -1,9 +1,9 @@ export const hasLatestPipeline = state => !state.isLoadingPipeline && !!state.latestPipeline; -export const failedJobs = state => +export const failedJobsCount = state => state.stages.reduce( - (acc, stage) => acc.concat(stage.jobs.filter(job => job.status === 'failed')), - [], + (acc, stage) => acc + stage.jobs.filter(j => j.status.label === 'failed').length, + 0, ); export const jobsCount = state => state.stages.reduce((acc, stage) => acc + stage.jobs.length, 0); 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 6b5701670a6..0911b8ee6fb 100644 --- a/app/assets/javascripts/ide/stores/modules/pipelines/mutation_types.js +++ b/app/assets/javascripts/ide/stores/modules/pipelines/mutation_types.js @@ -2,6 +2,10 @@ export const REQUEST_LATEST_PIPELINE = 'REQUEST_LATEST_PIPELINE'; export const RECEIVE_LASTEST_PIPELINE_ERROR = 'RECEIVE_LASTEST_PIPELINE_ERROR'; export const RECEIVE_LASTEST_PIPELINE_SUCCESS = 'RECEIVE_LASTEST_PIPELINE_SUCCESS'; +export const REQUEST_STAGES = 'REQUEST_STAGES'; +export const RECEIVE_STAGES_ERROR = 'RECEIVE_STAGES_ERROR'; +export const RECEIVE_STAGES_SUCCESS = 'RECEIVE_STAGES_SUCCESS'; + export const REQUEST_JOBS = 'REQUEST_JOBS'; export const RECEIVE_JOBS_ERROR = 'RECEIVE_JOBS_ERROR'; export const RECEIVE_JOBS_SUCCESS = 'RECEIVE_JOBS_SUCCESS'; diff --git a/app/assets/javascripts/ide/stores/modules/pipelines/mutations.js b/app/assets/javascripts/ide/stores/modules/pipelines/mutations.js index d28d9ca776d..7115f5e5386 100644 --- a/app/assets/javascripts/ide/stores/modules/pipelines/mutations.js +++ b/app/assets/javascripts/ide/stores/modules/pipelines/mutations.js @@ -18,37 +18,52 @@ export default { }; } }, - [types.REQUEST_JOBS](state) { + [types.REQUEST_STAGES](state) { state.isLoadingJobs = true; }, - [types.RECEIVE_JOBS_ERROR](state) { + [types.RECEIVE_STAGES_ERROR](state) { state.isLoadingJobs = false; }, - [types.RECEIVE_JOBS_SUCCESS](state, jobs) { + [types.RECEIVE_STAGES_SUCCESS](state, stages) { state.isLoadingJobs = false; - state.stages = jobs.reduce((acc, job) => { - let stage = acc.find(s => s.title === job.stage); - - if (!stage) { - stage = { - title: job.stage, - isCollapsed: false, - jobs: [], - }; - - acc.push(stage); - } - - stage.jobs = stage.jobs.concat({ - id: job.id, - name: job.name, - status: job.status, - stage: job.stage, - duration: job.duration, - }); - - return acc; - }, state.stages); + state.stages = stages.map((stage, i) => ({ + ...stage, + id: i, + isCollapsed: false, + isLoading: false, + jobs: [], + })); + }, + [types.REQUEST_JOBS](state, id) { + state.stages = state.stages.reduce( + (acc, stage) => + acc.concat({ + ...stage, + isLoading: id === stage.id ? true : stage.isLoading, + }), + [], + ); + }, + [types.RECEIVE_JOBS_ERROR](state, id) { + state.stages = state.stages.reduce( + (acc, stage) => + acc.concat({ + ...stage, + isLoading: id === stage.id ? false : stage.isLoading, + }), + [], + ); + }, + [types.RECEIVE_JOBS_SUCCESS](state, { id, data }) { + state.stages = state.stages.reduce( + (acc, stage) => + acc.concat({ + ...stage, + isLoading: id === stage.id ? false : stage.isLoading, + jobs: id === stage.id ? data.latest_statuses : stage.jobs, + }), + [], + ); }, }; diff --git a/app/assets/javascripts/vue_shared/components/tabs/tab.vue b/app/assets/javascripts/vue_shared/components/tabs/tab.vue index 2a35d6bc151..0ec7a0199fe 100644 --- a/app/assets/javascripts/vue_shared/components/tabs/tab.vue +++ b/app/assets/javascripts/vue_shared/components/tabs/tab.vue @@ -26,6 +26,9 @@ export default { created() { this.isTab = true; }, + updated() { + this.$parent.$forceUpdate(); + }, }; diff --git a/app/controllers/projects/pipelines_controller.rb b/app/controllers/projects/pipelines_controller.rb index 6b40fc2fe68..7b43ef8ab76 100644 --- a/app/controllers/projects/pipelines_controller.rb +++ b/app/controllers/projects/pipelines_controller.rb @@ -76,7 +76,16 @@ class Projects::PipelinesController < Projects::ApplicationController end def builds - render_show + respond_to do |format| + format.html do + render_show + end + format.json do + render json: PipelineSerializer + .new(project: @project, current_user: @current_user) + .represent_stages(@pipeline) + end + end end def failures From 76ffde63189efd81249857a6a9bf612f328322c6 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Wed, 23 May 2018 15:29:34 +0100 Subject: [PATCH 006/153] style improvements fixed multiple requests causing state to be emptied at wrong time --- .../javascripts/ide/components/jobs/list.vue | 25 +++++ .../javascripts/ide/components/jobs/stage.vue | 94 +++++++++++++++++++ .../ide/components/panes/right.vue | 11 ++- .../ide/components/pipelines/jobs.vue | 51 ++-------- .../ide/components/pipelines/list.vue | 4 +- .../ide/stores/modules/pipelines/actions.js | 1 - .../ide/stores/modules/pipelines/getters.js | 2 + .../ide/stores/modules/pipelines/mutations.js | 17 ++-- .../vue_shared/components/tabs/tabs.js | 4 +- .../stylesheets/framework/gitlab_theme.scss | 4 + app/assets/stylesheets/pages/repo.scss | 7 ++ 11 files changed, 163 insertions(+), 57 deletions(-) create mode 100644 app/assets/javascripts/ide/components/jobs/list.vue create mode 100644 app/assets/javascripts/ide/components/jobs/stage.vue diff --git a/app/assets/javascripts/ide/components/jobs/list.vue b/app/assets/javascripts/ide/components/jobs/list.vue new file mode 100644 index 00000000000..7ac1ce3ef54 --- /dev/null +++ b/app/assets/javascripts/ide/components/jobs/list.vue @@ -0,0 +1,25 @@ + + + diff --git a/app/assets/javascripts/ide/components/jobs/stage.vue b/app/assets/javascripts/ide/components/jobs/stage.vue new file mode 100644 index 00000000000..62042892e13 --- /dev/null +++ b/app/assets/javascripts/ide/components/jobs/stage.vue @@ -0,0 +1,94 @@ + + + + + diff --git a/app/assets/javascripts/ide/components/panes/right.vue b/app/assets/javascripts/ide/components/panes/right.vue index 7ac79347225..7449822516b 100644 --- a/app/assets/javascripts/ide/components/panes/right.vue +++ b/app/assets/javascripts/ide/components/panes/right.vue @@ -31,19 +31,20 @@ export default { class="multi-file-commit-panel-inner" v-if="rightPane" > - - - +
From 4b4618936d0af203820be3a9392d7e555464cf3f Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Thu, 24 May 2018 11:49:57 +0100 Subject: [PATCH 008/153] improve design of job items allow ci icon to have a different size & be borderless --- .../javascripts/ide/components/jobs/item.vue | 46 ++++++++++++++++--- .../javascripts/ide/components/jobs/list.vue | 23 ++++++++-- .../javascripts/ide/components/jobs/stage.vue | 39 ++++++++++++++-- .../ide/components/panes/right.vue | 2 +- .../ide/components/pipelines/jobs.vue | 18 ++++++-- .../ide/components/pipelines/list.vue | 1 + .../ide/stores/modules/pipelines/getters.js | 8 +++- .../vue_shared/components/ci_icon.vue | 23 +++++++++- 8 files changed, 137 insertions(+), 23 deletions(-) diff --git a/app/assets/javascripts/ide/components/jobs/item.vue b/app/assets/javascripts/ide/components/jobs/item.vue index 53d9baffd78..a6ca629a358 100644 --- a/app/assets/javascripts/ide/components/jobs/item.vue +++ b/app/assets/javascripts/ide/components/jobs/item.vue @@ -11,16 +11,48 @@ export default { required: true, }, }, + computed: { + jobId() { + return `#${this.job.id}`; + }, + }, }; + + diff --git a/app/assets/javascripts/ide/components/jobs/list.vue b/app/assets/javascripts/ide/components/jobs/list.vue index 7ac1ce3ef54..ef1d4580d6a 100644 --- a/app/assets/javascripts/ide/components/jobs/list.vue +++ b/app/assets/javascripts/ide/components/jobs/list.vue @@ -1,8 +1,11 @@ diff --git a/app/assets/javascripts/ide/components/jobs/stage.vue b/app/assets/javascripts/ide/components/jobs/stage.vue index a4e3b8e7926..c00ac458745 100644 --- a/app/assets/javascripts/ide/components/jobs/stage.vue +++ b/app/assets/javascripts/ide/components/jobs/stage.vue @@ -1,11 +1,15 @@ From 1e48b7eec0499e44eb1dcd32393005f709b5c816 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Thu, 24 May 2018 12:11:55 +0100 Subject: [PATCH 009/153] removed need for jobs component --- .../javascripts/ide/components/jobs/list.vue | 10 ++-- .../javascripts/ide/components/jobs/stage.vue | 10 +++- .../ide/components/pipelines/jobs.vue | 59 ------------------- .../ide/components/pipelines/list.vue | 46 +++++++++++++-- 4 files changed, 54 insertions(+), 71 deletions(-) delete mode 100644 app/assets/javascripts/ide/components/pipelines/jobs.vue diff --git a/app/assets/javascripts/ide/components/jobs/list.vue b/app/assets/javascripts/ide/components/jobs/list.vue index ef1d4580d6a..fd6bfdf86d0 100644 --- a/app/assets/javascripts/ide/components/jobs/list.vue +++ b/app/assets/javascripts/ide/components/jobs/list.vue @@ -1,5 +1,4 @@ @@ -23,7 +23,7 @@ export default { From 4bfd54f3d2e303d751d49834879867f3c62156e9 Mon Sep 17 00:00:00 2001 From: Bart Libert Date: Thu, 5 Apr 2018 13:44:18 +0200 Subject: [PATCH 010/153] Import bitbucket issues that are reported by an anonymous user For these kind of issues, the "reporter" field is present but zero. In such a case, "fetch" will not return the default value, but it will return nil. Hence, importing fails, because the "username" field of nil is referenced Fixes issue #44381 --- .../fix-bitbucket_import_anonymous.yml | 5 ++++ lib/bitbucket/representation/issue.rb | 2 +- lib/gitlab/import_formatter.rb | 1 + .../gitlab/bitbucket_import/importer_spec.rb | 30 +++++++++++++++++++ 4 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 changelogs/unreleased/fix-bitbucket_import_anonymous.yml diff --git a/changelogs/unreleased/fix-bitbucket_import_anonymous.yml b/changelogs/unreleased/fix-bitbucket_import_anonymous.yml new file mode 100644 index 00000000000..6e214b3c957 --- /dev/null +++ b/changelogs/unreleased/fix-bitbucket_import_anonymous.yml @@ -0,0 +1,5 @@ +--- +title: Import bitbucket issues that are reported by an anonymous user +merge_request: 18199 +author: bartl +type: fixed diff --git a/lib/bitbucket/representation/issue.rb b/lib/bitbucket/representation/issue.rb index 054064395c3..44bcbc250b3 100644 --- a/lib/bitbucket/representation/issue.rb +++ b/lib/bitbucket/representation/issue.rb @@ -12,7 +12,7 @@ module Bitbucket end def author - raw.fetch('reporter', {}).fetch('username', nil) + raw.dig('reporter', 'username') end def description diff --git a/lib/gitlab/import_formatter.rb b/lib/gitlab/import_formatter.rb index 3e54456e936..4e611e7f16c 100644 --- a/lib/gitlab/import_formatter.rb +++ b/lib/gitlab/import_formatter.rb @@ -9,6 +9,7 @@ module Gitlab end def author_line(author) + author ||= "Anonymous" "*Created by: #{author}*\n\n" end end diff --git a/spec/lib/gitlab/bitbucket_import/importer_spec.rb b/spec/lib/gitlab/bitbucket_import/importer_spec.rb index c63120b0b29..05c232d22cf 100644 --- a/spec/lib/gitlab/bitbucket_import/importer_spec.rb +++ b/spec/lib/gitlab/bitbucket_import/importer_spec.rb @@ -19,6 +19,18 @@ describe Gitlab::BitbucketImport::Importer do ] end + let(:reporters) do + [ + nil, + { "username" => "reporter1" }, + nil, + { "username" => "reporter2" }, + { "username" => "reporter1" }, + nil, + { "username" => "reporter3" } + ] + end + let(:sample_issues_statuses) do issues = [] @@ -36,6 +48,10 @@ describe Gitlab::BitbucketImport::Importer do } end + reporters.map.with_index do |reporter, index| + issues[index]['reporter'] = reporter + end + issues end @@ -147,5 +163,19 @@ describe Gitlab::BitbucketImport::Importer do expect(importer.errors).to be_empty end end + + describe 'issue import' do + it 'maps reporters to anonymous if bitbucket reporter is nil' do + allow(importer).to receive(:import_wiki) + importer.execute + + expect(project.issues.size).to eq(7) + expect(project.issues.where("description LIKE ?", '%Anonymous%').size).to eq(3) + expect(project.issues.where("description LIKE ?", '%reporter1%').size).to eq(2) + expect(project.issues.where("description LIKE ?", '%reporter2%').size).to eq(1) + expect(project.issues.where("description LIKE ?", '%reporter3%').size).to eq(1) + expect(importer.errors).to be_empty + end + end end end From 8b1c43bde36c902d138b06dfb91ffab4bc1eb8ad Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Thu, 24 May 2018 15:07:09 +0100 Subject: [PATCH 011/153] spec fixes --- .../javascripts/ide/stores/mutations.js | 4 +- .../vue_shared/components/tabs/tab.vue | 4 +- spec/javascripts/ide/mock_data.js | 21 ++++ .../stores/modules/pipelines/actions_spec.js | 100 +++++------------- .../stores/modules/pipelines/getters_spec.js | 31 ------ .../modules/pipelines/mutations_spec.js | 61 ++++------- 6 files changed, 74 insertions(+), 147 deletions(-) diff --git a/app/assets/javascripts/ide/stores/mutations.js b/app/assets/javascripts/ide/stores/mutations.js index 375b4d8233d..633c93ed0a8 100644 --- a/app/assets/javascripts/ide/stores/mutations.js +++ b/app/assets/javascripts/ide/stores/mutations.js @@ -149,7 +149,9 @@ export default { }); }, [types.SET_RIGHT_PANE](state, view) { - state.rightPane = state.rightPane === view ? null : view; + Object.assign(state, { + rightPane: state.rightPane === view ? null : view, + }); }, ...projectMutations, ...mergeRequestMutation, diff --git a/app/assets/javascripts/vue_shared/components/tabs/tab.vue b/app/assets/javascripts/vue_shared/components/tabs/tab.vue index 0ec7a0199fe..9b2f46186ac 100644 --- a/app/assets/javascripts/vue_shared/components/tabs/tab.vue +++ b/app/assets/javascripts/vue_shared/components/tabs/tab.vue @@ -27,7 +27,9 @@ export default { this.isTab = true; }, updated() { - this.$parent.$forceUpdate(); + if (this.$parent) { + this.$parent.$forceUpdate(); + } }, }; diff --git a/spec/javascripts/ide/mock_data.js b/spec/javascripts/ide/mock_data.js index 7e641c7984b..a0cb8bae91c 100644 --- a/spec/javascripts/ide/mock_data.js +++ b/spec/javascripts/ide/mock_data.js @@ -29,6 +29,27 @@ export const pipelines = [ }, ]; +export const stages = [ + { + dropdown_path: 'testing', + name: 'build', + status: { + icon: 'status_failed', + group: 'failed', + text: 'Failed', + }, + }, + { + dropdown_path: 'testing', + name: 'test', + status: { + icon: 'status_failed', + group: 'failed', + text: 'Failed', + }, + }, +]; + export const jobs = [ { id: 1, diff --git a/spec/javascripts/ide/stores/modules/pipelines/actions_spec.js b/spec/javascripts/ide/stores/modules/pipelines/actions_spec.js index 85fbcf8084b..bcf9d9e1513 100644 --- a/spec/javascripts/ide/stores/modules/pipelines/actions_spec.js +++ b/spec/javascripts/ide/stores/modules/pipelines/actions_spec.js @@ -5,15 +5,15 @@ import actions, { receiveLatestPipelineError, receiveLatestPipelineSuccess, fetchLatestPipeline, - requestJobs, - receiveJobsError, - receiveJobsSuccess, - fetchJobs, + requestStages, + receiveStagesError, + receiveStagesSuccess, + fetchStages, } from '~/ide/stores/modules/pipelines/actions'; import state from '~/ide/stores/modules/pipelines/state'; import * as types from '~/ide/stores/modules/pipelines/mutation_types'; import testAction from '../../../../helpers/vuex_action_helper'; -import { pipelines, jobs } from '../../../mock_data'; +import { pipelines, stages } from '../../../mock_data'; describe('IDE pipelines actions', () => { let mockedState; @@ -141,19 +141,19 @@ describe('IDE pipelines actions', () => { }); }); - describe('requestJobs', () => { + describe('requestStages', () => { it('commits request', done => { - testAction(requestJobs, null, mockedState, [{ type: types.REQUEST_JOBS }], [], done); + testAction(requestStages, null, mockedState, [{ type: types.REQUEST_STAGES }], [], done); }); }); describe('receiveJobsError', () => { it('commits error', done => { testAction( - receiveJobsError, + receiveStagesError, null, mockedState, - [{ type: types.RECEIVE_JOBS_ERROR }], + [{ type: types.RECEIVE_STAGES_ERROR }], [], done, ); @@ -162,80 +162,53 @@ describe('IDE pipelines actions', () => { it('creates flash message', () => { const flashSpy = spyOnDependency(actions, 'flash'); - receiveJobsError({ commit() {} }); + receiveStagesError({ commit() {} }); expect(flashSpy).toHaveBeenCalled(); }); }); - describe('receiveJobsSuccess', () => { + describe('receiveStagesSuccess', () => { it('commits jobs', done => { testAction( - receiveJobsSuccess, - jobs, + receiveStagesSuccess, + stages, mockedState, - [{ type: types.RECEIVE_JOBS_SUCCESS, payload: jobs }], + [{ type: types.RECEIVE_STAGES_SUCCESS, payload: stages }], [], done, ); }); }); - describe('fetchJobs', () => { - let page = ''; - + describe('fetchStages', () => { beforeEach(() => { mockedState.latestPipeline = pipelines[0]; }); describe('success', () => { beforeEach(() => { - mock.onGet(/\/api\/v4\/projects\/(.*)\/pipelines\/(.*)\/jobs/).replyOnce(() => [ - 200, - jobs, - { - 'x-next-page': page, - }, - ]); + mock.onGet(/\/(.*)\/pipelines\/(.*)\/builds.json/).replyOnce(200, stages); }); it('dispatches request', done => { testAction( - fetchJobs, + fetchStages, null, mockedState, [], - [{ type: 'requestJobs' }, { type: 'receiveJobsSuccess' }], + [{ type: 'requestStages' }, { type: 'receiveStagesSuccess' }], done, ); }); it('dispatches success with latest pipeline', done => { testAction( - fetchJobs, + fetchStages, null, mockedState, [], - [{ type: 'requestJobs' }, { type: 'receiveJobsSuccess', payload: jobs }], - done, - ); - }); - - it('dispatches twice for both pages', done => { - page = '2'; - - testAction( - fetchJobs, - null, - mockedState, - [], - [ - { type: 'requestJobs' }, - { type: 'receiveJobsSuccess', payload: jobs }, - { type: 'fetchJobs', payload: '2' }, - { type: 'requestJobs' }, - { type: 'receiveJobsSuccess', payload: jobs }, - ], + [{ type: 'requestStages' }, { type: 'receiveStagesSuccess', payload: stages }], done, ); }); @@ -243,44 +216,27 @@ describe('IDE pipelines actions', () => { it('calls axios with correct URL', () => { const apiSpy = spyOn(axios, 'get').and.callThrough(); - fetchJobs({ dispatch() {}, state: mockedState, rootState: mockedState }); + fetchStages({ dispatch() {}, state: mockedState, rootState: mockedState }); - expect(apiSpy).toHaveBeenCalledWith('/api/v4/projects/test%2Fproject/pipelines/1/jobs', { - params: { page: '1' }, - }); - }); - - it('calls axios with page next page', () => { - const apiSpy = spyOn(axios, 'get').and.callThrough(); - - fetchJobs({ dispatch() {}, state: mockedState, rootState: mockedState }); - - expect(apiSpy).toHaveBeenCalledWith('/api/v4/projects/test%2Fproject/pipelines/1/jobs', { - params: { page: '1' }, - }); - - page = '2'; - - fetchJobs({ dispatch() {}, state: mockedState, rootState: mockedState }, page); - - expect(apiSpy).toHaveBeenCalledWith('/api/v4/projects/test%2Fproject/pipelines/1/jobs', { - params: { page: '2' }, - }); + expect(apiSpy).toHaveBeenCalledWith( + '/test/project/pipelines/1/builds.json', + jasmine.anything(), + ); }); }); describe('error', () => { beforeEach(() => { - mock.onGet(/\/api\/v4\/projects\/(.*)\/pipelines(.*)/).replyOnce(500); + mock.onGet(/\/(.*)\/pipelines\/(.*)\/builds.json/).replyOnce(500); }); it('dispatches error', done => { testAction( - fetchJobs, + fetchStages, null, mockedState, [], - [{ type: 'requestJobs' }, { type: 'receiveJobsError' }], + [{ type: 'requestStages' }, { type: 'receiveStagesError' }], done, ); }); diff --git a/spec/javascripts/ide/stores/modules/pipelines/getters_spec.js b/spec/javascripts/ide/stores/modules/pipelines/getters_spec.js index b2a7e8a9025..4514896b5ea 100644 --- a/spec/javascripts/ide/stores/modules/pipelines/getters_spec.js +++ b/spec/javascripts/ide/stores/modules/pipelines/getters_spec.js @@ -37,35 +37,4 @@ describe('IDE pipeline getters', () => { expect(getters.hasLatestPipeline(mockedState)).toBe(true); }); }); - - describe('failedJobs', () => { - it('returns array of failed jobs', () => { - mockedState.stages = [ - { - title: 'test', - jobs: [{ id: 1, status: 'failed' }, { id: 2, status: 'success' }], - }, - { - title: 'build', - jobs: [{ id: 3, status: 'failed' }, { id: 4, status: 'failed' }], - }, - ]; - - expect(getters.failedJobs(mockedState).length).toBe(3); - expect(getters.failedJobs(mockedState)).toEqual([ - { - id: 1, - status: jasmine.anything(), - }, - { - id: 3, - status: jasmine.anything(), - }, - { - id: 4, - status: jasmine.anything(), - }, - ]); - }); - }); }); diff --git a/spec/javascripts/ide/stores/modules/pipelines/mutations_spec.js b/spec/javascripts/ide/stores/modules/pipelines/mutations_spec.js index 8262e916243..d47ec33ad4d 100644 --- a/spec/javascripts/ide/stores/modules/pipelines/mutations_spec.js +++ b/spec/javascripts/ide/stores/modules/pipelines/mutations_spec.js @@ -1,7 +1,7 @@ import mutations from '~/ide/stores/modules/pipelines/mutations'; import state from '~/ide/stores/modules/pipelines/state'; import * as types from '~/ide/stores/modules/pipelines/mutation_types'; -import { pipelines, jobs } from '../../../mock_data'; +import { pipelines, stages } from '../../../mock_data'; describe('IDE pipelines mutations', () => { let mockedState; @@ -49,70 +49,47 @@ describe('IDE pipelines mutations', () => { }); }); - describe(types.REQUEST_JOBS, () => { - it('sets jobs loading to true', () => { - mutations[types.REQUEST_JOBS](mockedState); + describe(types.REQUEST_STAGES, () => { + it('sets stages loading to true', () => { + mutations[types.REQUEST_STAGES](mockedState); expect(mockedState.isLoadingJobs).toBe(true); }); }); - describe(types.RECEIVE_JOBS_ERROR, () => { + describe(types.RECEIVE_STAGES_ERROR, () => { it('sets jobs loading to false', () => { - mutations[types.RECEIVE_JOBS_ERROR](mockedState); + mutations[types.RECEIVE_STAGES_ERROR](mockedState); expect(mockedState.isLoadingJobs).toBe(false); }); }); - describe(types.RECEIVE_JOBS_SUCCESS, () => { + describe(types.RECEIVE_STAGES_SUCCESS, () => { it('sets jobs loading to false on success', () => { - mutations[types.RECEIVE_JOBS_SUCCESS](mockedState, jobs); + mutations[types.RECEIVE_STAGES_SUCCESS](mockedState, stages); expect(mockedState.isLoadingJobs).toBe(false); }); it('sets stages', () => { - mutations[types.RECEIVE_JOBS_SUCCESS](mockedState, jobs); + mutations[types.RECEIVE_STAGES_SUCCESS](mockedState, stages); expect(mockedState.stages.length).toBe(2); expect(mockedState.stages).toEqual([ { - title: 'test', - jobs: jasmine.anything(), + ...stages[0], + id: 0, + isCollapsed: false, + isLoading: false, + jobs: [], }, { - title: 'build', - jobs: jasmine.anything(), - }, - ]); - }); - - it('sets jobs in stages', () => { - mutations[types.RECEIVE_JOBS_SUCCESS](mockedState, jobs); - - expect(mockedState.stages[0].jobs.length).toBe(3); - expect(mockedState.stages[1].jobs.length).toBe(1); - expect(mockedState.stages).toEqual([ - { - title: jasmine.anything(), - jobs: jobs.filter(job => job.stage === 'test').map(job => ({ - id: job.id, - name: job.name, - status: job.status, - stage: job.stage, - duration: job.duration, - })), - }, - { - title: jasmine.anything(), - jobs: jobs.filter(job => job.stage === 'build').map(job => ({ - id: job.id, - name: job.name, - status: job.status, - stage: job.stage, - duration: job.duration, - })), + ...stages[1], + id: 1, + isCollapsed: false, + isLoading: false, + jobs: [], }, ]); }); From 0bc9e0b4feb746f1b9fe233bfbb6e9afd70e0b98 Mon Sep 17 00:00:00 2001 From: Mayra Cabrera Date: Thu, 24 May 2018 15:06:58 -0500 Subject: [PATCH 012/153] Removes redundant error message for script failures Script failure message was redundant so it was removed. Also 'check your job log' message was removed from all the error messages. Closes #44271 --- app/presenters/commit_status_presenter.rb | 3 +-- app/serializers/job_entity.rb | 2 +- ...-redundant-message-for-failure-reasons.yml | 5 ++++ spec/presenters/ci/build_presenter_spec.rb | 4 +-- spec/serializers/job_entity_spec.rb | 25 +++++++++++++------ 5 files changed, 26 insertions(+), 13 deletions(-) create mode 100644 changelogs/unreleased/46552-fixes-redundant-message-for-failure-reasons.yml diff --git a/app/presenters/commit_status_presenter.rb b/app/presenters/commit_status_presenter.rb index c7f7aa836bd..9a7aaf4ef32 100644 --- a/app/presenters/commit_status_presenter.rb +++ b/app/presenters/commit_status_presenter.rb @@ -1,11 +1,10 @@ class CommitStatusPresenter < Gitlab::View::Presenter::Delegated CALLOUT_FAILURE_MESSAGES = { unknown_failure: 'There is an unknown failure, please try again', - script_failure: 'There has been a script failure. Check the job log for more information', api_failure: 'There has been an API failure, please try again', stuck_or_timeout_failure: 'There has been a timeout failure or the job got stuck. Check your timeout limits or try again', runner_system_failure: 'There has been a runner system failure, please try again', - missing_dependency_failure: 'There has been a missing dependency failure, check the job log for more information' + missing_dependency_failure: 'There has been a missing dependency failure' }.freeze presents :build diff --git a/app/serializers/job_entity.rb b/app/serializers/job_entity.rb index 3076fed1674..30f0bda9418 100644 --- a/app/serializers/job_entity.rb +++ b/app/serializers/job_entity.rb @@ -54,7 +54,7 @@ class JobEntity < Grape::Entity end def failed? - build.failed? + build.failed? && !build.script_failure? end def callout_message diff --git a/changelogs/unreleased/46552-fixes-redundant-message-for-failure-reasons.yml b/changelogs/unreleased/46552-fixes-redundant-message-for-failure-reasons.yml new file mode 100644 index 00000000000..43427aaa242 --- /dev/null +++ b/changelogs/unreleased/46552-fixes-redundant-message-for-failure-reasons.yml @@ -0,0 +1,5 @@ +--- +title: Removes redundant script failure message from Job page +merge_request: 19138 +author: +type: changed diff --git a/spec/presenters/ci/build_presenter_spec.rb b/spec/presenters/ci/build_presenter_spec.rb index efd175247b5..6dfaa3b72f7 100644 --- a/spec/presenters/ci/build_presenter_spec.rb +++ b/spec/presenters/ci/build_presenter_spec.rb @@ -219,11 +219,11 @@ describe Ci::BuildPresenter do end describe '#callout_failure_message' do - let(:build) { create(:ci_build, :failed, :script_failure) } + let(:build) { create(:ci_build, :failed, :api_failure) } it 'returns a verbose failure reason' do description = subject.callout_failure_message - expect(description).to eq('There has been a script failure. Check the job log for more information') + expect(description).to eq('There has been an API failure, please try again') end end diff --git a/spec/serializers/job_entity_spec.rb b/spec/serializers/job_entity_spec.rb index c90396ebb28..a5581a34517 100644 --- a/spec/serializers/job_entity_spec.rb +++ b/spec/serializers/job_entity_spec.rb @@ -131,7 +131,7 @@ describe JobEntity do end context 'when job failed' do - let(:job) { create(:ci_build, :script_failure) } + let(:job) { create(:ci_build, :api_failure) } it 'contains details' do expect(subject[:status]).to include :icon, :favicon, :text, :label, :tooltip @@ -142,20 +142,20 @@ describe JobEntity do end it 'should indicate the failure reason on tooltip' do - expect(subject[:status][:tooltip]).to eq('failed
(script failure)') + expect(subject[:status][:tooltip]).to eq('failed
(API failure)') end it 'should include a callout message with a verbose output' do - expect(subject[:callout_message]).to eq('There has been a script failure. Check the job log for more information') + expect(subject[:callout_message]).to eq('There has been an API failure, please try again') end it 'should state that it is not recoverable' do - expect(subject[:recoverable]).to be_falsy + expect(subject[:recoverable]).to be_truthy end end context 'when job is allowed to fail' do - let(:job) { create(:ci_build, :allowed_to_fail, :script_failure) } + let(:job) { create(:ci_build, :allowed_to_fail, :api_failure) } it 'contains details' do expect(subject[:status]).to include :icon, :favicon, :text, :label, :tooltip @@ -166,15 +166,24 @@ describe JobEntity do end it 'should indicate the failure reason on tooltip' do - expect(subject[:status][:tooltip]).to eq('failed
(script failure) (allowed to fail)') + expect(subject[:status][:tooltip]).to eq('failed
(API failure) (allowed to fail)') end it 'should include a callout message with a verbose output' do - expect(subject[:callout_message]).to eq('There has been a script failure. Check the job log for more information') + expect(subject[:callout_message]).to eq('There has been an API failure, please try again') end it 'should state that it is not recoverable' do - expect(subject[:recoverable]).to be_falsy + expect(subject[:recoverable]).to be_truthy + end + end + + context 'when the job failed with a script failure' do + let(:job) { create(:ci_build, :failed, :script_failure) } + + it 'should not include callout message or recoverable keys' do + expect(subject).not_to include('callout_message') + expect(subject).not_to include('recoverable') end end From 9c464e5eae6e4eb268bbdae6ad542d1de698b623 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Fri, 25 May 2018 13:26:44 +0100 Subject: [PATCH 013/153] removed hacky $forceUpdate --- app/assets/javascripts/vue_shared/components/tabs/tab.vue | 5 ----- 1 file changed, 5 deletions(-) diff --git a/app/assets/javascripts/vue_shared/components/tabs/tab.vue b/app/assets/javascripts/vue_shared/components/tabs/tab.vue index 9b2f46186ac..2a35d6bc151 100644 --- a/app/assets/javascripts/vue_shared/components/tabs/tab.vue +++ b/app/assets/javascripts/vue_shared/components/tabs/tab.vue @@ -26,11 +26,6 @@ export default { created() { this.isTab = true; }, - updated() { - if (this.$parent) { - this.$parent.$forceUpdate(); - } - }, }; From a83dd6642104faf4c8764283f5b7252a8ecd9590 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Fri, 25 May 2018 14:13:59 +0100 Subject: [PATCH 014/153] refactored to use data we already have this required moving some data store actions & mutations around --- app/assets/javascripts/api.js | 16 ----- .../ide/components/ide_status_bar.vue | 15 +++-- .../javascripts/ide/components/jobs/stage.vue | 5 +- .../ide/components/pipelines/list.vue | 19 ++---- .../javascripts/ide/stores/actions/project.js | 62 ----------------- .../ide/stores/modules/pipelines/actions.js | 66 +++++++++++-------- .../modules/pipelines/mutation_types.js | 4 -- .../ide/stores/modules/pipelines/mutations.js | 35 ++++------ .../javascripts/ide/stores/mutation_types.js | 1 - .../ide/stores/mutations/branch.js | 9 --- .../projects/pipelines_controller.rb | 11 +--- 11 files changed, 69 insertions(+), 174 deletions(-) diff --git a/app/assets/javascripts/api.js b/app/assets/javascripts/api.js index dbdc4de7986..47bf55b1c23 100644 --- a/app/assets/javascripts/api.js +++ b/app/assets/javascripts/api.js @@ -24,8 +24,6 @@ const Api = { commitPipelinesPath: '/:project_id/commit/:sha/pipelines', branchSinglePath: '/api/:version/projects/:id/repository/branches/:branch', createBranchPath: '/api/:version/projects/:id/repository/branches', - pipelinesPath: '/api/:version/projects/:id/pipelines', - pipelineJobsPath: '/:project_path/pipelines/:id/builds.json', group(groupId, callback) { const url = Api.buildUrl(Api.groupPath).replace(':id', groupId); @@ -238,20 +236,6 @@ const Api = { }); }, - pipelines(projectPath, params = {}) { - const url = Api.buildUrl(this.pipelinesPath).replace(':id', encodeURIComponent(projectPath)); - - return axios.get(url, { params }); - }, - - pipelineJobs(projectPath, pipelineId, params = {}) { - const url = Api.buildUrl(this.pipelineJobsPath) - .replace(':project_path', projectPath) - .replace(':id', pipelineId); - - return axios.get(url, { params }); - }, - buildUrl(url) { let urlRoot = ''; if (gon.relative_url_root != null) { diff --git a/app/assets/javascripts/ide/components/ide_status_bar.vue b/app/assets/javascripts/ide/components/ide_status_bar.vue index 6f60cfbf184..368a2995ed9 100644 --- a/app/assets/javascripts/ide/components/ide_status_bar.vue +++ b/app/assets/javascripts/ide/components/ide_status_bar.vue @@ -31,6 +31,7 @@ export default { computed: { ...mapState(['currentBranchId', 'currentProjectId']), ...mapGetters(['currentProject', 'lastCommit']), + ...mapState('pipelines', ['latestPipeline']), }, watch: { lastCommit() { @@ -51,14 +52,14 @@ export default { } }, methods: { - ...mapActions(['pipelinePoll', 'stopPipelinePolling']), + ...mapActions('pipelines', ['fetchLatestPipeline', 'stopPipelinePolling']), startTimer() { this.intervalId = setInterval(() => { this.commitAgeUpdate(); }, 1000); }, initPipelinePolling() { - this.pipelinePoll(); + this.fetchLatestPipeline(); this.isPollingInitialized = true; }, commitAgeUpdate() { @@ -81,18 +82,18 @@ export default { > Pipeline #{{ lastCommit.pipeline.id }} - {{ lastCommit.pipeline.details.status.text }} + :href="latestPipeline.details.status.details_path">#{{ latestPipeline.id }} + {{ latestPipeline.details.status.text }} for diff --git a/app/assets/javascripts/ide/components/jobs/stage.vue b/app/assets/javascripts/ide/components/jobs/stage.vue index 7f1a0ed1218..342d5f2a859 100644 --- a/app/assets/javascripts/ide/components/jobs/stage.vue +++ b/app/assets/javascripts/ide/components/jobs/stage.vue @@ -73,7 +73,10 @@ export default { > {{ stage.name }} -
+
{{ jobsCount }} diff --git a/app/assets/javascripts/ide/components/pipelines/list.vue b/app/assets/javascripts/ide/components/pipelines/list.vue index 8eed902d4e2..124585cd331 100644 --- a/app/assets/javascripts/ide/components/pipelines/list.vue +++ b/app/assets/javascripts/ide/components/pipelines/list.vue @@ -15,21 +15,14 @@ export default { JobsList, }, computed: { - ...mapGetters(['currentProject']), ...mapGetters('pipelines', ['jobsCount', 'failedJobsCount', 'failedStages']), ...mapState('pipelines', ['isLoadingPipeline', 'latestPipeline', 'stages', 'isLoadingJobs']), - statusIcon() { - return { - group: this.latestPipeline.status, - icon: `status_${this.latestPipeline.status}`, - }; - }, }, created() { - return this.fetchLatestPipeline().then(() => this.fetchStages()); + this.fetchLatestPipeline(); }, methods: { - ...mapActions('pipelines', ['fetchLatestPipeline', 'fetchStages']), + ...mapActions('pipelines', ['fetchLatestPipeline']), }, }; @@ -46,7 +39,7 @@ export default { class="ide-tree-header ide-pipeline-header" > @@ -54,7 +47,7 @@ export default { Pipeline #{{ latestPipeline.id }} @@ -66,7 +59,7 @@ export default {