summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG2
-rw-r--r--CONTRIBUTING.md4
-rw-r--r--app/assets/javascripts/dispatcher.js4
-rw-r--r--app/assets/javascripts/gl_dropdown.js6
-rw-r--r--app/assets/javascripts/protected_branch_access_dropdown.js.es624
-rw-r--r--app/assets/javascripts/protected_branch_create.js.es656
-rw-r--r--app/assets/javascripts/protected_branch_dropdown.js.es675
-rw-r--r--app/assets/javascripts/protected_branch_edit.js.es661
-rw-r--r--app/assets/javascripts/protected_branch_edit_list.js.es617
-rw-r--r--app/assets/javascripts/protected_branch_select.js72
-rw-r--r--app/assets/javascripts/protected_branches_access_select.js.es663
-rw-r--r--app/assets/javascripts/users_select.js38
-rw-r--r--app/assets/stylesheets/framework/dropdowns.scss8
-rw-r--r--app/assets/stylesheets/framework/panels.scss5
-rw-r--r--app/assets/stylesheets/pages/projects.scss23
-rw-r--r--app/models/project.rb11
-rw-r--r--app/views/projects/protected_branches/_branches_list.html.haml50
-rw-r--r--app/views/projects/protected_branches/_create_protected_branch.html.haml36
-rw-r--r--app/views/projects/protected_branches/_dropdown.html.haml12
-rw-r--r--app/views/projects/protected_branches/_protected_branch.html.haml17
-rw-r--r--app/views/projects/protected_branches/index.html.haml36
-rw-r--r--app/views/shared/web_hooks/_form.html.haml2
-rw-r--r--doc/administration/container_registry.md3
-rw-r--r--doc/administration/custom_hooks.md3
-rw-r--r--doc/administration/housekeeping.md2
-rw-r--r--doc/administration/raketasks/project_import_export.md15
-rw-r--r--doc/administration/repository_checks.md5
-rw-r--r--doc/api/README.md2
-rw-r--r--doc/api/award_emoji.md4
-rw-r--r--doc/api/todos.md2
-rw-r--r--doc/ci/triggers/README.md2
-rw-r--r--doc/container_registry/README.md5
-rw-r--r--doc/development/doc_styleguide.md10
-rw-r--r--doc/monitoring/health_check.md2
-rw-r--r--doc/user/project/labels.md12
-rw-r--r--doc/user/project/protected_branches.md3
-rw-r--r--doc/user/project/settings/import_export.md25
-rw-r--r--doc/web_hooks/web_hooks.md4
-rw-r--r--doc/workflow/award_emoji.md6
-rw-r--r--doc/workflow/cherry_pick_changes.md3
-rw-r--r--doc/workflow/file_finder.md2
-rw-r--r--doc/workflow/revert_changes.md2
-rw-r--r--doc/workflow/todos.md2
-rw-r--r--doc/workflow/web_editor.md3
-rw-r--r--lib/gitlab/user_access.rb2
-rw-r--r--spec/features/protected_branches_spec.rb14
-rw-r--r--spec/lib/gitlab/user_access_spec.rb52
-rw-r--r--spec/models/project_spec.rb92
48 files changed, 583 insertions, 316 deletions
diff --git a/CHANGELOG b/CHANGELOG
index 48235e2c1f7..ec2df962675 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -24,6 +24,7 @@ v 8.11.0 (unreleased)
- Remove unused images (ClemMakesApps)
- Limit git rev-list output count to one in forced push check
- Clean up unused routes (Josef Strzibny)
+ - Fix issue on empty project to allow developers to only push to protected branches if given permission
- Add green outline to New Branch button. !5447 (winniehell)
- Improve performance of syntax highlighting Markdown code blocks
- Update to gitlab_git 10.4.1 and take advantage of preserved Ref objects
@@ -39,6 +40,7 @@ v 8.11.0 (unreleased)
- Include old revision in merge request update hooks (Ben Boeckel)
- Add build event color in HipChat messages (David Eisner)
- Make fork counter always clickable. !5463 (winniehell)
+ - Document that webhook secret token is sent in X-Gitlab-Token HTTP header !5664 (lycoperdon)
- Gitlab::Highlight is now instrumented
- All created issues, API or WebUI, can be submitted to Akismet for spam check !5333
- The overhead of instrumented method calls has been reduced
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 2f522c1e39a..d94673e82ce 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -336,6 +336,10 @@ request is as follows:
1. If your code creates new files on disk please read the
[shared files guidelines](doc/development/shared_files.md).
1. When writing commit messages please follow [these](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html) [guidelines](http://chris.beams.io/posts/git-commit/).
+1. If your merge request adds one or more migrations, make sure to execute all
+ migrations on a fresh database before the MR is reviewed. If the review leads
+ to large changes in the MR, do this again once the review is complete.
+1. For more complex migrations, write tests.
The **official merge window** is in the beginning of the month from the 1st to
the 7th day of the month. This is the best time to submit an MR and get
diff --git a/app/assets/javascripts/dispatcher.js b/app/assets/javascripts/dispatcher.js
index 9e6901962c6..20f2b1d69b5 100644
--- a/app/assets/javascripts/dispatcher.js
+++ b/app/assets/javascripts/dispatcher.js
@@ -173,8 +173,8 @@
new Search();
break;
case 'projects:protected_branches:index':
- new ProtectedBranchesAccessSelect($(".new_protected_branch"), false, true);
- new ProtectedBranchesAccessSelect($(".protected-branches-list"), true, false);
+ new gl.ProtectedBranchCreate();
+ new gl.ProtectedBranchEditList();
break;
}
switch (path.first()) {
diff --git a/app/assets/javascripts/gl_dropdown.js b/app/assets/javascripts/gl_dropdown.js
index cc7e422fd89..d3394fae3f9 100644
--- a/app/assets/javascripts/gl_dropdown.js
+++ b/app/assets/javascripts/gl_dropdown.js
@@ -607,7 +607,7 @@
return this.dropdown.before($input);
};
- GitLabDropdown.prototype.selectRowAtIndex = function(e, index) {
+ GitLabDropdown.prototype.selectRowAtIndex = function(index) {
var $el, selector;
selector = ".dropdown-content li:not(.divider,.dropdown-header,.separator):eq(" + index + ") a";
if (this.dropdown.find(".dropdown-toggle-page").length) {
@@ -615,8 +615,6 @@
}
$el = $(selector, this.dropdown);
if ($el.length) {
- e.preventDefault();
- e.stopImmediatePropagation();
return $el.first().trigger('click');
}
};
@@ -653,7 +651,7 @@
return false;
}
if (currentKeyCode === 13 && currentIndex !== -1) {
- return _this.selectRowAtIndex(e, $('.is-focused', _this.dropdown).closest('li').index() - 1);
+ return _this.selectRowAtIndex($('.is-focused', _this.dropdown).closest('li').index() - 1);
}
};
})(this));
diff --git a/app/assets/javascripts/protected_branch_access_dropdown.js.es6 b/app/assets/javascripts/protected_branch_access_dropdown.js.es6
new file mode 100644
index 00000000000..2fbb088fa04
--- /dev/null
+++ b/app/assets/javascripts/protected_branch_access_dropdown.js.es6
@@ -0,0 +1,24 @@
+(global => {
+ global.gl = global.gl || {};
+
+ gl.ProtectedBranchAccessDropdown = class {
+ constructor(options) {
+ const { $dropdown, data, onSelect } = options;
+
+ $dropdown.glDropdown({
+ data: data,
+ selectable: true,
+ inputId: $dropdown.data('input-id'),
+ fieldName: $dropdown.data('field-name'),
+ toggleLabel(item) {
+ return item.text;
+ },
+ clicked(item, $el, e) {
+ e.preventDefault();
+ onSelect();
+ }
+ });
+ }
+ }
+
+})(window);
diff --git a/app/assets/javascripts/protected_branch_create.js.es6 b/app/assets/javascripts/protected_branch_create.js.es6
new file mode 100644
index 00000000000..00e20a03b04
--- /dev/null
+++ b/app/assets/javascripts/protected_branch_create.js.es6
@@ -0,0 +1,56 @@
+(global => {
+ global.gl = global.gl || {};
+
+ gl.ProtectedBranchCreate = class {
+ constructor() {
+ this.$wrap = this.$form = $('#new_protected_branch');
+ this.buildDropdowns();
+ }
+
+ buildDropdowns() {
+ const $allowedToMergeDropdown = this.$wrap.find('.js-allowed-to-merge');
+ const $allowedToPushDropdown = this.$wrap.find('.js-allowed-to-push');
+
+ // Cache callback
+ this.onSelectCallback = this.onSelect.bind(this);
+
+ // Allowed to Merge dropdown
+ new gl.ProtectedBranchAccessDropdown({
+ $dropdown: $allowedToMergeDropdown,
+ data: gon.merge_access_levels,
+ onSelect: this.onSelectCallback
+ });
+
+ // Allowed to Push dropdown
+ new gl.ProtectedBranchAccessDropdown({
+ $dropdown: $allowedToPushDropdown,
+ data: gon.push_access_levels,
+ onSelect: this.onSelectCallback
+ });
+
+ // Select default
+ $allowedToPushDropdown.data('glDropdown').selectRowAtIndex(0);
+ $allowedToMergeDropdown.data('glDropdown').selectRowAtIndex(0);
+
+ // Protected branch dropdown
+ new ProtectedBranchDropdown({
+ $dropdown: this.$wrap.find('.js-protected-branch-select'),
+ onSelect: this.onSelectCallback
+ });
+ }
+
+ // This will run after clicked callback
+ onSelect() {
+
+ // Enable submit button
+ const $branchInput = this.$wrap.find('input[name="protected_branch[name]"]');
+ const $allowedToMergeInput = this.$wrap.find('input[name="protected_branch[merge_access_level_attributes][access_level]"]');
+ const $allowedToPushInput = this.$wrap.find('input[name="protected_branch[push_access_level_attributes][access_level]"]');
+
+ if ($branchInput.val() && $allowedToMergeInput.val() && $allowedToPushInput.val()){
+ this.$form.find('input[type="submit"]').removeAttr('disabled');
+ }
+ }
+ }
+
+})(window);
diff --git a/app/assets/javascripts/protected_branch_dropdown.js.es6 b/app/assets/javascripts/protected_branch_dropdown.js.es6
new file mode 100644
index 00000000000..6738dc8862d
--- /dev/null
+++ b/app/assets/javascripts/protected_branch_dropdown.js.es6
@@ -0,0 +1,75 @@
+class ProtectedBranchDropdown {
+ constructor(options) {
+ this.onSelect = options.onSelect;
+ this.$dropdown = options.$dropdown;
+ this.$dropdownContainer = this.$dropdown.parent();
+ this.$dropdownFooter = this.$dropdownContainer.find('.dropdown-footer');
+ this.$protectedBranch = this.$dropdownContainer.find('.create-new-protected-branch');
+
+ this.buildDropdown();
+ this.bindEvents();
+
+ // Hide footer
+ this.$dropdownFooter.addClass('hidden');
+ }
+
+ buildDropdown() {
+ this.$dropdown.glDropdown({
+ data: this.getProtectedBranches.bind(this),
+ filterable: true,
+ remote: false,
+ search: {
+ fields: ['title']
+ },
+ selectable: true,
+ toggleLabel(selected) {
+ return (selected && 'id' in selected) ? selected.title : 'Protected Branch';
+ },
+ fieldName: 'protected_branch[name]',
+ text(protectedBranch) {
+ return _.escape(protectedBranch.title);
+ },
+ id(protectedBranch) {
+ return _.escape(protectedBranch.id);
+ },
+ onFilter: this.toggleCreateNewButton.bind(this),
+ clicked: (item, $el, e) => {
+ e.preventDefault();
+ this.onSelect();
+ }
+ });
+ }
+
+ bindEvents() {
+ this.$protectedBranch.on('click', this.onClickCreateWildcard.bind(this));
+ }
+
+ onClickCreateWildcard() {
+ this.$dropdown.data('glDropdown').remote.execute();
+ this.$dropdown.data('glDropdown').selectRowAtIndex(0);
+ }
+
+ getProtectedBranches(term, callback) {
+ if (this.selectedBranch) {
+ callback(gon.open_branches.concat(this.selectedBranch));
+ } else {
+ callback(gon.open_branches);
+ }
+ }
+
+ toggleCreateNewButton(branchName) {
+ this.selectedBranch = {
+ title: branchName,
+ id: branchName,
+ text: branchName
+ };
+
+ if (branchName) {
+ this.$dropdownContainer
+ .find('.create-new-protected-branch code')
+ .text(branchName);
+ }
+
+ this.$dropdownFooter.toggleClass('hidden', !branchName);
+ }
+}
diff --git a/app/assets/javascripts/protected_branch_edit.js.es6 b/app/assets/javascripts/protected_branch_edit.js.es6
new file mode 100644
index 00000000000..8d42e268ebc
--- /dev/null
+++ b/app/assets/javascripts/protected_branch_edit.js.es6
@@ -0,0 +1,61 @@
+(global => {
+ global.gl = global.gl || {};
+
+ gl.ProtectedBranchEdit = class {
+ constructor(options) {
+ this.$wrap = options.$wrap;
+ this.$allowedToMergeDropdown = this.$wrap.find('.js-allowed-to-merge');
+ this.$allowedToPushDropdown = this.$wrap.find('.js-allowed-to-push');
+
+ this.buildDropdowns();
+ }
+
+ buildDropdowns() {
+
+ // Allowed to merge dropdown
+ new gl.ProtectedBranchAccessDropdown({
+ $dropdown: this.$allowedToMergeDropdown,
+ data: gon.merge_access_levels,
+ onSelect: this.onSelect.bind(this)
+ });
+
+ // Allowed to push dropdown
+ new gl.ProtectedBranchAccessDropdown({
+ $dropdown: this.$allowedToPushDropdown,
+ data: gon.push_access_levels,
+ onSelect: this.onSelect.bind(this)
+ });
+ }
+
+ onSelect() {
+ const $allowedToMergeInput = this.$wrap.find(`input[name="${this.$allowedToMergeDropdown.data('fieldName')}"]`);
+ const $allowedToPushInput = this.$wrap.find(`input[name="${this.$allowedToPushDropdown.data('fieldName')}"]`);
+
+ $.ajax({
+ type: 'POST',
+ url: this.$wrap.data('url'),
+ dataType: 'json',
+ data: {
+ _method: 'PATCH',
+ id: this.$wrap.data('banchId'),
+ protected_branch: {
+ merge_access_level_attributes: {
+ access_level: $allowedToMergeInput.val()
+ },
+ push_access_level_attributes: {
+ access_level: $allowedToPushInput.val()
+ }
+ }
+ },
+ success: () => {
+ this.$wrap.effect('highlight');
+ },
+ error() {
+ $.scrollTo(0);
+ new Flash('Failed to update branch!');
+ }
+ });
+ }
+ }
+
+})(window);
diff --git a/app/assets/javascripts/protected_branch_edit_list.js.es6 b/app/assets/javascripts/protected_branch_edit_list.js.es6
new file mode 100644
index 00000000000..9ff0fd12c76
--- /dev/null
+++ b/app/assets/javascripts/protected_branch_edit_list.js.es6
@@ -0,0 +1,17 @@
+(global => {
+ global.gl = global.gl || {};
+
+ gl.ProtectedBranchEditList = class {
+ constructor() {
+ this.$wrap = $('.protected-branches-list');
+
+ // Build edit forms
+ this.$wrap.find('.js-protected-branch-edit-form').each((i, el) => {
+ new gl.ProtectedBranchEdit({
+ $wrap: $(el)
+ });
+ });
+ }
+ }
+
+})(window);
diff --git a/app/assets/javascripts/protected_branch_select.js b/app/assets/javascripts/protected_branch_select.js
deleted file mode 100644
index 3a47fc972dc..00000000000
--- a/app/assets/javascripts/protected_branch_select.js
+++ /dev/null
@@ -1,72 +0,0 @@
-(function() {
- var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
-
- this.ProtectedBranchSelect = (function() {
- function ProtectedBranchSelect(currentProject) {
- this.toggleCreateNewButton = bind(this.toggleCreateNewButton, this);
- this.getProtectedBranches = bind(this.getProtectedBranches, this);
- $('.dropdown-footer').hide();
- this.dropdown = $('.js-protected-branch-select').glDropdown({
- data: this.getProtectedBranches,
- filterable: true,
- remote: false,
- search: {
- fields: ['title']
- },
- selectable: true,
- toggleLabel: function(selected) {
- if (selected && 'id' in selected) {
- return selected.title;
- } else {
- return 'Protected Branch';
- }
- },
- fieldName: 'protected_branch[name]',
- text: function(protected_branch) {
- return _.escape(protected_branch.title);
- },
- id: function(protected_branch) {
- return _.escape(protected_branch.id);
- },
- onFilter: this.toggleCreateNewButton,
- clicked: function() {
- return $('.protect-branch-btn').attr('disabled', false);
- }
- });
- $('.create-new-protected-branch').on('click', (function(_this) {
- return function(event) {
- _this.dropdown.data('glDropdown').remote.execute();
- return _this.dropdown.data('glDropdown').selectRowAtIndex(event, 0);
- };
- })(this));
- }
-
- ProtectedBranchSelect.prototype.getProtectedBranches = function(term, callback) {
- if (this.selectedBranch) {
- return callback(gon.open_branches.concat(this.selectedBranch));
- } else {
- return callback(gon.open_branches);
- }
- };
-
- ProtectedBranchSelect.prototype.toggleCreateNewButton = function(branchName) {
- this.selectedBranch = {
- title: branchName,
- id: branchName,
- text: branchName
- };
- if (branchName === '') {
- $('.protected-branch-select-footer-list').addClass('hidden');
- return $('.dropdown-footer').hide();
- } else {
- $('.create-new-protected-branch').text("Create Protected Branch: " + branchName);
- $('.protected-branch-select-footer-list').removeClass('hidden');
- return $('.dropdown-footer').show();
- }
- };
-
- return ProtectedBranchSelect;
-
- })();
-
-}).call(this);
diff --git a/app/assets/javascripts/protected_branches_access_select.js.es6 b/app/assets/javascripts/protected_branches_access_select.js.es6
deleted file mode 100644
index e98312bbf37..00000000000
--- a/app/assets/javascripts/protected_branches_access_select.js.es6
+++ /dev/null
@@ -1,63 +0,0 @@
-class ProtectedBranchesAccessSelect {
- constructor(container, saveOnSelect, selectDefault) {
- this.container = container;
- this.saveOnSelect = saveOnSelect;
-
- this.container.find(".allowed-to-merge").each((i, element) => {
- var fieldName = $(element).data('field-name');
- var dropdown = $(element).glDropdown({
- data: gon.merge_access_levels,
- selectable: true,
- fieldName: fieldName,
- clicked: _.chain(this.onSelect).partial(element).bind(this).value()
- });
-
- if (selectDefault) {
- dropdown.data('glDropdown').selectRowAtIndex(document.createEvent("Event"), 0);
- }
- });
-
-
- this.container.find(".allowed-to-push").each((i, element) => {
- var fieldName = $(element).data('field-name');
- var dropdown = $(element).glDropdown({
- data: gon.push_access_levels,
- selectable: true,
- fieldName: fieldName,
- clicked: _.chain(this.onSelect).partial(element).bind(this).value()
- });
-
- if (selectDefault) {
- dropdown.data('glDropdown').selectRowAtIndex(document.createEvent("Event"), 0);
- }
- });
- }
-
- onSelect(dropdown, selected, element, e) {
- $(dropdown).find('.dropdown-toggle-text').text(selected.text);
- if (this.saveOnSelect) {
- return $.ajax({
- type: "POST",
- url: $(dropdown).data('url'),
- dataType: "json",
- data: {
- _method: 'PATCH',
- id: $(dropdown).data('id'),
- protected_branch: {
- ["" + ($(dropdown).data('type')) + "_attributes"]: {
- "access_level": selected.id
- }
- }
- },
- success: function() {
- var row;
- row = $(e.target);
- return row.closest('tr').effect('highlight');
- },
- error: function() {
- return new Flash("Failed to update branch!", "alert");
- }
- });
- }
- }
-}
diff --git a/app/assets/javascripts/users_select.js b/app/assets/javascripts/users_select.js
index 4af2a214e12..65d362e072c 100644
--- a/app/assets/javascripts/users_select.js
+++ b/app/assets/javascripts/users_select.js
@@ -13,14 +13,15 @@
}
$('.js-user-search').each((function(_this) {
return function(i, dropdown) {
+ var options = {};
var $block, $collapsedSidebar, $dropdown, $loading, $selectbox, $value, abilityName, assignTo, assigneeTemplate, collapsedAssigneeTemplate, defaultLabel, firstUser, issueURL, selectedId, showAnyUser, showNullUser;
$dropdown = $(dropdown);
- _this.projectId = $dropdown.data('project-id');
- _this.showCurrentUser = $dropdown.data('current-user');
+ options.projectId = $dropdown.data('project-id');
+ options.showCurrentUser = $dropdown.data('current-user');
showNullUser = $dropdown.data('null-user');
showAnyUser = $dropdown.data('any-user');
firstUser = $dropdown.data('first-user');
- _this.authorId = $dropdown.data('author-id');
+ options.authorId = $dropdown.data('author-id');
selectedId = $dropdown.data('selected');
defaultLabel = $dropdown.data('default-label');
issueURL = $dropdown.data('issueUpdate');
@@ -75,7 +76,7 @@
data: function(term, callback) {
var isAuthorFilter;
isAuthorFilter = $('.js-author-search');
- return _this.users(term, function(users) {
+ return _this.users(term, options, function(users) {
var anyUser, index, j, len, name, obj, showDivider;
if (term.length === 0) {
showDivider = 0;
@@ -185,11 +186,14 @@
$('.ajax-users-select').each((function(_this) {
return function(i, select) {
var firstUser, showAnyUser, showEmailUser, showNullUser;
- _this.projectId = $(select).data('project-id');
- _this.groupId = $(select).data('group-id');
- _this.showCurrentUser = $(select).data('current-user');
- _this.authorId = $(select).data('author-id');
- _this.skipUsers = $(select).data('skip-users');
+ var options = {};
+ options.skipLdap = $(select).hasClass('skip_ldap');
+ options.projectId = $(select).data('project-id');
+ options.groupId = $(select).data('group-id');
+ options.showCurrentUser = $(select).data('current-user');
+ options.pushCodeToProtectedBranches = $(select).data('push-code-to-protected-branches');
+ options.authorId = $(select).data('author-id');
+ options.skipUsers = $(select).data('skip-users');
showNullUser = $(select).data('null-user');
showAnyUser = $(select).data('any-user');
showEmailUser = $(select).data('email-user');
@@ -199,7 +203,7 @@
multiple: $(select).hasClass('multiselect'),
minimumInputLength: 0,
query: function(query) {
- return _this.users(query.term, function(users) {
+ return _this.users(query.term, options, function(users) {
var anyUser, data, emailUser, index, j, len, name, nullUser, obj, ref;
data = {
results: users
@@ -309,7 +313,7 @@
});
};
- UsersSelect.prototype.users = function(query, callback) {
+ UsersSelect.prototype.users = function(query, options, callback) {
var url;
url = this.buildUrl(this.usersPath);
return $.ajax({
@@ -318,11 +322,13 @@
search: query,
per_page: 20,
active: true,
- project_id: this.projectId,
- group_id: this.groupId,
- current_user: this.showCurrentUser,
- author_id: this.authorId,
- skip_users: this.skipUsers
+ project_id: options.projectId || null,
+ group_id: options.groupId || null,
+ skip_ldap: options.skipLdap || null,
+ current_user: options.showCurrentUser || null,
+ push_code_to_protected_branches: options.pushCodeToProtectedBranches || null,
+ author_id: options.authorId || null,
+ skip_users: options.skipUsers || null
},
dataType: "json"
}).done(function(users) {
diff --git a/app/assets/stylesheets/framework/dropdowns.scss b/app/assets/stylesheets/framework/dropdowns.scss
index c54eb0d6479..e8eafa15899 100644
--- a/app/assets/stylesheets/framework/dropdowns.scss
+++ b/app/assets/stylesheets/framework/dropdowns.scss
@@ -72,6 +72,14 @@
&.large {
width: 200px;
}
+
+ &.wide {
+ width: 100%;
+
+ + .dropdown-select {
+ width: 100%;
+ }
+ }
}
.dropdown-menu,
diff --git a/app/assets/stylesheets/framework/panels.scss b/app/assets/stylesheets/framework/panels.scss
index 874416e1007..c6f30e144fd 100644
--- a/app/assets/stylesheets/framework/panels.scss
+++ b/app/assets/stylesheets/framework/panels.scss
@@ -23,4 +23,9 @@
margin-top: $gl-padding;
}
}
+
+ .panel-title {
+ font-size: inherit;
+ line-height: inherit;
+ }
}
diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss
index d91c8e61165..cf9aa02600d 100644
--- a/app/assets/stylesheets/pages/projects.scss
+++ b/app/assets/stylesheets/pages/projects.scss
@@ -656,13 +656,9 @@ pre.light-well {
}
.new_protected_branch {
- .dropdown {
- display: inline;
- margin-left: 15px;
- }
-
label {
- min-width: 120px;
+ margin-top: 6px;
+ font-weight: normal;
}
}
@@ -678,6 +674,21 @@ pre.light-well {
font-weight: 600;
}
}
+
+ .settings-message {
+ margin: 0;
+ border-radius: 0 0 1px 1px;
+ padding: 20px 0;
+ border: none;
+ }
+
+ .table-bordered {
+ border-radius: 1px;
+
+ th:not(:last-child), td:not(:last-child) {
+ border-right: solid 1px transparent;
+ }
+ }
}
.custom-notifications-form {
diff --git a/app/models/project.rb b/app/models/project.rb
index a18aef09acd..a667857d058 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -865,10 +865,16 @@ class Project < ActiveRecord::Base
# Check if current branch name is marked as protected in the system
def protected_branch?(branch_name)
+ return true if empty_repo? && default_branch_protected?
+
@protected_branches ||= self.protected_branches.to_a
ProtectedBranch.matching(branch_name, protected_branches: @protected_branches).present?
end
+ def user_can_push_to_empty_repo?(user)
+ !default_branch_protected? || team.max_member_access(user.id) > Gitlab::Access::DEVELOPER
+ end
+
def forked?
!(forked_project_link.nil? || forked_project_link.forked_from_project.nil?)
end
@@ -1260,6 +1266,11 @@ class Project < ActiveRecord::Base
private
+ def default_branch_protected?
+ current_application_settings.default_branch_protection == Gitlab::Access::PROTECTION_FULL ||
+ current_application_settings.default_branch_protection == Gitlab::Access::PROTECTION_DEV_CAN_MERGE
+ end
+
def authorized_for_user_by_group?(user, min_access_level)
member = user.group_members.find_by(source_id: group)
diff --git a/app/views/projects/protected_branches/_branches_list.html.haml b/app/views/projects/protected_branches/_branches_list.html.haml
index 0603a014008..04b19a8c5a7 100644
--- a/app/views/projects/protected_branches/_branches_list.html.haml
+++ b/app/views/projects/protected_branches/_branches_list.html.haml
@@ -1,26 +1,28 @@
-%h5.prepend-top-0
- Already Protected (#{@protected_branches.size})
-- if @protected_branches.empty?
- %p.settings-message.text-center
- No branches are protected, protect a branch with the form above.
-- else
- - can_admin_project = can?(current_user, :admin_project, @project)
+.panel.panel-default.protected-branches-list
+ - if @protected_branches.empty?
+ .panel-heading
+ %h3.panel-title
+ Protected branch (#{@protected_branches.size})
+ %p.settings-message.text-center
+ There are currently no protected branches, protect a branch with the form above.
+ - else
+ - can_admin_project = can?(current_user, :admin_project, @project)
- %table.table.protected-branches-list
- %colgroup
- %col{ width: "20%" }
- %col{ width: "30%" }
- %col{ width: "25%" }
- %col{ width: "25%" }
- %thead
- %tr
- %th Branch
- %th Last commit
- %th Allowed to merge
- %th Allowed to push
- - if can_admin_project
- %th
- %tbody
- = render partial: @protected_branches, locals: { can_admin_project: can_admin_project }
+ %table.table.table-bordered
+ %colgroup
+ %col{ width: "25%" }
+ %col{ width: "30%" }
+ %col{ width: "25%" }
+ %col{ width: "20%" }
+ %thead
+ %tr
+ %th Protected branch (#{@protected_branches.size})
+ %th Last commit
+ %th Allowed to merge
+ %th Allowed to push
+ - if can_admin_project
+ %th
+ %tbody
+ = render partial: @protected_branches, locals: { can_admin_project: can_admin_project }
- = paginate @protected_branches, theme: 'gitlab'
+ = paginate @protected_branches, theme: 'gitlab'
diff --git a/app/views/projects/protected_branches/_create_protected_branch.html.haml b/app/views/projects/protected_branches/_create_protected_branch.html.haml
new file mode 100644
index 00000000000..85d0c494ba8
--- /dev/null
+++ b/app/views/projects/protected_branches/_create_protected_branch.html.haml
@@ -0,0 +1,36 @@
+= form_for [@project.namespace.becomes(Namespace), @project, @protected_branch] do |f|
+ .panel.panel-default
+ .panel-heading
+ %h3.panel-title
+ Protect a branch
+ .panel-body
+ .form-horizontal
+ .form-group
+ = f.label :name, class: 'col-md-2 text-right' do
+ Branch:
+ .col-md-10
+ = render partial: "dropdown", locals: { f: f }
+ .help-block
+ = link_to 'Wildcards', help_page_path('user/project/protected_branches', anchor: 'wildcard-protected-branches')
+ such as
+ %code *-stable
+ or
+ %code production/*
+ are supported
+ .form-group
+ %label.col-md-2.text-right{ for: 'merge_access_level_attributes' }
+ Allowed to merge:
+ .col-md-10
+ = dropdown_tag('Select',
+ options: { toggle_class: 'js-allowed-to-merge wide',
+ data: { field_name: 'protected_branch[merge_access_level_attributes][access_level]', input_id: 'merge_access_level_attributes' }})
+ .form-group
+ %label.col-md-2.text-right{ for: 'push_access_level_attributes' }
+ Allowed to push:
+ .col-md-10
+ = dropdown_tag('Select',
+ options: { toggle_class: 'js-allowed-to-push wide',
+ data: { field_name: 'protected_branch[push_access_level_attributes][access_level]', input_id: 'push_access_level_attributes' }})
+
+ .panel-footer
+ = f.submit 'Protect', class: 'btn-create btn', disabled: true
diff --git a/app/views/projects/protected_branches/_dropdown.html.haml b/app/views/projects/protected_branches/_dropdown.html.haml
index b803d932e67..a9e27df5a87 100644
--- a/app/views/projects/protected_branches/_dropdown.html.haml
+++ b/app/views/projects/protected_branches/_dropdown.html.haml
@@ -1,17 +1,15 @@
= f.hidden_field(:name)
-= dropdown_tag("Protected Branch",
- options: { title: "Pick protected branch", toggle_class: 'js-protected-branch-select js-filter-submit',
+= dropdown_tag('Select branch or create wildcard',
+ options: { toggle_class: 'js-protected-branch-select js-filter-submit wide',
filter: true, dropdown_class: "dropdown-menu-selectable", placeholder: "Search protected branches",
footer_content: true,
data: { show_no: true, show_any: true, show_upcoming: true,
selected: params[:protected_branch_name],
project_id: @project.try(:id) } }) do
- %ul.dropdown-footer-list.hidden.protected-branch-select-footer-list
+ %ul.dropdown-footer-list
%li
= link_to '#', title: "New Protected Branch", class: "create-new-protected-branch" do
- Create new
-
-:javascript
- new ProtectedBranchSelect();
+ Create wildcard
+ %code
diff --git a/app/views/projects/protected_branches/_protected_branch.html.haml b/app/views/projects/protected_branches/_protected_branch.html.haml
index 498e412235e..e2e01ee78f8 100644
--- a/app/views/projects/protected_branches/_protected_branch.html.haml
+++ b/app/views/projects/protected_branches/_protected_branch.html.haml
@@ -1,5 +1,4 @@
-- url = namespace_project_protected_branch_path(@project.namespace, @project, protected_branch)
-%tr
+%tr.js-protected-branch-edit-form{ data: { url: namespace_project_protected_branch_path(@project.namespace, @project, protected_branch), branch_id: protected_branch.id } }
%td
= protected_branch.name
- if @project.root_ref?(protected_branch.name)
@@ -16,14 +15,14 @@
(branch was removed from repository)
%td
= hidden_field_tag "allowed_to_merge_#{protected_branch.id}", protected_branch.merge_access_level.access_level
- = dropdown_tag(protected_branch.merge_access_level.humanize,
- options: { title: "Allowed to merge", toggle_class: 'allowed-to-merge', dropdown_class: 'dropdown-menu-selectable merge',
- data: { field_name: "allowed_to_merge_#{protected_branch.id}", url: url, id: protected_branch.id, type: "merge_access_level" }})
+ = dropdown_tag( (protected_branch.merge_access_level.humanize || 'Select') ,
+ options: { toggle_class: 'js-allowed-to-merge', dropdown_class: 'dropdown-menu-selectable js-allowed-to-merge-container',
+ data: { field_name: "allowed_to_merge_#{protected_branch.id}" }})
%td
= hidden_field_tag "allowed_to_push_#{protected_branch.id}", protected_branch.push_access_level.access_level
- = dropdown_tag(protected_branch.push_access_level.humanize,
- options: { title: "Allowed to push", toggle_class: 'allowed-to-push', dropdown_class: 'dropdown-menu-selectable push',
- data: { field_name: "allowed_to_push_#{protected_branch.id}", url: url, id: protected_branch.id, type: "push_access_level" }})
+ = dropdown_tag( (protected_branch.push_access_level.humanize || 'Select') ,
+ options: { toggle_class: 'js-allowed-to-push', dropdown_class: 'dropdown-menu-selectable js-allowed-to-push-container',
+ data: { field_name: "allowed_to_push_#{protected_branch.id}" }})
- if can_admin_project
%td
- = link_to 'Unprotect', [@project.namespace.becomes(Namespace), @project, protected_branch], data: { confirm: 'Branch will be writable for developers. Are you sure?' }, method: :delete, class: "btn btn-warning btn-sm pull-right"
+ = link_to 'Unprotect', [@project.namespace.becomes(Namespace), @project, protected_branch], data: { confirm: 'Branch will be writable for developers. Are you sure?' }, method: :delete, class: 'btn btn-warning'
diff --git a/app/views/projects/protected_branches/index.html.haml b/app/views/projects/protected_branches/index.html.haml
index 4efe44c7233..49dcc9a6ba4 100644
--- a/app/views/projects/protected_branches/index.html.haml
+++ b/app/views/projects/protected_branches/index.html.haml
@@ -14,41 +14,7 @@
%li prevent <strong>anyone</strong> from deleting the branch
%p.append-bottom-0 Read more about #{link_to "protected branches", help_page_path("user/project/protected_branches"), class: "underlined-link"} and #{link_to "project permissions", help_page_path("user/permissions"), class: "underlined-link"}.
.col-lg-9
- %h5.prepend-top-0
- Protect a branch
- if can? current_user, :admin_project, @project
- = form_for [@project.namespace.becomes(Namespace), @project, @protected_branch] do |f|
- = form_errors(@protected_branch)
+ = render 'create_protected_branch'
- .form-group
- = f.label :name, "Branch", class: "label-light"
- = render partial: "dropdown", locals: { f: f }
- %p.help-block
- = link_to "Wildcards", help_page_path('user/project/protected_branches', anchor: "wildcard-protected-branches")
- such as
- %code *-stable
- or
- %code production/*
- are supported.
-
- .form-group
- = hidden_field_tag 'protected_branch[merge_access_level_attributes][access_level]'
- = label_tag "Allowed to merge: ", nil, class: "label-light append-bottom-0"
- = dropdown_tag("<Make a selection>",
- options: { title: "Allowed to merge", toggle_class: 'allowed-to-merge',
- dropdown_class: 'dropdown-menu-selectable',
- data: { field_name: "protected_branch[merge_access_level_attributes][access_level]" }})
-
- .form-group
- = hidden_field_tag 'protected_branch[push_access_level_attributes][access_level]'
- = label_tag "Allowed to push: ", nil, class: "label-light append-bottom-0"
- = dropdown_tag("<Make a selection>",
- options: { title: "Allowed to push", toggle_class: 'allowed-to-push',
- dropdown_class: 'dropdown-menu-selectable',
- data: { field_name: "protected_branch[push_access_level_attributes][access_level]" }})
-
-
- = f.submit "Protect", class: "btn-create btn protect-branch-btn", disabled: true
-
- %hr
= render "branches_list"
diff --git a/app/views/shared/web_hooks/_form.html.haml b/app/views/shared/web_hooks/_form.html.haml
index 2585ed9360b..470dac6d75b 100644
--- a/app/views/shared/web_hooks/_form.html.haml
+++ b/app/views/shared/web_hooks/_form.html.haml
@@ -19,7 +19,7 @@
= f.label :token, "Secret Token", class: 'label-light'
= f.text_field :token, class: "form-control", placeholder: ''
%p.help-block
- Use this token to validate received payloads
+ Use this token to validate received payloads. It will be sent with the request in the X-Gitlab-Token HTTP header.
.form-group
= f.label :url, "Trigger", class: 'label-light'
%ul.list-unstyled
diff --git a/doc/administration/container_registry.md b/doc/administration/container_registry.md
index d5d43303454..b5db575477c 100644
--- a/doc/administration/container_registry.md
+++ b/doc/administration/container_registry.md
@@ -1,7 +1,6 @@
# GitLab Container Registry Administration
-> **Note:**
-This feature was [introduced][ce-4040] in GitLab 8.8.
+> [Introduced][ce-4040] in GitLab 8.8.
With the Docker Container Registry integrated into GitLab, every project can
have its own space to store its Docker images.
diff --git a/doc/administration/custom_hooks.md b/doc/administration/custom_hooks.md
index e3306c22d3f..0387d730489 100644
--- a/doc/administration/custom_hooks.md
+++ b/doc/administration/custom_hooks.md
@@ -44,8 +44,7 @@ as appropriate.
## Custom error messages
->**Note:**
-This feature was [introduced][5073] in GitLab 8.10.
+> [Introduced][5073] in GitLab 8.10.
If the commit is declined or an error occurs during the Git hook check,
the STDERR or STDOUT message of the hook will be present in GitLab's UI.
diff --git a/doc/administration/housekeeping.md b/doc/administration/housekeeping.md
index a5fa7d358a2..34b4f1faa94 100644
--- a/doc/administration/housekeeping.md
+++ b/doc/administration/housekeeping.md
@@ -1,6 +1,6 @@
# Housekeeping
-_**Note:** This feature was [introduced][ce-2371] in GitLab 8.4_
+> [Introduced][ce-2371] in GitLab 8.4.
---
diff --git a/doc/administration/raketasks/project_import_export.md b/doc/administration/raketasks/project_import_export.md
index c212059b9d5..39b1883375e 100644
--- a/doc/administration/raketasks/project_import_export.md
+++ b/doc/administration/raketasks/project_import_export.md
@@ -1,13 +1,14 @@
# Project import/export
>**Note:**
- - This feature was [introduced][ce-3050] in GitLab 8.9
- - Importing will not be possible if the import instance version is lower
- than that of the exporter.
- - For existing installations, the project import option has to be enabled in
- application settings (`/admin/application_settings`) under 'Import sources'.
- - The exports are stored in a temporary [shared directory][tmp] and are deleted
- every 24 hours by a specific worker.
+>
+> - [Introduced][ce-3050] in GitLab 8.9.
+> - Importing will not be possible if the import instance version is lower
+> than that of the exporter.
+> - For existing installations, the project import option has to be enabled in
+> application settings (`/admin/application_settings`) under 'Import sources'.
+> - The exports are stored in a temporary [shared directory][tmp] and are deleted
+> every 24 hours by a specific worker.
The GitLab Import/Export version can be checked by using:
diff --git a/doc/administration/repository_checks.md b/doc/administration/repository_checks.md
index 4172b604cec..bc2b1f20ed3 100644
--- a/doc/administration/repository_checks.md
+++ b/doc/administration/repository_checks.md
@@ -1,8 +1,7 @@
# Repository checks
->**Note:**
-This feature was [introduced][ce-3232] in GitLab 8.7. It is OFF by
-default because it still causes too many false alarms.
+> [Introduced][ce-3232] in GitLab 8.7. It is OFF by default because it still
+causes too many false alarms.
Git has a built-in mechanism, [git fsck][git-fsck], to verify the
integrity of all data committed to a repository. GitLab administrators
diff --git a/doc/api/README.md b/doc/api/README.md
index d1e6c54c521..21141d350cf 100644
--- a/doc/api/README.md
+++ b/doc/api/README.md
@@ -81,7 +81,7 @@ Read more about [GitLab as an OAuth2 client](oauth2.md).
### Personal Access Tokens
-> **Note:** This feature was [introduced][ce-3749] in GitLab 8.8
+> [Introduced][ce-3749] in GitLab 8.8.
You can create as many personal access tokens as you like from your GitLab
profile (`/profile/personal_access_tokens`); perhaps one for each application
diff --git a/doc/api/award_emoji.md b/doc/api/award_emoji.md
index 796b3680a75..158fb189005 100644
--- a/doc/api/award_emoji.md
+++ b/doc/api/award_emoji.md
@@ -1,6 +1,6 @@
# Award Emoji
- >**Note:** This feature was introduced in GitLab 8.9
+> [Introduced][ce-4575] in GitLab 8.9.
An awarded emoji tells a thousand words, and can be awarded on issues, merge
requests and notes/comments. Issues, merge requests and notes are further called
@@ -365,3 +365,5 @@ Example Response:
"awardable_type": "Note"
}
```
+
+[ce-4575]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/4575
diff --git a/doc/api/todos.md b/doc/api/todos.md
index 937c71de386..c9e1e83e28a 100644
--- a/doc/api/todos.md
+++ b/doc/api/todos.md
@@ -1,6 +1,6 @@
# Todos
-**Note:** This feature was [introduced][ce-3188] in GitLab 8.10
+> [Introduced][ce-3188] in GitLab 8.10.
## Get a list of todos
diff --git a/doc/ci/triggers/README.md b/doc/ci/triggers/README.md
index 5c316510d0e..57a12526363 100644
--- a/doc/ci/triggers/README.md
+++ b/doc/ci/triggers/README.md
@@ -1,6 +1,6 @@
# Triggering Builds through the API
-_**Note:** This feature was [introduced][ci-229] in GitLab CE 7.14_
+> [Introduced][ci-229] in GitLab CE 7.14.
Triggers can be used to force a rebuild of a specific branch, tag or commit,
with an API call.
diff --git a/doc/container_registry/README.md b/doc/container_registry/README.md
index 3db351811a8..047a0b08406 100644
--- a/doc/container_registry/README.md
+++ b/doc/container_registry/README.md
@@ -1,8 +1,7 @@
# GitLab Container Registry
-> **Note:**
-This feature was [introduced][ce-4040] in GitLab 8.8. Docker Registry manifest
-v1 support was added in GitLab 8.9 to support Docker versions earlier than 1.10.
+> [Introduced][ce-4040] in GitLab 8.8. Docker Registry manifest
+`v1` support was added in GitLab 8.9 to support Docker versions earlier than 1.10.
> **Note:**
This document is about the user guide. To learn how to enable GitLab Container
diff --git a/doc/development/doc_styleguide.md b/doc/development/doc_styleguide.md
index da6b7686427..994005f929f 100644
--- a/doc/development/doc_styleguide.md
+++ b/doc/development/doc_styleguide.md
@@ -155,15 +155,15 @@ Inside the document:
- Every piece of documentation that comes with a new feature should declare the
GitLab version that feature got introduced. Right below the heading add a
- note: `>**Note:** This feature was introduced in GitLab 8.3`
+ note: `> Introduced in GitLab 8.3.`.
- If possible every feature should have a link to the MR that introduced it.
The above note would be then transformed to:
- `>**Note:** This feature was [introduced][ce-1242] in GitLab 8.3`, where
+ `> [Introduced][ce-1242] in GitLab 8.3.`, where
the [link identifier](#links) is named after the repository (CE) and the MR
- number
+ number.
- If the feature is only in GitLab EE, don't forget to mention it, like:
- `>**Note:** This feature was introduced in GitLab EE 8.3`. Otherwise, leave
- this mention out
+ `> Introduced in GitLab EE 8.3.`. Otherwise, leave
+ this mention out.
## References
diff --git a/doc/monitoring/health_check.md b/doc/monitoring/health_check.md
index 0d17799372f..70326f1ff80 100644
--- a/doc/monitoring/health_check.md
+++ b/doc/monitoring/health_check.md
@@ -1,6 +1,6 @@
# Health Check
->**Note:** This feature was [introduced][ce-3888] in GitLab 8.8.
+> [Introduced][ce-3888] in GitLab 8.8.
GitLab provides a health check endpoint for uptime monitoring on the `health_check` web
endpoint. The health check reports on the overall system status based on the status of
diff --git a/doc/user/project/labels.md b/doc/user/project/labels.md
index 4258185b7d0..1259a16330b 100644
--- a/doc/user/project/labels.md
+++ b/doc/user/project/labels.md
@@ -46,10 +46,11 @@ When you are ready press the **Create label** button to create the new label.
## Prioritize labels
>**Notes:**
- - This feature was introduced in GitLab 8.9.
- - Priority sorting is based on the highest priority label only. This might
- change in the future, follow the discussion in
- https://gitlab.com/gitlab-org/gitlab-ce/issues/18554.
+>
+> - Introduced in GitLab 8.9.
+> - Priority sorting is based on the highest priority label only. This might
+> change in the future, follow the discussion in
+> https://gitlab.com/gitlab-org/gitlab-ce/issues/18554.
Prioritized labels are like any other label, but sorted by priority. This allows
you to sort issues and merge requests by priority.
@@ -87,8 +88,7 @@ important.
## Create a new label right from the issue tracker
->**Note:**
-This feature was introduced in GitLab 8.6.
+> Introduced in GitLab 8.6.
There are times when you are already in the issue tracker searching for a
label, only to realize it doesn't exist. Instead of going to the **Labels**
diff --git a/doc/user/project/protected_branches.md b/doc/user/project/protected_branches.md
index 6a8170b5ecb..96d9bdc1b29 100644
--- a/doc/user/project/protected_branches.md
+++ b/doc/user/project/protected_branches.md
@@ -47,8 +47,7 @@ creation.
## Wildcard protected branches
->**Note:**
-This feature was [introduced][ce-4665] in GitLab 8.10.
+> [Introduced][ce-4665] in GitLab 8.10.
You can specify a wildcard protected branch, which will protect all branches
matching the wildcard. For example:
diff --git a/doc/user/project/settings/import_export.md b/doc/user/project/settings/import_export.md
index 38e9786123d..2513def49a4 100644
--- a/doc/user/project/settings/import_export.md
+++ b/doc/user/project/settings/import_export.md
@@ -1,18 +1,19 @@
# Project import/export
>**Notes:**
- - This feature was [introduced][ce-3050] in GitLab 8.9
- - Importing will not be possible if the import instance version is lower
- than that of the exporter.
- - For existing installations, the project import option has to be enabled in
- application settings (`/admin/application_settings`) under 'Import sources'.
- Ask your administrator if you don't see the **GitLab export** button when
- creating a new project.
- - You can find some useful raketasks if you are an administrator in the
- [import_export](../../../administration/raketasks/project_import_export.md)
- raketask.
- - The exports are stored in a temporary [shared directory][tmp] and are deleted
- every 24 hours by a specific worker.
+>
+> - [Introduced][ce-3050] in GitLab 8.9.
+> - Importing will not be possible if the import instance version is lower
+> than that of the exporter.
+> - For existing installations, the project import option has to be enabled in
+> application settings (`/admin/application_settings`) under 'Import sources'.
+> Ask your administrator if you don't see the **GitLab export** button when
+> creating a new project.
+> - You can find some useful raketasks if you are an administrator in the
+> [import_export](../../../administration/raketasks/project_import_export.md)
+> raketask.
+> - The exports are stored in a temporary [shared directory][tmp] and are deleted
+> every 24 hours by a specific worker.
Existing projects running on any GitLab instance or GitLab.com can be exported
with all their related data and be moved into a new GitLab instance.
diff --git a/doc/web_hooks/web_hooks.md b/doc/web_hooks/web_hooks.md
index 8559b67af04..d4b28d875cd 100644
--- a/doc/web_hooks/web_hooks.md
+++ b/doc/web_hooks/web_hooks.md
@@ -26,6 +26,10 @@ GitLab webhooks keep in mind the following things:
you are writing a low-level hook this is important to remember.
- GitLab ignores the HTTP status code returned by your endpoint.
+## Secret Token
+
+If you specify a secret token, it will be sent with the hook request in the `X-Gitlab-Token` HTTP header. Your webhook endpoint can check that to verify that the request is legitimate.
+
## SSL Verification
By default, the SSL certificate of the webhook endpoint is verified based on
diff --git a/doc/workflow/award_emoji.md b/doc/workflow/award_emoji.md
index e6f8b792707..1df0698afd0 100644
--- a/doc/workflow/award_emoji.md
+++ b/doc/workflow/award_emoji.md
@@ -1,7 +1,7 @@
# Award emoji
>**Note:**
-This feature was [introduced][1825] in GitLab 8.2.
+[Introduced][1825] in GitLab 8.2.
When you're collaborating online, you get fewer opportunities for high-fives
and thumbs-ups. Emoji can be awarded to issues and merge requests, making
@@ -16,7 +16,7 @@ award emoji.
## Sort issues and merge requests on vote count
>**Note:**
-This feature was [introduced][2871] in GitLab 8.5.
+[Introduced][2871] in GitLab 8.5.
You can quickly sort issues and merge requests by the number of votes they
have received. The sort options can be found in the dropdown menu as "Most
@@ -45,7 +45,7 @@ downvotes.
## Award emoji for comments
>**Note:**
-This feature was [introduced][4291] in GitLab 8.9.
+[Introduced][4291] in GitLab 8.9.
Award emoji can also be applied to individual comments when you want to
celebrate an accomplishment or agree with an opinion.
diff --git a/doc/workflow/cherry_pick_changes.md b/doc/workflow/cherry_pick_changes.md
index 4a499009842..64b94d81024 100644
--- a/doc/workflow/cherry_pick_changes.md
+++ b/doc/workflow/cherry_pick_changes.md
@@ -1,7 +1,6 @@
# Cherry-pick changes
->**Note:**
-This feature was [introduced][ce-3514] in GitLab 8.7.
+> [Introduced][ce-3514] in GitLab 8.7.
---
diff --git a/doc/workflow/file_finder.md b/doc/workflow/file_finder.md
index b69ae663272..8d87b030c83 100644
--- a/doc/workflow/file_finder.md
+++ b/doc/workflow/file_finder.md
@@ -1,6 +1,6 @@
# File finder
-_**Note:** This feature was [introduced][gh-9889] in GitLab 8.4._
+> [Introduced][gh-9889] in GitLab 8.4.
---
diff --git a/doc/workflow/revert_changes.md b/doc/workflow/revert_changes.md
index 399366b0cdc..5ead9f4177f 100644
--- a/doc/workflow/revert_changes.md
+++ b/doc/workflow/revert_changes.md
@@ -1,6 +1,6 @@
# Reverting changes
-_**Note:** This feature was [introduced][ce-1990] in GitLab 8.5._
+> [Introduced][ce-1990] in GitLab 8.5.
---
diff --git a/doc/workflow/todos.md b/doc/workflow/todos.md
index 9524ffd5420..a50ba305deb 100644
--- a/doc/workflow/todos.md
+++ b/doc/workflow/todos.md
@@ -1,6 +1,6 @@
# GitLab Todos
->**Note:** This feature was [introduced][ce-2817] in GitLab 8.5.
+> [Introduced][ce-2817] in GitLab 8.5.
When you log into GitLab, you normally want to see where you should spend your
time and take some action, or what you need to keep an eye on. All without the
diff --git a/doc/workflow/web_editor.md b/doc/workflow/web_editor.md
index 1832567a34c..ee8e7862572 100644
--- a/doc/workflow/web_editor.md
+++ b/doc/workflow/web_editor.md
@@ -70,8 +70,7 @@ There are multiple ways to create a branch from GitLab's web interface.
### Create a new branch from an issue
->**Note:**
-This feature was [introduced][ce-2808] in GitLab 8.6.
+> [Introduced][ce-2808] in GitLab 8.6.
In case your development workflow dictates to have an issue for every merge
request, you can quickly create a branch right on the issue page which will be
diff --git a/lib/gitlab/user_access.rb b/lib/gitlab/user_access.rb
index 3a69027368f..c55a7fc4d3d 100644
--- a/lib/gitlab/user_access.rb
+++ b/lib/gitlab/user_access.rb
@@ -30,6 +30,8 @@ module Gitlab
return false unless user
if project.protected_branch?(ref)
+ return true if project.empty_repo? && project.user_can_push_to_empty_repo?(user)
+
access_levels = project.protected_branches.matching(ref).map(&:push_access_level)
access_levels.any? { |access_level| access_level.check_access(user) }
else
diff --git a/spec/features/protected_branches_spec.rb b/spec/features/protected_branches_spec.rb
index 57734b33a44..3499460c84d 100644
--- a/spec/features/protected_branches_spec.rb
+++ b/spec/features/protected_branches_spec.rb
@@ -11,7 +11,7 @@ feature 'Projected Branches', feature: true, js: true do
def set_protected_branch_name(branch_name)
find(".js-protected-branch-select").click
find(".dropdown-input-field").set(branch_name)
- click_on "Create Protected Branch: #{branch_name}"
+ click_on("Create wildcard #{branch_name}")
end
describe "explicit protected branches" do
@@ -90,7 +90,7 @@ feature 'Projected Branches', feature: true, js: true do
visit namespace_project_protected_branches_path(project.namespace, project)
set_protected_branch_name('master')
within('.new_protected_branch') do
- find(".allowed-to-push").click
+ find(".js-allowed-to-push").click
within(".dropdown.open .dropdown-menu") { click_on access_type_name }
end
click_on "Protect"
@@ -107,8 +107,8 @@ feature 'Projected Branches', feature: true, js: true do
expect(ProtectedBranch.count).to eq(1)
within(".protected-branches-list") do
- find(".allowed-to-push").click
- within('.dropdown-menu.push') { click_on access_type_name }
+ find(".js-allowed-to-push").click
+ within('.js-allowed-to-push-container') { click_on access_type_name }
end
wait_for_ajax
@@ -121,7 +121,7 @@ feature 'Projected Branches', feature: true, js: true do
visit namespace_project_protected_branches_path(project.namespace, project)
set_protected_branch_name('master')
within('.new_protected_branch') do
- find(".allowed-to-merge").click
+ find(".js-allowed-to-merge").click
within(".dropdown.open .dropdown-menu") { click_on access_type_name }
end
click_on "Protect"
@@ -138,8 +138,8 @@ feature 'Projected Branches', feature: true, js: true do
expect(ProtectedBranch.count).to eq(1)
within(".protected-branches-list") do
- find(".allowed-to-merge").click
- within('.dropdown-menu.merge') { click_on access_type_name }
+ find(".js-allowed-to-merge").click
+ within('.js-allowed-to-merge-container') { click_on access_type_name }
end
wait_for_ajax
diff --git a/spec/lib/gitlab/user_access_spec.rb b/spec/lib/gitlab/user_access_spec.rb
index 5bb095366fa..d3c3b800b94 100644
--- a/spec/lib/gitlab/user_access_spec.rb
+++ b/spec/lib/gitlab/user_access_spec.rb
@@ -9,35 +9,80 @@ describe Gitlab::UserAccess, lib: true do
describe 'push to none protected branch' do
it 'returns true if user is a master' do
project.team << [user, :master]
+
expect(access.can_push_to_branch?('random_branch')).to be_truthy
end
it 'returns true if user is a developer' do
project.team << [user, :developer]
+
expect(access.can_push_to_branch?('random_branch')).to be_truthy
end
it 'returns false if user is a reporter' do
project.team << [user, :reporter]
+
expect(access.can_push_to_branch?('random_branch')).to be_falsey
end
end
+ describe 'push to empty project' do
+ let(:empty_project) { create(:project_empty_repo) }
+ let(:project_access) { Gitlab::UserAccess.new(user, project: empty_project) }
+
+ it 'returns true if user is master' do
+ empty_project.team << [user, :master]
+
+ expect(project_access.can_push_to_branch?('master')).to be_truthy
+ end
+
+ it 'returns false if user is developer and project is fully protected' do
+ empty_project.team << [user, :developer]
+ stub_application_setting(default_branch_protection: Gitlab::Access::PROTECTION_FULL)
+
+ expect(project_access.can_push_to_branch?('master')).to be_falsey
+ end
+
+ it 'returns false if user is developer and it is not allowed to push new commits but can merge into branch' do
+ empty_project.team << [user, :developer]
+ stub_application_setting(default_branch_protection: Gitlab::Access::PROTECTION_DEV_CAN_MERGE)
+
+ expect(project_access.can_push_to_branch?('master')).to be_falsey
+ end
+
+ it 'returns true if user is developer and project is unprotected' do
+ empty_project.team << [user, :developer]
+ stub_application_setting(default_branch_protection: Gitlab::Access::PROTECTION_NONE)
+
+ expect(project_access.can_push_to_branch?('master')).to be_truthy
+ end
+
+ it 'returns true if user is developer and project grants developers permission' do
+ empty_project.team << [user, :developer]
+ stub_application_setting(default_branch_protection: Gitlab::Access::PROTECTION_DEV_CAN_PUSH)
+
+ expect(project_access.can_push_to_branch?('master')).to be_truthy
+ end
+ end
+
describe 'push to protected branch' do
let(:branch) { create :protected_branch, project: project }
it 'returns true if user is a master' do
project.team << [user, :master]
+
expect(access.can_push_to_branch?(branch.name)).to be_truthy
end
it 'returns false if user is a developer' do
project.team << [user, :developer]
+
expect(access.can_push_to_branch?(branch.name)).to be_falsey
end
it 'returns false if user is a reporter' do
project.team << [user, :reporter]
+
expect(access.can_push_to_branch?(branch.name)).to be_falsey
end
end
@@ -49,16 +94,19 @@ describe Gitlab::UserAccess, lib: true do
it 'returns true if user is a master' do
project.team << [user, :master]
+
expect(access.can_push_to_branch?(@branch.name)).to be_truthy
end
it 'returns true if user is a developer' do
project.team << [user, :developer]
+
expect(access.can_push_to_branch?(@branch.name)).to be_truthy
end
it 'returns false if user is a reporter' do
project.team << [user, :reporter]
+
expect(access.can_push_to_branch?(@branch.name)).to be_falsey
end
end
@@ -70,19 +118,21 @@ describe Gitlab::UserAccess, lib: true do
it 'returns true if user is a master' do
project.team << [user, :master]
+
expect(access.can_merge_to_branch?(@branch.name)).to be_truthy
end
it 'returns true if user is a developer' do
project.team << [user, :developer]
+
expect(access.can_merge_to_branch?(@branch.name)).to be_truthy
end
it 'returns false if user is a reporter' do
project.team << [user, :reporter]
+
expect(access.can_merge_to_branch?(@branch.name)).to be_falsey
end
end
-
end
end
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index e365e4e98b2..567f87b9970 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -69,6 +69,7 @@ describe Project, models: true do
it { is_expected.to include_module(Gitlab::ConfigHelper) }
it { is_expected.to include_module(Gitlab::ShellAdapter) }
it { is_expected.to include_module(Gitlab::VisibilityLevel) }
+ it { is_expected.to include_module(Gitlab::CurrentSettings) }
it { is_expected.to include_module(Referable) }
it { is_expected.to include_module(Sortable) }
end
@@ -1070,28 +1071,97 @@ describe Project, models: true do
end
describe '#protected_branch?' do
+ context 'existing project' do
+ let(:project) { create(:project) }
+
+ it 'returns true when the branch matches a protected branch via direct match' do
+ project.protected_branches.create!(name: 'foo')
+
+ expect(project.protected_branch?('foo')).to eq(true)
+ end
+
+ it 'returns true when the branch matches a protected branch via wildcard match' do
+ project.protected_branches.create!(name: 'production/*')
+
+ expect(project.protected_branch?('production/some-branch')).to eq(true)
+ end
+
+ it 'returns false when the branch does not match a protected branch via direct match' do
+ expect(project.protected_branch?('foo')).to eq(false)
+ end
+
+ it 'returns false when the branch does not match a protected branch via wildcard match' do
+ project.protected_branches.create!(name: 'production/*')
+
+ expect(project.protected_branch?('staging/some-branch')).to eq(false)
+ end
+ end
+
+ context "new project" do
+ let(:project) { create(:empty_project) }
+
+ it 'returns false when default_protected_branch is unprotected' do
+ stub_application_setting(default_branch_protection: Gitlab::Access::PROTECTION_NONE)
+
+ expect(project.protected_branch?('master')).to be false
+ end
+
+ it 'returns false when default_protected_branch lets developers push' do
+ stub_application_setting(default_branch_protection: Gitlab::Access::PROTECTION_DEV_CAN_PUSH)
+
+ expect(project.protected_branch?('master')).to be false
+ end
+
+ it 'returns true when default_branch_protection does not let developers push but let developer merge branches' do
+ stub_application_setting(default_branch_protection: Gitlab::Access::PROTECTION_DEV_CAN_MERGE)
+
+ expect(project.protected_branch?('master')).to be true
+ end
+
+ it 'returns true when default_branch_protection is in full protection' do
+ stub_application_setting(default_branch_protection: Gitlab::Access::PROTECTION_FULL)
+
+ expect(project.protected_branch?('master')).to be true
+ end
+ end
+ end
+
+ describe '#user_can_push_to_empty_repo?' do
let(:project) { create(:empty_project) }
+ let(:user) { create(:user) }
- it 'returns true when the branch matches a protected branch via direct match' do
- project.protected_branches.create!(name: 'foo')
+ it 'returns false when default_branch_protection is in full protection and user is developer' do
+ project.team << [user, :developer]
+ stub_application_setting(default_branch_protection: Gitlab::Access::PROTECTION_FULL)
- expect(project.protected_branch?('foo')).to eq(true)
+ expect(project.user_can_push_to_empty_repo?(user)).to be_falsey
end
- it 'returns true when the branch matches a protected branch via wildcard match' do
- project.protected_branches.create!(name: 'production/*')
+ it 'returns false when default_branch_protection only lets devs merge and user is dev' do
+ project.team << [user, :developer]
+ stub_application_setting(default_branch_protection: Gitlab::Access::PROTECTION_DEV_CAN_MERGE)
- expect(project.protected_branch?('production/some-branch')).to eq(true)
+ expect(project.user_can_push_to_empty_repo?(user)).to be_falsey
end
- it 'returns false when the branch does not match a protected branch via direct match' do
- expect(project.protected_branch?('foo')).to eq(false)
+ it 'returns true when default_branch_protection lets devs push and user is developer' do
+ project.team << [user, :developer]
+ stub_application_setting(default_branch_protection: Gitlab::Access::PROTECTION_DEV_CAN_PUSH)
+
+ expect(project.user_can_push_to_empty_repo?(user)).to be_truthy
+ end
+
+ it 'returns true when default_branch_protection is unprotected and user is developer' do
+ project.team << [user, :developer]
+ stub_application_setting(default_branch_protection: Gitlab::Access::PROTECTION_NONE)
+
+ expect(project.user_can_push_to_empty_repo?(user)).to be_truthy
end
- it 'returns false when the branch does not match a protected branch via wildcard match' do
- project.protected_branches.create!(name: 'production/*')
+ it 'returns true when user is master' do
+ project.team << [user, :master]
- expect(project.protected_branch?('staging/some-branch')).to eq(false)
+ expect(project.user_can_push_to_empty_repo?(user)).to be_truthy
end
end