summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
authorFilipa Lacerda <filipa@gitlab.com>2018-03-07 09:48:44 +0000
committerFilipa Lacerda <filipa@gitlab.com>2018-03-07 09:48:44 +0000
commit2b8e9add859f66f32c76948ed7b95f783188695c (patch)
treead26b9c897d3a32c5665e57383b1845bc7f4b2cf /app
parent8975bd3a66d726e6f5ff85d6c55a707d5c7dceb6 (diff)
parent95016507d49c3099afde0ef3909377bf70061dc3 (diff)
downloadgitlab-ce-2b8e9add859f66f32c76948ed7b95f783188695c.tar.gz
[ci skip] Merge branch 'master' into 43770-change-clear-runners-cache-ujs-action-to-an-axios-request
* master: (97 commits) Remove common_vue bundle Gitlab::Utils - backport `.ensure_array_from_string` from EE Fix false positive spec of using digest instead of hexdigest Change queue namespace of UpdateHeadPipelineForMergeRequestWorker prefer checking explicitly for null replace fixture with simple mount point add new tests for metrics dashboard changes Set sha256 checksum when archiving traces Move update_head_pipeline_for_merge_request queue to pipeline_processing namespace Docs guidelines: cherry-picking commits from CE to EE CI charts now include the current day New .dedicated-no-docs-pull-cache-job and .no-db-job definitions Add date calls in .gitlab-ci.yml to see how long each step take move misplaced entry point Arg.id via method pointer Backport description composition from EE Fix a typo, a conflict and improve documentation Remove extra breadcrumb on tags Lazy fetch parent commits for a commit Fix Gitaly n+1 in NetworkController#show ...
Diffstat (limited to 'app')
-rw-r--r--app/assets/javascripts/boards/components/board_card.vue5
-rw-r--r--app/assets/javascripts/boards/components/board_list.vue7
-rw-r--r--app/assets/javascripts/boards/components/board_new_issue.vue110
-rw-r--r--app/assets/javascripts/boards/components/issue_card_inner.js14
-rw-r--r--app/assets/javascripts/boards/components/project_select.vue127
-rw-r--r--app/assets/javascripts/boards/components/sidebar/remove_issue.js8
-rw-r--r--app/assets/javascripts/boards/filtered_search_boards.js1
-rw-r--r--app/assets/javascripts/boards/index.js5
-rw-r--r--app/assets/javascripts/boards/mixins/sortable_default_options.js13
-rw-r--r--app/assets/javascripts/boards/models/issue.js10
-rw-r--r--app/assets/javascripts/boards/models/project.js6
-rw-r--r--app/assets/javascripts/clusters/components/applications.vue5
-rw-r--r--app/assets/javascripts/commons/vue.js1
-rw-r--r--app/assets/javascripts/monitoring/components/dashboard.vue86
-rw-r--r--app/assets/javascripts/monitoring/components/graph.vue17
-rw-r--r--app/assets/javascripts/monitoring/components/graph/legend.vue78
-rw-r--r--app/assets/javascripts/monitoring/components/graph_group.vue16
-rw-r--r--app/assets/javascripts/monitoring/monitoring_bundle.js23
-rw-r--r--app/assets/javascripts/monitoring/services/monitoring_service.js3
-rw-r--r--app/assets/javascripts/monitoring/utils/multiple_time_series.js2
-rw-r--r--app/assets/javascripts/pages/groups/boards/index.js9
-rw-r--r--app/assets/javascripts/pages/projects/environments/index/index.js (renamed from app/assets/javascripts/pages/projects/environments/index.js)0
-rw-r--r--app/assets/javascripts/sortable/sortable_config.js7
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/labels_select/base.vue4
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/labels_select/dropdown_value.vue2
-rw-r--r--app/assets/stylesheets/pages/environments.scss3
-rw-r--r--app/controllers/boards/issues_controller.rb15
-rw-r--r--app/controllers/concerns/boards_responses.rb44
-rw-r--r--app/controllers/groups/boards_controller.rb27
-rw-r--r--app/controllers/groups/labels_controller.rb16
-rw-r--r--app/controllers/projects/branches_controller.rb41
-rw-r--r--app/controllers/projects/deployments_controller.rb2
-rw-r--r--app/controllers/projects/network_controller.rb25
-rw-r--r--app/controllers/projects/prometheus/metrics_controller.rb11
-rw-r--r--app/finders/branches_finder.rb2
-rw-r--r--app/helpers/boards_helper.rb28
-rw-r--r--app/helpers/branches_helper.rb11
-rw-r--r--app/helpers/form_helper.rb2
-rw-r--r--app/helpers/groups_helper.rb2
-rw-r--r--app/models/board.rb8
-rw-r--r--app/models/clusters/applications/prometheus.rb4
-rw-r--r--app/models/clusters/cluster.rb3
-rw-r--r--app/models/commit.rb7
-rw-r--r--app/models/concerns/prometheus_adapter.rb48
-rw-r--r--app/models/deployment.rb17
-rw-r--r--app/models/environment.rb18
-rw-r--r--app/models/group.rb1
-rw-r--r--app/models/label.rb1
-rw-r--r--app/models/namespace.rb5
-rw-r--r--app/models/network/commit.rb7
-rw-r--r--app/models/project.rb30
-rw-r--r--app/models/project_services/monitoring_service.rb4
-rw-r--r--app/models/project_services/prometheus_service.rb89
-rw-r--r--app/models/repository.rb8
-rw-r--r--app/policies/group_policy.rb7
-rw-r--r--app/services/boards/issues/list_service.rb6
-rw-r--r--app/services/boards/issues/move_service.rb4
-rw-r--r--app/services/boards/lists/create_service.rb6
-rw-r--r--app/services/ci/create_trace_artifact_service.rb36
-rw-r--r--app/services/projects/import_export/export_service.rb2
-rw-r--r--app/services/projects/update_pages_service.rb35
-rw-r--r--app/services/prometheus/adapter_service.rb36
-rw-r--r--app/views/groups/boards/index.html.haml1
-rw-r--r--app/views/groups/boards/show.html.haml1
-rw-r--r--app/views/groups/issues.html.haml3
-rw-r--r--app/views/groups/labels/index.html.haml6
-rw-r--r--app/views/layouts/nav/sidebar/_group.html.haml9
-rw-r--r--app/views/projects/branches/_panel.html.haml19
-rw-r--r--app/views/projects/branches/index.html.haml53
-rw-r--r--app/views/projects/cycle_analytics/show.html.haml2
-rw-r--r--app/views/projects/environments/index.html.haml3
-rw-r--r--app/views/projects/environments/metrics.html.haml5
-rw-r--r--app/views/projects/issues/index.html.haml3
-rw-r--r--app/views/projects/merge_requests/index.html.haml3
-rw-r--r--app/views/projects/pipelines/show.html.haml3
-rw-r--r--app/views/projects/registry/repositories/index.html.haml2
-rw-r--r--app/views/projects/settings/repository/show.html.haml3
-rw-r--r--app/views/projects/tags/index.html.haml1
-rw-r--r--app/views/shared/boards/_show.html.haml5
-rw-r--r--app/views/shared/boards/components/_board.html.haml1
-rw-r--r--app/views/shared/issuable/_search_bar.html.haml3
-rw-r--r--app/workers/all_queues.yml4
-rw-r--r--app/workers/archive_trace_worker.rb10
-rw-r--r--app/workers/build_finished_worker.rb2
-rw-r--r--app/workers/concerns/pipeline_background_queue.rb10
-rw-r--r--app/workers/create_trace_artifact_worker.rb10
-rw-r--r--app/workers/pages_worker.rb2
-rw-r--r--app/workers/update_head_pipeline_for_merge_request_worker.rb2
88 files changed, 924 insertions, 412 deletions
diff --git a/app/assets/javascripts/boards/components/board_card.vue b/app/assets/javascripts/boards/components/board_card.vue
index 23fec503586..84885ca9306 100644
--- a/app/assets/javascripts/boards/components/board_card.vue
+++ b/app/assets/javascripts/boards/components/board_card.vue
@@ -1,4 +1,5 @@
<script>
+/* eslint-disable vue/require-default-prop */
import './issue_card_inner';
import eventHub from '../eventhub';
@@ -34,6 +35,9 @@ export default {
type: String,
default: '',
},
+ groupId: {
+ type: Number,
+ },
},
data() {
return {
@@ -88,6 +92,7 @@ export default {
:list="list"
:issue="issue"
:issue-link-base="issueLinkBase"
+ :group-id="groupId"
:root-path="rootPath"
:update-filters="true"
/>
diff --git a/app/assets/javascripts/boards/components/board_list.vue b/app/assets/javascripts/boards/components/board_list.vue
index 6637904d87d..0d03c1c419c 100644
--- a/app/assets/javascripts/boards/components/board_list.vue
+++ b/app/assets/javascripts/boards/components/board_list.vue
@@ -15,6 +15,11 @@ export default {
loadingIcon,
},
props: {
+ groupId: {
+ type: Number,
+ required: false,
+ default: 0,
+ },
disabled: {
type: Boolean,
required: true,
@@ -170,6 +175,7 @@ export default {
<loading-icon />
</div>
<board-new-issue
+ :group-id="groupId"
:list="list"
v-if="list.type !== 'closed' && showIssueForm"/>
<ul
@@ -185,6 +191,7 @@ export default {
:list="list"
:issue="issue"
:issue-link-base="issueLinkBase"
+ :group-id="groupId"
:root-path="rootPath"
:disabled="disabled"
:key="issue.id" />
diff --git a/app/assets/javascripts/boards/components/board_new_issue.vue b/app/assets/javascripts/boards/components/board_new_issue.vue
index efface7143d..870d242e774 100644
--- a/app/assets/javascripts/boards/components/board_new_issue.vue
+++ b/app/assets/javascripts/boards/components/board_new_issue.vue
@@ -1,12 +1,21 @@
<script>
import eventHub from '../eventhub';
+import ProjectSelect from './project_select.vue';
import ListIssue from '../models/issue';
const Store = gl.issueBoards.BoardsStore;
export default {
name: 'BoardNewIssue',
+ components: {
+ ProjectSelect,
+ },
props: {
+ groupId: {
+ type: Number,
+ required: false,
+ default: 0,
+ },
list: {
type: Object,
required: true,
@@ -16,10 +25,20 @@ export default {
return {
title: '',
error: false,
+ selectedProject: {},
};
},
+ computed: {
+ disabled() {
+ if (this.groupId) {
+ return this.title === '' || !this.selectedProject.name;
+ }
+ return this.title === '';
+ },
+ },
mounted() {
this.$refs.input.focus();
+ eventHub.$on('setSelectedProject', this.setSelectedProject);
},
methods: {
submit(e) {
@@ -34,6 +53,7 @@ export default {
labels,
subscribed: true,
assignees: [],
+ project_id: this.selectedProject.id,
});
eventHub.$emit(`scroll-board-list-${this.list.id}`);
@@ -62,52 +82,62 @@ export default {
this.title = '';
eventHub.$emit(`hide-issue-form-${this.list.id}`);
},
+ setSelectedProject(selectedProject) {
+ this.selectedProject = selectedProject;
+ },
},
};
</script>
<template>
- <div class="card board-new-issue-form">
- <form @submit="submit($event)">
- <div
- class="flash-container"
- v-if="error"
- >
- <div class="flash-alert">
- An error occurred. Please try again.
- </div>
- </div>
- <label
- class="label-light"
- :for="list.id + '-title'"
- >
- Title
- </label>
- <input
- class="form-control"
- type="text"
- v-model="title"
- ref="input"
- autocomplete="off"
- :id="list.id + '-title'"
- />
- <div class="clearfix prepend-top-10">
- <button
- class="btn btn-success pull-left"
- type="submit"
- :disabled="title === ''"
- ref="submit-button"
+ <div class="board-new-issue-form">
+ <div class="card">
+ <form @submit="submit($event)">
+ <div
+ class="flash-container"
+ v-if="error"
>
- Submit issue
- </button>
- <button
- class="btn btn-default pull-right"
- type="button"
- @click="cancel"
+ <div class="flash-alert">
+ An error occurred. Please try again.
+ </div>
+ </div>
+ <label
+ class="label-light"
+ :for="list.id + '-title'"
>
- Cancel
- </button>
- </div>
- </form>
+ Title
+ </label>
+ <input
+ class="form-control"
+ type="text"
+ v-model="title"
+ ref="input"
+ autocomplete="off"
+ :id="list.id + '-title'"
+ />
+ <project-select
+ v-if="groupId"
+ :group-id="groupId"
+ />
+ <div class="clearfix prepend-top-10">
+ <button
+ class="btn btn-success pull-left"
+ type="submit"
+ :disabled="disabled"
+ ref="submit-button"
+ >
+ Submit issue
+ </button>
+ <button
+ class="btn btn-default pull-right"
+ type="button"
+ @click="cancel"
+ >
+ Cancel
+ </button>
+ </div>
+ </form>
+ </div>
</div>
</template>
+
diff --git a/app/assets/javascripts/boards/components/issue_card_inner.js b/app/assets/javascripts/boards/components/issue_card_inner.js
index bf474879024..fc2bad2415f 100644
--- a/app/assets/javascripts/boards/components/issue_card_inner.js
+++ b/app/assets/javascripts/boards/components/issue_card_inner.js
@@ -31,6 +31,10 @@ gl.issueBoards.IssueCardInner = Vue.extend({
required: false,
default: false,
},
+ groupId: {
+ type: Number,
+ required: false,
+ },
},
data() {
return {
@@ -64,7 +68,13 @@ gl.issueBoards.IssueCardInner = Vue.extend({
return this.issue.assignees.length > this.numberOverLimit;
},
cardUrl() {
- return `${this.issueLinkBase}/${this.issue.iid}`;
+ let baseUrl = this.issueLinkBase;
+
+ if (this.groupId && this.issue.project) {
+ baseUrl = this.issueLinkBase.replace(':project_path', this.issue.project.path);
+ }
+
+ return `${baseUrl}/${this.issue.iid}`;
},
issueId() {
if (this.issue.iid) {
@@ -148,7 +158,7 @@ gl.issueBoards.IssueCardInner = Vue.extend({
class="card-number"
v-if="issueId"
>
- {{ issueId }}
+ <template v-if="groupId && issue.project">{{issue.project.path}}</template>{{ issueId }}
</span>
</h4>
<div class="card-assignee">
diff --git a/app/assets/javascripts/boards/components/project_select.vue b/app/assets/javascripts/boards/components/project_select.vue
new file mode 100644
index 00000000000..d99b222c305
--- /dev/null
+++ b/app/assets/javascripts/boards/components/project_select.vue
@@ -0,0 +1,127 @@
+<script>
+ /* global ListIssue */
+ import _ from 'underscore';
+ import eventHub from '../eventhub';
+ import loadingIcon from '../../vue_shared/components/loading_icon.vue';
+ import Api from '../../api';
+
+ export default {
+ name: 'BoardProjectSelect',
+ components: {
+ loadingIcon,
+ },
+ props: {
+ groupId: {
+ type: Number,
+ required: true,
+ default: 0,
+ },
+ },
+ data() {
+ return {
+ loading: true,
+ selectedProject: {},
+ };
+ },
+ computed: {
+ selectedProjectName() {
+ return this.selectedProject.name || 'Select a project';
+ },
+ },
+ mounted() {
+ $(this.$refs.projectsDropdown).glDropdown({
+ filterable: true,
+ filterRemote: true,
+ search: {
+ fields: ['name_with_namespace'],
+ },
+ clicked: ({ $el, e }) => {
+ e.preventDefault();
+ this.selectedProject = {
+ id: $el.data('project-id'),
+ name: $el.data('project-name'),
+ };
+ eventHub.$emit('setSelectedProject', this.selectedProject);
+ },
+ selectable: true,
+ data: (term, callback) => {
+ this.loading = true;
+ return Api.groupProjects(this.groupId, term, (projects) => {
+ this.loading = false;
+ callback(projects);
+ });
+ },
+ renderRow(project) {
+ return `
+ <li>
+ <a href='#' class='dropdown-menu-link' data-project-id="${project.id}" data-project-name="${project.name}">
+ ${_.escape(project.name)}
+ </a>
+ </li>
+ `;
+ },
+ text: project => project.name,
+ });
+ },
+ };
+</script>
+
+<template>
+ <div>
+ <label class="label-light prepend-top-10">
+ Project
+ </label>
+ <div
+ ref="projectsDropdown"
+ class="dropdown"
+ >
+ <button
+ class="dropdown-menu-toggle wide"
+ type="button"
+ data-toggle="dropdown"
+ aria-expanded="false"
+ >
+ {{ selectedProjectName }}
+ <i
+ class="fa fa-chevron-down"
+ aria-hidden="true"
+ >
+ </i>
+ </button>
+ <div class="dropdown-menu dropdown-menu-selectable dropdown-menu-full-width">
+ <div class="dropdown-title">
+ <span>Projects</span>
+ <button
+ aria-label="Close"
+ type="button"
+ class="dropdown-title-button dropdown-menu-close"
+ >
+ <i
+ aria-hidden="true"
+ data-hidden="true"
+ class="fa fa-times dropdown-menu-close-icon"
+ >
+ </i>
+ </button>
+ </div>
+ <div class="dropdown-input">
+ <input
+ class="dropdown-input-field"
+ type="search"
+ placeholder="Search projects"
+ />
+ <i
+ aria-hidden="true"
+ data-hidden="true"
+ class="fa fa-search dropdown-input-search"
+ >
+ </i>
+ </div>
+ <div class="dropdown-content"></div>
+ <div class="dropdown-loading">
+ <loading-icon />
+ </div>
+ </div>
+ </div>
+ </div>
+</template>
diff --git a/app/assets/javascripts/boards/components/sidebar/remove_issue.js b/app/assets/javascripts/boards/components/sidebar/remove_issue.js
index 0ae32bb4d0a..09c683ff621 100644
--- a/app/assets/javascripts/boards/components/sidebar/remove_issue.js
+++ b/app/assets/javascripts/boards/components/sidebar/remove_issue.js
@@ -24,7 +24,7 @@ gl.issueBoards.RemoveIssueBtn = Vue.extend({
},
computed: {
updateUrl() {
- return this.issueUpdate;
+ return this.issueUpdate.replace(':project_path', this.issue.project.path);
},
},
methods: {
@@ -32,17 +32,21 @@ gl.issueBoards.RemoveIssueBtn = Vue.extend({
const issue = this.issue;
const lists = issue.getLists();
const listLabelIds = lists.map(list => list.label.id);
- let labelIds = this.issue.labels
+
+ let labelIds = issue.labels
.map(label => label.id)
.filter(id => !listLabelIds.includes(id));
if (labelIds.length === 0) {
labelIds = [''];
}
+
const data = {
issue: {
label_ids: labelIds,
},
};
+
+ // Post the remove data
Vue.http.patch(this.updateUrl, data).catch(() => {
Flash(__('Failed to remove issue from board, please try again.'));
diff --git a/app/assets/javascripts/boards/filtered_search_boards.js b/app/assets/javascripts/boards/filtered_search_boards.js
index 57a7cc4ca30..fb40b9f5565 100644
--- a/app/assets/javascripts/boards/filtered_search_boards.js
+++ b/app/assets/javascripts/boards/filtered_search_boards.js
@@ -6,6 +6,7 @@ export default class FilteredSearchBoards extends FilteredSearchManager {
constructor(store, updateUrl = false, cantEdit = []) {
super({
page: 'boards',
+ stateFiltersSelector: '.issues-state-filters',
});
this.store = store;
diff --git a/app/assets/javascripts/boards/index.js b/app/assets/javascripts/boards/index.js
index 8b34fe232c2..efc0da2e7a2 100644
--- a/app/assets/javascripts/boards/index.js
+++ b/app/assets/javascripts/boards/index.js
@@ -13,6 +13,7 @@ import sidebarEventHub from '~/sidebar/event_hub'; // eslint-disable-line import
import './models/issue';
import './models/list';
import './models/milestone';
+import './models/project';
import './models/assignee';
import './stores/boards_store';
import './stores/modal_store';
@@ -89,7 +90,7 @@ export default () => {
sidebarEventHub.$off('toggleSubscription', this.toggleSubscription);
},
mounted () {
- this.filterManager = new FilteredSearchBoards(Store.filter, true);
+ this.filterManager = new FilteredSearchBoards(Store.filter, true, Store.cantEdit);
this.filterManager.setup();
Store.disabled = this.disabled;
@@ -179,6 +180,7 @@ export default () => {
return {
modal: ModalStore.store,
store: Store.state,
+ canAdminList: this.$options.el.hasAttribute('data-can-admin-list'),
};
},
computed: {
@@ -232,6 +234,7 @@ export default () => {
:class="{ 'disabled': disabled }"
:title="tooltipTitle"
:aria-disabled="disabled"
+ v-if="canAdminList"
@click="openModal">
Add issues
</button>
diff --git a/app/assets/javascripts/boards/mixins/sortable_default_options.js b/app/assets/javascripts/boards/mixins/sortable_default_options.js
index 38a0eb12f92..5e31c6314b2 100644
--- a/app/assets/javascripts/boards/mixins/sortable_default_options.js
+++ b/app/assets/javascripts/boards/mixins/sortable_default_options.js
@@ -1,6 +1,8 @@
/* eslint-disable no-unused-vars, no-mixed-operators, comma-dangle */
/* global DocumentTouch */
+import sortableConfig from '../../sortable/sortable_config';
+
window.gl = window.gl || {};
window.gl.issueBoards = window.gl.issueBoards || {};
@@ -18,19 +20,14 @@ gl.issueBoards.onEnd = () => {
gl.issueBoards.touchEnabled = ('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch;
gl.issueBoards.getBoardSortableDefaultOptions = (obj) => {
- const defaultSortOptions = {
- animation: 200,
- forceFallback: true,
- fallbackClass: 'is-dragging',
- fallbackOnBody: true,
- ghostClass: 'is-ghost',
+ const defaultSortOptions = Object.assign({}, sortableConfig, {
filter: '.board-delete, .btn',
delay: gl.issueBoards.touchEnabled ? 100 : 0,
scrollSensitivity: gl.issueBoards.touchEnabled ? 60 : 100,
scrollSpeed: 20,
onStart: gl.issueBoards.onStart,
- onEnd: gl.issueBoards.onEnd
- };
+ onEnd: gl.issueBoards.onEnd,
+ });
Object.keys(obj).forEach((key) => { defaultSortOptions[key] = obj[key]; });
return defaultSortOptions;
diff --git a/app/assets/javascripts/boards/models/issue.js b/app/assets/javascripts/boards/models/issue.js
index 3bfb6d39ad5..4c5079efc8b 100644
--- a/app/assets/javascripts/boards/models/issue.js
+++ b/app/assets/javascripts/boards/models/issue.js
@@ -4,6 +4,7 @@
/* global ListAssignee */
import Vue from 'vue';
+import IssueProject from './project';
class ListIssue {
constructor (obj, defaultAvatar) {
@@ -23,6 +24,12 @@ class ListIssue {
this.isLoading = {};
this.sidebarInfoEndpoint = obj.issue_sidebar_endpoint;
this.toggleSubscriptionEndpoint = obj.toggle_subscription_endpoint;
+ this.milestone_id = obj.milestone_id;
+ this.project_id = obj.project_id;
+
+ if (obj.project) {
+ this.project = new IssueProject(obj.project);
+ }
if (obj.milestone) {
this.milestone = new ListMilestone(obj.milestone);
@@ -105,7 +112,8 @@ class ListIssue {
data.issue.label_ids = [''];
}
- return Vue.http.patch(url, data);
+ const projectPath = this.project ? this.project.path : '';
+ return Vue.http.patch(url.replace(':project_path', projectPath), data);
}
}
diff --git a/app/assets/javascripts/boards/models/project.js b/app/assets/javascripts/boards/models/project.js
new file mode 100644
index 00000000000..a3d5c7af7ac
--- /dev/null
+++ b/app/assets/javascripts/boards/models/project.js
@@ -0,0 +1,6 @@
+export default class IssueProject {
+ constructor(obj) {
+ this.id = obj.id;
+ this.path = obj.path;
+ }
+}
diff --git a/app/assets/javascripts/clusters/components/applications.vue b/app/assets/javascripts/clusters/components/applications.vue
index 1325a268214..27136c7289f 100644
--- a/app/assets/javascripts/clusters/components/applications.vue
+++ b/app/assets/javascripts/clusters/components/applications.vue
@@ -117,7 +117,10 @@
</script>
<template>
- <section class="settings no-animate expanded">
+ <section
+ id="cluster-applications"
+ class="settings no-animate expanded"
+ >
<div class="settings-header">
<h4>
{{ s__('ClusterIntegration|Applications') }}
diff --git a/app/assets/javascripts/commons/vue.js b/app/assets/javascripts/commons/vue.js
index 8b62d78c043..798623b94fb 100644
--- a/app/assets/javascripts/commons/vue.js
+++ b/app/assets/javascripts/commons/vue.js
@@ -1,4 +1,5 @@
import Vue from 'vue';
+import '../vue_shared/vue_resource_interceptor';
if (process.env.NODE_ENV !== 'production') {
Vue.config.productionTip = false;
diff --git a/app/assets/javascripts/monitoring/components/dashboard.vue b/app/assets/javascripts/monitoring/components/dashboard.vue
index 031badc7026..8ca94ef3e2a 100644
--- a/app/assets/javascripts/monitoring/components/dashboard.vue
+++ b/app/assets/javascripts/monitoring/components/dashboard.vue
@@ -7,34 +7,82 @@
import EmptyState from './empty_state.vue';
import MonitoringStore from '../stores/monitoring_store';
import eventHub from '../event_hub';
- import { convertPermissionToBoolean } from '../../lib/utils/common_utils';
export default {
-
components: {
Graph,
GraphGroup,
EmptyState,
},
- data() {
- const metricsData = document.querySelector('#prometheus-graphs').dataset;
- const store = new MonitoringStore();
+ props: {
+ hasMetrics: {
+ type: Boolean,
+ required: false,
+ default: true,
+ },
+ showLegend: {
+ type: Boolean,
+ required: false,
+ default: true,
+ },
+ showPanels: {
+ type: Boolean,
+ required: false,
+ default: true,
+ },
+ forceSmallGraph: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ documentationPath: {
+ type: String,
+ required: true,
+ },
+ settingsPath: {
+ type: String,
+ required: true,
+ },
+ clustersPath: {
+ type: String,
+ required: true,
+ },
+ tagsPath: {
+ type: String,
+ required: true,
+ },
+ projectPath: {
+ type: String,
+ required: true,
+ },
+ metricsEndpoint: {
+ type: String,
+ required: true,
+ },
+ deploymentEndpoint: {
+ type: String,
+ required: false,
+ default: null,
+ },
+ emptyGettingStartedSvgPath: {
+ type: String,
+ required: true,
+ },
+ emptyLoadingSvgPath: {
+ type: String,
+ required: true,
+ },
+ emptyUnableToConnectSvgPath: {
+ type: String,
+ required: true,
+ },
+ },
+ data() {
return {
- store,
+ store: new MonitoringStore(),
state: 'gettingStarted',
- hasMetrics: convertPermissionToBoolean(metricsData.hasMetrics),
- documentationPath: metricsData.documentationPath,
- settingsPath: metricsData.settingsPath,
- clustersPath: metricsData.clustersPath,
- tagsPath: metricsData.tagsPath,
- projectPath: metricsData.projectPath,
- metricsEndpoint: metricsData.additionalMetrics,
- deploymentEndpoint: metricsData.deploymentEndpoint,
- emptyGettingStartedSvgPath: metricsData.emptyGettingStartedSvgPath,
- emptyLoadingSvgPath: metricsData.emptyLoadingSvgPath,
- emptyUnableToConnectSvgPath: metricsData.emptyUnableToConnectSvgPath,
showEmptyState: true,
updateAspectRatio: false,
updatedAspectRatios: 0,
@@ -67,6 +115,7 @@
window.addEventListener('resize', this.resizeThrottled, false);
}
},
+
methods: {
getGraphsData() {
this.state = 'loading';
@@ -115,6 +164,7 @@
v-for="(groupData, index) in store.groups"
:key="index"
:name="groupData.group"
+ :show-panels="showPanels"
>
<graph
v-for="(graphData, index) in groupData.metrics"
@@ -125,6 +175,8 @@
:deployment-data="store.deploymentData"
:project-path="projectPath"
:tags-path="tagsPath"
+ :show-legend="showLegend"
+ :small-graph="forceSmallGraph"
/>
</graph-group>
</div>
diff --git a/app/assets/javascripts/monitoring/components/graph.vue b/app/assets/javascripts/monitoring/components/graph.vue
index ea5c24efaf9..9e67a6f2146 100644
--- a/app/assets/javascripts/monitoring/components/graph.vue
+++ b/app/assets/javascripts/monitoring/components/graph.vue
@@ -52,6 +52,16 @@
type: String,
required: true,
},
+ showLegend: {
+ type: Boolean,
+ required: false,
+ default: true,
+ },
+ smallGraph: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
},
data() {
@@ -130,7 +140,7 @@
const breakpointSize = bp.getBreakpointSize();
const query = this.graphData.queries[0];
this.margin = measurements.large.margin;
- if (breakpointSize === 'xs' || breakpointSize === 'sm') {
+ if (this.smallGraph || breakpointSize === 'xs' || breakpointSize === 'sm') {
this.graphHeight = 300;
this.margin = measurements.small.margin;
this.measurements = measurements.small;
@@ -182,7 +192,9 @@
this.graphHeightOffset,
);
- if (this.timeSeries.length > 3) {
+ if (!this.showLegend) {
+ this.baseGraphHeight -= 50;
+ } else if (this.timeSeries.length > 3) {
this.baseGraphHeight = this.baseGraphHeight += (this.timeSeries.length - 3) * 20;
}
@@ -255,6 +267,7 @@
:time-series="timeSeries"
:unit-of-display="unitOfDisplay"
:current-data-index="currentDataIndex"
+ :show-legend-group="showLegend"
/>
<svg
class="graph-data"
diff --git a/app/assets/javascripts/monitoring/components/graph/legend.vue b/app/assets/javascripts/monitoring/components/graph/legend.vue
index c6e8d726ffc..3149397b61f 100644
--- a/app/assets/javascripts/monitoring/components/graph/legend.vue
+++ b/app/assets/javascripts/monitoring/components/graph/legend.vue
@@ -39,6 +39,11 @@
type: Number,
required: true,
},
+ showLegendGroup: {
+ type: Boolean,
+ required: false,
+ default: true,
+ },
},
data() {
return {
@@ -57,8 +62,9 @@
},
rectTransform() {
- const yCoordinate = ((this.graphHeight - this.margin.top) / 2)
- + (this.yLabelWidth / 2) + 10 || 0;
+ const yCoordinate = (((this.graphHeight - this.margin.top)
+ + this.measurements.axisLabelLineOffset) / 2)
+ + (this.yLabelWidth / 2) || 0;
return `translate(0, ${yCoordinate}) rotate(-90)`;
},
@@ -166,39 +172,41 @@
>
Time
</text>
- <g
- class="legend-group"
- v-for="(series, index) in timeSeries"
- :key="index"
- :transform="translateLegendGroup(index)"
- >
- <line
- :stroke="series.lineColor"
- :stroke-width="measurements.legends.height"
- :stroke-dasharray="strokeDashArray(series.lineStyle)"
- :x1="measurements.legends.offsetX"
- :x2="measurements.legends.offsetX + measurements.legends.width"
- :y1="graphHeight - measurements.legends.offsetY"
- :y2="graphHeight - measurements.legends.offsetY"
- />
- <text
- v-if="timeSeries.length > 1"
- class="legend-metric-title"
- ref="legendTitleSvg"
- x="38"
- :y="graphHeight - 30"
- >
- {{ createSeriesString(index, series) }}
- </text>
- <text
- v-else
- class="legend-metric-title"
- ref="legendTitleSvg"
- x="38"
- :y="graphHeight - 30"
+ <template v-if="showLegendGroup">
+ <g
+ class="legend-group"
+ v-for="(series, index) in timeSeries"
+ :key="index"
+ :transform="translateLegendGroup(index)"
>
- {{ legendTitle }} {{ formatMetricUsage(series) }}
- </text>
- </g>
+ <line
+ :stroke="series.lineColor"
+ :stroke-width="measurements.legends.height"
+ :stroke-dasharray="strokeDashArray(series.lineStyle)"
+ :x1="measurements.legends.offsetX"
+ :x2="measurements.legends.offsetX + measurements.legends.width"
+ :y1="graphHeight - measurements.legends.offsetY"
+ :y2="graphHeight - measurements.legends.offsetY"
+ />
+ <text
+ v-if="timeSeries.length > 1"
+ class="legend-metric-title"
+ ref="legendTitleSvg"
+ x="38"
+ :y="graphHeight - 30"
+ >
+ {{ createSeriesString(index, series) }}
+ </text>
+ <text
+ v-else
+ class="legend-metric-title"
+ ref="legendTitleSvg"
+ x="38"
+ :y="graphHeight - 30"
+ >
+ {{ legendTitle }} {{ formatMetricUsage(series) }}
+ </text>
+ </g>
+ </template>
</g>
</template>
diff --git a/app/assets/javascripts/monitoring/components/graph_group.vue b/app/assets/javascripts/monitoring/components/graph_group.vue
index 079351a69af..f71cf614552 100644
--- a/app/assets/javascripts/monitoring/components/graph_group.vue
+++ b/app/assets/javascripts/monitoring/components/graph_group.vue
@@ -5,12 +5,20 @@
type: String,
required: true,
},
+ showPanels: {
+ type: Boolean,
+ required: false,
+ default: true,
+ },
},
};
</script>
<template>
- <div class="panel panel-default prometheus-panel">
+ <div
+ v-if="showPanels"
+ class="panel panel-default prometheus-panel"
+ >
<div class="panel-heading">
<h4>{{ name }}</h4>
</div>
@@ -18,4 +26,10 @@
<slot></slot>
</div>
</div>
+ <div
+ v-else
+ class="prometheus-graph-group"
+ >
+ <slot></slot>
+ </div>
</template>
diff --git a/app/assets/javascripts/monitoring/monitoring_bundle.js b/app/assets/javascripts/monitoring/monitoring_bundle.js
index c3b0ef7e9ca..41270e015d4 100644
--- a/app/assets/javascripts/monitoring/monitoring_bundle.js
+++ b/app/assets/javascripts/monitoring/monitoring_bundle.js
@@ -1,7 +1,22 @@
import Vue from 'vue';
+import { convertPermissionToBoolean } from '~/lib/utils/common_utils';
import Dashboard from './components/dashboard.vue';
-export default () => new Vue({
- el: '#prometheus-graphs',
- render: createElement => createElement(Dashboard),
-});
+export default () => {
+ const el = document.getElementById('prometheus-graphs');
+
+ if (el && el.dataset) {
+ // eslint-disable-next-line no-new
+ new Vue({
+ el,
+ render(createElement) {
+ return createElement(Dashboard, {
+ props: {
+ ...el.dataset,
+ hasMetrics: convertPermissionToBoolean(el.dataset.hasMetrics),
+ },
+ });
+ },
+ });
+ }
+};
diff --git a/app/assets/javascripts/monitoring/services/monitoring_service.js b/app/assets/javascripts/monitoring/services/monitoring_service.js
index e230a06cd8c..6fcca36d2fa 100644
--- a/app/assets/javascripts/monitoring/services/monitoring_service.js
+++ b/app/assets/javascripts/monitoring/services/monitoring_service.js
@@ -40,6 +40,9 @@ export default class MonitoringService {
}
getDeploymentData() {
+ if (!this.deploymentEndpoint) {
+ return Promise.resolve([]);
+ }
return backOffRequest(() => axios.get(this.deploymentEndpoint))
.then(resp => resp.data)
.then((response) => {
diff --git a/app/assets/javascripts/monitoring/utils/multiple_time_series.js b/app/assets/javascripts/monitoring/utils/multiple_time_series.js
index 4ce3dad440c..b5b8e3c255d 100644
--- a/app/assets/javascripts/monitoring/utils/multiple_time_series.js
+++ b/app/assets/javascripts/monitoring/utils/multiple_time_series.js
@@ -76,7 +76,7 @@ function queryTimeSeries(query, graphWidth, graphHeight, graphHeightOffset, xDom
metricTag = seriesCustomizationData.value || timeSeriesMetricLabel;
[lineColor, areaColor] = pickColor(seriesCustomizationData.color);
} else {
- metricTag = timeSeriesMetricLabel || `series ${timeSeriesNumber + 1}`;
+ metricTag = timeSeriesMetricLabel || query.label || `series ${timeSeriesNumber + 1}`;
[lineColor, areaColor] = pickColor();
}
diff --git a/app/assets/javascripts/pages/groups/boards/index.js b/app/assets/javascripts/pages/groups/boards/index.js
new file mode 100644
index 00000000000..5cfe8723204
--- /dev/null
+++ b/app/assets/javascripts/pages/groups/boards/index.js
@@ -0,0 +1,9 @@
+import UsersSelect from '~/users_select';
+import ShortcutsNavigation from '~/shortcuts_navigation';
+import initBoards from '~/boards';
+
+document.addEventListener('DOMContentLoaded', () => {
+ new UsersSelect(); // eslint-disable-line no-new
+ new ShortcutsNavigation(); // eslint-disable-line no-new
+ initBoards();
+});
diff --git a/app/assets/javascripts/pages/projects/environments/index.js b/app/assets/javascripts/pages/projects/environments/index/index.js
index ace8af00ece..ace8af00ece 100644
--- a/app/assets/javascripts/pages/projects/environments/index.js
+++ b/app/assets/javascripts/pages/projects/environments/index/index.js
diff --git a/app/assets/javascripts/sortable/sortable_config.js b/app/assets/javascripts/sortable/sortable_config.js
new file mode 100644
index 00000000000..43ef5d66422
--- /dev/null
+++ b/app/assets/javascripts/sortable/sortable_config.js
@@ -0,0 +1,7 @@
+export default {
+ animation: 200,
+ forceFallback: true,
+ fallbackClass: 'is-dragging',
+ fallbackOnBody: true,
+ ghostClass: 'is-ghost',
+};
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select/base.vue b/app/assets/javascripts/vue_shared/components/sidebar/labels_select/base.vue
index 3b17135f0e5..c1dd4d42d9d 100644
--- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select/base.vue
+++ b/app/assets/javascripts/vue_shared/components/sidebar/labels_select/base.vue
@@ -88,7 +88,7 @@ export default {
</script>
<template>
- <div class="block labels">
+ <div class="block labels js-labels-block">
<dropdown-value-collapsed
v-if="showCreate"
:labels="context.labels"
@@ -104,7 +104,7 @@ export default {
</dropdown-value>
<div
v-if="canEdit"
- class="selectbox"
+ class="selectbox js-selectbox"
style="display: none;"
>
<dropdown-hidden-input
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select/dropdown_value.vue b/app/assets/javascripts/vue_shared/components/sidebar/labels_select/dropdown_value.vue
index ba4c8fba5ec..69d588eb25d 100644
--- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select/dropdown_value.vue
+++ b/app/assets/javascripts/vue_shared/components/sidebar/labels_select/dropdown_value.vue
@@ -35,7 +35,7 @@ export default {
</script>
<template>
- <div class="hide-collapsed value issuable-show-labels">
+ <div class="hide-collapsed value issuable-show-labels js-value">
<span
v-if="isEmpty"
class="text-secondary"
diff --git a/app/assets/stylesheets/pages/environments.scss b/app/assets/stylesheets/pages/environments.scss
index 884665d35c7..58700661142 100644
--- a/app/assets/stylesheets/pages/environments.scss
+++ b/app/assets/stylesheets/pages/environments.scss
@@ -369,7 +369,8 @@
}
> text {
- font-size: 12px;
+ fill: $theme-gray-600;
+ font-size: 10px;
}
}
diff --git a/app/controllers/boards/issues_controller.rb b/app/controllers/boards/issues_controller.rb
index 352f12a89fd..19dbee84c11 100644
--- a/app/controllers/boards/issues_controller.rb
+++ b/app/controllers/boards/issues_controller.rb
@@ -1,6 +1,9 @@
module Boards
class IssuesController < Boards::ApplicationController
include BoardsResponses
+ include ControllerWithCrossProjectAccessCheck
+
+ requires_cross_project_access if: -> { board&.group_board? }
before_action :whitelist_query_limiting, only: [:index, :update]
before_action :authorize_read_issue, only: [:index]
@@ -64,11 +67,19 @@ module Boards
end
def issues_finder
- IssuesFinder.new(current_user, project_id: board_parent.id)
+ if board.group_board?
+ IssuesFinder.new(current_user, group_id: board_parent.id)
+ else
+ IssuesFinder.new(current_user, project_id: board_parent.id)
+ end
end
def project
- board_parent
+ @project ||= if board.group_board?
+ Project.find(issue_params[:project_id])
+ else
+ board_parent
+ end
end
def move_params
diff --git a/app/controllers/concerns/boards_responses.rb b/app/controllers/concerns/boards_responses.rb
index a145049dc7d..da830ec2cb1 100644
--- a/app/controllers/concerns/boards_responses.rb
+++ b/app/controllers/concerns/boards_responses.rb
@@ -1,10 +1,46 @@
module BoardsResponses
+ include Gitlab::Utils::StrongMemoize
+
+ def board_params
+ params.require(:board).permit(:name, :weight, :milestone_id, :assignee_id, label_ids: [])
+ end
+
+ def parent
+ strong_memoize(:parent) do
+ group? ? group : project
+ end
+ end
+
+ def boards_path
+ if group?
+ group_boards_path(parent)
+ else
+ project_boards_path(parent)
+ end
+ end
+
+ def board_path(board)
+ if group?
+ group_board_path(parent, board)
+ else
+ project_board_path(parent, board)
+ end
+ end
+
+ def group?
+ instance_variable_defined?(:@group)
+ end
+
def authorize_read_list
- authorize_action_for!(board.parent, :read_list)
+ ability = board.group_board? ? :read_group : :read_list
+
+ authorize_action_for!(board.parent, ability)
end
def authorize_read_issue
- authorize_action_for!(board.parent, :read_issue)
+ ability = board.group_board? ? :read_group : :read_issue
+
+ authorize_action_for!(board.parent, ability)
end
def authorize_update_issue
@@ -31,6 +67,10 @@ module BoardsResponses
respond_with(@board) # rubocop:disable Gitlab/ModuleWithInstanceVariables
end
+ def serialize_as_json(resource)
+ resource.as_json(only: [:id])
+ end
+
def respond_with(resource)
respond_to do |format|
format.html
diff --git a/app/controllers/groups/boards_controller.rb b/app/controllers/groups/boards_controller.rb
new file mode 100644
index 00000000000..7c2016f0326
--- /dev/null
+++ b/app/controllers/groups/boards_controller.rb
@@ -0,0 +1,27 @@
+class Groups::BoardsController < Groups::ApplicationController
+ include BoardsResponses
+
+ before_action :assign_endpoint_vars
+
+ def index
+ @boards = Boards::ListService.new(group, current_user).execute
+
+ respond_with_boards
+ end
+
+ def show
+ @board = group.boards.find(params[:id])
+
+ respond_with_board
+ end
+
+ def assign_endpoint_vars
+ @boards_endpoint = group_boards_url(group)
+ @namespace_path = group.to_param
+ @labels_endpoint = group_labels_url(group)
+ end
+
+ def serialize_as_json(resource)
+ resource.as_json(only: [:id])
+ end
+end
diff --git a/app/controllers/groups/labels_controller.rb b/app/controllers/groups/labels_controller.rb
index ac1d97dc54a..58be330f466 100644
--- a/app/controllers/groups/labels_controller.rb
+++ b/app/controllers/groups/labels_controller.rb
@@ -35,10 +35,18 @@ class Groups::LabelsController < Groups::ApplicationController
def create
@label = Labels::CreateService.new(label_params).execute(group: group)
- if @label.valid?
- redirect_to group_labels_path(@group)
- else
- render :new
+ respond_to do |format|
+ format.html do
+ if @label.valid?
+ redirect_to group_labels_path(@group)
+ else
+ render :new
+ end
+ end
+
+ format.json do
+ render json: LabelSerializer.new.represent_appearance(@label)
+ end
end
end
diff --git a/app/controllers/projects/branches_controller.rb b/app/controllers/projects/branches_controller.rb
index cabafe26357..965cece600e 100644
--- a/app/controllers/projects/branches_controller.rb
+++ b/app/controllers/projects/branches_controller.rb
@@ -7,13 +7,19 @@ class Projects::BranchesController < Projects::ApplicationController
before_action :authorize_download_code!
before_action :authorize_push_code!, only: [:new, :create, :destroy, :destroy_all_merged]
- def index
- @sort = params[:sort].presence || sort_value_recently_updated
- @branches = BranchesFinder.new(@repository, params.merge(sort: @sort)).execute
- @branches = Kaminari.paginate_array(@branches).page(params[:page])
+ # Support legacy URLs
+ before_action :redirect_for_legacy_index_sort_or_search, only: [:index]
+ def index
respond_to do |format|
format.html do
+ @sort = params[:sort].presence || sort_value_recently_updated
+ @mode = params[:state].presence || 'overview'
+ @overview_max_branches = 5
+
+ # Fetch branches for the specified mode
+ fetch_branches_by_mode
+
@refs_pipelines = @project.pipelines.latest_successful_for_refs(@branches.map(&:name))
@merged_branch_names =
repository.merged_branch_names(@branches.map(&:name))
@@ -28,7 +34,9 @@ class Projects::BranchesController < Projects::ApplicationController
end
end
format.json do
- render json: @branches.map(&:name)
+ branches = BranchesFinder.new(@repository, params).execute
+ branches = Kaminari.paginate_array(branches).page(params[:page])
+ render json: branches.map(&:name)
end
end
end
@@ -123,4 +131,27 @@ class Projects::BranchesController < Projects::ApplicationController
context: 'autodeploy'
)
end
+
+ def redirect_for_legacy_index_sort_or_search
+ # Normalize a legacy URL with redirect
+ if request.format != :json && !params[:state].presence && [:sort, :search, :page].any? { |key| params[key].presence }
+ redirect_to project_branches_filtered_path(@project, state: 'all'), notice: 'Update your bookmarked URLs as filtered/sorted branches URL has been changed.'
+ end
+ end
+
+ def fetch_branches_by_mode
+ if @mode == 'overview'
+ # overview mode
+ @active_branches, @stale_branches = BranchesFinder.new(@repository, sort: sort_value_recently_updated).execute.partition(&:active?)
+ # Here we get one more branch to indicate if there are more data we're not showing
+ @active_branches = @active_branches.first(@overview_max_branches + 1)
+ @stale_branches = @stale_branches.first(@overview_max_branches + 1)
+ @branches = @active_branches + @stale_branches
+ else
+ # active/stale/all view mode
+ @branches = BranchesFinder.new(@repository, params.merge(sort: @sort)).execute
+ @branches = @branches.select { |b| b.state.to_s == @mode } if %w[active stale].include?(@mode)
+ @branches = Kaminari.paginate_array(@branches).page(params[:page])
+ end
+ end
end
diff --git a/app/controllers/projects/deployments_controller.rb b/app/controllers/projects/deployments_controller.rb
index 1a418d0f15a..b68cdc39cb8 100644
--- a/app/controllers/projects/deployments_controller.rb
+++ b/app/controllers/projects/deployments_controller.rb
@@ -24,7 +24,7 @@ class Projects::DeploymentsController < Projects::ApplicationController
end
def additional_metrics
- return render_404 unless deployment.has_additional_metrics?
+ return render_404 unless deployment.has_metrics?
respond_to do |format|
format.json do
diff --git a/app/controllers/projects/network_controller.rb b/app/controllers/projects/network_controller.rb
index 3b10a93e97f..35fec229db7 100644
--- a/app/controllers/projects/network_controller.rb
+++ b/app/controllers/projects/network_controller.rb
@@ -9,25 +9,22 @@ class Projects::NetworkController < Projects::ApplicationController
before_action :assign_commit
def show
- # n+1: https://gitlab.com/gitlab-org/gitlab-ce/issues/37602
- Gitlab::GitalyClient.allow_n_plus_1_calls do
- @url = project_network_path(@project, @ref, @options.merge(format: :json))
- @commit_url = project_commit_path(@project, 'ae45ca32').gsub("ae45ca32", "%s")
-
- respond_to do |format|
- format.html do
- if @options[:extended_sha1] && !@commit
- flash.now[:alert] = "Git revision '#{@options[:extended_sha1]}' does not exist."
- end
- end
+ @url = project_network_path(@project, @ref, @options.merge(format: :json))
+ @commit_url = project_commit_path(@project, 'ae45ca32').gsub("ae45ca32", "%s")
- format.json do
- @graph = Network::Graph.new(project, @ref, @commit, @options[:filter_ref])
+ respond_to do |format|
+ format.html do
+ if @options[:extended_sha1] && !@commit
+ flash.now[:alert] = "Git revision '#{@options[:extended_sha1]}' does not exist."
end
end
- render
+ format.json do
+ @graph = Network::Graph.new(project, @ref, @commit, @options[:filter_ref])
+ end
end
+
+ render
end
def assign_commit
diff --git a/app/controllers/projects/prometheus/metrics_controller.rb b/app/controllers/projects/prometheus/metrics_controller.rb
index b739d0f0f90..1dd886409a5 100644
--- a/app/controllers/projects/prometheus/metrics_controller.rb
+++ b/app/controllers/projects/prometheus/metrics_controller.rb
@@ -2,11 +2,12 @@ module Projects
module Prometheus
class MetricsController < Projects::ApplicationController
before_action :authorize_admin_project!
+ before_action :require_prometheus_metrics!
def active_common
respond_to do |format|
format.json do
- matched_metrics = prometheus_service.matched_metrics || {}
+ matched_metrics = prometheus_adapter.query(:matched_metrics) || {}
if matched_metrics.any?
render json: matched_metrics
@@ -19,8 +20,12 @@ module Projects
private
- def prometheus_service
- @prometheus_service ||= project.find_or_initialize_service('prometheus')
+ def prometheus_adapter
+ @prometheus_adapter ||= ::Prometheus::AdapterService.new(project).prometheus_adapter
+ end
+
+ def require_prometheus_metrics!
+ render_404 unless prometheus_adapter.can_query?
end
end
end
diff --git a/app/finders/branches_finder.rb b/app/finders/branches_finder.rb
index 852eac3647d..8bb1366867c 100644
--- a/app/finders/branches_finder.rb
+++ b/app/finders/branches_finder.rb
@@ -1,5 +1,5 @@
class BranchesFinder
- def initialize(repository, params)
+ def initialize(repository, params = {})
@repository = repository
@params = params
end
diff --git a/app/helpers/boards_helper.rb b/app/helpers/boards_helper.rb
index 12b3d9bac1a..275e892b2e6 100644
--- a/app/helpers/boards_helper.rb
+++ b/app/helpers/boards_helper.rb
@@ -17,23 +17,35 @@ module BoardsHelper
end
def build_issue_link_base
- project_issues_path(@project)
+ if board.group_board?
+ "#{group_path(@board.group)}/:project_path/issues"
+ else
+ project_issues_path(@project)
+ end
end
def board_base_url
- project_boards_path(@project)
+ if board.group_board?
+ group_boards_url(@group)
+ else
+ project_boards_path(@project)
+ end
end
def multiple_boards_available?
- current_board_parent.multiple_issue_boards_available?(current_user)
+ current_board_parent.multiple_issue_boards_available?
end
def current_board_path(board)
- @current_board_path ||= project_board_path(current_board_parent, board)
+ @current_board_path ||= if board.group_board?
+ group_board_path(current_board_parent, board)
+ else
+ project_board_path(current_board_parent, board)
+ end
end
def current_board_parent
- @current_board_parent ||= @project
+ @current_board_parent ||= @group || @project
end
def can_admin_issue?
@@ -47,7 +59,8 @@ module BoardsHelper
labels: labels_filter_path(true),
labels_endpoint: @labels_endpoint,
namespace_path: @namespace_path,
- project_path: @project&.try(:path)
+ project_path: @project&.path,
+ group_path: @group&.path
}
end
@@ -59,7 +72,8 @@ module BoardsHelper
field_name: 'issue[assignee_ids][]',
first_user: current_user&.username,
current_user: 'true',
- project_id: @project&.try(:id),
+ project_id: @project&.id,
+ group_id: @group&.id,
null_user: 'true',
multi_select: 'true',
'dropdown-header': dropdown_options[:data][:'dropdown-header'],
diff --git a/app/helpers/branches_helper.rb b/app/helpers/branches_helper.rb
index 00b9a0e00eb..07b1fc3d7cf 100644
--- a/app/helpers/branches_helper.rb
+++ b/app/helpers/branches_helper.rb
@@ -1,15 +1,4 @@
module BranchesHelper
- def filter_branches_path(options = {})
- exist_opts = {
- search: params[:search],
- sort: params[:sort]
- }
-
- options = exist_opts.merge(options)
-
- project_branches_path(@project, @id, options)
- end
-
def project_branches
options_for_select(@project.repository.branch_names, @project.default_branch)
end
diff --git a/app/helpers/form_helper.rb b/app/helpers/form_helper.rb
index e26ce6da030..905e2002592 100644
--- a/app/helpers/form_helper.rb
+++ b/app/helpers/form_helper.rb
@@ -27,7 +27,7 @@ module FormHelper
first_user: current_user&.username,
null_user: true,
current_user: true,
- project_id: @project.id,
+ project_id: @project&.id,
field_name: 'issue[assignee_ids][]',
default_label: 'Unassigned',
'max-select': 1,
diff --git a/app/helpers/groups_helper.rb b/app/helpers/groups_helper.rb
index 7910de73c52..16eceb3f48f 100644
--- a/app/helpers/groups_helper.rb
+++ b/app/helpers/groups_helper.rb
@@ -129,7 +129,7 @@ module GroupsHelper
links = [:overview, :group_members]
if can?(current_user, :read_cross_project)
- links += [:activity, :issues, :labels, :milestones, :merge_requests]
+ links += [:activity, :issues, :boards, :labels, :milestones, :merge_requests]
end
if can?(current_user, :admin_group, @group)
diff --git a/app/models/board.rb b/app/models/board.rb
index 5bb7d3d3722..3cede6fc99a 100644
--- a/app/models/board.rb
+++ b/app/models/board.rb
@@ -1,20 +1,22 @@
class Board < ActiveRecord::Base
+ belongs_to :group
belongs_to :project
has_many :lists, -> { order(:list_type, :position) }, dependent: :delete_all # rubocop:disable Cop/ActiveRecordDependent
validates :project, presence: true, if: :project_needed?
+ validates :group, presence: true, unless: :project
def project_needed?
- true
+ !group
end
def parent
- project
+ @parent ||= group || project
end
def group_board?
- false
+ group_id.present?
end
def backlog_list
diff --git a/app/models/clusters/applications/prometheus.rb b/app/models/clusters/applications/prometheus.rb
index 89ebd63e605..7b25d8c4089 100644
--- a/app/models/clusters/applications/prometheus.rb
+++ b/app/models/clusters/applications/prometheus.rb
@@ -1,6 +1,8 @@
module Clusters
module Applications
class Prometheus < ActiveRecord::Base
+ include PrometheusAdapter
+
VERSION = "2.0.0".freeze
self.table_name = 'clusters_applications_prometheus'
@@ -39,7 +41,7 @@ module Clusters
)
end
- def proxy_client
+ def prometheus_client
return unless kube_client
proxy_url = kube_client.proxy_url('service', service_name, service_port, Gitlab::Kubernetes::Helm::NAMESPACE)
diff --git a/app/models/clusters/cluster.rb b/app/models/clusters/cluster.rb
index 1c0046107d7..49eb069016a 100644
--- a/app/models/clusters/cluster.rb
+++ b/app/models/clusters/cluster.rb
@@ -51,9 +51,6 @@ module Clusters
scope :enabled, -> { where(enabled: true) }
scope :disabled, -> { where(enabled: false) }
- scope :for_environment, -> (env) { where(environment_scope: ['*', '', env.slug]) }
- scope :for_all_environments, -> { where(environment_scope: ['*', '']) }
-
def status_name
if provider
provider.status_name
diff --git a/app/models/commit.rb b/app/models/commit.rb
index b9106309142..cceae5efb72 100644
--- a/app/models/commit.rb
+++ b/app/models/commit.rb
@@ -9,6 +9,7 @@ class Commit
include Mentionable
include Referable
include StaticModel
+ include ::Gitlab::Utils::StrongMemoize
attr_mentionable :safe_message, pipeline: :single_line
@@ -225,11 +226,13 @@ class Commit
end
def parents
- @parents ||= parent_ids.map { |id| project.commit(id) }
+ @parents ||= parent_ids.map { |oid| Commit.lazy(project, oid) }
end
def parent
- @parent ||= project.commit(self.parent_id) if self.parent_id
+ strong_memoize(:parent) do
+ project.commit_by(oid: self.parent_id) if self.parent_id
+ end
end
def notes
diff --git a/app/models/concerns/prometheus_adapter.rb b/app/models/concerns/prometheus_adapter.rb
new file mode 100644
index 00000000000..18cbbd871a1
--- /dev/null
+++ b/app/models/concerns/prometheus_adapter.rb
@@ -0,0 +1,48 @@
+module PrometheusAdapter
+ extend ActiveSupport::Concern
+
+ included do
+ include ReactiveCaching
+
+ self.reactive_cache_key = ->(adapter) { [adapter.class.model_name.singular, adapter.id] }
+ self.reactive_cache_lease_timeout = 30.seconds
+ self.reactive_cache_refresh_interval = 30.seconds
+ self.reactive_cache_lifetime = 1.minute
+
+ def prometheus_client
+ raise NotImplementedError
+ end
+
+ def prometheus_client_wrapper
+ Gitlab::PrometheusClient.new(prometheus_client)
+ end
+
+ def can_query?
+ prometheus_client.present?
+ end
+
+ def query(query_name, *args)
+ return unless can_query?
+
+ query_class = Gitlab::Prometheus::Queries.const_get("#{query_name.to_s.classify}Query")
+
+ args.map!(&:id)
+
+ with_reactive_cache(query_class.name, *args, &query_class.method(:transform_reactive_result))
+ end
+
+ # Cache metrics for specific environment
+ def calculate_reactive_cache(query_class_name, *args)
+ return unless prometheus_client
+
+ data = Kernel.const_get(query_class_name).new(prometheus_client_wrapper).query(*args)
+ {
+ success: true,
+ data: data,
+ last_update: Time.now.utc
+ }
+ rescue Gitlab::PrometheusClient::Error => err
+ { success: false, result: err.message }
+ end
+ end
+end
diff --git a/app/models/deployment.rb b/app/models/deployment.rb
index b6cf168d60e..66e61c06765 100644
--- a/app/models/deployment.rb
+++ b/app/models/deployment.rb
@@ -98,28 +98,29 @@ class Deployment < ActiveRecord::Base
end
def has_metrics?
- project.monitoring_service.present?
+ prometheus_adapter&.can_query?
end
def metrics
return {} unless has_metrics?
- project.monitoring_service.deployment_metrics(self)
- end
-
- def has_additional_metrics?
- project.prometheus_service.present?
+ metrics = prometheus_adapter.query(:deployment, self)
+ metrics&.merge(deployment_time: created_at.to_i) || {}
end
def additional_metrics
- return {} unless project.prometheus_service.present?
+ return {} unless has_metrics?
- metrics = project.prometheus_service.additional_deployment_metrics(self)
+ metrics = prometheus_adapter.query(:additional_metrics_deployment, self)
metrics&.merge(deployment_time: created_at.to_i) || {}
end
private
+ def prometheus_adapter
+ environment.prometheus_adapter
+ end
+
def ref_path
File.join(environment.ref_path, 'deployments', iid.to_s)
end
diff --git a/app/models/environment.rb b/app/models/environment.rb
index f78c21aebe5..24d4f1d8761 100644
--- a/app/models/environment.rb
+++ b/app/models/environment.rb
@@ -146,21 +146,19 @@ class Environment < ActiveRecord::Base
end
def has_metrics?
- project.monitoring_service.present? && available? && last_deployment.present?
+ prometheus_adapter&.can_query? && available? && last_deployment.present?
end
def metrics
- project.monitoring_service.environment_metrics(self) if has_metrics?
+ prometheus_adapter.query(:environment, self) if has_metrics?
end
- def has_additional_metrics?
- project.prometheus_service.present? && available? && last_deployment.present?
+ def additional_metrics
+ prometheus_adapter.query(:additional_metrics_environment, self) if has_metrics?
end
- def additional_metrics
- if has_additional_metrics?
- project.prometheus_service.additional_environment_metrics(self)
- end
+ def prometheus_adapter
+ @prometheus_adapter ||= Prometheus::AdapterService.new(project, deployment_platform).prometheus_adapter
end
def slug
@@ -226,6 +224,10 @@ class Environment < ActiveRecord::Base
self.environment_type || self.name
end
+ def deployment_platform
+ project.deployment_platform
+ end
+
private
# Slugifying a name may remove the uniqueness guarantee afforded by it being
diff --git a/app/models/group.rb b/app/models/group.rb
index 201505c3d3c..8d183006c65 100644
--- a/app/models/group.rb
+++ b/app/models/group.rb
@@ -31,6 +31,7 @@ class Group < Namespace
has_many :uploads, as: :model, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
+ has_many :boards
has_many :badges, class_name: 'GroupBadge'
accepts_nested_attributes_for :variables, allow_destroy: true
diff --git a/app/models/label.rb b/app/models/label.rb
index 7538f2d8718..de7f1d56c64 100644
--- a/app/models/label.rb
+++ b/app/models/label.rb
@@ -35,6 +35,7 @@ class Label < ActiveRecord::Base
scope :templates, -> { where(template: true) }
scope :with_title, ->(title) { where(title: title) }
scope :with_lists_and_board, -> { joins(lists: :board).merge(List.movable) }
+ scope :on_group_boards, ->(group_id) { with_lists_and_board.where(boards: { group_id: group_id }) }
scope :on_project_boards, ->(project_id) { with_lists_and_board.where(boards: { project_id: project_id }) }
def self.prioritized(project)
diff --git a/app/models/namespace.rb b/app/models/namespace.rb
index db274ea8172..e350b675639 100644
--- a/app/models/namespace.rb
+++ b/app/models/namespace.rb
@@ -222,6 +222,11 @@ class Namespace < ActiveRecord::Base
has_parent?
end
+ # Overridden on EE module
+ def multiple_issue_boards_available?
+ false
+ end
+
def full_path_was
if parent_id_was.nil?
path_was
diff --git a/app/models/network/commit.rb b/app/models/network/commit.rb
index 9357e55b419..22d48c9e661 100644
--- a/app/models/network/commit.rb
+++ b/app/models/network/commit.rb
@@ -24,12 +24,7 @@ module Network
end
def parents(map)
- @commit.parents.map do |p|
- if map.include?(p.id)
- map[p.id]
- end
- end
- .compact
+ map.values_at(*@commit.parent_ids).compact
end
end
end
diff --git a/app/models/project.rb b/app/models/project.rb
index a11b1e4f554..5cd1da43645 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -276,7 +276,8 @@ class Project < ActiveRecord::Base
scope :without_storage_feature, ->(feature) { where('storage_version < :version OR storage_version IS NULL', version: HASHED_STORAGE_FEATURES[feature]) }
scope :with_unmigrated_storage, -> { where('storage_version < :version OR storage_version IS NULL', version: LATEST_STORAGE_VERSION) }
- scope :sorted_by_activity, -> { reorder(last_activity_at: :desc) }
+ # last_activity_at is throttled every minute, but last_repository_updated_at is updated with every push
+ scope :sorted_by_activity, -> { reorder("GREATEST(COALESCE(last_activity_at, '1970-01-01'), COALESCE(last_repository_updated_at, '1970-01-01')) DESC") }
scope :sorted_by_stars, -> { reorder('projects.star_count DESC') }
scope :in_namespace, ->(namespace_ids) { where(namespace_id: namespace_ids) }
@@ -778,7 +779,7 @@ class Project < ActiveRecord::Base
end
def last_activity_date
- last_repository_updated_at || last_activity_at || updated_at
+ [last_activity_at, last_repository_updated_at, updated_at].compact.max
end
def project_id
@@ -1527,16 +1528,34 @@ class Project < ActiveRecord::Base
end
end
+ def import_export_shared
+ @import_export_shared ||= Gitlab::ImportExport::Shared.new(self)
+ end
+
def export_path
return nil unless namespace.present? || hashed_storage?(:repository)
- File.join(Gitlab::ImportExport.storage_path, disk_path)
+ import_export_shared.archive_path
end
def export_project_path
Dir.glob("#{export_path}/*export.tar.gz").max_by { |f| File.ctime(f) }
end
+ def export_status
+ if export_in_progress?
+ :started
+ elsif export_project_path
+ :finished
+ else
+ :none
+ end
+ end
+
+ def export_in_progress?
+ import_export_shared.active_export_count > 0
+ end
+
def remove_exports
return nil unless export_path.present?
@@ -1665,8 +1684,9 @@ class Project < ActiveRecord::Base
end
end
- def multiple_issue_boards_available?(user)
- feature_available?(:multiple_issue_boards, user)
+ # Overridden on EE module
+ def multiple_issue_boards_available?
+ false
end
def issue_board_milestone_available?(user = nil)
diff --git a/app/models/project_services/monitoring_service.rb b/app/models/project_services/monitoring_service.rb
index ee9cd78327a..9af68b4e821 100644
--- a/app/models/project_services/monitoring_service.rb
+++ b/app/models/project_services/monitoring_service.rb
@@ -9,11 +9,11 @@ class MonitoringService < Service
%w()
end
- def environment_metrics(environment)
+ def can_query?
raise NotImplementedError
end
- def deployment_metrics(deployment)
+ def query(_, *_)
raise NotImplementedError
end
end
diff --git a/app/models/project_services/prometheus_service.rb b/app/models/project_services/prometheus_service.rb
index 58731451429..dcaeb65dc32 100644
--- a/app/models/project_services/prometheus_service.rb
+++ b/app/models/project_services/prometheus_service.rb
@@ -1,9 +1,5 @@
class PrometheusService < MonitoringService
- include ReactiveService
-
- self.reactive_cache_lease_timeout = 30.seconds
- self.reactive_cache_refresh_interval = 30.seconds
- self.reactive_cache_lifetime = 1.minute
+ include PrometheusAdapter
# Access to prometheus is directly through the API
prop_accessor :api_url
@@ -13,7 +9,7 @@ class PrometheusService < MonitoringService
validates :api_url, url: true
end
- before_save :synchronize_service_state!
+ before_save :synchronize_service_state
after_save :clear_reactive_cache!
@@ -66,63 +62,15 @@ class PrometheusService < MonitoringService
# Check we can connect to the Prometheus API
def test(*args)
- client.ping
+ Gitlab::PrometheusClient.new(prometheus_client).ping
{ success: true, result: 'Checked API endpoint' }
rescue Gitlab::PrometheusClient::Error => err
{ success: false, result: err }
end
- def environment_metrics(environment)
- with_reactive_cache(Gitlab::Prometheus::Queries::EnvironmentQuery.name, environment.id, &rename_field(:data, :metrics))
- end
-
- def deployment_metrics(deployment)
- metrics = with_reactive_cache(Gitlab::Prometheus::Queries::DeploymentQuery.name, deployment.environment.id, deployment.id, &rename_field(:data, :metrics))
- metrics&.merge(deployment_time: deployment.created_at.to_i) || {}
- end
-
- def additional_environment_metrics(environment)
- with_reactive_cache(Gitlab::Prometheus::Queries::AdditionalMetricsEnvironmentQuery.name, environment.id, &:itself)
- end
-
- def additional_deployment_metrics(deployment)
- with_reactive_cache(Gitlab::Prometheus::Queries::AdditionalMetricsDeploymentQuery.name, deployment.environment.id, deployment.id, &:itself)
- end
-
- def matched_metrics
- with_reactive_cache(Gitlab::Prometheus::Queries::MatchedMetricsQuery.name, &:itself)
- end
-
- # Cache metrics for specific environment
- def calculate_reactive_cache(query_class_name, *args)
- return unless active? && project && !project.pending_delete?
-
- environment_id = args.first
- client = client(environment_id)
-
- data = Kernel.const_get(query_class_name).new(client).query(*args)
- {
- success: true,
- data: data,
- last_update: Time.now.utc
- }
- rescue Gitlab::PrometheusClient::Error => err
- { success: false, result: err.message }
- end
-
- def client(environment_id = nil)
- if manual_configuration?
- Gitlab::PrometheusClient.new(RestClient::Resource.new(api_url))
- else
- cluster = cluster_with_prometheus(environment_id)
- raise Gitlab::PrometheusClient::Error, "couldn't find cluster with Prometheus installed" unless cluster
-
- rest_client = client_from_cluster(cluster)
- raise Gitlab::PrometheusClient::Error, "couldn't create proxy Prometheus client" unless rest_client
-
- Gitlab::PrometheusClient.new(rest_client)
- end
+ def prometheus_client
+ RestClient::Resource.new(api_url) if api_url && manual_configuration? && active?
end
def prometheus_installed?
@@ -134,32 +82,7 @@ class PrometheusService < MonitoringService
private
- def cluster_with_prometheus(environment_id = nil)
- clusters = if environment_id
- ::Environment.find_by(id: environment_id).try do |env|
- # sort results by descending order based on environment_scope being longer
- # thus more closely matching environment slug
- project.clusters.enabled.for_environment(env).sort_by { |c| c.environment_scope&.length }.reverse!
- end
- else
- project.clusters.enabled.for_all_environments
- end
-
- clusters&.detect { |cluster| cluster.application_prometheus&.installed? }
- end
-
- def client_from_cluster(cluster)
- cluster.application_prometheus.proxy_client
- end
-
- def rename_field(old_field, new_field)
- -> (metrics) do
- metrics[new_field] = metrics.delete(old_field)
- metrics
- end
- end
-
- def synchronize_service_state!
+ def synchronize_service_state
self.active = prometheus_installed? || manual_configuration?
true
diff --git a/app/models/repository.rb b/app/models/repository.rb
index 1a14afb951a..e6b88320110 100644
--- a/app/models/repository.rb
+++ b/app/models/repository.rb
@@ -866,20 +866,20 @@ class Repository
raw_repository.ancestor?(ancestor_id, descendant_id)
end
- def fetch_as_mirror(url, forced: false, refmap: :all_refs, remote_name: nil)
+ def fetch_as_mirror(url, forced: false, refmap: :all_refs, remote_name: nil, prune: true)
unless remote_name
remote_name = "tmp-#{SecureRandom.hex}"
tmp_remote_name = true
end
add_remote(remote_name, url, mirror_refmap: refmap)
- fetch_remote(remote_name, forced: forced)
+ fetch_remote(remote_name, forced: forced, prune: prune)
ensure
remove_remote(remote_name) if tmp_remote_name
end
- def fetch_remote(remote, forced: false, ssh_auth: nil, no_tags: false)
- gitlab_shell.fetch_remote(raw_repository, remote, ssh_auth: ssh_auth, forced: forced, no_tags: no_tags)
+ def fetch_remote(remote, forced: false, ssh_auth: nil, no_tags: false, prune: true)
+ gitlab_shell.fetch_remote(raw_repository, remote, ssh_auth: ssh_auth, forced: forced, no_tags: no_tags, prune: prune)
end
def fetch_source_branch!(source_repository, source_branch, local_ref)
diff --git a/app/policies/group_policy.rb b/app/policies/group_policy.rb
index f0bcba588a2..c9cb730c4e9 100644
--- a/app/policies/group_policy.rb
+++ b/app/policies/group_policy.rb
@@ -48,7 +48,12 @@ class GroupPolicy < BasePolicy
rule { has_access }.enable :read_namespace
rule { developer }.enable :admin_milestones
- rule { reporter }.enable :admin_label
+
+ rule { reporter }.policy do
+ enable :admin_label
+ enable :admin_list
+ enable :admin_issue
+ end
rule { master }.policy do
enable :create_projects
diff --git a/app/services/boards/issues/list_service.rb b/app/services/boards/issues/list_service.rb
index 6078fe38064..ecd74b74f8a 100644
--- a/app/services/boards/issues/list_service.rb
+++ b/app/services/boards/issues/list_service.rb
@@ -40,7 +40,11 @@ module Boards
end
def set_parent
- params[:project_id] = parent.id
+ if parent.is_a?(Group)
+ params[:group_id] = parent.id
+ else
+ params[:project_id] = parent.id
+ end
end
def set_state
diff --git a/app/services/boards/issues/move_service.rb b/app/services/boards/issues/move_service.rb
index 797d6df7c1a..15fed7d17c1 100644
--- a/app/services/boards/issues/move_service.rb
+++ b/app/services/boards/issues/move_service.rb
@@ -60,8 +60,10 @@ module Boards
label_ids =
if moving_to_list.movable?
moving_from_list.label_id
+ elsif board.group_board?
+ ::Label.on_group_boards(parent.id).pluck(:label_id)
else
- Label.on_project_boards(parent.id).pluck(:label_id)
+ ::Label.on_project_boards(parent.id).pluck(:label_id)
end
Array(label_ids).compact
diff --git a/app/services/boards/lists/create_service.rb b/app/services/boards/lists/create_service.rb
index 183556a1d6b..bebc90c7a8d 100644
--- a/app/services/boards/lists/create_service.rb
+++ b/app/services/boards/lists/create_service.rb
@@ -12,7 +12,11 @@ module Boards
private
def available_labels_for(board)
- LabelsFinder.new(current_user, project_id: parent.id).execute
+ if board.group_board?
+ parent.labels
+ else
+ LabelsFinder.new(current_user, project_id: parent.id).execute
+ end
end
def next_position(board)
diff --git a/app/services/ci/create_trace_artifact_service.rb b/app/services/ci/create_trace_artifact_service.rb
deleted file mode 100644
index ffde824972c..00000000000
--- a/app/services/ci/create_trace_artifact_service.rb
+++ /dev/null
@@ -1,36 +0,0 @@
-module Ci
- class CreateTraceArtifactService < BaseService
- def execute(job)
- return if job.job_artifacts_trace
-
- job.trace.read do |stream|
- break unless stream.file?
-
- clone_file!(stream.path, JobArtifactUploader.workhorse_upload_path) do |clone_path|
- create_job_trace!(job, clone_path)
- FileUtils.rm(stream.path)
- end
- end
- end
-
- private
-
- def create_job_trace!(job, path)
- File.open(path) do |stream|
- job.create_job_artifacts_trace!(
- project: job.project,
- file_type: :trace,
- file: stream)
- end
- end
-
- def clone_file!(src_path, temp_dir)
- FileUtils.mkdir_p(temp_dir)
- Dir.mktmpdir('tmp-trace', temp_dir) do |dir_path|
- temp_path = File.join(dir_path, "job.log")
- FileUtils.copy(src_path, temp_path)
- yield(temp_path)
- end
- end
- end
-end
diff --git a/app/services/projects/import_export/export_service.rb b/app/services/projects/import_export/export_service.rb
index fe4e8ea10bf..af41ce82f65 100644
--- a/app/services/projects/import_export/export_service.rb
+++ b/app/services/projects/import_export/export_service.rb
@@ -2,7 +2,7 @@ module Projects
module ImportExport
class ExportService < BaseService
def execute(_options = {})
- @shared = Gitlab::ImportExport::Shared.new(relative_path: File.join(project.disk_path, 'work'))
+ @shared = project.import_export_shared
save_all
end
diff --git a/app/services/projects/update_pages_service.rb b/app/services/projects/update_pages_service.rb
index c760bd3b626..00fdd047208 100644
--- a/app/services/projects/update_pages_service.rb
+++ b/app/services/projects/update_pages_service.rb
@@ -1,5 +1,8 @@
module Projects
class UpdatePagesService < BaseService
+ InvaildStateError = Class.new(StandardError)
+ FailedToExtractError = Class.new(StandardError)
+
BLOCK_SIZE = 32.kilobytes
MAX_SIZE = 1.terabyte
SITE_PATH = 'public/'.freeze
@@ -11,13 +14,15 @@ module Projects
end
def execute
+ register_attempt
+
# Create status notifying the deployment of pages
@status = create_status
@status.enqueue!
@status.run!
- raise 'missing pages artifacts' unless build.artifacts?
- raise 'pages are outdated' unless latest?
+ raise InvaildStateError, 'missing pages artifacts' unless build.artifacts?
+ raise InvaildStateError, 'pages are outdated' unless latest?
# Create temporary directory in which we will extract the artifacts
FileUtils.mkdir_p(tmp_path)
@@ -26,24 +31,22 @@ module Projects
# Check if we did extract public directory
archive_public_path = File.join(archive_path, 'public')
- raise 'pages miss the public folder' unless Dir.exist?(archive_public_path)
- raise 'pages are outdated' unless latest?
+ raise FailedToExtractError, 'pages miss the public folder' unless Dir.exist?(archive_public_path)
+ raise InvaildStateError, 'pages are outdated' unless latest?
deploy_page!(archive_public_path)
success
end
- rescue => e
+ rescue InvaildStateError, FailedToExtractError => e
register_failure
error(e.message)
- ensure
- register_attempt
- build.erase_artifacts! unless build.has_expiring_artifacts?
end
private
def success
@status.success
+ delete_artifact!
super
end
@@ -52,6 +55,7 @@ module Projects
@status.allow_failure = !latest?
@status.description = message
@status.drop(:script_failure)
+ delete_artifact!
super
end
@@ -72,7 +76,7 @@ module Projects
elsif artifacts.ends_with?('.zip')
extract_zip_archive!(temp_path)
else
- raise 'unsupported artifacts format'
+ raise FailedToExtractError, 'unsupported artifacts format'
end
end
@@ -81,17 +85,17 @@ module Projects
%W(dd bs=#{BLOCK_SIZE} count=#{blocks}),
%W(tar -x -C #{temp_path} #{SITE_PATH}),
err: '/dev/null')
- raise 'pages failed to extract' unless results.compact.all?(&:success?)
+ raise FailedToExtractError, 'pages failed to extract' unless results.compact.all?(&:success?)
end
def extract_zip_archive!(temp_path)
- raise 'missing artifacts metadata' unless build.artifacts_metadata?
+ raise FailedToExtractError, 'missing artifacts metadata' unless build.artifacts_metadata?
# Calculate page size after extract
public_entry = build.artifacts_metadata_entry(SITE_PATH, recursive: true)
if public_entry.total_size > max_size
- raise "artifacts for pages are too large: #{public_entry.total_size}"
+ raise FailedToExtractError, "artifacts for pages are too large: #{public_entry.total_size}"
end
# Requires UnZip at least 6.00 Info-ZIP.
@@ -100,7 +104,7 @@ module Projects
# We add * to end of SITE_PATH, because we want to extract SITE_PATH and all subdirectories
site_path = File.join(SITE_PATH, '*')
unless system(*%W(unzip -qq -n #{artifacts} #{site_path} -d #{temp_path}))
- raise 'pages failed to extract'
+ raise FailedToExtractError, 'pages failed to extract'
end
end
@@ -163,6 +167,11 @@ module Projects
build.artifacts_file.path
end
+ def delete_artifact!
+ build.reload # Reload stable object to prevent erase artifacts with old state
+ build.erase_artifacts! unless build.has_expiring_artifacts?
+ end
+
def latest_sha
project.commit(build.ref).try(:sha).to_s
end
diff --git a/app/services/prometheus/adapter_service.rb b/app/services/prometheus/adapter_service.rb
new file mode 100644
index 00000000000..4504d2ccfe6
--- /dev/null
+++ b/app/services/prometheus/adapter_service.rb
@@ -0,0 +1,36 @@
+module Prometheus
+ class AdapterService
+ def initialize(project, deployment_platform = nil)
+ @project = project
+
+ @deployment_platform = if deployment_platform
+ deployment_platform
+ else
+ project.deployment_platform
+ end
+ end
+
+ attr_reader :deployment_platform, :project
+
+ def prometheus_adapter
+ @prometheus_adapter ||= if service_prometheus_adapter.can_query?
+ service_prometheus_adapter
+ else
+ cluster_prometheus_adapter
+ end
+ end
+
+ def service_prometheus_adapter
+ project.find_or_initialize_service('prometheus')
+ end
+
+ def cluster_prometheus_adapter
+ return unless deployment_platform.respond_to?(:cluster)
+
+ cluster = deployment_platform.cluster
+ return unless cluster.application_prometheus&.installed?
+
+ cluster.application_prometheus
+ end
+ end
+end
diff --git a/app/views/groups/boards/index.html.haml b/app/views/groups/boards/index.html.haml
new file mode 100644
index 00000000000..bb56769bd3f
--- /dev/null
+++ b/app/views/groups/boards/index.html.haml
@@ -0,0 +1 @@
+= render "shared/boards/show", board: @boards.first
diff --git a/app/views/groups/boards/show.html.haml b/app/views/groups/boards/show.html.haml
new file mode 100644
index 00000000000..92838fa4b11
--- /dev/null
+++ b/app/views/groups/boards/show.html.haml
@@ -0,0 +1 @@
+= render "shared/boards/show", board: @board, group: true
diff --git a/app/views/groups/issues.html.haml b/app/views/groups/issues.html.haml
index ca3f018c5e6..36df03302e8 100644
--- a/app/views/groups/issues.html.haml
+++ b/app/views/groups/issues.html.haml
@@ -2,9 +2,6 @@
= content_for :meta_tags do
= auto_discovery_link_tag(:atom, params.merge(rss_url_options), title: "#{@group.name} issues")
-- content_for :page_specific_javascripts do
- = webpack_bundle_tag 'common_vue'
-
- if group_issues_count(state: 'all').zero?
= render 'shared/empty_states/issues', project_select_button: true
- else
diff --git a/app/views/groups/labels/index.html.haml b/app/views/groups/labels/index.html.haml
index d10efdad53b..ac7e12fcd0b 100644
--- a/app/views/groups/labels/index.html.haml
+++ b/app/views/groups/labels/index.html.haml
@@ -1,8 +1,10 @@
- page_title 'Labels'
+- issuables = ['issues', 'merge requests']
+
.top-area.adjust
.nav-text
- Labels can be applied to issues and merge requests. Group labels are available for any project within the group.
+ = _("Labels can be applied to %{features}. Group labels are available for any project within the group.") % { features: issuables.to_sentence }
.nav-controls
- if can?(current_user, :admin_label, @group)
@@ -16,4 +18,4 @@
= paginate @labels, theme: 'gitlab'
- else
.nothing-here-block
- No labels created yet.
+ = _("No labels created yet.")
diff --git a/app/views/layouts/nav/sidebar/_group.html.haml b/app/views/layouts/nav/sidebar/_group.html.haml
index b520f28123f..5ea19c9882d 100644
--- a/app/views/layouts/nav/sidebar/_group.html.haml
+++ b/app/views/layouts/nav/sidebar/_group.html.haml
@@ -1,6 +1,6 @@
- issues_count = group_issues_count(state: 'opened')
- merge_requests_count = group_merge_requests_count(state: 'opened')
-- issues_sub_menu_items = ['groups#issues', 'labels#index', 'milestones#index']
+- issues_sub_menu_items = ['groups#issues', 'labels#index', 'milestones#index', 'boards#index', 'boards#show']
.nav-sidebar{ class: ("sidebar-collapsed-desktop" if collapsed_sidebar?) }
.nav-sidebar-inner-scroll
@@ -51,12 +51,19 @@
%strong.fly-out-top-item-name
#{ _('Issues') }
%span.badge.count.issue_counter.fly-out-badge= number_with_delimiter(issues_count)
+
%li.divider.fly-out-top-item
= nav_link(path: 'groups#issues', html_options: { class: 'home' }) do
= link_to issues_group_path(@group), title: 'List' do
%span
List
+ - if group_sidebar_link?(:boards)
+ = nav_link(path: ['boards#index', 'boards#show']) do
+ = link_to group_boards_path(@group), title: boards_link_text do
+ %span
+ = boards_link_text
+
- if group_sidebar_link?(:labels)
= nav_link(path: 'labels#index') do
= link_to group_labels_path(@group), title: 'Labels' do
diff --git a/app/views/projects/branches/_panel.html.haml b/app/views/projects/branches/_panel.html.haml
new file mode 100644
index 00000000000..12e5a8e8d69
--- /dev/null
+++ b/app/views/projects/branches/_panel.html.haml
@@ -0,0 +1,19 @@
+- branches = local_assigns.fetch(:branches)
+- state = local_assigns.fetch(:state)
+- panel_title = local_assigns.fetch(:panel_title)
+- show_more_text = local_assigns.fetch(:show_more_text)
+- project = local_assigns.fetch(:project)
+- overview_max_branches = local_assigns.fetch(:overview_max_branches)
+
+- return unless branches.any?
+
+.panel.panel-default.prepend-top-10
+ .panel-heading
+ %h4.panel-title
+ = panel_title
+ %ul.content-list.all-branches
+ - branches.first(overview_max_branches).each do |branch|
+ = render "projects/branches/branch", branch: branch, merged: project.repository.merged_to_root_ref?(branch)
+ - if branches.size > overview_max_branches
+ .panel-footer.text-center
+ = link_to show_more_text, project_branches_filtered_path(project, state: state), id: "state-#{state}", data: { state: state }
diff --git a/app/views/projects/branches/index.html.haml b/app/views/projects/branches/index.html.haml
index fb770764364..5dcc72d8263 100644
--- a/app/views/projects/branches/index.html.haml
+++ b/app/views/projects/branches/index.html.haml
@@ -3,26 +3,35 @@
%div{ class: container_class }
.top-area.adjust
- - if can?(current_user, :admin_project, @project)
- .nav-text
- - project_settings_link = link_to s_('Branches|project settings'), project_protected_branches_path(@project)
- = s_('Branches|Protected branches can be managed in %{project_settings_link}').html_safe % { project_settings_link: project_settings_link }
+ %ul.nav-links.issues-state-filters
+ %li{ class: active_when(@mode == 'overview') }>
+ = link_to s_('Branches|Overview'), project_branches_path(@project), title: s_('Branches|Show overview of the branches')
+
+ %li{ class: active_when(@mode == 'active') }>
+ = link_to s_('Branches|Active'), project_branches_filtered_path(@project, state: 'active'), title: s_('Branches|Show active branches')
+
+ %li{ class: active_when(@mode == 'stale') }>
+ = link_to s_('Branches|Stale'), project_branches_filtered_path(@project, state: 'stale'), title: s_('Branches|Show stale branches')
+
+ %li{ class: active_when(!%w[overview active stale].include?(@mode)) }>
+ = link_to s_('Branches|All'), project_branches_filtered_path(@project, state: 'all'), title: s_('Branches|Show all branches')
.nav-controls
- = form_tag(filter_branches_path, method: :get) do
+ = form_tag(project_branches_filtered_path(@project, state: 'all'), method: :get) do
= search_field_tag :search, params[:search], { placeholder: s_('Branches|Filter by branch name'), id: 'branch-search', class: 'form-control search-text-input input-short', spellcheck: false }
- .dropdown.inline>
- %button.dropdown-menu-toggle{ type: 'button', 'data-toggle' => 'dropdown' }
- %span.light
- = branches_sort_options_hash[@sort]
- = icon('chevron-down')
- %ul.dropdown-menu.dropdown-menu-align-right.dropdown-menu-selectable
- %li.dropdown-header
- = s_('Branches|Sort by')
- - branches_sort_options_hash.each do |value, title|
- %li
- = link_to title, filter_branches_path(sort: value), class: ("is-active" if @sort == value)
+ - unless @mode == 'overview'
+ .dropdown.inline>
+ %button.dropdown-menu-toggle{ type: 'button', 'data-toggle' => 'dropdown' }
+ %span.light
+ = branches_sort_options_hash[@sort]
+ = icon('chevron-down')
+ %ul.dropdown-menu.dropdown-menu-align-right.dropdown-menu-selectable
+ %li.dropdown-header
+ = s_('Branches|Sort by')
+ - branches_sort_options_hash.each do |value, title|
+ %li
+ = link_to title, project_branches_filtered_path(@project, state: 'all', search: params[:search], sort: value), class: ("is-active" if @sort == value)
- if can? current_user, :push_code, @project
= link_to project_merged_branches_path(@project),
@@ -35,7 +44,17 @@
= link_to new_project_branch_path(@project), class: 'btn btn-create' do
= s_('Branches|New branch')
- - if @branches.any?
+ - if can?(current_user, :admin_project, @project)
+ - project_settings_link = link_to s_('Branches|project settings'), project_protected_branches_path(@project)
+ .row-content-block
+ %h5
+ = s_('Branches|Protected branches can be managed in %{project_settings_link}.').html_safe % { project_settings_link: project_settings_link }
+
+ - if @mode == 'overview' && (@active_branches.any? || @stale_branches.any?)
+ = render "projects/branches/panel", branches: @active_branches, state: 'active', panel_title: s_('Branches|Active branches'), show_more_text: s_('Branches|Show more active branches'), project: @project, overview_max_branches: @overview_max_branches
+ = render "projects/branches/panel", branches: @stale_branches, state: 'stale', panel_title: s_('Branches|Stale branches'), show_more_text: s_('Branches|Show more stale branches'), project: @project, overview_max_branches: @overview_max_branches
+
+ - elsif @branches.any?
%ul.content-list.all-branches
- @branches.each do |branch|
= render "projects/branches/branch", branch: branch, merged: @merged_branch_names.include?(branch.name)
diff --git a/app/views/projects/cycle_analytics/show.html.haml b/app/views/projects/cycle_analytics/show.html.haml
index 02395b6eb9b..5041f322612 100644
--- a/app/views/projects/cycle_analytics/show.html.haml
+++ b/app/views/projects/cycle_analytics/show.html.haml
@@ -1,7 +1,5 @@
- @no_container = true
- page_title "Cycle Analytics"
-- content_for :page_specific_javascripts do
- = webpack_bundle_tag('common_vue')
#cycle-analytics{ class: container_class, "v-cloak" => "true", data: { request_path: project_cycle_analytics_path(@project) } }
- if @cycle_analytics_no_data
diff --git a/app/views/projects/environments/index.html.haml b/app/views/projects/environments/index.html.haml
index 0d656b25bc8..7ebe617766f 100644
--- a/app/views/projects/environments/index.html.haml
+++ b/app/views/projects/environments/index.html.haml
@@ -2,9 +2,6 @@
- page_title "Environments"
- add_to_breadcrumbs("Pipelines", project_pipelines_path(@project))
-- content_for :page_specific_javascripts do
- = webpack_bundle_tag("common_vue")
-
#environments-list-view{ data: { environments_data: environments_list_data,
"can-create-deployment" => can?(current_user, :create_deployment, @project).to_s,
"can-read-environment" => can?(current_user, :read_environment, @project).to_s,
diff --git a/app/views/projects/environments/metrics.html.haml b/app/views/projects/environments/metrics.html.haml
index 9d9759ebc5f..c151b5acdf7 100644
--- a/app/views/projects/environments/metrics.html.haml
+++ b/app/views/projects/environments/metrics.html.haml
@@ -15,7 +15,8 @@
"empty-getting-started-svg-path": image_path('illustrations/monitoring/getting_started.svg'),
"empty-loading-svg-path": image_path('illustrations/monitoring/loading.svg'),
"empty-unable-to-connect-svg-path": image_path('illustrations/monitoring/unable_to_connect.svg'),
- "additional-metrics": additional_metrics_project_environment_path(@project, @environment, format: :json),
+ "metrics-endpoint": additional_metrics_project_environment_path(@project, @environment, format: :json),
+ "deployment-endpoint": project_environment_deployments_path(@project, @environment, format: :json),
"project-path": project_path(@project),
"tags-path": project_tags_path(@project),
- "has-metrics": "#{@environment.has_metrics?}", deployment_endpoint: project_environment_deployments_path(@project, @environment, format: :json) } }
+ "has-metrics": "#{@environment.has_metrics?}" } }
diff --git a/app/views/projects/issues/index.html.haml b/app/views/projects/issues/index.html.haml
index fb06ba58c27..c427a9eedc2 100644
--- a/app/views/projects/issues/index.html.haml
+++ b/app/views/projects/issues/index.html.haml
@@ -4,9 +4,6 @@
- page_title "Issues"
- new_issue_email = @project.new_issuable_address(current_user, 'issue')
-- content_for :page_specific_javascripts do
- = webpack_bundle_tag 'common_vue'
-
= content_for :meta_tags do
= auto_discovery_link_tag(:atom, params.merge(rss_url_options), title: "#{@project.name} issues")
diff --git a/app/views/projects/merge_requests/index.html.haml b/app/views/projects/merge_requests/index.html.haml
index 720ba236434..b2c0d9e1cfa 100644
--- a/app/views/projects/merge_requests/index.html.haml
+++ b/app/views/projects/merge_requests/index.html.haml
@@ -6,9 +6,6 @@
- page_title "Merge Requests"
- new_merge_request_email = @project.new_issuable_address(current_user, 'merge_request')
-- content_for :page_specific_javascripts do
- = webpack_bundle_tag 'common_vue'
-
%div{ class: container_class }
= render 'projects/last_push'
diff --git a/app/views/projects/pipelines/show.html.haml b/app/views/projects/pipelines/show.html.haml
index ffb0ae95f9b..a7d7c923957 100644
--- a/app/views/projects/pipelines/show.html.haml
+++ b/app/views/projects/pipelines/show.html.haml
@@ -10,6 +10,3 @@
= render "projects/pipelines/with_tabs", pipeline: @pipeline
.js-pipeline-details-vue{ data: { endpoint: project_pipeline_path(@project, @pipeline, format: :json) } }
-
-- content_for :page_specific_javascripts do
- = webpack_bundle_tag('common_vue')
diff --git a/app/views/projects/registry/repositories/index.html.haml b/app/views/projects/registry/repositories/index.html.haml
index 27e1f9fba3e..12d56e244ce 100644
--- a/app/views/projects/registry/repositories/index.html.haml
+++ b/app/views/projects/registry/repositories/index.html.haml
@@ -14,8 +14,6 @@
.col-lg-12
#js-vue-registry-images{ data: { endpoint: project_container_registry_index_path(@project, format: :json) } }
- = webpack_bundle_tag('common_vue')
-
.row.prepend-top-10
.col-lg-12
.panel.panel-default
diff --git a/app/views/projects/settings/repository/show.html.haml b/app/views/projects/settings/repository/show.html.haml
index 235d532bf98..6bef4d19434 100644
--- a/app/views/projects/settings/repository/show.html.haml
+++ b/app/views/projects/settings/repository/show.html.haml
@@ -2,9 +2,6 @@
- page_title "Repository"
- @content_class = "limit-container-width" unless fluid_layout
-- content_for :page_specific_javascripts do
- = webpack_bundle_tag('common_vue')
-
-# Protected branches & tags use a lot of nested partials.
-# The shared parts of the views can be found in the `shared` directory.
-# Those are used throughout the actual views. These `shared` views are then
diff --git a/app/views/projects/tags/index.html.haml b/app/views/projects/tags/index.html.haml
index da364b58e36..10415d011d6 100644
--- a/app/views/projects/tags/index.html.haml
+++ b/app/views/projects/tags/index.html.haml
@@ -1,7 +1,6 @@
- @no_container = true
- @sort ||= sort_value_recently_updated
- page_title s_('TagsPage|Tags')
-- add_to_breadcrumbs("Repository", project_tree_path(@project))
.flex-list{ class: container_class }
.top-area.adjust
diff --git a/app/views/shared/boards/_show.html.haml b/app/views/shared/boards/_show.html.haml
index 014b8de1dc9..44b09545a61 100644
--- a/app/views/shared/boards/_show.html.haml
+++ b/app/views/shared/boards/_show.html.haml
@@ -1,3 +1,5 @@
+- board = local_assigns.fetch(:board, nil)
+- group = local_assigns.fetch(:group, false)
- @no_breadcrumb_container = true
- @no_container = true
- @content_class = "issue-boards-content"
@@ -5,7 +7,6 @@
- page_title "Boards"
- content_for :page_specific_javascripts do
- = webpack_bundle_tag 'common_vue'
-# haml-lint:disable InlineJavaScript
%script#js-board-template{ type: "text/x-template" }= render "shared/boards/components/board"
@@ -27,7 +28,7 @@
":root-path" => "rootPath",
":board-id" => "boardId",
":key" => "_uid" }
- = render "shared/boards/components/sidebar"
+ = render "shared/boards/components/sidebar", group: group
- if @project
%board-add-issues-modal{ "new-issue-path" => new_project_issue_path(@project),
"milestone-path" => milestones_filter_dropdown_path,
diff --git a/app/views/shared/boards/components/_board.html.haml b/app/views/shared/boards/components/_board.html.haml
index c687e66fd43..2e9ad380012 100644
--- a/app/views/shared/boards/components/_board.html.haml
+++ b/app/views/shared/boards/components/_board.html.haml
@@ -42,6 +42,7 @@
":disabled" => "disabled",
":issue-link-base" => "issueLinkBase",
":root-path" => "rootPath",
+ ":groupId" => ((current_board_parent.id if @group) || 'null'),
"ref" => "board-list" }
- if can?(current_user, :admin_list, current_board_parent)
%board-blank-state{ "v-if" => 'list.id == "blank"' }
diff --git a/app/views/shared/issuable/_search_bar.html.haml b/app/views/shared/issuable/_search_bar.html.haml
index fabb17c7340..fc6f71ef60f 100644
--- a/app/views/shared/issuable/_search_bar.html.haml
+++ b/app/views/shared/issuable/_search_bar.html.haml
@@ -112,6 +112,7 @@
- if can?(current_user, :admin_label, board.parent)
= render partial: "shared/issuable/label_page_create"
= dropdown_loading
- #js-add-issues-btn.prepend-left-10
+ - if @project
+ #js-add-issues-btn.prepend-left-10{ data: { can_admin_list: can?(current_user, :admin_list, @project) } }
- elsif type != :boards_modal
= render 'shared/sort_dropdown'
diff --git a/app/workers/all_queues.yml b/app/workers/all_queues.yml
index 328db19be29..f65e8385ac8 100644
--- a/app/workers/all_queues.yml
+++ b/app/workers/all_queues.yml
@@ -43,12 +43,11 @@
- pipeline_cache:expire_pipeline_cache
- pipeline_creation:create_pipeline
- pipeline_creation:run_pipeline_schedule
+- pipeline_background:archive_trace
- pipeline_default:build_coverage
- pipeline_default:build_trace_sections
-- pipeline_default:create_trace_artifact
- pipeline_default:pipeline_metrics
- pipeline_default:pipeline_notification
-- pipeline_default:update_head_pipeline_for_merge_request
- pipeline_hooks:build_hooks
- pipeline_hooks:pipeline_hooks
- pipeline_processing:build_finished
@@ -58,6 +57,7 @@
- pipeline_processing:pipeline_success
- pipeline_processing:pipeline_update
- pipeline_processing:stage_update
+- pipeline_processing:update_head_pipeline_for_merge_request
- repository_check:repository_check_clear
- repository_check:repository_check_single_repository
diff --git a/app/workers/archive_trace_worker.rb b/app/workers/archive_trace_worker.rb
new file mode 100644
index 00000000000..dea7425ad88
--- /dev/null
+++ b/app/workers/archive_trace_worker.rb
@@ -0,0 +1,10 @@
+class ArchiveTraceWorker
+ include ApplicationWorker
+ include PipelineBackgroundQueue
+
+ def perform(job_id)
+ Ci::Build.find_by(id: job_id).try do |job|
+ job.trace.archive!
+ end
+ end
+end
diff --git a/app/workers/build_finished_worker.rb b/app/workers/build_finished_worker.rb
index b5ed8d607b3..46f1ac09915 100644
--- a/app/workers/build_finished_worker.rb
+++ b/app/workers/build_finished_worker.rb
@@ -12,7 +12,7 @@ class BuildFinishedWorker
# We execute that async as this are two indepentent operations that can be executed after TraceSections and Coverage
BuildHooksWorker.perform_async(build.id)
- CreateTraceArtifactWorker.perform_async(build.id)
+ ArchiveTraceWorker.perform_async(build.id)
end
end
end
diff --git a/app/workers/concerns/pipeline_background_queue.rb b/app/workers/concerns/pipeline_background_queue.rb
new file mode 100644
index 00000000000..8bf43de6b26
--- /dev/null
+++ b/app/workers/concerns/pipeline_background_queue.rb
@@ -0,0 +1,10 @@
+##
+# Concern for setting Sidekiq settings for the low priority CI pipeline workers.
+#
+module PipelineBackgroundQueue
+ extend ActiveSupport::Concern
+
+ included do
+ queue_namespace :pipeline_background
+ end
+end
diff --git a/app/workers/create_trace_artifact_worker.rb b/app/workers/create_trace_artifact_worker.rb
deleted file mode 100644
index 11cda58021e..00000000000
--- a/app/workers/create_trace_artifact_worker.rb
+++ /dev/null
@@ -1,10 +0,0 @@
-class CreateTraceArtifactWorker
- include ApplicationWorker
- include PipelineQueue
-
- def perform(job_id)
- Ci::Build.preload(:project, :user).find_by(id: job_id).try do |job|
- Ci::CreateTraceArtifactService.new(job.project, job.user).execute(job)
- end
- end
-end
diff --git a/app/workers/pages_worker.rb b/app/workers/pages_worker.rb
index d3b95009364..66a0ff83bef 100644
--- a/app/workers/pages_worker.rb
+++ b/app/workers/pages_worker.rb
@@ -1,7 +1,7 @@
class PagesWorker
include ApplicationWorker
- sidekiq_options retry: false
+ sidekiq_options retry: 3
def perform(action, *arg)
send(action, *arg) # rubocop:disable GitlabSecurity/PublicSend
diff --git a/app/workers/update_head_pipeline_for_merge_request_worker.rb b/app/workers/update_head_pipeline_for_merge_request_worker.rb
index f09d89aa170..76f84ff920f 100644
--- a/app/workers/update_head_pipeline_for_merge_request_worker.rb
+++ b/app/workers/update_head_pipeline_for_merge_request_worker.rb
@@ -2,6 +2,8 @@ class UpdateHeadPipelineForMergeRequestWorker
include ApplicationWorker
include PipelineQueue
+ queue_namespace :pipeline_processing
+
def perform(merge_request_id)
merge_request = MergeRequest.find(merge_request_id)
pipeline = Ci::Pipeline.where(project: merge_request.source_project, ref: merge_request.source_branch).last