Commit 4a6ed479 authored by Stan Hu's avatar Stan Hu

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

parents 615e1807 4f7dce76
...@@ -418,7 +418,7 @@ group :ed25519 do ...@@ -418,7 +418,7 @@ group :ed25519 do
end end
# Gitaly GRPC client # Gitaly GRPC client
gem 'gitaly-proto', '~> 0.102.0', require: 'gitaly' gem 'gitaly-proto', '~> 0.103.0', require: 'gitaly'
gem 'grpc', '~> 1.11.0' gem 'grpc', '~> 1.11.0'
# Locked until https://github.com/google/protobuf/issues/4210 is closed # Locked until https://github.com/google/protobuf/issues/4210 is closed
......
...@@ -282,7 +282,7 @@ GEM ...@@ -282,7 +282,7 @@ GEM
gettext_i18n_rails (>= 0.7.1) gettext_i18n_rails (>= 0.7.1)
po_to_json (>= 1.0.0) po_to_json (>= 1.0.0)
rails (>= 3.2.0) rails (>= 3.2.0)
gitaly-proto (0.102.0) gitaly-proto (0.103.0)
google-protobuf (~> 3.1) google-protobuf (~> 3.1)
grpc (~> 1.10) grpc (~> 1.10)
github-linguist (5.3.3) github-linguist (5.3.3)
...@@ -1041,7 +1041,7 @@ DEPENDENCIES ...@@ -1041,7 +1041,7 @@ DEPENDENCIES
gettext (~> 3.2.2) gettext (~> 3.2.2)
gettext_i18n_rails (~> 1.8.0) gettext_i18n_rails (~> 1.8.0)
gettext_i18n_rails_js (~> 1.3) gettext_i18n_rails_js (~> 1.3)
gitaly-proto (~> 0.102.0) gitaly-proto (~> 0.103.0)
github-linguist (~> 5.3.3) github-linguist (~> 5.3.3)
gitlab-flowdock-git-hook (~> 1.0.1) gitlab-flowdock-git-hook (~> 1.0.1)
gitlab-gollum-lib (~> 4.2) gitlab-gollum-lib (~> 4.2)
......
...@@ -285,7 +285,7 @@ GEM ...@@ -285,7 +285,7 @@ GEM
gettext_i18n_rails (>= 0.7.1) gettext_i18n_rails (>= 0.7.1)
po_to_json (>= 1.0.0) po_to_json (>= 1.0.0)
rails (>= 3.2.0) rails (>= 3.2.0)
gitaly-proto (0.102.0) gitaly-proto (0.103.0)
google-protobuf (~> 3.1) google-protobuf (~> 3.1)
grpc (~> 1.10) grpc (~> 1.10)
github-linguist (5.3.3) github-linguist (5.3.3)
...@@ -1051,7 +1051,7 @@ DEPENDENCIES ...@@ -1051,7 +1051,7 @@ DEPENDENCIES
gettext (~> 3.2.2) gettext (~> 3.2.2)
gettext_i18n_rails (~> 1.8.0) gettext_i18n_rails (~> 1.8.0)
gettext_i18n_rails_js (~> 1.3) gettext_i18n_rails_js (~> 1.3)
gitaly-proto (~> 0.102.0) gitaly-proto (~> 0.103.0)
github-linguist (~> 5.3.3) github-linguist (~> 5.3.3)
gitlab-flowdock-git-hook (~> 1.0.1) gitlab-flowdock-git-hook (~> 1.0.1)
gitlab-gollum-lib (~> 4.2) gitlab-gollum-lib (~> 4.2)
......
...@@ -197,24 +197,7 @@ to. For example: ...@@ -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, 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. 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 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.
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.
### Regressions ### Regressions
......
<script> <script>
/* eslint-disable vue/require-default-prop */ /* eslint-disable vue/require-default-prop */
import './issue_card_inner'; import IssueCardInner from './issue_card_inner.vue';
import eventHub from '../eventhub'; import eventHub from '../eventhub';
const Store = gl.issueBoards.BoardsStore; const Store = gl.issueBoards.BoardsStore;
export default { export default {
name: 'BoardsIssueCard', name: 'BoardsIssueCard',
components: { components: {
'issue-card-inner': gl.issueBoards.IssueCardInner, IssueCardInner,
},
props: {
list: {
type: Object,
default: () => ({}),
}, },
issue: { props: {
type: Object, list: {
default: () => ({}), 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: { data() {
type: String, return {
default: '', showDetail: false,
detailIssue: Store.detail,
};
}, },
disabled: { computed: {
type: Boolean, issueDetailVisible() {
default: false, return this.detailIssue.issue && this.detailIssue.issue.id === this.issue.id;
},
}, },
index: { methods: {
type: Number, mouseDown() {
default: 0, this.showDetail = true;
}, },
rootPath: { mouseMove() {
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) {
this.showDetail = false; this.showDetail = false;
},
showIssue(e) {
if (e.target.classList.contains('js-no-trigger')) return;
if (this.showDetail) {
this.showDetail = false;
if (Store.detail.issue && Store.detail.issue.id === this.issue.id) { if (Store.detail.issue && Store.detail.issue.id === this.issue.id) {
eventHub.$emit('clearDetailIssue'); eventHub.$emit('clearDetailIssue');
} else { } else {
eventHub.$emit('newDetailIssue', this.issue); eventHub.$emit('newDetailIssue', this.issue);
Store.detail.list = this.list; Store.detail.list = this.list;
}
} }
} },
}, },
}, };
};
</script> </script>
<template> <template>
......
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>
`,
});
<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>
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>
`,
});
<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>
/* 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>
`,
});
<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>
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>
`,
});
<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>
...@@ -25,7 +25,7 @@ import './filters/due_date_filters'; ...@@ -25,7 +25,7 @@ import './filters/due_date_filters';
import './components/board'; import './components/board';
import './components/board_sidebar'; import './components/board_sidebar';
import './components/new_list_dropdown'; 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 import '~/vue_shared/vue_resource_interceptor'; // eslint-disable-line import/first
export default () => { export default () => {
...@@ -49,7 +49,7 @@ export default () => { ...@@ -49,7 +49,7 @@ export default () => {
components: { components: {
'board': gl.issueBoards.Board, 'board': gl.issueBoards.Board,
'board-sidebar': gl.issueBoards.BoardSidebar, 'board-sidebar': gl.issueBoards.BoardSidebar,
'board-add-issues-modal': gl.issueBoards.IssuesModal, BoardAddIssuesModal,
}, },
data: { data: {
state: Store.state, state: Store.state,
......
...@@ -189,12 +189,25 @@ export const getParameterByName = (name, urlToParse) => { ...@@ -189,12 +189,25 @@ export const getParameterByName = (name, urlToParse) => {
return decodeURIComponent(results[2].replace(/\+/g, ' ')); 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 = () => { export const getSelectedFragment = () => {
const selection = window.getSelection(); const selection = window.getSelection();
if (selection.rangeCount === 0) return null; if (selection.rangeCount === 0) return null;
const documentFragment = document.createDocumentFragment(); const documentFragment = document.createDocumentFragment();
for (let i = 0; i < selection.rangeCount; i += 1) { 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; if (documentFragment.textContent.length === 0) return null;
......
import $ from 'jquery'; import $ from 'jquery';
import _ from 'underscore';
import ProtectedBranchAccessDropdown from './protected_branch_access_dropdown'; import ProtectedBranchAccessDropdown from './protected_branch_access_dropdown';
import CreateItemDropdown from '../create_item_dropdown'; import CreateItemDropdown from '../create_item_dropdown';
import AccessorUtilities from '../lib/utils/accessor'; import AccessorUtilities from '../lib/utils/accessor';
const PB_LOCAL_STORAGE_KEY = 'protected-branches-defaults';
export default class ProtectedBranchCreate { export default class ProtectedBranchCreate {
constructor() { constructor() {
this.$form = $('.js-new-protected-branch'); this.$form = $('.js-new-protected-branch');
...@@ -43,8 +40,6 @@ export default class ProtectedBranchCreate { ...@@ -43,8 +40,6 @@ export default class ProtectedBranchCreate {
onSelect: this.onSelectCallback, onSelect: this.onSelectCallback,
getData: ProtectedBranchCreate.getProtectedBranches, getData: ProtectedBranchCreate.getProtectedBranches,
}); });
this.loadPreviousSelection($allowedToMergeDropdown.data('glDropdown'), $allowedToPushDropdown.data('glDropdown'));
} }
// This will run after clicked callback // This will run after clicked callback
...@@ -59,39 +54,10 @@ export default class ProtectedBranchCreate { ...@@ -59,39 +54,10 @@ export default class ProtectedBranchCreate {
$allowedToPushInput.length $allowedToPushInput.length
); );
this.savePreviousSelection($allowedToMergeInput.val(), $allowedToPushInput.val());
this.$form.find('input[type="submit"]').prop('disabled', completedForm); this.$form.find('input[type="submit"]').prop('disabled', completedForm);
} }
static getProtectedBranches(term, callback) { static getProtectedBranches(term, callback) {
callback(gon.open_branches); 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));
}
}
} }
...@@ -289,7 +289,7 @@ export default class SearchAutocomplete { ...@@ -289,7 +289,7 @@ export default class SearchAutocomplete {
} }
// If the dropdown is closed, we'll open it // If the dropdown is closed, we'll open it
if (!this.dropdown.hasClass('open')) { if (!this.dropdown.hasClass('show')) {
this.loadingSuggestions = false; this.loadingSuggestions = false;
this.dropdownToggle.dropdown('toggle'); this.dropdownToggle.dropdown('toggle');
return this.searchInput.removeClass('disabled'); return this.searchInput.removeClass('disabled');
...@@ -424,9 +424,9 @@ export default class SearchAutocomplete { ...@@ -424,9 +424,9 @@ export default class SearchAutocomplete {
} }
disableAutocomplete() { disableAutocomplete() {
if (!this.searchInput.hasClass('disabled') && this.dropdown.hasClass('open')) { if (!this.searchInput.hasClass('disabled') && this.dropdown.hasClass('show')) {
this.searchInput.addClass('disabled'); this.searchInput.addClass('disabled');
this.dropdown.removeClass('open').trigger('hidden.bs.dropdown'); this.dropdown.removeClass('show').trigger('hidden.bs.dropdown');
this.restoreMenu(); this.restoreMenu();
} }
} }
......
...@@ -8,7 +8,6 @@ class HealthController < ActionController::Base ...@@ -8,7 +8,6 @@ class HealthController < ActionController::Base
Gitlab::HealthChecks::Redis::CacheCheck, Gitlab::HealthChecks::Redis::CacheCheck,
Gitlab::HealthChecks::Redis::QueuesCheck, Gitlab::HealthChecks::Redis::QueuesCheck,
Gitlab::HealthChecks::Redis::SharedStateCheck, Gitlab::HealthChecks::Redis::SharedStateCheck,
Gitlab::HealthChecks::FsShardsCheck,
Gitlab::HealthChecks::GitalyCheck Gitlab::HealthChecks::GitalyCheck
].freeze ].freeze
......
...@@ -270,7 +270,7 @@ module ApplicationHelper ...@@ -270,7 +270,7 @@ module ApplicationHelper
{ {
members: members_project_autocomplete_sources_path(object, type: noteable_type, type_id: params[:id]), members: members_project_autocomplete_sources_path(object, type: noteable_type, type_id: params[:id]),
issues: issues_project_autocomplete_sources_path(object), 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]), labels: labels_project_autocomplete_sources_path(object, type: noteable_type, type_id: params[:id]),
milestones: milestones_project_autocomplete_sources_path(object), milestones: milestones_project_autocomplete_sources_path(object),
commands: commands_project_autocomplete_sources_path(object, type: noteable_type, type_id: params[:id]) commands: commands_project_autocomplete_sources_path(object, type: noteable_type, type_id: params[:id])
......
...@@ -6,7 +6,8 @@ class MetricsService ...@@ -6,7 +6,8 @@ class MetricsService
Gitlab::HealthChecks::Redis::RedisCheck, Gitlab::HealthChecks::Redis::RedisCheck,
Gitlab::HealthChecks::Redis::CacheCheck, Gitlab::HealthChecks::Redis::CacheCheck,
Gitlab::HealthChecks::Redis::QueuesCheck, Gitlab::HealthChecks::Redis::QueuesCheck,
Gitlab::HealthChecks::Redis::SharedStateCheck Gitlab::HealthChecks::Redis::SharedStateCheck,
Gitlab::HealthChecks::GitalyCheck
].freeze ].freeze
def prometheus_metrics_text def prometheus_metrics_text
......
# frozen_string_literal: true
class AdminEmailWorker class AdminEmailWorker
include ApplicationWorker include ApplicationWorker
include CronjobQueue include CronjobQueue
......
# frozen_string_literal: true
class ArchiveTraceWorker class ArchiveTraceWorker
include ApplicationWorker include ApplicationWorker
include PipelineBackgroundQueue include PipelineBackgroundQueue
......
# frozen_string_literal: true
class AuthorizedProjectsWorker class AuthorizedProjectsWorker
include ApplicationWorker include ApplicationWorker
prepend WaitableWorker prepend WaitableWorker
......
# frozen_string_literal: true
class BackgroundMigrationWorker class BackgroundMigrationWorker
include ApplicationWorker include ApplicationWorker
......
# frozen_string_literal: true
class BuildCoverageWorker class BuildCoverageWorker
include ApplicationWorker include ApplicationWorker
include PipelineQueue include PipelineQueue
......
# frozen_string_literal: true
class BuildFinishedWorker class BuildFinishedWorker
include ApplicationWorker include ApplicationWorker
include PipelineQueue include PipelineQueue
......
# frozen_string_literal: true
class BuildHooksWorker class BuildHooksWorker
include ApplicationWorker include ApplicationWorker
include PipelineQueue include PipelineQueue
......
# frozen_string_literal: true
class BuildQueueWorker class BuildQueueWorker
include ApplicationWorker include ApplicationWorker
include PipelineQueue include PipelineQueue
......
# frozen_string_literal: true
class BuildSuccessWorker class BuildSuccessWorker
include ApplicationWorker include ApplicationWorker
include PipelineQueue include PipelineQueue
......
# frozen_string_literal: true
class BuildTraceSectionsWorker class BuildTraceSectionsWorker
include ApplicationWorker include ApplicationWorker
include PipelineQueue include PipelineQueue
......
# frozen_string_literal: true
module Ci module Ci
class ArchiveTracesCronWorker class ArchiveTracesCronWorker
include ApplicationWorker include ApplicationWorker
......
# frozen_string_literal: true
module Ci module Ci
class BuildTraceChunkFlushWorker class BuildTraceChunkFlushWorker
include ApplicationWorker include ApplicationWorker
......
# frozen_string_literal: true
class ClusterInstallAppWorker class ClusterInstallAppWorker
include ApplicationWorker include ApplicationWorker
include ClusterQueue include ClusterQueue
......
# frozen_string_literal: true
class ClusterProvisionWorker class ClusterProvisionWorker
include ApplicationWorker include ApplicationWorker
include ClusterQueue include ClusterQueue
......
# frozen_string_literal: true
class ClusterWaitForAppInstallationWorker class ClusterWaitForAppInstallationWorker
include ApplicationWorker include ApplicationWorker
include ClusterQueue include ClusterQueue
......
# frozen_string_literal: true
class ClusterWaitForIngressIpAddressWorker class ClusterWaitForIngressIpAddressWorker
include ApplicationWorker include ApplicationWorker
include ClusterQueue include ClusterQueue
......
# frozen_string_literal: true
Sidekiq::Worker.extend ActiveSupport::Concern Sidekiq::Worker.extend ActiveSupport::Concern
module ApplicationWorker module ApplicationWorker
......
# frozen_string_literal: true
module ClusterApplications module ClusterApplications
extend ActiveSupport::Concern extend ActiveSupport::Concern
......
# frozen_string_literal: true
## ##
# Concern for setting Sidekiq settings for the various Gcp clusters workers. # Concern for setting Sidekiq settings for the various Gcp clusters workers.
# #
......
# frozen_string_literal: true
# Concern that sets various Sidekiq settings for workers executed using a # Concern that sets various Sidekiq settings for workers executed using a
# cronjob. # cronjob.
module CronjobQueue module CronjobQueue
......
# frozen_string_literal: true
# Concern for enabling a few lines of exception backtraces in Sidekiq # Concern for enabling a few lines of exception backtraces in Sidekiq
module ExceptionBacktrace module ExceptionBacktrace
extend ActiveSupport::Concern extend ActiveSupport::Concern
......
# frozen_string_literal: true
module Gitlab module Gitlab
module GithubImport module GithubImport
module Queue module Queue
......
# frozen_string_literal: true
module MailSchedulerQueue module MailSchedulerQueue
extend ActiveSupport::Concern extend ActiveSupport::Concern
......
# frozen_string_literal: true
module NewIssuable module NewIssuable
attr_reader :issuable, :user attr_reader :issuable, :user
......
# frozen_string_literal: true
# Concern for setting Sidekiq settings for the various GitLab ObjectStorage workers. # Concern for setting Sidekiq settings for the various GitLab ObjectStorage workers.
module ObjectStorageQueue module ObjectStorageQueue
extend ActiveSupport::Concern extend ActiveSupport::Concern
......
# frozen_string_literal: true
## ##
# Concern for setting Sidekiq settings for the low priority CI pipeline workers. # Concern for setting Sidekiq settings for the low priority CI pipeline workers.
# #
......
# frozen_string_literal: true
## ##
# Concern for setting Sidekiq settings for the various CI pipeline workers. # Concern for setting Sidekiq settings for the various CI pipeline workers.
# #
......
# frozen_string_literal: true
module ProjectImportOptions module ProjectImportOptions
extend ActiveSupport::Concern extend ActiveSupport::Concern
......
# frozen_string_literal: true
# Used in EE by mirroring # Used in EE by mirroring
module ProjectStartImport module ProjectStartImport
def start(project) def start(project)
......
# frozen_string_literal: true
# Concern for setting Sidekiq settings for the various repository check workers. # Concern for setting Sidekiq settings for the various repository check workers.
module RepositoryCheckQueue module RepositoryCheckQueue
extend ActiveSupport::Concern extend ActiveSupport::Concern
......
# frozen_string_literal: true
module WaitableWorker module WaitableWorker
extend ActiveSupport::Concern extend ActiveSupport::Concern
......
# frozen_string_literal: true
class CreateGpgSignatureWorker class CreateGpgSignatureWorker
include ApplicationWorker include ApplicationWorker
......
# frozen_string_literal: true
class CreateNoteDiffFileWorker class CreateNoteDiffFileWorker
include ApplicationWorker include ApplicationWorker
......
# frozen_string_literal: true
class CreatePipelineWorker class CreatePipelineWorker
include ApplicationWorker include ApplicationWorker
include PipelineQueue include PipelineQueue
......
# frozen_string_literal: true
class DeleteMergedBranchesWorker class DeleteMergedBranchesWorker
include ApplicationWorker include ApplicationWorker
......
# frozen_string_literal: true
class DeleteUserWorker class DeleteUserWorker
include ApplicationWorker include ApplicationWorker
......
# frozen_string_literal: true
class EmailReceiverWorker class EmailReceiverWorker
include ApplicationWorker include ApplicationWorker
......
# frozen_string_literal: true
class EmailsOnPushWorker class EmailsOnPushWorker
include ApplicationWorker include ApplicationWorker
......
# frozen_string_literal: true
class ExpireBuildArtifactsWorker class ExpireBuildArtifactsWorker
include ApplicationWorker include ApplicationWorker
include CronjobQueue include CronjobQueue
......
# frozen_string_literal: true
class ExpireBuildInstanceArtifactsWorker class ExpireBuildInstanceArtifactsWorker
include ApplicationWorker include ApplicationWorker
......
# frozen_string_literal: true
class ExpireJobCacheWorker class ExpireJobCacheWorker
include ApplicationWorker include ApplicationWorker
include PipelineQueue include PipelineQueue
......
# frozen_string_literal: true
class ExpirePipelineCacheWorker class ExpirePipelineCacheWorker
include ApplicationWorker include ApplicationWorker
include PipelineQueue include PipelineQueue
......
# frozen_string_literal: true
class GitGarbageCollectWorker class GitGarbageCollectWorker
include ApplicationWorker include ApplicationWorker
......
# frozen_string_literal: true
class GitlabShellWorker class GitlabShellWorker
include ApplicationWorker include ApplicationWorker
include Gitlab::ShellAdapter include Gitlab::ShellAdapter
......
# frozen_string_literal: true
class GitlabUsagePingWorker class GitlabUsagePingWorker
LEASE_TIMEOUT = 86400 LEASE_TIMEOUT = 86400
......
# frozen_string_literal: true
class GroupDestroyWorker class GroupDestroyWorker
include ApplicationWorker include ApplicationWorker
include ExceptionBacktrace include ExceptionBacktrace
......
# frozen_string_literal: true
class ImportExportProjectCleanupWorker class ImportExportProjectCleanupWorker
include ApplicationWorker include ApplicationWorker
include CronjobQueue include CronjobQueue
......
# frozen_string_literal: true
class InvalidGpgSignatureUpdateWorker class InvalidGpgSignatureUpdateWorker
include ApplicationWorker include ApplicationWorker
......
# frozen_string_literal: true
require 'json' require 'json'
require 'socket' require 'socket'
...@@ -69,8 +71,8 @@ class IrkerWorker ...@@ -69,8 +71,8 @@ class IrkerWorker
newbranch = "#{Gitlab.config.gitlab.url}/#{repo_path}/branches" newbranch = "#{Gitlab.config.gitlab.url}/#{repo_path}/branches"
newbranch = "\x0302\x1f#{newbranch}\x0f" if @colors newbranch = "\x0302\x1f#{newbranch}\x0f" if @colors
privmsg = "[#{repo_name}] #{committer} has created a new branch " privmsg = "[#{repo_name}] #{committer} has created a new branch " \
privmsg += "#{branch}: #{newbranch}" "#{branch}: #{newbranch}"
sendtoirker privmsg sendtoirker privmsg
end end
...@@ -112,9 +114,7 @@ class IrkerWorker ...@@ -112,9 +114,7 @@ class IrkerWorker
url = compare_url data, project.full_path url = compare_url data, project.full_path
commits = colorize_commits data['total_commits_count'] commits = colorize_commits data['total_commits_count']
new_commits = 'new commit' new_commits = 'new commit'.pluralize(data['total_commits_count'])
new_commits += 's' if data['total_commits_count'] > 1
sendtoirker "[#{repo}] #{committer} pushed #{commits} #{new_commits} " \ sendtoirker "[#{repo}] #{committer} pushed #{commits} #{new_commits} " \
"to #{branch}: #{url}" "to #{branch}: #{url}"
end end
...@@ -122,8 +122,8 @@ class IrkerWorker ...@@ -122,8 +122,8 @@ class IrkerWorker
def compare_url(data, repo_path) def compare_url(data, repo_path)
sha1 = Commit.truncate_sha(data['before']) sha1 = Commit.truncate_sha(data['before'])
sha2 = Commit.truncate_sha(data['after']) sha2 = Commit.truncate_sha(data['after'])
compare_url = "#{Gitlab.config.gitlab.url}/#{repo_path}/compare" compare_url = "#{Gitlab.config.gitlab.url}/#{repo_path}/compare" \
compare_url += "/#{sha1}...#{sha2}" "/#{sha1}...#{sha2}"
colorize_url compare_url colorize_url compare_url
end end
...@@ -144,8 +144,7 @@ class IrkerWorker ...@@ -144,8 +144,7 @@ class IrkerWorker
def files_count(commit) def files_count(commit)
diff_size = commit.raw_deltas.size diff_size = commit.raw_deltas.size
files = "#{diff_size} file" files = "#{diff_size} file".pluralize(diff_size)
files += 's' if diff_size > 1
files files
end end
......
# frozen_string_literal: true
class IssueDueSchedulerWorker class IssueDueSchedulerWorker
include ApplicationWorker include ApplicationWorker
include CronjobQueue include CronjobQueue
......
# frozen_string_literal: true
module MailScheduler module MailScheduler
class IssueDueWorker class IssueDueWorker
include ApplicationWorker include ApplicationWorker
......
# frozen_string_literal: true
require 'active_job/arguments' require 'active_job/arguments'
module MailScheduler module MailScheduler
......
# frozen_string_literal: true
class MergeWorker class MergeWorker
include ApplicationWorker include ApplicationWorker
......
# frozen_string_literal: true
# Worker to destroy projects that do not have a namespace # Worker to destroy projects that do not have a namespace
# #
# It destroys everything it can without having the info about the namespace it # It destroys everything it can without having the info about the namespace it
......
# frozen_string_literal: true
class NewIssueWorker class NewIssueWorker
include ApplicationWorker include ApplicationWorker
include NewIssuable include NewIssuable
......
# frozen_string_literal: true
class NewMergeRequestWorker class NewMergeRequestWorker
include ApplicationWorker include ApplicationWorker
include NewIssuable include NewIssuable
......
# frozen_string_literal: true
class NewNoteWorker class NewNoteWorker
include ApplicationWorker include ApplicationWorker
......
# frozen_string_literal: true
module ObjectStorage module ObjectStorage
class BackgroundMoveWorker class BackgroundMoveWorker
include ApplicationWorker include ApplicationWorker
......
# frozen_string_literal: true
# @Deprecated - remove once the `object_storage_upload` queue is empty # @Deprecated - remove once the `object_storage_upload` queue is empty
# The queue has been renamed `object_storage:object_storage_background_upload` # The queue has been renamed `object_storage:object_storage_background_upload`
# #
......
# frozen_string_literal: true
class PagesDomainVerificationCronWorker class PagesDomainVerificationCronWorker
include ApplicationWorker include ApplicationWorker
include CronjobQueue include CronjobQueue
......
# frozen_string_literal: true
class PagesDomainVerificationWorker class PagesDomainVerificationWorker
include ApplicationWorker include ApplicationWorker
......
# frozen_string_literal: true
class PagesWorker class PagesWorker
include ApplicationWorker include ApplicationWorker
......
# frozen_string_literal: true
class PipelineHooksWorker class PipelineHooksWorker
include ApplicationWorker include ApplicationWorker
include PipelineQueue include PipelineQueue
......
# frozen_string_literal: true
class PipelineMetricsWorker class PipelineMetricsWorker
include ApplicationWorker include ApplicationWorker
include PipelineQueue include PipelineQueue
......
# frozen_string_literal: true
class PipelineNotificationWorker class PipelineNotificationWorker
include ApplicationWorker include ApplicationWorker
include PipelineQueue include PipelineQueue
......
# frozen_string_literal: true
class PipelineProcessWorker class PipelineProcessWorker
include ApplicationWorker include ApplicationWorker
include PipelineQueue include PipelineQueue
......
# frozen_string_literal: true
class PipelineScheduleWorker class PipelineScheduleWorker
include ApplicationWorker include ApplicationWorker
include CronjobQueue include CronjobQueue
......
# frozen_string_literal: true
class PipelineSuccessWorker class PipelineSuccessWorker
include ApplicationWorker include ApplicationWorker
include PipelineQueue include PipelineQueue
......
# frozen_string_literal: true
class PipelineUpdateWorker class PipelineUpdateWorker
include ApplicationWorker include ApplicationWorker
include PipelineQueue include PipelineQueue
......
# frozen_string_literal: true
class PluginWorker class PluginWorker
include ApplicationWorker include ApplicationWorker
......
# frozen_string_literal: true
class PostReceive class PostReceive
include ApplicationWorker include ApplicationWorker
......
# frozen_string_literal: true
# Worker for processing individiual commit messages pushed to a repository. # 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 # Jobs for this worker are scheduled for every commit that is being pushed. As a
......
# frozen_string_literal: true
# Worker for updating any project specific caches. # Worker for updating any project specific caches.
class ProjectCacheWorker class ProjectCacheWorker
include ApplicationWorker include ApplicationWorker
......
# frozen_string_literal: true
class ProjectDestroyWorker class ProjectDestroyWorker
include ApplicationWorker include ApplicationWorker
include ExceptionBacktrace include ExceptionBacktrace
......
# frozen_string_literal: true
class ProjectExportWorker class ProjectExportWorker
include ApplicationWorker include ApplicationWorker
include ExceptionBacktrace include ExceptionBacktrace
......
# frozen_string_literal: true
class ProjectMigrateHashedStorageWorker class ProjectMigrateHashedStorageWorker
include ApplicationWorker include ApplicationWorker
......
# frozen_string_literal: true
class ProjectServiceWorker class ProjectServiceWorker
include ApplicationWorker include ApplicationWorker
......
# frozen_string_literal: true
# Worker for updating any project specific caches. # Worker for updating any project specific caches.
class PropagateServiceTemplateWorker class PropagateServiceTemplateWorker
include ApplicationWorker include ApplicationWorker
......
# frozen_string_literal: true
class PruneOldEventsWorker class PruneOldEventsWorker
include ApplicationWorker include ApplicationWorker
include CronjobQueue include CronjobQueue
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment