summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitlab-ci.yml29
-rw-r--r--.vagrant_enabled0
-rw-r--r--CHANGELOG54
-rw-r--r--GITLAB_SHELL_VERSION2
-rw-r--r--Gemfile8
-rw-r--r--Gemfile.lock14
-rw-r--r--app/assets/javascripts/api.js7
-rw-r--r--app/assets/javascripts/project_new.js4
-rw-r--r--app/assets/javascripts/users_select.js5
-rw-r--r--app/assets/stylesheets/pages/merge_requests.scss10
-rw-r--r--app/assets/stylesheets/pages/pipelines.scss7
-rw-r--r--app/controllers/projects/merge_requests_controller.rb23
-rw-r--r--app/controllers/projects/tags_controller.rb2
-rw-r--r--app/controllers/snippets_controller.rb14
-rw-r--r--app/helpers/button_helper.rb4
-rw-r--r--app/helpers/merge_requests_helper.rb17
-rw-r--r--app/models/ci/pipeline.rb17
-rw-r--r--app/models/commit_status.rb16
-rw-r--r--app/models/compare.rb21
-rw-r--r--app/models/cycle_analytics.rb14
-rw-r--r--app/models/event.rb8
-rw-r--r--app/models/merge_request_diff.rb7
-rw-r--r--app/models/project_group_link.rb2
-rw-r--r--app/models/repository.rb3
-rw-r--r--app/policies/project_policy.rb3
-rw-r--r--app/services/compare_service.rb7
-rw-r--r--app/services/merge_requests/add_todo_when_build_fails_service.rb4
-rw-r--r--app/services/merge_requests/assign_issues_service.rb35
-rw-r--r--app/services/merge_requests/base_service.rb27
-rw-r--r--app/services/merge_requests/merge_when_build_succeeds_service.rb9
-rw-r--r--app/services/merge_requests/update_service.rb5
-rw-r--r--app/services/notification_service.rb6
-rw-r--r--app/services/slash_commands/interpret_service.rb21
-rw-r--r--app/services/todo_service.rb6
-rw-r--r--app/views/admin/application_settings/_form.html.haml6
-rw-r--r--app/views/projects/branches/_branch.html.haml4
-rw-r--r--app/views/projects/commit/_pipeline_status_group.html.haml11
-rw-r--r--app/views/projects/edit.html.haml2
-rw-r--r--app/views/projects/merge_requests/show/_versions.html.haml10
-rw-r--r--app/views/projects/merge_requests/widget/_open.html.haml1
-rw-r--r--app/views/snippets/show.html.haml1
-rw-r--r--app/workers/pipeline_process_worker.rb (renamed from app/workers/process_pipeline_worker.rb)2
-rw-r--r--app/workers/pipeline_success_worker.rb12
-rw-r--r--app/workers/pipeline_update_worker.rb (renamed from app/workers/update_pipeline_worker.rb)2
-rw-r--r--config/routes/project.rb3
-rw-r--r--config/routes/snippets.rb1
-rw-r--r--db/schema.rb1174
-rw-r--r--doc/README.md7
-rw-r--r--doc/administration/high_availability/gitlab.md6
-rw-r--r--doc/administration/monitoring/performance/gitlab_configuration.md40
-rw-r--r--doc/administration/monitoring/performance/grafana_configuration.md111
-rw-r--r--doc/administration/monitoring/performance/img/grafana_dashboard_dropdown.pngbin0 -> 14368 bytes
-rw-r--r--doc/administration/monitoring/performance/img/grafana_dashboard_import.pngbin0 -> 18267 bytes
-rw-r--r--doc/administration/monitoring/performance/img/grafana_data_source_configuration.pngbin0 -> 26060 bytes
-rw-r--r--doc/administration/monitoring/performance/img/grafana_data_source_empty.pngbin0 -> 21821 bytes
-rw-r--r--doc/administration/monitoring/performance/img/grafana_save_icon.pngbin0 -> 9107 bytes
-rw-r--r--doc/administration/monitoring/performance/img/metrics_gitlab_configuration_settings.pngbin0 -> 61357 bytes
-rw-r--r--doc/administration/monitoring/performance/influxdb_configuration.md193
-rw-r--r--doc/administration/monitoring/performance/influxdb_schema.md97
-rw-r--r--doc/administration/monitoring/performance/introduction.md65
-rw-r--r--doc/administration/operations.md6
-rw-r--r--doc/administration/operations/cleaning_up_redis_sessions.md52
-rw-r--r--doc/administration/operations/moving_repositories.md180
-rw-r--r--doc/administration/operations/sidekiq_memory_killer.md40
-rw-r--r--doc/administration/operations/unicorn.md86
-rw-r--r--doc/administration/troubleshooting/debug.md4
-rw-r--r--doc/api/README.md5
-rw-r--r--doc/api/projects.md2
-rw-r--r--doc/api/templates/gitignores.md579
-rw-r--r--doc/api/templates/gitlab_ci_ymls.md120
-rw-r--r--doc/api/templates/licenses.md (renamed from doc/api/licenses.md)8
-rw-r--r--doc/api/users.md146
-rw-r--r--doc/api/version.md23
-rw-r--r--doc/development/doc_styleguide.md23
-rw-r--r--doc/development/frontend.md11
-rw-r--r--doc/monitoring/health_check.md67
-rw-r--r--doc/monitoring/performance/gitlab_configuration.md41
-rw-r--r--doc/monitoring/performance/grafana_configuration.md112
-rw-r--r--doc/monitoring/performance/influxdb_configuration.md194
-rw-r--r--doc/monitoring/performance/influxdb_schema.md98
-rw-r--r--doc/monitoring/performance/introduction.md66
-rw-r--r--doc/operations/README.md6
-rw-r--r--doc/operations/cleaning_up_redis_sessions.md53
-rw-r--r--doc/operations/moving_repositories.md181
-rw-r--r--doc/operations/sidekiq_memory_killer.md41
-rw-r--r--doc/operations/unicorn.md87
-rw-r--r--doc/user/admin_area/monitoring/health_check.md66
-rw-r--r--doc/user/admin_area/monitoring/img/health_check_token.png (renamed from doc/monitoring/img/health_check_token.png)bin6630 -> 6630 bytes
-rw-r--r--doc/user/permissions.md1
-rw-r--r--doc/user/project/cycle_analytics.md22
-rw-r--r--doc/user/project/git_attributes.md22
-rw-r--r--doc/user/project/merge_requests/merge_when_build_succeeds.md20
-rw-r--r--lib/api/api.rb2
-rw-r--r--lib/api/license_templates.rb58
-rw-r--r--lib/api/projects.rb6
-rw-r--r--lib/api/templates.rb124
-rw-r--r--lib/api/users.rb20
-rw-r--r--lib/api/variables.rb89
-rw-r--r--lib/api/version.rb12
-rw-r--r--lib/extracts_path.rb33
-rw-r--r--spec/controllers/projects/commit_controller_spec.rb16
-rw-r--r--spec/controllers/projects/commits_controller_spec.rb41
-rw-r--r--spec/controllers/projects/merge_requests_controller_spec.rb48
-rw-r--r--spec/controllers/projects/tags_controller_spec.rb14
-rw-r--r--spec/features/merge_requests/assign_issues_spec.rb51
-rw-r--r--spec/features/merge_requests/merge_when_build_succeeds_spec.rb57
-rw-r--r--spec/features/projects/guest_navigation_menu_spec.rb28
-rw-r--r--spec/features/security/project/private_access_spec.rb2
-rw-r--r--spec/helpers/search_helper_spec.rb2
-rw-r--r--spec/lib/extracts_path_spec.rb87
-rw-r--r--spec/lib/gitlab/data_builder/push_spec.rb8
-rw-r--r--spec/mailers/notify_spec.rb2
-rw-r--r--spec/models/appearance_spec.rb2
-rw-r--r--spec/models/commit_spec.rb8
-rw-r--r--spec/models/cycle_analytics/code_spec.rb88
-rw-r--r--spec/models/cycle_analytics/issue_spec.rb2
-rw-r--r--spec/models/cycle_analytics/plan_spec.rb2
-rw-r--r--spec/models/cycle_analytics/review_spec.rb4
-rw-r--r--spec/models/cycle_analytics/test_spec.rb6
-rw-r--r--spec/models/deploy_key_spec.rb3
-rw-r--r--spec/models/event_spec.rb109
-rw-r--r--spec/models/key_spec.rb3
-rw-r--r--spec/models/label_link_spec.rb3
-rw-r--r--spec/models/merge_request_diff_spec.rb52
-rw-r--r--spec/models/merge_request_spec.rb4
-rw-r--r--spec/models/project_group_link_spec.rb2
-rw-r--r--spec/models/repository_spec.rb29
-rw-r--r--spec/policies/project_policy_spec.rb5
-rw-r--r--spec/requests/api/commits_spec.rb12
-rw-r--r--spec/requests/api/license_templates_spec.rb136
-rw-r--r--spec/requests/api/projects_spec.rb42
-rw-r--r--spec/requests/api/repositories_spec.rb8
-rw-r--r--spec/requests/api/templates_spec.rb204
-rw-r--r--spec/requests/api/users_spec.rb54
-rw-r--r--spec/requests/api/version_spec.rb27
-rw-r--r--spec/requests/git_http_spec.rb4
-rw-r--r--spec/routing/project_routing_spec.rb2
-rw-r--r--spec/services/compare_service_spec.rb21
-rw-r--r--spec/services/merge_requests/assign_issues_service_spec.rb49
-rw-r--r--spec/services/merge_requests/merge_when_build_succeeds_service_spec.rb93
-rw-r--r--spec/services/merge_requests/refresh_service_spec.rb4
-rw-r--r--spec/services/merge_requests/update_service_spec.rb18
-rw-r--r--spec/services/notification_service_spec.rb57
-rw-r--r--spec/services/slash_commands/interpret_service_spec.rb43
-rw-r--r--spec/services/system_note_service_spec.rb8
-rw-r--r--spec/services/todo_service_spec.rb22
-rw-r--r--spec/spec_helper.rb5
-rw-r--r--spec/support/test_env.rb4
-rw-r--r--spec/workers/emails_on_push_worker_spec.rb4
-rw-r--r--spec/workers/pipeline_proccess_worker_spec.rb (renamed from spec/workers/process_pipeline_worker_spec.rb)2
-rw-r--r--spec/workers/pipeline_success_worker_spec.rb24
-rw-r--r--spec/workers/pipeline_update_worker_spec.rb (renamed from spec/workers/update_pipeline_worker_spec.rb)2
152 files changed, 4162 insertions, 2226 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index cb6f691058e..7d19f55aca3 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -19,8 +19,6 @@ variables:
before_script:
- source ./scripts/prepare_build.sh
- cp config/gitlab.yml.example config/gitlab.yml
- - mkdir -p tmp/tests
- - mount -t tmpfs tmpfs tmp/tests || echo "tmpfs mount failed, falling back to disc"
- bundle --version
- '[ "$USE_BUNDLE_INSTALL" != "true" ] || retry bundle install --without postgres production --jobs $(nproc) "${FLAGS[@]}"'
- retry gem install knapsack
@@ -257,6 +255,12 @@ lint-doc:
script:
- scripts/lint-doc.sh
+bundler:check:
+ stage: test
+ <<: *ruby-static-analysis
+ script:
+ - bundle check
+
bundler:audit:
stage: test
<<: *ruby-static-analysis
@@ -295,6 +299,14 @@ coverage:
- coverage/index.html
- coverage/assets/
+# Trigger docs build
+trigger_docs:
+ stage: post-test
+ script:
+ - "curl -X POST -F token=${DOCS_TRIGGER_TOKEN} -F ref=master https://gitlab.com/api/v3/projects/38069/trigger/builds"
+ only:
+ - master
+
# Notify slack in the end
notify:slack:
@@ -327,3 +339,16 @@ pages:
- public
only:
- master
+
+# Insurance in case a gem needed by one of our releases gets yanked from
+# rubygems.org in the future.
+cache gems:
+ only:
+ - tags
+ variables:
+ SETUP_DB: "false"
+ script:
+ - bundle package --all --all-platforms
+ artifacts:
+ paths:
+ - vendor/cache
diff --git a/.vagrant_enabled b/.vagrant_enabled
deleted file mode 100644
index e69de29bb2d..00000000000
--- a/.vagrant_enabled
+++ /dev/null
diff --git a/CHANGELOG b/CHANGELOG
index 2095e4fc0fd..4129e9a8461 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,31 +1,41 @@
Please view this file on the master branch, on stable branches it's out of date.
v 8.13.0 (unreleased)
+ - Improve Merge When Build Succeeds triggers and execute on pipeline success. (!6675)
+ - Respond with 404 Not Found for non-existent tags (Linus Thiel)
- Truncate long labels with ellipsis in labels page
- - Bump mail_room to v0.8.1 to fix thread cleanup issue
+ - Adding members no longer silently fails when there is extra whitespace
- Update runner version only when updating contacted_at
- Add link from system note to compare with previous version
- - Improve issue load time performance by avoiding ORDER BY in find_by call
- - Use gitlab-shell v3.6.2 (GIT TRACE logging)
+ - Use gitlab-shell v3.6.6
- Add `/projects/visible` API endpoint (Ben Boeckel)
- Fix centering of custom header logos (Ashley Dumaine)
- ExpireBuildArtifactsWorker query builds table without ordering enqueuing one job per build to cleanup
- Add an example for testing a phoenix application with Gitlab CI in the docs (Manthan Mallikarjun)
+ - Updating verbiage on git basics to be more intuitive
+ - Clarify documentation for Runners API (Gennady Trafimenkov)
+ - Change user & group landing page routing from /u/:username to /:username
+ - Prevent running GfmAutocomplete setup for each diff note !6569
+ - Added documentation for .gitattributes files
- AbstractReferenceFilter caches project_refs on RequestStore when active
- Replaced the check sign to arrow in the show build view. !6501
- Add a /wip slash command to toggle the Work In Progress status of a merge request. !6259 (tbalthazar)
- Fix Error 500 when viewing old merge requests with bad diff data
+ - Create a new /templates namespace for the /licenses, /gitignores and /gitlab_ci_ymls API endpoints. !5717 (tbalthazar)
- Speed-up group milestones show page
- Fix inconsistent options dropdown caret on mobile viewports (ClemMakesApps)
- Don't include archived projects when creating group milestones. !4940 (Jeroen Jacobs)
- Add tag shortcut from the Commit page. !6543
- Keep refs for each deployment
+ - Allow browsing branches that end with '.atom'
- Log LDAP lookup errors and don't swallow unrelated exceptions. !6103 (Markus Koller)
- Add more tests for calendar contribution (ClemMakesApps)
- Update Gitlab Shell to fix some problems with moving projects between storages
- Cache rendered markdown in the database, rather than Redis
- Avoid database queries on Banzai::ReferenceParser::BaseParser for nodes without references
+ - Do not alter 'force_remove_source_branch' options on MergeRequest unless specified
- Simplify Mentionable concern instance methods
+ - API: Ability to retrieve version information (Robert Schilling)
- Fix permission for setting an issue's due date
- API: Multi-file commit !6096 (mahcsig)
- Revert "Label list shows all issues (opened or closed) with that label"
@@ -42,6 +52,7 @@ v 8.13.0 (unreleased)
- Fix todos page mobile viewport layout (ClemMakesApps)
- Fix inconsistent highlighting of already selected activity nav-links (ClemMakesApps)
- Remove redundant mixins (ClemMakesApps)
+ - Added 'Download' button to the Snippets page (Justin DiPierro)
- Fix robots.txt disallowing access to groups starting with "s" (Matt Harrison)
- Close open merge request without source project (Katarzyna Kobierska Ula Budziszewska)
- Fix that manual jobs would no longer block jobs in the next stage. !6604
@@ -65,12 +76,15 @@ v 8.13.0 (unreleased)
- Fix Long commit messages overflow viewport in file tree
- Revert avoid touching file system on Build#artifacts?
- Stop using a Redis lease when updating the project activity timestamp whenever a new event is created
+ - Add disabled delete button to protected branches (ClemMakesApps)
- Add broadcast messages and alerts below sub-nav
- Better empty state for Groups view
+ - API: New /users/:id/events endpoint
- Update ruby-prof to 0.16.2. !6026 (Elan Ruusamäe)
- Replace bootstrap caret with fontawesome caret (ClemMakesApps)
- Fix unnecessary escaping of reserved HTML characters in milestone title. !6533
- Add organization field to user profile
+ - Ignore deployment for statistics in Cycle Analytics, except in staging and production stages
- Fix enter key when navigating search site search dropdown. !6643 (Brennan Roberts)
- Fix deploy status responsiveness error !6633
- Make searching for commits case insensitive
@@ -79,23 +93,37 @@ v 8.13.0 (unreleased)
- API: expose pipeline data in builds API (!6502, Guilherme Salazar)
- Notify the Merger about merge after successful build (Dimitris Karakasilis)
- Reorder issue and merge request titles to show IDs first. !6503 (Greg Laubenstein)
- - Add a new gitlab:users:clear_all_authentication_tokens task. !6745
- Reduce queries needed to find users using their SSH keys when pushing commits
- Prevent rendering the link to all when the author has no access (Katarzyna Kobierska Ula Budziszewska)
- Fix broken repository 500 errors in project list
- Fix Pipeline list commit column width should be adjusted
- Close todos when accepting merge requests via the API !6486 (tonygambone)
+ - Ability to batch assign issues relating to a merge request to the author. !5725 (jamedjo)
- Changed Slack service user referencing from full name to username (Sebastian Poxhofer)
- Retouch environments list and deployments list
+ - Add multiple command support for all label related slash commands !6780 (barthc)
- Add Container Registry on/off status to Admin Area !6638 (the-undefined)
- Allow empty merge requests !6384 (Artem Sidorenko)
- Grouped pipeline dropdown is a scrollable container
- Cleanup Ci::ApplicationController. !6757 (Takuya Noguchi)
+ - Fixes padding in all clipboard icons that have .btn class
- Fix a typo in doc/api/labels.md
- API: all unknown routing will be handled with 404 Not Found
+ - Make guests unable to view MRs on private projects
+
+v 8.12.7
+ - Use gitlab-markup gem instead of github-markup to fix `.rst` file rendering. !6659
+
+v 8.12.6
+ - Update mailroom to 0.8.1 in Gemfile.lock !6814
-v 8.12.5 (unreleased)
- - Update the mail_room gem to 0.8.1 to fix a race condition with the mailbox watching thread
+v 8.12.5
+ - Switch from request to env in ::API::Helpers. !6615
+ - Update the mail_room gem to 0.8.1 to fix a race condition with the mailbox watching thread. !6714
+ - Improve issue load time performance by avoiding ORDER BY in find_by call. !6724
+ - Add a new gitlab:users:clear_all_authentication_tokens task. !6745
+ - Don't send Private-Token (API authentication) headers to Sentry
+ - Share projects via the API only with groups the authenticated user can access
v 8.12.4
- Fix "Copy to clipboard" tooltip to say "Copied!" when clipboard button is clicked. !6294 (lukehowell)
@@ -110,7 +138,7 @@ v 8.12.4
- Fix failed project deletion when feature visibility set to private. !6688
- Prevent claiming associated model IDs via import.
- Set GitLab project exported file permissions to owner only
- - Change user & group landing page routing from /u/:username to /:username
+ - Improve the way merge request versions are compared with each other
v 8.12.3
- Update Gitlab Shell to support low IO priority for storage moves
@@ -130,13 +158,13 @@ v 8.12.2
- Fix bug where 'Search results' repeated many times when a search in the emoji search form is cleared (Xavier Bick) (@zeiv)
- Fix resolve discussion buttons endpoint path
- Refactor remnants of CoffeeScript destructured opts and super !6261
- - Prevent running GfmAutocomplete setup for each diff note !6569
v 8.12.1
- Fix a memory leak in HTML::Pipeline::SanitizationFilter::WHITELIST
- Fix issue with search filter labels not displaying
v 8.12.0
+ - Removes inconsistency regarding tagging immediatelly as merged once you create a new branch. !6408
- Update the rouge gem to 2.0.6, which adds highlighting support for JSX, Prometheus, and others. !6251
- Only check :can_resolve permission if the note is resolvable
- Bump fog-aws to v0.11.0 to support ap-south-1 region
@@ -324,6 +352,10 @@ v 8.12.0
- Fix non-master branch readme display in tree view
- Add UX improvements for merge request version diffs
+v 8.11.9
+ - Don't send Private-Token (API authentication) headers to Sentry
+ - Share projects via the API only with groups the authenticated user can access
+
v 8.11.8
- Respect the fork_project permission when forking projects
- Set a restrictive CORS policy on the API for credentialed requests
@@ -334,7 +366,6 @@ v 8.11.7
- Avoid conflict with admin labels when importing GitHub labels. !6158
- Restores `fieldName` to allow only string values in `gl_dropdown.js`. !6234
- Allow the Rails cookie to be used for API authentication.
- - Updating verbiage on git basics to be more intuitive
v 8.11.6
- Fix unnecessary horizontal scroll area in pipeline visualizations. !6005
@@ -495,7 +526,6 @@ v 8.11.0
- Add pipeline events hook
- Bump gitlab_git to speedup DiffCollection iterations
- Rewrite description of a blocked user in admin settings. (Elias Werberich)
- - Clarify documentation for Runners API (Gennady Trafimenkov)
- Make branches sortable without push permission !5462 (winniehell)
- Check for Ci::Build artifacts at database level on pipeline partial
- Convert image diff background image to CSS (ClemMakesApps)
@@ -551,6 +581,10 @@ v 8.11.0
- Update gitlab_git gem to 10.4.7
- Simplify SQL queries of marking a todo as done
+v 8.10.12
+ - Don't send Private-Token (API authentication) headers to Sentry
+ - Share projects via the API only with groups the authenticated user can access
+
v 8.10.11
- Respect the fork_project permission when forking projects
- Set a restrictive CORS policy on the API for credentialed requests
diff --git a/GITLAB_SHELL_VERSION b/GITLAB_SHELL_VERSION
index 0f44168a4d5..4f2c1d15f6d 100644
--- a/GITLAB_SHELL_VERSION
+++ b/GITLAB_SHELL_VERSION
@@ -1 +1 @@
-3.6.4
+3.6.6
diff --git a/Gemfile b/Gemfile
index c5f1ce26daf..1204a546983 100644
--- a/Gemfile
+++ b/Gemfile
@@ -51,7 +51,7 @@ gem 'browser', '~> 2.2'
# Extracting information from a git repository
# Provide access to Gitlab::Git library
-gem 'gitlab_git', '~> 10.6.7'
+gem 'gitlab_git', '~> 10.6.8'
# LDAP Auth
# GitLab fork with several improvements to original library. For full list of changes
@@ -101,7 +101,7 @@ gem 'seed-fu', '~> 2.3.5'
# Markdown and HTML processing
gem 'html-pipeline', '~> 1.11.0'
gem 'deckar01-task_list', '1.0.5', require: 'task_list/railtie'
-gem 'github-markup', '~> 1.4'
+gem 'gitlab-markup', '~> 1.5.0'
gem 'redcarpet', '~> 3.3.3'
gem 'RedCloth', '~> 4.3.2'
gem 'rdoc', '~>3.6'
@@ -262,6 +262,8 @@ group :development do
# thin instead webrick
gem 'thin', '~> 1.7.0'
+
+ gem 'activerecord_sane_schema_dumper', '0.2'
end
group :development, :test do
@@ -341,7 +343,7 @@ gem 'oauth2', '~> 1.2.0'
gem 'paranoia', '~> 2.0'
# Health check
-gem 'health_check', '~> 2.1.0'
+gem 'health_check', '~> 2.2.0'
# System information
gem 'vmstat', '~> 2.2'
diff --git a/Gemfile.lock b/Gemfile.lock
index 2feec4c4eb5..25a176da8fd 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -38,6 +38,8 @@ GEM
multi_json (~> 1.11, >= 1.11.2)
rack (>= 1.5.2, < 3)
railties (>= 4.0, < 5.1)
+ activerecord_sane_schema_dumper (0.2)
+ rails (>= 4, < 5)
activesupport (4.2.7.1)
i18n (~> 0.7)
json (~> 1.7, >= 1.7.7)
@@ -280,7 +282,8 @@ GEM
diff-lcs (~> 1.1)
mime-types (>= 1.16, < 3)
posix-spawn (~> 0.3)
- gitlab_git (10.6.7)
+ gitlab-markup (1.5.0)
+ gitlab_git (10.6.8)
activesupport (~> 4.0)
charlock_holmes (~> 0.7.3)
github-linguist (~> 4.7.0)
@@ -334,7 +337,7 @@ GEM
thor
tilt
hashie (3.4.4)
- health_check (2.1.0)
+ health_check (2.2.1)
rails (>= 4.0)
hipchat (1.5.2)
httparty
@@ -805,6 +808,7 @@ DEPENDENCIES
RedCloth (~> 4.3.2)
ace-rails-ap (~> 4.1.0)
activerecord-session_store (~> 1.0.0)
+ activerecord_sane_schema_dumper (= 0.2)
acts-as-taggable-on (~> 4.0)
addressable (~> 2.3.8)
after_commit_queue (~> 1.3.0)
@@ -861,9 +865,9 @@ DEPENDENCIES
gemnasium-gitlab-service (~> 0.2)
gemojione (~> 3.0)
github-linguist (~> 4.7.0)
- github-markup (~> 1.4)
gitlab-flowdock-git-hook (~> 1.0.1)
- gitlab_git (~> 10.6.7)
+ gitlab-markup (~> 1.5.0)
+ gitlab_git (~> 10.6.8)
gitlab_omniauth-ldap (~> 1.2.1)
gollum-lib (~> 4.2)
gollum-rugged_adapter (~> 0.4.2)
@@ -872,7 +876,7 @@ DEPENDENCIES
grape-entity (~> 0.4.2)
haml_lint (~> 0.18.2)
hamlit (~> 2.6.1)
- health_check (~> 2.1.0)
+ health_check (~> 2.2.0)
hipchat (~> 1.5.0)
html-pipeline (~> 1.11.0)
httparty (~> 0.13.3)
diff --git a/app/assets/javascripts/api.js b/app/assets/javascripts/api.js
index 599331df3f5..56ec1489f89 100644
--- a/app/assets/javascripts/api.js
+++ b/app/assets/javascripts/api.js
@@ -6,11 +6,10 @@
groupProjectsPath: "/api/:version/groups/:id/projects.json",
projectsPath: "/api/:version/projects.json?simple=true",
labelsPath: "/:namespace_path/:project_path/labels",
- licensePath: "/api/:version/licenses/:key",
- gitignorePath: "/api/:version/gitignores/:key",
- gitlabCiYmlPath: "/api/:version/gitlab_ci_ymls/:key",
+ licensePath: "/api/:version/templates/licenses/:key",
+ gitignorePath: "/api/:version/templates/gitignores/:key",
+ gitlabCiYmlPath: "/api/:version/templates/gitlab_ci_ymls/:key",
issuableTemplatePath: "/:namespace_path/:project_path/templates/:type/:key",
-
group: function(group_id, callback) {
var url = Api.buildUrl(Api.groupPath)
.replace(':id', group_id);
diff --git a/app/assets/javascripts/project_new.js b/app/assets/javascripts/project_new.js
index a787b11f2a9..3cf41505814 100644
--- a/app/assets/javascripts/project_new.js
+++ b/app/assets/javascripts/project_new.js
@@ -4,7 +4,9 @@
this.ProjectNew = (function() {
function ProjectNew() {
this.toggleSettings = bind(this.toggleSettings, this);
- this.$selects = $('.features select');
+ this.$selects = $('.features select').filter(function () {
+ return $(this).data('field');
+ });
$('.project-edit-container').on('ajax:before', (function(_this) {
return function() {
diff --git a/app/assets/javascripts/users_select.js b/app/assets/javascripts/users_select.js
index 4c071f334fd..6aa0e1cd2b6 100644
--- a/app/assets/javascripts/users_select.js
+++ b/app/assets/javascripts/users_select.js
@@ -261,10 +261,11 @@
}
}
if (showEmailUser && data.results.length === 0 && query.term.match(/^[^@]+@[^@]+$/)) {
+ var trimmed = query.term.trim();
emailUser = {
name: "Invite \"" + query.term + "\"",
- username: query.term,
- id: query.term
+ username: trimmed,
+ id: trimmed
};
data.results.unshift(emailUser);
}
diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss
index 1006b3e62e8..7cf69c56d15 100644
--- a/app/assets/stylesheets/pages/merge_requests.scss
+++ b/app/assets/stylesheets/pages/merge_requests.scss
@@ -401,8 +401,12 @@
padding: 16px;
}
+ .content-block {
+ border-top: 1px solid $border-color;
+ padding: $gl-padding-top $gl-padding;
+ }
+
.comments-disabled-notif {
- padding: 10px 16px;
.btn {
margin-left: 5px;
}
@@ -413,10 +417,6 @@
margin: 0 7px;
}
- .comments-disabled-notif {
- border-top: 1px solid $border-color;
- }
-
.dropdown-title {
color: $gl-text-color;
}
diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss
index 05f59279637..7843355f0ab 100644
--- a/app/assets/stylesheets/pages/pipelines.scss
+++ b/app/assets/stylesheets/pages/pipelines.scss
@@ -420,8 +420,11 @@
left: auto;
right: -197px;
top: -9px;
- max-height: 245px;
- overflow-y: scroll;
+
+ ul {
+ max-height: 245px;
+ overflow: auto;
+ }
a {
color: $gl-text-color;
diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb
index ffd9833e3b1..869d96b86f4 100644
--- a/app/controllers/projects/merge_requests_controller.rb
+++ b/app/controllers/projects/merge_requests_controller.rb
@@ -10,7 +10,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
before_action :module_enabled
before_action :merge_request, only: [
:edit, :update, :show, :diffs, :commits, :conflicts, :builds, :pipelines, :merge, :merge_check,
- :ci_status, :toggle_subscription, :cancel_merge_when_build_succeeds, :remove_wip, :resolve_conflicts
+ :ci_status, :toggle_subscription, :cancel_merge_when_build_succeeds, :remove_wip, :resolve_conflicts, :assign_related_issues
]
before_action :validates_merge_request, only: [:show, :diffs, :commits, :builds, :pipelines]
before_action :define_show_vars, only: [:show, :diffs, :commits, :conflicts, :builds, :pipelines]
@@ -31,6 +31,8 @@ class Projects::MergeRequestsController < Projects::ApplicationController
# Allow modify merge_request
before_action :authorize_update_merge_request!, only: [:close, :edit, :update, :remove_wip, :sort]
+ before_action :authenticate_user!, only: [:assign_related_issues]
+
before_action :authorize_can_resolve_conflicts!, only: [:conflicts, :resolve_conflicts]
def index
@@ -354,6 +356,25 @@ class Projects::MergeRequestsController < Projects::ApplicationController
render layout: false
end
+ def assign_related_issues
+ result = MergeRequests::AssignIssuesService.new(project, current_user, merge_request: @merge_request).execute
+
+ respond_to do |format|
+ format.html do
+ case result[:count]
+ when 0
+ flash[:error] = "Failed to assign you issues related to the merge request"
+ when 1
+ flash[:notice] = "1 issue has been assigned to you"
+ else
+ flash[:notice] = "#{result[:count]} issues have been assigned to you"
+ end
+
+ redirect_to(merge_request_path(@merge_request))
+ end
+ end
+ end
+
def ci_status
pipeline = @merge_request.pipeline
if pipeline
diff --git a/app/controllers/projects/tags_controller.rb b/app/controllers/projects/tags_controller.rb
index 6ea8ee62bc5..8fea20cefef 100644
--- a/app/controllers/projects/tags_controller.rb
+++ b/app/controllers/projects/tags_controller.rb
@@ -20,6 +20,8 @@ class Projects::TagsController < Projects::ApplicationController
def show
@tag = @repository.find_tag(params[:id])
+ return render_404 unless @tag
+
@release = @project.releases.find_or_initialize_by(tag: @tag.name)
@commit = @repository.commit(@tag.target)
end
diff --git a/app/controllers/snippets_controller.rb b/app/controllers/snippets_controller.rb
index d198782138a..dee57e4a388 100644
--- a/app/controllers/snippets_controller.rb
+++ b/app/controllers/snippets_controller.rb
@@ -1,10 +1,10 @@
class SnippetsController < ApplicationController
include ToggleAwardEmoji
- before_action :snippet, only: [:show, :edit, :destroy, :update, :raw]
+ before_action :snippet, only: [:show, :edit, :destroy, :update, :raw, :download]
# Allow read snippet
- before_action :authorize_read_snippet!, only: [:show, :raw]
+ before_action :authorize_read_snippet!, only: [:show, :raw, :download]
# Allow modify snippet
before_action :authorize_update_snippet!, only: [:edit, :update]
@@ -12,7 +12,7 @@ class SnippetsController < ApplicationController
# Allow destroy snippet
before_action :authorize_admin_snippet!, only: [:destroy]
- skip_before_action :authenticate_user!, only: [:index, :show, :raw]
+ skip_before_action :authenticate_user!, only: [:index, :show, :raw, :download]
layout 'snippets'
respond_to :html
@@ -75,6 +75,14 @@ class SnippetsController < ApplicationController
)
end
+ def download
+ send_data(
+ @snippet.content,
+ type: 'text/plain; charset=utf-8',
+ filename: @snippet.sanitized_file_name
+ )
+ end
+
protected
def snippet
diff --git a/app/helpers/button_helper.rb b/app/helpers/button_helper.rb
index a695aceea76..85e1dc33ee8 100644
--- a/app/helpers/button_helper.rb
+++ b/app/helpers/button_helper.rb
@@ -15,14 +15,14 @@ module ButtonHelper
#
# See http://clipboardjs.com/#usage
def clipboard_button(data = {})
- css_class = data[:class] || 'btn-clipboard'
+ css_class = data[:class] || 'btn-clipboard btn-transparent'
data = { toggle: 'tooltip', placement: 'bottom', container: 'body' }.merge(data)
content_tag :button,
icon('clipboard'),
class: "btn #{css_class}",
data: data,
type: :button,
- title: "Copy to Clipboard"
+ title: 'Copy to Clipboard'
end
def http_clone_button(project, placement = 'right', append_link: true)
diff --git a/app/helpers/merge_requests_helper.rb b/app/helpers/merge_requests_helper.rb
index 8abe7865fed..249cb44e9d5 100644
--- a/app/helpers/merge_requests_helper.rb
+++ b/app/helpers/merge_requests_helper.rb
@@ -72,6 +72,19 @@ module MergeRequestsHelper
)
end
+ def mr_assign_issues_link
+ issues = MergeRequests::AssignIssuesService.new(@project,
+ current_user,
+ merge_request: @merge_request,
+ closes_issues: mr_closes_issues
+ ).assignable_issues
+ path = assign_related_issues_namespace_project_merge_request_path(@project.namespace, @project, @merge_request)
+ if issues.present?
+ pluralize_this_issue = issues.count > 1 ? "these issues" : "this issue"
+ link_to "Assign yourself to #{pluralize_this_issue}", path, method: :post
+ end
+ end
+
def source_branch_with_namespace(merge_request)
branch = link_to(merge_request.source_branch, namespace_project_commits_path(merge_request.source_project.namespace, merge_request.source_project, merge_request.source_branch))
@@ -110,4 +123,8 @@ module MergeRequestsHelper
def version_index(merge_request_diff)
@merge_request_diffs.size - @merge_request_diffs.index(merge_request_diff)
end
+
+ def different_base?(version1, version2)
+ version1 && version2 && version1.base_commit_sha != version2.base_commit_sha
+ end
end
diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb
index 2cf9892edc5..957f6755b2e 100644
--- a/app/models/ci/pipeline.rb
+++ b/app/models/ci/pipeline.rb
@@ -3,6 +3,7 @@ module Ci
extend Ci::Model
include HasStatus
include Importable
+ include AfterCommitQueue
self.table_name = 'ci_commits'
@@ -56,6 +57,10 @@ module Ci
pipeline.finished_at = Time.now
end
+ before_transition do |pipeline|
+ pipeline.update_duration
+ end
+
after_transition [:created, :pending] => :running do |pipeline|
MergeRequest::Metrics.where(merge_request_id: pipeline.merge_requests.map(&:id)).
update_all(latest_build_started_at: pipeline.started_at, latest_build_finished_at: nil)
@@ -66,8 +71,8 @@ module Ci
update_all(latest_build_finished_at: pipeline.finished_at)
end
- before_transition do |pipeline|
- pipeline.update_duration
+ after_transition [:created, :pending, :running] => :success do |pipeline|
+ pipeline.run_after_commit { PipelineSuccessWorker.perform_async(id) }
end
after_transition do |pipeline, transition|
@@ -292,11 +297,9 @@ module Ci
# Merge requests for which the current pipeline is running against
# the merge request's latest commit.
def merge_requests
- @merge_requests ||=
- begin
- project.merge_requests.where(source_branch: self.ref).
- select { |merge_request| merge_request.pipeline.try(:id) == self.id }
- end
+ @merge_requests ||= project.merge_requests
+ .where(source_branch: self.ref)
+ .select { |merge_request| merge_request.pipeline.try(:id) == self.id }
end
private
diff --git a/app/models/commit_status.rb b/app/models/commit_status.rb
index 5d6d534cd31..7b554be4f9a 100644
--- a/app/models/commit_status.rb
+++ b/app/models/commit_status.rb
@@ -86,29 +86,19 @@ class CommitStatus < ActiveRecord::Base
end
after_transition do |commit_status, transition|
- return if transition.loopback?
+ next if transition.loopback?
commit_status.run_after_commit do
pipeline.try do |pipeline|
if complete?
- ProcessPipelineWorker.perform_async(pipeline.id)
+ PipelineProcessWorker.perform_async(pipeline.id)
else
- UpdatePipelineWorker.perform_async(pipeline.id)
+ PipelineUpdateWorker.perform_async(pipeline.id)
end
end
end
end
- after_transition [:created, :pending, :running] => :success do |commit_status|
- commit_status.run_after_commit do
- # TODO, temporary fix for race condition
- UpdatePipelineWorker.new.perform(pipeline.id)
-
- MergeRequests::MergeWhenBuildSucceedsService
- .new(pipeline.project, nil).trigger(self)
- end
- end
-
after_transition any => :failed do |commit_status|
commit_status.run_after_commit do
MergeRequests::AddTodoWhenBuildFailsService
diff --git a/app/models/compare.rb b/app/models/compare.rb
index 4856510f526..3a8bbcb1acd 100644
--- a/app/models/compare.rb
+++ b/app/models/compare.rb
@@ -11,9 +11,10 @@ class Compare
end
end
- def initialize(compare, project)
+ def initialize(compare, project, straight: false)
@compare = compare
@project = project
+ @straight = straight
end
def commits
@@ -45,6 +46,18 @@ class Compare
end
end
+ def start_commit_sha
+ start_commit.try(:sha)
+ end
+
+ def base_commit_sha
+ base_commit.try(:sha)
+ end
+
+ def head_commit_sha
+ commit.try(:sha)
+ end
+
def raw_diffs(*args)
@compare.diffs(*args)
end
@@ -58,9 +71,9 @@ class Compare
def diff_refs
Gitlab::Diff::DiffRefs.new(
- base_sha: base_commit.try(:sha),
- start_sha: start_commit.try(:sha),
- head_sha: commit.try(:sha)
+ base_sha: @straight ? start_commit_sha : base_commit_sha,
+ start_sha: start_commit_sha,
+ head_sha: head_commit_sha
)
end
end
diff --git a/app/models/cycle_analytics.rb b/app/models/cycle_analytics.rb
index be295487fd2..8ed4a56b19b 100644
--- a/app/models/cycle_analytics.rb
+++ b/app/models/cycle_analytics.rb
@@ -2,6 +2,8 @@ class CycleAnalytics
include Gitlab::Database::Median
include Gitlab::Database::DateTime
+ DEPLOYMENT_METRIC_STAGES = %i[production staging]
+
def initialize(project, from:)
@project = project
@from = from
@@ -66,7 +68,7 @@ class CycleAnalytics
# cycle analytics stage.
interval_query = Arel::Nodes::As.new(
cte_table,
- subtract_datetimes(base_query, end_time_attrs, start_time_attrs, name.to_s))
+ subtract_datetimes(base_query_for(name), end_time_attrs, start_time_attrs, name.to_s))
median_datetime(cte_table, interval_query, name)
end
@@ -75,7 +77,7 @@ class CycleAnalytics
# closes the given issue) with issue and merge request metrics included. The metrics
# are loaded with an inner join, so issues / merge requests without metrics are
# automatically excluded.
- def base_query
+ def base_query_for(name)
arel_table = MergeRequestsClosingIssues.arel_table
# Load issues
@@ -91,7 +93,11 @@ class CycleAnalytics
join(MergeRequest::Metrics.arel_table).
on(MergeRequest.arel_table[:id].eq(MergeRequest::Metrics.arel_table[:merge_request_id]))
- # Limit to merge requests that have been deployed to production after `@from`
- query.where(MergeRequest::Metrics.arel_table[:first_deployed_to_production_at].gteq(@from))
+ if DEPLOYMENT_METRIC_STAGES.include?(name)
+ # Limit to merge requests that have been deployed to production after `@from`
+ query.where(MergeRequest::Metrics.arel_table[:first_deployed_to_production_at].gteq(@from))
+ end
+
+ query
end
end
diff --git a/app/models/event.rb b/app/models/event.rb
index 314d5ba438f..0764cb8cabd 100644
--- a/app/models/event.rb
+++ b/app/models/event.rb
@@ -68,8 +68,10 @@ class Event < ActiveRecord::Base
true
elsif issue? || issue_note?
Ability.allowed?(user, :read_issue, note? ? note_target : target)
+ elsif merge_request? || merge_request_note?
+ Ability.allowed?(user, :read_merge_request, note? ? note_target : target)
else
- ((merge_request? || note?) && target.present?) || milestone?
+ milestone?
end
end
@@ -280,6 +282,10 @@ class Event < ActiveRecord::Base
note? && target && target.for_issue?
end
+ def merge_request_note?
+ note? && target && target.for_merge_request?
+ end
+
def project_snippet_note?
target.for_snippet?
end
diff --git a/app/models/merge_request_diff.rb b/app/models/merge_request_diff.rb
index 3f7e96186a1..b8a10b7968e 100644
--- a/app/models/merge_request_diff.rb
+++ b/app/models/merge_request_diff.rb
@@ -167,8 +167,11 @@ class MergeRequestDiff < ActiveRecord::Base
self == merge_request.merge_request_diff
end
- def compare_with(sha)
- CompareService.new.execute(project, head_commit_sha, project, sha)
+ def compare_with(sha, straight: true)
+ # When compare merge request versions we want diff A..B instead of A...B
+ # so we handle cases when user does squash and rebase of the commits between versions.
+ # For this reason we set straight to true by default.
+ CompareService.new.execute(project, head_commit_sha, project, sha, straight: straight)
end
private
diff --git a/app/models/project_group_link.rb b/app/models/project_group_link.rb
index 7613cbdea93..db46def11eb 100644
--- a/app/models/project_group_link.rb
+++ b/app/models/project_group_link.rb
@@ -10,7 +10,7 @@ class ProjectGroupLink < ActiveRecord::Base
belongs_to :group
validates :project_id, presence: true
- validates :group_id, presence: true
+ validates :group, presence: true
validates :group_id, uniqueness: { scope: [:project_id], message: "already shared with this group" }
validates :group_access, presence: true
validates :group_access, inclusion: { in: Gitlab::Access.values }, presence: true
diff --git a/app/models/repository.rb b/app/models/repository.rb
index 4da1933c189..608c99eed46 100644
--- a/app/models/repository.rb
+++ b/app/models/repository.rb
@@ -1016,7 +1016,8 @@ class Repository
root_ref_commit = commit(root_ref)
if branch_commit
- is_ancestor?(branch_commit.id, root_ref_commit.id)
+ same_head = branch_commit.id == root_ref_commit.id
+ !same_head && is_ancestor?(branch_commit.id, root_ref_commit.id)
else
nil
end
diff --git a/app/policies/project_policy.rb b/app/policies/project_policy.rb
index a806cf83782..be4721d7a51 100644
--- a/app/policies/project_policy.rb
+++ b/app/policies/project_policy.rb
@@ -40,7 +40,6 @@ class ProjectPolicy < BasePolicy
can! :read_milestone
can! :read_project_snippet
can! :read_project_member
- can! :read_merge_request
can! :read_note
can! :create_project
can! :create_issue
@@ -63,6 +62,7 @@ class ProjectPolicy < BasePolicy
can! :read_pipeline
can! :read_environment
can! :read_deployment
+ can! :read_merge_request
end
# Permissions given when an user is team member of a project
@@ -117,6 +117,7 @@ class ProjectPolicy < BasePolicy
can! :read_container_image
can! :build_download_code
can! :build_read_container_image
+ can! :read_merge_request
end
def owner_access!
diff --git a/app/services/compare_service.rb b/app/services/compare_service.rb
index 6d6075628af..5e8fafca98c 100644
--- a/app/services/compare_service.rb
+++ b/app/services/compare_service.rb
@@ -3,7 +3,7 @@ require 'securerandom'
# Compare 2 branches for one repo or between repositories
# and return Gitlab::Git::Compare object that responds to commits and diffs
class CompareService
- def execute(source_project, source_branch, target_project, target_branch)
+ def execute(source_project, source_branch, target_project, target_branch, straight: false)
source_commit = source_project.commit(source_branch)
return unless source_commit
@@ -23,9 +23,10 @@ class CompareService
raw_compare = Gitlab::Git::Compare.new(
target_project.repository.raw_repository,
target_branch,
- source_sha
+ source_sha,
+ straight
)
- Compare.new(raw_compare, target_project)
+ Compare.new(raw_compare, target_project, straight: straight)
end
end
diff --git a/app/services/merge_requests/add_todo_when_build_fails_service.rb b/app/services/merge_requests/add_todo_when_build_fails_service.rb
index 566049525cb..d572a928a42 100644
--- a/app/services/merge_requests/add_todo_when_build_fails_service.rb
+++ b/app/services/merge_requests/add_todo_when_build_fails_service.rb
@@ -2,14 +2,14 @@ module MergeRequests
class AddTodoWhenBuildFailsService < MergeRequests::BaseService
# Adds a todo to the parent merge_request when a CI build fails
def execute(commit_status)
- each_merge_request(commit_status) do |merge_request|
+ commit_status_merge_requests(commit_status) do |merge_request|
todo_service.merge_request_build_failed(merge_request)
end
end
# Closes any pending build failed todos for the parent MRs when a build is retried
def close(commit_status)
- each_merge_request(commit_status) do |merge_request|
+ commit_status_merge_requests(commit_status) do |merge_request|
todo_service.merge_request_build_retried(merge_request)
end
end
diff --git a/app/services/merge_requests/assign_issues_service.rb b/app/services/merge_requests/assign_issues_service.rb
new file mode 100644
index 00000000000..f636e5fec4f
--- /dev/null
+++ b/app/services/merge_requests/assign_issues_service.rb
@@ -0,0 +1,35 @@
+module MergeRequests
+ class AssignIssuesService < BaseService
+ def assignable_issues
+ @assignable_issues ||= begin
+ if current_user == merge_request.author
+ closes_issues.select do |issue|
+ !issue.assignee_id? && can?(current_user, :admin_issue, issue)
+ end
+ else
+ []
+ end
+ end
+ end
+
+ def execute
+ assignable_issues.each do |issue|
+ Issues::UpdateService.new(issue.project, current_user, assignee_id: current_user.id).execute(issue)
+ end
+
+ {
+ count: assignable_issues.count
+ }
+ end
+
+ private
+
+ def merge_request
+ params[:merge_request]
+ end
+
+ def closes_issues
+ @closes_issues ||= params[:closes_issues] || merge_request.closes_issues(current_user)
+ end
+ end
+end
diff --git a/app/services/merge_requests/base_service.rb b/app/services/merge_requests/base_service.rb
index d0d155b7ee1..58f69a41e14 100644
--- a/app/services/merge_requests/base_service.rb
+++ b/app/services/merge_requests/base_service.rb
@@ -42,28 +42,33 @@ module MergeRequests
super(:merge_request)
end
- def merge_request_from(commit_status)
- branches = commit_status.ref
+ def merge_requests_for(branch)
+ origin_merge_requests = @project.origin_merge_requests
+ .opened.where(source_branch: branch).to_a
- # This is for ref-less builds
- branches ||= @project.repository.branch_names_contains(commit_status.sha)
+ fork_merge_requests = @project.fork_merge_requests
+ .opened.where(source_branch: branch).to_a
- return [] if branches.blank?
+ (origin_merge_requests + fork_merge_requests)
+ .uniq.select(&:source_project)
+ end
- merge_requests = @project.origin_merge_requests.opened.where(source_branch: branches).to_a
- merge_requests += @project.fork_merge_requests.opened.where(source_branch: branches).to_a
+ def pipeline_merge_requests(pipeline)
+ merge_requests_for(pipeline.ref).each do |merge_request|
+ next unless pipeline == merge_request.pipeline
- merge_requests.uniq.select(&:source_project)
+ yield merge_request
+ end
end
- def each_merge_request(commit_status)
- merge_request_from(commit_status).each do |merge_request|
+ def commit_status_merge_requests(commit_status)
+ merge_requests_for(commit_status.ref).each do |merge_request|
pipeline = merge_request.pipeline
next unless pipeline
next unless pipeline.sha == commit_status.sha
- yield merge_request, pipeline
+ yield merge_request
end
end
end
diff --git a/app/services/merge_requests/merge_when_build_succeeds_service.rb b/app/services/merge_requests/merge_when_build_succeeds_service.rb
index 4ad5fb08311..dc159de0058 100644
--- a/app/services/merge_requests/merge_when_build_succeeds_service.rb
+++ b/app/services/merge_requests/merge_when_build_succeeds_service.rb
@@ -18,12 +18,13 @@ module MergeRequests
merge_request.save
end
- # Triggers the automatic merge of merge_request once the build succeeds
- def trigger(commit_status)
- each_merge_request(commit_status) do |merge_request, pipeline|
+ # Triggers the automatic merge of merge_request once the pipeline succeeds
+ def trigger(pipeline)
+ return unless pipeline.success?
+
+ pipeline_merge_requests(pipeline) do |merge_request|
next unless merge_request.merge_when_build_succeeds?
next unless merge_request.mergeable?
- next unless pipeline.success?
MergeWorker.perform_async(merge_request.id, merge_request.merge_user_id, merge_request.merge_params)
end
diff --git a/app/services/merge_requests/update_service.rb b/app/services/merge_requests/update_service.rb
index 9dbec49d163..a37cc3fdf21 100644
--- a/app/services/merge_requests/update_service.rb
+++ b/app/services/merge_requests/update_service.rb
@@ -15,7 +15,10 @@ module MergeRequests
params.except!(:target_branch, :force_remove_source_branch)
end
- merge_request.merge_params['force_remove_source_branch'] = params.delete(:force_remove_source_branch)
+ if params[:force_remove_source_branch].present?
+ merge_request.merge_params['force_remove_source_branch'] = params.delete(:force_remove_source_branch)
+ end
+
handle_wip_event(merge_request)
update(merge_request)
end
diff --git a/app/services/notification_service.rb b/app/services/notification_service.rb
index de8049b8e2e..72712afc07e 100644
--- a/app/services/notification_service.rb
+++ b/app/services/notification_service.rb
@@ -475,10 +475,12 @@ class NotificationService
end
def reject_users_without_access(recipients, target)
- return recipients unless target.is_a?(Issue)
+ return recipients unless target.is_a?(Issuable)
+
+ ability = :"read_#{target.to_ability_name}"
recipients.select do |user|
- user.can?(:read_issue, target)
+ user.can?(ability, target)
end
end
diff --git a/app/services/slash_commands/interpret_service.rb b/app/services/slash_commands/interpret_service.rb
index 1725a30fae5..e4ae3dec8aa 100644
--- a/app/services/slash_commands/interpret_service.rb
+++ b/app/services/slash_commands/interpret_service.rb
@@ -122,7 +122,12 @@ module SlashCommands
command :label do |labels_param|
label_ids = find_label_ids(labels_param)
- @updates[:add_label_ids] = label_ids unless label_ids.empty?
+ if label_ids.any?
+ @updates[:add_label_ids] ||= []
+ @updates[:add_label_ids] += label_ids
+
+ @updates[:add_label_ids].uniq!
+ end
end
desc 'Remove all or specific label(s)'
@@ -136,7 +141,12 @@ module SlashCommands
if labels_param.present?
label_ids = find_label_ids(labels_param)
- @updates[:remove_label_ids] = label_ids unless label_ids.empty?
+ if label_ids.any?
+ @updates[:remove_label_ids] ||= []
+ @updates[:remove_label_ids] += label_ids
+
+ @updates[:remove_label_ids].uniq!
+ end
else
@updates[:label_ids] = []
end
@@ -152,7 +162,12 @@ module SlashCommands
command :relabel do |labels_param|
label_ids = find_label_ids(labels_param)
- @updates[:label_ids] = label_ids unless label_ids.empty?
+ if label_ids.any?
+ @updates[:label_ids] ||= []
+ @updates[:label_ids] += label_ids
+
+ @updates[:label_ids].uniq!
+ end
end
desc 'Add a todo'
diff --git a/app/services/todo_service.rb b/app/services/todo_service.rb
index 776530ac0a5..f8e6b2ef094 100644
--- a/app/services/todo_service.rb
+++ b/app/services/todo_service.rb
@@ -273,12 +273,12 @@ class TodoService
end
def reject_users_without_access(users, project, target)
- if target.is_a?(Note) && target.for_issue?
+ if target.is_a?(Note) && (target.for_issue? || target.for_merge_request?)
target = target.noteable
end
- if target.is_a?(Issue)
- select_users(users, :read_issue, target)
+ if target.is_a?(Issuable)
+ select_users(users, :"read_#{target.to_ability_name}", target)
else
select_users(users, :read_project, project)
end
diff --git a/app/views/admin/application_settings/_form.html.haml b/app/views/admin/application_settings/_form.html.haml
index 0d79ca7dc52..c4c68cd7891 100644
--- a/app/views/admin/application_settings/_form.html.haml
+++ b/app/views/admin/application_settings/_form.html.haml
@@ -221,7 +221,11 @@
%fieldset
%legend Metrics
%p
- These settings require a restart to take effect.
+ Setup InfluxDB to measure a wide variety of statistics like the time spent
+ in running SQL queries. These settings require a
+ = link_to 'restart', help_page_path('administration/restart_gitlab')
+ to take effect.
+ = link_to icon('question-circle'), help_page_path('administration/monitoring/performance/introduction')
.form-group
.col-sm-offset-2.col-sm-10
.checkbox
diff --git a/app/views/projects/branches/_branch.html.haml b/app/views/projects/branches/_branch.html.haml
index 5217b8bf028..4480b2f22c3 100644
--- a/app/views/projects/branches/_branch.html.haml
+++ b/app/views/projects/branches/_branch.html.haml
@@ -30,8 +30,8 @@
= render 'projects/buttons/download', project: @project, ref: branch.name
- - if can_remove_branch?(@project, branch.name)
- = link_to namespace_project_branch_path(@project.namespace, @project, branch.name), class: 'btn btn-remove remove-row has-tooltip', title: "Delete branch", method: :delete, data: { confirm: "Deleting the '#{branch.name}' branch cannot be undone. Are you sure?", container: 'body' }, remote: true do
+ - if can?(current_user, :push_code, @project)
+ = link_to namespace_project_branch_path(@project.namespace, @project, branch.name), class: "btn btn-remove remove-row has-tooltip #{can_remove_branch?(@project, branch.name) ? '' : 'disabled'}", title: "Delete branch", method: :delete, data: { confirm: "Deleting the '#{branch.name}' branch cannot be undone. Are you sure?", container: 'body' }, remote: true do
= icon("trash-o")
- if branch.name != @repository.root_ref
diff --git a/app/views/projects/commit/_pipeline_status_group.html.haml b/app/views/projects/commit/_pipeline_status_group.html.haml
index 6ada719e006..5d0d5ba0262 100644
--- a/app/views/projects/commit/_pipeline_status_group.html.haml
+++ b/app/views/projects/commit/_pipeline_status_group.html.haml
@@ -5,8 +5,9 @@
%span.ci-status-text
= name
%span.badge= subject.size
-%ul.dropdown-menu.grouped-pipeline-dropdown
- %li.arrow
- - subject.each do |status|
- %li
- = render "projects/#{status.to_partial_path}_pipeline", subject: status
+.dropdown-menu.grouped-pipeline-dropdown
+ .arrow
+ %ul
+ - subject.each do |status|
+ %li
+ = render "projects/#{status.to_partial_path}_pipeline", subject: status
diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml
index d19422c8657..c8f84b96cb7 100644
--- a/app/views/projects/edit.html.haml
+++ b/app/views/projects/edit.html.haml
@@ -91,7 +91,7 @@
Git Large File Storage
= link_to icon('question-circle'), help_page_path('workflow/lfs/manage_large_binaries_with_git_lfs')
.col-md-3
- = f.select :lfs_enabled, [%w(Enabled true), %w(Disabled false)], {}, selected: @project.lfs_enabled?, class: 'pull-right form-control'
+ = f.select :lfs_enabled, [%w(Enabled true), %w(Disabled false)], {}, selected: @project.lfs_enabled?, class: 'pull-right form-control', data: { field: 'lfs_enabled' }
- if Gitlab.config.registry.enabled
.form-group
diff --git a/app/views/projects/merge_requests/show/_versions.html.haml b/app/views/projects/merge_requests/show/_versions.html.haml
index 988ac0feae1..eab48b78cb3 100644
--- a/app/views/projects/merge_requests/show/_versions.html.haml
+++ b/app/views/projects/merge_requests/show/_versions.html.haml
@@ -64,6 +64,16 @@
#{@merge_request.target_branch} (base)
.monospace #{short_sha(@merge_request_diff.base_commit_sha)}
+ - if different_base?(@start_version, @merge_request_diff)
+ .content-block
+ = icon('info-circle')
+ Selected versions have different base commits.
+ Changes will include
+ = link_to namespace_project_compare_path(@project.namespace, @project, from: @start_version.base_commit_sha, to: @merge_request_diff.base_commit_sha) do
+ new commits
+ from
+ %code #{@merge_request.target_branch}
+
- unless @merge_request_diff.latest? && !@start_sha
.comments-disabled-notif.content-block
= icon('info-circle')
diff --git a/app/views/projects/merge_requests/widget/_open.html.haml b/app/views/projects/merge_requests/widget/_open.html.haml
index 6f5ee5f16c5..842b6df310d 100644
--- a/app/views/projects/merge_requests/widget/_open.html.haml
+++ b/app/views/projects/merge_requests/widget/_open.html.haml
@@ -35,3 +35,4 @@
Accepting this merge request will close #{"issue".pluralize(mr_closes_issues.size)}
= succeed '.' do
!= markdown issues_sentence(mr_closes_issues), pipeline: :gfm, author: @merge_request.author
+ = mr_assign_issues_link
diff --git a/app/views/snippets/show.html.haml b/app/views/snippets/show.html.haml
index cd89155c616..27d7a6c5bb6 100644
--- a/app/views/snippets/show.html.haml
+++ b/app/views/snippets/show.html.haml
@@ -9,6 +9,7 @@
.file-actions
= clipboard_button(clipboard_target: ".blob-content[data-blob-id='#{@snippet.id}']")
= link_to 'Raw', raw_snippet_path(@snippet), class: "btn btn-sm", target: "_blank"
+ = link_to 'Download', download_snippet_path(@snippet), class: "btn btn-sm"
= render 'shared/snippets/blob'
= render 'award_emoji/awards_block', awardable: @snippet, inline: true \ No newline at end of file
diff --git a/app/workers/process_pipeline_worker.rb b/app/workers/pipeline_process_worker.rb
index 26ea5f1c24d..f44227d7086 100644
--- a/app/workers/process_pipeline_worker.rb
+++ b/app/workers/pipeline_process_worker.rb
@@ -1,4 +1,4 @@
-class ProcessPipelineWorker
+class PipelineProcessWorker
include Sidekiq::Worker
sidekiq_options queue: :default
diff --git a/app/workers/pipeline_success_worker.rb b/app/workers/pipeline_success_worker.rb
new file mode 100644
index 00000000000..5dd443fea59
--- /dev/null
+++ b/app/workers/pipeline_success_worker.rb
@@ -0,0 +1,12 @@
+class PipelineSuccessWorker
+ include Sidekiq::Worker
+ sidekiq_options queue: :default
+
+ def perform(pipeline_id)
+ Ci::Pipeline.find_by(id: pipeline_id).try do |pipeline|
+ MergeRequests::MergeWhenBuildSucceedsService
+ .new(pipeline.project, nil)
+ .trigger(pipeline)
+ end
+ end
+end
diff --git a/app/workers/update_pipeline_worker.rb b/app/workers/pipeline_update_worker.rb
index 6ef5678073e..44a7f24e401 100644
--- a/app/workers/update_pipeline_worker.rb
+++ b/app/workers/pipeline_update_worker.rb
@@ -1,4 +1,4 @@
-class UpdatePipelineWorker
+class PipelineUpdateWorker
include Sidekiq::Worker
sidekiq_options queue: :default
diff --git a/config/routes/project.rb b/config/routes/project.rb
index 3a7c4f86301..f9d58f5d5b2 100644
--- a/config/routes/project.rb
+++ b/config/routes/project.rb
@@ -159,7 +159,7 @@ resources :namespaces, path: '/', constraints: { id: /[a-zA-Z.0-9_\-]+/ }, only:
get(
'/commits/*id',
to: 'commits#show',
- constraints: { id: /(?:[^.]|\.(?!atom$))+/, format: /atom/ },
+ constraints: { id: /.+/, format: false },
as: :commits
)
end
@@ -277,6 +277,7 @@ resources :namespaces, path: '/', constraints: { id: /[a-zA-Z.0-9_\-]+/ }, only:
post :remove_wip
get :diff_for_path
post :resolve_conflicts
+ post :assign_related_issues
end
collection do
diff --git a/config/routes/snippets.rb b/config/routes/snippets.rb
index 1949f215c66..3ca096f31ba 100644
--- a/config/routes/snippets.rb
+++ b/config/routes/snippets.rb
@@ -1,6 +1,7 @@
resources :snippets, concerns: :awardable do
member do
get 'raw'
+ get 'download'
end
end
diff --git a/db/schema.rb b/db/schema.rb
index c5ddf9eee32..a362fd8f228 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -18,94 +18,94 @@ ActiveRecord::Schema.define(version: 20161007133303) do
enable_extension "pg_trgm"
create_table "abuse_reports", force: :cascade do |t|
- t.integer "reporter_id"
- t.integer "user_id"
- t.text "message"
+ t.integer "reporter_id"
+ t.integer "user_id"
+ t.text "message"
t.datetime "created_at"
t.datetime "updated_at"
- t.text "message_html"
+ t.text "message_html"
end
create_table "appearances", force: :cascade do |t|
- t.string "title"
- t.text "description"
- t.string "header_logo"
- t.string "logo"
- t.datetime "created_at", null: false
- t.datetime "updated_at", null: false
- t.text "description_html"
+ t.string "title"
+ t.text "description"
+ t.string "header_logo"
+ t.string "logo"
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
+ t.text "description_html"
end
create_table "application_settings", force: :cascade do |t|
- t.integer "default_projects_limit"
- t.boolean "signup_enabled"
- t.boolean "signin_enabled"
- t.boolean "gravatar_enabled"
- t.text "sign_in_text"
+ t.integer "default_projects_limit"
+ t.boolean "signup_enabled"
+ t.boolean "signin_enabled"
+ t.boolean "gravatar_enabled"
+ t.text "sign_in_text"
t.datetime "created_at"
t.datetime "updated_at"
- t.string "home_page_url"
- t.integer "default_branch_protection", default: 2
- t.text "restricted_visibility_levels"
- t.boolean "version_check_enabled", default: true
- t.integer "max_attachment_size", default: 10, null: false
- t.integer "default_project_visibility"
- t.integer "default_snippet_visibility"
- t.text "domain_whitelist"
- t.boolean "user_oauth_applications", default: true
- t.string "after_sign_out_path"
- t.integer "session_expire_delay", default: 10080, null: false
- t.text "import_sources"
- t.text "help_page_text"
- t.string "admin_notification_email"
- t.boolean "shared_runners_enabled", default: true, null: false
- t.integer "max_artifacts_size", default: 100, null: false
- t.string "runners_registration_token"
- t.boolean "require_two_factor_authentication", default: false
- t.integer "two_factor_grace_period", default: 48
- t.boolean "metrics_enabled", default: false
- t.string "metrics_host", default: "localhost"
- t.integer "metrics_pool_size", default: 16
- t.integer "metrics_timeout", default: 10
- t.integer "metrics_method_call_threshold", default: 10
- t.boolean "recaptcha_enabled", default: false
- t.string "recaptcha_site_key"
- t.string "recaptcha_private_key"
- t.integer "metrics_port", default: 8089
- t.boolean "akismet_enabled", default: false
- t.string "akismet_api_key"
- t.integer "metrics_sample_interval", default: 15
- t.boolean "sentry_enabled", default: false
- t.string "sentry_dsn"
- t.boolean "email_author_in_body", default: false
- t.integer "default_group_visibility"
- t.boolean "repository_checks_enabled", default: false
- t.text "shared_runners_text"
- t.integer "metrics_packet_size", default: 1
- t.text "disabled_oauth_sign_in_sources"
- t.string "health_check_access_token"
- t.boolean "send_user_confirmation_email", default: false
- t.integer "container_registry_token_expire_delay", default: 5
- t.text "after_sign_up_text"
- t.boolean "user_default_external", default: false, null: false
- t.string "repository_storage", default: "default"
- t.string "enabled_git_access_protocol"
- t.boolean "domain_blacklist_enabled", default: false
- t.text "domain_blacklist"
- t.boolean "koding_enabled"
- t.string "koding_url"
- t.text "sign_in_text_html"
- t.text "help_page_text_html"
- t.text "shared_runners_text_html"
- t.text "after_sign_up_text_html"
+ t.string "home_page_url"
+ t.integer "default_branch_protection", default: 2
+ t.text "restricted_visibility_levels"
+ t.boolean "version_check_enabled", default: true
+ t.integer "max_attachment_size", default: 10, null: false
+ t.integer "default_project_visibility"
+ t.integer "default_snippet_visibility"
+ t.text "domain_whitelist"
+ t.boolean "user_oauth_applications", default: true
+ t.string "after_sign_out_path"
+ t.integer "session_expire_delay", default: 10080, null: false
+ t.text "import_sources"
+ t.text "help_page_text"
+ t.string "admin_notification_email"
+ t.boolean "shared_runners_enabled", default: true, null: false
+ t.integer "max_artifacts_size", default: 100, null: false
+ t.string "runners_registration_token"
+ t.boolean "require_two_factor_authentication", default: false
+ t.integer "two_factor_grace_period", default: 48
+ t.boolean "metrics_enabled", default: false
+ t.string "metrics_host", default: "localhost"
+ t.integer "metrics_pool_size", default: 16
+ t.integer "metrics_timeout", default: 10
+ t.integer "metrics_method_call_threshold", default: 10
+ t.boolean "recaptcha_enabled", default: false
+ t.string "recaptcha_site_key"
+ t.string "recaptcha_private_key"
+ t.integer "metrics_port", default: 8089
+ t.boolean "akismet_enabled", default: false
+ t.string "akismet_api_key"
+ t.integer "metrics_sample_interval", default: 15
+ t.boolean "sentry_enabled", default: false
+ t.string "sentry_dsn"
+ t.boolean "email_author_in_body", default: false
+ t.integer "default_group_visibility"
+ t.boolean "repository_checks_enabled", default: false
+ t.text "shared_runners_text"
+ t.integer "metrics_packet_size", default: 1
+ t.text "disabled_oauth_sign_in_sources"
+ t.string "health_check_access_token"
+ t.boolean "send_user_confirmation_email", default: false
+ t.integer "container_registry_token_expire_delay", default: 5
+ t.text "after_sign_up_text"
+ t.boolean "user_default_external", default: false, null: false
+ t.string "repository_storage", default: "default"
+ t.string "enabled_git_access_protocol"
+ t.boolean "domain_blacklist_enabled", default: false
+ t.text "domain_blacklist"
+ t.boolean "koding_enabled"
+ t.string "koding_url"
+ t.text "sign_in_text_html"
+ t.text "help_page_text_html"
+ t.text "shared_runners_text_html"
+ t.text "after_sign_up_text_html"
end
create_table "audit_events", force: :cascade do |t|
- t.integer "author_id", null: false
- t.string "type", null: false
- t.integer "entity_id", null: false
- t.string "entity_type", null: false
- t.text "details"
+ t.integer "author_id", null: false
+ t.string "type", null: false
+ t.integer "entity_id", null: false
+ t.string "entity_type", null: false
+ t.text "details"
t.datetime "created_at"
t.datetime "updated_at"
end
@@ -113,10 +113,10 @@ ActiveRecord::Schema.define(version: 20161007133303) do
add_index "audit_events", ["entity_id", "entity_type"], name: "index_audit_events_on_entity_id_and_entity_type", using: :btree
create_table "award_emoji", force: :cascade do |t|
- t.string "name"
- t.integer "user_id"
- t.integer "awardable_id"
- t.string "awardable_type"
+ t.string "name"
+ t.integer "user_id"
+ t.integer "awardable_id"
+ t.string "awardable_type"
t.datetime "created_at"
t.datetime "updated_at"
end
@@ -126,7 +126,7 @@ ActiveRecord::Schema.define(version: 20161007133303) do
add_index "award_emoji", ["user_id"], name: "index_award_emoji_on_user_id", using: :btree
create_table "boards", force: :cascade do |t|
- t.integer "project_id", null: false
+ t.integer "project_id", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
@@ -134,61 +134,61 @@ ActiveRecord::Schema.define(version: 20161007133303) do
add_index "boards", ["project_id"], name: "index_boards_on_project_id", using: :btree
create_table "broadcast_messages", force: :cascade do |t|
- t.text "message", null: false
+ t.text "message", null: false
t.datetime "starts_at"
t.datetime "ends_at"
t.datetime "created_at"
t.datetime "updated_at"
- t.string "color"
- t.string "font"
- t.text "message_html"
+ t.string "color"
+ t.string "font"
+ t.text "message_html"
end
create_table "ci_application_settings", force: :cascade do |t|
- t.boolean "all_broken_builds"
- t.boolean "add_pusher"
+ t.boolean "all_broken_builds"
+ t.boolean "add_pusher"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "ci_builds", force: :cascade do |t|
- t.integer "project_id"
- t.string "status"
+ t.integer "project_id"
+ t.string "status"
t.datetime "finished_at"
- t.text "trace"
+ t.text "trace"
t.datetime "created_at"
t.datetime "updated_at"
t.datetime "started_at"
- t.integer "runner_id"
- t.float "coverage"
- t.integer "commit_id"
- t.text "commands"
- t.integer "job_id"
- t.string "name"
- t.boolean "deploy", default: false
- t.text "options"
- t.boolean "allow_failure", default: false, null: false
- t.string "stage"
- t.integer "trigger_request_id"
- t.integer "stage_idx"
- t.boolean "tag"
- t.string "ref"
- t.integer "user_id"
- t.string "type"
- t.string "target_url"
- t.string "description"
- t.text "artifacts_file"
- t.integer "gl_project_id"
- t.text "artifacts_metadata"
- t.integer "erased_by_id"
+ t.integer "runner_id"
+ t.float "coverage"
+ t.integer "commit_id"
+ t.text "commands"
+ t.integer "job_id"
+ t.string "name"
+ t.boolean "deploy", default: false
+ t.text "options"
+ t.boolean "allow_failure", default: false, null: false
+ t.string "stage"
+ t.integer "trigger_request_id"
+ t.integer "stage_idx"
+ t.boolean "tag"
+ t.string "ref"
+ t.integer "user_id"
+ t.string "type"
+ t.string "target_url"
+ t.string "description"
+ t.text "artifacts_file"
+ t.integer "gl_project_id"
+ t.text "artifacts_metadata"
+ t.integer "erased_by_id"
t.datetime "erased_at"
t.datetime "artifacts_expire_at"
- t.string "environment"
- t.integer "artifacts_size", limit: 8
- t.string "when"
- t.text "yaml_variables"
+ t.string "environment"
+ t.integer "artifacts_size", limit: 8
+ t.string "when"
+ t.text "yaml_variables"
t.datetime "queued_at"
- t.string "token"
+ t.string "token"
end
add_index "ci_builds", ["commit_id", "stage_idx", "created_at"], name: "index_ci_builds_on_commit_id_and_stage_idx_and_created_at", using: :btree
@@ -203,22 +203,22 @@ ActiveRecord::Schema.define(version: 20161007133303) do
add_index "ci_builds", ["token"], name: "index_ci_builds_on_token", unique: true, using: :btree
create_table "ci_commits", force: :cascade do |t|
- t.integer "project_id"
- t.string "ref"
- t.string "sha"
- t.string "before_sha"
- t.text "push_data"
+ t.integer "project_id"
+ t.string "ref"
+ t.string "sha"
+ t.string "before_sha"
+ t.text "push_data"
t.datetime "created_at"
t.datetime "updated_at"
- t.boolean "tag", default: false
- t.text "yaml_errors"
+ t.boolean "tag", default: false
+ t.text "yaml_errors"
t.datetime "committed_at"
- t.integer "gl_project_id"
- t.string "status"
+ t.integer "gl_project_id"
+ t.string "status"
t.datetime "started_at"
t.datetime "finished_at"
- t.integer "duration"
- t.integer "user_id"
+ t.integer "duration"
+ t.integer "user_id"
end
add_index "ci_commits", ["gl_project_id", "sha"], name: "index_ci_commits_on_gl_project_id_and_sha", using: :btree
@@ -228,140 +228,140 @@ ActiveRecord::Schema.define(version: 20161007133303) do
add_index "ci_commits", ["user_id"], name: "index_ci_commits_on_user_id", using: :btree
create_table "ci_events", force: :cascade do |t|
- t.integer "project_id"
- t.integer "user_id"
- t.integer "is_admin"
- t.text "description"
+ t.integer "project_id"
+ t.integer "user_id"
+ t.integer "is_admin"
+ t.text "description"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "ci_jobs", force: :cascade do |t|
- t.integer "project_id", null: false
- t.text "commands"
- t.boolean "active", default: true, null: false
+ t.integer "project_id", null: false
+ t.text "commands"
+ t.boolean "active", default: true, null: false
t.datetime "created_at"
t.datetime "updated_at"
- t.string "name"
- t.boolean "build_branches", default: true, null: false
- t.boolean "build_tags", default: false, null: false
- t.string "job_type", default: "parallel"
- t.string "refs"
+ t.string "name"
+ t.boolean "build_branches", default: true, null: false
+ t.boolean "build_tags", default: false, null: false
+ t.string "job_type", default: "parallel"
+ t.string "refs"
t.datetime "deleted_at"
end
create_table "ci_projects", force: :cascade do |t|
- t.string "name"
- t.integer "timeout", default: 3600, null: false
+ t.string "name"
+ t.integer "timeout", default: 3600, null: false
t.datetime "created_at"
t.datetime "updated_at"
- t.string "token"
- t.string "default_ref"
- t.string "path"
- t.boolean "always_build", default: false, null: false
- t.integer "polling_interval"
- t.boolean "public", default: false, null: false
- t.string "ssh_url_to_repo"
- t.integer "gitlab_id"
- t.boolean "allow_git_fetch", default: true, null: false
- t.string "email_recipients", default: "", null: false
- t.boolean "email_add_pusher", default: true, null: false
- t.boolean "email_only_broken_builds", default: true, null: false
- t.string "skip_refs"
- t.string "coverage_regex"
- t.boolean "shared_runners_enabled", default: false
- t.text "generated_yaml_config"
+ t.string "token"
+ t.string "default_ref"
+ t.string "path"
+ t.boolean "always_build", default: false, null: false
+ t.integer "polling_interval"
+ t.boolean "public", default: false, null: false
+ t.string "ssh_url_to_repo"
+ t.integer "gitlab_id"
+ t.boolean "allow_git_fetch", default: true, null: false
+ t.string "email_recipients", default: "", null: false
+ t.boolean "email_add_pusher", default: true, null: false
+ t.boolean "email_only_broken_builds", default: true, null: false
+ t.string "skip_refs"
+ t.string "coverage_regex"
+ t.boolean "shared_runners_enabled", default: false
+ t.text "generated_yaml_config"
end
create_table "ci_runner_projects", force: :cascade do |t|
- t.integer "runner_id", null: false
- t.integer "project_id"
+ t.integer "runner_id", null: false
+ t.integer "project_id"
t.datetime "created_at"
t.datetime "updated_at"
- t.integer "gl_project_id"
+ t.integer "gl_project_id"
end
add_index "ci_runner_projects", ["gl_project_id"], name: "index_ci_runner_projects_on_gl_project_id", using: :btree
add_index "ci_runner_projects", ["runner_id"], name: "index_ci_runner_projects_on_runner_id", using: :btree
create_table "ci_runners", force: :cascade do |t|
- t.string "token"
+ t.string "token"
t.datetime "created_at"
t.datetime "updated_at"
- t.string "description"
+ t.string "description"
t.datetime "contacted_at"
- t.boolean "active", default: true, null: false
- t.boolean "is_shared", default: false
- t.string "name"
- t.string "version"
- t.string "revision"
- t.string "platform"
- t.string "architecture"
- t.boolean "run_untagged", default: true, null: false
- t.boolean "locked", default: false, null: false
+ t.boolean "active", default: true, null: false
+ t.boolean "is_shared", default: false
+ t.string "name"
+ t.string "version"
+ t.string "revision"
+ t.string "platform"
+ t.string "architecture"
+ t.boolean "run_untagged", default: true, null: false
+ t.boolean "locked", default: false, null: false
end
add_index "ci_runners", ["locked"], name: "index_ci_runners_on_locked", using: :btree
add_index "ci_runners", ["token"], name: "index_ci_runners_on_token", using: :btree
create_table "ci_sessions", force: :cascade do |t|
- t.string "session_id", null: false
- t.text "data"
+ t.string "session_id", null: false
+ t.text "data"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "ci_taggings", force: :cascade do |t|
- t.integer "tag_id"
- t.integer "taggable_id"
- t.string "taggable_type"
- t.integer "tagger_id"
- t.string "tagger_type"
- t.string "context", limit: 128
+ t.integer "tag_id"
+ t.integer "taggable_id"
+ t.string "taggable_type"
+ t.integer "tagger_id"
+ t.string "tagger_type"
+ t.string "context", limit: 128
t.datetime "created_at"
end
add_index "ci_taggings", ["taggable_id", "taggable_type", "context"], name: "index_ci_taggings_on_taggable_id_and_taggable_type_and_context", using: :btree
create_table "ci_tags", force: :cascade do |t|
- t.string "name"
+ t.string "name"
t.integer "taggings_count", default: 0
end
create_table "ci_trigger_requests", force: :cascade do |t|
- t.integer "trigger_id", null: false
- t.text "variables"
+ t.integer "trigger_id", null: false
+ t.text "variables"
t.datetime "created_at"
t.datetime "updated_at"
- t.integer "commit_id"
+ t.integer "commit_id"
end
create_table "ci_triggers", force: :cascade do |t|
- t.string "token"
- t.integer "project_id"
+ t.string "token"
+ t.integer "project_id"
t.datetime "deleted_at"
t.datetime "created_at"
t.datetime "updated_at"
- t.integer "gl_project_id"
+ t.integer "gl_project_id"
end
add_index "ci_triggers", ["gl_project_id"], name: "index_ci_triggers_on_gl_project_id", using: :btree
create_table "ci_variables", force: :cascade do |t|
t.integer "project_id"
- t.string "key"
- t.text "value"
- t.text "encrypted_value"
- t.string "encrypted_value_salt"
- t.string "encrypted_value_iv"
+ t.string "key"
+ t.text "value"
+ t.text "encrypted_value"
+ t.string "encrypted_value_salt"
+ t.string "encrypted_value_iv"
t.integer "gl_project_id"
end
add_index "ci_variables", ["gl_project_id"], name: "index_ci_variables_on_gl_project_id", using: :btree
create_table "deploy_keys_projects", force: :cascade do |t|
- t.integer "deploy_key_id", null: false
- t.integer "project_id", null: false
+ t.integer "deploy_key_id", null: false
+ t.integer "project_id", null: false
t.datetime "created_at"
t.datetime "updated_at"
end
@@ -369,15 +369,15 @@ ActiveRecord::Schema.define(version: 20161007133303) do
add_index "deploy_keys_projects", ["project_id"], name: "index_deploy_keys_projects_on_project_id", using: :btree
create_table "deployments", force: :cascade do |t|
- t.integer "iid", null: false
- t.integer "project_id", null: false
- t.integer "environment_id", null: false
- t.string "ref", null: false
- t.boolean "tag", null: false
- t.string "sha", null: false
- t.integer "user_id"
- t.integer "deployable_id"
- t.string "deployable_type"
+ t.integer "iid", null: false
+ t.integer "project_id", null: false
+ t.integer "environment_id", null: false
+ t.string "ref", null: false
+ t.boolean "tag", null: false
+ t.string "sha", null: false
+ t.integer "user_id"
+ t.integer "deployable_id"
+ t.string "deployable_type"
t.datetime "created_at"
t.datetime "updated_at"
end
@@ -388,8 +388,8 @@ ActiveRecord::Schema.define(version: 20161007133303) do
add_index "deployments", ["project_id"], name: "index_deployments_on_project_id", using: :btree
create_table "emails", force: :cascade do |t|
- t.integer "user_id", null: false
- t.string "email", null: false
+ t.integer "user_id", null: false
+ t.string "email", null: false
t.datetime "created_at"
t.datetime "updated_at"
end
@@ -398,26 +398,26 @@ ActiveRecord::Schema.define(version: 20161007133303) do
add_index "emails", ["user_id"], name: "index_emails_on_user_id", using: :btree
create_table "environments", force: :cascade do |t|
- t.integer "project_id"
- t.string "name", null: false
+ t.integer "project_id"
+ t.string "name", null: false
t.datetime "created_at"
t.datetime "updated_at"
- t.string "external_url"
- t.string "environment_type"
+ t.string "external_url"
+ t.string "environment_type"
end
add_index "environments", ["project_id", "name"], name: "index_environments_on_project_id_and_name", using: :btree
create_table "events", force: :cascade do |t|
- t.string "target_type"
- t.integer "target_id"
- t.string "title"
- t.text "data"
- t.integer "project_id"
+ t.string "target_type"
+ t.integer "target_id"
+ t.string "title"
+ t.text "data"
+ t.integer "project_id"
t.datetime "created_at"
t.datetime "updated_at"
- t.integer "action"
- t.integer "author_id"
+ t.integer "action"
+ t.integer "author_id"
end
add_index "events", ["action"], name: "index_events_on_action", using: :btree
@@ -428,8 +428,8 @@ ActiveRecord::Schema.define(version: 20161007133303) do
add_index "events", ["target_type"], name: "index_events_on_target_type", using: :btree
create_table "forked_project_links", force: :cascade do |t|
- t.integer "forked_to_project_id", null: false
- t.integer "forked_from_project_id", null: false
+ t.integer "forked_to_project_id", null: false
+ t.integer "forked_from_project_id", null: false
t.datetime "created_at"
t.datetime "updated_at"
end
@@ -437,9 +437,9 @@ ActiveRecord::Schema.define(version: 20161007133303) do
add_index "forked_project_links", ["forked_to_project_id"], name: "index_forked_project_links_on_forked_to_project_id", unique: true, using: :btree
create_table "identities", force: :cascade do |t|
- t.string "extern_uid"
- t.string "provider"
- t.integer "user_id"
+ t.string "extern_uid"
+ t.string "provider"
+ t.integer "user_id"
t.datetime "created_at"
t.datetime "updated_at"
end
@@ -447,37 +447,37 @@ ActiveRecord::Schema.define(version: 20161007133303) do
add_index "identities", ["user_id"], name: "index_identities_on_user_id", using: :btree
create_table "issue_metrics", force: :cascade do |t|
- t.integer "issue_id", null: false
+ t.integer "issue_id", null: false
t.datetime "first_mentioned_in_commit_at"
t.datetime "first_associated_with_milestone_at"
t.datetime "first_added_to_board_at"
- t.datetime "created_at", null: false
- t.datetime "updated_at", null: false
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
end
add_index "issue_metrics", ["issue_id"], name: "index_issue_metrics", using: :btree
create_table "issues", force: :cascade do |t|
- t.string "title"
- t.integer "assignee_id"
- t.integer "author_id"
- t.integer "project_id"
+ t.string "title"
+ t.integer "assignee_id"
+ t.integer "author_id"
+ t.integer "project_id"
t.datetime "created_at"
t.datetime "updated_at"
- t.integer "position", default: 0
- t.string "branch_name"
- t.text "description"
- t.integer "milestone_id"
- t.string "state"
- t.integer "iid"
- t.integer "updated_by_id"
- t.boolean "confidential", default: false
+ t.integer "position", default: 0
+ t.string "branch_name"
+ t.text "description"
+ t.integer "milestone_id"
+ t.string "state"
+ t.integer "iid"
+ t.integer "updated_by_id"
+ t.boolean "confidential", default: false
t.datetime "deleted_at"
- t.date "due_date"
- t.integer "moved_to_id"
- t.integer "lock_version"
- t.text "title_html"
- t.text "description_html"
+ t.date "due_date"
+ t.integer "moved_to_id"
+ t.integer "lock_version"
+ t.text "title_html"
+ t.text "description_html"
end
add_index "issues", ["assignee_id"], name: "index_issues_on_assignee_id", using: :btree
@@ -493,23 +493,23 @@ ActiveRecord::Schema.define(version: 20161007133303) do
add_index "issues", ["title"], name: "index_issues_on_title_trigram", using: :gin, opclasses: {"title"=>"gin_trgm_ops"}
create_table "keys", force: :cascade do |t|
- t.integer "user_id"
+ t.integer "user_id"
t.datetime "created_at"
t.datetime "updated_at"
- t.text "key"
- t.string "title"
- t.string "type"
- t.string "fingerprint"
- t.boolean "public", default: false, null: false
+ t.text "key"
+ t.string "title"
+ t.string "type"
+ t.string "fingerprint"
+ t.boolean "public", default: false, null: false
end
add_index "keys", ["fingerprint"], name: "index_keys_on_fingerprint", unique: true, using: :btree
add_index "keys", ["user_id"], name: "index_keys_on_user_id", using: :btree
create_table "label_links", force: :cascade do |t|
- t.integer "label_id"
- t.integer "target_id"
- t.string "target_type"
+ t.integer "label_id"
+ t.integer "target_id"
+ t.string "target_type"
t.datetime "created_at"
t.datetime "updated_at"
end
@@ -518,15 +518,15 @@ ActiveRecord::Schema.define(version: 20161007133303) do
add_index "label_links", ["target_id", "target_type"], name: "index_label_links_on_target_id_and_target_type", using: :btree
create_table "labels", force: :cascade do |t|
- t.string "title"
- t.string "color"
- t.integer "project_id"
+ t.string "title"
+ t.string "color"
+ t.integer "project_id"
t.datetime "created_at"
t.datetime "updated_at"
- t.boolean "template", default: false
- t.string "description"
- t.integer "priority"
- t.text "description_html"
+ t.boolean "template", default: false
+ t.string "description"
+ t.integer "priority"
+ t.text "description_html"
end
add_index "labels", ["priority"], name: "index_labels_on_priority", using: :btree
@@ -534,18 +534,18 @@ ActiveRecord::Schema.define(version: 20161007133303) do
add_index "labels", ["title"], name: "index_labels_on_title", using: :btree
create_table "lfs_objects", force: :cascade do |t|
- t.string "oid", null: false
- t.integer "size", limit: 8, null: false
+ t.string "oid", null: false
+ t.integer "size", limit: 8, null: false
t.datetime "created_at"
t.datetime "updated_at"
- t.string "file"
+ t.string "file"
end
add_index "lfs_objects", ["oid"], name: "index_lfs_objects_on_oid", unique: true, using: :btree
create_table "lfs_objects_projects", force: :cascade do |t|
- t.integer "lfs_object_id", null: false
- t.integer "project_id", null: false
+ t.integer "lfs_object_id", null: false
+ t.integer "project_id", null: false
t.datetime "created_at"
t.datetime "updated_at"
end
@@ -553,12 +553,12 @@ ActiveRecord::Schema.define(version: 20161007133303) do
add_index "lfs_objects_projects", ["project_id"], name: "index_lfs_objects_projects_on_project_id", using: :btree
create_table "lists", force: :cascade do |t|
- t.integer "board_id", null: false
- t.integer "label_id"
- t.integer "list_type", default: 1, null: false
- t.integer "position"
- t.datetime "created_at", null: false
- t.datetime "updated_at", null: false
+ t.integer "board_id", null: false
+ t.integer "label_id"
+ t.integer "list_type", default: 1, null: false
+ t.integer "position"
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
end
add_index "lists", ["board_id", "label_id"], name: "index_lists_on_board_id_and_label_id", unique: true, using: :btree
@@ -566,20 +566,20 @@ ActiveRecord::Schema.define(version: 20161007133303) do
add_index "lists", ["label_id"], name: "index_lists_on_label_id", using: :btree
create_table "members", force: :cascade do |t|
- t.integer "access_level", null: false
- t.integer "source_id", null: false
- t.string "source_type", null: false
- t.integer "user_id"
- t.integer "notification_level", null: false
- t.string "type"
+ t.integer "access_level", null: false
+ t.integer "source_id", null: false
+ t.string "source_type", null: false
+ t.integer "user_id"
+ t.integer "notification_level", null: false
+ t.string "type"
t.datetime "created_at"
t.datetime "updated_at"
- t.integer "created_by_id"
- t.string "invite_email"
- t.string "invite_token"
+ t.integer "created_by_id"
+ t.string "invite_email"
+ t.string "invite_token"
t.datetime "invite_accepted_at"
t.datetime "requested_at"
- t.date "expires_at"
+ t.date "expires_at"
end
add_index "members", ["access_level"], name: "index_members_on_access_level", using: :btree
@@ -589,61 +589,61 @@ ActiveRecord::Schema.define(version: 20161007133303) do
add_index "members", ["user_id"], name: "index_members_on_user_id", using: :btree
create_table "merge_request_diffs", force: :cascade do |t|
- t.string "state"
- t.text "st_commits"
- t.text "st_diffs"
- t.integer "merge_request_id", null: false
+ t.string "state"
+ t.text "st_commits"
+ t.text "st_diffs"
+ t.integer "merge_request_id", null: false
t.datetime "created_at"
t.datetime "updated_at"
- t.string "base_commit_sha"
- t.string "real_size"
- t.string "head_commit_sha"
- t.string "start_commit_sha"
+ t.string "base_commit_sha"
+ t.string "real_size"
+ t.string "head_commit_sha"
+ t.string "start_commit_sha"
end
add_index "merge_request_diffs", ["merge_request_id"], name: "index_merge_request_diffs_on_merge_request_id", using: :btree
create_table "merge_request_metrics", force: :cascade do |t|
- t.integer "merge_request_id", null: false
+ t.integer "merge_request_id", null: false
t.datetime "latest_build_started_at"
t.datetime "latest_build_finished_at"
t.datetime "first_deployed_to_production_at"
t.datetime "merged_at"
- t.datetime "created_at", null: false
- t.datetime "updated_at", null: false
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
end
add_index "merge_request_metrics", ["first_deployed_to_production_at"], name: "index_merge_request_metrics_on_first_deployed_to_production_at", using: :btree
add_index "merge_request_metrics", ["merge_request_id"], name: "index_merge_request_metrics", using: :btree
create_table "merge_requests", force: :cascade do |t|
- t.string "target_branch", null: false
- t.string "source_branch", null: false
- t.integer "source_project_id", null: false
- t.integer "author_id"
- t.integer "assignee_id"
- t.string "title"
+ t.string "target_branch", null: false
+ t.string "source_branch", null: false
+ t.integer "source_project_id", null: false
+ t.integer "author_id"
+ t.integer "assignee_id"
+ t.string "title"
t.datetime "created_at"
t.datetime "updated_at"
- t.integer "milestone_id"
- t.string "state"
- t.string "merge_status"
- t.integer "target_project_id", null: false
- t.integer "iid"
- t.text "description"
- t.integer "position", default: 0
+ t.integer "milestone_id"
+ t.string "state"
+ t.string "merge_status"
+ t.integer "target_project_id", null: false
+ t.integer "iid"
+ t.text "description"
+ t.integer "position", default: 0
t.datetime "locked_at"
- t.integer "updated_by_id"
- t.text "merge_error"
- t.text "merge_params"
- t.boolean "merge_when_build_succeeds", default: false, null: false
- t.integer "merge_user_id"
- t.string "merge_commit_sha"
+ t.integer "updated_by_id"
+ t.text "merge_error"
+ t.text "merge_params"
+ t.boolean "merge_when_build_succeeds", default: false, null: false
+ t.integer "merge_user_id"
+ t.string "merge_commit_sha"
t.datetime "deleted_at"
- t.string "in_progress_merge_commit_sha"
- t.integer "lock_version"
- t.text "title_html"
- t.text "description_html"
+ t.string "in_progress_merge_commit_sha"
+ t.integer "lock_version"
+ t.text "title_html"
+ t.text "description_html"
end
add_index "merge_requests", ["assignee_id"], name: "index_merge_requests_on_assignee_id", using: :btree
@@ -660,26 +660,26 @@ ActiveRecord::Schema.define(version: 20161007133303) do
add_index "merge_requests", ["title"], name: "index_merge_requests_on_title_trigram", using: :gin, opclasses: {"title"=>"gin_trgm_ops"}
create_table "merge_requests_closing_issues", force: :cascade do |t|
- t.integer "merge_request_id", null: false
- t.integer "issue_id", null: false
- t.datetime "created_at", null: false
- t.datetime "updated_at", null: false
+ t.integer "merge_request_id", null: false
+ t.integer "issue_id", null: false
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
end
add_index "merge_requests_closing_issues", ["issue_id"], name: "index_merge_requests_closing_issues_on_issue_id", using: :btree
add_index "merge_requests_closing_issues", ["merge_request_id"], name: "index_merge_requests_closing_issues_on_merge_request_id", using: :btree
create_table "milestones", force: :cascade do |t|
- t.string "title", null: false
- t.integer "project_id", null: false
- t.text "description"
- t.date "due_date"
+ t.string "title", null: false
+ t.integer "project_id", null: false
+ t.text "description"
+ t.date "due_date"
t.datetime "created_at"
t.datetime "updated_at"
- t.string "state"
- t.integer "iid"
- t.text "title_html"
- t.text "description_html"
+ t.string "state"
+ t.integer "iid"
+ t.text "title_html"
+ t.text "description_html"
end
add_index "milestones", ["description"], name: "index_milestones_on_description_trigram", using: :gin, opclasses: {"description"=>"gin_trgm_ops"}
@@ -690,20 +690,20 @@ ActiveRecord::Schema.define(version: 20161007133303) do
add_index "milestones", ["title"], name: "index_milestones_on_title_trigram", using: :gin, opclasses: {"title"=>"gin_trgm_ops"}
create_table "namespaces", force: :cascade do |t|
- t.string "name", null: false
- t.string "path", null: false
- t.integer "owner_id"
+ t.string "name", null: false
+ t.string "path", null: false
+ t.integer "owner_id"
t.datetime "created_at"
t.datetime "updated_at"
- t.string "type"
- t.string "description", default: "", null: false
- t.string "avatar"
- t.boolean "share_with_group_lock", default: false
- t.integer "visibility_level", default: 20, null: false
- t.boolean "request_access_enabled", default: true, null: false
+ t.string "type"
+ t.string "description", default: "", null: false
+ t.string "avatar"
+ t.boolean "share_with_group_lock", default: false
+ t.integer "visibility_level", default: 20, null: false
+ t.boolean "request_access_enabled", default: true, null: false
t.datetime "deleted_at"
- t.boolean "lfs_enabled"
- t.text "description_html"
+ t.boolean "lfs_enabled"
+ t.text "description_html"
end
add_index "namespaces", ["created_at"], name: "index_namespaces_on_created_at", using: :btree
@@ -716,27 +716,27 @@ ActiveRecord::Schema.define(version: 20161007133303) do
add_index "namespaces", ["type"], name: "index_namespaces_on_type", using: :btree
create_table "notes", force: :cascade do |t|
- t.text "note"
- t.string "noteable_type"
- t.integer "author_id"
+ t.text "note"
+ t.string "noteable_type"
+ t.integer "author_id"
t.datetime "created_at"
t.datetime "updated_at"
- t.integer "project_id"
- t.string "attachment"
- t.string "line_code"
- t.string "commit_id"
- t.integer "noteable_id"
- t.boolean "system", default: false, null: false
- t.text "st_diff"
- t.integer "updated_by_id"
- t.string "type"
- t.text "position"
- t.text "original_position"
+ t.integer "project_id"
+ t.string "attachment"
+ t.string "line_code"
+ t.string "commit_id"
+ t.integer "noteable_id"
+ t.boolean "system", default: false, null: false
+ t.text "st_diff"
+ t.integer "updated_by_id"
+ t.string "type"
+ t.text "position"
+ t.text "original_position"
t.datetime "resolved_at"
- t.integer "resolved_by_id"
- t.string "discussion_id"
- t.string "original_discussion_id"
- t.text "note_html"
+ t.integer "resolved_by_id"
+ t.string "discussion_id"
+ t.string "original_discussion_id"
+ t.text "note_html"
end
add_index "notes", ["author_id"], name: "index_notes_on_author_id", using: :btree
@@ -752,13 +752,13 @@ ActiveRecord::Schema.define(version: 20161007133303) do
add_index "notes", ["updated_at"], name: "index_notes_on_updated_at", using: :btree
create_table "notification_settings", force: :cascade do |t|
- t.integer "user_id", null: false
- t.integer "source_id"
- t.string "source_type"
- t.integer "level", default: 0, null: false
- t.datetime "created_at", null: false
- t.datetime "updated_at", null: false
- t.text "events"
+ t.integer "user_id", null: false
+ t.integer "source_id"
+ t.string "source_type"
+ t.integer "level", default: 0, null: false
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
+ t.text "events"
end
add_index "notification_settings", ["source_id", "source_type"], name: "index_notification_settings_on_source_id_and_source_type", using: :btree
@@ -766,27 +766,27 @@ ActiveRecord::Schema.define(version: 20161007133303) do
add_index "notification_settings", ["user_id"], name: "index_notification_settings_on_user_id", using: :btree
create_table "oauth_access_grants", force: :cascade do |t|
- t.integer "resource_owner_id", null: false
- t.integer "application_id", null: false
- t.string "token", null: false
- t.integer "expires_in", null: false
- t.text "redirect_uri", null: false
- t.datetime "created_at", null: false
+ t.integer "resource_owner_id", null: false
+ t.integer "application_id", null: false
+ t.string "token", null: false
+ t.integer "expires_in", null: false
+ t.text "redirect_uri", null: false
+ t.datetime "created_at", null: false
t.datetime "revoked_at"
- t.string "scopes"
+ t.string "scopes"
end
add_index "oauth_access_grants", ["token"], name: "index_oauth_access_grants_on_token", unique: true, using: :btree
create_table "oauth_access_tokens", force: :cascade do |t|
- t.integer "resource_owner_id"
- t.integer "application_id"
- t.string "token", null: false
- t.string "refresh_token"
- t.integer "expires_in"
+ t.integer "resource_owner_id"
+ t.integer "application_id"
+ t.string "token", null: false
+ t.string "refresh_token"
+ t.integer "expires_in"
t.datetime "revoked_at"
- t.datetime "created_at", null: false
- t.string "scopes"
+ t.datetime "created_at", null: false
+ t.string "scopes"
end
add_index "oauth_access_tokens", ["refresh_token"], name: "index_oauth_access_tokens_on_refresh_token", unique: true, using: :btree
@@ -794,40 +794,40 @@ ActiveRecord::Schema.define(version: 20161007133303) do
add_index "oauth_access_tokens", ["token"], name: "index_oauth_access_tokens_on_token", unique: true, using: :btree
create_table "oauth_applications", force: :cascade do |t|
- t.string "name", null: false
- t.string "uid", null: false
- t.string "secret", null: false
- t.text "redirect_uri", null: false
- t.string "scopes", default: "", null: false
+ t.string "name", null: false
+ t.string "uid", null: false
+ t.string "secret", null: false
+ t.text "redirect_uri", null: false
+ t.string "scopes", default: "", null: false
t.datetime "created_at"
t.datetime "updated_at"
- t.integer "owner_id"
- t.string "owner_type"
+ t.integer "owner_id"
+ t.string "owner_type"
end
add_index "oauth_applications", ["owner_id", "owner_type"], name: "index_oauth_applications_on_owner_id_and_owner_type", using: :btree
add_index "oauth_applications", ["uid"], name: "index_oauth_applications_on_uid", unique: true, using: :btree
create_table "personal_access_tokens", force: :cascade do |t|
- t.integer "user_id", null: false
- t.string "token", null: false
- t.string "name", null: false
- t.boolean "revoked", default: false
+ t.integer "user_id", null: false
+ t.string "token", null: false
+ t.string "name", null: false
+ t.boolean "revoked", default: false
t.datetime "expires_at"
- t.datetime "created_at", null: false
- t.datetime "updated_at", null: false
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
end
add_index "personal_access_tokens", ["token"], name: "index_personal_access_tokens_on_token", unique: true, using: :btree
add_index "personal_access_tokens", ["user_id"], name: "index_personal_access_tokens_on_user_id", using: :btree
create_table "project_features", force: :cascade do |t|
- t.integer "project_id"
- t.integer "merge_requests_access_level"
- t.integer "issues_access_level"
- t.integer "wiki_access_level"
- t.integer "snippets_access_level"
- t.integer "builds_access_level"
+ t.integer "project_id"
+ t.integer "merge_requests_access_level"
+ t.integer "issues_access_level"
+ t.integer "wiki_access_level"
+ t.integer "snippets_access_level"
+ t.integer "builds_access_level"
t.datetime "created_at"
t.datetime "updated_at"
end
@@ -835,60 +835,60 @@ ActiveRecord::Schema.define(version: 20161007133303) do
add_index "project_features", ["project_id"], name: "index_project_features_on_project_id", using: :btree
create_table "project_group_links", force: :cascade do |t|
- t.integer "project_id", null: false
- t.integer "group_id", null: false
+ t.integer "project_id", null: false
+ t.integer "group_id", null: false
t.datetime "created_at"
t.datetime "updated_at"
- t.integer "group_access", default: 30, null: false
- t.date "expires_at"
+ t.integer "group_access", default: 30, null: false
+ t.date "expires_at"
end
create_table "project_import_data", force: :cascade do |t|
t.integer "project_id"
- t.text "data"
- t.text "encrypted_credentials"
- t.string "encrypted_credentials_iv"
- t.string "encrypted_credentials_salt"
+ t.text "data"
+ t.text "encrypted_credentials"
+ t.string "encrypted_credentials_iv"
+ t.string "encrypted_credentials_salt"
end
create_table "projects", force: :cascade do |t|
- t.string "name"
- t.string "path"
- t.text "description"
+ t.string "name"
+ t.string "path"
+ t.text "description"
t.datetime "created_at"
t.datetime "updated_at"
- t.integer "creator_id"
- t.integer "namespace_id"
+ t.integer "creator_id"
+ t.integer "namespace_id"
t.datetime "last_activity_at"
- t.string "import_url"
- t.integer "visibility_level", default: 0, null: false
- t.boolean "archived", default: false, null: false
- t.string "avatar"
- t.string "import_status"
- t.float "repository_size", default: 0.0
- t.integer "star_count", default: 0, null: false
- t.string "import_type"
- t.string "import_source"
- t.integer "commit_count", default: 0
- t.text "import_error"
- t.integer "ci_id"
- t.boolean "shared_runners_enabled", default: true, null: false
- t.string "runners_token"
- t.string "build_coverage_regex"
- t.boolean "build_allow_git_fetch", default: true, null: false
- t.integer "build_timeout", default: 3600, null: false
- t.boolean "pending_delete", default: false
- t.boolean "public_builds", default: true, null: false
- t.boolean "last_repository_check_failed"
+ t.string "import_url"
+ t.integer "visibility_level", default: 0, null: false
+ t.boolean "archived", default: false, null: false
+ t.string "avatar"
+ t.string "import_status"
+ t.float "repository_size", default: 0.0
+ t.integer "star_count", default: 0, null: false
+ t.string "import_type"
+ t.string "import_source"
+ t.integer "commit_count", default: 0
+ t.text "import_error"
+ t.integer "ci_id"
+ t.boolean "shared_runners_enabled", default: true, null: false
+ t.string "runners_token"
+ t.string "build_coverage_regex"
+ t.boolean "build_allow_git_fetch", default: true, null: false
+ t.integer "build_timeout", default: 3600, null: false
+ t.boolean "pending_delete", default: false
+ t.boolean "public_builds", default: true, null: false
+ t.boolean "last_repository_check_failed"
t.datetime "last_repository_check_at"
- t.boolean "container_registry_enabled"
- t.boolean "only_allow_merge_if_build_succeeds", default: false, null: false
- t.boolean "has_external_issue_tracker"
- t.string "repository_storage", default: "default", null: false
- t.boolean "request_access_enabled", default: true, null: false
- t.boolean "has_external_wiki"
- t.boolean "lfs_enabled"
- t.text "description_html"
+ t.boolean "container_registry_enabled"
+ t.boolean "only_allow_merge_if_build_succeeds", default: false, null: false
+ t.boolean "has_external_issue_tracker"
+ t.string "repository_storage", default: "default", null: false
+ t.boolean "request_access_enabled", default: true, null: false
+ t.boolean "has_external_wiki"
+ t.boolean "lfs_enabled"
+ t.text "description_html"
end
add_index "projects", ["ci_id"], name: "index_projects_on_ci_id", using: :btree
@@ -907,26 +907,26 @@ ActiveRecord::Schema.define(version: 20161007133303) do
add_index "projects", ["visibility_level"], name: "index_projects_on_visibility_level", using: :btree
create_table "protected_branch_merge_access_levels", force: :cascade do |t|
- t.integer "protected_branch_id", null: false
- t.integer "access_level", default: 40, null: false
- t.datetime "created_at", null: false
- t.datetime "updated_at", null: false
+ t.integer "protected_branch_id", null: false
+ t.integer "access_level", default: 40, null: false
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
end
add_index "protected_branch_merge_access_levels", ["protected_branch_id"], name: "index_protected_branch_merge_access", using: :btree
create_table "protected_branch_push_access_levels", force: :cascade do |t|
- t.integer "protected_branch_id", null: false
- t.integer "access_level", default: 40, null: false
- t.datetime "created_at", null: false
- t.datetime "updated_at", null: false
+ t.integer "protected_branch_id", null: false
+ t.integer "access_level", default: 40, null: false
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
end
add_index "protected_branch_push_access_levels", ["protected_branch_id"], name: "index_protected_branch_push_access", using: :btree
create_table "protected_branches", force: :cascade do |t|
- t.integer "project_id", null: false
- t.string "name", null: false
+ t.integer "project_id", null: false
+ t.string "name", null: false
t.datetime "created_at"
t.datetime "updated_at"
end
@@ -934,12 +934,12 @@ ActiveRecord::Schema.define(version: 20161007133303) do
add_index "protected_branches", ["project_id"], name: "index_protected_branches_on_project_id", using: :btree
create_table "releases", force: :cascade do |t|
- t.string "tag"
- t.text "description"
- t.integer "project_id"
+ t.string "tag"
+ t.text "description"
+ t.integer "project_id"
t.datetime "created_at"
t.datetime "updated_at"
- t.text "description_html"
+ t.text "description_html"
end
add_index "releases", ["project_id", "tag"], name: "index_releases_on_project_id_and_tag", using: :btree
@@ -948,54 +948,54 @@ ActiveRecord::Schema.define(version: 20161007133303) do
create_table "sent_notifications", force: :cascade do |t|
t.integer "project_id"
t.integer "noteable_id"
- t.string "noteable_type"
+ t.string "noteable_type"
t.integer "recipient_id"
- t.string "commit_id"
- t.string "reply_key", null: false
- t.string "line_code"
- t.string "note_type"
- t.text "position"
+ t.string "commit_id"
+ t.string "reply_key", null: false
+ t.string "line_code"
+ t.string "note_type"
+ t.text "position"
end
add_index "sent_notifications", ["reply_key"], name: "index_sent_notifications_on_reply_key", unique: true, using: :btree
create_table "services", force: :cascade do |t|
- t.string "type"
- t.string "title"
- t.integer "project_id"
+ t.string "type"
+ t.string "title"
+ t.integer "project_id"
t.datetime "created_at"
t.datetime "updated_at"
- t.boolean "active", default: false, null: false
- t.text "properties"
- t.boolean "template", default: false
- t.boolean "push_events", default: true
- t.boolean "issues_events", default: true
- t.boolean "merge_requests_events", default: true
- t.boolean "tag_push_events", default: true
- t.boolean "note_events", default: true, null: false
- t.boolean "build_events", default: false, null: false
- t.string "category", default: "common", null: false
- t.boolean "default", default: false
- t.boolean "wiki_page_events", default: true
- t.boolean "pipeline_events", default: false, null: false
- t.boolean "confidential_issues_events", default: true, null: false
+ t.boolean "active", default: false, null: false
+ t.text "properties"
+ t.boolean "template", default: false
+ t.boolean "push_events", default: true
+ t.boolean "issues_events", default: true
+ t.boolean "merge_requests_events", default: true
+ t.boolean "tag_push_events", default: true
+ t.boolean "note_events", default: true, null: false
+ t.boolean "build_events", default: false, null: false
+ t.string "category", default: "common", null: false
+ t.boolean "default", default: false
+ t.boolean "wiki_page_events", default: true
+ t.boolean "pipeline_events", default: false, null: false
+ t.boolean "confidential_issues_events", default: true, null: false
end
add_index "services", ["project_id"], name: "index_services_on_project_id", using: :btree
add_index "services", ["template"], name: "index_services_on_template", using: :btree
create_table "snippets", force: :cascade do |t|
- t.string "title"
- t.text "content"
- t.integer "author_id", null: false
- t.integer "project_id"
+ t.string "title"
+ t.text "content"
+ t.integer "author_id", null: false
+ t.integer "project_id"
t.datetime "created_at"
t.datetime "updated_at"
- t.string "file_name"
- t.string "type"
- t.integer "visibility_level", default: 0, null: false
- t.text "title_html"
- t.text "content_html"
+ t.string "file_name"
+ t.string "type"
+ t.integer "visibility_level", default: 0, null: false
+ t.text "title_html"
+ t.text "content_html"
end
add_index "snippets", ["author_id"], name: "index_snippets_on_author_id", using: :btree
@@ -1006,23 +1006,23 @@ ActiveRecord::Schema.define(version: 20161007133303) do
add_index "snippets", ["visibility_level"], name: "index_snippets_on_visibility_level", using: :btree
create_table "spam_logs", force: :cascade do |t|
- t.integer "user_id"
- t.string "source_ip"
- t.string "user_agent"
- t.boolean "via_api"
- t.string "noteable_type"
- t.string "title"
- t.text "description"
- t.datetime "created_at", null: false
- t.datetime "updated_at", null: false
- t.boolean "submitted_as_ham", default: false, null: false
+ t.integer "user_id"
+ t.string "source_ip"
+ t.string "user_agent"
+ t.boolean "via_api"
+ t.string "noteable_type"
+ t.string "title"
+ t.text "description"
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
+ t.boolean "submitted_as_ham", default: false, null: false
end
create_table "subscriptions", force: :cascade do |t|
- t.integer "user_id"
- t.integer "subscribable_id"
- t.string "subscribable_type"
- t.boolean "subscribed"
+ t.integer "user_id"
+ t.integer "subscribable_id"
+ t.string "subscribable_type"
+ t.boolean "subscribed"
t.datetime "created_at"
t.datetime "updated_at"
end
@@ -1030,12 +1030,12 @@ ActiveRecord::Schema.define(version: 20161007133303) do
add_index "subscriptions", ["subscribable_id", "subscribable_type", "user_id"], name: "subscriptions_user_id_and_ref_fields", unique: true, using: :btree
create_table "taggings", force: :cascade do |t|
- t.integer "tag_id"
- t.integer "taggable_id"
- t.string "taggable_type"
- t.integer "tagger_id"
- t.string "tagger_type"
- t.string "context"
+ t.integer "tag_id"
+ t.integer "taggable_id"
+ t.string "taggable_type"
+ t.integer "tagger_id"
+ t.string "tagger_type"
+ t.string "context"
t.datetime "created_at"
end
@@ -1043,24 +1043,24 @@ ActiveRecord::Schema.define(version: 20161007133303) do
add_index "taggings", ["taggable_id", "taggable_type", "context"], name: "index_taggings_on_taggable_id_and_taggable_type_and_context", using: :btree
create_table "tags", force: :cascade do |t|
- t.string "name"
+ t.string "name"
t.integer "taggings_count", default: 0
end
add_index "tags", ["name"], name: "index_tags_on_name", unique: true, using: :btree
create_table "todos", force: :cascade do |t|
- t.integer "user_id", null: false
- t.integer "project_id", null: false
- t.integer "target_id"
- t.string "target_type", null: false
- t.integer "author_id"
- t.integer "action", null: false
- t.string "state", null: false
+ t.integer "user_id", null: false
+ t.integer "project_id", null: false
+ t.integer "target_id"
+ t.string "target_type", null: false
+ t.integer "author_id"
+ t.integer "action", null: false
+ t.string "state", null: false
t.datetime "created_at"
t.datetime "updated_at"
- t.integer "note_id"
- t.string "commit_id"
+ t.integer "note_id"
+ t.string "commit_id"
end
add_index "todos", ["author_id"], name: "index_todos_on_author_id", using: :btree
@@ -1077,88 +1077,88 @@ ActiveRecord::Schema.define(version: 20161007133303) do
add_index "trending_projects", ["project_id"], name: "index_trending_projects_on_project_id", using: :btree
create_table "u2f_registrations", force: :cascade do |t|
- t.text "certificate"
- t.string "key_handle"
- t.string "public_key"
- t.integer "counter"
- t.integer "user_id"
- t.datetime "created_at", null: false
- t.datetime "updated_at", null: false
- t.string "name"
+ t.text "certificate"
+ t.string "key_handle"
+ t.string "public_key"
+ t.integer "counter"
+ t.integer "user_id"
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
+ t.string "name"
end
add_index "u2f_registrations", ["key_handle"], name: "index_u2f_registrations_on_key_handle", using: :btree
add_index "u2f_registrations", ["user_id"], name: "index_u2f_registrations_on_user_id", using: :btree
create_table "user_agent_details", force: :cascade do |t|
- t.string "user_agent", null: false
- t.string "ip_address", null: false
- t.integer "subject_id", null: false
- t.string "subject_type", null: false
- t.boolean "submitted", default: false, null: false
- t.datetime "created_at", null: false
- t.datetime "updated_at", null: false
+ t.string "user_agent", null: false
+ t.string "ip_address", null: false
+ t.integer "subject_id", null: false
+ t.string "subject_type", null: false
+ t.boolean "submitted", default: false, null: false
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
end
create_table "users", force: :cascade do |t|
- t.string "email", default: "", null: false
- t.string "encrypted_password", default: "", null: false
- t.string "reset_password_token"
+ t.string "email", default: "", null: false
+ t.string "encrypted_password", default: "", null: false
+ t.string "reset_password_token"
t.datetime "reset_password_sent_at"
t.datetime "remember_created_at"
- t.integer "sign_in_count", default: 0
+ t.integer "sign_in_count", default: 0
t.datetime "current_sign_in_at"
t.datetime "last_sign_in_at"
- t.string "current_sign_in_ip"
- t.string "last_sign_in_ip"
+ t.string "current_sign_in_ip"
+ t.string "last_sign_in_ip"
t.datetime "created_at"
t.datetime "updated_at"
- t.string "name"
- t.boolean "admin", default: false, null: false
- t.integer "projects_limit", default: 10
- t.string "skype", default: "", null: false
- t.string "linkedin", default: "", null: false
- t.string "twitter", default: "", null: false
- t.string "authentication_token"
- t.integer "theme_id", default: 1, null: false
- t.string "bio"
- t.integer "failed_attempts", default: 0
+ t.string "name"
+ t.boolean "admin", default: false, null: false
+ t.integer "projects_limit", default: 10
+ t.string "skype", default: "", null: false
+ t.string "linkedin", default: "", null: false
+ t.string "twitter", default: "", null: false
+ t.string "authentication_token"
+ t.integer "theme_id", default: 1, null: false
+ t.string "bio"
+ t.integer "failed_attempts", default: 0
t.datetime "locked_at"
- t.string "username"
- t.boolean "can_create_group", default: true, null: false
- t.boolean "can_create_team", default: true, null: false
- t.string "state"
- t.integer "color_scheme_id", default: 1, null: false
+ t.string "username"
+ t.boolean "can_create_group", default: true, null: false
+ t.boolean "can_create_team", default: true, null: false
+ t.string "state"
+ t.integer "color_scheme_id", default: 1, null: false
t.datetime "password_expires_at"
- t.integer "created_by_id"
+ t.integer "created_by_id"
t.datetime "last_credential_check_at"
- t.string "avatar"
- t.string "confirmation_token"
+ t.string "avatar"
+ t.string "confirmation_token"
t.datetime "confirmed_at"
t.datetime "confirmation_sent_at"
- t.string "unconfirmed_email"
- t.boolean "hide_no_ssh_key", default: false
- t.string "website_url", default: "", null: false
- t.string "notification_email"
- t.boolean "hide_no_password", default: false
- t.boolean "password_automatically_set", default: false
- t.string "location"
- t.string "encrypted_otp_secret"
- t.string "encrypted_otp_secret_iv"
- t.string "encrypted_otp_secret_salt"
- t.boolean "otp_required_for_login", default: false, null: false
- t.text "otp_backup_codes"
- t.string "public_email", default: "", null: false
- t.integer "dashboard", default: 0
- t.integer "project_view", default: 0
- t.integer "consumed_timestep"
- t.integer "layout", default: 0
- t.boolean "hide_project_limit", default: false
- t.string "unlock_token"
+ t.string "unconfirmed_email"
+ t.boolean "hide_no_ssh_key", default: false
+ t.string "website_url", default: "", null: false
+ t.string "notification_email"
+ t.boolean "hide_no_password", default: false
+ t.boolean "password_automatically_set", default: false
+ t.string "location"
+ t.string "encrypted_otp_secret"
+ t.string "encrypted_otp_secret_iv"
+ t.string "encrypted_otp_secret_salt"
+ t.boolean "otp_required_for_login", default: false, null: false
+ t.text "otp_backup_codes"
+ t.string "public_email", default: "", null: false
+ t.integer "dashboard", default: 0
+ t.integer "project_view", default: 0
+ t.integer "consumed_timestep"
+ t.integer "layout", default: 0
+ t.boolean "hide_project_limit", default: false
+ t.string "unlock_token"
t.datetime "otp_grace_period_started_at"
- t.boolean "ldap_email", default: false, null: false
- t.boolean "external", default: false
- t.string "organization"
+ t.boolean "ldap_email", default: false, null: false
+ t.boolean "external", default: false
+ t.string "organization"
end
add_index "users", ["admin"], name: "index_users_on_admin", using: :btree
@@ -1176,8 +1176,8 @@ ActiveRecord::Schema.define(version: 20161007133303) do
add_index "users", ["username"], name: "index_users_on_username_trigram", using: :gin, opclasses: {"username"=>"gin_trgm_ops"}
create_table "users_star_projects", force: :cascade do |t|
- t.integer "project_id", null: false
- t.integer "user_id", null: false
+ t.integer "project_id", null: false
+ t.integer "user_id", null: false
t.datetime "created_at"
t.datetime "updated_at"
end
@@ -1187,23 +1187,23 @@ ActiveRecord::Schema.define(version: 20161007133303) do
add_index "users_star_projects", ["user_id"], name: "index_users_star_projects_on_user_id", using: :btree
create_table "web_hooks", force: :cascade do |t|
- t.string "url", limit: 2000
- t.integer "project_id"
+ t.string "url", limit: 2000
+ t.integer "project_id"
t.datetime "created_at"
t.datetime "updated_at"
- t.string "type", default: "ProjectHook"
- t.integer "service_id"
- t.boolean "push_events", default: true, null: false
- t.boolean "issues_events", default: false, null: false
- t.boolean "merge_requests_events", default: false, null: false
- t.boolean "tag_push_events", default: false
- t.boolean "note_events", default: false, null: false
- t.boolean "enable_ssl_verification", default: true
- t.boolean "build_events", default: false, null: false
- t.boolean "wiki_page_events", default: false, null: false
- t.string "token"
- t.boolean "pipeline_events", default: false, null: false
- t.boolean "confidential_issues_events", default: false, null: false
+ t.string "type", default: "ProjectHook"
+ t.integer "service_id"
+ t.boolean "push_events", default: true, null: false
+ t.boolean "issues_events", default: false, null: false
+ t.boolean "merge_requests_events", default: false, null: false
+ t.boolean "tag_push_events", default: false
+ t.boolean "note_events", default: false, null: false
+ t.boolean "enable_ssl_verification", default: true
+ t.boolean "build_events", default: false, null: false
+ t.boolean "wiki_page_events", default: false, null: false
+ t.string "token"
+ t.boolean "pipeline_events", default: false, null: false
+ t.boolean "confidential_issues_events", default: false, null: false
end
add_index "web_hooks", ["project_id"], name: "index_web_hooks_on_project_id", using: :btree
diff --git a/doc/README.md b/doc/README.md
index e7712eed5d3..7e3d9b00900 100644
--- a/doc/README.md
+++ b/doc/README.md
@@ -20,6 +20,7 @@
- [Webhooks](web_hooks/web_hooks.md) Let GitLab notify you when new code has been pushed to your project.
- [Workflow](workflow/README.md) Using GitLab functionality and importing projects from GitHub and SVN.
- [University](university/README.md) Learn Git and GitLab through videos and courses.
+- [Git Attributes](user/project/git_attributes.md) Managing Git attributes using a `.gitattributes` file.
## Administrator documentation
@@ -35,7 +36,7 @@
- [Libravatar](customization/libravatar.md) Use Libravatar instead of Gravatar for user avatars.
- [Log system](administration/logs.md) Log system.
- [Environment Variables](administration/environment_variables.md) to configure GitLab.
-- [Operations](operations/README.md) Keeping GitLab up and running.
+- [Operations](administration/operations.md) Keeping GitLab up and running.
- [Raketasks](raketasks/README.md) Backups, maintenance, automatic webhook setup and the importing of projects.
- [Repository checks](administration/repository_checks.md) Periodic Git repository checks.
- [Repository storages](administration/repository_storages.md) Manage the paths used to store repositories.
@@ -47,8 +48,8 @@
- [Migrate GitLab CI to CE/EE](migrate_ci_to_ce/README.md) Follow this guide to migrate your existing GitLab CI data to GitLab CE/EE.
- [Git LFS configuration](workflow/lfs/lfs_administration.md)
- [Housekeeping](administration/housekeeping.md) Keep your Git repository tidy and fast.
-- [GitLab Performance Monitoring](monitoring/performance/introduction.md) Configure GitLab and InfluxDB for measuring performance metrics.
-- [Monitoring uptime](monitoring/health_check.md) Check the server status using the health check endpoint.
+- [GitLab Performance Monitoring](administration/monitoring/performance/introduction.md) Configure GitLab and InfluxDB for measuring performance metrics.
+- [Monitoring uptime](user/admin_area/monitoring/health_check.md) Check the server status using the health check endpoint.
- [Debugging Tips](administration/troubleshooting/debug.md) Tips to debug problems when things go wrong
- [Sidekiq Troubleshooting](administration/troubleshooting/sidekiq.md) Debug when Sidekiq appears hung and is not processing jobs.
- [High Availability](administration/high_availability/README.md) Configure multiple servers for scaling or high availability.
diff --git a/doc/administration/high_availability/gitlab.md b/doc/administration/high_availability/gitlab.md
index 8a881ce8863..137fed35a73 100644
--- a/doc/administration/high_availability/gitlab.md
+++ b/doc/administration/high_availability/gitlab.md
@@ -101,9 +101,9 @@ need some additional configuration.
```ruby
gitlab_shell['secret_token'] = 'fbfb19c355066a9afb030992231c4a363357f77345edd0f2e772359e5be59b02538e1fa6cae8f93f7d23355341cea2b93600dab6d6c3edcdced558fc6d739860'
- gitlab_rails['secret_token'] = 'b719fe119132c7810908bba18315259ed12888d4f5ee5430c42a776d840a396799b0a5ef0a801348c8a357f07aa72bbd58e25a84b8f247a25c72f539c7a6c5fa'
- gitlab_ci['secret_key_base'] = '6e657410d57c71b4fc3ed0d694e7842b1895a8b401d812c17fe61caf95b48a6d703cb53c112bc01ebd197a85da81b18e29682040e99b4f26594772a4a2c98c6d'
- gitlab_ci['db_key_base'] = 'bf2e47b68d6cafaef1d767e628b619365becf27571e10f196f98dc85e7771042b9203199d39aff91fcb6837c8ed83f2a912b278da50999bb11a2fbc0fba52964'
+ gitlab_rails['otp_key_base'] = 'b719fe119132c7810908bba18315259ed12888d4f5ee5430c42a776d840a396799b0a5ef0a801348c8a357f07aa72bbd58e25a84b8f247a25c72f539c7a6c5fa'
+ gitlab_rails['secret_key_base'] = '6e657410d57c71b4fc3ed0d694e7842b1895a8b401d812c17fe61caf95b48a6d703cb53c112bc01ebd197a85da81b18e29682040e99b4f26594772a4a2c98c6d'
+ gitlab_rails['db_key_base'] = 'bf2e47b68d6cafaef1d767e628b619365becf27571e10f196f98dc85e7771042b9203199d39aff91fcb6837c8ed83f2a912b278da50999bb11a2fbc0fba52964'
```
1. Run `touch /etc/gitlab/skip-auto-migrations` to prevent database migrations
diff --git a/doc/administration/monitoring/performance/gitlab_configuration.md b/doc/administration/monitoring/performance/gitlab_configuration.md
new file mode 100644
index 00000000000..771584268d9
--- /dev/null
+++ b/doc/administration/monitoring/performance/gitlab_configuration.md
@@ -0,0 +1,40 @@
+# GitLab Configuration
+
+GitLab Performance Monitoring is disabled by default. To enable it and change any of its
+settings, navigate to the Admin area in **Settings > Metrics**
+(`/admin/application_settings`).
+
+The minimum required settings you need to set are the InfluxDB host and port.
+Make sure _Enable InfluxDB Metrics_ is checked and hit **Save** to save the
+changes.
+
+---
+
+![GitLab Performance Monitoring Admin Settings](img/metrics_gitlab_configuration_settings.png)
+
+---
+
+Finally, a restart of all GitLab processes is required for the changes to take
+effect:
+
+```bash
+# For Omnibus installations
+sudo gitlab-ctl restart
+
+# For installations from source
+sudo service gitlab restart
+```
+
+## Pending Migrations
+
+When any migrations are pending, the metrics are disabled until the migrations
+have been performed.
+
+---
+
+Read more on:
+
+- [Introduction to GitLab Performance Monitoring](introduction.md)
+- [InfluxDB Configuration](influxdb_configuration.md)
+- [InfluxDB Schema](influxdb_schema.md)
+- [Grafana Install/Configuration](grafana_configuration.md)
diff --git a/doc/administration/monitoring/performance/grafana_configuration.md b/doc/administration/monitoring/performance/grafana_configuration.md
new file mode 100644
index 00000000000..7947b0fedc4
--- /dev/null
+++ b/doc/administration/monitoring/performance/grafana_configuration.md
@@ -0,0 +1,111 @@
+# Grafana Configuration
+
+[Grafana](http://grafana.org/) is a tool that allows you to visualize time
+series metrics through graphs and dashboards. It supports several backend
+data stores, including InfluxDB. GitLab writes performance data to InfluxDB
+and Grafana will allow you to query InfluxDB to display useful graphs.
+
+For the easiest installation and configuration, install Grafana on the same
+server as InfluxDB. For larger installations, you may want to split out these
+services.
+
+## Installation
+
+Grafana supplies package repositories (Yum/Apt) for easy installation.
+See [Grafana installation documentation](http://docs.grafana.org/installation/)
+for detailed steps.
+
+> **Note**: Before starting Grafana for the first time, set the admin user
+and password in `/etc/grafana/grafana.ini`. Otherwise, the default password
+will be `admin`.
+
+## Configuration
+
+Login as the admin user. Expand the menu by clicking the Grafana logo in the
+top left corner. Choose 'Data Sources' from the menu. Then, click 'Add new'
+in the top bar.
+
+![Grafana empty data source page](img/grafana_data_source_empty.png)
+
+Fill in the configuration details for the InfluxDB data source. Save and
+Test Connection to ensure the configuration is correct.
+
+- **Name**: InfluxDB
+- **Default**: Checked
+- **Type**: InfluxDB 0.9.x (Even if you're using InfluxDB 0.10.x)
+- **Url**: https://localhost:8086 (Or the remote URL if you've installed InfluxDB
+on a separate server)
+- **Access**: proxy
+- **Database**: gitlab
+- **User**: admin (Or the username configured when setting up InfluxDB)
+- **Password**: The password configured when you set up InfluxDB
+
+![Grafana data source configurations](img/grafana_data_source_configuration.png)
+
+## Apply retention policies and create continuous queries
+
+If you intend to import the GitLab provided Grafana dashboards, you will need to
+set up the right retention policies and continuous queries. The easiest way of
+doing this is by using the [influxdb-management](https://gitlab.com/gitlab-org/influxdb-management)
+repository.
+
+To use this repository you must first clone it:
+
+```
+git clone https://gitlab.com/gitlab-org/influxdb-management.git
+cd influxdb-management
+```
+
+Next you must install the required dependencies:
+
+```
+gem install bundler
+bundle install
+```
+
+Now you must configure the repository by first copying `.env.example` to `.env`
+and then editing the `.env` file to contain the correct InfluxDB settings. Once
+configured you can simply run `bundle exec rake` and the InfluxDB database will
+be configured for you.
+
+For more information see the [influxdb-management README](https://gitlab.com/gitlab-org/influxdb-management/blob/master/README.md).
+
+## Import Dashboards
+
+You can now import a set of default dashboards that will give you a good
+start on displaying useful information. GitLab has published a set of default
+[Grafana dashboards][grafana-dashboards] to get you started. Clone the
+repository or download a zip/tarball, then follow these steps to import each
+JSON file.
+
+Open the dashboard dropdown menu and click 'Import'
+
+![Grafana dashboard dropdown](img/grafana_dashboard_dropdown.png)
+
+Click 'Choose file' and browse to the location where you downloaded or cloned
+the dashboard repository. Pick one of the JSON files to import.
+
+![Grafana dashboard import](img/grafana_dashboard_import.png)
+
+Once the dashboard is imported, be sure to click save icon in the top bar. If
+you do not save the dashboard after importing it will be removed when you
+navigate away.
+
+![Grafana save icon](img/grafana_save_icon.png)
+
+Repeat this process for each dashboard you wish to import.
+
+Alternatively you can automatically import all the dashboards into your Grafana
+instance. See the README of the [Grafana dashboards][grafana-dashboards]
+repository for more information on this process.
+
+[grafana-dashboards]: https://gitlab.com/gitlab-org/grafana-dashboards
+
+---
+
+Read more on:
+
+- [Introduction to GitLab Performance Monitoring](introduction.md)
+- [GitLab Configuration](gitlab_configuration.md)
+- [InfluxDB Installation/Configuration](influxdb_configuration.md)
+- [InfluxDB Schema](influxdb_schema.md)
diff --git a/doc/administration/monitoring/performance/img/grafana_dashboard_dropdown.png b/doc/administration/monitoring/performance/img/grafana_dashboard_dropdown.png
new file mode 100644
index 00000000000..7e34fad71ce
--- /dev/null
+++ b/doc/administration/monitoring/performance/img/grafana_dashboard_dropdown.png
Binary files differ
diff --git a/doc/administration/monitoring/performance/img/grafana_dashboard_import.png b/doc/administration/monitoring/performance/img/grafana_dashboard_import.png
new file mode 100644
index 00000000000..f97624365c7
--- /dev/null
+++ b/doc/administration/monitoring/performance/img/grafana_dashboard_import.png
Binary files differ
diff --git a/doc/administration/monitoring/performance/img/grafana_data_source_configuration.png b/doc/administration/monitoring/performance/img/grafana_data_source_configuration.png
new file mode 100644
index 00000000000..7d50e4c88c2
--- /dev/null
+++ b/doc/administration/monitoring/performance/img/grafana_data_source_configuration.png
Binary files differ
diff --git a/doc/administration/monitoring/performance/img/grafana_data_source_empty.png b/doc/administration/monitoring/performance/img/grafana_data_source_empty.png
new file mode 100644
index 00000000000..aa39a53acae
--- /dev/null
+++ b/doc/administration/monitoring/performance/img/grafana_data_source_empty.png
Binary files differ
diff --git a/doc/administration/monitoring/performance/img/grafana_save_icon.png b/doc/administration/monitoring/performance/img/grafana_save_icon.png
new file mode 100644
index 00000000000..c740e33cd1c
--- /dev/null
+++ b/doc/administration/monitoring/performance/img/grafana_save_icon.png
Binary files differ
diff --git a/doc/administration/monitoring/performance/img/metrics_gitlab_configuration_settings.png b/doc/administration/monitoring/performance/img/metrics_gitlab_configuration_settings.png
new file mode 100644
index 00000000000..db396423e30
--- /dev/null
+++ b/doc/administration/monitoring/performance/img/metrics_gitlab_configuration_settings.png
Binary files differ
diff --git a/doc/administration/monitoring/performance/influxdb_configuration.md b/doc/administration/monitoring/performance/influxdb_configuration.md
new file mode 100644
index 00000000000..c30cd2950d8
--- /dev/null
+++ b/doc/administration/monitoring/performance/influxdb_configuration.md
@@ -0,0 +1,193 @@
+# InfluxDB Configuration
+
+The default settings provided by [InfluxDB] are not sufficient for a high traffic
+GitLab environment. The settings discussed in this document are based on the
+settings GitLab uses for GitLab.com, depending on your own needs you may need to
+further adjust them.
+
+If you are intending to run InfluxDB on the same server as GitLab, make sure
+you have plenty of RAM since InfluxDB can use quite a bit depending on traffic.
+
+Unless you are going with a budget setup, it's advised to run it separately.
+
+## Requirements
+
+- InfluxDB 0.9.5 or newer
+- A fairly modern version of Linux
+- At least 4GB of RAM
+- At least 10GB of storage for InfluxDB data
+
+Note that the RAM and storage requirements can differ greatly depending on the
+amount of data received/stored. To limit the amount of stored data users can
+look into [InfluxDB Retention Policies][influxdb-retention].
+
+## Installation
+
+Installing InfluxDB is out of the scope of this document. Please refer to the
+[InfluxDB documentation].
+
+## InfluxDB Server Settings
+
+Since InfluxDB has many settings that users may wish to customize themselves
+(e.g. what port to run InfluxDB on), we'll only cover the essentials.
+
+The configuration file in question is usually located at
+`/etc/influxdb/influxdb.conf`. Whenever you make a change in this file,
+InfluxDB needs to be restarted.
+
+### Storage Engine
+
+InfluxDB comes with different storage engines and as of InfluxDB 0.9.5 a new
+storage engine is available, called [TSM Tree]. All users **must** use the new
+`tsm1` storage engine as this [will be the default engine][tsm1-commit] in
+upcoming InfluxDB releases.
+
+Make sure you have the following in your configuration file:
+
+```
+[data]
+ dir = "/var/lib/influxdb/data"
+ engine = "tsm1"
+```
+
+### Admin Panel
+
+Production environments should have the InfluxDB admin panel **disabled**. This
+feature can be disabled by adding the following to your InfluxDB configuration
+file:
+
+```
+[admin]
+ enabled = false
+```
+
+### HTTP
+
+HTTP is required when using the [InfluxDB CLI] or other tools such as Grafana,
+thus it should be enabled. When enabling make sure to _also_ enable
+authentication:
+
+```
+[http]
+ enabled = true
+ auth-enabled = true
+```
+
+_**Note:** Before you enable authentication, you might want to [create an
+admin user](#create-a-new-admin-user)._
+
+### UDP
+
+GitLab writes data to InfluxDB via UDP and thus this must be enabled. Enabling
+UDP can be done using the following settings:
+
+```
+[[udp]]
+ enabled = true
+ bind-address = ":8089"
+ database = "gitlab"
+ batch-size = 1000
+ batch-pending = 5
+ batch-timeout = "1s"
+ read-buffer = 209715200
+```
+
+This does the following:
+
+1. Enable UDP and bind it to port 8089 for all addresses.
+2. Store any data received in the "gitlab" database.
+3. Define a batch of points to be 1000 points in size and allow a maximum of
+ 5 batches _or_ flush them automatically after 1 second.
+4. Define a UDP read buffer size of 200 MB.
+
+One of the most important settings here is the UDP read buffer size as if this
+value is set too low, packets will be dropped. You must also make sure the OS
+buffer size is set to the same value, the default value is almost never enough.
+
+To set the OS buffer size to 200 MB, on Linux you can run the following command:
+
+```bash
+sysctl -w net.core.rmem_max=209715200
+```
+
+To make this permanent, add the following to `/etc/sysctl.conf` and restart the
+server:
+
+```bash
+net.core.rmem_max=209715200
+```
+
+It is **very important** to make sure the buffer sizes are large enough to
+handle all data sent to InfluxDB as otherwise you _will_ lose data. The above
+buffer sizes are based on the traffic for GitLab.com. Depending on the amount of
+traffic, users may be able to use a smaller buffer size, but we highly recommend
+using _at least_ 100 MB.
+
+When enabling UDP, users should take care to not expose the port to the public,
+as doing so will allow anybody to write data into your InfluxDB database (as
+[InfluxDB's UDP protocol][udp] doesn't support authentication). We recommend either
+whitelisting the allowed IP addresses/ranges, or setting up a VLAN and only
+allowing traffic from members of said VLAN.
+
+## Create a new admin user
+
+If you want to [enable authentication](#http), you might want to [create an
+admin user][influx-admin]:
+
+```
+influx -execute "CREATE USER jeff WITH PASSWORD '1234' WITH ALL PRIVILEGES"
+```
+
+## Create the `gitlab` database
+
+Once you get InfluxDB up and running, you need to create a database for GitLab.
+Make sure you have changed the [storage engine](#storage-engine) to `tsm1`
+before creating a database.
+
+_**Note:** If you [created an admin user](#create-a-new-admin-user) and enabled
+[HTTP authentication](#http), remember to append the username (`-username <username>`)
+and password (`-password <password>`) you set earlier to the commands below._
+
+Run the following command to create a database named `gitlab`:
+
+```bash
+influx -execute 'CREATE DATABASE gitlab'
+```
+
+The name **must** be `gitlab`, do not use any other name.
+
+Next, make sure that the database was successfully created:
+
+```bash
+influx -execute 'SHOW DATABASES'
+```
+
+The output should be similar to:
+
+```
+name: databases
+---------------
+name
+_internal
+gitlab
+```
+
+That's it! Now your GitLab instance should send data to InfluxDB.
+
+---
+
+Read more on:
+
+- [Introduction to GitLab Performance Monitoring](introduction.md)
+- [GitLab Configuration](gitlab_configuration.md)
+- [InfluxDB Schema](influxdb_schema.md)
+- [Grafana Install/Configuration](grafana_configuration.md)
+
+[influxdb-retention]: https://docs.influxdata.com/influxdb/v0.9/query_language/database_management/#retention-policy-management
+[influxdb documentation]: https://docs.influxdata.com/influxdb/v0.9/
+[influxdb cli]: https://docs.influxdata.com/influxdb/v0.9/tools/shell/
+[udp]: https://docs.influxdata.com/influxdb/v0.9/write_protocols/udp/
+[influxdb]: https://influxdata.com/time-series-platform/influxdb/
+[tsm tree]: https://influxdata.com/blog/new-storage-engine-time-structured-merge-tree/
+[tsm1-commit]: https://github.com/influxdata/influxdb/commit/15d723dc77651bac83e09e2b1c94be480966cb0d
+[influx-admin]: https://docs.influxdata.com/influxdb/v0.9/administration/authentication_and_authorization/#create-a-new-admin-user
diff --git a/doc/administration/monitoring/performance/influxdb_schema.md b/doc/administration/monitoring/performance/influxdb_schema.md
new file mode 100644
index 00000000000..eff0e29f58d
--- /dev/null
+++ b/doc/administration/monitoring/performance/influxdb_schema.md
@@ -0,0 +1,97 @@
+# InfluxDB Schema
+
+The following measurements are currently stored in InfluxDB:
+
+- `PROCESS_file_descriptors`
+- `PROCESS_gc_statistics`
+- `PROCESS_memory_usage`
+- `PROCESS_method_calls`
+- `PROCESS_object_counts`
+- `PROCESS_transactions`
+- `PROCESS_views`
+- `events`
+
+Here, `PROCESS` is replaced with either `rails` or `sidekiq` depending on the
+process type. In all series, any form of duration is stored in milliseconds.
+
+## PROCESS_file_descriptors
+
+This measurement contains the number of open file descriptors over time. The
+value field `value` contains the number of descriptors.
+
+## PROCESS_gc_statistics
+
+This measurement contains Ruby garbage collection statistics such as the amount
+of minor/major GC runs (relative to the last sampling interval), the time spent
+in garbage collection cycles, and all fields/values returned by `GC.stat`.
+
+## PROCESS_memory_usage
+
+This measurement contains the process' memory usage (in bytes) over time. The
+value field `value` contains the number of bytes.
+
+## PROCESS_method_calls
+
+This measurement contains the methods called during a transaction along with
+their duration, and a name of the transaction action that invoked the method (if
+available). The method call duration is stored in the value field `duration`,
+while the method name is stored in the tag `method`. The tag `action` contains
+the full name of the transaction action. Both the `method` and `action` fields
+are in the following format:
+
+```
+ClassName#method_name
+```
+
+For example, a method called by the `show` method in the `UsersController` class
+would have `action` set to `UsersController#show`.
+
+## PROCESS_object_counts
+
+This measurement is used to store retained Ruby objects (per class) and the
+amount of retained objects. The number of objects is stored in the `count` value
+field while the class name is stored in the `type` tag.
+
+## PROCESS_transactions
+
+This measurement is used to store basic transaction details such as the time it
+took to complete a transaction, how much time was spent in SQL queries, etc. The
+following value fields are available:
+
+| Value | Description |
+| ----- | ----------- |
+| `duration` | The total duration of the transaction |
+| `allocated_memory` | The amount of bytes allocated while the transaction was running. This value is only reliable when using single-threaded application servers |
+| `method_duration` | The total time spent in method calls |
+| `sql_duration` | The total time spent in SQL queries |
+| `view_duration` | The total time spent in views |
+
+## PROCESS_views
+
+This measurement is used to store view rendering timings for a transaction. The
+following value fields are available:
+
+| Value | Description |
+| ----- | ----------- |
+| `duration` | The rendering time of the view |
+| `view` | The path of the view, relative to the application's root directory |
+
+The `action` tag contains the action name of the transaction that rendered the
+view.
+
+## events
+
+This measurement is used to store generic events such as the number of Git
+pushes, Emails sent, etc. Each point in this measurement has a single value
+field called `count`. The value of this field is simply set to `1`. Each point
+also has at least one tag: `event`. This tag's value is set to the event name.
+Depending on the event type additional tags may be available as well.
+
+---
+
+Read more on:
+
+- [Introduction to GitLab Performance Monitoring](introduction.md)
+- [GitLab Configuration](gitlab_configuration.md)
+- [InfluxDB Configuration](influxdb_configuration.md)
+- [Grafana Install/Configuration](grafana_configuration.md)
diff --git a/doc/administration/monitoring/performance/introduction.md b/doc/administration/monitoring/performance/introduction.md
new file mode 100644
index 00000000000..79904916b7e
--- /dev/null
+++ b/doc/administration/monitoring/performance/introduction.md
@@ -0,0 +1,65 @@
+# GitLab Performance Monitoring
+
+GitLab comes with its own application performance measuring system as of GitLab
+8.4, simply called "GitLab Performance Monitoring". GitLab Performance Monitoring is available in both the
+Community and Enterprise editions.
+
+Apart from this introduction, you are advised to read through the following
+documents in order to understand and properly configure GitLab Performance Monitoring:
+
+- [GitLab Configuration](gitlab_configuration.md)
+- [InfluxDB Install/Configuration](influxdb_configuration.md)
+- [InfluxDB Schema](influxdb_schema.md)
+- [Grafana Install/Configuration](grafana_configuration.md)
+
+## Introduction to GitLab Performance Monitoring
+
+GitLab Performance Monitoring makes it possible to measure a wide variety of statistics
+including (but not limited to):
+
+- The time it took to complete a transaction (a web request or Sidekiq job).
+- The time spent in running SQL queries and rendering HAML views.
+- The time spent executing (instrumented) Ruby methods.
+- Ruby object allocations, and retained objects in particular.
+- System statistics such as the process' memory usage and open file descriptors.
+- Ruby garbage collection statistics.
+
+Metrics data is written to [InfluxDB][influxdb] over [UDP][influxdb-udp]. Stored
+data can be visualized using [Grafana][grafana] or any other application that
+supports reading data from InfluxDB. Alternatively data can be queried using the
+InfluxDB CLI.
+
+## Metric Types
+
+Two types of metrics are collected:
+
+1. Transaction specific metrics.
+1. Sampled metrics, collected at a certain interval in a separate thread.
+
+### Transaction Metrics
+
+Transaction metrics are metrics that can be associated with a single
+transaction. This includes statistics such as the transaction duration, timings
+of any executed SQL queries, time spent rendering HAML views, etc. These metrics
+are collected for every Rack request and Sidekiq job processed.
+
+### Sampled Metrics
+
+Sampled metrics are metrics that can't be associated with a single transaction.
+Examples include garbage collection statistics and retained Ruby objects. These
+metrics are collected at a regular interval. This interval is made up out of two
+parts:
+
+1. A user defined interval.
+1. A randomly generated offset added on top of the interval, the same offset
+ can't be used twice in a row.
+
+The actual interval can be anywhere between a half of the defined interval and a
+half above the interval. For example, for a user defined interval of 15 seconds
+the actual interval can be anywhere between 7.5 and 22.5. The interval is
+re-generated for every sampling run instead of being generated once and re-used
+for the duration of the process' lifetime.
+
+[influxdb]: https://influxdata.com/time-series-platform/influxdb/
+[influxdb-udp]: https://docs.influxdata.com/influxdb/v0.9/write_protocols/udp/
+[grafana]: http://grafana.org/
diff --git a/doc/administration/operations.md b/doc/administration/operations.md
new file mode 100644
index 00000000000..4b582d16b64
--- /dev/null
+++ b/doc/administration/operations.md
@@ -0,0 +1,6 @@
+# GitLab operations
+
+- [Sidekiq MemoryKiller](operations/sidekiq_memory_killer.md)
+- [Cleaning up Redis sessions](operations/cleaning_up_redis_sessions.md)
+- [Understanding Unicorn and unicorn-worker-killer](operations/unicorn.md)
+- [Moving repositories to a new location](operations/moving_repositories.md)
diff --git a/doc/administration/operations/cleaning_up_redis_sessions.md b/doc/administration/operations/cleaning_up_redis_sessions.md
new file mode 100644
index 00000000000..93521e976d5
--- /dev/null
+++ b/doc/administration/operations/cleaning_up_redis_sessions.md
@@ -0,0 +1,52 @@
+# Cleaning up stale Redis sessions
+
+Since version 6.2, GitLab stores web user sessions as key-value pairs in Redis.
+Prior to GitLab 7.3, user sessions did not automatically expire from Redis. If
+you have been running a large GitLab server (thousands of users) since before
+GitLab 7.3 we recommend cleaning up stale sessions to compact the Redis
+database after you upgrade to GitLab 7.3. You can also perform a cleanup while
+still running GitLab 7.2 or older, but in that case new stale sessions will
+start building up again after you clean up.
+
+In GitLab versions prior to 7.3.0, the session keys in Redis are 16-byte
+hexadecimal values such as '976aa289e2189b17d7ef525a6702ace9'. Starting with
+GitLab 7.3.0, the keys are
+prefixed with 'session:gitlab:', so they would look like
+'session:gitlab:976aa289e2189b17d7ef525a6702ace9'. Below we describe how to
+remove the keys in the old format.
+
+First we define a shell function with the proper Redis connection details.
+
+```
+rcli() {
+ # This example works for Omnibus installations of GitLab 7.3 or newer. For an
+ # installation from source you will have to change the socket path and the
+ # path to redis-cli.
+ sudo /opt/gitlab/embedded/bin/redis-cli -s /var/opt/gitlab/redis/redis.socket "$@"
+}
+
+# test the new shell function; the response should be PONG
+rcli ping
+```
+
+Now we do a search to see if there are any session keys in the old format for
+us to clean up.
+
+```
+# returns the number of old-format session keys in Redis
+rcli keys '*' | grep '^[a-f0-9]\{32\}$' | wc -l
+```
+
+If the number is larger than zero, you can proceed to expire the keys from
+Redis. If the number is zero there is nothing to clean up.
+
+```
+# Tell Redis to expire each matched key after 600 seconds.
+rcli keys '*' | grep '^[a-f0-9]\{32\}$' | awk '{ print "expire", $0, 600 }' | rcli
+# This will print '(integer) 1' for each key that gets expired.
+```
+
+Over the next 15 minutes (10 minutes expiry time plus 5 minutes Redis
+background save interval) your Redis database will be compacted. If you are
+still using GitLab 7.2, users who are not clicking around in GitLab during the
+10 minute expiry window will be signed out of GitLab.
diff --git a/doc/administration/operations/moving_repositories.md b/doc/administration/operations/moving_repositories.md
new file mode 100644
index 00000000000..54adb99386a
--- /dev/null
+++ b/doc/administration/operations/moving_repositories.md
@@ -0,0 +1,180 @@
+# Moving repositories managed by GitLab
+
+Sometimes you need to move all repositories managed by GitLab to
+another filesystem or another server. In this document we will look
+at some of the ways you can copy all your repositories from
+`/var/opt/gitlab/git-data/repositories` to `/mnt/gitlab/repositories`.
+
+We will look at three scenarios: the target directory is empty, the
+target directory contains an outdated copy of the repositories, and
+how to deal with thousands of repositories.
+
+**Each of the approaches we list can/will overwrite data in the
+target directory `/mnt/gitlab/repositories`. Do not mix up the
+source and the target.**
+
+## Target directory is empty: use a tar pipe
+
+If the target directory `/mnt/gitlab/repositories` is empty the
+simplest thing to do is to use a tar pipe. This method has low
+overhead and tar is almost always already installed on your system.
+However, it is not possible to resume an interrupted tar pipe: if
+that happens then all data must be copied again.
+
+```
+# As the git user
+tar -C /var/opt/gitlab/git-data/repositories -cf - -- . |\
+ tar -C /mnt/gitlab/repositories -xf -
+```
+
+If you want to see progress, replace `-xf` with `-xvf`.
+
+### Tar pipe to another server
+
+You can also use a tar pipe to copy data to another server. If your
+'git' user has SSH access to the newserver as 'git@newserver', you
+can pipe the data through SSH.
+
+```
+# As the git user
+tar -C /var/opt/gitlab/git-data/repositories -cf - -- . |\
+ ssh git@newserver tar -C /mnt/gitlab/repositories -xf -
+```
+
+If you want to compress the data before it goes over the network
+(which will cost you CPU cycles) you can replace `ssh` with `ssh -C`.
+
+## The target directory contains an outdated copy of the repositories: use rsync
+
+If the target directory already contains a partial / outdated copy
+of the repositories it may be wasteful to copy all the data again
+with tar. In this scenario it is better to use rsync. This utility
+is either already installed on your system or easily installable
+via apt, yum etc.
+
+```
+# As the 'git' user
+rsync -a --delete /var/opt/gitlab/git-data/repositories/. \
+ /mnt/gitlab/repositories
+```
+
+The `/.` in the command above is very important, without it you can
+easily get the wrong directory structure in the target directory.
+If you want to see progress, replace `-a` with `-av`.
+
+### Single rsync to another server
+
+If the 'git' user on your source system has SSH access to the target
+server you can send the repositories over the network with rsync.
+
+```
+# As the 'git' user
+rsync -a --delete /var/opt/gitlab/git-data/repositories/. \
+ git@newserver:/mnt/gitlab/repositories
+```
+
+## Thousands of Git repositories: use one rsync per repository
+
+Every time you start an rsync job it has to inspect all files in
+the source directory, all files in the target directory, and then
+decide what files to copy or not. If the source or target directory
+has many contents this startup phase of rsync can become a burden
+for your GitLab server. In cases like this you can make rsync's
+life easier by dividing its work in smaller pieces, and sync one
+repository at a time.
+
+In addition to rsync we will use [GNU
+Parallel](http://www.gnu.org/software/parallel/). This utility is
+not included in GitLab so you need to install it yourself with apt
+or yum. Also note that the GitLab scripts we used below were added
+in GitLab 8.1.
+
+** This process does not clean up repositories at the target location that no
+longer exist at the source. ** If you start using your GitLab instance with
+`/mnt/gitlab/repositories`, you need to run `gitlab-rake gitlab:cleanup:repos`
+after switching to the new repository storage directory.
+
+### Parallel rsync for all repositories known to GitLab
+
+This will sync repositories with 10 rsync processes at a time. We keep
+track of progress so that the transfer can be restarted if necessary.
+
+First we create a new directory, owned by 'git', to hold transfer
+logs. We assume the directory is empty before we start the transfer
+procedure, and that we are the only ones writing files in it.
+
+```
+# Omnibus
+sudo mkdir /var/opt/gitlab/transfer-logs
+sudo chown git:git /var/opt/gitlab/transfer-logs
+
+# Source
+sudo -u git -H mkdir /home/git/transfer-logs
+```
+
+We seed the process with a list of the directories we want to copy.
+
+```
+# Omnibus
+sudo -u git sh -c 'gitlab-rake gitlab:list_repos > /var/opt/gitlab/transfer-logs/all-repos-$(date +%s).txt'
+
+# Source
+cd /home/git/gitlab
+sudo -u git -H sh -c 'bundle exec rake gitlab:list_repos > /home/git/transfer-logs/all-repos-$(date +%s).txt'
+```
+
+Now we can start the transfer. The command below is idempotent, and
+the number of jobs done by GNU Parallel should converge to zero. If it
+does not some repositories listed in all-repos-1234.txt may have been
+deleted/renamed before they could be copied.
+
+```
+# Omnibus
+sudo -u git sh -c '
+cat /var/opt/gitlab/transfer-logs/* | sort | uniq -u |\
+ /usr/bin/env JOBS=10 \
+ /opt/gitlab/embedded/service/gitlab-rails/bin/parallel-rsync-repos \
+ /var/opt/gitlab/transfer-logs/success-$(date +%s).log \
+ /var/opt/gitlab/git-data/repositories \
+ /mnt/gitlab/repositories
+'
+
+# Source
+cd /home/git/gitlab
+sudo -u git -H sh -c '
+cat /home/git/transfer-logs/* | sort | uniq -u |\
+ /usr/bin/env JOBS=10 \
+ bin/parallel-rsync-repos \
+ /home/git/transfer-logs/success-$(date +%s).log \
+ /home/git/repositories \
+ /mnt/gitlab/repositories
+`
+```
+
+### Parallel rsync only for repositories with recent activity
+
+Suppose you have already done one sync that started after 2015-10-1 12:00 UTC.
+Then you might only want to sync repositories that were changed via GitLab
+_after_ that time. You can use the 'SINCE' variable to tell 'rake
+gitlab:list_repos' to only print repositories with recent activity.
+
+```
+# Omnibus
+sudo gitlab-rake gitlab:list_repos SINCE='2015-10-1 12:00 UTC' |\
+ sudo -u git \
+ /usr/bin/env JOBS=10 \
+ /opt/gitlab/embedded/service/gitlab-rails/bin/parallel-rsync-repos \
+ success-$(date +%s).log \
+ /var/opt/gitlab/git-data/repositories \
+ /mnt/gitlab/repositories
+
+# Source
+cd /home/git/gitlab
+sudo -u git -H bundle exec rake gitlab:list_repos SINCE='2015-10-1 12:00 UTC' |\
+ sudo -u git -H \
+ /usr/bin/env JOBS=10 \
+ bin/parallel-rsync-repos \
+ success-$(date +%s).log \
+ /home/git/repositories \
+ /mnt/gitlab/repositories
+```
diff --git a/doc/administration/operations/sidekiq_memory_killer.md b/doc/administration/operations/sidekiq_memory_killer.md
new file mode 100644
index 00000000000..b5e78348989
--- /dev/null
+++ b/doc/administration/operations/sidekiq_memory_killer.md
@@ -0,0 +1,40 @@
+# Sidekiq MemoryKiller
+
+The GitLab Rails application code suffers from memory leaks. For web requests
+this problem is made manageable using
+[unicorn-worker-killer](https://github.com/kzk/unicorn-worker-killer) which
+restarts Unicorn worker processes in between requests when needed. The Sidekiq
+MemoryKiller applies the same approach to the Sidekiq processes used by GitLab
+to process background jobs.
+
+Unlike unicorn-worker-killer, which is enabled by default for all GitLab
+installations since GitLab 6.4, the Sidekiq MemoryKiller is enabled by default
+_only_ for Omnibus packages. The reason for this is that the MemoryKiller
+relies on Runit to restart Sidekiq after a memory-induced shutdown and GitLab
+installations from source do not all use Runit or an equivalent.
+
+With the default settings, the MemoryKiller will cause a Sidekiq restart no
+more often than once every 15 minutes, with the restart causing about one
+minute of delay for incoming background jobs.
+
+## Configuring the MemoryKiller
+
+The MemoryKiller is controlled using environment variables.
+
+- `SIDEKIQ_MEMORY_KILLER_MAX_RSS`: if this variable is set, and its value is
+ greater than 0, then after each Sidekiq job, the MemoryKiller will check the
+ RSS of the Sidekiq process that executed the job. If the RSS of the Sidekiq
+ process (expressed in kilobytes) exceeds SIDEKIQ_MEMORY_KILLER_MAX_RSS, a
+ delayed shutdown is triggered. The default value for Omnibus packages is set
+ [in the omnibus-gitlab
+ repository](https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/files/gitlab-cookbooks/gitlab/attributes/default.rb).
+- `SIDEKIQ_MEMORY_KILLER_GRACE_TIME`: defaults 900 seconds (15 minutes). When
+ a shutdown is triggered, the Sidekiq process will keep working normally for
+ another 15 minutes.
+- `SIDEKIQ_MEMORY_KILLER_SHUTDOWN_WAIT`: defaults to 30 seconds. When the grace
+ time has expired, the MemoryKiller tells Sidekiq to stop accepting new jobs.
+ Existing jobs get 30 seconds to finish. After that, the MemoryKiller tells
+ Sidekiq to shut down, and an external supervision mechanism (e.g. Runit) must
+ restart Sidekiq.
+- `SIDEKIQ_MEMORY_KILLER_SHUTDOWN_SIGNAL`: defaults to `SIGKILL`. The name of
+ the final signal sent to the Sidekiq process when we want it to shut down.
diff --git a/doc/administration/operations/unicorn.md b/doc/administration/operations/unicorn.md
new file mode 100644
index 00000000000..bad61151bda
--- /dev/null
+++ b/doc/administration/operations/unicorn.md
@@ -0,0 +1,86 @@
+# Understanding Unicorn and unicorn-worker-killer
+
+## Unicorn
+
+GitLab uses [Unicorn](http://unicorn.bogomips.org/), a pre-forking Ruby web
+server, to handle web requests (web browsers and Git HTTP clients). Unicorn is
+a daemon written in Ruby and C that can load and run a Ruby on Rails
+application; in our case the Rails application is GitLab Community Edition or
+GitLab Enterprise Edition.
+
+Unicorn has a multi-process architecture to make better use of available CPU
+cores (processes can run on different cores) and to have stronger fault
+tolerance (most failures stay isolated in only one process and cannot take down
+GitLab entirely). On startup, the Unicorn 'master' process loads a clean Ruby
+environment with the GitLab application code, and then spawns 'workers' which
+inherit this clean initial environment. The 'master' never handles any
+requests, that is left to the workers. The operating system network stack
+queues incoming requests and distributes them among the workers.
+
+In a perfect world, the master would spawn its pool of workers once, and then
+the workers handle incoming web requests one after another until the end of
+time. In reality, worker processes can crash or time out: if the master notices
+that a worker takes too long to handle a request it will terminate the worker
+process with SIGKILL ('kill -9'). No matter how the worker process ended, the
+master process will replace it with a new 'clean' process again. Unicorn is
+designed to be able to replace 'crashed' workers without dropping user
+requests.
+
+This is what a Unicorn worker timeout looks like in `unicorn_stderr.log`. The
+master process has PID 56227 below.
+
+```
+[2015-06-05T10:58:08.660325 #56227] ERROR -- : worker=10 PID:53009 timeout (61s > 60s), killing
+[2015-06-05T10:58:08.699360 #56227] ERROR -- : reaped #<Process::Status: pid 53009 SIGKILL (signal 9)> worker=10
+[2015-06-05T10:58:08.708141 #62538] INFO -- : worker=10 spawned pid=62538
+[2015-06-05T10:58:08.708824 #62538] INFO -- : worker=10 ready
+```
+
+### Tunables
+
+The main tunables for Unicorn are the number of worker processes and the
+request timeout after which the Unicorn master terminates a worker process.
+See the [omnibus-gitlab Unicorn settings
+documentation](https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/doc/settings/unicorn.md)
+if you want to adjust these settings.
+
+## unicorn-worker-killer
+
+GitLab has memory leaks. These memory leaks manifest themselves in long-running
+processes, such as Unicorn workers. (The Unicorn master process is not known to
+leak memory, probably because it does not handle user requests.)
+
+To make these memory leaks manageable, GitLab comes with the
+[unicorn-worker-killer gem](https://github.com/kzk/unicorn-worker-killer). This
+gem [monkey-patches](https://en.wikipedia.org/wiki/Monkey_patch) the Unicorn
+workers to do a memory self-check after every 16 requests. If the memory of the
+Unicorn worker exceeds a pre-set limit then the worker process exits. The
+Unicorn master then automatically replaces the worker process.
+
+This is a robust way to handle memory leaks: Unicorn is designed to handle
+workers that 'crash' so no user requests will be dropped. The
+unicorn-worker-killer gem is designed to only terminate a worker process _in
+between requests_, so no user requests are affected.
+
+This is what a Unicorn worker memory restart looks like in unicorn_stderr.log.
+You see that worker 4 (PID 125918) is inspecting itself and decides to exit.
+The threshold memory value was 254802235 bytes, about 250MB. With GitLab this
+threshold is a random value between 200 and 250 MB. The master process (PID
+117565) then reaps the worker process and spawns a new 'worker 4' with PID
+127549.
+
+```
+[2015-06-05T12:07:41.828374 #125918] WARN -- : #<Unicorn::HttpServer:0x00000002734770>: worker (pid: 125918) exceeds memory limit (256413696 bytes > 254802235 bytes)
+[2015-06-05T12:07:41.828472 #125918] WARN -- : Unicorn::WorkerKiller send SIGQUIT (pid: 125918) alive: 23 sec (trial 1)
+[2015-06-05T12:07:42.025916 #117565] INFO -- : reaped #<Process::Status: pid 125918 exit 0> worker=4
+[2015-06-05T12:07:42.034527 #127549] INFO -- : worker=4 spawned pid=127549
+[2015-06-05T12:07:42.035217 #127549] INFO -- : worker=4 ready
+```
+
+One other thing that stands out in the log snippet above, taken from
+GitLab.com, is that 'worker 4' was serving requests for only 23 seconds. This
+is a normal value for our current GitLab.com setup and traffic.
+
+The high frequency of Unicorn memory restarts on some GitLab sites can be a
+source of confusion for administrators. Usually they are a [red
+herring](https://en.wikipedia.org/wiki/Red_herring).
diff --git a/doc/administration/troubleshooting/debug.md b/doc/administration/troubleshooting/debug.md
index d127d7b85e5..d8dce4388e1 100644
--- a/doc/administration/troubleshooting/debug.md
+++ b/doc/administration/troubleshooting/debug.md
@@ -144,14 +144,14 @@ separate Rails process to debug the issue:
1. Obtain the private token for your user (Profile Settings -> Account).
1. Bring up the GitLab Rails console. For omnibus users, run:
- ````
+ ```
sudo gitlab-rails console
```
1. At the Rails console, run:
```ruby
- [1] pry(main)> app.get '<URL FROM STEP 1>/private_token?<TOKEN FROM STEP 2>'
+ [1] pry(main)> app.get '<URL FROM STEP 2>/?private_token=<TOKEN FROM STEP 3>'
```
For example:
diff --git a/doc/api/README.md b/doc/api/README.md
index 9e907689c80..3fbe5197a21 100644
--- a/doc/api/README.md
+++ b/doc/api/README.md
@@ -17,6 +17,8 @@ following locations:
- [Commits](commits.md)
- [Deployments](deployments.md)
- [Deploy Keys](deploy_keys.md)
+- [Gitignores templates](templates/gitignores.md)
+- [GitLab CI Config templates](templates/gitlab_ci_ymls.md)
- [Groups](groups.md)
- [Group Access Requests](access_requests.md)
- [Group Members](members.md)
@@ -25,7 +27,7 @@ following locations:
- [Labels](labels.md)
- [Merge Requests](merge_requests.md)
- [Milestones](milestones.md)
-- [Open source license templates](licenses.md)
+- [Open source license templates](templates/licenses.md)
- [Namespaces](namespaces.md)
- [Notes](notes.md) (comments)
- [Notification settings](notification_settings.md)
@@ -46,6 +48,7 @@ following locations:
- [Todos](todos.md)
- [Users](users.md)
- [Validate CI configuration](ci/lint.md)
+- [Version](version.md)
### Internal CI API
diff --git a/doc/api/projects.md b/doc/api/projects.md
index 27436a052da..f96bf7f6d63 100644
--- a/doc/api/projects.md
+++ b/doc/api/projects.md
@@ -436,7 +436,7 @@ Parameters:
### Get project events
Get the events for the specified project.
-Sorted from newest to latest
+Sorted from newest to oldest
```
GET /projects/:id/events
diff --git a/doc/api/templates/gitignores.md b/doc/api/templates/gitignores.md
new file mode 100644
index 00000000000..8235be92b12
--- /dev/null
+++ b/doc/api/templates/gitignores.md
@@ -0,0 +1,579 @@
+# Gitignores
+
+## List gitignore templates
+
+Get all gitignore templates.
+
+```
+GET /templates/gitignores
+```
+
+```bash
+curl https://gitlab.example.com/api/v3/templates/gitignores
+```
+
+Example response:
+
+```json
+[
+ {
+ "name": "AppEngine"
+ },
+ {
+ "name": "Laravel"
+ },
+ {
+ "name": "Elisp"
+ },
+ {
+ "name": "SketchUp"
+ },
+ {
+ "name": "Ada"
+ },
+ {
+ "name": "Ruby"
+ },
+ {
+ "name": "Kohana"
+ },
+ {
+ "name": "Nanoc"
+ },
+ {
+ "name": "Erlang"
+ },
+ {
+ "name": "OCaml"
+ },
+ {
+ "name": "Lithium"
+ },
+ {
+ "name": "Fortran"
+ },
+ {
+ "name": "Scala"
+ },
+ {
+ "name": "Node"
+ },
+ {
+ "name": "Fancy"
+ },
+ {
+ "name": "Perl"
+ },
+ {
+ "name": "Zephir"
+ },
+ {
+ "name": "WordPress"
+ },
+ {
+ "name": "Symfony"
+ },
+ {
+ "name": "FuelPHP"
+ },
+ {
+ "name": "DM"
+ },
+ {
+ "name": "Sdcc"
+ },
+ {
+ "name": "Rust"
+ },
+ {
+ "name": "C"
+ },
+ {
+ "name": "Umbraco"
+ },
+ {
+ "name": "Actionscript"
+ },
+ {
+ "name": "Android"
+ },
+ {
+ "name": "Grails"
+ },
+ {
+ "name": "Composer"
+ },
+ {
+ "name": "ExpressionEngine"
+ },
+ {
+ "name": "Gcov"
+ },
+ {
+ "name": "Qt"
+ },
+ {
+ "name": "Phalcon"
+ },
+ {
+ "name": "ArchLinuxPackages"
+ },
+ {
+ "name": "TeX"
+ },
+ {
+ "name": "SCons"
+ },
+ {
+ "name": "Lilypond"
+ },
+ {
+ "name": "CommonLisp"
+ },
+ {
+ "name": "Rails"
+ },
+ {
+ "name": "Mercury"
+ },
+ {
+ "name": "Magento"
+ },
+ {
+ "name": "ChefCookbook"
+ },
+ {
+ "name": "GitBook"
+ },
+ {
+ "name": "C++"
+ },
+ {
+ "name": "Eagle"
+ },
+ {
+ "name": "Go"
+ },
+ {
+ "name": "OpenCart"
+ },
+ {
+ "name": "Scheme"
+ },
+ {
+ "name": "Typo3"
+ },
+ {
+ "name": "SeamGen"
+ },
+ {
+ "name": "Swift"
+ },
+ {
+ "name": "Elm"
+ },
+ {
+ "name": "Unity"
+ },
+ {
+ "name": "Agda"
+ },
+ {
+ "name": "CUDA"
+ },
+ {
+ "name": "VVVV"
+ },
+ {
+ "name": "Finale"
+ },
+ {
+ "name": "LemonStand"
+ },
+ {
+ "name": "Textpattern"
+ },
+ {
+ "name": "Julia"
+ },
+ {
+ "name": "Packer"
+ },
+ {
+ "name": "Scrivener"
+ },
+ {
+ "name": "Dart"
+ },
+ {
+ "name": "Plone"
+ },
+ {
+ "name": "Jekyll"
+ },
+ {
+ "name": "Xojo"
+ },
+ {
+ "name": "LabVIEW"
+ },
+ {
+ "name": "Autotools"
+ },
+ {
+ "name": "KiCad"
+ },
+ {
+ "name": "Prestashop"
+ },
+ {
+ "name": "ROS"
+ },
+ {
+ "name": "Smalltalk"
+ },
+ {
+ "name": "GWT"
+ },
+ {
+ "name": "OracleForms"
+ },
+ {
+ "name": "SugarCRM"
+ },
+ {
+ "name": "Nim"
+ },
+ {
+ "name": "SymphonyCMS"
+ },
+ {
+ "name": "Maven"
+ },
+ {
+ "name": "CFWheels"
+ },
+ {
+ "name": "Python"
+ },
+ {
+ "name": "ZendFramework"
+ },
+ {
+ "name": "CakePHP"
+ },
+ {
+ "name": "Concrete5"
+ },
+ {
+ "name": "PlayFramework"
+ },
+ {
+ "name": "Terraform"
+ },
+ {
+ "name": "Elixir"
+ },
+ {
+ "name": "CMake"
+ },
+ {
+ "name": "Joomla"
+ },
+ {
+ "name": "Coq"
+ },
+ {
+ "name": "Delphi"
+ },
+ {
+ "name": "Haskell"
+ },
+ {
+ "name": "Yii"
+ },
+ {
+ "name": "Java"
+ },
+ {
+ "name": "UnrealEngine"
+ },
+ {
+ "name": "AppceleratorTitanium"
+ },
+ {
+ "name": "CraftCMS"
+ },
+ {
+ "name": "ForceDotCom"
+ },
+ {
+ "name": "ExtJs"
+ },
+ {
+ "name": "MetaProgrammingSystem"
+ },
+ {
+ "name": "D"
+ },
+ {
+ "name": "Objective-C"
+ },
+ {
+ "name": "RhodesRhomobile"
+ },
+ {
+ "name": "R"
+ },
+ {
+ "name": "EPiServer"
+ },
+ {
+ "name": "Yeoman"
+ },
+ {
+ "name": "VisualStudio"
+ },
+ {
+ "name": "Processing"
+ },
+ {
+ "name": "Leiningen"
+ },
+ {
+ "name": "Stella"
+ },
+ {
+ "name": "Opa"
+ },
+ {
+ "name": "Drupal"
+ },
+ {
+ "name": "TurboGears2"
+ },
+ {
+ "name": "Idris"
+ },
+ {
+ "name": "Jboss"
+ },
+ {
+ "name": "CodeIgniter"
+ },
+ {
+ "name": "Qooxdoo"
+ },
+ {
+ "name": "Waf"
+ },
+ {
+ "name": "Sass"
+ },
+ {
+ "name": "Lua"
+ },
+ {
+ "name": "Clojure"
+ },
+ {
+ "name": "IGORPro"
+ },
+ {
+ "name": "Gradle"
+ },
+ {
+ "name": "Archives"
+ },
+ {
+ "name": "SynopsysVCS"
+ },
+ {
+ "name": "Ninja"
+ },
+ {
+ "name": "Tags"
+ },
+ {
+ "name": "OSX"
+ },
+ {
+ "name": "Dreamweaver"
+ },
+ {
+ "name": "CodeKit"
+ },
+ {
+ "name": "NotepadPP"
+ },
+ {
+ "name": "VisualStudioCode"
+ },
+ {
+ "name": "Mercurial"
+ },
+ {
+ "name": "BricxCC"
+ },
+ {
+ "name": "DartEditor"
+ },
+ {
+ "name": "Eclipse"
+ },
+ {
+ "name": "Cloud9"
+ },
+ {
+ "name": "TortoiseGit"
+ },
+ {
+ "name": "NetBeans"
+ },
+ {
+ "name": "GPG"
+ },
+ {
+ "name": "Espresso"
+ },
+ {
+ "name": "Redcar"
+ },
+ {
+ "name": "Xcode"
+ },
+ {
+ "name": "Matlab"
+ },
+ {
+ "name": "LyX"
+ },
+ {
+ "name": "SlickEdit"
+ },
+ {
+ "name": "Dropbox"
+ },
+ {
+ "name": "CVS"
+ },
+ {
+ "name": "Calabash"
+ },
+ {
+ "name": "JDeveloper"
+ },
+ {
+ "name": "Vagrant"
+ },
+ {
+ "name": "IPythonNotebook"
+ },
+ {
+ "name": "TextMate"
+ },
+ {
+ "name": "Ensime"
+ },
+ {
+ "name": "WebMethods"
+ },
+ {
+ "name": "VirtualEnv"
+ },
+ {
+ "name": "Emacs"
+ },
+ {
+ "name": "Momentics"
+ },
+ {
+ "name": "JetBrains"
+ },
+ {
+ "name": "SublimeText"
+ },
+ {
+ "name": "Kate"
+ },
+ {
+ "name": "ModelSim"
+ },
+ {
+ "name": "Redis"
+ },
+ {
+ "name": "KDevelop4"
+ },
+ {
+ "name": "Bazaar"
+ },
+ {
+ "name": "Linux"
+ },
+ {
+ "name": "Windows"
+ },
+ {
+ "name": "XilinxISE"
+ },
+ {
+ "name": "Lazarus"
+ },
+ {
+ "name": "EiffelStudio"
+ },
+ {
+ "name": "Anjuta"
+ },
+ {
+ "name": "Vim"
+ },
+ {
+ "name": "Otto"
+ },
+ {
+ "name": "MicrosoftOffice"
+ },
+ {
+ "name": "LibreOffice"
+ },
+ {
+ "name": "SBT"
+ },
+ {
+ "name": "MonoDevelop"
+ },
+ {
+ "name": "SVN"
+ },
+ {
+ "name": "FlexBuilder"
+ }
+]
+```
+
+## Single gitignore template
+
+Get a single gitignore template.
+
+```
+GET /templates/gitignores/:key
+```
+
+| Attribute | Type | Required | Description |
+| ---------- | ------ | -------- | ----------- |
+| `key` | string | yes | The key of the gitignore template |
+
+```bash
+curl https://gitlab.example.com/api/v3/templates/gitignores/Ruby
+```
+
+Example response:
+
+```json
+{
+ "name": "Ruby",
+ "content": "*.gem\n*.rbc\n/.config\n/coverage/\n/InstalledFiles\n/pkg/\n/spec/reports/\n/spec/examples.txt\n/test/tmp/\n/test/version_tmp/\n/tmp/\n\n# Used by dotenv library to load environment variables.\n# .env\n\n## Specific to RubyMotion:\n.dat*\n.repl_history\nbuild/\n*.bridgesupport\nbuild-iPhoneOS/\nbuild-iPhoneSimulator/\n\n## Specific to RubyMotion (use of CocoaPods):\n#\n# We recommend against adding the Pods directory to your .gitignore. However\n# you should judge for yourself, the pros and cons are mentioned at:\n# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control\n#\n# vendor/Pods/\n\n## Documentation cache and generated files:\n/.yardoc/\n/_yardoc/\n/doc/\n/rdoc/\n\n## Environment normalization:\n/.bundle/\n/vendor/bundle\n/lib/bundler/man/\n\n# for a library or gem, you might want to ignore these files since the code is\n# intended to run in multiple environments; otherwise, check them in:\n# Gemfile.lock\n# .ruby-version\n# .ruby-gemset\n\n# unless supporting rvm < 1.11.0 or doing something fancy, ignore this:\n.rvmrc\n"
+}
+```
diff --git a/doc/api/templates/gitlab_ci_ymls.md b/doc/api/templates/gitlab_ci_ymls.md
new file mode 100644
index 00000000000..e120016fbe6
--- /dev/null
+++ b/doc/api/templates/gitlab_ci_ymls.md
@@ -0,0 +1,120 @@
+# GitLab CI YMLs
+
+## List GitLab CI YML templates
+
+Get all GitLab CI YML templates.
+
+```
+GET /templates/gitlab_ci_ymls
+```
+
+```bash
+curl https://gitlab.example.com/api/v3/templates/gitlab_ci_ymls
+```
+
+Example response:
+
+```json
+[
+ {
+ "name": "C++"
+ },
+ {
+ "name": "Docker"
+ },
+ {
+ "name": "Elixir"
+ },
+ {
+ "name": "LaTeX"
+ },
+ {
+ "name": "Grails"
+ },
+ {
+ "name": "Rust"
+ },
+ {
+ "name": "Nodejs"
+ },
+ {
+ "name": "Ruby"
+ },
+ {
+ "name": "Scala"
+ },
+ {
+ "name": "Maven"
+ },
+ {
+ "name": "Harp"
+ },
+ {
+ "name": "Pelican"
+ },
+ {
+ "name": "Hyde"
+ },
+ {
+ "name": "Nanoc"
+ },
+ {
+ "name": "Octopress"
+ },
+ {
+ "name": "JBake"
+ },
+ {
+ "name": "HTML"
+ },
+ {
+ "name": "Hugo"
+ },
+ {
+ "name": "Metalsmith"
+ },
+ {
+ "name": "Hexo"
+ },
+ {
+ "name": "Lektor"
+ },
+ {
+ "name": "Doxygen"
+ },
+ {
+ "name": "Brunch"
+ },
+ {
+ "name": "Jekyll"
+ },
+ {
+ "name": "Middleman"
+ }
+]
+```
+
+## Single GitLab CI YML template
+
+Get a single GitLab CI YML template.
+
+```
+GET /templates/gitlab_ci_ymls/:key
+```
+
+| Attribute | Type | Required | Description |
+| ---------- | ------ | -------- | ----------- |
+| `key` | string | yes | The key of the GitLab CI YML template |
+
+```bash
+curl https://gitlab.example.com/api/v3/templates/gitlab_ci_ymls/Ruby
+```
+
+Example response:
+
+```json
+{
+ "name": "Ruby",
+ "content": "# This file is a template, and might need editing before it works on your project.\n# Official language image. Look for the different tagged releases at:\n# https://hub.docker.com/r/library/ruby/tags/\nimage: \"ruby:2.3\"\n\n# Pick zero or more services to be used on all builds.\n# Only needed when using a docker container to run your tests in.\n# Check out: http://docs.gitlab.com/ce/ci/docker/using_docker_images.html#what-is-service\nservices:\n - mysql:latest\n - redis:latest\n - postgres:latest\n\nvariables:\n POSTGRES_DB: database_name\n\n# Cache gems in between builds\ncache:\n paths:\n - vendor/ruby\n\n# This is a basic example for a gem or script which doesn't use\n# services such as redis or postgres\nbefore_script:\n - ruby -v # Print out ruby version for debugging\n # Uncomment next line if your rails app needs a JS runtime:\n # - apt-get update -q && apt-get install nodejs -yqq\n - gem install bundler --no-ri --no-rdoc # Bundler is not installed with the image\n - bundle install -j $(nproc) --path vendor # Install dependencies into ./vendor/ruby\n\n# Optional - Delete if not using `rubocop`\nrubocop:\n script:\n - rubocop\n\nrspec:\n script:\n - rspec spec\n\nrails:\n variables:\n DATABASE_URL: \"postgresql://postgres:postgres@postgres:5432/$POSTGRES_DB\"\n script:\n - bundle exec rake db:migrate\n - bundle exec rake db:seed\n - bundle exec rake test\n"
+}
+```
diff --git a/doc/api/licenses.md b/doc/api/templates/licenses.md
index ed26d1fb7fb..ae7218cf1bd 100644
--- a/doc/api/licenses.md
+++ b/doc/api/templates/licenses.md
@@ -5,7 +5,7 @@
Get all license templates.
```
-GET /licenses
+GET /templates/licenses
```
| Attribute | Type | Required | Description |
@@ -13,7 +13,7 @@ GET /licenses
| `popular` | boolean | no | If passed, returns only popular licenses |
```bash
-curl https://gitlab.example.com/api/v3/licenses?popular=1
+curl https://gitlab.example.com/api/v3/templates/licenses?popular=1
```
Example response:
@@ -102,7 +102,7 @@ Get a single license template. You can pass parameters to replace the license
placeholder.
```
-GET /licenses/:key
+GET /templates/licenses/:key
```
| Attribute | Type | Required | Description |
@@ -116,7 +116,7 @@ If you omit the `fullname` parameter but authenticate your request, the name of
the authenticated user will be used to replace the copyright holder placeholder.
```bash
-curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/licenses/mit?project=My+Cool+Project
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/templates/licenses/mit?project=My+Cool+Project
```
Example response:
diff --git a/doc/api/users.md b/doc/api/users.md
index 9be4f2e6ec3..a52b2d51d78 100644
--- a/doc/api/users.md
+++ b/doc/api/users.md
@@ -627,3 +627,149 @@ Parameters:
Will return `200 OK` on success, `404 User Not Found` is user cannot be found or
`403 Forbidden` when trying to unblock a user blocked by LDAP synchronization.
+
+### Get user contribution events
+
+Get the contribution events for the specified user, sorted from newest to oldest.
+
+```
+GET /users/:id/events
+```
+
+Parameters:
+
+| Attribute | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `id` | integer | yes | The ID of the user |
+
+```bash
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/user/:id/events
+```
+
+Example response:
+
+```json
+[
+ {
+ "title": null,
+ "project_id": 15,
+ "action_name": "closed",
+ "target_id": 830,
+ "target_type": "Issue",
+ "author_id": 1,
+ "data": null,
+ "target_title": "Public project search field",
+ "author": {
+ "name": "Dmitriy Zaporozhets",
+ "username": "root",
+ "id": 1,
+ "state": "active",
+ "avatar_url": "http://localhost:3000/uploads/user/avatar/1/fox_avatar.png",
+ "web_url": "http://localhost:3000/u/root"
+ },
+ "author_username": "root"
+ },
+ {
+ "title": null,
+ "project_id": 15,
+ "action_name": "opened",
+ "target_id": null,
+ "target_type": null,
+ "author_id": 1,
+ "author": {
+ "name": "Dmitriy Zaporozhets",
+ "username": "root",
+ "id": 1,
+ "state": "active",
+ "avatar_url": "http://localhost:3000/uploads/user/avatar/1/fox_avatar.png",
+ "web_url": "http://localhost:3000/u/root"
+ },
+ "author_username": "john",
+ "data": {
+ "before": "50d4420237a9de7be1304607147aec22e4a14af7",
+ "after": "c5feabde2d8cd023215af4d2ceeb7a64839fc428",
+ "ref": "refs/heads/master",
+ "user_id": 1,
+ "user_name": "Dmitriy Zaporozhets",
+ "repository": {
+ "name": "gitlabhq",
+ "url": "git@dev.gitlab.org:gitlab/gitlabhq.git",
+ "description": "GitLab: self hosted Git management software. \r\nDistributed under the MIT License.",
+ "homepage": "https://dev.gitlab.org/gitlab/gitlabhq"
+ },
+ "commits": [
+ {
+ "id": "c5feabde2d8cd023215af4d2ceeb7a64839fc428",
+ "message": "Add simple search to projects in public area",
+ "timestamp": "2013-05-13T18:18:08+00:00",
+ "url": "https://dev.gitlab.org/gitlab/gitlabhq/commit/c5feabde2d8cd023215af4d2ceeb7a64839fc428",
+ "author": {
+ "name": "Dmitriy Zaporozhets",
+ "email": "dmitriy.zaporozhets@gmail.com"
+ }
+ }
+ ],
+ "total_commits_count": 1
+ },
+ "target_title": null
+ },
+ {
+ "title": null,
+ "project_id": 15,
+ "action_name": "closed",
+ "target_id": 840,
+ "target_type": "Issue",
+ "author_id": 1,
+ "data": null,
+ "target_title": "Finish & merge Code search PR",
+ "author": {
+ "name": "Dmitriy Zaporozhets",
+ "username": "root",
+ "id": 1,
+ "state": "active",
+ "avatar_url": "http://localhost:3000/uploads/user/avatar/1/fox_avatar.png",
+ "web_url": "http://localhost:3000/u/root"
+ },
+ "author_username": "root"
+ },
+ {
+ "title": null,
+ "project_id": 15,
+ "action_name": "commented on",
+ "target_id": 1312,
+ "target_type": "Note",
+ "author_id": 1,
+ "data": null,
+ "target_title": null,
+ "created_at": "2015-12-04T10:33:58.089Z",
+ "note": {
+ "id": 1312,
+ "body": "What an awesome day!",
+ "attachment": null,
+ "author": {
+ "name": "Dmitriy Zaporozhets",
+ "username": "root",
+ "id": 1,
+ "state": "active",
+ "avatar_url": "http://localhost:3000/uploads/user/avatar/1/fox_avatar.png",
+ "web_url": "http://localhost:3000/u/root"
+ },
+ "created_at": "2015-12-04T10:33:56.698Z",
+ "system": false,
+ "upvote": false,
+ "downvote": false,
+ "noteable_id": 377,
+ "noteable_type": "Issue"
+ },
+ "author": {
+ "name": "Dmitriy Zaporozhets",
+ "username": "root",
+ "id": 1,
+ "state": "active",
+ "avatar_url": "http://localhost:3000/uploads/user/avatar/1/fox_avatar.png",
+ "web_url": "http://localhost:3000/u/root"
+ },
+ "author_username": "root"
+ }
+]
+```
diff --git a/doc/api/version.md b/doc/api/version.md
new file mode 100644
index 00000000000..287d17cf97f
--- /dev/null
+++ b/doc/api/version.md
@@ -0,0 +1,23 @@
+# Version API
+
+>**Note:** This feature was introduced in GitLab 8.13
+
+Retrieve version information for this GitLab instance. Responds `200 OK` for
+authenticated users.
+
+```
+GET /version
+```
+
+```bash
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/version
+```
+
+Example response:
+
+```json
+{
+ "version": "8.13.0-pre",
+ "revision": "4e963fe"
+}
+```
diff --git a/doc/development/doc_styleguide.md b/doc/development/doc_styleguide.md
index 39b801f761d..0b725cf200c 100644
--- a/doc/development/doc_styleguide.md
+++ b/doc/development/doc_styleguide.md
@@ -314,6 +314,29 @@ In this case:
- different highlighting languages are used for each config in the code block
- the [references](#references) guide is used for reconfigure/restart
+## Fake tokens
+
+There may be times where a token is needed to demonstrate an API call using
+cURL or a secret variable used in CI. It is strongly advised not to use real
+tokens in documentation even if the probability of a token being exploited is
+low.
+
+You can use the following fake tokens as examples.
+
+| **Token type** | **Token value** |
+| --------------------- | --------------------------------- |
+| Private user token | `9koXpg98eAheJpvBs5tK` |
+| Personal access token | `n671WNGecHugsdEDPsyo` |
+| Application ID | `2fcb195768c39e9a94cec2c2e32c59c0aad7a3365c10892e8116b5d83d4096b6` |
+| Application secret | `04f294d1eaca42b8692017b426d53bbc8fe75f827734f0260710b83a556082df` |
+| Secret CI variable | `Li8j-mLUVA3eZYjPfd_H` |
+| Specific Runner token | `yrnZW46BrtBFqM7xDzE7dddd` |
+| Shared Runner token | `6Vk7ZsosqQyfreAxXTZr` |
+| Trigger token | `be20d8dcc028677c931e04f3871a9b` |
+| Webhook secret token | `6XhDroRcYPM5by_h-HLY` |
+| Health check token | `Tu7BgjR9qeZTEyRzGG2P` |
+| Request profile token | `7VgpS4Ax5utVD2esNstz` |
+
## API
Here is a list of must-have items. Use them in the exact order that appears
diff --git a/doc/development/frontend.md b/doc/development/frontend.md
index f879cd57e25..56c8516508e 100644
--- a/doc/development/frontend.md
+++ b/doc/development/frontend.md
@@ -223,3 +223,14 @@ For our currently-supported browsers, see our [requirements][requirements].
[xss]: https://en.wikipedia.org/wiki/Cross-site_scripting
[scss-style-guide]: scss_styleguide.md
[requirements]: ../install/requirements.md#supported-web-browsers
+
+## Common Errors
+
+### Rspec (Capybara/Poltergeist) chokes on general JavaScript errors
+
+If you see very generic JavaScript errors (e.g. `jQuery is undefined`) being thrown in tests, but
+can't reproduce them manually, you may have included `ES6`-style JavaScript in files that don't
+have the `.js.es6` file extension. Either use ES5-friendly JavaScript or rename the file you're
+working in (`git mv <file>.js> <file.js.es6>`).
+
+
diff --git a/doc/monitoring/health_check.md b/doc/monitoring/health_check.md
index eac57bc3de4..6cf93c33ec2 100644
--- a/doc/monitoring/health_check.md
+++ b/doc/monitoring/health_check.md
@@ -1,66 +1 @@
-# Health Check
-
-> [Introduced][ce-3888] in GitLab 8.8.
-
-GitLab provides a health check endpoint for uptime monitoring on the `health_check` web
-endpoint. The health check reports on the overall system status based on the status of
-the database connection, the state of the database migrations, and the ability to write
-and access the cache. This endpoint can be provided to uptime monitoring services like
-[Pingdom][pingdom], [Nagios][nagios-health], and [NewRelic][newrelic-health].
-
-## Access Token
-
-An access token needs to be provided while accessing the health check endpoint. The current
-accepted token can be found on the `admin/health_check` page of your GitLab instance.
-
-![access token](img/health_check_token.png)
-
-The access token can be passed as a URL parameter:
-
-```
-https://gitlab.example.com/health_check.json?token=ACCESS_TOKEN
-```
-
-or as an HTTP header:
-
-```bash
-curl --header "TOKEN: ACCESS_TOKEN" https://gitlab.example.com/health_check.json
-```
-
-## Using the Endpoint
-
-Once you have the access token, health information can be retrieved as plain text, JSON,
-or XML using the `health_check` endpoint:
-
-- `https://gitlab.example.com/health_check?token=ACCESS_TOKEN`
-- `https://gitlab.example.com/health_check.json?token=ACCESS_TOKEN`
-- `https://gitlab.example.com/health_check.xml?token=ACCESS_TOKEN`
-
-You can also ask for the status of specific services:
-
-- `https://gitlab.example.com/health_check/cache.json?token=ACCESS_TOKEN`
-- `https://gitlab.example.com/health_check/database.json?token=ACCESS_TOKEN`
-- `https://gitlab.example.com/health_check/migrations.json?token=ACCESS_TOKEN`
-
-For example, the JSON output of the following health check:
-
-```bash
-curl --header "TOKEN: ACCESS_TOKEN" https://gitlab.example.com/health_check.json
-```
-
-would be like:
-
-```
-{"healthy":true,"message":"success"}
-```
-
-## Status
-
-On failure, the endpoint will return a `500` HTTP status code. On success, the endpoint
-will return a valid successful HTTP status code, and a `success` message. Ideally your
-uptime monitoring should look for the success message.
-
-[ce-3888]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/3888
-[pingdom]: https://www.pingdom.com
-[nagios-health]: https://nagios-plugins.org/doc/man/check_http.html
-[newrelic-health]: https://docs.newrelic.com/docs/alerts/alert-policies/downtime-alerts/availability-monitoring
+This document was moved to [user/admin_area/monitoring/health_check](../user/admin_area/monitoring/health_check.md).
diff --git a/doc/monitoring/performance/gitlab_configuration.md b/doc/monitoring/performance/gitlab_configuration.md
index 771584268d9..a669bb28904 100644
--- a/doc/monitoring/performance/gitlab_configuration.md
+++ b/doc/monitoring/performance/gitlab_configuration.md
@@ -1,40 +1 @@
-# GitLab Configuration
-
-GitLab Performance Monitoring is disabled by default. To enable it and change any of its
-settings, navigate to the Admin area in **Settings > Metrics**
-(`/admin/application_settings`).
-
-The minimum required settings you need to set are the InfluxDB host and port.
-Make sure _Enable InfluxDB Metrics_ is checked and hit **Save** to save the
-changes.
-
----
-
-![GitLab Performance Monitoring Admin Settings](img/metrics_gitlab_configuration_settings.png)
-
----
-
-Finally, a restart of all GitLab processes is required for the changes to take
-effect:
-
-```bash
-# For Omnibus installations
-sudo gitlab-ctl restart
-
-# For installations from source
-sudo service gitlab restart
-```
-
-## Pending Migrations
-
-When any migrations are pending, the metrics are disabled until the migrations
-have been performed.
-
----
-
-Read more on:
-
-- [Introduction to GitLab Performance Monitoring](introduction.md)
-- [InfluxDB Configuration](influxdb_configuration.md)
-- [InfluxDB Schema](influxdb_schema.md)
-- [Grafana Install/Configuration](grafana_configuration.md)
+This document was moved to [administration/monitoring/performance/gitlab_configuration](../administration/monitoring/performance/gitlab_configuration.md).
diff --git a/doc/monitoring/performance/grafana_configuration.md b/doc/monitoring/performance/grafana_configuration.md
index 7947b0fedc4..93320b40174 100644
--- a/doc/monitoring/performance/grafana_configuration.md
+++ b/doc/monitoring/performance/grafana_configuration.md
@@ -1,111 +1 @@
-# Grafana Configuration
-
-[Grafana](http://grafana.org/) is a tool that allows you to visualize time
-series metrics through graphs and dashboards. It supports several backend
-data stores, including InfluxDB. GitLab writes performance data to InfluxDB
-and Grafana will allow you to query InfluxDB to display useful graphs.
-
-For the easiest installation and configuration, install Grafana on the same
-server as InfluxDB. For larger installations, you may want to split out these
-services.
-
-## Installation
-
-Grafana supplies package repositories (Yum/Apt) for easy installation.
-See [Grafana installation documentation](http://docs.grafana.org/installation/)
-for detailed steps.
-
-> **Note**: Before starting Grafana for the first time, set the admin user
-and password in `/etc/grafana/grafana.ini`. Otherwise, the default password
-will be `admin`.
-
-## Configuration
-
-Login as the admin user. Expand the menu by clicking the Grafana logo in the
-top left corner. Choose 'Data Sources' from the menu. Then, click 'Add new'
-in the top bar.
-
-![Grafana empty data source page](img/grafana_data_source_empty.png)
-
-Fill in the configuration details for the InfluxDB data source. Save and
-Test Connection to ensure the configuration is correct.
-
-- **Name**: InfluxDB
-- **Default**: Checked
-- **Type**: InfluxDB 0.9.x (Even if you're using InfluxDB 0.10.x)
-- **Url**: https://localhost:8086 (Or the remote URL if you've installed InfluxDB
-on a separate server)
-- **Access**: proxy
-- **Database**: gitlab
-- **User**: admin (Or the username configured when setting up InfluxDB)
-- **Password**: The password configured when you set up InfluxDB
-
-![Grafana data source configurations](img/grafana_data_source_configuration.png)
-
-## Apply retention policies and create continuous queries
-
-If you intend to import the GitLab provided Grafana dashboards, you will need to
-set up the right retention policies and continuous queries. The easiest way of
-doing this is by using the [influxdb-management](https://gitlab.com/gitlab-org/influxdb-management)
-repository.
-
-To use this repository you must first clone it:
-
-```
-git clone https://gitlab.com/gitlab-org/influxdb-management.git
-cd influxdb-management
-```
-
-Next you must install the required dependencies:
-
-```
-gem install bundler
-bundle install
-```
-
-Now you must configure the repository by first copying `.env.example` to `.env`
-and then editing the `.env` file to contain the correct InfluxDB settings. Once
-configured you can simply run `bundle exec rake` and the InfluxDB database will
-be configured for you.
-
-For more information see the [influxdb-management README](https://gitlab.com/gitlab-org/influxdb-management/blob/master/README.md).
-
-## Import Dashboards
-
-You can now import a set of default dashboards that will give you a good
-start on displaying useful information. GitLab has published a set of default
-[Grafana dashboards][grafana-dashboards] to get you started. Clone the
-repository or download a zip/tarball, then follow these steps to import each
-JSON file.
-
-Open the dashboard dropdown menu and click 'Import'
-
-![Grafana dashboard dropdown](img/grafana_dashboard_dropdown.png)
-
-Click 'Choose file' and browse to the location where you downloaded or cloned
-the dashboard repository. Pick one of the JSON files to import.
-
-![Grafana dashboard import](img/grafana_dashboard_import.png)
-
-Once the dashboard is imported, be sure to click save icon in the top bar. If
-you do not save the dashboard after importing it will be removed when you
-navigate away.
-
-![Grafana save icon](img/grafana_save_icon.png)
-
-Repeat this process for each dashboard you wish to import.
-
-Alternatively you can automatically import all the dashboards into your Grafana
-instance. See the README of the [Grafana dashboards][grafana-dashboards]
-repository for more information on this process.
-
-[grafana-dashboards]: https://gitlab.com/gitlab-org/grafana-dashboards
-
----
-
-Read more on:
-
-- [Introduction to GitLab Performance Monitoring](introduction.md)
-- [GitLab Configuration](gitlab_configuration.md)
-- [InfluxDB Installation/Configuration](influxdb_configuration.md)
-- [InfluxDB Schema](influxdb_schema.md)
+This document was moved to [administration/monitoring/performance/grafana_configuration](../administration/monitoring/performance/grafana_configuration.md).
diff --git a/doc/monitoring/performance/influxdb_configuration.md b/doc/monitoring/performance/influxdb_configuration.md
index c30cd2950d8..02647de1eb0 100644
--- a/doc/monitoring/performance/influxdb_configuration.md
+++ b/doc/monitoring/performance/influxdb_configuration.md
@@ -1,193 +1 @@
-# InfluxDB Configuration
-
-The default settings provided by [InfluxDB] are not sufficient for a high traffic
-GitLab environment. The settings discussed in this document are based on the
-settings GitLab uses for GitLab.com, depending on your own needs you may need to
-further adjust them.
-
-If you are intending to run InfluxDB on the same server as GitLab, make sure
-you have plenty of RAM since InfluxDB can use quite a bit depending on traffic.
-
-Unless you are going with a budget setup, it's advised to run it separately.
-
-## Requirements
-
-- InfluxDB 0.9.5 or newer
-- A fairly modern version of Linux
-- At least 4GB of RAM
-- At least 10GB of storage for InfluxDB data
-
-Note that the RAM and storage requirements can differ greatly depending on the
-amount of data received/stored. To limit the amount of stored data users can
-look into [InfluxDB Retention Policies][influxdb-retention].
-
-## Installation
-
-Installing InfluxDB is out of the scope of this document. Please refer to the
-[InfluxDB documentation].
-
-## InfluxDB Server Settings
-
-Since InfluxDB has many settings that users may wish to customize themselves
-(e.g. what port to run InfluxDB on), we'll only cover the essentials.
-
-The configuration file in question is usually located at
-`/etc/influxdb/influxdb.conf`. Whenever you make a change in this file,
-InfluxDB needs to be restarted.
-
-### Storage Engine
-
-InfluxDB comes with different storage engines and as of InfluxDB 0.9.5 a new
-storage engine is available, called [TSM Tree]. All users **must** use the new
-`tsm1` storage engine as this [will be the default engine][tsm1-commit] in
-upcoming InfluxDB releases.
-
-Make sure you have the following in your configuration file:
-
-```
-[data]
- dir = "/var/lib/influxdb/data"
- engine = "tsm1"
-```
-
-### Admin Panel
-
-Production environments should have the InfluxDB admin panel **disabled**. This
-feature can be disabled by adding the following to your InfluxDB configuration
-file:
-
-```
-[admin]
- enabled = false
-```
-
-### HTTP
-
-HTTP is required when using the [InfluxDB CLI] or other tools such as Grafana,
-thus it should be enabled. When enabling make sure to _also_ enable
-authentication:
-
-```
-[http]
- enabled = true
- auth-enabled = true
-```
-
-_**Note:** Before you enable authentication, you might want to [create an
-admin user](#create-a-new-admin-user)._
-
-### UDP
-
-GitLab writes data to InfluxDB via UDP and thus this must be enabled. Enabling
-UDP can be done using the following settings:
-
-```
-[[udp]]
- enabled = true
- bind-address = ":8089"
- database = "gitlab"
- batch-size = 1000
- batch-pending = 5
- batch-timeout = "1s"
- read-buffer = 209715200
-```
-
-This does the following:
-
-1. Enable UDP and bind it to port 8089 for all addresses.
-2. Store any data received in the "gitlab" database.
-3. Define a batch of points to be 1000 points in size and allow a maximum of
- 5 batches _or_ flush them automatically after 1 second.
-4. Define a UDP read buffer size of 200 MB.
-
-One of the most important settings here is the UDP read buffer size as if this
-value is set too low, packets will be dropped. You must also make sure the OS
-buffer size is set to the same value, the default value is almost never enough.
-
-To set the OS buffer size to 200 MB, on Linux you can run the following command:
-
-```bash
-sysctl -w net.core.rmem_max=209715200
-```
-
-To make this permanent, add the following to `/etc/sysctl.conf` and restart the
-server:
-
-```bash
-net.core.rmem_max=209715200
-```
-
-It is **very important** to make sure the buffer sizes are large enough to
-handle all data sent to InfluxDB as otherwise you _will_ lose data. The above
-buffer sizes are based on the traffic for GitLab.com. Depending on the amount of
-traffic, users may be able to use a smaller buffer size, but we highly recommend
-using _at least_ 100 MB.
-
-When enabling UDP, users should take care to not expose the port to the public,
-as doing so will allow anybody to write data into your InfluxDB database (as
-[InfluxDB's UDP protocol][udp] doesn't support authentication). We recommend either
-whitelisting the allowed IP addresses/ranges, or setting up a VLAN and only
-allowing traffic from members of said VLAN.
-
-## Create a new admin user
-
-If you want to [enable authentication](#http), you might want to [create an
-admin user][influx-admin]:
-
-```
-influx -execute "CREATE USER jeff WITH PASSWORD '1234' WITH ALL PRIVILEGES"
-```
-
-## Create the `gitlab` database
-
-Once you get InfluxDB up and running, you need to create a database for GitLab.
-Make sure you have changed the [storage engine](#storage-engine) to `tsm1`
-before creating a database.
-
-_**Note:** If you [created an admin user](#create-a-new-admin-user) and enabled
-[HTTP authentication](#http), remember to append the username (`-username <username>`)
-and password (`-password <password>`) you set earlier to the commands below._
-
-Run the following command to create a database named `gitlab`:
-
-```bash
-influx -execute 'CREATE DATABASE gitlab'
-```
-
-The name **must** be `gitlab`, do not use any other name.
-
-Next, make sure that the database was successfully created:
-
-```bash
-influx -execute 'SHOW DATABASES'
-```
-
-The output should be similar to:
-
-```
-name: databases
----------------
-name
-_internal
-gitlab
-```
-
-That's it! Now your GitLab instance should send data to InfluxDB.
-
----
-
-Read more on:
-
-- [Introduction to GitLab Performance Monitoring](introduction.md)
-- [GitLab Configuration](gitlab_configuration.md)
-- [InfluxDB Schema](influxdb_schema.md)
-- [Grafana Install/Configuration](grafana_configuration.md)
-
-[influxdb-retention]: https://docs.influxdata.com/influxdb/v0.9/query_language/database_management/#retention-policy-management
-[influxdb documentation]: https://docs.influxdata.com/influxdb/v0.9/
-[influxdb cli]: https://docs.influxdata.com/influxdb/v0.9/tools/shell/
-[udp]: https://docs.influxdata.com/influxdb/v0.9/write_protocols/udp/
-[influxdb]: https://influxdata.com/time-series-platform/influxdb/
-[tsm tree]: https://influxdata.com/blog/new-storage-engine-time-structured-merge-tree/
-[tsm1-commit]: https://github.com/influxdata/influxdb/commit/15d723dc77651bac83e09e2b1c94be480966cb0d
-[influx-admin]: https://docs.influxdata.com/influxdb/v0.9/administration/authentication_and_authorization/#create-a-new-admin-user
+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 eff0e29f58d..a989e323e04 100644
--- a/doc/monitoring/performance/influxdb_schema.md
+++ b/doc/monitoring/performance/influxdb_schema.md
@@ -1,97 +1 @@
-# InfluxDB Schema
-
-The following measurements are currently stored in InfluxDB:
-
-- `PROCESS_file_descriptors`
-- `PROCESS_gc_statistics`
-- `PROCESS_memory_usage`
-- `PROCESS_method_calls`
-- `PROCESS_object_counts`
-- `PROCESS_transactions`
-- `PROCESS_views`
-- `events`
-
-Here, `PROCESS` is replaced with either `rails` or `sidekiq` depending on the
-process type. In all series, any form of duration is stored in milliseconds.
-
-## PROCESS_file_descriptors
-
-This measurement contains the number of open file descriptors over time. The
-value field `value` contains the number of descriptors.
-
-## PROCESS_gc_statistics
-
-This measurement contains Ruby garbage collection statistics such as the amount
-of minor/major GC runs (relative to the last sampling interval), the time spent
-in garbage collection cycles, and all fields/values returned by `GC.stat`.
-
-## PROCESS_memory_usage
-
-This measurement contains the process' memory usage (in bytes) over time. The
-value field `value` contains the number of bytes.
-
-## PROCESS_method_calls
-
-This measurement contains the methods called during a transaction along with
-their duration, and a name of the transaction action that invoked the method (if
-available). The method call duration is stored in the value field `duration`,
-while the method name is stored in the tag `method`. The tag `action` contains
-the full name of the transaction action. Both the `method` and `action` fields
-are in the following format:
-
-```
-ClassName#method_name
-```
-
-For example, a method called by the `show` method in the `UsersController` class
-would have `action` set to `UsersController#show`.
-
-## PROCESS_object_counts
-
-This measurement is used to store retained Ruby objects (per class) and the
-amount of retained objects. The number of objects is stored in the `count` value
-field while the class name is stored in the `type` tag.
-
-## PROCESS_transactions
-
-This measurement is used to store basic transaction details such as the time it
-took to complete a transaction, how much time was spent in SQL queries, etc. The
-following value fields are available:
-
-| Value | Description |
-| ----- | ----------- |
-| `duration` | The total duration of the transaction |
-| `allocated_memory` | The amount of bytes allocated while the transaction was running. This value is only reliable when using single-threaded application servers |
-| `method_duration` | The total time spent in method calls |
-| `sql_duration` | The total time spent in SQL queries |
-| `view_duration` | The total time spent in views |
-
-## PROCESS_views
-
-This measurement is used to store view rendering timings for a transaction. The
-following value fields are available:
-
-| Value | Description |
-| ----- | ----------- |
-| `duration` | The rendering time of the view |
-| `view` | The path of the view, relative to the application's root directory |
-
-The `action` tag contains the action name of the transaction that rendered the
-view.
-
-## events
-
-This measurement is used to store generic events such as the number of Git
-pushes, Emails sent, etc. Each point in this measurement has a single value
-field called `count`. The value of this field is simply set to `1`. Each point
-also has at least one tag: `event`. This tag's value is set to the event name.
-Depending on the event type additional tags may be available as well.
-
----
-
-Read more on:
-
-- [Introduction to GitLab Performance Monitoring](introduction.md)
-- [GitLab Configuration](gitlab_configuration.md)
-- [InfluxDB Configuration](influxdb_configuration.md)
-- [Grafana Install/Configuration](grafana_configuration.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 79904916b7e..ab3f3ac1664 100644
--- a/doc/monitoring/performance/introduction.md
+++ b/doc/monitoring/performance/introduction.md
@@ -1,65 +1 @@
-# GitLab Performance Monitoring
-
-GitLab comes with its own application performance measuring system as of GitLab
-8.4, simply called "GitLab Performance Monitoring". GitLab Performance Monitoring is available in both the
-Community and Enterprise editions.
-
-Apart from this introduction, you are advised to read through the following
-documents in order to understand and properly configure GitLab Performance Monitoring:
-
-- [GitLab Configuration](gitlab_configuration.md)
-- [InfluxDB Install/Configuration](influxdb_configuration.md)
-- [InfluxDB Schema](influxdb_schema.md)
-- [Grafana Install/Configuration](grafana_configuration.md)
-
-## Introduction to GitLab Performance Monitoring
-
-GitLab Performance Monitoring makes it possible to measure a wide variety of statistics
-including (but not limited to):
-
-- The time it took to complete a transaction (a web request or Sidekiq job).
-- The time spent in running SQL queries and rendering HAML views.
-- The time spent executing (instrumented) Ruby methods.
-- Ruby object allocations, and retained objects in particular.
-- System statistics such as the process' memory usage and open file descriptors.
-- Ruby garbage collection statistics.
-
-Metrics data is written to [InfluxDB][influxdb] over [UDP][influxdb-udp]. Stored
-data can be visualized using [Grafana][grafana] or any other application that
-supports reading data from InfluxDB. Alternatively data can be queried using the
-InfluxDB CLI.
-
-## Metric Types
-
-Two types of metrics are collected:
-
-1. Transaction specific metrics.
-1. Sampled metrics, collected at a certain interval in a separate thread.
-
-### Transaction Metrics
-
-Transaction metrics are metrics that can be associated with a single
-transaction. This includes statistics such as the transaction duration, timings
-of any executed SQL queries, time spent rendering HAML views, etc. These metrics
-are collected for every Rack request and Sidekiq job processed.
-
-### Sampled Metrics
-
-Sampled metrics are metrics that can't be associated with a single transaction.
-Examples include garbage collection statistics and retained Ruby objects. These
-metrics are collected at a regular interval. This interval is made up out of two
-parts:
-
-1. A user defined interval.
-1. A randomly generated offset added on top of the interval, the same offset
- can't be used twice in a row.
-
-The actual interval can be anywhere between a half of the defined interval and a
-half above the interval. For example, for a user defined interval of 15 seconds
-the actual interval can be anywhere between 7.5 and 22.5. The interval is
-re-generated for every sampling run instead of being generated once and re-used
-for the duration of the process' lifetime.
-
-[influxdb]: https://influxdata.com/time-series-platform/influxdb/
-[influxdb-udp]: https://docs.influxdata.com/influxdb/v0.9/write_protocols/udp/
-[grafana]: http://grafana.org/
+This document was moved to [administration/monitoring/performance/introduction](../administration/monitoring/performance/introduction.md).
diff --git a/doc/operations/README.md b/doc/operations/README.md
index 6a35dab7b6c..58f16aff7bd 100644
--- a/doc/operations/README.md
+++ b/doc/operations/README.md
@@ -1,5 +1 @@
-# GitLab operations
-
-- [Sidekiq MemoryKiller](sidekiq_memory_killer.md)
-- [Cleaning up Redis sessions](cleaning_up_redis_sessions.md)
-- [Understanding Unicorn and unicorn-worker-killer](unicorn.md)
+This document was moved to [administration/operations](../administration/operations.md).
diff --git a/doc/operations/cleaning_up_redis_sessions.md b/doc/operations/cleaning_up_redis_sessions.md
index 93521e976d5..2a1d0a8c8eb 100644
--- a/doc/operations/cleaning_up_redis_sessions.md
+++ b/doc/operations/cleaning_up_redis_sessions.md
@@ -1,52 +1 @@
-# Cleaning up stale Redis sessions
-
-Since version 6.2, GitLab stores web user sessions as key-value pairs in Redis.
-Prior to GitLab 7.3, user sessions did not automatically expire from Redis. If
-you have been running a large GitLab server (thousands of users) since before
-GitLab 7.3 we recommend cleaning up stale sessions to compact the Redis
-database after you upgrade to GitLab 7.3. You can also perform a cleanup while
-still running GitLab 7.2 or older, but in that case new stale sessions will
-start building up again after you clean up.
-
-In GitLab versions prior to 7.3.0, the session keys in Redis are 16-byte
-hexadecimal values such as '976aa289e2189b17d7ef525a6702ace9'. Starting with
-GitLab 7.3.0, the keys are
-prefixed with 'session:gitlab:', so they would look like
-'session:gitlab:976aa289e2189b17d7ef525a6702ace9'. Below we describe how to
-remove the keys in the old format.
-
-First we define a shell function with the proper Redis connection details.
-
-```
-rcli() {
- # This example works for Omnibus installations of GitLab 7.3 or newer. For an
- # installation from source you will have to change the socket path and the
- # path to redis-cli.
- sudo /opt/gitlab/embedded/bin/redis-cli -s /var/opt/gitlab/redis/redis.socket "$@"
-}
-
-# test the new shell function; the response should be PONG
-rcli ping
-```
-
-Now we do a search to see if there are any session keys in the old format for
-us to clean up.
-
-```
-# returns the number of old-format session keys in Redis
-rcli keys '*' | grep '^[a-f0-9]\{32\}$' | wc -l
-```
-
-If the number is larger than zero, you can proceed to expire the keys from
-Redis. If the number is zero there is nothing to clean up.
-
-```
-# Tell Redis to expire each matched key after 600 seconds.
-rcli keys '*' | grep '^[a-f0-9]\{32\}$' | awk '{ print "expire", $0, 600 }' | rcli
-# This will print '(integer) 1' for each key that gets expired.
-```
-
-Over the next 15 minutes (10 minutes expiry time plus 5 minutes Redis
-background save interval) your Redis database will be compacted. If you are
-still using GitLab 7.2, users who are not clicking around in GitLab during the
-10 minute expiry window will be signed out of GitLab.
+This document was moved to [administration/operations/cleaning_up_redis_sessions](../administration/operations/cleaning_up_redis_sessions.md).
diff --git a/doc/operations/moving_repositories.md b/doc/operations/moving_repositories.md
index 54adb99386a..c54bca324a5 100644
--- a/doc/operations/moving_repositories.md
+++ b/doc/operations/moving_repositories.md
@@ -1,180 +1 @@
-# Moving repositories managed by GitLab
-
-Sometimes you need to move all repositories managed by GitLab to
-another filesystem or another server. In this document we will look
-at some of the ways you can copy all your repositories from
-`/var/opt/gitlab/git-data/repositories` to `/mnt/gitlab/repositories`.
-
-We will look at three scenarios: the target directory is empty, the
-target directory contains an outdated copy of the repositories, and
-how to deal with thousands of repositories.
-
-**Each of the approaches we list can/will overwrite data in the
-target directory `/mnt/gitlab/repositories`. Do not mix up the
-source and the target.**
-
-## Target directory is empty: use a tar pipe
-
-If the target directory `/mnt/gitlab/repositories` is empty the
-simplest thing to do is to use a tar pipe. This method has low
-overhead and tar is almost always already installed on your system.
-However, it is not possible to resume an interrupted tar pipe: if
-that happens then all data must be copied again.
-
-```
-# As the git user
-tar -C /var/opt/gitlab/git-data/repositories -cf - -- . |\
- tar -C /mnt/gitlab/repositories -xf -
-```
-
-If you want to see progress, replace `-xf` with `-xvf`.
-
-### Tar pipe to another server
-
-You can also use a tar pipe to copy data to another server. If your
-'git' user has SSH access to the newserver as 'git@newserver', you
-can pipe the data through SSH.
-
-```
-# As the git user
-tar -C /var/opt/gitlab/git-data/repositories -cf - -- . |\
- ssh git@newserver tar -C /mnt/gitlab/repositories -xf -
-```
-
-If you want to compress the data before it goes over the network
-(which will cost you CPU cycles) you can replace `ssh` with `ssh -C`.
-
-## The target directory contains an outdated copy of the repositories: use rsync
-
-If the target directory already contains a partial / outdated copy
-of the repositories it may be wasteful to copy all the data again
-with tar. In this scenario it is better to use rsync. This utility
-is either already installed on your system or easily installable
-via apt, yum etc.
-
-```
-# As the 'git' user
-rsync -a --delete /var/opt/gitlab/git-data/repositories/. \
- /mnt/gitlab/repositories
-```
-
-The `/.` in the command above is very important, without it you can
-easily get the wrong directory structure in the target directory.
-If you want to see progress, replace `-a` with `-av`.
-
-### Single rsync to another server
-
-If the 'git' user on your source system has SSH access to the target
-server you can send the repositories over the network with rsync.
-
-```
-# As the 'git' user
-rsync -a --delete /var/opt/gitlab/git-data/repositories/. \
- git@newserver:/mnt/gitlab/repositories
-```
-
-## Thousands of Git repositories: use one rsync per repository
-
-Every time you start an rsync job it has to inspect all files in
-the source directory, all files in the target directory, and then
-decide what files to copy or not. If the source or target directory
-has many contents this startup phase of rsync can become a burden
-for your GitLab server. In cases like this you can make rsync's
-life easier by dividing its work in smaller pieces, and sync one
-repository at a time.
-
-In addition to rsync we will use [GNU
-Parallel](http://www.gnu.org/software/parallel/). This utility is
-not included in GitLab so you need to install it yourself with apt
-or yum. Also note that the GitLab scripts we used below were added
-in GitLab 8.1.
-
-** This process does not clean up repositories at the target location that no
-longer exist at the source. ** If you start using your GitLab instance with
-`/mnt/gitlab/repositories`, you need to run `gitlab-rake gitlab:cleanup:repos`
-after switching to the new repository storage directory.
-
-### Parallel rsync for all repositories known to GitLab
-
-This will sync repositories with 10 rsync processes at a time. We keep
-track of progress so that the transfer can be restarted if necessary.
-
-First we create a new directory, owned by 'git', to hold transfer
-logs. We assume the directory is empty before we start the transfer
-procedure, and that we are the only ones writing files in it.
-
-```
-# Omnibus
-sudo mkdir /var/opt/gitlab/transfer-logs
-sudo chown git:git /var/opt/gitlab/transfer-logs
-
-# Source
-sudo -u git -H mkdir /home/git/transfer-logs
-```
-
-We seed the process with a list of the directories we want to copy.
-
-```
-# Omnibus
-sudo -u git sh -c 'gitlab-rake gitlab:list_repos > /var/opt/gitlab/transfer-logs/all-repos-$(date +%s).txt'
-
-# Source
-cd /home/git/gitlab
-sudo -u git -H sh -c 'bundle exec rake gitlab:list_repos > /home/git/transfer-logs/all-repos-$(date +%s).txt'
-```
-
-Now we can start the transfer. The command below is idempotent, and
-the number of jobs done by GNU Parallel should converge to zero. If it
-does not some repositories listed in all-repos-1234.txt may have been
-deleted/renamed before they could be copied.
-
-```
-# Omnibus
-sudo -u git sh -c '
-cat /var/opt/gitlab/transfer-logs/* | sort | uniq -u |\
- /usr/bin/env JOBS=10 \
- /opt/gitlab/embedded/service/gitlab-rails/bin/parallel-rsync-repos \
- /var/opt/gitlab/transfer-logs/success-$(date +%s).log \
- /var/opt/gitlab/git-data/repositories \
- /mnt/gitlab/repositories
-'
-
-# Source
-cd /home/git/gitlab
-sudo -u git -H sh -c '
-cat /home/git/transfer-logs/* | sort | uniq -u |\
- /usr/bin/env JOBS=10 \
- bin/parallel-rsync-repos \
- /home/git/transfer-logs/success-$(date +%s).log \
- /home/git/repositories \
- /mnt/gitlab/repositories
-`
-```
-
-### Parallel rsync only for repositories with recent activity
-
-Suppose you have already done one sync that started after 2015-10-1 12:00 UTC.
-Then you might only want to sync repositories that were changed via GitLab
-_after_ that time. You can use the 'SINCE' variable to tell 'rake
-gitlab:list_repos' to only print repositories with recent activity.
-
-```
-# Omnibus
-sudo gitlab-rake gitlab:list_repos SINCE='2015-10-1 12:00 UTC' |\
- sudo -u git \
- /usr/bin/env JOBS=10 \
- /opt/gitlab/embedded/service/gitlab-rails/bin/parallel-rsync-repos \
- success-$(date +%s).log \
- /var/opt/gitlab/git-data/repositories \
- /mnt/gitlab/repositories
-
-# Source
-cd /home/git/gitlab
-sudo -u git -H bundle exec rake gitlab:list_repos SINCE='2015-10-1 12:00 UTC' |\
- sudo -u git -H \
- /usr/bin/env JOBS=10 \
- bin/parallel-rsync-repos \
- success-$(date +%s).log \
- /home/git/repositories \
- /mnt/gitlab/repositories
-```
+This document was moved to [administration/operations/moving_repositories](../administration/operations/moving_repositories.md).
diff --git a/doc/operations/sidekiq_memory_killer.md b/doc/operations/sidekiq_memory_killer.md
index b5e78348989..cf7c3b2e2ed 100644
--- a/doc/operations/sidekiq_memory_killer.md
+++ b/doc/operations/sidekiq_memory_killer.md
@@ -1,40 +1 @@
-# Sidekiq MemoryKiller
-
-The GitLab Rails application code suffers from memory leaks. For web requests
-this problem is made manageable using
-[unicorn-worker-killer](https://github.com/kzk/unicorn-worker-killer) which
-restarts Unicorn worker processes in between requests when needed. The Sidekiq
-MemoryKiller applies the same approach to the Sidekiq processes used by GitLab
-to process background jobs.
-
-Unlike unicorn-worker-killer, which is enabled by default for all GitLab
-installations since GitLab 6.4, the Sidekiq MemoryKiller is enabled by default
-_only_ for Omnibus packages. The reason for this is that the MemoryKiller
-relies on Runit to restart Sidekiq after a memory-induced shutdown and GitLab
-installations from source do not all use Runit or an equivalent.
-
-With the default settings, the MemoryKiller will cause a Sidekiq restart no
-more often than once every 15 minutes, with the restart causing about one
-minute of delay for incoming background jobs.
-
-## Configuring the MemoryKiller
-
-The MemoryKiller is controlled using environment variables.
-
-- `SIDEKIQ_MEMORY_KILLER_MAX_RSS`: if this variable is set, and its value is
- greater than 0, then after each Sidekiq job, the MemoryKiller will check the
- RSS of the Sidekiq process that executed the job. If the RSS of the Sidekiq
- process (expressed in kilobytes) exceeds SIDEKIQ_MEMORY_KILLER_MAX_RSS, a
- delayed shutdown is triggered. The default value for Omnibus packages is set
- [in the omnibus-gitlab
- repository](https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/files/gitlab-cookbooks/gitlab/attributes/default.rb).
-- `SIDEKIQ_MEMORY_KILLER_GRACE_TIME`: defaults 900 seconds (15 minutes). When
- a shutdown is triggered, the Sidekiq process will keep working normally for
- another 15 minutes.
-- `SIDEKIQ_MEMORY_KILLER_SHUTDOWN_WAIT`: defaults to 30 seconds. When the grace
- time has expired, the MemoryKiller tells Sidekiq to stop accepting new jobs.
- Existing jobs get 30 seconds to finish. After that, the MemoryKiller tells
- Sidekiq to shut down, and an external supervision mechanism (e.g. Runit) must
- restart Sidekiq.
-- `SIDEKIQ_MEMORY_KILLER_SHUTDOWN_SIGNAL`: defaults to `SIGKILL`. The name of
- the final signal sent to the Sidekiq process when we want it to shut down.
+This document was moved to [administration/operations/sidekiq_memory_killer](../administration/operations/sidekiq_memory_killer.md).
diff --git a/doc/operations/unicorn.md b/doc/operations/unicorn.md
index bad61151bda..fbc9697b755 100644
--- a/doc/operations/unicorn.md
+++ b/doc/operations/unicorn.md
@@ -1,86 +1 @@
-# Understanding Unicorn and unicorn-worker-killer
-
-## Unicorn
-
-GitLab uses [Unicorn](http://unicorn.bogomips.org/), a pre-forking Ruby web
-server, to handle web requests (web browsers and Git HTTP clients). Unicorn is
-a daemon written in Ruby and C that can load and run a Ruby on Rails
-application; in our case the Rails application is GitLab Community Edition or
-GitLab Enterprise Edition.
-
-Unicorn has a multi-process architecture to make better use of available CPU
-cores (processes can run on different cores) and to have stronger fault
-tolerance (most failures stay isolated in only one process and cannot take down
-GitLab entirely). On startup, the Unicorn 'master' process loads a clean Ruby
-environment with the GitLab application code, and then spawns 'workers' which
-inherit this clean initial environment. The 'master' never handles any
-requests, that is left to the workers. The operating system network stack
-queues incoming requests and distributes them among the workers.
-
-In a perfect world, the master would spawn its pool of workers once, and then
-the workers handle incoming web requests one after another until the end of
-time. In reality, worker processes can crash or time out: if the master notices
-that a worker takes too long to handle a request it will terminate the worker
-process with SIGKILL ('kill -9'). No matter how the worker process ended, the
-master process will replace it with a new 'clean' process again. Unicorn is
-designed to be able to replace 'crashed' workers without dropping user
-requests.
-
-This is what a Unicorn worker timeout looks like in `unicorn_stderr.log`. The
-master process has PID 56227 below.
-
-```
-[2015-06-05T10:58:08.660325 #56227] ERROR -- : worker=10 PID:53009 timeout (61s > 60s), killing
-[2015-06-05T10:58:08.699360 #56227] ERROR -- : reaped #<Process::Status: pid 53009 SIGKILL (signal 9)> worker=10
-[2015-06-05T10:58:08.708141 #62538] INFO -- : worker=10 spawned pid=62538
-[2015-06-05T10:58:08.708824 #62538] INFO -- : worker=10 ready
-```
-
-### Tunables
-
-The main tunables for Unicorn are the number of worker processes and the
-request timeout after which the Unicorn master terminates a worker process.
-See the [omnibus-gitlab Unicorn settings
-documentation](https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/doc/settings/unicorn.md)
-if you want to adjust these settings.
-
-## unicorn-worker-killer
-
-GitLab has memory leaks. These memory leaks manifest themselves in long-running
-processes, such as Unicorn workers. (The Unicorn master process is not known to
-leak memory, probably because it does not handle user requests.)
-
-To make these memory leaks manageable, GitLab comes with the
-[unicorn-worker-killer gem](https://github.com/kzk/unicorn-worker-killer). This
-gem [monkey-patches](https://en.wikipedia.org/wiki/Monkey_patch) the Unicorn
-workers to do a memory self-check after every 16 requests. If the memory of the
-Unicorn worker exceeds a pre-set limit then the worker process exits. The
-Unicorn master then automatically replaces the worker process.
-
-This is a robust way to handle memory leaks: Unicorn is designed to handle
-workers that 'crash' so no user requests will be dropped. The
-unicorn-worker-killer gem is designed to only terminate a worker process _in
-between requests_, so no user requests are affected.
-
-This is what a Unicorn worker memory restart looks like in unicorn_stderr.log.
-You see that worker 4 (PID 125918) is inspecting itself and decides to exit.
-The threshold memory value was 254802235 bytes, about 250MB. With GitLab this
-threshold is a random value between 200 and 250 MB. The master process (PID
-117565) then reaps the worker process and spawns a new 'worker 4' with PID
-127549.
-
-```
-[2015-06-05T12:07:41.828374 #125918] WARN -- : #<Unicorn::HttpServer:0x00000002734770>: worker (pid: 125918) exceeds memory limit (256413696 bytes > 254802235 bytes)
-[2015-06-05T12:07:41.828472 #125918] WARN -- : Unicorn::WorkerKiller send SIGQUIT (pid: 125918) alive: 23 sec (trial 1)
-[2015-06-05T12:07:42.025916 #117565] INFO -- : reaped #<Process::Status: pid 125918 exit 0> worker=4
-[2015-06-05T12:07:42.034527 #127549] INFO -- : worker=4 spawned pid=127549
-[2015-06-05T12:07:42.035217 #127549] INFO -- : worker=4 ready
-```
-
-One other thing that stands out in the log snippet above, taken from
-GitLab.com, is that 'worker 4' was serving requests for only 23 seconds. This
-is a normal value for our current GitLab.com setup and traffic.
-
-The high frequency of Unicorn memory restarts on some GitLab sites can be a
-source of confusion for administrators. Usually they are a [red
-herring](https://en.wikipedia.org/wiki/Red_herring).
+This document was moved to [administration/operations/unicorn](../administration/operations/unicorn.md).
diff --git a/doc/user/admin_area/monitoring/health_check.md b/doc/user/admin_area/monitoring/health_check.md
new file mode 100644
index 00000000000..eac57bc3de4
--- /dev/null
+++ b/doc/user/admin_area/monitoring/health_check.md
@@ -0,0 +1,66 @@
+# Health Check
+
+> [Introduced][ce-3888] in GitLab 8.8.
+
+GitLab provides a health check endpoint for uptime monitoring on the `health_check` web
+endpoint. The health check reports on the overall system status based on the status of
+the database connection, the state of the database migrations, and the ability to write
+and access the cache. This endpoint can be provided to uptime monitoring services like
+[Pingdom][pingdom], [Nagios][nagios-health], and [NewRelic][newrelic-health].
+
+## Access Token
+
+An access token needs to be provided while accessing the health check endpoint. The current
+accepted token can be found on the `admin/health_check` page of your GitLab instance.
+
+![access token](img/health_check_token.png)
+
+The access token can be passed as a URL parameter:
+
+```
+https://gitlab.example.com/health_check.json?token=ACCESS_TOKEN
+```
+
+or as an HTTP header:
+
+```bash
+curl --header "TOKEN: ACCESS_TOKEN" https://gitlab.example.com/health_check.json
+```
+
+## Using the Endpoint
+
+Once you have the access token, health information can be retrieved as plain text, JSON,
+or XML using the `health_check` endpoint:
+
+- `https://gitlab.example.com/health_check?token=ACCESS_TOKEN`
+- `https://gitlab.example.com/health_check.json?token=ACCESS_TOKEN`
+- `https://gitlab.example.com/health_check.xml?token=ACCESS_TOKEN`
+
+You can also ask for the status of specific services:
+
+- `https://gitlab.example.com/health_check/cache.json?token=ACCESS_TOKEN`
+- `https://gitlab.example.com/health_check/database.json?token=ACCESS_TOKEN`
+- `https://gitlab.example.com/health_check/migrations.json?token=ACCESS_TOKEN`
+
+For example, the JSON output of the following health check:
+
+```bash
+curl --header "TOKEN: ACCESS_TOKEN" https://gitlab.example.com/health_check.json
+```
+
+would be like:
+
+```
+{"healthy":true,"message":"success"}
+```
+
+## Status
+
+On failure, the endpoint will return a `500` HTTP status code. On success, the endpoint
+will return a valid successful HTTP status code, and a `success` message. Ideally your
+uptime monitoring should look for the success message.
+
+[ce-3888]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/3888
+[pingdom]: https://www.pingdom.com
+[nagios-health]: https://nagios-plugins.org/doc/man/check_http.html
+[newrelic-health]: https://docs.newrelic.com/docs/alerts/alert-policies/downtime-alerts/availability-monitoring
diff --git a/doc/monitoring/img/health_check_token.png b/doc/user/admin_area/monitoring/img/health_check_token.png
index 2d7c82a65a8..2d7c82a65a8 100644
--- a/doc/monitoring/img/health_check_token.png
+++ b/doc/user/admin_area/monitoring/img/health_check_token.png
Binary files differ
diff --git a/doc/user/permissions.md b/doc/user/permissions.md
index c0dc80325b6..d6216a8dd50 100644
--- a/doc/user/permissions.md
+++ b/doc/user/permissions.md
@@ -32,6 +32,7 @@ The following table depicts the various user permission levels in a project.
| See a commit status | | ✓ | ✓ | ✓ | ✓ |
| See a container registry | | ✓ | ✓ | ✓ | ✓ |
| See environments | | ✓ | ✓ | ✓ | ✓ |
+| See a list of merge requests | | ✓ | ✓ | ✓ | ✓ |
| Manage/Accept merge requests | | | ✓ | ✓ | ✓ |
| Create new merge request | | | ✓ | ✓ | ✓ |
| Create new branches | | | ✓ | ✓ | ✓ |
diff --git a/doc/user/project/cycle_analytics.md b/doc/user/project/cycle_analytics.md
index c16058165d7..1892ccabb70 100644
--- a/doc/user/project/cycle_analytics.md
+++ b/doc/user/project/cycle_analytics.md
@@ -3,8 +3,8 @@
> [Introduced][ce-5986] in GitLab 8.12.
>
> **Note:**
-This the first iteration of Cycle Analytics, you can follow the following issue
-to track the changes that are coming to this feature: [#20975][ce-20975].
+There are more changes coming to Cycle Analytics, you can follow the following
+issue to track the changes to this feature: [#20975][ce-20975].
Cycle Analytics measures the time it takes to go from an [idea to production] for
each project you have. This is achieved by not only indicating the total time it
@@ -48,13 +48,12 @@ You can see that there are seven stages in total:
## How the data is measured
-Cycle Analytics records cycle time so only data on the issues that have been
-deployed to production are measured. In case you just started a new project and
-you have not pushed anything to production, then you will not be able to
-properly see the Cycle Analytics of your project.
+Cycle Analytics records cycle time and data based on the project issues with the
+exception of the staging and production stages, where only data deployed to
+production are measured.
Specifically, if your CI is not set up and you have not defined a `production`
-[environment], then you will not have any data.
+[environment], then you will not have any data for those stages.
Below you can see in more detail what the various stages of Cycle Analytics mean.
@@ -76,9 +75,8 @@ Here's a little explanation of how this works behind the scenes:
`<issue, merge request>` pair, the merge request has the [issue closing pattern]
for the corresponding issue. All other issues and merge requests are **not**
considered.
-1. Then the <issue, merge request> pairs are filtered out. Any merge request
- that has **not** been deployed to production in the last XX days (specified
- by the UI - default is 90 days) prohibits these pairs from being considered.
+1. Then the <issue, merge request> pairs are filtered out by last XX days (specified
+ by the UI - default is 90 days). So it prohibits these pairs from being considered.
1. For the remaining `<issue, merge request>` pairs, we check the information that
we need for the stages, like issue creation date, merge request merge time,
etc.
@@ -86,8 +84,8 @@ Here's a little explanation of how this works behind the scenes:
To sum up, anything that doesn't follow the [GitLab flow] won't be tracked at all.
So, if a merge request doesn't close an issue or an issue is not labeled with a
label present in the Issue Board or assigned a milestone or a project has no
-`production` environment, the Cycle Analytics dashboard won't present any data
-at all.
+`production` environment (for staging and production stages), the Cycle Analytics
+dashboard won't present any data at all.
## Example workflow
diff --git a/doc/user/project/git_attributes.md b/doc/user/project/git_attributes.md
new file mode 100644
index 00000000000..21ef94e61f7
--- /dev/null
+++ b/doc/user/project/git_attributes.md
@@ -0,0 +1,22 @@
+# Git Attributes
+
+GitLab supports defining custom [Git attributes][gitattributes] such as what
+files to treat as binary, and what language to use for syntax highlighting
+diffs.
+
+To define these attributes, create a file called `.gitattributes` in the root
+directory of your repository and push it to the default branch of your project.
+
+## Encoding Requirements
+
+The `.gitattributes` file _must_ be encoded in UTF-8 and _must not_ contain a
+Byte Order Mark. If a different encoding is used, the file's contents will be
+ignored.
+
+## Syntax Highlighting
+
+The `.gitattributes` file can be used to define which language to use when
+syntax highlighting files and diffs. See ["Syntax
+Highlighting"](highlighting.md) for more information.
+
+[gitattributes]: https://git-scm.com/docs/gitattributes
diff --git a/doc/user/project/merge_requests/merge_when_build_succeeds.md b/doc/user/project/merge_requests/merge_when_build_succeeds.md
index 011f9cbc381..c138061fd40 100644
--- a/doc/user/project/merge_requests/merge_when_build_succeeds.md
+++ b/doc/user/project/merge_requests/merge_when_build_succeeds.md
@@ -1,16 +1,16 @@
# Merge When Build Succeeds
When reviewing a merge request that looks ready to merge but still has one or
-more CI builds running, you can set it to be merged automatically when all
-builds succeed. This way, you don't have to wait for the builds to finish and
-remember to merge the request manually.
+more CI builds running, you can set it to be merged automatically when the
+builds pipeline succeed. This way, you don't have to wait for the builds to
+finish and remember to merge the request manually.
![Enable](img/merge_when_build_succeeds_enable.png)
When you hit the "Merge When Build Succeeds" button, the status of the merge
request will be updated to represent the impending merge. If you cannot wait
-for the build to succeed and want to merge immediately, this option is available
-in the dropdown menu on the right of the main button.
+for the pipeline to succeed and want to merge immediately, this option is
+available in the dropdown menu on the right of the main button.
Both team developers and the author of the merge request have the option to
cancel the automatic merge if they find a reason why it shouldn't be merged
@@ -18,9 +18,9 @@ after all.
![Status](img/merge_when_build_succeeds_status.png)
-When the build succeeds, the merge request will automatically be merged. When
-the build fails, the author gets a chance to retry any failed builds, or to
-push new commits to fix the failure.
+When the pipeline succeeds, the merge request will automatically be merged.
+When the pipeline fails, the author gets a chance to retry any failed builds,
+or to push new commits to fix the failure.
When the builds are retried and succeed on the second try, the merge request
will automatically be merged after all. When the merge request is updated with
@@ -40,7 +40,7 @@ hit **Save** for the changes to take effect.
![Only allow merge if build succeeds settings](img/merge_when_build_succeeds_only_if_succeeds_settings.png)
-From now on, every time the build fails you will not be able to merge the merge
-request from the UI, until you make the build pass.
+From now on, every time the pipelinefails you will not be able to merge the
+merge request from the UI, until you make all relevant builds pass.
![Only allow merge if build succeeds msg](img/merge_when_build_succeeds_only_if_succeeds_msg.png)
diff --git a/lib/api/api.rb b/lib/api/api.rb
index 99722a0a65c..920d098bb24 100644
--- a/lib/api/api.rb
+++ b/lib/api/api.rb
@@ -46,7 +46,6 @@ module API
mount ::API::Boards
mount ::API::Keys
mount ::API::Labels
- mount ::API::LicenseTemplates
mount ::API::Lint
mount ::API::Members
mount ::API::MergeRequests
@@ -73,6 +72,7 @@ module API
mount ::API::Triggers
mount ::API::Users
mount ::API::Variables
+ mount ::API::Version
route :any, '*path' do
error!('404 Not Found', 404)
diff --git a/lib/api/license_templates.rb b/lib/api/license_templates.rb
deleted file mode 100644
index d0552299ed0..00000000000
--- a/lib/api/license_templates.rb
+++ /dev/null
@@ -1,58 +0,0 @@
-module API
- # License Templates API
- class LicenseTemplates < Grape::API
- PROJECT_TEMPLATE_REGEX =
- /[\<\{\[]
- (project|description|
- one\sline\s.+\swhat\sit\sdoes\.) # matching the start and end is enough here
- [\>\}\]]/xi.freeze
- YEAR_TEMPLATE_REGEX = /[<{\[](year|yyyy)[>}\]]/i.freeze
- FULLNAME_TEMPLATE_REGEX =
- /[\<\{\[]
- (fullname|name\sof\s(author|copyright\sowner))
- [\>\}\]]/xi.freeze
-
- # Get the list of the available license templates
- #
- # Parameters:
- # popular - Filter licenses to only the popular ones
- #
- # Example Request:
- # GET /licenses
- # GET /licenses?popular=1
- get 'licenses' do
- options = {
- featured: params[:popular].present? ? true : nil
- }
- present Licensee::License.all(options), with: Entities::RepoLicense
- end
-
- # Get text for specific license
- #
- # Parameters:
- # key (required) - The key of a license
- # project - Copyrighted project name
- # fullname - Full name of copyright holder
- #
- # Example Request:
- # GET /licenses/mit
- #
- get 'licenses/:key', requirements: { key: /[\w\.-]+/ } do
- required_attributes! [:key]
-
- not_found!('License') unless Licensee::License.find(params[:key])
-
- # We create a fresh Licensee::License object since we'll modify its
- # content in place below.
- license = Licensee::License.new(params[:key])
-
- license.content.gsub!(YEAR_TEMPLATE_REGEX, Time.now.year.to_s)
- license.content.gsub!(PROJECT_TEMPLATE_REGEX, params[:project]) if params[:project].present?
-
- fullname = params[:fullname].presence || current_user.try(:name)
- license.content.gsub!(FULLNAME_TEMPLATE_REGEX, fullname) if fullname
-
- present license, with: Entities::RepoLicense
- end
- end
-end
diff --git a/lib/api/projects.rb b/lib/api/projects.rb
index c24e8e8bd9b..da16e24d7ea 100644
--- a/lib/api/projects.rb
+++ b/lib/api/projects.rb
@@ -416,6 +416,12 @@ module API
required_attributes! [:group_id, :group_access]
attrs = attributes_for_keys [:group_id, :group_access, :expires_at]
+ group = Group.find_by_id(attrs[:group_id])
+
+ unless group && can?(current_user, :read_group, group)
+ not_found!('Group')
+ end
+
unless user_project.allowed_to_share_with_group?
return render_api_error!("The project sharing with group is disabled", 400)
end
diff --git a/lib/api/templates.rb b/lib/api/templates.rb
index b9e718147e1..8a53d9c0095 100644
--- a/lib/api/templates.rb
+++ b/lib/api/templates.rb
@@ -1,39 +1,115 @@
module API
class Templates < Grape::API
GLOBAL_TEMPLATE_TYPES = {
- gitignores: Gitlab::Template::GitignoreTemplate,
- gitlab_ci_ymls: Gitlab::Template::GitlabCiYmlTemplate
+ gitignores: {
+ klass: Gitlab::Template::GitignoreTemplate,
+ gitlab_version: 8.8
+ },
+ gitlab_ci_ymls: {
+ klass: Gitlab::Template::GitlabCiYmlTemplate,
+ gitlab_version: 8.9
+ }
}.freeze
+ PROJECT_TEMPLATE_REGEX =
+ /[\<\{\[]
+ (project|description|
+ one\sline\s.+\swhat\sit\sdoes\.) # matching the start and end is enough here
+ [\>\}\]]/xi.freeze
+ YEAR_TEMPLATE_REGEX = /[<{\[](year|yyyy)[>}\]]/i.freeze
+ FULLNAME_TEMPLATE_REGEX =
+ /[\<\{\[]
+ (fullname|name\sof\s(author|copyright\sowner))
+ [\>\}\]]/xi.freeze
+ DEPRECATION_MESSAGE = ' This endpoint is deprecated and will be removed in GitLab 9.0.'.freeze
helpers do
+ def parsed_license_template
+ # We create a fresh Licensee::License object since we'll modify its
+ # content in place below.
+ template = Licensee::License.new(params[:name])
+
+ template.content.gsub!(YEAR_TEMPLATE_REGEX, Time.now.year.to_s)
+ template.content.gsub!(PROJECT_TEMPLATE_REGEX, params[:project]) if params[:project].present?
+
+ fullname = params[:fullname].presence || current_user.try(:name)
+ template.content.gsub!(FULLNAME_TEMPLATE_REGEX, fullname) if fullname
+ template
+ end
+
def render_response(template_type, template)
not_found!(template_type.to_s.singularize) unless template
present template, with: Entities::Template
end
end
- GLOBAL_TEMPLATE_TYPES.each do |template_type, klass|
- # Get the list of the available template
- #
- # Example Request:
- # GET /gitignores
- # GET /gitlab_ci_ymls
- get template_type.to_s do
- present klass.all, with: Entities::TemplatesList
- end
-
- # Get the text for a specific template present in local filesystem
- #
- # Parameters:
- # name (required) - The name of a template
- #
- # Example Request:
- # GET /gitignores/Elixir
- # GET /gitlab_ci_ymls/Ruby
- get "#{template_type}/:name" do
- required_attributes! [:name]
- new_template = klass.find(params[:name])
- render_response(template_type, new_template)
+ { "licenses" => :deprecated, "templates/licenses" => :ok }.each do |route, status|
+ desc 'Get the list of the available license template' do
+ detailed_desc = 'This feature was introduced in GitLab 8.7.'
+ detailed_desc << DEPRECATION_MESSAGE unless status == :ok
+ detail detailed_desc
+ success Entities::RepoLicense
+ end
+ params do
+ optional :popular, type: Boolean, desc: 'If passed, returns only popular licenses'
+ end
+ get route do
+ options = {
+ featured: declared(params).popular.present? ? true : nil
+ }
+ present Licensee::License.all(options), with: Entities::RepoLicense
+ end
+ end
+
+ { "licenses/:name" => :deprecated, "templates/licenses/:name" => :ok }.each do |route, status|
+ desc 'Get the text for a specific license' do
+ detailed_desc = 'This feature was introduced in GitLab 8.7.'
+ detailed_desc << DEPRECATION_MESSAGE unless status == :ok
+ detail detailed_desc
+ success Entities::RepoLicense
+ end
+ params do
+ requires :name, type: String, desc: 'The name of the template'
+ end
+ get route, requirements: { name: /[\w\.-]+/ } do
+ not_found!('License') unless Licensee::License.find(declared(params).name)
+
+ template = parsed_license_template
+
+ present template, with: Entities::RepoLicense
+ end
+ end
+
+ GLOBAL_TEMPLATE_TYPES.each do |template_type, properties|
+ klass = properties[:klass]
+ gitlab_version = properties[:gitlab_version]
+
+ { template_type => :deprecated, "templates/#{template_type}" => :ok }.each do |route, status|
+ desc 'Get the list of the available template' do
+ detailed_desc = "This feature was introduced in GitLab #{gitlab_version}."
+ detailed_desc << DEPRECATION_MESSAGE unless status == :ok
+ detail detailed_desc
+ success Entities::TemplatesList
+ end
+ get route do
+ present klass.all, with: Entities::TemplatesList
+ end
+ end
+
+ { "#{template_type}/:name" => :deprecated, "templates/#{template_type}/:name" => :ok }.each do |route, status|
+ desc 'Get the text for a specific template present in local filesystem' do
+ detailed_desc = "This feature was introduced in GitLab #{gitlab_version}."
+ detailed_desc << DEPRECATION_MESSAGE unless status == :ok
+ detail detailed_desc
+ success Entities::Template
+ end
+ params do
+ requires :name, type: String, desc: 'The name of the template'
+ end
+ get route do
+ new_template = klass.find(declared(params).name)
+
+ render_response(template_type, new_template)
+ end
end
end
end
diff --git a/lib/api/users.rb b/lib/api/users.rb
index 18c4cad09ae..e868f628404 100644
--- a/lib/api/users.rb
+++ b/lib/api/users.rb
@@ -321,6 +321,26 @@ module API
user.activate
end
end
+
+ desc 'Get contribution events of a specified user' do
+ detail 'This feature was introduced in GitLab 8.13.'
+ success Entities::Event
+ end
+ params do
+ requires :id, type: String, desc: 'The user ID'
+ end
+ get ':id/events' do
+ user = User.find_by(id: declared(params).id)
+ not_found!('User') unless user
+
+ events = user.recent_events.
+ merge(ProjectsFinder.new.execute(current_user)).
+ references(:project).
+ with_associations.
+ page(params[:page])
+
+ present paginate(events), with: Entities::Event
+ end
end
resource :user do
diff --git a/lib/api/variables.rb b/lib/api/variables.rb
index f6495071a11..b9fb3c21dbb 100644
--- a/lib/api/variables.rb
+++ b/lib/api/variables.rb
@@ -4,27 +4,29 @@ module API
before { authenticate! }
before { authorize! :admin_build, user_project }
+ params do
+ requires :id, type: String, desc: 'The ID of a project'
+ end
+
resource :projects do
- # Get project variables
- #
- # Parameters:
- # id (required) - The ID of a project
- # page (optional) - The page number for pagination
- # per_page (optional) - The value of items per page to show
- # Example Request:
- # GET /projects/:id/variables
+ desc 'Get project variables' do
+ success Entities::Variable
+ end
+ params do
+ optional :page, type: Integer, desc: 'The page number for pagination'
+ optional :per_page, type: Integer, desc: 'The value of items per page to show'
+ end
get ':id/variables' do
variables = user_project.variables
present paginate(variables), with: Entities::Variable
end
- # Get specific variable of a project
- #
- # Parameters:
- # id (required) - The ID of a project
- # key (required) - The `key` of variable
- # Example Request:
- # GET /projects/:id/variables/:key
+ desc 'Get a specific variable from a project' do
+ success Entities::Variable
+ end
+ params do
+ requires :key, type: String, desc: 'The key of the variable'
+ end
get ':id/variables/:key' do
key = params[:key]
variable = user_project.variables.find_by(key: key.to_s)
@@ -34,18 +36,15 @@ module API
present variable, with: Entities::Variable
end
- # Create a new variable in project
- #
- # Parameters:
- # id (required) - The ID of a project
- # key (required) - The key of variable
- # value (required) - The value of variable
- # Example Request:
- # POST /projects/:id/variables
+ desc 'Create a new variable in a project' do
+ success Entities::Variable
+ end
+ params do
+ requires :key, type: String, desc: 'The key of the variable'
+ requires :value, type: String, desc: 'The value of the variable'
+ end
post ':id/variables' do
- required_attributes! [:key, :value]
-
- variable = user_project.variables.create(key: params[:key], value: params[:value])
+ variable = user_project.variables.create(declared(params, include_parent_namespaces: false).to_h)
if variable.valid?
present variable, with: Entities::Variable
@@ -54,41 +53,37 @@ module API
end
end
- # Update existing variable of a project
- #
- # Parameters:
- # id (required) - The ID of a project
- # key (optional) - The `key` of variable
- # value (optional) - New value for `value` field of variable
- # Example Request:
- # PUT /projects/:id/variables/:key
+ desc 'Update an existing variable from a project' do
+ success Entities::Variable
+ end
+ params do
+ optional :key, type: String, desc: 'The key of the variable'
+ optional :value, type: String, desc: 'The value of the variable'
+ end
put ':id/variables/:key' do
- variable = user_project.variables.find_by(key: params[:key].to_s)
+ variable = user_project.variables.find_by(key: params[:key])
return not_found!('Variable') unless variable
- attrs = attributes_for_keys [:value]
- if variable.update(attrs)
+ if variable.update(value: params[:value])
present variable, with: Entities::Variable
else
render_validation_error!(variable)
end
end
- # Delete existing variable of a project
- #
- # Parameters:
- # id (required) - The ID of a project
- # key (required) - The ID of a variable
- # Example Request:
- # DELETE /projects/:id/variables/:key
+ desc 'Delete an existing variable from a project' do
+ success Entities::Variable
+ end
+ params do
+ requires :key, type: String, desc: 'The key of the variable'
+ end
delete ':id/variables/:key' do
- variable = user_project.variables.find_by(key: params[:key].to_s)
+ variable = user_project.variables.find_by(key: params[:key])
return not_found!('Variable') unless variable
- variable.destroy
- present variable, with: Entities::Variable
+ present variable.destroy, with: Entities::Variable
end
end
end
diff --git a/lib/api/version.rb b/lib/api/version.rb
new file mode 100644
index 00000000000..9ba576bd828
--- /dev/null
+++ b/lib/api/version.rb
@@ -0,0 +1,12 @@
+module API
+ class Version < Grape::API
+ before { authenticate! }
+
+ desc 'Get the version information of the GitLab instance.' do
+ detail 'This feature was introduced in GitLab 8.13.'
+ end
+ get '/version' do
+ { version: Gitlab::VERSION, revision: Gitlab::REVISION }
+ end
+ end
+end
diff --git a/lib/extracts_path.rb b/lib/extracts_path.rb
index a4558d157c0..e4d996a3fb6 100644
--- a/lib/extracts_path.rb
+++ b/lib/extracts_path.rb
@@ -52,8 +52,7 @@ module ExtractsPath
# Append a trailing slash if we only get a ref and no file path
id += '/' unless id.ends_with?('/')
- valid_refs = @project.repository.ref_names
- valid_refs.select! { |v| id.start_with?("#{v}/") }
+ valid_refs = ref_names.select { |v| id.start_with?("#{v}/") }
if valid_refs.length == 0
# No exact ref match, so just try our best
@@ -74,6 +73,19 @@ module ExtractsPath
pair
end
+ # If we have an ID of 'foo.atom', and the controller provides Atom and HTML
+ # formats, then we have to check if the request was for the Atom version of
+ # the ID without the '.atom' suffix, or the HTML version of the ID including
+ # the suffix. We only check this if the version including the suffix doesn't
+ # match, so it is possible to create a branch which has an unroutable Atom
+ # feed.
+ def extract_ref_without_atom(id)
+ id_without_atom = id.sub(/\.atom$/, '')
+ valid_refs = ref_names.select { |v| "#{id_without_atom}/".start_with?("#{v}/") }
+
+ valid_refs.max_by(&:length)
+ end
+
# Assigns common instance variables for views working with Git tree-ish objects
#
# Assignments are:
@@ -86,6 +98,10 @@ module ExtractsPath
# If the :id parameter appears to be requesting a specific response format,
# that will be handled as well.
#
+ # If there is no path and the ref doesn't exist in the repo, try to resolve
+ # the ref without an '.atom' suffix. If _that_ ref is found, set the request's
+ # format to Atom manually.
+ #
# Automatically renders `not_found!` if a valid tree path could not be
# resolved (e.g., when a user inserts an invalid path or ref).
def assign_ref_vars
@@ -103,6 +119,13 @@ module ExtractsPath
@commit = @repo.commit(@options[:extended_sha1])
end
+ if @path.empty? && !@commit
+ @id = @ref = extract_ref_without_atom(@id)
+ @commit = @repo.commit(@ref)
+
+ request.format = :atom if @commit
+ end
+
raise InvalidPathError unless @commit
@hex_path = Digest::SHA1.hexdigest(@path)
@@ -125,4 +148,10 @@ module ExtractsPath
id += "/" + params[:path] unless params[:path].blank?
id
end
+
+ def ref_names
+ return [] unless @project
+
+ @ref_names ||= @project.repository.ref_names
+ end
end
diff --git a/spec/controllers/projects/commit_controller_spec.rb b/spec/controllers/projects/commit_controller_spec.rb
index 7e440193d7b..646b097d74e 100644
--- a/spec/controllers/projects/commit_controller_spec.rb
+++ b/spec/controllers/projects/commit_controller_spec.rb
@@ -102,15 +102,16 @@ describe Projects::CommitController do
describe "as patch" do
include_examples "export as", :patch
let(:format) { :patch }
+ let(:commit2) { project.commit('498214de67004b1da3d820901307bed2a68a8ef6') }
it "is a git email patch" do
- go(id: commit.id, format: format)
+ go(id: commit2.id, format: format)
- expect(response.body).to start_with("From #{commit.id}")
+ expect(response.body).to start_with("From #{commit2.id}")
end
it "contains a git diff" do
- go(id: commit.id, format: format)
+ go(id: commit2.id, format: format)
expect(response.body).to match(/^diff --git/)
end
@@ -135,6 +136,8 @@ describe Projects::CommitController do
describe "GET branches" do
it "contains branch and tags information" do
+ commit = project.commit('5937ac0a7beb003549fc5fd26fc247adbce4a52e')
+
get(:branches,
namespace_id: project.namespace.to_param,
project_id: project.to_param,
@@ -254,16 +257,17 @@ describe Projects::CommitController do
end
let(:existing_path) { '.gitmodules' }
+ let(:commit2) { project.commit('5937ac0a7beb003549fc5fd26fc247adbce4a52e') }
context 'when the commit exists' do
context 'when the user has access to the project' do
context 'when the path exists in the diff' do
it 'enables diff notes' do
- diff_for_path(id: commit.id, old_path: existing_path, new_path: existing_path)
+ diff_for_path(id: commit2.id, old_path: existing_path, new_path: existing_path)
expect(assigns(:diff_notes_disabled)).to be_falsey
expect(assigns(:comments_target)).to eq(noteable_type: 'Commit',
- commit_id: commit.id)
+ commit_id: commit2.id)
end
it 'only renders the diffs for the path given' do
@@ -272,7 +276,7 @@ describe Projects::CommitController do
meth.call(diffs)
end
- diff_for_path(id: commit.id, old_path: existing_path, new_path: existing_path)
+ diff_for_path(id: commit2.id, old_path: existing_path, new_path: existing_path)
end
end
diff --git a/spec/controllers/projects/commits_controller_spec.rb b/spec/controllers/projects/commits_controller_spec.rb
index 2518a48e336..1ac7e03a2db 100644
--- a/spec/controllers/projects/commits_controller_spec.rb
+++ b/spec/controllers/projects/commits_controller_spec.rb
@@ -10,15 +10,38 @@ describe Projects::CommitsController do
end
describe "GET show" do
- context "as atom feed" do
- it "renders as atom" do
- get(:show,
- namespace_id: project.namespace.to_param,
- project_id: project.to_param,
- id: "master",
- format: "atom")
- expect(response).to be_success
- expect(response.content_type).to eq('application/atom+xml')
+ context "when the ref name ends in .atom" do
+ render_views
+
+ context "when the ref does not exist with the suffix" do
+ it "renders as atom" do
+ get(:show,
+ namespace_id: project.namespace.to_param,
+ project_id: project.to_param,
+ id: "master.atom")
+
+ expect(response).to be_success
+ expect(response.content_type).to eq('application/atom+xml')
+ end
+ end
+
+ context "when the ref exists with the suffix" do
+ before do
+ commit = project.repository.commit('master')
+
+ allow_any_instance_of(Repository).to receive(:commit).and_call_original
+ allow_any_instance_of(Repository).to receive(:commit).with('master.atom').and_return(commit)
+
+ get(:show,
+ namespace_id: project.namespace.to_param,
+ project_id: project.to_param,
+ id: "master.atom")
+ end
+
+ it "renders as HTML" do
+ expect(response).to be_success
+ expect(response.content_type).to eq('text/html')
+ end
end
end
end
diff --git a/spec/controllers/projects/merge_requests_controller_spec.rb b/spec/controllers/projects/merge_requests_controller_spec.rb
index 742edd8ba3d..84298f8bef4 100644
--- a/spec/controllers/projects/merge_requests_controller_spec.rb
+++ b/spec/controllers/projects/merge_requests_controller_spec.rb
@@ -708,4 +708,52 @@ describe Projects::MergeRequestsController do
end
end
end
+
+ describe 'POST assign_related_issues' do
+ let(:issue1) { create(:issue, project: project) }
+ let(:issue2) { create(:issue, project: project) }
+
+ def post_assign_issues
+ merge_request.update!(description: "Closes #{issue1.to_reference} and #{issue2.to_reference}",
+ author: user,
+ source_branch: 'feature',
+ target_branch: 'master')
+
+ post :assign_related_issues,
+ namespace_id: project.namespace.to_param,
+ project_id: project.to_param,
+ id: merge_request.iid
+ end
+
+ it 'shows a flash message on success' do
+ post_assign_issues
+
+ expect(flash[:notice]).to eq '2 issues have been assigned to you'
+ end
+
+ it 'correctly pluralizes flash message on success' do
+ issue2.update!(assignee: user)
+
+ post_assign_issues
+
+ expect(flash[:notice]).to eq '1 issue has been assigned to you'
+ end
+
+ it 'calls MergeRequests::AssignIssuesService' do
+ expect(MergeRequests::AssignIssuesService).to receive(:new).
+ with(project, user, merge_request: merge_request).
+ and_return(double(execute: { count: 1 }))
+
+ post_assign_issues
+ end
+
+ it 'is skipped when not signed in' do
+ project.update!(visibility_level: Gitlab::VisibilityLevel::PUBLIC)
+ sign_out(:user)
+
+ expect(MergeRequests::AssignIssuesService).not_to receive(:new)
+
+ post_assign_issues
+ end
+ end
end
diff --git a/spec/controllers/projects/tags_controller_spec.rb b/spec/controllers/projects/tags_controller_spec.rb
index a6995145cc1..5e661c2c41d 100644
--- a/spec/controllers/projects/tags_controller_spec.rb
+++ b/spec/controllers/projects/tags_controller_spec.rb
@@ -17,4 +17,18 @@ describe Projects::TagsController do
expect(assigns(:releases)).not_to include(invalid_release)
end
end
+
+ describe 'GET show' do
+ before { get :show, namespace_id: project.namespace.to_param, project_id: project.to_param, id: id }
+
+ context "valid tag" do
+ let(:id) { 'v1.0.0' }
+ it { is_expected.to respond_with(:success) }
+ end
+
+ context "invalid tag" do
+ let(:id) { 'latest' }
+ it { is_expected.to respond_with(:not_found) }
+ end
+ end
end
diff --git a/spec/features/merge_requests/assign_issues_spec.rb b/spec/features/merge_requests/assign_issues_spec.rb
new file mode 100644
index 00000000000..43cc6f2a2a7
--- /dev/null
+++ b/spec/features/merge_requests/assign_issues_spec.rb
@@ -0,0 +1,51 @@
+require 'rails_helper'
+
+feature 'Merge request issue assignment', js: true, feature: true do
+ let(:user) { create(:user) }
+ let(:project) { create(:project, :public) }
+ let(:issue1) { create(:issue, project: project) }
+ let(:issue2) { create(:issue, project: project) }
+ let(:merge_request) { create(:merge_request, :simple, source_project: project, author: user, description: "fixes #{issue1.to_reference} and #{issue2.to_reference}") }
+ let(:service) { MergeRequests::AssignIssuesService.new(merge_request, user, user, project) }
+
+ before do
+ project.team << [user, :developer]
+ end
+
+ def visit_merge_request(current_user = nil)
+ login_as(current_user || user)
+ visit namespace_project_merge_request_path(project.namespace, project, merge_request)
+ end
+
+ context 'logged in as author' do
+ scenario 'updates related issues' do
+ visit_merge_request
+ click_link "Assign yourself to these issues"
+
+ expect(page).to have_content "2 issues have been assigned to you"
+ end
+
+ it 'returns user to the merge request' do
+ visit_merge_request
+ click_link "Assign yourself to these issues"
+
+ expect(page).to have_content merge_request.description
+ end
+
+ it "doesn't display if related issues are already assigned" do
+ [issue1, issue2].each { |issue| issue.update!(assignee: user) }
+
+ visit_merge_request
+
+ expect(page).not_to have_content "Assign yourself"
+ end
+ end
+
+ context 'not MR author' do
+ it "doesn't not show assignment link" do
+ visit_merge_request(create(:user))
+
+ expect(page).not_to have_content "Assign yourself"
+ end
+ end
+end
diff --git a/spec/features/merge_requests/merge_when_build_succeeds_spec.rb b/spec/features/merge_requests/merge_when_build_succeeds_spec.rb
index 60bc07bd1a0..bc2b0ff3e2c 100644
--- a/spec/features/merge_requests/merge_when_build_succeeds_spec.rb
+++ b/spec/features/merge_requests/merge_when_build_succeeds_spec.rb
@@ -2,18 +2,26 @@ require 'spec_helper'
feature 'Merge When Build Succeeds', feature: true, js: true do
let(:user) { create(:user) }
+ let(:project) { create(:project, :public) }
- let(:project) { create(:project, :public) }
- let(:merge_request) { create(:merge_request_with_diffs, source_project: project, author: user, title: "Bug NS-04") }
+ let(:merge_request) do
+ create(:merge_request_with_diffs, source_project: project,
+ author: user,
+ title: 'Bug NS-04')
+ end
- before do
- project.team << [user, :master]
- project.enable_ci
+ let(:pipeline) do
+ create(:ci_pipeline, project: project,
+ sha: merge_request.diff_head_sha,
+ ref: merge_request.source_branch)
end
- context "Active build for Merge Request" do
- let!(:pipeline) { create(:ci_pipeline, project: project, sha: merge_request.diff_head_sha, ref: merge_request.source_branch) }
- let!(:ci_build) { create(:ci_build, pipeline: pipeline) }
+ before { project.team << [user, :master] }
+
+ context 'when there is active build for merge request' do
+ background do
+ create(:ci_build, pipeline: pipeline)
+ end
before do
login_as user
@@ -41,26 +49,30 @@ feature 'Merge When Build Succeeds', feature: true, js: true do
end
end
- context 'When it is enabled' do
+ context 'when merge when build succeeds is enabled' do
let(:merge_request) do
- create(:merge_request_with_diffs, :simple, source_project: project, author: user,
- merge_user: user, title: "MepMep", merge_when_build_succeeds: true)
+ create(:merge_request_with_diffs, :simple, source_project: project,
+ author: user,
+ merge_user: user,
+ title: 'MepMep',
+ merge_when_build_succeeds: true)
end
- let!(:pipeline) { create(:ci_pipeline, project: project, sha: merge_request.diff_head_sha, ref: merge_request.source_branch) }
- let!(:ci_build) { create(:ci_build, pipeline: pipeline) }
+ let!(:build) do
+ create(:ci_build, pipeline: pipeline)
+ end
before do
login_as user
visit_merge_request(merge_request)
end
- it 'cancels the automatic merge' do
+ it 'allows to cancel the automatic merge' do
click_link "Cancel Automatic Merge"
expect(page).to have_button "Merge When Build Succeeds"
- visit_merge_request(merge_request) # Needed to refresh the page
+ visit_merge_request(merge_request) # refresh the page
expect(page).to have_content "Canceled the automatic merge"
end
@@ -70,10 +82,21 @@ feature 'Merge When Build Succeeds', feature: true, js: true do
click_link "Remove Source Branch When Merged"
expect(page).to have_content "The source branch will be removed"
end
+
+ context 'when build succeeds' do
+ background { build.success }
+
+ it 'merges merge request' do
+ visit_merge_request(merge_request) # refresh the page
+
+ expect(page).to have_content 'The changes were merged'
+ expect(merge_request.reload).to be_merged
+ end
+ end
end
- context 'Build is not active' do
- it "does not allow for enabling" do
+ context 'when build is not active' do
+ it "does not allow to enable merge when build succeeds" do
visit_merge_request(merge_request)
expect(page).not_to have_link "Merge When Build Succeeds"
end
diff --git a/spec/features/projects/guest_navigation_menu_spec.rb b/spec/features/projects/guest_navigation_menu_spec.rb
new file mode 100644
index 00000000000..c22441f8929
--- /dev/null
+++ b/spec/features/projects/guest_navigation_menu_spec.rb
@@ -0,0 +1,28 @@
+require 'spec_helper'
+
+describe "Guest navigation menu" do
+ let(:project) { create :empty_project, :private }
+ let(:guest) { create :user }
+
+ before do
+ project.team << [guest, :guest]
+
+ login_as(guest)
+ end
+
+ it "shows allowed tabs only" do
+ visit namespace_project_path(project.namespace, project)
+
+ within(".nav-links") do
+ expect(page).to have_content 'Project'
+ expect(page).to have_content 'Activity'
+ expect(page).to have_content 'Issues'
+ expect(page).to have_content 'Wiki'
+
+ expect(page).not_to have_content 'Repository'
+ expect(page).not_to have_content 'Pipelines'
+ expect(page).not_to have_content 'Graphs'
+ expect(page).not_to have_content 'Merge Requests'
+ end
+ end
+end
diff --git a/spec/features/security/project/private_access_spec.rb b/spec/features/security/project/private_access_spec.rb
index ccb5c06dab0..79417c769a8 100644
--- a/spec/features/security/project/private_access_spec.rb
+++ b/spec/features/security/project/private_access_spec.rb
@@ -203,7 +203,7 @@ describe "Private Project Access", feature: true do
it { is_expected.to be_allowed_for master }
it { is_expected.to be_allowed_for developer }
it { is_expected.to be_allowed_for reporter }
- it { is_expected.to be_allowed_for guest }
+ it { is_expected.to be_denied_for guest }
it { is_expected.to be_denied_for :user }
it { is_expected.to be_denied_for :external }
it { is_expected.to be_denied_for :visitor }
diff --git a/spec/helpers/search_helper_spec.rb b/spec/helpers/search_helper_spec.rb
index c5b5aa8c445..64aa41020c9 100644
--- a/spec/helpers/search_helper_spec.rb
+++ b/spec/helpers/search_helper_spec.rb
@@ -19,7 +19,7 @@ describe SearchHelper do
expect(subject.filename).to eq('CHANGELOG')
expect(subject.basename).to eq('CHANGELOG')
expect(subject.ref).to eq('master')
- expect(subject.startline).to eq(186)
+ expect(subject.startline).to eq(188)
expect(subject.data.lines[2]).to eq(" - Feature: Replace teams with group membership\n")
end
diff --git a/spec/lib/extracts_path_spec.rb b/spec/lib/extracts_path_spec.rb
index e10c1f5c547..0e85e302f29 100644
--- a/spec/lib/extracts_path_spec.rb
+++ b/spec/lib/extracts_path_spec.rb
@@ -6,6 +6,7 @@ describe ExtractsPath, lib: true do
include Gitlab::Routing.url_helpers
let(:project) { double('project') }
+ let(:request) { double('request') }
before do
@project = project
@@ -15,9 +16,10 @@ describe ExtractsPath, lib: true do
allow(project).to receive(:repository).and_return(repo)
allow(project).to receive(:path_with_namespace).
and_return('gitlab/gitlab-ci')
+ allow(request).to receive(:format=)
end
- describe '#assign_ref' do
+ describe '#assign_ref_vars' do
let(:ref) { sample_commit[:id] }
let(:params) { { path: sample_commit[:line_code_path], ref: ref } }
@@ -61,6 +63,75 @@ describe ExtractsPath, lib: true do
expect(@id).to eq(get_id)
end
end
+
+ context 'ref only exists without .atom suffix' do
+ context 'with a path' do
+ let(:params) { { ref: 'v1.0.0.atom', path: 'README.md' } }
+
+ it 'renders a 404' do
+ expect(self).to receive(:render_404)
+
+ assign_ref_vars
+ end
+ end
+
+ context 'without a path' do
+ let(:params) { { ref: 'v1.0.0.atom' } }
+ before { assign_ref_vars }
+
+ it 'sets the un-suffixed version as @ref' do
+ expect(@ref).to eq('v1.0.0')
+ end
+
+ it 'sets the request format to Atom' do
+ expect(request).to have_received(:format=).with(:atom)
+ end
+ end
+ end
+
+ context 'ref exists with .atom suffix' do
+ context 'with a path' do
+ let(:params) { { ref: 'master.atom', path: 'README.md' } }
+
+ before do
+ repository = @project.repository
+ allow(repository).to receive(:commit).and_call_original
+ allow(repository).to receive(:commit).with('master.atom').and_return(repository.commit('master'))
+
+ assign_ref_vars
+ end
+
+ it 'sets the suffixed version as @ref' do
+ expect(@ref).to eq('master.atom')
+ end
+
+ it 'does not change the request format' do
+ expect(request).not_to have_received(:format=)
+ end
+ end
+
+ context 'without a path' do
+ let(:params) { { ref: 'master.atom' } }
+
+ before do
+ repository = @project.repository
+ allow(repository).to receive(:commit).and_call_original
+ allow(repository).to receive(:commit).with('master.atom').and_return(repository.commit('master'))
+ end
+
+ it 'sets the suffixed version as @ref' do
+ assign_ref_vars
+
+ expect(@ref).to eq('master.atom')
+ end
+
+ it 'does not change the request format' do
+ expect(request).not_to receive(:format=)
+
+ assign_ref_vars
+ end
+ end
+ end
end
describe '#extract_ref' do
@@ -115,4 +186,18 @@ describe ExtractsPath, lib: true do
end
end
end
+
+ describe '#extract_ref_without_atom' do
+ it 'ignores any matching refs suffixed with atom' do
+ expect(extract_ref_without_atom('master.atom')).to eq('master')
+ end
+
+ it 'returns the longest matching ref' do
+ expect(extract_ref_without_atom('release/app/v1.0.0.atom')).to eq('release/app/v1.0.0')
+ end
+
+ it 'returns nil if there are no matching refs' do
+ expect(extract_ref_without_atom('foo.atom')).to eq(nil)
+ end
+ end
end
diff --git a/spec/lib/gitlab/data_builder/push_spec.rb b/spec/lib/gitlab/data_builder/push_spec.rb
index b73434e8dd7..a379f798a16 100644
--- a/spec/lib/gitlab/data_builder/push_spec.rb
+++ b/spec/lib/gitlab/data_builder/push_spec.rb
@@ -8,13 +8,13 @@ describe Gitlab::DataBuilder::Push, lib: true do
let(:data) { described_class.build_sample(project, user) }
it { expect(data).to be_a(Hash) }
- it { expect(data[:before]).to eq('6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9') }
- it { expect(data[:after]).to eq('5937ac0a7beb003549fc5fd26fc247adbce4a52e') }
+ it { expect(data[:before]).to eq('1b12f15a11fc6e62177bef08f47bc7b5ce50b141') }
+ it { expect(data[:after]).to eq('b83d6e391c22777fca1ed3012fce84f633d7fed0') }
it { expect(data[:ref]).to eq('refs/heads/master') }
it { expect(data[:commits].size).to eq(3) }
it { expect(data[:total_commits_count]).to eq(3) }
- it { expect(data[:commits].first[:added]).to eq(['gitlab-grack']) }
- it { expect(data[:commits].first[:modified]).to eq(['.gitmodules']) }
+ it { expect(data[:commits].first[:added]).to eq(['bar/branch-test.txt']) }
+ it { expect(data[:commits].first[:modified]).to eq([]) }
it { expect(data[:commits].first[:removed]).to eq([]) }
include_examples 'project hook data with deprecateds'
diff --git a/spec/mailers/notify_spec.rb b/spec/mailers/notify_spec.rb
index 0e4130e8a3a..c8207e58e90 100644
--- a/spec/mailers/notify_spec.rb
+++ b/spec/mailers/notify_spec.rb
@@ -628,7 +628,7 @@ describe Notify do
it_behaves_like 'a user cannot unsubscribe through footer link'
it 'has the correct subject' do
- is_expected.to have_subject /#{commit.title} \(#{commit.short_id}\)/
+ is_expected.to have_subject /Re: #{project.name} | #{commit.title} \(#{commit.short_id}\)/
end
it 'contains a link to the commit' do
diff --git a/spec/models/appearance_spec.rb b/spec/models/appearance_spec.rb
index c5658bd26e1..0b72a2f979b 100644
--- a/spec/models/appearance_spec.rb
+++ b/spec/models/appearance_spec.rb
@@ -1,7 +1,7 @@
require 'rails_helper'
RSpec.describe Appearance, type: :model do
- subject { create(:appearance) }
+ subject { build(:appearance) }
it { is_expected.to be_valid }
diff --git a/spec/models/commit_spec.rb b/spec/models/commit_spec.rb
index d3e6a6648cc..51be3f36135 100644
--- a/spec/models/commit_spec.rb
+++ b/spec/models/commit_spec.rb
@@ -164,10 +164,10 @@ eos
let(:data) { commit.hook_attrs(with_changed_files: true) }
it { expect(data).to be_a(Hash) }
- it { expect(data[:message]).to include('Add submodule from gitlab.com') }
- it { expect(data[:timestamp]).to eq('2014-02-27T11:01:38+02:00') }
- it { expect(data[:added]).to eq(["gitlab-grack"]) }
- it { expect(data[:modified]).to eq([".gitmodules"]) }
+ it { expect(data[:message]).to include('adds bar folder and branch-test text file to check Repository merged_to_root_ref method') }
+ it { expect(data[:timestamp]).to eq('2016-09-27T14:37:46+00:00') }
+ it { expect(data[:added]).to eq(["bar/branch-test.txt"]) }
+ it { expect(data[:modified]).to eq([]) }
it { expect(data[:removed]).to eq([]) }
end
diff --git a/spec/models/cycle_analytics/code_spec.rb b/spec/models/cycle_analytics/code_spec.rb
index b9381e33914..7691d690db0 100644
--- a/spec/models/cycle_analytics/code_spec.rb
+++ b/spec/models/cycle_analytics/code_spec.rb
@@ -8,35 +8,69 @@ describe 'CycleAnalytics#code', feature: true do
let(:user) { create(:user, :admin) }
subject { CycleAnalytics.new(project, from: from_date) }
- generate_cycle_analytics_spec(
- phase: :code,
- data_fn: -> (context) { { issue: context.create(:issue, project: context.project) } },
- start_time_conditions: [["issue mentioned in a commit",
- -> (context, data) do
- context.create_commit_referencing_issue(data[:issue])
- end]],
- end_time_conditions: [["merge request that closes issue is created",
- -> (context, data) do
- context.create_merge_request_closing_issue(data[:issue])
- end]],
- post_fn: -> (context, data) do
- context.merge_merge_requests_closing_issue(data[:issue])
- context.deploy_master
- end)
-
- context "when a regular merge request (that doesn't close the issue) is created" do
- it "returns nil" do
- 5.times do
- issue = create(:issue, project: project)
-
- create_commit_referencing_issue(issue)
- create_merge_request_closing_issue(issue, message: "Closes nothing")
-
- merge_merge_requests_closing_issue(issue)
- deploy_master
+ context 'with deployment' do
+ generate_cycle_analytics_spec(
+ phase: :code,
+ data_fn: -> (context) { { issue: context.create(:issue, project: context.project) } },
+ start_time_conditions: [["issue mentioned in a commit",
+ -> (context, data) do
+ context.create_commit_referencing_issue(data[:issue])
+ end]],
+ end_time_conditions: [["merge request that closes issue is created",
+ -> (context, data) do
+ context.create_merge_request_closing_issue(data[:issue])
+ end]],
+ post_fn: -> (context, data) do
+ context.merge_merge_requests_closing_issue(data[:issue])
+ context.deploy_master
+ end)
+
+ context "when a regular merge request (that doesn't close the issue) is created" do
+ it "returns nil" do
+ 5.times do
+ issue = create(:issue, project: project)
+
+ create_commit_referencing_issue(issue)
+ create_merge_request_closing_issue(issue, message: "Closes nothing")
+
+ merge_merge_requests_closing_issue(issue)
+ deploy_master
+ end
+
+ expect(subject.code).to be_nil
end
+ end
+ end
- expect(subject.code).to be_nil
+ context 'without deployment' do
+ generate_cycle_analytics_spec(
+ phase: :code,
+ data_fn: -> (context) { { issue: context.create(:issue, project: context.project) } },
+ start_time_conditions: [["issue mentioned in a commit",
+ -> (context, data) do
+ context.create_commit_referencing_issue(data[:issue])
+ end]],
+ end_time_conditions: [["merge request that closes issue is created",
+ -> (context, data) do
+ context.create_merge_request_closing_issue(data[:issue])
+ end]],
+ post_fn: -> (context, data) do
+ context.merge_merge_requests_closing_issue(data[:issue])
+ end)
+
+ context "when a regular merge request (that doesn't close the issue) is created" do
+ it "returns nil" do
+ 5.times do
+ issue = create(:issue, project: project)
+
+ create_commit_referencing_issue(issue)
+ create_merge_request_closing_issue(issue, message: "Closes nothing")
+
+ merge_merge_requests_closing_issue(issue)
+ end
+
+ expect(subject.code).to be_nil
+ end
end
end
end
diff --git a/spec/models/cycle_analytics/issue_spec.rb b/spec/models/cycle_analytics/issue_spec.rb
index e9cc71254ab..f649b44d367 100644
--- a/spec/models/cycle_analytics/issue_spec.rb
+++ b/spec/models/cycle_analytics/issue_spec.rb
@@ -28,7 +28,6 @@ describe 'CycleAnalytics#issue', models: true do
if data[:issue].persisted?
context.create_merge_request_closing_issue(data[:issue].reload)
context.merge_merge_requests_closing_issue(data[:issue])
- context.deploy_master
end
end)
@@ -41,7 +40,6 @@ describe 'CycleAnalytics#issue', models: true do
create_merge_request_closing_issue(issue)
merge_merge_requests_closing_issue(issue)
- deploy_master
end
expect(subject.issue).to be_nil
diff --git a/spec/models/cycle_analytics/plan_spec.rb b/spec/models/cycle_analytics/plan_spec.rb
index 5b8c96dc992..2cdefbeef21 100644
--- a/spec/models/cycle_analytics/plan_spec.rb
+++ b/spec/models/cycle_analytics/plan_spec.rb
@@ -31,7 +31,6 @@ describe 'CycleAnalytics#plan', feature: true do
post_fn: -> (context, data) do
context.create_merge_request_closing_issue(data[:issue], source_branch: data[:branch_name])
context.merge_merge_requests_closing_issue(data[:issue])
- context.deploy_master
end)
context "when a regular label (instead of a list label) is added to the issue" do
@@ -44,7 +43,6 @@ describe 'CycleAnalytics#plan', feature: true do
create_merge_request_closing_issue(issue, source_branch: branch_name)
merge_merge_requests_closing_issue(issue)
- deploy_master
expect(subject.issue).to be_nil
end
diff --git a/spec/models/cycle_analytics/review_spec.rb b/spec/models/cycle_analytics/review_spec.rb
index b6e26d8f261..0ed080a42b1 100644
--- a/spec/models/cycle_analytics/review_spec.rb
+++ b/spec/models/cycle_analytics/review_spec.rb
@@ -19,14 +19,12 @@ describe 'CycleAnalytics#review', feature: true do
-> (context, data) do
context.merge_merge_requests_closing_issue(data[:issue])
end]],
- post_fn: -> (context, data) { context.deploy_master })
+ post_fn: nil)
context "when a regular merge request (that doesn't close the issue) is created and merged" do
it "returns nil" do
5.times do
MergeRequests::MergeService.new(project, user).execute(create(:merge_request))
-
- deploy_master
end
expect(subject.review).to be_nil
diff --git a/spec/models/cycle_analytics/test_spec.rb b/spec/models/cycle_analytics/test_spec.rb
index 89ace0b2742..02ddfeed9c1 100644
--- a/spec/models/cycle_analytics/test_spec.rb
+++ b/spec/models/cycle_analytics/test_spec.rb
@@ -20,7 +20,6 @@ describe 'CycleAnalytics#test', feature: true do
end_time_conditions: [["pipeline is finished", -> (context, data) { data[:pipeline].succeed! }]],
post_fn: -> (context, data) do
context.merge_merge_requests_closing_issue(data[:issue])
- context.deploy_master
end)
context "when the pipeline is for a regular merge request (that doesn't close an issue)" do
@@ -34,7 +33,6 @@ describe 'CycleAnalytics#test', feature: true do
pipeline.succeed!
merge_merge_requests_closing_issue(issue)
- deploy_master
end
expect(subject.test).to be_nil
@@ -48,8 +46,6 @@ describe 'CycleAnalytics#test', feature: true do
pipeline.run!
pipeline.succeed!
-
- deploy_master
end
expect(subject.test).to be_nil
@@ -67,7 +63,6 @@ describe 'CycleAnalytics#test', feature: true do
pipeline.drop!
merge_merge_requests_closing_issue(issue)
- deploy_master
end
expect(subject.test).to be_nil
@@ -85,7 +80,6 @@ describe 'CycleAnalytics#test', feature: true do
pipeline.cancel!
merge_merge_requests_closing_issue(issue)
- deploy_master
end
expect(subject.test).to be_nil
diff --git a/spec/models/deploy_key_spec.rb b/spec/models/deploy_key_spec.rb
index 6a90598a629..93623e8e99b 100644
--- a/spec/models/deploy_key_spec.rb
+++ b/spec/models/deploy_key_spec.rb
@@ -1,9 +1,6 @@
require 'spec_helper'
describe DeployKey, models: true do
- let(:project) { create(:project) }
- let(:deploy_key) { create(:deploy_key, projects: [project]) }
-
describe "Associations" do
it { is_expected.to have_many(:deploy_keys_projects) }
it { is_expected.to have_many(:projects) }
diff --git a/spec/models/event_spec.rb b/spec/models/event_spec.rb
index af5002487cc..733b79079ed 100644
--- a/spec/models/event_spec.rb
+++ b/spec/models/event_spec.rb
@@ -27,17 +27,17 @@ describe Event, models: true do
end
describe "Push event" do
- before do
- project = create(:project)
- @user = project.owner
- @event = create_event(project, @user)
+ let(:project) { create(:project) }
+ let(:user) { project.owner }
+ let(:event) { create_event(project, user) }
+
+ it do
+ expect(event.push?).to be_truthy
+ expect(event.visible_to_user?).to be_truthy
+ expect(event.tag?).to be_falsey
+ expect(event.branch_name).to eq("master")
+ expect(event.author).to eq(user)
end
-
- it { expect(@event.push?).to be_truthy }
- it { expect(@event.visible_to_user?).to be_truthy }
- it { expect(@event.tag?).to be_falsey }
- it { expect(@event.branch_name).to eq("master") }
- it { expect(@event.author).to eq(@user) }
end
describe '#note?' do
@@ -59,8 +59,8 @@ describe Event, models: true do
describe '#visible_to_user?' do
let(:project) { create(:empty_project, :public) }
let(:non_member) { create(:user) }
- let(:member) { create(:user) }
- let(:guest) { create(:user) }
+ let(:member) { create(:user) }
+ let(:guest) { create(:user) }
let(:author) { create(:author) }
let(:assignee) { create(:user) }
let(:admin) { create(:admin) }
@@ -79,23 +79,27 @@ describe Event, models: true do
context 'for non confidential issues' do
let(:target) { issue }
- it { expect(event.visible_to_user?(non_member)).to eq true }
- it { expect(event.visible_to_user?(author)).to eq true }
- it { expect(event.visible_to_user?(assignee)).to eq true }
- it { expect(event.visible_to_user?(member)).to eq true }
- it { expect(event.visible_to_user?(guest)).to eq true }
- it { expect(event.visible_to_user?(admin)).to eq true }
+ it do
+ expect(event.visible_to_user?(non_member)).to eq true
+ expect(event.visible_to_user?(author)).to eq true
+ expect(event.visible_to_user?(assignee)).to eq true
+ expect(event.visible_to_user?(member)).to eq true
+ expect(event.visible_to_user?(guest)).to eq true
+ expect(event.visible_to_user?(admin)).to eq true
+ end
end
context 'for confidential issues' do
let(:target) { confidential_issue }
- it { expect(event.visible_to_user?(non_member)).to eq false }
- it { expect(event.visible_to_user?(author)).to eq true }
- it { expect(event.visible_to_user?(assignee)).to eq true }
- it { expect(event.visible_to_user?(member)).to eq true }
- it { expect(event.visible_to_user?(guest)).to eq false }
- it { expect(event.visible_to_user?(admin)).to eq true }
+ it do
+ expect(event.visible_to_user?(non_member)).to eq false
+ expect(event.visible_to_user?(author)).to eq true
+ expect(event.visible_to_user?(assignee)).to eq true
+ expect(event.visible_to_user?(member)).to eq true
+ expect(event.visible_to_user?(guest)).to eq false
+ expect(event.visible_to_user?(admin)).to eq true
+ end
end
end
@@ -103,23 +107,27 @@ describe Event, models: true do
context 'on non confidential issues' do
let(:target) { note_on_issue }
- it { expect(event.visible_to_user?(non_member)).to eq true }
- it { expect(event.visible_to_user?(author)).to eq true }
- it { expect(event.visible_to_user?(assignee)).to eq true }
- it { expect(event.visible_to_user?(member)).to eq true }
- it { expect(event.visible_to_user?(guest)).to eq true }
- it { expect(event.visible_to_user?(admin)).to eq true }
+ it do
+ expect(event.visible_to_user?(non_member)).to eq true
+ expect(event.visible_to_user?(author)).to eq true
+ expect(event.visible_to_user?(assignee)).to eq true
+ expect(event.visible_to_user?(member)).to eq true
+ expect(event.visible_to_user?(guest)).to eq true
+ expect(event.visible_to_user?(admin)).to eq true
+ end
end
context 'on confidential issues' do
let(:target) { note_on_confidential_issue }
- it { expect(event.visible_to_user?(non_member)).to eq false }
- it { expect(event.visible_to_user?(author)).to eq true }
- it { expect(event.visible_to_user?(assignee)).to eq true }
- it { expect(event.visible_to_user?(member)).to eq true }
- it { expect(event.visible_to_user?(guest)).to eq false }
- it { expect(event.visible_to_user?(admin)).to eq true }
+ it do
+ expect(event.visible_to_user?(non_member)).to eq false
+ expect(event.visible_to_user?(author)).to eq true
+ expect(event.visible_to_user?(assignee)).to eq true
+ expect(event.visible_to_user?(member)).to eq true
+ expect(event.visible_to_user?(guest)).to eq false
+ expect(event.visible_to_user?(admin)).to eq true
+ end
end
end
@@ -129,12 +137,27 @@ describe Event, models: true do
let(:note_on_merge_request) { create(:legacy_diff_note_on_merge_request, noteable: merge_request, project: project) }
let(:target) { note_on_merge_request }
- it { expect(event.visible_to_user?(non_member)).to eq true }
- it { expect(event.visible_to_user?(author)).to eq true }
- it { expect(event.visible_to_user?(assignee)).to eq true }
- it { expect(event.visible_to_user?(member)).to eq true }
- it { expect(event.visible_to_user?(guest)).to eq true }
- it { expect(event.visible_to_user?(admin)).to eq true }
+ it do
+ expect(event.visible_to_user?(non_member)).to eq true
+ expect(event.visible_to_user?(author)).to eq true
+ expect(event.visible_to_user?(assignee)).to eq true
+ expect(event.visible_to_user?(member)).to eq true
+ expect(event.visible_to_user?(guest)).to eq true
+ expect(event.visible_to_user?(admin)).to eq true
+ end
+
+ context 'private project' do
+ let(:project) { create(:project, :private) }
+
+ it do
+ expect(event.visible_to_user?(non_member)).to eq false
+ expect(event.visible_to_user?(author)).to eq true
+ expect(event.visible_to_user?(assignee)).to eq true
+ expect(event.visible_to_user?(member)).to eq true
+ expect(event.visible_to_user?(guest)).to eq false
+ expect(event.visible_to_user?(admin)).to eq true
+ end
+ end
end
end
@@ -203,6 +226,6 @@ describe Event, models: true do
action: Event::PUSHED,
data: data,
author_id: user.id
- }.merge(attrs))
+ }.merge!(attrs))
end
end
diff --git a/spec/models/key_spec.rb b/spec/models/key_spec.rb
index fd4a2beff58..7fc6ed1dd54 100644
--- a/spec/models/key_spec.rb
+++ b/spec/models/key_spec.rb
@@ -5,9 +5,6 @@ describe Key, models: true do
it { is_expected.to belong_to(:user) }
end
- describe "Mass assignment" do
- end
-
describe "Validation" do
it { is_expected.to validate_presence_of(:title) }
it { is_expected.to validate_presence_of(:key) }
diff --git a/spec/models/label_link_spec.rb b/spec/models/label_link_spec.rb
index 5e6f8ca1528..c18ed8574b1 100644
--- a/spec/models/label_link_spec.rb
+++ b/spec/models/label_link_spec.rb
@@ -1,8 +1,7 @@
require 'spec_helper'
describe LabelLink, models: true do
- let(:label) { create(:label_link) }
- it { expect(label).to be_valid }
+ it { expect(build(:label_link)).to be_valid }
it { is_expected.to belong_to(:label) }
it { is_expected.to belong_to(:target) }
diff --git a/spec/models/merge_request_diff_spec.rb b/spec/models/merge_request_diff_spec.rb
index 96f1f60dbc0..e5007424041 100644
--- a/spec/models/merge_request_diff_spec.rb
+++ b/spec/models/merge_request_diff_spec.rb
@@ -6,9 +6,9 @@ describe MergeRequestDiff, models: true do
it { expect(subject).to be_valid }
it { expect(subject).to be_persisted }
- it { expect(subject.commits.count).to eq(5) }
- it { expect(subject.diffs.count).to eq(8) }
- it { expect(subject.head_commit_sha).to eq('5937ac0a7beb003549fc5fd26fc247adbce4a52e') }
+ it { expect(subject.commits.count).to eq(29) }
+ it { expect(subject.diffs.count).to eq(20) }
+ it { expect(subject.head_commit_sha).to eq('b83d6e391c22777fca1ed3012fce84f633d7fed0') }
it { expect(subject.base_commit_sha).to eq('ae73cb07c9eeaf35924a10f713b364d32b2dd34f') }
it { expect(subject.start_commit_sha).to eq('0b4bc9a49b562e85de7cc9e834518ea6828729b9') }
end
@@ -74,27 +74,43 @@ describe MergeRequestDiff, models: true do
end
end
end
+ end
- describe '#commits_sha' do
- shared_examples 'returning all commits SHA' do
- it 'returns all commits SHA' do
- commits_sha = subject.commits_sha
+ describe '#commits_sha' do
+ shared_examples 'returning all commits SHA' do
+ it 'returns all commits SHA' do
+ commits_sha = subject.commits_sha
- expect(commits_sha).to eq(subject.commits.map(&:sha))
- end
+ expect(commits_sha).to eq(subject.commits.map(&:sha))
end
+ end
- context 'when commits were loaded' do
- before do
- subject.commits
- end
-
- it_behaves_like 'returning all commits SHA'
+ context 'when commits were loaded' do
+ before do
+ subject.commits
end
- context 'when commits were not loaded' do
- it_behaves_like 'returning all commits SHA'
- end
+ it_behaves_like 'returning all commits SHA'
+ end
+
+ context 'when commits were not loaded' do
+ it_behaves_like 'returning all commits SHA'
+ end
+ end
+
+ describe '#compare_with' do
+ subject { create(:merge_request, source_branch: 'fix').merge_request_diff }
+
+ it 'delegates compare to the service' do
+ expect(CompareService).to receive(:new).and_call_original
+
+ subject.compare_with(nil)
+ end
+
+ it 'uses git diff A..B approach by default' do
+ diffs = subject.compare_with('0b4bc9a49b562e85de7cc9e834518ea6828729b9').diffs
+
+ expect(diffs.size).to eq(3)
end
end
end
diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb
index 38b6da50168..5884b4cff8c 100644
--- a/spec/models/merge_request_spec.rb
+++ b/spec/models/merge_request_spec.rb
@@ -489,7 +489,7 @@ describe MergeRequest, models: true do
subject(:merge_request_with_divergence) { create(:merge_request, :diverged, source_project: project, target_project: project) }
it 'counts commits that are on target branch but not on source branch' do
- expect(subject.diverged_commits_count).to eq(5)
+ expect(subject.diverged_commits_count).to eq(29)
end
end
@@ -497,7 +497,7 @@ describe MergeRequest, models: true do
subject(:merge_request_fork_with_divergence) { create(:merge_request, :diverged, source_project: fork_project, target_project: project) }
it 'counts commits that are on target branch but not on source branch' do
- expect(subject.diverged_commits_count).to eq(5)
+ expect(subject.diverged_commits_count).to eq(29)
end
end
diff --git a/spec/models/project_group_link_spec.rb b/spec/models/project_group_link_spec.rb
index 2fa6715fcaf..c5ff1941378 100644
--- a/spec/models/project_group_link_spec.rb
+++ b/spec/models/project_group_link_spec.rb
@@ -11,7 +11,7 @@ describe ProjectGroupLink do
it { should validate_presence_of(:project_id) }
it { should validate_uniqueness_of(:group_id).scoped_to(:project_id).with_message(/already shared/) }
- it { should validate_presence_of(:group_id) }
+ it { should validate_presence_of(:group) }
it { should validate_presence_of(:group_access) }
end
end
diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb
index 4641f297465..4b80efbe12b 100644
--- a/spec/models/repository_spec.rb
+++ b/spec/models/repository_spec.rb
@@ -122,11 +122,30 @@ describe Repository, models: true do
end
describe '#merged_to_root_ref?' do
- context 'merged branch' do
+ context 'merged branch without ff' do
+ subject { repository.merged_to_root_ref?('branch-merged') }
+
+ it { is_expected.to be_truthy }
+ end
+
+ # If the HEAD was ff then it will be false
+ context 'merged with ff' do
subject { repository.merged_to_root_ref?('improve/awesome') }
it { is_expected.to be_truthy }
end
+
+ context 'not merged branch' do
+ subject { repository.merged_to_root_ref?('not-merged-branch') }
+
+ it { is_expected.to be_falsey }
+ end
+
+ context 'default branch' do
+ subject { repository.merged_to_root_ref?('master') }
+
+ it { is_expected.to be_falsey }
+ end
end
describe '#can_be_merged?' do
@@ -324,7 +343,7 @@ describe Repository, models: true do
subject { results.first }
it { is_expected.to be_an String }
- it { expect(subject.lines[2]).to eq("master:CHANGELOG:188: - Feature: Replace teams with group membership\n") }
+ it { expect(subject.lines[2]).to eq("master:CHANGELOG:190: - Feature: Replace teams with group membership\n") }
end
end
@@ -968,10 +987,10 @@ describe Repository, models: true do
context 'cherry-picking a merge commit' do
it 'cherry-picks the changes' do
- expect(repository.blob_at_branch('master', 'foo/bar/.gitkeep')).to be_nil
+ expect(repository.blob_at_branch('improve/awesome', 'foo/bar/.gitkeep')).to be_nil
- repository.cherry_pick(user, pickable_merge, 'master')
- expect(repository.blob_at_branch('master', 'foo/bar/.gitkeep')).not_to be_nil
+ repository.cherry_pick(user, pickable_merge, 'improve/awesome')
+ expect(repository.blob_at_branch('improve/awesome', 'foo/bar/.gitkeep')).not_to be_nil
end
end
end
diff --git a/spec/policies/project_policy_spec.rb b/spec/policies/project_policy_spec.rb
index 43c8d884a47..658e3c13a73 100644
--- a/spec/policies/project_policy_spec.rb
+++ b/spec/policies/project_policy_spec.rb
@@ -12,7 +12,7 @@ describe ProjectPolicy, models: true do
[
:read_project, :read_board, :read_list, :read_wiki, :read_issue, :read_label,
:read_milestone, :read_project_snippet, :read_project_member,
- :read_merge_request, :read_note, :create_project, :create_issue, :create_note,
+ :read_note, :create_project, :create_issue, :create_note,
:upload_file
]
end
@@ -21,7 +21,8 @@ describe ProjectPolicy, models: true do
[
:download_code, :fork_project, :create_project_snippet, :update_issue,
:admin_issue, :admin_label, :admin_list, :read_commit_status, :read_build,
- :read_container_image, :read_pipeline, :read_environment, :read_deployment
+ :read_container_image, :read_pipeline, :read_environment, :read_deployment,
+ :read_merge_request
]
end
diff --git a/spec/requests/api/commits_spec.rb b/spec/requests/api/commits_spec.rb
index aa610557056..66fa0c0c01f 100644
--- a/spec/requests/api/commits_spec.rb
+++ b/spec/requests/api/commits_spec.rb
@@ -53,7 +53,12 @@ describe API::API, api: true do
get api("/projects/#{project.id}/repository/commits?until=#{before.utc.iso8601}", user)
- expect(json_response.size).to eq(commits.size - 1)
+ if commits.size >= 20
+ expect(json_response.size).to eq(20)
+ else
+ expect(json_response.size).to eq(commits.size - 1)
+ end
+
expect(json_response.first["id"]).to eq(commits.second.id)
expect(json_response.second["id"]).to eq(commits.third.id)
end
@@ -447,11 +452,12 @@ describe API::API, api: true do
end
it 'returns the inline comment' do
- post api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}/comments", user), note: 'My comment', path: project.repository.commit.raw_diffs.first.new_path, line: 7, line_type: 'new'
+ post api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}/comments", user), note: 'My comment', path: project.repository.commit.raw_diffs.first.new_path, line: 1, line_type: 'new'
+
expect(response).to have_http_status(201)
expect(json_response['note']).to eq('My comment')
expect(json_response['path']).to eq(project.repository.commit.raw_diffs.first.new_path)
- expect(json_response['line']).to eq(7)
+ expect(json_response['line']).to eq(1)
expect(json_response['line_type']).to eq('new')
end
diff --git a/spec/requests/api/license_templates_spec.rb b/spec/requests/api/license_templates_spec.rb
deleted file mode 100644
index 9a1894d63a2..00000000000
--- a/spec/requests/api/license_templates_spec.rb
+++ /dev/null
@@ -1,136 +0,0 @@
-require 'spec_helper'
-
-describe API::API, api: true do
- include ApiHelpers
-
- describe 'Entity' do
- before { get api('/licenses/mit') }
-
- it { expect(json_response['key']).to eq('mit') }
- it { expect(json_response['name']).to eq('MIT License') }
- it { expect(json_response['nickname']).to be_nil }
- it { expect(json_response['popular']).to be true }
- it { expect(json_response['html_url']).to eq('http://choosealicense.com/licenses/mit/') }
- it { expect(json_response['source_url']).to eq('https://opensource.org/licenses/MIT') }
- it { expect(json_response['description']).to include('A permissive license that is short and to the point.') }
- it { expect(json_response['conditions']).to eq(%w[include-copyright]) }
- it { expect(json_response['permissions']).to eq(%w[commercial-use modifications distribution private-use]) }
- it { expect(json_response['limitations']).to eq(%w[no-liability]) }
- it { expect(json_response['content']).to include('The MIT License (MIT)') }
- end
-
- describe 'GET /licenses' do
- it 'returns a list of available license templates' do
- get api('/licenses')
-
- expect(response).to have_http_status(200)
- expect(json_response).to be_an Array
- expect(json_response.size).to eq(15)
- expect(json_response.map { |l| l['key'] }).to include('agpl-3.0')
- end
-
- describe 'the popular parameter' do
- context 'with popular=1' do
- it 'returns a list of available popular license templates' do
- get api('/licenses?popular=1')
-
- expect(response).to have_http_status(200)
- expect(json_response).to be_an Array
- expect(json_response.size).to eq(3)
- expect(json_response.map { |l| l['key'] }).to include('apache-2.0')
- end
- end
- end
- end
-
- describe 'GET /licenses/:key' do
- context 'with :project and :fullname given' do
- before do
- get api("/licenses/#{license_type}?project=My+Awesome+Project&fullname=Anton+#{license_type.upcase}")
- end
-
- context 'for the mit license' do
- let(:license_type) { 'mit' }
-
- it 'returns the license text' do
- expect(json_response['content']).to include('The MIT License (MIT)')
- end
-
- it 'replaces placeholder values' do
- expect(json_response['content']).to include("Copyright (c) #{Time.now.year} Anton")
- end
- end
-
- context 'for the agpl-3.0 license' do
- let(:license_type) { 'agpl-3.0' }
-
- it 'returns the license text' do
- expect(json_response['content']).to include('GNU AFFERO GENERAL PUBLIC LICENSE')
- end
-
- it 'replaces placeholder values' do
- expect(json_response['content']).to include('My Awesome Project')
- expect(json_response['content']).to include("Copyright (C) #{Time.now.year} Anton")
- end
- end
-
- context 'for the gpl-3.0 license' do
- let(:license_type) { 'gpl-3.0' }
-
- it 'returns the license text' do
- expect(json_response['content']).to include('GNU GENERAL PUBLIC LICENSE')
- end
-
- it 'replaces placeholder values' do
- expect(json_response['content']).to include('My Awesome Project')
- expect(json_response['content']).to include("Copyright (C) #{Time.now.year} Anton")
- end
- end
-
- context 'for the gpl-2.0 license' do
- let(:license_type) { 'gpl-2.0' }
-
- it 'returns the license text' do
- expect(json_response['content']).to include('GNU GENERAL PUBLIC LICENSE')
- end
-
- it 'replaces placeholder values' do
- expect(json_response['content']).to include('My Awesome Project')
- expect(json_response['content']).to include("Copyright (C) #{Time.now.year} Anton")
- end
- end
-
- context 'for the apache-2.0 license' do
- let(:license_type) { 'apache-2.0' }
-
- it 'returns the license text' do
- expect(json_response['content']).to include('Apache License')
- end
-
- it 'replaces placeholder values' do
- expect(json_response['content']).to include("Copyright #{Time.now.year} Anton")
- end
- end
-
- context 'for an uknown license' do
- let(:license_type) { 'muth-over9000' }
-
- it 'returns a 404' do
- expect(response).to have_http_status(404)
- end
- end
- end
-
- context 'with no :fullname given' do
- context 'with an authenticated user' do
- let(:user) { create(:user) }
-
- it 'replaces the copyright owner placeholder with the name of the current user' do
- get api('/licenses/mit', user)
-
- expect(json_response['content']).to include("Copyright (c) #{Time.now.year} #{user.name}")
- end
- end
- end
- end
-end
diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb
index 5f19638b460..973928d007a 100644
--- a/spec/requests/api/projects_spec.rb
+++ b/spec/requests/api/projects_spec.rb
@@ -588,37 +588,39 @@ describe API::API, api: true do
before do
note = create(:note_on_issue, note: 'What an awesome day!', project: project)
EventCreateService.new.leave_note(note, note.author)
- get api("/projects/#{project.id}/events", user)
end
- it { expect(response).to have_http_status(200) }
+ it 'returns all events' do
+ get api("/projects/#{project.id}/events", user)
- context 'joined event' do
- let(:json_event) { json_response[1] }
+ expect(response).to have_http_status(200)
- it { expect(json_event['action_name']).to eq('joined') }
- it { expect(json_event['project_id'].to_i).to eq(project.id) }
- it { expect(json_event['author_username']).to eq(user3.username) }
- it { expect(json_event['author']['name']).to eq(user3.name) }
- end
+ first_event = json_response.first
- context 'comment event' do
- let(:json_event) { json_response.first }
+ expect(first_event['action_name']).to eq('commented on')
+ expect(first_event['note']['body']).to eq('What an awesome day!')
- it { expect(json_event['action_name']).to eq('commented on') }
- it { expect(json_event['note']['body']).to eq('What an awesome day!') }
+ last_event = json_response.last
+
+ expect(last_event['action_name']).to eq('joined')
+ expect(last_event['project_id'].to_i).to eq(project.id)
+ expect(last_event['author_username']).to eq(user3.username)
+ expect(last_event['author']['name']).to eq(user3.name)
end
end
it 'returns a 404 error if not found' do
get api('/projects/42/events', user)
+
expect(response).to have_http_status(404)
expect(json_response['message']).to eq('404 Project Not Found')
end
it 'returns a 404 error if user is not a member' do
other_user = create(:user)
+
get api("/projects/#{project.id}/events", other_user)
+
expect(response).to have_http_status(404)
end
end
@@ -819,6 +821,20 @@ describe API::API, api: true do
expect(response.status).to eq 400
end
+ it 'returns a 404 error when user cannot read group' do
+ private_group = create(:group, :private)
+
+ post api("/projects/#{project.id}/share", user), group_id: private_group.id, group_access: Gitlab::Access::DEVELOPER
+
+ expect(response.status).to eq 404
+ end
+
+ it 'returns a 404 error when group does not exist' do
+ post api("/projects/#{project.id}/share", user), group_id: 1234, group_access: Gitlab::Access::DEVELOPER
+
+ expect(response.status).to eq 404
+ end
+
it "returns a 409 error when wrong params passed" do
post api("/projects/#{project.id}/share", user), group_id: group.id, group_access: 1234
expect(response.status).to eq 409
diff --git a/spec/requests/api/repositories_spec.rb b/spec/requests/api/repositories_spec.rb
index 80a856a6e90..c4dc2d9006a 100644
--- a/spec/requests/api/repositories_spec.rb
+++ b/spec/requests/api/repositories_spec.rb
@@ -21,7 +21,7 @@ describe API::API, api: true do
expect(response).to have_http_status(200)
expect(json_response).to be_an Array
- expect(json_response.first['name']).to eq('encoding')
+ expect(json_response.first['name']).to eq('bar')
expect(json_response.first['type']).to eq('tree')
expect(json_response.first['mode']).to eq('040000')
end
@@ -166,9 +166,9 @@ describe API::API, api: true do
expect(response).to have_http_status(200)
expect(json_response).to be_an Array
contributor = json_response.first
- expect(contributor['email']).to eq('dmitriy.zaporozhets@gmail.com')
- expect(contributor['name']).to eq('Dmitriy Zaporozhets')
- expect(contributor['commits']).to eq(13)
+ expect(contributor['email']).to eq('tiagonbotelho@hotmail.com')
+ expect(contributor['name']).to eq('tiagonbotelho')
+ expect(contributor['commits']).to eq(1)
expect(contributor['additions']).to eq(0)
expect(contributor['deletions']).to eq(0)
end
diff --git a/spec/requests/api/templates_spec.rb b/spec/requests/api/templates_spec.rb
index 5bd5b861792..d32ba60fc4c 100644
--- a/spec/requests/api/templates_spec.rb
+++ b/spec/requests/api/templates_spec.rb
@@ -3,53 +3,201 @@ require 'spec_helper'
describe API::Templates, api: true do
include ApiHelpers
- context 'global templates' do
- describe 'the Template Entity' do
- before { get api('/gitignores/Ruby') }
+ shared_examples_for 'the Template Entity' do |path|
+ before { get api(path) }
- it { expect(json_response['name']).to eq('Ruby') }
- it { expect(json_response['content']).to include('*.gem') }
+ it { expect(json_response['name']).to eq('Ruby') }
+ it { expect(json_response['content']).to include('*.gem') }
+ end
+
+ shared_examples_for 'the TemplateList Entity' do |path|
+ before { get api(path) }
+
+ it { expect(json_response.first['name']).not_to be_nil }
+ it { expect(json_response.first['content']).to be_nil }
+ end
+
+ shared_examples_for 'requesting gitignores' do |path|
+ it 'returns a list of available gitignore templates' do
+ get api(path)
+
+ expect(response).to have_http_status(200)
+ expect(json_response).to be_an Array
+ expect(json_response.size).to be > 15
end
+ end
- describe 'the TemplateList Entity' do
- before { get api('/gitignores') }
+ shared_examples_for 'requesting gitlab-ci-ymls' do |path|
+ it 'returns a list of available gitlab_ci_ymls' do
+ get api(path)
- it { expect(json_response.first['name']).not_to be_nil }
- it { expect(json_response.first['content']).to be_nil }
+ expect(response).to have_http_status(200)
+ expect(json_response).to be_an Array
+ expect(json_response.first['name']).not_to be_nil
end
+ end
+
+ shared_examples_for 'requesting gitlab-ci-yml for Ruby' do |path|
+ it 'adds a disclaimer on the top' do
+ get api(path)
+
+ expect(response).to have_http_status(200)
+ expect(json_response['content']).to start_with("# This file is a template,")
+ end
+ end
+
+ shared_examples_for 'the License Template Entity' do |path|
+ before { get api(path) }
+
+ it 'returns a license template' do
+ expect(json_response['key']).to eq('mit')
+ expect(json_response['name']).to eq('MIT License')
+ expect(json_response['nickname']).to be_nil
+ expect(json_response['popular']).to be true
+ expect(json_response['html_url']).to eq('http://choosealicense.com/licenses/mit/')
+ expect(json_response['source_url']).to eq('https://opensource.org/licenses/MIT')
+ expect(json_response['description']).to include('A permissive license that is short and to the point.')
+ expect(json_response['conditions']).to eq(%w[include-copyright])
+ expect(json_response['permissions']).to eq(%w[commercial-use modifications distribution private-use])
+ expect(json_response['limitations']).to eq(%w[no-liability])
+ expect(json_response['content']).to include('The MIT License (MIT)')
+ end
+ end
- context 'requesting gitignores' do
- describe 'GET /gitignores' do
- it 'returns a list of available gitignore templates' do
- get api('/gitignores')
+ shared_examples_for 'GET licenses' do |path|
+ it 'returns a list of available license templates' do
+ get api(path)
- expect(response.status).to eq(200)
+ expect(response).to have_http_status(200)
+ expect(json_response).to be_an Array
+ expect(json_response.size).to eq(15)
+ expect(json_response.map { |l| l['key'] }).to include('agpl-3.0')
+ end
+
+ describe 'the popular parameter' do
+ context 'with popular=1' do
+ it 'returns a list of available popular license templates' do
+ get api("#{path}?popular=1")
+
+ expect(response).to have_http_status(200)
expect(json_response).to be_an Array
- expect(json_response.size).to be > 15
+ expect(json_response.size).to eq(3)
+ expect(json_response.map { |l| l['key'] }).to include('apache-2.0')
end
end
end
+ end
- context 'requesting gitlab-ci-ymls' do
- describe 'GET /gitlab_ci_ymls' do
- it 'returns a list of available gitlab_ci_ymls' do
- get api('/gitlab_ci_ymls')
+ shared_examples_for 'GET licenses/:name' do |path|
+ context 'with :project and :fullname given' do
+ before do
+ get api("#{path}/#{license_type}?project=My+Awesome+Project&fullname=Anton+#{license_type.upcase}")
+ end
- expect(response.status).to eq(200)
- expect(json_response).to be_an Array
- expect(json_response.first['name']).not_to be_nil
+ context 'for the mit license' do
+ let(:license_type) { 'mit' }
+
+ it 'returns the license text' do
+ expect(json_response['content']).to include('The MIT License (MIT)')
+ end
+
+ it 'replaces placeholder values' do
+ expect(json_response['content']).to include("Copyright (c) #{Time.now.year} Anton")
+ end
+ end
+
+ context 'for the agpl-3.0 license' do
+ let(:license_type) { 'agpl-3.0' }
+
+ it 'returns the license text' do
+ expect(json_response['content']).to include('GNU AFFERO GENERAL PUBLIC LICENSE')
+ end
+
+ it 'replaces placeholder values' do
+ expect(json_response['content']).to include('My Awesome Project')
+ expect(json_response['content']).to include("Copyright (C) #{Time.now.year} Anton")
+ end
+ end
+
+ context 'for the gpl-3.0 license' do
+ let(:license_type) { 'gpl-3.0' }
+
+ it 'returns the license text' do
+ expect(json_response['content']).to include('GNU GENERAL PUBLIC LICENSE')
+ end
+
+ it 'replaces placeholder values' do
+ expect(json_response['content']).to include('My Awesome Project')
+ expect(json_response['content']).to include("Copyright (C) #{Time.now.year} Anton")
+ end
+ end
+
+ context 'for the gpl-2.0 license' do
+ let(:license_type) { 'gpl-2.0' }
+
+ it 'returns the license text' do
+ expect(json_response['content']).to include('GNU GENERAL PUBLIC LICENSE')
+ end
+
+ it 'replaces placeholder values' do
+ expect(json_response['content']).to include('My Awesome Project')
+ expect(json_response['content']).to include("Copyright (C) #{Time.now.year} Anton")
+ end
+ end
+
+ context 'for the apache-2.0 license' do
+ let(:license_type) { 'apache-2.0' }
+
+ it 'returns the license text' do
+ expect(json_response['content']).to include('Apache License')
+ end
+
+ it 'replaces placeholder values' do
+ expect(json_response['content']).to include("Copyright #{Time.now.year} Anton")
+ end
+ end
+
+ context 'for an uknown license' do
+ let(:license_type) { 'muth-over9000' }
+
+ it 'returns a 404' do
+ expect(response).to have_http_status(404)
end
end
end
- describe 'GET /gitlab_ci_ymls/Ruby' do
- it 'adds a disclaimer on the top' do
- get api('/gitlab_ci_ymls/Ruby')
+ context 'with no :fullname given' do
+ context 'with an authenticated user' do
+ let(:user) { create(:user) }
+
+ it 'replaces the copyright owner placeholder with the name of the current user' do
+ get api('/templates/licenses/mit', user)
- expect(response).to have_http_status(200)
- expect(json_response['name']).not_to be_nil
- expect(json_response['content']).to start_with("# This file is a template,")
+ expect(json_response['content']).to include("Copyright (c) #{Time.now.year} #{user.name}")
+ end
end
end
end
+
+ describe 'with /templates namespace' do
+ it_behaves_like 'the Template Entity', '/templates/gitignores/Ruby'
+ it_behaves_like 'the TemplateList Entity', '/templates/gitignores'
+ it_behaves_like 'requesting gitignores', '/templates/gitignores'
+ it_behaves_like 'requesting gitlab-ci-ymls', '/templates/gitlab_ci_ymls'
+ it_behaves_like 'requesting gitlab-ci-yml for Ruby', '/templates/gitlab_ci_ymls/Ruby'
+ it_behaves_like 'the License Template Entity', '/templates/licenses/mit'
+ it_behaves_like 'GET licenses', '/templates/licenses'
+ it_behaves_like 'GET licenses/:name', '/templates/licenses'
+ end
+
+ describe 'without /templates namespace' do
+ it_behaves_like 'the Template Entity', '/gitignores/Ruby'
+ it_behaves_like 'the TemplateList Entity', '/gitignores'
+ it_behaves_like 'requesting gitignores', '/gitignores'
+ it_behaves_like 'requesting gitlab-ci-ymls', '/gitlab_ci_ymls'
+ it_behaves_like 'requesting gitlab-ci-yml for Ruby', '/gitlab_ci_ymls/Ruby'
+ it_behaves_like 'the License Template Entity', '/licenses/mit'
+ it_behaves_like 'GET licenses', '/licenses'
+ it_behaves_like 'GET licenses/:name', '/licenses'
+ end
end
diff --git a/spec/requests/api/users_spec.rb b/spec/requests/api/users_spec.rb
index b002949b41b..f83f4d2c9b1 100644
--- a/spec/requests/api/users_spec.rb
+++ b/spec/requests/api/users_spec.rb
@@ -913,4 +913,58 @@ describe API::API, api: true do
expect(response).to have_http_status(404)
end
end
+
+ describe 'GET /user/: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) }
+
+ before do
+ project.add_user(user, :developer)
+ EventCreateService.new.leave_note(note, user)
+ end
+
+ context "as a user than cannot see the event's project" do
+ it 'returns no events' do
+ other_user = create(:user)
+
+ get api("/users/#{user.id}/events", other_user)
+
+ expect(response).to have_http_status(200)
+ expect(json_response).to be_empty
+ end
+ end
+
+ context "as a user than can see the event's project" do
+ it_behaves_like 'a paginated resources' do
+ let(:request) { get api("/users/#{user.id}/events", user) }
+ end
+
+ context 'joined event' do
+ it 'returns the "joined" event' do
+ get api("/users/#{user.id}/events", user)
+
+ comment_event = json_response.find { |e| e['action_name'] == 'commented on' }
+
+ expect(comment_event['project_id'].to_i).to eq(project.id)
+ expect(comment_event['author_username']).to eq(user.username)
+ expect(comment_event['note']['id']).to eq(note.id)
+ expect(comment_event['note']['body']).to eq('What an awesome day!')
+
+ joined_event = json_response.find { |e| e['action_name'] == 'joined' }
+
+ expect(joined_event['project_id'].to_i).to eq(project.id)
+ expect(joined_event['author_username']).to eq(user.username)
+ expect(joined_event['author']['name']).to eq(user.name)
+ end
+ end
+ end
+
+ it 'returns a 404 error if not found' do
+ get api('/users/42/events', user)
+
+ expect(response).to have_http_status(404)
+ expect(json_response['message']).to eq('404 User Not Found')
+ end
+ end
end
diff --git a/spec/requests/api/version_spec.rb b/spec/requests/api/version_spec.rb
new file mode 100644
index 00000000000..54b69a0cae7
--- /dev/null
+++ b/spec/requests/api/version_spec.rb
@@ -0,0 +1,27 @@
+require 'spec_helper'
+
+describe API::API, api: true do
+ include ApiHelpers
+
+ describe 'GET /version' do
+ context 'when unauthenticated' do
+ it 'returns authentication error' do
+ get api('/version')
+
+ expect(response).to have_http_status(401)
+ end
+ end
+
+ context 'when authenticated' do
+ let(:user) { create(:user) }
+
+ it 'returns the version information' do
+ get api('/version', user)
+
+ expect(response).to have_http_status(200)
+ expect(json_response['version']).to eq(Gitlab::VERSION)
+ expect(json_response['revision']).to eq(Gitlab::REVISION)
+ end
+ end
+ end
+end
diff --git a/spec/requests/git_http_spec.rb b/spec/requests/git_http_spec.rb
index 413d06715b3..5a1ed7d4a25 100644
--- a/spec/requests/git_http_spec.rb
+++ b/spec/requests/git_http_spec.rb
@@ -439,8 +439,8 @@ describe 'Git HTTP requests', lib: true do
before do
# Provide a dummy file in its place
allow_any_instance_of(Repository).to receive(:blob_at).and_call_original
- allow_any_instance_of(Repository).to receive(:blob_at).with('5937ac0a7beb003549fc5fd26fc247adbce4a52e', 'info/refs') do
- Gitlab::Git::Blob.find(project.repository, 'master', '.gitignore')
+ allow_any_instance_of(Repository).to receive(:blob_at).with('b83d6e391c22777fca1ed3012fce84f633d7fed0', 'info/refs') do
+ Gitlab::Git::Blob.find(project.repository, 'master', 'bar/branch-test.txt')
end
get "/#{project.path_with_namespace}/blob/master/info/refs"
diff --git a/spec/routing/project_routing_spec.rb b/spec/routing/project_routing_spec.rb
index 77842057a10..2322430d212 100644
--- a/spec/routing/project_routing_spec.rb
+++ b/spec/routing/project_routing_spec.rb
@@ -337,7 +337,7 @@ describe Projects::CommitsController, 'routing' do
end
it 'to #show' do
- expect(get('/gitlab/gitlabhq/commits/master.atom')).to route_to('projects/commits#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'master', format: 'atom')
+ expect(get('/gitlab/gitlabhq/commits/master.atom')).to route_to('projects/commits#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'master.atom')
end
end
diff --git a/spec/services/compare_service_spec.rb b/spec/services/compare_service_spec.rb
new file mode 100644
index 00000000000..3760f19aaa2
--- /dev/null
+++ b/spec/services/compare_service_spec.rb
@@ -0,0 +1,21 @@
+require 'spec_helper'
+
+describe CompareService, services: true do
+ let(:project) { create(:project) }
+ let(:user) { create(:user) }
+ let(:service) { described_class.new }
+
+ describe '#execute' do
+ context 'compare with base, like feature...fix' do
+ subject { service.execute(project, 'feature', project, 'fix', straight: false) }
+
+ it { expect(subject.diffs.size).to eq(1) }
+ end
+
+ context 'straight compare, like feature..fix' do
+ subject { service.execute(project, 'feature', project, 'fix', straight: true) }
+
+ it { expect(subject.diffs.size).to eq(3) }
+ 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
new file mode 100644
index 00000000000..7aeb95a15ea
--- /dev/null
+++ b/spec/services/merge_requests/assign_issues_service_spec.rb
@@ -0,0 +1,49 @@
+require 'spec_helper'
+
+describe MergeRequests::AssignIssuesService, services: true do
+ let(:user) { create(:user) }
+ let(:project) { create(:project, :public) }
+ let(:issue) { create(:issue, project: project) }
+ let(:merge_request) { create(:merge_request, :simple, source_project: project, author: user, description: "fixes #{issue.to_reference}") }
+ let(:service) { described_class.new(project, user, merge_request: merge_request) }
+
+ before do
+ project.team << [user, :developer]
+ end
+
+ it 'finds unassigned issues fixed in merge request' do
+ expect(service.assignable_issues.map(&:id)).to include(issue.id)
+ end
+
+ it 'ignores issues already assigned to any user' do
+ issue.update!(assignee: create(:user))
+
+ expect(service.assignable_issues).to be_empty
+ end
+
+ it 'ignores issues the user cannot update assignee on' do
+ project.team.truncate
+
+ expect(service.assignable_issues).to be_empty
+ end
+
+ it 'ignores all issues unless current_user is merge_request.author' do
+ merge_request.update!(author: create(:user))
+
+ expect(service.assignable_issues).to be_empty
+ end
+
+ it 'accepts precomputed data for closes_issues' do
+ issue2 = create(:issue, project: project)
+ service2 = described_class.new(project,
+ user,
+ merge_request: merge_request,
+ closes_issues: [issue, issue2])
+
+ expect(service2.assignable_issues.count).to eq 2
+ end
+
+ it 'assigns these to the merge request owner' do
+ expect { service.execute }.to change { issue.reload.assignee }.to(user)
+ end
+end
diff --git a/spec/services/merge_requests/merge_when_build_succeeds_service_spec.rb b/spec/services/merge_requests/merge_when_build_succeeds_service_spec.rb
index 9a29e400654..b80cfd8f450 100644
--- a/spec/services/merge_requests/merge_when_build_succeeds_service_spec.rb
+++ b/spec/services/merge_requests/merge_when_build_succeeds_service_spec.rb
@@ -58,57 +58,67 @@ describe MergeRequests::MergeWhenBuildSucceedsService do
end
describe "#trigger" do
- context 'build with ref' do
- let(:build) { create(:ci_build, ref: mr_merge_if_green_enabled.source_branch, status: "success") }
+ let(:merge_request_ref) { mr_merge_if_green_enabled.source_branch }
+ let(:merge_request_head) do
+ project.commit(mr_merge_if_green_enabled.source_branch).id
+ end
- it "merges all merge requests with merge when build succeeds enabled" do
- allow_any_instance_of(MergeRequest).to receive(:pipeline).and_return(pipeline)
- allow(pipeline).to receive(:success?).and_return(true)
+ context 'when triggered by pipeline with valid ref and sha' do
+ let(:triggering_pipeline) do
+ create(:ci_pipeline, project: project, ref: merge_request_ref,
+ sha: merge_request_head, status: 'success')
+ end
+ it "merges all merge requests with merge when build succeeds enabled" do
expect(MergeWorker).to receive(:perform_async)
- service.trigger(build)
+ service.trigger(triggering_pipeline)
end
end
- context 'triggered by an old build' do
- let(:old_build) { create(:ci_build, ref: mr_merge_if_green_enabled.source_branch, status: "success") }
- let(:build) { create(:ci_build, ref: mr_merge_if_green_enabled.source_branch, status: "success") }
-
- it "merges all merge requests with merge when build succeeds enabled" do
- allow_any_instance_of(MergeRequest).to receive(:pipeline).and_return(pipeline)
- allow(pipeline).to receive(:success?).and_return(true)
- allow(old_build).to receive(:sha).and_return('1234abcdef')
+ context 'when triggered by an old pipeline' do
+ let(:old_pipeline) do
+ create(:ci_pipeline, project: project, ref: merge_request_ref,
+ sha: '1234abcdef', status: 'success')
+ end
+ it 'it does not merge merge request' do
expect(MergeWorker).not_to receive(:perform_async)
- service.trigger(old_build)
+ service.trigger(old_pipeline)
end
end
- context 'commit status without ref' do
- let(:commit_status) { create(:generic_commit_status, status: 'success') }
-
- before { mr_merge_if_green_enabled }
-
- it "doesn't merge a requests for status on other branch" do
- allow(project.repository).to receive(:branch_names_contains).with(commit_status.sha).and_return([])
+ context 'when triggered by pipeline from a different branch' do
+ let(:unrelated_pipeline) do
+ create(:ci_pipeline, project: project, ref: 'feature',
+ sha: merge_request_head, status: 'success')
+ end
+ it 'does not merge request' do
expect(MergeWorker).not_to receive(:perform_async)
- service.trigger(commit_status)
+ service.trigger(unrelated_pipeline)
end
+ end
+ end
- it 'discovers branches and merges all merge requests when status is success' do
- allow(project.repository).to receive(:branch_names_contains).
- with(commit_status.sha).and_return([mr_merge_if_green_enabled.source_branch])
- allow(pipeline).to receive(:success?).and_return(true)
- allow_any_instance_of(MergeRequest).to receive(:pipeline).and_return(pipeline)
- allow(pipeline).to receive(:success?).and_return(true)
+ describe "#cancel" do
+ before do
+ service.cancel(mr_merge_if_green_enabled)
+ end
- expect(MergeWorker).to receive(:perform_async)
- service.trigger(commit_status)
- end
+ it "resets all the merge_when_build_succeeds params" do
+ expect(mr_merge_if_green_enabled.merge_when_build_succeeds).to be_falsey
+ expect(mr_merge_if_green_enabled.merge_params).to eq({})
+ expect(mr_merge_if_green_enabled.merge_user).to be nil
+ end
+
+ it 'Posts a system note' do
+ note = mr_merge_if_green_enabled.notes.last
+ expect(note.note).to include 'Canceled the automatic merge'
end
+ end
- context 'properly handles multiple stages' do
+ describe 'pipeline integration' do
+ context 'when there are multiple stages in the pipeline' do
let(:ref) { mr_merge_if_green_enabled.source_branch }
let(:sha) { project.commit(ref).id }
@@ -148,21 +158,4 @@ describe MergeRequests::MergeWhenBuildSucceedsService do
end
end
end
-
- describe "#cancel" do
- before do
- service.cancel(mr_merge_if_green_enabled)
- end
-
- it "resets all the merge_when_build_succeeds params" do
- expect(mr_merge_if_green_enabled.merge_when_build_succeeds).to be_falsey
- expect(mr_merge_if_green_enabled.merge_params).to eq({})
- expect(mr_merge_if_green_enabled.merge_user).to be nil
- end
-
- it 'Posts a system note' do
- note = mr_merge_if_green_enabled.notes.last
- expect(note.note).to include 'Canceled the automatic merge'
- end
- end
end
diff --git a/spec/services/merge_requests/refresh_service_spec.rb b/spec/services/merge_requests/refresh_service_spec.rb
index 59d3912018a..5b4e4908add 100644
--- a/spec/services/merge_requests/refresh_service_spec.rb
+++ b/spec/services/merge_requests/refresh_service_spec.rb
@@ -118,7 +118,7 @@ describe MergeRequests::RefreshService, services: true do
it { expect(@merge_request.notes).to be_empty }
it { expect(@merge_request).to be_open }
- it { expect(@fork_merge_request.notes.last.note).to include('Added 4 commits') }
+ it { expect(@fork_merge_request.notes.last.note).to include('Added 28 commits') }
it { expect(@fork_merge_request).to be_open }
it { expect(@build_failed_todo).to be_pending }
it { expect(@fork_build_failed_todo).to be_pending }
@@ -169,7 +169,7 @@ describe MergeRequests::RefreshService, services: true do
notes = @fork_merge_request.notes.reorder(:created_at).map(&:note)
expect(notes[0]).to include('Restored source branch `master`')
- expect(notes[1]).to include('Added 4 commits')
+ expect(notes[1]).to include('Added 28 commits')
expect(@fork_merge_request).to be_open
end
end
diff --git a/spec/services/merge_requests/update_service_spec.rb b/spec/services/merge_requests/update_service_spec.rb
index 33db34c0f62..2433a7dad06 100644
--- a/spec/services/merge_requests/update_service_spec.rb
+++ b/spec/services/merge_requests/update_service_spec.rb
@@ -17,6 +17,7 @@ describe MergeRequests::UpdateService, services: true do
before do
project.team << [user, :master]
project.team << [user2, :developer]
+ project.team << [user3, :developer]
end
describe 'execute' do
@@ -104,6 +105,18 @@ describe MergeRequests::UpdateService, services: true do
expect(note).not_to be_nil
expect(note.note).to eq 'Target branch changed from `master` to `target`'
end
+
+ context 'when not including source branch removal options' do
+ before do
+ opts.delete(:force_remove_source_branch)
+ end
+
+ it 'maintains the original options' do
+ update_merge_request(opts)
+
+ expect(@merge_request.merge_params["force_remove_source_branch"]).to eq("1")
+ end
+ end
end
context 'todos' do
@@ -188,6 +201,11 @@ describe MergeRequests::UpdateService, services: true do
let!(:non_subscriber) { create(:user) }
let!(:subscriber) { create(:user).tap { |u| label.toggle_subscription(u) } }
+ before do
+ project.team << [non_subscriber, :developer]
+ project.team << [subscriber, :developer]
+ end
+
it 'sends notifications for subscribers of newly added labels' do
opts = { label_ids: [label.id] }
diff --git a/spec/services/notification_service_spec.rb b/spec/services/notification_service_spec.rb
index d820646ebdf..699b9925b4e 100644
--- a/spec/services/notification_service_spec.rb
+++ b/spec/services/notification_service_spec.rb
@@ -331,7 +331,7 @@ describe NotificationService, services: true do
describe '#new_note' do
it "records sent notifications" do
# Ensure create SentNotification by noteable = merge_request 6 times, not noteable = note
- expect(SentNotification).to receive(:record_note).with(note, any_args).exactly(4).times.and_call_original
+ expect(SentNotification).to receive(:record_note).with(note, any_args).exactly(3).times.and_call_original
notification.new_note(note)
@@ -1169,6 +1169,61 @@ describe NotificationService, services: true do
end
end
+ context 'guest user in private project' do
+ let(:private_project) { create(:empty_project, :private) }
+ let(:guest) { create(:user) }
+ let(:developer) { create(:user) }
+ let(:assignee) { create(:user) }
+ let(:merge_request) { create(:merge_request, source_project: private_project, assignee: assignee) }
+ let(:merge_request1) { create(:merge_request, source_project: private_project, assignee: assignee, description: "cc @#{guest.username}") }
+ let(:note) { create(:note, noteable: merge_request, project: private_project) }
+
+ before do
+ private_project.team << [assignee, :developer]
+ private_project.team << [developer, :developer]
+ private_project.team << [guest, :guest]
+
+ ActionMailer::Base.deliveries.clear
+ end
+
+ it 'filters out guests when new note is created' do
+ expect(SentNotification).to receive(:record).with(merge_request, any_args).exactly(1).times
+
+ notification.new_note(note)
+
+ should_not_email(guest)
+ should_email(assignee)
+ end
+
+ it 'filters out guests when new merge request is created' do
+ notification.new_merge_request(merge_request1, @u_disabled)
+
+ should_not_email(guest)
+ should_email(assignee)
+ end
+
+ it 'filters out guests when merge request is closed' do
+ notification.close_mr(merge_request, developer)
+
+ should_not_email(guest)
+ should_email(assignee)
+ end
+
+ it 'filters out guests when merge request is reopened' do
+ notification.reopen_mr(merge_request, developer)
+
+ should_not_email(guest)
+ should_email(assignee)
+ end
+
+ it 'filters out guests when merge request is merged' do
+ notification.merge_mr(merge_request, developer)
+
+ should_not_email(guest)
+ should_email(assignee)
+ end
+ end
+
def build_team(project)
@u_watcher = create_global_setting_for(create(:user), :watch)
@u_participating = create_global_setting_for(create(:user), :participating)
diff --git a/spec/services/slash_commands/interpret_service_spec.rb b/spec/services/slash_commands/interpret_service_spec.rb
index ae4d286d250..b57e338b782 100644
--- a/spec/services/slash_commands/interpret_service_spec.rb
+++ b/spec/services/slash_commands/interpret_service_spec.rb
@@ -86,6 +86,25 @@ describe SlashCommands::InterpretService, services: true do
end
end
+ shared_examples 'multiple label command' do
+ it 'fetches label ids and populates add_label_ids if content contains multiple /label' do
+ bug # populate the label
+ inprogress # populate the label
+ _, updates = service.execute(content, issuable)
+
+ expect(updates).to eq(add_label_ids: [inprogress.id, bug.id])
+ end
+ end
+
+ shared_examples 'multiple label with same argument' do
+ it 'prevents duplicate label ids and populates add_label_ids if content contains multiple /label' do
+ inprogress # populate the label
+ _, updates = service.execute(content, issuable)
+
+ expect(updates).to eq(add_label_ids: [inprogress.id])
+ end
+ end
+
shared_examples 'unlabel command' do
it 'fetches label ids and populates remove_label_ids if content contains /unlabel' do
issuable.update(label_ids: [inprogress.id]) # populate the label
@@ -95,6 +114,15 @@ describe SlashCommands::InterpretService, services: true do
end
end
+ shared_examples 'multiple unlabel command' do
+ it 'fetches label ids and populates remove_label_ids if content contains mutiple /unlabel' do
+ issuable.update(label_ids: [inprogress.id, bug.id]) # populate the label
+ _, updates = service.execute(content, issuable)
+
+ expect(updates).to eq(remove_label_ids: [inprogress.id, bug.id])
+ end
+ end
+
shared_examples 'unlabel command with no argument' do
it 'populates label_ids: [] if content contains /unlabel with no arguments' do
issuable.update(label_ids: [inprogress.id]) # populate the label
@@ -285,6 +313,16 @@ describe SlashCommands::InterpretService, services: true do
let(:issuable) { merge_request }
end
+ it_behaves_like 'multiple label command' do
+ let(:content) { %(/label ~"#{inprogress.title}" \n/label ~#{bug.title}) }
+ let(:issuable) { issue }
+ end
+
+ it_behaves_like 'multiple label with same argument' do
+ let(:content) { %(/label ~"#{inprogress.title}" \n/label ~#{inprogress.title}) }
+ let(:issuable) { issue }
+ end
+
it_behaves_like 'unlabel command' do
let(:content) { %(/unlabel ~"#{inprogress.title}") }
let(:issuable) { issue }
@@ -295,6 +333,11 @@ describe SlashCommands::InterpretService, services: true do
let(:issuable) { merge_request }
end
+ it_behaves_like 'multiple unlabel command' do
+ let(:content) { %(/unlabel ~"#{inprogress.title}" \n/unlabel ~#{bug.title}) }
+ let(:issuable) { issue }
+ end
+
it_behaves_like 'unlabel command with no argument' do
let(:content) { %(/unlabel) }
let(:issuable) { issue }
diff --git a/spec/services/system_note_service_spec.rb b/spec/services/system_note_service_spec.rb
index 304d4e62396..b4ba28dfe8e 100644
--- a/spec/services/system_note_service_spec.rb
+++ b/spec/services/system_note_service_spec.rb
@@ -54,7 +54,7 @@ describe SystemNoteService, services: true do
it 'adds a message line for each commit' do
new_commits.each_with_index do |commit, i|
# Skip the header
- expect(note_lines[i + 1]).to eq "* #{commit.short_id} - #{commit.title}"
+ expect(HTMLEntities.new.decode(note_lines[i + 1])).to eq "* #{commit.short_id} - #{commit.title}"
end
end
end
@@ -81,7 +81,7 @@ describe SystemNoteService, services: true do
end
it 'includes a commit count' do
- expect(summary_line).to end_with " - 2 commits from branch `feature`"
+ expect(summary_line).to end_with " - 26 commits from branch `feature`"
end
end
@@ -91,7 +91,7 @@ describe SystemNoteService, services: true do
end
it 'includes a commit count' do
- expect(summary_line).to end_with " - 2 commits from branch `feature`"
+ expect(summary_line).to end_with " - 26 commits from branch `feature`"
end
end
@@ -537,7 +537,7 @@ describe SystemNoteService, services: true do
let(:mergereq) { create(:merge_request, :simple, target_project: project, source_project: project) }
let(:jira_issue) { ExternalIssue.new("JIRA-1", project)}
let(:jira_tracker) { project.jira_service }
- let(:commit) { project.commit }
+ let(:commit) { project.repository.commits('master').find { |commit| commit.id == '5937ac0a7beb003549fc5fd26fc247adbce4a52e' } }
context 'in JIRA issue tracker' do
before do
diff --git a/spec/services/todo_service_spec.rb b/spec/services/todo_service_spec.rb
index b41f6f14fbd..ed55791d24e 100644
--- a/spec/services/todo_service_spec.rb
+++ b/spec/services/todo_service_spec.rb
@@ -345,7 +345,7 @@ describe TodoService, services: true do
service.new_merge_request(mr_assigned, author)
should_create_todo(user: member, target: mr_assigned, action: Todo::MENTIONED)
- should_create_todo(user: guest, target: mr_assigned, action: Todo::MENTIONED)
+ should_not_create_todo(user: guest, target: mr_assigned, action: Todo::MENTIONED)
should_create_todo(user: author, target: mr_assigned, action: Todo::MENTIONED)
should_not_create_todo(user: john_doe, target: mr_assigned, action: Todo::MENTIONED)
should_not_create_todo(user: non_member, target: mr_assigned, action: Todo::MENTIONED)
@@ -357,7 +357,7 @@ describe TodoService, services: true do
service.update_merge_request(mr_assigned, author)
should_create_todo(user: member, target: mr_assigned, action: Todo::MENTIONED)
- should_create_todo(user: guest, target: mr_assigned, action: Todo::MENTIONED)
+ should_not_create_todo(user: guest, target: mr_assigned, action: Todo::MENTIONED)
should_create_todo(user: john_doe, target: mr_assigned, action: Todo::MENTIONED)
should_create_todo(user: author, target: mr_assigned, action: Todo::MENTIONED)
should_not_create_todo(user: non_member, target: mr_assigned, action: Todo::MENTIONED)
@@ -381,6 +381,7 @@ describe TodoService, services: true do
should_not_create_todo(user: john_doe, target: mr_assigned, action: Todo::MENTIONED)
should_not_create_todo(user: member, target: mr_assigned, action: Todo::MENTIONED)
should_not_create_todo(user: non_member, target: mr_assigned, action: Todo::MENTIONED)
+ should_not_create_todo(user: guest, target: mr_assigned, action: Todo::MENTIONED)
end
it 'does not raise an error when description not change' do
@@ -430,6 +431,11 @@ describe TodoService, services: true do
should_create_todo(user: john_doe, target: mr_assigned, author: john_doe, action: Todo::ASSIGNED)
end
+
+ it 'does not create a todo for guests' do
+ service.reassigned_merge_request(mr_assigned, author)
+ should_not_create_todo(user: guest, target: mr_assigned, action: Todo::MENTIONED)
+ end
end
describe '#merge_merge_request' do
@@ -441,6 +447,11 @@ describe TodoService, services: true do
expect(first_todo.reload).to be_done
expect(second_todo.reload).to be_done
end
+
+ it 'does not create todo for guests' do
+ service.merge_merge_request(mr_assigned, john_doe)
+ should_not_create_todo(user: guest, target: mr_assigned, action: Todo::MENTIONED)
+ end
end
describe '#new_award_emoji' do
@@ -495,6 +506,13 @@ describe TodoService, services: true do
should_create_todo(user: john_doe, target: mr_unassigned, author: author, action: Todo::MENTIONED, note: legacy_diff_note_on_merge_request)
end
+
+ it 'does not create todo for guests' do
+ note_on_merge_request = create :note_on_merge_request, project: project, noteable: mr_assigned, note: mentions
+ service.new_note(note_on_merge_request, author)
+
+ should_not_create_todo(user: guest, target: mr_assigned, action: Todo::MENTIONED)
+ end
end
end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index f313bd4f249..b19f5824236 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -50,11 +50,6 @@ RSpec.configure do |config|
example.run
Rails.cache = caching_store
end
-
- config.after(:each) do
- FileUtils.rm_rf("tmp/tests/repositories")
- FileUtils.mkdir_p("tmp/tests/repositories")
- end
end
FactoryGirl::SyntaxRunner.class_eval do
diff --git a/spec/support/test_env.rb b/spec/support/test_env.rb
index 0097dbf8fad..d56274d0979 100644
--- a/spec/support/test_env.rb
+++ b/spec/support/test_env.rb
@@ -5,6 +5,8 @@ module TestEnv
# When developing the seed repository, comment out the branch you will modify.
BRANCH_SHA = {
+ 'not-merged-branch' => 'b83d6e3',
+ 'branch-merged' => '498214d',
'empty-branch' => '7efb185',
'ends-with.json' => '98b0d8b',
'flatten-dir' => 'e56497b',
@@ -14,7 +16,7 @@ module TestEnv
'improve/awesome' => '5937ac0',
'markdown' => '0ed8c6c',
'lfs' => 'be93687',
- 'master' => '5937ac0',
+ 'master' => 'b83d6e3',
"'test'" => 'e56497b',
'orphaned-branch' => '45127a9',
'binary-encoding' => '7b1cf43',
diff --git a/spec/workers/emails_on_push_worker_spec.rb b/spec/workers/emails_on_push_worker_spec.rb
index 7ca2c29da1c..036d037f3f9 100644
--- a/spec/workers/emails_on_push_worker_spec.rb
+++ b/spec/workers/emails_on_push_worker_spec.rb
@@ -57,7 +57,7 @@ describe EmailsOnPushWorker do
end
it "sends a mail with the correct subject" do
- expect(email.subject).to include('Change some files')
+ expect(email.subject).to include('adds bar folder and branch-test text file')
end
it "mentions force pushing in the body" do
@@ -73,7 +73,7 @@ describe EmailsOnPushWorker do
before { perform }
it "sends a mail with the correct subject" do
- expect(email.subject).to include('Change some files')
+ expect(email.subject).to include('adds bar folder and branch-test text file')
end
it "does not mention force pushing in the body" do
diff --git a/spec/workers/process_pipeline_worker_spec.rb b/spec/workers/pipeline_proccess_worker_spec.rb
index 7b5f98d5763..86e9d7f6684 100644
--- a/spec/workers/process_pipeline_worker_spec.rb
+++ b/spec/workers/pipeline_proccess_worker_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe ProcessPipelineWorker do
+describe PipelineProcessWorker do
describe '#perform' do
context 'when pipeline exists' do
let(:pipeline) { create(:ci_pipeline) }
diff --git a/spec/workers/pipeline_success_worker_spec.rb b/spec/workers/pipeline_success_worker_spec.rb
new file mode 100644
index 00000000000..5e31cc2c8e7
--- /dev/null
+++ b/spec/workers/pipeline_success_worker_spec.rb
@@ -0,0 +1,24 @@
+require 'spec_helper'
+
+describe PipelineSuccessWorker do
+ describe '#perform' do
+ context 'when pipeline exists' do
+ let(:pipeline) { create(:ci_pipeline, status: 'success') }
+
+ it 'performs "merge when pipeline succeeds"' do
+ expect_any_instance_of(
+ MergeRequests::MergeWhenBuildSucceedsService
+ ).to receive(:trigger)
+
+ described_class.new.perform(pipeline.id)
+ end
+ end
+
+ context 'when pipeline does not exist' do
+ it 'does not raise exception' do
+ expect { described_class.new.perform(123) }
+ .not_to raise_error
+ end
+ end
+ end
+end
diff --git a/spec/workers/update_pipeline_worker_spec.rb b/spec/workers/pipeline_update_worker_spec.rb
index fadc42b22f0..0b456cfd0da 100644
--- a/spec/workers/update_pipeline_worker_spec.rb
+++ b/spec/workers/pipeline_update_worker_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe UpdatePipelineWorker do
+describe PipelineUpdateWorker do
describe '#perform' do
context 'when pipeline exists' do
let(:pipeline) { create(:ci_pipeline) }