summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinus G Thiel <linus@yesbabyyes.se>2016-10-25 13:14:16 +0200
committerLinus G Thiel <linus@yesbabyyes.se>2016-10-25 13:14:16 +0200
commitcc8ebae527b270ea65d408fa60c2e5c78db5a155 (patch)
tree8c40399b819956a32b916e538bcec94b3f04056d
parent1ff140ea386d856c526b4797f38b4937e9b26f80 (diff)
parentba27abec815e476481b976175c67273c1cbb6e57 (diff)
downloadgitlab-ce-cc8ebae527b270ea65d408fa60c2e5c78db5a155.tar.gz
Merge branch 'master' into 22743-issue-tooltip-will-not-disappear-going-back-to-previous-page
-rw-r--r--.rubocop.yml2
-rw-r--r--.scss-lint.yml6
-rw-r--r--CHANGELOG.md30
-rw-r--r--app/assets/javascripts/application.js5
-rw-r--r--app/assets/javascripts/build.js2
-rw-r--r--app/assets/javascripts/cycle_analytics.js.es66
-rw-r--r--app/assets/javascripts/dispatcher.js.es63
-rw-r--r--app/assets/javascripts/gl_field_errors.js.es67
-rw-r--r--app/assets/javascripts/members.js.es61
-rw-r--r--app/assets/javascripts/merge_request_tabs.js22
-rw-r--r--app/assets/stylesheets/framework/animations.scss4
-rw-r--r--app/assets/stylesheets/framework/common.scss2
-rw-r--r--app/assets/stylesheets/framework/dropdowns.scss2
-rw-r--r--app/assets/stylesheets/framework/logo.scss2
-rw-r--r--app/assets/stylesheets/framework/modal.scss2
-rw-r--r--app/assets/stylesheets/framework/sidebar.scss8
-rw-r--r--app/assets/stylesheets/framework/tw_bootstrap_variables.scss122
-rw-r--r--app/assets/stylesheets/framework/variables.scss44
-rw-r--r--app/assets/stylesheets/mailers/devise.scss10
-rw-r--r--app/assets/stylesheets/pages/cycle_analytics.scss18
-rw-r--r--app/assets/stylesheets/pages/diff.scss4
-rw-r--r--app/assets/stylesheets/pages/environments.scss8
-rw-r--r--app/assets/stylesheets/pages/events.scss2
-rw-r--r--app/assets/stylesheets/pages/login.scss41
-rw-r--r--app/assets/stylesheets/pages/merge_conflicts.scss2
-rw-r--r--app/assets/stylesheets/pages/merge_requests.scss13
-rw-r--r--app/assets/stylesheets/pages/note_form.scss2
-rw-r--r--app/assets/stylesheets/pages/notes.scss2
-rw-r--r--app/assets/stylesheets/pages/projects.scss16
-rw-r--r--app/assets/stylesheets/pages/status.scss2
-rw-r--r--app/assets/stylesheets/pages/tree.scss4
-rw-r--r--app/controllers/groups/group_members_controller.rb4
-rw-r--r--app/controllers/import/gitlab_projects_controller.rb4
-rw-r--r--app/controllers/projects/project_members_controller.rb6
-rw-r--r--app/finders/labels_finder.rb8
-rw-r--r--app/models/ci/build.rb4
-rw-r--r--app/models/ci/pipeline.rb6
-rw-r--r--app/models/ci/runner.rb6
-rw-r--r--app/models/ci/runner_project.rb4
-rw-r--r--app/models/ci/trigger.rb4
-rw-r--r--app/models/ci/trigger_request.rb6
-rw-r--r--app/models/ci/variable.rb2
-rw-r--r--app/models/commit_status.rb2
-rw-r--r--app/models/concerns/protected_branch_access.rb5
-rw-r--r--app/models/email.rb6
-rw-r--r--app/models/group.rb2
-rw-r--r--app/models/issue.rb8
-rw-r--r--app/models/members/group_member.rb2
-rw-r--r--app/models/members/project_member.rb2
-rw-r--r--app/models/merge_request.rb4
-rw-r--r--app/models/merge_request_diff.rb8
-rw-r--r--app/models/project.rb8
-rw-r--r--app/models/repository.rb27
-rw-r--r--app/models/user.rb10
-rw-r--r--app/services/merge_requests/assign_issues_service.rb2
-rw-r--r--app/services/notes/create_service.rb6
-rw-r--r--app/services/protected_branches/api_create_service.rb29
-rw-r--r--app/services/protected_branches/api_update_service.rb47
-rw-r--r--app/views/admin/runners/index.html.haml2
-rw-r--r--app/views/admin/runners/show.html.haml2
-rw-r--r--app/views/devise/sessions/_new_base.html.haml2
-rw-r--r--app/views/devise/sessions/two_factor.html.haml2
-rw-r--r--app/views/devise/shared/_tabs_ldap.html.haml2
-rw-r--r--app/views/devise/shared/_tabs_normal.html.haml5
-rw-r--r--app/views/import/gitlab_projects/new.html.haml4
-rw-r--r--app/views/projects/boards/components/_card.html.haml2
-rw-r--r--app/views/projects/compare/_form.html.haml2
-rw-r--r--app/views/projects/edit.html.haml116
-rw-r--r--app/views/projects/issues/show.html.haml2
-rw-r--r--app/views/projects/merge_requests/_show.html.haml68
-rw-r--r--app/views/projects/merge_requests/show/_commits.html.haml2
-rw-r--r--app/views/projects/milestones/show.html.haml2
-rw-r--r--app/views/projects/new.html.haml5
-rw-r--r--app/views/projects/notes/_note.html.haml2
-rw-r--r--app/views/projects/runners/_specific_runners.html.haml2
-rw-r--r--app/workers/admin_email_worker.rb3
-rw-r--r--app/workers/build_coverage_worker.rb2
-rw-r--r--app/workers/build_email_worker.rb1
-rw-r--r--app/workers/build_finished_worker.rb1
-rw-r--r--app/workers/build_hooks_worker.rb2
-rw-r--r--app/workers/build_success_worker.rb2
-rw-r--r--app/workers/clear_database_cache_worker.rb1
-rw-r--r--app/workers/concerns/build_queue.rb8
-rw-r--r--app/workers/concerns/cronjob_queue.rb9
-rw-r--r--app/workers/concerns/dedicated_sidekiq_queue.rb9
-rw-r--r--app/workers/concerns/pipeline_queue.rb8
-rw-r--r--app/workers/concerns/repository_check_queue.rb8
-rw-r--r--app/workers/delete_user_worker.rb1
-rw-r--r--app/workers/email_receiver_worker.rb3
-rw-r--r--app/workers/emails_on_push_worker.rb2
-rw-r--r--app/workers/expire_build_artifacts_worker.rb1
-rw-r--r--app/workers/expire_build_instance_artifacts_worker.rb1
-rw-r--r--app/workers/git_garbage_collect_worker.rb3
-rw-r--r--app/workers/gitlab_shell_worker.rb3
-rw-r--r--app/workers/group_destroy_worker.rb3
-rw-r--r--app/workers/import_export_project_cleanup_worker.rb3
-rw-r--r--app/workers/irker_worker.rb1
-rw-r--r--app/workers/merge_worker.rb3
-rw-r--r--app/workers/new_note_worker.rb3
-rw-r--r--app/workers/pipeline_hooks_worker.rb2
-rw-r--r--app/workers/pipeline_metrics_worker.rb3
-rw-r--r--app/workers/pipeline_process_worker.rb3
-rw-r--r--app/workers/pipeline_success_worker.rb2
-rw-r--r--app/workers/pipeline_update_worker.rb3
-rw-r--r--app/workers/post_receive.rb3
-rw-r--r--app/workers/project_cache_worker.rb3
-rw-r--r--app/workers/project_destroy_worker.rb3
-rw-r--r--app/workers/project_export_worker.rb3
-rw-r--r--app/workers/project_service_worker.rb3
-rw-r--r--app/workers/project_web_hook_worker.rb3
-rw-r--r--app/workers/prune_old_events_worker.rb1
-rw-r--r--app/workers/remove_expired_group_links_worker.rb1
-rw-r--r--app/workers/remove_expired_members_worker.rb1
-rw-r--r--app/workers/repository_archive_cache_worker.rb3
-rw-r--r--app/workers/repository_check/batch_worker.rb21
-rw-r--r--app/workers/repository_check/clear_worker.rb3
-rw-r--r--app/workers/repository_check/single_repository_worker.rb3
-rw-r--r--app/workers/repository_fork_worker.rb3
-rw-r--r--app/workers/repository_import_worker.rb3
-rw-r--r--app/workers/requests_profiles_worker.rb3
-rw-r--r--app/workers/stuck_ci_builds_worker.rb1
-rw-r--r--app/workers/system_hook_worker.rb3
-rw-r--r--app/workers/trending_projects_worker.rb3
-rw-r--r--app/workers/update_merge_requests_worker.rb1
-rwxr-xr-xbin/background_jobs3
-rw-r--r--config/application.rb3
-rw-r--r--config/mail_room.yml2
-rw-r--r--config/sidekiq_queues.yml47
-rw-r--r--db/migrate/20161017125927_add_unique_index_to_labels.rb4
-rw-r--r--db/migrate/20161018124658_make_project_owners_masters.rb15
-rw-r--r--db/migrate/20161019190736_migrate_sidekiq_queues_from_default.rb109
-rw-r--r--db/migrate/20161024042317_migrate_mailroom_queue_from_default.rb63
-rw-r--r--db/schema.rb4
-rw-r--r--doc/administration/integration/koding.md1
-rw-r--r--doc/api/builds.md8
-rw-r--r--doc/api/commits.md2
-rw-r--r--doc/api/users.md2
-rw-r--r--doc/ci/docker/using_docker_build.md2
-rw-r--r--doc/ci/docker/using_docker_images.md6
-rw-r--r--doc/ci/yaml/README.md160
-rw-r--r--doc/development/README.md3
-rw-r--r--doc/development/performance.md2
-rw-r--r--doc/development/sidekiq_style_guide.md38
-rw-r--r--doc/monitoring/performance/gitlab_configuration.md2
-rw-r--r--doc/monitoring/performance/influxdb_configuration.md2
-rw-r--r--doc/monitoring/performance/influxdb_schema.md2
-rw-r--r--doc/monitoring/performance/introduction.md2
-rw-r--r--doc/project_services/img/builds_emails_service.pngbin33943 -> 30956 bytes
-rw-r--r--doc/raketasks/backup_restore.md4
-rw-r--r--doc/university/glossary/README.md385
-rw-r--r--doc/update/8.11-to-8.12.md2
-rw-r--r--doc/user/markdown.md8
-rw-r--r--doc/user/project/new_ci_build_permissions_model.md6
-rw-r--r--features/support/capybara.rb2
-rw-r--r--lib/api/branches.rb48
-rw-r--r--lib/api/builds.rb162
-rw-r--r--lib/api/commits.rb2
-rw-r--r--lib/api/labels.rb91
-rw-r--r--lib/banzai/filter/relative_link_filter.rb4
-rw-r--r--lib/constraints/namespace_url_constrainer.rb13
-rw-r--r--lib/gitlab/issues_labels.rb2
-rw-r--r--lib/tasks/cache.rake2
-rw-r--r--lib/tasks/gitlab/backup.rake2
-rw-r--r--spec/controllers/groups/group_members_controller_spec.rb43
-rw-r--r--spec/controllers/projects/labels_controller_spec.rb15
-rw-r--r--spec/controllers/projects/project_members_controller_spec.rb84
-rw-r--r--spec/features/login_spec.rb65
-rw-r--r--spec/finders/labels_finder_spec.rb32
-rw-r--r--spec/javascripts/gl_field_errors_spec.js.es64
-rw-r--r--spec/lib/banzai/filter/relative_link_filter_spec.rb40
-rw-r--r--spec/lib/constraints/namespace_url_constrainer_spec.rb10
-rw-r--r--spec/mailers/emails/builds_spec.rb1
-rw-r--r--spec/mailers/emails/merge_requests_spec.rb1
-rw-r--r--spec/mailers/emails/profile_spec.rb1
-rw-r--r--spec/mailers/notify_spec.rb1
-rw-r--r--spec/models/email_spec.rb5
-rw-r--r--spec/models/issue_spec.rb8
-rw-r--r--spec/models/members/project_member_spec.rb2
-rw-r--r--spec/models/merge_request_spec.rb7
-rw-r--r--spec/models/repository_spec.rb21
-rw-r--r--spec/models/user_spec.rb4
-rw-r--r--spec/requests/api/branches_spec.rb190
-rw-r--r--spec/requests/api/commits_spec.rb11
-rw-r--r--spec/requests/api/labels_spec.rb6
-rw-r--r--spec/requests/api/members_spec.rb11
-rw-r--r--spec/requests/api/notes_spec.rb15
-rw-r--r--spec/requests/api/users_spec.rb6
-rw-r--r--spec/services/issues/move_service_spec.rb43
-rw-r--r--spec/services/merge_requests/assign_issues_service_spec.rb12
-rw-r--r--spec/support/notify_shared_examples.rb (renamed from spec/mailers/shared/notify.rb)0
-rw-r--r--spec/support/select2_helper.rb4
-rw-r--r--spec/tasks/gitlab/backup_rake_spec.rb1
-rw-r--r--spec/views/projects/merge_requests/_commits.html.haml_spec.rb38
-rw-r--r--spec/workers/concerns/build_queue_spec.rb14
-rw-r--r--spec/workers/concerns/cronjob_queue_spec.rb18
-rw-r--r--spec/workers/concerns/dedicated_sidekiq_queue_spec.rb20
-rw-r--r--spec/workers/concerns/pipeline_queue_spec.rb14
-rw-r--r--spec/workers/concerns/repository_check_queue_spec.rb18
-rw-r--r--spec/workers/every_sidekiq_worker_spec.rb44
199 files changed, 2162 insertions, 862 deletions
diff --git a/.rubocop.yml b/.rubocop.yml
index bec2464c740..13df3f99613 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -5,7 +5,7 @@ require:
inherit_from: .rubocop_todo.yml
AllCops:
- TargetRubyVersion: 2.3
+ TargetRubyVersion: 2.1
# Cop names are not d§splayed in offense messages by default. Change behavior
# by overriding DisplayCopNames, or by giving the -D/--display-cop-names
# option.
diff --git a/.scss-lint.yml b/.scss-lint.yml
index 5093702519b..9bdf438d995 100644
--- a/.scss-lint.yml
+++ b/.scss-lint.yml
@@ -191,7 +191,7 @@ linters:
# Variables should be formatted with a single space separating the colon
# from the variable's value.
SpaceAfterVariableColon:
- enabled: false
+ enabled: true
# Variables should be formatted with no space between the name and the
# colon.
@@ -201,7 +201,7 @@ linters:
# Operators should be formatted with a single space on both sides of an
# infix operator.
SpaceAroundOperator:
- enabled: false
+ enabled: true
# Opening braces should be preceded by a single space.
SpaceBeforeBrace:
@@ -223,7 +223,7 @@ linters:
# Reports lines containing trailing whitespace.
TrailingWhitespace:
- enabled: false
+ enabled: true
# Don't write trailing zeros for numeric values with a decimal point.
TrailingZero:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 646426a437c..57e4491ac76 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,12 +2,35 @@ Please view this file on the master branch, on stable branches it's out of date.
## 8.14.0 (2016-11-22)
- Adds user project membership expired event to clarify why user was removed (Callum Dryden)
+ - Trim leading and trailing whitespace on project_path (Linus Thiel)
+ - Prevent award emoji via notes for issues/MRs authored by user (barthc)
+ - Adds an optional path parameter to the Commits API to filter commits by path (Luis HGO)
+ - Fix extra space on Build sidebar on Firefox !7060
- Fix HipChat notifications rendering (airatshigapov, eisnerd)
+ - Add hover to trash icon in notes !7008 (blackst0ne)
+ - Escape ref and path for relative links !6050 (winniehell)
- Simpler arguments passed to named_route on toggle_award_url helper method
+ - Fix: Backup restore doesn't clear cache
+ - Use MergeRequestsClosingIssues cache data on Issue#closed_by_merge_requests method
+ - Fix Sign in page 'Forgot your password?' link overlaps on medium-large screens
+ - Fix documents and comments on Build API `scope`
+ - Refactor email, use setter method instead AR callbacks for email attribute (Semyon Pupkov)
+
+## 8.13.1 (unreleased)
+ - Fix bug where labels would be assigned to issues that were moved
+ - Fix error in generating labels
+ - Fix reply-by-email not working due to queue name mismatch
+ - Fixed hidden pipeline graph on commit and MR page !6895
+ - Expire and build repository cache after project import
+ - Fix 404 for group pages when GitLab setup uses relative url
+ - Simpler arguments passed to named_route on toggle_award_url helper method
+ - Better handle when no users were selected for adding to group or project. (Linus Thiel)
+ - Only show register tab if signup enabled.
## 8.13.0 (2016-10-22)
-
+ - Removes extra line for empty issue description. (!7045)
- Fix save button on project pipeline settings page. (!6955)
+ - All Sidekiq workers now use their own queue
- Avoid race condition when asynchronously removing expired artifacts. (!6881)
- Improve Merge When Build Succeeds triggers and execute on pipeline success. (!6675)
- Respond with 404 Not Found for non-existent tags (Linus Thiel)
@@ -26,6 +49,7 @@ Please view this file on the master branch, on stable branches it's out of date.
- Update duration at the end of pipeline
- ExpireBuildArtifactsWorker query builds table without ordering enqueuing one job per build to cleanup
- Add group level labels. (!6425)
+ - Fix Cycle analytics not showing correct data when filtering by date. !6906
- Add an example for testing a phoenix application with Gitlab CI in the docs (Manthan Mallikarjun)
- Cancelled pipelines could be retried. !6927
- Updating verbiage on git basics to be more intuitive
@@ -33,6 +57,7 @@ Please view this file on the master branch, on stable branches it's out of date.
- Clarify documentation for Runners API (Gennady Trafimenkov)
- The instrumentation for Banzai::Renderer has been restored
- Change user & group landing page routing from /u/:username to /:username
+ - Fixed issue boards user link when in subdirectory
- Added documentation for .gitattributes files
- Move Pipeline Metrics to separate worker
- AbstractReferenceFilter caches project_refs on RequestStore when active
@@ -83,6 +108,7 @@ Please view this file on the master branch, on stable branches it's out of date.
- Add visibility level to project repository
- Fix robots.txt disallowing access to groups starting with "s" (Matt Harrison)
- Close open merge request without source project (Katarzyna Kobierska Ula Budziszewska)
+ - Fix showing commits from source project for merge request !6658
- Fix that manual jobs would no longer block jobs in the next stage. !6604
- Add configurable email subject suffix (Fu Xu)
- Use defined colour for a language when available !6748 (nilsding)
@@ -143,6 +169,7 @@ Please view this file on the master branch, on stable branches it's out of date.
- Fix buggy iOS tooltip layering behavior.
- Make guests unable to view MRs on private projects
- Fix broken Project API docs (Takuya Noguchi)
+ - Migrate invalid project members (owner -> master)
## 8.12.7
@@ -376,6 +403,7 @@ Please view this file on the master branch, on stable branches it's out of date.
- Fix inconsistent checkbox alignment (ClemMakesApps)
- Use the default branch for displaying the project icon instead of master !5792 (Hannes Rosenögger)
- Adds response mime type to transaction metric action when it's not HTML
+ - Fix branch protection API !6215
- Fix hover leading space bug in pipeline graph !5980
- Avoid conflict with admin labels when importing GitHub labels
- User can edit closed MR with deleted fork (Katarzyna Kobierska Ula Budziszewska) !5496
diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js
index b966a568bbd..c6c3c82e1ee 100644
--- a/app/assets/javascripts/application.js
+++ b/app/assets/javascripts/application.js
@@ -83,14 +83,15 @@
};
// Disable button if text field is empty
- window.disableButtonIfEmptyField = function(field_selector, button_selector) {
+ 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('input', function() {
+ return field.on(event_name, function() {
if (rstrip($(this).val()) === "") {
return closest_submit.disable();
} else {
diff --git a/app/assets/javascripts/build.js b/app/assets/javascripts/build.js
index 97462a5959c..f4c387a1a05 100644
--- a/app/assets/javascripts/build.js
+++ b/app/assets/javascripts/build.js
@@ -148,7 +148,7 @@
};
Build.prototype.translateSidebar = function(e) {
- var newPosition = this.sidebarTranslationLimits.max - document.body.scrollTop;
+ var newPosition = this.sidebarTranslationLimits.max - (document.body.scrollTop || document.documentElement.scrollTop);
if (newPosition < this.sidebarTranslationLimits.min) newPosition = this.sidebarTranslationLimits.min;
this.$sidebar.css({
top: newPosition
diff --git a/app/assets/javascripts/cycle_analytics.js.es6 b/app/assets/javascripts/cycle_analytics.js.es6
index bd9accacb8c..20791bab942 100644
--- a/app/assets/javascripts/cycle_analytics.js.es6
+++ b/app/assets/javascripts/cycle_analytics.js.es6
@@ -36,7 +36,11 @@
method: 'GET',
dataType: 'json',
contentType: 'application/json',
- data: { start_date: options.startDate }
+ data: {
+ cycle_analytics: {
+ start_date: options.startDate
+ }
+ }
}).done((data) => {
this.decorateData(data);
this.initDropdown();
diff --git a/app/assets/javascripts/dispatcher.js.es6 b/app/assets/javascripts/dispatcher.js.es6
index afc0d6f8c62..a1fe57562fa 100644
--- a/app/assets/javascripts/dispatcher.js.es6
+++ b/app/assets/javascripts/dispatcher.js.es6
@@ -117,6 +117,9 @@
new ZenMode();
shortcut_handler = new ShortcutsNavigation();
break;
+ case 'projects:commit:builds':
+ new gl.Pipelines();
+ break;
case 'projects:commits:show':
case 'projects:activity':
shortcut_handler = new ShortcutsNavigation();
diff --git a/app/assets/javascripts/gl_field_errors.js.es6 b/app/assets/javascripts/gl_field_errors.js.es6
index 8657e7b4abf..8e8f9f29ab3 100644
--- a/app/assets/javascripts/gl_field_errors.js.es6
+++ b/app/assets/javascripts/gl_field_errors.js.es6
@@ -137,8 +137,11 @@
}
initValidators () {
- // select all non-hidden inputs in form
- this.state.inputs = this.form.find(':input:not([type=hidden])').toArray()
+ // register selectors here as needed
+ const validateSelectors = [':text', ':password', '[type=email]']
+ .map((selector) => `input${selector}`).join(',');
+
+ this.state.inputs = this.form.find(validateSelectors).toArray()
.filter((input) => !input.classList.contains(customValidationFlag))
.map((input) => new GlFieldError({ input, formErrors: this }));
diff --git a/app/assets/javascripts/members.js.es6 b/app/assets/javascripts/members.js.es6
index a0cd20f21e8..2bdd0f7a637 100644
--- a/app/assets/javascripts/members.js.es6
+++ b/app/assets/javascripts/members.js.es6
@@ -10,6 +10,7 @@
$('.project_member, .group_member').off('ajax:success').on('ajax:success', this.removeRow);
$('.js-member-update-control').off('change').on('change', this.formSubmit);
$('.js-edit-member-form').off('ajax:success').on('ajax:success', this.formSuccess);
+ disableButtonIfEmptyField('#user_ids', 'input[name=commit]', 'change');
}
removeRow(e) {
diff --git a/app/assets/javascripts/merge_request_tabs.js b/app/assets/javascripts/merge_request_tabs.js
index fd21aa1fefa..3dde979185b 100644
--- a/app/assets/javascripts/merge_request_tabs.js
+++ b/app/assets/javascripts/merge_request_tabs.js
@@ -282,6 +282,7 @@
document.querySelector("div#builds").innerHTML = data.html;
gl.utils.localTimeAgo($('.js-timeago', 'div#builds'));
_this.buildsLoaded = true;
+ if (!this.pipelines) this.pipelines = new gl.Pipelines();
return _this.scrollToElement("#builds");
};
})(this)
@@ -388,28 +389,25 @@
// So we dont affix the tabs on these
if (Breakpoints.get().getBreakpointSize() === 'xs' || !$tabs.length) return;
- var tabsWidth = $tabs.outerWidth(),
- $diffTabs = $('#diff-notes-app'),
- offsetTop = $tabs.offset().top - ($('.navbar-fixed-top').height() + $('.layout-nav').height());
+ var $diffTabs = $('#diff-notes-app'),
+ $fixedNav = $('.navbar-fixed-top'),
+ $layoutNav = $('.layout-nav');
$tabs.off('affix.bs.affix affix-top.bs.affix')
.affix({
offset: {
- top: offsetTop
+ top: function () {
+ var tabsTop = $diffTabs.offset().top - $tabs.height();
+ tabsTop = tabsTop - ($fixedNav.height() + $layoutNav.height());
+
+ return tabsTop;
+ }
}
}).on('affix.bs.affix', function () {
- $tabs.css({
- left: $tabs.offset().left,
- width: tabsWidth
- });
$diffTabs.css({
marginTop: $tabs.height()
});
}).on('affix-top.bs.affix', function () {
- $tabs.css({
- left: '',
- width: ''
- });
$diffTabs.css({
marginTop: ''
});
diff --git a/app/assets/stylesheets/framework/animations.scss b/app/assets/stylesheets/framework/animations.scss
index 1e9a45c19b8..98d3889cd44 100644
--- a/app/assets/stylesheets/framework/animations.scss
+++ b/app/assets/stylesheets/framework/animations.scss
@@ -1,10 +1,10 @@
// This file is based off animate.css 3.5.1, available here:
// https://github.com/daneden/animate.css/blob/3.5.1/animate.css
-//
+//
// animate.css - http://daneden.me/animate
// Version - 3.5.1
// Licensed under the MIT license - http://opensource.org/licenses/MIT
-//
+//
// Copyright (c) 2016 Daniel Eden
.animated {
diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss
index 81e4e264560..800e2dba018 100644
--- a/app/assets/stylesheets/framework/common.scss
+++ b/app/assets/stylesheets/framework/common.scss
@@ -372,3 +372,5 @@ table {
margin-right: -$gl-padding;
border-top: 1px solid $border-color;
}
+
+.hide-bottom-border { border-bottom: none !important; }
diff --git a/app/assets/stylesheets/framework/dropdowns.scss b/app/assets/stylesheets/framework/dropdowns.scss
index a839371a6f2..224bc58f7a7 100644
--- a/app/assets/stylesheets/framework/dropdowns.scss
+++ b/app/assets/stylesheets/framework/dropdowns.scss
@@ -402,7 +402,7 @@
.dropdown-content {
max-height: 215px;
- overflow-y: scroll;
+ overflow-y: auto;
}
.dropdown-footer {
diff --git a/app/assets/stylesheets/framework/logo.scss b/app/assets/stylesheets/framework/logo.scss
index a90e45bb5f4..429cfbe7235 100644
--- a/app/assets/stylesheets/framework/logo.scss
+++ b/app/assets/stylesheets/framework/logo.scss
@@ -61,7 +61,7 @@
10%, 80% {
fill: $tanuki-red;
}
-
+
20%, 90% {
fill: lighten($tanuki-red, 25%);
}
diff --git a/app/assets/stylesheets/framework/modal.scss b/app/assets/stylesheets/framework/modal.scss
index 8374f30d0b2..8cd49280e1c 100644
--- a/app/assets/stylesheets/framework/modal.scss
+++ b/app/assets/stylesheets/framework/modal.scss
@@ -3,7 +3,7 @@
padding: 15px;
.form-actions {
- margin: -$gl-padding+1;
+ margin: -$gl-padding + 1;
margin-top: 15px;
}
diff --git a/app/assets/stylesheets/framework/sidebar.scss b/app/assets/stylesheets/framework/sidebar.scss
index ec52f326eb9..1d8e64a0e4b 100644
--- a/app/assets/stylesheets/framework/sidebar.scss
+++ b/app/assets/stylesheets/framework/sidebar.scss
@@ -185,6 +185,10 @@ header.header-sidebar-pinned {
@media (min-width: $screen-sm-min) {
padding-right: $sidebar_collapsed_width;
+
+ .merge-request-tabs-holder.affix {
+ right: $sidebar_collapsed_width;
+ }
}
.sidebar-collapsed-icon {
@@ -207,6 +211,10 @@ header.header-sidebar-pinned {
@media (min-width: $screen-md-min) {
padding-right: $gutter_width;
+
+ .merge-request-tabs-holder.affix {
+ right: $gutter_width;
+ }
}
&.with-overlay {
diff --git a/app/assets/stylesheets/framework/tw_bootstrap_variables.scss b/app/assets/stylesheets/framework/tw_bootstrap_variables.scss
index 915aa631ef8..44fe37d3a4a 100644
--- a/app/assets/stylesheets/framework/tw_bootstrap_variables.scss
+++ b/app/assets/stylesheets/framework/tw_bootstrap_variables.scss
@@ -16,21 +16,21 @@
// $gray-light: lighten($gray-base, 46.7%) // #777
// $gray-lighter: lighten($gray-base, 93.5%) // #eee
-$brand-primary: $gl-primary;
-$brand-success: $gl-success;
-$brand-info: $gl-info;
-$brand-warning: $gl-warning;
-$brand-danger: $gl-danger;
+$brand-primary: $gl-primary;
+$brand-success: $gl-success;
+$brand-info: $gl-info;
+$brand-warning: $gl-warning;
+$brand-danger: $gl-danger;
-$border-radius-base: 3px !default;
-$border-radius-large: 3px !default;
-$border-radius-small: 3px !default;
+$border-radius-base: 3px !default;
+$border-radius-large: 3px !default;
+$border-radius-small: 3px !default;
//== Scaffolding
//
-$text-color: $gl-text-color;
-$link-color: $gl-link-color;
+$text-color: $gl-text-color;
+$link-color: $gl-link-color;
//== Typography
@@ -38,112 +38,112 @@ $link-color: $gl-link-color;
//## Font, line-height, and color for body text, headings, and more.
$font-family-sans-serif: $regular_font;
-$font-family-monospace: $monospace_font;
-$font-size-base: $gl-font-size;
+$font-family-monospace: $monospace_font;
+$font-size-base: $gl-font-size;
//== Components
//
//## Define common padding and border radius sizes and more. Values based on 14px text and 1.428 line-height (~20px to start).
-$padding-base-vertical: $gl-vert-padding;
-$padding-base-horizontal: $gl-padding;
-$component-active-color: #fff;
-$component-active-bg: $brand-info;
+$padding-base-vertical: $gl-vert-padding;
+$padding-base-horizontal: $gl-padding;
+$component-active-color: #fff;
+$component-active-bg: $brand-info;
//== Forms
//
//##
-$input-color: $text-color;
-$input-border: $border-color;
-$input-border-focus: $focus-border-color;
-$legend-color: $text-color;
+$input-color: $text-color;
+$input-border: $border-color;
+$input-border-focus: $focus-border-color;
+$legend-color: $text-color;
//== Pagination
//
//##
-$pagination-color: $gl-gray;
-$pagination-bg: #fff;
-$pagination-border: $border-color;
+$pagination-color: $gl-gray;
+$pagination-bg: #fff;
+$pagination-border: $border-color;
-$pagination-hover-color: $gl-gray;
-$pagination-hover-bg: $row-hover;
-$pagination-hover-border: $border-color;
+$pagination-hover-color: $gl-gray;
+$pagination-hover-bg: $row-hover;
+$pagination-hover-border: $border-color;
-$pagination-active-color: $blue-dark;
-$pagination-active-bg: #fff;
-$pagination-active-border: $border-color;
+$pagination-active-color: $blue-dark;
+$pagination-active-bg: #fff;
+$pagination-active-border: $border-color;
-$pagination-disabled-color: #cdcdcd;
-$pagination-disabled-bg: $background-color;
-$pagination-disabled-border: $border-color;
+$pagination-disabled-color: #cdcdcd;
+$pagination-disabled-bg: $background-color;
+$pagination-disabled-border: $border-color;
//== Form states and alerts
//
//## Define colors for form feedback states and, by default, alerts.
-$state-success-text: #fff;
-$state-success-bg: $brand-success;
-$state-success-border: $brand-success;
+$state-success-text: #fff;
+$state-success-bg: $brand-success;
+$state-success-border: $brand-success;
-$state-info-text: #fff;
-$state-info-bg: $brand-info;
-$state-info-border: $brand-info;
+$state-info-text: #fff;
+$state-info-bg: $brand-info;
+$state-info-border: $brand-info;
-$state-warning-text: #fff;
-$state-warning-bg: $brand-warning;
-$state-warning-border: $brand-warning;
+$state-warning-text: #fff;
+$state-warning-bg: $brand-warning;
+$state-warning-border: $brand-warning;
-$state-danger-text: #fff;
-$state-danger-bg: $brand-danger;
-$state-danger-border: $brand-danger;
+$state-danger-text: #fff;
+$state-danger-bg: $brand-danger;
+$state-danger-border: $brand-danger;
//== Alerts
//
//## Define alert colors, border radius, and padding.
-$alert-border-radius: 0;
+$alert-border-radius: 0;
//== Panels
//
//##
-$panel-border-radius: 2px;
-$panel-default-text: $text-color;
-$panel-default-border: $border-color;
+$panel-border-radius: 2px;
+$panel-default-text: $text-color;
+$panel-default-border: $border-color;
$panel-default-heading-bg: $background-color;
-$panel-footer-bg: $background-color;
-$panel-inner-border: $border-color;
+$panel-footer-bg: $background-color;
+$panel-inner-border: $border-color;
//== Wells
//
//##
-$well-bg: $gray-light;
-$well-border: #eee;
+$well-bg: $gray-light;
+$well-border: #eee;
//== Code
//
//##
-$code-color: #c7254e;
-$code-bg: #f9f2f4;
+$code-color: #c7254e;
+$code-bg: #f9f2f4;
-$kbd-color: #fff;
-$kbd-bg: #333;
+$kbd-color: #fff;
+$kbd-bg: #333;
//== Buttons
//
//##
-$btn-default-color: $gl-text-color;
-$btn-default-bg: #fff;
-$btn-default-border: #e7e9ed;
+$btn-default-color: $gl-text-color;
+$btn-default-bg: #fff;
+$btn-default-border: #e7e9ed;
//== Nav
//
@@ -153,8 +153,8 @@ $nav-link-padding: 13px $gl-padding;
//== Code
//
//##
-$pre-bg: $background-color !default;
-$pre-color: $gl-gray !default;
+$pre-bg: $background-color !default;
+$pre-color: $gl-gray !default;
$pre-border-color: $border-color;
$table-bg-accent: $background-color;
diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss
index eafe84570a8..b271f8cf332 100644
--- a/app/assets/stylesheets/framework/variables.scss
+++ b/app/assets/stylesheets/framework/variables.scss
@@ -84,39 +84,39 @@ $warning-message-border: #f0e2bb;
/*
* UI elements
*/
-$border-color: #e5e5e5;
-$focus-border-color: #3aabf0;
-$table-border-color: #f0f0f0;
-$background-color: $gray-light;
+$border-color: #e5e5e5;
+$focus-border-color: #3aabf0;
+$table-border-color: #f0f0f0;
+$background-color: $gray-light;
$dark-background-color: #f5f5f5;
-$table-text-gray: #8f8f8f;
+$table-text-gray: #8f8f8f;
/*
* Text
*/
-$gl-font-size: 15px;
-$gl-title-color: #333;
-$gl-text-color: #5c5c5c;
-$gl-text-color-light: #8c8c8c;
-$gl-text-green: #4a2;
-$gl-text-red: #d12f19;
-$gl-text-orange: #d90;
-$gl-link-color: #3084bb;
-$gl-dark-link-color: #333;
+$gl-font-size: 15px;
+$gl-title-color: #333;
+$gl-text-color: #5c5c5c;
+$gl-text-color-light: #8c8c8c;
+$gl-text-green: #4a2;
+$gl-text-red: #d12f19;
+$gl-text-orange: #d90;
+$gl-link-color: #3084bb;
+$gl-dark-link-color: #333;
$gl-placeholder-color: #8f8f8f;
-$gl-icon-color: $gl-placeholder-color;
-$gl-grayish-blue: #7f8fa4;
-$gl-gray: $gl-text-color;
-$gl-gray-dark: #313236;
-$gl-gray-light: $gl-placeholder-color;
-$gl-header-color: #4c4e54;
+$gl-icon-color: $gl-placeholder-color;
+$gl-grayish-blue: #7f8fa4;
+$gl-gray: $gl-text-color;
+$gl-gray-dark: #313236;
+$gl-gray-light: $gl-placeholder-color;
+$gl-header-color: #4c4e54;
/*
* Lists
*/
-$list-font-size: $gl-font-size;
+$list-font-size: $gl-font-size;
$list-title-color: $gl-title-color;
-$list-text-color: $gl-text-color;
+$list-text-color: $gl-text-color;
$list-text-height: 42px;
/*
diff --git a/app/assets/stylesheets/mailers/devise.scss b/app/assets/stylesheets/mailers/devise.scss
index 9495c5b3f37..b2bce482fde 100644
--- a/app/assets/stylesheets/mailers/devise.scss
+++ b/app/assets/stylesheets/mailers/devise.scss
@@ -5,13 +5,13 @@
// Styles defined here are embedded directly into the resulting email HTML via
// the `premailer` gem.
-$body-background-color: #363636;
+$body-background-color: #363636;
$message-background-color: #fafafa;
-$header-color: #6b4fbb;
-$body-color: #444;
-$cta-color: #e14329;
-$footer-link-color: #7e7e7e;
+$header-color: #6b4fbb;
+$body-color: #444;
+$cta-color: #e14329;
+$footer-link-color: #7e7e7e;
$font-family: Helvetica, Arial, sans-serif;
diff --git a/app/assets/stylesheets/pages/cycle_analytics.scss b/app/assets/stylesheets/pages/cycle_analytics.scss
index d732008de3d..572e1e7d558 100644
--- a/app/assets/stylesheets/pages/cycle_analytics.scss
+++ b/app/assets/stylesheets/pages/cycle_analytics.scss
@@ -9,15 +9,15 @@
padding: 24px 0;
border-bottom: none;
position: relative;
-
+
@media (max-width: $screen-sm-min) {
padding: 6px 0 24px;
- }
+ }
}
.column {
text-align: center;
-
+
@media (max-width: $screen-sm-min) {
padding: 15px 0;
}
@@ -36,7 +36,7 @@
&:last-child {
text-align: right;
-
+
@media (max-width: $screen-sm-min) {
text-align: center;
}
@@ -51,7 +51,7 @@
.bordered-box {
border: 1px solid $border-color;
border-radius: $border-radius-default;
-
+
}
.content-list {
@@ -73,10 +73,10 @@
font-weight: 600;
color: $gl-title-color;
}
-
+
&.text {
color: $layout-link-gray;
-
+
&.value-col {
color: $gl-title-color;
}
@@ -108,13 +108,13 @@
.svg-container {
text-align: center;
-
+
svg {
width: 136px;
height: 136px;
}
}
-
+
.inner-content {
@media (max-width: $screen-sm-min) {
padding: 0 28px;
diff --git a/app/assets/stylesheets/pages/diff.scss b/app/assets/stylesheets/pages/diff.scss
index fe6421f8b3f..f8e3ca29a2b 100644
--- a/app/assets/stylesheets/pages/diff.scss
+++ b/app/assets/stylesheets/pages/diff.scss
@@ -222,12 +222,12 @@
top: 13px;
right: 7px;
}
-
+
.frame {
top: 0;
right: 0;
position: absolute;
-
+
&.deleted {
margin: 0;
display: block;
diff --git a/app/assets/stylesheets/pages/environments.scss b/app/assets/stylesheets/pages/environments.scss
index 12ee0a5dc3d..fc49ff780fc 100644
--- a/app/assets/stylesheets/pages/environments.scss
+++ b/app/assets/stylesheets/pages/environments.scss
@@ -37,10 +37,10 @@
.branch-name {
color: $gl-dark-link-color;
}
-
+
.stop-env-link {
color: $table-text-gray;
-
+
.stop-env-icon {
font-size: 14px;
}
@@ -48,11 +48,11 @@
.deployment {
.build-column {
-
+
.build-link {
color: $gl-dark-link-color;
}
-
+
.avatar {
float: none;
}
diff --git a/app/assets/stylesheets/pages/events.scss b/app/assets/stylesheets/pages/events.scss
index 5d9a76dac05..3004959ff7b 100644
--- a/app/assets/stylesheets/pages/events.scss
+++ b/app/assets/stylesheets/pages/events.scss
@@ -142,7 +142,7 @@
.event-last-push {
overflow: auto;
width: 100%;
-
+
.event-last-push-text {
@include str-truncated(100%);
padding: 4px 0;
diff --git a/app/assets/stylesheets/pages/login.scss b/app/assets/stylesheets/pages/login.scss
index bdb13bee178..2be9453aaee 100644
--- a/app/assets/stylesheets/pages/login.scss
+++ b/app/assets/stylesheets/pages/login.scss
@@ -143,6 +143,7 @@
&:not(.active) {
background-color: $gray-light;
+ border-left: 1px solid $border-color;
}
a {
@@ -170,6 +171,31 @@
}
}
+ // Ldap configurations may need more tabs & the tab labels are user generated (arbitrarily long).
+ // These styles prevent this from breaking the layout, and only applied when providers are configured.
+
+ .new-session-tabs.custom-provider-tabs {
+ flex-wrap: wrap;
+
+ li {
+ min-width: 85px;
+ flex-basis: auto;
+
+ // This styles tab elements that have wrapped to a second line. We cannot easily predict when this will happen.
+ // We are making somewhat of an assumption about the configuration here: that users do not have more than
+ // 3 LDAP servers configured (in addition to standard login) and they are not using especially long names for any
+ // of them. If either condition is false, this will work as expected. If both are true, there may be a missing border
+ // above one of the bottom row elements. If you know a better way, please implement it!
+ &:nth-child(n+5) {
+ border-top: 1px solid $border-color;
+ }
+ }
+
+ a {
+ font-size: 16px;
+ }
+ }
+
.form-control {
&:active, &:focus {
@@ -203,6 +229,7 @@
.login-page {
.col-sm-5.pull-right {
float: none !important;
+ margin-bottom: 45px;
}
}
}
@@ -244,7 +271,11 @@
}
.navless-container {
- padding: 65px; // height of footer + bottom padding of email confirmation link
+ padding: 65px 15px; // height of footer + bottom padding of email confirmation link
+
+ @media (max-width: $screen-xs-max) {
+ padding: 0 15px 65px;
+ }
}
}
@@ -255,6 +286,13 @@
.new_user {
position: relative;
padding-bottom: 35px;
+
+ @media (min-width: $screen-sm-min) and (max-width: $screen-sm-max) {
+ .forgot-password {
+ float: none !important;
+ margin-top: 5px;
+ }
+ }
}
.move-submit-down {
@@ -263,3 +301,4 @@
bottom: 0;
}
}
+
diff --git a/app/assets/stylesheets/pages/merge_conflicts.scss b/app/assets/stylesheets/pages/merge_conflicts.scss
index eed2b0ab7cc..2e917361b25 100644
--- a/app/assets/stylesheets/pages/merge_conflicts.scss
+++ b/app/assets/stylesheets/pages/merge_conflicts.scss
@@ -254,7 +254,7 @@ $colors: (
border-top: solid 2px $border-green-extra-light;
}
}
-
+
.editor {
pre {
height: 350px;
diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss
index 35a1877df95..70afa568554 100644
--- a/app/assets/stylesheets/pages/merge_requests.scss
+++ b/app/assets/stylesheets/pages/merge_requests.scss
@@ -183,11 +183,11 @@
.ci-coverage {
float: right;
}
-
+
.stop-env-container {
color: $gl-text-color;
float: right;
-
+
a {
color: $gl-text-color;
}
@@ -438,11 +438,18 @@
}
}
-.merge-request-tabs {
+.merge-request-tabs-holder {
background-color: #fff;
&.affix {
top: 100px;
+ left: 0;
z-index: 9;
+ transition: right .15s;
+ }
+
+ &:not(.affix) .container-fluid {
+ padding-left: 0;
+ padding-right: 0;
}
}
diff --git a/app/assets/stylesheets/pages/note_form.scss b/app/assets/stylesheets/pages/note_form.scss
index 17f28959414..25c1bbdc1c9 100644
--- a/app/assets/stylesheets/pages/note_form.scss
+++ b/app/assets/stylesheets/pages/note_form.scss
@@ -8,7 +8,7 @@
.diff-file .diff-content {
tr.line_holder:hover > td .line_note_link {
opacity: 1.0;
- filter: alpha(opacity=100);
+ filter: alpha(opacity = 100);
}
}
diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss
index fffcdc812a7..faa0fc82ca8 100644
--- a/app/assets/stylesheets/pages/notes.scss
+++ b/app/assets/stylesheets/pages/notes.scss
@@ -458,7 +458,7 @@ ul.notes {
.discussion-next-btn {
svg {
margin: 0;
-
+
path {
fill: $gray-darkest;
}
diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss
index 1062d7effb0..fe7cf3c87e3 100644
--- a/app/assets/stylesheets/pages/projects.scss
+++ b/app/assets/stylesheets/pages/projects.scss
@@ -13,9 +13,18 @@
.new_project,
.edit-project {
+
fieldset {
- &.features .control-label {
- font-weight: normal;
+
+ &.features {
+
+ .label-light {
+ margin-bottom: 0;
+ }
+
+ .help-block {
+ margin-top: 0;
+ }
}
.form-group {
@@ -40,6 +49,7 @@
}
.input-group > div {
+
&:last-child {
padding-right: 0;
}
@@ -47,6 +57,7 @@
@media (max-width: $screen-xs-max) {
.input-group > div {
+
margin-bottom: 14px;
&:last-child {
@@ -60,6 +71,7 @@
}
.input-group-addon {
+
&.static-namespace {
height: 35px;
border-radius: 3px;
diff --git a/app/assets/stylesheets/pages/status.scss b/app/assets/stylesheets/pages/status.scss
index f1d53c7b8bc..01426e28e92 100644
--- a/app/assets/stylesheets/pages/status.scss
+++ b/app/assets/stylesheets/pages/status.scss
@@ -74,7 +74,7 @@
.ci-status-icon-success_with_warning {
color: $gl-warning;
}
-
+
.ci-status-icon-running {
color: $blue-normal;
}
diff --git a/app/assets/stylesheets/pages/tree.scss b/app/assets/stylesheets/pages/tree.scss
index 6ea7a2b5498..84dcd6835d5 100644
--- a/app/assets/stylesheets/pages/tree.scss
+++ b/app/assets/stylesheets/pages/tree.scss
@@ -29,11 +29,11 @@
.last-commit {
@include str-truncated(506px);
-
+
@media (min-width: $screen-sm-max) and (max-width: $screen-md-max) {
@include str-truncated(450px);
}
-
+
}
.commit-history-link-spacer {
diff --git a/app/controllers/groups/group_members_controller.rb b/app/controllers/groups/group_members_controller.rb
index 18cd800c619..940a3ad20ba 100644
--- a/app/controllers/groups/group_members_controller.rb
+++ b/app/controllers/groups/group_members_controller.rb
@@ -21,6 +21,10 @@ class Groups::GroupMembersController < Groups::ApplicationController
end
def create
+ if params[:user_ids].blank?
+ return redirect_to(group_group_members_path(@group), alert: 'No users specified.')
+ end
+
@group.add_users(
params[:user_ids].split(','),
params[:access_level],
diff --git a/app/controllers/import/gitlab_projects_controller.rb b/app/controllers/import/gitlab_projects_controller.rb
index 3ec173abcdb..36d246d185b 100644
--- a/app/controllers/import/gitlab_projects_controller.rb
+++ b/app/controllers/import/gitlab_projects_controller.rb
@@ -2,8 +2,8 @@ class Import::GitlabProjectsController < Import::BaseController
before_action :verify_gitlab_project_import_enabled
def new
- @namespace_id = project_params[:namespace_id]
- @namespace_name = Namespace.find(project_params[:namespace_id]).name
+ @namespace = Namespace.find(project_params[:namespace_id])
+ return render_404 unless current_user.can?(:create_projects, @namespace)
@path = project_params[:path]
end
diff --git a/app/controllers/projects/project_members_controller.rb b/app/controllers/projects/project_members_controller.rb
index 2a07d154853..d08f490de18 100644
--- a/app/controllers/projects/project_members_controller.rb
+++ b/app/controllers/projects/project_members_controller.rb
@@ -25,6 +25,10 @@ class Projects::ProjectMembersController < Projects::ApplicationController
end
def create
+ if params[:user_ids].blank?
+ return redirect_to(namespace_project_project_members_path(@project.namespace, @project), alert: 'No users or groups specified.')
+ end
+
@project.team.add_users(
params[:user_ids].split(','),
params[:access_level],
@@ -32,7 +36,7 @@ class Projects::ProjectMembersController < Projects::ApplicationController
current_user: current_user
)
- redirect_to namespace_project_project_members_path(@project.namespace, @project)
+ redirect_to namespace_project_project_members_path(@project.namespace, @project), notice: 'Users were successfully added.'
end
def update
diff --git a/app/finders/labels_finder.rb b/app/finders/labels_finder.rb
index 6ace14a4bb5..95e62cdb02a 100644
--- a/app/finders/labels_finder.rb
+++ b/app/finders/labels_finder.rb
@@ -35,8 +35,10 @@ class LabelsFinder < UnionFinder
end
def with_title(items)
- items = items.where(title: title) if title
- items
+ return items if title.nil?
+ return items.none if title.blank?
+
+ items.where(title: title)
end
def group_id
@@ -52,7 +54,7 @@ class LabelsFinder < UnionFinder
end
def title
- params[:title].presence || params[:name].presence
+ params[:title] || params[:name]
end
def project
diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb
index a6b606d13de..bf5f92f8462 100644
--- a/app/models/ci/build.rb
+++ b/app/models/ci/build.rb
@@ -3,8 +3,8 @@ module Ci
include TokenAuthenticatable
include AfterCommitQueue
- belongs_to :runner, class_name: 'Ci::Runner'
- belongs_to :trigger_request, class_name: 'Ci::TriggerRequest'
+ belongs_to :runner
+ belongs_to :trigger_request
belongs_to :erased_by, class_name: 'User'
serialize :options
diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb
index d5c1e03b461..adda3b8f40c 100644
--- a/app/models/ci/pipeline.rb
+++ b/app/models/ci/pipeline.rb
@@ -7,12 +7,12 @@ module Ci
self.table_name = 'ci_commits'
- belongs_to :project, class_name: '::Project', foreign_key: :gl_project_id
+ belongs_to :project, foreign_key: :gl_project_id
belongs_to :user
has_many :statuses, class_name: 'CommitStatus', foreign_key: :commit_id
- has_many :builds, class_name: 'Ci::Build', foreign_key: :commit_id
- has_many :trigger_requests, dependent: :destroy, class_name: 'Ci::TriggerRequest', foreign_key: :commit_id
+ has_many :builds, foreign_key: :commit_id
+ has_many :trigger_requests, dependent: :destroy, foreign_key: :commit_id
validates_presence_of :sha, unless: :importing?
validates_presence_of :ref, unless: :importing?
diff --git a/app/models/ci/runner.rb b/app/models/ci/runner.rb
index 44cb19ece3b..123930273e0 100644
--- a/app/models/ci/runner.rb
+++ b/app/models/ci/runner.rb
@@ -6,9 +6,9 @@ module Ci
AVAILABLE_SCOPES = %w[specific shared active paused online]
FORM_EDITABLE = %i[description tag_list active run_untagged locked]
- has_many :builds, class_name: 'Ci::Build'
- has_many :runner_projects, dependent: :destroy, class_name: 'Ci::RunnerProject'
- has_many :projects, through: :runner_projects, class_name: '::Project', foreign_key: :gl_project_id
+ has_many :builds
+ has_many :runner_projects, dependent: :destroy
+ has_many :projects, through: :runner_projects, foreign_key: :gl_project_id
has_one :last_build, ->() { order('id DESC') }, class_name: 'Ci::Build'
diff --git a/app/models/ci/runner_project.rb b/app/models/ci/runner_project.rb
index 4b44ffa886e..1f9baeca5b1 100644
--- a/app/models/ci/runner_project.rb
+++ b/app/models/ci/runner_project.rb
@@ -2,8 +2,8 @@ module Ci
class RunnerProject < ActiveRecord::Base
extend Ci::Model
- belongs_to :runner, class_name: 'Ci::Runner'
- belongs_to :project, class_name: '::Project', foreign_key: :gl_project_id
+ belongs_to :runner
+ belongs_to :project, foreign_key: :gl_project_id
validates_uniqueness_of :runner_id, scope: :gl_project_id
end
diff --git a/app/models/ci/trigger.rb b/app/models/ci/trigger.rb
index a0b19b51a12..62889fe80d8 100644
--- a/app/models/ci/trigger.rb
+++ b/app/models/ci/trigger.rb
@@ -4,8 +4,8 @@ module Ci
acts_as_paranoid
- belongs_to :project, class_name: '::Project', foreign_key: :gl_project_id
- has_many :trigger_requests, dependent: :destroy, class_name: 'Ci::TriggerRequest'
+ belongs_to :project, foreign_key: :gl_project_id
+ has_many :trigger_requests, dependent: :destroy
validates_presence_of :token
validates_uniqueness_of :token
diff --git a/app/models/ci/trigger_request.rb b/app/models/ci/trigger_request.rb
index fc674871743..2b807731d0d 100644
--- a/app/models/ci/trigger_request.rb
+++ b/app/models/ci/trigger_request.rb
@@ -2,9 +2,9 @@ module Ci
class TriggerRequest < ActiveRecord::Base
extend Ci::Model
- belongs_to :trigger, class_name: 'Ci::Trigger'
- belongs_to :pipeline, class_name: 'Ci::Pipeline', foreign_key: :commit_id
- has_many :builds, class_name: 'Ci::Build'
+ belongs_to :trigger
+ belongs_to :pipeline, foreign_key: :commit_id
+ has_many :builds
serialize :variables
diff --git a/app/models/ci/variable.rb b/app/models/ci/variable.rb
index 6959223aed9..94d9e2b3208 100644
--- a/app/models/ci/variable.rb
+++ b/app/models/ci/variable.rb
@@ -2,7 +2,7 @@ module Ci
class Variable < ActiveRecord::Base
extend Ci::Model
- belongs_to :project, class_name: '::Project', foreign_key: :gl_project_id
+ belongs_to :project, foreign_key: :gl_project_id
validates_uniqueness_of :key, scope: :gl_project_id
validates :key,
diff --git a/app/models/commit_status.rb b/app/models/commit_status.rb
index 7b554be4f9a..4cb3a69416e 100644
--- a/app/models/commit_status.rb
+++ b/app/models/commit_status.rb
@@ -5,7 +5,7 @@ class CommitStatus < ActiveRecord::Base
self.table_name = 'ci_builds'
- belongs_to :project, class_name: '::Project', foreign_key: :gl_project_id
+ belongs_to :project, foreign_key: :gl_project_id
belongs_to :pipeline, class_name: 'Ci::Pipeline', foreign_key: :commit_id
belongs_to :user
diff --git a/app/models/concerns/protected_branch_access.rb b/app/models/concerns/protected_branch_access.rb
index 5a7b36070e7..7fd0905ee81 100644
--- a/app/models/concerns/protected_branch_access.rb
+++ b/app/models/concerns/protected_branch_access.rb
@@ -1,6 +1,11 @@
module ProtectedBranchAccess
extend ActiveSupport::Concern
+ included do
+ scope :master, -> { where(access_level: Gitlab::Access::MASTER) }
+ scope :developer, -> { where(access_level: Gitlab::Access::DEVELOPER) }
+ end
+
def humanize
self.class.human_access_levels[self.access_level]
end
diff --git a/app/models/email.rb b/app/models/email.rb
index 32a412ab878..826d4f16edb 100644
--- a/app/models/email.rb
+++ b/app/models/email.rb
@@ -7,10 +7,8 @@ class Email < ActiveRecord::Base
validates :email, presence: true, uniqueness: true, email: true
validate :unique_email, if: ->(email) { email.email_changed? }
- before_validation :cleanup_email
-
- def cleanup_email
- self.email = self.email.downcase.strip
+ def email=(value)
+ write_attribute(:email, value.downcase.strip)
end
def unique_email
diff --git a/app/models/group.rb b/app/models/group.rb
index 00a595d2705..552e1154df6 100644
--- a/app/models/group.rb
+++ b/app/models/group.rb
@@ -6,7 +6,7 @@ class Group < Namespace
include AccessRequestable
include Referable
- has_many :group_members, -> { where(requested_at: nil) }, dependent: :destroy, as: :source, class_name: 'GroupMember'
+ has_many :group_members, -> { where(requested_at: nil) }, dependent: :destroy, as: :source
alias_method :members, :group_members
has_many :users, through: :group_members
has_many :owners,
diff --git a/app/models/issue.rb b/app/models/issue.rb
index 133a5993815..89158a50353 100644
--- a/app/models/issue.rb
+++ b/app/models/issue.rb
@@ -211,7 +211,13 @@ class Issue < ActiveRecord::Base
note.all_references(current_user, extractor: ext)
end
- ext.merge_requests.select { |mr| mr.open? && mr.closes_issue?(self) }
+ merge_requests = ext.merge_requests.select(&:open?)
+ if merge_requests.any?
+ ids = MergeRequestsClosingIssues.where(merge_request_id: merge_requests.map(&:id), issue_id: id).pluck(:merge_request_id)
+ merge_requests.select { |mr| mr.id.in?(ids) }
+ else
+ []
+ end
end
def moved?
diff --git a/app/models/members/group_member.rb b/app/models/members/group_member.rb
index 1b54a85d064..204f34f0269 100644
--- a/app/models/members/group_member.rb
+++ b/app/models/members/group_member.rb
@@ -1,7 +1,7 @@
class GroupMember < Member
SOURCE_TYPE = 'Namespace'
- belongs_to :group, class_name: 'Group', foreign_key: 'source_id'
+ belongs_to :group, foreign_key: 'source_id'
# Make sure group member points only to group as it source
default_value_for :source_type, SOURCE_TYPE
diff --git a/app/models/members/project_member.rb b/app/models/members/project_member.rb
index e4880973117..008fff0857c 100644
--- a/app/models/members/project_member.rb
+++ b/app/models/members/project_member.rb
@@ -3,7 +3,7 @@ class ProjectMember < Member
include Gitlab::ShellAdapter
- belongs_to :project, class_name: 'Project', foreign_key: 'source_id'
+ belongs_to :project, foreign_key: 'source_id'
# Make sure project member points only to project as it source
default_value_for :source_type, SOURCE_TYPE
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index c476a3bb14e..4872f8b8649 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -6,8 +6,8 @@ class MergeRequest < ActiveRecord::Base
include Taskable
include Importable
- belongs_to :target_project, foreign_key: :target_project_id, class_name: "Project"
- belongs_to :source_project, foreign_key: :source_project_id, class_name: "Project"
+ belongs_to :target_project, class_name: "Project"
+ belongs_to :source_project, class_name: "Project"
belongs_to :merge_user, class_name: "User"
has_many :merge_request_diffs, dependent: :destroy
diff --git a/app/models/merge_request_diff.rb b/app/models/merge_request_diff.rb
index b8a10b7968e..dd65a9a8b86 100644
--- a/app/models/merge_request_diff.rb
+++ b/app/models/merge_request_diff.rb
@@ -299,8 +299,10 @@ class MergeRequestDiff < ActiveRecord::Base
end
def keep_around_commits
- repository.keep_around(start_commit_sha)
- repository.keep_around(head_commit_sha)
- repository.keep_around(base_commit_sha)
+ [repository, merge_request.source_project.repository].each do |repo|
+ repo.keep_around(start_commit_sha)
+ repo.keep_around(head_commit_sha)
+ repo.keep_around(base_commit_sha)
+ end
end
end
diff --git a/app/models/project.rb b/app/models/project.rb
index af117f0acb0..fbf7012972e 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -63,11 +63,11 @@ class Project < ActiveRecord::Base
alias_attribute :title, :name
# Relations
- belongs_to :creator, foreign_key: 'creator_id', class_name: 'User'
+ belongs_to :creator, class_name: 'User'
belongs_to :group, -> { where(type: 'Group') }, foreign_key: 'namespace_id'
belongs_to :namespace
- has_one :last_event, -> {order 'events.created_at DESC'}, class_name: 'Event', foreign_key: 'project_id'
+ has_one :last_event, -> {order 'events.created_at DESC'}, class_name: 'Event'
has_many :boards, before_add: :validate_board_limit, dependent: :destroy
# Project services
@@ -116,7 +116,7 @@ class Project < ActiveRecord::Base
has_many :hooks, dependent: :destroy, class_name: 'ProjectHook'
has_many :protected_branches, dependent: :destroy
- has_many :project_members, -> { where(requested_at: nil) }, dependent: :destroy, as: :source, class_name: 'ProjectMember'
+ has_many :project_members, -> { where(requested_at: nil) }, dependent: :destroy, as: :source
alias_method :members, :project_members
has_many :users, through: :project_members
@@ -137,7 +137,7 @@ class Project < ActiveRecord::Base
has_one :import_data, dependent: :destroy, class_name: "ProjectImportData"
has_one :project_feature, dependent: :destroy
- has_many :commit_statuses, dependent: :destroy, class_name: 'CommitStatus', foreign_key: :gl_project_id
+ has_many :commit_statuses, dependent: :destroy, foreign_key: :gl_project_id
has_many :pipelines, dependent: :destroy, class_name: 'Ci::Pipeline', foreign_key: :gl_project_id
has_many :builds, class_name: 'Ci::Build', foreign_key: :gl_project_id # the builds are created from the commit_statuses
has_many :runner_projects, dependent: :destroy, class_name: 'Ci::RunnerProject', foreign_key: :gl_project_id
diff --git a/app/models/repository.rb b/app/models/repository.rb
index 1b7f20a2134..4ae9c20726f 100644
--- a/app/models/repository.rb
+++ b/app/models/repository.rb
@@ -419,6 +419,17 @@ class Repository
@exists = nil
end
+ # expire cache that doesn't depend on repository data (when expiring)
+ def expire_content_cache
+ expire_tags_cache
+ expire_tag_count_cache
+ expire_branches_cache
+ expire_branch_count_cache
+ expire_root_ref_cache
+ expire_emptiness_caches
+ expire_exists_cache
+ end
+
# Runs code after a repository has been created.
def after_create
expire_exists_cache
@@ -434,14 +445,7 @@ class Repository
expire_cache if exists?
- # expire cache that don't depend on repository data (when expiring)
- expire_tags_cache
- expire_tag_count_cache
- expire_branches_cache
- expire_branch_count_cache
- expire_root_ref_cache
- expire_emptiness_caches
- expire_exists_cache
+ expire_content_cache
repository_event(:remove_repository)
end
@@ -473,14 +477,13 @@ class Repository
end
def before_import
- expire_emptiness_caches
- expire_exists_cache
+ expire_content_cache
end
# Runs code after a repository has been forked/imported.
def after_import
- expire_emptiness_caches
- expire_exists_cache
+ expire_content_cache
+ build_cache
end
# Runs code after a new commit has been pushed.
diff --git a/app/models/user.rb b/app/models/user.rb
index f367f4616fb..9e76df63d31 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -47,7 +47,7 @@ class User < ActiveRecord::Base
#
# Namespace for personal projects
- has_one :namespace, -> { where type: nil }, dependent: :destroy, foreign_key: :owner_id, class_name: "Namespace"
+ has_one :namespace, -> { where type: nil }, dependent: :destroy, foreign_key: :owner_id
# Profile
has_many :keys, dependent: :destroy
@@ -66,17 +66,17 @@ class User < ActiveRecord::Base
# Projects
has_many :groups_projects, through: :groups, source: :projects
has_many :personal_projects, through: :namespace, source: :projects
- has_many :project_members, -> { where(requested_at: nil) }, dependent: :destroy, class_name: 'ProjectMember'
+ has_many :project_members, -> { where(requested_at: nil) }, dependent: :destroy
has_many :projects, through: :project_members
has_many :created_projects, foreign_key: :creator_id, class_name: 'Project'
has_many :users_star_projects, dependent: :destroy
has_many :starred_projects, through: :users_star_projects, source: :project
- has_many :snippets, dependent: :destroy, foreign_key: :author_id, class_name: "Snippet"
+ has_many :snippets, dependent: :destroy, foreign_key: :author_id
has_many :issues, dependent: :destroy, foreign_key: :author_id
has_many :notes, dependent: :destroy, foreign_key: :author_id
has_many :merge_requests, dependent: :destroy, foreign_key: :author_id
- has_many :events, dependent: :destroy, foreign_key: :author_id, class_name: "Event"
+ has_many :events, dependent: :destroy, foreign_key: :author_id
has_many :subscriptions, dependent: :destroy
has_many :recent_events, -> { order "id DESC" }, foreign_key: :author_id, class_name: "Event"
has_many :assigned_issues, dependent: :destroy, foreign_key: :assignee_id, class_name: "Issue"
@@ -309,7 +309,7 @@ class User < ActiveRecord::Base
username
end
- def to_reference(_from_project = nil)
+ def to_reference(_from_project = nil, _target_project = nil)
"#{self.class.reference_prefix}#{username}"
end
diff --git a/app/services/merge_requests/assign_issues_service.rb b/app/services/merge_requests/assign_issues_service.rb
index f636e5fec4f..066efa1acc3 100644
--- a/app/services/merge_requests/assign_issues_service.rb
+++ b/app/services/merge_requests/assign_issues_service.rb
@@ -4,7 +4,7 @@ module MergeRequests
@assignable_issues ||= begin
if current_user == merge_request.author
closes_issues.select do |issue|
- !issue.assignee_id? && can?(current_user, :admin_issue, issue)
+ !issue.is_a?(ExternalIssue) && !issue.assignee_id? && can?(current_user, :admin_issue, issue)
end
else
[]
diff --git a/app/services/notes/create_service.rb b/app/services/notes/create_service.rb
index a36008c3ef5..723cc0e6834 100644
--- a/app/services/notes/create_service.rb
+++ b/app/services/notes/create_service.rb
@@ -7,8 +7,10 @@ module Notes
if note.award_emoji?
noteable = note.noteable
- todo_service.new_award_emoji(noteable, current_user)
- return noteable.create_award_emoji(note.award_emoji_name, current_user)
+ if noteable.user_can_award?(current_user, note.award_emoji_name)
+ todo_service.new_award_emoji(noteable, current_user)
+ return noteable.create_award_emoji(note.award_emoji_name, current_user)
+ end
end
# We execute commands (extracted from `params[:note]`) on the noteable
diff --git a/app/services/protected_branches/api_create_service.rb b/app/services/protected_branches/api_create_service.rb
new file mode 100644
index 00000000000..f2040dfa03a
--- /dev/null
+++ b/app/services/protected_branches/api_create_service.rb
@@ -0,0 +1,29 @@
+# The protected branches API still uses the `developers_can_push` and `developers_can_merge`
+# flags for backward compatibility, and so performs translation between that format and the
+# internal data model (separate access levels). The translation code is non-trivial, and so
+# lives in this service.
+module ProtectedBranches
+ class ApiCreateService < BaseService
+ def execute
+ push_access_level =
+ if params.delete(:developers_can_push)
+ Gitlab::Access::DEVELOPER
+ else
+ Gitlab::Access::MASTER
+ end
+
+ merge_access_level =
+ if params.delete(:developers_can_merge)
+ Gitlab::Access::DEVELOPER
+ else
+ Gitlab::Access::MASTER
+ end
+
+ @params.merge!(push_access_levels_attributes: [{ access_level: push_access_level }],
+ merge_access_levels_attributes: [{ access_level: merge_access_level }])
+
+ service = ProtectedBranches::CreateService.new(@project, @current_user, @params)
+ service.execute
+ end
+ end
+end
diff --git a/app/services/protected_branches/api_update_service.rb b/app/services/protected_branches/api_update_service.rb
new file mode 100644
index 00000000000..050cb3b738b
--- /dev/null
+++ b/app/services/protected_branches/api_update_service.rb
@@ -0,0 +1,47 @@
+# The protected branches API still uses the `developers_can_push` and `developers_can_merge`
+# flags for backward compatibility, and so performs translation between that format and the
+# internal data model (separate access levels). The translation code is non-trivial, and so
+# lives in this service.
+module ProtectedBranches
+ class ApiUpdateService < BaseService
+ def execute(protected_branch)
+ @developers_can_push = params.delete(:developers_can_push)
+ @developers_can_merge = params.delete(:developers_can_merge)
+
+ @protected_branch = protected_branch
+
+ protected_branch.transaction do
+ delete_redundant_access_levels
+
+ case @developers_can_push
+ when true
+ params.merge!(push_access_levels_attributes: [{ access_level: Gitlab::Access::DEVELOPER }])
+ when false
+ params.merge!(push_access_levels_attributes: [{ access_level: Gitlab::Access::MASTER }])
+ end
+
+ case @developers_can_merge
+ when true
+ params.merge!(merge_access_levels_attributes: [{ access_level: Gitlab::Access::DEVELOPER }])
+ when false
+ params.merge!(merge_access_levels_attributes: [{ access_level: Gitlab::Access::MASTER }])
+ end
+
+ service = ProtectedBranches::UpdateService.new(@project, @current_user, @params)
+ service.execute(protected_branch)
+ end
+ end
+
+ private
+
+ def delete_redundant_access_levels
+ unless @developers_can_merge.nil?
+ @protected_branch.merge_access_levels.destroy_all
+ end
+
+ unless @developers_can_push.nil?
+ @protected_branch.push_access_levels.destroy_all
+ end
+ end
+ end
+end
diff --git a/app/views/admin/runners/index.html.haml b/app/views/admin/runners/index.html.haml
index b760b42fde0..37bb6a3b0e0 100644
--- a/app/views/admin/runners/index.html.haml
+++ b/app/views/admin/runners/index.html.haml
@@ -75,4 +75,4 @@
- @runners.each do |runner|
= render "admin/runners/runner", runner: runner
- = paginate @runners
+ = paginate @runners, theme: "gitlab"
diff --git a/app/views/admin/runners/show.html.haml b/app/views/admin/runners/show.html.haml
index 10fea1996aa..73038164056 100644
--- a/app/views/admin/runners/show.html.haml
+++ b/app/views/admin/runners/show.html.haml
@@ -67,7 +67,7 @@
= form_for [:admin, project.namespace.becomes(Namespace), project, project.runner_projects.new] do |f|
= f.hidden_field :runner_id, value: @runner.id
= f.submit 'Enable', class: 'btn btn-xs'
- = paginate @projects
+ = paginate @projects, theme: "gitlab"
.col-md-6
%h4 Recent builds served by this Runner
diff --git a/app/views/devise/sessions/_new_base.html.haml b/app/views/devise/sessions/_new_base.html.haml
index 525e7d99d71..5fd896f6835 100644
--- a/app/views/devise/sessions/_new_base.html.haml
+++ b/app/views/devise/sessions/_new_base.html.haml
@@ -12,5 +12,5 @@
%label{for: "user_remember_me"}
= f.check_box :remember_me
%span Remember me
- .pull-right
+ .pull-right.forgot-password
= link_to "Forgot your password?", new_password_path(resource_name)
diff --git a/app/views/devise/sessions/two_factor.html.haml b/app/views/devise/sessions/two_factor.html.haml
index 0e865b807c1..fd77cdbee2e 100644
--- a/app/views/devise/sessions/two_factor.html.haml
+++ b/app/views/devise/sessions/two_factor.html.haml
@@ -10,7 +10,7 @@
= form_for(resource, as: resource_name, url: session_path(resource_name), method: :post, html: { class: 'edit_user show-gl-field-errors' }) do |f|
- resource_params = params[resource_name].presence || params
= f.hidden_field :remember_me, value: resource_params.fetch(:remember_me, 0)
- .form-group
+ %div
= f.label 'Two-Factor Authentication code', name: :otp_attempt
= f.text_field :otp_attempt, class: 'form-control', required: true, autofocus: true, autocomplete: 'off', title: 'This field is required.'
%p.help-block.hint Enter the code from the two-factor app on your mobile device. If you've lost your device, you may enter one of your recovery codes.
diff --git a/app/views/devise/shared/_tabs_ldap.html.haml b/app/views/devise/shared/_tabs_ldap.html.haml
index a057f126c45..1e957f0935f 100644
--- a/app/views/devise/shared/_tabs_ldap.html.haml
+++ b/app/views/devise/shared/_tabs_ldap.html.haml
@@ -1,4 +1,4 @@
-%ul.new-session-tabs.nav-links.nav-tabs
+%ul.new-session-tabs.nav-links.nav-tabs{ class: ('custom-provider-tabs' if form_based_providers.any?) }
- if crowd_enabled?
%li.active
= link_to "Crowd", "#crowd", 'data-toggle' => 'tab'
diff --git a/app/views/devise/shared/_tabs_normal.html.haml b/app/views/devise/shared/_tabs_normal.html.haml
index 79b1d447a92..05246303fb6 100644
--- a/app/views/devise/shared/_tabs_normal.html.haml
+++ b/app/views/devise/shared/_tabs_normal.html.haml
@@ -1,5 +1,6 @@
%ul.nav-links.new-session-tabs.nav-tabs{ role: 'tablist'}
%li.active{ role: 'presentation' }
%a{ href: '#login-pane', data: { toggle: 'tab' }, role: 'tab'} Sign in
- %li{ role: 'presentation'}
- %a{ href: '#register-pane', data: { toggle: 'tab' }, role: 'tab'} Register
+ - if signin_enabled? && signup_enabled?
+ %li{ role: 'presentation'}
+ %a{ href: '#register-pane', data: { toggle: 'tab' }, role: 'tab'} Register
diff --git a/app/views/import/gitlab_projects/new.html.haml b/app/views/import/gitlab_projects/new.html.haml
index 44e2653ca4a..767dffb5589 100644
--- a/app/views/import/gitlab_projects/new.html.haml
+++ b/app/views/import/gitlab_projects/new.html.haml
@@ -9,12 +9,12 @@
%p
Project will be imported as
%strong
- #{@namespace_name}/#{@path}
+ #{@namespace.name}/#{@path}
%p
To move or copy an entire GitLab project from another GitLab installation to this one, navigate to the original project's settings page, generate an export file, and upload it here.
.form-group
- = hidden_field_tag :namespace_id, @namespace_id
+ = hidden_field_tag :namespace_id, @namespace.id
= hidden_field_tag :path, @path
= label_tag :file, class: 'control-label' do
%span GitLab project export
diff --git a/app/views/projects/boards/components/_card.html.haml b/app/views/projects/boards/components/_card.html.haml
index d8f16022407..c6d718a1cd1 100644
--- a/app/views/projects/boards/components/_card.html.haml
+++ b/app/views/projects/boards/components/_card.html.haml
@@ -26,7 +26,7 @@
":title" => "label.description",
data: { container: 'body' } }
{{ label.title }}
- %a.has-tooltip{ ":href" => "'/' + issue.assignee.username",
+ %a.has-tooltip{ ":href" => "'#{root_path}' + issue.assignee.username",
":title" => "'Assigned to ' + issue.assignee.name",
"v-if" => "issue.assignee",
data: { container: 'body' } }
diff --git a/app/views/projects/compare/_form.html.haml b/app/views/projects/compare/_form.html.haml
index 76b68c544aa..7bde20c3286 100644
--- a/app/views/projects/compare/_form.html.haml
+++ b/app/views/projects/compare/_form.html.haml
@@ -10,7 +10,7 @@
= button_tag type: 'button', class: "form-control compare-dropdown-toggle js-compare-dropdown", required: true, data: { refs_url: refs_namespace_project_path(@project.namespace, @project), toggle: "dropdown", target: ".js-compare-from-dropdown", selected: params[:from], field_name: :from } do
.dropdown-toggle-text= params[:from] || 'Select branch/tag'
= render "ref_dropdown"
- .compare-ellipsis ...
+ .compare-ellipsis.inline ...
.form-group.dropdown.compare-form-group.to.js-compare-to-dropdown
.input-group.inline-input-group
%span.input-group-addon to
diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml
index fb776e3a3e7..30473d14b9b 100644
--- a/app/views/projects/edit.html.haml
+++ b/app/views/projects/edit.html.haml
@@ -46,70 +46,70 @@
%h5.prepend-top-0
Feature Visibility
- = f.fields_for :project_feature do |feature_fields|
- .form_group.prepend-top-20
- .row
- .col-md-9
- = feature_fields.label :repository_access_level, "Repository", class: 'label-light'
- %span.help-block Push files to be stored in this project
- .col-md-3.js-repo-access-level
- = project_feature_access_select(:repository_access_level)
+ = f.fields_for :project_feature do |feature_fields|
+ .form_group.prepend-top-20
+ .row
+ .col-md-9
+ = feature_fields.label :repository_access_level, "Repository", class: 'label-light'
+ %span.help-block Push files to be stored in this project
+ .col-md-3.js-repo-access-level
+ = project_feature_access_select(:repository_access_level)
- .col-sm-12
- .row
- .col-md-9.project-feature-nested
- = feature_fields.label :merge_requests_access_level, "Merge requests", class: 'label-light'
- %span.help-block Submit changes to be merged upstream
- .col-md-3
- = project_feature_access_select(:merge_requests_access_level)
+ .col-sm-12
+ .row
+ .col-md-9.project-feature-nested
+ = feature_fields.label :merge_requests_access_level, "Merge requests", class: 'label-light'
+ %span.help-block Submit changes to be merged upstream
+ .col-md-3
+ = project_feature_access_select(:merge_requests_access_level)
- .row
- .col-md-9.project-feature-nested
- = feature_fields.label :builds_access_level, "Builds", class: 'label-light'
- %span.help-block Submit, test and deploy your changes before merge
- .col-md-3
- = project_feature_access_select(:builds_access_level)
+ .row
+ .col-md-9.project-feature-nested
+ = feature_fields.label :builds_access_level, "Builds", class: 'label-light'
+ %span.help-block Submit, test and deploy your changes before merge
+ .col-md-3
+ = project_feature_access_select(:builds_access_level)
- .row
- .col-md-9
- = feature_fields.label :snippets_access_level, "Snippets", class: 'label-light'
- %span.help-block Share code pastes with others out of Git repository
- .col-md-3
- = project_feature_access_select(:snippets_access_level)
+ .row
+ .col-md-9
+ = feature_fields.label :snippets_access_level, "Snippets", class: 'label-light'
+ %span.help-block Share code pastes with others out of Git repository
+ .col-md-3
+ = project_feature_access_select(:snippets_access_level)
- .row
- .col-md-9
- = feature_fields.label :issues_access_level, "Issues", class: 'label-light'
- %span.help-block Lightweight issue tracking system for this project
- .col-md-3
- = project_feature_access_select(:issues_access_level)
+ .row
+ .col-md-9
+ = feature_fields.label :issues_access_level, "Issues", class: 'label-light'
+ %span.help-block Lightweight issue tracking system for this project
+ .col-md-3
+ = project_feature_access_select(:issues_access_level)
- .row
- .col-md-9
- = feature_fields.label :wiki_access_level, "Wiki", class: 'label-light'
- %span.help-block Pages for project documentation
- .col-md-3
- = project_feature_access_select(:wiki_access_level)
-
- - if Gitlab.config.lfs.enabled && current_user.admin?
- .checkbox
- = f.label :lfs_enabled do
- = f.check_box :lfs_enabled
- %strong LFS
- %br
- %span.descr
- Git Large File Storage
- = link_to icon('question-circle'), help_page_path('workflow/lfs/manage_large_binaries_with_git_lfs')
+ .row
+ .col-md-9
+ = feature_fields.label :wiki_access_level, "Wiki", class: 'label-light'
+ %span.help-block Pages for project documentation
+ .col-md-3
+ = project_feature_access_select(:wiki_access_level)
- if Gitlab.config.lfs.enabled && current_user.admin?
- .form-group
- .checkbox
- = f.label :container_registry_enabled do
- = f.check_box :container_registry_enabled
- %strong Container Registry
- %br
- %span.descr Enable Container Registry for this project
- = link_to icon('question-circle'), help_page_path('user/project/container_registry'), target: '_blank'
+ .checkbox
+ = f.label :lfs_enabled do
+ = f.check_box :lfs_enabled
+ %strong LFS
+ %br
+ %span.descr
+ Git Large File Storage
+ = link_to icon('question-circle'), help_page_path('workflow/lfs/manage_large_binaries_with_git_lfs')
+
+ - if Gitlab.config.registry.enabled
+ .form-group
+ .checkbox
+ = f.label :container_registry_enabled do
+ = f.check_box :container_registry_enabled
+ %strong Container Registry
+ %br
+ %span.descr Enable Container Registry for this project
+ = link_to icon('question-circle'), help_page_path('user/project/container_registry'), target: '_blank'
= render 'merge_request_settings', f: f
%hr
@@ -288,4 +288,4 @@
Saving project.
%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
diff --git a/app/views/projects/issues/show.html.haml b/app/views/projects/issues/show.html.haml
index 6f3f238a436..bd629b5c519 100644
--- a/app/views/projects/issues/show.html.haml
+++ b/app/views/projects/issues/show.html.haml
@@ -53,7 +53,7 @@
.issue-details.issuable-details
- .detail-page-description.content-block
+ .detail-page-description.content-block{ class: ('hide-bottom-border' unless @issue.description.present? ) }
%h2.title
= markdown_field(@issue, :title)
- if @issue.description.present?
diff --git a/app/views/projects/merge_requests/_show.html.haml b/app/views/projects/merge_requests/_show.html.haml
index 0e19d224fcd..f57abe73977 100644
--- a/app/views/projects/merge_requests/_show.html.haml
+++ b/app/views/projects/merge_requests/_show.html.haml
@@ -47,39 +47,41 @@
= link_to "command line", "#modal_merge_info", class: "how_to_merge_link vlink", title: "How To Merge", "data-toggle" => "modal"
- if @commits_count.nonzero?
- %ul.merge-request-tabs.nav-links.no-top.no-bottom{ class: ("js-tabs-affix" unless ENV['RAILS_ENV'] == 'test') }
- %li.notes-tab
- = link_to namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: { target: 'div#notes', action: 'notes', toggle: 'tab' } do
- Discussion
- %span.badge= @merge_request.mr_and_commit_notes.user.count
- - if @merge_request.source_project
- %li.commits-tab
- = link_to commits_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: { target: 'div#commits', action: 'commits', toggle: 'tab' } do
- Commits
- %span.badge= @commits_count
- - if @pipeline
- %li.pipelines-tab
- = link_to pipelines_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: { target: '#pipelines', action: 'pipelines', toggle: 'tab' } do
- Pipelines
- %span.badge= @pipelines.size
- %li.builds-tab
- = link_to builds_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: { target: '#builds', action: 'builds', toggle: 'tab' } do
- Builds
- %span.badge= @statuses.size
- %li.diffs-tab
- = link_to diffs_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: { target: 'div#diffs', action: 'diffs', toggle: 'tab' } do
- Changes
- %span.badge= @merge_request.diff_size
- %li#resolve-count-app.line-resolve-all-container.pull-right.prepend-top-10.hidden-xs{ "v-cloak" => true }
- %resolve-count{ "inline-template" => true, ":logged-out" => "#{current_user.nil?}" }
- .line-resolve-all{ "v-show" => "discussionCount > 0",
- ":class" => "{ 'has-next-btn': !loggedOut && resolvedDiscussionCount !== discussionCount }" }
- %span.line-resolve-btn.is-disabled{ type: "button",
- ":class" => "{ 'is-active': resolvedDiscussionCount === discussionCount }" }
- = render "shared/icons/icon_status_success.svg"
- %span.line-resolve-text
- {{ resolvedDiscussionCount }}/{{ discussionCount }} {{ discussionCount | pluralize 'discussion' }} resolved
- = render "discussions/jump_to_next"
+ .merge-request-tabs-holder{ class: ("js-tabs-affix" unless ENV['RAILS_ENV'] == 'test') }
+ %div{ class: container_class }
+ %ul.merge-request-tabs.nav-links.no-top.no-bottom
+ %li.notes-tab
+ = link_to namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: { target: 'div#notes', action: 'notes', toggle: 'tab' } do
+ Discussion
+ %span.badge= @merge_request.mr_and_commit_notes.user.count
+ - if @merge_request.source_project
+ %li.commits-tab
+ = link_to commits_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: { target: 'div#commits', action: 'commits', toggle: 'tab' } do
+ Commits
+ %span.badge= @commits_count
+ - if @pipeline
+ %li.pipelines-tab
+ = link_to pipelines_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: { target: '#pipelines', action: 'pipelines', toggle: 'tab' } do
+ Pipelines
+ %span.badge= @pipelines.size
+ %li.builds-tab
+ = link_to builds_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: { target: '#builds', action: 'builds', toggle: 'tab' } do
+ Builds
+ %span.badge= @statuses.size
+ %li.diffs-tab
+ = link_to diffs_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: { target: 'div#diffs', action: 'diffs', toggle: 'tab' } do
+ Changes
+ %span.badge= @merge_request.diff_size
+ %li#resolve-count-app.line-resolve-all-container.pull-right.prepend-top-10.hidden-xs{ "v-cloak" => true }
+ %resolve-count{ "inline-template" => true, ":logged-out" => "#{current_user.nil?}" }
+ .line-resolve-all{ "v-show" => "discussionCount > 0",
+ ":class" => "{ 'has-next-btn': !loggedOut && resolvedDiscussionCount !== discussionCount }" }
+ %span.line-resolve-btn.is-disabled{ type: "button",
+ ":class" => "{ 'is-active': resolvedDiscussionCount === discussionCount }" }
+ = render "shared/icons/icon_status_success.svg"
+ %span.line-resolve-text
+ {{ resolvedDiscussionCount }}/{{ discussionCount }} {{ discussionCount | pluralize 'discussion' }} resolved
+ = render "discussions/jump_to_next"
.tab-content#diff-notes-app
#notes.notes.tab-pane.voting_notes
diff --git a/app/views/projects/merge_requests/show/_commits.html.haml b/app/views/projects/merge_requests/show/_commits.html.haml
index 0b05785430b..61020516bcf 100644
--- a/app/views/projects/merge_requests/show/_commits.html.haml
+++ b/app/views/projects/merge_requests/show/_commits.html.haml
@@ -3,4 +3,4 @@
Most recent commits displayed first
%ol#commits-list.list-unstyled
- = render "projects/commits/commits", project: @merge_request.project
+ = render "projects/commits/commits", project: @merge_request.source_project
diff --git a/app/views/projects/milestones/show.html.haml b/app/views/projects/milestones/show.html.haml
index c83818e9199..f9ba77e87b5 100644
--- a/app/views/projects/milestones/show.html.haml
+++ b/app/views/projects/milestones/show.html.haml
@@ -31,7 +31,7 @@
= link_to namespace_project_milestone_path(@project.namespace, @project, @milestone), data: { confirm: 'Are you sure?' }, method: :delete, class: "btn btn-grouped btn-danger" do
Delete
- .detail-page-description.milestone-detail
+ .detail-page-description.milestone-detail{ class: ('hide-bottom-border' unless @milestone.description.present? ) }
%h2.title
= markdown_field(@milestone, :title)
%div
diff --git a/app/views/projects/new.html.haml b/app/views/projects/new.html.haml
index 399ccf15b7f..932603f03b0 100644
--- a/app/views/projects/new.html.haml
+++ b/app/views/projects/new.html.haml
@@ -127,6 +127,11 @@
}
});
+ $('#new_project').submit(function(){
+ var $path = $('#project_path');
+ $path.val($path.val().trim());
+ });
+
$('#project_path').keyup(function(){
if($(this).val().length !=0) {
$('.btn_import_gitlab_project').attr('disabled', false);
diff --git a/app/views/projects/notes/_note.html.haml b/app/views/projects/notes/_note.html.haml
index 73fe6a715fa..ab719e38904 100644
--- a/app/views/projects/notes/_note.html.haml
+++ b/app/views/projects/notes/_note.html.haml
@@ -57,7 +57,7 @@
= link_to '#', title: 'Edit comment', class: 'note-action-button js-note-edit' do
= icon('pencil', class: 'link-highlight')
= link_to namespace_project_note_path(note.project.namespace, note.project, note), title: 'Remove comment', method: :delete, data: { confirm: 'Are you sure you want to remove this comment?' }, remote: true, class: 'note-action-button hidden-xs js-note-delete danger' do
- = icon('trash-o')
+ = icon('trash-o', class: 'danger-highlight')
.note-body{class: note_editable ? 'js-task-list-container' : ''}
.note-text.md
= preserve do
diff --git a/app/views/projects/runners/_specific_runners.html.haml b/app/views/projects/runners/_specific_runners.html.haml
index 858af78f7bf..51b0939564e 100644
--- a/app/views/projects/runners/_specific_runners.html.haml
+++ b/app/views/projects/runners/_specific_runners.html.haml
@@ -26,4 +26,4 @@
%h4.underlined-title Available specific runners
%ul.bordered-list.available-specific-runners
= render partial: 'runner', collection: @assignable_runners, as: :runner
- = paginate @assignable_runners
+ = paginate @assignable_runners, theme: "gitlab"
diff --git a/app/workers/admin_email_worker.rb b/app/workers/admin_email_worker.rb
index 667fff031dd..c2dc955b27c 100644
--- a/app/workers/admin_email_worker.rb
+++ b/app/workers/admin_email_worker.rb
@@ -1,7 +1,6 @@
class AdminEmailWorker
include Sidekiq::Worker
-
- sidekiq_options retry: false # this job auto-repeats via sidekiq-cron
+ include CronjobQueue
def perform
repository_check_failed_count = Project.where(last_repository_check_failed: true).count
diff --git a/app/workers/build_coverage_worker.rb b/app/workers/build_coverage_worker.rb
index 0680645a8db..def0ab1dde1 100644
--- a/app/workers/build_coverage_worker.rb
+++ b/app/workers/build_coverage_worker.rb
@@ -1,6 +1,6 @@
class BuildCoverageWorker
include Sidekiq::Worker
- sidekiq_options queue: :default
+ include BuildQueue
def perform(build_id)
Ci::Build.find_by(id: build_id)
diff --git a/app/workers/build_email_worker.rb b/app/workers/build_email_worker.rb
index 1c7a04a66a8..5fdb1f2baa0 100644
--- a/app/workers/build_email_worker.rb
+++ b/app/workers/build_email_worker.rb
@@ -1,5 +1,6 @@
class BuildEmailWorker
include Sidekiq::Worker
+ include BuildQueue
def perform(build_id, recipients, push_data)
recipients.each do |recipient|
diff --git a/app/workers/build_finished_worker.rb b/app/workers/build_finished_worker.rb
index e7286b77ac5..466410bf08c 100644
--- a/app/workers/build_finished_worker.rb
+++ b/app/workers/build_finished_worker.rb
@@ -1,5 +1,6 @@
class BuildFinishedWorker
include Sidekiq::Worker
+ include BuildQueue
def perform(build_id)
Ci::Build.find_by(id: build_id).try do |build|
diff --git a/app/workers/build_hooks_worker.rb b/app/workers/build_hooks_worker.rb
index e22ececb3fd..9965af935d4 100644
--- a/app/workers/build_hooks_worker.rb
+++ b/app/workers/build_hooks_worker.rb
@@ -1,6 +1,6 @@
class BuildHooksWorker
include Sidekiq::Worker
- sidekiq_options queue: :default
+ include BuildQueue
def perform(build_id)
Ci::Build.find_by(id: build_id)
diff --git a/app/workers/build_success_worker.rb b/app/workers/build_success_worker.rb
index 500d357ce31..e0ad5268664 100644
--- a/app/workers/build_success_worker.rb
+++ b/app/workers/build_success_worker.rb
@@ -1,6 +1,6 @@
class BuildSuccessWorker
include Sidekiq::Worker
- sidekiq_options queue: :default
+ include BuildQueue
def perform(build_id)
Ci::Build.find_by(id: build_id).try do |build|
diff --git a/app/workers/clear_database_cache_worker.rb b/app/workers/clear_database_cache_worker.rb
index c541daba50e..c4cb4733482 100644
--- a/app/workers/clear_database_cache_worker.rb
+++ b/app/workers/clear_database_cache_worker.rb
@@ -1,6 +1,7 @@
# This worker clears all cache fields in the database, working in batches.
class ClearDatabaseCacheWorker
include Sidekiq::Worker
+ include DedicatedSidekiqQueue
BATCH_SIZE = 1000
diff --git a/app/workers/concerns/build_queue.rb b/app/workers/concerns/build_queue.rb
new file mode 100644
index 00000000000..cf0ead40a8b
--- /dev/null
+++ b/app/workers/concerns/build_queue.rb
@@ -0,0 +1,8 @@
+# Concern for setting Sidekiq settings for the various CI build workers.
+module BuildQueue
+ extend ActiveSupport::Concern
+
+ included do
+ sidekiq_options queue: :build
+ end
+end
diff --git a/app/workers/concerns/cronjob_queue.rb b/app/workers/concerns/cronjob_queue.rb
new file mode 100644
index 00000000000..e918bb011e0
--- /dev/null
+++ b/app/workers/concerns/cronjob_queue.rb
@@ -0,0 +1,9 @@
+# Concern that sets various Sidekiq settings for workers executed using a
+# cronjob.
+module CronjobQueue
+ extend ActiveSupport::Concern
+
+ included do
+ sidekiq_options queue: :cronjob, retry: false
+ end
+end
diff --git a/app/workers/concerns/dedicated_sidekiq_queue.rb b/app/workers/concerns/dedicated_sidekiq_queue.rb
new file mode 100644
index 00000000000..132bae6022b
--- /dev/null
+++ b/app/workers/concerns/dedicated_sidekiq_queue.rb
@@ -0,0 +1,9 @@
+# Concern that sets the queue of a Sidekiq worker based on the worker's class
+# name/namespace.
+module DedicatedSidekiqQueue
+ extend ActiveSupport::Concern
+
+ included do
+ sidekiq_options queue: name.sub(/Worker\z/, '').underscore.tr('/', '_')
+ end
+end
diff --git a/app/workers/concerns/pipeline_queue.rb b/app/workers/concerns/pipeline_queue.rb
new file mode 100644
index 00000000000..ca3860e1d38
--- /dev/null
+++ b/app/workers/concerns/pipeline_queue.rb
@@ -0,0 +1,8 @@
+# Concern for setting Sidekiq settings for the various CI pipeline workers.
+module PipelineQueue
+ extend ActiveSupport::Concern
+
+ included do
+ sidekiq_options queue: :pipeline
+ end
+end
diff --git a/app/workers/concerns/repository_check_queue.rb b/app/workers/concerns/repository_check_queue.rb
new file mode 100644
index 00000000000..a597321ccf4
--- /dev/null
+++ b/app/workers/concerns/repository_check_queue.rb
@@ -0,0 +1,8 @@
+# Concern for setting Sidekiq settings for the various repository check workers.
+module RepositoryCheckQueue
+ extend ActiveSupport::Concern
+
+ included do
+ sidekiq_options queue: :repository_check, retry: false
+ end
+end
diff --git a/app/workers/delete_user_worker.rb b/app/workers/delete_user_worker.rb
index 6ff361e4d80..3194c389b3d 100644
--- a/app/workers/delete_user_worker.rb
+++ b/app/workers/delete_user_worker.rb
@@ -1,5 +1,6 @@
class DeleteUserWorker
include Sidekiq::Worker
+ include DedicatedSidekiqQueue
def perform(current_user_id, delete_user_id, options = {})
delete_user = User.find(delete_user_id)
diff --git a/app/workers/email_receiver_worker.rb b/app/workers/email_receiver_worker.rb
index 842eebdea9e..d3f7e479a8d 100644
--- a/app/workers/email_receiver_worker.rb
+++ b/app/workers/email_receiver_worker.rb
@@ -1,7 +1,6 @@
class EmailReceiverWorker
include Sidekiq::Worker
-
- sidekiq_options queue: :incoming_email
+ include DedicatedSidekiqQueue
def perform(raw)
return unless Gitlab::IncomingEmail.enabled?
diff --git a/app/workers/emails_on_push_worker.rb b/app/workers/emails_on_push_worker.rb
index 1dc7e0adef7..b9cd49985dc 100644
--- a/app/workers/emails_on_push_worker.rb
+++ b/app/workers/emails_on_push_worker.rb
@@ -1,7 +1,7 @@
class EmailsOnPushWorker
include Sidekiq::Worker
+ include DedicatedSidekiqQueue
- sidekiq_options queue: :mailers
attr_reader :email, :skip_premailer
def perform(project_id, recipients, push_data, options = {})
diff --git a/app/workers/expire_build_artifacts_worker.rb b/app/workers/expire_build_artifacts_worker.rb
index 174eabff9fd..a27585fd389 100644
--- a/app/workers/expire_build_artifacts_worker.rb
+++ b/app/workers/expire_build_artifacts_worker.rb
@@ -1,5 +1,6 @@
class ExpireBuildArtifactsWorker
include Sidekiq::Worker
+ include CronjobQueue
def perform
Rails.logger.info 'Scheduling removal of build artifacts'
diff --git a/app/workers/expire_build_instance_artifacts_worker.rb b/app/workers/expire_build_instance_artifacts_worker.rb
index d9e2cc37bb3..eb403c134d1 100644
--- a/app/workers/expire_build_instance_artifacts_worker.rb
+++ b/app/workers/expire_build_instance_artifacts_worker.rb
@@ -1,5 +1,6 @@
class ExpireBuildInstanceArtifactsWorker
include Sidekiq::Worker
+ include DedicatedSidekiqQueue
def perform(build_id)
build = Ci::Build
diff --git a/app/workers/git_garbage_collect_worker.rb b/app/workers/git_garbage_collect_worker.rb
index a6cefd4d601..65f8093b5b0 100644
--- a/app/workers/git_garbage_collect_worker.rb
+++ b/app/workers/git_garbage_collect_worker.rb
@@ -1,8 +1,9 @@
class GitGarbageCollectWorker
include Sidekiq::Worker
include Gitlab::ShellAdapter
+ include DedicatedSidekiqQueue
- sidekiq_options queue: :gitlab_shell, retry: false
+ sidekiq_options retry: false
def perform(project_id)
project = Project.find(project_id)
diff --git a/app/workers/gitlab_shell_worker.rb b/app/workers/gitlab_shell_worker.rb
index cfeda88bbc5..964287a1793 100644
--- a/app/workers/gitlab_shell_worker.rb
+++ b/app/workers/gitlab_shell_worker.rb
@@ -1,8 +1,7 @@
class GitlabShellWorker
include Sidekiq::Worker
include Gitlab::ShellAdapter
-
- sidekiq_options queue: :gitlab_shell
+ include DedicatedSidekiqQueue
def perform(action, *arg)
gitlab_shell.send(action, *arg)
diff --git a/app/workers/group_destroy_worker.rb b/app/workers/group_destroy_worker.rb
index 5048746f09b..a49a5fd0855 100644
--- a/app/workers/group_destroy_worker.rb
+++ b/app/workers/group_destroy_worker.rb
@@ -1,7 +1,6 @@
class GroupDestroyWorker
include Sidekiq::Worker
-
- sidekiq_options queue: :default
+ include DedicatedSidekiqQueue
def perform(group_id, user_id)
begin
diff --git a/app/workers/import_export_project_cleanup_worker.rb b/app/workers/import_export_project_cleanup_worker.rb
index 72e3a9ae734..7957ed807ab 100644
--- a/app/workers/import_export_project_cleanup_worker.rb
+++ b/app/workers/import_export_project_cleanup_worker.rb
@@ -1,7 +1,6 @@
class ImportExportProjectCleanupWorker
include Sidekiq::Worker
-
- sidekiq_options queue: :default
+ include CronjobQueue
def perform
ImportExportCleanUpService.new.execute
diff --git a/app/workers/irker_worker.rb b/app/workers/irker_worker.rb
index 19f38358eb5..7e44b241743 100644
--- a/app/workers/irker_worker.rb
+++ b/app/workers/irker_worker.rb
@@ -3,6 +3,7 @@ require 'socket'
class IrkerWorker
include Sidekiq::Worker
+ include DedicatedSidekiqQueue
def perform(project_id, chans, colors, push_data, settings)
project = Project.find(project_id)
diff --git a/app/workers/merge_worker.rb b/app/workers/merge_worker.rb
index c87c0a252b1..79efca4f2f9 100644
--- a/app/workers/merge_worker.rb
+++ b/app/workers/merge_worker.rb
@@ -1,7 +1,6 @@
class MergeWorker
include Sidekiq::Worker
-
- sidekiq_options queue: :default
+ include DedicatedSidekiqQueue
def perform(merge_request_id, current_user_id, params)
params = params.with_indifferent_access
diff --git a/app/workers/new_note_worker.rb b/app/workers/new_note_worker.rb
index 1b3232cd365..c3e62bb88c0 100644
--- a/app/workers/new_note_worker.rb
+++ b/app/workers/new_note_worker.rb
@@ -1,7 +1,6 @@
class NewNoteWorker
include Sidekiq::Worker
-
- sidekiq_options queue: :default
+ include DedicatedSidekiqQueue
def perform(note_id, note_params)
note = Note.find(note_id)
diff --git a/app/workers/pipeline_hooks_worker.rb b/app/workers/pipeline_hooks_worker.rb
index ab5e9f6daad..7e36eacebf8 100644
--- a/app/workers/pipeline_hooks_worker.rb
+++ b/app/workers/pipeline_hooks_worker.rb
@@ -1,6 +1,6 @@
class PipelineHooksWorker
include Sidekiq::Worker
- sidekiq_options queue: :default
+ include PipelineQueue
def perform(pipeline_id)
Ci::Pipeline.find_by(id: pipeline_id)
diff --git a/app/workers/pipeline_metrics_worker.rb b/app/workers/pipeline_metrics_worker.rb
index 7bb92df3bbd..34f6ef161fb 100644
--- a/app/workers/pipeline_metrics_worker.rb
+++ b/app/workers/pipeline_metrics_worker.rb
@@ -1,7 +1,6 @@
class PipelineMetricsWorker
include Sidekiq::Worker
-
- sidekiq_options queue: :default
+ include PipelineQueue
def perform(pipeline_id)
Ci::Pipeline.find_by(id: pipeline_id).try do |pipeline|
diff --git a/app/workers/pipeline_process_worker.rb b/app/workers/pipeline_process_worker.rb
index f44227d7086..357e4a9a1c3 100644
--- a/app/workers/pipeline_process_worker.rb
+++ b/app/workers/pipeline_process_worker.rb
@@ -1,7 +1,6 @@
class PipelineProcessWorker
include Sidekiq::Worker
-
- sidekiq_options queue: :default
+ include PipelineQueue
def perform(pipeline_id)
Ci::Pipeline.find_by(id: pipeline_id)
diff --git a/app/workers/pipeline_success_worker.rb b/app/workers/pipeline_success_worker.rb
index 5dd443fea59..2aa6fff24da 100644
--- a/app/workers/pipeline_success_worker.rb
+++ b/app/workers/pipeline_success_worker.rb
@@ -1,6 +1,6 @@
class PipelineSuccessWorker
include Sidekiq::Worker
- sidekiq_options queue: :default
+ include PipelineQueue
def perform(pipeline_id)
Ci::Pipeline.find_by(id: pipeline_id).try do |pipeline|
diff --git a/app/workers/pipeline_update_worker.rb b/app/workers/pipeline_update_worker.rb
index 44a7f24e401..96c4152c674 100644
--- a/app/workers/pipeline_update_worker.rb
+++ b/app/workers/pipeline_update_worker.rb
@@ -1,7 +1,6 @@
class PipelineUpdateWorker
include Sidekiq::Worker
-
- sidekiq_options queue: :default
+ include PipelineQueue
def perform(pipeline_id)
Ci::Pipeline.find_by(id: pipeline_id)
diff --git a/app/workers/post_receive.rb b/app/workers/post_receive.rb
index a9a2b716005..eee0ca12af9 100644
--- a/app/workers/post_receive.rb
+++ b/app/workers/post_receive.rb
@@ -1,7 +1,6 @@
class PostReceive
include Sidekiq::Worker
-
- sidekiq_options queue: :post_receive
+ include DedicatedSidekiqQueue
def perform(repo_path, identifier, changes)
if path = Gitlab.config.repositories.storages.find { |p| repo_path.start_with?(p[1].to_s) }
diff --git a/app/workers/project_cache_worker.rb b/app/workers/project_cache_worker.rb
index 0d524e88dc3..71b274e0c99 100644
--- a/app/workers/project_cache_worker.rb
+++ b/app/workers/project_cache_worker.rb
@@ -5,8 +5,7 @@
# storage engine as much.
class ProjectCacheWorker
include Sidekiq::Worker
-
- sidekiq_options queue: :default
+ include DedicatedSidekiqQueue
LEASE_TIMEOUT = 15.minutes.to_i
diff --git a/app/workers/project_destroy_worker.rb b/app/workers/project_destroy_worker.rb
index 3062301a9b1..b462327490e 100644
--- a/app/workers/project_destroy_worker.rb
+++ b/app/workers/project_destroy_worker.rb
@@ -1,7 +1,6 @@
class ProjectDestroyWorker
include Sidekiq::Worker
-
- sidekiq_options queue: :default
+ include DedicatedSidekiqQueue
def perform(project_id, user_id, params)
begin
diff --git a/app/workers/project_export_worker.rb b/app/workers/project_export_worker.rb
index 615311e63f5..6009aa1b191 100644
--- a/app/workers/project_export_worker.rb
+++ b/app/workers/project_export_worker.rb
@@ -1,7 +1,8 @@
class ProjectExportWorker
include Sidekiq::Worker
+ include DedicatedSidekiqQueue
- sidekiq_options queue: :gitlab_shell, retry: 3
+ sidekiq_options retry: 3
def perform(current_user_id, project_id)
current_user = User.find(current_user_id)
diff --git a/app/workers/project_service_worker.rb b/app/workers/project_service_worker.rb
index 64d39c4d3f7..fdfdeab7b41 100644
--- a/app/workers/project_service_worker.rb
+++ b/app/workers/project_service_worker.rb
@@ -1,7 +1,6 @@
class ProjectServiceWorker
include Sidekiq::Worker
-
- sidekiq_options queue: :project_web_hook
+ include DedicatedSidekiqQueue
def perform(hook_id, data)
data = data.with_indifferent_access
diff --git a/app/workers/project_web_hook_worker.rb b/app/workers/project_web_hook_worker.rb
index fb878965288..efb85eafd15 100644
--- a/app/workers/project_web_hook_worker.rb
+++ b/app/workers/project_web_hook_worker.rb
@@ -1,7 +1,6 @@
class ProjectWebHookWorker
include Sidekiq::Worker
-
- sidekiq_options queue: :project_web_hook
+ include DedicatedSidekiqQueue
def perform(hook_id, data, hook_name)
data = data.with_indifferent_access
diff --git a/app/workers/prune_old_events_worker.rb b/app/workers/prune_old_events_worker.rb
index 5883cafe1d1..392abb9c21b 100644
--- a/app/workers/prune_old_events_worker.rb
+++ b/app/workers/prune_old_events_worker.rb
@@ -1,5 +1,6 @@
class PruneOldEventsWorker
include Sidekiq::Worker
+ include CronjobQueue
def perform
# Contribution calendar shows maximum 12 months of events.
diff --git a/app/workers/remove_expired_group_links_worker.rb b/app/workers/remove_expired_group_links_worker.rb
index 246c8b6650a..2a619f83410 100644
--- a/app/workers/remove_expired_group_links_worker.rb
+++ b/app/workers/remove_expired_group_links_worker.rb
@@ -1,5 +1,6 @@
class RemoveExpiredGroupLinksWorker
include Sidekiq::Worker
+ include CronjobQueue
def perform
ProjectGroupLink.expired.destroy_all
diff --git a/app/workers/remove_expired_members_worker.rb b/app/workers/remove_expired_members_worker.rb
index cf765af97ce..31f652e5f9b 100644
--- a/app/workers/remove_expired_members_worker.rb
+++ b/app/workers/remove_expired_members_worker.rb
@@ -1,5 +1,6 @@
class RemoveExpiredMembersWorker
include Sidekiq::Worker
+ include CronjobQueue
def perform
Member.expired.find_each do |member|
diff --git a/app/workers/repository_archive_cache_worker.rb b/app/workers/repository_archive_cache_worker.rb
index a2e49c61f59..e47069df189 100644
--- a/app/workers/repository_archive_cache_worker.rb
+++ b/app/workers/repository_archive_cache_worker.rb
@@ -1,7 +1,6 @@
class RepositoryArchiveCacheWorker
include Sidekiq::Worker
-
- sidekiq_options queue: :default
+ include CronjobQueue
def perform
RepositoryArchiveCleanUpService.new.execute
diff --git a/app/workers/repository_check/batch_worker.rb b/app/workers/repository_check/batch_worker.rb
index a3e16fa5212..c3e7491ec4e 100644
--- a/app/workers/repository_check/batch_worker.rb
+++ b/app/workers/repository_check/batch_worker.rb
@@ -1,14 +1,13 @@
module RepositoryCheck
class BatchWorker
include Sidekiq::Worker
-
+ include CronjobQueue
+
RUN_TIME = 3600
-
- sidekiq_options retry: false
-
+
def perform
start = Time.now
-
+
# This loop will break after a little more than one hour ('a little
# more' because `git fsck` may take a few minutes), or if it runs out of
# projects to check. By default sidekiq-cron will start a new
@@ -17,15 +16,15 @@ module RepositoryCheck
project_ids.each do |project_id|
break if Time.now - start >= RUN_TIME
break unless current_settings.repository_checks_enabled
-
+
next unless try_obtain_lease(project_id)
-
+
SingleRepositoryWorker.new.perform(project_id)
end
end
-
+
private
-
+
# Project.find_each does not support WHERE clauses and
# Project.find_in_batches does not support ordering. So we just build an
# array of ID's. This is OK because we do it only once an hour, because
@@ -39,7 +38,7 @@ module RepositoryCheck
reorder('last_repository_check_at ASC').limit(limit).pluck(:id)
never_checked_projects + old_check_projects
end
-
+
def try_obtain_lease(id)
# Use a 24-hour timeout because on servers/projects where 'git fsck' is
# super slow we definitely do not want to run it twice in parallel.
@@ -48,7 +47,7 @@ module RepositoryCheck
timeout: 24.hours
).try_obtain
end
-
+
def current_settings
# No caching of the settings! If we cache them and an admin disables
# this feature, an active RepositoryCheckWorker would keep going for up
diff --git a/app/workers/repository_check/clear_worker.rb b/app/workers/repository_check/clear_worker.rb
index b7202ddff34..1f1b38540ee 100644
--- a/app/workers/repository_check/clear_worker.rb
+++ b/app/workers/repository_check/clear_worker.rb
@@ -1,8 +1,7 @@
module RepositoryCheck
class ClearWorker
include Sidekiq::Worker
-
- sidekiq_options retry: false
+ include RepositoryCheckQueue
def perform
# Do small batched updates because these updates will be slow and locking
diff --git a/app/workers/repository_check/single_repository_worker.rb b/app/workers/repository_check/single_repository_worker.rb
index 98ddf5d0688..3d8bfc6fc6c 100644
--- a/app/workers/repository_check/single_repository_worker.rb
+++ b/app/workers/repository_check/single_repository_worker.rb
@@ -1,8 +1,7 @@
module RepositoryCheck
class SingleRepositoryWorker
include Sidekiq::Worker
-
- sidekiq_options retry: false
+ include RepositoryCheckQueue
def perform(project_id)
project = Project.find(project_id)
diff --git a/app/workers/repository_fork_worker.rb b/app/workers/repository_fork_worker.rb
index 61ed1c38ac4..efc99ec962a 100644
--- a/app/workers/repository_fork_worker.rb
+++ b/app/workers/repository_fork_worker.rb
@@ -1,8 +1,7 @@
class RepositoryForkWorker
include Sidekiq::Worker
include Gitlab::ShellAdapter
-
- sidekiq_options queue: :gitlab_shell
+ include DedicatedSidekiqQueue
def perform(project_id, forked_from_repository_storage_path, source_path, target_path)
Gitlab::Metrics.add_event(:fork_repository,
diff --git a/app/workers/repository_import_worker.rb b/app/workers/repository_import_worker.rb
index d2ca8813ab9..c8a77e21c12 100644
--- a/app/workers/repository_import_worker.rb
+++ b/app/workers/repository_import_worker.rb
@@ -1,8 +1,7 @@
class RepositoryImportWorker
include Sidekiq::Worker
include Gitlab::ShellAdapter
-
- sidekiq_options queue: :gitlab_shell
+ include DedicatedSidekiqQueue
attr_accessor :project, :current_user
diff --git a/app/workers/requests_profiles_worker.rb b/app/workers/requests_profiles_worker.rb
index 9dd228a2483..703b025d76e 100644
--- a/app/workers/requests_profiles_worker.rb
+++ b/app/workers/requests_profiles_worker.rb
@@ -1,7 +1,6 @@
class RequestsProfilesWorker
include Sidekiq::Worker
-
- sidekiq_options queue: :default
+ include CronjobQueue
def perform
Gitlab::RequestProfiler.remove_all_profiles
diff --git a/app/workers/stuck_ci_builds_worker.rb b/app/workers/stuck_ci_builds_worker.rb
index 6828013b377..b70df5a1afa 100644
--- a/app/workers/stuck_ci_builds_worker.rb
+++ b/app/workers/stuck_ci_builds_worker.rb
@@ -1,5 +1,6 @@
class StuckCiBuildsWorker
include Sidekiq::Worker
+ include CronjobQueue
BUILD_STUCK_TIMEOUT = 1.day
diff --git a/app/workers/system_hook_worker.rb b/app/workers/system_hook_worker.rb
index a122c274763..baf2f12eeac 100644
--- a/app/workers/system_hook_worker.rb
+++ b/app/workers/system_hook_worker.rb
@@ -1,7 +1,6 @@
class SystemHookWorker
include Sidekiq::Worker
-
- sidekiq_options queue: :system_hook
+ include DedicatedSidekiqQueue
def perform(hook_id, data, hook_name)
SystemHook.find(hook_id).execute(data, hook_name)
diff --git a/app/workers/trending_projects_worker.rb b/app/workers/trending_projects_worker.rb
index df4c4a6628b..0531630d13a 100644
--- a/app/workers/trending_projects_worker.rb
+++ b/app/workers/trending_projects_worker.rb
@@ -1,7 +1,6 @@
class TrendingProjectsWorker
include Sidekiq::Worker
-
- sidekiq_options queue: :trending_projects
+ include CronjobQueue
def perform
Rails.logger.info('Refreshing trending projects')
diff --git a/app/workers/update_merge_requests_worker.rb b/app/workers/update_merge_requests_worker.rb
index 03f0528cdae..acc4d858136 100644
--- a/app/workers/update_merge_requests_worker.rb
+++ b/app/workers/update_merge_requests_worker.rb
@@ -1,5 +1,6 @@
class UpdateMergeRequestsWorker
include Sidekiq::Worker
+ include DedicatedSidekiqQueue
def perform(project_id, user_id, oldrev, newrev, ref)
project = Project.find_by(id: project_id)
diff --git a/bin/background_jobs b/bin/background_jobs
index 25a578a1c49..f28e2f722dc 100755
--- a/bin/background_jobs
+++ b/bin/background_jobs
@@ -4,6 +4,7 @@ cd $(dirname $0)/..
app_root=$(pwd)
sidekiq_pidfile="$app_root/tmp/pids/sidekiq.pid"
sidekiq_logfile="$app_root/log/sidekiq.log"
+sidekiq_config="$app_root/config/sidekiq_queues.yml"
gitlab_user=$(ls -l config.ru | awk '{print $3}')
warn()
@@ -37,7 +38,7 @@ start_no_deamonize()
start_sidekiq()
{
- exec bundle exec sidekiq -q post_receive -q mailers -q archive_repo -q system_hook -q project_web_hook -q gitlab_shell -q incoming_email -q runner -q common -q default -e $RAILS_ENV -P $sidekiq_pidfile "$@"
+ exec bundle exec sidekiq -C "${sidekiq_config}" -e $RAILS_ENV -P $sidekiq_pidfile "$@"
}
load_ok()
diff --git a/config/application.rb b/config/application.rb
index f3337b00dc6..92c8467e7f4 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -24,7 +24,8 @@ module Gitlab
#{config.root}/app/models/ci
#{config.root}/app/models/hooks
#{config.root}/app/models/members
- #{config.root}/app/models/project_services))
+ #{config.root}/app/models/project_services
+ #{config.root}/app/workers/concerns))
config.generators.templates.push("#{config.root}/generator_templates")
diff --git a/config/mail_room.yml b/config/mail_room.yml
index c639f8260aa..68697bd1dc4 100644
--- a/config/mail_room.yml
+++ b/config/mail_room.yml
@@ -25,7 +25,7 @@
:delivery_options:
:redis_url: <%= config[:redis_url].to_json %>
:namespace: <%= Gitlab::Redis::SIDEKIQ_NAMESPACE %>
- :queue: incoming_email
+ :queue: email_receiver
:worker: EmailReceiverWorker
:arbitration_method: redis
diff --git a/config/sidekiq_queues.yml b/config/sidekiq_queues.yml
new file mode 100644
index 00000000000..f36fe893fd0
--- /dev/null
+++ b/config/sidekiq_queues.yml
@@ -0,0 +1,47 @@
+# This configuration file should be exclusively used to set queue settings for
+# Sidekiq. Any other setting should be specified using the Sidekiq CLI or the
+# Sidekiq Ruby API (see config/initializers/sidekiq.rb).
+---
+# All the queues to process and their weights. Every queue _must_ have a weight
+# defined.
+#
+# The available weights are as follows
+#
+# 1: low priority
+# 2: medium priority
+# 3: high priority
+# 5: _super_ high priority, this should only be used for _very_ important queues
+#
+# As per http://stackoverflow.com/a/21241357/290102 the formula for calculating
+# the likelihood of a job being popped off a queue (given all queues have work
+# to perform) is:
+#
+# chance = (queue weight / total weight of all queues) * 100
+:queues:
+ - [post_receive, 5]
+ - [merge, 5]
+ - [update_merge_requests, 3]
+ - [new_note, 2]
+ - [build, 2]
+ - [pipeline, 2]
+ - [gitlab_shell, 2]
+ - [email_receiver, 2]
+ - [emails_on_push, 2]
+ - [mailers, 2]
+ - [repository_fork, 1]
+ - [repository_import, 1]
+ - [project_service, 1]
+ - [clear_database_cache, 1]
+ - [delete_user, 1]
+ - [expire_build_instance_artifacts, 1]
+ - [group_destroy, 1]
+ - [irker, 1]
+ - [project_cache, 1]
+ - [project_destroy, 1]
+ - [project_export, 1]
+ - [project_web_hook, 1]
+ - [repository_check, 1]
+ - [system_hook, 1]
+ - [git_garbage_collect, 1]
+ - [cronjob, 1]
+ - [default, 1]
diff --git a/db/migrate/20161017125927_add_unique_index_to_labels.rb b/db/migrate/20161017125927_add_unique_index_to_labels.rb
index 16ae38612de..f2b56ebfb7b 100644
--- a/db/migrate/20161017125927_add_unique_index_to_labels.rb
+++ b/db/migrate/20161017125927_add_unique_index_to_labels.rb
@@ -7,9 +7,9 @@ class AddUniqueIndexToLabels < ActiveRecord::Migration
disable_ddl_transaction!
def up
- select_all('SELECT title, COUNT(id) as cnt FROM labels GROUP BY project_id, title HAVING COUNT(id) > 1').each do |label|
+ select_all('SELECT title, project_id, COUNT(id) as cnt FROM labels GROUP BY project_id, title HAVING COUNT(id) > 1').each do |label|
label_title = quote_string(label['title'])
- duplicated_ids = select_all("SELECT id FROM labels WHERE title = '#{label_title}' ORDER BY id ASC").map{ |label| label['id'] }
+ duplicated_ids = select_all("SELECT id FROM labels WHERE project_id = #{label['project_id']} AND title = '#{label_title}' ORDER BY id ASC").map{ |label| label['id'] }
label_id = duplicated_ids.first
duplicated_ids.delete(label_id)
diff --git a/db/migrate/20161018124658_make_project_owners_masters.rb b/db/migrate/20161018124658_make_project_owners_masters.rb
new file mode 100644
index 00000000000..a576bb7b622
--- /dev/null
+++ b/db/migrate/20161018124658_make_project_owners_masters.rb
@@ -0,0 +1,15 @@
+class MakeProjectOwnersMasters < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ def up
+ update_column_in_batches(:members, :access_level, 40) do |table, query|
+ query.where(table[:access_level].eq(50).and(table[:source_type].eq('Project')))
+ end
+ end
+
+ def down
+ # do nothing
+ end
+end
diff --git a/db/migrate/20161019190736_migrate_sidekiq_queues_from_default.rb b/db/migrate/20161019190736_migrate_sidekiq_queues_from_default.rb
new file mode 100644
index 00000000000..e875213ab96
--- /dev/null
+++ b/db/migrate/20161019190736_migrate_sidekiq_queues_from_default.rb
@@ -0,0 +1,109 @@
+require 'json'
+
+# See http://doc.gitlab.com/ce/development/migration_style_guide.html
+# for more information on how to write migrations for GitLab.
+
+class MigrateSidekiqQueuesFromDefault < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = true
+
+ DOWNTIME_REASON = <<-EOF
+ Moving Sidekiq jobs from queues requires Sidekiq to be stopped. Not stopping
+ Sidekiq will result in the loss of jobs that are scheduled after this
+ migration completes.
+ EOF
+
+ disable_ddl_transaction!
+
+ # Jobs for which the queue names have been changed (e.g. multiple workers
+ # using the same non-default queue).
+ #
+ # The keys are the old queue names, the values the jobs to move and their new
+ # queue names.
+ RENAMED_QUEUES = {
+ gitlab_shell: {
+ 'GitGarbageCollectorWorker' => :git_garbage_collector,
+ 'ProjectExportWorker' => :project_export,
+ 'RepositoryForkWorker' => :repository_fork,
+ 'RepositoryImportWorker' => :repository_import
+ },
+ project_web_hook: {
+ 'ProjectServiceWorker' => :project_service
+ },
+ incoming_email: {
+ 'EmailReceiverWorker' => :email_receiver
+ },
+ mailers: {
+ 'EmailsOnPushWorker' => :emails_on_push
+ },
+ default: {
+ 'AdminEmailWorker' => :cronjob,
+ 'BuildCoverageWorker' => :build,
+ 'BuildEmailWorker' => :build,
+ 'BuildFinishedWorker' => :build,
+ 'BuildHooksWorker' => :build,
+ 'BuildSuccessWorker' => :build,
+ 'ClearDatabaseCacheWorker' => :clear_database_cache,
+ 'DeleteUserWorker' => :delete_user,
+ 'ExpireBuildArtifactsWorker' => :cronjob,
+ 'ExpireBuildInstanceArtifactsWorker' => :expire_build_instance_artifacts,
+ 'GroupDestroyWorker' => :group_destroy,
+ 'ImportExportProjectCleanupWorker' => :cronjob,
+ 'IrkerWorker' => :irker,
+ 'MergeWorker' => :merge,
+ 'NewNoteWorker' => :new_note,
+ 'PipelineHooksWorker' => :pipeline,
+ 'PipelineMetricsWorker' => :pipeline,
+ 'PipelineProcessWorker' => :pipeline,
+ 'PipelineSuccessWorker' => :pipeline,
+ 'PipelineUpdateWorker' => :pipeline,
+ 'ProjectCacheWorker' => :project_cache,
+ 'ProjectDestroyWorker' => :project_destroy,
+ 'PruneOldEventsWorker' => :cronjob,
+ 'RemoveExpiredGroupLinksWorker' => :cronjob,
+ 'RemoveExpiredMembersWorker' => :cronjob,
+ 'RepositoryArchiveCacheWorker' => :cronjob,
+ 'RepositoryCheck::BatchWorker' => :cronjob,
+ 'RepositoryCheck::ClearWorker' => :repository_check,
+ 'RepositoryCheck::SingleRepositoryWorker' => :repository_check,
+ 'RequestsProfilesWorker' => :cronjob,
+ 'StuckCiBuildsWorker' => :cronjob,
+ 'UpdateMergeRequestsWorker' => :update_merge_requests
+ }
+ }
+
+ def up
+ Sidekiq.redis do |redis|
+ RENAMED_QUEUES.each do |queue, jobs|
+ migrate_from_queue(redis, queue, jobs)
+ end
+ end
+ end
+
+ def down
+ Sidekiq.redis do |redis|
+ RENAMED_QUEUES.each do |dest_queue, jobs|
+ jobs.each do |worker, from_queue|
+ migrate_from_queue(redis, from_queue, worker => dest_queue)
+ end
+ end
+ end
+ end
+
+ def migrate_from_queue(redis, queue, job_mapping)
+ while job = redis.lpop("queue:#{queue}")
+ payload = JSON.load(job)
+ new_queue = job_mapping[payload['class']]
+
+ # If we have no target queue to migrate to we're probably dealing with
+ # some ancient job for which the worker no longer exists. In that case
+ # there's no sane option we can take, other than just dropping the job.
+ next unless new_queue
+
+ payload['queue'] = new_queue
+
+ redis.lpush("queue:#{new_queue}", JSON.dump(payload))
+ end
+ end
+end
diff --git a/db/migrate/20161024042317_migrate_mailroom_queue_from_default.rb b/db/migrate/20161024042317_migrate_mailroom_queue_from_default.rb
new file mode 100644
index 00000000000..06d07bdb835
--- /dev/null
+++ b/db/migrate/20161024042317_migrate_mailroom_queue_from_default.rb
@@ -0,0 +1,63 @@
+require 'json'
+
+# See http://doc.gitlab.com/ce/development/migration_style_guide.html
+# for more information on how to write migrations for GitLab.
+
+class MigrateMailroomQueueFromDefault < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = true
+
+ DOWNTIME_REASON = <<-EOF
+ Moving Sidekiq jobs from queues requires Sidekiq to be stopped. Not stopping
+ Sidekiq will result in the loss of jobs that are scheduled after this
+ migration completes.
+ EOF
+
+ disable_ddl_transaction!
+
+ # Jobs for which the queue names have been changed (e.g. multiple workers
+ # using the same non-default queue).
+ #
+ # The keys are the old queue names, the values the jobs to move and their new
+ # queue names.
+ RENAMED_QUEUES = {
+ incoming_email: {
+ 'EmailReceiverWorker' => :email_receiver
+ }
+ }
+
+ def up
+ Sidekiq.redis do |redis|
+ RENAMED_QUEUES.each do |queue, jobs|
+ migrate_from_queue(redis, queue, jobs)
+ end
+ end
+ end
+
+ def down
+ Sidekiq.redis do |redis|
+ RENAMED_QUEUES.each do |dest_queue, jobs|
+ jobs.each do |worker, from_queue|
+ migrate_from_queue(redis, from_queue, worker => dest_queue)
+ end
+ end
+ end
+ end
+
+ def migrate_from_queue(redis, queue, job_mapping)
+ while job = redis.lpop("queue:#{queue}")
+ payload = JSON.load(job)
+ new_queue = job_mapping[payload['class']]
+
+ # If we have no target queue to migrate to we're probably dealing with
+ # some ancient job for which the worker no longer exists. In that case
+ # there's no sane option we can take, other than just dropping the job.
+ next unless new_queue
+
+ payload['queue'] = new_queue
+
+ redis.lpush("queue:#{new_queue}", JSON.dump(payload))
+ end
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index a3c7fc2fd57..02282b0f666 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -11,7 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema.define(version: 20161019213545) do
+ActiveRecord::Schema.define(version: 20161024042317) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -843,7 +843,7 @@ ActiveRecord::Schema.define(version: 20161019213545) do
t.integer "builds_access_level"
t.datetime "created_at"
t.datetime "updated_at"
- t.integer "repository_access_level", default: 20, null: false
+ t.integer "repository_access_level", default: 20, null: false
end
add_index "project_features", ["project_id"], name: "index_project_features_on_project_id", using: :btree
diff --git a/doc/administration/integration/koding.md b/doc/administration/integration/koding.md
index a2c358af095..b95c425842c 100644
--- a/doc/administration/integration/koding.md
+++ b/doc/administration/integration/koding.md
@@ -61,6 +61,7 @@ executing commands in the following snippet.
```bash
git clone https://github.com/koding/koding.git
cd koding
+docker-compose -f docker-compose-init.yml run init
docker-compose up
```
diff --git a/doc/api/builds.md b/doc/api/builds.md
index e40f198696d..0476cac0eda 100644
--- a/doc/api/builds.md
+++ b/doc/api/builds.md
@@ -11,10 +11,10 @@ GET /projects/:id/builds
| Attribute | Type | Required | Description |
|-----------|---------|----------|---------------------|
| `id` | integer | yes | The ID of a project |
-| `scope` | string **or** array of strings | no | The scope of builds to show, one or array of: `pending`, `running`, `failed`, `success`, `canceled`; showing all builds if none provided |
+| `scope` | string **or** array of strings | no | The scope of builds to show, one or array of: `created`, `pending`, `running`, `failed`, `success`, `canceled`, `skipped`; showing all builds if none provided |
```
-curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/1/builds"
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" 'https://gitlab.example.com/api/v3/projects/1/builds?scope%5B0%5D=pending&scope%5B1%5D=running'
```
Example of response
@@ -132,10 +132,10 @@ GET /projects/:id/repository/commits/:sha/builds
|-----------|---------|----------|---------------------|
| `id` | integer | yes | The ID of a project |
| `sha` | string | yes | The SHA id of a commit |
-| `scope` | string **or** array of strings | no | The scope of builds to show, one or array of: `pending`, `running`, `failed`, `success`, `canceled`; showing all builds if none provided |
+| `scope` | string **or** array of strings | no | The scope of builds to show, one or array of: `created`, `pending`, `running`, `failed`, `success`, `canceled`, `skipped`; showing all builds if none provided |
```
-curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/1/repository/commits/0ff3ae198f8601a285adcf5c0fff204ee6fba5fd/builds"
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" 'https://gitlab.example.com/api/v3/projects/1/repository/commits/0ff3ae198f8601a285adcf5c0fff204ee6fba5fd/builds?scope%5B0%5D=pending&scope%5B1%5D=running'
```
Example of response
diff --git a/doc/api/commits.md b/doc/api/commits.md
index 6e0882a94de..e1ed99d98d3 100644
--- a/doc/api/commits.md
+++ b/doc/api/commits.md
@@ -319,7 +319,7 @@ GET /projects/:id/repository/commits/:sha/statuses
| --------- | ---- | -------- | ----------- |
| `id` | integer/string | yes | The ID of a project or NAMESPACE/PROJECT_NAME owned by the authenticated user
| `sha` | string | yes | The commit SHA
-| `ref_name`| string | no | The name of a repository branch or tag or, if not given, the default branch
+| `ref` | string | no | The name of a repository branch or tag or, if not given, the default branch
| `stage` | string | no | Filter by [build stage](../ci/yaml/README.md#stages), e.g., `test`
| `name` | string | no | Filter by [job name](../ci/yaml/README.md#jobs), e.g., `bundler:audit`
| `all` | boolean | no | Return all statuses, not only the latest ones
diff --git a/doc/api/users.md b/doc/api/users.md
index 2b12770d5a5..a50ba5432fe 100644
--- a/doc/api/users.md
+++ b/doc/api/users.md
@@ -643,7 +643,7 @@ Parameters:
| `id` | integer | yes | The ID of the user |
```bash
-curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/user/:id/events
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/users/:id/events
```
Example response:
diff --git a/doc/ci/docker/using_docker_build.md b/doc/ci/docker/using_docker_build.md
index 0f64137a8a9..79bbe8421c6 100644
--- a/doc/ci/docker/using_docker_build.md
+++ b/doc/ci/docker/using_docker_build.md
@@ -188,7 +188,7 @@ In order to do that, follow the steps:
image = "docker:latest"
privileged = false
disable_cache = false
- volumes = ["/var/run/docker.sock", "/cache"]
+ volumes = ["/var/run/docker.sock:/var/run/docker.sock", "/cache"]
[runners.cache]
Insecure = false
```
diff --git a/doc/ci/docker/using_docker_images.md b/doc/ci/docker/using_docker_images.md
index 520c8b36a95..aba77490915 100644
--- a/doc/ci/docker/using_docker_images.md
+++ b/doc/ci/docker/using_docker_images.md
@@ -37,7 +37,7 @@ The registered runner will use the `ruby:2.1` docker image and will run two
services, `postgres:latest` and `mysql:latest`, both of which will be
accessible during the build process.
-## What is image
+## What is an image
The `image` keyword is the name of the docker image that is present in the
local Docker Engine (list all images with `docker images`) or any image that
@@ -47,7 +47,7 @@ Hub please read the [Docker Fundamentals][] documentation.
In short, with `image` we refer to the docker image, which will be used to
create a container on which your build will run.
-## What is service
+## What is a service
The `services` keyword defines just another docker image that is run during
your build and is linked to the docker image that the `image` keyword defines.
@@ -61,7 +61,7 @@ time the project is built.
You can see some widely used services examples in the relevant documentation of
[CI services examples](../services/README.md).
-### How is service linked to the build
+### How services are linked to the build
To better understand how the container linking works, read
[Linking containers together][linking-containers].
diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md
index 84ea59ab687..5c0e1c44e3f 100644
--- a/doc/ci/yaml/README.md
+++ b/doc/ci/yaml/README.md
@@ -146,13 +146,17 @@ variables:
```
These variables can be later used in all executed commands and scripts.
-
The YAML-defined variables are also set to all created service containers,
-thus allowing to fine tune them.
+thus allowing to fine tune them. Variables can be also defined on a
+[job level](#job-variables).
-Variables can be also defined on [job level](#job-variables).
+Except for the user defined variables, there are also the ones set up by the
+Runner itself. One example would be `CI_BUILD_REF_NAME` which has the value of
+the branch or tag name for which project is built. Apart from the variables
+you can set in `.gitlab-ci.yml`, there are also the so called secret variables
+which can be set in GitLab's UI.
-[Learn more about variables.](../variables/README.md)
+[Learn more about variables.][variables]
### cache
@@ -541,20 +545,29 @@ An example usage of manual actions is deployment to production.
> Introduced in GitLab 8.9.
-`environment` is used to define that a job deploys to a specific [environment].
-This allows easy tracking of all deployments to your environments straight from
-GitLab.
+> You can read more about environments and find more examples in the
+[documentation about environments][environment].
+`environment` is used to define that a job deploys to a specific environment.
If `environment` is specified and no environment under that name exists, a new
one will be created automatically.
-The `environment` name must contain only letters, digits, '-', '_', '/', '$', '{', '}' and spaces. Common
-names are `qa`, `staging`, and `production`, but you can use whatever name works
-with your workflow.
+The `environment` name can contain:
----
+- letters
+- digits
+- spaces
+- `-`
+- `_`
+- `/`
+- `$`
+- `{`
+- `}`
-**Example configurations**
+Common names are `qa`, `staging`, and `production`, but you can use whatever
+name works with your workflow.
+
+In its simplest form, the `environment` keyword can be defined like:
```
deploy to production:
@@ -563,39 +576,134 @@ deploy to production:
environment: production
```
-The `deploy to production` job will be marked as doing deployment to
-`production` environment.
+In the above example, the `deploy to production` job will be marked as doing a
+deployment to the `production` environment.
+
+#### environment:name
+
+> Introduced in GitLab 8.11.
+
+>**Note:**
+Before GitLab 8.11, the name of an environment could be defined as a string like
+`environment: production`. The recommended way now is to define it under the
+`name` keyword.
+
+Instead of defining the name of the environment right after the `environment`
+keyword, it is also possible to define it as a separate value. For that, use
+the `name` keyword under `environment`:
+
+```
+deploy to production:
+ stage: deploy
+ script: git push production HEAD:master
+ environment:
+ name: production
+```
+
+#### environment:url
+
+> Introduced in GitLab 8.11.
+
+>**Note:**
+Before GitLab 8.11, the URL could be added only in GitLab's UI. The
+recommended way now is to define it in `.gitlab-ci.yml`.
+
+This is an optional value that when set, it exposes buttons in various places
+in GitLab which when clicked take you to the defined URL.
+
+In the example below, if the job finishes successfully, it will create buttons
+in the merge requests and in the environments/deployments pages which will point
+to `https://prod.example.com`.
+
+```
+deploy to production:
+ stage: deploy
+ script: git push production HEAD:master
+ environment:
+ name: production
+ url: https://prod.example.com
+```
+
+#### environment:on_stop
+
+> [Introduced][ce-6669] in GitLab 8.13.
+
+Closing (stoping) environments can be achieved with the `on_stop` keyword defined under
+`environment`. It declares a different job that runs in order to close
+the environment.
+
+Read the `environment:action` section for an example.
+
+#### environment:action
+
+> [Introduced][ce-6669] in GitLab 8.13.
+
+The `action` keyword is to be used in conjunction with `on_stop` and is defined
+in the job that is called to close the environment.
+
+Take for instance:
+
+```yaml
+review_app:
+ stage: deploy
+ script: make deploy-app
+ environment:
+ name: review
+ on_stop: stop_review_app
+
+stop_review_app:
+ stage: deploy
+ script: make delete-app
+ when: manual
+ environment:
+ name: review
+ action: stop
+```
+
+In the above example we set up the `review_app` job to deploy to the `review`
+environment, and we also defined a new `stop_review_app` job under `on_stop`.
+Once the `review_app` job is successfully finished, it will trigger the
+`stop_review_app` job based on what is defined under `when`. In this case we
+set it up to `manual` so it will need a [manual action](#manual-actions) via
+GitLab's web interface in order to run.
+
+The `stop_review_app` job is **required** to have the following keywords defined:
+
+- `when` - [reference](#when)
+- `environment:name`
+- `environment:action`
#### dynamic environments
> [Introduced][ce-6323] in GitLab 8.12 and GitLab Runner 1.6.
`environment` can also represent a configuration hash with `name` and `url`.
-These parameters can use any of the defined CI [variables](#variables)
+These parameters can use any of the defined [CI variables](#variables)
(including predefined, secure variables and `.gitlab-ci.yml` variables).
-The common use case is to create dynamic environments for branches and use them
-as review apps.
-
----
-
-**Example configurations**
+For example:
```
deploy as review app:
stage: deploy
- script: ...
+ script: make deploy
environment:
name: review-apps/$CI_BUILD_REF_NAME
url: https://$CI_BUILD_REF_NAME.review.example.com/
```
The `deploy as review app` job will be marked as deployment to dynamically
-create the `review-apps/branch-name` environment.
+create the `review-apps/$CI_BUILD_REF_NAME` environment, which `$CI_BUILD_REF_NAME`
+is an [environment variable][variables] set by the Runner. If for example the
+`deploy as review app` job was run in a branch named `pow`, this environment
+should be accessible under `https://pow.review.example.com/`.
-This environment should be accessible under `https://branch-name.review.example.com/`.
+This of course implies that the underlying server which hosts the application
+is properly configured.
-You can see a simple example at https://gitlab.com/gitlab-examples/review-apps-nginx/.
+The common use case is to create dynamic environments for branches and use them
+as Review Apps. You can see a simple example using Review Apps at
+https://gitlab.com/gitlab-examples/review-apps-nginx/.
### artifacts
@@ -1105,3 +1213,5 @@ CI with various languages.
[examples]: ../examples/README.md
[ce-6323]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/6323
[environment]: ../environments.md
+[ce-6669]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/6669
+[variables]: ../variables/README.md
diff --git a/doc/development/README.md b/doc/development/README.md
index 9706cb1de7f..fb6a8a5b095 100644
--- a/doc/development/README.md
+++ b/doc/development/README.md
@@ -14,7 +14,8 @@
- [Testing standards and style guidelines](testing.md)
- [UI guide](ui_guide.md) for building GitLab with existing CSS styles and elements
- [Frontend guidelines](frontend.md)
-- [SQL guidelines](sql.md) for SQL guidelines
+- [SQL guidelines](sql.md) for working with SQL queries
+- [Sidekiq guidelines](sidekiq_style_guide.md) for working with Sidekiq workers
## Process
diff --git a/doc/development/performance.md b/doc/development/performance.md
index c4a964d1da3..8337c2d9cb3 100644
--- a/doc/development/performance.md
+++ b/doc/development/performance.md
@@ -37,7 +37,7 @@ graphs/dashboards.
GitLab provides built-in tools to aid the process of improving performance:
* [Sherlock](profiling.md#sherlock)
-* [GitLab Performance Monitoring](../monitoring/performance/monitoring.md)
+* [GitLab Performance Monitoring](../administration/monitoring/performance/monitoring.md)
* [Request Profiling](../administration/monitoring/performance/request_profiling.md)
GitLab employees can use GitLab.com's performance monitoring systems located at
diff --git a/doc/development/sidekiq_style_guide.md b/doc/development/sidekiq_style_guide.md
new file mode 100644
index 00000000000..e3a20f29a09
--- /dev/null
+++ b/doc/development/sidekiq_style_guide.md
@@ -0,0 +1,38 @@
+# Sidekiq Style Guide
+
+This document outlines various guidelines that should be followed when adding or
+modifying Sidekiq workers.
+
+## Default Queue
+
+Use of the "default" queue is not allowed. Every worker should use a queue that
+matches the worker's purpose the closest. For example, workers that are to be
+executed periodically should use the "cronjob" queue.
+
+A list of all available queues can be found in `config/sidekiq_queues.yml`.
+
+## Dedicated Queues
+
+Most workers should use their own queue. To ease this process a worker can
+include the `DedicatedSidekiqQueue` concern as follows:
+
+```ruby
+class ProcessSomethingWorker
+ include Sidekiq::Worker
+ include DedicatedSidekiqQueue
+end
+```
+
+This will set the queue name based on the class' name, minus the `Worker`
+suffix. In the above example this would lead to the queue being
+`process_something`.
+
+In some cases multiple workers do use the same queue. For example, the various
+workers for updating CI pipelines all use the `pipeline` queue. Adding workers
+to existing queues should be done with care, as adding more workers can lead to
+slow jobs blocking work (even for different jobs) on the shared queue.
+
+## Tests
+
+Each Sidekiq worker must be tested using RSpec, just like any other class. These
+tests should be placed in `spec/workers`.
diff --git a/doc/monitoring/performance/gitlab_configuration.md b/doc/monitoring/performance/gitlab_configuration.md
index a669bb28904..19d46135930 100644
--- a/doc/monitoring/performance/gitlab_configuration.md
+++ b/doc/monitoring/performance/gitlab_configuration.md
@@ -1 +1 @@
-This document was moved to [administration/monitoring/performance/gitlab_configuration](../administration/monitoring/performance/gitlab_configuration.md).
+This document was moved to [administration/monitoring/performance/gitlab_configuration](../../administration/monitoring/performance/gitlab_configuration.md).
diff --git a/doc/monitoring/performance/influxdb_configuration.md b/doc/monitoring/performance/influxdb_configuration.md
index 02647de1eb0..15fd275e916 100644
--- a/doc/monitoring/performance/influxdb_configuration.md
+++ b/doc/monitoring/performance/influxdb_configuration.md
@@ -1 +1 @@
-This document was moved to [administration/monitoring/performance/influxdb_configuration](../administration/monitoring/performance/influxdb_configuration.md).
+This document was moved to [administration/monitoring/performance/influxdb_configuration](../../administration/monitoring/performance/influxdb_configuration.md).
diff --git a/doc/monitoring/performance/influxdb_schema.md b/doc/monitoring/performance/influxdb_schema.md
index a989e323e04..e53f9701dc3 100644
--- a/doc/monitoring/performance/influxdb_schema.md
+++ b/doc/monitoring/performance/influxdb_schema.md
@@ -1 +1 @@
-This document was moved to [administration/monitoring/performance/influxdb_schema](../administration/monitoring/performance/influxdb_schema.md).
+This document was moved to [administration/monitoring/performance/influxdb_schema](../../administration/monitoring/performance/influxdb_schema.md).
diff --git a/doc/monitoring/performance/introduction.md b/doc/monitoring/performance/introduction.md
index ab3f3ac1664..ae88baa0c14 100644
--- a/doc/monitoring/performance/introduction.md
+++ b/doc/monitoring/performance/introduction.md
@@ -1 +1 @@
-This document was moved to [administration/monitoring/performance/introduction](../administration/monitoring/performance/introduction.md).
+This document was moved to [administration/monitoring/performance/introduction](../../administration/monitoring/performance/introduction.md).
diff --git a/doc/project_services/img/builds_emails_service.png b/doc/project_services/img/builds_emails_service.png
index 88943dc410e..440728795be 100644
--- a/doc/project_services/img/builds_emails_service.png
+++ b/doc/project_services/img/builds_emails_service.png
Binary files differ
diff --git a/doc/raketasks/backup_restore.md b/doc/raketasks/backup_restore.md
index 26baffdf792..fc0cd1b8af2 100644
--- a/doc/raketasks/backup_restore.md
+++ b/doc/raketasks/backup_restore.md
@@ -30,6 +30,10 @@ Use this if you've installed GitLab from source:
```
sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production
```
+If you are running GitLab within a Docker container, you can run the backup from the host:
+```
+docker -t exec <container name> gitlab-rake gitlab:backup:create
+```
You can specify that portions of the application data be skipped using the
environment variable `SKIP`. You can skip:
diff --git a/doc/university/glossary/README.md b/doc/university/glossary/README.md
index a86ff165f2e..cf836667fac 100644
--- a/doc/university/glossary/README.md
+++ b/doc/university/glossary/README.md
@@ -6,83 +6,87 @@ Please add any terms that you discover that you think would be useful for others
### 2FA
-User authentication by combination of 2 different steps during login. This allows for more security.
+User authentication by combination of 2 different steps during login. This allows for [more security](https://about.gitlab.com/handbook/security/).
### 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](http://doc.gitlab.com/ce/permissions/permissions.html)
### Active Directory (AD)
-A Microsoft based directory service for windows domain networks. It uses LDAP technology under the hood
+A Microsoft-based [directory service](https://msdn.microsoft.com/en-us/library/bb742424.aspx) for windows domain networks. It uses LDAP technology under the hood.
### Agile
-Building and delivering software in phases/parts rather than trying to build everything at once then delivering to the user/client. The later is known as a WaterFall model
+Building and [delivering software](http://agilemethodology.org/) in phases/parts rather than trying to build everything at once then delivering to the user/client. The latter is known as the WaterFall model.
### Application Lifecycle Management (ALM)
-Entire product lifecycle management process for an application. From requirements management, development and testing until deployment.
+The entire product lifecycle management process for an application, from requirements management, development, and testing until deployment. GitLab has [advantages](https://docs.google.com/presentation/d/1vCU-NbZWz8NTNK8Vu3y4zGMAHb5DpC8PE5mHtw1PWfI/edit#slide=id.g72f2e4906_2_288) over both legacy and modern ALM tools.
### Artifactory
-Version control for binaries.
+A version control [system](https://www.jfrog.com/open-source/#os-arti) for non-text files.
### Artifacts
-objects (usually binary and large) created by a build process
+Objects (usually binary and large) created by a build process. These can include use cases, class diagrams, requirements and design documents.
### Atlassian
-A company that develops software products for developers and project managers including Bitbucket, Jira, Hipchat, Confluence, Bamboo. See [Atlassian] (https://www.atlassian.com)
+A [company](https://www.atlassian.com) that develops software products for developers and project managers including Bitbucket, Jira, Hipchat, Confluence, Bamboo.
### Audit Log
-*** Needs definition here
+Also called an [audit trail](https://en.wikipedia.org/wiki/Audit_trail), an audit log is a document that records an event in an IT system.
### Auto Defined User Group
-User groups are a way of centralizing control over important management tasks, particularly access control and password policies.
-A simple example of such groups are the users and the admins groups.
-In most of the cases these groups are auto defined in terms of access, rules of usage, conditions to be part of, etc...
+User groups are a way of centralizing control over important management tasks, particularly access control and password policies. A simple example of such groups are the users and the admins groups.
+In most of the cases these groups are auto defined in terms of access, rules of usage, conditions to be part of, etc.
### Bamboo
-Atlassian's CI tool similar to GitLab CI and Jenkins
+Atlassian's CI tool similar to GitLab CI and Jenkins.
### Basic Subscription
-Entry level subscription for GitLab EE currently available in packs of 10 see [Basic subscription](https://about.gitlab.com/pricing/)
+Entry level [subscription](https://about.gitlab.com/pricing/) for GitLab EE currently available in packs of 10.
### Bitbucket
-Atlassian's web hosting service for Git and Mercurial Projects i.e. GitLab.com competitor
+Atlassian's web hosting service for Git and Mercurial Projects. Read about [migrating](https://docs.gitlab.com/ce/workflow/importing/import_projects_from_bitbucket.html) from BitBucket to a GitLab instance.
### Branch
-A branch is a parallel version of a repository. Allows you to work on the repository without you affecting the "master" branch. Allows you to make changes without affecting the current "live" version. When you have made all your changes to your branch you can then merge to the master and to make the changes fo "live".
+A branch is a parallel version of a repository. This allows you to work on the repository without affecting the "master" branch, and without affecting the current "live" version. When you have made all your changes to your branch you can then merge to the master. When your merge request is accepted your changes will be "live."
### Branded Login
-Having your own logo on your GitLab instance login page instead of the GitLab logo.
+Having your own logo on [your GitLab instance login page](https://docs.gitlab.com/ee/customization/branded_login_page.html) instead of the GitLab logo.
+
+### Build triggers
+These protect your code base against breaks, for instance when a team is working on the same project. Learn about [setting up](https://docs.gitlab.com/ce/ci/triggers/README.html) build triggers.
### CEPH
-is a distributed object store and file system designed to provide excellent performance, reliability and scalability.
+ A distributed object store and file [system](http://ceph.com/) designed to provide excellent performance, reliability and scalability.
+
+### ChatOps
+
+The ability to [initiate an action](https://gitlab.com/gitlab-org/omnibus-gitlab/issues/1412) from chat. ChatBots run in your chat application and give you the ability to do "anything" from chat.
### Clone
-A copy of a repository stored on your machine that allows you to use your own editor without being online, but still tracks the changes made remotely.
+A [copy](https://git-scm.com/docs/git-clone) of a repository stored on your machine that allows you to use your own editor without being online, but still tracks the changes made remotely.
### Code Review
-Examination of a progam's code. The main aim is to maintain high standards quality of code that is being shipped.
+Examination of a progam's code. The main aim is to maintain high quality standards of code that is being shipped. Merge requests [serve as a code review tool](https://about.gitlab.com/2014/09/29/gitlab-flow/) in GitLab.
### Code Snippet
-A small amount of code. Usually for the purpose of showing other developers how
-to do something specific or reproduce a problem.
+A small amount of code, usually selected for the purpose of showing other developers how to do something specific or reproduce a problem.
### Collaborator
@@ -90,31 +94,39 @@ Person with read and write access to a repository who has been invited by reposi
### Commit
-Is a change (revision) to a file, and also creates an ID that allows you to see revision history and who made the changes.
+A [change](https://git-scm.com/docs/git-commit) (revision) to a file that also creates an ID, allowing you to see revision history and the author of the changes.
### Community
-Everyone who is using GitLab
+[Everyone](https://about.gitlab.com/community/) who uses GitLab.
### Confluence
-Atlassian's product for collaboration of documents and projects.
+Atlassian's product for collaboration on documents and projects.
-### Continuous Deivery
+### Continuous Delivery
-Continuous delivery is a series of practices designed to ensure that code can be rapidly and safely deployed to production by delivering every change to a production-like environment and ensuring business applications and services function as expected through rigorous automated testing.
+A [software engineering approach](https://about.gitlab.com/2016/08/05/continuous-integration-delivery-and-deployment-with-gitlab/) in which continuous integration, automated testing, and automated deployment capabilities allow software to be developed and deployed rapidly, reliably and repeatedly with minimal human intervention. Still, the deployment to production is defined strategically and triggered manually.
### Continuous Deployment
-Continuous deployment is the next step of continuous delivery: Every change that passes the automated tests is deployed to production automatically.
+A [software development practice](https://about.gitlab.com/2016/08/05/continuous-integration-delivery-and-deployment-with-gitlab/) in which every code change goes through the entire pipeline and is put into production automatically, resulting in many production deployments every day. It does everything that Continuous Delivery does, but the process is fully automated, there's no human intervention at all.
### Continuous Integration
-A process that involves adding new code commits to source code with the combined code being run on an automated test to ensure that the changes do not break the software.
+A [software development practice](https://about.gitlab.com/2016/08/05/continuous-integration-delivery-and-deployment-with-gitlab/) in which you build and test software every time a developer pushes code to the application, and it happens several times a day.
### Contributor
-Term used to a person contributing to an Open Source Project.
+Term used for a person contributing to an open source project.
+
+### Conversational Development (ConvDev)
+
+A [natural evolution](https://about.gitlab.com/2016/09/14/gitlab-live-event-recap/) of software development that carries a conversation across functional groups throughout the development process, enabling developers to track the full path of development in a cohesive and intuitive way. ConvDev accelerates the development lifecycle by fostering collaboration and knowledge sharing from idea to production.
+
+### Cycle Time
+
+The time it takes to move from [idea to production](https://about.gitlab.com/2016/08/05/continuous-integration-delivery-and-deployment-with-gitlab/#from-idea-to-production-with-gitlab).
### Data Centre
@@ -122,41 +134,59 @@ Atlassian product for High Availability.
### Deploy Keys
-An SSH key stored on the your server that grants access to a single GitLab repository. This is used by a GitLab runner to clone a project's code so that tests can be run against the checked out code.
+A [SSH key](https://docs.gitlab.com/ce/gitlab-basics/create-your-ssh-keys.html)stored on your server that grants access to a single GitLab repository. This is used by a GitLab runner to clone a project's code so that tests can be run against the checked out code.
### Developer
-For us (GitLab) this means a software developer, i.e. someone who makes software. It is also one of the levels of access in our multi level approval system.
+For us at GitLab, this means a software developer, or someone who makes software. It is also one of the levels of access in our multi-level approval system.
+
+### DevOps
+
+The intersection of software engineering, quality assurance, and technology operations. Explore more DevOps topics in the [glossary by XebiaLabs](https://xebialabs.com/glossary/)
### Diff
-Is the difference between two commits, or saved changes. This will also be shown visually after the changes.
+The difference between two commits, or saved changes. This will also be shown visually after the changes.
-### Docker
+#### Directory
-Containers wrap up a piece of software in a complete filesystem that contains everything it needs to run: code, runtime, system tools, system libraries – anything you can install on a server.
-This guarantees that it will always run the same, regardless of the environment it is running in.
+A folder used for storing multiple files.
+
+### Docker Container Registry
+
+A [feature](https://docs.gitlab.com/ce/user/project/container_registry.html) of GitLab projects. Containers wrap up a piece of software in a complete filesystem that contains everything it needs to run: code, runtime, system tools, system libraries – anything you can install on a server. This guarantees that it will always run the same, regardless of the environment it is running in.
+
+### Dynamic Environment
+
+### ElasticSearch
+
+Elasticsearch is a flexible, scalable and powerful search service. When [enabled](https://gitlab.com/help/integration/elasticsearch.md), it helps keep GitLab's search fast when dealing with a huge amount of data.
+
+### Emacs
### Fork
-Your own copy of a repository that allows you to make changes to the repository without affecting the original.
+Your [own copy](https://docs.gitlab.com/ce/workflow/forking_workflow.html) of a repository that allows you to make changes to the repository without affecting the original.
### Gerrit
-A code review tool built on top of Git.
+A code review [tool](https://www.gerritcodereview.com/) built on top of Git.
+
+### Git Attributes
+
+A [git attributes file](https://git-scm.com/docs/gitattributes) is a simple text file that gives attributes to pathnames.
### Git Hooks
-Are scripts you can use to trigger actions at certain points.
+[Scripts](https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks) you can use to trigger actions at certain points.
### GitHost.io
-Is a single-tenant solution that provides GitLab CE or EE as a managed service. GitLab Inc. is responsible for
-installing, updating, hosting, and backing up customers own private and secure GitLab instance.
+A single-tenant solution that provides GitLab CE or EE as a managed service. GitLab Inc. is responsible for installing, updating, hosting, and backing up customers' own private and secure GitLab instance.
### GitHub
-A web-based Git repository hosting service with an enterprise offering. Its main features are: issue tracking, pull request with code review, abundancy of integrations and wiki. As of April 2016, the service has over 14 million users. It offers free public repos, private repos and enterprise services are paid.
+A web-based Git repository hosting service with an enterprise offering. Its main features are: issue tracking, pull request with code review, abundancy of integrations and wiki. It offers free public repos, private repos and enterprise services are paid. Read about [importing a project](https://docs.gitlab.com/ce/workflow/importing/import_projects_from_github.html) from GitHub to GitLab.
### GitLab CE
@@ -164,51 +194,78 @@ Our free on Premise solution with >100,000 users
### GitLab CI
-Our own Continuos Integration feature that is shipped with each instance
+Our own Continuos Integration [feature](https://about.gitlab.com/gitlab-ci/) that is shipped with each instance
### GitLab EE
-Our premium on premise solution that currently has Basic, Standard and Plus subscription packages with additional features and support.
+Our premium on premise [solution](https://about.gitlab.com/features/#enterprise) that currently has Basic, Standard and Plus subscription packages with additional features and support.
### GitLab.com
Our free SaaS for public and private repositories.
+### GitLab Geo
+
+Allows you to replicate your GitLab instance to other geographical locations as a read-only fully operational version. It [can be used](https://docs.gitlab.com/ee/gitlab-geo/README.html) for cloning and fetching projects, in addition to reading any data. This will make working with large repositories over large distances much faster.
+
+### GitLab Pages
+These allow you to [create websites](https://gitlab.com/help/pages/README.md) for your GitLab projects, groups, or user account.
+
### Gitolite
-Is basically an access layer that sits on top of Git. Users are granted access to repos via a simple config file and you as an admin only needs the users public SSH key and a username from the user.
+An [access layer](https://git-scm.com/book/en/v1/Git-on-the-Server-Gitolite) that sits on top of Git. Users are granted access to repos via a simple config file. As an admin, you only need the users' public SSH key and a username.
### Gitorious
-A web based hosting service for projects using Git. It was acquired by GitLab and we discontinued the service. [Gitorious Acquisition Blog Post](https://about.gitlab.com/2015/03/03/gitlab-acquires-gitorious/)
+A web-based hosting service for projects using Git. It was acquired by GitLab and we discontinued the service. Read the[Gitorious Acquisition Blog Post](https://about.gitlab.com/2015/03/03/gitlab-acquires-gitorious/).
+
+### Go
+
+An open source programming [language](https://golang.org/).
-### HADR
+### GUI/ Git GUI
-Sometimes written HA/DR. High Availability for Disaster Recovery. Usually refers to a strategy having a failover server in place in case the main server fails.
+A portable [graphical interface](https://git-scm.com/docs/git-gui) to Git that allows users to make changes to their repository by making new commits, amending existing ones, creating branches, performing local merges, and fetching/pushing to remote repositories.
+
+### High Availability for Disaster Recovery (HADR)
+
+Sometimes written HA/DR, this usually refers to a strategy for having a failover server in place in case the main server fails.
### Hip Chat
-Atlassian's real time chat application for teams. Competitor to Slack, RocketChat and MatterMost.
+Atlassian's real time chat application for teams, Hip Chat is a competitor to Slack, RocketChat and MatterMost.
### High Availability
-Refers to a system or component that is continuously operational for a desirably long length of time. Availability can be measured relative to "100% operational" or "never failing."
+Refers to a [system or component](https://about.gitlab.com/high-availability/) that is continuously operational for a desirably long length of time. Availability can be measured relative to "100% operational" or "never failing."
+
+### Inner-sourcing
+
+The [use of](https://about.gitlab.com/2014/09/05/innersourcing-using-the-open-source-workflow-to-improve-collaboration-within-an-organization/) open source development techniques within the corporation.
+
+### Internet Relay Chat (IRC)
+
+An [application layer protocol](http://www.irchelp.org/) that facilitates communication in the form of text.
### Issue Tracker
-A tool used to manage, organize, and maintain a list of issues, making it easier for an organization to manage.
+A [tool](https://docs.gitlab.com/ee/integration/external-issue-tracker.html) used to manage, organize, and maintain a list of issues, making it easier for an organization to manage.
### Jenkins
-An Open Source CI tool written using the Java programming language. Does the same job as GitLab CI, Bamboo, Travis CI. It is extremely popular. see [Jenkins](https://jenkins-ci.org/)
+An Open Source CI tool written using the Java programming language. [Jenkins](https://jenkins-ci.org/) does the same job as GitLab CI, Bamboo, and Travis CI. It is extremely popular.
### Jira
-Atlassian's project management software. i.e. a complex issue tracker. See[Jira](https://www.atlassian.com/software/jira)
+Atlassian's [project management software](https://www.atlassian.com/software/jira), i.e. a complex issue tracker. GitLab [can be configured](https://docs.gitlab.com/ee/project_services/jira.html) to interact with JIRA Core either using an on-premise instance or the SaaS solution that Atlassian offers.
+
+### JUnit
+
+A testing framework for the Java programming language, [JUnit](http://junit.org/junit4/) has been important in the evolution of test-driven development.
### Kerberos
-A network authentication protocol that uses secret-key cryptography for security.
+A network authentication [protocol](http://web.mit.edu/kerberos/) that uses secret-key cryptography for security.
### Kubernetes
@@ -216,23 +273,27 @@ An open source container cluster manager originally designed by Google. It's bas
### Labels
-An identifier to describe a group of one or more specific file revisions
+An [identifier](https://docs.gitlab.com/ce/user/project/labels.html) to describe a group of one or more specific file revisions.
-### LDAP
+### Lightweight Directory Access Protocol (LDAP)
-Lightweight Directory Access Protocol - basically its a directory (electronic address book) with user information e.g. name, phone_number etc
+ A directory (electronic address book) with user information (e.g. name, phone_number etc.)
### LDAP User Authentication
-Allowing GitLab to sign in people from an LDAP server i.e. Allow people whose names are on the electronic user directory server) to be able to use their LDAP accounts to login.
+GitLab [integrates](https://docs.gitlab.com/ce/administration/auth/ldap.html) with LDAP to support user authentication. This enables GitLab to sign in people from an LDAP server (i.e., allowing people whose names are on the electronic user directory server to be able to use their LDAP accounts to login.)
### LDAP Group Sync
Allows you to synchronize the members of a GitLab group with one or more LDAP groups.
-### Git LFS
+### Load Balancer
-Git Large File Storage. A way to enable git to handle large binary files by using reference pointers within small text files to point to the large files.
+A [device](https://en.wikipedia.org/wiki/Load_balancing_(computing)) that distributes network or application traffic across multiple servers.
+
+### Git Large File Storage (LFS)
+
+A way [to enable](https://about.gitlab.com/2015/11/23/announcing-git-lfs-support-in-gitlab/) git to handle large binary files by using reference pointers within small text files to point to the large files. Large files such as high resolution images and videos, audio files, and assets can be called from a remote server.
### Linux
@@ -240,8 +301,7 @@ An operating system like Windows or OS X. It is mostly used by software develope
### Markdown
-Is a lightweight markup language with plain text formatting syntax designed so that it can be converted to HTML and many other formats using a tool by the same name.
-Markdown is often used to format readme files, for writing messages in online discussion forums, and to create rich text using a plain text editor.
+A lightweight markup language with plain text formatting syntax designed so that it can be converted to HTML and many other formats using a tool by the same name. Markdown is often used to format readme files, for writing messages in online discussion forums, and to create rich text using a plain text editor. Checkout GitLab's [Markdown guide](https://gitlab.com/help/user/markdown.md).
### Maria DB
@@ -249,193 +309,215 @@ A community developed fork/variation of MySQL. MySQL is owned by Oracle.
### Master
-Name of the default branch in every git repository.
+Name of the [default branch](https://git-scm.com/book/en/v1/Git-Branching-What-a-Branch-Is) in every git repository.
+
+### Mattermost
+
+An open source, self-hosted messaging alternative to Slack. View GitLab's Mattermost [feature](https://gitlab.com/gitlab-org/gitlab-mattermost).
### Mercurial
-A free distributed version control system like Git. Think of it as a competitor to Git.
+A free distributed version control system similar to and a competitor with Git.
### Merge
-Takes changes from one branch, and applies them into another branch.
+Takes changes from one branch, and [applies them](https://git-scm.com/docs/git-merge) into another branch.
+
+### Merge Conflict
+
+[Arises](https://about.gitlab.com/2016/09/06/resolving-merge-conflicts-from-the-gitlab-ui/) when a merge can't be performed cleanly between two versions of the same file.
### Meteor
-A hip platform for building javascript apps.[Meteor] (https://www.meteor.com)
+A [platform](https://www.meteor.com) for building javascript apps.
### Milestones
-Allows you to track the progress on issues, and merge requests, which allows you to get a snapshot of the progress made.
+Allow you to [organize issues](https://docs.gitlab.com/ce/workflow/milestones.html) and merge requests in GitLab into a cohesive group, optionally setting a due date. A common use is keeping track of an upcoming software version. Milestones are created per-project.
### Mirror Repositories
-You can set up a project to automatically have its branches, tags, and commits updated from an upstream repository. This is useful when a repository you're interested in is located on a different server, and you want to be able to browse its content and its activity using the familiar GitLab interface.
+A project that is setup to automatically have its branches, tags, and commits [updated from an upstream repository](https://docs.gitlab.com/ee/workflow/repository_mirroring.html). This is useful when a repository you're interested in is located on a different server, and you want to be able to browse its content and activity using the familiar GitLab interface.
### MIT License
-A type of software license. It lets people do anything with your code with proper attribution and without warranty. It is the most common license for open source applications written in Ruby on Rails. GitLab CE is issued under this license.
-This means, you can download the code, modify it as you want even build a new commercial product using the underlying code and its not illegal. The only condition is that there is no form of waranty provided by GitLab so whatever happens if you use the code is your own problem.
-
-### Mondo
+A type of software license. It lets people do anything with your code with proper attribution and without warranty. It is the most common license for open source applications written in Ruby on Rails. GitLab CE is issued under this [license](https://docs.gitlab.com/ce/development/licensing.html). This means you can download the code, modify it as you want, and even build a new commercial product using the underlying code and it's not illegal. The only condition is that there is no form of warranty provided by GitLab so whatever happens when you use the code is your own problem.
-*** Needs definition here
+### Mondo Rescue
-### Multi LDAP Server
+A free disaster recovery [software](https://help.ubuntu.com/community/MondoMindi).
-*** Needs definition here
+### MySQL
-### My SQL
-
-A relational database. Currently only supported if you are using EE. It is owned by Oracle.
+A relational [database](http://www.mysql.com/) owned by Oracle. Currently only supported if you are using EE.
### Namespace
-In computing, a namespace is a set of symbols that are used to organize objects of various kinds, so that these objects may be referred to by name.
-
-Prominent examples include:
-- file systems are namespaces that assign names to files;
-- programming languages organize their variables and subroutines in namespaces;
-- computer networks and distributed systems assign names to resources, such as computers, printers, websites, (remote) files, etc.
+A set of symbols that are used to organize objects of various kinds so that these objects may be referred to by name. Examples of namespaces in action include file systems that assign names to files; programming languages that organize their variables and subroutines in namespaces; and computer networks and distributed systems that assign names to resources, such as computers, printers, websites, (remote) files, etc.
### Nginx
-(pronounced "engine x") is a web server. It can act as a reverse proxy server for HTTP, HTTPS, SMTP, POP3, and IMAP protocols, as well as a load balancer and an HTTP cache.
+A web [server](https://www.nginx.com/resources/wiki/) (pronounced "engine x"). It can act as a reverse proxy server for HTTP, HTTPS, SMTP, POP3, and IMAP protocols, as well as a load balancer and an HTTP cache.
-### oAuth
+### OAuth
-Is an open standard for authorization, commonly used as a way for Internet users to log into third party websites using their Microsoft, Google, Facebook or Twitter accounts without exposing their password.
+An open standard for authorization, commonly used as a way for internet users to log into third party websites using their Microsoft, Google, Facebook or Twitter accounts without exposing their password. GitLab [is](https://docs.gitlab.com/ce/integration/oauth_provider.html) an OAuth2 authentication service provider.
### Omnibus Packages
-Omnibus is a way to package the different services and tools required to run GitLab, so that users can install it without as much work.
+A way to [package different services and tools](https://docs.gitlab.com/omnibus/) required to run GitLab, so that most developers can install it without laborious configuration.
### On Premise
-On your own server. In GitLab, this refers to the ability to download GitLab EE/GitLab CE and host it on your own server rather than using GitLab.com which is hosted by GitLab Inc's servers.
+On your own server. In GitLab, this [refers](https://about.gitlab.com/2015/02/12/why-ship-on-premises-in-the-saas-era/) to the ability to download GitLab EE/GitLab CE and host it on your own server rather than using GitLab.com, which is hosted by GitLab Inc's servers.
+
+### Open Core
+
+GitLab's [business model](https://about.gitlab.com/2016/07/20/gitlab-is-open-core-github-is-closed-source/). Coined by Andrew Lampitt in 2008, the [open core model](https://en.wikipedia.org/wiki/Open_core) primarily involves offering a "core" or feature-limited version of a software product as free and open-source software, while offering "commercial" versions or add-ons as proprietary software.
### Open Source Software
-Software for which the original source code is freely available and may be redistributed and modified.
+Software for which the original source code is freely [available](https://opensource.org/docs/osd) and may be redistributed and modified. GitLab prioritizes open source [stewardship](https://about.gitlab.com/2016/01/11/being-a-good-open-source-steward/).
### Owner
-This is the most powerful person on a GitLab project. He has the permissions of all the other users plus the additional permission of being able to destroy i.e. delete the project
+The most powerful person on a GitLab project. They have the permissions of all the other users plus the additional permission of being able to destroy (i.e. delete) the project.
-### PaaS
+### Platform as a Service (PaaS)
-Typically referred to in regards to application development, it is a model in which a cloud provider delivers hardware and software tools to its users as a service
+Typically referred to in regards to application development, PaaS is a model in which a cloud provider delivers hardware and software tools to its users as a service.
### Perforce
-The company that produces Helix. A commercial, proprietary, centralised VCS well known for it's ability to version files of any size and type. They OEM a re-branded version of GitLab called "GitSwarm" that is tightly integrated with their "GitFusion" product, which in turn represents a portion of a Helix repository (called a depot) as a git repo
+The company that produces Helix. A commercial, proprietary, centralised VCS well known for its ability to version files of any size and type. They OEM a re-branded version of GitLab called "GitSwarm" that is tightly integrated with their "GitFusion" product, which in turn represents a portion of a Helix repository (called a depot) as a git repo.
### Phabricator
-Is a suite of web-based software development collaboration tools, including the Differential code review tool, the Diffusion repository browser, the Herald change monitoring tool, the Maniphest bug tracker and the Phriction wiki. Phabricator integrates with Git, Mercurial, and Subversion.
+A suite of web-based software development collaboration tools, including the Differential code review tool, the Diffusion repository browser, the Herald change monitoring tool, the Maniphest bug tracker and the Phriction wiki. Phabricator integrates with Git, Mercurial, and Subversion.
### Piwik Analytics
-An open source analytics software to help you analyze web traffic. It is similar to google analytics only that google analytics is not open source and information is stored by google while in Piwik the information is stored in your own server hence fully private.
+An open source analytics software to help you analyze web traffic. It is similar to Google Analytics, except that the latter is not open source and information is stored by Google. In Piwik, the information is stored on your own server and hence is fully private.
### Plus Subscription
-GitLab Premium EE subscription that includes training and dedicated Account Management and Service Engineer and complete support package [Plus subscription](https://about.gitlab.com/pricing/)
+GitLab Premium EE [subscription](https://about.gitlab.com/pricing/) that includes training and dedicated Account Management and Service Engineer and complete support package.
### PostgreSQL
-A relational database. Touted as the most advanced open source database.
+An [object-relational](https://en.wikipedia.org/wiki/PostgreSQL) database. Touted as the most advanced open source database, it is one of two database management systems [supported by](https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/doc/settings/database.md) GitLab, the other being MySQL.
### Protected Branches
-A feature that protects branches from unauthorized pushes, force pushing or deletion.
+A [feature](https://docs.gitlab.com/ce/user/project/protected_branches.html) that protects branches from unauthorized pushes, force pushing or deletion.
### Pull
-Git command to synchronize the local repository with the remote repository, by fetching all remote changes and merging them into the local repository.
+Git command to [synchronize](https://git-scm.com/docs/git-pull) the local repository with the remote repository, by fetching all remote changes and merging them into the local repository.
### Puppet
-A popular devops automation tool
+A popular DevOps [automation tool](https://puppet.com/product/how-puppet-works).
### Push
-Git command to send commits from the local repository to the remote repository.
+Git [command](https://git-scm.com/docs/git-push) to send commits from the local repository to the remote repository. Read about [advanced push rules](https://gitlab.com/help/pages/README.md) in GitLab.
### RE Read Only
-Permissions to see a file and it's contents, but not change it
+Permissions to see a file and its contents, but not change it.
### Rebase
-Moves a branch from one commit to another. This allows you to re-write your project's history.
+In addition to the merge, the [rebase](https://git-scm.com/book/en/v2/Git-Branching-Rebasing) is a main way to integrate changes from one branch into another.
-### Git Repository
+### (Git) Repository
-Storage location of all files which are tracked by git.
+A directory where Git [has been initiatlized](https://git-scm.com/book/en/v2/Git-Basics-Getting-a-Git-Repository) to start version controlling your files. The history of your work is stored here. A remote repository is not on your machine, but usually online (like on GitLab.com, for instance). The main remote repository is usually called "Origin."
### Requirements management
-*** Needs definition here
-
-### Revision
-
-*** Needs definition here
+Gives your distributed teams a single shared repository to collaborate and share requirements, understand their relationship to tests, and evaluate linked defects. It includes multiple, preconfigured requirement types.
### Revision Control
-Also known as version control or source control, is the management of changes to documents, computer programs, large web sites, and other collections of information. Changes are usually identified by a number or letter code, termed the "revision number", "revision level", or simply "revision".
+Also known as version control or source control, this is the management of changes to documents, computer programs, large web sites, and other collections of information. Changes are usually identified by a number or letter code, termed the "revision number," "revision level," or simply "revision."
### RocketChat
-An open source chat application for teams. Very similar to Slack only that is is open-source.
+An open source chat application for teams, RocketChat is very similar to Slack but it is also open-source.
+
+### Route Table
+
+A route table contains rules (called routes) that determine where network traffic is directed. Each [subnet in a VPC](http://docs.aws.amazon.com/AmazonVPC/latest/UserGuide/VPC_Route_Tables.html) must be associated with a route table.
### Runners
-Actual build machines/containers that run/execute tests you have specified to be run on GitLab CI
+Actual build machines/containers that [run and execute tests](https://gitlab.com/gitlab-org/gitlab-ci-multi-runner) you have specified to be run on GitLab CI.
+
+### Sidekiq
+
+The background job processor GitLab [uses](https://docs.gitlab.com/ce/administration/troubleshooting/sidekiq.html) to asynchronously run tasks.
-### SaaS
+### Software as a service (SaaS)
-Software as a service. Software is hosted centrally and accessed on-demand i.e. when you want to. This refers to GitLab.com in our scenario
+Software that is hosted centrally and accessed on-demand (i.e. whenever you want to). This applies to GitLab.com.
-### SCM
+### Software Configuration Management (SCM)
-Software Configuration Management. Often used by people when they mean Version Control
+This term is often used by people when they mean "Version Control."
## Scrum
-An Agile framework designed to help complete complex (typically) software projects. It's made up of several parts: product requirments backlog, sprint plannnig, sprint (development), sprint review, retrospec (analyzing the sprint). The goal is to end up with potentially shippable products.
+An Agile [framework](https://www.scrum.org/Resources/What-is-Scrum) designed to typically help complete complex software projects. It's made up of several parts: product requirements backlog, sprint planning, sprint (development), sprint review, and retrospec (analyzing the sprint). The goal is to end up with potentially shippable products.
### Scrum Board
The board used to track the status and progress of each of the sprint backlog items.
+### Shell
+
+Terminal on Mac OSX, GitBash on Windows, or Linux Terminal on Linux. You [use git]() and make changes to GitLab projects in your shell. You [use git](https://docs.gitlab.com/ce/gitlab-basics/start-using-git.html) and make changes to GitLab projects in your shell.
+
+### Single-tenant
+
+The tenant purchases their own copy of the software and the software can be customized to meet the specific and needs of that customer. [GitHost.io](https://about.gitlab.com/handbook/positioning-faq/) is our provider of single-tenant 'managed cloud' GitLab instances.
+
### Slack
-Real time messaging app for teams. Used internally by GitLab
+Real time messaging app for teams that is used internally by GitLab team members. GitLab users can enable [Slack integration](https://docs.gitlab.com/ce/project_services/slack.html) to trigger push, issue, and merge request events among others.
### Slave Servers
-Also known as secondary servers. They help to spread the load over multiple machines, they also provide backups when the master/primary server crashes.
+Also known as secondary servers, these help to spread the load over multiple machines. They also provide backups when the master/primary server crashes.
### Source Code
-Program code as typed by a computer programmer. i.e. it has not yet been compiled/translated by the computer to machine language.
+Program code as typed by a computer programmer (i.e. it has not yet been compiled/translated by the computer to machine language).
### SSH Key
-A unique identifier of a computer. It is used to identify computers without the need for a password. e.g. On GitLab I have added the ssh key of all my work machines so that the GitLab instance knows that it can accept code pushes and pulls from this trusted machines whose keys are I have added.
+A unique identifier of a computer. It is used to identify computers without the need for a password (e.g., On GitLab I have [added the ssh key](https://docs.gitlab.com/ce/gitlab-basics/create-your-ssh-keys.html) of all my work machines so that the GitLab instance knows that it can accept code pushes and pulls from this trusted machines whose keys are I have added.)
-### SSO
+### Single Sign On (SSO)
-Single Sign On. An authentication process that allows you enter one username and password to access multiple applications.
+An authentication process that allows you enter one username and password to access multiple applications.
+
+### Staging Area
+
+[Staging occurs](https://git-scm.com/book/en/v2/Getting-Started-Git-Basics) before the commit process in git. The staging area is a file, generally contained in your Git directory, that stores information about what will go into your next commit. It’s sometimes referred to as the “index.""
### Standard Subscription
-Our mid range EE subscription that includes 24/7 support, support for High Availability [Standard Subscription](https://about.gitlab.com/pricing/)
+Our mid range EE subscription that includes 24/7 support and support for High Availability [Standard Subscription](https://about.gitlab.com/pricing/).
### Stash
-Atlassian's Git On-Premises solution. Think of it as Atlassian's GitLab EE. It is now known as BitBucket Server.
+Atlassian's Git on-premise solution. Think of it as Atlassian's GitLab EE, now known as BitBucket Server.
+
+### Static Site Generators (SSGs)
+
+A [software](https://wiki.python.org/moin/StaticSiteGenerator) that takes some text and templates as input and produces html files on the output.
### Subversion
@@ -443,40 +525,65 @@ Non-proprietary, centralized version control system.
### Sudo
-A program that allows you to perform superuser/administrator actions on Unix Operating Systems e.g. Linux, OS X. It actually stands for 'superuser do'
+A program that allows you to perform superuser/administrator actions on Unix Operating Systems (e.g., Linux, OS X.) It actually stands for 'superuser do.'
-### SVN
+### Subversion (SVN)
-Abbreviation for Subversion.
+An open source version control system. Read about [migrating from SVN](https://docs.gitlab.com/ce/workflow/importing/migrating_from_svn.html) to GitLab using SubGit.
### Tag
-Represents a version of a particular branch at a moment in time.
+[Represents](https://docs.gitlab.com/ce/api/tags.html) a version of a particular branch at a moment in time.
### Tool Stack
-Set of tools used in a process to achieve a common outcome. E.g. set of tools used in Application Lifecycle Management.
+The set of tools used in a process to achieve a common outcome (e.g. set of tools used in Application Lifecycle Management).
### Trac
-An Open Source project management and bug tracking web application.
+An open source project management and bug tracking web [application](https://trac.edgewall.org/).
+
+### Untracked files
+
+New files that Git has not [been told](https://git-scm.com/book/en/v2/Git-Basics-Recording-Changes-to-the-Repository) to track previously.
### User
Anyone interacting with the software.
-### VCS
+### Version Control Software (VCS)
+
+Version control is a system that records changes to a file or set of files over time so that you can recall specific versions later. VCS [has evolved](https://docs.google.com/presentation/d/16sX7hUrCZyOFbpvnrAFrg6tVO5_yT98IgdAqOmXwBho/edit#slide=id.gd69537a19_0_32) from local version control systems, to centralized version control systems, to the present distributed version control systems like Git, Mercurial, Bazaar, and Darcs.
+
+### Virtual Private Cloud (VPC)
+
+An on demand configurable pool of shared computing resources allocated within a public cloud environment, providing some isolation between the different users using the resources. GitLab users need to create a new Amazon VPC in order to [setup High Availability](https://docs.gitlab.com/ce/university/high-availability/aws/).
-Version Control Software
+### Virtual private server (VPS)
+
+A [virtual machine](https://en.wikipedia.org/wiki/Virtual_private_server) sold as a service by an Internet hosting service. A VPS runs its own copy of an operating system, and customers have superuser-level access to that operating system instance, so they can install almost any software that runs on that OS.
+
+### VM Instance
+
+In object-oriented programming, an [instance](http://stackoverflow.com/questions/20461907/what-is-meaning-of-instance-in-programming) is a specific realization of any object. An object may be varied in a number of ways. Each realized variation of that object is an instance. Therefore, a VM instance is an instance of a virtual machine, which is an emulation of a computer system.
### Waterfall
-A model of building software that involves collecting all requirements from the customer, then building and refining all the requirements and finally delivering the COMPLETE software to the customer that meets all the requirements specified by the customer
+A [model](http://www.umsl.edu/~hugheyd/is6840/waterfall.html) of building software that involves collecting all requirements from the customer, then building and refining all the requirements and finally delivering the complete software to the customer that meets all the requirements they specified.
### Webhooks
-A way for for an app to provide other applications with real-time information. e.g. send a message to a slack channel when a commit is pushed
+A way for for an app to [provide](https://docs.gitlab.com/ce/web_hooks/web_hooks.html) other applications with real-time information (e.g., send a message to a slack channel when a commit is pushed.) Read about setting up [custom git hooks](https://gitlab.com/help/administration/custom_hooks.md) for when webhooks are insufficient.
### Wiki
-A website/system that allows for collaborative editing of its content by the users. In programming, they usually contain documentation of how to use the software
+A [website/system](http://www.wiki.com/) that allows for collaborative editing of its content by the users. In programming, wikis usually contain documentation of how to use the software.
+
+### Working Tree
+
+[Consists of files](http://stackoverflow.com/questions/3689838/difference-between-head-working-tree-index-in-git) that you are currently working on.
+
+### YAML
+
+A human-readable data serialization [language](http://www.yaml.org/about.html) that takes concepts from programming languages such as C, Perl, and Python, and ideas from XML and the data format of electronic mail.
+
diff --git a/doc/update/8.11-to-8.12.md b/doc/update/8.11-to-8.12.md
index 07743d050f7..cddfa7e3e01 100644
--- a/doc/update/8.11-to-8.12.md
+++ b/doc/update/8.11-to-8.12.md
@@ -72,7 +72,7 @@ sudo -u git -H git checkout 8-12-stable-ee
```bash
cd /home/git/gitlab-shell
sudo -u git -H git fetch --all --tags
-sudo -u git -H git checkout v3.6.0
+sudo -u git -H git checkout v3.6.1
```
### 6. Update gitlab-workhorse
diff --git a/doc/user/markdown.md b/doc/user/markdown.md
index 56e5b802a52..7a7a0b864bd 100644
--- a/doc/user/markdown.md
+++ b/doc/user/markdown.md
@@ -501,6 +501,10 @@ There are two ways to create links, inline-style and reference-style.
[I'm a reference-style link][Arbitrary case-insensitive reference text]
[I'm a relative reference to a repository file](LICENSE)
+
+ [I am an absolute reference within the repository](/doc/user/markdown.md)
+
+ [I link to the Milestones page](/../milestones)
[You can use numbers for reference-style link definitions][1]
@@ -518,6 +522,10 @@ There are two ways to create links, inline-style and reference-style.
[I'm a relative reference to a repository file](LICENSE)[^1]
+[I am an absolute reference within the repository](/doc/user/markdown.md)
+
+[I link to the Milestones page](/../milestones)
+
[You can use numbers for reference-style link definitions][1]
Or leave it empty and use the [link text itself][]
diff --git a/doc/user/project/new_ci_build_permissions_model.md b/doc/user/project/new_ci_build_permissions_model.md
index 8827b501901..60b7bec2ba7 100644
--- a/doc/user/project/new_ci_build_permissions_model.md
+++ b/doc/user/project/new_ci_build_permissions_model.md
@@ -254,6 +254,12 @@ test:
This will make GitLab CI initialize (fetch) and update (checkout) all your
submodules recursively.
+If Git does not use the newly added relative URLs but still uses your old URLs,
+you might need to add `git submodule sync --recursive` to your `.gitlab-ci.yml`,
+prior to running `git submodule update --init --recursive`. This transfers the
+changes from your `.gitmodules` file into the `.git` folder, which is kept by
+runners between runs.
+
In case your environment or your Docker image doesn't have Git installed,
you have to either ask your Administrator or install the missing dependency
yourself:
diff --git a/features/support/capybara.rb b/features/support/capybara.rb
index fe9e39cf509..dae0d0f918c 100644
--- a/features/support/capybara.rb
+++ b/features/support/capybara.rb
@@ -20,5 +20,5 @@ unless ENV['CI'] || ENV['CI_SERVER']
end
Spinach.hooks.before_run do
- TestEnv.warm_asset_cache
+ TestEnv.warm_asset_cache unless ENV['CI'] || ENV['CI_SERVER']
end
diff --git a/lib/api/branches.rb b/lib/api/branches.rb
index b615703df93..6d827448994 100644
--- a/lib/api/branches.rb
+++ b/lib/api/branches.rb
@@ -54,43 +54,25 @@ module API
not_found!('Branch') unless @branch
protected_branch = user_project.protected_branches.find_by(name: @branch.name)
- developers_can_merge = to_boolean(params[:developers_can_merge])
- developers_can_push = to_boolean(params[:developers_can_push])
-
protected_branch_params = {
- name: @branch.name
+ name: @branch.name,
+ developers_can_push: to_boolean(params[:developers_can_push]),
+ developers_can_merge: to_boolean(params[:developers_can_merge])
}
- # If `developers_can_merge` is switched off, _all_ `DEVELOPER`
- # merge_access_levels need to be deleted.
- if developers_can_merge == false
- protected_branch.merge_access_levels.where(access_level: Gitlab::Access::DEVELOPER).destroy_all
- end
+ service_args = [user_project, current_user, protected_branch_params]
- # If `developers_can_push` is switched off, _all_ `DEVELOPER`
- # push_access_levels need to be deleted.
- if developers_can_push == false
- protected_branch.push_access_levels.where(access_level: Gitlab::Access::DEVELOPER).destroy_all
- end
+ protected_branch = if protected_branch
+ ProtectedBranches::ApiUpdateService.new(*service_args).execute(protected_branch)
+ else
+ ProtectedBranches::ApiCreateService.new(*service_args).execute
+ end
- protected_branch_params.merge!(
- merge_access_levels_attributes: [{
- access_level: developers_can_merge ? Gitlab::Access::DEVELOPER : Gitlab::Access::MASTER
- }],
- push_access_levels_attributes: [{
- access_level: developers_can_push ? Gitlab::Access::DEVELOPER : Gitlab::Access::MASTER
- }]
- )
-
- if protected_branch
- service = ProtectedBranches::UpdateService.new(user_project, current_user, protected_branch_params)
- service.execute(protected_branch)
+ if protected_branch.valid?
+ present @branch, with: Entities::RepoBranch, project: user_project
else
- service = ProtectedBranches::CreateService.new(user_project, current_user, protected_branch_params)
- service.execute
+ render_api_error!(protected_branch.errors.full_messages, 422)
end
-
- present @branch, with: Entities::RepoBranch, project: user_project
end
# Unprotect a single branch
@@ -123,7 +105,7 @@ module API
post ":id/repository/branches" do
authorize_push_project
result = CreateBranchService.new(user_project, current_user).
- execute(params[:branch_name], params[:ref])
+ execute(params[:branch_name], params[:ref])
if result[:status] == :success
present result[:branch],
@@ -142,10 +124,10 @@ module API
# Example Request:
# DELETE /projects/:id/repository/branches/:branch
delete ":id/repository/branches/:branch",
- requirements: { branch: /.+/ } do
+ requirements: { branch: /.+/ } do
authorize_push_project
result = DeleteBranchService.new(user_project, current_user).
- execute(params[:branch])
+ execute(params[:branch])
if result[:status] == :success
{
diff --git a/lib/api/builds.rb b/lib/api/builds.rb
index 52bdbcae5a8..67adca6605f 100644
--- a/lib/api/builds.rb
+++ b/lib/api/builds.rb
@@ -3,15 +3,32 @@ module API
class Builds < Grape::API
before { authenticate! }
+ params do
+ requires :id, type: String, desc: 'The ID of a project'
+ end
resource :projects do
- # Get a project builds
- #
- # Parameters:
- # id (required) - The ID of a project
- # scope (optional) - The scope of builds to show (one or array of: pending, running, failed, success, canceled;
- # if none provided showing all builds)
- # Example Request:
- # GET /projects/:id/builds
+ helpers do
+ params :optional_scope do
+ optional :scope, types: [String, Array[String]], desc: 'The scope of builds to show',
+ values: ['pending', 'running', 'failed', 'success', 'canceled'],
+ coerce_with: ->(scope) {
+ if scope.is_a?(String)
+ [scope]
+ elsif scope.is_a?(Hashie::Mash)
+ scope.values
+ else
+ ['unknown']
+ end
+ }
+ end
+ end
+
+ desc 'Get a project builds' do
+ success Entities::Build
+ end
+ params do
+ use :optional_scope
+ end
get ':id/builds' do
builds = user_project.builds.order('id DESC')
builds = filter_builds(builds, params[:scope])
@@ -20,15 +37,13 @@ module API
user_can_download_artifacts: can?(current_user, :read_build, user_project)
end
- # Get builds for a specific commit of a project
- #
- # Parameters:
- # id (required) - The ID of a project
- # sha (required) - The SHA id of a commit
- # scope (optional) - The scope of builds to show (one or array of: pending, running, failed, success, canceled;
- # if none provided showing all builds)
- # Example Request:
- # GET /projects/:id/repository/commits/:sha/builds
+ desc 'Get builds for a specific commit of a project' do
+ success Entities::Build
+ end
+ params do
+ requires :sha, type: String, desc: 'The SHA id of a commit'
+ use :optional_scope
+ end
get ':id/repository/commits/:sha/builds' do
authorize_read_builds!
@@ -42,13 +57,12 @@ module API
user_can_download_artifacts: can?(current_user, :read_build, user_project)
end
- # Get a specific build of a project
- #
- # Parameters:
- # id (required) - The ID of a project
- # build_id (required) - The ID of a build
- # Example Request:
- # GET /projects/:id/builds/:build_id
+ desc 'Get a specific build of a project' do
+ success Entities::Build
+ end
+ params do
+ requires :build_id, type: Integer, desc: 'The ID of a build'
+ end
get ':id/builds/:build_id' do
authorize_read_builds!
@@ -58,13 +72,12 @@ module API
user_can_download_artifacts: can?(current_user, :read_build, user_project)
end
- # Download the artifacts file from build
- #
- # Parameters:
- # id (required) - The ID of a build
- # token (required) - The build authorization token
- # Example Request:
- # GET /projects/:id/builds/:build_id/artifacts
+ desc 'Download the artifacts file from build' do
+ detail 'This feature was introduced in GitLab 8.5'
+ end
+ params do
+ requires :build_id, type: Integer, desc: 'The ID of a build'
+ end
get ':id/builds/:build_id/artifacts' do
authorize_read_builds!
@@ -73,14 +86,13 @@ module API
present_artifacts!(build.artifacts_file)
end
- # Download the artifacts file from ref_name and job
- #
- # Parameters:
- # id (required) - The ID of a project
- # ref_name (required) - The ref from repository
- # job (required) - The name for the build
- # Example Request:
- # GET /projects/:id/builds/artifacts/:ref_name/download?job=name
+ desc 'Download the artifacts file from build' do
+ detail 'This feature was introduced in GitLab 8.10'
+ end
+ params do
+ requires :ref_name, type: String, desc: 'The ref from repository'
+ requires :job, type: String, desc: 'The name for the build'
+ end
get ':id/builds/artifacts/:ref_name/download',
requirements: { ref_name: /.+/ } do
authorize_read_builds!
@@ -91,17 +103,13 @@ module API
present_artifacts!(latest_build.artifacts_file)
end
- # Get a trace of a specific build of a project
- #
- # Parameters:
- # id (required) - The ID of a project
- # build_id (required) - The ID of a build
- # Example Request:
- # GET /projects/:id/build/:build_id/trace
- #
# TODO: We should use `present_file!` and leave this implementation for backward compatibility (when build trace
# is saved in the DB instead of file). But before that, we need to consider how to replace the value of
# `runners_token` with some mask (like `xxxxxx`) when sending trace file directly by workhorse.
+ desc 'Get a trace of a specific build of a project'
+ params do
+ requires :build_id, type: Integer, desc: 'The ID of a build'
+ end
get ':id/builds/:build_id/trace' do
authorize_read_builds!
@@ -115,13 +123,12 @@ module API
body trace
end
- # Cancel a specific build of a project
- #
- # parameters:
- # id (required) - the id of a project
- # build_id (required) - the id of a build
- # example request:
- # post /projects/:id/build/:build_id/cancel
+ desc 'Cancel a specific build of a project' do
+ success Entities::Build
+ end
+ params do
+ requires :build_id, type: Integer, desc: 'The ID of a build'
+ end
post ':id/builds/:build_id/cancel' do
authorize_update_builds!
@@ -133,13 +140,12 @@ module API
user_can_download_artifacts: can?(current_user, :read_build, user_project)
end
- # Retry a specific build of a project
- #
- # parameters:
- # id (required) - the id of a project
- # build_id (required) - the id of a build
- # example request:
- # post /projects/:id/build/:build_id/retry
+ desc 'Retry a specific build of a project' do
+ success Entities::Build
+ end
+ params do
+ requires :build_id, type: Integer, desc: 'The ID of a build'
+ end
post ':id/builds/:build_id/retry' do
authorize_update_builds!
@@ -152,13 +158,12 @@ module API
user_can_download_artifacts: can?(current_user, :read_build, user_project)
end
- # Erase build (remove artifacts and build trace)
- #
- # Parameters:
- # id (required) - the id of a project
- # build_id (required) - the id of a build
- # example Request:
- # post /projects/:id/build/:build_id/erase
+ desc 'Erase build (remove artifacts and build trace)' do
+ success Entities::Build
+ end
+ params do
+ requires :build_id, type: Integer, desc: 'The ID of a build'
+ end
post ':id/builds/:build_id/erase' do
authorize_update_builds!
@@ -170,13 +175,12 @@ module API
user_can_download_artifacts: can?(current_user, :download_build_artifacts, user_project)
end
- # Keep the artifacts to prevent them from being deleted
- #
- # Parameters:
- # id (required) - the id of a project
- # build_id (required) - The ID of a build
- # Example Request:
- # POST /projects/:id/builds/:build_id/artifacts/keep
+ desc 'Keep the artifacts to prevent them from being deleted' do
+ success Entities::Build
+ end
+ params do
+ requires :build_id, type: Integer, desc: 'The ID of a build'
+ end
post ':id/builds/:build_id/artifacts/keep' do
authorize_update_builds!
@@ -235,14 +239,6 @@ module API
return builds if scope.nil? || scope.empty?
available_statuses = ::CommitStatus::AVAILABLE_STATUSES
- scope =
- if scope.is_a?(String)
- [scope]
- elsif scope.is_a?(Hashie::Mash)
- scope.values
- else
- ['unknown']
- end
unknown = scope - available_statuses
render_api_error!('Scope contains invalid value(s)', 400) unless unknown.empty?
diff --git a/lib/api/commits.rb b/lib/api/commits.rb
index 617a240318a..2f2cf769481 100644
--- a/lib/api/commits.rb
+++ b/lib/api/commits.rb
@@ -19,6 +19,7 @@ module API
optional :until, type: String, desc: 'Only commits before or in this date will be returned'
optional :page, type: Integer, default: 0, desc: 'The page for pagination'
optional :per_page, type: Integer, default: 20, desc: 'The number of results per page'
+ optional :path, type: String, desc: 'The file path'
end
get ":id/repository/commits" do
# TODO remove the next line for 9.0, use DateTime type in the params block
@@ -28,6 +29,7 @@ module API
offset = params[:page] * params[:per_page]
commits = user_project.repository.commits(ref,
+ path: params[:path],
limit: params[:per_page],
offset: offset,
after: params[:since],
diff --git a/lib/api/labels.rb b/lib/api/labels.rb
index 642e6345b9e..326e1e7ae00 100644
--- a/lib/api/labels.rb
+++ b/lib/api/labels.rb
@@ -3,37 +3,32 @@ module API
class Labels < Grape::API
before { authenticate! }
+ params do
+ requires :id, type: String, desc: 'The ID of a project'
+ end
resource :projects do
- # Get all labels of the project
- #
- # Parameters:
- # id (required) - The ID of a project
- # Example Request:
- # GET /projects/:id/labels
+ desc 'Get all labels of the project' do
+ success Entities::Label
+ end
get ':id/labels' do
present available_labels, with: Entities::Label, current_user: current_user
end
- # Creates a new label
- #
- # Parameters:
- # id (required) - The ID of a project
- # name (required) - The name of the label to be created
- # color (required) - Color of the label given in 6-digit hex
- # notation with leading '#' sign (e.g. #FFAABB)
- # description (optional) - The description of label to be created
- # Example Request:
- # POST /projects/:id/labels
+ desc 'Create a new label' do
+ success Entities::Label
+ end
+ params do
+ requires :name, type: String, desc: 'The name of the label to be created'
+ requires :color, type: String, desc: "The color of the label given in 6-digit hex notation with leading '#' sign (e.g. #FFAABB)"
+ optional :description, type: String, desc: 'The description of label to be created'
+ end
post ':id/labels' do
authorize! :admin_label, user_project
- required_attributes! [:name, :color]
-
- attrs = attributes_for_keys [:name, :color, :description]
- label = user_project.find_label(attrs[:name])
+ label = user_project.find_label(params[:name])
conflict!('Label already exists') if label
- label = user_project.labels.create(attrs)
+ label = user_project.labels.create(declared(params, include_parent_namespaces: false).to_h)
if label.valid?
present label, with: Entities::Label, current_user: current_user
@@ -42,54 +37,44 @@ module API
end
end
- # Deletes an existing label
- #
- # Parameters:
- # id (required) - The ID of a project
- # name (required) - The name of the label to be deleted
- #
- # Example Request:
- # DELETE /projects/:id/labels
+ desc 'Delete an existing label' do
+ success Entities::Label
+ end
+ params do
+ requires :name, type: String, desc: 'The name of the label to be deleted'
+ end
delete ':id/labels' do
authorize! :admin_label, user_project
- required_attributes! [:name]
label = user_project.find_label(params[:name])
not_found!('Label') unless label
- label.destroy
+ present label.destroy, with: Entities::Label, current_user: current_user
end
- # Updates an existing label. At least one optional parameter is required.
- #
- # Parameters:
- # id (required) - The ID of a project
- # name (required) - The name of the label to be deleted
- # new_name (optional) - The new name of the label
- # color (optional) - Color of the label given in 6-digit hex
- # notation with leading '#' sign (e.g. #FFAABB)
- # description (optional) - The description of label to be created
- # Example Request:
- # PUT /projects/:id/labels
+ desc 'Update an existing label. At least one optional parameter is required.' do
+ success Entities::Label
+ end
+ params do
+ requires :name, type: String, desc: 'The name of the label to be updated'
+ optional :new_name, type: String, desc: 'The new name of the label'
+ optional :color, type: String, desc: "The new color of the label given in 6-digit hex notation with leading '#' sign (e.g. #FFAABB)"
+ optional :description, type: String, desc: 'The new description of label'
+ at_least_one_of :new_name, :color, :description
+ end
put ':id/labels' do
authorize! :admin_label, user_project
- required_attributes! [:name]
label = user_project.find_label(params[:name])
not_found!('Label not found') unless label
- attrs = attributes_for_keys [:new_name, :color, :description]
-
- if attrs.empty?
- render_api_error!('Required parameters "new_name" or "color" ' \
- 'missing',
- 400)
- end
-
+ update_params = declared(params,
+ include_parent_namespaces: false,
+ include_missing: false).to_h
# Rename new name to the actual label attribute name
- attrs[:name] = attrs.delete(:new_name) if attrs.key?(:new_name)
+ update_params['name'] = update_params.delete('new_name') if update_params.key?('new_name')
- if label.update(attrs)
+ if label.update(update_params)
present label, with: Entities::Label, current_user: current_user
else
render_validation_error!(label)
diff --git a/lib/banzai/filter/relative_link_filter.rb b/lib/banzai/filter/relative_link_filter.rb
index 4fa8d05481f..f09d78be0ce 100644
--- a/lib/banzai/filter/relative_link_filter.rb
+++ b/lib/banzai/filter/relative_link_filter.rb
@@ -52,8 +52,8 @@ module Banzai
relative_url_root,
context[:project].path_with_namespace,
uri_type(file_path),
- ref,
- file_path
+ Addressable::URI.escape(ref),
+ Addressable::URI.escape(file_path)
].compact.join('/').squeeze('/').chomp('/')
uri
diff --git a/lib/constraints/namespace_url_constrainer.rb b/lib/constraints/namespace_url_constrainer.rb
index 23920193743..91b70143f11 100644
--- a/lib/constraints/namespace_url_constrainer.rb
+++ b/lib/constraints/namespace_url_constrainer.rb
@@ -1,6 +1,9 @@
class NamespaceUrlConstrainer
def matches?(request)
- id = request.path.sub(/\A\/+/, '').split('/').first.sub(/.atom\z/, '')
+ id = request.path
+ id = id.sub(/\A#{relative_url_root}/, '') if relative_url_root
+ id = id.sub(/\A\/+/, '').split('/').first
+ id = id.sub(/.atom\z/, '') if id
if id =~ Gitlab::Regex.namespace_regex
find_resource(id)
@@ -10,4 +13,12 @@ class NamespaceUrlConstrainer
def find_resource(id)
Namespace.find_by_path(id)
end
+
+ private
+
+ def relative_url_root
+ if defined?(Gitlab::Application.config.relative_url_root)
+ Gitlab::Application.config.relative_url_root
+ end
+ end
end
diff --git a/lib/gitlab/issues_labels.rb b/lib/gitlab/issues_labels.rb
index 01a2c19ab23..dbc759367eb 100644
--- a/lib/gitlab/issues_labels.rb
+++ b/lib/gitlab/issues_labels.rb
@@ -19,7 +19,7 @@ module Gitlab
]
labels.each do |params|
- ::Labels::FindOrCreateService.new(project.owner, project).execute(params)
+ ::Labels::FindOrCreateService.new(project.owner, project, params).execute
end
end
end
diff --git a/lib/tasks/cache.rake b/lib/tasks/cache.rake
index a95a3455a4a..78ae187817a 100644
--- a/lib/tasks/cache.rake
+++ b/lib/tasks/cache.rake
@@ -29,5 +29,5 @@ namespace :cache do
task all: [:db, :redis]
end
- task clear: 'cache:clear:all'
+ task clear: 'cache:clear:redis'
end
diff --git a/lib/tasks/gitlab/backup.rake b/lib/tasks/gitlab/backup.rake
index b43ee5b3383..a9f1255e8cf 100644
--- a/lib/tasks/gitlab/backup.rake
+++ b/lib/tasks/gitlab/backup.rake
@@ -51,6 +51,7 @@ namespace :gitlab do
$progress.puts 'done'.color(:green)
Rake::Task['gitlab:backup:db:restore'].invoke
end
+
Rake::Task['gitlab:backup:repo:restore'].invoke unless backup.skipped?('repositories')
Rake::Task['gitlab:backup:uploads:restore'].invoke unless backup.skipped?('uploads')
Rake::Task['gitlab:backup:builds:restore'].invoke unless backup.skipped?('builds')
@@ -58,6 +59,7 @@ namespace :gitlab do
Rake::Task['gitlab:backup:lfs:restore'].invoke unless backup.skipped?('lfs')
Rake::Task['gitlab:backup:registry:restore'].invoke unless backup.skipped?('registry')
Rake::Task['gitlab:shell:setup'].invoke
+ Rake::Task['cache:clear'].invoke
backup.cleanup
end
diff --git a/spec/controllers/groups/group_members_controller_spec.rb b/spec/controllers/groups/group_members_controller_spec.rb
index ad15b3f8f40..c7db84dd5f9 100644
--- a/spec/controllers/groups/group_members_controller_spec.rb
+++ b/spec/controllers/groups/group_members_controller_spec.rb
@@ -13,6 +13,49 @@ describe Groups::GroupMembersController do
end
end
+ describe 'POST create' do
+ let(:group_user) { create(:user) }
+
+ before { sign_in(user) }
+
+ context 'when user does not have enough rights' do
+ before { group.add_developer(user) }
+
+ it 'returns 403' do
+ post :create, group_id: group,
+ user_ids: group_user.id,
+ access_level: Gitlab::Access::GUEST
+
+ expect(response).to have_http_status(403)
+ expect(group.users).not_to include group_user
+ end
+ end
+
+ context 'when user has enough rights' do
+ before { group.add_owner(user) }
+
+ it 'adds user to members' do
+ post :create, group_id: group,
+ user_ids: group_user.id,
+ access_level: Gitlab::Access::GUEST
+
+ expect(response).to set_flash.to 'Users were successfully added.'
+ expect(response).to redirect_to(group_group_members_path(group))
+ expect(group.users).to include group_user
+ end
+
+ it 'adds no user to members' do
+ post :create, group_id: group,
+ user_ids: '',
+ access_level: Gitlab::Access::GUEST
+
+ expect(response).to set_flash.to 'No users specified.'
+ expect(response).to redirect_to(group_group_members_path(group))
+ expect(group.users).not_to include group_user
+ end
+ end
+ end
+
describe 'DELETE destroy' do
let(:member) { create(:group_member, :developer, group: group) }
diff --git a/spec/controllers/projects/labels_controller_spec.rb b/spec/controllers/projects/labels_controller_spec.rb
index 622ab154493..41df63d445a 100644
--- a/spec/controllers/projects/labels_controller_spec.rb
+++ b/spec/controllers/projects/labels_controller_spec.rb
@@ -70,4 +70,19 @@ describe Projects::LabelsController do
get :index, namespace_id: project.namespace.to_param, project_id: project.to_param
end
end
+
+ describe 'POST #generate' do
+ let(:admin) { create(:admin) }
+ let(:project) { create(:empty_project) }
+
+ before do
+ sign_in(admin)
+ end
+
+ it 'creates labels' do
+ post :generate, namespace_id: project.namespace.to_param, project_id: project.to_param
+
+ expect(response).to have_http_status(302)
+ end
+ end
end
diff --git a/spec/controllers/projects/project_members_controller_spec.rb b/spec/controllers/projects/project_members_controller_spec.rb
index 8519ebc1d5f..b4f066d8600 100644
--- a/spec/controllers/projects/project_members_controller_spec.rb
+++ b/spec/controllers/projects/project_members_controller_spec.rb
@@ -13,6 +13,54 @@ describe Projects::ProjectMembersController do
end
end
+ describe 'POST create' do
+ context 'when users are added' do
+ let(:project_user) { create(:user) }
+
+ before { sign_in(user) }
+
+ context 'when user does not have enough rights' do
+ before { project.team << [user, :developer] }
+
+ it 'returns 404' do
+ post :create, namespace_id: project.namespace,
+ project_id: project,
+ user_ids: project_user.id,
+ access_level: Gitlab::Access::GUEST
+
+ expect(response).to have_http_status(404)
+ expect(project.users).not_to include project_user
+ end
+ end
+
+ context 'when user has enough rights' do
+ before { project.team << [user, :master] }
+
+ it 'adds user to members' do
+ post :create, namespace_id: project.namespace,
+ project_id: project,
+ user_ids: project_user.id,
+ access_level: Gitlab::Access::GUEST
+
+ expect(response).to set_flash.to 'Users were successfully added.'
+ expect(response).to redirect_to(namespace_project_project_members_path(project.namespace, project))
+ expect(project.users).to include project_user
+ end
+
+ it 'adds no user to members' do
+ post :create, namespace_id: project.namespace,
+ project_id: project,
+ user_ids: '',
+ access_level: Gitlab::Access::GUEST
+
+ expect(response).to set_flash.to 'No users or groups specified.'
+ expect(response).to redirect_to(namespace_project_project_members_path(project.namespace, project))
+ expect(project.users).not_to include project_user
+ end
+ end
+ end
+ end
+
describe 'DELETE destroy' do
let(:member) { create(:project_member, :developer, project: project) }
@@ -228,4 +276,40 @@ describe Projects::ProjectMembersController do
end
end
end
+
+ describe 'POST create' do
+ let(:stranger) { create(:user) }
+
+ context 'when creating owner' do
+ before do
+ project.team << [user, :master]
+ sign_in(user)
+ end
+
+ it 'does not create a member' do
+ expect do
+ post :create, user_ids: stranger.id,
+ namespace_id: project.namespace,
+ access_level: Member::OWNER,
+ project_id: project
+ end.to change { project.members.count }.by(0)
+ end
+ end
+
+ context 'when create master' do
+ before do
+ project.team << [user, :master]
+ sign_in(user)
+ end
+
+ it 'creates a member' do
+ expect do
+ post :create, user_ids: stranger.id,
+ namespace_id: project.namespace,
+ access_level: Member::MASTER,
+ project_id: project
+ end.to change { project.members.count }.by(1)
+ end
+ end
+ end
end
diff --git a/spec/features/login_spec.rb b/spec/features/login_spec.rb
index 996f39ea06d..76bcfbe523a 100644
--- a/spec/features/login_spec.rb
+++ b/spec/features/login_spec.rb
@@ -215,4 +215,69 @@ feature 'Login', feature: true do
end
end
end
+
+ describe 'UI tabs and panes' do
+ context 'when no defaults are changed' do
+ it 'correctly renders tabs and panes' do
+ ensure_tab_pane_correctness
+ end
+ end
+
+ context 'when signup is disabled' do
+ before do
+ stub_application_setting(signup_enabled: false)
+ end
+
+ it 'correctly renders tabs and panes' do
+ ensure_tab_pane_correctness
+ end
+ end
+
+ context 'when ldap is enabled' do
+ before do
+ visit new_user_session_path
+ allow(page).to receive(:form_based_providers).and_return([:ldapmain])
+ allow(page).to receive(:ldap_enabled).and_return(true)
+ end
+
+ it 'correctly renders tabs and panes' do
+ ensure_tab_pane_correctness(false)
+ end
+ end
+
+ context 'when crowd is enabled' do
+ before do
+ visit new_user_session_path
+ allow(page).to receive(:form_based_providers).and_return([:crowd])
+ allow(page).to receive(:crowd_enabled?).and_return(true)
+ end
+
+ it 'correctly renders tabs and panes' do
+ ensure_tab_pane_correctness(false)
+ end
+ end
+
+ def ensure_tab_pane_correctness(visit_path = true)
+ if visit_path
+ visit new_user_session_path
+ end
+
+ ensure_tab_pane_counts
+ ensure_one_active_tab
+ ensure_one_active_pane
+ end
+
+ def ensure_tab_pane_counts
+ tabs_count = page.all('[role="tab"]').size
+ expect(page).to have_selector('[role="tabpanel"]', count: tabs_count)
+ end
+
+ def ensure_one_active_tab
+ expect(page).to have_selector('.nav-tabs > li.active', count: 1)
+ end
+
+ def ensure_one_active_pane
+ expect(page).to have_selector('.tab-pane.active', count: 1)
+ end
+ end
end
diff --git a/spec/finders/labels_finder_spec.rb b/spec/finders/labels_finder_spec.rb
index 27acc464ea2..10cfb66ec1c 100644
--- a/spec/finders/labels_finder_spec.rb
+++ b/spec/finders/labels_finder_spec.rb
@@ -38,6 +38,14 @@ describe LabelsFinder do
expect(finder.execute).to eq [group_label_2, group_label_3, project_label_1, group_label_1, project_label_2, project_label_4]
end
+
+ it 'returns labels available if nil title is supplied' do
+ group_2.add_developer(user)
+ # params[:title] will return `nil` regardless whether it is specified
+ finder = described_class.new(user, title: nil)
+
+ expect(finder.execute).to eq [group_label_2, group_label_3, project_label_1, group_label_1, project_label_2, project_label_4]
+ end
end
context 'filtering by group_id' do
@@ -64,6 +72,30 @@ describe LabelsFinder do
expect(finder.execute).to eq [group_label_2]
end
+
+ it 'returns label with title alias' do
+ finder = described_class.new(user, name: 'Group Label 2')
+
+ expect(finder.execute).to eq [group_label_2]
+ end
+
+ it 'returns no labels if empty title is supplied' do
+ finder = described_class.new(user, title: [])
+
+ expect(finder.execute).to be_empty
+ end
+
+ it 'returns no labels if blank title is supplied' do
+ finder = described_class.new(user, title: '')
+
+ expect(finder.execute).to be_empty
+ end
+
+ it 'returns no labels if empty name is supplied' do
+ finder = described_class.new(user, name: [])
+
+ expect(finder.execute).to be_empty
+ end
end
end
end
diff --git a/spec/javascripts/gl_field_errors_spec.js.es6 b/spec/javascripts/gl_field_errors_spec.js.es6
index 36feb2b2aa5..da9259edd78 100644
--- a/spec/javascripts/gl_field_errors_spec.js.es6
+++ b/spec/javascripts/gl_field_errors_spec.js.es6
@@ -11,12 +11,12 @@
this.fieldErrors = new global.GlFieldErrors($form);
});
- it('should properly initialize the form', function() {
+ it('should select the correct input elements', function() {
expect(this.$form).toBeDefined();
expect(this.$form.length).toBe(1);
expect(this.fieldErrors).toBeDefined();
const inputs = this.fieldErrors.state.inputs;
- expect(inputs.length).toBe(5);
+ expect(inputs.length).toBe(4);
});
it('should ignore elements with custom error handling', function() {
diff --git a/spec/lib/banzai/filter/relative_link_filter_spec.rb b/spec/lib/banzai/filter/relative_link_filter_spec.rb
index 6b58f3e43ee..2bfa51deb20 100644
--- a/spec/lib/banzai/filter/relative_link_filter_spec.rb
+++ b/spec/lib/banzai/filter/relative_link_filter_spec.rb
@@ -50,14 +50,6 @@ describe Banzai::Filter::RelativeLinkFilter, lib: true do
end
end
- shared_examples :relative_to_requested do
- it 'rebuilds URL relative to the requested path' do
- doc = filter(link('users.md'))
- expect(doc.at_css('a')['href']).
- to eq "/#{project_path}/blob/#{ref}/doc/api/users.md"
- end
- end
-
context 'with a project_wiki' do
let(:project_wiki) { double('ProjectWiki') }
include_examples :preserve_unchanged
@@ -188,12 +180,38 @@ describe Banzai::Filter::RelativeLinkFilter, lib: true do
context 'when requested path is a file in the repo' do
let(:requested_path) { 'doc/api/README.md' }
- include_examples :relative_to_requested
+ it 'rebuilds URL relative to the containing directory' do
+ doc = filter(link('users.md'))
+ expect(doc.at_css('a')['href']).to eq "/#{project_path}/blob/#{Addressable::URI.escape(ref)}/doc/api/users.md"
+ end
end
context 'when requested path is a directory in the repo' do
- let(:requested_path) { 'doc/api' }
- include_examples :relative_to_requested
+ let(:requested_path) { 'doc/api/' }
+ it 'rebuilds URL relative to the directory' do
+ doc = filter(link('users.md'))
+ expect(doc.at_css('a')['href']).to eq "/#{project_path}/blob/#{Addressable::URI.escape(ref)}/doc/api/users.md"
+ end
+ end
+
+ context 'when ref name contains percent sign' do
+ let(:ref) { '100%branch' }
+ let(:commit) { project.commit('1b12f15a11fc6e62177bef08f47bc7b5ce50b141') }
+ let(:requested_path) { 'foo/bar/' }
+ it 'correctly escapes the ref' do
+ doc = filter(link('.gitkeep'))
+ expect(doc.at_css('a')['href']).to eq "/#{project_path}/blob/#{Addressable::URI.escape(ref)}/foo/bar/.gitkeep"
+ end
+ end
+
+ context 'when requested path is a directory with space in the repo' do
+ let(:ref) { 'master' }
+ let(:commit) { project.commit('38008cb17ce1466d8fec2dfa6f6ab8dcfe5cf49e') }
+ let(:requested_path) { 'with space/' }
+ it 'does not escape the space twice' do
+ doc = filter(link('README.md'))
+ expect(doc.at_css('a')['href']).to eq "/#{project_path}/blob/#{Addressable::URI.escape(ref)}/with%20space/README.md"
+ end
end
end
diff --git a/spec/lib/constraints/namespace_url_constrainer_spec.rb b/spec/lib/constraints/namespace_url_constrainer_spec.rb
index a5feaacb8ee..7814711fe27 100644
--- a/spec/lib/constraints/namespace_url_constrainer_spec.rb
+++ b/spec/lib/constraints/namespace_url_constrainer_spec.rb
@@ -17,6 +17,16 @@ describe NamespaceUrlConstrainer, lib: true do
it { expect(subject.matches?(request '/g/gitlab')).to be_falsey }
it { expect(subject.matches?(request '/.gitlab')).to be_falsey }
end
+
+ context 'relative url' do
+ before do
+ allow(Gitlab::Application.config).to receive(:relative_url_root) { '/gitlab' }
+ end
+
+ it { expect(subject.matches?(request '/gitlab/gitlab')).to be_truthy }
+ it { expect(subject.matches?(request '/gitlab/gitlab-ce')).to be_falsey }
+ it { expect(subject.matches?(request '/gitlab/')).to be_falsey }
+ end
end
def request(path)
diff --git a/spec/mailers/emails/builds_spec.rb b/spec/mailers/emails/builds_spec.rb
index 0df89938e97..d968096783c 100644
--- a/spec/mailers/emails/builds_spec.rb
+++ b/spec/mailers/emails/builds_spec.rb
@@ -1,6 +1,5 @@
require 'spec_helper'
require 'email_spec'
-require 'mailers/shared/notify'
describe Notify do
include EmailSpec::Matchers
diff --git a/spec/mailers/emails/merge_requests_spec.rb b/spec/mailers/emails/merge_requests_spec.rb
index 4d3811af254..e22858d1d8f 100644
--- a/spec/mailers/emails/merge_requests_spec.rb
+++ b/spec/mailers/emails/merge_requests_spec.rb
@@ -1,6 +1,5 @@
require 'spec_helper'
require 'email_spec'
-require 'mailers/shared/notify'
describe Notify, "merge request notifications" do
include EmailSpec::Matchers
diff --git a/spec/mailers/emails/profile_spec.rb b/spec/mailers/emails/profile_spec.rb
index 781472d0c00..14bc062ef12 100644
--- a/spec/mailers/emails/profile_spec.rb
+++ b/spec/mailers/emails/profile_spec.rb
@@ -1,6 +1,5 @@
require 'spec_helper'
require 'email_spec'
-require 'mailers/shared/notify'
describe Notify do
include EmailSpec::Matchers
diff --git a/spec/mailers/notify_spec.rb b/spec/mailers/notify_spec.rb
index c8207e58e90..f5f3f58613d 100644
--- a/spec/mailers/notify_spec.rb
+++ b/spec/mailers/notify_spec.rb
@@ -1,6 +1,5 @@
require 'spec_helper'
require 'email_spec'
-require 'mailers/shared/notify'
describe Notify do
include EmailSpec::Helpers
diff --git a/spec/models/email_spec.rb b/spec/models/email_spec.rb
index d9df9e0f907..fe4de1b2afb 100644
--- a/spec/models/email_spec.rb
+++ b/spec/models/email_spec.rb
@@ -6,4 +6,9 @@ describe Email, models: true do
subject { build(:email) }
end
end
+
+ it 'normalize email value' do
+ expect(described_class.new(email: ' inFO@exAMPLe.com ').email)
+ .to eq 'info@example.com'
+ end
end
diff --git a/spec/models/issue_spec.rb b/spec/models/issue_spec.rb
index 3b8b743af2d..60d30eb7418 100644
--- a/spec/models/issue_spec.rb
+++ b/spec/models/issue_spec.rb
@@ -100,11 +100,17 @@ describe Issue, models: true do
end
it 'returns the merge request to close this issue' do
- allow(mr).to receive(:closes_issue?).with(issue).and_return(true)
+ mr
expect(issue.closed_by_merge_requests).to eq([mr])
end
+ it "returns an empty array when the merge request is closed already" do
+ closed_mr
+
+ expect(issue.closed_by_merge_requests).to eq([])
+ end
+
it "returns an empty array when the current issue is closed already" do
expect(closed_issue.closed_by_merge_requests).to eq([])
end
diff --git a/spec/models/members/project_member_spec.rb b/spec/models/members/project_member_spec.rb
index b2fe96e2e02..f6b2ec5ae31 100644
--- a/spec/models/members/project_member_spec.rb
+++ b/spec/models/members/project_member_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
describe ProjectMember, models: true do
describe 'associations' do
- it { is_expected.to belong_to(:project).class_name('Project').with_foreign_key(:source_id) }
+ it { is_expected.to belong_to(:project).with_foreign_key(:source_id) }
end
describe 'validations' do
diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb
index 6e5137602aa..1067ff7bb4d 100644
--- a/spec/models/merge_request_spec.rb
+++ b/spec/models/merge_request_spec.rb
@@ -6,8 +6,8 @@ describe MergeRequest, models: true do
subject { create(:merge_request) }
describe 'associations' do
- it { is_expected.to belong_to(:target_project).with_foreign_key(:target_project_id).class_name('Project') }
- it { is_expected.to belong_to(:source_project).with_foreign_key(:source_project_id).class_name('Project') }
+ it { is_expected.to belong_to(:target_project).class_name('Project') }
+ it { is_expected.to belong_to(:source_project).class_name('Project') }
it { is_expected.to belong_to(:merge_user).class_name("User") }
it { is_expected.to have_many(:merge_request_diffs).dependent(:destroy) }
end
@@ -1286,7 +1286,8 @@ describe MergeRequest, models: true do
let(:project) { create(:project) }
let(:user) { create(:user) }
let(:fork_project) { create(:project, forked_from_project: project, namespace: user.namespace) }
- let(:merge_request) do
+
+ let!(:merge_request) do
create(:closed_merge_request,
source_project: fork_project,
target_project: project)
diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb
index f977cf73673..187a1bf2d79 100644
--- a/spec/models/repository_spec.rb
+++ b/spec/models/repository_spec.rb
@@ -1146,28 +1146,17 @@ describe Repository, models: true do
end
describe '#before_import' do
- it 'flushes the emptiness cachess' do
- expect(repository).to receive(:expire_emptiness_caches)
-
- repository.before_import
- end
-
- it 'flushes the exists cache' do
- expect(repository).to receive(:expire_exists_cache)
+ it 'flushes the repository caches' do
+ expect(repository).to receive(:expire_content_cache)
repository.before_import
end
end
describe '#after_import' do
- it 'flushes the emptiness cachess' do
- expect(repository).to receive(:expire_emptiness_caches)
-
- repository.after_import
- end
-
- it 'flushes the exists cache' do
- expect(repository).to receive(:expire_exists_cache)
+ it 'flushes and builds the cache' do
+ expect(repository).to receive(:expire_content_cache)
+ expect(repository).to receive(:build_cache)
repository.after_import
end
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index 65b2896930a..10c39b90212 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -15,11 +15,11 @@ describe User, models: true do
describe 'associations' do
it { is_expected.to have_one(:namespace) }
- it { is_expected.to have_many(:snippets).class_name('Snippet').dependent(:destroy) }
+ it { is_expected.to have_many(:snippets).dependent(:destroy) }
it { is_expected.to have_many(:project_members).dependent(:destroy) }
it { is_expected.to have_many(:groups) }
it { is_expected.to have_many(:keys).dependent(:destroy) }
- it { is_expected.to have_many(:events).class_name('Event').dependent(:destroy) }
+ it { is_expected.to have_many(:events).dependent(:destroy) }
it { is_expected.to have_many(:recent_events).class_name('Event') }
it { is_expected.to have_many(:issues).dependent(:destroy) }
it { is_expected.to have_many(:notes).dependent(:destroy) }
diff --git a/spec/requests/api/branches_spec.rb b/spec/requests/api/branches_spec.rb
index 3fd989dd7a6..905f762d578 100644
--- a/spec/requests/api/branches_spec.rb
+++ b/spec/requests/api/branches_spec.rb
@@ -48,92 +48,154 @@ describe API::API, api: true do
end
describe 'PUT /projects/:id/repository/branches/:branch/protect' do
- it 'protects a single branch' do
- put api("/projects/#{project.id}/repository/branches/#{branch_name}/protect", user)
+ context "when a protected branch doesn't already exist" do
+ it 'protects a single branch' do
+ put api("/projects/#{project.id}/repository/branches/#{branch_name}/protect", user)
- expect(response).to have_http_status(200)
- expect(json_response['name']).to eq(branch_name)
- expect(json_response['commit']['id']).to eq(branch_sha)
- expect(json_response['protected']).to eq(true)
- expect(json_response['developers_can_push']).to eq(false)
- expect(json_response['developers_can_merge']).to eq(false)
- end
+ expect(response).to have_http_status(200)
+ expect(json_response['name']).to eq(branch_name)
+ expect(json_response['commit']['id']).to eq(branch_sha)
+ expect(json_response['protected']).to eq(true)
+ expect(json_response['developers_can_push']).to eq(false)
+ expect(json_response['developers_can_merge']).to eq(false)
+ end
- it 'protects a single branch and developers can push' do
- put api("/projects/#{project.id}/repository/branches/#{branch_name}/protect", user),
- developers_can_push: true
+ it 'protects a single branch and developers can push' do
+ put api("/projects/#{project.id}/repository/branches/#{branch_name}/protect", user),
+ developers_can_push: true
- expect(response).to have_http_status(200)
- expect(json_response['name']).to eq(branch_name)
- expect(json_response['commit']['id']).to eq(branch_sha)
- expect(json_response['protected']).to eq(true)
- expect(json_response['developers_can_push']).to eq(true)
- expect(json_response['developers_can_merge']).to eq(false)
- end
+ expect(response).to have_http_status(200)
+ expect(json_response['name']).to eq(branch_name)
+ expect(json_response['commit']['id']).to eq(branch_sha)
+ expect(json_response['protected']).to eq(true)
+ expect(json_response['developers_can_push']).to eq(true)
+ expect(json_response['developers_can_merge']).to eq(false)
+ end
- it 'protects a single branch and developers can merge' do
- put api("/projects/#{project.id}/repository/branches/#{branch_name}/protect", user),
- developers_can_merge: true
+ it 'protects a single branch and developers can merge' do
+ put api("/projects/#{project.id}/repository/branches/#{branch_name}/protect", user),
+ developers_can_merge: true
- expect(response).to have_http_status(200)
- expect(json_response['name']).to eq(branch_name)
- expect(json_response['commit']['id']).to eq(branch_sha)
- expect(json_response['protected']).to eq(true)
- expect(json_response['developers_can_push']).to eq(false)
- expect(json_response['developers_can_merge']).to eq(true)
- end
+ expect(response).to have_http_status(200)
+ expect(json_response['name']).to eq(branch_name)
+ expect(json_response['commit']['id']).to eq(branch_sha)
+ expect(json_response['protected']).to eq(true)
+ expect(json_response['developers_can_push']).to eq(false)
+ expect(json_response['developers_can_merge']).to eq(true)
+ end
- it 'protects a single branch and developers can push and merge' do
- put api("/projects/#{project.id}/repository/branches/#{branch_name}/protect", user),
- developers_can_push: true, developers_can_merge: true
+ it 'protects a single branch and developers can push and merge' do
+ put api("/projects/#{project.id}/repository/branches/#{branch_name}/protect", user),
+ developers_can_push: true, developers_can_merge: true
- expect(response).to have_http_status(200)
- expect(json_response['name']).to eq(branch_name)
- expect(json_response['commit']['id']).to eq(branch_sha)
- expect(json_response['protected']).to eq(true)
- expect(json_response['developers_can_push']).to eq(true)
- expect(json_response['developers_can_merge']).to eq(true)
- end
+ expect(response).to have_http_status(200)
+ expect(json_response['name']).to eq(branch_name)
+ expect(json_response['commit']['id']).to eq(branch_sha)
+ expect(json_response['protected']).to eq(true)
+ expect(json_response['developers_can_push']).to eq(true)
+ expect(json_response['developers_can_merge']).to eq(true)
+ end
- it 'protects a single branch and developers cannot push and merge' do
- put api("/projects/#{project.id}/repository/branches/#{branch_name}/protect", user),
- developers_can_push: 'tru', developers_can_merge: 'tr'
+ it 'protects a single branch and developers cannot push and merge' do
+ put api("/projects/#{project.id}/repository/branches/#{branch_name}/protect", user),
+ developers_can_push: 'tru', developers_can_merge: 'tr'
- expect(response).to have_http_status(200)
- expect(json_response['name']).to eq(branch_name)
- expect(json_response['commit']['id']).to eq(branch_sha)
- expect(json_response['protected']).to eq(true)
- expect(json_response['developers_can_push']).to eq(false)
- expect(json_response['developers_can_merge']).to eq(false)
+ expect(response).to have_http_status(200)
+ expect(json_response['name']).to eq(branch_name)
+ expect(json_response['commit']['id']).to eq(branch_sha)
+ expect(json_response['protected']).to eq(true)
+ expect(json_response['developers_can_push']).to eq(false)
+ expect(json_response['developers_can_merge']).to eq(false)
+ end
end
- context 'on a protected branch' do
- let(:protected_branch) { 'foo' }
-
+ context 'for an existing protected branch' do
before do
- project.repository.add_branch(user, protected_branch, 'master')
- create(:protected_branch, :developers_can_push, :developers_can_merge, project: project, name: protected_branch)
+ project.repository.add_branch(user, protected_branch.name, 'master')
end
- it 'updates that a developer can push' do
- put api("/projects/#{project.id}/repository/branches/#{protected_branch}/protect", user),
- developers_can_push: false, developers_can_merge: false
+ context "when developers can push and merge" do
+ let(:protected_branch) { create(:protected_branch, :developers_can_push, :developers_can_merge, project: project, name: 'protected_branch') }
+
+ it 'updates that a developer cannot push or merge' do
+ put api("/projects/#{project.id}/repository/branches/#{protected_branch.name}/protect", user),
+ developers_can_push: false, developers_can_merge: false
+
+ expect(response).to have_http_status(200)
+ expect(json_response['name']).to eq(protected_branch.name)
+ expect(json_response['protected']).to eq(true)
+ expect(json_response['developers_can_push']).to eq(false)
+ expect(json_response['developers_can_merge']).to eq(false)
+ end
+
+ it "doesn't result in 0 access levels when 'developers_can_push' is switched off" do
+ put api("/projects/#{project.id}/repository/branches/#{protected_branch.name}/protect", user),
+ developers_can_push: false
+
+ expect(response).to have_http_status(200)
+ expect(json_response['name']).to eq(protected_branch.name)
+ expect(protected_branch.reload.push_access_levels.first).to be_present
+ expect(protected_branch.reload.push_access_levels.first.access_level).to eq(Gitlab::Access::MASTER)
+ end
+
+ it "doesn't result in 0 access levels when 'developers_can_merge' is switched off" do
+ put api("/projects/#{project.id}/repository/branches/#{protected_branch.name}/protect", user),
+ developers_can_merge: false
+
+ expect(response).to have_http_status(200)
+ expect(json_response['name']).to eq(protected_branch.name)
+ expect(protected_branch.reload.merge_access_levels.first).to be_present
+ expect(protected_branch.reload.merge_access_levels.first.access_level).to eq(Gitlab::Access::MASTER)
+ end
+ end
+
+ context "when developers cannot push or merge" do
+ let(:protected_branch) { create(:protected_branch, project: project, name: 'protected_branch') }
+
+ it 'updates that a developer can push and merge' do
+ put api("/projects/#{project.id}/repository/branches/#{protected_branch.name}/protect", user),
+ developers_can_push: true, developers_can_merge: true
+
+ expect(response).to have_http_status(200)
+ expect(json_response['name']).to eq(protected_branch.name)
+ expect(json_response['protected']).to eq(true)
+ expect(json_response['developers_can_push']).to eq(true)
+ expect(json_response['developers_can_merge']).to eq(true)
+ end
+ end
+ end
+
+ context "multiple API calls" do
+ it "returns success when `protect` is called twice" do
+ put api("/projects/#{project.id}/repository/branches/#{branch_name}/protect", user)
+ put api("/projects/#{project.id}/repository/branches/#{branch_name}/protect", user)
expect(response).to have_http_status(200)
- expect(json_response['name']).to eq(protected_branch)
+ expect(json_response['name']).to eq(branch_name)
expect(json_response['protected']).to eq(true)
expect(json_response['developers_can_push']).to eq(false)
expect(json_response['developers_can_merge']).to eq(false)
end
- it 'does not update that a developer can push' do
- put api("/projects/#{project.id}/repository/branches/#{protected_branch}/protect", user),
- developers_can_push: 'foobar', developers_can_merge: 'foo'
+ it "returns success when `protect` is called twice with `developers_can_push` turned on" do
+ put api("/projects/#{project.id}/repository/branches/#{branch_name}/protect", user), developers_can_push: true
+ put api("/projects/#{project.id}/repository/branches/#{branch_name}/protect", user), developers_can_push: true
expect(response).to have_http_status(200)
- expect(json_response['name']).to eq(protected_branch)
+ expect(json_response['name']).to eq(branch_name)
expect(json_response['protected']).to eq(true)
expect(json_response['developers_can_push']).to eq(true)
+ expect(json_response['developers_can_merge']).to eq(false)
+ end
+
+ it "returns success when `protect` is called twice with `developers_can_merge` turned on" do
+ put api("/projects/#{project.id}/repository/branches/#{branch_name}/protect", user), developers_can_merge: true
+ put api("/projects/#{project.id}/repository/branches/#{branch_name}/protect", user), developers_can_merge: true
+
+ expect(response).to have_http_status(200)
+ expect(json_response['name']).to eq(branch_name)
+ expect(json_response['protected']).to eq(true)
+ expect(json_response['developers_can_push']).to eq(false)
expect(json_response['developers_can_merge']).to eq(true)
end
end
@@ -147,12 +209,6 @@ describe API::API, api: true do
put api("/projects/#{project.id}/repository/branches/#{branch_name}/protect", user2)
expect(response).to have_http_status(403)
end
-
- it "returns success when protect branch again" do
- put api("/projects/#{project.id}/repository/branches/#{branch_name}/protect", user)
- put api("/projects/#{project.id}/repository/branches/#{branch_name}/protect", user)
- expect(response).to have_http_status(200)
- end
end
describe "PUT /projects/:id/repository/branches/:branch/unprotect" do
diff --git a/spec/requests/api/commits_spec.rb b/spec/requests/api/commits_spec.rb
index 66fa0c0c01f..a6e8550fac3 100644
--- a/spec/requests/api/commits_spec.rb
+++ b/spec/requests/api/commits_spec.rb
@@ -72,6 +72,17 @@ describe API::API, api: true do
expect(json_response['message']).to include "\"since\" must be a timestamp in ISO 8601 format"
end
end
+
+ context "path optional parameter" do
+ it "returns project commits matching provided path parameter" do
+ path = 'files/ruby/popen.rb'
+
+ get api("/projects/#{project.id}/repository/commits?path=#{path}", user)
+
+ expect(json_response.size).to eq(3)
+ expect(json_response.first["id"]).to eq("570e7b2abdd848b95f2f578043fc23bd6f6fd24d")
+ end
+ end
end
describe "Create a commit with multiple files and actions" do
diff --git a/spec/requests/api/labels_spec.rb b/spec/requests/api/labels_spec.rb
index 867bc615b97..46641fcd846 100644
--- a/spec/requests/api/labels_spec.rb
+++ b/spec/requests/api/labels_spec.rb
@@ -159,14 +159,14 @@ describe API::API, api: true do
it 'returns 400 if no label name given' do
put api("/projects/#{project.id}/labels", user), new_name: 'label2'
expect(response).to have_http_status(400)
- expect(json_response['message']).to eq('400 (Bad request) "name" not given')
+ expect(json_response['error']).to eq('name is missing')
end
it 'returns 400 if no new parameters given' do
put api("/projects/#{project.id}/labels", user), name: 'label1'
expect(response).to have_http_status(400)
- expect(json_response['message']).to eq('Required parameters '\
- '"new_name" or "color" missing')
+ expect(json_response['error']).to eq('new_name, color, description are missing, '\
+ 'at least one parameter must be provided')
end
it 'returns 400 for invalid name' do
diff --git a/spec/requests/api/members_spec.rb b/spec/requests/api/members_spec.rb
index d22e0595788..493c0a893d1 100644
--- a/spec/requests/api/members_spec.rb
+++ b/spec/requests/api/members_spec.rb
@@ -328,4 +328,15 @@ describe API::Members, api: true do
it_behaves_like 'DELETE /:sources/:id/members/:user_id', 'group' do
let(:source) { group }
end
+
+ context 'Adding owner to project' do
+ it 'returns 403' do
+ expect do
+ post api("/projects/#{project.id}/members", master),
+ user_id: stranger.id, access_level: Member::OWNER
+
+ expect(response).to have_http_status(422)
+ end.to change { project.members.count }.by(0)
+ end
+ end
end
diff --git a/spec/requests/api/notes_spec.rb b/spec/requests/api/notes_spec.rb
index d58bedc3bf7..0124b7271b3 100644
--- a/spec/requests/api/notes_spec.rb
+++ b/spec/requests/api/notes_spec.rb
@@ -221,12 +221,23 @@ describe API::API, api: true do
end
end
- context 'when the user is posting an award emoji' do
+ context 'when the user is posting an award emoji on an issue created by someone else' do
+ let(:issue2) { create(:issue, project: project) }
+
it 'returns an award emoji' do
+ post api("/projects/#{project.id}/issues/#{issue2.id}/notes", user), body: ':+1:'
+
+ expect(response).to have_http_status(201)
+ expect(json_response['awardable_id']).to eq issue2.id
+ end
+ end
+
+ context 'when the user is posting an award emoji on his/her own issue' do
+ it 'creates a new issue note' do
post api("/projects/#{project.id}/issues/#{issue.id}/notes", user), body: ':+1:'
expect(response).to have_http_status(201)
- expect(json_response['awardable_id']).to eq issue.id
+ expect(json_response['body']).to eq(':+1:')
end
end
end
diff --git a/spec/requests/api/users_spec.rb b/spec/requests/api/users_spec.rb
index f83f4d2c9b1..d48752473f3 100644
--- a/spec/requests/api/users_spec.rb
+++ b/spec/requests/api/users_spec.rb
@@ -846,7 +846,7 @@ describe API::API, api: true do
end
end
- describe 'PUT /user/:id/block' do
+ describe 'PUT /users/:id/block' do
before { admin }
it 'blocks existing user' do
put api("/users/#{user.id}/block", admin)
@@ -873,7 +873,7 @@ describe API::API, api: true do
end
end
- describe 'PUT /user/:id/unblock' do
+ describe 'PUT /users/:id/unblock' do
let(:blocked_user) { create(:user, state: 'blocked') }
before { admin }
@@ -914,7 +914,7 @@ describe API::API, api: true do
end
end
- describe 'GET /user/:id/events' do
+ describe 'GET /users/:id/events' do
let(:user) { create(:user) }
let(:project) { create(:empty_project) }
let(:note) { create(:note_on_issue, note: 'What an awesome day!', project: project) }
diff --git a/spec/services/issues/move_service_spec.rb b/spec/services/issues/move_service_spec.rb
index 93bf0f64963..f0ded06b785 100644
--- a/spec/services/issues/move_service_spec.rb
+++ b/spec/services/issues/move_service_spec.rb
@@ -23,14 +23,15 @@ describe Issues::MoveService, services: true do
old_project.team << [user, :reporter]
new_project.team << [user, :reporter]
- ['label1', 'label2'].each do |label|
+ labels = Array.new(2) { |x| "label%d" % (x + 1) }
+
+ labels.each do |label|
old_issue.labels << create(:label,
project_id: old_project.id,
title: label)
- end
- new_project.labels << create(:label, title: 'label1')
- new_project.labels << create(:label, title: 'label2')
+ new_project.labels << create(:label, title: label)
+ end
end
end
@@ -207,10 +208,10 @@ describe Issues::MoveService, services: true do
end
end
- describe 'rewritting references' do
+ describe 'rewriting references' do
include_context 'issue move executed'
- context 'issue reference' do
+ context 'issue references' do
let(:another_issue) { create(:issue, project: old_project) }
let(:description) { "Some description #{another_issue.to_reference}" }
@@ -219,6 +220,16 @@ describe Issues::MoveService, services: true do
.to eq "Some description #{old_project.to_reference}#{another_issue.to_reference}"
end
end
+
+ context "user references" do
+ let(:another_issue) { create(:issue, project: old_project) }
+ let(:description) { "Some description #{user.to_reference}" }
+
+ it "doesn't throw any errors for issues containing user references" do
+ expect(new_issue.description)
+ .to eq "Some description #{user.to_reference}"
+ end
+ end
end
context 'moving to same project' do
@@ -277,5 +288,25 @@ describe Issues::MoveService, services: true do
it { expect { move }.to raise_error(StandardError, /permissions/) }
end
end
+
+ context 'movable issue with no assigned labels' do
+ before do
+ old_project.team << [user, :reporter]
+ new_project.team << [user, :reporter]
+
+ labels = Array.new(2) { |x| "label%d" % (x + 1) }
+
+ labels.each do |label|
+ new_project.labels << create(:label, title: label)
+ end
+ end
+
+ include_context 'issue move executed'
+
+ it 'does not assign labels to new issue' do
+ expected_label_titles = new_issue.reload.labels.map(&:title)
+ expect(expected_label_titles.size).to eq 0
+ end
+ end
end
end
diff --git a/spec/services/merge_requests/assign_issues_service_spec.rb b/spec/services/merge_requests/assign_issues_service_spec.rb
index 7aeb95a15ea..5034b6ef33f 100644
--- a/spec/services/merge_requests/assign_issues_service_spec.rb
+++ b/spec/services/merge_requests/assign_issues_service_spec.rb
@@ -46,4 +46,16 @@ describe MergeRequests::AssignIssuesService, services: true do
it 'assigns these to the merge request owner' do
expect { service.execute }.to change { issue.reload.assignee }.to(user)
end
+
+ it 'ignores external issues' do
+ external_issue = ExternalIssue.new('JIRA-123', project)
+ service = described_class.new(
+ project,
+ user,
+ merge_request: merge_request,
+ closes_issues: [external_issue]
+ )
+
+ expect(service.assignable_issues.count).to eq 0
+ end
end
diff --git a/spec/mailers/shared/notify.rb b/spec/support/notify_shared_examples.rb
index 3956d05060b..3956d05060b 100644
--- a/spec/mailers/shared/notify.rb
+++ b/spec/support/notify_shared_examples.rb
diff --git a/spec/support/select2_helper.rb b/spec/support/select2_helper.rb
index 35cc51725c6..d30cc8ff9f2 100644
--- a/spec/support/select2_helper.rb
+++ b/spec/support/select2_helper.rb
@@ -17,9 +17,9 @@ module Select2Helper
selector = options.fetch(:from)
if options[:multiple]
- execute_script("$('#{selector}').select2('val', ['#{value}'], true);")
+ execute_script("$('#{selector}').select2('val', ['#{value}']).trigger('change');")
else
- execute_script("$('#{selector}').select2('val', '#{value}', true);")
+ execute_script("$('#{selector}').select2('val', '#{value}').trigger('change');")
end
end
end
diff --git a/spec/tasks/gitlab/backup_rake_spec.rb b/spec/tasks/gitlab/backup_rake_spec.rb
index 548e7780c36..73bc8326f02 100644
--- a/spec/tasks/gitlab/backup_rake_spec.rb
+++ b/spec/tasks/gitlab/backup_rake_spec.rb
@@ -9,6 +9,7 @@ describe 'gitlab:app namespace rake task' do
Rake.application.rake_require 'tasks/gitlab/backup'
Rake.application.rake_require 'tasks/gitlab/shell'
Rake.application.rake_require 'tasks/gitlab/db'
+ Rake.application.rake_require 'tasks/cache'
# empty task as env is already loaded
Rake::Task.define_task :environment
diff --git a/spec/views/projects/merge_requests/_commits.html.haml_spec.rb b/spec/views/projects/merge_requests/_commits.html.haml_spec.rb
new file mode 100644
index 00000000000..6f70b3daf8e
--- /dev/null
+++ b/spec/views/projects/merge_requests/_commits.html.haml_spec.rb
@@ -0,0 +1,38 @@
+require 'spec_helper'
+
+describe 'projects/merge_requests/show/_commits.html.haml' do
+ include Devise::Test::ControllerHelpers
+
+ let(:user) { create(:user) }
+ let(:target_project) { create(:project) }
+
+ let(:source_project) do
+ create(:project, forked_from_project: target_project)
+ end
+
+ let(:merge_request) do
+ create(:merge_request, :simple,
+ source_project: source_project,
+ target_project: target_project,
+ author: user)
+ end
+
+ before do
+ controller.prepend_view_path('app/views/projects')
+
+ assign(:merge_request, merge_request)
+ assign(:commits, merge_request.commits)
+ end
+
+ it 'shows commits from source project' do
+ render
+
+ commit = source_project.commit(merge_request.source_branch)
+ href = namespace_project_commit_path(
+ source_project.namespace,
+ source_project,
+ commit)
+
+ expect(rendered).to have_link(Commit.truncate_sha(commit.sha), href: href)
+ end
+end
diff --git a/spec/workers/concerns/build_queue_spec.rb b/spec/workers/concerns/build_queue_spec.rb
new file mode 100644
index 00000000000..6bf955e0be2
--- /dev/null
+++ b/spec/workers/concerns/build_queue_spec.rb
@@ -0,0 +1,14 @@
+require 'spec_helper'
+
+describe BuildQueue do
+ let(:worker) do
+ Class.new do
+ include Sidekiq::Worker
+ include BuildQueue
+ end
+ end
+
+ it 'sets the queue name of a worker' do
+ expect(worker.sidekiq_options['queue'].to_s).to eq('build')
+ end
+end
diff --git a/spec/workers/concerns/cronjob_queue_spec.rb b/spec/workers/concerns/cronjob_queue_spec.rb
new file mode 100644
index 00000000000..5d1336c21a6
--- /dev/null
+++ b/spec/workers/concerns/cronjob_queue_spec.rb
@@ -0,0 +1,18 @@
+require 'spec_helper'
+
+describe CronjobQueue do
+ let(:worker) do
+ Class.new do
+ include Sidekiq::Worker
+ include CronjobQueue
+ end
+ end
+
+ it 'sets the queue name of a worker' do
+ expect(worker.sidekiq_options['queue'].to_s).to eq('cronjob')
+ end
+
+ it 'disables retrying of failed jobs' do
+ expect(worker.sidekiq_options['retry']).to eq(false)
+ end
+end
diff --git a/spec/workers/concerns/dedicated_sidekiq_queue_spec.rb b/spec/workers/concerns/dedicated_sidekiq_queue_spec.rb
new file mode 100644
index 00000000000..512baec8b7e
--- /dev/null
+++ b/spec/workers/concerns/dedicated_sidekiq_queue_spec.rb
@@ -0,0 +1,20 @@
+require 'spec_helper'
+
+describe DedicatedSidekiqQueue do
+ let(:worker) do
+ Class.new do
+ def self.name
+ 'Foo::Bar::DummyWorker'
+ end
+
+ include Sidekiq::Worker
+ include DedicatedSidekiqQueue
+ end
+ end
+
+ describe 'queue names' do
+ it 'sets the queue name based on the class name' do
+ expect(worker.sidekiq_options['queue']).to eq('foo_bar_dummy')
+ end
+ end
+end
diff --git a/spec/workers/concerns/pipeline_queue_spec.rb b/spec/workers/concerns/pipeline_queue_spec.rb
new file mode 100644
index 00000000000..40794d0e42a
--- /dev/null
+++ b/spec/workers/concerns/pipeline_queue_spec.rb
@@ -0,0 +1,14 @@
+require 'spec_helper'
+
+describe PipelineQueue do
+ let(:worker) do
+ Class.new do
+ include Sidekiq::Worker
+ include PipelineQueue
+ end
+ end
+
+ it 'sets the queue name of a worker' do
+ expect(worker.sidekiq_options['queue'].to_s).to eq('pipeline')
+ end
+end
diff --git a/spec/workers/concerns/repository_check_queue_spec.rb b/spec/workers/concerns/repository_check_queue_spec.rb
new file mode 100644
index 00000000000..8868e969829
--- /dev/null
+++ b/spec/workers/concerns/repository_check_queue_spec.rb
@@ -0,0 +1,18 @@
+require 'spec_helper'
+
+describe RepositoryCheckQueue do
+ let(:worker) do
+ Class.new do
+ include Sidekiq::Worker
+ include RepositoryCheckQueue
+ end
+ end
+
+ it 'sets the queue name of a worker' do
+ expect(worker.sidekiq_options['queue'].to_s).to eq('repository_check')
+ end
+
+ it 'disables retrying of failed jobs' do
+ expect(worker.sidekiq_options['retry']).to eq(false)
+ end
+end
diff --git a/spec/workers/every_sidekiq_worker_spec.rb b/spec/workers/every_sidekiq_worker_spec.rb
new file mode 100644
index 00000000000..fc9adf47c1e
--- /dev/null
+++ b/spec/workers/every_sidekiq_worker_spec.rb
@@ -0,0 +1,44 @@
+require 'spec_helper'
+
+describe 'Every Sidekiq worker' do
+ let(:workers) do
+ root = Rails.root.join('app', 'workers')
+ concerns = root.join('concerns').to_s
+
+ workers = Dir[root.join('**', '*.rb')].
+ reject { |path| path.start_with?(concerns) }
+
+ workers.map do |path|
+ ns = Pathname.new(path).relative_path_from(root).to_s.gsub('.rb', '')
+
+ ns.camelize.constantize
+ end
+ end
+
+ it 'does not use the default queue' do
+ workers.each do |worker|
+ expect(worker.sidekiq_options['queue'].to_s).not_to eq('default')
+ end
+ end
+
+ it 'uses the cronjob queue when the worker runs as a cronjob' do
+ cron_workers = Settings.cron_jobs.
+ map { |job_name, options| options['job_class'].constantize }.
+ to_set
+
+ workers.each do |worker|
+ next unless cron_workers.include?(worker)
+
+ expect(worker.sidekiq_options['queue'].to_s).to eq('cronjob')
+ end
+ end
+
+ it 'defines the queue in the Sidekiq configuration file' do
+ config = YAML.load_file(Rails.root.join('config', 'sidekiq_queues.yml').to_s)
+ queue_names = config[:queues].map { |(queue, _)| queue }.to_set
+
+ workers.each do |worker|
+ expect(queue_names).to include(worker.sidekiq_options['queue'].to_s)
+ end
+ end
+end