Commit f9ace37a authored by Regis's avatar Regis

Merge branch 'master' into auto-pipelines-vue

parents 384ea58f b216d9bf
...@@ -249,7 +249,7 @@ teaspoon: ...@@ -249,7 +249,7 @@ teaspoon:
- curl --silent --location https://deb.nodesource.com/setup_6.x | bash - - curl --silent --location https://deb.nodesource.com/setup_6.x | bash -
- apt-get install --assume-yes nodejs - apt-get install --assume-yes nodejs
- npm install --global istanbul - npm install --global istanbul
- teaspoon - rake teaspoon
artifacts: artifacts:
name: coverage-javascript name: coverage-javascript
expire_in: 31d expire_in: 31d
......
...@@ -143,7 +143,7 @@ linters: ...@@ -143,7 +143,7 @@ linters:
# with two colons. Pseudo-classes, like :hover and :first-child, should # with two colons. Pseudo-classes, like :hover and :first-child, should
# be declared with one colon. # be declared with one colon.
PseudoElement: PseudoElement:
enabled: false enabled: true
# Avoid qualifying elements in selectors (also known as "tag-qualifying"). # Avoid qualifying elements in selectors (also known as "tag-qualifying").
QualifyingElement: QualifyingElement:
......
Please view this file on the master branch, on stable branches it's out of date. Please view this file on the master branch, on stable branches it's out of date.
## 8.14.0 (2016-11-22) ## 8.14.0 (2016-11-22)
- Backups do not fail anymore when using tar on annex and custom_hooks only. !5814
- Adds user project membership expired event to clarify why user was removed (Callum Dryden) - Adds user project membership expired event to clarify why user was removed (Callum Dryden)
- Trim leading and trailing whitespace on project_path (Linus Thiel) - Trim leading and trailing whitespace on project_path (Linus Thiel)
- Prevent award emoji via notes for issues/MRs authored by user (barthc) - Prevent award emoji via notes for issues/MRs authored by user (barthc)
- Adds an optional path parameter to the Commits API to filter commits by path (Luis HGO) - Adds an optional path parameter to the Commits API to filter commits by path (Luis HGO)
- Fix extra space on Build sidebar on Firefox !7060 - Fix extra space on Build sidebar on Firefox !7060
- Fix mobile layout issues in admin user overview page !7087
- Fix HipChat notifications rendering (airatshigapov, eisnerd) - Fix HipChat notifications rendering (airatshigapov, eisnerd)
- Refactor Jira service to use jira-ruby gem
- Add hover to trash icon in notes !7008 (blackst0ne) - Add hover to trash icon in notes !7008 (blackst0ne)
- Only show one error message for an invalid email !5905 (lycoperdon)
- Fix sidekiq stats in admin area (blackst0ne) - Fix sidekiq stats in admin area (blackst0ne)
- Created cycle analytics bundle JavaScript file
- API: Fix booleans not recognized as such when using the `to_boolean` helper
- Removed delete branch tooltip !6954
- Stop unauthorized users dragging on milestone page (blackst0ne)
- Restore issue boards welcome message when a project is created !6899
- Escape ref and path for relative links !6050 (winniehell) - Escape ref and path for relative links !6050 (winniehell)
- Fixed link typo on /help/ui to Alerts section. !6915 (Sam Rose) - Fixed link typo on /help/ui to Alerts section. !6915 (Sam Rose)
- Fix filtering of milestones with quotes in title (airatshigapov) - Fix filtering of milestones with quotes in title (airatshigapov)
- Refactor less readable existance checking code from CoffeeScript !6289 (jlogandavison) - Refactor less readable existance checking code from CoffeeScript !6289 (jlogandavison)
- Update mail_room and enable sentinel support to Reply By Email (!7101)
- Add task completion status in Issues and Merge Requests tabs: "X of Y tasks completed" (!6527, @gmesalazar)
- Simpler arguments passed to named_route on toggle_award_url helper method - Simpler arguments passed to named_route on toggle_award_url helper method
- Fix typo in framework css class. !7086 (Daniel Voogsgerd) - Fix typo in framework css class. !7086 (Daniel Voogsgerd)
- New issue board list dropdown stays open after adding a new list
- Fix: Backup restore doesn't clear cache - Fix: Backup restore doesn't clear cache
- API: Fix project deploy keys 400 and 500 errors when adding an existing key. !6784 (Joshua Welsh) - API: Fix project deploy keys 400 and 500 errors when adding an existing key. !6784 (Joshua Welsh)
- Replace jquery.cookie plugin with js.cookie !7085 - Replace jquery.cookie plugin with js.cookie !7085
- Use MergeRequestsClosingIssues cache data on Issue#closed_by_merge_requests method - Use MergeRequestsClosingIssues cache data on Issue#closed_by_merge_requests method
- Fix Sign in page 'Forgot your password?' link overlaps on medium-large screens - Fix Sign in page 'Forgot your password?' link overlaps on medium-large screens
- Show full status link on MR & commit pipelines
- Fix documents and comments on Build API `scope` - Fix documents and comments on Build API `scope`
- Refactor email, use setter method instead AR callbacks for email attribute (Semyon Pupkov) - Refactor email, use setter method instead AR callbacks for email attribute (Semyon Pupkov)
- Shortened merge request modal to let clipboard button not overlap
- In all filterable drop downs, put input field in focus only after load is complete (Ido @leibo)
## 8.13.2
- Fix builds dropdown overlapping bug !7124
- Fix applying labels for GitHub-imported MRs !7139
- Fix importing MR comments from GitHub !7139
- Modify GitHub importer to be retryable !7003
- Fix and improve `Sortable.highest_label_priority`
- Fixed sticky merge request tabs when sidebar is pinned
## 8.13.1 (2016-10-25) ## 8.13.1 (2016-10-25)
- Fix branch protection API. !6215 - Fix branch protection API. !6215
...@@ -72,6 +95,7 @@ Please view this file on the master branch, on stable branches it's out of date. ...@@ -72,6 +95,7 @@ Please view this file on the master branch, on stable branches it's out of date.
- Updating verbiage on git basics to be more intuitive - Updating verbiage on git basics to be more intuitive
- Fix project_feature record not generated on project creation - Fix project_feature record not generated on project creation
- Clarify documentation for Runners API (Gennady Trafimenkov) - Clarify documentation for Runners API (Gennady Trafimenkov)
- Use optimistic locking for pipelines and builds
- The instrumentation for Banzai::Renderer has been restored - The instrumentation for Banzai::Renderer has been restored
- Change user & group landing page routing from /u/:username to /:username - Change user & group landing page routing from /u/:username to /:username
- Added documentation for .gitattributes files - Added documentation for .gitattributes files
...@@ -97,6 +121,7 @@ Please view this file on the master branch, on stable branches it's out of date. ...@@ -97,6 +121,7 @@ Please view this file on the master branch, on stable branches it's out of date.
- Add more tests for calendar contribution (ClemMakesApps) - Add more tests for calendar contribution (ClemMakesApps)
- Update Gitlab Shell to fix some problems with moving projects between storages - Update Gitlab Shell to fix some problems with moving projects between storages
- Cache rendered markdown in the database, rather than Redis - Cache rendered markdown in the database, rather than Redis
- Add todo toggle event (ClemMakesApps)
- Avoid database queries on Banzai::ReferenceParser::BaseParser for nodes without references - Avoid database queries on Banzai::ReferenceParser::BaseParser for nodes without references
- Simplify Mentionable concern instance methods - Simplify Mentionable concern instance methods
- API: Ability to retrieve version information (Robert Schilling) - API: Ability to retrieve version information (Robert Schilling)
...@@ -138,6 +163,7 @@ Please view this file on the master branch, on stable branches it's out of date. ...@@ -138,6 +163,7 @@ Please view this file on the master branch, on stable branches it's out of date.
- Only update issuable labels if they have been changed - Only update issuable labels if they have been changed
- Take filters in account in issuable counters. !6496 - Take filters in account in issuable counters. !6496
- Use custom Ruby images to test builds (registry.dev.gitlab.org/gitlab/gitlab-build-images:*) - Use custom Ruby images to test builds (registry.dev.gitlab.org/gitlab/gitlab-build-images:*)
- Replace static issue fixtures by script !6059 (winniehell)
- Append issue template to existing description !6149 (Joseph Frazier) - Append issue template to existing description !6149 (Joseph Frazier)
- Trending projects now only show public projects and the list of projects is cached for a day - Trending projects now only show public projects and the list of projects is cached for a day
- Memoize Gitlab Shell's secret token (!6599, Justin DiPierro) - Memoize Gitlab Shell's secret token (!6599, Justin DiPierro)
......
...@@ -161,6 +161,9 @@ gem 'connection_pool', '~> 2.0' ...@@ -161,6 +161,9 @@ gem 'connection_pool', '~> 2.0'
# HipChat integration # HipChat integration
gem 'hipchat', '~> 1.5.0' gem 'hipchat', '~> 1.5.0'
# JIRA integration
gem 'jira-ruby', '~> 1.1.2'
# Flowdock integration # Flowdock integration
gem 'gitlab-flowdock-git-hook', '~> 1.0.1' gem 'gitlab-flowdock-git-hook', '~> 1.0.1'
...@@ -326,7 +329,7 @@ gem 'newrelic_rpm', '~> 3.16' ...@@ -326,7 +329,7 @@ gem 'newrelic_rpm', '~> 3.16'
gem 'octokit', '~> 4.3.0' gem 'octokit', '~> 4.3.0'
gem 'mail_room', '~> 0.8.1' gem 'mail_room', '~> 0.9.0'
gem 'email_reply_parser', '~> 0.5.8' gem 'email_reply_parser', '~> 0.5.8'
......
...@@ -356,6 +356,9 @@ GEM ...@@ -356,6 +356,9 @@ GEM
cause cause
json json
ipaddress (0.8.3) ipaddress (0.8.3)
jira-ruby (1.1.2)
activesupport
oauth (~> 0.5, >= 0.5.0)
jquery-atwho-rails (1.3.2) jquery-atwho-rails (1.3.2)
jquery-rails (4.1.1) jquery-rails (4.1.1)
rails-dom-testing (>= 1, < 3) rails-dom-testing (>= 1, < 3)
...@@ -402,7 +405,7 @@ GEM ...@@ -402,7 +405,7 @@ GEM
systemu (~> 2.6.2) systemu (~> 2.6.2)
mail (2.6.4) mail (2.6.4)
mime-types (>= 1.16, < 4) mime-types (>= 1.16, < 4)
mail_room (0.8.1) mail_room (0.9.0)
method_source (0.8.2) method_source (0.8.2)
mime-types (2.99.3) mime-types (2.99.3)
mimemagic (0.3.0) mimemagic (0.3.0)
...@@ -421,7 +424,7 @@ GEM ...@@ -421,7 +424,7 @@ GEM
mini_portile2 (~> 2.1.0) mini_portile2 (~> 2.1.0)
pkg-config (~> 1.1.7) pkg-config (~> 1.1.7)
numerizer (0.1.1) numerizer (0.1.1)
oauth (0.4.7) oauth (0.5.1)
oauth2 (1.2.0) oauth2 (1.2.0)
faraday (>= 0.8, < 0.10) faraday (>= 0.8, < 0.10)
jwt (~> 1.0) jwt (~> 1.0)
...@@ -881,6 +884,7 @@ DEPENDENCIES ...@@ -881,6 +884,7 @@ DEPENDENCIES
html-pipeline (~> 1.11.0) html-pipeline (~> 1.11.0)
httparty (~> 0.13.3) httparty (~> 0.13.3)
influxdb (~> 0.2) influxdb (~> 0.2)
jira-ruby (~> 1.1.2)
jquery-atwho-rails (~> 1.3.2) jquery-atwho-rails (~> 1.3.2)
jquery-rails (~> 4.1.0) jquery-rails (~> 4.1.0)
jquery-turbolinks (~> 2.1.0) jquery-turbolinks (~> 2.1.0)
...@@ -893,7 +897,7 @@ DEPENDENCIES ...@@ -893,7 +897,7 @@ DEPENDENCIES
license_finder (~> 2.1.0) license_finder (~> 2.1.0)
licensee (~> 8.0.0) licensee (~> 8.0.0)
loofah (~> 2.0.3) loofah (~> 2.0.3)
mail_room (~> 0.8.1) mail_room (~> 0.9.0)
method_source (~> 0.8) method_source (~> 0.8)
minitest (~> 5.7.0) minitest (~> 5.7.0)
mousetrap-rails (~> 1.4.6) mousetrap-rails (~> 1.4.6)
...@@ -994,4 +998,4 @@ DEPENDENCIES ...@@ -994,4 +998,4 @@ DEPENDENCIES
wikicloth (= 0.8.1) wikicloth (= 0.8.1)
BUNDLED WITH BUNDLED WITH
1.13.2 1.13.3
...@@ -56,6 +56,10 @@ There are various other options to install GitLab, please refer to the [installa ...@@ -56,6 +56,10 @@ There are various other options to install GitLab, please refer to the [installa
You can access a new installation with the login **`root`** and password **`5iveL!fe`**, after login you are required to set a unique password. You can access a new installation with the login **`root`** and password **`5iveL!fe`**, after login you are required to set a unique password.
## Contributing
GitLab is an open source project and we are very happy to accept community contributions. Please refer to [CONTRIBUTING.md](/CONTRIBUTING.md) for details.
## Install a development environment ## Install a development environment
To work on GitLab itself, we recommend setting up your development environment with [the GitLab Development Kit](https://gitlab.com/gitlab-org/gitlab-development-kit). To work on GitLab itself, we recommend setting up your development environment with [the GitLab Development Kit](https://gitlab.com/gitlab-org/gitlab-development-kit).
......
/* eslint-disable */
(() => { (() => {
const Store = gl.issueBoards.BoardsStore; const Store = gl.issueBoards.BoardsStore;
......
...@@ -33,6 +33,7 @@ $(() => { ...@@ -33,6 +33,7 @@ $(() => {
}, },
filterable: true, filterable: true,
selectable: true, selectable: true,
multiSelect: true,
clicked (label, $el, e) { clicked (label, $el, e) {
e.preventDefault(); e.preventDefault();
......
/* eslint-disable */
Vue.filter('due-date', (value) => { Vue.filter('due-date', (value) => {
const date = new Date(value); const date = new Date(value);
return $.datepicker.formatDate('M d, yy', date); return $.datepicker.formatDate('M d, yy', date);
......
/* eslint-disable */
class ListMilestone { class ListMilestone {
constructor (obj) { constructor (obj) {
this.id = obj.id; this.id = obj.id;
......
...@@ -63,7 +63,8 @@ ...@@ -63,7 +63,8 @@
this.removeList('blank'); this.removeList('blank');
Cookies.set('issue_board_welcome_hidden', 'true', { Cookies.set('issue_board_welcome_hidden', 'true', {
expires: 365 * 10 expires: 365 * 10,
path: ''
}); });
}, },
welcomeIsHidden () { welcomeIsHidden () {
......
...@@ -239,6 +239,7 @@ ...@@ -239,6 +239,7 @@
this.fullData = this.options.data; this.fullData = this.options.data;
currentIndex = -1; currentIndex = -1;
this.parseData(this.options.data); this.parseData(this.options.data);
this.focusTextInput();
} else { } else {
this.remote = new GitLabDropdownRemote(this.options.data, { this.remote = new GitLabDropdownRemote(this.options.data, {
dataType: this.options.dataType, dataType: this.options.dataType,
...@@ -247,6 +248,7 @@ ...@@ -247,6 +248,7 @@
return function(data) { return function(data) {
_this.fullData = data; _this.fullData = data;
_this.parseData(_this.fullData); _this.parseData(_this.fullData);
_this.focusTextInput();
if (_this.options.filterable && _this.filter && _this.filter.input) { if (_this.options.filterable && _this.filter && _this.filter.input) {
return _this.filter.input.trigger('input'); return _this.filter.input.trigger('input');
} }
...@@ -452,9 +454,8 @@ ...@@ -452,9 +454,8 @@
contentHtml = $('.dropdown-content', this.dropdown).html(); contentHtml = $('.dropdown-content', this.dropdown).html();
if (this.remote && contentHtml === "") { if (this.remote && contentHtml === "") {
this.remote.execute(); this.remote.execute();
} } else {
if (this.options.filterable) { this.focusTextInput();
this.filterInput.focus();
} }
if (this.options.showMenuAbove) { if (this.options.showMenuAbove) {
...@@ -691,6 +692,10 @@ ...@@ -691,6 +692,10 @@
return selectedObject; return selectedObject;
}; };
GitLabDropdown.prototype.focusTextInput = function() {
if (this.options.filterable) { this.filterInput.focus() }
}
GitLabDropdown.prototype.addInput = function(fieldName, value, selectedObject) { GitLabDropdown.prototype.addInput = function(fieldName, value, selectedObject) {
var $input; var $input;
// Create hidden input for form // Create hidden input for form
......
/* eslint-disable */
(function() {
$(document).on('todo:toggle', function(e, count) {
var $todoPendingCount = $('.todos-pending-count');
$todoPendingCount.text(gl.text.addDelimiter(count));
$todoPendingCount.toggleClass('hidden', count === 0);
});
})();
...@@ -95,7 +95,11 @@ ...@@ -95,7 +95,11 @@
return $.ajax({ return $.ajax({
type: 'PATCH', type: 'PATCH',
url: $('form.js-issuable-update').attr('action'), url: $('form.js-issuable-update').attr('action'),
data: patchData data: patchData,
success: function(issue) {
document.querySelector('#task_status').innerText = issue.task_status;
document.querySelector('#task_status_short').innerText = issue.task_status_short;
}
}); });
// TODO (rspeicher): Make the issue description inline-editable like a note so // TODO (rspeicher): Make the issue description inline-editable like a note so
// that we can re-use its form here // that we can re-use its form here
......
...@@ -8,6 +8,9 @@ ...@@ -8,6 +8,9 @@
if ((base = w.gl).text == null) { if ((base = w.gl).text == null) {
base.text = {}; base.text = {};
} }
gl.text.addDelimiter = function(text) {
return text ? text.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",") : text;
}
gl.text.randomString = function() { gl.text.randomString = function() {
return Math.random().toString(36).substring(7); return Math.random().toString(36).substring(7);
}; };
......
...@@ -97,7 +97,11 @@ ...@@ -97,7 +97,11 @@
return $.ajax({ return $.ajax({
type: 'PATCH', type: 'PATCH',
url: $('form.js-issuable-update').attr('action'), url: $('form.js-issuable-update').attr('action'),
data: patchData data: patchData,
success: function(mergeRequest) {
document.querySelector('#task_status').innerText = mergeRequest.task_status;
document.querySelector('#task_status_short').innerText = mergeRequest.task_status_short;
}
}); });
// TODO (rspeicher): Make the merge request description inline-editable like a // TODO (rspeicher): Make the merge request description inline-editable like a
// note so that we can re-use its form here // note so that we can re-use its form here
......
...@@ -82,16 +82,11 @@ ...@@ -82,16 +82,11 @@
}; };
Sidebar.prototype.todoUpdateDone = function(data, $btn, $btnText, $todoLoading) { Sidebar.prototype.todoUpdateDone = function(data, $btn, $btnText, $todoLoading) {
var $todoPendingCount; $(document).trigger('todo:toggle', data.count);
$todoPendingCount = $('.todos-pending-count');
$todoPendingCount.text(data.count);
$btn.enable(); $btn.enable();
$todoLoading.addClass('hidden'); $todoLoading.addClass('hidden');
if (data.count === 0) {
$todoPendingCount.addClass('hidden');
} else {
$todoPendingCount.removeClass('hidden');
}
if (data.delete_path != null) { if (data.delete_path != null) {
$btn.attr('aria-label', $btn.data('mark-text')).attr('data-delete-path', data.delete_path); $btn.attr('aria-label', $btn.data('mark-text')).attr('data-delete-path', data.delete_path);
return $btnText.text($btn.data('mark-text')); return $btnText.text($btn.data('mark-text'));
......
...@@ -38,7 +38,8 @@ ...@@ -38,7 +38,8 @@
.on('click', sidebarToggleSelector, () => this.toggleSidebar()) .on('click', sidebarToggleSelector, () => this.toggleSidebar())
.on('click', pinnedToggleSelector, () => this.togglePinnedState()) .on('click', pinnedToggleSelector, () => this.togglePinnedState())
.on('click', 'html, body', (e) => this.handleClickEvent(e)) .on('click', 'html, body', (e) => this.handleClickEvent(e))
.on('page:change', () => this.renderState()); .on('page:change', () => this.renderState())
.on('todo:toggle', (e, count) => this.updateTodoCount(count));
this.renderState(); this.renderState();
} }
...@@ -53,6 +54,10 @@ ...@@ -53,6 +54,10 @@
} }
} }
updateTodoCount(count) {
$('.js-todos-count').text(gl.text.addDelimiter(count));
}
toggleSidebar() { toggleSidebar() {
this.isExpanded = !this.isExpanded; this.isExpanded = !this.isExpanded;
this.renderState(); this.renderState();
......
...@@ -98,7 +98,8 @@ ...@@ -98,7 +98,8 @@
} }
updateBadges(data) { updateBadges(data) {
$('.todos-pending .badge, .todos-pending-count').text(data.count); $(document).trigger('todo:toggle', data.count);
$('.todos-pending .badge').text(data.count);
return $('.todos-done .badge').text(data.done_count); return $('.todos-done .badge').text(data.done_count);
} }
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
@media (max-width: $screen-xs-max) { @media (max-width: $screen-xs-max) {
width: 100%; width: 100%;
min-width: 240px;
} }
} }
...@@ -485,7 +486,7 @@ ...@@ -485,7 +486,7 @@
font-size: 20px; font-size: 20px;
text-indent: 0; text-indent: 0;
&:before { &::before {
display: block; display: block;
position: relative; position: relative;
top: -2px; top: -2px;
...@@ -517,7 +518,7 @@ ...@@ -517,7 +518,7 @@
background-color: transparent; background-color: transparent;
border: 0; border: 0;
.ui-icon:before { .ui-icon::before {
color: $md-link-color; color: $md-link-color;
} }
} }
...@@ -526,7 +527,7 @@ ...@@ -526,7 +527,7 @@
.ui-datepicker-prev { .ui-datepicker-prev {
left: 0; left: 0;
.ui-icon:before { .ui-icon::before {
content: '\f104'; content: '\f104';
text-align: left; text-align: left;
} }
...@@ -535,7 +536,7 @@ ...@@ -535,7 +536,7 @@
.ui-datepicker-next { .ui-datepicker-next {
right: 0; right: 0;
.ui-icon:before { .ui-icon::before {
content: '\f105'; content: '\f105';
text-align: right; text-align: right;
} }
......
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
border-bottom: 1px solid #eee; border-bottom: 1px solid #eee;
border-bottom: 1px solid rgba(0, 0, 0, 0.05); border-bottom: 1px solid rgba(0, 0, 0, 0.05);
&:after { &::after {
content: " "; content: " ";
display: table; display: table;
clear: both; clear: both;
...@@ -38,7 +38,7 @@ ...@@ -38,7 +38,7 @@
&.smoke { background-color: $background-color; } &.smoke { background-color: $background-color; }
&:hover { &:not(.ui-sort-disabled):hover {
background: $row-hover; background: $row-hover;
} }
......
...@@ -164,6 +164,18 @@ ...@@ -164,6 +164,18 @@
padding-left: $sidebar_width; padding-left: $sidebar_width;
} }
} }
.merge-request-tabs-holder.affix {
@media (min-width: $sidebar-breakpoint) {
left: $sidebar_width;
}
}
&.right-sidebar-expanded {
.line-resolve-all-container {
display: none;
}
}
} }
header.header-sidebar-pinned { header.header-sidebar-pinned {
......
...@@ -45,7 +45,7 @@ ...@@ -45,7 +45,7 @@
@media (max-width: $screen-xs-max) { @media (max-width: $screen-xs-max) {
.timeline { .timeline {
&:before { &::before {
background: none; background: none;
} }
......
...@@ -148,7 +148,7 @@ ...@@ -148,7 +148,7 @@
a[href*="/uploads/"], a[href*="/uploads/"],
a[href*="storage.googleapis.com/google-code-attachments/"] { a[href*="storage.googleapis.com/google-code-attachments/"] {
&:before { &::before {
margin-right: 4px; margin-right: 4px;
font: normal normal normal 14px/1 FontAwesome; font: normal normal normal 14px/1 FontAwesome;
...@@ -158,13 +158,13 @@ ...@@ -158,13 +158,13 @@
content: "\f0c6"; content: "\f0c6";
} }
&:hover:before { &:hover::before {
text-decoration: none; text-decoration: none;
} }
} }
a.no-attachment-icon { a.no-attachment-icon {
&:before { &::before {
display: none; display: none;
} }
} }
...@@ -183,13 +183,13 @@ ...@@ -183,13 +183,13 @@
position: absolute; position: absolute;
text-decoration: none; text-decoration: none;
&:after { &::after {
content: image-url('icon_anchor.svg'); content: image-url('icon_anchor.svg');
visibility: hidden; visibility: hidden;
} }
} }
&:hover > a.anchor:after { &:hover > a.anchor::after {
visibility: visible; visibility: visible;
} }
} }
......
...@@ -80,10 +80,13 @@ ...@@ -80,10 +80,13 @@
display: -webkit-flex; display: -webkit-flex;
display: -ms-flexbox; display: -ms-flexbox;
display: flex; display: flex;
white-space: nowrap;
} }
.user-details { .user-details {
flex: 1 1 auto; flex: 1 1 auto;
overflow: hidden;
padding-right: 8px;
} }
.user-name { .user-name {
...@@ -91,6 +94,12 @@ ...@@ -91,6 +94,12 @@
font-weight: 600; font-weight: 600;
} }
.user-name,
.user-email {
overflow: hidden;
text-overflow: ellipsis;
}
.dropdown { .dropdown {
.btn-block { .btn-block {
margin-bottom: 0; margin-bottom: 0;
......
...@@ -94,14 +94,14 @@ ...@@ -94,14 +94,14 @@
position: relative; position: relative;
&.old { &.old {
&:before { &::before {
content: '-'; content: '-';
position: absolute; position: absolute;
} }
} }
&.new { &.new {
&:before { &::before {
content: '+'; content: '+';
position: absolute; position: absolute;
} }
...@@ -471,7 +471,7 @@ ...@@ -471,7 +471,7 @@
.file-holder { .file-holder {
.diff-line-num:not(.js-unfold-bottom) { .diff-line-num:not(.js-unfold-bottom) {
a { a {
&:before { &::before {
content: attr(data-linenumber); content: attr(data-linenumber);
} }
} }
......
...@@ -279,6 +279,10 @@ ...@@ -279,6 +279,10 @@
#modal_merge_info .modal-dialog { #modal_merge_info .modal-dialog {
width: 600px; width: 600px;
.dark {
margin-right: 40px;
}
.btn-clipboard { .btn-clipboard {
@extend .pull-right; @extend .pull-right;
...@@ -439,12 +443,12 @@ ...@@ -439,12 +443,12 @@
} }
.merge-request-tabs-holder { .merge-request-tabs-holder {
background-color: #fff; background-color: $white-light;
&.affix { &.affix {
top: 100px; top: 100px;
left: 0; left: 0;
z-index: 9; z-index: 10;
transition: right .15s; transition: right .15s;
} }
......
...@@ -474,8 +474,8 @@ ...@@ -474,8 +474,8 @@
} }
.arrow { .arrow {
&:before, &::before,
&:after { &::after {
content: ''; content: '';
display: inline-block; display: inline-block;
position: absolute; position: absolute;
...@@ -486,14 +486,14 @@ ...@@ -486,14 +486,14 @@
top: 18px; top: 18px;
} }
&:before { &::before {
left: -5px; left: -5px;
margin-top: -6px; margin-top: -6px;
border-width: 7px 5px 7px 0; border-width: 7px 5px 7px 0;
border-right-color: $border-color; border-right-color: $border-color;
} }
&:after { &::after {
left: -4px; left: -4px;
margin-top: -9px; margin-top: -9px;
border-width: 10px 7px 10px 0; border-width: 10px 7px 10px 0;
...@@ -573,8 +573,7 @@ ...@@ -573,8 +573,7 @@
.build { .build {
// Remove right connecting horizontal line from first build in last stage // Remove right connecting horizontal line from first build in last stage
&:first-child { &:first-child {
&::after, &::after {
&::before {
border: none; border: none;
} }
} }
......
...@@ -77,14 +77,14 @@ ...@@ -77,14 +77,14 @@
// Middle dot divider between each element in a list of items. // Middle dot divider between each element in a list of items.
.middle-dot-divider { .middle-dot-divider {
&:after { &::after {
content: "\00B7"; // Middle Dot content: "\00B7"; // Middle Dot
padding: 0 6px; padding: 0 6px;
font-weight: bold; font-weight: bold;
} }
&:last-child { &:last-child {
&:after { &::after {
content: ""; content: "";
padding: 0; padding: 0;
} }
......
...@@ -193,7 +193,7 @@ ...@@ -193,7 +193,7 @@
margin-left: 4px; margin-left: 4px;
.arrow { .arrow {
&:before { &::before {
content: ''; content: '';
display: inline-block; display: inline-block;
position: absolute; position: absolute;
...@@ -209,7 +209,7 @@ ...@@ -209,7 +209,7 @@
pointer-events: none; pointer-events: none;
} }
&:after { &::after {
content: ''; content: '';
position: absolute; position: absolute;
width: 0; width: 0;
...@@ -351,7 +351,7 @@ a.deploy-project-label { ...@@ -351,7 +351,7 @@ a.deploy-project-label {
line-height: 36px; line-height: 36px;
margin: 0; margin: 0;
> li + li:before { > li + li::before {
padding: 0 3px; padding: 0 3px;
color: #999; color: #999;
} }
...@@ -790,7 +790,7 @@ pre.light-well { ...@@ -790,7 +790,7 @@ pre.light-well {
top: 7px; top: 7px;
color: $location-icon-color; color: $location-icon-color;
&:before { &::before {
font-family: FontAwesome; font-family: FontAwesome;
font-weight: normal; font-weight: normal;
font-style: normal; font-style: normal;
......
...@@ -72,7 +72,7 @@ ...@@ -72,7 +72,7 @@
top: 0; top: 0;
color: $location-icon-color; color: $location-icon-color;
&:before { &::before {
font-family: FontAwesome; font-family: FontAwesome;
font-weight: normal; font-weight: normal;
font-style: normal; font-style: normal;
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
} }
.example { .example {
&:before { &::before {
content: "Example"; content: "Example";
color: #bbb; color: #bbb;
} }
......
...@@ -35,7 +35,7 @@ nav.navbar-collapse.collapse, ...@@ -35,7 +35,7 @@ nav.navbar-collapse.collapse,
.nav, .nav,
.btn, .btn,
ul.notes-form, ul.notes-form,
.merge-request-ci-status .ci-status-link:after, .merge-request-ci-status .ci-status-link::after,
.issuable-gutter-toggle, .issuable-gutter-toggle,
.gutter-toggle, .gutter-toggle,
.issuable-details .content-block-small, .issuable-details .content-block-small,
......
...@@ -18,7 +18,7 @@ module ServiceParams ...@@ -18,7 +18,7 @@ module ServiceParams
:add_pusher, :send_from_committer_email, :disable_diffs, :add_pusher, :send_from_committer_email, :disable_diffs,
:external_wiki_url, :notify, :color, :external_wiki_url, :notify, :color,
:server_host, :server_port, :default_irc_uri, :enable_ssl_verification, :server_host, :server_port, :default_irc_uri, :enable_ssl_verification,
:jira_issue_transition_id] :jira_issue_transition_id, :url, :project_key]
# Parameters to ignore if no value is specified # Parameters to ignore if no value is specified
FILTER_BLANK_PARAMS = [:password] FILTER_BLANK_PARAMS = [:password]
......
...@@ -112,7 +112,7 @@ class Projects::IssuesController < Projects::ApplicationController ...@@ -112,7 +112,7 @@ class Projects::IssuesController < Projects::ApplicationController
end end
format.json do format.json do
render json: @issue.to_json(include: { milestone: {}, assignee: { methods: :avatar_url }, labels: { methods: :text_color } }) render json: @issue.to_json(include: { milestone: {}, assignee: { methods: :avatar_url }, labels: { methods: :text_color } }, methods: [:task_status, :task_status_short])
end end
end end
......
...@@ -278,7 +278,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -278,7 +278,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
@merge_request.target_project, @merge_request]) @merge_request.target_project, @merge_request])
end end
format.json do format.json do
render json: @merge_request.to_json(include: { milestone: {}, assignee: { methods: :avatar_url }, labels: { methods: :text_color } }) render json: @merge_request.to_json(include: { milestone: {}, assignee: { methods: :avatar_url }, labels: { methods: :text_color } }, methods: [:task_status, :task_status_short])
end end
end end
else else
......
...@@ -30,6 +30,8 @@ class ProjectsController < Projects::ApplicationController ...@@ -30,6 +30,8 @@ class ProjectsController < Projects::ApplicationController
@project = ::Projects::CreateService.new(current_user, project_params).execute @project = ::Projects::CreateService.new(current_user, project_params).execute
if @project.saved? if @project.saved?
cookies[:issue_board_welcome_hidden] = { path: project_path(@project), value: nil, expires: Time.at(0) }
redirect_to( redirect_to(
project_path(@project), project_path(@project),
notice: "Project '#{@project.name}' was successfully created." notice: "Project '#{@project.name}' was successfully created."
......
...@@ -4,9 +4,8 @@ class LabelsFinder < UnionFinder ...@@ -4,9 +4,8 @@ class LabelsFinder < UnionFinder
@params = params @params = params
end end
def execute(authorized_only: true) def execute(skip_authorization: false)
@authorized_only = authorized_only @skip_authorization = skip_authorization
items = find_union(label_ids, Label) items = find_union(label_ids, Label)
items = with_title(items) items = with_title(items)
sort(items) sort(items)
...@@ -14,7 +13,7 @@ class LabelsFinder < UnionFinder ...@@ -14,7 +13,7 @@ class LabelsFinder < UnionFinder
private private
attr_reader :current_user, :params, :authorized_only attr_reader :current_user, :params, :skip_authorization
def label_ids def label_ids
label_ids = [] label_ids = []
...@@ -70,17 +69,17 @@ class LabelsFinder < UnionFinder ...@@ -70,17 +69,17 @@ class LabelsFinder < UnionFinder
end end
def find_project def find_project
if authorized_only if skip_authorization
available_projects.find_by(id: project_id)
else
Project.find_by(id: project_id) Project.find_by(id: project_id)
else
available_projects.find_by(id: project_id)
end end
end end
def projects def projects
return @projects if defined?(@projects) return @projects if defined?(@projects)
@projects = authorized_only ? available_projects : Project.all @projects = skip_authorization ? Project.all : available_projects
@projects = @projects.in_namespace(group_id) if group_id @projects = @projects.in_namespace(group_id) if group_id
@projects = @projects.where(id: projects_ids) if projects_ids @projects = @projects.where(id: projects_ids) if projects_ids
@projects = @projects.reorder(nil) @projects = @projects.reorder(nil)
......
...@@ -71,6 +71,14 @@ module IssuablesHelper ...@@ -71,6 +71,14 @@ module IssuablesHelper
author_output = link_to_member(project, issuable.author, size: 24, mobile_classes: "hidden-xs", tooltip: true) author_output = link_to_member(project, issuable.author, size: 24, mobile_classes: "hidden-xs", tooltip: true)
author_output << link_to_member(project, issuable.author, size: 24, by_username: true, avatar: false, mobile_classes: "hidden-sm hidden-md hidden-lg") author_output << link_to_member(project, issuable.author, size: 24, by_username: true, avatar: false, mobile_classes: "hidden-sm hidden-md hidden-lg")
end end
if issuable.tasks?
output << "&ensp;".html_safe
output << content_tag(:span, issuable.task_status, id: "task_status", class: "hidden-xs")
output << content_tag(:span, issuable.task_status_short, id: "task_status_short", class: "hidden-sm hidden-md hidden-lg")
end
output
end end
def issuable_todo(issuable) def issuable_todo(issuable)
......
...@@ -30,23 +30,23 @@ module Ci ...@@ -30,23 +30,23 @@ module Ci
end end
event :run do event :run do
transition any => :running transition any - [:running] => :running
end end
event :skip do event :skip do
transition any => :skipped transition any - [:skipped] => :skipped
end end
event :drop do event :drop do
transition any => :failed transition any - [:failed] => :failed
end end
event :succeed do event :succeed do
transition any => :success transition any - [:success] => :success
end end
event :cancel do event :cancel do
transition any => :canceled transition any - [:canceled] => :canceled
end end
# IMPORTANT # IMPORTANT
...@@ -260,7 +260,7 @@ module Ci ...@@ -260,7 +260,7 @@ module Ci
end end
def update_status def update_status
with_lock do Gitlab::OptimisticLocking.retry_lock(self) do
case latest_builds_status case latest_builds_status
when 'pending' then enqueue when 'pending' then enqueue
when 'running' then run when 'running' then run
......
...@@ -73,16 +73,16 @@ class CommitStatus < ActiveRecord::Base ...@@ -73,16 +73,16 @@ class CommitStatus < ActiveRecord::Base
transition [:created, :pending, :running] => :canceled transition [:created, :pending, :running] => :canceled
end end
after_transition created: [:pending, :running] do |commit_status| before_transition created: [:pending, :running] do |commit_status|
commit_status.update_attributes queued_at: Time.now commit_status.queued_at = Time.now
end end
after_transition [:created, :pending] => :running do |commit_status| before_transition [:created, :pending] => :running do |commit_status|
commit_status.update_attributes started_at: Time.now commit_status.started_at = Time.now
end end
after_transition any => [:success, :failed, :canceled] do |commit_status| before_transition any => [:success, :failed, :canceled] do |commit_status|
commit_status.update_attributes finished_at: Time.now commit_status.finished_at = Time.now
end end
after_transition do |commit_status, transition| after_transition do |commit_status, transition|
......
...@@ -12,6 +12,7 @@ module Issuable ...@@ -12,6 +12,7 @@ module Issuable
include Subscribable include Subscribable
include StripAttribute include StripAttribute
include Awardable include Awardable
include Taskable
included do included do
cache_markdown_field :title, pipeline: :single_line cache_markdown_field :title, pipeline: :single_line
......
...@@ -38,16 +38,21 @@ module Sortable ...@@ -38,16 +38,21 @@ module Sortable
private private
def highest_label_priority(target_type:, target_column:, project_column:, excluded_labels: []) def highest_label_priority(target_type_column: nil, target_type: nil, target_column:, project_column:, excluded_labels: [])
query = Label.select(LabelPriority.arel_table[:priority].minimum). query = Label.select(LabelPriority.arel_table[:priority].minimum).
left_join_priorities. left_join_priorities.
joins(:label_links). joins(:label_links).
where("label_priorities.project_id = #{project_column}"). where("label_priorities.project_id = #{project_column}").
where(label_links: { target_type: target_type }).
where("label_links.target_id = #{target_column}"). where("label_links.target_id = #{target_column}").
reorder(nil) reorder(nil)
query.where.not(title: excluded_labels) if excluded_labels.present? if target_type_column
query = query.where("label_links.target_type = #{target_type_column}")
else
query = query.where(label_links: { target_type: target_type })
end
query = query.where.not(title: excluded_labels) if excluded_labels.present?
query query
end end
......
...@@ -53,10 +53,22 @@ module Taskable ...@@ -53,10 +53,22 @@ module Taskable
# Return a string that describes the current state of this Taskable's task # Return a string that describes the current state of this Taskable's task
# list items, e.g. "12 of 20 tasks completed" # list items, e.g. "12 of 20 tasks completed"
def task_status def task_status(short: false)
return '' if description.blank? return '' if description.blank?
prep, completed = if short
['/', '']
else
[' of ', ' completed']
end
sum = tasks.summary sum = tasks.summary
"#{sum.complete_count} of #{sum.item_count} #{'task'.pluralize(sum.item_count)} completed" "#{sum.complete_count}#{prep}#{sum.item_count} #{'task'.pluralize(sum.item_count)}#{completed}"
end
# Return a short string that describes the current state of this Taskable's
# task list items -- for small screens
def task_status_short
task_status(short: true)
end end
end end
...@@ -68,7 +68,7 @@ class Group < Namespace ...@@ -68,7 +68,7 @@ class Group < Namespace
end end
def web_url def web_url
Gitlab::Routing.url_helpers.group_url(self) Gitlab::Routing.url_helpers.group_canonical_url(self)
end end
def human_name def human_name
......
...@@ -5,7 +5,6 @@ class Issue < ActiveRecord::Base ...@@ -5,7 +5,6 @@ class Issue < ActiveRecord::Base
include Issuable include Issuable
include Referable include Referable
include Sortable include Sortable
include Taskable
include Spammable include Spammable
include FasterCacheKeys include FasterCacheKeys
......
...@@ -3,7 +3,6 @@ class MergeRequest < ActiveRecord::Base ...@@ -3,7 +3,6 @@ class MergeRequest < ActiveRecord::Base
include Issuable include Issuable
include Referable include Referable
include Sortable include Sortable
include Taskable
include Importable include Importable
belongs_to :target_project, class_name: "Project" belongs_to :target_project, class_name: "Project"
......
...@@ -738,7 +738,7 @@ class Project < ActiveRecord::Base ...@@ -738,7 +738,7 @@ class Project < ActiveRecord::Base
def create_labels def create_labels
Label.templates.each do |label| Label.templates.each do |label|
params = label.attributes.except('id', 'template', 'created_at', 'updated_at') params = label.attributes.except('id', 'template', 'created_at', 'updated_at')
Labels::FindOrCreateService.new(owner, self, params).execute Labels::FindOrCreateService.new(nil, self, params).execute(skip_authorization: true)
end end
end end
......
class BugzillaService < IssueTrackerService class BugzillaService < IssueTrackerService
validates :project_url, :issues_url, :new_issue_url, presence: true, url: true, if: :activated?
prop_accessor :title, :description, :project_url, :issues_url, :new_issue_url prop_accessor :title, :description, :project_url, :issues_url, :new_issue_url
def title def title
......
class CustomIssueTrackerService < IssueTrackerService class CustomIssueTrackerService < IssueTrackerService
validates :project_url, :issues_url, :new_issue_url, presence: true, url: true, if: :activated?
prop_accessor :title, :description, :project_url, :issues_url, :new_issue_url prop_accessor :title, :description, :project_url, :issues_url, :new_issue_url
def title def title
......
class GitlabIssueTrackerService < IssueTrackerService class GitlabIssueTrackerService < IssueTrackerService
include Gitlab::Routing.url_helpers include Gitlab::Routing.url_helpers
validates :project_url, :issues_url, :new_issue_url, presence: true, url: true, if: :activated?
prop_accessor :title, :description, :project_url, :issues_url, :new_issue_url prop_accessor :title, :description, :project_url, :issues_url, :new_issue_url
default_value_for :default, true default_value_for :default, true
......
class IssueTrackerService < Service class IssueTrackerService < Service
validates :project_url, :issues_url, :new_issue_url, presence: true, url: true, if: :activated?
default_value_for :category, 'issue_tracker' default_value_for :category, 'issue_tracker'
# Pattern used to extract links from comments # Pattern used to extract links from comments
...@@ -38,18 +36,24 @@ class IssueTrackerService < Service ...@@ -38,18 +36,24 @@ class IssueTrackerService < Service
] ]
end end
def initialize_properties # Initialize with default properties values
if properties.nil? # or receive a block with custom properties
if enabled_in_gitlab_config def initialize_properties(&block)
return unless properties.nil?
if enabled_in_gitlab_config
if block_given?
yield
else
self.properties = { self.properties = {
title: issues_tracker['title'], title: issues_tracker['title'],
project_url: issues_tracker['project_url'], project_url: issues_tracker['project_url'],
issues_url: issues_tracker['issues_url'], issues_url: issues_tracker['issues_url'],
new_issue_url: issues_tracker['new_issue_url'] new_issue_url: issues_tracker['new_issue_url']
} }
else
self.properties = {}
end end
else
self.properties = {}
end end
end end
......
This diff is collapsed.
class RedmineService < IssueTrackerService class RedmineService < IssueTrackerService
validates :project_url, :issues_url, :new_issue_url, presence: true, url: true, if: :activated?
prop_accessor :title, :description, :project_url, :issues_url, :new_issue_url prop_accessor :title, :description, :project_url, :issues_url, :new_issue_url
def title def title
......
...@@ -125,14 +125,8 @@ class ProjectTeam ...@@ -125,14 +125,8 @@ class ProjectTeam
max_member_access(user.id) == Gitlab::Access::MASTER max_member_access(user.id) == Gitlab::Access::MASTER
end end
def member?(user, min_member_access = nil) def member?(user, min_member_access = Gitlab::Access::GUEST)
member = !!find_member(user.id) max_member_access(user.id) >= min_member_access
if min_member_access
member && max_member_access(user.id) >= min_member_access
else
member
end
end end
def human_max_access(user_id) def human_max_access(user_id)
......
...@@ -11,6 +11,20 @@ class Repository ...@@ -11,6 +11,20 @@ class Repository
attr_accessor :path_with_namespace, :project attr_accessor :path_with_namespace, :project
def self.storages
Gitlab.config.repositories.storages
end
def self.remove_storage_from_path(repo_path)
storages.find do |_, storage_path|
if repo_path.start_with?(storage_path)
return repo_path.sub(storage_path, '')
end
end
repo_path
end
def initialize(path_with_namespace, project) def initialize(path_with_namespace, project)
@path_with_namespace = path_with_namespace @path_with_namespace = path_with_namespace
@project = project @project = project
......
...@@ -53,7 +53,7 @@ class Todo < ActiveRecord::Base ...@@ -53,7 +53,7 @@ class Todo < ActiveRecord::Base
# Need to order by created_at last because of differences on Mysql and Postgres when joining by type "Merge_request/Issue" # Need to order by created_at last because of differences on Mysql and Postgres when joining by type "Merge_request/Issue"
def order_by_labels_priority def order_by_labels_priority
params = { params = {
target_type: ['Issue', 'MergeRequest'], target_type_column: "todos.target_type",
target_column: "todos.target_id", target_column: "todos.target_id",
project_column: "todos.project_id" project_column: "todos.project_id"
} }
......
...@@ -93,8 +93,10 @@ class User < ActiveRecord::Base ...@@ -93,8 +93,10 @@ class User < ActiveRecord::Base
# #
# Validations # Validations
# #
# Note: devise :validatable above adds validations for :email and :password
validates :name, presence: true validates :name, presence: true
validates :notification_email, presence: true, email: true validates :notification_email, presence: true
validates :notification_email, email: true, if: ->(user) { user.notification_email != user.email }
validates :public_email, presence: true, uniqueness: true, email: true, allow_blank: true validates :public_email, presence: true, uniqueness: true, email: true, allow_blank: true
validates :bio, length: { maximum: 255 }, allow_blank: true validates :bio, length: { maximum: 255 }, allow_blank: true
validates :projects_limit, presence: true, numericality: { greater_than_or_equal_to: 0 } validates :projects_limit, presence: true, numericality: { greater_than_or_equal_to: 0 }
......
...@@ -10,17 +10,14 @@ module Ci ...@@ -10,17 +10,14 @@ module Ci
create_builds! create_builds!
end end
@pipeline.with_lock do new_builds =
new_builds = stage_indexes_of_created_builds.map do |index|
stage_indexes_of_created_builds.map do |index| process_stage(index)
process_stage(index) end
end
@pipeline.update_status @pipeline.update_status
# Return a flag if a when builds got enqueued new_builds.flatten.any?
new_builds.flatten.any?
end
end end
private private
...@@ -32,9 +29,11 @@ module Ci ...@@ -32,9 +29,11 @@ module Ci
def process_stage(index) def process_stage(index)
current_status = status_for_prior_stages(index) current_status = status_for_prior_stages(index)
created_builds_in_stage(index).select do |build| if HasStatus::COMPLETED_STATUSES.include?(current_status)
if HasStatus::COMPLETED_STATUSES.include?(current_status) created_builds_in_stage(index).select do |build|
process_build(build, current_status) Gitlab::OptimisticLocking.retry_lock(build) do |subject|
process_build(subject, current_status)
end
end end
end end
end end
......
...@@ -28,17 +28,14 @@ module Ci ...@@ -28,17 +28,14 @@ module Ci
if build if build
# In case when 2 runners try to assign the same build, second runner will be declined # In case when 2 runners try to assign the same build, second runner will be declined
# with StateMachines::InvalidTransition in run! method. # with StateMachines::InvalidTransition or StaleObjectError when doing run! or save method.
build.with_lock do build.runner_id = current_runner.id
build.runner_id = current_runner.id build.run!
build.save!
build.run!
end
end end
build build
rescue StateMachines::InvalidTransition rescue StateMachines::InvalidTransition, ActiveRecord::StaleObjectError
nil nil
end end
......
...@@ -2,21 +2,24 @@ module Labels ...@@ -2,21 +2,24 @@ module Labels
class FindOrCreateService class FindOrCreateService
def initialize(current_user, project, params = {}) def initialize(current_user, project, params = {})
@current_user = current_user @current_user = current_user
@group = project.group
@project = project @project = project
@params = params.dup @params = params.dup
end end
def execute def execute(skip_authorization: false)
@skip_authorization = skip_authorization
find_or_create_label find_or_create_label
end end
private private
attr_reader :current_user, :group, :project, :params attr_reader :current_user, :project, :params, :skip_authorization
def available_labels def available_labels
@available_labels ||= LabelsFinder.new(current_user, project_id: project.id).execute @available_labels ||= LabelsFinder.new(
current_user,
project_id: project.id
).execute(skip_authorization: skip_authorization)
end end
def find_or_create_label def find_or_create_label
......
...@@ -4,17 +4,25 @@ module Members ...@@ -4,17 +4,25 @@ module Members
attr_accessor :source attr_accessor :source
# source - The source object that respond to `#requesters` (i.g. project or group)
# current_user - The user that performs the access request approval
# params - A hash of parameters
# :user_id - User ID used to retrieve the access requester
# :id - Member ID used to retrieve the access requester
# :access_level - Optional access level set when the request is accepted
def initialize(source, current_user, params = {}) def initialize(source, current_user, params = {})
@source = source @source = source
@current_user = current_user @current_user = current_user
@params = params @params = params.slice(:user_id, :id, :access_level)
end end
def execute # opts - A hash of options
# :force - Bypass permission check: current_user can be nil in that case
def execute(opts = {})
condition = params[:user_id] ? { user_id: params[:user_id] } : { id: params[:id] } condition = params[:user_id] ? { user_id: params[:user_id] } : { id: params[:id] }
access_requester = source.requesters.find_by!(condition) access_requester = source.requesters.find_by!(condition)
raise Gitlab::Access::AccessDeniedError unless can_update_access_requester?(access_requester) raise Gitlab::Access::AccessDeniedError unless can_update_access_requester?(access_requester, opts)
access_requester.access_level = params[:access_level] if params[:access_level] access_requester.access_level = params[:access_level] if params[:access_level]
access_requester.accept_request access_requester.accept_request
...@@ -24,8 +32,11 @@ module Members ...@@ -24,8 +32,11 @@ module Members
private private
def can_update_access_requester?(access_requester) def can_update_access_requester?(access_requester, opts = {})
access_requester && can?(current_user, action_member_permission(:update, access_requester), access_requester) access_requester && (
opts[:force] ||
can?(current_user, action_member_permission(:update, access_requester), access_requester)
)
end end
end end
end end
...@@ -29,7 +29,7 @@ module Projects ...@@ -29,7 +29,7 @@ module Projects
if unknown_url? if unknown_url?
# In this case, we only want to import issues, not a repository. # In this case, we only want to import issues, not a repository.
create_repository create_repository
else elsif !project.repository_exists?
import_repository import_repository
end end
end end
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
= link_to dashboard_todos_path, title: 'Todos' do = link_to dashboard_todos_path, title: 'Todos' do
%span %span
Todos Todos
%span.count= number_with_delimiter(todos_pending_count) %span.count.js-todos-count= number_with_delimiter(todos_pending_count)
= nav_link(path: 'dashboard#activity') do = nav_link(path: 'dashboard#activity') do
= link_to activity_dashboard_path, class: 'dashboard-shortcuts-activity', title: 'Activity' do = link_to activity_dashboard_path, class: 'dashboard-shortcuts-activity', title: 'Activity' do
%span %span
......
...@@ -31,7 +31,12 @@ ...@@ -31,7 +31,12 @@
= render 'projects/buttons/download', project: @project, ref: branch.name = render 'projects/buttons/download', project: @project, ref: branch.name
- if can?(current_user, :push_code, @project) - if can?(current_user, :push_code, @project)
= link_to namespace_project_branch_path(@project.namespace, @project, branch.name), class: "btn btn-remove remove-row has-tooltip #{can_remove_branch?(@project, branch.name) ? '' : 'disabled'}", title: "Delete branch", method: :delete, data: { confirm: "Deleting the '#{branch.name}' branch cannot be undone. Are you sure?", container: 'body' }, remote: true do = link_to namespace_project_branch_path(@project.namespace, @project, branch.name),
class: "btn btn-remove remove-row #{can_remove_branch?(@project, branch.name) ? '' : 'disabled'}",
method: :delete,
data: { confirm: "Deleting the '#{branch.name}' branch cannot be undone. Are you sure?" },
remote: true,
"aria-label" => "Delete branch" do
= icon("trash-o") = icon("trash-o")
- if branch.name != @repository.root_ref - if branch.name != @repository.root_ref
......
- is_playable = subject.playable? && can?(current_user, :update_build, @project) - is_playable = subject.playable? && can?(current_user, :update_build, @project)
- if is_playable - if is_playable
= link_to play_namespace_project_build_path(subject.project.namespace, subject.project, subject, return_to: request.original_url), method: :post, data: { toggle: 'tooltip', title: "#{subject.name} - play", container: '.pipeline-graph', placement: 'bottom' } do = link_to play_namespace_project_build_path(subject.project.namespace, subject.project, subject, return_to: request.original_url), method: :post, data: { toggle: 'tooltip', title: "#{subject.name} - play", container: '.pipeline-graph', placement: 'bottom' } do
= render_status_with_link('build', 'play') = ci_icon_for_status('play')
.ci-status-text= subject.name .ci-status-text= subject.name
- elsif can?(current_user, :read_build, @project) - elsif can?(current_user, :read_build, @project)
= link_to namespace_project_build_path(subject.project.namespace, subject.project, subject), data: { toggle: 'tooltip', title: "#{subject.name} - #{subject.status}", container: '.pipeline-graph', placement: 'bottom' } do = link_to namespace_project_build_path(subject.project.namespace, subject.project, subject), data: { toggle: 'tooltip', title: "#{subject.name} - #{subject.status}", container: '.pipeline-graph', placement: 'bottom' } do
%span.ci-status-icon %span.ci-status-icon
= render_status_with_link('build', subject.status) = ci_icon_for_status(subject.status)
.ci-status-text= subject.name .ci-status-text= subject.name
- else - else
%span.ci-status-icon %span.ci-status-icon
= render_status_with_link('build', subject.status) = ci_icon_for_status(subject.status)
= ci_icon_for_status(subject.status)
...@@ -5,10 +5,7 @@ ...@@ -5,10 +5,7 @@
%tr.commit %tr.commit
%td.commit-link %td.commit-link
= link_to namespace_project_pipeline_path(pipeline.project.namespace, pipeline.project, pipeline.id) do = link_to namespace_project_pipeline_path(pipeline.project.namespace, pipeline.project, pipeline.id) do
- if defined?(status_icon_only) && status_icon_only = ci_status_with_icon(status)
= ci_icon_for_status(status)
- else
= ci_status_with_icon(status)
%td %td
= link_to namespace_project_pipeline_path(pipeline.project.namespace, pipeline.project, pipeline.id) do = link_to namespace_project_pipeline_path(pipeline.project.namespace, pipeline.project, pipeline.id) do
...@@ -40,7 +37,7 @@ ...@@ -40,7 +37,7 @@
%p.commit-title %p.commit-title
- if commit = pipeline.commit - if commit = pipeline.commit
= author_avatar(commit, size: 20) = author_avatar(commit, size: 20)
= link_to_gfm truncate(commit.title, length: 60), namespace_project_commit_path(pipeline.project.namespace, pipeline.project, commit.id), class: "commit-row-message" = link_to_gfm truncate(commit.title, length: 60, escape: false), namespace_project_commit_path(pipeline.project.namespace, pipeline.project, commit.id), class: "commit-row-message"
- else - else
Cant find HEAD commit for this branch Cant find HEAD commit for this branch
......
- group_status = CommitStatus.where(id: subject).status - group_status = CommitStatus.where(id: subject).status
%button.dropdown-menu-toggle.has-tooltip{ type: 'button', data: { toggle: 'dropdown', title: "#{name} - #{group_status}" } } %button.dropdown-menu-toggle.has-tooltip{ type: 'button', data: { toggle: 'dropdown', title: "#{name} - #{group_status}" } }
%span.ci-status-icon %span.ci-status-icon
= render_status_with_link('build', group_status) = ci_icon_for_status(group_status)
%span.ci-status-text %span.ci-status-text
= name = name
%span.badge= subject.size %span.badge= subject.size
......
...@@ -12,4 +12,4 @@ ...@@ -12,4 +12,4 @@
%th Stages %th Stages
%th %th
%th %th
= render pipelines, commit_sha: true, stage: true, allow_retry: true, stages: pipelines.stages, status_icon_only: true, show_commit: false = render pipelines, commit_sha: true, stage: true, allow_retry: true, stages: pipelines.stages, show_commit: false
- @no_container = true - @no_container = true
- page_title "Cycle Analytics" - page_title "Cycle Analytics"
- content_for :page_specific_javascripts do
= page_specific_javascript_tag('cycle_analytics/cycle_analytics_bundle.js')
= render "projects/pipelines/head" = render "projects/pipelines/head"
#cycle-analytics{class: container_class, "v-cloak" => "true", data: { request_path: project_cycle_analytics_path(@project) }} #cycle-analytics{class: container_class, "v-cloak" => "true", data: { request_path: project_cycle_analytics_path(@project) }}
......
...@@ -2,9 +2,9 @@ ...@@ -2,9 +2,9 @@
- if subject.target_url - if subject.target_url
= link_to subject.target_url do = link_to subject.target_url do
%span.ci-status-icon %span.ci-status-icon
= render_status_with_link('commit status', subject.status) = ci_icon_for_status(subject.status)
%span.ci-status-text= subject.name %span.ci-status-text= subject.name
- else - else
%span.ci-status-icon %span.ci-status-icon
= render_status_with_link('commit status', subject.status) = ci_icon_for_status(subject.status)
%span.ci-status-text= subject.name %span.ci-status-text= subject.name
...@@ -5,33 +5,59 @@ ...@@ -5,33 +5,59 @@
%h4.prepend-top-0 %h4.prepend-top-0
= page_title = page_title
.col-lg-9 .col-lg-9
%h5.prepend-top-0
Pipelines
= form_for @project, url: namespace_project_pipelines_settings_path(@project.namespace.becomes(Namespace), @project) do |f| = form_for @project, url: namespace_project_pipelines_settings_path(@project.namespace.becomes(Namespace), @project) do |f|
%fieldset.builds-feature %fieldset.builds-feature
- unless @repository.gitlab_ci_yml - unless @repository.gitlab_ci_yml
.form-group .form-group
%p Pipelines need to be configured before you can begin using Continuous Integration. %p Pipelines need to be configured before you can begin using Continuous Integration.
= link_to 'Get started with CI/CD Pipelines', help_page_path('ci/quick_start/README'), class: 'btn btn-info' = link_to 'Get started with CI/CD Pipelines', help_page_path('ci/quick_start/README'), class: 'btn btn-info'
%hr
.form-group.append-bottom-default
= f.label :runners_token, "Runner token", class: 'label-light'
= f.text_field :runners_token, class: "form-control", placeholder: 'xEeFCaDAB89'
%p.help-block The secure token used by the Runner to checkout the project
%hr
.form-group .form-group
%p Get recent application code using the following command: %h5.prepend-top-0
Git strategy for pipelines
%p
Choose between <code>clone</code> or <code>fetch</code> to get the recent application code
= link_to icon('question-circle'), help_page_path('user/project/pipelines/settings', anchor: 'git-strategy')
.radio .radio
= f.label :build_allow_git_fetch_false do = f.label :build_allow_git_fetch_false do
= f.radio_button :build_allow_git_fetch, 'false' = f.radio_button :build_allow_git_fetch, 'false'
%strong git clone %strong git clone
%br %br
%span.descr Slower but makes sure you have a clean dir before every build %span.descr
Slower but makes sure the project workspace is pristine as it clones the repository from scratch for every job
.radio .radio
= f.label :build_allow_git_fetch_true do = f.label :build_allow_git_fetch_true do
= f.radio_button :build_allow_git_fetch, 'true' = f.radio_button :build_allow_git_fetch, 'true'
%strong git fetch %strong git fetch
%br %br
%span.descr Faster %span.descr
Faster as it re-uses the project workspace (falling back to clone if it doesn't exist)
%hr
.form-group .form-group
= f.label :build_timeout_in_minutes, 'Timeout', class: 'label-light' = f.label :build_timeout_in_minutes, 'Timeout', class: 'label-light'
= f.number_field :build_timeout_in_minutes, class: 'form-control', min: '0' = f.number_field :build_timeout_in_minutes, class: 'form-control', min: '0'
%p.help-block per build in minutes %p.help-block
Per job in minutes. If a job passes this threshold, it will be marked as failed.
= link_to icon('question-circle'), help_page_path('user/project/pipelines/settings', anchor: 'timeout')
%hr
.form-group
.checkbox
= f.label :public_builds do
= f.check_box :public_builds
%strong Public pipelines
.help-block
Allow everyone to access pipelines for public and internal projects
= link_to icon('question-circle'), help_page_path('user/project/pipelines/settings', anchor: 'visibility-of-pipelines')
%hr
.form-group .form-group
= f.label :build_coverage_regex, "Test coverage parsing", class: 'label-light' = f.label :build_coverage_regex, "Test coverage parsing", class: 'label-light'
.input-group .input-group
...@@ -39,8 +65,9 @@ ...@@ -39,8 +65,9 @@
= f.text_field :build_coverage_regex, class: 'form-control', placeholder: '\(\d+.\d+\%\) covered' = f.text_field :build_coverage_regex, class: 'form-control', placeholder: '\(\d+.\d+\%\) covered'
%span.input-group-addon / %span.input-group-addon /
%p.help-block %p.help-block
We will use this regular expression to find test coverage output in build trace. A regular expression that will be used to find the test coverage
Leave blank if you want to disable this feature output in the build trace. Leave blank to disable
= link_to icon('question-circle'), help_page_path('user/project/pipelines/settings', anchor: 'test-coverage-parsing')
.bs-callout.bs-callout-info .bs-callout.bs-callout-info
%p Below are examples of regex for existing tools: %p Below are examples of regex for existing tools:
%ul %ul
...@@ -57,21 +84,9 @@ ...@@ -57,21 +84,9 @@
gcovr (C/C++) - gcovr (C/C++) -
%code ^TOTAL.*\s+(\d+\%)$ %code ^TOTAL.*\s+(\d+\%)$
%li %li
tap --coverage-report=text-summary (Node.js) - tap --coverage-report=text-summary (NodeJS) -
%code ^Statements\s*:\s*([^%]+) %code ^Statements\s*:\s*([^%]+)
.form-group
.checkbox
= f.label :public_builds do
= f.check_box :public_builds
%strong Public builds
.help-block Allow everyone to access builds traces for Public and Internal projects
.form-group.append-bottom-default
= f.label :runners_token, "Runners token", class: 'label-light'
= f.text_field :runners_token, class: "form-control", placeholder: 'xEeFCaDAB89'
%p.help-block The secure token used to checkout project.
= f.submit 'Save changes', class: "btn btn-save" = f.submit 'Save changes', class: "btn btn-save"
%hr %hr
......
...@@ -3,8 +3,9 @@ ...@@ -3,8 +3,9 @@
- assignee = issuable.assignee - assignee = issuable.assignee
- issuable_type = issuable.class.table_name - issuable_type = issuable.class.table_name
- base_url_args = [project.namespace.becomes(Namespace), project, issuable_type] - base_url_args = [project.namespace.becomes(Namespace), project, issuable_type]
- can_update = can?(current_user, :"update_#{issuable.to_ability_name}", issuable)
%li{ id: dom_id(issuable, 'sortable'), class: "issuable-row", 'data-iid' => issuable.iid, 'data-url' => polymorphic_path(issuable) } %li{ id: dom_id(issuable, 'sortable'), class: "issuable-row #{'ui-sort-disabled' unless can_update}", 'data-iid' => issuable.iid, 'data-url' => polymorphic_path(issuable) }
%span %span
- if show_project_name - if show_project_name
%strong #{project.name} &middot; %strong #{project.name} &middot;
......
...@@ -91,6 +91,7 @@ module Gitlab ...@@ -91,6 +91,7 @@ module Gitlab
config.assets.precompile << "protected_branches/protected_branches_bundle.js" config.assets.precompile << "protected_branches/protected_branches_bundle.js"
config.assets.precompile << "diff_notes/diff_notes_bundle.js" config.assets.precompile << "diff_notes/diff_notes_bundle.js"
config.assets.precompile << "boards/boards_bundle.js" config.assets.precompile << "boards/boards_bundle.js"
config.assets.precompile << "cycle_analytics/cycle_analytics_bundle.js"
config.assets.precompile << "merge_conflicts/merge_conflicts_bundle.js" config.assets.precompile << "merge_conflicts/merge_conflicts_bundle.js"
config.assets.precompile << "boards/test_utils/simulate_drag.js" config.assets.precompile << "boards/test_utils/simulate_drag.js"
config.assets.precompile << "blob_edit/blob_edit_bundle.js" config.assets.precompile << "blob_edit/blob_edit_bundle.js"
......
...@@ -547,6 +547,10 @@ test: ...@@ -547,6 +547,10 @@ test:
project_url: "http://redmine/projects/:issues_tracker_id" project_url: "http://redmine/projects/:issues_tracker_id"
issues_url: "http://redmine/:project_id/:issues_tracker_id/:id" issues_url: "http://redmine/:project_id/:issues_tracker_id/:id"
new_issue_url: "http://redmine/projects/:issues_tracker_id/issues/new" new_issue_url: "http://redmine/projects/:issues_tracker_id/issues/new"
jira:
title: "JIRA"
url: https://sample_company.atlasian.net
project_key: PROJECT
ldap: ldap:
enabled: false enabled: false
servers: servers:
......
...@@ -27,10 +27,25 @@ ...@@ -27,10 +27,25 @@
:namespace: <%= Gitlab::Redis::SIDEKIQ_NAMESPACE %> :namespace: <%= Gitlab::Redis::SIDEKIQ_NAMESPACE %>
:queue: email_receiver :queue: email_receiver
:worker: EmailReceiverWorker :worker: EmailReceiverWorker
<% if config[:sentinels] %>
:sentinels:
<% config[:sentinels].each do |sentinel| %>
-
:host: <%= sentinel[:host] %>
:port: <%= sentinel[:port] %>
<% end %>
<% end %>
:arbitration_method: redis :arbitration_method: redis
:arbitration_options: :arbitration_options:
:redis_url: <%= config[:redis_url].to_json %> :redis_url: <%= config[:redis_url].to_json %>
:namespace: <%= Gitlab::Redis::MAILROOM_NAMESPACE %> :namespace: <%= Gitlab::Redis::MAILROOM_NAMESPACE %>
<% if config[:sentinels] %>
:sentinels:
<% config[:sentinels].each do |sentinel| %>
-
:host: <%= sentinel[:host] %>
:port: <%= sentinel[:port] %>
<% end %>
<% end %>
<% end %> <% end %>
...@@ -12,23 +12,26 @@ constraints(GroupUrlConstrainer.new) do ...@@ -12,23 +12,26 @@ constraints(GroupUrlConstrainer.new) do
end end
end end
resources :groups, constraints: { id: /[a-zA-Z.0-9_\-]+(?<!\.atom)/ } do scope constraints: { id: /[a-zA-Z.0-9_\-]+(?<!\.atom)/ } do
member do resources :groups, except: [:show] do
get :issues member do
get :merge_requests get :issues
get :projects get :merge_requests
get :activity get :projects
end get :activity
scope module: :groups do
resources :group_members, only: [:index, :create, :update, :destroy], concerns: :access_requestable do
post :resend_invite, on: :member
delete :leave, on: :collection
end end
resource :avatar, only: [:destroy] scope module: :groups do
resources :milestones, constraints: { id: /[^\/]+/ }, only: [:index, :show, :update, :new, :create] resources :group_members, only: [:index, :create, :update, :destroy], concerns: :access_requestable do
post :resend_invite, on: :member
delete :leave, on: :collection
end
resources :labels, except: [:show], constraints: { id: /\d+/ } resource :avatar, only: [:destroy]
resources :milestones, constraints: { id: /[^\/]+/ }, only: [:index, :show, :update, :new, :create]
resources :labels, except: [:show], constraints: { id: /\d+/ }
end
end end
get 'groups/:id' => 'groups#show', as: :group_canonical
end end
class RemoveInactiveJiraServiceProperties < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = true
DOWNTIME_REASON = "Removes all inactive jira_service properties"
def up
execute("UPDATE services SET properties = '{}' WHERE services.type = 'JiraService' and services.active = false")
end
end
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
class AddLockVersionToBuildAndPipelines < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
# Set this constant to true if this migration requires downtime.
DOWNTIME = false
def change
add_column :ci_builds, :lock_version, :integer
add_column :ci_commits, :lock_version, :integer
end
end
class MigrateJiraToGem < ActiveRecord::Migration
DOWNTIME = true
DOWNTIME_REASON = <<-HEREDOC
Refactor all Jira services properties(serialized field) to use new jira-ruby gem.
There were properties on old Jira service that are not needed anymore after the
service refactoring: api_url, project_url, new_issue_url, issues_url.
We extract the new necessary some properties from old keys and delete them:
taking project_key from project_url and url from api_url
HEREDOC
def up
active_services_query = "SELECT id, properties FROM services WHERE services.type IN ('JiraService') AND services.active = true"
select_all(active_services_query).each do |service|
id = service['id']
properties = JSON.parse(service['properties'])
properties_was = properties.clone
# Migrate `project_url` to `project_key`
# Ignore if `project_url` doesn't have jql project query with project key
if properties['project_url'].present?
jql = properties['project_url'].match('project=([A-Za-z]*)')
properties['project_key'] = jql.captures.first if jql
end
# Migrate `api_url` to `url`
if properties['api_url'].present?
url = properties['api_url'].match('(.*)\/rest\/api')
properties['url'] = url.captures.first if url
end
# Delete now unnecessary properties
properties.delete('api_url')
properties.delete('project_url')
properties.delete('new_issue_url')
properties.delete('issues_url')
# Update changes properties
if properties != properties_was
execute("UPDATE services SET properties = '#{quote_string(properties.to_json)}' WHERE id = #{id}")
end
end
end
def down
active_services_query = "SELECT id, properties FROM services WHERE services.type IN ('JiraService') AND services.active = true"
select_all(active_services_query).each do |service|
id = service['id']
properties = JSON.parse(service['properties'])
properties_was = properties.clone
# Rebuild old properties based on sane defaults
if properties['url'].present?
properties['api_url'] = "#{properties['url']}/rest/api/2"
properties['project_url'] =
"#{properties['url']}/issues/?jql=project=#{properties['project_key']}"
properties['issues_url'] = "#{properties['url']}/browse/:id"
properties['new_issue_url'] = "#{properties['url']}/secure/CreateIssue.jspa"
end
# Delete the new properties
properties.delete('url')
properties.delete('project_key')
# Update changes properties
if properties != properties_was
execute("UPDATE services SET properties = '#{quote_string(properties.to_json)}' WHERE id = #{id}")
end
end
end
end
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20161024042317) do ActiveRecord::Schema.define(version: 20161025231710) do
# These are extensions that must be enabled in order to support this database # These are extensions that must be enabled in order to support this database
enable_extension "plpgsql" enable_extension "plpgsql"
...@@ -189,6 +189,7 @@ ActiveRecord::Schema.define(version: 20161024042317) do ...@@ -189,6 +189,7 @@ ActiveRecord::Schema.define(version: 20161024042317) do
t.text "yaml_variables" t.text "yaml_variables"
t.datetime "queued_at" t.datetime "queued_at"
t.string "token" t.string "token"
t.integer "lock_version"
end end
add_index "ci_builds", ["commit_id", "stage_idx", "created_at"], name: "index_ci_builds_on_commit_id_and_stage_idx_and_created_at", using: :btree add_index "ci_builds", ["commit_id", "stage_idx", "created_at"], name: "index_ci_builds_on_commit_id_and_stage_idx_and_created_at", using: :btree
...@@ -219,6 +220,7 @@ ActiveRecord::Schema.define(version: 20161024042317) do ...@@ -219,6 +220,7 @@ ActiveRecord::Schema.define(version: 20161024042317) do
t.datetime "finished_at" t.datetime "finished_at"
t.integer "duration" t.integer "duration"
t.integer "user_id" t.integer "user_id"
t.integer "lock_version"
end end
add_index "ci_commits", ["gl_project_id", "sha"], name: "index_ci_commits_on_gl_project_id_and_sha", using: :btree add_index "ci_commits", ["gl_project_id", "sha"], name: "index_ci_commits_on_gl_project_id_and_sha", using: :btree
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
## List all deploy keys ## List all deploy keys
Get a list of all deploy keys across all projects. Get a list of all deploy keys across all projects of the GitLab instance. This endpoint requires admin access.
``` ```
GET /deploy_keys GET /deploy_keys
......
...@@ -19,4 +19,5 @@ ...@@ -19,4 +19,5 @@
- [Build permissions](../user/permissions.md#build-permissions) - [Build permissions](../user/permissions.md#build-permissions)
- [API](../api/ci/README.md) - [API](../api/ci/README.md)
- [CI services (linked docker containers)](services/README.md) - [CI services (linked docker containers)](services/README.md)
- [CI/CD pipelines settings](../user/project/pipelines/settings.md)
- [**New CI build permissions model**](../user/project/new_ci_build_permissions_model.md) Read about what changed in GitLab 8.12 and how that affects your builds. There's a new way to access your Git submodules and LFS objects in builds. - [**New CI build permissions model**](../user/project/new_ci_build_permissions_model.md) Read about what changed in GitLab 8.12 and how that affects your builds. There's a new way to access your Git submodules and LFS objects in builds.
...@@ -5,9 +5,9 @@ Introduced in GitLab 8.8. ...@@ -5,9 +5,9 @@ Introduced in GitLab 8.8.
## Pipelines ## Pipelines
A pipeline is a group of [builds] that get executed in [stages] \(batches). All A pipeline is a group of [builds][] that get executed in [stages][](batches).
of the builds in a stage are executed in parallel (if there are enough All of the builds in a stage are executed in parallel (if there are enough
concurrent [runners]), and if they all succeed, the pipeline moves on to the concurrent [Runners]), and if they all succeed, the pipeline moves on to the
next stage. If one of the builds fails, the next stage is not (usually) next stage. If one of the builds fails, the next stage is not (usually)
executed. executed.
...@@ -25,8 +25,8 @@ See full [documentation](yaml/README.md#jobs). ...@@ -25,8 +25,8 @@ See full [documentation](yaml/README.md#jobs).
## Seeing pipeline status ## Seeing pipeline status
You can find the current and historical pipeline runs under **Pipelines** for your You can find the current and historical pipeline runs under **Pipelines** for
project. your project.
## Seeing build status ## Seeing build status
...@@ -36,42 +36,11 @@ cancel the build, retry it, or erase the build trace. ...@@ -36,42 +36,11 @@ cancel the build, retry it, or erase the build trace.
## Badges ## Badges
There are build status and test coverage report badges available. Build status and test coverage report badges are available. You can find their
respective link in the [Pipelines settings] page.
Go to pipeline settings to see available badges and code you can use to embed
badges in the `README.md` or your website.
### Build status badge
You can access a build status badge image using following link:
```
http://example.gitlab.com/namespace/project/badges/branch/build.svg
```
### Test coverage report badge
GitLab makes it possible to define the regular expression for coverage report,
that each build log will be matched against. This means that each build in the
pipeline can have the test coverage percentage value defined.
You can access test coverage badge using following link:
```
http://example.gitlab.com/namespace/project/badges/branch/coverage.svg
```
If you would like to get the coverage report from the specific job, you can add
a `job=coverage_job_name` parameter to the URL. For example, it is possible to
use following Markdown code to embed the est coverage report into `README.md`:
```markdown
![coverage](http://gitlab.com/gitlab-org/gitlab-ce/badges/master/coverage.svg?job=coverage)
```
The latest successful pipeline will be used to read the test coverage value.
[builds]: #builds [builds]: #builds
[jobs]: yaml/README.md#jobs [jobs]: yaml/README.md#jobs
[stages]: yaml/README.md#stages [stages]: yaml/README.md#stages
[runners]: runners/README.md [runners]: runners/READM
[pipelines settings]: ../user/project/pipelines/settings.md
...@@ -93,6 +93,8 @@ merge request. ...@@ -93,6 +93,8 @@ merge request.
links shift too, which eventually leads to dead links. If you think it is links shift too, which eventually leads to dead links. If you think it is
compelling to add numbers in headings, make sure to at least discuss it with compelling to add numbers in headings, make sure to at least discuss it with
someone in the Merge Request someone in the Merge Request
- Avoid adding things that show ephemeral statuses. For example, if a feature is
considered beta or experimental, put this info in a note, not in the heading.
- When introducing a new document, be careful for the headings to be - When introducing a new document, be careful for the headings to be
grammatically and syntactically correct. It is advised to mention one or all grammatically and syntactically correct. It is advised to mention one or all
of the following GitLab members for a review: `@axil`, `@rspeicher`, `@marcia`, of the following GitLab members for a review: `@axil`, `@rspeicher`, `@marcia`,
...@@ -466,4 +468,4 @@ curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" --data "domain ...@@ -466,4 +468,4 @@ curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" --data "domain
[doc-restart]: ../administration/restart_gitlab.md "GitLab restart documentation" [doc-restart]: ../administration/restart_gitlab.md "GitLab restart documentation"
[ce-3349]: https://gitlab.com/gitlab-org/gitlab-ce/issues/3349 "Documentation restructure" [ce-3349]: https://gitlab.com/gitlab-org/gitlab-ce/issues/3349 "Documentation restructure"
[graffle]: https://gitlab.com/gitlab-org/gitlab-design/blob/d8d39f4a87b90fb9ae89ca12dc565347b4900d5e/production/resources/gitlab-map.graffle [graffle]: https://gitlab.com/gitlab-org/gitlab-design/blob/d8d39f4a87b90fb9ae89ca12dc565347b4900d5e/production/resources/gitlab-map.graffle
[gitlab-map]: https://gitlab.com/gitlab-org/gitlab-design/raw/master/production/resources/gitlab-map.png [gitlab-map]: https://gitlab.com/gitlab-org/gitlab-design/raw/master/production/resources/gitlab-map.png
\ No newline at end of file
...@@ -185,6 +185,20 @@ again in the future. ...@@ -185,6 +185,20 @@ again in the future.
See [the Testing Standards and Style Guidelines](testing.md) for more See [the Testing Standards and Style Guidelines](testing.md) for more
information. information.
### Running frontend tests
`rake teaspoon` runs the frontend-only (JavaScript) tests.
It consists of two subtasks:
- `rake teaspoon:fixtures` (re-)generates fixtures
- `rake teaspoon:tests` actually executes the tests
As long as the fixtures don't change, `rake teaspoon:tests` is sufficient
(and saves you some time).
Please note: Not all of the frontend fixtures are generated. Some are still static
files. These will not be touched by `rake teaspoon:fixtures`.
## Supported browsers ## Supported browsers
For our currently-supported browsers, see our [requirements][requirements]. For our currently-supported browsers, see our [requirements][requirements].
......
...@@ -5,7 +5,7 @@ trackers and external authentication. ...@@ -5,7 +5,7 @@ trackers and external authentication.
See the documentation below for details on how to configure these services. See the documentation below for details on how to configure these services.
- [Jira](../project_services/jira.md) Integrate with the JIRA issue tracker - [JIRA](jira.md) Integrate with the JIRA issue tracker
- [External issue tracker](external-issue-tracker.md) Redmine, JIRA, etc. - [External issue tracker](external-issue-tracker.md) Redmine, JIRA, etc.
- [LDAP](ldap.md) Set up sign in via LDAP - [LDAP](ldap.md) Set up sign in via LDAP
- [OmniAuth](omniauth.md) Sign in via Twitter, GitHub, GitLab.com, Google, Bitbucket, Facebook, Shibboleth, SAML, Crowd and Azure - [OmniAuth](omniauth.md) Sign in via Twitter, GitHub, GitLab.com, Google, Bitbucket, Facebook, Shibboleth, SAML, Crowd and Azure
......
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