Commit fb9a0e52 authored by Lin Jen-Shin's avatar Lin Jen-Shin

Merge remote-tracking branch 'upstream/master' into show-status-from-branch

* upstream/master: (35 commits)
  Only skip group when it's actually a group in the "Share with group" select
  Fix: Todos Filter Shows All Users
  Fix: Guest sees some repository details and gets 404
  Move shared params to a helper
  GrapeDSL for project hooks
  Update commits.scss
  updated styling commit SHA on branches page + added to changelog
  change build list height to show 6,5 builds + improve padding of list, with first/last child selectors
  Ignore builds directory from eslint
  Add changelog entry
  Document multiple repository storage paths
  Allow multiple repository storage shards to be enabled, and automatically round-robin between them
  Cleaned up global namespace JS
  Add tip for using Chrome to run and debug teaspoon tests.
  Add CHANGELOG entry file
  Add jquery.timeago.js to application.js
  Update match-regex to fix filename convention
  Move jquery.timeago to vendor directory
  Change a bunch of doc links to either relative or https://docs.gitlab.com.
  Show log corresponding to env in admin/logs
  ...
parents ce1dc4c2 5368b9f2
...@@ -2,4 +2,4 @@ ...@@ -2,4 +2,4 @@
/public/ /public/
/tmp/ /tmp/
/vendor/ /vendor/
/builds/
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
"filenames" "filenames"
], ],
"rules": { "rules": {
"filenames/match-regex": [2, "^[a-z_]+$"] "filenames/match-regex": [2, "^[a-z0-9_]+(.js)?$"]
}, },
"globals": { "globals": {
"$": false, "$": false,
......
...@@ -4,6 +4,7 @@ entry. ...@@ -4,6 +4,7 @@ entry.
## 8.14.0 (2016-11-22) ## 8.14.0 (2016-11-22)
- Show correct environment log in admin/logs (@duk3luk3 !7191)
- Fix Milestone dropdown not stay selected for `Upcoming` and `No Milestone` option !7117 - Fix Milestone dropdown not stay selected for `Upcoming` and `No Milestone` option !7117
- Backups do not fail anymore when using tar on annex and custom_hooks only. !5814 - 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)
...@@ -17,6 +18,7 @@ entry. ...@@ -17,6 +18,7 @@ entry.
- Fix mobile layout issues in admin user overview page !7087 - Fix mobile layout issues in admin user overview page !7087
- Fix HipChat notifications rendering (airatshigapov, eisnerd) - Fix HipChat notifications rendering (airatshigapov, eisnerd)
- Remove 'Edit' button from wiki edit view !7143 (Hiroyuki Sato) - Remove 'Edit' button from wiki edit view !7143 (Hiroyuki Sato)
- Cleaned up global namespace JS !19661 (Jose Ivan Vargas)
- Refactor Jira service to use jira-ruby gem - Refactor Jira service to use jira-ruby gem
- Improved todos empty state - Improved todos empty state
- Add hover to trash icon in notes !7008 (blackst0ne) - Add hover to trash icon in notes !7008 (blackst0ne)
...@@ -26,6 +28,7 @@ entry. ...@@ -26,6 +28,7 @@ entry.
- Fix sidekiq stats in admin area (blackst0ne) - Fix sidekiq stats in admin area (blackst0ne)
- Added label description as tooltip to issue board list title - Added label description as tooltip to issue board list title
- Created cycle analytics bundle JavaScript file - Created cycle analytics bundle JavaScript file
- Hides container registry when repository is disabled
- API: Fix booleans not recognized as such when using the `to_boolean` helper - API: Fix booleans not recognized as such when using the `to_boolean` helper
- Removed delete branch tooltip !6954 - Removed delete branch tooltip !6954
- Stop unauthorized users dragging on milestone page (blackst0ne) - Stop unauthorized users dragging on milestone page (blackst0ne)
...@@ -45,6 +48,8 @@ entry. ...@@ -45,6 +48,8 @@ entry.
- New issue board list dropdown stays open after adding a new list - 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
- Optimize Event queries by removing default order - Optimize Event queries by removing default order
- Add new icon for skipped builds
- Show created icon in pipeline mini-graph
- Remove duplicate links from sidebar - Remove duplicate links from sidebar
- 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)
- Add Rake task to create/repair GitLab Shell hooks symlinks !5634 - Add Rake task to create/repair GitLab Shell hooks symlinks !5634
...@@ -62,11 +67,16 @@ entry. ...@@ -62,11 +67,16 @@ entry.
- Improve search query parameter naming in /admin/users !7115 (YarNayar) - Improve search query parameter naming in /admin/users !7115 (YarNayar)
- Fix table pagination to be responsive - Fix table pagination to be responsive
- Allow to search for user by secondary email address in the admin interface(/admin/users) !7115 (YarNayar) - Allow to search for user by secondary email address in the admin interface(/admin/users) !7115 (YarNayar)
- Updated commit SHA styling on the branches page.
## 8.13.3 (2016-11-02) ## 8.13.3 (2016-11-02)
- Removes any symlinks before importing a project export file. CVE-2016-9086 - Removes any symlinks before importing a project export file. CVE-2016-9086
- Fixed Import/Export foreign key issue to do with project members. - Fixed Import/Export foreign key issue to do with project members.
- Fix relative links in Markdown wiki when displayed in "Project" tab !7218
- Reduce the overhead to calculate number of open/closed issues and merge requests within the group or project
- Fix project features default values
- Changed build dropdown list length to be 6,5 builds long in the pipeline graph
## 8.13.2 (2016-10-31) ## 8.13.2 (2016-10-31)
......
...@@ -104,7 +104,7 @@ gem 'deckar01-task_list', '1.0.5', require: 'task_list/railtie' ...@@ -104,7 +104,7 @@ gem 'deckar01-task_list', '1.0.5', require: 'task_list/railtie'
gem 'gitlab-markup', '~> 1.5.0' gem 'gitlab-markup', '~> 1.5.0'
gem 'redcarpet', '~> 3.3.3' gem 'redcarpet', '~> 3.3.3'
gem 'RedCloth', '~> 4.3.2' gem 'RedCloth', '~> 4.3.2'
gem 'rdoc', '~>3.6' gem 'rdoc', '~> 4.2'
gem 'org-ruby', '~> 0.9.12' gem 'org-ruby', '~> 0.9.12'
gem 'creole', '~> 0.5.0' gem 'creole', '~> 0.5.0'
gem 'wikicloth', '0.8.1' gem 'wikicloth', '0.8.1'
...@@ -260,9 +260,6 @@ group :development do ...@@ -260,9 +260,6 @@ group :development do
gem 'better_errors', '~> 1.0.1' gem 'better_errors', '~> 1.0.1'
gem 'binding_of_caller', '~> 0.7.2' gem 'binding_of_caller', '~> 0.7.2'
# Docs generator
gem 'sdoc', '~> 0.3.20'
# thin instead webrick # thin instead webrick
gem 'thin', '~> 1.7.0' gem 'thin', '~> 1.7.0'
end end
......
...@@ -567,7 +567,7 @@ GEM ...@@ -567,7 +567,7 @@ GEM
ffi (>= 0.5.0) ffi (>= 0.5.0)
rblineprof (0.3.6) rblineprof (0.3.6)
debugger-ruby_core_source (~> 1.3) debugger-ruby_core_source (~> 1.3)
rdoc (3.12.2) rdoc (4.2.2)
json (~> 1.4) json (~> 1.4)
recaptcha (3.0.0) recaptcha (3.0.0)
json json
...@@ -663,9 +663,6 @@ GEM ...@@ -663,9 +663,6 @@ GEM
scss_lint (0.47.1) scss_lint (0.47.1)
rake (>= 0.9, < 11) rake (>= 0.9, < 11)
sass (~> 3.4.15) sass (~> 3.4.15)
sdoc (0.3.20)
json (>= 1.1.3)
rdoc (~> 3.10)
seed-fu (2.3.6) seed-fu (2.3.6)
activerecord (>= 3.1) activerecord (>= 3.1)
activesupport (>= 3.1) activesupport (>= 3.1)
...@@ -936,7 +933,7 @@ DEPENDENCIES ...@@ -936,7 +933,7 @@ DEPENDENCIES
rails-deprecated_sanitizer (~> 1.0.3) rails-deprecated_sanitizer (~> 1.0.3)
rainbow (~> 2.1.0) rainbow (~> 2.1.0)
rblineprof (~> 0.3.6) rblineprof (~> 0.3.6)
rdoc (~> 3.6) rdoc (~> 4.2)
recaptcha (~> 3.0) recaptcha (~> 3.0)
redcarpet (~> 3.3.3) redcarpet (~> 3.3.3)
redis (~> 3.2) redis (~> 3.2)
...@@ -956,7 +953,6 @@ DEPENDENCIES ...@@ -956,7 +953,6 @@ DEPENDENCIES
sanitize (~> 2.0) sanitize (~> 2.0)
sass-rails (~> 5.0.6) sass-rails (~> 5.0.6)
scss_lint (~> 0.47.0) scss_lint (~> 0.47.0)
sdoc (~> 0.3.20)
seed-fu (~> 2.3.5) seed-fu (~> 2.3.5)
select2-rails (~> 3.5.9) select2-rails (~> 3.5.9)
sentry-raven (~> 2.0.0) sentry-raven (~> 2.0.0)
......
...@@ -80,7 +80,7 @@ GitLab is a Ruby on Rails application that runs on the following software: ...@@ -80,7 +80,7 @@ GitLab is a Ruby on Rails application that runs on the following software:
- Redis 2.8+ - Redis 2.8+
- MySQL or PostgreSQL - MySQL or PostgreSQL
For more information please see the [architecture documentation](http://doc.gitlab.com/ce/development/architecture.html). For more information please see the [architecture documentation](https://docs.gitlab.com/ce/development/architecture.html).
## Third-party applications ## Third-party applications
...@@ -96,7 +96,7 @@ For upgrading information please see our [update page](https://about.gitlab.com/ ...@@ -96,7 +96,7 @@ For upgrading information please see our [update page](https://about.gitlab.com/
## Documentation ## Documentation
All documentation can be found on [doc.gitlab.com/ce/](http://doc.gitlab.com/ce/). All documentation can be found on [docs.gitlab.com/ce/](https://docs.gitlab.com/ce/).
## Getting help ## Getting help
......
...@@ -22,16 +22,14 @@ ...@@ -22,16 +22,14 @@
}); });
}, },
// Return groups list. Filtered by query // Return groups list. Filtered by query
// Only active groups retrieved groups: function(query, options, callback) {
groups: function(query, skip_ldap, skip_groups, callback) {
var url = Api.buildUrl(Api.groupsPath); var url = Api.buildUrl(Api.groupsPath);
return $.ajax({ return $.ajax({
url: url, url: url,
data: { data: $.extend({
search: query, search: query,
skip_groups: skip_groups, per_page: 20
per_page: 20 }, options),
},
dataType: "json" dataType: "json"
}).done(function(groups) { }).done(function(groups) {
return callback(groups); return callback(groups);
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
/*= require jquery-ui/sortable */ /*= require jquery-ui/sortable */
/*= require jquery_ujs */ /*= require jquery_ujs */
/*= require jquery.endless-scroll */ /*= require jquery.endless-scroll */
/*= require jquery.timeago */
/*= require jquery.highlight */ /*= require jquery.highlight */
/*= require jquery.waitforimages */ /*= require jquery.waitforimages */
/*= require jquery.atwho */ /*= require jquery.atwho */
...@@ -54,125 +55,53 @@ ...@@ -54,125 +55,53 @@
/*= require_directory . */ /*= require_directory . */
/*= require fuzzaldrin-plus */ /*= require fuzzaldrin-plus */
(function() { (function () {
window.slugify = function(text) { document.addEventListener('page:fetch', gl.utils.cleanupBeforeFetch);
return text.replace(/[^-a-zA-Z0-9]+/g, '_').toLowerCase(); window.addEventListener('hashchange', gl.utils.shiftWindow);
};
window.ajaxGet = function(url) {
return $.ajax({
type: "GET",
url: url,
dataType: "script"
});
};
window.split = function(val) {
return val.split(/,\s*/);
};
window.extractLast = function(term) {
return split(term).pop();
};
window.rstrip = function(val) {
if (val) {
return val.replace(/\s+$/, '');
} else {
return val;
}
};
// Disable button if text field is empty
window.disableButtonIfEmptyField = function(field_selector, button_selector, event_name) {
event_name = event_name || 'input';
var closest_submit, field;
field = $(field_selector);
closest_submit = field.closest('form').find(button_selector);
if (rstrip(field.val()) === "") {
closest_submit.disable();
}
return field.on(event_name, function() {
if (rstrip($(this).val()) === "") {
return closest_submit.disable();
} else {
return closest_submit.enable();
}
});
};
// Disable button if any input field with given selector is empty window.onload = function () {
window.disableButtonIfAnyEmptyField = function(form, form_selector, button_selector) {
var closest_submit, updateButtons;
closest_submit = form.find(button_selector);
updateButtons = function() {
var filled;
filled = true;
form.find('input').filter(form_selector).each(function() {
return filled = rstrip($(this).val()) !== "" || !$(this).attr('required');
});
if (filled) {
return closest_submit.enable();
} else {
return closest_submit.disable();
}
};
updateButtons();
return form.keyup(updateButtons);
};
window.sanitize = function(str) {
return str.replace(/<(?:.|\n)*?>/gm, '');
};
window.shiftWindow = function() {
return scrollBy(0, -100);
};
document.addEventListener("page:fetch", gl.utils.cleanupBeforeFetch);
window.addEventListener("hashchange", shiftWindow);
window.onload = function() {
// Scroll the window to avoid the topnav bar // Scroll the window to avoid the topnav bar
// https://github.com/twitter/bootstrap/issues/1768 // https://github.com/twitter/bootstrap/issues/1768
if (location.hash) { if (location.hash) {
return setTimeout(shiftWindow, 100); return setTimeout(gl.utils.shiftWindow, 100);
} }
}; };
$(function() { $(function () {
var $body, $document, $sidebarGutterToggle, $window, bootstrapBreakpoint, checkInitialSidebarSize, fitSidebarForSize, flash; var $body = $('body');
$document = $(document); var $document = $(document);
$window = $(window); var $window = $(window);
$body = $('body'); var $sidebarGutterToggle = $('.js-sidebar-toggle');
var $flash = $('.flash-container');
var bootstrapBreakpoint = bp.getBreakpointSize();
var checkInitialSidebarSize;
var fitSidebarForSize;
// Set the default path for all cookies to GitLab's root directory // Set the default path for all cookies to GitLab's root directory
Cookies.defaults.path = gon.relative_url_root || '/'; Cookies.defaults.path = gon.relative_url_root || '/';
gl.utils.preventDisabledButtons(); gl.utils.preventDisabledButtons();
bootstrapBreakpoint = bp.getBreakpointSize(); $('.nav-sidebar').niceScroll({
$(".nav-sidebar").niceScroll({
cursoropacitymax: '0.4', cursoropacitymax: '0.4',
cursorcolor: '#FFF', cursorcolor: '#FFF',
cursorborder: "1px solid #FFF" cursorborder: '1px solid #FFF'
}); });
$(".js-select-on-focus").on("focusin", function() { $('.js-select-on-focus').on('focusin', function () {
return $(this).select().one('mouseup', function(e) { return $(this).select().one('mouseup', function (e) {
return e.preventDefault(); return e.preventDefault();
}); });
// Click a .js-select-on-focus field, select the contents // Click a .js-select-on-focus field, select the contents
// Prevent a mouseup event from deselecting the input // Prevent a mouseup event from deselecting the input
}); });
$('.remove-row').bind('ajax:success', function() { $('.remove-row').bind('ajax:success', function () {
$(this).tooltip('destroy') $(this).tooltip('destroy')
.closest('li') .closest('li')
.fadeOut(); .fadeOut();
}); });
$('.js-remove-tr').bind('ajax:before', function() { $('.js-remove-tr').bind('ajax:before', function () {
return $(this).hide(); return $(this).hide();
}); });
$('.js-remove-tr').bind('ajax:success', function() { $('.js-remove-tr').bind('ajax:success', function () {
return $(this).closest('tr').fadeOut(); return $(this).closest('tr').fadeOut();
}); });
$('select.select2').select2({ $('select.select2').select2({
...@@ -180,8 +109,8 @@ ...@@ -180,8 +109,8 @@
// Initialize select2 selects // Initialize select2 selects
dropdownAutoWidth: true dropdownAutoWidth: true
}); });
$('.js-select2').bind('select2-close', function() { $('.js-select2').bind('select2-close', function () {
return setTimeout((function() { return setTimeout((function () {
$('.select2-container-active').removeClass('select2-container-active'); $('.select2-container-active').removeClass('select2-container-active');
return $(':focus').blur(); return $(':focus').blur();
}), 1); }), 1);
...@@ -191,24 +120,24 @@ ...@@ -191,24 +120,24 @@
$.fn.tooltip.Constructor.DEFAULTS.trigger = 'hover'; $.fn.tooltip.Constructor.DEFAULTS.trigger = 'hover';
$body.tooltip({ $body.tooltip({
selector: '.has-tooltip, [data-toggle="tooltip"]', selector: '.has-tooltip, [data-toggle="tooltip"]',
placement: function(_, el) { placement: function (_, el) {
return $(el).data('placement') || 'bottom'; return $(el).data('placement') || 'bottom';
} }
}); });
$('.trigger-submit').on('change', function() { $('.trigger-submit').on('change', function () {
return $(this).parents('form').submit(); return $(this).parents('form').submit();
// Form submitter // Form submitter
}); });
gl.utils.localTimeAgo($('abbr.timeago, .js-timeago'), true); gl.utils.localTimeAgo($('abbr.timeago, .js-timeago'), true);
// Flash // Flash
if ((flash = $(".flash-container")).length > 0) { if ($flash.length > 0) {
flash.click(function() { $flash.click(function () {
return $(this).fadeOut(); return $(this).fadeOut();
}); });
flash.show(); $flash.show();
} }
// Disable form buttons while a form is submitting // Disable form buttons while a form is submitting
$body.on('ajax:complete, ajax:beforeSend, submit', 'form', function(e) { $body.on('ajax:complete, ajax:beforeSend, submit', 'form', function (e) {
var buttons; var buttons;
buttons = $('[type="submit"]', this); buttons = $('[type="submit"]', this);
switch (e.type) { switch (e.type) {
...@@ -219,36 +148,36 @@ ...@@ -219,36 +148,36 @@
return buttons.enable(); return buttons.enable();
} }
}); });
$(document).ajaxError(function(e, xhrObj, xhrSetting, xhrErrorText) { $(document).ajaxError(function (e, xhrObj) {
var ref; var ref = xhrObj.status;
if (xhrObj.status === 401) { if (xhrObj.status === 401) {
return new Flash('You need to be logged in.', 'alert'); return new Flash('You need to be logged in.', 'alert');
} else if ((ref = xhrObj.status) === 404 || ref === 500) { } else if (ref === 404 || ref === 500) {
return new Flash('Something went wrong on our end.', 'alert'); return new Flash('Something went wrong on our end.', 'alert');
} }
}); });
$('.account-box').hover(function() { $('.account-box').hover(function () {
// Show/Hide the profile menu when hovering the account box // Show/Hide the profile menu when hovering the account box
return $(this).toggleClass('hover'); return $(this).toggleClass('hover');
}); });
$document.on('click', '.diff-content .js-show-suppressed-diff', function() { $document.on('click', '.diff-content .js-show-suppressed-diff', function () {
var $container; var $container;
$container = $(this).parent(); $container = $(this).parent();
$container.next('table').show(); $container.next('table').show();
return $container.remove(); return $container.remove();
// Commit show suppressed diff // Commit show suppressed diff
}); });
$('.navbar-toggle').on('click', function() { $('.navbar-toggle').on('click', function () {
$('.header-content .title').toggle(); $('.header-content .title').toggle();
$('.header-content .header-logo').toggle(); $('.header-content .header-logo').toggle();
$('.header-content .navbar-collapse').toggle(); $('.header-content .navbar-collapse').toggle();
return $('.navbar-toggle').toggleClass('active'); return $('.navbar-toggle').toggleClass('active');
}); });
// Show/hide comments on diff // Show/hide comments on diff
$body.on("click", ".js-toggle-diff-comments", function(e) { $body.on('click', '.js-toggle-diff-comments', function (e) {
var $this = $(this); var $this = $(this);
$this.toggleClass('active');
var notesHolders = $this.closest('.diff-file').find('.notes_holder'); var notesHolders = $this.closest('.diff-file').find('.notes_holder');
$this.toggleClass('active');
if ($this.hasClass('active')) { if ($this.hasClass('active')) {
notesHolders.show().find('.hide').show(); notesHolders.show().find('.hide').show();
} else { } else {
...@@ -257,30 +186,27 @@ ...@@ -257,30 +186,27 @@
$this.trigger('blur'); $this.trigger('blur');
return e.preventDefault(); return e.preventDefault();
}); });
$document.off("click", '.js-confirm-danger'); $document.off('click', '.js-confirm-danger');
$document.on("click", '.js-confirm-danger', function(e) { $document.on('click', '.js-confirm-danger', function (e) {
var btn, form, text; var btn = $(e.target);
var form = btn.closest('form');
var text = btn.data('confirm-danger-message');
e.preventDefault(); e.preventDefault();
btn = $(e.target);
text = btn.data("confirm-danger-message");
form = btn.closest("form");
return new ConfirmDangerModal(form, text); return new ConfirmDangerModal(form, text);
}); });
$document.on('click', 'button', function() { $document.on('click', 'button', function () {
return $(this).blur(); return $(this).blur();
}); });
$('input[type="search"]').each(function() { $('input[type="search"]').each(function () {
var $this; var $this = $(this);
$this = $(this);
$this.attr('value', $this.val()); $this.attr('value', $this.val());
}); });
$document.off('keyup', 'input[type="search"]').on('keyup', 'input[type="search"]', function(e) { $document.off('keyup', 'input[type="search"]').on('keyup', 'input[type="search"]', function () {
var $this; var $this;
$this = $(this); $this = $(this);
return $this.attr('value', $this.val()); return $this.attr('value', $this.val());
}); });
$sidebarGutterToggle = $('.js-sidebar-toggle'); $document.off('breakpoint:change').on('breakpoint:change', function (e, breakpoint) {
$document.off('breakpoint:change').on('breakpoint:change', function(e, breakpoint) {
var $gutterIcon; var $gutterIcon;
if (breakpoint === 'sm' || breakpoint === 'xs') { if (breakpoint === 'sm' || breakpoint === 'xs') {
$gutterIcon = $sidebarGutterToggle.find('i'); $gutterIcon = $sidebarGutterToggle.find('i');
...@@ -289,7 +215,7 @@ ...@@ -289,7 +215,7 @@
} }
} }
}); });
fitSidebarForSize = function() { fitSidebarForSize = function () {
var oldBootstrapBreakpoint; var oldBootstrapBreakpoint;
oldBootstrapBreakpoint = bootstrapBreakpoint; oldBootstrapBreakpoint = bootstrapBreakpoint;
bootstrapBreakpoint = bp.getBreakpointSize(); bootstrapBreakpoint = bp.getBreakpointSize();
...@@ -297,13 +223,13 @@ ...@@ -297,13 +223,13 @@
return $document.trigger('breakpoint:change', [bootstrapBreakpoint]); return $document.trigger('breakpoint:change', [bootstrapBreakpoint]);
} }
}; };
checkInitialSidebarSize = function() { checkInitialSidebarSize = function () {
bootstrapBreakpoint = bp.getBreakpointSize(); bootstrapBreakpoint = bp.getBreakpointSize();
if (bootstrapBreakpoint === "xs" || "sm") { if (bootstrapBreakpoint === 'xs' || 'sm') {
return $document.trigger('breakpoint:change', [bootstrapBreakpoint]); return $document.trigger('breakpoint:change', [bootstrapBreakpoint]);
} }
}; };
$window.off("resize.app").on("resize.app", function(e) { $window.off('resize.app').on('resize.app', function () {
return fitSidebarForSize(); return fitSidebarForSize();
}); });
gl.awardsHandler = new AwardsHandler(); gl.awardsHandler = new AwardsHandler();
......
/* eslint-disable */ /* eslint-disable */
(function() { (function() {
this.AwardsHandler = (function() { this.AwardsHandler = (function() {
const FROM_SENTENCE_REGEX = /(?:, and | and |, )/; //For separating lists produced by ruby's Array#toSentence var FROM_SENTENCE_REGEX = /(?:, and | and |, )/; //For separating lists produced by ruby's Array#toSentence
function AwardsHandler() { function AwardsHandler() {
this.aliases = gl.emojiAliases(); this.aliases = gl.emojiAliases();
$(document).off('click', '.js-add-award').on('click', '.js-add-award', (function(_this) { $(document).off('click', '.js-add-award').on('click', '.js-add-award', (function(_this) {
......
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
submit.disable(); submit.disable();
$('.js-confirm-danger-input').off('input'); $('.js-confirm-danger-input').off('input');
$('.js-confirm-danger-input').on('input', function() { $('.js-confirm-danger-input').on('input', function() {
if (rstrip($(this).val()) === project_path) { if (gl.utils.rstrip($(this).val()) === project_path) {
return submit.enable(); return submit.enable();
} else { } else {
return submit.disable(); return submit.disable();
......
...@@ -126,8 +126,8 @@ ...@@ -126,8 +126,8 @@
} }
return { return {
username: m.username, username: m.username,
title: sanitize(title), title: gl.utils.sanitize(title),
search: sanitize(m.username + " " + m.name) search: gl.utils.sanitize(m.username + " " + m.name)
}; };
}); });
} }
...@@ -159,7 +159,7 @@ ...@@ -159,7 +159,7 @@
} }
return { return {
id: i.iid, id: i.iid,
title: sanitize(i.title), title: gl.utils.sanitize(i.title),
search: i.iid + " " + i.title search: i.iid + " " + i.title
}; };
}); });
...@@ -189,7 +189,7 @@ ...@@ -189,7 +189,7 @@
} }
return { return {
id: m.iid, id: m.iid,
title: sanitize(m.title), title: gl.utils.sanitize(m.title),
search: "" + m.title search: "" + m.title
}; };
}); });
...@@ -222,7 +222,7 @@ ...@@ -222,7 +222,7 @@
} }
return { return {
id: m.iid, id: m.iid,
title: sanitize(m.title), title: gl.utils.sanitize(m.title),
search: m.iid + " " + m.title search: m.iid + " " + m.title
}; };
}); });
...@@ -240,9 +240,9 @@ ...@@ -240,9 +240,9 @@
var sanitizeLabelTitle; var sanitizeLabelTitle;
sanitizeLabelTitle = function(title) { sanitizeLabelTitle = function(title) {
if (/[\w\?&]+\s+[\w\?&]+/g.test(title)) { if (/[\w\?&]+\s+[\w\?&]+/g.test(title)) {
return "\"" + (sanitize(title)) + "\""; return "\"" + (gl.utils.sanitize(title)) + "\"";
} else { } else {
return sanitize(title); return gl.utils.sanitize(title);
} }
}; };
return $.map(merges, function(m) { return $.map(merges, function(m) {
......
...@@ -24,8 +24,8 @@ ...@@ -24,8 +24,8 @@
if (isNewForm) { if (isNewForm) {
this.form.find('.div-dropzone').remove(); this.form.find('.div-dropzone').remove();
this.form.addClass('gfm-form'); this.form.addClass('gfm-form');
disableButtonIfEmptyField(this.form.find('.js-note-text'), this.form.find('.js-comment-button'));
// remove notify commit author checkbox for non-commit notes // remove notify commit author checkbox for non-commit notes
gl.utils.disableButtonIfEmptyField(this.form.find('.js-note-text'), this.form.find('.js-comment-button'));
GitLab.GfmAutoComplete.setup(this.form.find('.js-gfm-input')); GitLab.GfmAutoComplete.setup(this.form.find('.js-gfm-input'));
new DropzoneInput(this.form); new DropzoneInput(this.form);
autosize(this.textarea); autosize(this.textarea);
......
...@@ -6,15 +6,16 @@ ...@@ -6,15 +6,16 @@
function GroupsSelect() { function GroupsSelect() {
$('.ajax-groups-select').each((function(_this) { $('.ajax-groups-select').each((function(_this) {
return function(i, select) { return function(i, select) {
var skip_ldap, skip_groups; var all_available, skip_groups;
skip_ldap = $(select).hasClass('skip_ldap'); all_available = $(select).data('all-available');
skip_groups = $(select).data('skip-groups') || []; skip_groups = $(select).data('skip-groups') || [];
return $(select).select2({ return $(select).select2({
placeholder: "Search for a group", placeholder: "Search for a group",
multiple: $(select).hasClass('multiselect'), multiple: $(select).hasClass('multiselect'),
minimumInputLength: 0, minimumInputLength: 0,
query: function(query) { query: function(query) {
return Api.groups(query.term, skip_ldap, skip_groups, function(groups) { options = { all_available: all_available, skip_groups: skip_groups };
return Api.groups(query.term, options, function(groups) {
var data; var data;
data = { data = {
results: groups results: groups
......
...@@ -24,6 +24,81 @@ ...@@ -24,6 +24,81 @@
return null; return null;
} }
}; };
w.gl.utils.ajaxGet = function(url) {
return $.ajax({
type: "GET",
url: url,
dataType: "script"
});
};
w.gl.utils.split = function(val) {
return val.split(/,\s*/);
};
w.gl.utils.extractLast = function(term) {
return this.split(term).pop();
};
w.gl.utils.rstrip = function rstrip(val) {
if (val) {
return val.replace(/\s+$/, '');
} else {
return val;
}
};
w.gl.utils.disableButtonIfEmptyField = function(field_selector, button_selector, event_name) {
event_name = event_name || 'input';
var closest_submit, field, that;
that = this;
field = $(field_selector);
closest_submit = field.closest('form').find(button_selector);
if (this.rstrip(field.val()) === "") {
closest_submit.disable();
}
return field.on(event_name, function() {
if (that.rstrip($(this).val()) === "") {
return closest_submit.disable();
} else {
return closest_submit.enable();
}
});
};
w.gl.utils.disableButtonIfAnyEmptyField = function(form, form_selector, button_selector) {
var closest_submit, updateButtons;
closest_submit = form.find(button_selector);
updateButtons = function() {
var filled;
filled = true;
form.find('input').filter(form_selector).each(function() {
return filled = this.rstrip($(this).val()) !== "" || !$(this).attr('required');
});
if (filled) {
return closest_submit.enable();
} else {
return closest_submit.disable();
}
};
updateButtons();
return form.keyup(updateButtons);
};
w.gl.utils.sanitize = function(str) {
return str.replace(/<(?:.|\n)*?>/gm, '');
};
w.gl.utils.unbindEvents = function() {
return $(document).off('scroll');
};
w.gl.utils.shiftWindow = function() {
return w.scrollBy(0, -100);
};
gl.utils.updateTooltipTitle = function($tooltipEl, newTitle) { gl.utils.updateTooltipTitle = function($tooltipEl, newTitle) {
return $tooltipEl.tooltip('destroy').attr('title', newTitle).tooltip('fixTitle'); return $tooltipEl.tooltip('destroy').attr('title', newTitle).tooltip('fixTitle');
}; };
......
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
$('.project_member, .group_member').off('ajax:success').on('ajax:success', this.removeRow); $('.project_member, .group_member').off('ajax:success').on('ajax:success', this.removeRow);
$('.js-member-update-control').off('change').on('change', this.formSubmit); $('.js-member-update-control').off('change').on('change', this.formSubmit);
$('.js-edit-member-form').off('ajax:success').on('ajax:success', this.formSuccess); $('.js-edit-member-form').off('ajax:success').on('ajax:success', this.formSuccess);
disableButtonIfEmptyField('#user_ids', 'input[name=commit]', 'change'); gl.utils.disableButtonIfEmptyField('#user_ids', 'input[name=commit]', 'change');
} }
removeRow(e) { removeRow(e) {
......
...@@ -45,7 +45,9 @@ ...@@ -45,7 +45,9 @@
}; };
ProjectNew.prototype.toggleRepoVisibility = function () { ProjectNew.prototype.toggleRepoVisibility = function () {
var $repoAccessLevel = $('.js-repo-access-level select'); var $repoAccessLevel = $('.js-repo-access-level select'),
containerRegistry = document.querySelectorAll('.js-container-registry')[0],
containerRegistryCheckbox = document.getElementById('project_container_registry_enabled');
this.$repoSelects.find("option[value='" + $repoAccessLevel.val() + "']") this.$repoSelects.find("option[value='" + $repoAccessLevel.val() + "']")
.nextAll() .nextAll()
...@@ -70,8 +72,17 @@ ...@@ -70,8 +72,17 @@
if (selectedVal) { if (selectedVal) {
this.$repoSelects.removeClass('disabled'); this.$repoSelects.removeClass('disabled');
if (containerRegistry) {
containerRegistry.style.display = '';
}
} else { } else {
this.$repoSelects.addClass('disabled'); this.$repoSelects.addClass('disabled');
if (containerRegistry) {
containerRegistry.style.display = 'none';
containerRegistryCheckbox.checked = false;
}
} }
}.bind(this)); }.bind(this));
}; };
......
...@@ -24,7 +24,7 @@ ...@@ -24,7 +24,7 @@
data = groups.concat(projects); data = groups.concat(projects);
return finalCallback(data); return finalCallback(data);
}; };
return Api.groups(term, false, false, groupsCallback); return Api.groups(term, {}, groupsCallback);
}; };
} else { } else {
projectsCallback = finalCallback; projectsCallback = finalCallback;
...@@ -73,7 +73,7 @@ ...@@ -73,7 +73,7 @@
data = groups.concat(projects); data = groups.concat(projects);
return finalCallback(data); return finalCallback(data);
}; };
return Api.groups(query.term, false, false, groupsCallback); return Api.groups(query.term, {}, groupsCallback);
}; };
} else { } else {
projectsCallback = finalCallback; projectsCallback = finalCallback;
......
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
filterable: true, filterable: true,
fieldName: 'group_id', fieldName: 'group_id',
data: function(term, callback) { data: function(term, callback) {
return Api.groups(term, false, false, function(data) { return Api.groups(term, {}, function(data) {
data.unshift({ data.unshift({
name: 'Any' name: 'Any'
}); });
......
...@@ -23,6 +23,8 @@ ...@@ -23,6 +23,8 @@
$dropdown = $(dropdown); $dropdown = $(dropdown);
options.projectId = $dropdown.data('project-id'); options.projectId = $dropdown.data('project-id');
options.showCurrentUser = $dropdown.data('current-user'); options.showCurrentUser = $dropdown.data('current-user');
options.todoFilter = $dropdown.data('todo-filter');
options.todoStateFilter = $dropdown.data('todo-state-filter');
showNullUser = $dropdown.data('null-user'); showNullUser = $dropdown.data('null-user');
showMenuAbove = $dropdown.data('showMenuAbove'); showMenuAbove = $dropdown.data('showMenuAbove');
showAnyUser = $dropdown.data('any-user'); showAnyUser = $dropdown.data('any-user');
...@@ -394,6 +396,8 @@ ...@@ -394,6 +396,8 @@
project_id: options.projectId || null, project_id: options.projectId || null,
group_id: options.groupId || null, group_id: options.groupId || null,
skip_ldap: options.skipLdap || null, skip_ldap: options.skipLdap || null,
todo_filter: options.todoFilter || null,
todo_state_filter: options.todoStateFilter || null,
current_user: options.showCurrentUser || null, current_user: options.showCurrentUser || null,
push_code_to_protected_branches: options.pushCodeToProtectedBranches || null, push_code_to_protected_branches: options.pushCodeToProtectedBranches || null,
author_id: options.authorId || null, author_id: options.authorId || null,
......
...@@ -36,7 +36,7 @@ ...@@ -36,7 +36,7 @@
color: $dropdown-toggle-color; color: $dropdown-toggle-color;
font-size: 15px; font-size: 15px;
text-align: left; text-align: left;
border: 1px solid $dropdown-toggle-border-color; border: 1px solid $border-color;
border-radius: $border-radius-base; border-radius: $border-radius-base;
outline: 0; outline: 0;
text-overflow: ellipsis; text-overflow: ellipsis;
...@@ -45,11 +45,9 @@ ...@@ -45,11 +45,9 @@
.fa { .fa {
position: absolute; position: absolute;
top: 50%; top: 10px;
right: 6px; right: 8px;
margin-top: -6px;
color: $dropdown-toggle-icon-color; color: $dropdown-toggle-icon-color;
font-size: 10px;
&.fa-spinner { &.fa-spinner {
font-size: 16px; font-size: 16px;
......
...@@ -27,9 +27,9 @@ ...@@ -27,9 +27,9 @@
height: 0; height: 0;
margin-left: 2px; margin-left: 2px;
vertical-align: middle; vertical-align: middle;
border-top: $caret-width-base dashed; border-top: 5px dashed;
border-right: $caret-width-base solid transparent; border-right: 5px solid transparent;
border-left: $caret-width-base solid transparent; border-left: 5px solid transparent;
color: $gray-darkest; color: $gray-darkest;
} }
} }
......
...@@ -164,7 +164,22 @@ ...@@ -164,7 +164,22 @@
.branch-commit { .branch-commit {
color: $gl-gray; color: $gl-gray;
.commit-id, .commit-icon {
text-align: center;
display: inline-block;
svg {
height: 14px;
width: 14px;
vertical-align: middle;
fill: $table-text-gray;
}
}
.commit-id {
color: $gl-link-color;
}
.commit-row-message { .commit-row-message {
color: $gl-gray; color: $gl-gray;
} }
......
...@@ -55,6 +55,10 @@ ...@@ -55,6 +55,10 @@
float: left; float: left;
} }
.file-buttons {
font-size: 0;
}
.select2 { .select2 {
float: right; float: right;
} }
......
// CI icon colors
.ci-status-icon {
&-created {
fill: $gray-darkest;
}
&-skipped,
&-canceled {
fill: $gl-text-color;
}
}
...@@ -85,6 +85,11 @@ ...@@ -85,6 +85,11 @@
} }
.commit-link { .commit-link {
a {
&:focus {
text-decoration: none;
}
}
.ci-status { .ci-status {
...@@ -439,7 +444,7 @@ ...@@ -439,7 +444,7 @@
} }
.grouped-pipeline-dropdown { .grouped-pipeline-dropdown {
padding: 8px 0; padding: 0;
width: 186px; width: 186px;
left: auto; left: auto;
right: -197px; right: -197px;
...@@ -448,6 +453,14 @@ ...@@ -448,6 +453,14 @@
ul { ul {
max-height: 245px; max-height: 245px;
overflow: auto; overflow: auto;
li:first-child {
padding-top: 8px;
}
li:last-child {
padding-bottom: 8px;
}
} }
a { a {
......
...@@ -6,7 +6,8 @@ ...@@ -6,7 +6,8 @@
white-space: nowrap; white-space: nowrap;
border-radius: 4px; border-radius: 4px;
&:hover { &:hover,
&:focus {
text-decoration: none; text-decoration: none;
} }
......
...@@ -116,8 +116,8 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController ...@@ -116,8 +116,8 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
:metrics_packet_size, :metrics_packet_size,
:send_user_confirmation_email, :send_user_confirmation_email,
:container_registry_token_expire_delay, :container_registry_token_expire_delay,
:repository_storage,
:enabled_git_access_protocol, :enabled_git_access_protocol,
repository_storages: [],
restricted_visibility_levels: [], restricted_visibility_levels: [],
import_sources: [], import_sources: [],
disabled_oauth_sign_in_sources: [] disabled_oauth_sign_in_sources: []
......
...@@ -11,9 +11,13 @@ class AutocompleteController < ApplicationController ...@@ -11,9 +11,13 @@ class AutocompleteController < ApplicationController
@users = @users.reorder(:name) @users = @users.reorder(:name)
@users = @users.page(params[:page]) @users = @users.page(params[:page])
if params[:todo_filter].present?
@users = @users.todo_authors(current_user.id, params[:todo_state_filter])
end
if params[:search].blank? if params[:search].blank?
# Include current user if available to filter by "Me" # Include current user if available to filter by "Me"
if params[:current_user] && current_user if params[:current_user].present? && current_user
@users = [*@users, current_user] @users = [*@users, current_user]
end end
......
...@@ -7,7 +7,7 @@ class Projects::GroupLinksController < Projects::ApplicationController ...@@ -7,7 +7,7 @@ class Projects::GroupLinksController < Projects::ApplicationController
@group_links = project.project_group_links.all @group_links = project.project_group_links.all
@skip_groups = @group_links.pluck(:group_id) @skip_groups = @group_links.pluck(:group_id)
@skip_groups << project.group.try(:id) @skip_groups << project.namespace_id unless project.personal?
end end
def create def create
......
...@@ -93,11 +93,11 @@ module ApplicationSettingsHelper ...@@ -93,11 +93,11 @@ module ApplicationSettingsHelper
end end
end end
def repository_storage_options_for_select def repository_storages_options_for_select
options = Gitlab.config.repositories.storages.map do |name, path| options = Gitlab.config.repositories.storages.map do |name, path|
["#{name} - #{path}", name] ["#{name} - #{path}", name]
end end
options_for_select(options, @application_setting.repository_storage) options_for_select(options, @application_setting.repository_storages)
end end
end end
...@@ -47,8 +47,10 @@ module CiStatusHelper ...@@ -47,8 +47,10 @@ module CiStatusHelper
'icon_play' 'icon_play'
when 'created' when 'created'
'icon_status_created' 'icon_status_created'
when 'skipped'
'icon_status_skipped'
else else
'icon_status_cancel' 'icon_status_canceled'
end end
custom_icon(icon_name) custom_icon(icon_name)
......
...@@ -43,7 +43,7 @@ module DropdownsHelper ...@@ -43,7 +43,7 @@ module DropdownsHelper
default_label = data_attr[:default_label] default_label = data_attr[:default_label]
content_tag(:button, class: "dropdown-menu-toggle #{options[:toggle_class] if options.has_key?(:toggle_class)}", id: (options[:id] if options.has_key?(:id)), type: "button", data: data_attr) do content_tag(:button, class: "dropdown-menu-toggle #{options[:toggle_class] if options.has_key?(:toggle_class)}", id: (options[:id] if options.has_key?(:id)), type: "button", data: data_attr) do
output = content_tag(:span, toggle_text, class: "dropdown-toggle-text #{'is-default' if toggle_text == default_label}") output = content_tag(:span, toggle_text, class: "dropdown-toggle-text #{'is-default' if toggle_text == default_label}")
output << icon('chevron-down') output << icon('caret-down')
output.html_safe output.html_safe
end end
end end
......
...@@ -18,6 +18,7 @@ class ApplicationSetting < ActiveRecord::Base ...@@ -18,6 +18,7 @@ class ApplicationSetting < ActiveRecord::Base
serialize :disabled_oauth_sign_in_sources, Array serialize :disabled_oauth_sign_in_sources, Array
serialize :domain_whitelist, Array serialize :domain_whitelist, Array
serialize :domain_blacklist, Array serialize :domain_blacklist, Array
serialize :repository_storages
cache_markdown_field :sign_in_text cache_markdown_field :sign_in_text
cache_markdown_field :help_page_text cache_markdown_field :help_page_text
...@@ -74,9 +75,8 @@ class ApplicationSetting < ActiveRecord::Base ...@@ -74,9 +75,8 @@ class ApplicationSetting < ActiveRecord::Base
presence: true, presence: true,
numericality: { only_integer: true, greater_than: 0 } numericality: { only_integer: true, greater_than: 0 }
validates :repository_storage, validates :repository_storages, presence: true
presence: true, validate :check_repository_storages
inclusion: { in: ->(_object) { Gitlab.config.repositories.storages.keys } }
validates :enabled_git_access_protocol, validates :enabled_git_access_protocol,
inclusion: { in: %w(ssh http), allow_blank: true, allow_nil: true } inclusion: { in: %w(ssh http), allow_blank: true, allow_nil: true }
...@@ -166,7 +166,7 @@ class ApplicationSetting < ActiveRecord::Base ...@@ -166,7 +166,7 @@ class ApplicationSetting < ActiveRecord::Base
disabled_oauth_sign_in_sources: [], disabled_oauth_sign_in_sources: [],
send_user_confirmation_email: false, send_user_confirmation_email: false,
container_registry_token_expire_delay: 5, container_registry_token_expire_delay: 5,
repository_storage: 'default', repository_storages: ['default'],
user_default_external: false, user_default_external: false,
) )
end end
...@@ -201,6 +201,29 @@ class ApplicationSetting < ActiveRecord::Base ...@@ -201,6 +201,29 @@ class ApplicationSetting < ActiveRecord::Base
self.domain_blacklist_raw = file.read self.domain_blacklist_raw = file.read
end end
def repository_storages
value = read_attribute(:repository_storages)
value = [value] if value.is_a?(String)
value = [] if value.nil?
value
end
# repository_storage is still required in the API. Remove in 9.0
def repository_storage
repository_storages.first
end
def repository_storage=(value)
self.repository_storages = [value]
end
# Choose one of the available repository storage options. Currently all have
# equal weighting.
def pick_repository_storage
repository_storages.sample
end
def runners_registration_token def runners_registration_token
ensure_runners_registration_token! ensure_runners_registration_token!
end end
...@@ -208,4 +231,12 @@ class ApplicationSetting < ActiveRecord::Base ...@@ -208,4 +231,12 @@ class ApplicationSetting < ActiveRecord::Base
def health_check_access_token def health_check_access_token
ensure_health_check_access_token! ensure_health_check_access_token!
end end
private
def check_repository_storages
invalid = repository_storages - Gitlab.config.repositories.storages.keys
errors.add(:repository_storages, "can't include: #{invalid.join(", ")}") unless
invalid.empty?
end
end end
...@@ -441,11 +441,11 @@ class MergeRequest < ActiveRecord::Base ...@@ -441,11 +441,11 @@ class MergeRequest < ActiveRecord::Base
end end
def should_remove_source_branch? def should_remove_source_branch?
merge_params['should_remove_source_branch'].present? Gitlab::Utils.to_boolean(merge_params['should_remove_source_branch'])
end end
def force_remove_source_branch? def force_remove_source_branch?
merge_params['force_remove_source_branch'].present? Gitlab::Utils.to_boolean(merge_params['force_remove_source_branch'])
end end
def remove_source_branch? def remove_source_branch?
......
...@@ -28,7 +28,7 @@ class Project < ActiveRecord::Base ...@@ -28,7 +28,7 @@ class Project < ActiveRecord::Base
default_value_for :archived, false default_value_for :archived, false
default_value_for :visibility_level, gitlab_config_features.visibility_level default_value_for :visibility_level, gitlab_config_features.visibility_level
default_value_for :container_registry_enabled, gitlab_config_features.container_registry default_value_for :container_registry_enabled, gitlab_config_features.container_registry
default_value_for(:repository_storage) { current_application_settings.repository_storage } default_value_for(:repository_storage) { current_application_settings.pick_repository_storage }
default_value_for(:shared_runners_enabled) { current_application_settings.shared_runners_enabled } default_value_for(:shared_runners_enabled) { current_application_settings.shared_runners_enabled }
default_value_for :issues_enabled, gitlab_config_features.issues default_value_for :issues_enabled, gitlab_config_features.issues
default_value_for :merge_requests_enabled, gitlab_config_features.merge_requests default_value_for :merge_requests_enabled, gitlab_config_features.merge_requests
......
...@@ -173,6 +173,7 @@ class User < ActiveRecord::Base ...@@ -173,6 +173,7 @@ class User < ActiveRecord::Base
scope :active, -> { with_state(:active) } scope :active, -> { with_state(:active) }
scope :not_in_project, ->(project) { project.users.present? ? where("id not in (:ids)", ids: project.users.map(&:id) ) : all } scope :not_in_project, ->(project) { project.users.present? ? where("id not in (:ids)", ids: project.users.map(&:id) ) : all }
scope :without_projects, -> { where('id NOT IN (SELECT DISTINCT(user_id) FROM members)') } scope :without_projects, -> { where('id NOT IN (SELECT DISTINCT(user_id) FROM members)') }
scope :todo_authors, ->(user_id, state) { where(id: Todo.where(user_id: user_id, state: state).select(:author_id)) }
def self.with_two_factor def self.with_two_factor
joins("LEFT OUTER JOIN u2f_registrations AS u2f ON u2f.user_id = users.id"). joins("LEFT OUTER JOIN u2f_registrations AS u2f ON u2f.user_id = users.id").
......
...@@ -353,9 +353,9 @@ ...@@ -353,9 +353,9 @@
%fieldset %fieldset
%legend Repository Storage %legend Repository Storage
.form-group .form-group
= f.label :repository_storage, 'Storage path for new projects', class: 'control-label col-sm-2' = f.label :repository_storages, 'Storage paths for new projects', class: 'control-label col-sm-2'
.col-sm-10 .col-sm-10
= f.select :repository_storage, repository_storage_options_for_select, {}, class: 'form-control' = f.select :repository_storages, repository_storages_options_for_select, {include_hidden: false}, multiple: true, class: 'form-control'
.help-block .help-block
Manage repository storage paths. Learn more in the Manage repository storage paths. Learn more in the
= succeed "." do = succeed "." do
......
- @no_container = true - @no_container = true
- page_title "Logs" - page_title "Logs"
- loggers = [Gitlab::GitLogger, Gitlab::AppLogger, - loggers = [Gitlab::GitLogger, Gitlab::AppLogger,
Gitlab::ProductionLogger, Gitlab::SidekiqLogger, Gitlab::EnvironmentLogger, Gitlab::SidekiqLogger,
Gitlab::RepositoryCheckLogger] Gitlab::RepositoryCheckLogger]
= render 'admin/background_jobs/head' = render 'admin/background_jobs/head'
......
xml.instruct! xml.instruct!
xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://search.yahoo.com/mrss/" do xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://search.yahoo.com/mrss/" do
xml.title "#{current_user.name} issues" xml.title "#{current_user.name} issues"
xml.link href: issues_dashboard_url(format: :atom, private_token: current_user.try(:private_token)), rel: "self", type: "application/atom+xml" xml.link href: url_for(params), rel: "self", type: "application/atom+xml"
xml.link href: issues_dashboard_url, rel: "alternate", type: "text/html" xml.link href: issues_dashboard_url, rel: "alternate", type: "text/html"
xml.id issues_dashboard_url xml.id issues_dashboard_url
xml.updated @issues.first.created_at.xmlschema if @issues.reorder(nil).any? xml.updated @issues.first.created_at.xmlschema if @issues.reorder(nil).any?
......
...@@ -2,13 +2,13 @@ ...@@ -2,13 +2,13 @@
- header_title "Issues", issues_dashboard_path(assignee_id: current_user.id) - header_title "Issues", issues_dashboard_path(assignee_id: current_user.id)
= content_for :meta_tags do = content_for :meta_tags do
- if current_user - if current_user
= auto_discovery_link_tag(:atom, issues_dashboard_url(format: :atom, private_token: current_user.private_token), title: "#{current_user.name} issues") = auto_discovery_link_tag(:atom, url_for(params.merge(format: :atom, private_token: current_user.private_token)), title: "#{current_user.name} issues")
.top-area .top-area
= render 'shared/issuable/nav', type: :issues = render 'shared/issuable/nav', type: :issues
.nav-controls .nav-controls
- if current_user - if current_user
= link_to issues_dashboard_url(format: :atom, private_token: current_user.private_token), class: 'btn' do = link_to url_for(params.merge(format: :atom, private_token: current_user.private_token)), class: 'btn' do
= icon('rss') = icon('rss')
%span.icon-label %span.icon-label
Subscribe Subscribe
......
...@@ -37,7 +37,7 @@ ...@@ -37,7 +37,7 @@
- if params[:author_id].present? - if params[:author_id].present?
= hidden_field_tag(:author_id, params[:author_id]) = hidden_field_tag(:author_id, params[:author_id])
= dropdown_tag(user_dropdown_label(params[:author_id], 'Author'), options: { toggle_class: 'js-user-search js-filter-submit js-author-search', title: 'Filter by author', filter: true, filterInput: 'input#author-search', dropdown_class: 'dropdown-menu-user dropdown-menu-selectable dropdown-menu-author js-filter-submit', = dropdown_tag(user_dropdown_label(params[:author_id], 'Author'), options: { toggle_class: 'js-user-search js-filter-submit js-author-search', title: 'Filter by author', filter: true, filterInput: 'input#author-search', dropdown_class: 'dropdown-menu-user dropdown-menu-selectable dropdown-menu-author js-filter-submit',
placeholder: 'Search authors', data: { any_user: 'Any Author', first_user: (current_user.username if current_user), current_user: true, project_id: (@project.id if @project), selected: params[:author_id], field_name: 'author_id', default_label: 'Author' } }) placeholder: 'Search authors', data: { any_user: 'Any Author', first_user: (current_user.username if current_user), project_id: (@project.id if @project), selected: params[:author_id], field_name: 'author_id', default_label: 'Author', todo_filter: true, todo_state_filter: params[:state] || 'pending' } })
.filter-item.inline .filter-item.inline
- if params[:type].present? - if params[:type].present?
= hidden_field_tag(:type, params[:type]) = hidden_field_tag(:type, params[:type])
......
xml.instruct! xml.instruct!
xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://search.yahoo.com/mrss/" do xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://search.yahoo.com/mrss/" do
xml.title "#{@group.name} issues" xml.title "#{@group.name} issues"
xml.link href: issues_group_url(format: :atom, private_token: current_user.try(:private_token)), rel: "self", type: "application/atom+xml" xml.link href: url_for(params), rel: "self", type: "application/atom+xml"
xml.link href: issues_group_url, rel: "alternate", type: "text/html" xml.link href: issues_group_url, rel: "alternate", type: "text/html"
xml.id issues_group_url xml.id issues_group_url
xml.updated @issues.first.created_at.xmlschema if @issues.reorder(nil).any? xml.updated @issues.first.created_at.xmlschema if @issues.reorder(nil).any?
......
- page_title "Issues" - page_title "Issues"
= content_for :meta_tags do = content_for :meta_tags do
- if current_user - if current_user
= auto_discovery_link_tag(:atom, issues_group_url(@group, format: :atom, private_token: current_user.private_token), title: "#{@group.name} issues") = auto_discovery_link_tag(:atom, url_for(params.merge(format: :atom, private_token: current_user.private_token)), title: "#{@group.name} issues")
.top-area .top-area
= render 'shared/issuable/nav', type: :issues = render 'shared/issuable/nav', type: :issues
.nav-controls .nav-controls
- if current_user - if current_user
= link_to issues_group_url(@group, format: :atom, private_token: current_user.private_token), class: 'btn' do = link_to url_for(params.merge(format: :atom, private_token: current_user.private_token)), class: 'btn' do
= icon('rss') = icon('rss')
%span.icon-label %span.icon-label
Subscribe Subscribe
......
...@@ -26,8 +26,8 @@ ...@@ -26,8 +26,8 @@
Perform code reviews and enhance collaboration with merge requests. Perform code reviews and enhance collaboration with merge requests.
Each project can also have an issue tracker and a wiki. Each project can also have an issue tracker and a wiki.
- if current_application_settings.sign_in_text.present? - if current_application_settings.sign_in_text.present?
= markdown_field(current_application_settings, :sign_in_text) = markdown_field(current_application_settings, :sign_in_text)
%hr.footer-fixed %hr.footer-fixed
.container.footer-container .container.footer-container
......
...@@ -14,13 +14,13 @@ ...@@ -14,13 +14,13 @@
= text_field_tag 'file_name', params[:file_name], placeholder: "File name", = text_field_tag 'file_name', params[:file_name], placeholder: "File name",
required: true, class: 'form-control new-file-name' required: true, class: 'form-control new-file-name'
.pull-right .pull-right.file-buttons
.license-selector.js-license-selector-wrap.hidden .license-selector.js-license-selector-wrap.hidden
= dropdown_tag("Choose a License template", options: { toggle_class: 'js-license-selector', title: "Choose a license", filter: true, placeholder: "Filter", data: { data: licenses_for_select, project: @project.name, fullname: @project.namespace.human_name } } ) = dropdown_tag("Choose a License template", options: { toggle_class: 'btn js-license-selector', title: "Choose a license", filter: true, placeholder: "Filter", data: { data: licenses_for_select, project: @project.name, fullname: @project.namespace.human_name } } )
.gitignore-selector.js-gitignore-selector-wrap.hidden .gitignore-selector.js-gitignore-selector-wrap.hidden
= dropdown_tag("Choose a .gitignore template", options: { toggle_class: 'js-gitignore-selector', title: "Choose a template", filter: true, placeholder: "Filter", data: { data: gitignore_names } } ) = dropdown_tag("Choose a .gitignore template", options: { toggle_class: 'btn js-gitignore-selector', title: "Choose a template", filter: true, placeholder: "Filter", data: { data: gitignore_names } } )
.gitlab-ci-yml-selector.js-gitlab-ci-yml-selector-wrap.hidden .gitlab-ci-yml-selector.js-gitlab-ci-yml-selector-wrap.hidden
= dropdown_tag("Choose a GitLab CI Yaml template", options: { toggle_class: 'js-gitlab-ci-yml-selector', title: "Choose a template", filter: true, placeholder: "Filter", data: { data: gitlab_ci_ymls } } ) = dropdown_tag("Choose a GitLab CI Yaml template", options: { toggle_class: 'btn js-gitlab-ci-yml-selector', title: "Choose a template", filter: true, placeholder: "Filter", data: { data: gitlab_ci_ymls } } )
= button_tag class: 'soft-wrap-toggle btn', type: 'button' do = button_tag class: 'soft-wrap-toggle btn', type: 'button' do
%span.no-wrap %span.no-wrap
= custom_icon('icon_no_wrap') = custom_icon('icon_no_wrap')
......
...@@ -26,6 +26,6 @@ ...@@ -26,6 +26,6 @@
:javascript :javascript
disableButtonIfEmptyField($('.js-upload-blob-form').find('.js-commit-message'), '.btn-upload-file'); gl.utils.disableButtonIfEmptyField($('.js-upload-blob-form').find('.js-commit-message'), '.btn-upload-file');
new BlobFileDropzone($('.js-upload-blob-form'), '#{method}'); new BlobFileDropzone($('.js-upload-blob-form'), '#{method}');
new NewCommitForm($('.js-upload-blob-form')) new NewCommitForm($('.js-upload-blob-form'))
.branch-commit .branch-commit
.icon-container.commit-icon
= custom_icon("icon_commit")
= link_to commit.short_id, namespace_project_commit_path(project.namespace, project, commit.id), class: "commit-id monospace" = link_to commit.short_id, namespace_project_commit_path(project.namespace, project, commit.id), class: "commit-id monospace"
&middot; &middot;
%span.str-truncated %span.str-truncated
......
...@@ -41,7 +41,7 @@ ...@@ -41,7 +41,7 @@
- else - else
Cant find HEAD commit for this branch Cant find HEAD commit for this branch
- stages_status = pipeline.statuses.relevant.latest.stages_status - stages_status = pipeline.statuses.latest.stages_status
%td.stage-cell %td.stage-cell
- stages.each do |stage| - stages.each do |stage|
- status = stages_status[stage] - status = stages_status[stage]
......
...@@ -102,7 +102,7 @@ ...@@ -102,7 +102,7 @@
= link_to icon('question-circle'), help_page_path('workflow/lfs/manage_large_binaries_with_git_lfs') = link_to icon('question-circle'), help_page_path('workflow/lfs/manage_large_binaries_with_git_lfs')
- if Gitlab.config.registry.enabled - if Gitlab.config.registry.enabled
.form-group .form-group.js-container-registry{ style: ("display: none;" if @project.project_feature.send(:repository_access_level) == 0) }
.checkbox .checkbox
= f.label :container_registry_enabled do = f.label :container_registry_enabled do
= f.check_box :container_registry_enabled = f.check_box :container_registry_enabled
...@@ -290,4 +290,4 @@ ...@@ -290,4 +290,4 @@
Saving project. Saving project.
%p Please wait a moment, this page will automatically refresh when ready. %p Please wait a moment, this page will automatically refresh when ready.
= render 'shared/confirm_modal', phrase: @project.path = render 'shared/confirm_modal', phrase: @project.path
\ No newline at end of file
xml.instruct! xml.instruct!
xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://search.yahoo.com/mrss/" do xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://search.yahoo.com/mrss/" do
xml.title "#{@project.name} issues" xml.title "#{@project.name} issues"
xml.link href: namespace_project_issues_url(@project.namespace, @project, format: :atom, private_token: current_user.try(:private_token)), rel: "self", type: "application/atom+xml" xml.link href: url_for(params), rel: "self", type: "application/atom+xml"
xml.link href: namespace_project_issues_url(@project.namespace, @project), rel: "alternate", type: "text/html" xml.link href: namespace_project_issues_url(@project.namespace, @project), rel: "alternate", type: "text/html"
xml.id namespace_project_issues_url(@project.namespace, @project) xml.id namespace_project_issues_url(@project.namespace, @project)
xml.updated @issues.first.created_at.xmlschema if @issues.reorder(nil).any? xml.updated @issues.first.created_at.xmlschema if @issues.reorder(nil).any?
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
= content_for :meta_tags do = content_for :meta_tags do
- if current_user - if current_user
= auto_discovery_link_tag(:atom, namespace_project_issues_url(@project.namespace, @project, :atom, private_token: current_user.private_token), title: "#{@project.name} issues") = auto_discovery_link_tag(:atom, url_for(params.merge(format: :atom, private_token: current_user.private_token)), title: "#{@project.name} issues")
%div{ class: (container_class) } %div{ class: (container_class) }
- if @project.issues.any? - if @project.issues.any?
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
= render 'shared/issuable/nav', type: :issues = render 'shared/issuable/nav', type: :issues
.nav-controls .nav-controls
- if current_user - if current_user
= link_to namespace_project_issues_path(@project.namespace, @project, :atom, { private_token: current_user.private_token }), class: 'btn append-right-10' do = link_to url_for(params.merge(format: :atom, private_token: current_user.private_token)), class: 'btn append-right-10' do
= icon('rss') = icon('rss')
%span.icon-label %span.icon-label
Subscribe Subscribe
......
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
= render 'projects/last_push' = render 'projects/last_push'
= render "home_panel" = render "home_panel"
- if @project.feature_available?(:repository, current_user) - if current_user && can?(current_user, :download_code, @project)
%nav.project-stats{ class: container_class } %nav.project-stats{ class: container_class }
%ul.nav %ul.nav
%li %li
......
...@@ -37,5 +37,5 @@ ...@@ -37,5 +37,5 @@
:javascript :javascript
// Load last commit log for each file in tree // Load last commit log for each file in tree
$('#tree-slider').waitForImages(function() { $('#tree-slider').waitForImages(function() {
ajaxGet("#{escape_javascript(@logs_path)}"); gl.utils.ajaxGet("#{escape_javascript(@logs_path)}");
}); });
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 14 14"> <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" class="ci-status-icon-canceled" viewBox="0 0 14 14">
<g fill="#5C5C5C" fill-rule="evenodd"> <g fill="#5C5C5C" fill-rule="evenodd">
<path d="M12.5,7 C12.5,3.96243388 10.0375661,1.5 7,1.5 C3.96243388,1.5 1.5,3.96243388 1.5,7 C1.5,10.0375661 3.96243388,12.5 7,12.5 C10.0375661,12.5 12.5,10.0375661 12.5,7 Z M0,7 C0,3.13400675 3.13400675,0 7,0 C10.8659932,0 14,3.13400675 14,7 C14,10.8659932 10.8659932,14 7,14 C3.13400675,14 0,10.8659932 0,7 Z"/> <path d="M12.5,7 C12.5,3.96243388 10.0375661,1.5 7,1.5 C3.96243388,1.5 1.5,3.96243388 1.5,7 C1.5,10.0375661 3.96243388,12.5 7,12.5 C10.0375661,12.5 12.5,10.0375661 12.5,7 Z M0,7 C0,3.13400675 3.13400675,0 7,0 C10.8659932,0 14,3.13400675 14,7 C14,10.8659932 10.8659932,14 7,14 C3.13400675,14 0,10.8659932 0,7 Z"/>
<rect width="8" height="2" x="3" y="6" transform="rotate(45 7 7)" rx=".5"/> <rect width="8" height="2" x="3" y="6" transform="rotate(45 7 7)" rx=".5"/>
......
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 14 14" enable-background="new 0 0 14 14"><path d="M12.5,7 C12.5,4 10,1.5 7,1.5 C4,1.5 1.5,4 1.5,7 C1.5,10 4,12.5 7,12.5 C10,12.5 12.5,10 12.5,7 L12.5,7 Z M0,7 C0,3.1 3.1,0 7,0 C10.9,0 14,3.1 14,7 C14,10.9 10.9,14 7,14 C3.1,14 0,10.9 0,7 L0,7 Z" /><circle cx="7" cy="7" r="3.25"/></svg> <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" class="ci-status-icon-created" viewBox="0 0 14 14" enable-background="new 0 0 14 14"><path d="M12.5,7 C12.5,4 10,1.5 7,1.5 C4,1.5 1.5,4 1.5,7 C1.5,10 4,12.5 7,12.5 C10,12.5 12.5,10 12.5,7 L12.5,7 Z M0,7 C0,3.1 3.1,0 7,0 C10.9,0 14,3.1 14,7 C14,10.9 10.9,14 7,14 C3.1,14 0,10.9 0,7 L0,7 Z" /><circle cx="7" cy="7" r="3.25"/></svg>
<svg width="20" height="20" class="ci-status-icon-skipped" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><title>Group Copy 31</title><g fill="#5C5C5C" fill-rule="evenodd"><path d="M10 17.857c4.286 0 7.857-3.571 7.857-7.857S14.286 2.143 10 2.143 2.143 5.714 2.143 10 5.714 17.857 10 17.857M10 0c5.571 0 10 4.429 10 10s-4.429 10-10 10S0 15.571 0 10 4.429 0 10 0"/><path d="M10.986 11l-1.293 1.293a1 1 0 0 0 1.414 1.414l2.644-2.644a1.505 1.505 0 0 0 0-2.126l-2.644-2.644a1 1 0 0 0-1.414 1.414L10.986 9H6.4a1 1 0 0 0 0 2h4.586z"/></g></svg>
...@@ -142,6 +142,7 @@ ...@@ -142,6 +142,7 @@
.col-sm-10.col-sm-offset-2 .col-sm-10.col-sm-offset-2
.checkbox .checkbox
= label_tag 'merge_request[force_remove_source_branch]' do = label_tag 'merge_request[force_remove_source_branch]' do
= hidden_field_tag 'merge_request[force_remove_source_branch]', '0'
= check_box_tag 'merge_request[force_remove_source_branch]', '1', @merge_request.force_remove_source_branch? = check_box_tag 'merge_request[force_remove_source_branch]', '1', @merge_request.force_remove_source_branch?
Remove source branch when merge request is accepted. Remove source branch when merge request is accepted.
......
...@@ -22,7 +22,7 @@ ...@@ -22,7 +22,7 @@
%button.dropdown-menu-toggle.js-label-select.js-multiselect{class: classes.join(' '), type: "button", data: dropdown_data} %button.dropdown-menu-toggle.js-label-select.js-multiselect{class: classes.join(' '), type: "button", data: dropdown_data}
%span.dropdown-toggle-text{ class: ("is-default" if selected.nil? || selected.empty?) } %span.dropdown-toggle-text{ class: ("is-default" if selected.nil? || selected.empty?) }
= multi_label_name(selected, "Labels") = multi_label_name(selected, "Labels")
= icon('chevron-down') = icon('caret-down')
.dropdown-menu.dropdown-select.dropdown-menu-paging.dropdown-menu-labels.dropdown-menu-selectable .dropdown-menu.dropdown-select.dropdown-menu-paging.dropdown-menu-labels.dropdown-menu-selectable
= render partial: "shared/issuable/label_page_default", locals: { title: dropdown_title, show_footer: show_footer, show_create: show_create } = render partial: "shared/issuable/label_page_default", locals: { title: dropdown_title, show_footer: show_footer, show_create: show_create }
- if show_create && project && can?(current_user, :admin_label, project) - if show_create && project && can?(current_user, :admin_label, project)
......
---
title: 'Fix: Todos Filter Shows All Users'
merge_request:
author:
---
title: Issues atom feed url reflect filters on dashboard
merge_request: 7114
author: Lucas Deschamps
---
title: Only skip group when it's actually a group in the "Share with group" select
merge_request: 7262
author:
---
title: 'Fix: Guest sees some repository details and gets 404'
merge_request:
author:
---
title: Introduce round-robin project creation to spread load over multiple shards
merge_request: 7266
author:
---
title: Ensure merge request's "remove branch" accessors return booleans
merge_request: 7267
author:
---
title: Fix invalid filename validation on eslint
merge_request: 7281
author:
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
class RenameRepositoryStorageColumn < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
# Set this constant to true if this migration requires downtime.
DOWNTIME = false
# When a migration requires downtime you **must** uncomment the following
# constant and define a short and easy to understand explanation as to why the
# migration requires downtime.
# DOWNTIME_REASON = ''
# When using the methods "add_concurrent_index" or "add_column_with_default"
# you must disable the use of transactions as these methods can not run in an
# existing transaction. When using "add_concurrent_index" make sure that this
# method is the _only_ method called in the migration, any other changes
# should go in a separate migration. This ensures that upon failure _only_ the
# index creation fails and can be retried or reverted easily.
#
# To disable transactions uncomment the following line and remove these
# comments:
# disable_ddl_transaction!
def change
rename_column :application_settings, :repository_storage, :repository_storages
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: 20161025231710) do ActiveRecord::Schema.define(version: 20161103171205) 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"
...@@ -88,7 +88,7 @@ ActiveRecord::Schema.define(version: 20161025231710) do ...@@ -88,7 +88,7 @@ ActiveRecord::Schema.define(version: 20161025231710) do
t.integer "container_registry_token_expire_delay", default: 5 t.integer "container_registry_token_expire_delay", default: 5
t.text "after_sign_up_text" t.text "after_sign_up_text"
t.boolean "user_default_external", default: false, null: false t.boolean "user_default_external", default: false, null: false
t.string "repository_storage", default: "default" t.string "repository_storages", default: "default"
t.string "enabled_git_access_protocol" t.string "enabled_git_access_protocol"
t.boolean "domain_blacklist_enabled", default: false t.boolean "domain_blacklist_enabled", default: false
t.text "domain_blacklist" t.text "domain_blacklist"
......
...@@ -13,7 +13,8 @@ This guide talks about how to read and use these system log files. ...@@ -13,7 +13,8 @@ This guide talks about how to read and use these system log files.
This file lives in `/var/log/gitlab/gitlab-rails/production.log` for This file lives in `/var/log/gitlab/gitlab-rails/production.log` for
omnibus package or in `/home/git/gitlab/log/production.log` for omnibus package or in `/home/git/gitlab/log/production.log` for
installations from source. installations from source. (When Gitlab is running in an environment
other than production, the corresponding logfile is shown here.)
It contains information about all performed requests. You can see the It contains information about all performed requests. You can see the
URL and type of request, IP address and what exactly parts of code were URL and type of request, IP address and what exactly parts of code were
......
...@@ -91,6 +91,9 @@ be stored via the **Application Settings** in the Admin area. ...@@ -91,6 +91,9 @@ be stored via the **Application Settings** in the Admin area.
![Choose repository storage path in Admin area](img/repository_storages_admin_ui.png) ![Choose repository storage path in Admin area](img/repository_storages_admin_ui.png)
Beginning with GitLab 8.13.4, multiple paths can be chosen. New projects will be
randomly placed on one of the selected paths.
[ce-4578]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/4578 [ce-4578]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/4578
[restart gitlab]: restart_gitlab.md#installations-from-source [restart gitlab]: restart_gitlab.md#installations-from-source
[reconfigure gitlab]: restart_gitlab.md#omnibus-gitlab-reconfigure [reconfigure gitlab]: restart_gitlab.md#omnibus-gitlab-reconfigure
......
...@@ -2,7 +2,12 @@ ...@@ -2,7 +2,12 @@
## List groups ## List groups
Get a list of groups. (As user: my groups, as admin: all groups) Get a list of groups. (As user: my groups or all available, as admin: all groups).
Parameters:
- `all_available` (optional) - if passed, show all groups you have access to
- `skip_groups` (optional)(array of group IDs) - if passed, skip groups
``` ```
GET /groups GET /groups
...@@ -21,7 +26,6 @@ GET /groups ...@@ -21,7 +26,6 @@ GET /groups
You can search for groups by name or path, see below. You can search for groups by name or path, see below.
## List a group's projects ## List a group's projects
Get a list of projects in this group. Get a list of projects in this group.
......
...@@ -42,6 +42,7 @@ Example response: ...@@ -42,6 +42,7 @@ Example response:
"sign_in_text" : null, "sign_in_text" : null,
"container_registry_token_expire_delay": 5, "container_registry_token_expire_delay": 5,
"repository_storage": "default", "repository_storage": "default",
"repository_storages": ["default"],
"koding_enabled": false, "koding_enabled": false,
"koding_url": null "koding_url": null
} }
...@@ -73,7 +74,8 @@ PUT /application/settings ...@@ -73,7 +74,8 @@ PUT /application/settings
| `user_oauth_applications` | boolean | no | Allow users to register any application to use GitLab as an OAuth provider | | `user_oauth_applications` | boolean | no | Allow users to register any application to use GitLab as an OAuth provider |
| `after_sign_out_path` | string | no | Where to redirect users after logout | | `after_sign_out_path` | string | no | Where to redirect users after logout |
| `container_registry_token_expire_delay` | integer | no | Container Registry token duration in minutes | | `container_registry_token_expire_delay` | integer | no | Container Registry token duration in minutes |
| `repository_storage` | string | no | Storage path for new projects. The value should be the name of one of the repository storage paths defined in your gitlab.yml | | `repository_storages` | array of strings | no | A list of names of enabled storage paths, taken from `gitlab.yml`. New projects will be created in one of these stores, chosen at random. |
| `repository_storage` | string | no | The first entry in `repository_storages`. Deprecated, but retained for compatibility reasons |
| `enabled_git_access_protocol` | string | no | Enabled protocols for Git access. Allowed values are: `ssh`, `http`, and `nil` to allow both protocols. | | `enabled_git_access_protocol` | string | no | Enabled protocols for Git access. Allowed values are: `ssh`, `http`, and `nil` to allow both protocols. |
| `koding_enabled` | boolean | no | Enable Koding integration. Default is `false`. | | `koding_enabled` | boolean | no | Enable Koding integration. Default is `false`. |
| `koding_url` | string | yes (if `koding_enabled` is `true`) | The Koding instance URL for integration. | | `koding_url` | string | yes (if `koding_enabled` is `true`) | The Koding instance URL for integration. |
......
...@@ -226,7 +226,7 @@ e.g. `docker run --rm -t -i -v $(pwd)/src:/home/app/src test-image:latest run_ap ...@@ -226,7 +226,7 @@ e.g. `docker run --rm -t -i -v $(pwd)/src:/home/app/src test-image:latest run_ap
> **Note:** > **Note:**
This feature requires GitLab 8.8 and GitLab Runner 1.2. This feature requires GitLab 8.8 and GitLab Runner 1.2.
Once you've built a Docker image, you can push it up to the built-in [GitLab Container Registry](../../container_registry/README.md). For example, if you're using Once you've built a Docker image, you can push it up to the built-in [GitLab Container Registry](../../user/project/container_registry.md). For example, if you're using
docker-in-docker on your runners, this is how your `.gitlab-ci.yml` could look: docker-in-docker on your runners, this is how your `.gitlab-ci.yml` could look:
......
...@@ -465,6 +465,7 @@ curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" --data "domain ...@@ -465,6 +465,7 @@ curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" --data "domain
[cURL]: http://curl.haxx.se/ "cURL website" [cURL]: http://curl.haxx.se/ "cURL website"
[single spaces]: http://www.slate.com/articles/technology/technology/2011/01/space_invaders.html [single spaces]: http://www.slate.com/articles/technology/technology/2011/01/space_invaders.html
[gfm]: http://docs.gitlab.com/ce/user/markdown.html#newlines "GitLab flavored markdown documentation" [gfm]: http://docs.gitlab.com/ce/user/markdown.html#newlines "GitLab flavored markdown documentation"
[ce-1242]: https://gitlab.com/gitlab-org/gitlab-ce/issues/1242
[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
......
...@@ -196,6 +196,12 @@ It consists of two subtasks: ...@@ -196,6 +196,12 @@ It consists of two subtasks:
As long as the fixtures don't change, `rake teaspoon:tests` is sufficient As long as the fixtures don't change, `rake teaspoon:tests` is sufficient
(and saves you some time). (and saves you some time).
If you need to debug your tests and/or application code while they're
running, navigate to [localhost:3000/teaspoon](http://localhost:3000/teaspoon)
in your browser, open DevTools, and run tests for individual files by clicking
on them. This is also much faster than setting up and running tests from the
command line.
Please note: Not all of the frontend fixtures are generated. Some are still static Please note: Not all of the frontend fixtures are generated. Some are still static
files. These will not be touched by `rake teaspoon:fixtures`. files. These will not be touched by `rake teaspoon:fixtures`.
......
...@@ -42,14 +42,6 @@ To run several tests inside one directory: ...@@ -42,14 +42,6 @@ To run several tests inside one directory:
If you want to use [Spring](https://github.com/rails/spring) set If you want to use [Spring](https://github.com/rails/spring) set
`ENABLE_SPRING=1` in your environment. `ENABLE_SPRING=1` in your environment.
## Generate searchable docs for source code
You can find results under the `doc/code` directory.
```
bundle exec rake gitlab:generate_docs
```
## Generate API documentation for project services (e.g. Slack) ## Generate API documentation for project services (e.g. Slack)
``` ```
......
...@@ -479,10 +479,14 @@ Copy the example site config: ...@@ -479,10 +479,14 @@ Copy the example site config:
sudo cp lib/support/nginx/gitlab /etc/nginx/sites-available/gitlab sudo cp lib/support/nginx/gitlab /etc/nginx/sites-available/gitlab
sudo ln -s /etc/nginx/sites-available/gitlab /etc/nginx/sites-enabled/gitlab sudo ln -s /etc/nginx/sites-available/gitlab /etc/nginx/sites-enabled/gitlab
Make sure to edit the config file to match your setup: Make sure to edit the config file to match your setup. Also, ensure that you match your paths to GitLab, especially if installing for a user other than the 'git' user:
# Change YOUR_SERVER_FQDN to the fully-qualified # Change YOUR_SERVER_FQDN to the fully-qualified
# domain name of your host serving GitLab. # domain name of your host serving GitLab.
#
# Remember to match your paths to GitLab, especially
# if installing for a user other than 'git'.
#
# If using Ubuntu default nginx install: # If using Ubuntu default nginx install:
# either remove the default_server from the listen line # either remove the default_server from the listen line
# or else sudo rm -f /etc/nginx/sites-enabled/default # or else sudo rm -f /etc/nginx/sites-enabled/default
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
GitLab University is the best place to learn about **Version Control with Git and GitLab**. GitLab University is the best place to learn about **Version Control with Git and GitLab**.
It doesn't replace, but accompanies our great [Documentation](http://docs.gitlab.com) It doesn't replace, but accompanies our great [Documentation](https://docs.gitlab.com)
and [Blog Articles](https://about.gitlab.com/blog/). and [Blog Articles](https://about.gitlab.com/blog/).
Would you like to contribute to GitLab University? Then please take a look at our contribution [process](/process) for more information. Would you like to contribute to GitLab University? Then please take a look at our contribution [process](/process) for more information.
...@@ -31,7 +31,7 @@ The curriculum is composed of GitLab videos, screencasts, presentations, project ...@@ -31,7 +31,7 @@ The curriculum is composed of GitLab videos, screencasts, presentations, project
1. [An Overview of GitLab.com - Video](https://www.youtube.com/watch?v=WaiL5DGEMR4) 1. [An Overview of GitLab.com - Video](https://www.youtube.com/watch?v=WaiL5DGEMR4)
1. [Why Use Git and GitLab - Slides](https://docs.google.com/a/gitlab.com/presentation/d/1RcZhFmn5VPvoFu6UMxhMOy7lAsToeBZRjLRn0LIdaNc/edit?usp=drive_web) 1. [Why Use Git and GitLab - Slides](https://docs.google.com/a/gitlab.com/presentation/d/1RcZhFmn5VPvoFu6UMxhMOy7lAsToeBZRjLRn0LIdaNc/edit?usp=drive_web)
1. [GitLab Basics - Article](http://doc.gitlab.com/ce/gitlab-basics/README.html) 1. [GitLab Basics - Article](../gitlab-basics/README.md)
1. [Git and GitLab Basics - Video](https://www.youtube.com/watch?v=03wb9FvO4Ak&index=5&list=PLFGfElNsQthbQu_IWlNOxul0TbS_2JH-e) 1. [Git and GitLab Basics - Video](https://www.youtube.com/watch?v=03wb9FvO4Ak&index=5&list=PLFGfElNsQthbQu_IWlNOxul0TbS_2JH-e)
1. [Git and GitLab Basics - Online Course](https://courses.platzi.com/classes/git-gitlab/concepto/part-1/part-23370/material/) 1. [Git and GitLab Basics - Online Course](https://courses.platzi.com/classes/git-gitlab/concepto/part-1/part-23370/material/)
1. [Comparison of GitLab Versions](https://about.gitlab.com/features/#compare) 1. [Comparison of GitLab Versions](https://about.gitlab.com/features/#compare)
...@@ -51,10 +51,10 @@ The curriculum is composed of GitLab videos, screencasts, presentations, project ...@@ -51,10 +51,10 @@ The curriculum is composed of GitLab videos, screencasts, presentations, project
#### 1.5. Migrating from other Source Control #### 1.5. Migrating from other Source Control
1. [Migrating from BitBucket/Stash](http://doc.gitlab.com/ee/workflow/importing/import_projects_from_bitbucket.html) 1. [Migrating from BitBucket/Stash](https://docs.gitlab.com/ee/workflow/importing/import_projects_from_bitbucket.html)
1. [Migrating from GitHub](http://doc.gitlab.com/ee/workflow/importing/import_projects_from_github.html) 1. [Migrating from GitHub](https://docs.gitlab.com/ee/workflow/importing/import_projects_from_github.html)
1. [Migrating from SVN](http://doc.gitlab.com/ee/workflow/importing/migrating_from_svn.html) 1. [Migrating from SVN](https://docs.gitlab.com/ee/workflow/importing/migrating_from_svn.html)
1. [Migrating from Fogbugz](http://doc.gitlab.com/ee/workflow/importing/import_projects_from_fogbugz.html) 1. [Migrating from Fogbugz](https://docs.gitlab.com/ee/workflow/importing/import_projects_from_fogbugz.html)
#### 1.6. GitLab Inc. #### 1.6. GitLab Inc.
...@@ -91,11 +91,11 @@ The curriculum is composed of GitLab videos, screencasts, presentations, project ...@@ -91,11 +91,11 @@ The curriculum is composed of GitLab videos, screencasts, presentations, project
1. [Using any Static Site Generator with GitLab Pages](https://about.gitlab.com/2016/06/17/ssg-overview-gitlab-pages-part-3-examples-ci/) 1. [Using any Static Site Generator with GitLab Pages](https://about.gitlab.com/2016/06/17/ssg-overview-gitlab-pages-part-3-examples-ci/)
1. [Securing GitLab Pages with SSL](https://about.gitlab.com/2016/06/24/secure-gitlab-pages-with-startssl/) 1. [Securing GitLab Pages with SSL](https://about.gitlab.com/2016/06/24/secure-gitlab-pages-with-startssl/)
1. [GitLab Pages Documentation](http://doc.gitlab.com/ee/pages/README.html) 1. [GitLab Pages Documentation](https://docs.gitlab.com/ee/pages/README.html)
#### 2.2. GitLab Issues #### 2.2. GitLab Issues
1. [Markdown in GitLab](http://doc.gitlab.com/ce/markdown/markdown.html) 1. [Markdown in GitLab](../user/markdown.md)
1. [Issues and Merge Requests - Video](https://www.youtube.com/watch?v=raXvuwet78M) 1. [Issues and Merge Requests - Video](https://www.youtube.com/watch?v=raXvuwet78M)
1. [Due Dates and Milestones fro GitLab Issues](https://about.gitlab.com/2016/08/05/feature-highlight-set-dates-for-issues/) 1. [Due Dates and Milestones fro GitLab Issues](https://about.gitlab.com/2016/08/05/feature-highlight-set-dates-for-issues/)
1. [How to Use GitLab Labels](https://about.gitlab.com/2016/08/17/using-gitlab-labels/) 1. [How to Use GitLab Labels](https://about.gitlab.com/2016/08/17/using-gitlab-labels/)
...@@ -129,7 +129,7 @@ The curriculum is composed of GitLab videos, screencasts, presentations, project ...@@ -129,7 +129,7 @@ The curriculum is composed of GitLab videos, screencasts, presentations, project
1. [GitLab Flow vs Forking in GitLab - Video](https://www.youtube.com/watch?v=UGotqAUACZA) 1. [GitLab Flow vs Forking in GitLab - Video](https://www.youtube.com/watch?v=UGotqAUACZA)
1. [GitLab Flow Overview](https://about.gitlab.com/2014/09/29/gitlab-flow/) 1. [GitLab Flow Overview](https://about.gitlab.com/2014/09/29/gitlab-flow/)
1. [Always Start with an Issue](https://about.gitlab.com/2016/03/03/start-with-an-issue/) 1. [Always Start with an Issue](https://about.gitlab.com/2016/03/03/start-with-an-issue/)
1. [GitLab Flow Documentation](http://doc.gitlab.com/ee/workflow/gitlab_flow.html) 1. [GitLab Flow Documentation](https://docs.gitlab.com/ee/workflow/gitlab_flow.html)
#### 2.5. GitLab Comparisons #### 2.5. GitLab Comparisons
...@@ -189,8 +189,8 @@ The curriculum is composed of GitLab videos, screencasts, presentations, project ...@@ -189,8 +189,8 @@ The curriculum is composed of GitLab videos, screencasts, presentations, project
#### 3.9. <a name="integrations"></a> Integrations #### 3.9. <a name="integrations"></a> Integrations
1. [How to Integrate JIRA and Jenkins with GitLab - Video](https://gitlabmeetings.webex.com/gitlabmeetings/ldr.php?RCID=44b548147a67ab4d8a62274047146415) 1. [How to Integrate JIRA and Jenkins with GitLab - Video](https://gitlabmeetings.webex.com/gitlabmeetings/ldr.php?RCID=44b548147a67ab4d8a62274047146415)
1. [How to Integrate Jira with GitLab](http://doc.gitlab.com/ee/integration/jira.html) 1. [How to Integrate Jira with GitLab](https://docs.gitlab.com/ee/integration/jira.html)
1. [How to Integrate Jenkins with GitLab](http://doc.gitlab.com/ee/integration/jenkins.html) 1. [How to Integrate Jenkins with GitLab](https://docs.gitlab.com/ee/integration/jenkins.html)
1. [How to Integrate Bamboo with GitLab](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/project_services/bamboo.md) 1. [How to Integrate Bamboo with GitLab](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/project_services/bamboo.md)
1. [How to Integrate Slack with GitLab](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/integration/slack.md) 1. [How to Integrate Slack with GitLab](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/integration/slack.md)
1. [How to Integrate Convox with GitLab](https://about.gitlab.com/2016/06/09/continuous-delivery-with-gitlab-and-convox/) 1. [How to Integrate Convox with GitLab](https://about.gitlab.com/2016/06/09/continuous-delivery-with-gitlab-and-convox/)
......
...@@ -10,7 +10,7 @@ User authentication by combination of 2 different steps during login. This allow ...@@ -10,7 +10,7 @@ User authentication by combination of 2 different steps during login. This allow
### Access Levels ### Access Levels
Process of selective restriction to create, view, modify or delete a resource based on a set of assigned permissions. See [GitLab's Permission Guidelines](http://doc.gitlab.com/ce/permissions/permissions.html) Process of selective restriction to create, view, modify or delete a resource based on a set of assigned permissions. See [GitLab's Permission Guidelines](../../permissions/permissions.md
### Active Directory (AD) ### Active Directory (AD)
......
...@@ -58,28 +58,28 @@ Sometimes we need to upgrade customers from old versions of GitLab to latest, so ...@@ -58,28 +58,28 @@ Sometimes we need to upgrade customers from old versions of GitLab to latest, so
- Users - Users
- Groups - Groups
- Projects - Projects
- [Backup using our Backup rake task](http://docs.gitlab.com/ce/raketasks/backup_restore.html#create-a-backup-of-the-gitlab-system) - [Backup using our Backup rake task](https://docs.gitlab.com/ce/raketasks/backup_restore.html#create-a-backup-of-the-gitlab-system)
- [Upgrade to 5.0 source using our Upgrade documentation](https://gitlab.com/gitlab-org/gitlab-ee/blob/master/doc/update/4.2-to-5.0.md) - [Upgrade to 5.0 source using our Upgrade documentation](https://gitlab.com/gitlab-org/gitlab-ee/blob/master/doc/update/4.2-to-5.0.md)
- [Upgrade to 5.1 source](https://gitlab.com/gitlab-org/gitlab-ee/blob/master/doc/update/5.0-to-5.1.md) - [Upgrade to 5.1 source](https://gitlab.com/gitlab-org/gitlab-ee/blob/master/doc/update/5.0-to-5.1.md)
- [Upgrade to 6.0 source](https://gitlab.com/gitlab-org/gitlab-ee/blob/master/doc/update/5.1-to-6.0.md) - [Upgrade to 6.0 source](https://gitlab.com/gitlab-org/gitlab-ee/blob/master/doc/update/5.1-to-6.0.md)
- [Upgrade to 7.14 source](https://gitlab.com/gitlab-org/gitlab-ee/blob/master/doc/update/6.x-or-7.x-to-7.14.md) - [Upgrade to 7.14 source](https://gitlab.com/gitlab-org/gitlab-ee/blob/master/doc/update/6.x-or-7.x-to-7.14.md)
- [Backup using our Backup rake task](http://docs.gitlab.com/ce/raketasks/backup_restore.html#create-a-backup-of-the-gitlab-system) - [Backup using our Backup rake task](https://docs.gitlab.com/ce/raketasks/backup_restore.html#create-a-backup-of-the-gitlab-system)
- [Perform the MySQL to PostgreSQL migration to convert your backup](http://docs.gitlab.com/ce/update/mysql_to_postgresql.html#converting-a-gitlab-backup-file-from-mysql-to-postgres) - [Perform the MySQL to PostgreSQL migration to convert your backup](https://docs.gitlab.com/ce/update/mysql_to_postgresql.html#converting-a-gitlab-backup-file-from-mysql-to-postgres)
- [Upgrade to Omnibus 7.14](http://doc.gitlab.com/omnibus/update/README.html#upgrading-from-a-non-omnibus-installation-to-an-omnibus-installation) - [Upgrade to Omnibus 7.14](https://docs.gitlab.com/omnibus/update/README.html#upgrading-from-a-non-omnibus-installation-to-an-omnibus-installation)
- [Restore backup using our Restore rake task](http://docs.gitlab.com/ce/raketasks/backup_restore.html#restore-a-previously-created-backup) - [Restore backup using our Restore rake task](https://docs.gitlab.com/ce/raketasks/backup_restore.html#restore-a-previously-created-backup)
- [Upgrade to latest EE](https://about.gitlab.com/downloads-ee) - [Upgrade to latest EE](https://about.gitlab.com/downloads-ee)
- (GitLab inc. only) Acquire and apply a license for the Enterprise Edition product, ask in #support - (GitLab inc. only) Acquire and apply a license for the Enterprise Edition product, ask in #support
- Perform a downgrade from [EE to CE](http://doc.gitlab.com/ee/downgrade_ee_to_ce/README.html) - Perform a downgrade from [EE to CE](https://docs.gitlab.com/ee/downgrade_ee_to_ce/README.html)
#### Start to learn about some of the integrations that we support #### Start to learn about some of the integrations that we support
Our integrations add great value to GitLab. User questions often relate to integrating GitLab with existing external services and the configuration involved Our integrations add great value to GitLab. User questions often relate to integrating GitLab with existing external services and the configuration involved
- Learn about our Integrations (specially, not only): - Learn about our Integrations (specially, not only):
- [LDAP](http://doc.gitlab.com/ee/integration/ldap.html) - [LDAP](https://docs.gitlab.com/ee/integration/ldap.html)
- [JIRA](http://doc.gitlab.com/ee/project_services/jira.html) - [JIRA](https://docs.gitlab.com/ee/project_services/jira.html)
- [Jenkins](http://doc.gitlab.com/ee/integration/jenkins.html) - [Jenkins](https://docs.gitlab.com/ee/integration/jenkins.html)
- [SAML](http://doc.gitlab.com/ce/integration/saml.html) - [SAML](https://docs.gitlab.com/ce/integration/saml.html)
#### Goals #### Goals
...@@ -91,8 +91,8 @@ Our integrations add great value to GitLab. User questions often relate to integ ...@@ -91,8 +91,8 @@ Our integrations add great value to GitLab. User questions often relate to integ
#### Understand the gathering of diagnostics for GitLab instances #### Understand the gathering of diagnostics for GitLab instances
- Learn about the GitLab checks that are available - Learn about the GitLab checks that are available
- [Environment Information and maintenance checks](http://docs.gitlab.com/ce/raketasks/maintenance.html) - [Environment Information and maintenance checks](https://docs.gitlab.com/ce/raketasks/maintenance.html)
- [GitLab check](http://docs.gitlab.com/ce/raketasks/check.html) - [GitLab check](https://docs.gitlab.com/ce/raketasks/check.html)
- Omnibus commands - Omnibus commands
- [Status](https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/doc/maintenance/README.md#get-service-status) - [Status](https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/doc/maintenance/README.md#get-service-status)
- [Starting and stopping services](https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/doc/maintenance/README.md#starting-and-stopping) - [Starting and stopping services](https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/doc/maintenance/README.md#starting-and-stopping)
...@@ -167,12 +167,12 @@ Some tickets need specific knowledge or a deep understanding of a particular com ...@@ -167,12 +167,12 @@ Some tickets need specific knowledge or a deep understanding of a particular com
Move on to understanding some of GitLab's more advanced features. You can make use of GitLab.com to understand the features from an end-user perspective and then use your own instance to understand setup and configuration of the feature from an Administrative perspective Move on to understanding some of GitLab's more advanced features. You can make use of GitLab.com to understand the features from an end-user perspective and then use your own instance to understand setup and configuration of the feature from an Administrative perspective
- Set up and try [Git Annex](http://doc.gitlab.com/ee/workflow/git_annex.html) - Set up and try [Git Annex](https://docs.gitlab.com/ee/workflow/git_annex.html)
- Set up and try [Git LFS](http://doc.gitlab.com/ee/workflow/lfs/manage_large_binaries_with_git_lfs.html) - Set up and try [Git LFS](https://docs.gitlab.com/ee/workflow/lfs/manage_large_binaries_with_git_lfs.html)
- Get to know the [GitLab API](http://doc.gitlab.com/ee/api/README.html), its capabilities and shortcomings - Get to know the [GitLab API](https://docs.gitlab.com/ee/api/README.html), its capabilities and shortcomings
- Learn how to [migrate from SVN to Git](http://doc.gitlab.com/ee/workflow/importing/migrating_from_svn.html) - Learn how to [migrate from SVN to Git](https://docs.gitlab.com/ee/workflow/importing/migrating_from_svn.html)
- Set up [GitLab CI](http://doc.gitlab.com/ee/ci/quick_start/README.html) - Set up [GitLab CI](https://docs.gitlab.com/ee/ci/quick_start/README.html)
- Create your first [GitLab Page](http://doc.gitlab.com/ee/pages/administration.html) - Create your first [GitLab Page](https://docs.gitlab.com/ee/pages/administration.html)
- Get to know the GitLab Codebase by reading through the source code: - Get to know the GitLab Codebase by reading through the source code:
- Find the differences between the [EE codebase](https://gitlab.com/gitlab-org/gitlab-ce) - Find the differences between the [EE codebase](https://gitlab.com/gitlab-org/gitlab-ce)
and the [CE codebase](https://gitlab.com/gitlab-org/gitlab-ce) and the [CE codebase](https://gitlab.com/gitlab-org/gitlab-ce)
......
...@@ -4,7 +4,7 @@ class Spinach::Features::AdminLogs < Spinach::FeatureSteps ...@@ -4,7 +4,7 @@ class Spinach::Features::AdminLogs < Spinach::FeatureSteps
include SharedAdmin include SharedAdmin
step 'I should see tabs with available logs' do step 'I should see tabs with available logs' do
expect(page).to have_content 'production.log' expect(page).to have_content 'test.log'
expect(page).to have_content 'githost.log' expect(page).to have_content 'githost.log'
expect(page).to have_content 'application.log' expect(page).to have_content 'application.log'
end end
......
...@@ -509,6 +509,7 @@ module API ...@@ -509,6 +509,7 @@ module API
expose :after_sign_out_path expose :after_sign_out_path
expose :container_registry_token_expire_delay expose :container_registry_token_expire_delay
expose :repository_storage expose :repository_storage
expose :repository_storages
expose :koding_enabled expose :koding_enabled
expose :koding_url expose :koding_url
end end
......
...@@ -8,11 +8,14 @@ module API ...@@ -8,11 +8,14 @@ module API
# #
# Parameters: # Parameters:
# skip_groups (optional) - Array of group ids to exclude from list # skip_groups (optional) - Array of group ids to exclude from list
# all_available (optional, boolean) - Show all group that you have access to
# Example Request: # Example Request:
# GET /groups # GET /groups
get do get do
@groups = if current_user.admin @groups = if current_user.admin
Group.all Group.all
elsif params[:all_available]
GroupsFinder.new.execute(current_user)
else else
current_user.groups current_user.groups
end end
......
module API module API
# Projects API # Projects API
class ProjectHooks < Grape::API class ProjectHooks < Grape::API
helpers do
params :project_hook_properties do
requires :url, type: String, desc: "The URL to send the request to"
optional :push_events, type: Boolean, desc: "Trigger hook on push events"
optional :issues_events, type: Boolean, desc: "Trigger hook on issues events"
optional :merge_requests_events, type: Boolean, desc: "Trigger hook on merge request events"
optional :tag_push_events, type: Boolean, desc: "Trigger hook on tag push events"
optional :note_events, type: Boolean, desc: "Trigger hook on note(comment) events"
optional :build_events, type: Boolean, desc: "Trigger hook on build events"
optional :pipeline_events, type: Boolean, desc: "Trigger hook on pipeline events"
optional :wiki_events, type: Boolean, desc: "Trigger hook on wiki events"
optional :enable_ssl_verification, type: Boolean, desc: "Do SSL verification when triggering the hook"
optional :token, type: String, desc: "Secret token to validate received payloads; this will not be returned in the response"
end
end
before { authenticate! } before { authenticate! }
before { authorize_admin_project } before { authorize_admin_project }
params do
requires :id, type: String, desc: 'The ID of a project'
end
resource :projects do resource :projects do
# Get project hooks desc 'Get project hooks' do
# success Entities::ProjectHook
# Parameters: end
# id (required) - The ID of a project
# Example Request:
# GET /projects/:id/hooks
get ":id/hooks" do get ":id/hooks" do
@hooks = paginate user_project.hooks hooks = paginate user_project.hooks
present @hooks, with: Entities::ProjectHook
present hooks, with: Entities::ProjectHook
end end
# Get a project hook desc 'Get a project hook' do
# success Entities::ProjectHook
# Parameters: end
# id (required) - The ID of a project params do
# hook_id (required) - The ID of a project hook requires :hook_id, type: Integer, desc: 'The ID of a project hook'
# Example Request: end
# GET /projects/:id/hooks/:hook_id
get ":id/hooks/:hook_id" do get ":id/hooks/:hook_id" do
@hook = user_project.hooks.find(params[:hook_id]) hook = user_project.hooks.find(params[:hook_id])
present @hook, with: Entities::ProjectHook present hook, with: Entities::ProjectHook
end end
# Add hook to project desc 'Add hook to project' do
# success Entities::ProjectHook
# Parameters: end
# id (required) - The ID of a project params do
# url (required) - The hook URL use :project_hook_properties
# Example Request: end
# POST /projects/:id/hooks
post ":id/hooks" do post ":id/hooks" do
required_attributes! [:url] new_hook_params = declared(params, include_missing: false, include_parent_namespaces: false).to_h
attrs = attributes_for_keys [ hook = user_project.hooks.new(new_hook_params)
:url,
:push_events,
:issues_events,
:merge_requests_events,
:tag_push_events,
:note_events,
:build_events,
:pipeline_events,
:wiki_page_events,
:enable_ssl_verification,
:token
]
@hook = user_project.hooks.new(attrs)
if @hook.save if hook.save
present @hook, with: Entities::ProjectHook present hook, with: Entities::ProjectHook
else else
if @hook.errors[:url].present? error!("Invalid url given", 422) if hook.errors[:url].present?
error!("Invalid url given", 422)
end not_found!("Project hook #{hook.errors.messages}")
not_found!("Project hook #{@hook.errors.messages}")
end end
end end
# Update an existing project hook desc 'Update an existing project hook' do
# success Entities::ProjectHook
# Parameters: end
# id (required) - The ID of a project params do
# hook_id (required) - The ID of a project hook requires :hook_id, type: Integer, desc: "The ID of the hook to update"
# url (required) - The hook URL use :project_hook_properties
# Example Request: end
# PUT /projects/:id/hooks/:hook_id
put ":id/hooks/:hook_id" do put ":id/hooks/:hook_id" do
@hook = user_project.hooks.find(params[:hook_id]) hook = user_project.hooks.find(params[:hook_id])
required_attributes! [:url]
attrs = attributes_for_keys [ new_params = declared(params, include_missing: false, include_parent_namespaces: false).to_h
:url, new_params.delete('hook_id')
:push_events,
:issues_events,
:merge_requests_events,
:tag_push_events,
:note_events,
:build_events,
:pipeline_events,
:wiki_page_events,
:enable_ssl_verification,
:token
]
if @hook.update_attributes attrs if hook.update_attributes(new_params)
present @hook, with: Entities::ProjectHook present hook, with: Entities::ProjectHook
else else
if @hook.errors[:url].present? error!("Invalid url given", 422) if hook.errors[:url].present?
error!("Invalid url given", 422)
end not_found!("Project hook #{hook.errors.messages}")
not_found!("Project hook #{@hook.errors.messages}")
end end
end end
# Deletes project hook. This is an idempotent function. desc 'Deletes project hook' do
# success Entities::ProjectHook
# Parameters: end
# id (required) - The ID of a project params do
# hook_id (required) - The ID of hook to delete requires :hook_id, type: Integer, desc: 'The ID of the hook to delete'
# Example Request: end
# DELETE /projects/:id/hooks/:hook_id
delete ":id/hooks/:hook_id" do delete ":id/hooks/:hook_id" do
required_attributes! [:hook_id]
begin begin
@hook = user_project.hooks.destroy(params[:hook_id]) present user_project.hooks.destroy(params[:hook_id]), with: Entities::ProjectHook
rescue rescue
# ProjectHook can raise Error if hook_id not found # ProjectHook can raise Error if hook_id not found
not_found!("Error deleting hook #{params[:hook_id]}") not_found!("Error deleting hook #{params[:hook_id]}")
......
...@@ -17,12 +17,12 @@ module API ...@@ -17,12 +17,12 @@ module API
present current_settings, with: Entities::ApplicationSetting present current_settings, with: Entities::ApplicationSetting
end end
# Modify applicaiton settings # Modify application settings
# #
# Example Request: # Example Request:
# PUT /application/settings # PUT /application/settings
put "application/settings" do put "application/settings" do
attributes = current_settings.attributes.keys - ["id"] attributes = ["repository_storage"] + current_settings.attributes.keys - ["id"]
attrs = attributes_for_keys(attributes) attrs = attributes_for_keys(attributes)
if current_settings.update_attributes(attrs) if current_settings.update_attributes(attrs)
......
module Gitlab module Gitlab
class ProductionLogger < Gitlab::Logger class EnvironmentLogger < Gitlab::Logger
def self.file_name_noext def self.file_name_noext
'production' Rails.env
end end
end end
end end
namespace :gitlab do
desc "GitLab | Generate sdocs for project"
task generate_docs: :environment do
system(*%W(bundle exec sdoc -o doc/code app lib))
end
end
...@@ -10,54 +10,38 @@ describe 'bin/changelog' do ...@@ -10,54 +10,38 @@ describe 'bin/changelog' do
expect(options.amend).to eq true expect(options.amend).to eq true
end end
it 'parses --force' do it 'parses --force and -f' do
options = described_class.parse(%w[foo --force bar]) %w[--force -f].each do |flag|
options = described_class.parse(%W[foo #{flag} bar])
expect(options.force).to eq true expect(options.force).to eq true
end
end end
it 'parses -f' do it 'parses --merge-request and -m' do
options = described_class.parse(%w[foo -f bar]) %w[--merge-request -m].each do |flag|
options = described_class.parse(%W[foo #{flag} 1234 bar])
expect(options.force).to eq true expect(options.merge_request).to eq 1234
end
end end
it 'parses --merge-request' do it 'parses --dry-run and -n' do
options = described_class.parse(%w[foo --merge-request 1234 bar]) %w[--dry-run -n].each do |flag|
options = described_class.parse(%W[foo #{flag} bar])
expect(options.merge_request).to eq 1234 expect(options.dry_run).to eq true
end
end end
it 'parses -m' do it 'parses --git-username and -u' do
options = described_class.parse(%w[foo -m 4321 bar])
expect(options.merge_request).to eq 4321
end
it 'parses --dry-run' do
options = described_class.parse(%w[foo --dry-run bar])
expect(options.dry_run).to eq true
end
it 'parses -n' do
options = described_class.parse(%w[foo -n bar])
expect(options.dry_run).to eq true
end
it 'parses --git-username' do
allow(described_class).to receive(:git_user_name).and_return('Jane Doe') allow(described_class).to receive(:git_user_name).and_return('Jane Doe')
options = described_class.parse(%w[foo --git-username bar])
expect(options.author).to eq 'Jane Doe'
end
it 'parses -u' do %w[--git-username -u].each do |flag|
allow(described_class).to receive(:git_user_name).and_return('John Smith') options = described_class.parse(%W[foo #{flag} bar])
options = described_class.parse(%w[foo -u bar])
expect(options.author).to eq 'John Smith' expect(options.author).to eq 'Jane Doe'
end
end end
it 'parses -h' do it 'parses -h' do
......
...@@ -7,15 +7,16 @@ describe "Admin Runners" do ...@@ -7,15 +7,16 @@ describe "Admin Runners" do
describe "Runners page" do describe "Runners page" do
before do before do
runner = FactoryGirl.create(:ci_runner) runner = FactoryGirl.create(:ci_runner, contacted_at: Time.now)
pipeline = FactoryGirl.create(:ci_pipeline) pipeline = FactoryGirl.create(:ci_pipeline)
FactoryGirl.create(:ci_build, pipeline: pipeline, runner_id: runner.id) FactoryGirl.create(:ci_build, pipeline: pipeline, runner_id: runner.id)
visit admin_runners_path visit admin_runners_path
end end
it { page.has_text? "Manage Runners" } it 'has all necessary texts' do
it { page.has_text? "To register a new runner" } expect(page).to have_text "To register a new Runner"
it { page.has_text? "Runners with last contact less than a minute ago: 1" } expect(page).to have_text "Runners with last contact less than a minute ago: 1"
end
describe 'search' do describe 'search' do
before do before do
...@@ -27,8 +28,10 @@ describe "Admin Runners" do ...@@ -27,8 +28,10 @@ describe "Admin Runners" do
search_form.click_button 'Search' search_form.click_button 'Search'
end end
it { expect(page).to have_content("runner-foo") } it 'shows correct runner' do
it { expect(page).not_to have_content("runner-bar") } expect(page).to have_content("runner-foo")
expect(page).not_to have_content("runner-bar")
end
end end
end end
...@@ -46,8 +49,10 @@ describe "Admin Runners" do ...@@ -46,8 +49,10 @@ describe "Admin Runners" do
end end
describe 'projects' do describe 'projects' do
it { expect(page).to have_content(@project1.name_with_namespace) } it 'contains project names' do
it { expect(page).to have_content(@project2.name_with_namespace) } expect(page).to have_content(@project1.name_with_namespace)
expect(page).to have_content(@project2.name_with_namespace)
end
end end
describe 'search' do describe 'search' do
...@@ -57,8 +62,10 @@ describe "Admin Runners" do ...@@ -57,8 +62,10 @@ describe "Admin Runners" do
search_form.click_button 'Search' search_form.click_button 'Search'
end end
it { expect(page).to have_content(@project1.name_with_namespace) } it 'contains name of correct project' do
it { expect(page).not_to have_content(@project2.name_with_namespace) } expect(page).to have_content(@project1.name_with_namespace)
expect(page).not_to have_content(@project2.name_with_namespace)
end
end end
describe 'enable/create' do describe 'enable/create' do
......
...@@ -19,6 +19,17 @@ describe "Dashboard Issues Feed", feature: true do ...@@ -19,6 +19,17 @@ describe "Dashboard Issues Feed", feature: true do
expect(body).to have_selector('title', text: "#{user.name} issues") expect(body).to have_selector('title', text: "#{user.name} issues")
end end
it "renders atom feed with url parameters" do
visit issues_dashboard_path(:atom, private_token: user.private_token, state: 'opened', assignee_id: user.id)
link = find('link[type="application/atom+xml"]')
params = CGI::parse(URI.parse(link[:href]).query)
expect(params).to include('private_token' => [user.private_token])
expect(params).to include('state' => ['opened'])
expect(params).to include('assignee_id' => [user.id.to_s])
end
context "issue with basic fields" do context "issue with basic fields" do
let!(:issue2) { create(:issue, author: user, assignee: user, project: project2, description: 'test desc') } let!(:issue2) { create(:issue, author: user, assignee: user, project: project2, description: 'test desc') }
......
...@@ -3,10 +3,14 @@ require 'spec_helper' ...@@ -3,10 +3,14 @@ require 'spec_helper'
describe 'Issues Feed', feature: true do describe 'Issues Feed', feature: true do
describe 'GET /issues' do describe 'GET /issues' do
let!(:user) { create(:user) } let!(:user) { create(:user) }
let!(:group) { create(:group) }
let!(:project) { create(:project) } let!(:project) { create(:project) }
let!(:issue) { create(:issue, author: user, project: project) } let!(:issue) { create(:issue, author: user, project: project) }
before { project.team << [user, :developer] } before do
project.team << [user, :developer]
group.add_developer(user)
end
context 'when authenticated' do context 'when authenticated' do
it 'renders atom feed' do it 'renders atom feed' do
...@@ -33,5 +37,28 @@ describe 'Issues Feed', feature: true do ...@@ -33,5 +37,28 @@ describe 'Issues Feed', feature: true do
expect(body).to have_selector('entry summary', text: issue.title) expect(body).to have_selector('entry summary', text: issue.title)
end end
end end
it "renders atom feed with url parameters for project issues" do
visit namespace_project_issues_path(project.namespace, project,
:atom, private_token: user.private_token, state: 'opened', assignee_id: user.id)
link = find('link[type="application/atom+xml"]')
params = CGI::parse(URI.parse(link[:href]).query)
expect(params).to include('private_token' => [user.private_token])
expect(params).to include('state' => ['opened'])
expect(params).to include('assignee_id' => [user.id.to_s])
end
it "renders atom feed with url parameters for group issues" do
visit issues_group_path(group, :atom, private_token: user.private_token, state: 'opened', assignee_id: user.id)
link = find('link[type="application/atom+xml"]')
params = CGI::parse(URI.parse(link[:href]).query)
expect(params).to include('private_token' => [user.private_token])
expect(params).to include('state' => ['opened'])
expect(params).to include('assignee_id' => [user.id.to_s])
end
end end
end end
...@@ -77,9 +77,11 @@ describe 'Commits' do ...@@ -77,9 +77,11 @@ describe 'Commits' do
visit ci_status_path(pipeline) visit ci_status_path(pipeline)
end end
it { expect(page).to have_content pipeline.sha[0..7] } it 'shows pipeline`s data' do
it { expect(page).to have_content pipeline.git_commit_message } expect(page).to have_content pipeline.sha[0..7]
it { expect(page).to have_content pipeline.git_author_name } expect(page).to have_content pipeline.git_commit_message
expect(page).to have_content pipeline.git_author_name
end
end end
context 'Download artifacts' do context 'Download artifacts' do
......
...@@ -44,6 +44,22 @@ describe "Dashboard Issues filtering", feature: true, js: true do ...@@ -44,6 +44,22 @@ describe "Dashboard Issues filtering", feature: true, js: true do
expect(page).to have_issuable_counts(open: 1, closed: 0, all: 1) expect(page).to have_issuable_counts(open: 1, closed: 0, all: 1)
expect(page).to have_selector('.issue', count: 1) expect(page).to have_selector('.issue', count: 1)
end end
it 'updates atom feed link' do
visit_issues(milestone_title: '', assignee_id: user.id)
link = find('.nav-controls a', text: 'Subscribe')
params = CGI::parse(URI.parse(link[:href]).query)
auto_discovery_link = find('link[type="application/atom+xml"]', visible: false)
auto_discovery_params = CGI::parse(URI.parse(auto_discovery_link[:href]).query)
expect(params).to include('private_token' => [user.private_token])
expect(params).to include('milestone_title' => [''])
expect(params).to include('assignee_id' => [user.id.to_s])
expect(auto_discovery_params).to include('private_token' => [user.private_token])
expect(auto_discovery_params).to include('milestone_title' => [''])
expect(auto_discovery_params).to include('assignee_id' => [user.id.to_s])
end
end end
def show_milestone_dropdown def show_milestone_dropdown
...@@ -51,7 +67,7 @@ describe "Dashboard Issues filtering", feature: true, js: true do ...@@ -51,7 +67,7 @@ describe "Dashboard Issues filtering", feature: true, js: true do
expect(page).to have_selector('.dropdown-content', visible: true) expect(page).to have_selector('.dropdown-content', visible: true)
end end
def visit_issues def visit_issues(*args)
visit issues_dashboard_path visit issues_dashboard_path(*args)
end end
end end
...@@ -4,6 +4,7 @@ describe 'Filter issues', feature: true do ...@@ -4,6 +4,7 @@ describe 'Filter issues', feature: true do
include WaitForAjax include WaitForAjax
let!(:project) { create(:project) } let!(:project) { create(:project) }
let!(:group) { create(:group) }
let!(:user) { create(:user)} let!(:user) { create(:user)}
let!(:milestone) { create(:milestone, project: project) } let!(:milestone) { create(:milestone, project: project) }
let!(:label) { create(:label, project: project) } let!(:label) { create(:label, project: project) }
...@@ -11,6 +12,7 @@ describe 'Filter issues', feature: true do ...@@ -11,6 +12,7 @@ describe 'Filter issues', feature: true do
before do before do
project.team << [user, :master] project.team << [user, :master]
group.add_developer(user)
login_as(user) login_as(user)
create(:issue, project: project) create(:issue, project: project)
end end
...@@ -347,4 +349,36 @@ describe 'Filter issues', feature: true do ...@@ -347,4 +349,36 @@ describe 'Filter issues', feature: true do
end end
end end
end end
it 'updates atom feed link for project issues' do
visit namespace_project_issues_path(project.namespace, project, milestone_title: '', assignee_id: user.id)
link = find('.nav-controls a', text: 'Subscribe')
params = CGI::parse(URI.parse(link[:href]).query)
auto_discovery_link = find('link[type="application/atom+xml"]', visible: false)
auto_discovery_params = CGI::parse(URI.parse(auto_discovery_link[:href]).query)
expect(params).to include('private_token' => [user.private_token])
expect(params).to include('milestone_title' => [''])
expect(params).to include('assignee_id' => [user.id.to_s])
expect(auto_discovery_params).to include('private_token' => [user.private_token])
expect(auto_discovery_params).to include('milestone_title' => [''])
expect(auto_discovery_params).to include('assignee_id' => [user.id.to_s])
end
it 'updates atom feed link for group issues' do
visit issues_group_path(group, milestone_title: '', assignee_id: user.id)
link = find('.nav-controls a', text: 'Subscribe')
params = CGI::parse(URI.parse(link[:href]).query)
auto_discovery_link = find('link[type="application/atom+xml"]', visible: false)
auto_discovery_params = CGI::parse(URI.parse(auto_discovery_link[:href]).query)
expect(params).to include('private_token' => [user.private_token])
expect(params).to include('milestone_title' => [''])
expect(params).to include('assignee_id' => [user.id.to_s])
expect(auto_discovery_params).to include('private_token' => [user.private_token])
expect(auto_discovery_params).to include('milestone_title' => [''])
expect(auto_discovery_params).to include('assignee_id' => [user.id.to_s])
end
end end
...@@ -3,7 +3,7 @@ require 'spec_helper' ...@@ -3,7 +3,7 @@ require 'spec_helper'
feature 'Edit Merge Request', feature: true do feature 'Edit Merge Request', feature: true do
let(:user) { create(:user) } let(:user) { create(:user) }
let(:project) { create(:project, :public) } let(:project) { create(:project, :public) }
let(:merge_request) { create(:merge_request, :with_diffs, source_project: project) } let(:merge_request) { create(:merge_request, :simple, source_project: project) }
before do before do
project.team << [user, :master] project.team << [user, :master]
...@@ -28,5 +28,17 @@ feature 'Edit Merge Request', feature: true do ...@@ -28,5 +28,17 @@ feature 'Edit Merge Request', feature: true do
expect(page).to have_content 'Someone edited the merge request the same time you did' expect(page).to have_content 'Someone edited the merge request the same time you did'
end end
it 'allows to unselect "Remove source branch"' do
merge_request.update(merge_params: { 'force_remove_source_branch' => '1' })
expect(merge_request.merge_params['force_remove_source_branch']).to be_truthy
visit edit_namespace_project_merge_request_path(project.namespace, project, merge_request)
uncheck 'Remove source branch when merge request is accepted'
click_button 'Save changes'
expect(page).to have_content 'Remove source branch'
end
end end
end end
...@@ -79,12 +79,14 @@ describe "Builds" do ...@@ -79,12 +79,14 @@ describe "Builds" do
click_link "Cancel running" click_link "Cancel running"
end end
it { expect(page).to have_selector('.nav-links li.active', text: 'All') } it 'shows all necessary content' do
it { expect(page).to have_content 'canceled' } expect(page).to have_selector('.nav-links li.active', text: 'All')
it { expect(page).to have_content @build.short_sha } expect(page).to have_content 'canceled'
it { expect(page).to have_content @build.ref } expect(page).to have_content @build.short_sha
it { expect(page).to have_content @build.name } expect(page).to have_content @build.ref
it { expect(page).not_to have_link 'Cancel running' } expect(page).to have_content @build.name
expect(page).not_to have_link 'Cancel running'
end
end end
describe "GET /:project/builds/:id" do describe "GET /:project/builds/:id" do
...@@ -93,10 +95,12 @@ describe "Builds" do ...@@ -93,10 +95,12 @@ describe "Builds" do
visit namespace_project_build_path(@project.namespace, @project, @build) visit namespace_project_build_path(@project.namespace, @project, @build)
end end
it { expect(page.status_code).to eq(200) } it 'shows commit`s data' do
it { expect(page).to have_content @commit.sha[0..7] } expect(page.status_code).to eq(200)
it { expect(page).to have_content @commit.git_commit_message } expect(page).to have_content @commit.sha[0..7]
it { expect(page).to have_content @commit.git_author_name } expect(page).to have_content @commit.git_commit_message
expect(page).to have_content @commit.git_author_name
end
end end
context "Build from other project" do context "Build from other project" do
...@@ -167,7 +171,7 @@ describe "Builds" do ...@@ -167,7 +171,7 @@ describe "Builds" do
describe 'Variables' do describe 'Variables' do
before do before do
@trigger_request = create :ci_trigger_request_with_variables @trigger_request = create :ci_trigger_request_with_variables
@build = create :ci_build, pipeline: @commit, trigger_request: @trigger_request @build = create :ci_build, pipeline: @commit, trigger_request: @trigger_request
visit namespace_project_build_path(@project.namespace, @project, @build) visit namespace_project_build_path(@project.namespace, @project, @build)
end end
...@@ -176,14 +180,14 @@ describe "Builds" do ...@@ -176,14 +180,14 @@ describe "Builds" do
expect(page).to have_css('.reveal-variables') expect(page).to have_css('.reveal-variables')
expect(page).not_to have_css('.js-build-variable') expect(page).not_to have_css('.js-build-variable')
expect(page).not_to have_css('.js-build-value') expect(page).not_to have_css('.js-build-value')
click_button 'Reveal Variables' click_button 'Reveal Variables'
expect(page).not_to have_css('.reveal-variables') expect(page).not_to have_css('.reveal-variables')
expect(page).to have_selector('.js-build-variable', text: 'TRIGGER_KEY_1') expect(page).to have_selector('.js-build-variable', text: 'TRIGGER_KEY_1')
expect(page).to have_selector('.js-build-value', text: 'TRIGGER_VALUE_1') expect(page).to have_selector('.js-build-value', text: 'TRIGGER_VALUE_1')
end end
end end
end end
describe "POST /:project/builds/:id/cancel" do describe "POST /:project/builds/:id/cancel" do
...@@ -194,9 +198,11 @@ describe "Builds" do ...@@ -194,9 +198,11 @@ describe "Builds" do
click_link "Cancel" click_link "Cancel"
end end
it { expect(page.status_code).to eq(200) } it 'loads the page and shows all needed controls' do
it { expect(page).to have_content 'canceled' } expect(page.status_code).to eq(200)
it { expect(page).to have_content 'Retry' } expect(page).to have_content 'canceled'
expect(page).to have_content 'Retry'
end
end end
context "Build from other project" do context "Build from other project" do
......
...@@ -183,4 +183,19 @@ describe 'Edit Project Settings', feature: true do ...@@ -183,4 +183,19 @@ describe 'Edit Project Settings', feature: true do
end end
end end
end end
# Regression spec for https://gitlab.com/gitlab-org/gitlab-ce/issues/24056
describe 'project statistic visibility' do
let!(:project) { create(:project, :private) }
before do
project.team << [member, :guest]
login_as(member)
visit namespace_project_path(project.namespace, project)
end
it "does not show project statistic for guest" do
expect(page).not_to have_selector('.project-stats')
end
end
end end
...@@ -36,17 +36,54 @@ describe 'Dashboard > User filters todos', feature: true, js: true do ...@@ -36,17 +36,54 @@ describe 'Dashboard > User filters todos', feature: true, js: true do
expect(page).not_to have_content project_2.name_with_namespace expect(page).not_to have_content project_2.name_with_namespace
end end
it 'filters by author' do context "Author filter" do
click_button 'Author' it 'filters by author' do
within '.dropdown-menu-author' do click_button 'Author'
fill_in 'Search authors', with: user_1.name
click_link user_1.name within '.dropdown-menu-author' do
fill_in 'Search authors', with: user_1.name
click_link user_1.name
end
wait_for_ajax
expect(find('.todos-list')).to have_content user_1.name
expect(find('.todos-list')).not_to have_content user_2.name
end end
wait_for_ajax it "shows only authors of existing todos" do
click_button 'Author'
within '.dropdown-menu-author' do
# It should contain two users + "Any Author"
expect(page).to have_selector('.dropdown-menu-user-link', count: 3)
expect(page).to have_content(user_1.name)
expect(page).to have_content(user_2.name)
end
end
expect(find('.todos-list')).to have_content user_1.name it "shows only authors of existing done todos" do
expect(find('.todos-list')).not_to have_content user_2.name user_3 = create :user
user_4 = create :user
create(:todo, user: user_1, author: user_3, project: project_1, target: issue, action: 1, state: :done)
create(:todo, user: user_1, author: user_4, project: project_2, target: merge_request, action: 2, state: :done)
project_1.team << [user_3, :developer]
project_2.team << [user_4, :developer]
visit dashboard_todos_path(state: 'done')
click_button 'Author'
within '.dropdown-menu-author' do
# It should contain two users + "Any Author"
expect(page).to have_selector('.dropdown-menu-user-link', count: 3)
expect(page).to have_content(user_3.name)
expect(page).to have_content(user_4.name)
expect(page).not_to have_content(user_1.name)
expect(page).not_to have_content(user_2.name)
end
end
end end
it 'filters by type' do it 'filters by type' do
......
/* eslint-disable */ /* eslint-disable */
/*= require merge_request_widget */ /*= require merge_request_widget */
/*= require lib/utils/jquery.timeago.js */ /*= require jquery.timeago.js */
(function() { (function() {
describe('MergeRequestWidget', function() { describe('MergeRequestWidget', function() {
......
...@@ -41,14 +41,62 @@ describe ApplicationSetting, models: true do ...@@ -41,14 +41,62 @@ describe ApplicationSetting, models: true do
subject { setting } subject { setting }
end end
context 'repository storages inclussion' do # Upgraded databases will have this sort of content
context 'repository_storages is a String, not an Array' do
before { setting.__send__(:raw_write_attribute, :repository_storages, 'default') }
it { expect(setting.repository_storages_before_type_cast).to eq('default') }
it { expect(setting.repository_storages).to eq(['default']) }
end
context 'repository storages' do
before do before do
storages = { 'custom' => 'tmp/tests/custom_repositories' } storages = {
'custom1' => 'tmp/tests/custom_repositories_1',
'custom2' => 'tmp/tests/custom_repositories_2',
'custom3' => 'tmp/tests/custom_repositories_3',
}
allow(Gitlab.config.repositories).to receive(:storages).and_return(storages) allow(Gitlab.config.repositories).to receive(:storages).and_return(storages)
end end
it { is_expected.to allow_value('custom').for(:repository_storage) } describe 'inclusion' do
it { is_expected.not_to allow_value('alternative').for(:repository_storage) } it { is_expected.to allow_value('custom1').for(:repository_storages) }
it { is_expected.to allow_value(['custom2', 'custom3']).for(:repository_storages) }
it { is_expected.not_to allow_value('alternative').for(:repository_storages) }
it { is_expected.not_to allow_value(['alternative', 'custom1']).for(:repository_storages) }
end
describe 'presence' do
it { is_expected.not_to allow_value([]).for(:repository_storages) }
it { is_expected.not_to allow_value("").for(:repository_storages) }
it { is_expected.not_to allow_value(nil).for(:repository_storages) }
end
describe '.pick_repository_storage' do
it 'uses Array#sample to pick a random storage' do
array = double('array', sample: 'random')
expect(setting).to receive(:repository_storages).and_return(array)
expect(setting.pick_repository_storage).to eq('random')
end
describe '#repository_storage' do
it 'returns the first storage' do
setting.repository_storages = ['good', 'bad']
expect(setting.repository_storage).to eq('good')
end
end
describe '#repository_storage=' do
it 'overwrites repository_storages' do
setting.repository_storage = 'overwritten'
expect(setting.repository_storages).to eq(['overwritten'])
end
end
end
end end
end end
......
...@@ -837,16 +837,19 @@ describe Project, models: true do ...@@ -837,16 +837,19 @@ describe Project, models: true do
context 'repository storage by default' do context 'repository storage by default' do
let(:project) { create(:empty_project) } let(:project) { create(:empty_project) }
subject { project.repository_storage }
before do before do
storages = { 'alternative_storage' => '/some/path' } storages = {
'default' => 'tmp/tests/repositories',
'picked' => 'tmp/tests/repositories',
}
allow(Gitlab.config.repositories).to receive(:storages).and_return(storages) allow(Gitlab.config.repositories).to receive(:storages).and_return(storages)
stub_application_setting(repository_storage: 'alternative_storage')
allow_any_instance_of(Project).to receive(:ensure_dir_exist).and_return(true)
end end
it { is_expected.to eq('alternative_storage') } it 'picks storage from ApplicationSetting' do
expect_any_instance_of(ApplicationSetting).to receive(:pick_repository_storage).and_return('picked')
expect(project.repository_storage).to eq('picked')
end
end end
context 'shared runners by default' do context 'shared runners by default' do
......
...@@ -256,6 +256,20 @@ describe User, models: true do ...@@ -256,6 +256,20 @@ describe User, models: true do
expect(users_without_two_factor).not_to include(user_with_2fa.id) expect(users_without_two_factor).not_to include(user_with_2fa.id)
end end
end end
describe '.todo_authors' do
it 'filters users' do
create :user
user_2 = create :user
user_3 = create :user
current_user = create :user
create(:todo, user: current_user, author: user_2, state: :done)
create(:todo, user: current_user, author: user_3, state: :pending)
expect(User.todo_authors(current_user.id, 'pending')).to eq [user_3]
expect(User.todo_authors(current_user.id, 'done')).to eq [user_2]
end
end
end end
describe "Respond to" do describe "Respond to" do
......
...@@ -37,7 +37,7 @@ describe API::API, api: true do ...@@ -37,7 +37,7 @@ describe API::API, api: true do
end end
end end
context "when authenticated as admin" do context "when authenticated as admin" do
it "admin: returns an array of all groups" do it "admin: returns an array of all groups" do
get api("/groups", admin) get api("/groups", admin)
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
...@@ -55,6 +55,17 @@ describe API::API, api: true do ...@@ -55,6 +55,17 @@ describe API::API, api: true do
expect(json_response.length).to eq(1) expect(json_response.length).to eq(1)
end end
end end
context "when using all_available in request" do
it "returns all groups you have access to" do
public_group = create :group, :public
get api("/groups", user1), all_available: true
expect(response).to have_http_status(200)
expect(json_response).to be_an Array
expect(json_response.first['name']).to eq(public_group.name)
end
end
end end
describe "GET /groups/:id" do describe "GET /groups/:id" do
......
...@@ -14,14 +14,14 @@ describe API::API, 'MergeRequestDiffs', api: true do ...@@ -14,14 +14,14 @@ describe API::API, 'MergeRequestDiffs', api: true do
end end
describe 'GET /projects/:id/merge_requests/:merge_request_id/versions' do describe 'GET /projects/:id/merge_requests/:merge_request_id/versions' do
context 'valid merge request' do it 'returns 200 for a valid merge request' do
before { get api("/projects/#{project.id}/merge_requests/#{merge_request.id}/versions", user) } get api("/projects/#{project.id}/merge_requests/#{merge_request.id}/versions", user)
let(:merge_request_diff) { merge_request.merge_request_diffs.first } merge_request_diff = merge_request.merge_request_diffs.first
it { expect(response.status).to eq 200 } expect(response.status).to eq 200
it { expect(json_response.size).to eq(merge_request.merge_request_diffs.size) } expect(json_response.size).to eq(merge_request.merge_request_diffs.size)
it { expect(json_response.first['id']).to eq(merge_request_diff.id) } expect(json_response.first['id']).to eq(merge_request_diff.id)
it { expect(json_response.first['head_commit_sha']).to eq(merge_request_diff.head_commit_sha) } expect(json_response.first['head_commit_sha']).to eq(merge_request_diff.head_commit_sha)
end end
it 'returns a 404 when merge_request_id not found' do it 'returns a 404 when merge_request_id not found' do
...@@ -31,14 +31,14 @@ describe API::API, 'MergeRequestDiffs', api: true do ...@@ -31,14 +31,14 @@ describe API::API, 'MergeRequestDiffs', api: true do
end end
describe 'GET /projects/:id/merge_requests/:merge_request_id/versions/:version_id' do describe 'GET /projects/:id/merge_requests/:merge_request_id/versions/:version_id' do
context 'valid merge request' do it 'returns a 200 for a valid merge request' do
before { get api("/projects/#{project.id}/merge_requests/#{merge_request.id}/versions/#{merge_request_diff.id}", user) } merge_request_diff = merge_request.merge_request_diffs.first
let(:merge_request_diff) { merge_request.merge_request_diffs.first } get api("/projects/#{project.id}/merge_requests/#{merge_request.id}/versions/#{merge_request_diff.id}", user)
it { expect(response.status).to eq 200 } expect(response.status).to eq 200
it { expect(json_response['id']).to eq(merge_request_diff.id) } expect(json_response['id']).to eq(merge_request_diff.id)
it { expect(json_response['head_commit_sha']).to eq(merge_request_diff.head_commit_sha) } expect(json_response['head_commit_sha']).to eq(merge_request_diff.head_commit_sha)
it { expect(json_response['diffs'].size).to eq(merge_request_diff.diffs.size) } expect(json_response['diffs'].size).to eq(merge_request_diff.diffs.size)
end end
it 'returns a 404 when merge_request_id not found' do it 'returns a 404 when merge_request_id not found' do
......
...@@ -186,14 +186,14 @@ describe API::API, api: true do ...@@ -186,14 +186,14 @@ describe API::API, api: true do
end end
describe 'GET /projects/:id/merge_requests/:merge_request_id/commits' do describe 'GET /projects/:id/merge_requests/:merge_request_id/commits' do
context 'valid merge request' do it 'returns a 200 when merge request is valid' do
before { get api("/projects/#{project.id}/merge_requests/#{merge_request.id}/commits", user) } get api("/projects/#{project.id}/merge_requests/#{merge_request.id}/commits", user)
let(:commit) { merge_request.commits.first } commit = merge_request.commits.first
it { expect(response.status).to eq 200 } expect(response.status).to eq 200
it { expect(json_response.size).to eq(merge_request.commits.size) } expect(json_response.size).to eq(merge_request.commits.size)
it { expect(json_response.first['id']).to eq(commit.id) } expect(json_response.first['id']).to eq(commit.id)
it { expect(json_response.first['title']).to eq(commit.title) } expect(json_response.first['title']).to eq(commit.title)
end end
it 'returns a 404 when merge_request_id not found' do it 'returns a 404 when merge_request_id not found' do
......
...@@ -33,6 +33,7 @@ describe API::API, 'Settings', api: true do ...@@ -33,6 +33,7 @@ describe API::API, 'Settings', api: true do
expect(json_response['default_projects_limit']).to eq(3) expect(json_response['default_projects_limit']).to eq(3)
expect(json_response['signin_enabled']).to be_falsey expect(json_response['signin_enabled']).to be_falsey
expect(json_response['repository_storage']).to eq('custom') expect(json_response['repository_storage']).to eq('custom')
expect(json_response['repository_storages']).to eq(['custom'])
expect(json_response['koding_enabled']).to be_truthy expect(json_response['koding_enabled']).to be_truthy
expect(json_response['koding_url']).to eq('http://koding.example.com') expect(json_response['koding_url']).to eq('http://koding.example.com')
end end
......
...@@ -220,26 +220,33 @@ describe Ci::API::API do ...@@ -220,26 +220,33 @@ describe Ci::API::API do
end end
context 'when request is valid' do context 'when request is valid' do
it { expect(response.status).to eq 202 } it 'gets correct response' do
expect(response.status).to eq 202
expect(response.header).to have_key 'Range'
expect(response.header).to have_key 'Build-Status'
end
it { expect(build.reload.trace).to eq 'BUILD TRACE appended' } it { expect(build.reload.trace).to eq 'BUILD TRACE appended' }
it { expect(response.header).to have_key 'Range' }
it { expect(response.header).to have_key 'Build-Status' }
end end
context 'when content-range start is too big' do context 'when content-range start is too big' do
let(:headers_with_range) { headers.merge({ 'Content-Range' => '15-20' }) } let(:headers_with_range) { headers.merge({ 'Content-Range' => '15-20' }) }
it { expect(response.status).to eq 416 } it 'gets correct response' do
it { expect(response.header).to have_key 'Range' } expect(response.status).to eq 416
it { expect(response.header['Range']).to eq '0-11' } expect(response.header).to have_key 'Range'
expect(response.header['Range']).to eq '0-11'
end
end end
context 'when content-range start is too small' do context 'when content-range start is too small' do
let(:headers_with_range) { headers.merge({ 'Content-Range' => '8-20' }) } let(:headers_with_range) { headers.merge({ 'Content-Range' => '8-20' }) }
it { expect(response.status).to eq 416 } it 'gets correct response' do
it { expect(response.header).to have_key 'Range' } expect(response.status).to eq 416
it { expect(response.header['Range']).to eq '0-11' } expect(response.header).to have_key 'Range'
expect(response.header['Range']).to eq '0-11'
end
end end
context 'when Content-Range header is missing' do context 'when Content-Range header is missing' do
......
...@@ -109,10 +109,12 @@ describe Ci::API::API do ...@@ -109,10 +109,12 @@ describe Ci::API::API do
end end
describe "DELETE /runners/delete" do describe "DELETE /runners/delete" do
let!(:runner) { FactoryGirl.create(:ci_runner) } it 'returns 200' do
before { delete ci_api("/runners/delete"), token: runner.token } runner = FactoryGirl.create(:ci_runner)
delete ci_api("/runners/delete"), token: runner.token
it { expect(response).to have_http_status 200 } expect(response).to have_http_status 200
it { expect(Ci::Runner.count).to eq(0) } expect(Ci::Runner.count).to eq(0)
end
end end
end end
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