summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/assets/javascripts/commit/pipelines/pipelines_table.js2
-rw-r--r--app/assets/javascripts/environments/components/environment_item.vue2
-rw-r--r--app/assets/javascripts/pipelines/components/pipelines.vue289
-rw-r--r--app/assets/javascripts/pipelines/components/pipelines_actions.js91
-rw-r--r--app/assets/javascripts/pipelines/components/pipelines_actions.vue88
-rw-r--r--app/assets/javascripts/pipelines/components/pipelines_artifacts.js33
-rw-r--r--app/assets/javascripts/pipelines/components/pipelines_artifacts.vue51
-rw-r--r--app/assets/javascripts/pipelines/components/time_ago.js98
-rw-r--r--app/assets/javascripts/pipelines/components/time_ago.vue85
-rw-r--r--app/assets/javascripts/pipelines/index.js22
-rw-r--r--app/assets/javascripts/pipelines/pipelines.js293
-rw-r--r--app/assets/javascripts/pipelines/pipelines_bundle.js24
-rw-r--r--app/assets/javascripts/vue_shared/components/commit.js159
-rw-r--r--app/assets/javascripts/vue_shared/components/commit.vue166
-rw-r--r--app/assets/javascripts/vue_shared/components/pipelines_table.js55
-rw-r--r--app/assets/javascripts/vue_shared/components/pipelines_table.vue55
-rw-r--r--app/assets/javascripts/vue_shared/components/pipelines_table_row.vue (renamed from app/assets/javascripts/vue_shared/components/pipelines_table_row.js)154
-rw-r--r--app/models/environment.rb3
-rw-r--r--app/serializers/build_details_entity.rb2
-rw-r--r--changelogs/unreleased/zj-raise-etag-route-regex-miss.yml4
-rw-r--r--config/webpack.config.js2
-rw-r--r--doc/user/project/integrations/jira.md2
-rw-r--r--lib/gitlab/etag_caching/middleware.rb2
-rw-r--r--lib/gitlab/etag_caching/router.rb4
-rw-r--r--lib/gitlab/etag_caching/store.rb2
-rw-r--r--spec/controllers/projects/jobs_controller_spec.rb136
-rw-r--r--spec/features/projects/jobs_spec.rb150
-rw-r--r--spec/javascripts/pipelines/pipelines_actions_spec.js2
-rw-r--r--spec/javascripts/pipelines/pipelines_artifacts_spec.js2
-rw-r--r--spec/javascripts/pipelines/pipelines_spec.js2
-rw-r--r--spec/javascripts/pipelines/time_ago_spec.js2
-rw-r--r--spec/javascripts/vue_shared/components/commit_spec.js2
-rw-r--r--spec/javascripts/vue_shared/components/pipelines_table_row_spec.js2
-rw-r--r--spec/javascripts/vue_shared/components/pipelines_table_spec.js2
-rw-r--r--spec/lib/gitlab/etag_caching/middleware_spec.rb33
-rw-r--r--spec/lib/gitlab/etag_caching/router_spec.rb44
-rw-r--r--spec/requests/api/jobs_spec.rb114
37 files changed, 1086 insertions, 1093 deletions
diff --git a/app/assets/javascripts/commit/pipelines/pipelines_table.js b/app/assets/javascripts/commit/pipelines/pipelines_table.js
index 082fbafb740..70ba83ce5b9 100644
--- a/app/assets/javascripts/commit/pipelines/pipelines_table.js
+++ b/app/assets/javascripts/commit/pipelines/pipelines_table.js
@@ -1,6 +1,6 @@
import Vue from 'vue';
import Visibility from 'visibilityjs';
-import pipelinesTableComponent from '../../vue_shared/components/pipelines_table';
+import pipelinesTableComponent from '../../vue_shared/components/pipelines_table.vue';
import PipelinesService from '../../pipelines/services/pipelines_service';
import PipelineStore from '../../pipelines/stores/pipelines_store';
import eventHub from '../../pipelines/event_hub';
diff --git a/app/assets/javascripts/environments/components/environment_item.vue b/app/assets/javascripts/environments/components/environment_item.vue
index de2269118cd..614637b637e 100644
--- a/app/assets/javascripts/environments/components/environment_item.vue
+++ b/app/assets/javascripts/environments/components/environment_item.vue
@@ -9,7 +9,7 @@ import StopComponent from './environment_stop.vue';
import RollbackComponent from './environment_rollback.vue';
import TerminalButtonComponent from './environment_terminal_button.vue';
import MonitoringButtonComponent from './environment_monitoring.vue';
-import CommitComponent from '../../vue_shared/components/commit';
+import CommitComponent from '../../vue_shared/components/commit.vue';
import eventHub from '../event_hub';
/**
diff --git a/app/assets/javascripts/pipelines/components/pipelines.vue b/app/assets/javascripts/pipelines/components/pipelines.vue
new file mode 100644
index 00000000000..fed42d23112
--- /dev/null
+++ b/app/assets/javascripts/pipelines/components/pipelines.vue
@@ -0,0 +1,289 @@
+<script>
+ import Visibility from 'visibilityjs';
+ import PipelinesService from '../services/pipelines_service';
+ import eventHub from '../event_hub';
+ import pipelinesTableComponent from '../../vue_shared/components/pipelines_table.vue';
+ import tablePagination from '../../vue_shared/components/table_pagination.vue';
+ import emptyState from './empty_state.vue';
+ import errorState from './error_state.vue';
+ import navigationTabs from './navigation_tabs.vue';
+ import navigationControls from './nav_controls.vue';
+ import loadingIcon from '../../vue_shared/components/loading_icon.vue';
+ import Poll from '../../lib/utils/poll';
+
+ export default {
+ props: {
+ store: {
+ type: Object,
+ required: true,
+ },
+ },
+ components: {
+ tablePagination,
+ pipelinesTableComponent,
+ emptyState,
+ errorState,
+ navigationTabs,
+ navigationControls,
+ loadingIcon,
+ },
+ data() {
+ const pipelinesData = document.querySelector('#pipelines-list-vue').dataset;
+
+ return {
+ endpoint: pipelinesData.endpoint,
+ cssClass: pipelinesData.cssClass,
+ helpPagePath: pipelinesData.helpPagePath,
+ newPipelinePath: pipelinesData.newPipelinePath,
+ canCreatePipeline: pipelinesData.canCreatePipeline,
+ allPath: pipelinesData.allPath,
+ pendingPath: pipelinesData.pendingPath,
+ runningPath: pipelinesData.runningPath,
+ finishedPath: pipelinesData.finishedPath,
+ branchesPath: pipelinesData.branchesPath,
+ tagsPath: pipelinesData.tagsPath,
+ hasCi: pipelinesData.hasCi,
+ ciLintPath: pipelinesData.ciLintPath,
+ state: this.store.state,
+ apiScope: 'all',
+ pagenum: 1,
+ isLoading: false,
+ hasError: false,
+ isMakingRequest: false,
+ updateGraphDropdown: false,
+ hasMadeRequest: false,
+ };
+ },
+ computed: {
+ canCreatePipelineParsed() {
+ return gl.utils.convertPermissionToBoolean(this.canCreatePipeline);
+ },
+ scope() {
+ const scope = gl.utils.getParameterByName('scope');
+ return scope === null ? 'all' : scope;
+ },
+ shouldRenderErrorState() {
+ return this.hasError && !this.isLoading;
+ },
+
+ /**
+ * The empty state should only be rendered when the request is made to fetch all pipelines
+ * and none is returned.
+ *
+ * @return {Boolean}
+ */
+ shouldRenderEmptyState() {
+ return !this.isLoading &&
+ !this.hasError &&
+ this.hasMadeRequest &&
+ !this.state.pipelines.length &&
+ (this.scope === 'all' || this.scope === null);
+ },
+ /**
+ * When a specific scope does not have pipelines we render a message.
+ *
+ * @return {Boolean}
+ */
+ shouldRenderNoPipelinesMessage() {
+ return !this.isLoading &&
+ !this.hasError &&
+ !this.state.pipelines.length &&
+ this.scope !== 'all' &&
+ this.scope !== null;
+ },
+
+ shouldRenderTable() {
+ return !this.hasError &&
+ !this.isLoading && this.state.pipelines.length;
+ },
+ /**
+ * Pagination should only be rendered when there is more than one page.
+ *
+ * @return {Boolean}
+ */
+ shouldRenderPagination() {
+ return !this.isLoading &&
+ this.state.pipelines.length &&
+ this.state.pageInfo.total > this.state.pageInfo.perPage;
+ },
+
+ hasCiEnabled() {
+ return this.hasCi !== undefined;
+ },
+ paths() {
+ return {
+ allPath: this.allPath,
+ pendingPath: this.pendingPath,
+ finishedPath: this.finishedPath,
+ runningPath: this.runningPath,
+ branchesPath: this.branchesPath,
+ tagsPath: this.tagsPath,
+ };
+ },
+ pageParameter() {
+ return gl.utils.getParameterByName('page') || this.pagenum;
+ },
+ scopeParameter() {
+ return gl.utils.getParameterByName('scope') || this.apiScope;
+ },
+ },
+ created() {
+ this.service = new PipelinesService(this.endpoint);
+
+ const poll = new Poll({
+ resource: this.service,
+ method: 'getPipelines',
+ data: { page: this.pageParameter, scope: this.scopeParameter },
+ successCallback: this.successCallback,
+ errorCallback: this.errorCallback,
+ notificationCallback: this.setIsMakingRequest,
+ });
+
+ if (!Visibility.hidden()) {
+ this.isLoading = true;
+ poll.makeRequest();
+ } else {
+ // If tab is not visible we need to make the first request so we don't show the empty
+ // state without knowing if there are any pipelines
+ this.fetchPipelines();
+ }
+
+ Visibility.change(() => {
+ if (!Visibility.hidden()) {
+ poll.restart();
+ } else {
+ poll.stop();
+ }
+ });
+
+ eventHub.$on('refreshPipelines', this.fetchPipelines);
+ },
+ beforeDestroy() {
+ eventHub.$off('refreshPipelines');
+ },
+ methods: {
+ /**
+ * Will change the page number and update the URL.
+ *
+ * @param {Number} pageNumber desired page to go to.
+ */
+ change(pageNumber) {
+ const param = gl.utils.setParamInURL('page', pageNumber);
+
+ gl.utils.visitUrl(param);
+ return param;
+ },
+
+ fetchPipelines() {
+ if (!this.isMakingRequest) {
+ this.isLoading = true;
+
+ this.service.getPipelines({ scope: this.scopeParameter, page: this.pageParameter })
+ .then(response => this.successCallback(response))
+ .catch(() => this.errorCallback());
+ }
+ },
+ successCallback(resp) {
+ const response = {
+ headers: resp.headers,
+ body: resp.json(),
+ };
+
+ this.store.storeCount(response.body.count);
+ this.store.storePipelines(response.body.pipelines);
+ this.store.storePagination(response.headers);
+
+ this.isLoading = false;
+ this.updateGraphDropdown = true;
+ this.hasMadeRequest = true;
+ },
+
+ errorCallback() {
+ this.hasError = true;
+ this.isLoading = false;
+ this.updateGraphDropdown = false;
+ },
+
+ setIsMakingRequest(isMakingRequest) {
+ this.isMakingRequest = isMakingRequest;
+
+ if (isMakingRequest) {
+ this.updateGraphDropdown = false;
+ }
+ },
+ },
+ };
+</script>
+<template>
+ <div :class="cssClass">
+
+ <div
+ class="top-area scrolling-tabs-container inner-page-scroll-tabs"
+ v-if="!isLoading && !shouldRenderEmptyState">
+ <div class="fade-left">
+ <i
+ class="fa fa-angle-left"
+ aria-hidden="true">
+ </i>
+ </div>
+ <div class="fade-right">
+ <i
+ class="fa fa-angle-right"
+ aria-hidden="true">
+ </i>
+ </div>
+ <navigation-tabs
+ :scope="scope"
+ :count="state.count"
+ :paths="paths"
+ />
+
+ <navigation-controls
+ :new-pipeline-path="newPipelinePath"
+ :has-ci-enabled="hasCiEnabled"
+ :help-page-path="helpPagePath"
+ :ciLintPath="ciLintPath"
+ :can-create-pipeline="canCreatePipelineParsed "
+ />
+ </div>
+
+ <div class="content-list pipelines">
+
+ <loading-icon
+ label="Loading Pipelines"
+ size="3"
+ v-if="isLoading"
+ />
+
+ <empty-state
+ v-if="shouldRenderEmptyState"
+ :help-page-path="helpPagePath"
+ />
+
+ <error-state v-if="shouldRenderErrorState" />
+
+ <div
+ class="blank-state blank-state-no-icon"
+ v-if="shouldRenderNoPipelinesMessage">
+ <h2 class="blank-state-title js-blank-state-title">No pipelines to show.</h2>
+ </div>
+
+ <div
+ class="table-holder"
+ v-if="shouldRenderTable">
+
+ <pipelines-table-component
+ :pipelines="state.pipelines"
+ :service="service"
+ :update-graph-dropdown="updateGraphDropdown"
+ />
+ </div>
+
+ <table-pagination
+ v-if="shouldRenderPagination"
+ :change="change"
+ :pageInfo="state.pageInfo"
+ />
+ </div>
+ </div>
+</template>
diff --git a/app/assets/javascripts/pipelines/components/pipelines_actions.js b/app/assets/javascripts/pipelines/components/pipelines_actions.js
deleted file mode 100644
index b9e066c5db1..00000000000
--- a/app/assets/javascripts/pipelines/components/pipelines_actions.js
+++ /dev/null
@@ -1,91 +0,0 @@
-/* eslint-disable no-new */
-/* global Flash */
-import '~/flash';
-import playIconSvg from 'icons/_icon_play.svg';
-import eventHub from '../event_hub';
-import loadingIconComponent from '../../vue_shared/components/loading_icon.vue';
-
-export default {
- props: {
- actions: {
- type: Array,
- required: true,
- },
-
- service: {
- type: Object,
- required: true,
- },
- },
-
- components: {
- loadingIconComponent,
- },
-
- data() {
- return {
- playIconSvg,
- isLoading: false,
- };
- },
-
- methods: {
- onClickAction(endpoint) {
- this.isLoading = true;
-
- $(this.$refs.tooltip).tooltip('destroy');
-
- this.service.postAction(endpoint)
- .then(() => {
- this.isLoading = false;
- eventHub.$emit('refreshPipelines');
- })
- .catch(() => {
- this.isLoading = false;
- new Flash('An error occured while making the request.');
- });
- },
-
- isActionDisabled(action) {
- if (action.playable === undefined) {
- return false;
- }
-
- return !action.playable;
- },
- },
-
- template: `
- <div class="btn-group" v-if="actions">
- <button
- type="button"
- class="dropdown-toggle btn btn-default has-tooltip js-pipeline-dropdown-manual-actions"
- title="Manual job"
- data-toggle="dropdown"
- data-placement="top"
- aria-label="Manual job"
- ref="tooltip"
- :disabled="isLoading">
- ${playIconSvg}
- <i
- class="fa fa-caret-down"
- aria-hidden="true" />
- <loading-icon v-if="isLoading" />
- </button>
-
- <ul class="dropdown-menu dropdown-menu-align-right">
- <li v-for="action in actions">
- <button
- type="button"
- class="js-pipeline-action-link no-btn btn"
- @click="onClickAction(action.path)"
- :class="{ 'disabled': isActionDisabled(action) }"
- :disabled="isActionDisabled(action)">
- ${playIconSvg}
- <span>{{action.name}}</span>
- </button>
- </li>
- </ul>
- </div>
- `,
-};
diff --git a/app/assets/javascripts/pipelines/components/pipelines_actions.vue b/app/assets/javascripts/pipelines/components/pipelines_actions.vue
new file mode 100644
index 00000000000..da5df2a06cf
--- /dev/null
+++ b/app/assets/javascripts/pipelines/components/pipelines_actions.vue
@@ -0,0 +1,88 @@
+<script>
+ /* global Flash */
+ import '~/flash';
+ import playIconSvg from 'icons/_icon_play.svg';
+ import eventHub from '../event_hub';
+ import loadingIcon from '../../vue_shared/components/loading_icon.vue';
+
+ export default {
+ props: {
+ actions: {
+ type: Array,
+ required: true,
+ },
+ service: {
+ type: Object,
+ required: true,
+ },
+ },
+ components: {
+ loadingIcon,
+ },
+ data() {
+ return {
+ playIconSvg,
+ isLoading: false,
+ };
+ },
+ methods: {
+ onClickAction(endpoint) {
+ this.isLoading = true;
+
+ $(this.$refs.tooltip).tooltip('destroy');
+
+ this.service.postAction(endpoint)
+ .then(() => {
+ this.isLoading = false;
+ eventHub.$emit('refreshPipelines');
+ })
+ .catch(() => {
+ this.isLoading = false;
+ // eslint-disable-next-line no-new
+ new Flash('An error occured while making the request.');
+ });
+ },
+ isActionDisabled(action) {
+ if (action.playable === undefined) {
+ return false;
+ }
+
+ return !action.playable;
+ },
+ },
+ };
+</script>
+<template>
+ <div class="btn-group">
+ <button
+ type="button"
+ class="dropdown-toggle btn btn-default has-tooltip js-pipeline-dropdown-manual-actions"
+ title="Manual job"
+ data-toggle="dropdown"
+ data-placement="top"
+ aria-label="Manual job"
+ ref="tooltip"
+ :disabled="isLoading">
+ <span v-html="playIconSvg"></span>
+ <i
+ class="fa fa-caret-down"
+ aria-hidden="true">
+ </i>
+ <loading-icon v-if="isLoading" />
+ </button>
+
+ <ul class="dropdown-menu dropdown-menu-align-right">
+ <li v-for="action in actions">
+ <button
+ type="button"
+ class="js-pipeline-action-link no-btn btn"
+ @click="onClickAction(action.path)"
+ :class="{ disabled: isActionDisabled(action) }"
+ :disabled="isActionDisabled(action)">
+ <span v-html="playIconSvg"></span>
+ <span>{{action.name}}</span>
+ </button>
+ </li>
+ </ul>
+ </div>
+</template>
diff --git a/app/assets/javascripts/pipelines/components/pipelines_artifacts.js b/app/assets/javascripts/pipelines/components/pipelines_artifacts.js
deleted file mode 100644
index f18e2dfadaf..00000000000
--- a/app/assets/javascripts/pipelines/components/pipelines_artifacts.js
+++ /dev/null
@@ -1,33 +0,0 @@
-export default {
- props: {
- artifacts: {
- type: Array,
- required: true,
- },
- },
-
- template: `
- <div class="btn-group" role="group">
- <button
- class="dropdown-toggle btn btn-default build-artifacts has-tooltip js-pipeline-dropdown-download"
- title="Artifacts"
- data-placement="top"
- data-toggle="dropdown"
- aria-label="Artifacts">
- <i class="fa fa-download" aria-hidden="true"></i>
- <i class="fa fa-caret-down" aria-hidden="true"></i>
- </button>
- <ul class="dropdown-menu dropdown-menu-align-right">
- <li v-for="artifact in artifacts">
- <a
- rel="nofollow"
- download
- :href="artifact.path">
- <i class="fa fa-download" aria-hidden="true"></i>
- <span>Download {{artifact.name}} artifacts</span>
- </a>
- </li>
- </ul>
- </div>
- `,
-};
diff --git a/app/assets/javascripts/pipelines/components/pipelines_artifacts.vue b/app/assets/javascripts/pipelines/components/pipelines_artifacts.vue
new file mode 100644
index 00000000000..b4520481cdc
--- /dev/null
+++ b/app/assets/javascripts/pipelines/components/pipelines_artifacts.vue
@@ -0,0 +1,51 @@
+<script>
+ import tooltipMixin from '../../vue_shared/mixins/tooltip';
+
+ export default {
+ props: {
+ artifacts: {
+ type: Array,
+ required: true,
+ },
+ },
+ mixins: [
+ tooltipMixin,
+ ],
+ };
+</script>
+<template>
+ <div
+ class="btn-group"
+ role="group">
+ <button
+ class="dropdown-toggle btn btn-default build-artifacts js-pipeline-dropdown-download"
+ title="Artifacts"
+ data-placement="top"
+ data-toggle="dropdown"
+ aria-label="Artifacts"
+ ref="tooltip">
+ <i
+ class="fa fa-download"
+ aria-hidden="true">
+ </i>
+ <i
+ class="fa fa-caret-down"
+ aria-hidden="true">
+ </i>
+ </button>
+ <ul class="dropdown-menu dropdown-menu-align-right">
+ <li v-for="artifact in artifacts">
+ <a
+ rel="nofollow"
+ download
+ :href="artifact.path">
+ <i
+ class="fa fa-download"
+ aria-hidden="true">
+ </i>
+ <span>Download {{artifact.name}} artifacts</span>
+ </a>
+ </li>
+ </ul>
+ </div>
+</template>
diff --git a/app/assets/javascripts/pipelines/components/time_ago.js b/app/assets/javascripts/pipelines/components/time_ago.js
deleted file mode 100644
index 188f74cc705..00000000000
--- a/app/assets/javascripts/pipelines/components/time_ago.js
+++ /dev/null
@@ -1,98 +0,0 @@
-import iconTimerSvg from 'icons/_icon_timer.svg';
-import '../../lib/utils/datetime_utility';
-
-export default {
- props: {
- finishedTime: {
- type: String,
- required: true,
- },
-
- duration: {
- type: Number,
- required: true,
- },
- },
-
- data() {
- return {
- iconTimerSvg,
- };
- },
-
- updated() {
- $(this.$refs.tooltip).tooltip('fixTitle');
- },
-
- computed: {
- hasDuration() {
- return this.duration > 0;
- },
-
- hasFinishedTime() {
- return this.finishedTime !== '';
- },
-
- localTimeFinished() {
- return gl.utils.formatDate(this.finishedTime);
- },
-
- durationFormated() {
- const date = new Date(this.duration * 1000);
-
- let hh = date.getUTCHours();
- let mm = date.getUTCMinutes();
- let ss = date.getSeconds();
-
- // left pad
- if (hh < 10) {
- hh = `0${hh}`;
- }
- if (mm < 10) {
- mm = `0${mm}`;
- }
- if (ss < 10) {
- ss = `0${ss}`;
- }
-
- return `${hh}:${mm}:${ss}`;
- },
-
- finishedTimeFormated() {
- const timeAgo = gl.utils.getTimeago();
-
- return timeAgo.format(this.finishedTime);
- },
- },
-
- template: `
- <td class="pipelines-time-ago">
- <p
- class="duration"
- v-if="hasDuration">
- <span
- v-html="iconTimerSvg">
- </span>
- {{durationFormated}}
- </p>
-
- <p
- class="finished-at"
- v-if="hasFinishedTime">
-
- <i
- class="fa fa-calendar"
- aria-hidden="true" />
-
- <time
- ref="tooltip"
- data-toggle="tooltip"
- data-placement="top"
- data-container="body"
- :title="localTimeFinished">
- {{finishedTimeFormated}}
- </time>
- </p>
- </td>
- `,
-};
diff --git a/app/assets/javascripts/pipelines/components/time_ago.vue b/app/assets/javascripts/pipelines/components/time_ago.vue
new file mode 100644
index 00000000000..c47658cd6e6
--- /dev/null
+++ b/app/assets/javascripts/pipelines/components/time_ago.vue
@@ -0,0 +1,85 @@
+<script>
+ import iconTimerSvg from 'icons/_icon_timer.svg';
+ import '../../lib/utils/datetime_utility';
+ import tooltipMixin from '../../vue_shared/mixins/tooltip';
+ import timeagoMixin from '../../vue_shared/mixins/timeago';
+
+ export default {
+ props: {
+ finishedTime: {
+ type: String,
+ required: true,
+ },
+ duration: {
+ type: Number,
+ required: true,
+ },
+ },
+ mixins: [
+ tooltipMixin,
+ timeagoMixin,
+ ],
+ data() {
+ return {
+ iconTimerSvg,
+ };
+ },
+ computed: {
+ hasDuration() {
+ return this.duration > 0;
+ },
+ hasFinishedTime() {
+ return this.finishedTime !== '';
+ },
+ durationFormated() {
+ const date = new Date(this.duration * 1000);
+
+ let hh = date.getUTCHours();
+ let mm = date.getUTCMinutes();
+ let ss = date.getSeconds();
+
+ // left pad
+ if (hh < 10) {
+ hh = `0${hh}`;
+ }
+ if (mm < 10) {
+ mm = `0${mm}`;
+ }
+ if (ss < 10) {
+ ss = `0${ss}`;
+ }
+
+ return `${hh}:${mm}:${ss}`;
+ },
+ },
+ };
+</script>
+<template>
+ <td class="pipelines-time-ago">
+ <p
+ class="duration"
+ v-if="hasDuration">
+ <span v-html="iconTimerSvg">
+ </span>
+ {{durationFormated}}
+ </p>
+
+ <p
+ class="finished-at"
+ v-if="hasFinishedTime">
+
+ <i
+ class="fa fa-calendar"
+ aria-hidden="true">
+ </i>
+
+ <time
+ ref="tooltip"
+ data-placement="top"
+ data-container="body"
+ :title="tooltipTitle(finishedTime)">
+ {{timeFormated(finishedTime)}}
+ </time>
+ </p>
+ </td>
+</script>
diff --git a/app/assets/javascripts/pipelines/index.js b/app/assets/javascripts/pipelines/index.js
deleted file mode 100644
index 48f9181a8d9..00000000000
--- a/app/assets/javascripts/pipelines/index.js
+++ /dev/null
@@ -1,22 +0,0 @@
-import Vue from 'vue';
-import PipelinesStore from './stores/pipelines_store';
-import PipelinesComponent from './pipelines';
-import '../vue_shared/vue_resource_interceptor';
-
-$(() => new Vue({
- el: document.querySelector('#pipelines-list-vue'),
-
- data() {
- const store = new PipelinesStore();
-
- return {
- store,
- };
- },
- components: {
- 'vue-pipelines': PipelinesComponent,
- },
- template: `
- <vue-pipelines :store="store" />
- `,
-}));
diff --git a/app/assets/javascripts/pipelines/pipelines.js b/app/assets/javascripts/pipelines/pipelines.js
deleted file mode 100644
index b530461837c..00000000000
--- a/app/assets/javascripts/pipelines/pipelines.js
+++ /dev/null
@@ -1,293 +0,0 @@
-import Visibility from 'visibilityjs';
-import PipelinesService from './services/pipelines_service';
-import eventHub from './event_hub';
-import pipelinesTableComponent from '../vue_shared/components/pipelines_table';
-import tablePagination from '../vue_shared/components/table_pagination.vue';
-import emptyState from './components/empty_state.vue';
-import errorState from './components/error_state.vue';
-import navigationTabs from './components/navigation_tabs.vue';
-import navigationControls from './components/nav_controls.vue';
-import loadingIcon from '../vue_shared/components/loading_icon.vue';
-import Poll from '../lib/utils/poll';
-
-export default {
- props: {
- store: {
- type: Object,
- required: true,
- },
- },
-
- components: {
- tablePagination,
- pipelinesTableComponent,
- emptyState,
- errorState,
- navigationTabs,
- navigationControls,
- loadingIcon,
- },
-
- data() {
- const pipelinesData = document.querySelector('#pipelines-list-vue').dataset;
-
- return {
- endpoint: pipelinesData.endpoint,
- cssClass: pipelinesData.cssClass,
- helpPagePath: pipelinesData.helpPagePath,
- newPipelinePath: pipelinesData.newPipelinePath,
- canCreatePipeline: pipelinesData.canCreatePipeline,
- allPath: pipelinesData.allPath,
- pendingPath: pipelinesData.pendingPath,
- runningPath: pipelinesData.runningPath,
- finishedPath: pipelinesData.finishedPath,
- branchesPath: pipelinesData.branchesPath,
- tagsPath: pipelinesData.tagsPath,
- hasCi: pipelinesData.hasCi,
- ciLintPath: pipelinesData.ciLintPath,
- state: this.store.state,
- apiScope: 'all',
- pagenum: 1,
- isLoading: false,
- hasError: false,
- isMakingRequest: false,
- updateGraphDropdown: false,
- hasMadeRequest: false,
- };
- },
-
- computed: {
- canCreatePipelineParsed() {
- return gl.utils.convertPermissionToBoolean(this.canCreatePipeline);
- },
-
- scope() {
- const scope = gl.utils.getParameterByName('scope');
- return scope === null ? 'all' : scope;
- },
-
- shouldRenderErrorState() {
- return this.hasError && !this.isLoading;
- },
-
- /**
- * The empty state should only be rendered when the request is made to fetch all pipelines
- * and none is returned.
- *
- * @return {Boolean}
- */
- shouldRenderEmptyState() {
- return !this.isLoading &&
- !this.hasError &&
- this.hasMadeRequest &&
- !this.state.pipelines.length &&
- (this.scope === 'all' || this.scope === null);
- },
-
- /**
- * When a specific scope does not have pipelines we render a message.
- *
- * @return {Boolean}
- */
- shouldRenderNoPipelinesMessage() {
- return !this.isLoading &&
- !this.hasError &&
- !this.state.pipelines.length &&
- this.scope !== 'all' &&
- this.scope !== null;
- },
-
- shouldRenderTable() {
- return !this.hasError &&
- !this.isLoading && this.state.pipelines.length;
- },
-
- /**
- * Pagination should only be rendered when there is more than one page.
- *
- * @return {Boolean}
- */
- shouldRenderPagination() {
- return !this.isLoading &&
- this.state.pipelines.length &&
- this.state.pageInfo.total > this.state.pageInfo.perPage;
- },
-
- hasCiEnabled() {
- return this.hasCi !== undefined;
- },
-
- paths() {
- return {
- allPath: this.allPath,
- pendingPath: this.pendingPath,
- finishedPath: this.finishedPath,
- runningPath: this.runningPath,
- branchesPath: this.branchesPath,
- tagsPath: this.tagsPath,
- };
- },
-
- pageParameter() {
- return gl.utils.getParameterByName('page') || this.pagenum;
- },
-
- scopeParameter() {
- return gl.utils.getParameterByName('scope') || this.apiScope;
- },
- },
-
- created() {
- this.service = new PipelinesService(this.endpoint);
-
- const poll = new Poll({
- resource: this.service,
- method: 'getPipelines',
- data: { page: this.pageParameter, scope: this.scopeParameter },
- successCallback: this.successCallback,
- errorCallback: this.errorCallback,
- notificationCallback: this.setIsMakingRequest,
- });
-
- if (!Visibility.hidden()) {
- this.isLoading = true;
- poll.makeRequest();
- } else {
- // If tab is not visible we need to make the first request so we don't show the empty
- // state without knowing if there are any pipelines
- this.fetchPipelines();
- }
-
- Visibility.change(() => {
- if (!Visibility.hidden()) {
- poll.restart();
- } else {
- poll.stop();
- }
- });
-
- eventHub.$on('refreshPipelines', this.fetchPipelines);
- },
-
- beforeDestroy() {
- eventHub.$off('refreshPipelines');
- },
-
- methods: {
- /**
- * Will change the page number and update the URL.
- *
- * @param {Number} pageNumber desired page to go to.
- */
- change(pageNumber) {
- const param = gl.utils.setParamInURL('page', pageNumber);
-
- gl.utils.visitUrl(param);
- return param;
- },
-
- fetchPipelines() {
- if (!this.isMakingRequest) {
- this.isLoading = true;
-
- this.service.getPipelines({ scope: this.scopeParameter, page: this.pageParameter })
- .then(response => this.successCallback(response))
- .catch(() => this.errorCallback());
- }
- },
-
- successCallback(resp) {
- const response = {
- headers: resp.headers,
- body: resp.json(),
- };
-
- this.store.storeCount(response.body.count);
- this.store.storePipelines(response.body.pipelines);
- this.store.storePagination(response.headers);
-
- this.isLoading = false;
- this.updateGraphDropdown = true;
- this.hasMadeRequest = true;
- },
-
- errorCallback() {
- this.hasError = true;
- this.isLoading = false;
- this.updateGraphDropdown = false;
- },
-
- setIsMakingRequest(isMakingRequest) {
- this.isMakingRequest = isMakingRequest;
-
- if (isMakingRequest) {
- this.updateGraphDropdown = false;
- }
- },
- },
-
- template: `
- <div :class="cssClass">
-
- <div
- class="top-area scrolling-tabs-container inner-page-scroll-tabs"
- v-if="!isLoading && !shouldRenderEmptyState">
- <div class="fade-left">
- <i class="fa fa-angle-left" aria-hidden="true"></i>
- </div>
- <div class="fade-right">
- <i class="fa fa-angle-right" aria-hidden="true"></i>
- </div>
- <navigation-tabs
- :scope="scope"
- :count="state.count"
- :paths="paths" />
-
- <navigation-controls
- :new-pipeline-path="newPipelinePath"
- :has-ci-enabled="hasCiEnabled"
- :help-page-path="helpPagePath"
- :ciLintPath="ciLintPath"
- :can-create-pipeline="canCreatePipelineParsed " />
- </div>
-
- <div class="content-list pipelines">
-
- <loading-icon
- label="Loading Pipelines"
- size="3"
- v-if="isLoading"
- />
-
- <empty-state
- v-if="shouldRenderEmptyState"
- :help-page-path="helpPagePath" />
-
- <error-state v-if="shouldRenderErrorState" />
-
- <div
- class="blank-state blank-state-no-icon"
- v-if="shouldRenderNoPipelinesMessage">
- <h2 class="blank-state-title js-blank-state-title">No pipelines to show.</h2>
- </div>
-
- <div
- class="table-holder"
- v-if="shouldRenderTable">
-
- <pipelines-table-component
- :pipelines="state.pipelines"
- :service="service"
- :update-graph-dropdown="updateGraphDropdown"
- />
- </div>
-
- <table-pagination
- v-if="shouldRenderPagination"
- :change="change"
- :pageInfo="state.pageInfo"
- />
- </div>
- </div>
- `,
-};
diff --git a/app/assets/javascripts/pipelines/pipelines_bundle.js b/app/assets/javascripts/pipelines/pipelines_bundle.js
new file mode 100644
index 00000000000..923d9bfb248
--- /dev/null
+++ b/app/assets/javascripts/pipelines/pipelines_bundle.js
@@ -0,0 +1,24 @@
+import Vue from 'vue';
+import PipelinesStore from './stores/pipelines_store';
+import pipelinesComponent from './components/pipelines.vue';
+
+document.addEventListener('DOMContentLoaded', () => new Vue({
+ el: '#pipelines-list-vue',
+ data() {
+ const store = new PipelinesStore();
+
+ return {
+ store,
+ };
+ },
+ components: {
+ pipelinesComponent,
+ },
+ render(createElement) {
+ return createElement('pipelines-component', {
+ props: {
+ store: this.store,
+ },
+ });
+ },
+}));
diff --git a/app/assets/javascripts/vue_shared/components/commit.js b/app/assets/javascripts/vue_shared/components/commit.js
deleted file mode 100644
index ff5ae28e062..00000000000
--- a/app/assets/javascripts/vue_shared/components/commit.js
+++ /dev/null
@@ -1,159 +0,0 @@
-import commitIconSvg from 'icons/_icon_commit.svg';
-import userAvatarLink from './user_avatar/user_avatar_link.vue';
-
-export default {
- props: {
- /**
- * Indicates the existance of a tag.
- * Used to render the correct icon, if true will render `fa-tag` icon,
- * if false will render `fa-code-fork` icon.
- */
- tag: {
- type: Boolean,
- required: false,
- default: false,
- },
-
- /**
- * If provided is used to render the branch name and url.
- * Should contain the following properties:
- * name
- * ref_url
- */
- commitRef: {
- type: Object,
- required: false,
- default: () => ({}),
- },
-
- /**
- * Used to link to the commit sha.
- */
- commitUrl: {
- type: String,
- required: false,
- default: '',
- },
-
- /**
- * Used to show the commit short sha that links to the commit url.
- */
- shortSha: {
- type: String,
- required: false,
- default: '',
- },
-
- /**
- * If provided shows the commit tile.
- */
- title: {
- type: String,
- required: false,
- default: '',
- },
-
- /**
- * If provided renders information about the author of the commit.
- * When provided should include:
- * `avatar_url` to render the avatar icon
- * `web_url` to link to user profile
- * `username` to render alt and title tags
- */
- author: {
- type: Object,
- required: false,
- default: () => ({}),
- },
- },
-
- computed: {
- /**
- * Used to verify if all the properties needed to render the commit
- * ref section were provided.
- *
- * TODO: Improve this! Use lodash _.has when we have it.
- *
- * @returns {Boolean}
- */
- hasCommitRef() {
- return this.commitRef && this.commitRef.name && this.commitRef.ref_url;
- },
-
- /**
- * Used to verify if all the properties needed to render the commit
- * author section were provided.
- *
- * TODO: Improve this! Use lodash _.has when we have it.
- *
- * @returns {Boolean}
- */
- hasAuthor() {
- return this.author &&
- this.author.avatar_url &&
- this.author.path &&
- this.author.username;
- },
-
- /**
- * If information about the author is provided will return a string
- * to be rendered as the alt attribute of the img tag.
- *
- * @returns {String}
- */
- userImageAltDescription() {
- return this.author &&
- this.author.username ? `${this.author.username}'s avatar` : null;
- },
- },
-
- data() {
- return { commitIconSvg };
- },
-
- components: {
- userAvatarLink,
- },
- template: `
- <div class="branch-commit">
-
- <div v-if="hasCommitRef" class="icon-container">
- <i v-if="tag" class="fa fa-tag"></i>
- <i v-if="!tag" class="fa fa-code-fork"></i>
- </div>
-
- <a v-if="hasCommitRef"
- class="ref-name"
- :href="commitRef.ref_url">
- {{commitRef.name}}
- </a>
-
- <div v-html="commitIconSvg" class="commit-icon js-commit-icon"></div>
-
- <a class="commit-sha"
- :href="commitUrl">
- {{shortSha}}
- </a>
-
- <div class="commit-title flex-truncate-parent">
- <span v-if="title" class="flex-truncate-child">
- <user-avatar-link
- v-if="hasAuthor"
- class="avatar-image-container"
- :link-href="author.path"
- :img-src="author.avatar_url"
- :img-alt="userImageAltDescription"
- :tooltip-text="author.username"
- />
- <a class="commit-row-message"
- :href="commitUrl">
- {{title}}
- </a>
- </span>
- <span v-else>
- Cant find HEAD commit for this branch
- </span>
- </div>
- </div>
- `,
-};
diff --git a/app/assets/javascripts/vue_shared/components/commit.vue b/app/assets/javascripts/vue_shared/components/commit.vue
new file mode 100644
index 00000000000..fcf48b11057
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/components/commit.vue
@@ -0,0 +1,166 @@
+<script>
+ import commitIconSvg from 'icons/_icon_commit.svg';
+ import userAvatarLink from './user_avatar/user_avatar_link.vue';
+
+ export default {
+ props: {
+ /**
+ * Indicates the existance of a tag.
+ * Used to render the correct icon, if true will render `fa-tag` icon,
+ * if false will render `fa-code-fork` icon.
+ */
+ tag: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ /**
+ * If provided is used to render the branch name and url.
+ * Should contain the following properties:
+ * name
+ * ref_url
+ */
+ commitRef: {
+ type: Object,
+ required: false,
+ default: () => ({}),
+ },
+ /**
+ * Used to link to the commit sha.
+ */
+ commitUrl: {
+ type: String,
+ required: false,
+ default: '',
+ },
+
+ /**
+ * Used to show the commit short sha that links to the commit url.
+ */
+ shortSha: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ /**
+ * If provided shows the commit tile.
+ */
+ title: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ /**
+ * If provided renders information about the author of the commit.
+ * When provided should include:
+ * `avatar_url` to render the avatar icon
+ * `web_url` to link to user profile
+ * `username` to render alt and title tags
+ */
+ author: {
+ type: Object,
+ required: false,
+ default: () => ({}),
+ },
+ },
+ computed: {
+ /**
+ * Used to verify if all the properties needed to render the commit
+ * ref section were provided.
+ *
+ * TODO: Improve this! Use lodash _.has when we have it.
+ *
+ * @returns {Boolean}
+ */
+ hasCommitRef() {
+ return this.commitRef && this.commitRef.name && this.commitRef.ref_url;
+ },
+ /**
+ * Used to verify if all the properties needed to render the commit
+ * author section were provided.
+ *
+ * TODO: Improve this! Use lodash _.has when we have it.
+ *
+ * @returns {Boolean}
+ */
+ hasAuthor() {
+ return this.author &&
+ this.author.avatar_url &&
+ this.author.path &&
+ this.author.username;
+ },
+ /**
+ * If information about the author is provided will return a string
+ * to be rendered as the alt attribute of the img tag.
+ *
+ * @returns {String}
+ */
+ userImageAltDescription() {
+ return this.author &&
+ this.author.username ? `${this.author.username}'s avatar` : null;
+ },
+ },
+ data() {
+ return { commitIconSvg };
+ },
+ components: {
+ userAvatarLink,
+ },
+ };
+</script>
+<template>
+ <div class="branch-commit">
+ <div v-if="hasCommitRef" class="icon-container">
+ <i
+ v-if="tag"
+ class="fa fa-tag"
+ aria-hidden="true">
+ </i>
+ <i
+ v-if="!tag"
+ class="fa fa-code-fork"
+ aria-hidden="true">
+ </i>
+ </div>
+
+ <a
+ v-if="hasCommitRef"
+ class="ref-name"
+ :href="commitRef.ref_url">
+ {{commitRef.name}}
+ </a>
+
+ <div
+ v-html="commitIconSvg"
+ class="commit-icon js-commit-icon">
+ </div>
+
+ <a
+ class="commit-sha"
+ :href="commitUrl">
+ {{shortSha}}
+ </a>
+
+ <div class="commit-title flex-truncate-parent">
+ <span
+ v-if="title"
+ class="flex-truncate-child">
+ <user-avatar-link
+ v-if="hasAuthor"
+ class="avatar-image-container"
+ :link-href="author.path"
+ :img-src="author.avatar_url"
+ :img-alt="userImageAltDescription"
+ :tooltip-text="author.username"
+ />
+ <a class="commit-row-message"
+ :href="commitUrl">
+ {{title}}
+ </a>
+ </span>
+ <span v-else>
+ Cant find HEAD commit for this branch
+ </span>
+ </div>
+ </div>
+</template>
diff --git a/app/assets/javascripts/vue_shared/components/pipelines_table.js b/app/assets/javascripts/vue_shared/components/pipelines_table.js
deleted file mode 100644
index 48a39f18112..00000000000
--- a/app/assets/javascripts/vue_shared/components/pipelines_table.js
+++ /dev/null
@@ -1,55 +0,0 @@
-import PipelinesTableRowComponent from './pipelines_table_row';
-
-/**
- * Pipelines Table Component.
- *
- * Given an array of objects, renders a table.
- */
-export default {
- props: {
- pipelines: {
- type: Array,
- required: true,
- },
-
- service: {
- type: Object,
- required: true,
- },
-
- updateGraphDropdown: {
- type: Boolean,
- required: false,
- default: false,
- },
- },
-
- components: {
- 'pipelines-table-row-component': PipelinesTableRowComponent,
- },
-
- template: `
- <table class="table ci-table">
- <thead>
- <tr>
- <th class="js-pipeline-status pipeline-status">Status</th>
- <th class="js-pipeline-info pipeline-info">Pipeline</th>
- <th class="js-pipeline-commit pipeline-commit">Commit</th>
- <th class="js-pipeline-stages pipeline-stages">Stages</th>
- <th class="js-pipeline-date pipeline-date"></th>
- <th class="js-pipeline-actions pipeline-actions"></th>
- </tr>
- </thead>
- <tbody>
- <template v-for="model in pipelines"
- v-bind:model="model">
- <tr is="pipelines-table-row-component"
- :pipeline="model"
- :service="service"
- :update-graph-dropdown="updateGraphDropdown"
- />
- </template>
- </tbody>
- </table>
- `,
-};
diff --git a/app/assets/javascripts/vue_shared/components/pipelines_table.vue b/app/assets/javascripts/vue_shared/components/pipelines_table.vue
new file mode 100644
index 00000000000..ebe8fba8a44
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/components/pipelines_table.vue
@@ -0,0 +1,55 @@
+<script>
+ import pipelinesTableRowComponent from './pipelines_table_row.vue';
+
+ /**
+ * Pipelines Table Component.
+ *
+ * Given an array of objects, renders a table.
+ */
+ export default {
+ props: {
+ pipelines: {
+ type: Array,
+ required: true,
+ },
+ service: {
+ type: Object,
+ required: true,
+ },
+ updateGraphDropdown: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ },
+ components: {
+ pipelinesTableRowComponent,
+ },
+ };
+</script>
+<template>
+ <table class="table ci-table">
+ <thead>
+ <tr>
+ <th class="js-pipeline-status pipeline-status">Status</th>
+ <th class="js-pipeline-info pipeline-info">Pipeline</th>
+ <th class="js-pipeline-commit pipeline-commit">Commit</th>
+ <th class="js-pipeline-stages pipeline-stages">Stages</th>
+ <th class="js-pipeline-date pipeline-date"></th>
+ <th class="js-pipeline-actions pipeline-actions"></th>
+ </tr>
+ </thead>
+ <tbody>
+ <template
+ v-for="model in pipelines"
+ :model="model">
+ <tr
+ is="pipelines-table-row-component"
+ :pipeline="model"
+ :service="service"
+ :update-graph-dropdown="updateGraphDropdown"
+ />
+ </template>
+ </tbody>
+ </table>
+</template>
diff --git a/app/assets/javascripts/vue_shared/components/pipelines_table_row.js b/app/assets/javascripts/vue_shared/components/pipelines_table_row.vue
index f60f8eeb43d..6e9757d5e5e 100644
--- a/app/assets/javascripts/vue_shared/components/pipelines_table_row.js
+++ b/app/assets/javascripts/vue_shared/components/pipelines_table_row.vue
@@ -1,12 +1,13 @@
+<script>
/* eslint-disable no-param-reassign */
-import AsyncButtonComponent from '../../pipelines/components/async_button.vue';
-import PipelinesActionsComponent from '../../pipelines/components/pipelines_actions';
-import PipelinesArtifactsComponent from '../../pipelines/components/pipelines_artifacts';
+import asyncButtonComponent from '../../pipelines/components/async_button.vue';
+import pipelinesActionsComponent from '../../pipelines/components/pipelines_actions.vue';
+import pipelinesArtifactsComponent from '../../pipelines/components/pipelines_artifacts.vue';
import ciBadge from './ci_badge_link.vue';
-import PipelinesStageComponent from '../../pipelines/components/stage.vue';
-import PipelinesUrlComponent from '../../pipelines/components/pipeline_url.vue';
-import PipelinesTimeagoComponent from '../../pipelines/components/time_ago';
-import CommitComponent from './commit';
+import pipelineStage from '../../pipelines/components/stage.vue';
+import pipelineUrl from '../../pipelines/components/pipeline_url.vue';
+import pipelinesTimeago from '../../pipelines/components/time_ago.vue';
+import commitComponent from './commit.vue';
/**
* Pipeline table row.
@@ -19,30 +20,26 @@ export default {
type: Object,
required: true,
},
-
service: {
type: Object,
required: true,
},
-
updateGraphDropdown: {
type: Boolean,
required: false,
default: false,
},
},
-
components: {
- 'async-button-component': AsyncButtonComponent,
- 'pipelines-actions-component': PipelinesActionsComponent,
- 'pipelines-artifacts-component': PipelinesArtifactsComponent,
- 'commit-component': CommitComponent,
- 'dropdown-stage': PipelinesStageComponent,
- 'pipeline-url': PipelinesUrlComponent,
+ asyncButtonComponent,
+ pipelinesActionsComponent,
+ pipelinesArtifactsComponent,
+ commitComponent,
+ pipelineStage,
+ pipelineUrl,
ciBadge,
- 'time-ago': PipelinesTimeagoComponent,
+ pipelinesTimeago,
},
-
computed: {
/**
* If provided, returns the commit tag.
@@ -204,69 +201,76 @@ export default {
return {};
},
},
+};
+</script>
+<template>
+ <tr class="commit">
+ <td class="commit-link">
+ <ci-badge :status="pipelineStatus" />
+ </td>
- template: `
- <tr class="commit">
- <td class="commit-link">
- <ci-badge :status="pipelineStatus"/>
- </td>
-
- <pipeline-url :pipeline="pipeline"></pipeline-url>
+ <pipeline-url :pipeline="pipeline" />
- <td>
- <commit-component
- :tag="commitTag"
- :commit-ref="commitRef"
- :commit-url="commitUrl"
- :short-sha="commitShortSha"
- :title="commitTitle"
- :author="commitAuthor"/>
- </td>
+ <td>
+ <commit-component
+ :tag="commitTag"
+ :commit-ref="commitRef"
+ :commit-url="commitUrl"
+ :short-sha="commitShortSha"
+ :title="commitTitle"
+ :author="commitAuthor"
+ />
+ </td>
- <td class="stage-cell">
- <div class="stage-container dropdown js-mini-pipeline-graph"
- v-if="pipeline.details.stages.length > 0"
- v-for="stage in pipeline.details.stages">
+ <td class="stage-cell">
+ <div class="stage-container dropdown js-mini-pipeline-graph"
+ v-if="pipeline.details.stages.length > 0"
+ v-for="stage in pipeline.details.stages">
- <dropdown-stage
- :stage="stage"
- :update-dropdown="updateGraphDropdown"/>
- </div>
- </td>
+ <pipeline-stage
+ :stage="stage"
+ :update-dropdown="updateGraphDropdown"
+ />
+ </div>
+ </td>
- <time-ago
- :duration="pipelineDuration"
- :finished-time="pipelineFinishedAt" />
+ <pipelines-timeago
+ :duration="pipelineDuration"
+ :finished-time="pipelineFinishedAt"
+ />
- <td class="pipeline-actions">
- <div class="pull-right btn-group">
- <pipelines-actions-component
- v-if="pipeline.details.manual_actions.length"
- :actions="pipeline.details.manual_actions"
- :service="service" />
+ <td class="pipeline-actions">
+ <div class="pull-right btn-group">
+ <pipelines-actions-component
+ v-if="pipeline.details.manual_actions.length"
+ :actions="pipeline.details.manual_actions"
+ :service="service"
+ />
- <pipelines-artifacts-component
- v-if="pipeline.details.artifacts.length"
- :artifacts="pipeline.details.artifacts" />
+ <pipelines-artifacts-component
+ v-if="pipeline.details.artifacts.length"
+ :artifacts="pipeline.details.artifacts"
+ />
- <async-button-component
- v-if="pipeline.flags.retryable"
- :service="service"
- :endpoint="pipeline.retry_path"
- css-class="js-pipelines-retry-button btn-default btn-retry"
- title="Retry"
- icon="repeat" />
+ <async-button-component
+ v-if="pipeline.flags.retryable"
+ :service="service"
+ :endpoint="pipeline.retry_path"
+ css-class="js-pipelines-retry-button btn-default btn-retry"
+ title="Retry"
+ icon="repeat"
+ />
- <async-button-component
- v-if="pipeline.flags.cancelable"
- :service="service"
- :endpoint="pipeline.cancel_path"
- css-class="js-pipelines-cancel-button btn-remove"
- title="Cancel"
- icon="remove"
- confirm-action-message="Are you sure you want to cancel this pipeline?" />
- </div>
- </td>
- </tr>
- `,
-};
+ <async-button-component
+ v-if="pipeline.flags.cancelable"
+ :service="service"
+ :endpoint="pipeline.cancel_path"
+ css-class="js-pipelines-cancel-button btn-remove"
+ title="Cancel"
+ icon="remove"
+ confirm-action-message="Are you sure you want to cancel this pipeline?"
+ />
+ </div>
+ </td>
+ </tr>
+</tr>
diff --git a/app/models/environment.rb b/app/models/environment.rb
index 6211a5c1e63..d5b974b2d31 100644
--- a/app/models/environment.rb
+++ b/app/models/environment.rb
@@ -209,7 +209,8 @@ class Environment < ActiveRecord::Base
def etag_cache_key
Gitlab::Routing.url_helpers.namespace_project_environments_path(
project.namespace,
- project)
+ project,
+ format: :json)
end
private
diff --git a/app/serializers/build_details_entity.rb b/app/serializers/build_details_entity.rb
index cb6d3dfec89..eeb5399aa8b 100644
--- a/app/serializers/build_details_entity.rb
+++ b/app/serializers/build_details_entity.rb
@@ -25,7 +25,7 @@ class BuildDetailsEntity < JobEntity
end
expose :raw_path do |build|
- raw_namespace_project_build_path(project.namespace, project, build)
+ raw_namespace_project_job_path(project.namespace, project, build)
end
private
diff --git a/changelogs/unreleased/zj-raise-etag-route-regex-miss.yml b/changelogs/unreleased/zj-raise-etag-route-regex-miss.yml
new file mode 100644
index 00000000000..57a5f4e44c0
--- /dev/null
+++ b/changelogs/unreleased/zj-raise-etag-route-regex-miss.yml
@@ -0,0 +1,4 @@
+---
+title: Fix etag route not being a match for environments
+merge_request:
+author:
diff --git a/config/webpack.config.js b/config/webpack.config.js
index 120f9d3193d..3c2455ebf35 100644
--- a/config/webpack.config.js
+++ b/config/webpack.config.js
@@ -61,7 +61,7 @@ var config = {
network: './network/network_bundle.js',
notebook_viewer: './blob/notebook_viewer.js',
pdf_viewer: './blob/pdf_viewer.js',
- pipelines: './pipelines/index.js',
+ pipelines: './pipelines/pipelines_bundle.js',
pipelines_details: './pipelines/pipeline_details_bundle.js',
profile: './profile/profile_bundle.js',
protected_branches: './protected_branches/protected_branches_bundle.js',
diff --git a/doc/user/project/integrations/jira.md b/doc/user/project/integrations/jira.md
index a048260b033..e8dbf8d08d2 100644
--- a/doc/user/project/integrations/jira.md
+++ b/doc/user/project/integrations/jira.md
@@ -76,7 +76,7 @@ We have split this stage in steps so it is easier to follow.
![JIRA add user to group](img/jira_add_user_to_group.png)
----
+ ---
The JIRA configuration is over. Write down the new JIRA username and its
password as they will be needed when configuring GitLab in the next section.
diff --git a/lib/gitlab/etag_caching/middleware.rb b/lib/gitlab/etag_caching/middleware.rb
index 7f884183bb1..1d6f5bb5e1c 100644
--- a/lib/gitlab/etag_caching/middleware.rb
+++ b/lib/gitlab/etag_caching/middleware.rb
@@ -7,7 +7,7 @@ module Gitlab
def call(env)
request = Rack::Request.new(env)
- route = Gitlab::EtagCaching::Router.match(request)
+ route = Gitlab::EtagCaching::Router.match(request.path_info)
return @app.call(env) unless route
track_event(:etag_caching_middleware_used, route)
diff --git a/lib/gitlab/etag_caching/router.rb b/lib/gitlab/etag_caching/router.rb
index dccc66b3918..75167a6b088 100644
--- a/lib/gitlab/etag_caching/router.rb
+++ b/lib/gitlab/etag_caching/router.rb
@@ -53,8 +53,8 @@ module Gitlab
)
].freeze
- def self.match(request)
- ROUTES.find { |route| route.regexp.match(request.path_info) }
+ def self.match(path)
+ ROUTES.find { |route| route.regexp.match(path) }
end
end
end
diff --git a/lib/gitlab/etag_caching/store.rb b/lib/gitlab/etag_caching/store.rb
index 0039fc01c8f..072fcfc65e6 100644
--- a/lib/gitlab/etag_caching/store.rb
+++ b/lib/gitlab/etag_caching/store.rb
@@ -25,6 +25,8 @@ module Gitlab
end
def redis_key(key)
+ raise 'Invalid key' if !Rails.env.production? && !Gitlab::EtagCaching::Router.match(key)
+
"#{REDIS_NAMESPACE}#{key}"
end
end
diff --git a/spec/controllers/projects/jobs_controller_spec.rb b/spec/controllers/projects/jobs_controller_spec.rb
index 4a737587899..472e5fc51a0 100644
--- a/spec/controllers/projects/jobs_controller_spec.rb
+++ b/spec/controllers/projects/jobs_controller_spec.rb
@@ -28,7 +28,7 @@ describe Projects::JobsController do
get_index(scope: 'running')
end
- it 'has only running builds' do
+ it 'has only running jobs' do
expect(response).to have_http_status(:ok)
expect(assigns(:builds).first.status).to eq('running')
end
@@ -41,7 +41,7 @@ describe Projects::JobsController do
get_index(scope: 'finished')
end
- it 'has only finished builds' do
+ it 'has only finished jobs' do
expect(response).to have_http_status(:ok)
expect(assigns(:builds).first.status).to eq('success')
end
@@ -67,7 +67,7 @@ describe Projects::JobsController do
context 'number of queries' do
before do
Ci::Build::AVAILABLE_STATUSES.each do |status|
- create_build(status, status)
+ create_job(status, status)
end
end
@@ -76,7 +76,7 @@ describe Projects::JobsController do
expect(recorded.count).to be_within(5).of(7)
end
- def create_build(name, status)
+ def create_job(name, status)
pipeline = create(:ci_pipeline, project: project)
create(:ci_build, :tags, :triggered, :artifacts,
pipeline: pipeline, name: name, status: status)
@@ -94,21 +94,21 @@ describe Projects::JobsController do
end
describe 'GET show' do
- let!(:build) { create(:ci_build, :failed, pipeline: pipeline) }
+ let!(:job) { create(:ci_build, :failed, pipeline: pipeline) }
context 'when requesting HTML' do
- context 'when build exists' do
+ context 'when job exists' do
before do
- get_show(id: build.id)
+ get_show(id: job.id)
end
- it 'has a build' do
+ it 'has a job' do
expect(response).to have_http_status(:ok)
- expect(assigns(:build).id).to eq(build.id)
+ expect(assigns(:build).id).to eq(job.id)
end
end
- context 'when build does not exist' do
+ context 'when job does not exist' do
before do
get_show(id: 1234)
end
@@ -128,12 +128,12 @@ describe Projects::JobsController do
allow_any_instance_of(Ci::Build).to receive(:merge_request).and_return(merge_request)
- get_show(id: build.id, format: :json)
+ get_show(id: job.id, format: :json)
end
it 'exposes needed information' do
expect(response).to have_http_status(:ok)
- expect(json_response['raw_path']).to match(/builds\/\d+\/raw\z/)
+ expect(json_response['raw_path']).to match(/jobs\/\d+\/raw\z/)
expect(json_response.dig('merge_request', 'path')).to match(/merge_requests\/\d+\z/)
expect(json_response['new_issue_path'])
.to include('/issues/new')
@@ -155,35 +155,35 @@ describe Projects::JobsController do
get_trace
end
- context 'when build has a trace' do
- let(:build) { create(:ci_build, :trace, pipeline: pipeline) }
+ context 'when job has a trace' do
+ let(:job) { create(:ci_build, :trace, pipeline: pipeline) }
it 'returns a trace' do
expect(response).to have_http_status(:ok)
- expect(json_response['id']).to eq build.id
- expect(json_response['status']).to eq build.status
+ expect(json_response['id']).to eq job.id
+ expect(json_response['status']).to eq job.status
expect(json_response['html']).to eq('BUILD TRACE')
end
end
- context 'when build has no traces' do
- let(:build) { create(:ci_build, pipeline: pipeline) }
+ context 'when job has no traces' do
+ let(:job) { create(:ci_build, pipeline: pipeline) }
it 'returns no traces' do
expect(response).to have_http_status(:ok)
- expect(json_response['id']).to eq build.id
- expect(json_response['status']).to eq build.status
+ expect(json_response['id']).to eq job.id
+ expect(json_response['status']).to eq job.status
expect(json_response['html']).to be_nil
end
end
- context 'when build has a trace with ANSI sequence and Unicode' do
- let(:build) { create(:ci_build, :unicode_trace, pipeline: pipeline) }
+ context 'when job has a trace with ANSI sequence and Unicode' do
+ let(:job) { create(:ci_build, :unicode_trace, pipeline: pipeline) }
it 'returns a trace with Unicode' do
expect(response).to have_http_status(:ok)
- expect(json_response['id']).to eq build.id
- expect(json_response['status']).to eq build.status
+ expect(json_response['id']).to eq job.id
+ expect(json_response['status']).to eq job.status
expect(json_response['html']).to include("ヾ(´༎ຶД༎ຶ`)ノ")
end
end
@@ -191,23 +191,23 @@ describe Projects::JobsController do
def get_trace
get :trace, namespace_id: project.namespace,
project_id: project,
- id: build.id,
+ id: job.id,
format: :json
end
end
describe 'GET status.json' do
- let(:build) { create(:ci_build, pipeline: pipeline) }
- let(:status) { build.detailed_status(double('user')) }
+ let(:job) { create(:ci_build, pipeline: pipeline) }
+ let(:status) { job.detailed_status(double('user')) }
before do
get :status, namespace_id: project.namespace,
project_id: project,
- id: build.id,
+ id: job.id,
format: :json
end
- it 'return a detailed build status in json' do
+ it 'return a detailed job status in json' do
expect(response).to have_http_status(:ok)
expect(json_response['text']).to eq status.text
expect(json_response['label']).to eq status.label
@@ -224,17 +224,17 @@ describe Projects::JobsController do
post_retry
end
- context 'when build is retryable' do
- let(:build) { create(:ci_build, :retryable, pipeline: pipeline) }
+ context 'when job is retryable' do
+ let(:job) { create(:ci_build, :retryable, pipeline: pipeline) }
- it 'redirects to the retried build page' do
+ it 'redirects to the retried job page' do
expect(response).to have_http_status(:found)
expect(response).to redirect_to(namespace_project_job_path(id: Ci::Build.last.id))
end
end
- context 'when build is not retryable' do
- let(:build) { create(:ci_build, pipeline: pipeline) }
+ context 'when job is not retryable' do
+ let(:job) { create(:ci_build, pipeline: pipeline) }
it 'renders unprocessable_entity' do
expect(response).to have_http_status(:unprocessable_entity)
@@ -244,7 +244,7 @@ describe Projects::JobsController do
def post_retry
post :retry, namespace_id: project.namespace,
project_id: project,
- id: build.id
+ id: job.id
end
end
@@ -260,21 +260,21 @@ describe Projects::JobsController do
post_play
end
- context 'when build is playable' do
- let(:build) { create(:ci_build, :playable, pipeline: pipeline) }
+ context 'when job is playable' do
+ let(:job) { create(:ci_build, :playable, pipeline: pipeline) }
- it 'redirects to the played build page' do
+ it 'redirects to the played job page' do
expect(response).to have_http_status(:found)
- expect(response).to redirect_to(namespace_project_job_path(id: build.id))
+ expect(response).to redirect_to(namespace_project_job_path(id: job.id))
end
it 'transits to pending' do
- expect(build.reload).to be_pending
+ expect(job.reload).to be_pending
end
end
- context 'when build is not playable' do
- let(:build) { create(:ci_build, pipeline: pipeline) }
+ context 'when job is not playable' do
+ let(:job) { create(:ci_build, pipeline: pipeline) }
it 'renders unprocessable_entity' do
expect(response).to have_http_status(:unprocessable_entity)
@@ -284,7 +284,7 @@ describe Projects::JobsController do
def post_play
post :play, namespace_id: project.namespace,
project_id: project,
- id: build.id
+ id: job.id
end
end
@@ -296,21 +296,21 @@ describe Projects::JobsController do
post_cancel
end
- context 'when build is cancelable' do
- let(:build) { create(:ci_build, :cancelable, pipeline: pipeline) }
+ context 'when job is cancelable' do
+ let(:job) { create(:ci_build, :cancelable, pipeline: pipeline) }
- it 'redirects to the canceled build page' do
+ it 'redirects to the canceled job page' do
expect(response).to have_http_status(:found)
- expect(response).to redirect_to(namespace_project_job_path(id: build.id))
+ expect(response).to redirect_to(namespace_project_job_path(id: job.id))
end
it 'transits to canceled' do
- expect(build.reload).to be_canceled
+ expect(job.reload).to be_canceled
end
end
- context 'when build is not cancelable' do
- let(:build) { create(:ci_build, :canceled, pipeline: pipeline) }
+ context 'when job is not cancelable' do
+ let(:job) { create(:ci_build, :canceled, pipeline: pipeline) }
it 'returns unprocessable_entity' do
expect(response).to have_http_status(:unprocessable_entity)
@@ -320,7 +320,7 @@ describe Projects::JobsController do
def post_cancel
post :cancel, namespace_id: project.namespace,
project_id: project,
- id: build.id
+ id: job.id
end
end
@@ -330,7 +330,7 @@ describe Projects::JobsController do
sign_in(user)
end
- context 'when builds are cancelable' do
+ context 'when jobs are cancelable' do
before do
create_list(:ci_build, 2, :cancelable, pipeline: pipeline)
@@ -347,7 +347,7 @@ describe Projects::JobsController do
end
end
- context 'when builds are not cancelable' do
+ context 'when jobs are not cancelable' do
before do
create_list(:ci_build, 2, :canceled, pipeline: pipeline)
@@ -374,26 +374,26 @@ describe Projects::JobsController do
post_erase
end
- context 'when build is erasable' do
- let(:build) { create(:ci_build, :erasable, :trace, pipeline: pipeline) }
+ context 'when job is erasable' do
+ let(:job) { create(:ci_build, :erasable, :trace, pipeline: pipeline) }
- it 'redirects to the erased build page' do
+ it 'redirects to the erased job page' do
expect(response).to have_http_status(:found)
- expect(response).to redirect_to(namespace_project_job_path(id: build.id))
+ expect(response).to redirect_to(namespace_project_job_path(id: job.id))
end
it 'erases artifacts' do
- expect(build.artifacts_file.exists?).to be_falsey
- expect(build.artifacts_metadata.exists?).to be_falsey
+ expect(job.artifacts_file.exists?).to be_falsey
+ expect(job.artifacts_metadata.exists?).to be_falsey
end
it 'erases trace' do
- expect(build.trace.exist?).to be_falsey
+ expect(job.trace.exist?).to be_falsey
end
end
- context 'when build is not erasable' do
- let(:build) { create(:ci_build, :erased, pipeline: pipeline) }
+ context 'when job is not erasable' do
+ let(:job) { create(:ci_build, :erased, pipeline: pipeline) }
it 'returns unprocessable_entity' do
expect(response).to have_http_status(:unprocessable_entity)
@@ -403,7 +403,7 @@ describe Projects::JobsController do
def post_erase
post :erase, namespace_id: project.namespace,
project_id: project,
- id: build.id
+ id: job.id
end
end
@@ -412,8 +412,8 @@ describe Projects::JobsController do
get_raw
end
- context 'when build has a trace file' do
- let(:build) { create(:ci_build, :trace, pipeline: pipeline) }
+ context 'when job has a trace file' do
+ let(:job) { create(:ci_build, :trace, pipeline: pipeline) }
it 'send a trace file' do
expect(response).to have_http_status(:ok)
@@ -422,8 +422,8 @@ describe Projects::JobsController do
end
end
- context 'when build does not have a trace file' do
- let(:build) { create(:ci_build, pipeline: pipeline) }
+ context 'when job does not have a trace file' do
+ let(:job) { create(:ci_build, pipeline: pipeline) }
it 'returns not_found' do
expect(response).to have_http_status(:not_found)
@@ -433,7 +433,7 @@ describe Projects::JobsController do
def get_raw
post :raw, namespace_id: project.namespace,
project_id: project,
- id: build.id
+ id: job.id
end
end
end
diff --git a/spec/features/projects/jobs_spec.rb b/spec/features/projects/jobs_spec.rb
index 727ae7081b0..31c93c75d25 100644
--- a/spec/features/projects/jobs_spec.rb
+++ b/spec/features/projects/jobs_spec.rb
@@ -8,8 +8,8 @@ feature 'Jobs', :feature do
let(:namespace) { project.namespace }
let(:pipeline) { create(:ci_pipeline, project: project) }
- let(:build) { create(:ci_build, :trace, pipeline: pipeline) }
- let(:build2) { create(:ci_build) }
+ let(:job) { create(:ci_build, :trace, pipeline: pipeline) }
+ let(:job2) { create(:ci_build) }
let(:artifacts_file) do
fixture_file_upload(Rails.root + 'spec/fixtures/banana_sample.gif', 'image/gif')
@@ -21,7 +21,7 @@ feature 'Jobs', :feature do
end
describe "GET /:project/jobs" do
- let!(:build) { create(:ci_build, pipeline: pipeline) }
+ let!(:job) { create(:ci_build, pipeline: pipeline) }
context "Pending scope" do
before do
@@ -31,30 +31,30 @@ feature 'Jobs', :feature do
it "shows Pending tab jobs" do
expect(page).to have_link 'Cancel running'
expect(page).to have_selector('.nav-links li.active', text: 'Pending')
- expect(page).to have_content build.short_sha
- expect(page).to have_content build.ref
- expect(page).to have_content build.name
+ expect(page).to have_content job.short_sha
+ expect(page).to have_content job.ref
+ expect(page).to have_content job.name
end
end
context "Running scope" do
before do
- build.run!
+ job.run!
visit namespace_project_jobs_path(project.namespace, project, scope: :running)
end
it "shows Running tab jobs" do
expect(page).to have_selector('.nav-links li.active', text: 'Running')
expect(page).to have_link 'Cancel running'
- expect(page).to have_content build.short_sha
- expect(page).to have_content build.ref
- expect(page).to have_content build.name
+ expect(page).to have_content job.short_sha
+ expect(page).to have_content job.ref
+ expect(page).to have_content job.name
end
end
context "Finished scope" do
before do
- build.run!
+ job.run!
visit namespace_project_jobs_path(project.namespace, project, scope: :finished)
end
@@ -73,9 +73,9 @@ feature 'Jobs', :feature do
it "shows All tab jobs" do
expect(page).to have_selector('.nav-links li.active', text: 'All')
- expect(page).to have_content build.short_sha
- expect(page).to have_content build.ref
- expect(page).to have_content build.name
+ expect(page).to have_content job.short_sha
+ expect(page).to have_content job.ref
+ expect(page).to have_content job.name
expect(page).not_to have_link 'Cancel running'
end
end
@@ -97,7 +97,7 @@ feature 'Jobs', :feature do
describe "POST /:project/jobs/:id/cancel_all" do
before do
- build.run!
+ job.run!
visit namespace_project_jobs_path(project.namespace, project)
click_link "Cancel running"
end
@@ -105,19 +105,19 @@ feature 'Jobs', :feature do
it 'shows all necessary content' do
expect(page).to have_selector('.nav-links li.active', text: 'All')
expect(page).to have_content 'canceled'
- expect(page).to have_content build.short_sha
- expect(page).to have_content build.ref
- expect(page).to have_content build.name
+ expect(page).to have_content job.short_sha
+ expect(page).to have_content job.ref
+ expect(page).to have_content job.name
expect(page).not_to have_link 'Cancel running'
end
end
describe "GET /:project/jobs/:id" do
context "Job from project" do
- let(:build) { create(:ci_build, :success, pipeline: pipeline) }
+ let(:job) { create(:ci_build, :success, pipeline: pipeline) }
before do
- visit namespace_project_job_path(project.namespace, project, build)
+ visit namespace_project_job_path(project.namespace, project, job)
end
it 'shows status name', :js do
@@ -131,33 +131,33 @@ feature 'Jobs', :feature do
expect(page).to have_content pipeline.git_author_name
end
- it 'shows active build' do
+ it 'shows active job' do
expect(page).to have_selector('.build-job.active')
end
end
context 'when job is not running', :js do
- let(:build) { create(:ci_build, :success, pipeline: pipeline) }
+ let(:job) { create(:ci_build, :success, pipeline: pipeline) }
before do
- visit namespace_project_job_path(project.namespace, project, build)
+ visit namespace_project_job_path(project.namespace, project, job)
end
it 'shows retry button' do
expect(page).to have_link('Retry')
end
- context 'if build passed' do
+ context 'if job passed' do
it 'does not show New issue button' do
expect(page).not_to have_link('New issue')
end
end
- context 'if build failed' do
- let(:build) { create(:ci_build, :failed, pipeline: pipeline) }
+ context 'if job failed' do
+ let(:job) { create(:ci_build, :failed, pipeline: pipeline) }
before do
- visit namespace_project_job_path(namespace, project, build)
+ visit namespace_project_job_path(namespace, project, job)
end
it 'shows New issue button' do
@@ -165,9 +165,9 @@ feature 'Jobs', :feature do
end
it 'links to issues/new with the title and description filled in' do
- button_title = "Build Failed ##{build.id}"
- build_path = namespace_project_job_path(namespace, project, build)
- options = { issue: { title: button_title, description: build_path } }
+ button_title = "Build Failed ##{job.id}"
+ job_path = namespace_project_job_path(namespace, project, job)
+ options = { issue: { title: button_title, description: job_path } }
href = new_namespace_project_issue_path(namespace, project, options)
@@ -180,7 +180,7 @@ feature 'Jobs', :feature do
context "Job from other project" do
before do
- visit namespace_project_job_path(project.namespace, project, build2)
+ visit namespace_project_job_path(project.namespace, project, job2)
end
it { expect(page.status_code).to eq(404) }
@@ -188,8 +188,8 @@ feature 'Jobs', :feature do
context "Download artifacts" do
before do
- build.update_attributes(artifacts_file: artifacts_file)
- visit namespace_project_job_path(project.namespace, project, build)
+ job.update_attributes(artifacts_file: artifacts_file)
+ visit namespace_project_job_path(project.namespace, project, job)
end
it 'has button to download artifacts' do
@@ -199,10 +199,10 @@ feature 'Jobs', :feature do
context 'Artifacts expire date' do
before do
- build.update_attributes(artifacts_file: artifacts_file,
- artifacts_expire_at: expire_at)
+ job.update_attributes(artifacts_file: artifacts_file,
+ artifacts_expire_at: expire_at)
- visit namespace_project_job_path(project.namespace, project, build)
+ visit namespace_project_job_path(project.namespace, project, job)
end
context 'no expire date defined' do
@@ -248,7 +248,7 @@ feature 'Jobs', :feature do
context "when visiting old URL" do
let(:job_url) do
- namespace_project_job_path(project.namespace, project, build)
+ namespace_project_job_path(project.namespace, project, job)
end
before do
@@ -262,9 +262,9 @@ feature 'Jobs', :feature do
feature 'Raw trace' do
before do
- build.run!
+ job.run!
- visit namespace_project_job_path(project.namespace, project, build)
+ visit namespace_project_job_path(project.namespace, project, job)
end
it do
@@ -274,16 +274,16 @@ feature 'Jobs', :feature do
feature 'HTML trace', :js do
before do
- build.run!
+ job.run!
- visit namespace_project_job_path(project.namespace, project, build)
+ visit namespace_project_job_path(project.namespace, project, job)
end
context 'when job has an initial trace' do
it 'loads job trace' do
expect(page).to have_content 'BUILD TRACE'
- build.trace.write do |stream|
+ job.trace.write do |stream|
stream.append(' and more trace', 11)
end
@@ -295,12 +295,12 @@ feature 'Jobs', :feature do
feature 'Variables' do
let(:trigger_request) { create(:ci_trigger_request_with_variables) }
- let(:build) do
+ let(:job) do
create :ci_build, pipeline: pipeline, trigger_request: trigger_request
end
before do
- visit namespace_project_job_path(project.namespace, project, build)
+ visit namespace_project_job_path(project.namespace, project, job)
end
it 'shows variable key and value after click', js: true do
@@ -322,20 +322,20 @@ feature 'Jobs', :feature do
context 'job is successfull and has deployment' do
let(:deployment) { create(:deployment) }
- let(:build) { create(:ci_build, :success, environment: environment.name, deployments: [deployment], pipeline: pipeline) }
+ let(:job) { create(:ci_build, :success, environment: environment.name, deployments: [deployment], pipeline: pipeline) }
it 'shows a link for the job' do
- visit namespace_project_job_path(project.namespace, project, build)
+ visit namespace_project_job_path(project.namespace, project, job)
expect(page).to have_link environment.name
end
end
context 'job is complete and not successful' do
- let(:build) { create(:ci_build, :failed, environment: environment.name, pipeline: pipeline) }
+ let(:job) { create(:ci_build, :failed, environment: environment.name, pipeline: pipeline) }
it 'shows a link for the job' do
- visit namespace_project_job_path(project.namespace, project, build)
+ visit namespace_project_job_path(project.namespace, project, job)
expect(page).to have_link environment.name
end
@@ -343,10 +343,10 @@ feature 'Jobs', :feature do
context 'job creates a new deployment' do
let!(:deployment) { create(:deployment, environment: environment, sha: project.commit.id) }
- let(:build) { create(:ci_build, :success, environment: environment.name, pipeline: pipeline) }
+ let(:job) { create(:ci_build, :success, environment: environment.name, pipeline: pipeline) }
it 'shows a link to latest deployment' do
- visit namespace_project_job_path(project.namespace, project, build)
+ visit namespace_project_job_path(project.namespace, project, job)
expect(page).to have_link('latest deployment')
end
@@ -357,8 +357,8 @@ feature 'Jobs', :feature do
describe "POST /:project/jobs/:id/cancel", :js do
context "Job from project" do
before do
- build.run!
- visit namespace_project_job_path(project.namespace, project, build)
+ job.run!
+ visit namespace_project_job_path(project.namespace, project, job)
find('.js-cancel-job').click()
end
@@ -372,8 +372,8 @@ feature 'Jobs', :feature do
describe "POST /:project/jobs/:id/retry" do
context "Job from project", :js do
before do
- build.run!
- visit namespace_project_job_path(project.namespace, project, build)
+ job.run!
+ visit namespace_project_job_path(project.namespace, project, job)
find('.js-cancel-job').click()
find('.js-retry-button').trigger('click')
end
@@ -388,13 +388,13 @@ feature 'Jobs', :feature do
context "Job that current user is not allowed to retry" do
before do
- build.run!
- build.cancel!
+ job.run!
+ job.cancel!
project.update(visibility_level: Gitlab::VisibilityLevel::PUBLIC)
logout_direct
login_with(create(:user))
- visit namespace_project_job_path(project.namespace, project, build)
+ visit namespace_project_job_path(project.namespace, project, job)
end
it 'does not show the Retry button' do
@@ -407,15 +407,15 @@ feature 'Jobs', :feature do
describe "GET /:project/jobs/:id/download" do
before do
- build.update_attributes(artifacts_file: artifacts_file)
- visit namespace_project_job_path(project.namespace, project, build)
+ job.update_attributes(artifacts_file: artifacts_file)
+ visit namespace_project_job_path(project.namespace, project, job)
click_link 'Download'
end
context "Build from other project" do
before do
- build2.update_attributes(artifacts_file: artifacts_file)
- visit download_namespace_project_job_artifacts_path(project.namespace, project, build2)
+ job2.update_attributes(artifacts_file: artifacts_file)
+ visit download_namespace_project_job_artifacts_path(project.namespace, project, job2)
end
it { expect(page.status_code).to eq(404) }
@@ -427,23 +427,23 @@ feature 'Jobs', :feature do
context 'job from project' do
before do
Capybara.current_session.driver.headers = { 'X-Sendfile-Type' => 'X-Sendfile' }
- build.run!
- visit namespace_project_job_path(project.namespace, project, build)
+ job.run!
+ visit namespace_project_job_path(project.namespace, project, job)
find('.js-raw-link-controller').click()
end
it 'sends the right headers' do
expect(page.status_code).to eq(200)
expect(page.response_headers['Content-Type']).to eq('text/plain; charset=utf-8')
- expect(page.response_headers['X-Sendfile']).to eq(build.trace.send(:current_path))
+ expect(page.response_headers['X-Sendfile']).to eq(job.trace.send(:current_path))
end
end
context 'job from other project' do
before do
Capybara.current_session.driver.headers = { 'X-Sendfile-Type' => 'X-Sendfile' }
- build2.run!
- visit raw_namespace_project_job_path(project.namespace, project, build2)
+ job2.run!
+ visit raw_namespace_project_job_path(project.namespace, project, job2)
end
it 'sends the right headers' do
@@ -458,16 +458,16 @@ feature 'Jobs', :feature do
before do
Capybara.current_session.driver.headers = { 'X-Sendfile-Type' => 'X-Sendfile' }
- build.run!
+ job.run!
end
- context 'when build has trace in file', :js do
+ context 'when job has trace in file', :js do
before do
allow_any_instance_of(Gitlab::Ci::Trace)
.to receive(:paths)
.and_return([existing_file])
- visit namespace_project_job_path(namespace, project, build)
+ visit namespace_project_job_path(namespace, project, job)
find('.js-raw-link-controller').click
end
@@ -485,7 +485,7 @@ feature 'Jobs', :feature do
.to receive(:paths)
.and_return([])
- visit namespace_project_job_path(namespace, project, build)
+ visit namespace_project_job_path(namespace, project, job)
end
it 'sends the right headers' do
@@ -496,7 +496,7 @@ feature 'Jobs', :feature do
context "when visiting old URL" do
let(:raw_job_url) do
- raw_namespace_project_job_path(project.namespace, project, build)
+ raw_namespace_project_job_path(project.namespace, project, job)
end
before do
@@ -512,7 +512,7 @@ feature 'Jobs', :feature do
describe "GET /:project/jobs/:id/trace.json" do
context "Job from project" do
before do
- visit trace_namespace_project_job_path(project.namespace, project, build, format: :json)
+ visit trace_namespace_project_job_path(project.namespace, project, job, format: :json)
end
it { expect(page.status_code).to eq(200) }
@@ -520,7 +520,7 @@ feature 'Jobs', :feature do
context "Job from other project" do
before do
- visit trace_namespace_project_job_path(project.namespace, project, build2, format: :json)
+ visit trace_namespace_project_job_path(project.namespace, project, job2, format: :json)
end
it { expect(page.status_code).to eq(404) }
@@ -530,7 +530,7 @@ feature 'Jobs', :feature do
describe "GET /:project/jobs/:id/status" do
context "Job from project" do
before do
- visit status_namespace_project_job_path(project.namespace, project, build)
+ visit status_namespace_project_job_path(project.namespace, project, job)
end
it { expect(page.status_code).to eq(200) }
@@ -538,7 +538,7 @@ feature 'Jobs', :feature do
context "Job from other project" do
before do
- visit status_namespace_project_job_path(project.namespace, project, build2)
+ visit status_namespace_project_job_path(project.namespace, project, job2)
end
it { expect(page.status_code).to eq(404) }
diff --git a/spec/javascripts/pipelines/pipelines_actions_spec.js b/spec/javascripts/pipelines/pipelines_actions_spec.js
index c89dacbcd93..8a58b77f1e3 100644
--- a/spec/javascripts/pipelines/pipelines_actions_spec.js
+++ b/spec/javascripts/pipelines/pipelines_actions_spec.js
@@ -1,5 +1,5 @@
import Vue from 'vue';
-import pipelinesActionsComp from '~/pipelines/components/pipelines_actions';
+import pipelinesActionsComp from '~/pipelines/components/pipelines_actions.vue';
describe('Pipelines Actions dropdown', () => {
let component;
diff --git a/spec/javascripts/pipelines/pipelines_artifacts_spec.js b/spec/javascripts/pipelines/pipelines_artifacts_spec.js
index 9724b63d957..acb67d0ec21 100644
--- a/spec/javascripts/pipelines/pipelines_artifacts_spec.js
+++ b/spec/javascripts/pipelines/pipelines_artifacts_spec.js
@@ -1,5 +1,5 @@
import Vue from 'vue';
-import artifactsComp from '~/pipelines/components/pipelines_artifacts';
+import artifactsComp from '~/pipelines/components/pipelines_artifacts.vue';
describe('Pipelines Artifacts dropdown', () => {
let component;
diff --git a/spec/javascripts/pipelines/pipelines_spec.js b/spec/javascripts/pipelines/pipelines_spec.js
index 3a56156358b..c30abb2edb0 100644
--- a/spec/javascripts/pipelines/pipelines_spec.js
+++ b/spec/javascripts/pipelines/pipelines_spec.js
@@ -1,5 +1,5 @@
import Vue from 'vue';
-import pipelinesComp from '~/pipelines/pipelines';
+import pipelinesComp from '~/pipelines/components/pipelines.vue';
import Store from '~/pipelines/stores/pipelines_store';
describe('Pipelines', () => {
diff --git a/spec/javascripts/pipelines/time_ago_spec.js b/spec/javascripts/pipelines/time_ago_spec.js
index 24581e8c672..42b34c82f89 100644
--- a/spec/javascripts/pipelines/time_ago_spec.js
+++ b/spec/javascripts/pipelines/time_ago_spec.js
@@ -1,5 +1,5 @@
import Vue from 'vue';
-import timeAgo from '~/pipelines/components/time_ago';
+import timeAgo from '~/pipelines/components/time_ago.vue';
describe('Timeago component', () => {
let TimeAgo;
diff --git a/spec/javascripts/vue_shared/components/commit_spec.js b/spec/javascripts/vue_shared/components/commit_spec.js
index 540245fe71e..1c3188cdda2 100644
--- a/spec/javascripts/vue_shared/components/commit_spec.js
+++ b/spec/javascripts/vue_shared/components/commit_spec.js
@@ -1,5 +1,5 @@
import Vue from 'vue';
-import commitComp from '~/vue_shared/components/commit';
+import commitComp from '~/vue_shared/components/commit.vue';
describe('Commit component', () => {
let props;
diff --git a/spec/javascripts/vue_shared/components/pipelines_table_row_spec.js b/spec/javascripts/vue_shared/components/pipelines_table_row_spec.js
index 67419cfcbea..346fd0ae010 100644
--- a/spec/javascripts/vue_shared/components/pipelines_table_row_spec.js
+++ b/spec/javascripts/vue_shared/components/pipelines_table_row_spec.js
@@ -1,5 +1,5 @@
import Vue from 'vue';
-import tableRowComp from '~/vue_shared/components/pipelines_table_row';
+import tableRowComp from '~/vue_shared/components/pipelines_table_row.vue';
describe('Pipelines Table Row', () => {
const jsonFixtureName = 'pipelines/pipelines.json';
diff --git a/spec/javascripts/vue_shared/components/pipelines_table_spec.js b/spec/javascripts/vue_shared/components/pipelines_table_spec.js
index 6cc178b8f1d..c362cfb7a96 100644
--- a/spec/javascripts/vue_shared/components/pipelines_table_spec.js
+++ b/spec/javascripts/vue_shared/components/pipelines_table_spec.js
@@ -1,5 +1,5 @@
import Vue from 'vue';
-import pipelinesTableComp from '~/vue_shared/components/pipelines_table';
+import pipelinesTableComp from '~/vue_shared/components/pipelines_table.vue';
import '~/lib/utils/datetime_utility';
describe('Pipelines Table', () => {
diff --git a/spec/lib/gitlab/etag_caching/middleware_spec.rb b/spec/lib/gitlab/etag_caching/middleware_spec.rb
index 3c6ef7c7ccb..4acf4f047f1 100644
--- a/spec/lib/gitlab/etag_caching/middleware_spec.rb
+++ b/spec/lib/gitlab/etag_caching/middleware_spec.rb
@@ -15,13 +15,13 @@ describe Gitlab::EtagCaching::Middleware do
end
it 'does not add ETag header' do
- _, headers, _ = middleware.call(build_env(path, if_none_match))
+ _, headers, _ = middleware.call(build_request(path, if_none_match))
expect(headers['ETag']).to be_nil
end
it 'passes status code from app' do
- status, _, _ = middleware.call(build_env(path, if_none_match))
+ status, _, _ = middleware.call(build_request(path, if_none_match))
expect(status).to eq app_status_code
end
@@ -39,7 +39,7 @@ describe Gitlab::EtagCaching::Middleware do
expect_any_instance_of(Gitlab::EtagCaching::Store)
.to receive(:touch).and_return('123')
- middleware.call(build_env(path, if_none_match))
+ middleware.call(build_request(path, if_none_match))
end
context 'when If-None-Match header was specified' do
@@ -51,7 +51,7 @@ describe Gitlab::EtagCaching::Middleware do
expect(Gitlab::Metrics).to receive(:add_event)
.with(:etag_caching_key_not_found, endpoint: 'issue_notes')
- middleware.call(build_env(path, if_none_match))
+ middleware.call(build_request(path, if_none_match))
end
end
end
@@ -65,7 +65,7 @@ describe Gitlab::EtagCaching::Middleware do
end
it 'returns this value as header' do
- _, headers, _ = middleware.call(build_env(path, if_none_match))
+ _, headers, _ = middleware.call(build_request(path, if_none_match))
expect(headers['ETag']).to eq 'W/"123"'
end
@@ -82,17 +82,17 @@ describe Gitlab::EtagCaching::Middleware do
it 'does not call app' do
expect(app).not_to receive(:call)
- middleware.call(build_env(path, if_none_match))
+ middleware.call(build_request(path, if_none_match))
end
it 'returns status code 304' do
- status, _, _ = middleware.call(build_env(path, if_none_match))
+ status, _, _ = middleware.call(build_request(path, if_none_match))
expect(status).to eq 304
end
it 'returns empty body' do
- _, _, body = middleware.call(build_env(path, if_none_match))
+ _, _, body = middleware.call(build_request(path, if_none_match))
expect(body).to be_empty
end
@@ -103,7 +103,7 @@ describe Gitlab::EtagCaching::Middleware do
expect(Gitlab::Metrics).to receive(:add_event)
.with(:etag_caching_cache_hit, endpoint: 'issue_notes')
- middleware.call(build_env(path, if_none_match))
+ middleware.call(build_request(path, if_none_match))
end
context 'when polling is disabled' do
@@ -113,7 +113,7 @@ describe Gitlab::EtagCaching::Middleware do
end
it 'returns status code 429' do
- status, _, _ = middleware.call(build_env(path, if_none_match))
+ status, _, _ = middleware.call(build_request(path, if_none_match))
expect(status).to eq 429
end
@@ -131,7 +131,7 @@ describe Gitlab::EtagCaching::Middleware do
it 'calls app' do
expect(app).to receive(:call).and_return([app_status_code, {}, ['body']])
- middleware.call(build_env(path, if_none_match))
+ middleware.call(build_request(path, if_none_match))
end
it 'tracks "etag_caching_resource_changed" event' do
@@ -142,7 +142,7 @@ describe Gitlab::EtagCaching::Middleware do
expect(Gitlab::Metrics).to receive(:add_event)
.with(:etag_caching_resource_changed, endpoint: 'issue_notes')
- middleware.call(build_env(path, if_none_match))
+ middleware.call(build_request(path, if_none_match))
end
end
@@ -160,7 +160,7 @@ describe Gitlab::EtagCaching::Middleware do
expect(Gitlab::Metrics).to receive(:add_event)
.with(:etag_caching_header_missing, endpoint: 'issue_notes')
- middleware.call(build_env(path, if_none_match))
+ middleware.call(build_request(path, if_none_match))
end
end
@@ -192,10 +192,7 @@ describe Gitlab::EtagCaching::Middleware do
.to receive(:get).and_return(value)
end
- def build_env(path, if_none_match)
- {
- 'PATH_INFO' => path,
- 'HTTP_IF_NONE_MATCH' => if_none_match
- }
+ def build_request(path, if_none_match)
+ { 'PATH_INFO' => path, 'HTTP_IF_NONE_MATCH' => if_none_match }
end
end
diff --git a/spec/lib/gitlab/etag_caching/router_spec.rb b/spec/lib/gitlab/etag_caching/router_spec.rb
index 2bb40827fcf..f69cb502ca6 100644
--- a/spec/lib/gitlab/etag_caching/router_spec.rb
+++ b/spec/lib/gitlab/etag_caching/router_spec.rb
@@ -2,115 +2,91 @@ require 'spec_helper'
describe Gitlab::EtagCaching::Router do
it 'matches issue notes endpoint' do
- request = build_request(
+ result = described_class.match(
'/my-group/and-subgroup/here-comes-the-project/noteable/issue/1/notes'
)
- result = described_class.match(request)
-
expect(result).to be_present
expect(result.name).to eq 'issue_notes'
end
it 'matches issue title endpoint' do
- request = build_request(
+ result = described_class.match(
'/my-group/my-project/issues/123/realtime_changes'
)
- result = described_class.match(request)
-
expect(result).to be_present
expect(result.name).to eq 'issue_title'
end
it 'matches project pipelines endpoint' do
- request = build_request(
+ result = described_class.match(
'/my-group/my-project/pipelines.json'
)
- result = described_class.match(request)
-
expect(result).to be_present
expect(result.name).to eq 'project_pipelines'
end
it 'matches commit pipelines endpoint' do
- request = build_request(
+ result = described_class.match(
'/my-group/my-project/commit/aa8260d253a53f73f6c26c734c72fdd600f6e6d4/pipelines.json'
)
- result = described_class.match(request)
-
expect(result).to be_present
expect(result.name).to eq 'commit_pipelines'
end
it 'matches new merge request pipelines endpoint' do
- request = build_request(
+ result = described_class.match(
'/my-group/my-project/merge_requests/new.json'
)
- result = described_class.match(request)
-
expect(result).to be_present
expect(result.name).to eq 'new_merge_request_pipelines'
end
it 'matches merge request pipelines endpoint' do
- request = build_request(
+ result = described_class.match(
'/my-group/my-project/merge_requests/234/pipelines.json'
)
- result = described_class.match(request)
-
expect(result).to be_present
expect(result.name).to eq 'merge_request_pipelines'
end
it 'matches build endpoint' do
- request = build_request(
+ result = described_class.match(
'/my-group/my-project/builds/234.json'
)
- result = described_class.match(request)
-
expect(result).to be_present
expect(result.name).to eq 'project_build'
end
it 'does not match blob with confusing name' do
- request = build_request(
+ result = described_class.match(
'/my-group/my-project/blob/master/pipelines.json'
)
- result = described_class.match(request)
-
expect(result).to be_blank
end
it 'matches the environments path' do
- request = build_request(
+ result = described_class.match(
'/my-group/my-project/environments.json'
)
- result = described_class.match(request)
expect(result).to be_present
-
expect(result.name).to eq 'environments'
end
it 'matches pipeline#show endpoint' do
- request = build_request(
+ result = described_class.match(
'/my-group/my-project/pipelines/2.json'
)
- result = described_class.match(request)
-
expect(result).to be_present
expect(result.name).to eq 'project_pipeline'
end
-
- def build_request(path)
- double(path_info: path)
- end
end
diff --git a/spec/requests/api/jobs_spec.rb b/spec/requests/api/jobs_spec.rb
index e5e5872dc1f..8d647eb1c7e 100644
--- a/spec/requests/api/jobs_spec.rb
+++ b/spec/requests/api/jobs_spec.rb
@@ -11,7 +11,7 @@ describe API::Jobs, :api do
ref: project.default_branch)
end
- let!(:build) { create(:ci_build, pipeline: pipeline) }
+ let!(:job) { create(:ci_build, pipeline: pipeline) }
let(:user) { create(:user) }
let(:api_user) { user }
@@ -42,13 +42,13 @@ describe API::Jobs, :api do
end
it 'returns pipeline data' do
- json_build = json_response.first
+ json_job = json_response.first
- expect(json_build['pipeline']).not_to be_empty
- expect(json_build['pipeline']['id']).to eq build.pipeline.id
- expect(json_build['pipeline']['ref']).to eq build.pipeline.ref
- expect(json_build['pipeline']['sha']).to eq build.pipeline.sha
- expect(json_build['pipeline']['status']).to eq build.pipeline.status
+ expect(json_job['pipeline']).not_to be_empty
+ expect(json_job['pipeline']['id']).to eq job.pipeline.id
+ expect(json_job['pipeline']['ref']).to eq job.pipeline.ref
+ expect(json_job['pipeline']['sha']).to eq job.pipeline.sha
+ expect(json_job['pipeline']['status']).to eq job.pipeline.status
end
context 'filter project with one scope element' do
@@ -79,7 +79,7 @@ describe API::Jobs, :api do
context 'unauthorized user' do
let(:api_user) { nil }
- it 'does not return project builds' do
+ it 'does not return project jobs' do
expect(response).to have_http_status(401)
end
end
@@ -105,13 +105,13 @@ describe API::Jobs, :api do
end
it 'returns pipeline data' do
- json_build = json_response.first
+ json_job = json_response.first
- expect(json_build['pipeline']).not_to be_empty
- expect(json_build['pipeline']['id']).to eq build.pipeline.id
- expect(json_build['pipeline']['ref']).to eq build.pipeline.ref
- expect(json_build['pipeline']['sha']).to eq build.pipeline.sha
- expect(json_build['pipeline']['status']).to eq build.pipeline.status
+ expect(json_job['pipeline']).not_to be_empty
+ expect(json_job['pipeline']['id']).to eq job.pipeline.id
+ expect(json_job['pipeline']['ref']).to eq job.pipeline.ref
+ expect(json_job['pipeline']['sha']).to eq job.pipeline.sha
+ expect(json_job['pipeline']['status']).to eq job.pipeline.status
end
context 'filter jobs with one scope element' do
@@ -140,7 +140,7 @@ describe API::Jobs, :api do
context 'jobs in different pipelines' do
let!(:pipeline2) { create(:ci_empty_pipeline, project: project) }
- let!(:build2) { create(:ci_build, pipeline: pipeline2) }
+ let!(:job2) { create(:ci_build, pipeline: pipeline2) }
it 'excludes jobs from other pipelines' do
json_response.each { |job| expect(job['pipeline']['id']).to eq(pipeline.id) }
@@ -159,7 +159,7 @@ describe API::Jobs, :api do
describe 'GET /projects/:id/jobs/:job_id' do
before do
- get api("/projects/#{project.id}/jobs/#{build.id}", api_user)
+ get api("/projects/#{project.id}/jobs/#{job.id}", api_user)
end
context 'authorized user' do
@@ -169,12 +169,13 @@ describe API::Jobs, :api do
end
it 'returns pipeline data' do
- json_build = json_response
- expect(json_build['pipeline']).not_to be_empty
- expect(json_build['pipeline']['id']).to eq build.pipeline.id
- expect(json_build['pipeline']['ref']).to eq build.pipeline.ref
- expect(json_build['pipeline']['sha']).to eq build.pipeline.sha
- expect(json_build['pipeline']['status']).to eq build.pipeline.status
+ json_job = json_response
+
+ expect(json_job['pipeline']).not_to be_empty
+ expect(json_job['pipeline']['id']).to eq job.pipeline.id
+ expect(json_job['pipeline']['ref']).to eq job.pipeline.ref
+ expect(json_job['pipeline']['sha']).to eq job.pipeline.sha
+ expect(json_job['pipeline']['status']).to eq job.pipeline.status
end
end
@@ -189,11 +190,11 @@ describe API::Jobs, :api do
describe 'GET /projects/:id/jobs/:job_id/artifacts' do
before do
- get api("/projects/#{project.id}/jobs/#{build.id}/artifacts", api_user)
+ get api("/projects/#{project.id}/jobs/#{job.id}/artifacts", api_user)
end
context 'job with artifacts' do
- let(:build) { create(:ci_build, :artifacts, pipeline: pipeline) }
+ let(:job) { create(:ci_build, :artifacts, pipeline: pipeline) }
context 'authorized user' do
let(:download_headers) do
@@ -204,7 +205,7 @@ describe API::Jobs, :api do
it 'returns specific job artifacts' do
expect(response).to have_http_status(200)
expect(response.headers).to include(download_headers)
- expect(response.body).to match_file(build.artifacts_file.file.file)
+ expect(response.body).to match_file(job.artifacts_file.file.file)
end
end
@@ -224,14 +225,14 @@ describe API::Jobs, :api do
describe 'GET /projects/:id/artifacts/:ref_name/download?job=name' do
let(:api_user) { reporter }
- let(:build) { create(:ci_build, :artifacts, pipeline: pipeline) }
+ let(:job) { create(:ci_build, :artifacts, pipeline: pipeline) }
before do
- build.success
+ job.success
end
- def get_for_ref(ref = pipeline.ref, job = build.name)
- get api("/projects/#{project.id}/jobs/artifacts/#{ref}/download", api_user), job: job
+ def get_for_ref(ref = pipeline.ref, job_name = job.name)
+ get api("/projects/#{project.id}/jobs/artifacts/#{ref}/download", api_user), job: job_name
end
context 'when not logged in' do
@@ -285,7 +286,7 @@ describe API::Jobs, :api do
let(:download_headers) do
{ 'Content-Transfer-Encoding' => 'binary',
'Content-Disposition' =>
- "attachment; filename=#{build.artifacts_file.filename}" }
+ "attachment; filename=#{job.artifacts_file.filename}" }
end
it { expect(response).to have_http_status(200) }
@@ -321,16 +322,16 @@ describe API::Jobs, :api do
end
describe 'GET /projects/:id/jobs/:job_id/trace' do
- let(:build) { create(:ci_build, :trace, pipeline: pipeline) }
+ let(:job) { create(:ci_build, :trace, pipeline: pipeline) }
before do
- get api("/projects/#{project.id}/jobs/#{build.id}/trace", api_user)
+ get api("/projects/#{project.id}/jobs/#{job.id}/trace", api_user)
end
context 'authorized user' do
it 'returns specific job trace' do
expect(response).to have_http_status(200)
- expect(response.body).to eq(build.trace.raw)
+ expect(response.body).to eq(job.trace.raw)
end
end
@@ -345,7 +346,7 @@ describe API::Jobs, :api do
describe 'POST /projects/:id/jobs/:job_id/cancel' do
before do
- post api("/projects/#{project.id}/jobs/#{build.id}/cancel", api_user)
+ post api("/projects/#{project.id}/jobs/#{job.id}/cancel", api_user)
end
context 'authorized user' do
@@ -375,10 +376,10 @@ describe API::Jobs, :api do
end
describe 'POST /projects/:id/jobs/:job_id/retry' do
- let(:build) { create(:ci_build, :canceled, pipeline: pipeline) }
+ let(:job) { create(:ci_build, :canceled, pipeline: pipeline) }
before do
- post api("/projects/#{project.id}/jobs/#{build.id}/retry", api_user)
+ post api("/projects/#{project.id}/jobs/#{job.id}/retry", api_user)
end
context 'authorized user' do
@@ -410,28 +411,29 @@ describe API::Jobs, :api do
describe 'POST /projects/:id/jobs/:job_id/erase' do
before do
- post api("/projects/#{project.id}/jobs/#{build.id}/erase", user)
+ post api("/projects/#{project.id}/jobs/#{job.id}/erase", user)
end
context 'job is erasable' do
- let(:build) { create(:ci_build, :trace, :artifacts, :success, project: project, pipeline: pipeline) }
+ let(:job) { create(:ci_build, :trace, :artifacts, :success, project: project, pipeline: pipeline) }
it 'erases job content' do
expect(response).to have_http_status(201)
- expect(build).not_to have_trace
- expect(build.artifacts_file.exists?).to be_falsy
- expect(build.artifacts_metadata.exists?).to be_falsy
+ expect(job).not_to have_trace
+ expect(job.artifacts_file.exists?).to be_falsy
+ expect(job.artifacts_metadata.exists?).to be_falsy
end
it 'updates job' do
- build.reload
- expect(build.erased_at).to be_truthy
- expect(build.erased_by).to eq(user)
+ job.reload
+
+ expect(job.erased_at).to be_truthy
+ expect(job.erased_by).to eq(user)
end
end
context 'job is not erasable' do
- let(:build) { create(:ci_build, :trace, project: project, pipeline: pipeline) }
+ let(:job) { create(:ci_build, :trace, project: project, pipeline: pipeline) }
it 'responds with forbidden' do
expect(response).to have_http_status(403)
@@ -439,25 +441,25 @@ describe API::Jobs, :api do
end
end
- describe 'POST /projects/:id/jobs/:build_id/artifacts/keep' do
+ describe 'POST /projects/:id/jobs/:job_id/artifacts/keep' do
before do
- post api("/projects/#{project.id}/jobs/#{build.id}/artifacts/keep", user)
+ post api("/projects/#{project.id}/jobs/#{job.id}/artifacts/keep", user)
end
context 'artifacts did not expire' do
- let(:build) do
+ let(:job) do
create(:ci_build, :trace, :artifacts, :success,
project: project, pipeline: pipeline, artifacts_expire_at: Time.now + 7.days)
end
it 'keeps artifacts' do
expect(response).to have_http_status(200)
- expect(build.reload.artifacts_expire_at).to be_nil
+ expect(job.reload.artifacts_expire_at).to be_nil
end
end
context 'no artifacts' do
- let(:build) { create(:ci_build, project: project, pipeline: pipeline) }
+ let(:job) { create(:ci_build, project: project, pipeline: pipeline) }
it 'responds with not found' do
expect(response).to have_http_status(404)
@@ -467,18 +469,18 @@ describe API::Jobs, :api do
describe 'POST /projects/:id/jobs/:job_id/play' do
before do
- post api("/projects/#{project.id}/jobs/#{build.id}/play", api_user)
+ post api("/projects/#{project.id}/jobs/#{job.id}/play", api_user)
end
context 'on an playable job' do
- let(:build) { create(:ci_build, :manual, project: project, pipeline: pipeline) }
+ let(:job) { create(:ci_build, :manual, project: project, pipeline: pipeline) }
context 'when user is authorized to trigger a manual action' do
it 'plays the job' do
expect(response).to have_http_status(200)
expect(json_response['user']['id']).to eq(user.id)
- expect(json_response['id']).to eq(build.id)
- expect(build.reload).to be_pending
+ expect(json_response['id']).to eq(job.id)
+ expect(job.reload).to be_pending
end
end
@@ -487,7 +489,7 @@ describe API::Jobs, :api do
let(:api_user) { create(:user) }
it 'does not trigger a manual action' do
- expect(build.reload).to be_manual
+ expect(job.reload).to be_manual
expect(response).to have_http_status(404)
end
end
@@ -496,7 +498,7 @@ describe API::Jobs, :api do
let(:api_user) { reporter }
it 'does not trigger a manual action' do
- expect(build.reload).to be_manual
+ expect(job.reload).to be_manual
expect(response).to have_http_status(403)
end
end