Commit ac92554d authored by Kamil Trzcinski's avatar Kamil Trzcinski

Merge remote-tracking branch 'origin/master' into backport-ee-changes-for-build-minutes

parents b7f4553e 8c9a06c3
...@@ -35,7 +35,6 @@ stages: ...@@ -35,7 +35,6 @@ stages:
.dedicated-runner: &dedicated-runner .dedicated-runner: &dedicated-runner
tags: tags:
- gitlab-org - gitlab-org
- 2gb
.knapsack-state: &knapsack-state .knapsack-state: &knapsack-state
services: [] services: []
......
...@@ -2,6 +2,136 @@ ...@@ -2,6 +2,136 @@
documentation](doc/development/changelog.md) for instructions on adding your own documentation](doc/development/changelog.md) for instructions on adding your own
entry. entry.
## 8.16.0 (2017-02-22)
- Add LDAP Rake task to rename a provider. !2181
- Validate label's title length. !5767 (Tomáš Kukrál)
- Allow to add deploy keys with write-access. !5807 (Ali Ibrahim)
- Allow to use + symbol in filenames. !6644 (blackst0ne)
- Search bar redesign first iteration. !7345
- Fix date inconsistency on due date picker. !7422 (Giuliano Varriale)
- Add email confirmation field to registration form. !7432
- Updated project visibility settings UX. !7645
- Go to a project order. !7737 (Jacopo Beschi @jacopo-beschi)
- Support slash comand `/merge` for merging merge requests. !7746 (Jarka Kadlecova)
- Add more storage statistics. !7754 (Markus Koller)
- Add support for PlantUML diagrams in AsciiDoc documents. !7810 (Horacio Sanson)
- Remove extra orphaned rows when removing stray namespaces. !7841
- Added lighter count badge background-color for on white backgrounds. !7873
- Fixes issue boards list colored top border visual glitch. !7898 (Pier Paolo Ramon)
- change 'gray' color theme name to 'black' to match the actual color. !7908 (BM5k)
- Remove trailing whitespace when generating changelog entry. !7948
- Remove checking branches state in issue new branch button. !8023
- Log LDAP blocking/unblocking events to application log. !8042 (Markus Koller)
- ensure permalinks scroll to correct position on multiple clicks. !8046
- Allow to use ENV variables in redis config. !8073 (Semyon Pupkov)
- fix button layout issue on branches page. !8074
- Reduce DB-load for build-queues by storing last_update in Redis. !8084
- Record and show last used date of SSH Keys. !8113 (Vincent Wong)
- Resolves overflow in compare branch and tags dropdown. !8118
- Replace wording for slash command confirmation message. !8123
- remove build_user. !8162 (Arsenev Vladislav)
- Prevent empty pagination when list is not empty. !8172
- Make successful pipeline emails off for watchers. !8176
- Improve copy in Issue Tracker empty state. !8202
- Adds CSS class to status icon on MR widget to prevent non-colored icon. !8219
- Improve visibility of "Resolve conflicts" and "Merge locally" actions. !8229
- Add Gitaly to the architecture documentation. !8264 (Pablo Carranza <pablo@gitlab.com>)
- Sort numbers in build names more intelligently. !8277
- Show nested groups tab on group page. !8308
- Rename users with namespace ending with .git. !8309
- Rename filename to file path in tooltip of file header in merge request diff. !8314
- About GitLab link in sidebar that links to help page. !8316
- Merged the 'Groups' and 'Projects' tabs when viewing user profiles. !8323 (James Gregory)
- re-enable change username button after failure. !8332
- Darkened hr border color in descriptions because of update of bootstrap. !8333
- display merge request discussion tab for empty branches. !8347
- Fix double spaced CI log. !8349 (Jared Deckard <jared.deckard@gmail.com>)
- Refactored note edit form to improve frontend performance on MR and Issues pages, especially pages with has a lot of discussions in it. !8356
- Make CTRL+Enter submits a new merge request. !8360 (Saad Shahd)
- Fixes too short input for placeholder message in commit listing page. !8367
- Fix typo: seach to search. !8370
- Adds label to Environments "Date Created". !8376 (Saad Shahd)
- Convert project setting text into protected branch path link. !8377 (Ken Ding)
- Precompile all JavaScript fixtures. !8384
- Use original casing for build action text. !8387
- Scroll to bottom on build completion if autoscroll was active. !8391
- Properly handle failed reCAPTCHA on user registration. !8403
- Changed alerts to be responsive, centered text on smaller viewports. !8424 (Connor Smallman)
- Pass Gitaly resource path to gitlab-workhorse if Gitaly is enabled. !8440
- Fixes and Improves CSS and HTML problems in mini pipeline graph and builds dropdown. !8443
- Don't instrument 405 Grape calls. !8445
- Change CI template linter textarea with Ace Editor. !8452 (Didem Acet)
- Removes unneeded `window` declaration in environments related code. !8456
- API: fix query response for `/projects/:id/issues?milestone="No%20Milestone"`. !8457 (Panagiotis Atmatzidis, David Eisner)
- Fix broken url on group avatar. !8464 (hogewest)
- Fixes buttons not being accessible via the keyboard when creating new group. !8469
- Restore backup correctly when "BACKUP" environment variable is passed. !8477
- Add new endpoints for Time Tracking. !8483
- Fix Compare page throws 500 error when any branch/reference is not selected. !8492 (Martin Cabrera)
- Treat environments matching `production/*` as Production. !8500
- Hide build artifacts keep button if operation is not allowed. !8501
- Update the gitlab-markup gem to the version 1.5.1. !8509
- Remove Lock Icon on Protected Tag. !8513 (Sergey Nikitin)
- Use cached values to compute total issues count in milestone index pages. !8518
- Speed up dashboard milestone index by scoping IssuesFinder to user authorized projects. !8524
- Copy <some text> to clipboard. !8535
- Check for env[Grape::Env::GRAPE_ROUTING_ARGS] instead of endpoint.route. !8544
- Fixes builds dropdown making request when clicked to be closed. !8545
- Fixes pipeline status cell is too wide by adding missing classes in table head cells. !8549
- Mutate the attribute instead of issuing a write operation to the DB in `ProjectFeaturesCompatibility` concern. !8552
- Fix links to commits pages on pipelines list page. !8558
- Ensure updating project settings shows a flash message on success. !8579 (Sandish Chen)
- Fixes big pipeline and small pipeline width problems and tooltips text being outside the tooltip. !8593
- Autoresize markdown preview. !8607 (Didem Acet)
- Link external build badge to its target URL. !8611
- Adjust ProjectStatistic#repository_size with values saved as MB. !8616
- Correct User-agent placement in robots.txt. !8623 (Eric Sabelhaus)
- Record used SSH keys only once per day. !8655
- Do not generate pipeline branch/tag path if not present. !8658
- Fix Merge When Pipeline Succeeds immediate merge bug. !8685
- Fix blame 500 error on invalid path. !25761 (Jeff Stubler)
- Added animations to issue boards interactions.
- Check if user can read project before being assigned to issue.
- Show 'too many changes' message for created merge requests when they are too large.
- Fix redirect after update file when user has forked project.
- Parse JIRA issue references even if Issue Tracker is disabled.
- Made download artifacts button accessible via keyboard by changing it from an anchor tag to an actual button. (Ryan Harris)
- Make play button on Pipelines page accessible via keyboard. (Ryan Harris)
- Decreases font-size on login page.
- Fixed merge request tabs dont move when opening collapsed sidebar.
- Display project avatars on Admin Area and Projects pages for mobile views. (Ryan Harris)
- Fix participants margins to fit on one line.
- 26352 Change Profile settings to User / Settings.
- Fix Commits API to accept a Project path upon POST.
- Expire related caches after changing HEAD. (Minqi Pan)
- Add various hover animations throughout the application.
- Re-order update steps in the 8.14 -> 8.15 upgrade guide.
- Move award emoji's out of the discussion tab for merge requests.
- Synchronize all project authorization refreshing work to prevent race conditions.
- Remove the project_authorizations.id column.
- Combined the settings options project members and groups into a single one called members.
- Change earlier to task_status_short to avoid titlebar line wraps.
- 25701 standardize text colors.
- Handle HTTP errors in environment list.
- Re-add Google Cloud Storage as a backup strategy.
- Change status colors of runners to better defaults.
- Added number_with_delimiter to counter on milestone panels. (Ryan Harris)
- Query external CI statuses in the background.
- Allow group and project paths when transferring projects via the API.
- Don't validate environment urls on .gitlab-ci.yml.
- Fix a Grape deprecation, use `#request_method` instead of `#route_method`.
- Fill missing authorized projects rows.
- Allow API query to find projects with dots in their name. (Bruno Melli)
- Fix import/export wrong user mapping.
- Removed bottom padding from merge manually from CLI because of repositioning award emoji's.
- Fix project queued for deletion re-creation tooltip.
- Fix search group/project filtering to show results.
- Fix 500 error when POSTing to Users API with optional confirm param.
- 26504 Fix styling of MR jump to discussion button.
- Add margin to markdown math blocks.
- Add hover state to MR comment reply button.
## 8.15.4 (2017-01-09) ## 8.15.4 (2017-01-09)
- Make successful pipeline emails off for watchers. !8176 - Make successful pipeline emails off for watchers. !8176
......
...@@ -21,7 +21,7 @@ gem 'rugged', '~> 0.24.0' ...@@ -21,7 +21,7 @@ gem 'rugged', '~> 0.24.0'
# Authentication libraries # Authentication libraries
gem 'devise', '~> 4.2' gem 'devise', '~> 4.2'
gem 'doorkeeper', '~> 4.2.0' gem 'doorkeeper', '~> 4.2.0'
gem 'omniauth', '~> 1.3.1' gem 'omniauth', '~> 1.3.2'
gem 'omniauth-auth0', '~> 1.4.1' gem 'omniauth-auth0', '~> 1.4.1'
gem 'omniauth-azure-oauth2', '~> 0.0.6' gem 'omniauth-azure-oauth2', '~> 0.0.6'
gem 'omniauth-cas3', '~> 1.1.2' gem 'omniauth-cas3', '~> 1.1.2'
......
...@@ -449,7 +449,7 @@ GEM ...@@ -449,7 +449,7 @@ GEM
octokit (4.6.2) octokit (4.6.2)
sawyer (~> 0.8.0, >= 0.5.3) sawyer (~> 0.8.0, >= 0.5.3)
oj (2.17.4) oj (2.17.4)
omniauth (1.3.1) omniauth (1.3.2)
hashie (>= 1.2, < 4) hashie (>= 1.2, < 4)
rack (>= 1.0, < 3) rack (>= 1.0, < 3)
omniauth-auth0 (1.4.1) omniauth-auth0 (1.4.1)
...@@ -925,7 +925,7 @@ DEPENDENCIES ...@@ -925,7 +925,7 @@ DEPENDENCIES
oauth2 (~> 1.2.0) oauth2 (~> 1.2.0)
octokit (~> 4.6.2) octokit (~> 4.6.2)
oj (~> 2.17.4) oj (~> 2.17.4)
omniauth (~> 1.3.1) omniauth (~> 1.3.2)
omniauth-auth0 (~> 1.4.1) omniauth-auth0 (~> 1.4.1)
omniauth-authentiq (~> 0.2.0) omniauth-authentiq (~> 0.2.0)
omniauth-azure-oauth2 (~> 0.0.6) omniauth-azure-oauth2 (~> 0.0.6)
......
...@@ -113,4 +113,4 @@ Please see [Getting help for GitLab](https://about.gitlab.com/getting-help/) on ...@@ -113,4 +113,4 @@ Please see [Getting help for GitLab](https://about.gitlab.com/getting-help/) on
## Is it awesome? ## Is it awesome?
Thanks for [asking this question](https://twitter.com/supersloth/status/489462789384056832) Joshua. Thanks for [asking this question](https://twitter.com/supersloth/status/489462789384056832) Joshua.
[These people](https://twitter.com/gitlab/favorites) seem to like it. [These people](https://twitter.com/gitlab/likes) seem to like it.
8.16.0-pre 8.17.0-pre
...@@ -62,6 +62,7 @@ var DropDown = function(list) { ...@@ -62,6 +62,7 @@ var DropDown = function(list) {
this.list = list; this.list = list;
this.items = []; this.items = [];
this.getItems(); this.getItems();
this.initTemplateString();
this.addEvents(); this.addEvents();
this.initialState = list.innerHTML; this.initialState = list.innerHTML;
}; };
...@@ -72,6 +73,17 @@ Object.assign(DropDown.prototype, { ...@@ -72,6 +73,17 @@ Object.assign(DropDown.prototype, {
return this.items; return this.items;
}, },
initTemplateString: function() {
var items = this.items || this.getItems();
var templateString = '';
if(items.length > 0) {
templateString = items[items.length - 1].outerHTML;
}
this.templateString = templateString;
return this.templateString;
},
clickEvent: function(e) { clickEvent: function(e) {
// climb up the tree to find the LI // climb up the tree to find the LI
var selected = utils.closest(e.target, 'LI'); var selected = utils.closest(e.target, 'LI');
...@@ -111,30 +123,21 @@ Object.assign(DropDown.prototype, { ...@@ -111,30 +123,21 @@ Object.assign(DropDown.prototype, {
addData: function(data) { addData: function(data) {
this.data = (this.data || []).concat(data); this.data = (this.data || []).concat(data);
this.render(data); this.render(this.data);
}, },
// call render manually on data; // call render manually on data;
render: function(data){ render: function(data){
// debugger // debugger
// empty the list first // empty the list first
var sampleItem; var templateString = this.templateString;
var newChildren = []; var newChildren = [];
var toAppend; var toAppend;
for(var i = 0; i < this.items.length; i++) { newChildren = (data ||[]).map(function(dat){
var item = this.items[i]; var html = utils.t(templateString, dat);
sampleItem = item;
if(item.parentNode && item.parentNode.dataset.hasOwnProperty('dynamic')) {
item.parentNode.removeChild(item);
}
}
newChildren = this.data.map(function(dat){
var html = utils.t(sampleItem.outerHTML, dat);
var template = document.createElement('div'); var template = document.createElement('div');
template.innerHTML = html; template.innerHTML = html;
// console.log(template.content)
// Help set the image src template // Help set the image src template
var imageTags = template.querySelectorAll('img[data-src]'); var imageTags = template.querySelectorAll('img[data-src]');
...@@ -173,10 +176,7 @@ Object.assign(DropDown.prototype, { ...@@ -173,10 +176,7 @@ Object.assign(DropDown.prototype, {
}, },
destroy: function() { destroy: function() {
if (!this.hidden) {
this.hide(); this.hide();
}
this.list.removeEventListener('click', this.clickWrapper); this.list.removeEventListener('click', this.clickWrapper);
} }
}); });
...@@ -278,7 +278,7 @@ require('./window')(function(w){ ...@@ -278,7 +278,7 @@ require('./window')(function(w){
self.hooks[i].list.hide(); self.hooks[i].list.hide();
} }
}.bind(this); }.bind(this);
w.addEventListener('click', this.windowClickedWrapper); document.addEventListener('click', this.windowClickedWrapper);
}, },
removeEvents: function(){ removeEvents: function(){
...@@ -462,6 +462,8 @@ Object.assign(HookInput.prototype, { ...@@ -462,6 +462,8 @@ Object.assign(HookInput.prototype, {
var self = this; var self = this;
this.mousedown = function mousedown(e) { this.mousedown = function mousedown(e) {
if(self.hasRemovedEvents) return;
var mouseEvent = new CustomEvent('mousedown.dl', { var mouseEvent = new CustomEvent('mousedown.dl', {
detail: { detail: {
hook: self, hook: self,
...@@ -474,6 +476,8 @@ Object.assign(HookInput.prototype, { ...@@ -474,6 +476,8 @@ Object.assign(HookInput.prototype, {
} }
this.input = function input(e) { this.input = function input(e) {
if(self.hasRemovedEvents) return;
var inputEvent = new CustomEvent('input.dl', { var inputEvent = new CustomEvent('input.dl', {
detail: { detail: {
hook: self, hook: self,
...@@ -487,10 +491,14 @@ Object.assign(HookInput.prototype, { ...@@ -487,10 +491,14 @@ Object.assign(HookInput.prototype, {
} }
this.keyup = function keyup(e) { this.keyup = function keyup(e) {
if(self.hasRemovedEvents) return;
keyEvent(e, 'keyup.dl'); keyEvent(e, 'keyup.dl');
} }
this.keydown = function keydown(e) { this.keydown = function keydown(e) {
if(self.hasRemovedEvents) return;
keyEvent(e, 'keydown.dl'); keyEvent(e, 'keydown.dl');
} }
...@@ -520,7 +528,8 @@ Object.assign(HookInput.prototype, { ...@@ -520,7 +528,8 @@ Object.assign(HookInput.prototype, {
this.trigger.addEventListener('keydown', this.keydown); this.trigger.addEventListener('keydown', this.keydown);
}, },
removeEvents: function(){ removeEvents: function() {
this.hasRemovedEvents = true;
this.trigger.removeEventListener('mousedown', this.mousedown); this.trigger.removeEventListener('mousedown', this.mousedown);
this.trigger.removeEventListener('input', this.input); this.trigger.removeEventListener('input', this.input);
this.trigger.removeEventListener('keyup', this.keyup); this.trigger.removeEventListener('keyup', this.keyup);
...@@ -668,14 +677,14 @@ var camelize = function(str) { ...@@ -668,14 +677,14 @@ var camelize = function(str) {
}; };
var closest = function(thisTag, stopTag) { var closest = function(thisTag, stopTag) {
while(thisTag.tagName !== stopTag && thisTag.tagName !== 'HTML'){ while(thisTag && thisTag.tagName !== stopTag && thisTag.tagName !== 'HTML'){
thisTag = thisTag.parentNode; thisTag = thisTag.parentNode;
} }
return thisTag; return thisTag;
}; };
var isDropDownParts = function(target) { var isDropDownParts = function(target) {
if(target.tagName === 'HTML') { return false; } if(!target || target.tagName === 'HTML') { return false; }
return ( return (
target.hasAttribute(DATA_TRIGGER) || target.hasAttribute(DATA_TRIGGER) ||
target.hasAttribute(DATA_DROPDOWN) target.hasAttribute(DATA_DROPDOWN)
......
/* global CustomEvent */
/* eslint-disable no-global-assign */
// Custom event support for IE
CustomEvent = function CustomEvent(event, parameters) {
const params = parameters || { bubbles: false, cancelable: false, detail: undefined };
const evt = document.createEvent('CustomEvent');
evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail);
return evt;
};
CustomEvent.prototype = window.Event.prototype;
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
this.config = { this.config = {
droplabFilter: { droplabFilter: {
template: 'hint', template: 'hint',
filterFunction: gl.DropdownUtils.filterHint, filterFunction: gl.DropdownUtils.filterHint.bind(null, input),
}, },
}; };
} }
...@@ -20,6 +20,9 @@ ...@@ -20,6 +20,9 @@
if (selected.tagName === 'LI') { if (selected.tagName === 'LI') {
if (selected.hasAttribute('data-value')) { if (selected.hasAttribute('data-value')) {
this.dismissDropdown(); this.dismissDropdown();
} else if (selected.getAttribute('data-action') === 'submit') {
this.dismissDropdown();
this.dispatchFormSubmitEvent();
} else { } else {
const token = selected.querySelector('.js-filter-hint').innerText.trim(); const token = selected.querySelector('.js-filter-hint').innerText.trim();
const tag = selected.querySelector('.js-filter-tag').innerText.trim(); const tag = selected.querySelector('.js-filter-tag').innerText.trim();
......
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
loadingTemplate: this.loadingTemplate, loadingTemplate: this.loadingTemplate,
}, },
droplabFilter: { droplabFilter: {
filterFunction: gl.DropdownUtils.filterWithSymbol.bind(null, this.symbol), filterFunction: gl.DropdownUtils.filterWithSymbol.bind(null, this.symbol, input),
}, },
}; };
} }
......
...@@ -37,7 +37,7 @@ ...@@ -37,7 +37,7 @@
} }
getSearchInput() { getSearchInput() {
const query = this.input.value.trim(); const query = gl.DropdownUtils.getSearchInput(this.input);
const { lastToken } = gl.FilteredSearchTokenizer.processTokens(query); const { lastToken } = gl.FilteredSearchTokenizer.processTokens(query);
return lastToken.value || ''; return lastToken.value || '';
......
...@@ -20,17 +20,15 @@ ...@@ -20,17 +20,15 @@
return escapedText; return escapedText;
} }
static filterWithSymbol(filterSymbol, item, query) { static filterWithSymbol(filterSymbol, input, item) {
const updatedItem = item; const updatedItem = item;
const query = gl.DropdownUtils.getSearchInput(input);
const { lastToken, searchToken } = gl.FilteredSearchTokenizer.processTokens(query); const { lastToken, searchToken } = gl.FilteredSearchTokenizer.processTokens(query);
if (lastToken !== searchToken) { if (lastToken !== searchToken) {
const title = updatedItem.title.toLowerCase(); const title = updatedItem.title.toLowerCase();
let value = lastToken.value.toLowerCase(); let value = lastToken.value.toLowerCase();
value = value.replace(/"(.*?)"/g, str => str.slice(1).slice(0, -1));
if ((value[0] === '"' || value[0] === '\'') && title.indexOf(' ') !== -1) {
value = value.slice(1);
}
// Eg. filterSymbol = ~ for labels // Eg. filterSymbol = ~ for labels
const matchWithoutSymbol = lastToken.symbol === filterSymbol && title.indexOf(value) !== -1; const matchWithoutSymbol = lastToken.symbol === filterSymbol && title.indexOf(value) !== -1;
...@@ -44,8 +42,9 @@ ...@@ -44,8 +42,9 @@
return updatedItem; return updatedItem;
} }
static filterHint(item, query) { static filterHint(input, item) {
const updatedItem = item; const updatedItem = item;
const query = gl.DropdownUtils.getSearchInput(input);
let { lastToken } = gl.FilteredSearchTokenizer.processTokens(query); let { lastToken } = gl.FilteredSearchTokenizer.processTokens(query);
lastToken = lastToken.key || lastToken || ''; lastToken = lastToken.key || lastToken || '';
...@@ -72,6 +71,48 @@ ...@@ -72,6 +71,48 @@
// Return boolean based on whether it was set // Return boolean based on whether it was set
return dataValue !== null; return dataValue !== null;
} }
static getSearchInput(filteredSearchInput) {
const inputValue = filteredSearchInput.value;
const { right } = gl.DropdownUtils.getInputSelectionPosition(filteredSearchInput);
return inputValue.slice(0, right);
}
static getInputSelectionPosition(input) {
const selectionStart = input.selectionStart;
let inputValue = input.value;
// Replace all spaces inside quote marks with underscores
// This helps with matching the beginning & end of a token:key
inputValue = inputValue.replace(/"(.*?)"/g, str => str.replace(/\s/g, '_'));
// Get the right position for the word selected
// Regex matches first space
let right = inputValue.slice(selectionStart).search(/\s/);
if (right >= 0) {
right += selectionStart;
} else if (right < 0) {
right = inputValue.length;
}
// Get the left position for the word selected
// Regex matches last non-whitespace character
let left = inputValue.slice(0, right).search(/\S+$/);
if (selectionStart === 0) {
left = 0;
} else if (selectionStart === inputValue.length && left < 0) {
left = inputValue.length;
} else if (left < 0) {
left = selectionStart;
}
return {
left,
right,
};
}
} }
window.gl = window.gl || {}; window.gl = window.gl || {};
......
...@@ -39,6 +39,7 @@ ...@@ -39,6 +39,7 @@
} }
this.dismissDropdown(); this.dismissDropdown();
this.dispatchInputEvent();
} }
} }
...@@ -78,7 +79,16 @@ ...@@ -78,7 +79,16 @@
dispatchInputEvent() { dispatchInputEvent() {
// Propogate input change to FilteredSearchDropdownManager // Propogate input change to FilteredSearchDropdownManager
// so that it can determine which dropdowns to open // so that it can determine which dropdowns to open
this.input.dispatchEvent(new Event('input')); this.input.dispatchEvent(new CustomEvent('input', {
bubbles: true,
cancelable: true,
}));
}
dispatchFormSubmitEvent() {
// dispatchEvent() is necessary as form.submit() does not
// trigger event handlers
this.input.form.dispatchEvent(new Event('submit'));
} }
hideDropdown() { hideDropdown() {
......
...@@ -57,28 +57,33 @@ ...@@ -57,28 +57,33 @@
static addWordToInput(tokenName, tokenValue = '') { static addWordToInput(tokenName, tokenValue = '') {
const input = document.querySelector('.filtered-search'); const input = document.querySelector('.filtered-search');
const inputValue = input.value;
const word = `${tokenName}:${tokenValue}`; const word = `${tokenName}:${tokenValue}`;
const { lastToken, searchToken } = gl.FilteredSearchTokenizer.processTokens(input.value); // Get the string to replace
const lastSearchToken = searchToken.split(' ').last(); let newCaretPosition = input.selectionStart;
const lastInputCharacter = input.value[input.value.length - 1]; const { left, right } = gl.DropdownUtils.getInputSelectionPosition(input);
const lastInputTrimmedCharacter = input.value.trim()[input.value.trim().length - 1];
// Remove the typed tokenName input.value = `${inputValue.substr(0, left)}${word}${inputValue.substr(right)}`;
if (word.indexOf(lastSearchToken) === 0 && searchToken !== '') {
// Remove spaces after the colon // If we have added a tokenValue at the end of the input,
if (lastInputCharacter === ' ' && lastInputTrimmedCharacter === ':') { // add a space and set selection to the end
input.value = input.value.trim(); if (right >= inputValue.length && tokenValue !== '') {
input.value += ' ';
newCaretPosition = input.value.length;
} }
input.value = input.value.slice(0, -1 * lastSearchToken.length); gl.FilteredSearchDropdownManager.updateInputCaretPosition(newCaretPosition, input);
} else if (lastInputCharacter !== ' ' || (lastToken && lastToken.value[lastToken.value.length - 1] === ' ')) {
// Remove the existing tokenValue
const lastTokenString = `${lastToken.key}:${lastToken.symbol}${lastToken.value}`;
input.value = input.value.slice(0, -1 * lastTokenString.length);
} }
input.value += word; static updateInputCaretPosition(selectionStart, input) {
// Reset the position
// Sometimes can end up at end of input
input.setSelectionRange(selectionStart, selectionStart);
const { right } = gl.DropdownUtils.getInputSelectionPosition(input);
input.setSelectionRange(right, right);
} }
updateCurrentDropdownOffset() { updateCurrentDropdownOffset() {
...@@ -90,9 +95,18 @@ ...@@ -90,9 +95,18 @@
this.font = window.getComputedStyle(this.filteredSearchInput).font; this.font = window.getComputedStyle(this.filteredSearchInput).font;
} }
const input = this.filteredSearchInput;
const inputText = input.value.slice(0, input.selectionStart);
const filterIconPadding = 27; const filterIconPadding = 27;
const offset = gl.text let offset = gl.text.getTextWidth(inputText, this.font) + filterIconPadding;
.getTextWidth(this.filteredSearchInput.value, this.font) + filterIconPadding;
const currentDropdownWidth = this.mapping[key].element.clientWidth === 0 ? 200 :
this.mapping[key].element.clientWidth;
const offsetMaxWidth = this.filteredSearchInput.clientWidth - currentDropdownWidth;
if (offsetMaxWidth < offset) {
offset = offsetMaxWidth;
}
this.mapping[key].reference.setOffset(offset); this.mapping[key].reference.setOffset(offset);
} }
...@@ -148,9 +162,9 @@ ...@@ -148,9 +162,9 @@
setDropdown() { setDropdown() {
const { lastToken, searchToken } = this.tokenizer const { lastToken, searchToken } = this.tokenizer
.processTokens(this.filteredSearchInput.value); .processTokens(gl.DropdownUtils.getSearchInput(this.filteredSearchInput));
if (this.filteredSearchInput.value.split('').last() === ' ') { if (this.currentDropdown) {
this.updateCurrentDropdownOffset(); this.updateCurrentDropdownOffset();
} }
......
...@@ -25,24 +25,32 @@ ...@@ -25,24 +25,32 @@
} }
bindEvents() { bindEvents() {
this.handleFormSubmit = this.handleFormSubmit.bind(this);
this.setDropdownWrapper = this.dropdownManager.setDropdown.bind(this.dropdownManager); this.setDropdownWrapper = this.dropdownManager.setDropdown.bind(this.dropdownManager);
this.toggleClearSearchButtonWrapper = this.toggleClearSearchButton.bind(this); this.toggleClearSearchButtonWrapper = this.toggleClearSearchButton.bind(this);
this.checkForEnterWrapper = this.checkForEnter.bind(this); this.checkForEnterWrapper = this.checkForEnter.bind(this);
this.clearSearchWrapper = this.clearSearch.bind(this); this.clearSearchWrapper = this.clearSearch.bind(this);
this.checkForBackspaceWrapper = this.checkForBackspace.bind(this); this.checkForBackspaceWrapper = this.checkForBackspace.bind(this);
this.tokenChange = this.tokenChange.bind(this);
this.filteredSearchInput.form.addEventListener('submit', this.handleFormSubmit);
this.filteredSearchInput.addEventListener('input', this.setDropdownWrapper); this.filteredSearchInput.addEventListener('input', this.setDropdownWrapper);
this.filteredSearchInput.addEventListener('input', this.toggleClearSearchButtonWrapper); this.filteredSearchInput.addEventListener('input', this.toggleClearSearchButtonWrapper);
this.filteredSearchInput.addEventListener('keydown', this.checkForEnterWrapper); this.filteredSearchInput.addEventListener('keydown', this.checkForEnterWrapper);
this.filteredSearchInput.addEventListener('keyup', this.checkForBackspaceWrapper); this.filteredSearchInput.addEventListener('keyup', this.checkForBackspaceWrapper);
this.filteredSearchInput.addEventListener('click', this.tokenChange);
this.filteredSearchInput.addEventListener('keyup', this.tokenChange);
this.clearSearchButton.addEventListener('click', this.clearSearchWrapper); this.clearSearchButton.addEventListener('click', this.clearSearchWrapper);
} }
unbindEvents() { unbindEvents() {
this.filteredSearchInput.form.removeEventListener('submit', this.handleFormSubmit);
this.filteredSearchInput.removeEventListener('input', this.setDropdownWrapper); this.filteredSearchInput.removeEventListener('input', this.setDropdownWrapper);
this.filteredSearchInput.removeEventListener('input', this.toggleClearSearchButtonWrapper); this.filteredSearchInput.removeEventListener('input', this.toggleClearSearchButtonWrapper);
this.filteredSearchInput.removeEventListener('keydown', this.checkForEnterWrapper); this.filteredSearchInput.removeEventListener('keydown', this.checkForEnterWrapper);
this.filteredSearchInput.removeEventListener('keyup', this.checkForBackspaceWrapper); this.filteredSearchInput.removeEventListener('keyup', this.checkForBackspaceWrapper);
this.filteredSearchInput.removeEventListener('click', this.tokenChange);
this.filteredSearchInput.removeEventListener('keyup', this.tokenChange);
this.clearSearchButton.removeEventListener('click', this.clearSearchWrapper); this.clearSearchButton.removeEventListener('click', this.clearSearchWrapper);
} }
...@@ -83,8 +91,14 @@ ...@@ -83,8 +91,14 @@
this.dropdownManager.resetDropdowns(); this.dropdownManager.resetDropdowns();
} }
handleFormSubmit(e) {
e.preventDefault();
this.search();
}
loadSearchParamsFromURL() { loadSearchParamsFromURL() {
const params = gl.utils.getUrlParamsArray(); const params = gl.utils.getUrlParamsArray();
const usernameParams = this.getUsernameParams();
const inputValues = []; const inputValues = [];
params.forEach((p) => { params.forEach((p) => {
...@@ -115,6 +129,16 @@ ...@@ -115,6 +129,16 @@
} }
inputValues.push(`${sanitizedKey}:${symbol}${quotationsToUse}${sanitizedValue}${quotationsToUse}`); inputValues.push(`${sanitizedKey}:${symbol}${quotationsToUse}${sanitizedValue}${quotationsToUse}`);
} else if (!match && keyParam === 'assignee_id') {
const id = parseInt(value, 10);
if (usernameParams[id]) {
inputValues.push(`assignee:@${usernameParams[id]}`);
}
} else if (!match && keyParam === 'author_id') {
const id = parseInt(value, 10);
if (usernameParams[id]) {
inputValues.push(`author:@${usernameParams[id]}`);
}
} else if (!match && keyParam === 'search') { } else if (!match && keyParam === 'search') {
inputValues.push(sanitizedValue); inputValues.push(sanitizedValue);
} }
...@@ -164,6 +188,27 @@ ...@@ -164,6 +188,27 @@
Turbolinks.visit(`?scope=all&utf8=✓&${paths.join('&')}`); Turbolinks.visit(`?scope=all&utf8=✓&${paths.join('&')}`);
} }
getUsernameParams() {
const usernamesById = {};
try {
const attribute = this.filteredSearchInput.getAttribute('data-username-params');
JSON.parse(attribute).forEach((user) => {
usernamesById[user.id] = user.username;
});
} catch (e) {
// do nothing
}
return usernamesById;
}
tokenChange() {
const dropdown = this.dropdownManager.mapping[this.dropdownManager.currentDropdown];
const currentDropdownRef = dropdown.reference;
this.setDropdownWrapper();
currentDropdownRef.dispatchInputEvent();
}
} }
window.gl = window.gl || {}; window.gl = window.gl || {};
......
...@@ -2,12 +2,12 @@ ...@@ -2,12 +2,12 @@
(function() { (function() {
this.GroupAvatar = (function() { this.GroupAvatar = (function() {
function GroupAvatar() { function GroupAvatar() {
$('.js-choose-group-avatar-button').bind("click", function() { $('.js-choose-group-avatar-button').on("click", function() {
var form; var form;
form = $(this).closest("form"); form = $(this).closest("form");
return form.find(".js-group-avatar-input").click(); return form.find(".js-group-avatar-input").click();
}); });
$('.js-group-avatar-input').bind("change", function() { $('.js-group-avatar-input').on("change", function() {
var filename, form; var filename, form;
form = $(this).closest("form"); form = $(this).closest("form");
filename = $(this).val().replace(/^.*[\\\/]/, ''); filename = $(this).val().replace(/^.*[\\\/]/, '');
......
...@@ -126,7 +126,9 @@ ...@@ -126,7 +126,9 @@
MergeRequestWidget.prototype.getMergeStatus = function() { MergeRequestWidget.prototype.getMergeStatus = function() {
return $.get(this.opts.merge_check_url, function(data) { return $.get(this.opts.merge_check_url, function(data) {
return $('.mr-state-widget').replaceWith(data); var $html = $(data);
$('.mr-widget-body').replaceWith($html.find('.mr-widget-body'));
$('.mr-widget-footer').replaceWith($html.find('.mr-widget-footer'));
}); });
}; };
......
...@@ -8,31 +8,42 @@ ...@@ -8,31 +8,42 @@
* temporarily. * temporarily.
* */ * */
if ($('.accept-mr-form').length) { $(document)
$('.accept-mr-form').on('ajax:send', () => { .off('ajax:send', '.accept-mr-form')
.on('ajax:send', '.accept-mr-form', () => {
$('.accept-mr-form :input').disable(); $('.accept-mr-form :input').disable();
}); });
$('.accept_merge_request').on('click', () => { $(document)
.off('click', '.accept_merge_request')
.on('click', '.accept_merge_request', () => {
$('.js-merge-button').html('<i class="fa fa-spinner fa-spin"></i> Merge in progress'); $('.js-merge-button').html('<i class="fa fa-spinner fa-spin"></i> Merge in progress');
}); });
$('.merge_when_build_succeeds').on('click', () => { $(document)
.off('click', '.merge_when_build_succeeds')
.on('click', '.merge_when_build_succeeds', () => {
$('#merge_when_build_succeeds').val('1'); $('#merge_when_build_succeeds').val('1');
}); });
$('.js-merge-dropdown a').on('click', (e) => { $(document)
.off('click', '.js-merge-dropdown a')
.on('click', '.js-merge-dropdown a', (e) => {
e.preventDefault(); e.preventDefault();
$(this).closest('form').submit(); $(e.target).closest('form').submit();
}); });
} else if ($('.rebase-in-progress').length) { if ($('.rebase-in-progress').length) {
merge_request_widget.rebaseInProgress(); merge_request_widget.rebaseInProgress();
} else if ($('.rebase-mr-form').length) { } else if ($('.rebase-mr-form').length) {
$('.rebase-mr-form').on('ajax:send', () => { $(document)
.off('ajax:send', '.rebase-mr-form')
.on('ajax:send', '.rebase-mr-form', () => {
$('.rebase-mr-form :input').disable(); $('.rebase-mr-form :input').disable();
}); });
$('.js-rebase-button').on('click', () => { $(document)
.off('click', '.js-rebase-button')
.on('click', '.js-rebase-button', () => {
$('.js-rebase-button').html("<i class='fa fa-spinner fa-spin'></i> Rebase in progress"); $('.js-rebase-button').html("<i class='fa fa-spinner fa-spin'></i> Rebase in progress");
}); });
} else { } else {
......
/* global Vue, Flash, gl */ /* global Vue, Flash, gl */
/* eslint-disable no-param-reassign, no-bitwise */ /* eslint-disable no-param-reassign */
((gl) => { ((gl) => {
gl.VueStage = Vue.extend({ gl.VueStage = Vue.extend({
...@@ -9,7 +9,20 @@ ...@@ -9,7 +9,20 @@
spinner: '<span class="fa fa-spinner fa-spin"></span>', spinner: '<span class="fa fa-spinner fa-spin"></span>',
}; };
}, },
props: ['stage', 'svgs', 'match'], props: {
stage: {
type: Object,
required: true,
},
svgs: {
type: DOMStringMap,
required: true,
},
match: {
type: Function,
required: true,
},
},
methods: { methods: {
fetchBuilds(e) { fetchBuilds(e) {
const areaExpanded = e.currentTarget.attributes['aria-expanded']; const areaExpanded = e.currentTarget.attributes['aria-expanded'];
...@@ -24,6 +37,18 @@ ...@@ -24,6 +37,18 @@
return flash; return flash;
}); });
}, },
keepGraph(e) {
const { target } = e;
if (target.className.indexOf('js-ci-action-icon') >= 0) return null;
if (
target.parentElement &&
(target.parentElement.className.indexOf('js-ci-action-icon') >= 0)
) return null;
return e.stopPropagation();
},
}, },
computed: { computed: {
buildsOrSpinner() { buildsOrSpinner() {
...@@ -64,7 +89,7 @@ ...@@ -64,7 +89,7 @@
<ul class="dropdown-menu mini-pipeline-graph-dropdown-menu js-builds-dropdown-container"> <ul class="dropdown-menu mini-pipeline-graph-dropdown-menu js-builds-dropdown-container">
<div class="arrow-up"></div> <div class="arrow-up"></div>
<div <div
@click='' @click='keepGraph($event)'
:class="dropdownClass" :class="dropdownClass"
class="js-builds-dropdown-list scrollable-menu" class="js-builds-dropdown-list scrollable-menu"
v-html="buildsOrSpinner" v-html="buildsOrSpinner"
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
} }
.ci-status-icon-pending, .ci-status-icon-pending,
.ci-status-icon-failed_with_warnings,
.ci-status-icon-success_with_warnings { .ci-status-icon-success_with_warnings {
color: $gl-warning; color: $gl-warning;
......
...@@ -46,10 +46,6 @@ ...@@ -46,10 +46,6 @@
font-weight: bold; font-weight: bold;
} }
.fa-clipboard {
color: $dropdown-title-btn-color;
}
.commit-info { .commit-info {
&.branches { &.branches {
margin-left: 8px; margin-left: 8px;
......
...@@ -377,6 +377,10 @@ ...@@ -377,6 +377,10 @@
display: inline-block; display: inline-block;
padding: 5px; padding: 5px;
&:nth-of-type(7n) {
padding-right: 0;
}
.author_link { .author_link {
display: block; display: block;
} }
......
...@@ -195,10 +195,10 @@ ul.notes { ...@@ -195,10 +195,10 @@ ul.notes {
} }
.note-body { .note-body {
overflow: auto; overflow-x: auto;
overflow-y: hidden;
.note-text { .note-text {
overflow: auto;
word-wrap: break-word; word-wrap: break-word;
@include md-typography; @include md-typography;
// Reset ul style types since we're nested inside a ul already // Reset ul style types since we're nested inside a ul already
......
...@@ -18,7 +18,6 @@ ...@@ -18,7 +18,6 @@
.file-finder-input:hover, .file-finder-input:hover,
.issuable-search-form:hover, .issuable-search-form:hover,
.search-text-input:hover, .search-text-input:hover,
textarea:hover,
.form-control:hover { .form-control:hover {
border-color: lighten($dropdown-input-focus-border, 20%); border-color: lighten($dropdown-input-focus-border, 20%);
box-shadow: 0 0 4px lighten($search-input-focus-shadow-color, 20%); box-shadow: 0 0 4px lighten($search-input-focus-shadow-color, 20%);
......
...@@ -19,7 +19,8 @@ ...@@ -19,7 +19,8 @@
overflow: visible; overflow: visible;
} }
&.ci-failed { &.ci-failed,
&.ci-failed_with_warnings {
color: $gl-danger; color: $gl-danger;
border-color: $gl-danger; border-color: $gl-danger;
......
...@@ -14,12 +14,8 @@ class ConfirmationsController < Devise::ConfirmationsController ...@@ -14,12 +14,8 @@ class ConfirmationsController < Devise::ConfirmationsController
if signed_in?(resource_name) if signed_in?(resource_name)
after_sign_in_path_for(resource) after_sign_in_path_for(resource)
else else
sign_in(resource) flash[:notice] += " Please sign in."
if signed_in?(resource_name)
after_sign_in_path_for(resource)
else
new_session_path(resource_name) new_session_path(resource_name)
end end
end end
end
end end
...@@ -33,6 +33,18 @@ class Projects::IssuesController < Projects::ApplicationController ...@@ -33,6 +33,18 @@ class Projects::IssuesController < Projects::ApplicationController
@labels = LabelsFinder.new(current_user, project_id: @project.id, title: params[:label_name]).execute @labels = LabelsFinder.new(current_user, project_id: @project.id, title: params[:label_name]).execute
end end
@users = []
if params[:assignee_id].present?
assignee = User.find_by_id(params[:assignee_id])
@users.push(assignee) if assignee
end
if params[:author_id].present?
author = User.find_by_id(params[:author_id])
@users.push(author) if author
end
respond_to do |format| respond_to do |format|
format.html format.html
format.atom { render layout: false } format.atom { render layout: false }
......
...@@ -45,6 +45,8 @@ class SearchController < ApplicationController ...@@ -45,6 +45,8 @@ class SearchController < ApplicationController
end end
@search_objects = @search_results.objects(@scope, params[:page]) @search_objects = @search_results.objects(@scope, params[:page])
check_single_commit_result
end end
def autocomplete def autocomplete
...@@ -59,4 +61,16 @@ class SearchController < ApplicationController ...@@ -59,4 +61,16 @@ class SearchController < ApplicationController
render json: search_autocomplete_opts(term).to_json render json: search_autocomplete_opts(term).to_json
end end
private
def check_single_commit_result
if @search_results.single_commit_result?
only_commit = @search_results.objects('commits').first
query = params[:search].strip.downcase
found_by_commit_sha = Commit.valid_hash?(query) && only_commit.sha.start_with?(query)
redirect_to namespace_project_commit_path(@project.namespace, @project, only_commit) if found_by_commit_sha
end
end
end end
module ServicesHelper module ServicesHelper
def service_event_description(event) def service_event_description(event)
case event case event
when "push" when "push", "push_events"
"Event will be triggered by a push to the repository" "Event will be triggered by a push to the repository"
when "tag_push" when "tag_push", "tag_push_events"
"Event will be triggered when a new tag is pushed to the repository" "Event will be triggered when a new tag is pushed to the repository"
when "note" when "note", "note_events"
"Event will be triggered when someone adds a comment" "Event will be triggered when someone adds a comment"
when "issue" when "issue", "issue_events"
"Event will be triggered when an issue is created/updated/closed" "Event will be triggered when an issue is created/updated/closed"
when "confidential_issue" when "confidential_issue", "confidential_issue_events"
"Event will be triggered when a confidential issue is created/updated/closed" "Event will be triggered when a confidential issue is created/updated/closed"
when "merge_request" when "merge_request", "merge_request_events"
"Event will be triggered when a merge request is created/updated/merged" "Event will be triggered when a merge request is created/updated/merged"
when "build" when "build", "build_events"
"Event will be triggered when a build status changes" "Event will be triggered when a build status changes"
when "wiki_page" when "wiki_page", "wiki_page_events"
"Event will be triggered when a wiki page is created/updated" "Event will be triggered when a wiki page is created/updated"
when "commit" when "commit", "commit_events"
"Event will be triggered when a commit is created/updated" "Event will be triggered when a commit is created/updated"
end end
end end
...@@ -26,4 +26,6 @@ module ServicesHelper ...@@ -26,4 +26,6 @@ module ServicesHelper
event = event.pluralize if %w[merge_request issue confidential_issue].include?(event) event = event.pluralize if %w[merge_request issue confidential_issue].include?(event)
"#{event}_events" "#{event}_events"
end end
extend self
end end
...@@ -107,15 +107,11 @@ class Notify < BaseMailer ...@@ -107,15 +107,11 @@ class Notify < BaseMailer
def mail_thread(model, headers = {}) def mail_thread(model, headers = {})
add_project_headers add_project_headers
add_unsubscription_headers_and_links
headers["X-GitLab-#{model.class.name}-ID"] = model.id headers["X-GitLab-#{model.class.name}-ID"] = model.id
headers['X-GitLab-Reply-Key'] = reply_key headers['X-GitLab-Reply-Key'] = reply_key
if !@labels_url && @sent_notification && @sent_notification.unsubscribable?
headers['List-Unsubscribe'] = "<#{unsubscribe_sent_notification_url(@sent_notification, force: true)}>"
@sent_notification_url = unsubscribe_sent_notification_url(@sent_notification)
end
if Gitlab::IncomingEmail.enabled? if Gitlab::IncomingEmail.enabled?
address = Mail::Address.new(Gitlab::IncomingEmail.reply_address(reply_key)) address = Mail::Address.new(Gitlab::IncomingEmail.reply_address(reply_key))
address.display_name = @project.name_with_namespace address.display_name = @project.name_with_namespace
...@@ -171,4 +167,16 @@ class Notify < BaseMailer ...@@ -171,4 +167,16 @@ class Notify < BaseMailer
headers['X-GitLab-Project-Id'] = @project.id headers['X-GitLab-Project-Id'] = @project.id
headers['X-GitLab-Project-Path'] = @project.path_with_namespace headers['X-GitLab-Project-Path'] = @project.path_with_namespace
end end
def add_unsubscription_headers_and_links
return unless !@labels_url && @sent_notification && @sent_notification.unsubscribable?
list_unsubscribe_methods = [unsubscribe_sent_notification_url(@sent_notification, force: true)]
if Gitlab::IncomingEmail.enabled? && Gitlab::IncomingEmail.supports_wildcard?
list_unsubscribe_methods << "mailto:#{Gitlab::IncomingEmail.unsubscribe_address(reply_key)}"
end
headers['List-Unsubscribe'] = list_unsubscribe_methods.map { |e| "<#{e}>" }.join(',')
@sent_notification_url = unsubscribe_sent_notification_url(@sent_notification)
end
end end
...@@ -128,16 +128,21 @@ module Ci ...@@ -128,16 +128,21 @@ module Ci
end end
def stages def stages
# TODO, this needs refactoring, see gitlab-ce#26481.
stages_query = statuses
.group('stage').select(:stage).order('max(stage_idx)')
status_sql = statuses.latest.where('stage=sg.stage').status_sql status_sql = statuses.latest.where('stage=sg.stage').status_sql
stages_query = statuses.group('stage').select(:stage) warnings_sql = statuses.latest.select('COUNT(*) > 0')
.order('max(stage_idx)') .where('stage=sg.stage').failed_but_allowed.to_sql
stages_with_statuses = CommitStatus.from(stages_query, :sg). stages_with_statuses = CommitStatus.from(stages_query, :sg)
pluck('sg.stage', status_sql) .pluck('sg.stage', status_sql, "(#{warnings_sql})")
stages_with_statuses.map do |stage| stages_with_statuses.map do |stage|
Ci::Stage.new(self, name: stage.first, status: stage.last) Ci::Stage.new(self, Hash[%i[name status warnings].zip(stage)])
end end
end end
......
...@@ -126,9 +126,11 @@ module Ci ...@@ -126,9 +126,11 @@ module Ci
end end
def tick_runner_queue def tick_runner_queue
new_update = SecureRandom.hex SecureRandom.hex.tap do |new_update|
Gitlab::Redis.with { |redis| redis.set(runner_queue_key, new_update, ex: RUNNER_QUEUE_EXPIRY_TIME) } Gitlab::Redis.with do |redis|
new_update redis.set(runner_queue_key, new_update, ex: RUNNER_QUEUE_EXPIRY_TIME)
end
end
end end
def ensure_runner_queue_value def ensure_runner_queue_value
......
...@@ -8,10 +8,11 @@ module Ci ...@@ -8,10 +8,11 @@ module Ci
delegate :project, to: :pipeline delegate :project, to: :pipeline
def initialize(pipeline, name:, status: nil) def initialize(pipeline, name:, status: nil, warnings: nil)
@pipeline = pipeline @pipeline = pipeline
@name = name @name = name
@status = status @status = status
@warnings = warnings
end end
def to_param def to_param
...@@ -39,5 +40,17 @@ module Ci ...@@ -39,5 +40,17 @@ module Ci
def builds def builds
@builds ||= pipeline.builds.where(stage: name) @builds ||= pipeline.builds.where(stage: name)
end end
def success?
status.to_s == 'success'
end
def has_warnings?
if @warnings.nil?
statuses.latest.failed_but_allowed.any?
else
@warnings
end
end
end end
end end
...@@ -21,6 +21,9 @@ class Commit ...@@ -21,6 +21,9 @@ class Commit
DIFF_HARD_LIMIT_FILES = 1000 DIFF_HARD_LIMIT_FILES = 1000
DIFF_HARD_LIMIT_LINES = 50000 DIFF_HARD_LIMIT_LINES = 50000
# The SHA can be between 7 and 40 hex characters.
COMMIT_SHA_PATTERN = '\h{7,40}'
class << self class << self
def decorate(commits, project) def decorate(commits, project)
commits.map do |commit| commits.map do |commit|
...@@ -52,6 +55,10 @@ class Commit ...@@ -52,6 +55,10 @@ class Commit
def from_hash(hash, project) def from_hash(hash, project)
new(Gitlab::Git::Commit.new(hash), project) new(Gitlab::Git::Commit.new(hash), project)
end end
def valid_hash?(key)
!!(/\A#{COMMIT_SHA_PATTERN}\z/ =~ key)
end
end end
attr_accessor :raw attr_accessor :raw
...@@ -77,8 +84,6 @@ class Commit ...@@ -77,8 +84,6 @@ class Commit
# Pattern used to extract commit references from text # Pattern used to extract commit references from text
# #
# The SHA can be between 7 and 40 hex characters.
#
# This pattern supports cross-project references. # This pattern supports cross-project references.
def self.reference_pattern def self.reference_pattern
@reference_pattern ||= %r{ @reference_pattern ||= %r{
...@@ -88,7 +93,7 @@ class Commit ...@@ -88,7 +93,7 @@ class Commit
end end
def self.link_reference_pattern def self.link_reference_pattern
@link_reference_pattern ||= super("commit", /(?<commit>\h{7,40})/) @link_reference_pattern ||= super("commit", /(?<commit>#{COMMIT_SHA_PATTERN})/)
end end
def to_reference(from_project = nil, full: false) def to_reference(from_project = nil, full: false)
......
module HasStatus module HasStatus
extend ActiveSupport::Concern extend ActiveSupport::Concern
DEFAULT_STATUS = 'created'
AVAILABLE_STATUSES = %w[created pending running success failed canceled skipped] AVAILABLE_STATUSES = %w[created pending running success failed canceled skipped]
STARTED_STATUSES = %w[running success failed skipped] STARTED_STATUSES = %w[running success failed skipped]
ACTIVE_STATUSES = %w[pending running] ACTIVE_STATUSES = %w[pending running]
......
...@@ -11,7 +11,7 @@ module Taskable ...@@ -11,7 +11,7 @@ module Taskable
INCOMPLETE = 'incomplete'.freeze INCOMPLETE = 'incomplete'.freeze
ITEM_PATTERN = / ITEM_PATTERN = /
^ ^
(?:\s*[-+*]|(?:\d+\.))? # optional list prefix \s*(?:[-+*]|(?:\d+\.))? # optional list prefix
\s* # optional whitespace prefix \s* # optional whitespace prefix
(\[\s\]|\[[xX]\]) # checkbox (\[\s\]|\[[xX]\]) # checkbox
(\s.+) # followed by whitespace and some text. (\s.+) # followed by whitespace and some text.
......
...@@ -4,6 +4,8 @@ class Key < ActiveRecord::Base ...@@ -4,6 +4,8 @@ class Key < ActiveRecord::Base
include AfterCommitQueue include AfterCommitQueue
include Sortable include Sortable
LAST_USED_AT_REFRESH_TIME = 1.day.to_i
belongs_to :user belongs_to :user
before_validation :generate_fingerprint before_validation :generate_fingerprint
...@@ -50,7 +52,10 @@ class Key < ActiveRecord::Base ...@@ -50,7 +52,10 @@ class Key < ActiveRecord::Base
end end
def update_last_used_at def update_last_used_at
UseKeyWorker.perform_async(self.id) lease = Gitlab::ExclusiveLease.new("key_update_last_used_at:#{id}", timeout: LAST_USED_AT_REFRESH_TIME)
return unless lease.try_obtain
UseKeyWorker.perform_async(id)
end end
def add_to_shell def add_to_shell
......
...@@ -131,6 +131,8 @@ class Namespace < ActiveRecord::Base ...@@ -131,6 +131,8 @@ class Namespace < ActiveRecord::Base
Gitlab::UploadsTransfer.new.rename_namespace(path_was, path) Gitlab::UploadsTransfer.new.rename_namespace(path_was, path)
remove_exports!
# If repositories moved successfully we need to # If repositories moved successfully we need to
# send update instructions to users. # send update instructions to users.
# However we cannot allow rollback since we moved namespace dir # However we cannot allow rollback since we moved namespace dir
...@@ -219,6 +221,8 @@ class Namespace < ActiveRecord::Base ...@@ -219,6 +221,8 @@ class Namespace < ActiveRecord::Base
GitlabShellWorker.perform_in(5.minutes, :rm_namespace, repository_storage_path, new_path) GitlabShellWorker.perform_in(5.minutes, :rm_namespace, repository_storage_path, new_path)
end end
end end
remove_exports!
end end
def refresh_access_of_projects_invited_groups def refresh_access_of_projects_invited_groups
...@@ -231,4 +235,20 @@ class Namespace < ActiveRecord::Base ...@@ -231,4 +235,20 @@ class Namespace < ActiveRecord::Base
def full_path_changed? def full_path_changed?
path_changed? || parent_id_changed? path_changed? || parent_id_changed?
end end
def remove_exports!
Gitlab::Popen.popen(%W(find #{export_path} -not -path #{export_path} -delete))
end
def export_path
File.join(Gitlab::ImportExport.storage_path, full_path_was)
end
def full_path_was
if parent
parent.full_path + '/' + path_was
else
path_was
end
end
end end
...@@ -121,8 +121,6 @@ class Project < ActiveRecord::Base ...@@ -121,8 +121,6 @@ class Project < ActiveRecord::Base
# Merge Requests for target project should be removed with it # Merge Requests for target project should be removed with it
has_many :merge_requests, dependent: :destroy, foreign_key: 'target_project_id' has_many :merge_requests, dependent: :destroy, foreign_key: 'target_project_id'
# Merge requests from source project should be kept when source project was removed
has_many :fork_merge_requests, foreign_key: 'source_project_id', class_name: 'MergeRequest'
has_many :issues, dependent: :destroy has_many :issues, dependent: :destroy
has_many :labels, dependent: :destroy, class_name: 'ProjectLabel' has_many :labels, dependent: :destroy, class_name: 'ProjectLabel'
has_many :services, dependent: :destroy has_many :services, dependent: :destroy
......
...@@ -25,7 +25,7 @@ You can create a Personal Access Token here: ...@@ -25,7 +25,7 @@ You can create a Personal Access Token here:
http://app.asana.com/-/account_api' http://app.asana.com/-/account_api'
end end
def to_param def self.to_param
'asana' 'asana'
end end
...@@ -44,7 +44,7 @@ http://app.asana.com/-/account_api' ...@@ -44,7 +44,7 @@ http://app.asana.com/-/account_api'
] ]
end end
def supported_events def self.supported_events
%w(push) %w(push)
end end
......
...@@ -12,7 +12,7 @@ class AssemblaService < Service ...@@ -12,7 +12,7 @@ class AssemblaService < Service
'Project Management Software (Source Commits Endpoint)' 'Project Management Software (Source Commits Endpoint)'
end end
def to_param def self.to_param
'assembla' 'assembla'
end end
...@@ -23,7 +23,7 @@ class AssemblaService < Service ...@@ -23,7 +23,7 @@ class AssemblaService < Service
] ]
end end
def supported_events def self.supported_events
%w(push) %w(push)
end end
......
...@@ -40,7 +40,7 @@ class BambooService < CiService ...@@ -40,7 +40,7 @@ class BambooService < CiService
'You must set up automatic revision labeling and a repository trigger in Bamboo.' 'You must set up automatic revision labeling and a repository trigger in Bamboo.'
end end
def to_param def self.to_param
'bamboo' 'bamboo'
end end
...@@ -56,10 +56,6 @@ class BambooService < CiService ...@@ -56,10 +56,6 @@ class BambooService < CiService
] ]
end end
def supported_events
%w(push)
end
def build_page(sha, ref) def build_page(sha, ref)
with_reactive_cache(sha, ref) {|cached| cached[:build_page] } with_reactive_cache(sha, ref) {|cached| cached[:build_page] }
end end
......
...@@ -19,7 +19,7 @@ class BugzillaService < IssueTrackerService ...@@ -19,7 +19,7 @@ class BugzillaService < IssueTrackerService
end end
end end
def to_param def self.to_param
'bugzilla' 'bugzilla'
end end
end end
...@@ -24,10 +24,6 @@ class BuildkiteService < CiService ...@@ -24,10 +24,6 @@ class BuildkiteService < CiService
hook.save hook.save
end end
def supported_events
%w(push)
end
def execute(data) def execute(data)
return unless supported_events.include?(data[:object_kind]) return unless supported_events.include?(data[:object_kind])
...@@ -54,7 +50,7 @@ class BuildkiteService < CiService ...@@ -54,7 +50,7 @@ class BuildkiteService < CiService
'Continuous integration and deployments' 'Continuous integration and deployments'
end end
def to_param def self.to_param
'buildkite' 'buildkite'
end end
......
...@@ -19,11 +19,11 @@ class BuildsEmailService < Service ...@@ -19,11 +19,11 @@ class BuildsEmailService < Service
'Email the builds status to a list of recipients.' 'Email the builds status to a list of recipients.'
end end
def to_param def self.to_param
'builds_email' 'builds_email'
end end
def supported_events def self.supported_events
%w(build) %w(build)
end end
......
...@@ -12,7 +12,7 @@ class CampfireService < Service ...@@ -12,7 +12,7 @@ class CampfireService < Service
'Simple web-based real-time group chat' 'Simple web-based real-time group chat'
end end
def to_param def self.to_param
'campfire' 'campfire'
end end
...@@ -24,7 +24,7 @@ class CampfireService < Service ...@@ -24,7 +24,7 @@ class CampfireService < Service
] ]
end end
def supported_events def self.supported_events
%w(push) %w(push)
end end
......
...@@ -25,7 +25,7 @@ class ChatNotificationService < Service ...@@ -25,7 +25,7 @@ class ChatNotificationService < Service
valid? valid?
end end
def supported_events def self.supported_events
%w[push issue confidential_issue merge_request note tag_push %w[push issue confidential_issue merge_request note tag_push
build pipeline wiki_page] build pipeline wiki_page]
end end
...@@ -82,19 +82,19 @@ class ChatNotificationService < Service ...@@ -82,19 +82,19 @@ class ChatNotificationService < Service
def get_message(object_kind, data) def get_message(object_kind, data)
case object_kind case object_kind
when "push", "tag_push" when "push", "tag_push"
PushMessage.new(data) ChatMessage::PushMessage.new(data)
when "issue" when "issue"
IssueMessage.new(data) unless is_update?(data) ChatMessage::IssueMessage.new(data) unless is_update?(data)
when "merge_request" when "merge_request"
MergeMessage.new(data) unless is_update?(data) ChatMessage::MergeMessage.new(data) unless is_update?(data)
when "note" when "note"
NoteMessage.new(data) ChatMessage::NoteMessage.new(data)
when "build" when "build"
BuildMessage.new(data) if should_build_be_notified?(data) ChatMessage::BuildMessage.new(data) if should_build_be_notified?(data)
when "pipeline" when "pipeline"
PipelineMessage.new(data) if should_pipeline_be_notified?(data) ChatMessage::PipelineMessage.new(data) if should_pipeline_be_notified?(data)
when "wiki_page" when "wiki_page"
WikiPageMessage.new(data) ChatMessage::WikiPageMessage.new(data)
end end
end end
......
...@@ -13,8 +13,8 @@ class ChatSlashCommandsService < Service ...@@ -13,8 +13,8 @@ class ChatSlashCommandsService < Service
ActiveSupport::SecurityUtils.variable_size_secure_compare(token, self.token) ActiveSupport::SecurityUtils.variable_size_secure_compare(token, self.token)
end end
def supported_events def self.supported_events
[] %w()
end end
def can_test? def can_test?
......
...@@ -8,7 +8,7 @@ class CiService < Service ...@@ -8,7 +8,7 @@ class CiService < Service
self.respond_to?(:token) && self.token.present? && ActiveSupport::SecurityUtils.variable_size_secure_compare(token, self.token) self.respond_to?(:token) && self.token.present? && ActiveSupport::SecurityUtils.variable_size_secure_compare(token, self.token)
end end
def supported_events def self.supported_events
%w(push) %w(push)
end end
......
...@@ -23,7 +23,7 @@ class CustomIssueTrackerService < IssueTrackerService ...@@ -23,7 +23,7 @@ class CustomIssueTrackerService < IssueTrackerService
end end
end end
def to_param def self.to_param
'custom_issue_tracker' 'custom_issue_tracker'
end end
......
...@@ -5,8 +5,8 @@ ...@@ -5,8 +5,8 @@
class DeploymentService < Service class DeploymentService < Service
default_value_for :category, 'deployment' default_value_for :category, 'deployment'
def supported_events def self.supported_events
[] %w()
end end
def predefined_variables def predefined_variables
......
...@@ -32,7 +32,7 @@ class DroneCiService < CiService ...@@ -32,7 +32,7 @@ class DroneCiService < CiService
true true
end end
def supported_events def self.supported_events
%w(push merge_request tag_push) %w(push merge_request tag_push)
end end
...@@ -87,7 +87,7 @@ class DroneCiService < CiService ...@@ -87,7 +87,7 @@ class DroneCiService < CiService
'Drone is a Continuous Integration platform built on Docker, written in Go' 'Drone is a Continuous Integration platform built on Docker, written in Go'
end end
def to_param def self.to_param
'drone_ci' 'drone_ci'
end end
......
...@@ -12,11 +12,11 @@ class EmailsOnPushService < Service ...@@ -12,11 +12,11 @@ class EmailsOnPushService < Service
'Email the commits and diff of each push to a list of recipients.' 'Email the commits and diff of each push to a list of recipients.'
end end
def to_param def self.to_param
'emails_on_push' 'emails_on_push'
end end
def supported_events def self.supported_events
%w(push tag_push) %w(push tag_push)
end end
......
...@@ -13,7 +13,7 @@ class ExternalWikiService < Service ...@@ -13,7 +13,7 @@ class ExternalWikiService < Service
'Replaces the link to the internal wiki with a link to an external wiki.' 'Replaces the link to the internal wiki with a link to an external wiki.'
end end
def to_param def self.to_param
'external_wiki' 'external_wiki'
end end
...@@ -29,4 +29,8 @@ class ExternalWikiService < Service ...@@ -29,4 +29,8 @@ class ExternalWikiService < Service
nil nil
end end
end end
def self.supported_events
%w()
end
end end
...@@ -12,7 +12,7 @@ class FlowdockService < Service ...@@ -12,7 +12,7 @@ class FlowdockService < Service
'Flowdock is a collaboration web app for technical teams.' 'Flowdock is a collaboration web app for technical teams.'
end end
def to_param def self.to_param
'flowdock' 'flowdock'
end end
...@@ -22,7 +22,7 @@ class FlowdockService < Service ...@@ -22,7 +22,7 @@ class FlowdockService < Service
] ]
end end
def supported_events def self.supported_events
%w(push) %w(push)
end end
......
...@@ -12,7 +12,7 @@ class GemnasiumService < Service ...@@ -12,7 +12,7 @@ class GemnasiumService < Service
'Gemnasium monitors your project dependencies and alerts you about updates and security vulnerabilities.' 'Gemnasium monitors your project dependencies and alerts you about updates and security vulnerabilities.'
end end
def to_param def self.to_param
'gemnasium' 'gemnasium'
end end
...@@ -23,7 +23,7 @@ class GemnasiumService < Service ...@@ -23,7 +23,7 @@ class GemnasiumService < Service
] ]
end end
def supported_events def self.supported_events
%w(push) %w(push)
end end
......
...@@ -7,7 +7,7 @@ class GitlabIssueTrackerService < IssueTrackerService ...@@ -7,7 +7,7 @@ class GitlabIssueTrackerService < IssueTrackerService
default_value_for :default, true default_value_for :default, true
def to_param def self.to_param
'gitlab' 'gitlab'
end end
......
...@@ -27,7 +27,7 @@ class HipchatService < Service ...@@ -27,7 +27,7 @@ class HipchatService < Service
'Private group chat and IM' 'Private group chat and IM'
end end
def to_param def self.to_param
'hipchat' 'hipchat'
end end
...@@ -45,7 +45,7 @@ class HipchatService < Service ...@@ -45,7 +45,7 @@ class HipchatService < Service
] ]
end end
def supported_events def self.supported_events
%w(push issue confidential_issue merge_request note tag_push build) %w(push issue confidential_issue merge_request note tag_push build)
end end
......
...@@ -17,11 +17,11 @@ class IrkerService < Service ...@@ -17,11 +17,11 @@ class IrkerService < Service
'gateway.' 'gateway.'
end end
def to_param def self.to_param
'irker' 'irker'
end end
def supported_events def self.supported_events
%w(push) %w(push)
end end
......
...@@ -57,7 +57,7 @@ class IssueTrackerService < Service ...@@ -57,7 +57,7 @@ class IssueTrackerService < Service
end end
end end
def supported_events def self.supported_events
%w(push) %w(push)
end end
......
...@@ -12,7 +12,7 @@ class JiraService < IssueTrackerService ...@@ -12,7 +12,7 @@ class JiraService < IssueTrackerService
# This is confusing, but JiraService does not really support these events. # This is confusing, but JiraService does not really support these events.
# The values here are required to display correct options in the service # The values here are required to display correct options in the service
# configuration screen. # configuration screen.
def supported_events def self.supported_events
%w(commit merge_request) %w(commit merge_request)
end end
...@@ -81,7 +81,7 @@ class JiraService < IssueTrackerService ...@@ -81,7 +81,7 @@ class JiraService < IssueTrackerService
end end
end end
def to_param def self.to_param
'jira' 'jira'
end end
......
...@@ -52,7 +52,7 @@ class KubernetesService < DeploymentService ...@@ -52,7 +52,7 @@ class KubernetesService < DeploymentService
'deployments with `app=$CI_ENVIRONMENT_SLUG`' 'deployments with `app=$CI_ENVIRONMENT_SLUG`'
end end
def to_param def self.to_param
'kubernetes' 'kubernetes'
end end
......
...@@ -7,7 +7,7 @@ class MattermostService < ChatNotificationService ...@@ -7,7 +7,7 @@ class MattermostService < ChatNotificationService
'Receive event notifications in Mattermost' 'Receive event notifications in Mattermost'
end end
def to_param def self.to_param
'mattermost' 'mattermost'
end end
...@@ -36,6 +36,6 @@ class MattermostService < ChatNotificationService ...@@ -36,6 +36,6 @@ class MattermostService < ChatNotificationService
end end
def default_channel_placeholder def default_channel_placeholder
"#town-square" "town-square"
end end
end end
...@@ -15,7 +15,7 @@ class MattermostSlashCommandsService < ChatSlashCommandsService ...@@ -15,7 +15,7 @@ class MattermostSlashCommandsService < ChatSlashCommandsService
"Perform common operations on GitLab in Mattermost" "Perform common operations on GitLab in Mattermost"
end end
def to_param def self.to_param
'mattermost_slash_commands' 'mattermost_slash_commands'
end end
......
...@@ -15,11 +15,11 @@ class PipelinesEmailService < Service ...@@ -15,11 +15,11 @@ class PipelinesEmailService < Service
'Email the pipelines status to a list of recipients.' 'Email the pipelines status to a list of recipients.'
end end
def to_param def self.to_param
'pipelines_email' 'pipelines_email'
end end
def supported_events def self.supported_events
%w[pipeline] %w[pipeline]
end end
......
...@@ -14,7 +14,7 @@ class PivotaltrackerService < Service ...@@ -14,7 +14,7 @@ class PivotaltrackerService < Service
'Project Management Software (Source Commits Endpoint)' 'Project Management Software (Source Commits Endpoint)'
end end
def to_param def self.to_param
'pivotaltracker' 'pivotaltracker'
end end
...@@ -34,7 +34,7 @@ class PivotaltrackerService < Service ...@@ -34,7 +34,7 @@ class PivotaltrackerService < Service
] ]
end end
def supported_events def self.supported_events
%w(push) %w(push)
end end
......
...@@ -13,7 +13,7 @@ class PushoverService < Service ...@@ -13,7 +13,7 @@ class PushoverService < Service
'Pushover makes it easy to get real-time notifications on your Android device, iPhone, iPad, and Desktop.' 'Pushover makes it easy to get real-time notifications on your Android device, iPhone, iPad, and Desktop.'
end end
def to_param def self.to_param
'pushover' 'pushover'
end end
...@@ -61,7 +61,7 @@ class PushoverService < Service ...@@ -61,7 +61,7 @@ class PushoverService < Service
] ]
end end
def supported_events def self.supported_events
%w(push) %w(push)
end end
......
...@@ -19,7 +19,7 @@ class RedmineService < IssueTrackerService ...@@ -19,7 +19,7 @@ class RedmineService < IssueTrackerService
end end
end end
def to_param def self.to_param
'redmine' 'redmine'
end end
end end
...@@ -7,7 +7,7 @@ class SlackService < ChatNotificationService ...@@ -7,7 +7,7 @@ class SlackService < ChatNotificationService
'Receive event notifications in Slack' 'Receive event notifications in Slack'
end end
def to_param def self.to_param
'slack' 'slack'
end end
......
...@@ -9,7 +9,7 @@ class SlackSlashCommandsService < ChatSlashCommandsService ...@@ -9,7 +9,7 @@ class SlackSlashCommandsService < ChatSlashCommandsService
"Perform common operations on GitLab in Slack" "Perform common operations on GitLab in Slack"
end end
def to_param def self.to_param
'slack_slash_commands' 'slack_slash_commands'
end end
......
...@@ -43,14 +43,10 @@ class TeamcityService < CiService ...@@ -43,14 +43,10 @@ class TeamcityService < CiService
'requests build, that setting is in the vsc root advanced settings.' 'requests build, that setting is in the vsc root advanced settings.'
end end
def to_param def self.to_param
'teamcity' 'teamcity'
end end
def supported_events
%w(push)
end
def fields def fields
[ [
{ type: 'text', name: 'teamcity_url', { type: 'text', name: 'teamcity_url',
......
...@@ -76,6 +76,11 @@ class Service < ActiveRecord::Base ...@@ -76,6 +76,11 @@ class Service < ActiveRecord::Base
def to_param def to_param
# implement inside child # implement inside child
self.class.to_param
end
def self.to_param
raise NotImplementedError
end end
def fields def fields
...@@ -92,7 +97,11 @@ class Service < ActiveRecord::Base ...@@ -92,7 +97,11 @@ class Service < ActiveRecord::Base
end end
def event_names def event_names
supported_events.map { |event| "#{event}_events" } self.class.event_names
end
def self.event_names
self.supported_events.map { |event| "#{event}_events" }
end end
def event_field(event) def event_field(event)
...@@ -104,6 +113,10 @@ class Service < ActiveRecord::Base ...@@ -104,6 +113,10 @@ class Service < ActiveRecord::Base
end end
def supported_events def supported_events
self.class.supported_events
end
def self.supported_events
%w(push tag_push issue confidential_issue merge_request wiki_page) %w(push tag_push issue confidential_issue merge_request wiki_page)
end end
......
...@@ -40,11 +40,13 @@ class PipelineEntity < Grape::Entity ...@@ -40,11 +40,13 @@ class PipelineEntity < Grape::Entity
end end
expose :path do |pipeline| expose :path do |pipeline|
if pipeline.ref
namespace_project_tree_path( namespace_project_tree_path(
pipeline.project.namespace, pipeline.project.namespace,
pipeline.project, pipeline.project,
id: pipeline.ref) id: pipeline.ref)
end end
end
expose :tag?, as: :tag expose :tag?, as: :tag
expose :branch?, as: :branch expose :branch?, as: :branch
......
...@@ -6,6 +6,14 @@ module Ci ...@@ -6,6 +6,14 @@ module Ci
runner.tick_runner_queue runner.tick_runner_queue
end end
end end
return unless build.project.shared_runners_enabled?
Ci::Runner.shared.each do |runner|
if runner.can_pick?(build)
runner.tick_runner_queue
end
end
end end
end end
end end
...@@ -38,15 +38,13 @@ module MergeRequests ...@@ -38,15 +38,13 @@ module MergeRequests
private private
def merge_requests_for(branch) # Returns all origin and fork merge requests from `@project` satisfying passed arguments.
origin_merge_requests = @project.origin_merge_requests def merge_requests_for(source_branch, mr_states: [:opened])
.opened.where(source_branch: branch).to_a MergeRequest
.with_state(mr_states)
fork_merge_requests = @project.fork_merge_requests .where(source_branch: source_branch, source_project_id: @project.id)
.opened.where(source_branch: branch).to_a .preload(:source_project) # we don't need a #includes since we're just preloading for the #select
.select(&:source_project)
(origin_merge_requests + fork_merge_requests)
.uniq.select(&:source_project)
end end
def pipeline_merge_requests(pipeline) def pipeline_merge_requests(pipeline)
......
...@@ -42,7 +42,7 @@ module MergeRequests ...@@ -42,7 +42,7 @@ module MergeRequests
commit_ids.include?(merge_request.diff_head_sha) commit_ids.include?(merge_request.diff_head_sha)
end end
merge_requests.uniq.select(&:source_project).each do |merge_request| filter_merge_requests(merge_requests).each do |merge_request|
MergeRequests::PostMergeService. MergeRequests::PostMergeService.
new(merge_request.target_project, @current_user). new(merge_request.target_project, @current_user).
execute(merge_request) execute(merge_request)
...@@ -58,10 +58,13 @@ module MergeRequests ...@@ -58,10 +58,13 @@ module MergeRequests
def reload_merge_requests def reload_merge_requests
merge_requests = @project.merge_requests.opened. merge_requests = @project.merge_requests.opened.
by_source_or_target_branch(@branch_name).to_a by_source_or_target_branch(@branch_name).to_a
merge_requests += fork_merge_requests
merge_requests = filter_merge_requests(merge_requests)
merge_requests.each do |merge_request| # Fork merge requests
merge_requests += MergeRequest.opened
.where(source_branch: @branch_name, source_project: @project)
.where.not(target_project: @project).to_a
filter_merge_requests(merge_requests).each do |merge_request|
if merge_request.source_branch == @branch_name || force_push? if merge_request.source_branch == @branch_name || force_push?
merge_request.reload_diff merge_request.reload_diff
else else
...@@ -175,16 +178,7 @@ module MergeRequests ...@@ -175,16 +178,7 @@ module MergeRequests
end end
def merge_requests_for_source_branch def merge_requests_for_source_branch
@source_merge_requests ||= begin @source_merge_requests ||= merge_requests_for(@branch_name)
merge_requests = @project.origin_merge_requests.opened.where(source_branch: @branch_name).to_a
merge_requests += fork_merge_requests
filter_merge_requests(merge_requests)
end
end
def fork_merge_requests
@fork_merge_requests ||= @project.fork_merge_requests.opened.
where(source_branch: @branch_name).to_a
end end
def branch_added? def branch_added?
......
- content_for :page_specific_javascripts do
= page_specific_javascript_tag('merge_request_widget/ci_bundle.js')
%h4 %h4
Set by #{link_to_member(@project, @merge_request.merge_user, avatar: true)} Set by #{link_to_member(@project, @merge_request.merge_user, avatar: true)}
to be merged automatically when the pipeline succeeds. to be merged automatically when the pipeline succeeds.
......
%button.choose-btn.btn.btn-sm.js-choose-group-avatar-button %button.choose-btn.btn.btn-sm.js-choose-group-avatar-button{ type: 'button' }
%i.fa.fa-paperclip %i.fa.fa-paperclip
%span Choose File ... %span Choose File ...
&nbsp; &nbsp;
......
...@@ -11,13 +11,13 @@ ...@@ -11,13 +11,13 @@
class: "check_all_issues left" class: "check_all_issues left"
.issues-other-filters.filtered-search-container .issues-other-filters.filtered-search-container
.filtered-search-input-container .filtered-search-input-container
%input.form-control.filtered-search{ placeholder: 'Search or filter results...', 'data-id' => 'filtered-search', 'data-project-id' => @project.id } %input.form-control.filtered-search{ placeholder: 'Search or filter results...', 'data-id' => 'filtered-search', 'data-project-id' => @project.id, 'data-username-params' => @users.to_json(only: [:id, :username]) }
= icon('filter') = icon('filter')
%button.clear-search.hidden{ type: 'button' } %button.clear-search.hidden{ type: 'button' }
= icon('times') = icon('times')
#js-dropdown-hint.dropdown-menu.hint-dropdown #js-dropdown-hint.dropdown-menu.hint-dropdown
%ul{ 'data-dropdown' => true } %ul{ 'data-dropdown' => true }
%li.filter-dropdown-item{ 'data-value' => '' } %li.filter-dropdown-item{ 'data-action' => 'submit' }
%button.btn.btn-link %button.btn.btn-link
= icon('search') = icon('search')
%span %span
...@@ -47,6 +47,10 @@ ...@@ -47,6 +47,10 @@
%li.filter-dropdown-item{ 'data-value' => 'none' } %li.filter-dropdown-item{ 'data-value' => 'none' }
%button.btn.btn-link %button.btn.btn-link
No Assignee No Assignee
- if current_user
%li.filter-dropdown-item{ 'data-value' => current_user.to_reference }
%button.btn.btn-link
Assigned to me
%li.divider %li.divider
%ul.filter-dropdown{ 'data-dynamic' => true, 'data-dropdown' => true } %ul.filter-dropdown{ 'data-dynamic' => true, 'data-dropdown' => true }
%li.filter-dropdown-item %li.filter-dropdown-item
...@@ -121,7 +125,13 @@ ...@@ -121,7 +125,13 @@
new MilestoneSelect(); new MilestoneSelect();
new IssueStatusSelect(); new IssueStatusSelect();
new SubscriptionSelect(); new SubscriptionSelect();
$('form.filter-form').on('submit', function (event) {
event.preventDefault(); $(document).off('page:restore').on('page:restore', function (event) {
Turbolinks.visit(this.action + '&' + $(this).serialize()); if (gl.FilteredSearchManager) {
new gl.FilteredSearchManager();
}
Issuable.init();
new gl.IssuableBulkActions({
prefixId: 'issue_',
});
}); });
---
title: Go to a project order
merge_request: 7737
author: Jacopo Beschi @jacopo-beschi
---
title: Fix double spaced CI log
merge_request: 8349
author: Jared Deckard <jared.deckard@gmail.com>
---
title: Allow group and project paths when transferring projects via the API
merge_request:
author:
---
title: Prevent empty pagination when list is not empty
merge_request: 8172
author:
---
title: Improve visibility of "Resolve conflicts" and "Merge locally" actions
merge_request: 8229
author:
---
title: Reduce DB-load for build-queues by storing last_update in Redis
merge_request: 8084
author:
---
title: Remove Lock Icon on Protected Tag
merge_request: 8513
author: Sergey Nikitin
---
title: Handle unsubscribe from email notifications via replying to reply+%{key}+unsubscribe@ address
merge_request: 6597
author:
---
title: Allow creating protected branches when user can merge to such branch
merge_request: 8458
author:
---
title: Adds service trigger events to api
merge_request: 8324
author:
---
title: Updated project visibility settings UX
merge_request: 7645
author:
---
title: Treat environments matching `production/*` as Production
merge_request: 8500
author:
---
title: 'Allows to search within project by commit hash'
merge_request:
author: YarNayar
---
title: ensure permalinks scroll to correct position on multiple clicks
merge_request: 8046
author:
---
title: Support slash comand `/merge` for merging merge requests.
merge_request: 7746
author: Jarka Kadlecova
---
title: Fix nested tasks in ordered list
merge_request: 8626
author:
---
title: Added number_with_delimiter to counter on milestone panels
merge_request:
author: Ryan Harris
---
title: Adds label to Environments "Date Created"
merge_request: 8376
author: Saad Shahd
---
title: Change status colors of runners to better defaults
merge_request:
author:
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.
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.
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.
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