Merge branch 'master' into sh-support-bitbucket-server-import

This commit is contained in:
Stan Hu 2018-06-27 09:21:00 -07:00
commit 4a6ed4792f
155 changed files with 1277 additions and 1218 deletions

View File

@ -1 +1 @@
0.107.0
0.108.0

View File

@ -418,7 +418,7 @@ group :ed25519 do
end
# Gitaly GRPC client
gem 'gitaly-proto', '~> 0.102.0', require: 'gitaly'
gem 'gitaly-proto', '~> 0.103.0', require: 'gitaly'
gem 'grpc', '~> 1.11.0'
# Locked until https://github.com/google/protobuf/issues/4210 is closed

View File

@ -282,7 +282,7 @@ GEM
gettext_i18n_rails (>= 0.7.1)
po_to_json (>= 1.0.0)
rails (>= 3.2.0)
gitaly-proto (0.102.0)
gitaly-proto (0.103.0)
google-protobuf (~> 3.1)
grpc (~> 1.10)
github-linguist (5.3.3)
@ -1041,7 +1041,7 @@ DEPENDENCIES
gettext (~> 3.2.2)
gettext_i18n_rails (~> 1.8.0)
gettext_i18n_rails_js (~> 1.3)
gitaly-proto (~> 0.102.0)
gitaly-proto (~> 0.103.0)
github-linguist (~> 5.3.3)
gitlab-flowdock-git-hook (~> 1.0.1)
gitlab-gollum-lib (~> 4.2)

View File

@ -285,7 +285,7 @@ GEM
gettext_i18n_rails (>= 0.7.1)
po_to_json (>= 1.0.0)
rails (>= 3.2.0)
gitaly-proto (0.102.0)
gitaly-proto (0.103.0)
google-protobuf (~> 3.1)
grpc (~> 1.10)
github-linguist (5.3.3)
@ -1051,7 +1051,7 @@ DEPENDENCIES
gettext (~> 3.2.2)
gettext_i18n_rails (~> 1.8.0)
gettext_i18n_rails_js (~> 1.3)
gitaly-proto (~> 0.102.0)
gitaly-proto (~> 0.103.0)
github-linguist (~> 5.3.3)
gitlab-flowdock-git-hook (~> 1.0.1)
gitlab-gollum-lib (~> 4.2)

View File

@ -197,24 +197,7 @@ to. For example:
If you think a merge request should go into an RC or patch even though it does not meet these requirements,
you can ask for an exception to be made.
Go to [Release tasks issue tracker](https://gitlab.com/gitlab-org/release/tasks/issues/new) and create an issue
using the `Exception-request` issue template.
**Do not** set the relevant `Pick into X.Y` label (see above) before request an
exception; this should be done after the exception is approved.
You can find who is who on the [team page](https://about.gitlab.com/team/).
Whether an exception is made is determined by weighing the benefit and urgency of the change
(how important it is to the company that this is released _right now_ instead of in a month)
against the potential negative impact
(things breaking without enough time to comfortably find and fix them before the release on the 22nd).
When in doubt, we err on the side of _not_ cherry-picking.
For example, it is likely that an exception will be made for a trivial 1-5 line performance improvement
(e.g. adding a database index or adding `includes` to a query), but not for a new feature, no matter how relatively small or thoroughly tested.
All MRs which have had exceptions granted must be merged by the 15th.
Check [this guide](https://gitlab.com/gitlab-org/release/docs/blob/master/general/exception-request/process.md) about how to open an exception request before opening one.
### Regressions

View File

@ -1,78 +1,78 @@
<script>
/* eslint-disable vue/require-default-prop */
import './issue_card_inner';
import eventHub from '../eventhub';
/* eslint-disable vue/require-default-prop */
import IssueCardInner from './issue_card_inner.vue';
import eventHub from '../eventhub';
const Store = gl.issueBoards.BoardsStore;
const Store = gl.issueBoards.BoardsStore;
export default {
name: 'BoardsIssueCard',
components: {
'issue-card-inner': gl.issueBoards.IssueCardInner,
},
props: {
list: {
type: Object,
default: () => ({}),
export default {
name: 'BoardsIssueCard',
components: {
IssueCardInner,
},
issue: {
type: Object,
default: () => ({}),
props: {
list: {
type: Object,
default: () => ({}),
},
issue: {
type: Object,
default: () => ({}),
},
issueLinkBase: {
type: String,
default: '',
},
disabled: {
type: Boolean,
default: false,
},
index: {
type: Number,
default: 0,
},
rootPath: {
type: String,
default: '',
},
groupId: {
type: Number,
},
},
issueLinkBase: {
type: String,
default: '',
data() {
return {
showDetail: false,
detailIssue: Store.detail,
};
},
disabled: {
type: Boolean,
default: false,
computed: {
issueDetailVisible() {
return this.detailIssue.issue && this.detailIssue.issue.id === this.issue.id;
},
},
index: {
type: Number,
default: 0,
},
rootPath: {
type: String,
default: '',
},
groupId: {
type: Number,
},
},
data() {
return {
showDetail: false,
detailIssue: Store.detail,
};
},
computed: {
issueDetailVisible() {
return this.detailIssue.issue && this.detailIssue.issue.id === this.issue.id;
},
},
methods: {
mouseDown() {
this.showDetail = true;
},
mouseMove() {
this.showDetail = false;
},
showIssue(e) {
if (e.target.classList.contains('js-no-trigger')) return;
if (this.showDetail) {
methods: {
mouseDown() {
this.showDetail = true;
},
mouseMove() {
this.showDetail = false;
},
showIssue(e) {
if (e.target.classList.contains('js-no-trigger')) return;
if (Store.detail.issue && Store.detail.issue.id === this.issue.id) {
eventHub.$emit('clearDetailIssue');
} else {
eventHub.$emit('newDetailIssue', this.issue);
Store.detail.list = this.list;
if (this.showDetail) {
this.showDetail = false;
if (Store.detail.issue && Store.detail.issue.id === this.issue.id) {
eventHub.$emit('clearDetailIssue');
} else {
eventHub.$emit('newDetailIssue', this.issue);
Store.detail.list = this.list;
}
}
}
},
},
},
};
};
</script>
<template>

View File

@ -1,196 +0,0 @@
import $ from 'jquery';
import Vue from 'vue';
import UserAvatarLink from '../../vue_shared/components/user_avatar/user_avatar_link.vue';
import eventHub from '../eventhub';
const Store = gl.issueBoards.BoardsStore;
window.gl = window.gl || {};
window.gl.issueBoards = window.gl.issueBoards || {};
gl.issueBoards.IssueCardInner = Vue.extend({
components: {
UserAvatarLink,
},
props: {
issue: {
type: Object,
required: true,
},
issueLinkBase: {
type: String,
required: true,
},
list: {
type: Object,
required: false,
default: () => ({}),
},
rootPath: {
type: String,
required: true,
},
updateFilters: {
type: Boolean,
required: false,
default: false,
},
groupId: {
type: Number,
required: false,
default: null,
},
},
data() {
return {
limitBeforeCounter: 3,
maxRender: 4,
maxCounter: 99,
};
},
computed: {
numberOverLimit() {
return this.issue.assignees.length - this.limitBeforeCounter;
},
assigneeCounterTooltip() {
return `${this.assigneeCounterLabel} more`;
},
assigneeCounterLabel() {
if (this.numberOverLimit > this.maxCounter) {
return `${this.maxCounter}+`;
}
return `+${this.numberOverLimit}`;
},
shouldRenderCounter() {
if (this.issue.assignees.length <= this.maxRender) {
return false;
}
return this.issue.assignees.length > this.numberOverLimit;
},
issueId() {
if (this.issue.iid) {
return `#${this.issue.iid}`;
}
return false;
},
showLabelFooter() {
return this.issue.labels.find(l => this.showLabel(l)) !== undefined;
},
},
methods: {
isIndexLessThanlimit(index) {
return index < this.limitBeforeCounter;
},
shouldRenderAssignee(index) {
// Eg. maxRender is 4,
// Render up to all 4 assignees if there are only 4 assigness
// Otherwise render up to the limitBeforeCounter
if (this.issue.assignees.length <= this.maxRender) {
return index < this.maxRender;
}
return index < this.limitBeforeCounter;
},
assigneeUrl(assignee) {
return `${this.rootPath}${assignee.username}`;
},
assigneeUrlTitle(assignee) {
return `Assigned to ${assignee.name}`;
},
avatarUrlTitle(assignee) {
return `Avatar for ${assignee.name}`;
},
showLabel(label) {
if (!label.id) return false;
return true;
},
filterByLabel(label, e) {
if (!this.updateFilters) return;
const filterPath = gl.issueBoards.BoardsStore.filter.path.split('&');
const labelTitle = encodeURIComponent(label.title);
const param = `label_name[]=${labelTitle}`;
const labelIndex = filterPath.indexOf(param);
$(e.currentTarget).tooltip('hide');
if (labelIndex === -1) {
filterPath.push(param);
} else {
filterPath.splice(labelIndex, 1);
}
gl.issueBoards.BoardsStore.filter.path = filterPath.join('&');
Store.updateFiltersUrl();
eventHub.$emit('updateTokens');
},
labelStyle(label) {
return {
backgroundColor: label.color,
color: label.textColor,
};
},
},
template: `
<div>
<div class="board-card-header">
<h4 class="board-card-title">
<i
class="fa fa-eye-slash confidential-icon"
v-if="issue.confidential"
aria-hidden="true"
/>
<a
class="js-no-trigger"
:href="issue.path"
:title="issue.title">{{ issue.title }}</a>
<span
class="board-card-number"
v-if="issueId"
>
{{ issue.referencePath }}
</span>
</h4>
<div class="board-card-assignee">
<user-avatar-link
v-for="(assignee, index) in issue.assignees"
:key="assignee.id"
v-if="shouldRenderAssignee(index)"
class="js-no-trigger"
:link-href="assigneeUrl(assignee)"
:img-alt="avatarUrlTitle(assignee)"
:img-src="assignee.avatar"
:tooltip-text="assigneeUrlTitle(assignee)"
tooltip-placement="bottom"
/>
<span
class="avatar-counter has-tooltip"
:title="assigneeCounterTooltip"
v-if="shouldRenderCounter"
>
{{ assigneeCounterLabel }}
</span>
</div>
</div>
<div
class="board-card-footer"
v-if="showLabelFooter"
>
<button
class="badge color-label has-tooltip"
v-for="label in issue.labels"
type="button"
v-if="showLabel(label)"
@click="filterByLabel(label, $event)"
:style="labelStyle(label)"
:title="label.description"
data-container="body">
{{ label.title }}
</button>
</div>
</div>
`,
});

View File

@ -0,0 +1,196 @@
<script>
import $ from 'jquery';
import UserAvatarLink from '../../vue_shared/components/user_avatar/user_avatar_link.vue';
import eventHub from '../eventhub';
const Store = gl.issueBoards.BoardsStore;
export default {
components: {
UserAvatarLink,
},
props: {
issue: {
type: Object,
required: true,
},
issueLinkBase: {
type: String,
required: true,
},
list: {
type: Object,
required: false,
default: () => ({}),
},
rootPath: {
type: String,
required: true,
},
updateFilters: {
type: Boolean,
required: false,
default: false,
},
groupId: {
type: Number,
required: false,
default: null,
},
},
data() {
return {
limitBeforeCounter: 3,
maxRender: 4,
maxCounter: 99,
};
},
computed: {
numberOverLimit() {
return this.issue.assignees.length - this.limitBeforeCounter;
},
assigneeCounterTooltip() {
return `${this.assigneeCounterLabel} more`;
},
assigneeCounterLabel() {
if (this.numberOverLimit > this.maxCounter) {
return `${this.maxCounter}+`;
}
return `+${this.numberOverLimit}`;
},
shouldRenderCounter() {
if (this.issue.assignees.length <= this.maxRender) {
return false;
}
return this.issue.assignees.length > this.numberOverLimit;
},
issueId() {
if (this.issue.iid) {
return `#${this.issue.iid}`;
}
return false;
},
showLabelFooter() {
return this.issue.labels.find(l => this.showLabel(l)) !== undefined;
},
},
methods: {
isIndexLessThanlimit(index) {
return index < this.limitBeforeCounter;
},
shouldRenderAssignee(index) {
// Eg. maxRender is 4,
// Render up to all 4 assignees if there are only 4 assigness
// Otherwise render up to the limitBeforeCounter
if (this.issue.assignees.length <= this.maxRender) {
return index < this.maxRender;
}
return index < this.limitBeforeCounter;
},
assigneeUrl(assignee) {
return `${this.rootPath}${assignee.username}`;
},
assigneeUrlTitle(assignee) {
return `Assigned to ${assignee.name}`;
},
avatarUrlTitle(assignee) {
return `Avatar for ${assignee.name}`;
},
showLabel(label) {
if (!label.id) return false;
return true;
},
filterByLabel(label, e) {
if (!this.updateFilters) return;
const filterPath = gl.issueBoards.BoardsStore.filter.path.split('&');
const labelTitle = encodeURIComponent(label.title);
const param = `label_name[]=${labelTitle}`;
const labelIndex = filterPath.indexOf(param);
$(e.currentTarget).tooltip('hide');
if (labelIndex === -1) {
filterPath.push(param);
} else {
filterPath.splice(labelIndex, 1);
}
gl.issueBoards.BoardsStore.filter.path = filterPath.join('&');
Store.updateFiltersUrl();
eventHub.$emit('updateTokens');
},
labelStyle(label) {
return {
backgroundColor: label.color,
color: label.textColor,
};
},
},
};
</script>
<template>
<div>
<div class="board-card-header">
<h4 class="board-card-title">
<i
v-if="issue.confidential"
class="fa fa-eye-slash confidential-icon"
aria-hidden="true"
></i>
<a
:href="issue.path"
:title="issue.title"
class="js-no-trigger">{{ issue.title }}</a>
<span
v-if="issueId"
class="board-card-number"
>
{{ issue.referencePath }}
</span>
</h4>
<div class="board-card-assignee">
<user-avatar-link
v-for="(assignee, index) in issue.assignees"
v-if="shouldRenderAssignee(index)"
:key="assignee.id"
:link-href="assigneeUrl(assignee)"
:img-alt="avatarUrlTitle(assignee)"
:img-src="assignee.avatar"
:tooltip-text="assigneeUrlTitle(assignee)"
class="js-no-trigger"
tooltip-placement="bottom"
/>
<span
v-if="shouldRenderCounter"
:title="assigneeCounterTooltip"
class="avatar-counter has-tooltip"
>
{{ assigneeCounterLabel }}
</span>
</div>
</div>
<div
v-if="showLabelFooter"
class="board-card-footer"
>
<button
v-for="label in issue.labels"
v-if="showLabel(label)"
:key="label.id"
:style="labelStyle(label)"
:title="label.description"
class="badge color-label has-tooltip"
type="button"
data-container="body"
@click="filterByLabel(label, $event)"
>
{{ label.title }}
</button>
</div>
</div>
</template>

View File

@ -1,79 +0,0 @@
import Vue from 'vue';
import modalFilters from './filters';
import modalTabs from './tabs.vue';
import ModalStore from '../../stores/modal_store';
import modalMixin from '../../mixins/modal_mixins';
gl.issueBoards.ModalHeader = Vue.extend({
components: {
modalTabs,
modalFilters,
},
mixins: [modalMixin],
props: {
projectId: {
type: Number,
required: true,
},
milestonePath: {
type: String,
required: true,
},
labelPath: {
type: String,
required: true,
},
},
data() {
return ModalStore.store;
},
computed: {
selectAllText() {
if (ModalStore.selectedCount() !== this.issues.length || this.issues.length === 0) {
return 'Select all';
}
return 'Deselect all';
},
showSearch() {
return this.activeTab === 'all' && !this.loading && this.issuesCount > 0;
},
},
methods: {
toggleAll() {
this.$refs.selectAllBtn.blur();
ModalStore.toggleAll();
},
},
template: `
<div>
<header class="add-issues-header form-actions">
<h2>
Add issues
<button
type="button"
class="close"
data-dismiss="modal"
aria-label="Close"
@click="toggleModal(false)">
<span aria-hidden="true">×</span>
</button>
</h2>
</header>
<modal-tabs v-if="!loading && issuesCount > 0"></modal-tabs>
<div
class="add-issues-search append-bottom-10"
v-if="showSearch">
<modal-filters :store="filter" />
<button
type="button"
class="btn btn-success btn-inverted prepend-left-10"
ref="selectAllBtn"
@click="toggleAll">
{{ selectAllText }}
</button>
</div>
</div>
`,
});

View File

@ -0,0 +1,82 @@
<script>
import ModalFilters from './filters';
import ModalTabs from './tabs.vue';
import ModalStore from '../../stores/modal_store';
import modalMixin from '../../mixins/modal_mixins';
export default {
components: {
ModalTabs,
ModalFilters,
},
mixins: [modalMixin],
props: {
projectId: {
type: Number,
required: true,
},
milestonePath: {
type: String,
required: true,
},
labelPath: {
type: String,
required: true,
},
},
data() {
return ModalStore.store;
},
computed: {
selectAllText() {
if (ModalStore.selectedCount() !== this.issues.length || this.issues.length === 0) {
return 'Select all';
}
return 'Deselect all';
},
showSearch() {
return this.activeTab === 'all' && !this.loading && this.issuesCount > 0;
},
},
methods: {
toggleAll() {
this.$refs.selectAllBtn.blur();
ModalStore.toggleAll();
},
},
};
</script>
<template>
<div>
<header class="add-issues-header form-actions">
<h2>
Add issues
<button
type="button"
class="close"
data-dismiss="modal"
aria-label="Close"
@click="toggleModal(false)"
>
<span aria-hidden="true">×</span>
</button>
</h2>
</header>
<modal-tabs v-if="!loading && issuesCount > 0"/>
<div
v-if="showSearch"
class="add-issues-search append-bottom-10">
<modal-filters :store="filter" />
<button
ref="selectAllBtn"
type="button"
class="btn btn-success btn-inverted prepend-left-10"
@click="toggleAll"
>
{{ selectAllText }}
</button>
</div>
</div>
</template>

View File

@ -1,171 +0,0 @@
/* global ListIssue */
import Vue from 'vue';
import queryData from '~/boards/utils/query_data';
import loadingIcon from '~/vue_shared/components/loading_icon.vue';
import './header';
import './list';
import ModalFooter from './footer.vue';
import EmptyState from './empty_state.vue';
import ModalStore from '../../stores/modal_store';
gl.issueBoards.IssuesModal = Vue.extend({
components: {
EmptyState,
'modal-header': gl.issueBoards.ModalHeader,
'modal-list': gl.issueBoards.ModalList,
ModalFooter,
loadingIcon,
},
props: {
newIssuePath: {
type: String,
required: true,
},
emptyStateSvg: {
type: String,
required: true,
},
issueLinkBase: {
type: String,
required: true,
},
rootPath: {
type: String,
required: true,
},
projectId: {
type: Number,
required: true,
},
milestonePath: {
type: String,
required: true,
},
labelPath: {
type: String,
required: true,
},
},
data() {
return ModalStore.store;
},
computed: {
showList() {
if (this.activeTab === 'selected') {
return this.selectedIssues.length > 0;
}
return this.issuesCount > 0;
},
showEmptyState() {
if (!this.loading && this.issuesCount === 0) {
return true;
}
return this.activeTab === 'selected' && this.selectedIssues.length === 0;
},
},
watch: {
page() {
this.loadIssues();
},
showAddIssuesModal() {
if (this.showAddIssuesModal && !this.issues.length) {
this.loading = true;
const loadingDone = () => {
this.loading = false;
};
this.loadIssues()
.then(loadingDone)
.catch(loadingDone);
} else if (!this.showAddIssuesModal) {
this.issues = [];
this.selectedIssues = [];
this.issuesCount = false;
}
},
filter: {
handler() {
if (this.$el.tagName) {
this.page = 1;
this.filterLoading = true;
const loadingDone = () => {
this.filterLoading = false;
};
this.loadIssues(true)
.then(loadingDone)
.catch(loadingDone);
}
},
deep: true,
},
},
created() {
this.page = 1;
},
methods: {
loadIssues(clearIssues = false) {
if (!this.showAddIssuesModal) return false;
return gl.boardService.getBacklog(queryData(this.filter.path, {
page: this.page,
per: this.perPage,
}))
.then(res => res.data)
.then((data) => {
if (clearIssues) {
this.issues = [];
}
data.issues.forEach((issueObj) => {
const issue = new ListIssue(issueObj);
const foundSelectedIssue = ModalStore.findSelectedIssue(issue);
issue.selected = !!foundSelectedIssue;
this.issues.push(issue);
});
this.loadingNewPage = false;
if (!this.issuesCount) {
this.issuesCount = data.size;
}
}).catch(() => {
// TODO: handle request error
});
},
},
template: `
<div
class="add-issues-modal"
v-if="showAddIssuesModal">
<div class="add-issues-container">
<modal-header
:project-id="projectId"
:milestone-path="milestonePath"
:label-path="labelPath">
</modal-header>
<modal-list
:issue-link-base="issueLinkBase"
:root-path="rootPath"
:empty-state-svg="emptyStateSvg"
v-if="!loading && showList && !filterLoading"></modal-list>
<empty-state
v-if="showEmptyState"
:new-issue-path="newIssuePath"
:empty-state-svg="emptyStateSvg"></empty-state>
<section
class="add-issues-list text-center"
v-if="loading || filterLoading">
<div class="add-issues-list-loading">
<loading-icon />
</div>
</section>
<modal-footer></modal-footer>
</div>
</div>
`,
});

View File

@ -0,0 +1,178 @@
<script>
/* global ListIssue */
import queryData from '~/boards/utils/query_data';
import loadingIcon from '~/vue_shared/components/loading_icon.vue';
import ModalHeader from './header.vue';
import ModalList from './list.vue';
import ModalFooter from './footer.vue';
import EmptyState from './empty_state.vue';
import ModalStore from '../../stores/modal_store';
export default {
components: {
EmptyState,
ModalHeader,
ModalList,
ModalFooter,
loadingIcon,
},
props: {
newIssuePath: {
type: String,
required: true,
},
emptyStateSvg: {
type: String,
required: true,
},
issueLinkBase: {
type: String,
required: true,
},
rootPath: {
type: String,
required: true,
},
projectId: {
type: Number,
required: true,
},
milestonePath: {
type: String,
required: true,
},
labelPath: {
type: String,
required: true,
},
},
data() {
return ModalStore.store;
},
computed: {
showList() {
if (this.activeTab === 'selected') {
return this.selectedIssues.length > 0;
}
return this.issuesCount > 0;
},
showEmptyState() {
if (!this.loading && this.issuesCount === 0) {
return true;
}
return this.activeTab === 'selected' && this.selectedIssues.length === 0;
},
},
watch: {
page() {
this.loadIssues();
},
showAddIssuesModal() {
if (this.showAddIssuesModal && !this.issues.length) {
this.loading = true;
const loadingDone = () => {
this.loading = false;
};
this.loadIssues()
.then(loadingDone)
.catch(loadingDone);
} else if (!this.showAddIssuesModal) {
this.issues = [];
this.selectedIssues = [];
this.issuesCount = false;
}
},
filter: {
handler() {
if (this.$el.tagName) {
this.page = 1;
this.filterLoading = true;
const loadingDone = () => {
this.filterLoading = false;
};
this.loadIssues(true)
.then(loadingDone)
.catch(loadingDone);
}
},
deep: true,
},
},
created() {
this.page = 1;
},
methods: {
loadIssues(clearIssues = false) {
if (!this.showAddIssuesModal) return false;
return gl.boardService
.getBacklog(
queryData(this.filter.path, {
page: this.page,
per: this.perPage,
}),
)
.then(res => res.data)
.then(data => {
if (clearIssues) {
this.issues = [];
}
data.issues.forEach(issueObj => {
const issue = new ListIssue(issueObj);
const foundSelectedIssue = ModalStore.findSelectedIssue(issue);
issue.selected = !!foundSelectedIssue;
this.issues.push(issue);
});
this.loadingNewPage = false;
if (!this.issuesCount) {
this.issuesCount = data.size;
}
})
.catch(() => {
// TODO: handle request error
});
},
},
};
</script>
<template>
<div
v-if="showAddIssuesModal"
class="add-issues-modal">
<div class="add-issues-container">
<modal-header
:project-id="projectId"
:milestone-path="milestonePath"
:label-path="labelPath"
/>
<modal-list
v-if="!loading && showList && !filterLoading"
:issue-link-base="issueLinkBase"
:root-path="rootPath"
:empty-state-svg="emptyStateSvg"
/>
<empty-state
v-if="showEmptyState"
:new-issue-path="newIssuePath"
:empty-state-svg="emptyStateSvg"
/>
<section
v-if="loading || filterLoading"
class="add-issues-list text-center"
>
<div class="add-issues-list-loading">
<loading-icon />
</div>
</section>
<modal-footer/>
</div>
</div>
</template>

View File

@ -1,159 +0,0 @@
import Vue from 'vue';
import bp from '../../../breakpoints';
import ModalStore from '../../stores/modal_store';
gl.issueBoards.ModalList = Vue.extend({
components: {
'issue-card-inner': gl.issueBoards.IssueCardInner,
},
props: {
issueLinkBase: {
type: String,
required: true,
},
rootPath: {
type: String,
required: true,
},
emptyStateSvg: {
type: String,
required: true,
},
},
data() {
return ModalStore.store;
},
computed: {
loopIssues() {
if (this.activeTab === 'all') {
return this.issues;
}
return this.selectedIssues;
},
groupedIssues() {
const groups = [];
this.loopIssues.forEach((issue, i) => {
const index = i % this.columns;
if (!groups[index]) {
groups.push([]);
}
groups[index].push(issue);
});
return groups;
},
},
watch: {
activeTab() {
if (this.activeTab === 'all') {
ModalStore.purgeUnselectedIssues();
}
},
},
mounted() {
this.scrollHandlerWrapper = this.scrollHandler.bind(this);
this.setColumnCountWrapper = this.setColumnCount.bind(this);
this.setColumnCount();
this.$refs.list.addEventListener('scroll', this.scrollHandlerWrapper);
window.addEventListener('resize', this.setColumnCountWrapper);
},
beforeDestroy() {
this.$refs.list.removeEventListener('scroll', this.scrollHandlerWrapper);
window.removeEventListener('resize', this.setColumnCountWrapper);
},
methods: {
scrollHandler() {
const currentPage = Math.floor(this.issues.length / this.perPage);
if (
this.scrollTop() > this.scrollHeight() - 100 &&
!this.loadingNewPage &&
currentPage === this.page
) {
this.loadingNewPage = true;
this.page += 1;
}
},
toggleIssue(e, issue) {
if (e.target.tagName !== 'A') {
ModalStore.toggleIssue(issue);
}
},
listHeight() {
return this.$refs.list.getBoundingClientRect().height;
},
scrollHeight() {
return this.$refs.list.scrollHeight;
},
scrollTop() {
return this.$refs.list.scrollTop + this.listHeight();
},
showIssue(issue) {
if (this.activeTab === 'all') return true;
const index = ModalStore.selectedIssueIndex(issue);
return index !== -1;
},
setColumnCount() {
const breakpoint = bp.getBreakpointSize();
if (breakpoint === 'lg' || breakpoint === 'md') {
this.columns = 3;
} else if (breakpoint === 'sm') {
this.columns = 2;
} else {
this.columns = 1;
}
},
},
template: `
<section
class="add-issues-list add-issues-list-columns"
ref="list">
<div
class="empty-state add-issues-empty-state-filter text-center"
v-if="issuesCount > 0 && issues.length === 0">
<div
class="svg-content">
<img :src="emptyStateSvg"/>
</div>
<div class="text-content">
<h4>
There are no issues to show.
</h4>
</div>
</div>
<div
v-for="group in groupedIssues"
class="add-issues-list-column">
<div
v-for="issue in group"
v-if="showIssue(issue)"
class="board-card-parent">
<div
class="board-card"
:class="{ 'is-active': issue.selected }"
@click="toggleIssue($event, issue)">
<issue-card-inner
:issue="issue"
:issue-link-base="issueLinkBase"
:root-path="rootPath">
</issue-card-inner>
<span
:aria-label="'Issue #' + issue.id + ' selected'"
aria-checked="true"
v-if="issue.selected"
class="issue-card-selected text-center">
<i class="fa fa-check"></i>
</span>
</div>
</div>
</div>
</section>
`,
});

View File

@ -0,0 +1,162 @@
<script>
import bp from '../../../breakpoints';
import ModalStore from '../../stores/modal_store';
import IssueCardInner from '../issue_card_inner.vue';
export default {
components: {
IssueCardInner,
},
props: {
issueLinkBase: {
type: String,
required: true,
},
rootPath: {
type: String,
required: true,
},
emptyStateSvg: {
type: String,
required: true,
},
},
data() {
return ModalStore.store;
},
computed: {
loopIssues() {
if (this.activeTab === 'all') {
return this.issues;
}
return this.selectedIssues;
},
groupedIssues() {
const groups = [];
this.loopIssues.forEach((issue, i) => {
const index = i % this.columns;
if (!groups[index]) {
groups.push([]);
}
groups[index].push(issue);
});
return groups;
},
},
watch: {
activeTab() {
if (this.activeTab === 'all') {
ModalStore.purgeUnselectedIssues();
}
},
},
mounted() {
this.scrollHandlerWrapper = this.scrollHandler.bind(this);
this.setColumnCountWrapper = this.setColumnCount.bind(this);
this.setColumnCount();
this.$refs.list.addEventListener('scroll', this.scrollHandlerWrapper);
window.addEventListener('resize', this.setColumnCountWrapper);
},
beforeDestroy() {
this.$refs.list.removeEventListener('scroll', this.scrollHandlerWrapper);
window.removeEventListener('resize', this.setColumnCountWrapper);
},
methods: {
scrollHandler() {
const currentPage = Math.floor(this.issues.length / this.perPage);
if (
this.scrollTop() > this.scrollHeight() - 100 &&
!this.loadingNewPage &&
currentPage === this.page
) {
this.loadingNewPage = true;
this.page += 1;
}
},
toggleIssue(e, issue) {
if (e.target.tagName !== 'A') {
ModalStore.toggleIssue(issue);
}
},
listHeight() {
return this.$refs.list.getBoundingClientRect().height;
},
scrollHeight() {
return this.$refs.list.scrollHeight;
},
scrollTop() {
return this.$refs.list.scrollTop + this.listHeight();
},
showIssue(issue) {
if (this.activeTab === 'all') return true;
const index = ModalStore.selectedIssueIndex(issue);
return index !== -1;
},
setColumnCount() {
const breakpoint = bp.getBreakpointSize();
if (breakpoint === 'lg' || breakpoint === 'md') {
this.columns = 3;
} else if (breakpoint === 'sm') {
this.columns = 2;
} else {
this.columns = 1;
}
},
},
};
</script>
<template>
<section
ref="list"
class="add-issues-list add-issues-list-columns">
<div
v-if="issuesCount > 0 && issues.length === 0"
class="empty-state add-issues-empty-state-filter text-center">
<div
class="svg-content">
<img :src="emptyStateSvg" />
</div>
<div class="text-content">
<h4>
There are no issues to show.
</h4>
</div>
</div>
<div
v-for="(group, index) in groupedIssues"
:key="index"
class="add-issues-list-column">
<div
v-for="issue in group"
v-if="showIssue(issue)"
:key="issue.id"
class="board-card-parent">
<div
:class="{ 'is-active': issue.selected }"
class="board-card"
@click="toggleIssue($event, issue)">
<issue-card-inner
:issue="issue"
:issue-link-base="issueLinkBase"
:root-path="rootPath"/>
<span
v-if="issue.selected"
:aria-label="'Issue #' + issue.id + ' selected'"
aria-checked="true"
class="issue-card-selected text-center">
<i class="fa fa-check"></i>
</span>
</div>
</div>
</div>
</section>
</template>

View File

@ -25,7 +25,7 @@ import './filters/due_date_filters';
import './components/board';
import './components/board_sidebar';
import './components/new_list_dropdown';
import './components/modal/index';
import BoardAddIssuesModal from './components/modal/index.vue';
import '~/vue_shared/vue_resource_interceptor'; // eslint-disable-line import/first
export default () => {
@ -49,7 +49,7 @@ export default () => {
components: {
'board': gl.issueBoards.Board,
'board-sidebar': gl.issueBoards.BoardSidebar,
'board-add-issues-modal': gl.issueBoards.IssuesModal,
BoardAddIssuesModal,
},
data: {
state: Store.state,

View File

@ -189,12 +189,25 @@ export const getParameterByName = (name, urlToParse) => {
return decodeURIComponent(results[2].replace(/\+/g, ' '));
};
const handleSelectedRange = (range) => {
const container = range.commonAncestorContainer;
// add context to fragment if needed
if (container.tagName === 'OL') {
const parentContainer = document.createElement(container.tagName);
parentContainer.appendChild(range.cloneContents());
return parentContainer;
}
return range.cloneContents();
};
export const getSelectedFragment = () => {
const selection = window.getSelection();
if (selection.rangeCount === 0) return null;
const documentFragment = document.createDocumentFragment();
for (let i = 0; i < selection.rangeCount; i += 1) {
documentFragment.appendChild(selection.getRangeAt(i).cloneContents());
const range = selection.getRangeAt(i);
documentFragment.appendChild(handleSelectedRange(range));
}
if (documentFragment.textContent.length === 0) return null;

View File

@ -1,11 +1,8 @@
import $ from 'jquery';
import _ from 'underscore';
import ProtectedBranchAccessDropdown from './protected_branch_access_dropdown';
import CreateItemDropdown from '../create_item_dropdown';
import AccessorUtilities from '../lib/utils/accessor';
const PB_LOCAL_STORAGE_KEY = 'protected-branches-defaults';
export default class ProtectedBranchCreate {
constructor() {
this.$form = $('.js-new-protected-branch');
@ -43,8 +40,6 @@ export default class ProtectedBranchCreate {
onSelect: this.onSelectCallback,
getData: ProtectedBranchCreate.getProtectedBranches,
});
this.loadPreviousSelection($allowedToMergeDropdown.data('glDropdown'), $allowedToPushDropdown.data('glDropdown'));
}
// This will run after clicked callback
@ -59,39 +54,10 @@ export default class ProtectedBranchCreate {
$allowedToPushInput.length
);
this.savePreviousSelection($allowedToMergeInput.val(), $allowedToPushInput.val());
this.$form.find('input[type="submit"]').prop('disabled', completedForm);
}
static getProtectedBranches(term, callback) {
callback(gon.open_branches);
}
loadPreviousSelection(mergeDropdown, pushDropdown) {
let mergeIndex = 0;
let pushIndex = 0;
if (this.isLocalStorageAvailable) {
const savedDefaults = JSON.parse(window.localStorage.getItem(PB_LOCAL_STORAGE_KEY));
if (savedDefaults != null) {
mergeIndex = _.findLastIndex(mergeDropdown.fullData.roles, {
id: parseInt(savedDefaults.mergeSelection, 0),
});
pushIndex = _.findLastIndex(pushDropdown.fullData.roles, {
id: parseInt(savedDefaults.pushSelection, 0),
});
}
}
mergeDropdown.selectRowAtIndex(mergeIndex);
pushDropdown.selectRowAtIndex(pushIndex);
}
savePreviousSelection(mergeSelection, pushSelection) {
if (this.isLocalStorageAvailable) {
const branchDefaults = {
mergeSelection,
pushSelection,
};
window.localStorage.setItem(PB_LOCAL_STORAGE_KEY, JSON.stringify(branchDefaults));
}
}
}

View File

@ -289,7 +289,7 @@ export default class SearchAutocomplete {
}
// If the dropdown is closed, we'll open it
if (!this.dropdown.hasClass('open')) {
if (!this.dropdown.hasClass('show')) {
this.loadingSuggestions = false;
this.dropdownToggle.dropdown('toggle');
return this.searchInput.removeClass('disabled');
@ -424,9 +424,9 @@ export default class SearchAutocomplete {
}
disableAutocomplete() {
if (!this.searchInput.hasClass('disabled') && this.dropdown.hasClass('open')) {
if (!this.searchInput.hasClass('disabled') && this.dropdown.hasClass('show')) {
this.searchInput.addClass('disabled');
this.dropdown.removeClass('open').trigger('hidden.bs.dropdown');
this.dropdown.removeClass('show').trigger('hidden.bs.dropdown');
this.restoreMenu();
}
}

View File

@ -8,7 +8,6 @@ class HealthController < ActionController::Base
Gitlab::HealthChecks::Redis::CacheCheck,
Gitlab::HealthChecks::Redis::QueuesCheck,
Gitlab::HealthChecks::Redis::SharedStateCheck,
Gitlab::HealthChecks::FsShardsCheck,
Gitlab::HealthChecks::GitalyCheck
].freeze

View File

@ -270,7 +270,7 @@ module ApplicationHelper
{
members: members_project_autocomplete_sources_path(object, type: noteable_type, type_id: params[:id]),
issues: issues_project_autocomplete_sources_path(object),
merge_requests: merge_requests_project_autocomplete_sources_path(object),
mergeRequests: merge_requests_project_autocomplete_sources_path(object),
labels: labels_project_autocomplete_sources_path(object, type: noteable_type, type_id: params[:id]),
milestones: milestones_project_autocomplete_sources_path(object),
commands: commands_project_autocomplete_sources_path(object, type: noteable_type, type_id: params[:id])

View File

@ -6,7 +6,8 @@ class MetricsService
Gitlab::HealthChecks::Redis::RedisCheck,
Gitlab::HealthChecks::Redis::CacheCheck,
Gitlab::HealthChecks::Redis::QueuesCheck,
Gitlab::HealthChecks::Redis::SharedStateCheck
Gitlab::HealthChecks::Redis::SharedStateCheck,
Gitlab::HealthChecks::GitalyCheck
].freeze
def prometheus_metrics_text

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
class AdminEmailWorker
include ApplicationWorker
include CronjobQueue

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
class ArchiveTraceWorker
include ApplicationWorker
include PipelineBackgroundQueue

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
class AuthorizedProjectsWorker
include ApplicationWorker
prepend WaitableWorker

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
class BackgroundMigrationWorker
include ApplicationWorker

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
class BuildCoverageWorker
include ApplicationWorker
include PipelineQueue

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
class BuildFinishedWorker
include ApplicationWorker
include PipelineQueue

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
class BuildHooksWorker
include ApplicationWorker
include PipelineQueue

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
class BuildQueueWorker
include ApplicationWorker
include PipelineQueue

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
class BuildSuccessWorker
include ApplicationWorker
include PipelineQueue

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
class BuildTraceSectionsWorker
include ApplicationWorker
include PipelineQueue

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
module Ci
class ArchiveTracesCronWorker
include ApplicationWorker

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
module Ci
class BuildTraceChunkFlushWorker
include ApplicationWorker

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
class ClusterInstallAppWorker
include ApplicationWorker
include ClusterQueue

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
class ClusterProvisionWorker
include ApplicationWorker
include ClusterQueue

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
class ClusterWaitForAppInstallationWorker
include ApplicationWorker
include ClusterQueue

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
class ClusterWaitForIngressIpAddressWorker
include ApplicationWorker
include ClusterQueue

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
Sidekiq::Worker.extend ActiveSupport::Concern
module ApplicationWorker

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
module ClusterApplications
extend ActiveSupport::Concern

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
##
# Concern for setting Sidekiq settings for the various Gcp clusters workers.
#

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
# Concern that sets various Sidekiq settings for workers executed using a
# cronjob.
module CronjobQueue

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
# Concern for enabling a few lines of exception backtraces in Sidekiq
module ExceptionBacktrace
extend ActiveSupport::Concern

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
module Gitlab
module GithubImport
module Queue

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
module MailSchedulerQueue
extend ActiveSupport::Concern

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
module NewIssuable
attr_reader :issuable, :user

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
# Concern for setting Sidekiq settings for the various GitLab ObjectStorage workers.
module ObjectStorageQueue
extend ActiveSupport::Concern

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
##
# Concern for setting Sidekiq settings for the low priority CI pipeline workers.
#

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
##
# Concern for setting Sidekiq settings for the various CI pipeline workers.
#

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
module ProjectImportOptions
extend ActiveSupport::Concern

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
# Used in EE by mirroring
module ProjectStartImport
def start(project)

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
# Concern for setting Sidekiq settings for the various repository check workers.
module RepositoryCheckQueue
extend ActiveSupport::Concern

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
module WaitableWorker
extend ActiveSupport::Concern

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
class CreateGpgSignatureWorker
include ApplicationWorker

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
class CreateNoteDiffFileWorker
include ApplicationWorker

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
class CreatePipelineWorker
include ApplicationWorker
include PipelineQueue

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
class DeleteMergedBranchesWorker
include ApplicationWorker

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
class DeleteUserWorker
include ApplicationWorker

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
class EmailReceiverWorker
include ApplicationWorker

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
class EmailsOnPushWorker
include ApplicationWorker

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
class ExpireBuildArtifactsWorker
include ApplicationWorker
include CronjobQueue

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
class ExpireBuildInstanceArtifactsWorker
include ApplicationWorker

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
class ExpireJobCacheWorker
include ApplicationWorker
include PipelineQueue

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
class ExpirePipelineCacheWorker
include ApplicationWorker
include PipelineQueue

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
class GitGarbageCollectWorker
include ApplicationWorker

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
class GitlabShellWorker
include ApplicationWorker
include Gitlab::ShellAdapter

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
class GitlabUsagePingWorker
LEASE_TIMEOUT = 86400

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
class GroupDestroyWorker
include ApplicationWorker
include ExceptionBacktrace

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
class ImportExportProjectCleanupWorker
include ApplicationWorker
include CronjobQueue

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
class InvalidGpgSignatureUpdateWorker
include ApplicationWorker

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
require 'json'
require 'socket'
@ -69,8 +71,8 @@ class IrkerWorker
newbranch = "#{Gitlab.config.gitlab.url}/#{repo_path}/branches"
newbranch = "\x0302\x1f#{newbranch}\x0f" if @colors
privmsg = "[#{repo_name}] #{committer} has created a new branch "
privmsg += "#{branch}: #{newbranch}"
privmsg = "[#{repo_name}] #{committer} has created a new branch " \
"#{branch}: #{newbranch}"
sendtoirker privmsg
end
@ -112,9 +114,7 @@ class IrkerWorker
url = compare_url data, project.full_path
commits = colorize_commits data['total_commits_count']
new_commits = 'new commit'
new_commits += 's' if data['total_commits_count'] > 1
new_commits = 'new commit'.pluralize(data['total_commits_count'])
sendtoirker "[#{repo}] #{committer} pushed #{commits} #{new_commits} " \
"to #{branch}: #{url}"
end
@ -122,8 +122,8 @@ class IrkerWorker
def compare_url(data, repo_path)
sha1 = Commit.truncate_sha(data['before'])
sha2 = Commit.truncate_sha(data['after'])
compare_url = "#{Gitlab.config.gitlab.url}/#{repo_path}/compare"
compare_url += "/#{sha1}...#{sha2}"
compare_url = "#{Gitlab.config.gitlab.url}/#{repo_path}/compare" \
"/#{sha1}...#{sha2}"
colorize_url compare_url
end
@ -144,8 +144,7 @@ class IrkerWorker
def files_count(commit)
diff_size = commit.raw_deltas.size
files = "#{diff_size} file"
files += 's' if diff_size > 1
files = "#{diff_size} file".pluralize(diff_size)
files
end

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
class IssueDueSchedulerWorker
include ApplicationWorker
include CronjobQueue

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
module MailScheduler
class IssueDueWorker
include ApplicationWorker

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
require 'active_job/arguments'
module MailScheduler

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
class MergeWorker
include ApplicationWorker

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
# Worker to destroy projects that do not have a namespace
#
# It destroys everything it can without having the info about the namespace it

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
class NewIssueWorker
include ApplicationWorker
include NewIssuable

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
class NewMergeRequestWorker
include ApplicationWorker
include NewIssuable

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
class NewNoteWorker
include ApplicationWorker

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
module ObjectStorage
class BackgroundMoveWorker
include ApplicationWorker

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
# @Deprecated - remove once the `object_storage_upload` queue is empty
# The queue has been renamed `object_storage:object_storage_background_upload`
#

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
class PagesDomainVerificationCronWorker
include ApplicationWorker
include CronjobQueue

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
class PagesDomainVerificationWorker
include ApplicationWorker

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
class PagesWorker
include ApplicationWorker

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
class PipelineHooksWorker
include ApplicationWorker
include PipelineQueue

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
class PipelineMetricsWorker
include ApplicationWorker
include PipelineQueue

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
class PipelineNotificationWorker
include ApplicationWorker
include PipelineQueue

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
class PipelineProcessWorker
include ApplicationWorker
include PipelineQueue

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
class PipelineScheduleWorker
include ApplicationWorker
include CronjobQueue

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
class PipelineSuccessWorker
include ApplicationWorker
include PipelineQueue

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
class PipelineUpdateWorker
include ApplicationWorker
include PipelineQueue

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
class PluginWorker
include ApplicationWorker

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
class PostReceive
include ApplicationWorker

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
# Worker for processing individiual commit messages pushed to a repository.
#
# Jobs for this worker are scheduled for every commit that is being pushed. As a

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
# Worker for updating any project specific caches.
class ProjectCacheWorker
include ApplicationWorker

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
class ProjectDestroyWorker
include ApplicationWorker
include ExceptionBacktrace

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
class ProjectExportWorker
include ApplicationWorker
include ExceptionBacktrace

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
class ProjectMigrateHashedStorageWorker
include ApplicationWorker

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
class ProjectServiceWorker
include ApplicationWorker

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
# Worker for updating any project specific caches.
class PropagateServiceTemplateWorker
include ApplicationWorker

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
class PruneOldEventsWorker
include ApplicationWorker
include CronjobQueue

Some files were not shown because too many files have changed in this diff Show More