summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitlab-ci.yml4
-rw-r--r--.rubocop.yml18
-rw-r--r--.rubocop_todo.yml5
-rw-r--r--Gemfile8
-rw-r--r--Gemfile.lock24
-rw-r--r--PROCESS.md16
-rw-r--r--app/assets/javascripts/commits.js2
-rw-r--r--app/assets/javascripts/diff_notes/diff_notes_bundle.js4
-rw-r--r--app/assets/javascripts/dispatcher.js2
-rw-r--r--app/assets/javascripts/groups_select.js3
-rw-r--r--app/assets/javascripts/lib/utils/sticky.js2
-rw-r--r--app/assets/javascripts/main.js4
-rw-r--r--app/assets/javascripts/new_sidebar.js6
-rw-r--r--app/assets/javascripts/render_gfm.js4
-rw-r--r--app/assets/javascripts/repo/components/repo.vue21
-rw-r--r--app/assets/javascripts/sidebar/components/confidential/confidential_issue_sidebar.vue2
-rw-r--r--app/assets/stylesheets/framework/nav.scss2
-rw-r--r--app/assets/stylesheets/new_sidebar.scss11
-rw-r--r--app/assets/stylesheets/pages/issuable.scss3
-rw-r--r--app/assets/stylesheets/pages/repo.scss29
-rw-r--r--app/controllers/concerns/issuable_actions.rb2
-rw-r--r--app/controllers/groups/application_controller.rb2
-rw-r--r--app/controllers/import/github_controller.rb2
-rw-r--r--app/controllers/omniauth_callbacks_controller.rb4
-rw-r--r--app/controllers/projects/cycle_analytics/events_controller.rb24
-rw-r--r--app/controllers/projects/merge_requests_controller.rb4
-rw-r--r--app/controllers/uploads_controller.rb2
-rw-r--r--app/helpers/commits_helper.rb6
-rw-r--r--app/helpers/import_helper.rb2
-rw-r--r--app/helpers/issuables_helper.rb13
-rw-r--r--app/helpers/milestones_helper.rb13
-rw-r--r--app/helpers/pipeline_schedules_helper.rb10
-rw-r--r--app/helpers/projects_helper.rb7
-rw-r--r--app/helpers/version_check_helper.rb2
-rw-r--r--app/models/blob_viewer/notebook.rb2
-rw-r--r--app/models/commit.rb4
-rw-r--r--app/models/concerns/cache_markdown_field.rb6
-rw-r--r--app/models/concerns/internal_id.rb2
-rw-r--r--app/models/concerns/mentionable.rb4
-rw-r--r--app/models/concerns/participable.rb2
-rw-r--r--app/models/concerns/project_features_compatibility.rb2
-rw-r--r--app/models/concerns/referable.rb9
-rw-r--r--app/models/deploy_keys_project.rb2
-rw-r--r--app/models/gpg_signature.rb4
-rw-r--r--app/models/network/commit.rb2
-rw-r--r--app/models/project.rb34
-rw-r--r--app/models/project_services/chat_notification_service.rb2
-rw-r--r--app/models/project_services/hipchat_service.rb2
-rw-r--r--app/models/protectable_dropdown.rb8
-rw-r--r--app/models/redirect_route.rb2
-rw-r--r--app/models/repository.rb10
-rw-r--r--app/models/user.rb7
-rw-r--r--app/serializers/project_entity.rb2
-rw-r--r--app/services/akismet_service.rb2
-rw-r--r--app/services/ci/retry_build_service.rb2
-rw-r--r--app/services/commits/change_service.rb1
-rw-r--r--app/services/git_push_service.rb15
-rw-r--r--app/services/issuable_base_service.rb2
-rw-r--r--app/services/members/destroy_service.rb2
-rw-r--r--app/services/notification_service.rb2
-rw-r--r--app/services/system_hooks_service.rb2
-rw-r--r--app/services/test_hooks/base_service.rb2
-rw-r--r--app/views/help/ui.html.haml2
-rw-r--r--app/views/profiles/accounts/show.html.haml37
-rw-r--r--app/views/projects/_project_templates.html.haml2
-rw-r--r--app/views/projects/merge_requests/_discussion.html.haml6
-rw-r--r--app/views/projects/new.html.haml2
-rw-r--r--app/views/shared/_import_form.html.haml2
-rw-r--r--app/views/shared/milestones/_milestone.html.haml2
-rw-r--r--app/views/shared/milestones/_sidebar.html.haml2
-rw-r--r--app/views/shared/milestones/_top.html.haml2
-rw-r--r--app/workers/create_gpg_signature_worker.rb8
-rw-r--r--app/workers/gitlab_shell_worker.rb2
-rw-r--r--app/workers/namespaceless_project_destroy_worker.rb4
-rw-r--r--app/workers/repository_fork_worker.rb16
-rw-r--r--app/workers/repository_import_worker.rb24
-rw-r--r--app/workers/stuck_import_jobs_worker.rb50
-rw-r--r--changelogs/unreleased/34049-public-commits-should-not-require-authentication.yml4
-rw-r--r--changelogs/unreleased/35072-fix-pages-delete.yml5
-rw-r--r--changelogs/unreleased/36087-users-cannot-delete-their-account.yml5
-rw-r--r--changelogs/unreleased/commits-list-page-limit.yml5
-rw-r--r--changelogs/unreleased/issue_35580.yml4
-rw-r--r--changelogs/unreleased/zj-upgrade-grape.yml5
-rw-r--r--config/gitlab.yml.example3
-rw-r--r--config/initializers/0_acts_as_taggable.rb2
-rw-r--r--config/initializers/1_settings.rb2
-rw-r--r--config/initializers/6_validations.rb4
-rw-r--r--config/initializers/active_record_array_type_casting.rb20
-rw-r--r--config/initializers/static_files.rb10
-rw-r--r--config/initializers/trusted_proxies.rb2
-rw-r--r--config/routes/repository.rb2
-rw-r--r--db/migrate/20161020075830_default_request_access_projects.rb2
-rw-r--r--db/post_migrate/20170503004427_update_retried_for_ci_build.rb2
-rw-r--r--db/post_migrate/20170523083112_migrate_old_artifacts.rb6
-rw-r--r--doc/README.md2
-rw-r--r--doc/articles/index.md105
-rw-r--r--doc/articles/numerous_undo_possibilities_in_git/img/branching.pngbin0 -> 26245 bytes
-rw-r--r--doc/articles/numerous_undo_possibilities_in_git/img/rebase_reset.pngbin0 -> 43609 bytes
-rw-r--r--doc/articles/numerous_undo_possibilities_in_git/img/revert.pngbin0 -> 28112 bytes
-rw-r--r--doc/articles/numerous_undo_possibilities_in_git/index.md497
-rw-r--r--doc/development/README.md4
-rw-r--r--doc/development/database_merge_request_checklist.md15
-rw-r--r--doc/development/hash_indexes.md20
-rw-r--r--doc/development/ordering_table_columns.md127
-rw-r--r--doc/development/serializing_data.md3
-rw-r--r--doc/development/sql.md26
-rw-r--r--doc/development/verifying_database_capabilities.md26
-rw-r--r--doc/gitlab-basics/create-project.md12
-rw-r--r--doc/install/installation.md2
-rw-r--r--doc/topics/git/index.md1
-rw-r--r--doc/user/group/index.md7
-rw-r--r--doc/user/group/subgroups/index.md7
-rw-r--r--doc/user/project/import/bitbucket.md62
-rw-r--r--doc/user/project/import/fogbugz.md28
-rw-r--r--doc/user/project/import/gitea.md77
-rw-r--r--doc/user/project/import/github.md122
-rw-r--r--doc/user/project/import/gitlab_com.md20
-rw-r--r--doc/user/project/import/img/bitbucket_import_grant_access.png (renamed from doc/workflow/importing/img/bitbucket_import_grant_access.png)bin7248 -> 7248 bytes
-rw-r--r--doc/user/project/import/img/bitbucket_import_new_project.png (renamed from doc/workflow/importing/img/bitbucket_import_new_project.png)bin1316 -> 1316 bytes
-rw-r--r--doc/user/project/import/img/bitbucket_import_select_project.png (renamed from doc/workflow/importing/img/bitbucket_import_select_project.png)bin8688 -> 8688 bytes
-rw-r--r--doc/user/project/import/img/fogbugz_import_finished.png (renamed from doc/workflow/importing/fogbugz_importer/fogbugz_import_finished.png)bin17744 -> 17744 bytes
-rw-r--r--doc/user/project/import/img/fogbugz_import_login.png (renamed from doc/workflow/importing/fogbugz_importer/fogbugz_import_login.png)bin13751 -> 13751 bytes
-rw-r--r--doc/user/project/import/img/fogbugz_import_select_fogbogz.png (renamed from doc/workflow/importing/fogbugz_importer/fogbugz_import_select_fogbogz.png)bin12289 -> 12289 bytes
-rw-r--r--doc/user/project/import/img/fogbugz_import_select_project.png (renamed from doc/workflow/importing/fogbugz_importer/fogbugz_import_select_project.png)bin20905 -> 20905 bytes
-rw-r--r--doc/user/project/import/img/fogbugz_import_user_map.png (renamed from doc/workflow/importing/fogbugz_importer/fogbugz_import_user_map.png)bin51238 -> 51238 bytes
-rw-r--r--doc/user/project/import/img/gitlab_importer.png (renamed from doc/workflow/importing/gitlab_importer/importer.png)bin12864 -> 12864 bytes
-rw-r--r--doc/user/project/import/img/gitlab_new_project_page.png (renamed from doc/workflow/importing/gitlab_importer/new_project_page.png)bin21251 -> 21251 bytes
-rw-r--r--doc/user/project/import/img/import_projects_from_gitea_new_import.png (renamed from doc/workflow/importing/img/import_projects_from_gitea_new_import.png)bin15561 -> 15561 bytes
-rw-r--r--doc/user/project/import/img/import_projects_from_github_importer.png (renamed from doc/workflow/importing/img/import_projects_from_github_importer.png)bin17953 -> 17953 bytes
-rw-r--r--doc/user/project/import/img/import_projects_from_github_select_auth_method.png (renamed from doc/workflow/importing/img/import_projects_from_github_select_auth_method.png)bin17612 -> 17612 bytes
-rw-r--r--doc/user/project/import/img/import_projects_from_new_project_page.png (renamed from doc/workflow/importing/img/import_projects_from_new_project_page.png)bin36821 -> 36821 bytes
-rw-r--r--doc/user/project/import/index.md20
-rw-r--r--doc/user/project/import/svn.md183
-rw-r--r--doc/user/project/index.md10
-rw-r--r--doc/user/reserved_names.md109
-rw-r--r--doc/workflow/importing/README.md18
-rw-r--r--doc/workflow/importing/import_projects_from_bitbucket.md63
-rw-r--r--doc/workflow/importing/import_projects_from_fogbugz.md30
-rw-r--r--doc/workflow/importing/import_projects_from_gitea.md78
-rw-r--r--doc/workflow/importing/import_projects_from_github.md123
-rw-r--r--doc/workflow/importing/import_projects_from_gitlab_com.md22
-rw-r--r--doc/workflow/importing/migrating_from_svn.md184
-rw-r--r--features/steps/profile/emails.rb2
-rw-r--r--lib/after_commit_queue.rb30
-rw-r--r--lib/api/api_guard.rb2
-rw-r--r--lib/api/entities.rb7
-rw-r--r--lib/api/helpers.rb2
-rw-r--r--lib/api/jobs.rb4
-rw-r--r--lib/api/protected_branches.rb2
-rw-r--r--lib/api/runners.rb2
-rw-r--r--lib/api/templates.rb6
-rw-r--r--lib/api/v3/builds.rb2
-rw-r--r--lib/api/v3/notes.rb6
-rw-r--r--lib/api/v3/templates.rb6
-rw-r--r--lib/banzai/filter/external_issue_reference_filter.rb4
-rw-r--r--lib/banzai/filter/image_lazy_load_filter.rb4
-rw-r--r--lib/banzai/object_renderer.rb2
-rw-r--r--lib/banzai/pipeline/base_pipeline.rb2
-rw-r--r--lib/banzai/renderer.rb4
-rw-r--r--lib/bitbucket/collection.rb2
-rw-r--r--lib/ci/ansi2html.rb2
-rw-r--r--lib/constraints/project_url_constrainer.rb2
-rw-r--r--lib/declarative_policy/base.rb4
-rw-r--r--lib/declarative_policy/dsl.rb2
-rw-r--r--lib/file_size_validator.rb4
-rw-r--r--lib/gitlab/auth.rb4
-rw-r--r--lib/gitlab/auth/ip_rate_limiter.rb12
-rw-r--r--lib/gitlab/cache/request_cache.rb2
-rw-r--r--lib/gitlab/ci/build/artifacts/metadata.rb2
-rw-r--r--lib/gitlab/diff/line_mapper.rb6
-rw-r--r--lib/gitlab/git/blob.rb4
-rw-r--r--lib/gitlab/git/commit.rb19
-rw-r--r--lib/gitlab/git/tree.rb2
-rw-r--r--lib/gitlab/gitaly_client.rb2
-rw-r--r--lib/gitlab/gitaly_client/blob_service.rb13
-rw-r--r--lib/gitlab/gitaly_client/commit_service.rb18
-rw-r--r--lib/gitlab/github_import/base_formatter.rb4
-rw-r--r--lib/gitlab/github_import/client.rb2
-rw-r--r--lib/gitlab/github_import/importer.rb2
-rw-r--r--lib/gitlab/gpg/commit.rb38
-rw-r--r--lib/gitlab/gpg/invalid_gpg_signature_updater.rb4
-rw-r--r--lib/gitlab/import_export/attributes_finder.rb1
-rw-r--r--lib/gitlab/import_export/project_tree_restorer.rb21
-rw-r--r--lib/gitlab/lazy.rb2
-rw-r--r--lib/gitlab/ldap/auth_hash.rb2
-rw-r--r--lib/gitlab/ldap/person.rb4
-rw-r--r--lib/gitlab/markdown/pipeline.rb2
-rw-r--r--lib/gitlab/middleware/rails_queue_duration.rb2
-rw-r--r--lib/gitlab/sidekiq_status.rb9
-rw-r--r--lib/gitlab/slash_commands/presenters/help.rb2
-rw-r--r--lib/tasks/gitlab/gitaly.rake2
-rw-r--r--lib/uploaded_file.rb2
-rw-r--r--qa/qa/runtime/release.rb2
-rw-r--r--spec/features/admin/admin_settings_spec.rb2
-rw-r--r--spec/features/groups/milestone_spec.rb29
-rw-r--r--spec/features/help_pages_spec.rb2
-rw-r--r--spec/features/issues/filtered_search/dropdown_assignee_spec.rb12
-rw-r--r--spec/features/issues/filtered_search/dropdown_author_spec.rb10
-rw-r--r--spec/features/issues/filtered_search/dropdown_label_spec.rb18
-rw-r--r--spec/features/issues/filtered_search/dropdown_milestone_spec.rb20
-rw-r--r--spec/features/issues/filtered_search/filter_issues_spec.rb192
-rw-r--r--spec/features/issues/filtered_search/search_bar_spec.rb2
-rw-r--r--spec/features/issues/filtered_search/visual_tokens_spec.rb4
-rw-r--r--spec/features/issues_spec.rb2
-rw-r--r--spec/features/merge_requests/conflicts_spec.rb10
-rw-r--r--spec/features/merge_requests/filter_by_milestone_spec.rb2
-rw-r--r--spec/features/merge_requests/filter_merge_requests_spec.rb58
-rw-r--r--spec/features/milestones/show_spec.rb2
-rw-r--r--spec/features/profile_spec.rb15
-rw-r--r--spec/features/projects/files/undo_template_spec.rb2
-rw-r--r--spec/features/projects_spec.rb2
-rw-r--r--spec/features/search_spec.rb12
-rw-r--r--spec/helpers/version_check_helper_spec.rb2
-rw-r--r--spec/javascripts/fixtures/prometheus_service.rb2
-rw-r--r--spec/javascripts/fixtures/services.rb1
-rw-r--r--spec/javascripts/sidebar/confidential_issue_sidebar_spec.js2
-rw-r--r--spec/lib/after_commit_queue_spec.rb15
-rw-r--r--spec/lib/file_size_validator_spec.rb4
-rw-r--r--spec/lib/gitlab/background_migration/migrate_events_to_push_event_payloads_spec.rb21
-rw-r--r--spec/lib/gitlab/ci/trace/stream_spec.rb2
-rw-r--r--spec/lib/gitlab/cycle_analytics/base_event_fetcher_spec.rb4
-rw-r--r--spec/lib/gitlab/git/storage/circuit_breaker_spec.rb59
-rw-r--r--spec/lib/gitlab/gpg/commit_spec.rb71
-rw-r--r--spec/lib/gitlab/gpg/invalid_gpg_signature_updater_spec.rb23
-rw-r--r--spec/lib/gitlab/import_export/project.light.json100
-rw-r--r--spec/lib/gitlab/import_export/project_tree_restorer_spec.rb38
-rw-r--r--spec/lib/gitlab/key_fingerprint_spec.rb4
-rw-r--r--spec/lib/gitlab/ldap/auth_hash_spec.rb12
-rw-r--r--spec/lib/gitlab/o_auth/user_spec.rb2
-rw-r--r--spec/lib/gitlab/prometheus/additional_metrics_parser_spec.rb4
-rw-r--r--spec/migrations/README.md10
-rw-r--r--spec/migrations/migrate_old_artifacts_spec.rb4
-rw-r--r--spec/models/project_spec.rb65
-rw-r--r--spec/models/protectable_dropdown_spec.rb7
-rw-r--r--spec/requests/api/commits_spec.rb213
-rw-r--r--spec/requests/api/protected_branches_spec.rb4
-rw-r--r--spec/requests/api/settings_spec.rb2
-rw-r--r--spec/requests/ci/api/builds_spec.rb2
-rw-r--r--spec/services/git_push_service_spec.rb34
-rw-r--r--spec/services/web_hook_service_spec.rb2
-rw-r--r--spec/spec_helper.rb13
-rw-r--r--spec/support/db_cleaner.rb2
-rw-r--r--spec/support/filtered_search_helpers.rb26
-rwxr-xr-xspec/support/generate-seed-repo-rb12
-rw-r--r--spec/support/matchers/access_matchers_for_controller.rb2
-rw-r--r--spec/support/migrations_helpers.rb29
-rw-r--r--spec/support/stub_configuration.rb5
-rw-r--r--spec/tasks/gitlab/gitaly_rake_spec.rb2
-rw-r--r--spec/workers/create_gpg_signature_worker_spec.rb26
-rw-r--r--spec/workers/repository_import_worker_spec.rb2
-rw-r--r--spec/workers/stuck_import_jobs_worker_spec.rb34
251 files changed, 2802 insertions, 1390 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 4fcf51fb86e..df7244d5a2e 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -1,4 +1,4 @@
-image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.3.3-golang-1.8-git-2.7-phantomjs-2.1-node-7.1-postgresql-9.6"
+image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.3.3-golang-1.8-git-2.13-phantomjs-2.1-node-7.1-postgresql-9.6"
.default-cache: &default-cache
key: "ruby-233-with-yarn"
@@ -522,7 +522,7 @@ karma:
<<: *dedicated-runner
<<: *except-docs
<<: *pull-cache
- image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.3.3-golang-1.8-git-2.7-chrome-59.0-node-7.1-postgresql-9.6"
+ image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.3.3-golang-1.8-git-2.13-chrome-59.0-node-7.1-postgresql-9.6"
stage: test
variables:
BABEL_ENV: "coverage"
diff --git a/.rubocop.yml b/.rubocop.yml
index d25b4ac39c9..23bb0fa8be8 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -251,6 +251,10 @@ Layout/Tab:
Layout/TrailingBlankLines:
Enabled: true
+# Avoid trailing whitespace.
+Layout/TrailingWhitespace:
+ Enabled: true
+
# Style #######################################################################
# Check the naming of accessor methods for get_/set_.
@@ -1174,29 +1178,33 @@ RSpec/VerifiedDoubles:
GitlabSecurity/DeepMunge:
Enabled: true
Exclude:
- - 'spec/**/*'
- 'lib/**/*.rake'
+ - 'spec/**/*'
GitlabSecurity/PublicSend:
Enabled: true
Exclude:
- - 'spec/**/*'
+ - 'config/**/*'
+ - 'db/**/*'
+ - 'features/**/*'
- 'lib/**/*.rake'
+ - 'qa/**/*'
+ - 'spec/**/*'
GitlabSecurity/RedirectToParamsUpdate:
Enabled: true
Exclude:
- - 'spec/**/*'
- 'lib/**/*.rake'
+ - 'spec/**/*'
GitlabSecurity/SqlInjection:
Enabled: true
Exclude:
- - 'spec/**/*'
- 'lib/**/*.rake'
+ - 'spec/**/*'
GitlabSecurity/SystemCommandInjection:
Enabled: true
Exclude:
- - 'spec/**/*'
- 'lib/**/*.rake'
+ - 'spec/**/*'
diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml
index cf14285ec2a..4b4f14efea4 100644
--- a/.rubocop_todo.yml
+++ b/.rubocop_todo.yml
@@ -57,11 +57,6 @@ Layout/SpaceInsideParens:
Layout/SpaceInsidePercentLiteralDelimiters:
Enabled: false
-# Offense count: 89
-# Cop supports --auto-correct.
-Layout/TrailingWhitespace:
- Enabled: false
-
# Offense count: 272
RSpec/EmptyLineAfterFinalLet:
Enabled: false
diff --git a/Gemfile b/Gemfile
index a484cefb9a4..62d6b1e6446 100644
--- a/Gemfile
+++ b/Gemfile
@@ -16,7 +16,7 @@ gem 'mysql2', '~> 0.4.5', group: :mysql
gem 'pg', '~> 0.18.2', group: :postgres
gem 'rugged', '~> 0.26.0'
-gem 'grape-route-helpers', '~> 2.0.0'
+gem 'grape-route-helpers', '~> 2.1.0'
gem 'faraday', '~> 0.12'
@@ -76,7 +76,7 @@ gem 'gollum-rugged_adapter', '~> 0.4.4', require: false
gem 'github-linguist', '~> 4.7.0', require: 'linguist'
# API
-gem 'grape', '~> 0.19.2'
+gem 'grape', '~> 1.0'
gem 'grape-entity', '~> 0.6.0'
gem 'rack-cors', '~> 0.4.0', require: 'rack/cors'
@@ -144,8 +144,6 @@ end
# State machine
gem 'state_machines-activerecord', '~> 0.4.0'
-# Run events after state machine commits
-gem 'after_commit_queue', '~> 1.3.0'
# Issue tags
gem 'acts-as-taggable-on', '~> 4.0'
@@ -232,7 +230,7 @@ gem 'ace-rails-ap', '~> 4.1.0'
gem 'mousetrap-rails', '~> 1.4.6'
# Detect and convert string character encoding
-gem 'charlock_holmes', '~> 0.7.3'
+gem 'charlock_holmes', '~> 0.7.5'
# Faster JSON
gem 'oj', '~> 2.17.4'
diff --git a/Gemfile.lock b/Gemfile.lock
index a93caba2393..ae1df783a80 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -46,8 +46,6 @@ GEM
ice_nine (~> 0.11.0)
memoizable (~> 0.4.0)
addressable (2.3.8)
- after_commit_queue (1.3.0)
- activerecord (>= 3.0)
akismet (2.0.0)
allocations (1.0.5)
arel (6.0.4)
@@ -117,7 +115,7 @@ GEM
activesupport (>= 4.0.0)
mime-types (>= 1.16)
cause (0.1)
- charlock_holmes (0.7.3)
+ charlock_holmes (0.7.5)
chronic (0.10.2)
chronic_duration (0.10.6)
numerizer (~> 0.1.1)
@@ -342,12 +340,9 @@ GEM
signet (~> 0.7)
gpgme (2.0.13)
mini_portile2 (~> 2.1)
- grape (0.19.2)
+ grape (1.0.0)
activesupport
builder
- hashie (>= 2.1.0)
- multi_json (>= 1.3.2)
- multi_xml (>= 0.5.2)
mustermann-grape (~> 1.0.0)
rack (>= 1.3.0)
rack-accept
@@ -355,11 +350,11 @@ GEM
grape-entity (0.6.0)
activesupport
multi_json (>= 1.3.2)
- grape-route-helpers (2.0.0)
+ grape-route-helpers (2.1.0)
activesupport
- grape (~> 0.16, >= 0.16.0)
+ grape (>= 0.16.0)
rake
- grpc (1.4.0)
+ grpc (1.4.5)
google-protobuf (~> 3.1)
googleauth (~> 0.5.1)
haml (4.0.7)
@@ -375,7 +370,7 @@ GEM
thor
tilt
hashdiff (0.3.4)
- hashie (3.5.5)
+ hashie (3.5.6)
hashie-forbidden_attributes (0.1.1)
hashie (>= 3.0)
health_check (2.6.0)
@@ -963,7 +958,6 @@ DEPENDENCIES
activerecord_sane_schema_dumper (= 0.2)
acts-as-taggable-on (~> 4.0)
addressable (~> 2.3.8)
- after_commit_queue (~> 1.3.0)
akismet (~> 2.0)
allocations (~> 1.0)
asana (~> 0.6.0)
@@ -986,7 +980,7 @@ DEPENDENCIES
capybara (~> 2.6.2)
capybara-screenshot (~> 1.0.0)
carrierwave (~> 1.1)
- charlock_holmes (~> 0.7.3)
+ charlock_holmes (~> 0.7.5)
chronic (~> 0.10.2)
chronic_duration (~> 0.10.6)
concurrent-ruby (~> 1.0.5)
@@ -1035,9 +1029,9 @@ DEPENDENCIES
gon (~> 6.1.0)
google-api-client (~> 0.8.6)
gpgme
- grape (~> 0.19.2)
+ grape (~> 1.0)
grape-entity (~> 0.6.0)
- grape-route-helpers (~> 2.0.0)
+ grape-route-helpers (~> 2.1.0)
haml_lint (~> 0.26.0)
hamlit (~> 2.6.1)
hashie-forbidden_attributes
diff --git a/PROCESS.md b/PROCESS.md
index e5b17784d20..538e4389e00 100644
--- a/PROCESS.md
+++ b/PROCESS.md
@@ -141,18 +141,22 @@ the stable branch are:
* Fixes for security issues
* New or updated translations (as long as they do not touch application code)
-Any merge requests cherry-picked into the stable branch for a previous release
-will also be picked into the latest stable branch. These fixes will be shipped
-in the next RC for that release if it is before the 22nd. If the fixes are are
-completed on or after the 22nd, they will be shipped in a patch for that
-release.
-
During the feature freeze all merge requests that are meant to go into the upcoming
release should have the correct milestone assigned _and_ have the label
~"Pick into Stable" set, so that release managers can find and pick them.
Merge requests without a milestone and this label will
not be merged into any stable branches.
+Fixes marked like this will be shipped in the next RC for that release. Once
+the final RC has been prepared ready for release on the 22nd, further fixes
+marked ~"Pick into Stable" will go into a patch for that release.
+
+If a merge request is to be picked into more than one release it will also need
+the ~"Pick into Backports" label set to remind the release manager to change
+the milestone after cherry-picking. As before, it should still have the
+~"Pick into Stable" label and the milestone of the highest release it will be
+picked into.
+
### Asking for an exception
If you think a merge request should go into an RC or patch even though it does not meet these requirements,
diff --git a/app/assets/javascripts/commits.js b/app/assets/javascripts/commits.js
index 2b0bf49cf92..047544b1762 100644
--- a/app/assets/javascripts/commits.js
+++ b/app/assets/javascripts/commits.js
@@ -17,7 +17,7 @@ window.CommitsList = (function() {
}
});
- Pager.init(limit, false, false, this.processCommits);
+ Pager.init(parseInt(limit, 10), false, false, this.processCommits);
this.content = $("#commits-list");
this.searchField = $("#commits-search");
diff --git a/app/assets/javascripts/diff_notes/diff_notes_bundle.js b/app/assets/javascripts/diff_notes/diff_notes_bundle.js
index a2d33b0936e..5decfc1dc01 100644
--- a/app/assets/javascripts/diff_notes/diff_notes_bundle.js
+++ b/app/assets/javascripts/diff_notes/diff_notes_bundle.js
@@ -42,6 +42,10 @@ $(() => {
$components.each(function () {
const $this = $(this);
const noteId = $this.attr(':note-id');
+ const discussionId = $this.attr(':discussion-id');
+
+ if ($this.is('comment-and-resolve-btn') && !discussionId) return;
+
const tmp = Vue.extend({
template: $this.get(0).outerHTML
});
diff --git a/app/assets/javascripts/dispatcher.js b/app/assets/javascripts/dispatcher.js
index de47485c9f2..a0ed5c23ffe 100644
--- a/app/assets/javascripts/dispatcher.js
+++ b/app/assets/javascripts/dispatcher.js
@@ -644,7 +644,7 @@ import initChangesDropdown from './init_changes_dropdown';
return Dispatcher;
})();
- $(function() {
+ $(window).on('load', function() {
new Dispatcher();
});
}).call(window);
diff --git a/app/assets/javascripts/groups_select.js b/app/assets/javascripts/groups_select.js
index b5975295329..4d629bc6326 100644
--- a/app/assets/javascripts/groups_select.js
+++ b/app/assets/javascripts/groups_select.js
@@ -111,8 +111,7 @@ window.GroupsSelect = (function() {
};
GroupsSelect.prototype.forceOverflow = function (e) {
- const itemHeight = this.dropdown.querySelector('.select2-result:first-child').clientHeight;
- this.dropdown.style.height = `${Math.floor(this.dropdown.scrollHeight - (itemHeight * 0.9))}px`;
+ this.dropdown.style.height = `${Math.floor(this.dropdown.scrollHeight)}px`;
};
GroupsSelect.PER_PAGE = 20;
diff --git a/app/assets/javascripts/lib/utils/sticky.js b/app/assets/javascripts/lib/utils/sticky.js
index 43a808b6ab3..ff2b66046b4 100644
--- a/app/assets/javascripts/lib/utils/sticky.js
+++ b/app/assets/javascripts/lib/utils/sticky.js
@@ -1,7 +1,7 @@
export const isSticky = (el, scrollY, stickyTop) => {
const top = el.offsetTop - scrollY;
- if (top === stickyTop) {
+ if (top <= stickyTop) {
el.classList.add('is-stuck');
} else {
el.classList.remove('is-stuck');
diff --git a/app/assets/javascripts/main.js b/app/assets/javascripts/main.js
index 37f531c78f4..6d7c7e3c930 100644
--- a/app/assets/javascripts/main.js
+++ b/app/assets/javascripts/main.js
@@ -132,8 +132,9 @@ import './project_select';
import './project_show';
import './project_variables';
import './projects_list';
-import './render_gfm';
+import './syntax_highlight';
import './render_math';
+import './render_gfm';
import './right_sidebar';
import './search';
import './search_autocomplete';
@@ -141,7 +142,6 @@ import './smart_interval';
import './star';
import './subscription';
import './subscription_select';
-import './syntax_highlight';
import './dispatcher';
diff --git a/app/assets/javascripts/new_sidebar.js b/app/assets/javascripts/new_sidebar.js
index b10b074f5ac..2d1ed9e4076 100644
--- a/app/assets/javascripts/new_sidebar.js
+++ b/app/assets/javascripts/new_sidebar.js
@@ -43,10 +43,12 @@ export default class NewNavSidebar {
}
toggleCollapsedSidebar(collapsed) {
- this.$sidebar.toggleClass('sidebar-icons-only', collapsed);
+ const breakpoint = bp.getBreakpointSize();
+
if (this.$sidebar.length) {
+ this.$sidebar.toggleClass('sidebar-icons-only', collapsed);
this.$page.toggleClass('page-with-new-sidebar', !collapsed);
- this.$page.toggleClass('page-with-icon-sidebar', collapsed);
+ this.$page.toggleClass('page-with-icon-sidebar', breakpoint === 'sm' ? true : collapsed);
}
NewNavSidebar.setCollapsedCookie(collapsed);
}
diff --git a/app/assets/javascripts/render_gfm.js b/app/assets/javascripts/render_gfm.js
index 2c3a9cacd38..bcdc0fd67b8 100644
--- a/app/assets/javascripts/render_gfm.js
+++ b/app/assets/javascripts/render_gfm.js
@@ -11,7 +11,5 @@
return this;
};
- $(document).on('ready load', function() {
- return $('body').renderGFM();
- });
+ $(() => $('body').renderGFM());
}).call(window);
diff --git a/app/assets/javascripts/repo/components/repo.vue b/app/assets/javascripts/repo/components/repo.vue
index 3d5e01c8ec0..d6c864cb976 100644
--- a/app/assets/javascripts/repo/components/repo.vue
+++ b/app/assets/javascripts/repo/components/repo.vue
@@ -43,15 +43,18 @@ export default {
</script>
<template>
- <div class="repository-view tree-content-holder">
- <repo-sidebar/><div v-if="isMini"
- class="panel-right"
- :class="{'edit-mode': editMode}">
- <repo-tabs/>
- <component
- :is="currentBlobView"
- class="blob-viewer-container"/>
- <repo-file-buttons/>
+ <div class="repository-view">
+ <div class="tree-content-holder" :class="{'tree-content-holder-mini' : isMini}">
+ <repo-sidebar/>
+ <div v-if="isMini"
+ class="panel-right"
+ :class="{'edit-mode': editMode}">
+ <repo-tabs/>
+ <component
+ :is="currentBlobView"
+ class="blob-viewer-container"/>
+ <repo-file-buttons/>
+ </div>
</div>
<repo-commit-section/>
<popup-dialog
diff --git a/app/assets/javascripts/sidebar/components/confidential/confidential_issue_sidebar.vue b/app/assets/javascripts/sidebar/components/confidential/confidential_issue_sidebar.vue
index cfacba09fad..8e7abdbffef 100644
--- a/app/assets/javascripts/sidebar/components/confidential/confidential_issue_sidebar.vue
+++ b/app/assets/javascripts/sidebar/components/confidential/confidential_issue_sidebar.vue
@@ -71,7 +71,7 @@ export default {
/>
<div v-if="!isConfidential" class="no-value confidential-value">
<i class="fa fa-eye is-not-confidential"></i>
- This issue is not confidential
+ Not confidential
</div>
<div v-else class="value confidential-value hide-collapsed">
<i aria-hidden="true" data-hidden="true" class="fa fa-eye-slash is-confidential"></i>
diff --git a/app/assets/stylesheets/framework/nav.scss b/app/assets/stylesheets/framework/nav.scss
index d386ac5ba9c..071f20fc457 100644
--- a/app/assets/stylesheets/framework/nav.scss
+++ b/app/assets/stylesheets/framework/nav.scss
@@ -161,6 +161,8 @@
}
.nav-controls {
+ @include new-style-dropdown;
+
display: inline-block;
float: right;
text-align: right;
diff --git a/app/assets/stylesheets/new_sidebar.scss b/app/assets/stylesheets/new_sidebar.scss
index d078c8b956b..cee5b22adb9 100644
--- a/app/assets/stylesheets/new_sidebar.scss
+++ b/app/assets/stylesheets/new_sidebar.scss
@@ -103,7 +103,10 @@ $new-sidebar-collapsed-width: 50px;
&.sidebar-icons-only {
width: $new-sidebar-collapsed-width;
- overflow-x: hidden;
+
+ .nav-sidebar-inner-scroll {
+ overflow-x: hidden;
+ }
.badge,
.project-title {
@@ -111,7 +114,11 @@ $new-sidebar-collapsed-width: 50px;
}
.nav-item-name {
- opacity: 0;
+ display: none;
+ }
+
+ .sidebar-top-level-items > li > a {
+ min-height: 44px;
}
}
diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss
index 87eaf27663f..49839a9b528 100644
--- a/app/assets/stylesheets/pages/issuable.scss
+++ b/app/assets/stylesheets/pages/issuable.scss
@@ -81,6 +81,7 @@
border: 1px solid $white-normal;
padding: 5px;
max-height: calc(100vh - 100px);
+ max-width: 100%;
}
.emoji-block {
@@ -259,7 +260,7 @@
padding-top: 10px;
}
- &:not(.issue-boards-sidebar):not([data-signed-in]) {
+ &:not(.issue-boards-sidebar):not([data-signed-in]):not([data-always-show-toggle]) {
.issuable-sidebar-header {
display: none;
}
diff --git a/app/assets/stylesheets/pages/repo.scss b/app/assets/stylesheets/pages/repo.scss
index b3527fe8cd9..1f4d4698199 100644
--- a/app/assets/stylesheets/pages/repo.scss
+++ b/app/assets/stylesheets/pages/repo.scss
@@ -47,14 +47,26 @@
margin: 20px;
}
-.repository-view.tree-content-holder {
+.repository-view {
border: 1px solid $border-color;
border-radius: $border-radius-default;
color: $almost-black;
+ .tree-content-holder {
+ display: flex;
+ max-height: 100vh;
+ min-height: 300px;
+ }
+
+ .tree-content-holder-mini {
+ height: 100vh;
+ }
+
.panel-right {
- display: inline-block;
+ display: flex;
+ flex-direction: column;
width: 80%;
+ height: 100%;
.monaco-editor.vs {
.line-numbers {
@@ -85,16 +97,17 @@
}
.blob-viewer-container {
- height: calc(100vh - 62px);
+ flex: 1;
overflow: auto;
}
#tabs {
+ flex-shrink: 0;
+ display: flex;
+ width: 100%;
padding-left: 0;
margin-bottom: 0;
- display: flex;
white-space: nowrap;
- width: 100%;
overflow-y: hidden;
overflow-x: auto;
@@ -222,14 +235,12 @@
}
#sidebar {
+ flex: 1;
+ height: 100%;
&.sidebar-mini {
- display: inline-block;
- vertical-align: top;
width: 20%;
border-right: 1px solid $white-normal;
- min-height: 475px;
- height: calc(100vh + 20px);
overflow: auto;
}
diff --git a/app/controllers/concerns/issuable_actions.rb b/app/controllers/concerns/issuable_actions.rb
index 0c3b68a7ac3..4079072a930 100644
--- a/app/controllers/concerns/issuable_actions.rb
+++ b/app/controllers/concerns/issuable_actions.rb
@@ -10,7 +10,7 @@ module IssuableActions
def destroy
issuable.destroy
destroy_method = "destroy_#{issuable.class.name.underscore}".to_sym
- TodoService.new.public_send(destroy_method, issuable, current_user)
+ TodoService.new.public_send(destroy_method, issuable, current_user) # rubocop:disable GitlabSecurity/PublicSend
name = issuable.human_class_name
flash[:notice] = "The #{name} was successfully deleted."
diff --git a/app/controllers/groups/application_controller.rb b/app/controllers/groups/application_controller.rb
index c0ac47e363d..96ce686c989 100644
--- a/app/controllers/groups/application_controller.rb
+++ b/app/controllers/groups/application_controller.rb
@@ -34,7 +34,7 @@ class Groups::ApplicationController < ApplicationController
def build_canonical_path(group)
params[:group_id] = group.to_param
-
+
url_for(params)
end
end
diff --git a/app/controllers/import/github_controller.rb b/app/controllers/import/github_controller.rb
index baa6645e5ce..ab18d86dcae 100644
--- a/app/controllers/import/github_controller.rb
+++ b/app/controllers/import/github_controller.rb
@@ -64,7 +64,7 @@ class Import::GithubController < Import::BaseController
end
def import_enabled?
- __send__("#{provider}_import_enabled?")
+ __send__("#{provider}_import_enabled?") # rubocop:disable GitlabSecurity/PublicSend
end
def new_import_url
diff --git a/app/controllers/omniauth_callbacks_controller.rb b/app/controllers/omniauth_callbacks_controller.rb
index b4213574561..7444826a5d1 100644
--- a/app/controllers/omniauth_callbacks_controller.rb
+++ b/app/controllers/omniauth_callbacks_controller.rb
@@ -142,13 +142,13 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
def oauth
@oauth ||= request.env['omniauth.auth']
end
-
+
def fail_login
error_message = @user.errors.full_messages.to_sentence
return redirect_to omniauth_error_path(oauth['provider'], error: error_message)
end
-
+
def fail_ldap_login
flash[:alert] = 'Access denied for your LDAP account.'
diff --git a/app/controllers/projects/cycle_analytics/events_controller.rb b/app/controllers/projects/cycle_analytics/events_controller.rb
index b69d46f2c41..26f3c114108 100644
--- a/app/controllers/projects/cycle_analytics/events_controller.rb
+++ b/app/controllers/projects/cycle_analytics/events_controller.rb
@@ -2,7 +2,7 @@ module Projects
module CycleAnalytics
class EventsController < Projects::ApplicationController
include CycleAnalyticsParams
-
+
before_action :authorize_read_cycle_analytics!
before_action :authorize_read_build!, only: [:test, :staging]
before_action :authorize_read_issue!, only: [:issue, :production]
@@ -11,33 +11,33 @@ module Projects
def issue
render_events(cycle_analytics[:issue].events)
end
-
+
def plan
render_events(cycle_analytics[:plan].events)
end
-
+
def code
render_events(cycle_analytics[:code].events)
end
-
+
def test
options(events_params)[:branch] = events_params[:branch_name]
-
+
render_events(cycle_analytics[:test].events)
end
-
+
def review
render_events(cycle_analytics[:review].events)
end
-
+
def staging
render_events(cycle_analytics[:staging].events)
end
-
+
def production
render_events(cycle_analytics[:production].events)
end
-
+
private
def render_events(events)
@@ -46,14 +46,14 @@ module Projects
format.json { render json: { events: events } }
end
end
-
+
def cycle_analytics
@cycle_analytics ||= ::CycleAnalytics.new(project, options(events_params))
end
-
+
def events_params
return {} unless params[:events].present?
-
+
params[:events].permit(:start_date, :branch_name)
end
end
diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb
index 4de814d0ca8..2a3b73577a5 100644
--- a/app/controllers/projects/merge_requests_controller.rb
+++ b/app/controllers/projects/merge_requests_controller.rb
@@ -218,8 +218,8 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
if can?(current_user, :read_environment, environment) && environment.has_metrics?
metrics_project_environment_deployment_path(environment.project, environment, deployment)
end
-
- metrics_monitoring_url =
+
+ metrics_monitoring_url =
if can?(current_user, :read_environment, environment)
environment_metrics_path(environment)
end
diff --git a/app/controllers/uploads_controller.rb b/app/controllers/uploads_controller.rb
index dc882b17143..16a74f82d3f 100644
--- a/app/controllers/uploads_controller.rb
+++ b/app/controllers/uploads_controller.rb
@@ -89,7 +89,7 @@ class UploadsController < ApplicationController
@uploader.retrieve_from_store!(params[:filename])
else
- @uploader = @model.send(upload_mount)
+ @uploader = @model.public_send(upload_mount) # rubocop:disable GitlabSecurity/PublicSend
redirect_to @uploader.url unless @uploader.file_storage?
end
diff --git a/app/helpers/commits_helper.rb b/app/helpers/commits_helper.rb
index 69220a1c0f6..72e26b64e60 100644
--- a/app/helpers/commits_helper.rb
+++ b/app/helpers/commits_helper.rb
@@ -128,10 +128,10 @@ module CommitsHelper
# avatar: true will prepend the avatar image
# size: size of the avatar image in px
def commit_person_link(commit, options = {})
- user = commit.send(options[:source])
+ user = commit.public_send(options[:source]) # rubocop:disable GitlabSecurity/PublicSend
- source_name = clean(commit.send "#{options[:source]}_name".to_sym)
- source_email = clean(commit.send "#{options[:source]}_email".to_sym)
+ source_name = clean(commit.public_send(:"#{options[:source]}_name")) # rubocop:disable GitlabSecurity/PublicSend
+ source_email = clean(commit.public_send(:"#{options[:source]}_email")) # rubocop:disable GitlabSecurity/PublicSend
person_name = user.try(:name) || source_name
diff --git a/app/helpers/import_helper.rb b/app/helpers/import_helper.rb
index a57b5a8fea5..a18ebfb6030 100644
--- a/app/helpers/import_helper.rb
+++ b/app/helpers/import_helper.rb
@@ -5,7 +5,7 @@ module ImportHelper
end
def provider_project_link(provider, path_with_namespace)
- url = __send__("#{provider}_project_url", path_with_namespace)
+ url = __send__("#{provider}_project_url", path_with_namespace) # rubocop:disable GitlabSecurity/PublicSend
link_to path_with_namespace, url, target: '_blank', rel: 'noopener noreferrer'
end
diff --git a/app/helpers/issuables_helper.rb b/app/helpers/issuables_helper.rb
index 70ea35fab1e..197c90c4081 100644
--- a/app/helpers/issuables_helper.rb
+++ b/app/helpers/issuables_helper.rb
@@ -174,7 +174,14 @@ module IssuablesHelper
end
def assigned_issuables_count(issuable_type)
- current_user.public_send("assigned_open_#{issuable_type}_count")
+ case issuable_type
+ when :issues
+ current_user.assigned_open_issues_count
+ when :merge_requests
+ current_user.assigned_open_merge_requests_count
+ else
+ raise ArgumentError, "invalid issuable `#{issuable_type}`"
+ end
end
def issuable_filter_params
@@ -298,10 +305,6 @@ module IssuablesHelper
cookies[:collapsed_gutter] == 'true'
end
- def base_issuable_scope(issuable)
- issuable.project.send(issuable.class.table_name).send(issuable_state_scope(issuable))
- end
-
def issuable_state_scope(issuable)
if issuable.respond_to?(:merged?) && issuable.merged?
:merged
diff --git a/app/helpers/milestones_helper.rb b/app/helpers/milestones_helper.rb
index f8860bfee99..86666022a2a 100644
--- a/app/helpers/milestones_helper.rb
+++ b/app/helpers/milestones_helper.rb
@@ -32,7 +32,18 @@ module MilestonesHelper
end
def milestone_issues_by_label_count(milestone, label, state:)
- milestone.issues.with_label(label.title).send(state).size
+ issues = milestone.issues.with_label(label.title)
+ issues =
+ case state
+ when :opened
+ issues.opened
+ when :closed
+ issues.closed
+ else
+ raise ArgumentError, "invalid milestone state `#{state}`"
+ end
+
+ issues.size
end
# Returns count of milestones for different states
diff --git a/app/helpers/pipeline_schedules_helper.rb b/app/helpers/pipeline_schedules_helper.rb
index fee1edc2a1b..6edaf78de1b 100644
--- a/app/helpers/pipeline_schedules_helper.rb
+++ b/app/helpers/pipeline_schedules_helper.rb
@@ -1,10 +1,10 @@
module PipelineSchedulesHelper
def timezone_data
- ActiveSupport::TimeZone.all.map do |timezone|
- {
- name: timezone.name,
- offset: timezone.utc_offset,
- identifier: timezone.tzinfo.identifier
+ ActiveSupport::TimeZone.all.map do |timezone|
+ {
+ name: timezone.name,
+ offset: timezone.utc_offset,
+ identifier: timezone.tzinfo.identifier
}
end
end
diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb
index 278d394bc03..bee4950e414 100644
--- a/app/helpers/projects_helper.rb
+++ b/app/helpers/projects_helper.rb
@@ -149,15 +149,16 @@ module ProjectsHelper
# Don't show option "everyone with access" if project is private
options = project_feature_options
+ level = @project.project_feature.public_send(field) # rubocop:disable GitlabSecurity/PublicSend
+
if @project.private?
- level = @project.project_feature.send(field)
disabled_option = ProjectFeature::ENABLED
highest_available_option = ProjectFeature::PRIVATE if level == disabled_option
end
options = options_for_select(
options.invert,
- selected: highest_available_option || @project.project_feature.public_send(field),
+ selected: highest_available_option || level,
disabled: disabled_option
)
@@ -488,7 +489,7 @@ module ProjectsHelper
end
def filename_path(project, filename)
- if project && blob = project.repository.send(filename)
+ if project && blob = project.repository.public_send(filename) # rubocop:disable GitlabSecurity/PublicSend
project_blob_path(
project,
tree_join(project.default_branch, blob.name)
diff --git a/app/helpers/version_check_helper.rb b/app/helpers/version_check_helper.rb
index 3b175251446..456598b4c28 100644
--- a/app/helpers/version_check_helper.rb
+++ b/app/helpers/version_check_helper.rb
@@ -2,7 +2,7 @@ module VersionCheckHelper
def version_status_badge
if Rails.env.production? && current_application_settings.version_check_enabled
image_url = VersionCheck.new.url
- image_tag image_url, class: 'js-version-status-badge', lazy: false
+ image_tag image_url, class: 'js-version-status-badge'
end
end
end
diff --git a/app/models/blob_viewer/notebook.rb b/app/models/blob_viewer/notebook.rb
index 8632b8a9885..e00b47e6c17 100644
--- a/app/models/blob_viewer/notebook.rb
+++ b/app/models/blob_viewer/notebook.rb
@@ -2,7 +2,7 @@ module BlobViewer
class Notebook < Base
include Rich
include ClientSide
-
+
self.partial_name = 'notebook'
self.extensions = %w(ipynb)
self.binary = false
diff --git a/app/models/commit.rb b/app/models/commit.rb
index 638fddc5d3d..d41c88b4e30 100644
--- a/app/models/commit.rb
+++ b/app/models/commit.rb
@@ -200,7 +200,7 @@ class Commit
end
def method_missing(m, *args, &block)
- @raw.send(m, *args, &block)
+ @raw.__send__(m, *args, &block) # rubocop:disable GitlabSecurity/PublicSend
end
def respond_to_missing?(method, include_private = false)
@@ -383,6 +383,6 @@ class Commit
end
def gpg_commit
- @gpg_commit ||= Gitlab::Gpg::Commit.new(self)
+ @gpg_commit ||= Gitlab::Gpg::Commit.for_commit(self)
end
end
diff --git a/app/models/concerns/cache_markdown_field.rb b/app/models/concerns/cache_markdown_field.rb
index 48547a938fc..193e459977a 100644
--- a/app/models/concerns/cache_markdown_field.rb
+++ b/app/models/concerns/cache_markdown_field.rb
@@ -78,7 +78,7 @@ module CacheMarkdownField
def cached_html_up_to_date?(markdown_field)
html_field = cached_markdown_fields.html_field(markdown_field)
- cached = cached_html_for(markdown_field).present? && __send__(markdown_field).present?
+ cached = cached_html_for(markdown_field).present? && __send__(markdown_field).present? # rubocop:disable GitlabSecurity/PublicSend
return false unless cached
markdown_changed = attribute_changed?(markdown_field) || false
@@ -93,14 +93,14 @@ module CacheMarkdownField
end
def attribute_invalidated?(attr)
- __send__("#{attr}_invalidated?")
+ __send__("#{attr}_invalidated?") # rubocop:disable GitlabSecurity/PublicSend
end
def cached_html_for(markdown_field)
raise ArgumentError.new("Unknown field: #{field}") unless
cached_markdown_fields.markdown_fields.include?(markdown_field)
- __send__(cached_markdown_fields.html_field(markdown_field))
+ __send__(cached_markdown_fields.html_field(markdown_field)) # rubocop:disable GitlabSecurity/PublicSend
end
included do
diff --git a/app/models/concerns/internal_id.rb b/app/models/concerns/internal_id.rb
index 67a0adfcd56..a3d0ac8d862 100644
--- a/app/models/concerns/internal_id.rb
+++ b/app/models/concerns/internal_id.rb
@@ -9,7 +9,7 @@ module InternalId
def set_iid
if iid.blank?
parent = project || group
- records = parent.send(self.class.name.tableize)
+ records = parent.public_send(self.class.name.tableize) # rubocop:disable GitlabSecurity/PublicSend
records = records.with_deleted if self.paranoid?
max_iid = records.maximum(:iid)
diff --git a/app/models/concerns/mentionable.rb b/app/models/concerns/mentionable.rb
index c034bf9cbc0..1db6b2d2fa2 100644
--- a/app/models/concerns/mentionable.rb
+++ b/app/models/concerns/mentionable.rb
@@ -56,7 +56,7 @@ module Mentionable
end
self.class.mentionable_attrs.each do |attr, options|
- text = __send__(attr)
+ text = __send__(attr) # rubocop:disable GitlabSecurity/PublicSend
options = options.merge(
cache_key: [self, attr],
author: author,
@@ -100,7 +100,7 @@ module Mentionable
end
self.class.mentionable_attrs.any? do |attr, _|
- __send__(attr) =~ reference_pattern
+ __send__(attr) =~ reference_pattern # rubocop:disable GitlabSecurity/PublicSend
end
end
diff --git a/app/models/concerns/participable.rb b/app/models/concerns/participable.rb
index 4865c0a14b1..ce69fd34ac5 100644
--- a/app/models/concerns/participable.rb
+++ b/app/models/concerns/participable.rb
@@ -82,7 +82,7 @@ module Participable
if attr.respond_to?(:call)
source.instance_exec(current_user, ext, &attr)
else
- process << source.__send__(attr)
+ process << source.__send__(attr) # rubocop:disable GitlabSecurity/PublicSend
end
end
when Enumerable, ActiveRecord::Relation
diff --git a/app/models/concerns/project_features_compatibility.rb b/app/models/concerns/project_features_compatibility.rb
index 60734bc6660..cb59b4da3d7 100644
--- a/app/models/concerns/project_features_compatibility.rb
+++ b/app/models/concerns/project_features_compatibility.rb
@@ -32,6 +32,6 @@ module ProjectFeaturesCompatibility
build_project_feature unless project_feature
access_level = Gitlab::Utils.to_boolean(value) ? ProjectFeature::ENABLED : ProjectFeature::DISABLED
- project_feature.send(:write_attribute, field, access_level)
+ project_feature.__send__(:write_attribute, field, access_level) # rubocop:disable GitlabSecurity/PublicSend
end
end
diff --git a/app/models/concerns/referable.rb b/app/models/concerns/referable.rb
index 10f4be72016..78ac4f324e7 100644
--- a/app/models/concerns/referable.rb
+++ b/app/models/concerns/referable.rb
@@ -25,6 +25,11 @@ module Referable
to_reference(from_project)
end
+ included do
+ alias_method :non_referable_inspect, :inspect
+ alias_method :inspect, :referable_inspect
+ end
+
def referable_inspect
if respond_to?(:id)
"#<#{self.class.name} id:#{id} #{to_reference(full: true)}>"
@@ -33,10 +38,6 @@ module Referable
end
end
- def inspect
- referable_inspect
- end
-
module ClassMethods
# The character that prefixes the actual reference identifier
#
diff --git a/app/models/deploy_keys_project.rb b/app/models/deploy_keys_project.rb
index ae8486bd9ac..b37b9bfbdac 100644
--- a/app/models/deploy_keys_project.rb
+++ b/app/models/deploy_keys_project.rb
@@ -12,7 +12,7 @@ class DeployKeysProject < ActiveRecord::Base
def destroy_orphaned_deploy_key
return unless self.deploy_key.destroyed_when_orphaned? && self.deploy_key.orphaned?
-
+
self.deploy_key.destroy
end
end
diff --git a/app/models/gpg_signature.rb b/app/models/gpg_signature.rb
index 1ac0e123ff1..50fb35c77ec 100644
--- a/app/models/gpg_signature.rb
+++ b/app/models/gpg_signature.rb
@@ -18,4 +18,8 @@ class GpgSignature < ActiveRecord::Base
def commit
project.commit(commit_sha)
end
+
+ def gpg_commit
+ Gitlab::Gpg::Commit.new(project, commit_sha)
+ end
end
diff --git a/app/models/network/commit.rb b/app/models/network/commit.rb
index 8417f200e36..9357e55b419 100644
--- a/app/models/network/commit.rb
+++ b/app/models/network/commit.rb
@@ -12,7 +12,7 @@ module Network
end
def method_missing(m, *args, &block)
- @commit.send(m, *args, &block)
+ @commit.__send__(m, *args, &block) # rubocop:disable GitlabSecurity/PublicSend
end
def space
diff --git a/app/models/project.rb b/app/models/project.rb
index 0d15d644413..be248bc99e1 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -60,7 +60,7 @@ class Project < ActiveRecord::Base
end
before_destroy :remove_private_deploy_keys
- after_destroy :remove_pages
+ after_destroy -> { run_after_commit { remove_pages } }
# update visibility_level of forks
after_update :update_forks_visibility_level
@@ -369,7 +369,10 @@ class Project < ActiveRecord::Base
state :failed
after_transition [:none, :finished, :failed] => :scheduled do |project, _|
- project.run_after_commit { add_import_job }
+ project.run_after_commit do
+ job_id = add_import_job
+ update(import_jid: job_id) if job_id
+ end
end
after_transition started: :finished do |project, _|
@@ -524,17 +527,26 @@ class Project < ActiveRecord::Base
def add_import_job
job_id =
if forked?
- RepositoryForkWorker.perform_async(id, forked_from_project.repository_storage_path,
- forked_from_project.full_path,
- self.namespace.full_path)
+ RepositoryForkWorker.perform_async(id,
+ forked_from_project.repository_storage_path,
+ forked_from_project.full_path,
+ self.namespace.full_path)
else
RepositoryImportWorker.perform_async(self.id)
end
+ log_import_activity(job_id)
+
+ job_id
+ end
+
+ def log_import_activity(job_id, type: :import)
+ job_type = type.to_s.capitalize
+
if job_id
- Rails.logger.info "Import job started for #{full_path} with job ID #{job_id}"
+ Rails.logger.info("#{job_type} job scheduled for #{full_path} with job ID #{job_id}.")
else
- Rails.logger.error "Import job failed to start for #{full_path}"
+ Rails.logger.error("#{job_type} job failed to create for #{full_path}.")
end
end
@@ -543,6 +555,7 @@ class Project < ActiveRecord::Base
ProjectCacheWorker.perform_async(self.id)
end
+ update(import_error: nil)
remove_import_data
end
@@ -920,14 +933,14 @@ class Project < ActiveRecord::Base
end
def execute_hooks(data, hooks_scope = :push_hooks)
- hooks.send(hooks_scope).each do |hook|
+ hooks.public_send(hooks_scope).each do |hook| # rubocop:disable GitlabSecurity/PublicSend
hook.async_execute(data, hooks_scope.to_s)
end
end
def execute_services(data, hooks_scope = :push_hooks)
# Call only service hooks that are active for this scope
- services.send(hooks_scope).each do |service|
+ services.public_send(hooks_scope).each do |service| # rubocop:disable GitlabSecurity/PublicSend
service.async_execute(data)
end
end
@@ -1224,6 +1237,9 @@ class Project < ActiveRecord::Base
# TODO: what to do here when not using Legacy Storage? Do we still need to rename and delay removal?
def remove_pages
+ # Projects with a missing namespace cannot have their pages removed
+ return unless namespace
+
::Projects::UpdatePagesConfigurationService.new(self).execute
# 1. We rename pages to temporary directory
diff --git a/app/models/project_services/chat_notification_service.rb b/app/models/project_services/chat_notification_service.rb
index 6d1a321f651..7b15a5dd04d 100644
--- a/app/models/project_services/chat_notification_service.rb
+++ b/app/models/project_services/chat_notification_service.rb
@@ -115,7 +115,7 @@ class ChatNotificationService < Service
def get_channel_field(event)
field_name = event_channel_name(event)
- self.public_send(field_name)
+ self.public_send(field_name) # rubocop:disable GitlabSecurity/PublicSend
end
def build_event_channels
diff --git a/app/models/project_services/hipchat_service.rb b/app/models/project_services/hipchat_service.rb
index e3906943ecd..f422e0ea036 100644
--- a/app/models/project_services/hipchat_service.rb
+++ b/app/models/project_services/hipchat_service.rb
@@ -53,7 +53,7 @@ class HipchatService < Service
return unless supported_events.include?(data[:object_kind])
message = create_message(data)
return unless message.present?
- gate[room].send('GitLab', message, message_options(data))
+ gate[room].send('GitLab', message, message_options(data)) # rubocop:disable GitlabSecurity/PublicSend
end
def test(data)
diff --git a/app/models/protectable_dropdown.rb b/app/models/protectable_dropdown.rb
index 122fbce257d..c96edc5a259 100644
--- a/app/models/protectable_dropdown.rb
+++ b/app/models/protectable_dropdown.rb
@@ -1,5 +1,9 @@
class ProtectableDropdown
+ REF_TYPES = %i[branches tags].freeze
+
def initialize(project, ref_type)
+ raise ArgumentError, "invalid ref type `#{ref_type}`" unless ref_type.in?(REF_TYPES)
+
@project = project
@ref_type = ref_type
end
@@ -16,7 +20,7 @@ class ProtectableDropdown
private
def refs
- @project.repository.public_send(@ref_type)
+ @project.repository.public_send(@ref_type) # rubocop:disable GitlabSecurity/PublicSend
end
def ref_names
@@ -24,7 +28,7 @@ class ProtectableDropdown
end
def protections
- @project.public_send("protected_#{@ref_type}")
+ @project.public_send("protected_#{@ref_type}") # rubocop:disable GitlabSecurity/PublicSend
end
def non_wildcard_protected_ref_names
diff --git a/app/models/redirect_route.rb b/app/models/redirect_route.rb
index 090fbd61e6f..31de204d824 100644
--- a/app/models/redirect_route.rb
+++ b/app/models/redirect_route.rb
@@ -14,7 +14,7 @@ class RedirectRoute < ActiveRecord::Base
else
'redirect_routes.path = ? OR redirect_routes.path LIKE ?'
end
-
+
where(wheres, path, "#{sanitize_sql_like(path)}/%")
end
end
diff --git a/app/models/repository.rb b/app/models/repository.rb
index a761302b06b..c1e4fcf94a4 100644
--- a/app/models/repository.rb
+++ b/app/models/repository.rb
@@ -48,7 +48,9 @@ class Repository
alias_method(original, name)
define_method(name) do
- cache_method_output(name, fallback: fallback, memoize_only: memoize_only) { __send__(original) }
+ cache_method_output(name, fallback: fallback, memoize_only: memoize_only) do
+ __send__(original) # rubocop:disable GitlabSecurity/PublicSend
+ end
end
end
@@ -443,9 +445,9 @@ class Repository
def method_missing(m, *args, &block)
if m == :lookup && !block_given?
lookup_cache[m] ||= {}
- lookup_cache[m][args.join(":")] ||= raw_repository.send(m, *args, &block)
+ lookup_cache[m][args.join(":")] ||= raw_repository.__send__(m, *args, &block) # rubocop:disable GitlabSecurity/PublicSend
else
- raw_repository.send(m, *args, &block)
+ raw_repository.__send__(m, *args, &block) # rubocop:disable GitlabSecurity/PublicSend
end
end
@@ -776,7 +778,7 @@ class Repository
end
actions.each do |options|
- index.public_send(options.delete(:action), options)
+ index.public_send(options.delete(:action), options) # rubocop:disable GitlabSecurity/PublicSend
end
options = {
diff --git a/app/models/user.rb b/app/models/user.rb
index 2b25736bb26..02c3ab6654b 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -47,11 +47,6 @@ class User < ActiveRecord::Base
devise :lockable, :recoverable, :rememberable, :trackable,
:validatable, :omniauthable, :confirmable, :registerable
- # devise overrides #inspect, so we manually use the Referable one
- def inspect
- referable_inspect
- end
-
# Override Devise::Models::Trackable#update_tracked_fields!
# to limit database writes to at most once every hour
def update_tracked_fields!(request)
@@ -1070,7 +1065,7 @@ class User < ActiveRecord::Base
# Added according to https://github.com/plataformatec/devise/blob/7df57d5081f9884849ca15e4fde179ef164a575f/README.md#activejob-integration
def send_devise_notification(notification, *args)
return true unless can?(:receive_notifications)
- devise_mailer.send(notification, self, *args).deliver_later
+ devise_mailer.__send__(notification, self, *args).deliver_later # rubocop:disable GitlabSecurity/PublicSend
end
# This works around a bug in Devise 4.2.0 that erroneously causes a user to
diff --git a/app/serializers/project_entity.rb b/app/serializers/project_entity.rb
index dc283ba3e7a..b3e5fd21e97 100644
--- a/app/serializers/project_entity.rb
+++ b/app/serializers/project_entity.rb
@@ -1,6 +1,6 @@
class ProjectEntity < Grape::Entity
include RequestAwareEntity
-
+
expose :id
expose :name
diff --git a/app/services/akismet_service.rb b/app/services/akismet_service.rb
index 8e11a2a36a7..59153cbbc0a 100644
--- a/app/services/akismet_service.rb
+++ b/app/services/akismet_service.rb
@@ -58,7 +58,7 @@ class AkismetService
}
begin
- akismet_client.public_send(type, options[:ip_address], options[:user_agent], params)
+ akismet_client.public_send(type, options[:ip_address], options[:user_agent], params) # rubocop:disable GitlabSecurity/PublicSend
true
rescue => e
Rails.logger.error("Unable to connect to Akismet: #{e}, skipping!")
diff --git a/app/services/ci/retry_build_service.rb b/app/services/ci/retry_build_service.rb
index 6372e5755db..ea3b8d66ed9 100644
--- a/app/services/ci/retry_build_service.rb
+++ b/app/services/ci/retry_build_service.rb
@@ -23,7 +23,7 @@ module Ci
end
attributes = CLONE_ACCESSORS.map do |attribute|
- [attribute, build.send(attribute)]
+ [attribute, build.public_send(attribute)] # rubocop:disable GitlabSecurity/PublicSend
end
attributes.push([:user, current_user])
diff --git a/app/services/commits/change_service.rb b/app/services/commits/change_service.rb
index a48d6a976f0..85c2fcf9ea6 100644
--- a/app/services/commits/change_service.rb
+++ b/app/services/commits/change_service.rb
@@ -11,6 +11,7 @@ module Commits
def commit_change(action)
raise NotImplementedError unless repository.respond_to?(action)
+ # rubocop:disable GitlabSecurity/PublicSend
repository.public_send(
action,
current_user,
diff --git a/app/services/git_push_service.rb b/app/services/git_push_service.rb
index ada2b64a3a6..e81a56672e2 100644
--- a/app/services/git_push_service.rb
+++ b/app/services/git_push_service.rb
@@ -90,8 +90,19 @@ class GitPushService < BaseService
end
def update_signatures
- @push_commits.each do |commit|
- CreateGpgSignatureWorker.perform_async(commit.sha, @project.id)
+ commit_shas = @push_commits.last(PROCESS_COMMIT_LIMIT).map(&:sha)
+
+ return if commit_shas.empty?
+
+ shas_with_cached_signatures = GpgSignature.where(commit_sha: commit_shas).pluck(:commit_sha)
+ commit_shas -= shas_with_cached_signatures
+
+ return if commit_shas.empty?
+
+ commit_shas = Gitlab::Git::Commit.shas_with_signatures(project.repository, commit_shas)
+
+ commit_shas.each do |sha|
+ CreateGpgSignatureWorker.perform_async(sha, project.id)
end
end
diff --git a/app/services/issuable_base_service.rb b/app/services/issuable_base_service.rb
index b84a6fd2b7d..4a4f2b91182 100644
--- a/app/services/issuable_base_service.rb
+++ b/app/services/issuable_base_service.rb
@@ -338,7 +338,7 @@ class IssuableBaseService < BaseService
def invalidate_cache_counts(issuable, users: [], skip_project_cache: false)
users.each do |user|
- user.public_send("invalidate_#{issuable.model_name.singular}_cache_counts")
+ user.public_send("invalidate_#{issuable.model_name.singular}_cache_counts") # rubocop:disable GitlabSecurity/PublicSend
end
unless skip_project_cache
diff --git a/app/services/members/destroy_service.rb b/app/services/members/destroy_service.rb
index 2e089149ca8..46c505baf8b 100644
--- a/app/services/members/destroy_service.rb
+++ b/app/services/members/destroy_service.rb
@@ -31,7 +31,7 @@ module Members
source.members.find_by(condition) ||
source.requesters.find_by!(condition)
else
- source.public_send(scope).find_by!(condition)
+ source.public_send(scope).find_by!(condition) # rubocop:disable GitlabSecurity/PublicSend
end
end
diff --git a/app/services/notification_service.rb b/app/services/notification_service.rb
index 4267879b03d..e2a80db06a6 100644
--- a/app/services/notification_service.rb
+++ b/app/services/notification_service.rb
@@ -1,3 +1,5 @@
+# rubocop:disable GitlabSecurity/PublicSend
+
# NotificationService class
#
# Used for notifying users with emails about different events
diff --git a/app/services/system_hooks_service.rb b/app/services/system_hooks_service.rb
index cbcd4478af6..a1c2f8d0180 100644
--- a/app/services/system_hooks_service.rb
+++ b/app/services/system_hooks_service.rb
@@ -4,7 +4,7 @@ class SystemHooksService
end
def execute_hooks(data, hooks_scope = :all)
- SystemHook.public_send(hooks_scope).find_each do |hook|
+ SystemHook.public_send(hooks_scope).find_each do |hook| # rubocop:disable GitlabSecurity/PublicSend
hook.async_execute(data, 'system_hooks')
end
end
diff --git a/app/services/test_hooks/base_service.rb b/app/services/test_hooks/base_service.rb
index 74ba814afff..4abd2c44b2f 100644
--- a/app/services/test_hooks/base_service.rb
+++ b/app/services/test_hooks/base_service.rb
@@ -18,7 +18,7 @@ module TestHooks
end
error_message = catch(:validation_error) do
- sample_data = self.__send__(trigger_data_method)
+ sample_data = self.__send__(trigger_data_method) # rubocop:disable GitlabSecurity/PublicSend
return hook.execute(sample_data, trigger)
end
diff --git a/app/views/help/ui.html.haml b/app/views/help/ui.html.haml
index f18c3a74120..445f0dffbcc 100644
--- a/app/views/help/ui.html.haml
+++ b/app/views/help/ui.html.haml
@@ -189,7 +189,7 @@
= icon('chevron-down')
%ul.dropdown-menu
%li
- %a Sort by date
+ = link_to 'Sort by date', '#'
= link_to 'New issue', '#', class: 'btn btn-new btn-inverted'
diff --git a/app/views/profiles/accounts/show.html.haml b/app/views/profiles/accounts/show.html.haml
index ed079ed7dfb..5d778d67ae7 100644
--- a/app/views/profiles/accounts/show.html.haml
+++ b/app/views/profiles/accounts/show.html.haml
@@ -92,25 +92,24 @@
Update username
%hr
-- if signup_enabled?
- .row.prepend-top-default
- .col-lg-4.profile-settings-sidebar
- %h4.prepend-top-0.danger-title
- Remove account
- .col-lg-8
- - if @user.can_be_removed? && can?(current_user, :destroy_user, @user)
+.row.prepend-top-default
+ .col-lg-4.profile-settings-sidebar
+ %h4.prepend-top-0.danger-title
+ Remove account
+ .col-lg-8
+ - if @user.can_be_removed? && can?(current_user, :destroy_user, @user)
+ %p
+ Deleting an account has the following effects:
+ = render 'users/deletion_guidance', user: current_user
+ = link_to 'Delete account', user_registration_path, data: { confirm: "REMOVE #{current_user.name}? Are you sure?" }, method: :delete, class: "btn btn-remove"
+ - else
+ - if @user.solo_owned_groups.present?
%p
- Deleting an account has the following effects:
- = render 'users/deletion_guidance', user: current_user
- = link_to 'Delete account', user_registration_path, data: { confirm: "REMOVE #{current_user.name}? Are you sure?" }, method: :delete, class: "btn btn-remove"
+ Your account is currently an owner in these groups:
+ %strong= @user.solo_owned_groups.map(&:name).join(', ')
+ %p
+ You must transfer ownership or delete these groups before you can delete your account.
- else
- - if @user.solo_owned_groups.present?
- %p
- Your account is currently an owner in these groups:
- %strong= @user.solo_owned_groups.map(&:name).join(', ')
- %p
- You must transfer ownership or delete these groups before you can delete your account.
- - else
- %p
- You don't have access to delete this user.
+ %p
+ You don't have access to delete this user.
.append-bottom-default
diff --git a/app/views/projects/_project_templates.html.haml b/app/views/projects/_project_templates.html.haml
index 21baf35f2ac..97cf13df070 100644
--- a/app/views/projects/_project_templates.html.haml
+++ b/app/views/projects/_project_templates.html.haml
@@ -5,6 +5,6 @@
Blank
- Gitlab::ProjectTemplate.all.each do |template|
.btn
- %input{ type: "radio", autocomplete: "off", name: "project_templates", id: template.name }
+ %input{ type: "radio", autocomplete: "off", name: "project[template_name]", id: template.name, value: template.name }
= custom_icon(template.logo)
= template.title
diff --git a/app/views/projects/merge_requests/_discussion.html.haml b/app/views/projects/merge_requests/_discussion.html.haml
index b787edb3427..3303aa72604 100644
--- a/app/views/projects/merge_requests/_discussion.html.haml
+++ b/app/views/projects/merge_requests/_discussion.html.haml
@@ -4,8 +4,8 @@
= link_to 'Close merge request', merge_request_path(@merge_request, merge_request: { state_event: :close }), method: :put, class: "btn btn-nr btn-comment btn-close close-mr-link js-note-target-close", title: "Close merge request", data: { original_text: "Close merge request", alternative_text: "Comment & close merge request"}
- if @merge_request.reopenable?
= link_to 'Reopen merge request', merge_request_path(@merge_request, merge_request: { state_event: :reopen }), method: :put, class: "btn btn-nr btn-comment btn-reopen reopen-mr-link js-note-target-close js-note-target-reopen", title: "Reopen merge request", data: { original_text: "Reopen merge request", alternative_text: "Comment & reopen merge request"}
- %comment-and-resolve-btn{ "inline-template" => true, ":discussion-id" => "" }
- %button.btn.btn-nr.btn-default.append-right-10.js-comment-resolve-button{ "v-if" => "showButton", type: "submit", data: { project_path: "#{project_path(@merge_request.project)}" } }
- {{ buttonText }}
+ %comment-and-resolve-btn{ "inline-template" => true }
+ %button.btn.btn-nr.btn-default.append-right-10.js-comment-resolve-button{ "v-if" => "showButton", type: "submit", data: { project_path: "#{project_path(@merge_request.project)}" } }
+ {{ buttonText }}
#notes= render "shared/notes/notes_with_form", :autocomplete => true
diff --git a/app/views/projects/new.html.haml b/app/views/projects/new.html.haml
index e3bbebbcf4c..647e0a772b1 100644
--- a/app/views/projects/new.html.haml
+++ b/app/views/projects/new.html.haml
@@ -25,7 +25,7 @@
.form-group
= f.label :template_project, class: 'label-light' do
Create from template
- = link_to icon('question-circle'), help_page_path("public_access/public_access"), aria: { label: "What’s included in a template?" }, title: "What’s included in a template?", class: 'has-tooltip', data: { placement: 'top'}
+ = link_to icon('question-circle'), help_page_path("gitlab-basics/create-project"), target: '_blank', aria: { label: "What’s included in a template?" }, title: "What’s included in a template?", class: 'has-tooltip', data: { placement: 'top'}
%div
= render 'project_templates', f: f
.second-column
diff --git a/app/views/shared/_import_form.html.haml b/app/views/shared/_import_form.html.haml
index 873179339dc..233d8c95eda 100644
--- a/app/views/shared/_import_form.html.haml
+++ b/app/views/shared/_import_form.html.haml
@@ -13,4 +13,4 @@
%li
The import will time out after 15 minutes. For repositories that take longer, use a clone/push combination.
%li
- To migrate an SVN repository, check out #{link_to "this document", help_page_path('workflow/importing/migrating_from_svn')}.
+ To migrate an SVN repository, check out #{link_to "this document", help_page_path('user/project/import/svn')}.
diff --git a/app/views/shared/milestones/_milestone.html.haml b/app/views/shared/milestones/_milestone.html.haml
index 6f6a036b13f..6a85f7d0564 100644
--- a/app/views/shared/milestones/_milestone.html.haml
+++ b/app/views/shared/milestones/_milestone.html.haml
@@ -32,7 +32,7 @@
.col-sm-6.milestone-actions
- if can?(current_user, :admin_milestones, @group)
- if milestone.is_group_milestone?
- = link_to edit_group_milestone_path(@group, milestone.id), class: "btn btn-xs btn-grouped" do
+ = link_to edit_group_milestone_path(@group, milestone), class: "btn btn-xs btn-grouped" do
Edit
\
- if milestone.closed?
diff --git a/app/views/shared/milestones/_sidebar.html.haml b/app/views/shared/milestones/_sidebar.html.haml
index 66ac8196f2f..40379f48393 100644
--- a/app/views/shared/milestones/_sidebar.html.haml
+++ b/app/views/shared/milestones/_sidebar.html.haml
@@ -1,7 +1,7 @@
- affix_offset = local_assigns.fetch(:affix_offset, "50")
- project = local_assigns[:project]
-%aside.right-sidebar.js-right-sidebar{ data: { "offset-top" => affix_offset, "spy" => "affix" }, class: sidebar_gutter_collapsed_class, 'aria-live' => 'polite' }
+%aside.right-sidebar.js-right-sidebar{ data: { "offset-top" => affix_offset, "spy" => "affix", "always-show-toggle" => true }, class: sidebar_gutter_collapsed_class, 'aria-live' => 'polite' }
.issuable-sidebar.milestone-sidebar
.block.milestone-progress.issuable-sidebar-header
%a.gutter-toggle.pull-right.js-sidebar-toggle{ role: "button", href: "#", "aria-label" => "Toggle sidebar" }
diff --git a/app/views/shared/milestones/_top.html.haml b/app/views/shared/milestones/_top.html.haml
index b93837e3087..3014300fbe7 100644
--- a/app/views/shared/milestones/_top.html.haml
+++ b/app/views/shared/milestones/_top.html.haml
@@ -23,7 +23,7 @@
.pull-right
- if can?(current_user, :admin_milestones, group)
- if milestone.is_group_milestone?
- = link_to edit_group_milestone_path(group, milestone.iid), class: "btn btn btn-grouped" do
+ = link_to edit_group_milestone_path(group, milestone), class: "btn btn btn-grouped" do
Edit
- if milestone.active?
= link_to 'Close Milestone', group_milestone_route(milestone, {state_event: :close }), method: :put, class: "btn btn-grouped btn-close"
diff --git a/app/workers/create_gpg_signature_worker.rb b/app/workers/create_gpg_signature_worker.rb
index 4f47717ff69..f34dff2d656 100644
--- a/app/workers/create_gpg_signature_worker.rb
+++ b/app/workers/create_gpg_signature_worker.rb
@@ -4,13 +4,9 @@ class CreateGpgSignatureWorker
def perform(commit_sha, project_id)
project = Project.find_by(id: project_id)
-
return unless project
- commit = project.commit(commit_sha)
-
- return unless commit
-
- commit.signature
+ # This calculates and caches the signature in the database
+ Gitlab::Gpg::Commit.new(project, commit_sha).signature
end
end
diff --git a/app/workers/gitlab_shell_worker.rb b/app/workers/gitlab_shell_worker.rb
index 964287a1793..0ec871e00e1 100644
--- a/app/workers/gitlab_shell_worker.rb
+++ b/app/workers/gitlab_shell_worker.rb
@@ -4,6 +4,6 @@ class GitlabShellWorker
include DedicatedSidekiqQueue
def perform(action, *arg)
- gitlab_shell.send(action, *arg)
+ gitlab_shell.__send__(action, *arg) # rubocop:disable GitlabSecurity/PublicSend
end
end
diff --git a/app/workers/namespaceless_project_destroy_worker.rb b/app/workers/namespaceless_project_destroy_worker.rb
index bfae0c77700..a9073742ff7 100644
--- a/app/workers/namespaceless_project_destroy_worker.rb
+++ b/app/workers/namespaceless_project_destroy_worker.rb
@@ -24,10 +24,6 @@ class NamespacelessProjectDestroyWorker
unlink_fork(project) if project.forked?
- # Override Project#remove_pages for this instance so it doesn't do anything
- def project.remove_pages
- end
-
project.destroy!
end
diff --git a/app/workers/repository_fork_worker.rb b/app/workers/repository_fork_worker.rb
index a338523dc6b..cde5b45ad41 100644
--- a/app/workers/repository_fork_worker.rb
+++ b/app/workers/repository_fork_worker.rb
@@ -5,14 +5,17 @@ class RepositoryForkWorker
include Gitlab::ShellAdapter
include DedicatedSidekiqQueue
+ sidekiq_options status_expiration: StuckImportJobsWorker::IMPORT_JOBS_EXPIRATION
+
def perform(project_id, forked_from_repository_storage_path, source_path, target_path)
+ project = Project.find(project_id)
+
+ return unless start_fork(project)
+
Gitlab::Metrics.add_event(:fork_repository,
source_path: source_path,
target_path: target_path)
- project = Project.find(project_id)
- project.import_start
-
result = gitlab_shell.fork_repository(forked_from_repository_storage_path, source_path,
project.repository_storage_path, target_path)
raise ForkError, "Unable to fork project #{project_id} for repository #{source_path} -> #{target_path}" unless result
@@ -33,6 +36,13 @@ class RepositoryForkWorker
private
+ def start_fork(project)
+ return true if project.import_start
+
+ Rails.logger.info("Project #{project.full_path} was in inconsistent state (#{project.import_status}) while forking.")
+ false
+ end
+
def fail_fork(project, message)
Rails.logger.error(message)
project.mark_import_as_failed(message)
diff --git a/app/workers/repository_import_worker.rb b/app/workers/repository_import_worker.rb
index 6be541abd3e..2c2d1e8b91f 100644
--- a/app/workers/repository_import_worker.rb
+++ b/app/workers/repository_import_worker.rb
@@ -4,23 +4,18 @@ class RepositoryImportWorker
include Sidekiq::Worker
include DedicatedSidekiqQueue
- sidekiq_options status_expiration: StuckImportJobsWorker::IMPORT_EXPIRATION
-
- attr_accessor :project, :current_user
+ sidekiq_options status_expiration: StuckImportJobsWorker::IMPORT_JOBS_EXPIRATION
def perform(project_id)
- @project = Project.find(project_id)
- @current_user = @project.creator
+ project = Project.find(project_id)
- project.import_start
+ return unless start_import(project)
Gitlab::Metrics.add_event(:import_repository,
- import_url: @project.import_url,
- path: @project.full_path)
-
- project.update_columns(import_jid: self.jid, import_error: nil)
+ import_url: project.import_url,
+ path: project.full_path)
- result = Projects::ImportService.new(project, current_user).execute
+ result = Projects::ImportService.new(project, project.creator).execute
raise ImportError, result[:message] if result[:status] == :error
project.repository.after_import
@@ -37,6 +32,13 @@ class RepositoryImportWorker
private
+ def start_import(project)
+ return true if project.import_start
+
+ Rails.logger.info("Project #{project.full_path} was in inconsistent state (#{project.import_status}) while importing.")
+ false
+ end
+
def fail_import(project, message)
project.mark_import_as_failed(message)
end
diff --git a/app/workers/stuck_import_jobs_worker.rb b/app/workers/stuck_import_jobs_worker.rb
index bfc5e667bb6..f850e459cd9 100644
--- a/app/workers/stuck_import_jobs_worker.rb
+++ b/app/workers/stuck_import_jobs_worker.rb
@@ -2,36 +2,60 @@ class StuckImportJobsWorker
include Sidekiq::Worker
include CronjobQueue
- IMPORT_EXPIRATION = 15.hours.to_i
+ IMPORT_JOBS_EXPIRATION = 15.hours.to_i
def perform
- stuck_projects.find_in_batches(batch_size: 500) do |group|
+ projects_without_jid_count = mark_projects_without_jid_as_failed!
+ projects_with_jid_count = mark_projects_with_jid_as_failed!
+
+ Gitlab::Metrics.add_event(:stuck_import_jobs,
+ projects_without_jid_count: projects_without_jid_count,
+ projects_with_jid_count: projects_with_jid_count)
+ end
+
+ private
+
+ def mark_projects_without_jid_as_failed!
+ started_projects_without_jid.each do |project|
+ project.mark_import_as_failed(error_message)
+ end.count
+ end
+
+ def mark_projects_with_jid_as_failed!
+ completed_jids_count = 0
+
+ started_projects_with_jid.find_in_batches(batch_size: 500) do |group|
jids = group.map(&:import_jid)
# Find the jobs that aren't currently running or that exceeded the threshold.
- completed_jids = Gitlab::SidekiqStatus.completed_jids(jids)
+ completed_jids = Gitlab::SidekiqStatus.completed_jids(jids).to_set
if completed_jids.any?
- completed_ids = group.select { |project| completed_jids.include?(project.import_jid) }.map(&:id)
+ completed_jids_count += completed_jids.count
+ group.each do |project|
+ project.mark_import_as_failed(error_message) if completed_jids.include?(project.import_jid)
+ end
- fail_batch!(completed_jids, completed_ids)
+ Rails.logger.info("Marked stuck import jobs as failed. JIDs: #{completed_jids.to_a.join(', ')}")
end
end
- end
- private
+ completed_jids_count
+ end
- def stuck_projects
- Project.select('id, import_jid').with_import_status(:started).where.not(import_jid: nil)
+ def started_projects
+ Project.with_import_status(:started)
end
- def fail_batch!(completed_jids, completed_ids)
- Project.where(id: completed_ids).update_all(import_status: 'failed', import_error: error_message)
+ def started_projects_with_jid
+ started_projects.where.not(import_jid: nil)
+ end
- Rails.logger.info("Marked stuck import jobs as failed. JIDs: #{completed_jids.join(', ')}")
+ def started_projects_without_jid
+ started_projects.where(import_jid: nil)
end
def error_message
- "Import timed out. Import took longer than #{IMPORT_EXPIRATION} seconds"
+ "Import timed out. Import took longer than #{IMPORT_JOBS_EXPIRATION} seconds"
end
end
diff --git a/changelogs/unreleased/34049-public-commits-should-not-require-authentication.yml b/changelogs/unreleased/34049-public-commits-should-not-require-authentication.yml
new file mode 100644
index 00000000000..278ef2a8acb
--- /dev/null
+++ b/changelogs/unreleased/34049-public-commits-should-not-require-authentication.yml
@@ -0,0 +1,4 @@
+---
+title: Added tests for commits API unauthenticated user and public/private project
+merge_request: 13287
+author: Jacopo Beschi @jacopo-beschi
diff --git a/changelogs/unreleased/35072-fix-pages-delete.yml b/changelogs/unreleased/35072-fix-pages-delete.yml
new file mode 100644
index 00000000000..21af2bde201
--- /dev/null
+++ b/changelogs/unreleased/35072-fix-pages-delete.yml
@@ -0,0 +1,5 @@
+---
+title: Fix deleting GitLab Pages files when a project is removed
+merge_request: 13631
+author:
+type: fixed
diff --git a/changelogs/unreleased/36087-users-cannot-delete-their-account.yml b/changelogs/unreleased/36087-users-cannot-delete-their-account.yml
new file mode 100644
index 00000000000..9ba75d8b1d0
--- /dev/null
+++ b/changelogs/unreleased/36087-users-cannot-delete-their-account.yml
@@ -0,0 +1,5 @@
+---
+title: allow all users to delete their account
+merge_request: 13636
+author: Jacopo Beschi @jacopo-beschi
+type: changed
diff --git a/changelogs/unreleased/commits-list-page-limit.yml b/changelogs/unreleased/commits-list-page-limit.yml
new file mode 100644
index 00000000000..2fd54c5960a
--- /dev/null
+++ b/changelogs/unreleased/commits-list-page-limit.yml
@@ -0,0 +1,5 @@
+---
+title: Fix commit list not loading the correct page when scrolling
+merge_request:
+author:
+type: fixed
diff --git a/changelogs/unreleased/issue_35580.yml b/changelogs/unreleased/issue_35580.yml
new file mode 100644
index 00000000000..3a94e771e25
--- /dev/null
+++ b/changelogs/unreleased/issue_35580.yml
@@ -0,0 +1,4 @@
+---
+title: Fix project milestones import when projects belongs to a group
+merge_request:
+author:
diff --git a/changelogs/unreleased/zj-upgrade-grape.yml b/changelogs/unreleased/zj-upgrade-grape.yml
new file mode 100644
index 00000000000..daa6a234c07
--- /dev/null
+++ b/changelogs/unreleased/zj-upgrade-grape.yml
@@ -0,0 +1,5 @@
+---
+title: Upgrade grape to 1.0
+merge_request:
+author:
+type: other
diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example
index e73db08fcac..25285525846 100644
--- a/config/gitlab.yml.example
+++ b/config/gitlab.yml.example
@@ -649,6 +649,9 @@ test:
default:
path: tmp/tests/repositories/
gitaly_address: unix:tmp/tests/gitaly/gitaly.socket
+ failure_count_threshold: 999999
+ failure_wait_time: 0
+ storage_timeout: 30
broken:
path: tmp/tests/non-existent-repositories
gitaly_address: unix:tmp/tests/gitaly/gitaly.socket
diff --git a/config/initializers/0_acts_as_taggable.rb b/config/initializers/0_acts_as_taggable.rb
index 54e9fcc31db..50dc47673ab 100644
--- a/config/initializers/0_acts_as_taggable.rb
+++ b/config/initializers/0_acts_as_taggable.rb
@@ -5,5 +5,5 @@ ActsAsTaggableOn.strict_case_match = true
ActsAsTaggableOn.tags_counter = false
# validate that counter cache is disabled
-raise "Counter cache is not disabled" if
+raise "Counter cache is not disabled" if
ActsAsTaggableOn::Tagging.reflections["tag"].options[:counter_cache]
diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb
index 5c6578d3531..38ade18bdc0 100644
--- a/config/initializers/1_settings.rb
+++ b/config/initializers/1_settings.rb
@@ -1,3 +1,5 @@
+# rubocop:disable GitlabSecurity/PublicSend
+
require_dependency Rails.root.join('lib/gitlab') # Load Gitlab as soon as possible
class Settings < Settingslogic
diff --git a/config/initializers/6_validations.rb b/config/initializers/6_validations.rb
index 92ce4dd03cd..f8e67ce04c9 100644
--- a/config/initializers/6_validations.rb
+++ b/config/initializers/6_validations.rb
@@ -37,12 +37,12 @@ def validate_storages_config
storage_validation_error("#{name} is not a valid storage, because it has no `path` key. Refer to gitlab.yml.example for an updated example")
end
- %w(failure_count_threshold failure_wait_time failure_reset_time storage_timeout).each do |setting|
+ %w(failure_count_threshold failure_reset_time storage_timeout).each do |setting|
# Falling back to the defaults is fine!
next if repository_storage[setting].nil?
unless repository_storage[setting].to_f > 0
- storage_validation_error("#{setting}, for storage `#{name}` needs to be greater than 0")
+ storage_validation_error("`#{setting}` for storage `#{name}` needs to be greater than 0")
end
end
end
diff --git a/config/initializers/active_record_array_type_casting.rb b/config/initializers/active_record_array_type_casting.rb
new file mode 100644
index 00000000000..d94d592add6
--- /dev/null
+++ b/config/initializers/active_record_array_type_casting.rb
@@ -0,0 +1,20 @@
+module ActiveRecord
+ class PredicateBuilder
+ class ArrayHandler
+ module TypeCasting
+ def call(attribute, value)
+ # This is necessary because by default ActiveRecord does not respect
+ # custom type definitions (like our `ShaAttribute`) when providing an
+ # array in `where`, like in `where(commit_sha: [sha1, sha2, sha3])`.
+ model = attribute.relation&.engine
+ type = model.user_provided_columns[attribute.name] if model
+ value = value.map { |value| type.type_cast_for_database(value) } if type
+
+ super(attribute, value)
+ end
+ end
+
+ prepend TypeCasting
+ end
+ end
+end
diff --git a/config/initializers/static_files.rb b/config/initializers/static_files.rb
index 9ed96ddb0b4..943e01f1496 100644
--- a/config/initializers/static_files.rb
+++ b/config/initializers/static_files.rb
@@ -1,15 +1,15 @@
app = Rails.application
if app.config.serve_static_files
- # The `ActionDispatch::Static` middleware intercepts requests for static files
- # by checking if they exist in the `/public` directory.
+ # The `ActionDispatch::Static` middleware intercepts requests for static files
+ # by checking if they exist in the `/public` directory.
# We're replacing it with our `Gitlab::Middleware::Static` that does the same,
# except ignoring `/uploads`, letting those go through to the GitLab Rails app.
app.config.middleware.swap(
- ActionDispatch::Static,
- Gitlab::Middleware::Static,
- app.paths["public"].first,
+ ActionDispatch::Static,
+ Gitlab::Middleware::Static,
+ app.paths["public"].first,
app.config.static_cache_control
)
diff --git a/config/initializers/trusted_proxies.rb b/config/initializers/trusted_proxies.rb
index fc4f02453d7..0c32528311e 100644
--- a/config/initializers/trusted_proxies.rb
+++ b/config/initializers/trusted_proxies.rb
@@ -2,7 +2,7 @@
# as the ActionDispatch::Request object. This is necessary for libraries
# like rack_attack where they don't use ActionDispatch, and we want them
# to block/throttle requests on private networks.
-# Rack Attack specific issue: https://github.com/kickstarter/rack-attack/issues/145
+# Rack Attack specific issue: https://github.com/kickstarter/rack-attack/issues/145
module Rack
class Request
def trusted_proxy?(ip)
diff --git a/config/routes/repository.rb b/config/routes/repository.rb
index 57b7c55423d..9ffdebbcff1 100644
--- a/config/routes/repository.rb
+++ b/config/routes/repository.rb
@@ -3,7 +3,7 @@
resource :repository, only: [:create] do
member do
get ':ref/archive', constraints: { format: Gitlab::PathRegex.archive_formats_regex, ref: /.+/ }, action: 'archive', as: 'archive'
-
+
# deprecated since GitLab 9.5
get 'archive', constraints: { format: Gitlab::PathRegex.archive_formats_regex }, as: 'archive_alternative'
end
diff --git a/db/migrate/20161020075830_default_request_access_projects.rb b/db/migrate/20161020075830_default_request_access_projects.rb
index cb790291b24..a3a53350e8d 100644
--- a/db/migrate/20161020075830_default_request_access_projects.rb
+++ b/db/migrate/20161020075830_default_request_access_projects.rb
@@ -1,7 +1,7 @@
class DefaultRequestAccessProjects < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
-
+
def up
change_column_default :projects, :request_access_enabled, false
end
diff --git a/db/post_migrate/20170503004427_update_retried_for_ci_build.rb b/db/post_migrate/20170503004427_update_retried_for_ci_build.rb
index 705e11ed47d..3a4d6c4916b 100644
--- a/db/post_migrate/20170503004427_update_retried_for_ci_build.rb
+++ b/db/post_migrate/20170503004427_update_retried_for_ci_build.rb
@@ -21,7 +21,7 @@ class UpdateRetriedForCiBuild < ActiveRecord::Migration
private
def up_mysql
- # This is a trick to overcome MySQL limitation:
+ # This is a trick to overcome MySQL limitation:
# Mysql2::Error: Table 'ci_builds' is specified twice, both as a target for 'UPDATE' and as a separate source for data
# However, this leads to create a temporary table from `max(ci_builds.id)` which is slow and do full database update
execute <<-SQL.strip_heredoc
diff --git a/db/post_migrate/20170523083112_migrate_old_artifacts.rb b/db/post_migrate/20170523083112_migrate_old_artifacts.rb
index f2690bd0017..3a77b9751d3 100644
--- a/db/post_migrate/20170523083112_migrate_old_artifacts.rb
+++ b/db/post_migrate/20170523083112_migrate_old_artifacts.rb
@@ -7,7 +7,7 @@ class MigrateOldArtifacts < ActiveRecord::Migration
# This uses special heuristic to find potential candidates for data migration
# Read more about this here: https://gitlab.com/gitlab-org/gitlab-ce/issues/32036#note_30422345
-
+
def up
builds_with_artifacts.find_each do |build|
build.migrate_artifacts!
@@ -51,14 +51,14 @@ class MigrateOldArtifacts < ActiveRecord::Migration
private
def source_artifacts_path
- @source_artifacts_path ||=
+ @source_artifacts_path ||=
File.join(Gitlab.config.artifacts.path,
created_at.utc.strftime('%Y_%m'),
ci_id.to_s, id.to_s)
end
def target_artifacts_path
- @target_artifacts_path ||=
+ @target_artifacts_path ||=
File.join(Gitlab.config.artifacts.path,
created_at.utc.strftime('%Y_%m'),
project_id.to_s, id.to_s)
diff --git a/doc/README.md b/doc/README.md
index 547541c4876..267487520cd 100644
--- a/doc/README.md
+++ b/doc/README.md
@@ -102,7 +102,7 @@ Manage your [repositories](user/project/repository/index.md) from the UI (user i
### Migrate and import your projects from other platforms
-- [Importing to GitLab](workflow/importing/README.md): Import your projects from GitHub, Bitbucket, GitLab.com, FogBugz and SVN into GitLab.
+- [Importing to GitLab](user/project/import/index.md): Import your projects from GitHub, Bitbucket, GitLab.com, FogBugz and SVN into GitLab.
- [Migrating from SVN](workflow/importing/migrating_from_svn.md): Convert a SVN repository to Git and GitLab.
### Continuous Integration, Delivery, and Deployment
diff --git a/doc/articles/index.md b/doc/articles/index.md
index 3039faca411..1aa65504852 100644
--- a/doc/articles/index.md
+++ b/doc/articles/index.md
@@ -17,8 +17,8 @@ Explore GitLab's supported [authentications methods](../topics/authentication/in
| Article title | Category | Publishing date |
| :------------ | :------: | --------------: |
| **LDAP** |
-| [How to configure LDAP with GitLab CE](how_to_configure_ldap_gitlab_ce/index.md)| Admin guide | 2017/05/03 |
-| [How to configure LDAP with GitLab EE](https://docs.gitlab.com/ee/articles/how_to_configure_ldap_gitlab_ee/) | Admin guide | 2017/05/03 |
+| [How to configure LDAP with GitLab CE](how_to_configure_ldap_gitlab_ce/index.md)| Admin guide | 2017-05-03 |
+| [How to configure LDAP with GitLab EE](https://docs.gitlab.com/ee/articles/how_to_configure_ldap_gitlab_ee/) | Admin guide | 2017-05-03 |
## Build, test, and deploy with GitLab CI/CD
@@ -27,17 +27,17 @@ Build, test, and deploy the software you develop with [GitLab CI/CD](../ci/READM
| Article title | Category | Publishing date |
| :------------ | :------: | --------------: |
| [How to deploy Maven projects to Artifactory with GitLab CI/CD](artifactory_and_gitlab/index.md) | Tutorial | 2017-08-15 |
-| [Making CI Easier with GitLab](https://about.gitlab.com/2017/07/13/making-ci-easier-with-gitlab/) | Concepts | 2017/07/13 |
-| [Dockerizing GitLab Review Apps](https://about.gitlab.com/2017/07/11/dockerizing-review-apps/) | Concepts | 2017/07/11 |
-| [Continuous Integration: From Jenkins to GitLab Using Docker](https://about.gitlab.com/2017/07/27/docker-my-precious/) | Concepts | 2017/07/27 |
-| [Continuous Delivery of a Spring Boot application with GitLab CI and Kubernetes](https://about.gitlab.com/2016/12/14/continuous-delivery-of-a-spring-boot-application-with-gitlab-ci-and-kubernetes/) | Tutorial | 2016/12/14 |
-| [Setting up GitLab CI for Android projects](https://about.gitlab.com/2016/11/30/setting-up-gitlab-ci-for-android-projects/) | Tutorial | 2016/11/30 |
-| [Automated Debian Package Build with GitLab CI](https://about.gitlab.com/2016/10/12/automated-debian-package-build-with-gitlab-ci/) | Tutorial | 2016/10/12 |
-| [Building an Elixir Release into a Docker image using GitLab CI](https://about.gitlab.com/2016/08/11/building-an-elixir-release-into-docker-image-using-gitlab-ci-part-1/) | Tutorial | 2016/08/11 |
-| [Continuous Delivery with GitLab and Convox](https://about.gitlab.com/2016/06/09/continuous-delivery-with-gitlab-and-convox/) | Technical overview | 2016/06/09 |
-| [GitLab Container Registry](https://about.gitlab.com/2016/05/23/gitlab-container-registry/) | Technical overview | 2016/05/23 |
-| [How to use GitLab CI and MacStadium to build your macOS or iOS projects](https://about.gitlab.com/2017/05/15/how-to-use-macstadium-and-gitlab-ci-to-build-your-macos-or-ios-projects/) | Technical overview | 2017/05/15 |
-| [Setting up GitLab CI for iOS projects](https://about.gitlab.com/2016/03/10/setting-up-gitlab-ci-for-ios-projects/) | Tutorial | 2016/03/10 |
+| [Making CI Easier with GitLab](https://about.gitlab.com/2017/07/13/making-ci-easier-with-gitlab/) | Concepts | 2017-07-13 |
+| [Dockerizing GitLab Review Apps](https://about.gitlab.com/2017/07/11/dockerizing-review-apps/) | Concepts | 2017-07-11 |
+| [Continuous Integration: From Jenkins to GitLab Using Docker](https://about.gitlab.com/2017/07/27/docker-my-precious/) | Concepts | 2017-07-27 |
+| [Continuous Delivery of a Spring Boot application with GitLab CI and Kubernetes](https://about.gitlab.com/2016/12/14/continuous-delivery-of-a-spring-boot-application-with-gitlab-ci-and-kubernetes/) | Tutorial | 2016-12-14 |
+| [Setting up GitLab CI for Android projects](https://about.gitlab.com/2016/11/30/setting-up-gitlab-ci-for-android-projects/) | Tutorial | 2016-11-30 |
+| [Automated Debian Package Build with GitLab CI](https://about.gitlab.com/2016/10/12/automated-debian-package-build-with-gitlab-ci/) | Tutorial | 2016-10-12 |
+| [Building an Elixir Release into a Docker image using GitLab CI](https://about.gitlab.com/2016/08/11/building-an-elixir-release-into-docker-image-using-gitlab-ci-part-1/) | Tutorial | 2016-08-11 |
+| [Continuous Delivery with GitLab and Convox](https://about.gitlab.com/2016/06/09/continuous-delivery-with-gitlab-and-convox/) | Technical overview | 2016-06-09 |
+| [GitLab Container Registry](https://about.gitlab.com/2016/05/23/gitlab-container-registry/) | Technical overview | 2016-05-23 |
+| [How to use GitLab CI and MacStadium to build your macOS or iOS projects](https://about.gitlab.com/2017/05/15/how-to-use-macstadium-and-gitlab-ci-to-build-your-macos-or-ios-projects/) | Technical overview | 2017-05-15 |
+| [Setting up GitLab CI for iOS projects](https://about.gitlab.com/2016/03/10/setting-up-gitlab-ci-for-ios-projects/) | Tutorial | 2016-03-10 |
## Git
@@ -45,10 +45,11 @@ Learn how to use [Git with GitLab](../topics/git/index.md):
| Article title | Category | Publishing date |
| :------------ | :------: | --------------: |
-| [Why Git is Worth the Learning Curve](https://about.gitlab.com/2017/05/17/learning-curve-is-the-biggest-challenge-developers-face-with-git/) | Concepts | 2017/05/17 |
-| [How to install Git](how_to_install_git/index.md) | Tutorial | 2017/05/15 |
-| [Getting Started with Git LFS](https://about.gitlab.com/2017/01/30/getting-started-with-git-lfs-tutorial/) | Tutorial | 2017/01/30 |
-| [Git Tips & Tricks](https://about.gitlab.com/2016/12/08/git-tips-and-tricks/) | Technical overview | 2016/12/08 |
+| [Numerous _undo_ possibilities in Git](numerous_undo_possibilities_in_git/index.md) | Tutorial | 2017-08-17 |
+| [Why Git is Worth the Learning Curve](https://about.gitlab.com/2017/05/17/learning-curve-is-the-biggest-challenge-developers-face-with-git/) | Concepts | 2017-05-17 |
+| [How to install Git](how_to_install_git/index.md) | Tutorial | 2017-05-15 |
+| [Getting Started with Git LFS](https://about.gitlab.com/2017/01/30/getting-started-with-git-lfs-tutorial/) | Tutorial | 2017-01-30 |
+| [Git Tips & Tricks](https://about.gitlab.com/2016/12/08/git-tips-and-tricks/) | Technical overview | 2016-12-08 |
## GitLab Pages
@@ -57,21 +58,21 @@ Learn how to deploy a static website with [GitLab Pages](../user/project/pages/i
| Article title | Category | Publishing date |
| :------------ | :------: | --------------: |
| **Series: GitLab Pages from A to Z:** |
-| [- Part 1: Static sites and GitLab Pages domains](../user/project/pages/getting_started_part_one.md)| User guide | 2017/02/22 |
-| [- Part 2: Quick start guide - Setting up GitLab Pages](../user/project/pages/getting_started_part_two.md)| User guide | 2017/02/22 |
-| [- Part 3: Setting Up Custom Domains - DNS Records and SSL/TLS Certificates](../user/project/pages/getting_started_part_three.md)| User guide | 2017/02/22 |
-| [- Part 4: Creating and tweaking `.gitlab-ci.yml` for GitLab Pages](../user/project/pages/getting_started_part_four.md)| User guide | 2017/02/22 |
-| [Setting up GitLab Pages with CloudFlare Certificates](https://about.gitlab.com/2017/02/07/setting-up-gitlab-pages-with-cloudflare-certificates/) | Tutorial | 2017/02/07 |
-| [Building a new GitLab Docs site with Nanoc, GitLab CI, and GitLab Pages](https://about.gitlab.com/2016/12/07/building-a-new-gitlab-docs-site-with-nanoc-gitlab-ci-and-gitlab-pages/) | Tutorial | 2016/12/07 |
-| [Publish Code Coverage Report with GitLab Pages](https://about.gitlab.com/2016/11/03/publish-code-coverage-report-with-gitlab-pages/) | Tutorial | 2016/11/03 |
-| [GitLab CI: Deployment & Environments](https://about.gitlab.com/2016/08/26/ci-deployment-and-environments/) | Tutorial | 2016/08/26 |
-| [Posting to your GitLab Pages blog from iOS](https://about.gitlab.com/2016/08/19/posting-to-your-gitlab-pages-blog-from-ios/) | Tutorial | 2016/08/19 |
+| [- Part 1: Static sites and GitLab Pages domains](../user/project/pages/getting_started_part_one.md)| User guide | 2017-02-22 |
+| [- Part 2: Quick start guide - Setting up GitLab Pages](../user/project/pages/getting_started_part_two.md)| User guide | 2017-02-22 |
+| [- Part 3: Setting Up Custom Domains - DNS Records and SSL/TLS Certificates](../user/project/pages/getting_started_part_three.md)| User guide | 2017-02-22 |
+| [- Part 4: Creating and tweaking `.gitlab-ci.yml` for GitLab Pages](../user/project/pages/getting_started_part_four.md)| User guide | 2017-02-22 |
+| [Setting up GitLab Pages with CloudFlare Certificates](https://about.gitlab.com/2017/02/07/setting-up-gitlab-pages-with-cloudflare-certificates/) | Tutorial | 2017-02-07 |
+| [Building a new GitLab Docs site with Nanoc, GitLab CI, and GitLab Pages](https://about.gitlab.com/2016/12/07/building-a-new-gitlab-docs-site-with-nanoc-gitlab-ci-and-gitlab-pages/) | Tutorial | 2016-12-07 |
+| [Publish Code Coverage Report with GitLab Pages](https://about.gitlab.com/2016/11/03/publish-code-coverage-report-with-gitlab-pages/) | Tutorial | 2016-11-03 |
+| [GitLab CI: Deployment & Environments](https://about.gitlab.com/2016/08/26/ci-deployment-and-environments/) | Tutorial | 2016-08-26 |
+| [Posting to your GitLab Pages blog from iOS](https://about.gitlab.com/2016/08/19/posting-to-your-gitlab-pages-blog-from-ios/) | Tutorial | 2016-08-19 |
| **Series: Static Site Generator:** |
-| [- Part 1: Dynamic vs Static Websites](https://about.gitlab.com/2016/06/03/ssg-overview-gitlab-pages-part-1-dynamic-x-static/) | Tutorial | 2016/06/03 |
-| [- Part 2: Modern Static Site Generators](https://about.gitlab.com/2016/06/10/ssg-overview-gitlab-pages-part-2/) | Tutorial | 2016/06/10 |
-| [- Part 3: Build any SSG site with GitLab Pages](https://about.gitlab.com/2016/06/17/ssg-overview-gitlab-pages-part-3-examples-ci/) | Tutorial | 2016/06/17 |
-| [Securing your GitLab Pages with TLS and Let's Encrypt](https://about.gitlab.com/2016/04/11/tutorial-securing-your-gitlab-pages-with-tls-and-letsencrypt/) | Tutorial | 2016/04/11 |
-| [Hosting on GitLab.com with GitLab Pages](https://about.gitlab.com/2016/04/07/gitlab-pages-setup/) | Tutorial | 2016/04/07 |
+| [- Part 1: Dynamic vs Static Websites](https://about.gitlab.com/2016/06/03/ssg-overview-gitlab-pages-part-1-dynamic-x-static/) | Tutorial | 2016-06-03 |
+| [- Part 2: Modern Static Site Generators](https://about.gitlab.com/2016/06/10/ssg-overview-gitlab-pages-part-2/) | Tutorial | 2016-06-10 |
+| [- Part 3: Build any SSG site with GitLab Pages](https://about.gitlab.com/2016/06/17/ssg-overview-gitlab-pages-part-3-examples-ci/) | Tutorial | 2016-06-17 |
+| [Securing your GitLab Pages with TLS and Let's Encrypt](https://about.gitlab.com/2016/04/11/tutorial-securing-your-gitlab-pages-with-tls-and-letsencrypt/) | Tutorial | 2016-04-11 |
+| [Hosting on GitLab.com with GitLab Pages](https://about.gitlab.com/2016/04/07/gitlab-pages-setup/) | Tutorial | 2016-04-07 |
## Install and maintain GitLab
@@ -79,10 +80,10 @@ Install, upgrade, integrate, migrate to GitLab:
| Article title | Category | Publishing date |
| :------------ | :------: | --------------: |
-| [Video Tutorial: Idea to Production on Google Container Engine (GKE)](https://about.gitlab.com/2017/01/23/video-tutorial-idea-to-production-on-google-container-engine-gke/) | Tutorial | 2017/01/23 |
-| [How to Setup a GitLab Instance on Microsoft Azure](https://about.gitlab.com/2016/07/13/how-to-setup-a-gitlab-instance-on-microsoft-azure/) | Tutorial | 2016/07/13 |
-| [Get started with OpenShift Origin 3 and GitLab](openshift_and_gitlab/index.md) | Tutorial | 2016/06/28 |
-| [Getting started with GitLab and DigitalOcean](https://about.gitlab.com/2016/04/27/getting-started-with-gitlab-and-digitalocean/) | Tutorial | 2016/04/27 |
+| [Video Tutorial: Idea to Production on Google Container Engine (GKE)](https://about.gitlab.com/2017/01/23/video-tutorial-idea-to-production-on-google-container-engine-gke/) | Tutorial | 2017-01-23 |
+| [How to Setup a GitLab Instance on Microsoft Azure](https://about.gitlab.com/2016/07/13/how-to-setup-a-gitlab-instance-on-microsoft-azure/) | Tutorial | 2016-07-13 |
+| [Get started with OpenShift Origin 3 and GitLab](openshift_and_gitlab/index.md) | Tutorial | 2016-06-28 |
+| [Getting started with GitLab and DigitalOcean](https://about.gitlab.com/2016/04/27/getting-started-with-gitlab-and-digitalocean/) | Tutorial | 2016-04-27 |
## Software development
@@ -90,25 +91,25 @@ Explore the best of GitLab's software development's capabilities:
| Article title | Category | Publishing date |
| :------------ | :------: | --------------: |
-| [Making CI Easier with GitLab](https://about.gitlab.com/2017/07/13/making-ci-easier-with-gitlab/) | Concepts | 2017/07/13 |
-| [From 2/3 of the Self-Hosted Git Market, to the Next-Generation CI System, to Auto DevOps](https://about.gitlab.com/2017/06/29/whats-next-for-gitlab-ci/)| Concepts | 2017/06/29 |
-| [Fast and Natural Continuous Integration with GitLab CI](https://about.gitlab.com/2017/05/22/fast-and-natural-continuous-integration-with-gitlab-ci/) | Concepts | 2017/05/22 |
-| [Demo: Auto-Deploy from GitLab to an OpenShift Container Cluster](https://about.gitlab.com/2017/05/16/devops-containers-gitlab-openshift/) | Technical overview | 2017/05/16 |
-| [Demo: GitLab Service Desk](https://about.gitlab.com/2017/05/09/demo-service-desk/) | Feature highlight | 2017/05/09 |
-| [Demo: Mapping Work Versus Time, With Burndown Charts](https://about.gitlab.com/2017/04/25/mapping-work-to-do-versus-time-with-burndown-charts/) | Feature highlight | 2017/04/25 |
-| [Demo: Cloud Native Development with GitLab](https://about.gitlab.com/2017/04/18/cloud-native-demo/) | Feature highlight | 2017/04/18 |
-| [Demo: Mastering Code Review With GitLab](https://about.gitlab.com/2017/03/17/demo-mastering-code-review-with-gitlab/) | Feature highlight | 2017/03/17 |
-| [In 13 minutes from Kubernetes to a complete application development tool](https://about.gitlab.com/2016/11/14/idea-to-production/) | Technical overview | 2016/11/14 |
-| [GitLab Workflow, an Overview](https://about.gitlab.com/2016/10/25/gitlab-workflow-an-overview/) | Technical overview | 2016/10/25 |
-| [Trends in Version Control Land: Microservices](https://about.gitlab.com/2016/08/16/trends-in-version-control-land-microservices/) | Concepts | 2016/08/16 |
-| [Continuous Integration, Delivery, and Deployment with GitLab](https://about.gitlab.com/2016/08/05/continuous-integration-delivery-and-deployment-with-gitlab/) | Concepts | 2016/08/05 |
-| [Trends in Version Control Land: Innersourcing](https://about.gitlab.com/2016/07/07/trends-version-control-innersourcing/) | Concepts | 2016/07/07 |
-| [Tutorial: It's all connected in GitLab](https://about.gitlab.com/2016/03/08/gitlab-tutorial-its-all-connected/) | Technical overview | 2016/03/08 |
+| [Making CI Easier with GitLab](https://about.gitlab.com/2017/07/13/making-ci-easier-with-gitlab/) | Concepts | 2017-07-13 |
+| [From 2/3 of the Self-Hosted Git Market, to the Next-Generation CI System, to Auto DevOps](https://about.gitlab.com/2017/06/29/whats-next-for-gitlab-ci/)| Concepts | 2017-06-29 |
+| [Fast and Natural Continuous Integration with GitLab CI](https://about.gitlab.com/2017/05/22/fast-and-natural-continuous-integration-with-gitlab-ci/) | Concepts | 2017-05-22 |
+| [Demo: Auto-Deploy from GitLab to an OpenShift Container Cluster](https://about.gitlab.com/2017/05/16/devops-containers-gitlab-openshift/) | Technical overview | 2017-05-16 |
+| [Demo: GitLab Service Desk](https://about.gitlab.com/2017/05/09/demo-service-desk/) | Feature highlight | 2017-05-09 |
+| [Demo: Mapping Work Versus Time, With Burndown Charts](https://about.gitlab.com/2017/04/25/mapping-work-to-do-versus-time-with-burndown-charts/) | Feature highlight | 2017-04-25 |
+| [Demo: Cloud Native Development with GitLab](https://about.gitlab.com/2017/04/18/cloud-native-demo/) | Feature highlight | 2017-04-18 |
+| [Demo: Mastering Code Review With GitLab](https://about.gitlab.com/2017/03/17/demo-mastering-code-review-with-gitlab/) | Feature highlight | 2017-03-17 |
+| [In 13 minutes from Kubernetes to a complete application development tool](https://about.gitlab.com/2016/11/14/idea-to-production/) | Technical overview | 2016-11-14 |
+| [GitLab Workflow, an Overview](https://about.gitlab.com/2016/10/25/gitlab-workflow-an-overview/) | Technical overview | 2016-10-25 |
+| [Trends in Version Control Land: Microservices](https://about.gitlab.com/2016/08/16/trends-in-version-control-land-microservices/) | Concepts | 2016-08-16 |
+| [Continuous Integration, Delivery, and Deployment with GitLab](https://about.gitlab.com/2016/08/05/continuous-integration-delivery-and-deployment-with-gitlab/) | Concepts | 2016-08-05 |
+| [Trends in Version Control Land: Innersourcing](https://about.gitlab.com/2016/07/07/trends-version-control-innersourcing/) | Concepts | 2016-07-07 |
+| [Tutorial: It's all connected in GitLab](https://about.gitlab.com/2016/03/08/gitlab-tutorial-its-all-connected/) | Technical overview | 2016-03-08 |
## Technologies
| Article title | Category | Publishing date |
| :------------ | :------: | --------------: |
-| [Why we are not leaving the cloud](https://about.gitlab.com/2017/03/02/why-we-are-not-leaving-the-cloud/) | Concepts | 2017/03/02 |
-| [Why We Chose Vue.js](https://about.gitlab.com/2016/10/20/why-we-chose-vue/) | Concepts | 2016/10/20 |
-| [Markdown Kramdown Tips & Tricks](https://about.gitlab.com/2016/07/19/markdown-kramdown-tips-and-tricks/) | Technical overview | 2016/07/19 |
+| [Why we are not leaving the cloud](https://about.gitlab.com/2017/03/02/why-we-are-not-leaving-the-cloud/) | Concepts | 2017-03-02 |
+| [Why We Chose Vue.js](https://about.gitlab.com/2016/10/20/why-we-chose-vue/) | Concepts | 2016-10-20 |
+| [Markdown Kramdown Tips & Tricks](https://about.gitlab.com/2016/07/19/markdown-kramdown-tips-and-tricks/) | Technical overview | 2016-07-19 |
diff --git a/doc/articles/numerous_undo_possibilities_in_git/img/branching.png b/doc/articles/numerous_undo_possibilities_in_git/img/branching.png
new file mode 100644
index 00000000000..9a80c211c99
--- /dev/null
+++ b/doc/articles/numerous_undo_possibilities_in_git/img/branching.png
Binary files differ
diff --git a/doc/articles/numerous_undo_possibilities_in_git/img/rebase_reset.png b/doc/articles/numerous_undo_possibilities_in_git/img/rebase_reset.png
new file mode 100644
index 00000000000..ac7ea9ecddc
--- /dev/null
+++ b/doc/articles/numerous_undo_possibilities_in_git/img/rebase_reset.png
Binary files differ
diff --git a/doc/articles/numerous_undo_possibilities_in_git/img/revert.png b/doc/articles/numerous_undo_possibilities_in_git/img/revert.png
new file mode 100644
index 00000000000..13b3a35ca45
--- /dev/null
+++ b/doc/articles/numerous_undo_possibilities_in_git/img/revert.png
Binary files differ
diff --git a/doc/articles/numerous_undo_possibilities_in_git/index.md b/doc/articles/numerous_undo_possibilities_in_git/index.md
new file mode 100644
index 00000000000..9f1239b8f88
--- /dev/null
+++ b/doc/articles/numerous_undo_possibilities_in_git/index.md
@@ -0,0 +1,497 @@
+# Numerous undo possibilities in Git
+
+> **Article [Type](../../development/writing_documentation.md#types-of-technical-articles):** tutorial ||
+> **Level:** intermediary ||
+> **Author:** [Crt Mori](https://gitlab.com/Letme) ||
+> **Publication date:** 2017/08/17
+
+## Introduction
+
+In this tutorial, we will show you different ways of undoing your work in Git, for which
+we will assume you have a basic working knowledge of. Check GitLab's
+[Git documentation](../../topics/git/index.md#git-documentation) for reference.
+Also, we will only provide some general info of the commands, which is enough
+to get you started for the easy cases/examples, but for anything more advanced please refer to the [Git book](https://git-scm.com/book/en/v2).
+
+We will explain a few different techniques to undo your changes based on the stage
+of the change in your current development. Also, keep in mind that [nothing in
+Git is really deleted.][git-autoclean-ref]
+This means that until Git automatically cleans detached commits (which cannot be
+accessed by branch or tag) it will be possible to view them with `git reflog` command
+and access them with direct commit-id. Read more about _[redoing the undo](#redoing-the-undo)_ on the section below.
+
+This guide is organized depending on the [stage of development][git-basics]
+where you want to undo your changes from and if they were shared with other developers
+or not. Because Git is tracking changes a created or edited file is in the unstaged state
+(if created it is untracked by Git). After you add it to a repository (`git add`) you put
+a file into the **staged** state, which is then committed (`git commit`) to your
+local repository. After that, file can be shared with other developers (`git push`).
+Here's what we'll cover in this tutorial:
+
+ - [Undo local changes](#undo-local-changes) which were not pushed to remote repository
+
+ - Before you commit, in both unstaged and staged state
+ - After you committed
+
+ - Undo changes after they are pushed to remote repository
+
+ - [Without history modification](#undo-remote-changes-without-changing-history) (preferred way)
+ - [With history modification](#undo-remote-changes-with-modifying-history) (requires
+ coordination with team and force pushes).
+
+ - [Usecases when modifying history is generally acceptable](#where-modifying-history-is-generally-acceptable)
+ - [How to modify history](#how-modifying-history-is-done)
+ - [How to remove sensitive information from repository](#deleting-sensitive-information-from-commits)
+
+
+### Branching strategy
+
+[Git][git-official] is a de-centralized version control system, which means that beside regular
+versioning of the whole repository, it has possibilities to exchange changes
+with other repositories. To avoid chaos with
+[multiple sources of truth][git-distributed], various
+development workflows have to be followed, and it depends on your internal
+workflow how certain changes or commits can be undone or changed.
+[GitLab Flow][gitlab-flow] provides a good
+balance between developers clashing with each other while
+developing the same feature and cooperating seamlessly, but it does not enable
+joined development of the same feature by multiple developers by default.
+When multiple developers develop the same feature on the same branch, clashing
+with every synchronization is unavoidable, but a proper or chosen Git Workflow will
+prevent that anything is lost or out of sync when feature is complete. You can also
+read through this blog post on [Git Tips & Tricks][gitlab-git-tips-n-tricks]
+to learn how to easily **do** things in Git.
+
+
+## Undo local changes
+
+Until you push your changes to any remote repository, they will only affect you.
+That broadens your options on how to handle undoing them. Still, local changes
+can be on various stages and each stage has a different approach on how to tackle them.
+
+
+### Unstaged local changes (before you commit)
+
+When a change is made, but it is not added to the staged tree, Git itself
+proposes a solution to discard changes to certain file.
+
+Suppose you edited a file to change the content using your favorite editor:
+
+```shell
+vim <file>
+```
+
+Since you did not `git add <file>` to staging, it should be under unstaged files (or
+untracked if file was created). You can confirm that with:
+
+```shell
+$ git status
+On branch master
+Your branch is up-to-date with 'origin/master'.
+Changes not staged for commit:
+ (use "git add <file>..." to update what will be committed)
+ (use "git checkout -- <file>..." to discard changes in working directory)
+
+ modified: <file>
+no changes added to commit (use "git add" and/or "git commit -a")
+```
+
+At this point there are 3 options to undo the local changes you have:
+
+ - Discard all local changes, but save them for possible re-use [later](#quickly-save-local-changes)
+
+ ```shell
+ git stash
+ ```
+
+ - Discarding local changes (permanently) to a file
+
+ ```shell
+ git checkout -- <file>
+ ```
+
+ - Discard all local changes to all files permanently
+
+ ```shell
+ git reset --hard
+ ```
+
+
+Before executing `git reset --hard`, keep in mind that there is also a way to
+just temporary store the changes without committing them using `git stash`.
+This command resets the changes to all files, but it also saves them in case
+you would like to apply them at some later time. You can read more about it in
+[section below](#quickly-save-local-changes).
+
+### Quickly save local changes
+
+You are working on a feature when a boss drops by with an urgent task. Since your
+feature is not complete, but you need to swap to another branch, you can use
+`git stash` to save what you had done, swap to another branch, commit, push,
+test, then get back to previous feature branch, do `git stash pop` and continue
+where you left.
+
+The example above shows that discarding all changes is not always a preferred option,
+but Git provides a way to save them for later, while resetting the repository to state without
+them. This is achieved by Git stashing command `git stash`, which in fact saves your
+current work and runs `git reset --hard`, but it also has various
+additional options like:
+
+ - `git stash save`, which enables including temporary commit message, which will help you identify changes, among with other options
+ - `git stash list`, which lists all previously stashed commits (yes, there can be more) that were not `pop`ed
+ - `git stash pop`, which redoes previously stashed changes and removes them from stashed list
+ - `git stash apply`, which redoes previously stashed changes, but keeps them on stashed list
+
+### Staged local changes (before you commit)
+
+Let's say you have added some files to staging, but you want to remove them from the
+current commit, yet you want to retain those changes - just move them outside
+of the staging tree. You also have an option to discard all changes with
+`git reset --hard` or think about `git stash` [as described earlier.](#quickly-save-local-changes)
+
+Lets start the example by editing a file, with your favorite editor, to change the
+content and add it to staging
+
+```
+vim <file>
+git add <file>
+```
+
+The file is now added to staging as confirmed by `git status` command:
+
+```shell
+$ git status
+On branch master
+Your branch is up-to-date with 'origin/master'.
+Changes to be committed:
+ (use "git reset HEAD <file>..." to unstage)
+
+ new file: <file>
+```
+
+Now you have 4 options to undo your changes:
+
+ - Unstage the file to current commit (HEAD)
+
+ ```shell
+ git reset HEAD <file>
+ ```
+
+ - Unstage everything - retain changes
+
+ ```shell
+ git reset
+ ```
+
+ - Discard all local changes, but save them for [later](#quickly-save-local-changes)
+
+ ```shell
+ git stash
+ ```
+
+ - Discard everything permanently
+
+ ```shell
+ git reset --hard
+ ```
+
+## Committed local changes
+
+Once you commit, your changes are recorded by the version control system.
+Because you haven't pushed to your remote repository yet, your changes are
+still not public (or shared with other developers). At this point, undoing
+things is a lot easier, we have quite some workaround options. Once you push
+your code, you'll have less options to troubleshoot your work.
+
+### Without modifying history
+
+Through the development process some of the previously committed changes do not
+fit anymore in the end solution, or are source of the bugs. Once you find the
+commit which triggered bug, or once you have a faulty commit, you can simply
+revert it with `git revert commit-id`. This command inverts (swaps) the additions and
+deletions in that commit, so that it does not modify history. Retaining history
+can be helpful in future to notice that some changes have been tried
+unsuccessfully in the past.
+
+In our example we will assume there are commits `A`,`B`,`C`,`D`,`E` committed in this order: `A-B-C-D-E`,
+and `B` is the commit you want to undo. There are many different ways to identify commit
+`B` as bad, one of them is to pass a range to `git bisect` command. The provided range includes
+last known good commit (we assume `A`) and first known bad commit (where bug was detected - we will assume `E`).
+
+```shell
+git bisect A..E
+```
+
+Bisect will provide us with commit-id of the middle commit to test, and then guide us
+through simple bisection process. You can read more about it [in official Git Tools][git-debug]
+In our example we will end up with commit `B`, that introduced bug/error. We have
+4 options on how to remove it (or part of it) from our repository.
+
+- Undo (swap additions and deletions) changes introduced by commit `B`.
+
+ ```shell
+ git revert commit-B-id
+ ```
+
+- Undo changes on a single file or directory from commit `B`, but retain them in the staged state
+
+ ```shell
+ git checkout commit-B-id <file>
+ ```
+
+- Undo changes on a single file or directory from commit `B`, but retain them in the unstaged state
+
+ ```shell
+ git reset commit-B-id <file>
+ ```
+
+ - There is one command we also must not forget: **creating a new branch**
+ from the point where changes are not applicable or where the development has hit a
+ dead end. For example you have done commits `A-B-C-D` on your feature-branch
+ and then you figure `C` and `D` are wrong. At this point you either reset to `B`
+ and do commit `F` (which will cause problems with pushing and if forced pushed also with other developers)
+ since branch now looks `A-B-F`, which clashes with what other developers have locally (you will
+ [change history](#with-history-modification)), or you simply checkout commit `B` create
+ a new branch and do commit `F`. In the last case, everyone else can still do their work while you
+ have your new way to get it right and merge it back in later. Alternatively, with GitLab,
+ you can [cherry-pick](../../user/project/merge_requests/cherry_pick_changes.md#cherry-picking-a-commit)
+ that commit into a new merge request.
+
+ ![Create a new branch to avoid clashing](img/branching.png)
+
+ ```shell
+ git checkout commit-B-id
+ git checkout -b new-path-of-feature
+ # Create <commit F>
+ git commit -a
+ ```
+
+### With history modification
+
+There is one command for history modification and that is `git rebase`. Command
+provides interactive mode (`-i` flag) which enables you to:
+
+ - **reword** commit messages (there is also `git commit --amend` for editing
+ last commit message)
+ - **edit** the commit content (changes introduced by commit) and message
+ - **squash** multiple commits into a single one, and have a custom or aggregated
+ commit message
+ - **drop** commits - simply delete them
+ - and few more options
+
+Let us check few examples. Again there are commits `A-B-C-D` where you want to
+delete commit `B`.
+
+- Rebase the range from current commit D to A:
+
+ ```shell
+ git rebase -i A
+ ```
+
+- Command opens your favorite editor where you write `drop` in front of commit
+ `B`, but you leave default `pick` with all other commits. Save and exit the
+ editor to perform a rebase. Remember: if you want to cancel delete whole
+ file content before saving and exiting the editor
+
+In case you want to modify something introduced in commit `B`.
+
+- Rebase the range from current commit D to A:
+
+ ```shell
+ git rebase -i A
+ ```
+
+- Command opens your favorite text editor where you write `edit` in front of commit
+ `B`, but leave default `pick` with all other commits. Save and exit the editor to
+ perform a rebase
+
+- Now do your edits and commit changes:
+
+ ```shell
+ git commit -a
+ ```
+
+You can find some more examples in [below section where we explain how to modify
+history](#how-modifying-history-is-done)
+
+
+### Redoing the Undo
+
+Sometimes you realize that the changes you undid were useful and you want them
+back. Well because of first paragraph you are in luck. Command `git reflog`
+enables you to *recall* detached local commits by referencing or applying them
+via commit-id. Although, do not expect to see really old commits in reflog, because
+Git regularly [cleans the commits which are *unreachable* by branches or tags][git-autoclean-ref].
+
+To view repository history and to track older commits you can use below command:
+
+```shell
+$ git reflog show
+
+# Example output:
+b673187 HEAD@{4}: merge 6e43d5987921bde189640cc1e37661f7f75c9c0b: Merge made by the 'recursive' strategy.
+eb37e74 HEAD@{5}: rebase -i (finish): returning to refs/heads/master
+eb37e74 HEAD@{6}: rebase -i (pick): Commit C
+97436c6 HEAD@{7}: rebase -i (start): checkout 97436c6eec6396c63856c19b6a96372705b08b1b
+...
+88f1867 HEAD@{12}: commit: Commit D
+97436c6 HEAD@{13}: checkout: moving from 97436c6eec6396c63856c19b6a96372705b08b1b to test
+97436c6 HEAD@{14}: checkout: moving from master to 97436c6
+05cc326 HEAD@{15}: commit: Commit C
+6e43d59 HEAD@{16}: commit: Commit B
+```
+
+Output of command shows repository history. In first column there is commit-id,
+in following column, number next to `HEAD` indicates how many commits ago something
+was made, after that indicator of action that was made (commit, rebase, merge, ...)
+and then on end description of that action.
+
+## Undo remote changes without changing history
+
+This topic is roughly same as modifying committed local changes without modifying
+history. **It should be the preferred way of undoing changes on any remote repository
+or public branch.** Keep in mind that branching is the best solution when you want
+to retain the history of faulty development, yet start anew from certain point. Branching
+enables you to include the existing changes in new development (by merging) and
+it also provides a clear timeline and development structure.
+
+![Use revert to keep branch flowing](img/revert.png)
+
+If you want to revert changes introduced in certain `commit-id` you can simply
+revert that `commit-id` (swap additions and deletions) in newly created commit:
+You can do this with
+
+```shell
+git revert commit-id
+```
+
+or creating a new branch:
+
+```shell
+git checkout commit-id
+git checkout -b new-path-of-feature
+```
+
+## Undo remote changes with modifying history
+
+This is useful when you want to *hide* certain things - like secret keys,
+passwords, SSH keys, etc. It is and should not be used to hide mistakes, as
+it will make it harder to debug in case there are some other bugs. The main
+reason for this is that you loose the real development progress. **Also keep in
+mind that, even with modified history, commits are just detached and can still be
+accessed through commit-id** - at least until all repositories perform
+the cleanup of detached commits (happens automatically).
+
+![Modifying history causes problems on remote branch](img/rebase_reset.png)
+
+### Where modifying history is generally acceptable
+
+Modified history breaks the development chain of other developers, as changed
+history does not have matching commits'ids. For that reason it should not
+be used on any public branch or on branch that *might* be used by other
+developers. When contributing to big open source repositories (e.g. [GitLab CE][gitlab-ce]),
+it is acceptable to *squash* commits into a single one, to present
+a nicer history of your contribution.
+Keep in mind that this also removes the comments attached to certain commits
+in merge requests, so if you need to retain traceability in GitLab, then
+modifying history is not acceptable.
+A feature-branch of a merge request is a public branch and might be used by
+other developers, but project process and rules might allow or require
+you to use `git rebase` (command that changes history) to reduce number of
+displayed commits on target branch after reviews are done (for example
+GitLab). There is a `git merge --squash` command which does exactly that
+(squashes commits on feature-branch to a single commit on target branch
+at merge).
+
+>**Note:**
+Never modify the commit history of `master` or shared branch
+
+### How modifying history is done
+
+After you know what you want to modify (how far in history or how which range of
+old commits), use `git rebase -i commit-id`. This command will then display all the commits from
+current version to chosen commit-id and allow modification, squashing, deletion
+of that commits.
+
+```shell
+$ git rebase -i commit1-id..commit3-id
+pick <commit1-id> <commit1-commit-message>
+pick <commit2-id> <commit2-commit-message>
+pick <commit3-id> <commit3-commit-message>
+
+# Rebase commit1-id..commit3-id onto <commit4-id> (3 command(s))
+#
+# Commands:
+# p, pick = use commit
+# r, reword = use commit, but edit the commit message
+# e, edit = use commit, but stop for amending
+# s, squash = use commit, but meld into previous commit
+# f, fixup = like "squash", but discard this commit's log message
+# x, exec = run command (the rest of the line) using shell
+# d, drop = remove commit
+#
+# These lines can be re-ordered; they are executed from top to bottom.
+#
+# If you remove a line here THAT COMMIT WILL BE LOST.
+#
+# However, if you remove everything, the rebase will be aborted.
+#
+# Note that empty commits are commented out
+```
+
+>**Note:**
+It is important to notice that comment from the output clearly states that, if
+you decide to abort, then do not just close your editor (as that will in-fact
+modify history), but remove all uncommented lines and save.
+
+That is one of the reasons why `git rebase` should be used carefully on
+shared and remote branches. But don't worry, there will be nothing broken until
+you push back to the remote repository (so you can freely explore the
+different outcomes locally).
+
+```shell
+# Modify history from commit-id to HEAD (current commit)
+git rebase -i commit-id
+```
+
+### Deleting sensitive information from commits
+
+Git also enables you to delete sensitive information from your past commits and
+it does modify history in the progress. That is why we have included it in this
+section and not as a standalone topic. To do so, you should run the
+`git filter-branch`, which enables you to rewrite history with
+[certain filters][git-filters-manual].
+This command uses rebase to modify history and if you want to remove certain
+file from history altogether use:
+
+```shell
+git filter-branch --tree-filter 'rm filename' HEAD
+```
+
+Since `git filter-branch` command might be slow on big repositories, there are
+tools that can use some of Git specifics to enable faster execution of common
+tasks (which is exactly what removing sensitive information file is about).
+An alternative is [BFG Repo-cleaner][bfg-repo-cleaner]. Keep in mind that these
+tools are faster because they do not provide a same fully feature set as `git filter-branch`
+does, but focus on specific usecases.
+
+## Conclusion
+
+There are various options of undoing your work with any version control system, but
+because of de-centralized nature of Git, these options are multiplied (or limited)
+depending on the stage of your process. Git also enables rewriting history, but that
+should be avoided as it might cause problems when multiple developers are
+contributing to the same codebase.
+
+<!-- Identifiers, in alphabetical order -->
+
+[bfg-repo-cleaner]: https://rtyley.github.io/bfg-repo-cleaner/
+[git-autoclean-ref]: https://git-scm.com/book/en/v2/Git-Internals-Maintenance-and-Data-Recovery
+[git-basics]: https://git-scm.com/book/en/v2/Git-Basics-Recording-Changes-to-the-Repository
+[git-debug]: https://git-scm.com/book/en/v2/Git-Tools-Debugging-with-Git
+[git-distributed]: https://git-scm.com/about/distributed
+[git-filters-manual]: https://git-scm.com/docs/git-filter-branch#_options
+[git-official]: https://git-scm.com/
+[gitlab-ce]: https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md#contribution-acceptance-criteria
+[gitlab-flow]: https://about.gitlab.com/2014/09/29/gitlab-flow/
+[gitlab-git-tips-n-tricks]: https://about.gitlab.com/2016/12/08/git-tips-and-tricks/
diff --git a/doc/development/README.md b/doc/development/README.md
index 58993c52dcd..dd150421b65 100644
--- a/doc/development/README.md
+++ b/doc/development/README.md
@@ -46,6 +46,7 @@
## Databases
+- [Merge Request Checklist](database_merge_request_checklist.md)
- [What requires downtime?](what_requires_downtime.md)
- [Adding database indexes](adding_database_indexes.md)
- [Post Deployment Migrations](post_deployment_migrations.md)
@@ -56,6 +57,9 @@
- [Background Migrations](background_migrations.md)
- [Storing SHA1 Hashes As Binary](sha1_as_binary.md)
- [Iterating Tables In Batches](iterating_tables_in_batches.md)
+- [Ordering Table Columns](ordering_table_columns.md)
+- [Verifying Database Capabilities](verifying_database_capabilities.md)
+- [Hash Indexes](hash_indexes.md)
## i18n
diff --git a/doc/development/database_merge_request_checklist.md b/doc/development/database_merge_request_checklist.md
new file mode 100644
index 00000000000..75c395b61ef
--- /dev/null
+++ b/doc/development/database_merge_request_checklist.md
@@ -0,0 +1,15 @@
+# Merge Request Checklist
+
+When creating a merge request that performs database related changes (schema
+changes, adjusting queries to optimise performance, etc) you should use the
+merge request template called "Database Changes". This template contains a
+checklist of steps to follow to make sure the changes are up to snuff.
+
+To use the checklist, create a new merge request and click on the "Choose a
+template" dropdown, then click "Database Changes".
+
+An example of this checklist can be found at
+https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/12463.
+
+The source code of the checklist can be found in at
+https://gitlab.com/gitlab-org/gitlab-ce/blob/master/.gitlab/merge_request_templates/Database%20Changes.md
diff --git a/doc/development/hash_indexes.md b/doc/development/hash_indexes.md
new file mode 100644
index 00000000000..e6c1b3590b1
--- /dev/null
+++ b/doc/development/hash_indexes.md
@@ -0,0 +1,20 @@
+# Hash Indexes
+
+Both PostgreSQL and MySQL support hash indexes besides the regular btree
+indexes. Hash indexes however are to be avoided at all costs. While they may
+_sometimes_ provide better performance the cost of rehashing can be very high.
+More importantly: at least until PostgreSQL 10.0 hash indexes are not
+WAL-logged, meaning they are not replicated to any replicas. From the PostgreSQL
+documentation:
+
+> Hash index operations are not presently WAL-logged, so hash indexes might need
+> to be rebuilt with REINDEX after a database crash if there were unwritten
+> changes. Also, changes to hash indexes are not replicated over streaming or
+> file-based replication after the initial base backup, so they give wrong
+> answers to queries that subsequently use them. For these reasons, hash index
+> use is presently discouraged.
+
+RuboCop is configured to register an offence when it detects the use of a hash
+index.
+
+Instead of using hash indexes you should use regular btree indexes.
diff --git a/doc/development/ordering_table_columns.md b/doc/development/ordering_table_columns.md
new file mode 100644
index 00000000000..249e70c7b0e
--- /dev/null
+++ b/doc/development/ordering_table_columns.md
@@ -0,0 +1,127 @@
+# Ordering Table Columns
+
+Similar to C structures the space of a table is influenced by the order of
+columns. This is because the size of columns is aligned depending on the type of
+the column. Take the following column order for example:
+
+* id (integer, 4 bytes)
+* name (text, variable)
+* user_id (integer, 4 bytes)
+
+Integers are aligned to the word size. This means that on a 64 bit platform the
+actual size of each column would be: 8 bytes, variable, 8 bytes. This means that
+each row will require at least 16 bytes for the two integers, and a variable
+amount for the text field. If a table has a few rows this is not an issue, but
+once you start storing millions of rows you can save space by using a different
+order. For the above example a more ideal column order would be the following:
+
+* id (integer, 4 bytes)
+* user_id (integer, 4 bytes)
+* name (text, variable)
+
+In this setup the `id` and `user_id` columns can be packed together, which means
+we only need 8 bytes to store _both_ of them. This in turn each row will require
+8 bytes less of space.
+
+For GitLab we require that columns of new tables are ordered based to use the
+least amount of space. An easy way of doing this is to order them based on the
+type size in descending order with variable sizes (string and text columns for
+example) at the end.
+
+## Type Sizes
+
+While the PostgreSQL docuemntation
+(https://www.postgresql.org/docs/current/static/datatype.html) contains plenty
+of information we will list the sizes of common types here so it's easier to
+look them up. Here "word" refers to the word size, which is 4 bytes for a 32
+bits platform and 8 bytes for a 64 bits platform.
+
+| Type | Size | Aligned To |
+|:-----------------|:-------------------------------------|:-----------|
+| smallint | 2 bytes | 1 word |
+| integer | 4 bytes | 1 word |
+| bigint | 8 bytes | 8 bytes |
+| real | 4 bytes | 1 word |
+| double precision | 8 bytes | 8 bytes |
+| boolean | 1 byte | not needed |
+| text / string | variable, 1 byte plus the data | 1 word |
+| bytea | variable, 1 or 4 bytes plus the data | 1 word |
+| timestamp | 8 bytes | 8 bytes |
+| timestamptz | 8 bytes | 8 bytes |
+| date | 4 bytes | 1 word |
+
+A "variable" size means the actual size depends on the value being stored. If
+PostgreSQL determines this can be embedded directly into a row it may do so, but
+for very large values it will store the data externally and store a pointer (of
+1 word in size) in the column. Because of this variable sized columns should
+always be at the end of a table.
+
+## Real Example
+
+Let's use the "events" table as an example, which currently has the following
+layout:
+
+| Column | Type | Size |
+|:------------|:----------------------------|:---------|
+| id | integer | 4 bytes |
+| target_type | character varying | variable |
+| target_id | integer | 4 bytes |
+| title | character varying | variable |
+| data | text | variable |
+| project_id | integer | 4 bytes |
+| created_at | timestamp without time zone | 8 bytes |
+| updated_at | timestamp without time zone | 8 bytes |
+| action | integer | 4 bytes |
+| author_id | integer | 4 bytes |
+
+After adding padding to align the columns this would translate to columns being
+divided into fixed size chunks as follows:
+
+| Chunk Size | Columns |
+|:-----------|:------------------|
+| 8 bytes | id |
+| variable | target_type |
+| 8 bytes | target_id |
+| variable | title |
+| variable | data |
+| 8 bytes | project_id |
+| 8 bytes | created_at |
+| 8 bytes | updated_at |
+| 8 bytes | action, author_id |
+
+This means that excluding the variable sized data we need at least 48 bytes per
+row.
+
+We can optimise this by using the following column order instead:
+
+| Column | Type | Size |
+|:------------|:----------------------------|:---------|
+| created_at | timestamp without time zone | 8 bytes |
+| updated_at | timestamp without time zone | 8 bytes |
+| id | integer | 4 bytes |
+| target_id | integer | 4 bytes |
+| project_id | integer | 4 bytes |
+| action | integer | 4 bytes |
+| author_id | integer | 4 bytes |
+| target_type | character varying | variable |
+| title | character varying | variable |
+| data | text | variable |
+
+This would produce the following chunks:
+
+| Chunk Size | Columns |
+|:-----------|:-------------------|
+| 8 bytes | created_at |
+| 8 bytes | updated_at |
+| 8 bytes | id, target_id |
+| 8 bytes | project_id, action |
+| 8 bytes | author_id |
+| variable | target_type |
+| variable | title |
+| variable | data |
+
+Here we only need 40 bytes per row excluding the variable sized data. 8 bytes
+being saved may not sound like much, but for tables as large as the "events"
+table it does begin to matter. For example, when storing 80 000 000 rows this
+translates to a space saving of at least 610 MB: all by just changing the order
+of a few columns.
diff --git a/doc/development/serializing_data.md b/doc/development/serializing_data.md
index 2b56f48bc44..37332c20147 100644
--- a/doc/development/serializing_data.md
+++ b/doc/development/serializing_data.md
@@ -1,7 +1,8 @@
# Serializing Data
**Summary:** don't store serialized data in the database, use separate columns
-and/or tables instead.
+and/or tables instead. This includes storing of comma separated values as a
+string.
Rails makes it possible to store serialized data in JSON, YAML or other formats.
Such a field can be defined as follows:
diff --git a/doc/development/sql.md b/doc/development/sql.md
index 23fd7604957..974b1d99dff 100644
--- a/doc/development/sql.md
+++ b/doc/development/sql.md
@@ -216,4 +216,30 @@ exact same results. This also means there's no need to add an index on
`created_at` to ensure consistent performance as `id` is already indexed by
default.
+## Use WHERE EXISTS instead of WHERE IN
+
+While `WHERE IN` and `WHERE EXISTS` can be used to produce the same data it is
+recommended to use `WHERE EXISTS` whenever possible. While in many cases
+PostgreSQL can optimise `WHERE IN` quite well there are also many cases where
+`WHERE EXISTS` will perform (much) better.
+
+In Rails you have to use this by creating SQL fragments:
+
+```ruby
+Project.where('EXISTS (?)', User.select(1).where('projects.creator_id = users.id AND users.foo = X'))
+```
+
+This would then produce a query along the lines of the following:
+
+```sql
+SELECT *
+FROM projects
+WHERE EXISTS (
+ SELECT 1
+ FROM users
+ WHERE projects.creator_id = users.id
+ AND users.foo = X
+)
+```
+
[gin-index]: http://www.postgresql.org/docs/current/static/gin.html
diff --git a/doc/development/verifying_database_capabilities.md b/doc/development/verifying_database_capabilities.md
new file mode 100644
index 00000000000..cc6d62957e3
--- /dev/null
+++ b/doc/development/verifying_database_capabilities.md
@@ -0,0 +1,26 @@
+# Verifying Database Capabilities
+
+Sometimes certain bits of code may only work on a certain database and/or
+version. While we try to avoid such code as much as possible sometimes it is
+necessary to add database (version) specific behaviour.
+
+To facilitate this we have the following methods that you can use:
+
+* `Gitlab::Database.postgresql?`: returns `true` if PostgreSQL is being used
+* `Gitlab::Database.mysql?`: returns `true` if MySQL is being used
+* `Gitlab::Database.version`: returns the PostgreSQL version number as a string
+ in the format `X.Y.Z`. This method does not work for MySQL
+
+This allows you to write code such as:
+
+```ruby
+if Gitlab::Database.postgresql?
+ if Gitlab::Database.version.to_f >= 9.6
+ run_really_fast_query
+ else
+ run_fast_query
+ end
+else
+ run_query
+end
+```
diff --git a/doc/gitlab-basics/create-project.md b/doc/gitlab-basics/create-project.md
index 2513f4b420a..b4b77a2f94b 100644
--- a/doc/gitlab-basics/create-project.md
+++ b/doc/gitlab-basics/create-project.md
@@ -1,5 +1,9 @@
# How to create a project in GitLab
+>**Notes:**
+- For a list of words that are not allowed to be used as project names see the
+ [reserved names][reserved].
+
1. In your dashboard, click the green **New project** button or use the plus
icon in the upper right corner of the navigation bar.
@@ -25,4 +29,12 @@
1. Click **Create project**.
+## From a template
+
+To kickstart your development GitLab projects can be started from a template.
+For example, one of the templates included is Ruby on Rails. When filling out the
+form for new projects, click the 'Ruby on Rails' button. During project creation,
+this will import a Ruby on Rails template with GitLab CI preconfigured.
+
[import it]: ../workflow/importing/README.md
+[reserved]: ../user/reserved_names.md
diff --git a/doc/install/installation.md b/doc/install/installation.md
index b14cb2d44c4..66eb7675896 100644
--- a/doc/install/installation.md
+++ b/doc/install/installation.md
@@ -80,7 +80,7 @@ Make sure you have the right version of Git installed
# Install Git
sudo apt-get install -y git-core
- # Make sure Git is version 2.8.4 or higher
+ # Make sure Git is version 2.13.0 or higher
git --version
Is the system packaged Git too old? Remove it and compile from source.
diff --git a/doc/topics/git/index.md b/doc/topics/git/index.md
index 604f9375714..df56f031970 100644
--- a/doc/topics/git/index.md
+++ b/doc/topics/git/index.md
@@ -22,6 +22,7 @@ We've gathered some resources to help you to get the best from Git with GitLab.
- [Cherry-picking a commit](../../user/project/merge_requests/cherry_pick_changes.md#cherry-picking-a-commit)
- [Squashing commits](../../workflow/gitlab_flow.md#squashing-commits-with-rebase)
- **Articles:**
+ - [Numerous _undo_ possibilities in Git](../../articles/numerous_undo_possibilities_in_git/index.md)
- [How to install Git](../../articles/how_to_install_git/index.md)
- [Git Tips & Tricks](https://about.gitlab.com/2016/12/08/git-tips-and-tricks/)
- [Eight Tips to help you work better with Git](https://about.gitlab.com/2015/02/19/8-tips-to-help-you-work-better-with-git/)
diff --git a/doc/user/group/index.md b/doc/user/group/index.md
index ceec8b74373..9e168e830e5 100644
--- a/doc/user/group/index.md
+++ b/doc/user/group/index.md
@@ -57,6 +57,10 @@ By doing so:
## Create a new group
+> **Notes:**
+- For a list of words that are not allowed to be used as group names see the
+ [reserved names][reserved].
+
You can create a group in GitLab from:
1. The Groups page: expand the left menu, click **Groups**, and click the green button **New group**:
@@ -213,4 +217,5 @@ for the group (GitLab admins only, available in [GitLab Enterprise Edition Start
- **Pipelines quota**: keep track of the [pipeline quota](../admin_area/settings/continuous_integration.md) for the group
[permissions]: ../permissions.md#permissions
-[ee]: https://about.gitlab.com/products/ \ No newline at end of file
+[ee]: https://about.gitlab.com/products/
+[reserved]: ../reserved_names.md
diff --git a/doc/user/group/subgroups/index.md b/doc/user/group/subgroups/index.md
index 5724dcfab48..d2478aea4bd 100644
--- a/doc/user/group/subgroups/index.md
+++ b/doc/user/group/subgroups/index.md
@@ -83,10 +83,7 @@ structure.
- You need to be an Owner of a group in order to be able to create
a subgroup. For more information check the [permissions table][permissions].
- For a list of words that are not allowed to be used as group names see the
- [`path_regex.rb` file][reserved] under the `TOP_LEVEL_ROUTES`, `PROJECT_WILDCARD_ROUTES` and `GROUP_ROUTES` lists:
- - `TOP_LEVEL_ROUTES`: are names that are reserved as usernames or top level groups
- - `PROJECT_WILDCARD_ROUTES`: are names that are reserved for child groups or projects.
- - `GROUP_ROUTES`: are names that are reserved for all groups or projects.
+ [reserved names][reserved].
To create a subgroup:
@@ -175,5 +172,5 @@ Here's a list of what you can't do with subgroups:
[ce-2772]: https://gitlab.com/gitlab-org/gitlab-ce/issues/2772
[permissions]: ../../permissions.md#group
-[reserved]: https://gitlab.com/gitlab-org/gitlab-ce/blob/master/lib/gitlab/path_regex.rb
+[reserved]: ../../reserved_names.md
[issue]: https://gitlab.com/gitlab-org/gitlab-ce/issues/30472#note_27747600
diff --git a/doc/user/project/import/bitbucket.md b/doc/user/project/import/bitbucket.md
new file mode 100644
index 00000000000..b22c7db0047
--- /dev/null
+++ b/doc/user/project/import/bitbucket.md
@@ -0,0 +1,62 @@
+# Import your project from Bitbucket to GitLab
+
+Import your projects from Bitbucket to GitLab with minimal effort.
+
+## Overview
+
+>**Note:**
+The [Bitbucket integration][bb-import] must be first enabled in order to be
+able to import your projects from Bitbucket. Ask your GitLab administrator
+to enable this if not already.
+
+- At its current state, the Bitbucket importer can import:
+ - the repository description (GitLab 7.7+)
+ - the Git repository data (GitLab 7.7+)
+ - the issues (GitLab 7.7+)
+ - the issue comments (GitLab 8.15+)
+ - the pull requests (GitLab 8.4+)
+ - the pull request comments (GitLab 8.15+)
+ - the milestones (GitLab 8.15+)
+ - the wiki (GitLab 8.15+)
+- References to pull requests and issues are preserved (GitLab 8.7+)
+- Repository public access is retained. If a repository is private in Bitbucket
+ it will be created as private in GitLab as well.
+
+
+## How it works
+
+When issues/pull requests are being imported, the Bitbucket importer tries to find
+the Bitbucket author/assignee in GitLab's database using the Bitbucket ID. For this
+to work, the Bitbucket author/assignee should have signed in beforehand in GitLab
+and **associated their Bitbucket account**. If the user is not
+found in GitLab's database, the project creator (most of the times the current
+user that started the import process) is set as the author, but a reference on
+the issue about the original Bitbucket author is kept.
+
+The importer will create any new namespaces (groups) if they don't exist or in
+the case the namespace is taken, the repository will be imported under the user's
+namespace that started the import process.
+
+## Importing your Bitbucket repositories
+
+1. Sign in to GitLab and go to your dashboard.
+1. Click on **New project**.
+
+ ![New project in GitLab](img/bitbucket_import_new_project.png)
+
+1. Click on the "Bitbucket" button
+
+ ![Bitbucket](img/import_projects_from_new_project_page.png)
+
+1. Grant GitLab access to your Bitbucket account
+
+ ![Grant access](img/bitbucket_import_grant_access.png)
+
+1. Click on the projects that you'd like to import or **Import all projects**.
+ You can also select the namespace under which each project will be
+ imported.
+
+ ![Import projects](img/bitbucket_import_select_project.png)
+
+[bb-import]: ../../../integration/bitbucket.md
+[social sign-in]: ../../profile/account/social_sign_in.md
diff --git a/doc/user/project/import/fogbugz.md b/doc/user/project/import/fogbugz.md
new file mode 100644
index 00000000000..17222c53675
--- /dev/null
+++ b/doc/user/project/import/fogbugz.md
@@ -0,0 +1,28 @@
+# Import your project from FogBugz to GitLab
+
+It only takes a few simple steps to import your project from FogBugz.
+The importer will import all of your cases and comments with original case
+numbers and timestamps. You will also have the opportunity to map FogBugz
+users to GitLab users.
+
+1. From your GitLab dashboard click 'New project'
+1. Click on the 'FogBugz' button
+
+ ![FogBugz](img/fogbugz_import_select_fogbogz.png)
+
+1. Enter your FogBugz URL, email address, and password.
+
+ ![Login](img/fogbugz_import_login.png)
+
+1. Create mapping from FogBugz users to GitLab users.
+
+ ![User Map](img/fogbugz_import_user_map.png)
+
+1. Select the projects you wish to import by clicking the Import buttons
+
+ ![Import Project](img/fogbugz_import_select_project.png)
+
+1. Once the import has finished click the link to take you to the project
+dashboard. Follow the directions to push your existing repository.
+
+ ![Finished](img/fogbugz_import_finished.png)
diff --git a/doc/user/project/import/gitea.md b/doc/user/project/import/gitea.md
new file mode 100644
index 00000000000..f5746a0fb31
--- /dev/null
+++ b/doc/user/project/import/gitea.md
@@ -0,0 +1,77 @@
+# Import your project from Gitea to GitLab
+
+Import your projects from Gitea to GitLab with minimal effort.
+
+## Overview
+
+>**Note:**
+This requires Gitea `v1.0.0` or newer.
+
+- At its current state, Gitea importer can import:
+ - the repository description (GitLab 8.15+)
+ - the Git repository data (GitLab 8.15+)
+ - the issues (GitLab 8.15+)
+ - the pull requests (GitLab 8.15+)
+ - the milestones (GitLab 8.15+)
+ - the labels (GitLab 8.15+)
+- Repository public access is retained. If a repository is private in Gitea
+ it will be created as private in GitLab as well.
+
+## How it works
+
+Since Gitea is currently not an OAuth provider, author/assignee cannot be mapped
+to users in your GitLab's instance. This means that the project creator (most of
+the times the current user that started the import process) is set as the author,
+but a reference on the issue about the original Gitea author is kept.
+
+The importer will create any new namespaces (groups) if they don't exist or in
+the case the namespace is taken, the repository will be imported under the user's
+namespace that started the import process.
+
+## Importing your Gitea repositories
+
+The importer page is visible when you create a new project.
+
+![New project page on GitLab](img/import_projects_from_new_project_page.png)
+
+Click on the **Gitea** link and the import authorization process will start.
+
+![New Gitea project import](img/import_projects_from_gitea_new_import.png)
+
+### Authorize access to your repositories using a personal access token
+
+With this method, you will perform a one-off authorization with Gitea to grant
+GitLab access your repositories:
+
+1. Go to <https://you-gitea-instance/user/settings/applications> (replace
+ `you-gitea-instance` with the host of your Gitea instance).
+1. Click **Generate New Token**.
+1. Enter a token description.
+1. Click **Generate Token**.
+1. Copy the token hash.
+1. Go back to GitLab and provide the token to the Gitea importer.
+1. Hit the **List Your Gitea Repositories** button and wait while GitLab reads
+ your repositories' information. Once done, you'll be taken to the importer
+ page to select the repositories to import.
+
+### Select which repositories to import
+
+After you've authorized access to your Gitea repositories, you will be
+redirected to the Gitea importer page.
+
+From there, you can see the import statuses of your Gitea repositories.
+
+- Those that are being imported will show a _started_ status,
+- those already successfully imported will be green with a _done_ status,
+- whereas those that are not yet imported will have an **Import** button on the
+ right side of the table.
+
+If you want, you can import all your Gitea projects in one go by hitting
+**Import all projects** in the upper left corner.
+
+![Gitea importer page](img/import_projects_from_github_importer.png)
+
+---
+
+You can also choose a different name for the project and a different namespace,
+if you have the privileges to do so.
diff --git a/doc/user/project/import/github.md b/doc/user/project/import/github.md
new file mode 100644
index 00000000000..016f98966e3
--- /dev/null
+++ b/doc/user/project/import/github.md
@@ -0,0 +1,122 @@
+# Import your project from GitHub to GitLab
+
+Import your projects from GitHub to GitLab with minimal effort.
+
+## Overview
+
+>**Note:**
+If you are an administrator you can enable the [GitHub integration][gh-import]
+in your GitLab instance sitewide. This configuration is optional, users will
+still be able to import their GitHub repositories with a
+[personal access token][gh-token].
+
+>**Note:**
+Administrators of a GitLab instance (Community or Enterprise Edition) can also
+use the [GitHub rake task][gh-rake] to import projects from GitHub without the
+constrains of a Sidekiq worker.
+
+- At its current state, GitHub importer can import:
+ - the repository description (GitLab 7.7+)
+ - the Git repository data (GitLab 7.7+)
+ - the issues (GitLab 7.7+)
+ - the pull requests (GitLab 8.4+)
+ - the wiki pages (GitLab 8.4+)
+ - the milestones (GitLab 8.7+)
+ - the labels (GitLab 8.7+)
+ - the release note descriptions (GitLab 8.12+)
+- References to pull requests and issues are preserved (GitLab 8.7+)
+- Repository public access is retained. If a repository is private in GitHub
+ it will be created as private in GitLab as well.
+
+## How it works
+
+When issues/pull requests are being imported, the GitHub importer tries to find
+the GitHub author/assignee in GitLab's database using the GitHub ID. For this
+to work, the GitHub author/assignee should have signed in beforehand in GitLab
+and **associated their GitHub account**. If the user is not
+found in GitLab's database, the project creator (most of the times the current
+user that started the import process) is set as the author, but a reference on
+the issue about the original GitHub author is kept.
+
+The importer will create any new namespaces (groups) if they don't exist or in
+the case the namespace is taken, the repository will be imported under the user's
+namespace that started the import process.
+
+## Importing your GitHub repositories
+
+The importer page is visible when you create a new project.
+
+![New project page on GitLab](img/import_projects_from_new_project_page.png)
+
+Click on the **GitHub** link and the import authorization process will start.
+There are two ways to authorize access to your GitHub repositories:
+
+1. [Using the GitHub integration][gh-integration] (if it's enabled by your
+ GitLab administrator). This is the preferred way as it's possible to
+ preserve the GitHub authors/assignees. Read more in the [How it works](#how-it-works)
+ section.
+1. [Using a personal access token][gh-token] provided by GitHub.
+
+![Select authentication method](img/import_projects_from_github_select_auth_method.png)
+
+### Authorize access to your repositories using the GitHub integration
+
+If the [GitHub integration][gh-import] is enabled by your GitLab administrator,
+you can use it instead of the personal access token.
+
+1. First you may want to connect your GitHub account to GitLab in order for
+ the username mapping to be correct.
+1. Once you connect GitHub, click the **List your GitHub repositories** button
+ and you will be redirected to GitHub for permission to access your projects.
+1. After accepting, you'll be automatically redirected to the importer.
+
+You can now go on and [select which repositories to import](#select-which-repositories-to-import).
+
+### Authorize access to your repositories using a personal access token
+
+>**Note:**
+For a proper author/assignee mapping for issues and pull requests, the
+[GitHub integration][gh-integration] should be used instead of the
+[personal access token][gh-token]. If the GitHub integration is enabled by your
+GitLab administrator, it should be the preferred method to import your repositories.
+Read more in the [How it works](#how-it-works) section.
+
+If you are not using the GitHub integration, you can still perform a one-off
+authorization with GitHub to grant GitLab access your repositories:
+
+1. Go to <https://github.com/settings/tokens/new>.
+1. Enter a token description.
+1. Check the `repo` scope.
+1. Click **Generate token**.
+1. Copy the token hash.
+1. Go back to GitLab and provide the token to the GitHub importer.
+1. Hit the **List Your GitHub Repositories** button and wait while GitLab reads
+ your repositories' information. Once done, you'll be taken to the importer
+ page to select the repositories to import.
+
+### Select which repositories to import
+
+After you've authorized access to your GitHub repositories, you will be
+redirected to the GitHub importer page.
+
+From there, you can see the import statuses of your GitHub repositories.
+
+- Those that are being imported will show a _started_ status,
+- those already successfully imported will be green with a _done_ status,
+- whereas those that are not yet imported will have an **Import** button on the
+ right side of the table.
+
+If you want, you can import all your GitHub projects in one go by hitting
+**Import all projects** in the upper left corner.
+
+![GitHub importer page](img/import_projects_from_github_importer.png)
+
+---
+
+You can also choose a different name for the project and a different namespace,
+if you have the privileges to do so.
+
+[gh-import]: ../../../integration/github.md "GitHub integration"
+[gh-rake]: ../../../administration/raketasks/github_import.md "GitHub rake task"
+[gh-integration]: #authorize-access-to-your-repositories-using-the-github-integration
+[gh-token]: #authorize-access-to-your-repositories-using-a-personal-access-token
diff --git a/doc/user/project/import/gitlab_com.md b/doc/user/project/import/gitlab_com.md
new file mode 100644
index 00000000000..3b37da67a5b
--- /dev/null
+++ b/doc/user/project/import/gitlab_com.md
@@ -0,0 +1,20 @@
+# Project importing from GitLab.com to your private GitLab instance
+
+You can import your existing GitLab.com projects to your GitLab instance. But keep in mind that it is possible only if
+GitLab support is enabled on your GitLab instance.
+You can read more about GitLab support [here](http://docs.gitlab.com/ce/integration/gitlab.html)
+To get to the importer page you need to go to "New project" page.
+
+>**Note:**
+If you are interested in importing Wiki and Merge Request data to your new
+instance, you'll need to follow the instructions for [project export](../settings/import_export.md)
+
+![New project page](img/gitlab_new_project_page.png)
+
+Click on the "Import projects from GitLab.com" link and you will be redirected to GitLab.com
+for permission to access your projects. After accepting, you'll be automatically redirected to the importer.
+
+![Importer page](img/gitlab_importer.png)
+
+To import a project, you can simple click "Import". The importer will import your repository and issues.
+Once the importer is done, a new GitLab project will be created with your imported data.
diff --git a/doc/workflow/importing/img/bitbucket_import_grant_access.png b/doc/user/project/import/img/bitbucket_import_grant_access.png
index 429904e621d..429904e621d 100644
--- a/doc/workflow/importing/img/bitbucket_import_grant_access.png
+++ b/doc/user/project/import/img/bitbucket_import_grant_access.png
Binary files differ
diff --git a/doc/workflow/importing/img/bitbucket_import_new_project.png b/doc/user/project/import/img/bitbucket_import_new_project.png
index 8ed528c2f09..8ed528c2f09 100644
--- a/doc/workflow/importing/img/bitbucket_import_new_project.png
+++ b/doc/user/project/import/img/bitbucket_import_new_project.png
Binary files differ
diff --git a/doc/workflow/importing/img/bitbucket_import_select_project.png b/doc/user/project/import/img/bitbucket_import_select_project.png
index 1bca6166ec8..1bca6166ec8 100644
--- a/doc/workflow/importing/img/bitbucket_import_select_project.png
+++ b/doc/user/project/import/img/bitbucket_import_select_project.png
Binary files differ
diff --git a/doc/workflow/importing/fogbugz_importer/fogbugz_import_finished.png b/doc/user/project/import/img/fogbugz_import_finished.png
index 62c5c86c9b3..62c5c86c9b3 100644
--- a/doc/workflow/importing/fogbugz_importer/fogbugz_import_finished.png
+++ b/doc/user/project/import/img/fogbugz_import_finished.png
Binary files differ
diff --git a/doc/workflow/importing/fogbugz_importer/fogbugz_import_login.png b/doc/user/project/import/img/fogbugz_import_login.png
index 96bce70b74d..96bce70b74d 100644
--- a/doc/workflow/importing/fogbugz_importer/fogbugz_import_login.png
+++ b/doc/user/project/import/img/fogbugz_import_login.png
Binary files differ
diff --git a/doc/workflow/importing/fogbugz_importer/fogbugz_import_select_fogbogz.png b/doc/user/project/import/img/fogbugz_import_select_fogbogz.png
index b26c652e382..b26c652e382 100644
--- a/doc/workflow/importing/fogbugz_importer/fogbugz_import_select_fogbogz.png
+++ b/doc/user/project/import/img/fogbugz_import_select_fogbogz.png
Binary files differ
diff --git a/doc/workflow/importing/fogbugz_importer/fogbugz_import_select_project.png b/doc/user/project/import/img/fogbugz_import_select_project.png
index ccc82f9d4cd..ccc82f9d4cd 100644
--- a/doc/workflow/importing/fogbugz_importer/fogbugz_import_select_project.png
+++ b/doc/user/project/import/img/fogbugz_import_select_project.png
Binary files differ
diff --git a/doc/workflow/importing/fogbugz_importer/fogbugz_import_user_map.png b/doc/user/project/import/img/fogbugz_import_user_map.png
index 28ff55a8d89..28ff55a8d89 100644
--- a/doc/workflow/importing/fogbugz_importer/fogbugz_import_user_map.png
+++ b/doc/user/project/import/img/fogbugz_import_user_map.png
Binary files differ
diff --git a/doc/workflow/importing/gitlab_importer/importer.png b/doc/user/project/import/img/gitlab_importer.png
index 27d42eb492e..27d42eb492e 100644
--- a/doc/workflow/importing/gitlab_importer/importer.png
+++ b/doc/user/project/import/img/gitlab_importer.png
Binary files differ
diff --git a/doc/workflow/importing/gitlab_importer/new_project_page.png b/doc/user/project/import/img/gitlab_new_project_page.png
index c673724f436..c673724f436 100644
--- a/doc/workflow/importing/gitlab_importer/new_project_page.png
+++ b/doc/user/project/import/img/gitlab_new_project_page.png
Binary files differ
diff --git a/doc/workflow/importing/img/import_projects_from_gitea_new_import.png b/doc/user/project/import/img/import_projects_from_gitea_new_import.png
index a3f603cbd0a..a3f603cbd0a 100644
--- a/doc/workflow/importing/img/import_projects_from_gitea_new_import.png
+++ b/doc/user/project/import/img/import_projects_from_gitea_new_import.png
Binary files differ
diff --git a/doc/workflow/importing/img/import_projects_from_github_importer.png b/doc/user/project/import/img/import_projects_from_github_importer.png
index d8effaf6075..d8effaf6075 100644
--- a/doc/workflow/importing/img/import_projects_from_github_importer.png
+++ b/doc/user/project/import/img/import_projects_from_github_importer.png
Binary files differ
diff --git a/doc/workflow/importing/img/import_projects_from_github_select_auth_method.png b/doc/user/project/import/img/import_projects_from_github_select_auth_method.png
index 1ccb38a815e..1ccb38a815e 100644
--- a/doc/workflow/importing/img/import_projects_from_github_select_auth_method.png
+++ b/doc/user/project/import/img/import_projects_from_github_select_auth_method.png
Binary files differ
diff --git a/doc/workflow/importing/img/import_projects_from_new_project_page.png b/doc/user/project/import/img/import_projects_from_new_project_page.png
index 97ca30b2087..97ca30b2087 100644
--- a/doc/workflow/importing/img/import_projects_from_new_project_page.png
+++ b/doc/user/project/import/img/import_projects_from_new_project_page.png
Binary files differ
diff --git a/doc/user/project/import/index.md b/doc/user/project/import/index.md
new file mode 100644
index 00000000000..06e8c81ef06
--- /dev/null
+++ b/doc/user/project/import/index.md
@@ -0,0 +1,20 @@
+# Migrating projects to a GitLab instance
+
+1. [From Bitbucket.org](bitbucket.md)
+1. [From GitHub.com of GitHub Enterprise](github.md)
+1. [From GitLab.com](gitlab_com.md)
+1. [From FogBugz](fogbugz.md)
+1. [From Gitea](gitea.md)
+1. [From SVN](svn.md)
+
+In addition to the specific migration documentation above, you can import any
+Git repository via HTTP from the New Project page. Be aware that if the
+repository is too large the import can timeout.
+
+## Migrating from self-hosted GitLab to GitLab.com
+
+You can copy your repos by changing the remote and pushing to the new server,
+but issues and merge requests can't be imported.
+
+If you want to retain all metadata like issues and merge requests, you can use
+the [import/export feature](../settings/import_export.md).
diff --git a/doc/user/project/import/svn.md b/doc/user/project/import/svn.md
new file mode 100644
index 00000000000..7a3628a39d7
--- /dev/null
+++ b/doc/user/project/import/svn.md
@@ -0,0 +1,183 @@
+# Migrating from SVN to GitLab
+
+Subversion (SVN) is a central version control system (VCS) while
+Git is a distributed version control system. There are some major differences
+between the two, for more information consult your favorite search engine.
+
+## Overview
+
+There are two approaches to SVN to Git migration:
+
+1. [Git/SVN Mirror](#smooth-migration-with-a-gitsvn-mirror-using-subgit) which:
+ - Makes the GitLab repository to mirror the SVN project.
+ - Git and SVN repositories are kept in sync; you can use either one.
+ - Smoothens the migration process and allows to manage migration risks.
+
+1. [Cut over migration](#cut-over-migration-with-svn2git) which:
+ - Translates and imports the existing data and history from SVN to Git.
+ - Is a fire and forget approach, good for smaller teams.
+
+## Smooth migration with a Git/SVN mirror using SubGit
+
+[SubGit](https://subgit.com) is a tool for a smooth, stress-free SVN to Git
+migration. It creates a writable Git mirror of a local or remote Subversion
+repository and that way you can use both Subversion and Git as long as you like.
+It requires access to your GitLab server as it talks with the Git repositories
+directly in a filesystem level.
+
+### SubGit prerequisites
+
+1. Install Oracle JRE 1.8 or newer. On Debian-based Linux distributions you can
+ follow [this article](http://www.webupd8.org/2012/09/install-oracle-java-8-in-ubuntu-via-ppa.html).
+1. Download SubGit from https://subgit.com/download/.
+1. Unpack the downloaded SubGit zip archive to the `/opt` directory. The `subgit`
+ command will be available at `/opt/subgit-VERSION/bin/subgit`.
+
+### SubGit configuration
+
+The first step to mirror you SVN repository in GitLab is to create a new empty
+project which will be used as a mirror. For Omnibus installations the path to
+the repository will be located at
+`/var/opt/gitlab/git-data/repositories/USER/REPO.git` by default. For
+installations from source, the default repository directory will be
+`/home/git/repositories/USER/REPO.git`. For convenience, assign this path to a
+variable:
+
+```
+GIT_REPO_PATH=/var/opt/gitlab/git-data/repositories/USER/REPOS.git
+```
+
+SubGit will keep this repository in sync with a remote SVN project. For
+convenience, assign your remote SVN project URL to a variable:
+
+```
+SVN_PROJECT_URL=http://svn.company.com/repos/project
+```
+
+Next you need to run SubGit to set up a Git/SVN mirror. Make sure the following
+`subgit` command is ran on behalf of the same user that keeps ownership of
+GitLab Git repositories (by default `git`):
+
+```
+subgit configure --layout auto $SVN_PROJECT_URL $GIT_REPO_PATH
+```
+
+Adjust authors and branches mappings, if necessary. Open with your favorite
+text editor:
+
+```
+edit $GIT_REPO_PATH/subgit/authors.txt
+edit $GIT_REPO_PATH/subgit/config
+```
+
+For more information regarding the SubGit configuration options, refer to
+[SubGit's documentation](https://subgit.com/documentation.html) website.
+
+### Initial translation
+
+Now that SubGit has configured the Git/SVN repos, run `subgit` to perform the
+initial translation of existing SVN revisions into the Git repository:
+
+```
+subgit install $GIT_REPO_PATH
+```
+
+After the initial translation is completed, the Git repository and the SVN
+project will be kept in sync by `subgit` - new Git commits will be translated to
+SVN revisions and new SVN revisions will be translated to Git commits. Mirror
+works transparently and does not require any special commands.
+
+If you would prefer to perform one-time cut over migration with `subgit`, use
+the `import` command instead of `install`:
+
+```
+subgit import $GIT_REPO_PATH
+```
+
+### SubGit licensing
+
+Running SubGit in a mirror mode requires a
+[registration](https://subgit.com/pricing.html). Registration is free for open
+source, academic and startup projects.
+
+We're currently working on deeper GitLab/SubGit integration. You may track our
+progress at [this issue](https://gitlab.com/gitlab-org/gitlab-ee/issues/990).
+
+### SubGit support
+
+For any questions related to SVN to GitLab migration with SubGit, you can
+contact the SubGit team directly at [support@subgit.com](mailto:support@subgit.com).
+
+## Cut over migration with svn2git
+
+If you are currently using an SVN repository, you can migrate the repository
+to Git and GitLab. We recommend a hard cut over - run the migration command once
+and then have all developers start using the new GitLab repository immediately.
+Otherwise, it's hard to keep changing in sync in both directions. The conversion
+process should be run on a local workstation.
+
+Install `svn2git`. On all systems you can install as a Ruby gem if you already
+have Ruby and Git installed.
+
+```bash
+sudo gem install svn2git
+```
+
+On Debian-based Linux distributions you can install the native packages:
+
+```bash
+sudo apt-get install git-core git-svn ruby
+```
+
+Optionally, prepare an authors file so `svn2git` can map SVN authors to Git authors.
+If you choose not to create the authors file then commits will not be attributed
+to the correct GitLab user. Some users may not consider this a big issue while
+others will want to ensure they complete this step. If you choose to map authors
+you will be required to map every author that is present on changes in the SVN
+repository. If you don't, the conversion will fail and you will have to update
+the author file accordingly. The following command will search through the
+repository and output a list of authors.
+
+```bash
+svn log --quiet | grep -E "r[0-9]+ \| .+ \|" | cut -d'|' -f2 | sed 's/ //g' | sort | uniq
+```
+
+Use the output from the last command to construct the authors file.
+Create a file called `authors.txt` and add one mapping per line.
+
+```
+janedoe = Jane Doe <janedoe@example.com>
+johndoe = John Doe <johndoe@example.com>
+```
+
+If your SVN repository is in the standard format (trunk, branches, tags,
+not nested) the conversion is simple. For a non-standard repository see
+[svn2git documentation](https://github.com/nirvdrum/svn2git). The following
+command will checkout the repository and do the conversion in the current
+working directory. Be sure to create a new directory for each repository before
+running the `svn2git` command. The conversion process will take some time.
+
+```bash
+svn2git https://svn.example.com/path/to/repo --authors /path/to/authors.txt
+```
+
+If your SVN repository requires a username and password add the
+`--username <username>` and `--password <password` flags to the above command.
+`svn2git` also supports excluding certain file paths, branches, tags, etc. See
+[svn2git documentation](https://github.com/nirvdrum/svn2git) or run
+`svn2git --help` for full documentation on all of the available options.
+
+Create a new GitLab project, where you will eventually push your converted code.
+Copy the SSH or HTTP(S) repository URL from the project page. Add the GitLab
+repository as a Git remote and push all the changes. This will push all commits,
+branches and tags.
+
+```bash
+git remote add origin git@gitlab.com:<group>/<project>.git
+git push --all origin
+git push --tags origin
+```
+
+## Contribute to this guide
+We welcome all contributions that would expand this guide with instructions on
+how to migrate from SVN and other version control systems.
diff --git a/doc/user/project/index.md b/doc/user/project/index.md
index 0dd0faf35e9..ba6ac2797b3 100644
--- a/doc/user/project/index.md
+++ b/doc/user/project/index.md
@@ -90,11 +90,11 @@ from your fork to the upstream project
## Import or export a project
-- Import a project from:
- - [GitHub to GitLab](../../workflow/importing/import_projects_from_github.md)
- - [BitBucket to GitLab](../../workflow/importing/import_projects_from_bitbucket.md)
- - [Gitea to GitLab](../../workflow/importing/import_projects_from_gitea.md)
- - [FogBugz to GitLab](../../workflow/importing/import_projects_from_fogbugz.md)
+- [Import a project](import/index.md) from:
+ - [GitHub to GitLab](import/github.md)
+ - [BitBucket to GitLab](import/bitbucket.md)
+ - [Gitea to GitLab](import/gitea.md)
+ - [FogBugz to GitLab](import/fogbugz.md)
- [Export a project from GitLab](settings/import_export.md#exporting-a-project-and-its-data)
- [Importing and exporting projects between GitLab instances](settings/import_export.md)
diff --git a/doc/user/reserved_names.md b/doc/user/reserved_names.md
new file mode 100644
index 00000000000..50ec99be48b
--- /dev/null
+++ b/doc/user/reserved_names.md
@@ -0,0 +1,109 @@
+# Reserved project and group names
+
+Not all project & group names are allowed because they would conflict with
+existing routes used by GitLab.
+
+For a list of words that are not allowed to be used as group or project names, see the
+[`path_regex.rb` file][reserved] under the `TOP_LEVEL_ROUTES`, `PROJECT_WILDCARD_ROUTES` and `GROUP_ROUTES` lists:
+- `TOP_LEVEL_ROUTES`: are names that are reserved as usernames or top level groups
+- `PROJECT_WILDCARD_ROUTES`: are names that are reserved for child groups or projects.
+- `GROUP_ROUTES`: are names that are reserved for all groups or projects.
+
+## Reserved project names
+
+It is currently not possible to create a project with the following names:
+
+- -
+- badges
+- blame
+- blob
+- builds
+- commits
+- create
+- create_dir
+- edit
+- environments/folders
+- files
+- find_file
+- gitlab-lfs/objects
+- info/lfs/objects
+- new
+- preview
+- raw
+- refs
+- tree
+- update
+- wikis
+
+## Reserved group names
+
+Currently the following names are reserved as top level groups:
+
+- 503.html
+- -
+- .well-known
+- 404.html
+- 422.html
+- 500.html
+- 502.html
+- abuse_reports
+- admin
+- api
+- apple-touch-icon-precomposed.png
+- apple-touch-icon.png
+- files
+- assets
+- autocomplete
+- ci
+- dashboard
+- deploy.html
+- explore
+- favicon.ico
+- groups
+- header_logo_dark.png
+- header_logo_light.png
+- health_check
+- help
+- import
+- invites
+- jwt
+- koding
+- notification_settings
+- oauth
+- profile
+- projects
+- public
+- robots.txt
+- s
+- search
+- sent_notifications
+- slash-command-logo.png
+- snippets
+- u
+- unicorn_test
+- unsubscribes
+- uploads
+- users
+
+These group names are unavailable as subgroup names:
+
+- -
+- activity
+- analytics
+- audit_events
+- avatar
+- edit
+- group_members
+- hooks
+- issues
+- labels
+- ldap
+- ldap_group_links
+- merge_requests
+- milestones
+- notification_setting
+- pipeline_quota
+- projects
+- subgroups
+
+[reserved]: https://gitlab.com/gitlab-org/gitlab-ce/blob/master/lib/gitlab/path_regex.rb
diff --git a/doc/workflow/importing/README.md b/doc/workflow/importing/README.md
index 2d91bee0e94..f753708ad89 100644
--- a/doc/workflow/importing/README.md
+++ b/doc/workflow/importing/README.md
@@ -1,17 +1 @@
-# Migrating projects to a GitLab instance
-
-1. [Bitbucket](import_projects_from_bitbucket.md)
-1. [GitHub](import_projects_from_github.md)
-1. [GitLab.com](import_projects_from_gitlab_com.md)
-1. [FogBugz](import_projects_from_fogbugz.md)
-1. [Gitea](import_projects_from_gitea.md)
-1. [SVN](migrating_from_svn.md)
-
-In addition to the specific migration documentation above, you can import any
-Git repository via HTTP from the New Project page. Be aware that if the
-repository is too large the import can timeout.
-
-### Migrating from self-hosted GitLab to GitLab.com
-
-You can copy your repos by changing the remote and pushing to the new server;
-but issues and merge requests can't be imported.
+This document was moved to a [new location](../../user/project/import/index.md).
diff --git a/doc/workflow/importing/import_projects_from_bitbucket.md b/doc/workflow/importing/import_projects_from_bitbucket.md
index f3c636ed1d5..248c3990372 100644
--- a/doc/workflow/importing/import_projects_from_bitbucket.md
+++ b/doc/workflow/importing/import_projects_from_bitbucket.md
@@ -1,62 +1 @@
-# Import your project from Bitbucket to GitLab
-
-Import your projects from Bitbucket to GitLab with minimal effort.
-
-## Overview
-
->**Note:**
-The [Bitbucket integration][bb-import] must be first enabled in order to be
-able to import your projects from Bitbucket. Ask your GitLab administrator
-to enable this if not already.
-
-- At its current state, the Bitbucket importer can import:
- - the repository description (GitLab 7.7+)
- - the Git repository data (GitLab 7.7+)
- - the issues (GitLab 7.7+)
- - the issue comments (GitLab 8.15+)
- - the pull requests (GitLab 8.4+)
- - the pull request comments (GitLab 8.15+)
- - the milestones (GitLab 8.15+)
- - the wiki (GitLab 8.15+)
-- References to pull requests and issues are preserved (GitLab 8.7+)
-- Repository public access is retained. If a repository is private in Bitbucket
- it will be created as private in GitLab as well.
-
-
-## How it works
-
-When issues/pull requests are being imported, the Bitbucket importer tries to find
-the Bitbucket author/assignee in GitLab's database using the Bitbucket ID. For this
-to work, the Bitbucket author/assignee should have signed in beforehand in GitLab
-and **associated their Bitbucket account**. If the user is not
-found in GitLab's database, the project creator (most of the times the current
-user that started the import process) is set as the author, but a reference on
-the issue about the original Bitbucket author is kept.
-
-The importer will create any new namespaces (groups) if they don't exist or in
-the case the namespace is taken, the repository will be imported under the user's
-namespace that started the import process.
-
-## Importing your Bitbucket repositories
-
-1. Sign in to GitLab and go to your dashboard.
-1. Click on **New project**.
-
- ![New project in GitLab](img/bitbucket_import_new_project.png)
-
-1. Click on the "Bitbucket" button
-
- ![Bitbucket](img/import_projects_from_new_project_page.png)
-
-1. Grant GitLab access to your Bitbucket account
-
- ![Grant access](img/bitbucket_import_grant_access.png)
-
-1. Click on the projects that you'd like to import or **Import all projects**.
- You can also select the namespace under which each project will be
- imported.
-
- ![Import projects](img/bitbucket_import_select_project.png)
-
-[bb-import]: ../../integration/bitbucket.md
-[social sign-in]: ../../user/profile/account/social_sign_in.md
+This document was moved to a [new location](../../user/project/import/bitbucket.md).
diff --git a/doc/workflow/importing/import_projects_from_fogbugz.md b/doc/workflow/importing/import_projects_from_fogbugz.md
index 71af0f9ea44..050746e2b4d 100644
--- a/doc/workflow/importing/import_projects_from_fogbugz.md
+++ b/doc/workflow/importing/import_projects_from_fogbugz.md
@@ -1,29 +1 @@
-# Import your project from FogBugz to GitLab
-
-It only takes a few simple steps to import your project from FogBugz.
-The importer will import all of your cases and comments with original case
-numbers and timestamps. You will also have the opportunity to map FogBugz
-users to GitLab users.
-
-* From your GitLab dashboard click 'New project'
-
-* Click on the 'FogBugz' button
-
-![FogBugz](fogbugz_importer/fogbugz_import_select_fogbogz.png)
-
-* Enter your FogBugz URL, email address, and password.
-
-![Login](fogbugz_importer/fogbugz_import_login.png)
-
-* Create mapping from FogBugz users to GitLab users.
-
-![User Map](fogbugz_importer/fogbugz_import_user_map.png)
-
-* Select the projects you wish to import by clicking the Import buttons
-
-![Import Project](fogbugz_importer/fogbugz_import_select_project.png)
-
-* Once the import has finished click the link to take you to the project
-dashboard. Follow the directions to push your existing repository.
-
-![Finished](fogbugz_importer/fogbugz_import_finished.png)
+This document was moved to a [new location](../../user/project/import/fogbugz.md).
diff --git a/doc/workflow/importing/import_projects_from_gitea.md b/doc/workflow/importing/import_projects_from_gitea.md
index f5746a0fb31..cb90c490b0f 100644
--- a/doc/workflow/importing/import_projects_from_gitea.md
+++ b/doc/workflow/importing/import_projects_from_gitea.md
@@ -1,77 +1 @@
-# Import your project from Gitea to GitLab
-
-Import your projects from Gitea to GitLab with minimal effort.
-
-## Overview
-
->**Note:**
-This requires Gitea `v1.0.0` or newer.
-
-- At its current state, Gitea importer can import:
- - the repository description (GitLab 8.15+)
- - the Git repository data (GitLab 8.15+)
- - the issues (GitLab 8.15+)
- - the pull requests (GitLab 8.15+)
- - the milestones (GitLab 8.15+)
- - the labels (GitLab 8.15+)
-- Repository public access is retained. If a repository is private in Gitea
- it will be created as private in GitLab as well.
-
-## How it works
-
-Since Gitea is currently not an OAuth provider, author/assignee cannot be mapped
-to users in your GitLab's instance. This means that the project creator (most of
-the times the current user that started the import process) is set as the author,
-but a reference on the issue about the original Gitea author is kept.
-
-The importer will create any new namespaces (groups) if they don't exist or in
-the case the namespace is taken, the repository will be imported under the user's
-namespace that started the import process.
-
-## Importing your Gitea repositories
-
-The importer page is visible when you create a new project.
-
-![New project page on GitLab](img/import_projects_from_new_project_page.png)
-
-Click on the **Gitea** link and the import authorization process will start.
-
-![New Gitea project import](img/import_projects_from_gitea_new_import.png)
-
-### Authorize access to your repositories using a personal access token
-
-With this method, you will perform a one-off authorization with Gitea to grant
-GitLab access your repositories:
-
-1. Go to <https://you-gitea-instance/user/settings/applications> (replace
- `you-gitea-instance` with the host of your Gitea instance).
-1. Click **Generate New Token**.
-1. Enter a token description.
-1. Click **Generate Token**.
-1. Copy the token hash.
-1. Go back to GitLab and provide the token to the Gitea importer.
-1. Hit the **List Your Gitea Repositories** button and wait while GitLab reads
- your repositories' information. Once done, you'll be taken to the importer
- page to select the repositories to import.
-
-### Select which repositories to import
-
-After you've authorized access to your Gitea repositories, you will be
-redirected to the Gitea importer page.
-
-From there, you can see the import statuses of your Gitea repositories.
-
-- Those that are being imported will show a _started_ status,
-- those already successfully imported will be green with a _done_ status,
-- whereas those that are not yet imported will have an **Import** button on the
- right side of the table.
-
-If you want, you can import all your Gitea projects in one go by hitting
-**Import all projects** in the upper left corner.
-
-![Gitea importer page](img/import_projects_from_github_importer.png)
-
----
-
-You can also choose a different name for the project and a different namespace,
-if you have the privileges to do so.
+This document was moved to a [new location](../../user/project/import/gitea.md).
diff --git a/doc/workflow/importing/import_projects_from_github.md b/doc/workflow/importing/import_projects_from_github.md
index 8ed1d98d05b..13639feaa04 100644
--- a/doc/workflow/importing/import_projects_from_github.md
+++ b/doc/workflow/importing/import_projects_from_github.md
@@ -1,122 +1 @@
-# Import your project from GitHub to GitLab
-
-Import your projects from GitHub to GitLab with minimal effort.
-
-## Overview
-
->**Note:**
-If you are an administrator you can enable the [GitHub integration][gh-import]
-in your GitLab instance sitewide. This configuration is optional, users will
-still be able to import their GitHub repositories with a
-[personal access token][gh-token].
-
->**Note:**
-Administrators of a GitLab instance (Community or Enterprise Edition) can also
-use the [GitHub rake task][gh-rake] to import projects from GitHub without the
-constrains of a Sidekiq worker.
-
-- At its current state, GitHub importer can import:
- - the repository description (GitLab 7.7+)
- - the Git repository data (GitLab 7.7+)
- - the issues (GitLab 7.7+)
- - the pull requests (GitLab 8.4+)
- - the wiki pages (GitLab 8.4+)
- - the milestones (GitLab 8.7+)
- - the labels (GitLab 8.7+)
- - the release note descriptions (GitLab 8.12+)
-- References to pull requests and issues are preserved (GitLab 8.7+)
-- Repository public access is retained. If a repository is private in GitHub
- it will be created as private in GitLab as well.
-
-## How it works
-
-When issues/pull requests are being imported, the GitHub importer tries to find
-the GitHub author/assignee in GitLab's database using the GitHub ID. For this
-to work, the GitHub author/assignee should have signed in beforehand in GitLab
-and **associated their GitHub account**. If the user is not
-found in GitLab's database, the project creator (most of the times the current
-user that started the import process) is set as the author, but a reference on
-the issue about the original GitHub author is kept.
-
-The importer will create any new namespaces (groups) if they don't exist or in
-the case the namespace is taken, the repository will be imported under the user's
-namespace that started the import process.
-
-## Importing your GitHub repositories
-
-The importer page is visible when you create a new project.
-
-![New project page on GitLab](img/import_projects_from_new_project_page.png)
-
-Click on the **GitHub** link and the import authorization process will start.
-There are two ways to authorize access to your GitHub repositories:
-
-1. [Using the GitHub integration][gh-integration] (if it's enabled by your
- GitLab administrator). This is the preferred way as it's possible to
- preserve the GitHub authors/assignees. Read more in the [How it works](#how-it-works)
- section.
-1. [Using a personal access token][gh-token] provided by GitHub.
-
-![Select authentication method](img/import_projects_from_github_select_auth_method.png)
-
-### Authorize access to your repositories using the GitHub integration
-
-If the [GitHub integration][gh-import] is enabled by your GitLab administrator,
-you can use it instead of the personal access token.
-
-1. First you may want to connect your GitHub account to GitLab in order for
- the username mapping to be correct.
-1. Once you connect GitHub, click the **List your GitHub repositories** button
- and you will be redirected to GitHub for permission to access your projects.
-1. After accepting, you'll be automatically redirected to the importer.
-
-You can now go on and [select which repositories to import](#select-which-repositories-to-import).
-
-### Authorize access to your repositories using a personal access token
-
->**Note:**
-For a proper author/assignee mapping for issues and pull requests, the
-[GitHub integration][gh-integration] should be used instead of the
-[personal access token][gh-token]. If the GitHub integration is enabled by your
-GitLab administrator, it should be the preferred method to import your repositories.
-Read more in the [How it works](#how-it-works) section.
-
-If you are not using the GitHub integration, you can still perform a one-off
-authorization with GitHub to grant GitLab access your repositories:
-
-1. Go to <https://github.com/settings/tokens/new>.
-1. Enter a token description.
-1. Check the `repo` scope.
-1. Click **Generate token**.
-1. Copy the token hash.
-1. Go back to GitLab and provide the token to the GitHub importer.
-1. Hit the **List Your GitHub Repositories** button and wait while GitLab reads
- your repositories' information. Once done, you'll be taken to the importer
- page to select the repositories to import.
-
-### Select which repositories to import
-
-After you've authorized access to your GitHub repositories, you will be
-redirected to the GitHub importer page.
-
-From there, you can see the import statuses of your GitHub repositories.
-
-- Those that are being imported will show a _started_ status,
-- those already successfully imported will be green with a _done_ status,
-- whereas those that are not yet imported will have an **Import** button on the
- right side of the table.
-
-If you want, you can import all your GitHub projects in one go by hitting
-**Import all projects** in the upper left corner.
-
-![GitHub importer page](img/import_projects_from_github_importer.png)
-
----
-
-You can also choose a different name for the project and a different namespace,
-if you have the privileges to do so.
-
-[gh-import]: ../../integration/github.md "GitHub integration"
-[gh-rake]: ../../administration/raketasks/github_import.md "GitHub rake task"
-[gh-integration]: #authorize-access-to-your-repositories-using-the-github-integration
-[gh-token]: #authorize-access-to-your-repositories-using-a-personal-access-token
+This document was moved to a [new location](../../user/project/import/github.md).
diff --git a/doc/workflow/importing/import_projects_from_gitlab_com.md b/doc/workflow/importing/import_projects_from_gitlab_com.md
index b27125a44de..df4c180919a 100644
--- a/doc/workflow/importing/import_projects_from_gitlab_com.md
+++ b/doc/workflow/importing/import_projects_from_gitlab_com.md
@@ -1,21 +1 @@
-# Project importing from GitLab.com to your private GitLab instance
-
-You can import your existing GitLab.com projects to your GitLab instance. But keep in mind that it is possible only if
-GitLab support is enabled on your GitLab instance.
-You can read more about GitLab support [here](http://docs.gitlab.com/ce/integration/gitlab.html)
-To get to the importer page you need to go to "New project" page.
-
->**Note:**
-If you are interested in importing Wiki and Merge Request data to your new instance, you'll need to follow the instructions for [project export](../../user/project/settings/import_export.md)
-
-![New project page](gitlab_importer/new_project_page.png)
-
-Click on the "Import projects from GitLab.com" link and you will be redirected to GitLab.com
-for permission to access your projects. After accepting, you'll be automatically redirected to the importer.
-
-
-![Importer page](gitlab_importer/importer.png)
-
-
-To import a project, you can simple click "Import". The importer will import your repository and issues.
-Once the importer is done, a new GitLab project will be created with your imported data. \ No newline at end of file
+This document was moved to a [new location](../../user/project/import/gitlab_com.md).
diff --git a/doc/workflow/importing/migrating_from_svn.md b/doc/workflow/importing/migrating_from_svn.md
index 7a3628a39d7..81df3fbcdbb 100644
--- a/doc/workflow/importing/migrating_from_svn.md
+++ b/doc/workflow/importing/migrating_from_svn.md
@@ -1,183 +1 @@
-# Migrating from SVN to GitLab
-
-Subversion (SVN) is a central version control system (VCS) while
-Git is a distributed version control system. There are some major differences
-between the two, for more information consult your favorite search engine.
-
-## Overview
-
-There are two approaches to SVN to Git migration:
-
-1. [Git/SVN Mirror](#smooth-migration-with-a-gitsvn-mirror-using-subgit) which:
- - Makes the GitLab repository to mirror the SVN project.
- - Git and SVN repositories are kept in sync; you can use either one.
- - Smoothens the migration process and allows to manage migration risks.
-
-1. [Cut over migration](#cut-over-migration-with-svn2git) which:
- - Translates and imports the existing data and history from SVN to Git.
- - Is a fire and forget approach, good for smaller teams.
-
-## Smooth migration with a Git/SVN mirror using SubGit
-
-[SubGit](https://subgit.com) is a tool for a smooth, stress-free SVN to Git
-migration. It creates a writable Git mirror of a local or remote Subversion
-repository and that way you can use both Subversion and Git as long as you like.
-It requires access to your GitLab server as it talks with the Git repositories
-directly in a filesystem level.
-
-### SubGit prerequisites
-
-1. Install Oracle JRE 1.8 or newer. On Debian-based Linux distributions you can
- follow [this article](http://www.webupd8.org/2012/09/install-oracle-java-8-in-ubuntu-via-ppa.html).
-1. Download SubGit from https://subgit.com/download/.
-1. Unpack the downloaded SubGit zip archive to the `/opt` directory. The `subgit`
- command will be available at `/opt/subgit-VERSION/bin/subgit`.
-
-### SubGit configuration
-
-The first step to mirror you SVN repository in GitLab is to create a new empty
-project which will be used as a mirror. For Omnibus installations the path to
-the repository will be located at
-`/var/opt/gitlab/git-data/repositories/USER/REPO.git` by default. For
-installations from source, the default repository directory will be
-`/home/git/repositories/USER/REPO.git`. For convenience, assign this path to a
-variable:
-
-```
-GIT_REPO_PATH=/var/opt/gitlab/git-data/repositories/USER/REPOS.git
-```
-
-SubGit will keep this repository in sync with a remote SVN project. For
-convenience, assign your remote SVN project URL to a variable:
-
-```
-SVN_PROJECT_URL=http://svn.company.com/repos/project
-```
-
-Next you need to run SubGit to set up a Git/SVN mirror. Make sure the following
-`subgit` command is ran on behalf of the same user that keeps ownership of
-GitLab Git repositories (by default `git`):
-
-```
-subgit configure --layout auto $SVN_PROJECT_URL $GIT_REPO_PATH
-```
-
-Adjust authors and branches mappings, if necessary. Open with your favorite
-text editor:
-
-```
-edit $GIT_REPO_PATH/subgit/authors.txt
-edit $GIT_REPO_PATH/subgit/config
-```
-
-For more information regarding the SubGit configuration options, refer to
-[SubGit's documentation](https://subgit.com/documentation.html) website.
-
-### Initial translation
-
-Now that SubGit has configured the Git/SVN repos, run `subgit` to perform the
-initial translation of existing SVN revisions into the Git repository:
-
-```
-subgit install $GIT_REPO_PATH
-```
-
-After the initial translation is completed, the Git repository and the SVN
-project will be kept in sync by `subgit` - new Git commits will be translated to
-SVN revisions and new SVN revisions will be translated to Git commits. Mirror
-works transparently and does not require any special commands.
-
-If you would prefer to perform one-time cut over migration with `subgit`, use
-the `import` command instead of `install`:
-
-```
-subgit import $GIT_REPO_PATH
-```
-
-### SubGit licensing
-
-Running SubGit in a mirror mode requires a
-[registration](https://subgit.com/pricing.html). Registration is free for open
-source, academic and startup projects.
-
-We're currently working on deeper GitLab/SubGit integration. You may track our
-progress at [this issue](https://gitlab.com/gitlab-org/gitlab-ee/issues/990).
-
-### SubGit support
-
-For any questions related to SVN to GitLab migration with SubGit, you can
-contact the SubGit team directly at [support@subgit.com](mailto:support@subgit.com).
-
-## Cut over migration with svn2git
-
-If you are currently using an SVN repository, you can migrate the repository
-to Git and GitLab. We recommend a hard cut over - run the migration command once
-and then have all developers start using the new GitLab repository immediately.
-Otherwise, it's hard to keep changing in sync in both directions. The conversion
-process should be run on a local workstation.
-
-Install `svn2git`. On all systems you can install as a Ruby gem if you already
-have Ruby and Git installed.
-
-```bash
-sudo gem install svn2git
-```
-
-On Debian-based Linux distributions you can install the native packages:
-
-```bash
-sudo apt-get install git-core git-svn ruby
-```
-
-Optionally, prepare an authors file so `svn2git` can map SVN authors to Git authors.
-If you choose not to create the authors file then commits will not be attributed
-to the correct GitLab user. Some users may not consider this a big issue while
-others will want to ensure they complete this step. If you choose to map authors
-you will be required to map every author that is present on changes in the SVN
-repository. If you don't, the conversion will fail and you will have to update
-the author file accordingly. The following command will search through the
-repository and output a list of authors.
-
-```bash
-svn log --quiet | grep -E "r[0-9]+ \| .+ \|" | cut -d'|' -f2 | sed 's/ //g' | sort | uniq
-```
-
-Use the output from the last command to construct the authors file.
-Create a file called `authors.txt` and add one mapping per line.
-
-```
-janedoe = Jane Doe <janedoe@example.com>
-johndoe = John Doe <johndoe@example.com>
-```
-
-If your SVN repository is in the standard format (trunk, branches, tags,
-not nested) the conversion is simple. For a non-standard repository see
-[svn2git documentation](https://github.com/nirvdrum/svn2git). The following
-command will checkout the repository and do the conversion in the current
-working directory. Be sure to create a new directory for each repository before
-running the `svn2git` command. The conversion process will take some time.
-
-```bash
-svn2git https://svn.example.com/path/to/repo --authors /path/to/authors.txt
-```
-
-If your SVN repository requires a username and password add the
-`--username <username>` and `--password <password` flags to the above command.
-`svn2git` also supports excluding certain file paths, branches, tags, etc. See
-[svn2git documentation](https://github.com/nirvdrum/svn2git) or run
-`svn2git --help` for full documentation on all of the available options.
-
-Create a new GitLab project, where you will eventually push your converted code.
-Copy the SSH or HTTP(S) repository URL from the project page. Add the GitLab
-repository as a Git remote and push all the changes. This will push all commits,
-branches and tags.
-
-```bash
-git remote add origin git@gitlab.com:<group>/<project>.git
-git push --all origin
-git push --tags origin
-```
-
-## Contribute to this guide
-We welcome all contributions that would expand this guide with instructions on
-how to migrate from SVN and other version control systems.
+This document was moved to a [new location](../../user/project/import/svn.md).
diff --git a/features/steps/profile/emails.rb b/features/steps/profile/emails.rb
index 10ebe705365..4f44f932a6d 100644
--- a/features/steps/profile/emails.rb
+++ b/features/steps/profile/emails.rb
@@ -28,7 +28,7 @@ class Spinach::Features::ProfileEmails < Spinach::FeatureSteps
expect(email).to be_nil
expect(page).not_to have_content("my@email.com")
end
-
+
step 'I click link "Remove" for "my@email.com"' do
# there should only be one remove button at this time
click_link "Remove"
diff --git a/lib/after_commit_queue.rb b/lib/after_commit_queue.rb
new file mode 100644
index 00000000000..b67575a3ac2
--- /dev/null
+++ b/lib/after_commit_queue.rb
@@ -0,0 +1,30 @@
+module AfterCommitQueue
+ extend ActiveSupport::Concern
+
+ included do
+ after_commit :_run_after_commit_queue
+ after_rollback :_clear_after_commit_queue
+ end
+
+ def run_after_commit(method = nil, &block)
+ _after_commit_queue << proc { self.send(method) } if method
+ _after_commit_queue << block if block
+ true
+ end
+
+ protected
+
+ def _run_after_commit_queue
+ while action = _after_commit_queue.pop
+ self.instance_eval(&action)
+ end
+ end
+
+ def _after_commit_queue
+ @after_commit_queue ||= []
+ end
+
+ def _clear_after_commit_queue
+ _after_commit_queue.clear
+ end
+end
diff --git a/lib/api/api_guard.rb b/lib/api/api_guard.rb
index 0d2d71e336a..c4c0fdda665 100644
--- a/lib/api/api_guard.rb
+++ b/lib/api/api_guard.rb
@@ -122,7 +122,7 @@ module API
error_classes = [MissingTokenError, TokenNotFoundError,
ExpiredError, RevokedError, InsufficientScopeError]
- base.send :rescue_from, *error_classes, oauth2_bearer_token_error_handler
+ base.__send__(:rescue_from, *error_classes, oauth2_bearer_token_error_handler) # rubocop:disable GitlabSecurity/PublicSend
end
def oauth2_bearer_token_error_handler
diff --git a/lib/api/entities.rb b/lib/api/entities.rb
index 18cd604a216..e8dd61e493f 100644
--- a/lib/api/entities.rb
+++ b/lib/api/entities.rb
@@ -83,7 +83,7 @@ module API
expose :created_at, :last_activity_at
end
- class Project < BasicProjectDetails
+ class Project < BasicProjectDetails
include ::API::Helpers::RelatedResourcesHelpers
expose :_links do
@@ -541,8 +541,9 @@ module API
target_url = "namespace_project_#{target_type}_url"
target_anchor = "note_#{todo.note_id}" if todo.note_id?
- Gitlab::Routing.url_helpers.public_send(target_url,
- todo.project.namespace, todo.project, todo.target, anchor: target_anchor)
+ Gitlab::Routing
+ .url_helpers
+ .public_send(target_url, todo.project.namespace, todo.project, todo.target, anchor: target_anchor) # rubocop:disable GitlabSecurity/PublicSend
end
expose :body
diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb
index 3582ed81b0f..b56fd2388b3 100644
--- a/lib/api/helpers.rb
+++ b/lib/api/helpers.rb
@@ -290,7 +290,7 @@ module API
def uploaded_file(field, uploads_path)
if params[field]
- bad_request!("#{field} is not a file") unless params[field].respond_to?(:filename)
+ bad_request!("#{field} is not a file") unless params[field][:filename]
return params[field]
end
diff --git a/lib/api/jobs.rb b/lib/api/jobs.rb
index 8a67de10bca..a40018b214e 100644
--- a/lib/api/jobs.rb
+++ b/lib/api/jobs.rb
@@ -16,9 +16,9 @@ module API
case scope
when String
[scope]
- when Hashie::Mash
+ when ::Hash
scope.values
- when Hashie::Array
+ when ::Array
scope
else
['unknown']
diff --git a/lib/api/protected_branches.rb b/lib/api/protected_branches.rb
index d742f2e18d0..dccf4fa27a7 100644
--- a/lib/api/protected_branches.rb
+++ b/lib/api/protected_branches.rb
@@ -61,7 +61,7 @@ module API
service_args = [user_project, current_user, protected_branch_params]
protected_branch = ::ProtectedBranches::CreateService.new(*service_args).execute
-
+
if protected_branch.persisted?
present protected_branch, with: Entities::ProtectedBranch, project: user_project
else
diff --git a/lib/api/runners.rb b/lib/api/runners.rb
index 5bf5a18e42f..31f940fe96b 100644
--- a/lib/api/runners.rb
+++ b/lib/api/runners.rb
@@ -153,7 +153,7 @@ module API
render_api_error!('Scope contains invalid value', 400)
end
- runners.send(scope)
+ runners.public_send(scope) # rubocop:disable GitlabSecurity/PublicSend
end
def get_runner(id)
diff --git a/lib/api/templates.rb b/lib/api/templates.rb
index 0fc13b35d5b..f70bc0622b7 100644
--- a/lib/api/templates.rb
+++ b/lib/api/templates.rb
@@ -57,7 +57,7 @@ module API
end
get "templates/licenses" do
options = {
- featured: declared(params).popular.present? ? true : nil
+ featured: declared(params)[:popular].present? ? true : nil
}
licences = ::Kaminari.paginate_array(Licensee::License.all(options))
present paginate(licences), with: Entities::RepoLicense
@@ -71,7 +71,7 @@ module API
requires :name, type: String, desc: 'The name of the template'
end
get "templates/licenses/:name", requirements: { name: /[\w\.-]+/ } do
- not_found!('License') unless Licensee::License.find(declared(params).name)
+ not_found!('License') unless Licensee::License.find(declared(params)[:name])
template = parsed_license_template
@@ -102,7 +102,7 @@ module API
requires :name, type: String, desc: 'The name of the template'
end
get "templates/#{template_type}/:name" do
- new_template = klass.find(declared(params).name)
+ new_template = klass.find(declared(params)[:name])
render_response(template_type, new_template)
end
diff --git a/lib/api/v3/builds.rb b/lib/api/v3/builds.rb
index 93ad9eb26b8..c189d486f50 100644
--- a/lib/api/v3/builds.rb
+++ b/lib/api/v3/builds.rb
@@ -16,7 +16,7 @@ module API
coerce_with: ->(scope) {
if scope.is_a?(String)
[scope]
- elsif scope.is_a?(Hashie::Mash)
+ elsif scope.is_a?(::Hash)
scope.values
else
['unknown']
diff --git a/lib/api/v3/notes.rb b/lib/api/v3/notes.rb
index 23fe95e42e4..d49772b92f2 100644
--- a/lib/api/v3/notes.rb
+++ b/lib/api/v3/notes.rb
@@ -22,7 +22,7 @@ module API
use :pagination
end
get ":id/#{noteables_str}/:noteable_id/notes" do
- noteable = user_project.send(noteables_str.to_sym).find(params[:noteable_id])
+ noteable = user_project.public_send(noteables_str.to_sym).find(params[:noteable_id]) # rubocop:disable GitlabSecurity/PublicSend
if can?(current_user, noteable_read_ability_name(noteable), noteable)
# We exclude notes that are cross-references and that cannot be viewed
@@ -50,7 +50,7 @@ module API
requires :noteable_id, type: Integer, desc: 'The ID of the noteable'
end
get ":id/#{noteables_str}/:noteable_id/notes/:note_id" do
- noteable = user_project.send(noteables_str.to_sym).find(params[:noteable_id])
+ noteable = user_project.public_send(noteables_str.to_sym).find(params[:noteable_id]) # rubocop:disable GitlabSecurity/PublicSend
note = noteable.notes.find(params[:note_id])
can_read_note = can?(current_user, noteable_read_ability_name(noteable), noteable) && !note.cross_reference_not_visible_for?(current_user)
@@ -76,7 +76,7 @@ module API
noteable_id: params[:noteable_id]
}
- noteable = user_project.send(noteables_str.to_sym).find(params[:noteable_id])
+ noteable = user_project.public_send(noteables_str.to_sym).find(params[:noteable_id]) # rubocop:disable GitlabSecurity/PublicSend
if can?(current_user, noteable_read_ability_name(noteable), noteable)
if params[:created_at] && (current_user.admin? || user_project.owner == current_user)
diff --git a/lib/api/v3/templates.rb b/lib/api/v3/templates.rb
index 4c577a8d2b7..2a2fb59045c 100644
--- a/lib/api/v3/templates.rb
+++ b/lib/api/v3/templates.rb
@@ -59,7 +59,7 @@ module API
end
get route do
options = {
- featured: declared(params).popular.present? ? true : nil
+ featured: declared(params)[:popular].present? ? true : nil
}
present Licensee::License.all(options), with: ::API::Entities::RepoLicense
end
@@ -76,7 +76,7 @@ module API
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)
+ not_found!('License') unless Licensee::License.find(declared(params)[:name])
template = parsed_license_template
@@ -111,7 +111,7 @@ module API
requires :name, type: String, desc: 'The name of the template'
end
get route do
- new_template = klass.find(declared(params).name)
+ new_template = klass.find(declared(params)[:name])
render_response(template_type, new_template)
end
diff --git a/lib/banzai/filter/external_issue_reference_filter.rb b/lib/banzai/filter/external_issue_reference_filter.rb
index 53a229256a5..ed01a72ff9f 100644
--- a/lib/banzai/filter/external_issue_reference_filter.rb
+++ b/lib/banzai/filter/external_issue_reference_filter.rb
@@ -95,10 +95,10 @@ module Banzai
private
def external_issues_cached(attribute)
- return project.public_send(attribute) unless RequestStore.active?
+ return project.public_send(attribute) unless RequestStore.active? # rubocop:disable GitlabSecurity/PublicSend
cached_attributes = RequestStore[:banzai_external_issues_tracker_attributes] ||= Hash.new { |h, k| h[k] = {} }
- cached_attributes[project.id][attribute] = project.public_send(attribute) if cached_attributes[project.id][attribute].nil?
+ cached_attributes[project.id][attribute] = project.public_send(attribute) if cached_attributes[project.id][attribute].nil? # rubocop:disable GitlabSecurity/PublicSend
cached_attributes[project.id][attribute]
end
end
diff --git a/lib/banzai/filter/image_lazy_load_filter.rb b/lib/banzai/filter/image_lazy_load_filter.rb
index 7a81d583b82..bcb4f332267 100644
--- a/lib/banzai/filter/image_lazy_load_filter.rb
+++ b/lib/banzai/filter/image_lazy_load_filter.rb
@@ -6,9 +6,9 @@ module Banzai
doc.xpath('descendant-or-self::img').each do |img|
img['class'] ||= '' << 'lazy'
img['data-src'] = img['src']
- img['src'] = LazyImageTagHelper.placeholder_image
+ img['src'] = LazyImageTagHelper.placeholder_image
end
-
+
doc
end
end
diff --git a/lib/banzai/object_renderer.rb b/lib/banzai/object_renderer.rb
index 002a3341ccd..2196a92474c 100644
--- a/lib/banzai/object_renderer.rb
+++ b/lib/banzai/object_renderer.rb
@@ -37,7 +37,7 @@ module Banzai
objects.each_with_index do |object, index|
redacted_data = redacted[index]
- object.__send__("redacted_#{attribute}_html=", redacted_data[:document].to_html.html_safe)
+ object.__send__("redacted_#{attribute}_html=", redacted_data[:document].to_html.html_safe) # rubocop:disable GitlabSecurity/PublicSend
object.user_visible_reference_count = redacted_data[:visible_reference_count]
end
end
diff --git a/lib/banzai/pipeline/base_pipeline.rb b/lib/banzai/pipeline/base_pipeline.rb
index 321fd5bbe14..3ae3bed570d 100644
--- a/lib/banzai/pipeline/base_pipeline.rb
+++ b/lib/banzai/pipeline/base_pipeline.rb
@@ -18,7 +18,7 @@ module Banzai
define_method(meth) do |text, context|
context = transform_context(context)
- html_pipeline.send(meth, text, context)
+ html_pipeline.__send__(meth, text, context) # rubocop:disable GitlabSecurity/PublicSend
end
end
end
diff --git a/lib/banzai/renderer.rb b/lib/banzai/renderer.rb
index ad08c0905e2..95d82d17658 100644
--- a/lib/banzai/renderer.rb
+++ b/lib/banzai/renderer.rb
@@ -43,7 +43,7 @@ module Banzai
# Same as +render_field+, but without consulting or updating the cache field
def self.cacheless_render_field(object, field, options = {})
- text = object.__send__(field)
+ text = object.__send__(field) # rubocop:disable GitlabSecurity/PublicSend
context = object.banzai_render_context(field).merge(options)
cacheless_render(text, context)
@@ -156,7 +156,7 @@ module Banzai
# method.
def self.full_cache_multi_key(cache_key, pipeline_name)
return unless cache_key
- Rails.cache.send(:expanded_key, full_cache_key(cache_key, pipeline_name))
+ Rails.cache.__send__(:expanded_key, full_cache_key(cache_key, pipeline_name)) # rubocop:disable GitlabSecurity/PublicSend
end
# GitLab EE needs to disable updates on GET requests in Geo
diff --git a/lib/bitbucket/collection.rb b/lib/bitbucket/collection.rb
index 3a9379ff680..a78495dbf5e 100644
--- a/lib/bitbucket/collection.rb
+++ b/lib/bitbucket/collection.rb
@@ -13,7 +13,7 @@ module Bitbucket
def method_missing(method, *args)
return super unless self.respond_to?(method)
- self.send(method, *args) do |item|
+ self.__send__(method, *args) do |item| # rubocop:disable GitlabSecurity/PublicSend
block_given? ? yield(item) : item
end
end
diff --git a/lib/ci/ansi2html.rb b/lib/ci/ansi2html.rb
index 8354fc8d595..b9e9f9f7f4a 100644
--- a/lib/ci/ansi2html.rb
+++ b/lib/ci/ansi2html.rb
@@ -208,7 +208,7 @@ module Ci
return unless command = stack.shift()
if self.respond_to?("on_#{command}", true)
- self.send("on_#{command}", stack)
+ self.__send__("on_#{command}", stack) # rubocop:disable GitlabSecurity/PublicSend
end
evaluate_command_stack(stack)
diff --git a/lib/constraints/project_url_constrainer.rb b/lib/constraints/project_url_constrainer.rb
index fd7b97d3167..5bef29eb1da 100644
--- a/lib/constraints/project_url_constrainer.rb
+++ b/lib/constraints/project_url_constrainer.rb
@@ -7,7 +7,7 @@ class ProjectUrlConstrainer
return false unless DynamicPathValidator.valid_project_path?(full_path)
# We intentionally allow SELECT(*) here so result of this query can be used
- # as cache for further Project.find_by_full_path calls within request
+ # as cache for further Project.find_by_full_path calls within request
Project.find_by_full_path(full_path, follow_redirects: request.get?).present?
end
end
diff --git a/lib/declarative_policy/base.rb b/lib/declarative_policy/base.rb
index df94cafb6a1..b028169f500 100644
--- a/lib/declarative_policy/base.rb
+++ b/lib/declarative_policy/base.rb
@@ -109,7 +109,7 @@ module DeclarativePolicy
name = name.to_sym
if delegation_block.nil?
- delegation_block = proc { @subject.__send__(name) }
+ delegation_block = proc { @subject.__send__(name) } # rubocop:disable GitlabSecurity/PublicSend
end
own_delegations[name] = delegation_block
@@ -221,7 +221,7 @@ module DeclarativePolicy
end
# computes the given ability and prints a helpful debugging output
- # showing which
+ # showing which
def debug(ability, *a)
runner(ability).debug(*a)
end
diff --git a/lib/declarative_policy/dsl.rb b/lib/declarative_policy/dsl.rb
index b26807a7622..6ba1e7a3c5c 100644
--- a/lib/declarative_policy/dsl.rb
+++ b/lib/declarative_policy/dsl.rb
@@ -93,7 +93,7 @@ module DeclarativePolicy
def method_missing(m, *a, &b)
return super unless @context_class.respond_to?(m)
- @context_class.__send__(m, *a, &b)
+ @context_class.__send__(m, *a, &b) # rubocop:disable GitlabSecurity/PublicSend
end
def respond_to_missing?(m)
diff --git a/lib/file_size_validator.rb b/lib/file_size_validator.rb
index eb19ab45ac3..de391de9059 100644
--- a/lib/file_size_validator.rb
+++ b/lib/file_size_validator.rb
@@ -44,13 +44,13 @@ class FileSizeValidator < ActiveModel::EachValidator
when Integer
check_value
when Symbol
- record.send(check_value)
+ record.public_send(check_value) # rubocop:disable GitlabSecurity/PublicSend
end
value ||= [] if key == :maximum
value_size = value.size
- next if value_size.send(validity_check, check_value)
+ next if value_size.public_send(validity_check, check_value) # rubocop:disable GitlabSecurity/PublicSend
errors_options = options.except(*RESERVED_OPTIONS)
errors_options[:file_size] = help.number_to_human_size check_value
diff --git a/lib/gitlab/auth.rb b/lib/gitlab/auth.rb
index 7d3aa532750..8cb4060cd97 100644
--- a/lib/gitlab/auth.rb
+++ b/lib/gitlab/auth.rb
@@ -101,7 +101,7 @@ module Gitlab
if Service.available_services_names.include?(underscored_service)
# We treat underscored_service as a trusted input because it is included
# in the Service.available_services_names whitelist.
- service = project.public_send("#{underscored_service}_service")
+ service = project.public_send("#{underscored_service}_service") # rubocop:disable GitlabSecurity/PublicSend
if service && service.activated? && service.valid_token?(password)
Gitlab::Auth::Result.new(nil, project, :ci, build_authentication_abilities)
@@ -149,7 +149,7 @@ module Gitlab
def abilities_for_scope(scopes)
scopes.map do |scope|
- self.public_send(:"#{scope}_scope_authentication_abilities")
+ self.public_send(:"#{scope}_scope_authentication_abilities") # rubocop:disable GitlabSecurity/PublicSend
end.flatten.uniq
end
diff --git a/lib/gitlab/auth/ip_rate_limiter.rb b/lib/gitlab/auth/ip_rate_limiter.rb
index 1089bc9f89e..e6173d45af3 100644
--- a/lib/gitlab/auth/ip_rate_limiter.rb
+++ b/lib/gitlab/auth/ip_rate_limiter.rb
@@ -11,11 +11,11 @@ module Gitlab
def enabled?
config.enabled
end
-
+
def reset!
Rack::Attack::Allow2Ban.reset(ip, config)
end
-
+
def register_fail!
# Allow2Ban.filter will return false if this IP has not failed too often yet
@banned = Rack::Attack::Allow2Ban.filter(ip, config) do
@@ -23,17 +23,17 @@ module Gitlab
ip_can_be_banned?
end
end
-
+
def banned?
@banned
end
-
+
private
-
+
def config
Gitlab.config.rack_attack.git_basic_auth
end
-
+
def ip_can_be_banned?
config.ip_whitelist.exclude?(ip)
end
diff --git a/lib/gitlab/cache/request_cache.rb b/lib/gitlab/cache/request_cache.rb
index f1a04affd38..754a45c3257 100644
--- a/lib/gitlab/cache/request_cache.rb
+++ b/lib/gitlab/cache/request_cache.rb
@@ -69,7 +69,7 @@ module Gitlab
instance_variable_set(ivar_name, {})
end
- key = __send__(cache_key_method_name, args)
+ key = __send__(cache_key_method_name, args) # rubocop:disable GitlabSecurity/PublicSend
store.fetch(key) { store[key] = super(*args) }
end
diff --git a/lib/gitlab/ci/build/artifacts/metadata.rb b/lib/gitlab/ci/build/artifacts/metadata.rb
index a375ccbece0..a788fb3fcbc 100644
--- a/lib/gitlab/ci/build/artifacts/metadata.rb
+++ b/lib/gitlab/ci/build/artifacts/metadata.rb
@@ -60,7 +60,7 @@ module Gitlab
begin
path = read_string(gz).force_encoding('UTF-8')
meta = read_string(gz).force_encoding('UTF-8')
-
+
next unless path.valid_encoding? && meta.valid_encoding?
next unless path =~ match_pattern
next if path =~ INVALID_PATH_PATTERN
diff --git a/lib/gitlab/diff/line_mapper.rb b/lib/gitlab/diff/line_mapper.rb
index 576a761423e..cf71d47df8e 100644
--- a/lib/gitlab/diff/line_mapper.rb
+++ b/lib/gitlab/diff/line_mapper.rb
@@ -38,7 +38,7 @@ module Gitlab
# - The first diff line with a higher line number, if it falls between diff contexts
# - The last known diff line, if it falls after the last diff context
diff_line = diff_lines.find do |diff_line|
- diff_from_line = diff_line.send(from)
+ diff_from_line = diff_line.public_send(from) # rubocop:disable GitlabSecurity/PublicSend
diff_from_line && diff_from_line >= from_line
end
diff_line ||= diff_lines.last
@@ -47,8 +47,8 @@ module Gitlab
# mapped line number is the same as the specified line number.
return from_line unless diff_line
- diff_from_line = diff_line.send(from)
- diff_to_line = diff_line.send(to)
+ diff_from_line = diff_line.public_send(from) # rubocop:disable GitlabSecurity/PublicSend
+ diff_to_line = diff_line.public_send(to) # rubocop:disable GitlabSecurity/PublicSend
# If the line was removed, there is no mapped line number.
return unless diff_to_line
diff --git a/lib/gitlab/git/blob.rb b/lib/gitlab/git/blob.rb
index 77b81d2d437..7780f4e4d4f 100644
--- a/lib/gitlab/git/blob.rb
+++ b/lib/gitlab/git/blob.rb
@@ -54,7 +54,7 @@ module Gitlab
# [[commit_sha, path], [commit_sha, path], ...]. If blob_size_limit < 0 then the
# full blob contents are returned. If blob_size_limit >= 0 then each blob will
# contain no more than limit bytes in its data attribute.
- #
+ #
# Keep in mind that this method may allocate a lot of memory. It is up
# to the caller to limit the number of blobs and blob_size_limit.
#
@@ -173,7 +173,7 @@ module Gitlab
def initialize(options)
%w(id name path size data mode commit_id binary).each do |key|
- self.send("#{key}=", options[key.to_sym])
+ self.__send__("#{key}=", options[key.to_sym]) # rubocop:disable GitlabSecurity/PublicSend
end
@loaded_all_data = false
diff --git a/lib/gitlab/git/commit.rb b/lib/gitlab/git/commit.rb
index fd4dfdb09a2..a499bbc6266 100644
--- a/lib/gitlab/git/commit.rb
+++ b/lib/gitlab/git/commit.rb
@@ -210,6 +210,16 @@ module Gitlab
@rugged_sort_types.fetch(sort_type, Rugged::SORT_NONE)
end
+
+ def shas_with_signatures(repository, shas)
+ shas.select do |sha|
+ begin
+ Rugged::Commit.extract_signature(repository.rugged, sha)
+ rescue Rugged::OdbError
+ false
+ end
+ end
+ end
end
def initialize(repository, raw_commit, head = nil)
@@ -335,15 +345,6 @@ module Gitlab
parent_ids.map { |oid| self.class.find(@repository, oid) }.compact
end
- # Get the gpg signature of this commit.
- #
- # Ex.
- # commit.signature(repo)
- #
- def signature(repo)
- Rugged::Commit.extract_signature(repo.rugged, sha)
- end
-
def stats
Gitlab::Git::CommitStats.new(self)
end
diff --git a/lib/gitlab/git/tree.rb b/lib/gitlab/git/tree.rb
index 8e959c57c7c..b54962a4456 100644
--- a/lib/gitlab/git/tree.rb
+++ b/lib/gitlab/git/tree.rb
@@ -89,7 +89,7 @@ module Gitlab
def initialize(options)
%w(id root_id name path type mode commit_id).each do |key|
- self.send("#{key}=", options[key.to_sym])
+ self.send("#{key}=", options[key.to_sym]) # rubocop:disable GitlabSecurity/PublicSend
end
end
diff --git a/lib/gitlab/gitaly_client.rb b/lib/gitlab/gitaly_client.rb
index 70177cd0fec..9a5f4f598b2 100644
--- a/lib/gitlab/gitaly_client.rb
+++ b/lib/gitlab/gitaly_client.rb
@@ -55,7 +55,7 @@ module Gitlab
def self.call(storage, service, rpc, request)
metadata = request_metadata(storage)
metadata = yield(metadata) if block_given?
- stub(service, storage).send(rpc, request, metadata)
+ stub(service, storage).__send__(rpc, request, metadata) # rubocop:disable GitlabSecurity/PublicSend
end
def self.request_metadata(storage)
diff --git a/lib/gitlab/gitaly_client/blob_service.rb b/lib/gitlab/gitaly_client/blob_service.rb
index 7ea8e8d0857..a250eb75bd4 100644
--- a/lib/gitlab/gitaly_client/blob_service.rb
+++ b/lib/gitlab/gitaly_client/blob_service.rb
@@ -13,10 +13,17 @@ module Gitlab
)
response = GitalyClient.call(@gitaly_repo.storage_name, :blob_service, :get_blob, request)
- blob = response.first
- return unless blob.oid.present?
+ data = ''
+ blob = nil
+ response.each do |msg|
+ if blob.nil?
+ blob = msg
+ end
- data = response.reduce(blob.data.dup) { |memo, msg| memo << msg.data.dup }
+ data << msg.data
+ end
+
+ return nil if blob.oid.blank?
Gitlab::Git::Blob.new(
id: blob.oid,
diff --git a/lib/gitlab/gitaly_client/commit_service.rb b/lib/gitlab/gitaly_client/commit_service.rb
index 93268d9f33c..b36e81278d6 100644
--- a/lib/gitlab/gitaly_client/commit_service.rb
+++ b/lib/gitlab/gitaly_client/commit_service.rb
@@ -60,15 +60,21 @@ module Gitlab
)
response = GitalyClient.call(@repository.storage, :commit_service, :tree_entry, request)
- entry = response.first
- return unless entry.oid.present?
- if entry.type == :BLOB
- rest_of_data = response.reduce("") { |memo, msg| memo << msg.data }
- entry.data += rest_of_data
+ entry = nil
+ data = ''
+ response.each do |msg|
+ if entry.nil?
+ entry = msg
+
+ break unless entry.type == :BLOB
+ end
+
+ data << msg.data
end
+ entry.data = data
- entry
+ entry unless entry.oid.blank?
end
def tree_entries(repository, revision, path)
diff --git a/lib/gitlab/github_import/base_formatter.rb b/lib/gitlab/github_import/base_formatter.rb
index 8c80791e7c9..f330041cc00 100644
--- a/lib/gitlab/github_import/base_formatter.rb
+++ b/lib/gitlab/github_import/base_formatter.rb
@@ -11,7 +11,9 @@ module Gitlab
end
def create!
- project.public_send(project_association).find_or_create_by!(find_condition) do |record|
+ association = project.public_send(project_association) # rubocop:disable GitlabSecurity/PublicSend
+
+ association.find_or_create_by!(find_condition) do |record|
record.attributes = attributes
end
end
diff --git a/lib/gitlab/github_import/client.rb b/lib/gitlab/github_import/client.rb
index 7dbeec5b010..0550f9695bd 100644
--- a/lib/gitlab/github_import/client.rb
+++ b/lib/gitlab/github_import/client.rb
@@ -120,7 +120,7 @@ module Gitlab
def request(method, *args, &block)
sleep rate_limit_sleep_time if rate_limit_exceed?
- data = api.send(method, *args)
+ data = api.__send__(method, *args) # rubocop:disable GitlabSecurity/PublicSend
return data unless data.is_a?(Array)
last_response = api.last_response
diff --git a/lib/gitlab/github_import/importer.rb b/lib/gitlab/github_import/importer.rb
index 266b1a6fece..373062b354b 100644
--- a/lib/gitlab/github_import/importer.rb
+++ b/lib/gitlab/github_import/importer.rb
@@ -289,7 +289,7 @@ module Gitlab
opts.last[:page] = current_page(resource_type)
- client.public_send(resource_type, *opts) do |resources|
+ client.public_send(resource_type, *opts) do |resources| # rubocop:disable GitlabSecurity/PublicSend
yield resources
increment_page(resource_type)
end
diff --git a/lib/gitlab/gpg/commit.rb b/lib/gitlab/gpg/commit.rb
index 55428b85207..606c7576f70 100644
--- a/lib/gitlab/gpg/commit.rb
+++ b/lib/gitlab/gpg/commit.rb
@@ -1,12 +1,20 @@
module Gitlab
module Gpg
class Commit
- attr_reader :commit
+ def self.for_commit(commit)
+ new(commit.project, commit.sha)
+ end
- def initialize(commit)
- @commit = commit
+ def initialize(project, sha)
+ @project = project
+ @sha = sha
- @signature_text, @signed_text = commit.raw.signature(commit.project.repository)
+ @signature_text, @signed_text =
+ begin
+ Rugged::Commit.extract_signature(project.repository.rugged, sha)
+ rescue Rugged::OdbError
+ nil
+ end
end
def has_signature?
@@ -16,18 +24,20 @@ module Gitlab
def signature
return unless has_signature?
- cached_signature = GpgSignature.find_by(commit_sha: commit.sha)
- return cached_signature if cached_signature.present?
+ return @signature if @signature
- using_keychain do |gpg_key|
- create_cached_signature!(gpg_key)
- end
+ cached_signature = GpgSignature.find_by(commit_sha: @sha)
+ return @signature = cached_signature if cached_signature.present?
+
+ @signature = create_cached_signature!
end
def update_signature!(cached_signature)
using_keychain do |gpg_key|
cached_signature.update_attributes!(attributes(gpg_key))
end
+
+ @signature = cached_signature
end
private
@@ -55,16 +65,18 @@ module Gitlab
end
end
- def create_cached_signature!(gpg_key)
- GpgSignature.create!(attributes(gpg_key))
+ def create_cached_signature!
+ using_keychain do |gpg_key|
+ GpgSignature.create!(attributes(gpg_key))
+ end
end
def attributes(gpg_key)
user_infos = user_infos(gpg_key)
{
- commit_sha: commit.sha,
- project: commit.project,
+ commit_sha: @sha,
+ project: @project,
gpg_key: gpg_key,
gpg_key_primary_keyid: gpg_key&.primary_keyid || verified_signature.fingerprint,
gpg_key_user_name: user_infos[:name],
diff --git a/lib/gitlab/gpg/invalid_gpg_signature_updater.rb b/lib/gitlab/gpg/invalid_gpg_signature_updater.rb
index 3bb491120ba..a525ee7a9ee 100644
--- a/lib/gitlab/gpg/invalid_gpg_signature_updater.rb
+++ b/lib/gitlab/gpg/invalid_gpg_signature_updater.rb
@@ -10,9 +10,7 @@ module Gitlab
.select(:id, :commit_sha, :project_id)
.where('gpg_key_id IS NULL OR valid_signature = ?', false)
.where(gpg_key_primary_keyid: @gpg_key.primary_keyid)
- .find_each do |gpg_signature|
- Gitlab::Gpg::Commit.new(gpg_signature.commit).update_signature!(gpg_signature)
- end
+ .find_each { |sig| sig.gpg_commit.update_signature!(sig) }
end
end
end
diff --git a/lib/gitlab/import_export/attributes_finder.rb b/lib/gitlab/import_export/attributes_finder.rb
index d230de781d5..56042ddecbf 100644
--- a/lib/gitlab/import_export/attributes_finder.rb
+++ b/lib/gitlab/import_export/attributes_finder.rb
@@ -1,7 +1,6 @@
module Gitlab
module ImportExport
class AttributesFinder
-
def initialize(included_attributes:, excluded_attributes:, methods:)
@included_attributes = included_attributes || {}
@excluded_attributes = excluded_attributes || {}
diff --git a/lib/gitlab/import_export/project_tree_restorer.rb b/lib/gitlab/import_export/project_tree_restorer.rb
index 84ab1977dfa..cbc8d170936 100644
--- a/lib/gitlab/import_export/project_tree_restorer.rb
+++ b/lib/gitlab/import_export/project_tree_restorer.rb
@@ -1,6 +1,9 @@
module Gitlab
module ImportExport
class ProjectTreeRestorer
+ # Relations which cannot have both group_id and project_id at the same time
+ RESTRICT_PROJECT_AND_GROUP = %i(milestones).freeze
+
def initialize(user:, shared:, project:)
@path = File.join(shared.export_path, 'project.json')
@user = user
@@ -118,9 +121,11 @@ module Gitlab
end
def create_relation(relation, relation_hash_list)
+ relation_type = relation.to_sym
+
relation_array = [relation_hash_list].flatten.map do |relation_hash|
- Gitlab::ImportExport::RelationFactory.create(relation_sym: relation.to_sym,
- relation_hash: parsed_relation_hash(relation_hash),
+ Gitlab::ImportExport::RelationFactory.create(relation_sym: relation_type,
+ relation_hash: parsed_relation_hash(relation_hash, relation_type),
members_mapper: members_mapper,
user: @user,
project: restored_project)
@@ -129,8 +134,16 @@ module Gitlab
relation_hash_list.is_a?(Array) ? relation_array : relation_array.first
end
- def parsed_relation_hash(relation_hash)
- relation_hash.merge!('group_id' => restored_project.group.try(:id), 'project_id' => restored_project.id)
+ def parsed_relation_hash(relation_hash, relation_type)
+ if RESTRICT_PROJECT_AND_GROUP.include?(relation_type)
+ params = {}
+ params['group_id'] = restored_project.group.try(:id) if relation_hash['group_id']
+ params['project_id'] = restored_project.id if relation_hash['project_id']
+ else
+ params = { 'group_id' => restored_project.group.try(:id), 'project_id' => restored_project.id }
+ end
+
+ relation_hash.merge(params)
end
end
end
diff --git a/lib/gitlab/lazy.rb b/lib/gitlab/lazy.rb
index 2a659ae4c74..99594577141 100644
--- a/lib/gitlab/lazy.rb
+++ b/lib/gitlab/lazy.rb
@@ -16,7 +16,7 @@ module Gitlab
def method_missing(name, *args, &block)
__evaluate__
- @result.__send__(name, *args, &block)
+ @result.__send__(name, *args, &block) # rubocop:disable GitlabSecurity/PublicSend
end
def respond_to_missing?(name, include_private = false)
diff --git a/lib/gitlab/ldap/auth_hash.rb b/lib/gitlab/ldap/auth_hash.rb
index 95378e5a769..4fbc5fa5262 100644
--- a/lib/gitlab/ldap/auth_hash.rb
+++ b/lib/gitlab/ldap/auth_hash.rb
@@ -17,7 +17,7 @@ module Gitlab
value = value.first if value
break if value.present?
end
-
+
return super unless value
Gitlab::Utils.force_utf8(value)
diff --git a/lib/gitlab/ldap/person.rb b/lib/gitlab/ldap/person.rb
index 43eb73250b7..e138b466a34 100644
--- a/lib/gitlab/ldap/person.rb
+++ b/lib/gitlab/ldap/person.rb
@@ -32,7 +32,7 @@ module Gitlab
end
def uid
- entry.send(config.uid).first
+ entry.public_send(config.uid).first # rubocop:disable GitlabSecurity/PublicSend
end
def username
@@ -65,7 +65,7 @@ module Gitlab
return nil unless selected_attr
- entry.public_send(selected_attr)
+ entry.public_send(selected_attr) # rubocop:disable GitlabSecurity/PublicSend
end
end
end
diff --git a/lib/gitlab/markdown/pipeline.rb b/lib/gitlab/markdown/pipeline.rb
index 699d8b9fc07..306923902e0 100644
--- a/lib/gitlab/markdown/pipeline.rb
+++ b/lib/gitlab/markdown/pipeline.rb
@@ -23,7 +23,7 @@ module Gitlab
define_method(meth) do |text, context|
context = transform_context(context)
- html_pipeline.send(meth, text, context)
+ html_pipeline.__send__(meth, text, context) # rubocop:disable GitlabSecurity/PublicSend
end
end
end
diff --git a/lib/gitlab/middleware/rails_queue_duration.rb b/lib/gitlab/middleware/rails_queue_duration.rb
index 5d2d7d0026c..63c3372da51 100644
--- a/lib/gitlab/middleware/rails_queue_duration.rb
+++ b/lib/gitlab/middleware/rails_queue_duration.rb
@@ -8,7 +8,7 @@ module Gitlab
def initialize(app)
@app = app
end
-
+
def call(env)
trans = Gitlab::Metrics.current_transaction
proxy_start = env['HTTP_GITLAB_WORKHORSE_PROXY_START'].presence
diff --git a/lib/gitlab/sidekiq_status.rb b/lib/gitlab/sidekiq_status.rb
index ca8d3271541..a0a2769cf9e 100644
--- a/lib/gitlab/sidekiq_status.rb
+++ b/lib/gitlab/sidekiq_status.rb
@@ -90,9 +90,14 @@ module Gitlab
#
# Returns an array of completed JIDs
def self.completed_jids(job_ids)
- Sidekiq.redis do |redis|
- job_ids.reject { |jid| redis.exists(key_for(jid)) }
+ statuses = job_status(job_ids)
+
+ completed = []
+ job_ids.zip(statuses).each do |job_id, status|
+ completed << job_id unless status
end
+
+ completed
end
def self.key_for(jid)
diff --git a/lib/gitlab/slash_commands/presenters/help.rb b/lib/gitlab/slash_commands/presenters/help.rb
index ea611a4d629..ab855319077 100644
--- a/lib/gitlab/slash_commands/presenters/help.rb
+++ b/lib/gitlab/slash_commands/presenters/help.rb
@@ -14,7 +14,7 @@ module Gitlab
if text.start_with?('help')
header_with_list("Available commands", full_commands(trigger))
else
- header_with_list("Unknown command, these commands are available", full_commands(trigger))
+ header_with_list("Unknown command, these commands are available", full_commands(trigger))
end
end
diff --git a/lib/tasks/gitlab/gitaly.rake b/lib/tasks/gitlab/gitaly.rake
index e337c67a0f5..08677a98fc1 100644
--- a/lib/tasks/gitlab/gitaly.rake
+++ b/lib/tasks/gitlab/gitaly.rake
@@ -18,7 +18,7 @@ namespace :gitlab do
command = status.zero? ? ['gmake'] : ['make']
if Rails.env.test?
- command += %W[BUNDLE_PATH=#{Bundler.bundle_path}]
+ command += %W[BUNDLE_PATH=#{Bundler.bundle_path}]
end
Dir.chdir(args.dir) do
diff --git a/lib/uploaded_file.rb b/lib/uploaded_file.rb
index 41dee5fdc06..4a3c40f88eb 100644
--- a/lib/uploaded_file.rb
+++ b/lib/uploaded_file.rb
@@ -27,7 +27,7 @@ class UploadedFile
alias_method :local_path, :path
def method_missing(method_name, *args, &block) #:nodoc:
- @tempfile.__send__(method_name, *args, &block)
+ @tempfile.__send__(method_name, *args, &block) # rubocop:disable GitlabSecurity/PublicSend
end
def respond_to?(method_name, include_private = false) #:nodoc:
diff --git a/qa/qa/runtime/release.rb b/qa/qa/runtime/release.rb
index 4f83a773645..12e56404cf6 100644
--- a/qa/qa/runtime/release.rb
+++ b/qa/qa/runtime/release.rb
@@ -21,7 +21,7 @@ module QA
end
def self.method_missing(name, *args)
- self.new.strategy.public_send(name, *args)
+ self.new.strategy.public_send(name, *args) # rubocop:disable GitlabSecurity/PublicSend
end
end
end
diff --git a/spec/features/admin/admin_settings_spec.rb b/spec/features/admin/admin_settings_spec.rb
index 5db42175c15..dbb0ae9c86e 100644
--- a/spec/features/admin/admin_settings_spec.rb
+++ b/spec/features/admin/admin_settings_spec.rb
@@ -74,7 +74,7 @@ feature 'Admin updates settings' do
context 'sign-in restrictions', :js do
it 'de-activates oauth sign-in source' do
find('.btn', text: 'GitLab.com').click
-
+
expect(find('.btn', text: 'GitLab.com')).not_to have_css('.active')
end
end
diff --git a/spec/features/groups/milestone_spec.rb b/spec/features/groups/milestone_spec.rb
index 32b3e13c624..56144d17d4f 100644
--- a/spec/features/groups/milestone_spec.rb
+++ b/spec/features/groups/milestone_spec.rb
@@ -35,12 +35,12 @@ feature 'Group milestones', :js do
context 'milestones list' do
let!(:other_project) { create(:project_empty_repo, group: group) }
- let!(:active_group_milestone) { create(:milestone, group: group, state: 'active') }
let!(:active_project_milestone1) { create(:milestone, project: project, state: 'active', title: 'v1.0') }
let!(:active_project_milestone2) { create(:milestone, project: other_project, state: 'active', title: 'v1.0') }
- let!(:closed_group_milestone) { create(:milestone, group: group, state: 'closed') }
let!(:closed_project_milestone1) { create(:milestone, project: project, state: 'closed', title: 'v2.0') }
let!(:closed_project_milestone2) { create(:milestone, project: other_project, state: 'closed', title: 'v2.0') }
+ let!(:active_group_milestone) { create(:milestone, group: group, state: 'active') }
+ let!(:closed_group_milestone) { create(:milestone, group: group, state: 'closed') }
before do
visit group_milestones_path(group)
@@ -58,5 +58,30 @@ feature 'Group milestones', :js do
expect(page).to have_selector("#milestone_#{active_group_milestone.id}", count: 1)
expect(page).to have_selector("#milestone_#{legacy_milestone.milestones.first.id}", count: 1)
end
+
+ it 'updates milestone' do
+ page.within(".milestones #milestone_#{active_group_milestone.id}") do
+ click_link('Edit')
+ end
+
+ page.within('.milestone-form') do
+ fill_in 'milestone_title', with: 'new title'
+ click_button('Update milestone')
+ end
+
+ expect(find('#content-body h2')).to have_content('new title')
+ end
+
+ it 'shows milestone detail and supports its edit' do
+ page.within(".milestones #milestone_#{active_group_milestone.id}") do
+ click_link(active_group_milestone.title)
+ end
+
+ page.within('.detail-page-header') do
+ click_link('Edit')
+ end
+
+ expect(page).to have_selector('.milestone-form')
+ end
end
end
diff --git a/spec/features/help_pages_spec.rb b/spec/features/help_pages_spec.rb
index bd4f233cba9..93be3b066ee 100644
--- a/spec/features/help_pages_spec.rb
+++ b/spec/features/help_pages_spec.rb
@@ -50,7 +50,7 @@ describe 'Help Pages' do
it 'hides the version check image if the image request fails' do
# We use '--load-images=yes' with poltergeist so the image fails to load
- expect(find('.js-version-status-badge', visible: false)).not_to be_visible
+ expect(page).to have_selector('.js-version-status-badge', visible: false)
end
end
diff --git a/spec/features/issues/filtered_search/dropdown_assignee_spec.rb b/spec/features/issues/filtered_search/dropdown_assignee_spec.rb
index a69bd8a09b7..2cc027aac9e 100644
--- a/spec/features/issues/filtered_search/dropdown_assignee_spec.rb
+++ b/spec/features/issues/filtered_search/dropdown_assignee_spec.rb
@@ -134,8 +134,10 @@ describe 'Dropdown assignee', :js do
it 'fills in the assignee username when the assignee has not been filtered' do
click_assignee(user_jacob.name)
+ wait_for_requests
+
expect(page).to have_css(js_dropdown_assignee, visible: false)
- expect_tokens([{ name: 'assignee', value: "@#{user_jacob.username}" }])
+ expect_tokens([assignee_token(user_jacob.name)])
expect_filtered_search_input_empty
end
@@ -143,8 +145,10 @@ describe 'Dropdown assignee', :js do
filtered_search.send_keys('roo')
click_assignee(user.name)
+ wait_for_requests
+
expect(page).to have_css(js_dropdown_assignee, visible: false)
- expect_tokens([{ name: 'assignee', value: "@#{user.username}" }])
+ expect_tokens([assignee_token(user.name)])
expect_filtered_search_input_empty
end
@@ -152,7 +156,7 @@ describe 'Dropdown assignee', :js do
find('#js-dropdown-assignee .filter-dropdown-item', text: 'No Assignee').click
expect(page).to have_css(js_dropdown_assignee, visible: false)
- expect_tokens([{ name: 'assignee', value: 'none' }])
+ expect_tokens([assignee_token('none')])
expect_filtered_search_input_empty
end
end
@@ -171,7 +175,7 @@ describe 'Dropdown assignee', :js do
find('#js-dropdown-assignee .filter-dropdown-item', text: user.username).click
expect(page).to have_css(js_dropdown_assignee, visible: false)
- expect_tokens([{ name: 'assignee', value: user.username }])
+ expect_tokens([assignee_token(user.username)])
expect_filtered_search_input_empty
end
end
diff --git a/spec/features/issues/filtered_search/dropdown_author_spec.rb b/spec/features/issues/filtered_search/dropdown_author_spec.rb
index 4bbf18e1dbe..975dc035f2d 100644
--- a/spec/features/issues/filtered_search/dropdown_author_spec.rb
+++ b/spec/features/issues/filtered_search/dropdown_author_spec.rb
@@ -121,16 +121,20 @@ describe 'Dropdown author', js: true do
it 'fills in the author username when the author has not been filtered' do
click_author(user_jacob.name)
+ wait_for_requests
+
expect(page).to have_css(js_dropdown_author, visible: false)
- expect_tokens([{ name: 'author', value: "@#{user_jacob.username}" }])
+ expect_tokens([author_token(user_jacob.name)])
expect_filtered_search_input_empty
end
it 'fills in the author username when the author has been filtered' do
click_author(user.name)
+ wait_for_requests
+
expect(page).to have_css(js_dropdown_author, visible: false)
- expect_tokens([{ name: 'author', value: "@#{user.username}" }])
+ expect_tokens([author_token(user.name)])
expect_filtered_search_input_empty
end
end
@@ -149,7 +153,7 @@ describe 'Dropdown author', js: true do
find('#js-dropdown-author .filter-dropdown-item', text: user.username).click
expect(page).to have_css(js_dropdown_author, visible: false)
- expect_tokens([{ name: 'author', value: user.username }])
+ expect_tokens([author_token(user.username)])
expect_filtered_search_input_empty
end
end
diff --git a/spec/features/issues/filtered_search/dropdown_label_spec.rb b/spec/features/issues/filtered_search/dropdown_label_spec.rb
index 67eb0ef0119..e84b07ec2ef 100644
--- a/spec/features/issues/filtered_search/dropdown_label_spec.rb
+++ b/spec/features/issues/filtered_search/dropdown_label_spec.rb
@@ -47,7 +47,7 @@ describe 'Dropdown label', js: true do
filtered_search.native.send_keys(:down, :down, :enter)
- expect_tokens([{ name: 'label', value: "~#{bug_label.title}" }])
+ expect_tokens([label_token(bug_label.title)])
expect_filtered_search_input_empty
end
end
@@ -178,7 +178,7 @@ describe 'Dropdown label', js: true do
click_label(bug_label.title)
expect(page).not_to have_css(js_dropdown_label)
- expect_tokens([{ name: 'label', value: "~#{bug_label.title}" }])
+ expect_tokens([label_token(bug_label.title)])
expect_filtered_search_input_empty
end
@@ -187,7 +187,7 @@ describe 'Dropdown label', js: true do
click_label(bug_label.title)
expect(page).not_to have_css(js_dropdown_label)
- expect_tokens([{ name: 'label', value: "~#{bug_label.title}" }])
+ expect_tokens([label_token(bug_label.title)])
expect_filtered_search_input_empty
end
@@ -195,7 +195,7 @@ describe 'Dropdown label', js: true do
click_label(two_words_label.title)
expect(page).not_to have_css(js_dropdown_label)
- expect_tokens([{ name: 'label', value: "\"#{two_words_label.title}\"" }])
+ expect_tokens([label_token("\"#{two_words_label.title}\"")])
expect_filtered_search_input_empty
end
@@ -203,7 +203,7 @@ describe 'Dropdown label', js: true do
click_label(long_label.title)
expect(page).not_to have_css(js_dropdown_label)
- expect_tokens([{ name: 'label', value: "\"#{long_label.title}\"" }])
+ expect_tokens([label_token("\"#{long_label.title}\"")])
expect_filtered_search_input_empty
end
@@ -211,7 +211,7 @@ describe 'Dropdown label', js: true do
click_label(wont_fix_label.title)
expect(page).not_to have_css(js_dropdown_label)
- expect_tokens([{ name: 'label', value: "~'#{wont_fix_label.title}'" }])
+ expect_tokens([label_token("'#{wont_fix_label.title}'")])
expect_filtered_search_input_empty
end
@@ -219,7 +219,7 @@ describe 'Dropdown label', js: true do
click_label(uppercase_label.title)
expect(page).not_to have_css(js_dropdown_label)
- expect_tokens([{ name: 'label', value: "~#{uppercase_label.title}" }])
+ expect_tokens([label_token(uppercase_label.title)])
expect_filtered_search_input_empty
end
@@ -227,7 +227,7 @@ describe 'Dropdown label', js: true do
click_label(special_label.title)
expect(page).not_to have_css(js_dropdown_label)
- expect_tokens([{ name: 'label', value: "~#{special_label.title}" }])
+ expect_tokens([label_token(special_label.title)])
expect_filtered_search_input_empty
end
@@ -235,7 +235,7 @@ describe 'Dropdown label', js: true do
find("#{js_dropdown_label} .filter-dropdown-item", text: 'No Label').click
expect(page).not_to have_css(js_dropdown_label)
- expect_tokens([{ name: 'label', value: 'none' }])
+ expect_tokens([label_token('none', false)])
expect_filtered_search_input_empty
end
end
diff --git a/spec/features/issues/filtered_search/dropdown_milestone_spec.rb b/spec/features/issues/filtered_search/dropdown_milestone_spec.rb
index 456eb05f241..5f99921ae2e 100644
--- a/spec/features/issues/filtered_search/dropdown_milestone_spec.rb
+++ b/spec/features/issues/filtered_search/dropdown_milestone_spec.rb
@@ -134,7 +134,7 @@ describe 'Dropdown milestone', :js do
click_milestone(milestone.title)
expect(page).to have_css(js_dropdown_milestone, visible: false)
- expect_tokens([{ name: 'milestone', value: "%#{milestone.title}" }])
+ expect_tokens([milestone_token(milestone.title)])
expect_filtered_search_input_empty
end
@@ -143,7 +143,7 @@ describe 'Dropdown milestone', :js do
click_milestone(milestone.title)
expect(page).to have_css(js_dropdown_milestone, visible: false)
- expect_tokens([{ name: 'milestone', value: "%#{milestone.title}" }])
+ expect_tokens([milestone_token(milestone.title)])
expect_filtered_search_input_empty
end
@@ -151,7 +151,7 @@ describe 'Dropdown milestone', :js do
click_milestone(two_words_milestone.title)
expect(page).to have_css(js_dropdown_milestone, visible: false)
- expect_tokens([{ name: 'milestone', value: "%\"#{two_words_milestone.title}\"" }])
+ expect_tokens([milestone_token("\"#{two_words_milestone.title}\"")])
expect_filtered_search_input_empty
end
@@ -159,7 +159,7 @@ describe 'Dropdown milestone', :js do
click_milestone(long_milestone.title)
expect(page).to have_css(js_dropdown_milestone, visible: false)
- expect_tokens([{ name: 'milestone', value: "%\"#{long_milestone.title}\"" }])
+ expect_tokens([milestone_token("\"#{long_milestone.title}\"")])
expect_filtered_search_input_empty
end
@@ -167,7 +167,7 @@ describe 'Dropdown milestone', :js do
click_milestone(wont_fix_milestone.title)
expect(page).to have_css(js_dropdown_milestone, visible: false)
- expect_tokens([{ name: 'milestone', value: "%'#{wont_fix_milestone.title}'" }])
+ expect_tokens([milestone_token("'#{wont_fix_milestone.title}'")])
expect_filtered_search_input_empty
end
@@ -175,7 +175,7 @@ describe 'Dropdown milestone', :js do
click_milestone(uppercase_milestone.title)
expect(page).to have_css(js_dropdown_milestone, visible: false)
- expect_tokens([{ name: 'milestone', value: "%#{uppercase_milestone.title}" }])
+ expect_tokens([milestone_token(uppercase_milestone.title)])
expect_filtered_search_input_empty
end
@@ -183,7 +183,7 @@ describe 'Dropdown milestone', :js do
click_milestone(special_milestone.title)
expect(page).to have_css(js_dropdown_milestone, visible: false)
- expect_tokens([{ name: 'milestone', value: "%#{special_milestone.title}" }])
+ expect_tokens([milestone_token(special_milestone.title)])
expect_filtered_search_input_empty
end
@@ -191,7 +191,7 @@ describe 'Dropdown milestone', :js do
click_static_milestone('No Milestone')
expect(page).to have_css(js_dropdown_milestone, visible: false)
- expect_tokens([{ name: 'milestone', value: 'none' }])
+ expect_tokens([milestone_token('none', false)])
expect_filtered_search_input_empty
end
@@ -199,7 +199,7 @@ describe 'Dropdown milestone', :js do
click_static_milestone('Upcoming')
expect(page).to have_css(js_dropdown_milestone, visible: false)
- expect_tokens([{ name: 'milestone', value: 'upcoming' }])
+ expect_tokens([milestone_token('upcoming', false)])
expect_filtered_search_input_empty
end
@@ -207,7 +207,7 @@ describe 'Dropdown milestone', :js do
click_static_milestone('Started')
expect(page).to have_css(js_dropdown_milestone, visible: false)
- expect_tokens([{ name: 'milestone', value: 'started' }])
+ expect_tokens([milestone_token('started', false)])
expect_filtered_search_input_empty
end
end
diff --git a/spec/features/issues/filtered_search/filter_issues_spec.rb b/spec/features/issues/filtered_search/filter_issues_spec.rb
index cd2cbf4bfe7..2070043d842 100644
--- a/spec/features/issues/filtered_search/filter_issues_spec.rb
+++ b/spec/features/issues/filtered_search/filter_issues_spec.rb
@@ -97,7 +97,9 @@ describe 'Filter issues', js: true do
it 'filters issues by searched author' do
input_filtered_search("author:@#{user.username}")
- expect_tokens([{ name: 'author', value: user.username }])
+ wait_for_requests
+
+ expect_tokens([author_token(user.name)])
expect_issues_list_count(5)
expect_filtered_search_input_empty
end
@@ -117,7 +119,9 @@ describe 'Filter issues', js: true do
it 'filters issues by searched author and text' do
input_filtered_search("author:@#{user.username} #{search_term}")
- expect_tokens([{ name: 'author', value: user.username }])
+ wait_for_requests
+
+ expect_tokens([author_token(user.name)])
expect_issues_list_count(3)
expect_filtered_search_input(search_term)
end
@@ -125,10 +129,9 @@ describe 'Filter issues', js: true do
it 'filters issues by searched author, assignee and text' do
input_filtered_search("author:@#{user.username} assignee:@#{user.username} #{search_term}")
- expect_tokens([
- { name: 'author', value: user.username },
- { name: 'assignee', value: user.username }
- ])
+ wait_for_requests
+
+ expect_tokens([author_token(user.name), assignee_token(user.name)])
expect_issues_list_count(3)
expect_filtered_search_input(search_term)
end
@@ -136,10 +139,12 @@ describe 'Filter issues', js: true do
it 'filters issues by searched author, assignee, label, and text' do
input_filtered_search("author:@#{user.username} assignee:@#{user.username} label:~#{caps_sensitive_label.title} #{search_term}")
+ wait_for_requests
+
expect_tokens([
- { name: 'author', value: user.username },
- { name: 'assignee', value: user.username },
- { name: 'label', value: caps_sensitive_label.title }
+ author_token(user.name),
+ assignee_token(user.name),
+ label_token(caps_sensitive_label.title)
])
expect_issues_list_count(1)
expect_filtered_search_input(search_term)
@@ -148,11 +153,13 @@ describe 'Filter issues', js: true do
it 'filters issues by searched author, assignee, label, milestone and text' do
input_filtered_search("author:@#{user.username} assignee:@#{user.username} label:~#{caps_sensitive_label.title} milestone:%#{milestone.title} #{search_term}")
+ wait_for_requests
+
expect_tokens([
- { name: 'author', value: user.username },
- { name: 'assignee', value: user.username },
- { name: 'label', value: caps_sensitive_label.title },
- { name: 'milestone', value: milestone.title }
+ author_token(user.name),
+ assignee_token(user.name),
+ label_token(caps_sensitive_label.title),
+ milestone_token(milestone.title)
])
expect_issues_list_count(1)
expect_filtered_search_input(search_term)
@@ -169,7 +176,9 @@ describe 'Filter issues', js: true do
it 'filters issues by searched assignee' do
input_filtered_search("assignee:@#{user.username}")
- expect_tokens([{ name: 'assignee', value: user.username }])
+ wait_for_requests
+
+ expect_tokens([assignee_token(user.name)])
expect_issues_list_count(5)
expect_filtered_search_input_empty
end
@@ -177,7 +186,7 @@ describe 'Filter issues', js: true do
it 'filters issues by no assignee' do
input_filtered_search('assignee:none')
- expect_tokens([{ name: 'assignee', value: 'none' }])
+ expect_tokens([assignee_token('none')])
expect_issues_list_count(8, 1)
expect_filtered_search_input_empty
end
@@ -197,7 +206,9 @@ describe 'Filter issues', js: true do
it 'filters issues by searched assignee and text' do
input_filtered_search("assignee:@#{user.username} #{search_term}")
- expect_tokens([{ name: 'assignee', value: user.username }])
+ wait_for_requests
+
+ expect_tokens([assignee_token(user.name)])
expect_issues_list_count(2)
expect_filtered_search_input(search_term)
end
@@ -205,10 +216,9 @@ describe 'Filter issues', js: true do
it 'filters issues by searched assignee, author and text' do
input_filtered_search("assignee:@#{user.username} author:@#{user.username} #{search_term}")
- expect_tokens([
- { name: 'assignee', value: user.username },
- { name: 'author', value: user.username }
- ])
+ wait_for_requests
+
+ expect_tokens([assignee_token(user.name), author_token(user.name)])
expect_issues_list_count(2)
expect_filtered_search_input(search_term)
end
@@ -216,10 +226,12 @@ describe 'Filter issues', js: true do
it 'filters issues by searched assignee, author, label, text' do
input_filtered_search("assignee:@#{user.username} author:@#{user.username} label:~#{caps_sensitive_label.title} #{search_term}")
+ wait_for_requests
+
expect_tokens([
- { name: 'assignee', value: user.username },
- { name: 'author', value: user.username },
- { name: 'label', value: caps_sensitive_label.title }
+ assignee_token(user.name),
+ author_token(user.name),
+ label_token(caps_sensitive_label.title)
])
expect_issues_list_count(1)
expect_filtered_search_input(search_term)
@@ -229,10 +241,10 @@ describe 'Filter issues', js: true do
input_filtered_search("assignee:@#{user.username} author:@#{user.username} label:~#{caps_sensitive_label.title} milestone:%#{milestone.title} #{search_term}")
expect_tokens([
- { name: 'assignee', value: user.username },
- { name: 'author', value: user.username },
- { name: 'label', value: caps_sensitive_label.title },
- { name: 'milestone', value: milestone.title }
+ assignee_token(user.name),
+ author_token(user.name),
+ label_token(caps_sensitive_label.title),
+ milestone_token(milestone.title)
])
expect_issues_list_count(1)
expect_filtered_search_input(search_term)
@@ -253,7 +265,7 @@ describe 'Filter issues', js: true do
it 'filters issues by searched label' do
input_filtered_search("label:~#{bug_label.title}")
- expect_tokens([{ name: 'label', value: bug_label.title }])
+ expect_tokens([label_token(bug_label.title)])
expect_issues_list_count(2)
expect_filtered_search_input_empty
end
@@ -261,7 +273,7 @@ describe 'Filter issues', js: true do
it 'filters issues by no label' do
input_filtered_search('label:none')
- expect_tokens([{ name: 'label', value: 'none' }])
+ expect_tokens([label_token('none', false)])
expect_issues_list_count(9, 1)
expect_filtered_search_input_empty
end
@@ -274,8 +286,8 @@ describe 'Filter issues', js: true do
input_filtered_search("label:~#{bug_label.title} label:~#{caps_sensitive_label.title}")
expect_tokens([
- { name: 'label', value: bug_label.title },
- { name: 'label', value: caps_sensitive_label.title }
+ label_token(bug_label.title),
+ label_token(caps_sensitive_label.title)
])
expect_issues_list_count(1)
expect_filtered_search_input_empty
@@ -287,7 +299,8 @@ describe 'Filter issues', js: true do
special_issue.labels << special_label
input_filtered_search("label:~#{special_label.title}")
- expect_tokens([{ name: 'label', value: special_label.title }])
+
+ expect_tokens([label_token(special_label.title)])
expect_issues_list_count(1)
expect_filtered_search_input_empty
end
@@ -297,7 +310,7 @@ describe 'Filter issues', js: true do
input_filtered_search("label:~#{new_label.title}")
- expect_tokens([{ name: 'label', value: new_label.title }])
+ expect_tokens([label_token(new_label.title)])
expect_no_issues_list()
expect_filtered_search_input_empty
end
@@ -311,25 +324,27 @@ describe 'Filter issues', js: true do
input_filtered_search("label:~'#{special_multiple_label.title}'")
- # filtered search defaults quotations to double quotes
- expect_tokens([{ name: 'label', value: "\"#{special_multiple_label.title}\"" }])
+ # Check for search results (which makes sure that the page has changed)
expect_issues_list_count(1)
+ # filtered search defaults quotations to double quotes
+ expect_tokens([label_token("\"#{special_multiple_label.title}\"")])
+
expect_filtered_search_input_empty
end
it 'single quotes' do
input_filtered_search("label:~'#{multiple_words_label.title}'")
- expect_tokens([{ name: 'label', value: "\"#{multiple_words_label.title}\"" }])
expect_issues_list_count(1)
+ expect_tokens([label_token("\"#{multiple_words_label.title}\"")])
expect_filtered_search_input_empty
end
it 'double quotes' do
input_filtered_search("label:~\"#{multiple_words_label.title}\"")
- expect_tokens([{ name: 'label', value: "\"#{multiple_words_label.title}\"" }])
+ expect_tokens([label_token("\"#{multiple_words_label.title}\"")])
expect_issues_list_count(1)
expect_filtered_search_input_empty
end
@@ -341,7 +356,7 @@ describe 'Filter issues', js: true do
input_filtered_search("label:~'#{double_quotes_label.title}'")
- expect_tokens([{ name: 'label', value: "'#{double_quotes_label.title}'" }])
+ expect_tokens([label_token("'#{double_quotes_label.title}'")])
expect_issues_list_count(1)
expect_filtered_search_input_empty
end
@@ -353,7 +368,7 @@ describe 'Filter issues', js: true do
input_filtered_search("label:~\"#{single_quotes_label.title}\"")
- expect_tokens([{ name: 'label', value: "\"#{single_quotes_label.title}\"" }])
+ expect_tokens([label_token("\"#{single_quotes_label.title}\"")])
expect_issues_list_count(1)
expect_filtered_search_input_empty
end
@@ -363,7 +378,7 @@ describe 'Filter issues', js: true do
it 'filters issues by searched label and text' do
input_filtered_search("label:~#{caps_sensitive_label.title} #{search_term}")
- expect_tokens([{ name: 'label', value: caps_sensitive_label.title }])
+ expect_tokens([label_token(caps_sensitive_label.title)])
expect_issues_list_count(1)
expect_filtered_search_input(search_term)
end
@@ -371,10 +386,9 @@ describe 'Filter issues', js: true do
it 'filters issues by searched label, author and text' do
input_filtered_search("label:~#{caps_sensitive_label.title} author:@#{user.username} #{search_term}")
- expect_tokens([
- { name: 'label', value: caps_sensitive_label.title },
- { name: 'author', value: user.username }
- ])
+ wait_for_requests
+
+ expect_tokens([label_token(caps_sensitive_label.title), author_token(user.name)])
expect_issues_list_count(1)
expect_filtered_search_input(search_term)
end
@@ -382,10 +396,12 @@ describe 'Filter issues', js: true do
it 'filters issues by searched label, author, assignee and text' do
input_filtered_search("label:~#{caps_sensitive_label.title} author:@#{user.username} assignee:@#{user.username} #{search_term}")
+ wait_for_requests
+
expect_tokens([
- { name: 'label', value: caps_sensitive_label.title },
- { name: 'author', value: user.username },
- { name: 'assignee', value: user.username }
+ label_token(caps_sensitive_label.title),
+ author_token(user.name),
+ assignee_token(user.name)
])
expect_issues_list_count(1)
expect_filtered_search_input(search_term)
@@ -395,10 +411,10 @@ describe 'Filter issues', js: true do
input_filtered_search("label:~#{caps_sensitive_label.title} author:@#{user.username} assignee:@#{user.username} milestone:%#{milestone.title} #{search_term}")
expect_tokens([
- { name: 'label', value: caps_sensitive_label.title },
- { name: 'author', value: user.username },
- { name: 'assignee', value: user.username },
- { name: 'milestone', value: milestone.title }
+ label_token(caps_sensitive_label.title),
+ author_token(user.name),
+ assignee_token(user.name),
+ milestone_token(milestone.title)
])
expect_issues_list_count(1)
expect_filtered_search_input(search_term)
@@ -410,8 +426,8 @@ describe 'Filter issues', js: true do
input_filtered_search("label:~#{bug_label.title} label:~#{caps_sensitive_label.title} #{search_term}")
expect_tokens([
- { name: 'label', value: bug_label.title },
- { name: 'label', value: caps_sensitive_label.title }
+ label_token(bug_label.title),
+ label_token(caps_sensitive_label.title)
])
expect_issues_list_count(1)
expect_filtered_search_input(search_term)
@@ -420,10 +436,12 @@ describe 'Filter issues', js: true do
it 'filters issues by searched label, label2, author and text' do
input_filtered_search("label:~#{bug_label.title} label:~#{caps_sensitive_label.title} author:@#{user.username} #{search_term}")
+ wait_for_requests
+
expect_tokens([
- { name: 'label', value: bug_label.title },
- { name: 'label', value: caps_sensitive_label.title },
- { name: 'author', value: user.username }
+ label_token(bug_label.title),
+ label_token(caps_sensitive_label.title),
+ author_token(user.name)
])
expect_issues_list_count(1)
expect_filtered_search_input(search_term)
@@ -432,11 +450,13 @@ describe 'Filter issues', js: true do
it 'filters issues by searched label, label2, author, assignee and text' do
input_filtered_search("label:~#{bug_label.title} label:~#{caps_sensitive_label.title} author:@#{user.username} assignee:@#{user.username} #{search_term}")
+ wait_for_requests
+
expect_tokens([
- { name: 'label', value: bug_label.title },
- { name: 'label', value: caps_sensitive_label.title },
- { name: 'author', value: user.username },
- { name: 'assignee', value: user.username }
+ label_token(bug_label.title),
+ label_token(caps_sensitive_label.title),
+ author_token(user.name),
+ assignee_token(user.name)
])
expect_issues_list_count(1)
expect_filtered_search_input(search_term)
@@ -445,12 +465,14 @@ describe 'Filter issues', js: true do
it 'filters issues by searched label, label2, author, assignee, milestone and text' do
input_filtered_search("label:~#{bug_label.title} label:~#{caps_sensitive_label.title} author:@#{user.username} assignee:@#{user.username} milestone:%#{milestone.title} #{search_term}")
+ wait_for_requests
+
expect_tokens([
- { name: 'label', value: bug_label.title },
- { name: 'label', value: caps_sensitive_label.title },
- { name: 'author', value: user.username },
- { name: 'assignee', value: user.username },
- { name: 'milestone', value: milestone.title }
+ label_token(bug_label.title),
+ label_token(caps_sensitive_label.title),
+ author_token(user.name),
+ assignee_token(user.name),
+ milestone_token(milestone.title)
])
expect_issues_list_count(1)
expect_filtered_search_input(search_term)
@@ -467,7 +489,7 @@ describe 'Filter issues', js: true do
end
it 'displays in search bar' do
- expect_tokens([{ name: 'label', value: "\"#{multiple_words_label.title}\"" }])
+ expect_tokens([label_token("\"#{multiple_words_label.title}\"")])
expect_filtered_search_input_empty
end
end
@@ -484,7 +506,7 @@ describe 'Filter issues', js: true do
it 'filters issues by searched milestone' do
input_filtered_search("milestone:%#{milestone.title}")
- expect_tokens([{ name: 'milestone', value: milestone.title }])
+ expect_tokens([milestone_token(milestone.title)])
expect_issues_list_count(5)
expect_filtered_search_input_empty
end
@@ -492,7 +514,7 @@ describe 'Filter issues', js: true do
it 'filters issues by no milestone' do
input_filtered_search("milestone:none")
- expect_tokens([{ name: 'milestone', value: 'none' }])
+ expect_tokens([milestone_token('none', false)])
expect_issues_list_count(7, 1)
expect_filtered_search_input_empty
end
@@ -500,7 +522,7 @@ describe 'Filter issues', js: true do
it 'filters issues by upcoming milestones' do
input_filtered_search("milestone:upcoming")
- expect_tokens([{ name: 'milestone', value: 'upcoming' }])
+ expect_tokens([milestone_token('upcoming', false)])
expect_issues_list_count(1)
expect_filtered_search_input_empty
end
@@ -508,7 +530,7 @@ describe 'Filter issues', js: true do
it 'filters issues by started milestones' do
input_filtered_search("milestone:started")
- expect_tokens([{ name: 'milestone', value: 'started' }])
+ expect_tokens([milestone_token('started', false)])
expect_issues_list_count(5)
expect_filtered_search_input_empty
end
@@ -527,7 +549,7 @@ describe 'Filter issues', js: true do
input_filtered_search("milestone:%#{special_milestone.title}")
- expect_tokens([{ name: 'milestone', value: special_milestone.title }])
+ expect_tokens([milestone_token(special_milestone.title)])
expect_issues_list_count(1)
expect_filtered_search_input_empty
end
@@ -537,7 +559,7 @@ describe 'Filter issues', js: true do
input_filtered_search("milestone:%#{new_milestone.title}")
- expect_tokens([{ name: 'milestone', value: new_milestone.title }])
+ expect_tokens([milestone_token(new_milestone.title)])
expect_no_issues_list()
expect_filtered_search_input_empty
end
@@ -549,7 +571,7 @@ describe 'Filter issues', js: true do
it 'filters issues by searched milestone and text' do
input_filtered_search("milestone:%#{milestone.title} #{search_term}")
- expect_tokens([{ name: 'milestone', value: milestone.title }])
+ expect_tokens([milestone_token(milestone.title)])
expect_issues_list_count(2)
expect_filtered_search_input(search_term)
end
@@ -557,9 +579,11 @@ describe 'Filter issues', js: true do
it 'filters issues by searched milestone, author and text' do
input_filtered_search("milestone:%#{milestone.title} author:@#{user.username} #{search_term}")
+ wait_for_requests
+
expect_tokens([
- { name: 'milestone', value: milestone.title },
- { name: 'author', value: user.username }
+ milestone_token(milestone.title),
+ author_token(user.name)
])
expect_issues_list_count(2)
expect_filtered_search_input(search_term)
@@ -568,10 +592,12 @@ describe 'Filter issues', js: true do
it 'filters issues by searched milestone, author, assignee and text' do
input_filtered_search("milestone:%#{milestone.title} author:@#{user.username} assignee:@#{user.username} #{search_term}")
+ wait_for_requests
+
expect_tokens([
- { name: 'milestone', value: milestone.title },
- { name: 'author', value: user.username },
- { name: 'assignee', value: user.username }
+ milestone_token(milestone.title),
+ author_token(user.name),
+ assignee_token(user.name)
])
expect_issues_list_count(2)
expect_filtered_search_input(search_term)
@@ -580,11 +606,13 @@ describe 'Filter issues', js: true do
it 'filters issues by searched milestone, author, assignee, label and text' do
input_filtered_search("milestone:%#{milestone.title} author:@#{user.username} assignee:@#{user.username} label:~#{bug_label.title} #{search_term}")
+ wait_for_requests
+
expect_tokens([
- { name: 'milestone', value: milestone.title },
- { name: 'author', value: user.username },
- { name: 'assignee', value: user.username },
- { name: 'label', value: bug_label.title }
+ milestone_token(milestone.title),
+ author_token(user.name),
+ assignee_token(user.name),
+ label_token(bug_label.title)
])
expect_issues_list_count(2)
expect_filtered_search_input(search_term)
diff --git a/spec/features/issues/filtered_search/search_bar_spec.rb b/spec/features/issues/filtered_search/search_bar_spec.rb
index aa9d0d842de..a432d031337 100644
--- a/spec/features/issues/filtered_search/search_bar_spec.rb
+++ b/spec/features/issues/filtered_search/search_bar_spec.rb
@@ -32,7 +32,7 @@ describe 'Search bar', js: true do
it 'selects item' do
filtered_search.native.send_keys(:down, :down, :enter)
- expect_tokens([{ name: 'author' }])
+ expect_tokens([author_token])
expect_filtered_search_input_empty
end
end
diff --git a/spec/features/issues/filtered_search/visual_tokens_spec.rb b/spec/features/issues/filtered_search/visual_tokens_spec.rb
index 52efe944b69..14a555fde10 100644
--- a/spec/features/issues/filtered_search/visual_tokens_spec.rb
+++ b/spec/features/issues/filtered_search/visual_tokens_spec.rb
@@ -346,8 +346,8 @@ describe 'Visual tokens', js: true do
it 'tokenizes the search term to complete visual token' do
expect_tokens([
- { name: 'author', value: '@root' },
- { name: 'assignee', value: 'none' }
+ author_token(user.name),
+ assignee_token('none')
])
end
end
diff --git a/spec/features/issues_spec.rb b/spec/features/issues_spec.rb
index 3c8e37ff920..3ffc80622f5 100644
--- a/spec/features/issues_spec.rb
+++ b/spec/features/issues_spec.rb
@@ -708,7 +708,7 @@ describe 'Issues' do
end
describe 'confidential issue#show', js: true do
- it 'shows confidential sibebar information as confidential and can be turned off' do
+ it 'shows confidential sibebar information as confidential and can be turned off' do
issue = create(:issue, :confidential, project: project)
visit project_issue_path(project, issue)
diff --git a/spec/features/merge_requests/conflicts_spec.rb b/spec/features/merge_requests/conflicts_spec.rb
index 2c560632a1b..2d2c674f8fb 100644
--- a/spec/features/merge_requests/conflicts_spec.rb
+++ b/spec/features/merge_requests/conflicts_spec.rb
@@ -28,11 +28,12 @@ feature 'Merge request conflict resolution', js: true do
end
click_button 'Commit conflict resolution'
- wait_for_requests
expect(page).to have_content('All merge conflicts were resolved')
merge_request.reload_diff
+ wait_for_requests
+
click_on 'Changes'
wait_for_requests
@@ -69,10 +70,12 @@ feature 'Merge request conflict resolution', js: true do
end
click_button 'Commit conflict resolution'
- wait_for_requests
+
expect(page).to have_content('All merge conflicts were resolved')
merge_request.reload_diff
+ wait_for_requests
+
click_on 'Changes'
wait_for_requests
@@ -140,12 +143,13 @@ feature 'Merge request conflict resolution', js: true do
end
click_button 'Commit conflict resolution'
- wait_for_requests
expect(page).to have_content('All merge conflicts were resolved')
merge_request.reload_diff
+ wait_for_requests
+
click_on 'Changes'
wait_for_requests
click_link 'Expand all'
diff --git a/spec/features/merge_requests/filter_by_milestone_spec.rb b/spec/features/merge_requests/filter_by_milestone_spec.rb
index 521fcabc881..166c02a7a7f 100644
--- a/spec/features/merge_requests/filter_by_milestone_spec.rb
+++ b/spec/features/merge_requests/filter_by_milestone_spec.rb
@@ -25,7 +25,7 @@ feature 'Merge Request filtering by Milestone' do
visit_merge_requests(project)
input_filtered_search('milestone:none')
- expect_tokens([{ name: 'milestone', value: 'none' }])
+ expect_tokens([milestone_token('none', false)])
expect_filtered_search_input_empty
expect(page).to have_issuable_counts(open: 1, closed: 0, all: 1)
diff --git a/spec/features/merge_requests/filter_merge_requests_spec.rb b/spec/features/merge_requests/filter_merge_requests_spec.rb
index 3686131fee4..b51ae0890e4 100644
--- a/spec/features/merge_requests/filter_merge_requests_spec.rb
+++ b/spec/features/merge_requests/filter_merge_requests_spec.rb
@@ -24,7 +24,9 @@ describe 'Filter merge requests' do
let(:search_query) { "assignee:@#{user.username}" }
def expect_assignee_visual_tokens
- expect_tokens([{ name: 'assignee', value: "@#{user.username}" }])
+ wait_for_requests
+
+ expect_tokens([assignee_token(user.name)])
expect_filtered_search_input_empty
end
@@ -57,7 +59,7 @@ describe 'Filter merge requests' do
let(:search_query) { "milestone:%\"#{milestone.title}\"" }
def expect_milestone_visual_tokens
- expect_tokens([{ name: 'milestone', value: "%\"#{milestone.title}\"" }])
+ expect_tokens([milestone_token("\"#{milestone.title}\"")])
expect_filtered_search_input_empty
end
@@ -91,7 +93,7 @@ describe 'Filter merge requests' do
input_filtered_search('label:none')
expect_mr_list_count(1)
- expect_tokens([{ name: 'label', value: 'none' }])
+ expect_tokens([label_token('none', false)])
expect_filtered_search_input_empty
end
@@ -99,7 +101,7 @@ describe 'Filter merge requests' do
input_filtered_search("label:~#{label.title}")
expect_mr_list_count(0)
- expect_tokens([{ name: 'label', value: "~#{label.title}" }])
+ expect_tokens([label_token(label.title)])
expect_filtered_search_input_empty
end
@@ -107,10 +109,7 @@ describe 'Filter merge requests' do
input_filtered_search("label:~\"#{wontfix.title}\" label:~#{label.title}")
expect_mr_list_count(0)
- expect_tokens([
- { name: 'label', value: "~\"#{wontfix.title}\"" },
- { name: 'label', value: "~#{label.title}" }
- ])
+ expect_tokens([label_token("\"#{wontfix.title}\""), label_token(label.title)])
expect_filtered_search_input_empty
end
@@ -118,16 +117,13 @@ describe 'Filter merge requests' do
input_filtered_search("label:~\"#{wontfix.title}\"")
expect_mr_list_count(0)
- expect_tokens([{ name: 'label', value: "~\"#{wontfix.title}\"" }])
+ expect_tokens([label_token("\"#{wontfix.title}\"")])
expect_filtered_search_input_empty
input_filtered_search_keys("label:~#{label.title}")
expect_mr_list_count(0)
- expect_tokens([
- { name: 'label', value: "~\"#{wontfix.title}\"" },
- { name: 'label', value: "~#{label.title}" }
- ])
+ expect_tokens([label_token("\"#{wontfix.title}\""), label_token(label.title)])
expect_filtered_search_input_empty
end
end
@@ -143,10 +139,9 @@ describe 'Filter merge requests' do
context 'assignee and label', js: true do
def expect_assignee_label_visual_tokens
- expect_tokens([
- { name: 'assignee', value: "@#{user.username}" },
- { name: 'label', value: "~#{label.title}" }
- ])
+ wait_for_requests
+
+ expect_tokens([assignee_token(user.name), label_token(label.title)])
expect_filtered_search_input_empty
end
@@ -214,7 +209,7 @@ describe 'Filter merge requests' do
input_filtered_search_keys(' label:~bug')
expect_mr_list_count(1)
- expect_tokens([{ name: 'label', value: '~bug' }])
+ expect_tokens([label_token('bug')])
expect_filtered_search_input('Bug')
end
@@ -227,7 +222,7 @@ describe 'Filter merge requests' do
input_filtered_search_keys(' milestone:%8')
expect_mr_list_count(1)
- expect_tokens([{ name: 'milestone', value: '%8' }])
+ expect_tokens([milestone_token('8')])
expect_filtered_search_input('Bug')
end
@@ -240,7 +235,10 @@ describe 'Filter merge requests' do
input_filtered_search_keys(" assignee:@#{user.username}")
expect_mr_list_count(1)
- expect_tokens([{ name: 'assignee', value: "@#{user.username}" }])
+
+ wait_for_requests
+
+ expect_tokens([assignee_token(user.name)])
expect_filtered_search_input('Bug')
end
@@ -252,8 +250,10 @@ describe 'Filter merge requests' do
input_filtered_search_keys(" author:@#{user.username}")
+ wait_for_requests
+
expect_mr_list_count(1)
- expect_tokens([{ name: 'author', value: "@#{user.username}" }])
+ expect_tokens([author_token(user.name)])
expect_filtered_search_input('Bug')
end
end
@@ -293,7 +293,9 @@ describe 'Filter merge requests' do
it 'filter by current user' do
visit project_merge_requests_path(project, assignee_id: user.id)
- expect_tokens([{ name: 'assignee', value: "@#{user.username}" }])
+ wait_for_requests
+
+ expect_tokens([assignee_token(user.name)])
expect_filtered_search_input_empty
end
@@ -303,7 +305,9 @@ describe 'Filter merge requests' do
visit project_merge_requests_path(project, assignee_id: new_user.id)
- expect_tokens([{ name: 'assignee', value: "@#{new_user.username}" }])
+ wait_for_requests
+
+ expect_tokens([assignee_token(new_user.name)])
expect_filtered_search_input_empty
end
end
@@ -312,7 +316,9 @@ describe 'Filter merge requests' do
it 'filter by current user' do
visit project_merge_requests_path(project, author_id: user.id)
- expect_tokens([{ name: 'author', value: "@#{user.username}" }])
+ wait_for_requests
+
+ expect_tokens([author_token(user.name)])
expect_filtered_search_input_empty
end
@@ -322,7 +328,9 @@ describe 'Filter merge requests' do
visit project_merge_requests_path(project, author_id: new_user.id)
- expect_tokens([{ name: 'author', value: "@#{new_user.username}" }])
+ wait_for_requests
+
+ expect_tokens([author_token(new_user.name)])
expect_filtered_search_input_empty
end
end
diff --git a/spec/features/milestones/show_spec.rb b/spec/features/milestones/show_spec.rb
index 20303359c46..624f13922ed 100644
--- a/spec/features/milestones/show_spec.rb
+++ b/spec/features/milestones/show_spec.rb
@@ -8,7 +8,7 @@ describe 'Milestone show' do
let(:issue_params) { { project: project, assignees: [user], author: user, milestone: milestone, labels: labels } }
before do
- project.add_user(user, :developer)
+ project.add_user(user, :developer)
sign_in(user)
end
diff --git a/spec/features/profile_spec.rb b/spec/features/profile_spec.rb
index 672022304da..f183dd8cb75 100644
--- a/spec/features/profile_spec.rb
+++ b/spec/features/profile_spec.rb
@@ -7,9 +7,8 @@ describe 'Profile account page' do
sign_in(user)
end
- describe 'when signup is enabled' do
+ describe 'when I delete my account' do
before do
- stub_application_setting(signup_enabled: true)
visit profile_account_path
end
@@ -21,18 +20,6 @@ describe 'Profile account page' do
end
end
- describe 'when signup is disabled' do
- before do
- stub_application_setting(signup_enabled: false)
- visit profile_account_path
- end
-
- it 'does not have option to remove account' do
- expect(page).not_to have_content('Remove account')
- expect(current_path).to eq(profile_account_path)
- end
- end
-
describe 'when I reset private token' do
before do
visit profile_account_path
diff --git a/spec/features/projects/files/undo_template_spec.rb b/spec/features/projects/files/undo_template_spec.rb
index 4238d25e9ee..9bcd5beabb8 100644
--- a/spec/features/projects/files/undo_template_spec.rb
+++ b/spec/features/projects/files/undo_template_spec.rb
@@ -20,7 +20,7 @@ feature 'Template Undo Button', js: true do
end
end
- context 'creating a non-matching file' do
+ context 'creating a non-matching file' do
before do
visit project_new_blob_path(project, 'master')
select_file_template_type('LICENSE')
diff --git a/spec/features/projects_spec.rb b/spec/features/projects_spec.rb
index d3d7915bebf..baf3d29e6c5 100644
--- a/spec/features/projects_spec.rb
+++ b/spec/features/projects_spec.rb
@@ -18,7 +18,7 @@ feature 'Project' do
click_button "Create project"
end
- expect(page).to have_content 'This project Loading..'
+ expect(page).to have_content template.name
end
end
diff --git a/spec/features/search_spec.rb b/spec/features/search_spec.rb
index 9b49fc2225d..6742d77937f 100644
--- a/spec/features/search_spec.rb
+++ b/spec/features/search_spec.rb
@@ -195,37 +195,33 @@ describe "Search" do
it 'takes user to her issues page when issues assigned is clicked' do
find('.dropdown-menu').click_link 'Issues assigned to me'
- sleep 2
expect(page).to have_selector('.filtered-search')
- expect_tokens([{ name: 'assignee', value: "@#{user.username}" }])
+ expect_tokens([assignee_token(user.name)])
expect_filtered_search_input_empty
end
it 'takes user to her issues page when issues authored is clicked' do
find('.dropdown-menu').click_link "Issues I've created"
- sleep 2
expect(page).to have_selector('.filtered-search')
- expect_tokens([{ name: 'author', value: "@#{user.username}" }])
+ expect_tokens([author_token(user.name)])
expect_filtered_search_input_empty
end
it 'takes user to her MR page when MR assigned is clicked' do
find('.dropdown-menu').click_link 'Merge requests assigned to me'
- sleep 2
expect(page).to have_selector('.merge-requests-holder')
- expect_tokens([{ name: 'assignee', value: "@#{user.username}" }])
+ expect_tokens([assignee_token(user.name)])
expect_filtered_search_input_empty
end
it 'takes user to her MR page when MR authored is clicked' do
find('.dropdown-menu').click_link "Merge requests I've created"
- sleep 2
expect(page).to have_selector('.merge-requests-holder')
- expect_tokens([{ name: 'author', value: "@#{user.username}" }])
+ expect_tokens([author_token(user.name)])
expect_filtered_search_input_empty
end
end
diff --git a/spec/helpers/version_check_helper_spec.rb b/spec/helpers/version_check_helper_spec.rb
index 889fe441171..5eba03ef576 100644
--- a/spec/helpers/version_check_helper_spec.rb
+++ b/spec/helpers/version_check_helper_spec.rb
@@ -23,7 +23,7 @@ describe VersionCheckHelper do
end
it 'should have a js prefixed css class' do
- expect(@image_tag).to match(/class="js-version-status-badge"/)
+ expect(@image_tag).to match(/class="js-version-status-badge lazy"/)
end
it 'should have a VersionCheck url as the src' do
diff --git a/spec/javascripts/fixtures/prometheus_service.rb b/spec/javascripts/fixtures/prometheus_service.rb
index 7a46e47bb15..7968c9425f2 100644
--- a/spec/javascripts/fixtures/prometheus_service.rb
+++ b/spec/javascripts/fixtures/prometheus_service.rb
@@ -7,7 +7,7 @@ describe Projects::ServicesController, '(JavaScript fixtures)', type: :controlle
let(:namespace) { create(:namespace, name: 'frontend-fixtures' )}
let(:project) { create(:project_empty_repo, namespace: namespace, path: 'services-project') }
let!(:service) { create(:prometheus_service, project: project) }
-
+
render_views
before(:all) do
diff --git a/spec/javascripts/fixtures/services.rb b/spec/javascripts/fixtures/services.rb
index 0a3c64d5d31..80915c32a74 100644
--- a/spec/javascripts/fixtures/services.rb
+++ b/spec/javascripts/fixtures/services.rb
@@ -7,7 +7,6 @@ describe Projects::ServicesController, '(JavaScript fixtures)', type: :controlle
let(:namespace) { create(:namespace, name: 'frontend-fixtures' )}
let(:project) { create(:project_empty_repo, namespace: namespace, path: 'services-project') }
let!(:service) { create(:custom_issue_tracker_service, project: project, title: 'Custom Issue Tracker') }
-
render_views
diff --git a/spec/javascripts/sidebar/confidential_issue_sidebar_spec.js b/spec/javascripts/sidebar/confidential_issue_sidebar_spec.js
index 2e16adffb5b..88a33caf2e3 100644
--- a/spec/javascripts/sidebar/confidential_issue_sidebar_spec.js
+++ b/spec/javascripts/sidebar/confidential_issue_sidebar_spec.js
@@ -41,7 +41,7 @@ describe('Confidential Issue Sidebar Block', () => {
).toBe(true);
expect(
- vm2.$el.innerHTML.includes('This issue is not confidential'),
+ vm2.$el.innerHTML.includes('Not confidential'),
).toBe(true);
});
diff --git a/spec/lib/after_commit_queue_spec.rb b/spec/lib/after_commit_queue_spec.rb
new file mode 100644
index 00000000000..6e7c2ec2363
--- /dev/null
+++ b/spec/lib/after_commit_queue_spec.rb
@@ -0,0 +1,15 @@
+require 'spec_helper'
+
+describe AfterCommitQueue do
+ it 'runs after transaction is committed' do
+ called = false
+ test_proc = proc { called = true }
+
+ project = build(:project)
+ project.run_after_commit(&test_proc)
+
+ project.save
+
+ expect(called).to be true
+ end
+end
diff --git a/spec/lib/file_size_validator_spec.rb b/spec/lib/file_size_validator_spec.rb
index 49501931dd2..c44bc1840df 100644
--- a/spec/lib/file_size_validator_spec.rb
+++ b/spec/lib/file_size_validator_spec.rb
@@ -24,13 +24,13 @@ describe FileSizeValidator do
describe 'options uses a symbol' do
let(:options) do
{
- maximum: :test,
+ maximum: :max_attachment_size,
attributes: { attachment: attachment }
}
end
before do
- allow(note).to receive(:test) { 10 }
+ expect(note).to receive(:max_attachment_size) { 10 }
end
it 'attachment exceeds maximum limit' do
diff --git a/spec/lib/gitlab/background_migration/migrate_events_to_push_event_payloads_spec.rb b/spec/lib/gitlab/background_migration/migrate_events_to_push_event_payloads_spec.rb
index 87f45619e7a..0d5fffa38ff 100644
--- a/spec/lib/gitlab/background_migration/migrate_events_to_push_event_payloads_spec.rb
+++ b/spec/lib/gitlab/background_migration/migrate_events_to_push_event_payloads_spec.rb
@@ -210,7 +210,11 @@ describe Gitlab::BackgroundMigration::MigrateEventsToPushEventPayloads::Event do
end
end
-describe Gitlab::BackgroundMigration::MigrateEventsToPushEventPayloads do
+##
+# The background migration relies on a temporary table, hence we're migrating
+# to a specific version of the database where said table is still present.
+#
+describe Gitlab::BackgroundMigration::MigrateEventsToPushEventPayloads, :migration, schema: 20170608152748 do
let(:migration) { described_class.new }
let(:project) { create(:project_empty_repo) }
let(:author) { create(:user) }
@@ -229,21 +233,6 @@ describe Gitlab::BackgroundMigration::MigrateEventsToPushEventPayloads do
)
end
- # The background migration relies on a temporary table, hence we're migrating
- # to a specific version of the database where said table is still present.
- before :all do
- ActiveRecord::Migration.verbose = false
-
- ActiveRecord::Migrator
- .migrate(ActiveRecord::Migrator.migrations_paths, 20170608152748)
- end
-
- after :all do
- ActiveRecord::Migrator.migrate(ActiveRecord::Migrator.migrations_paths)
-
- ActiveRecord::Migration.verbose = true
- end
-
describe '#perform' do
it 'returns if data should not be migrated' do
allow(migration).to receive(:migrate?).and_return(false)
diff --git a/spec/lib/gitlab/ci/trace/stream_spec.rb b/spec/lib/gitlab/ci/trace/stream_spec.rb
index ebe5af56160..e5555546fa8 100644
--- a/spec/lib/gitlab/ci/trace/stream_spec.rb
+++ b/spec/lib/gitlab/ci/trace/stream_spec.rb
@@ -295,7 +295,7 @@ describe Gitlab::Ci::Trace::Stream do
end
context 'malicious regexp' do
- let(:data) { malicious_text }
+ let(:data) { malicious_text }
let(:regex) { malicious_regexp }
include_examples 'malicious regexp'
diff --git a/spec/lib/gitlab/cycle_analytics/base_event_fetcher_spec.rb b/spec/lib/gitlab/cycle_analytics/base_event_fetcher_spec.rb
index 854aaa34c73..0560c47f03f 100644
--- a/spec/lib/gitlab/cycle_analytics/base_event_fetcher_spec.rb
+++ b/spec/lib/gitlab/cycle_analytics/base_event_fetcher_spec.rb
@@ -6,10 +6,10 @@ describe Gitlab::CycleAnalytics::BaseEventFetcher do
let(:user) { create(:user, :admin) }
let(:start_time_attrs) { Issue.arel_table[:created_at] }
let(:end_time_attrs) { [Issue::Metrics.arel_table[:first_associated_with_milestone_at]] }
- let(:options) do
+ let(:options) do
{ start_time_attrs: start_time_attrs,
end_time_attrs: end_time_attrs,
- from: 30.days.ago }
+ from: 30.days.ago }
end
subject do
diff --git a/spec/lib/gitlab/git/storage/circuit_breaker_spec.rb b/spec/lib/gitlab/git/storage/circuit_breaker_spec.rb
index 9d1763b96ad..c86353abb7c 100644
--- a/spec/lib/gitlab/git/storage/circuit_breaker_spec.rb
+++ b/spec/lib/gitlab/git/storage/circuit_breaker_spec.rb
@@ -1,9 +1,30 @@
require 'spec_helper'
describe Gitlab::Git::Storage::CircuitBreaker, clean_gitlab_redis_shared_state: true, broken_storage: true do
- let(:circuit_breaker) { described_class.new('default') }
+ let(:storage_name) { 'default' }
+ let(:circuit_breaker) { described_class.new(storage_name) }
let(:hostname) { Gitlab::Environment.hostname }
- let(:cache_key) { "storage_accessible:default:#{hostname}" }
+ let(:cache_key) { "storage_accessible:#{storage_name}:#{hostname}" }
+
+ before do
+ # Override test-settings for the circuitbreaker with something more realistic
+ # for these specs.
+ stub_storage_settings('default' => {
+ 'path' => TestEnv.repos_path,
+ 'failure_count_threshold' => 10,
+ 'failure_wait_time' => 30,
+ 'failure_reset_time' => 1800,
+ 'storage_timeout' => 5
+ },
+ 'broken' => {
+ 'path' => 'tmp/tests/non-existent-repositories',
+ 'failure_count_threshold' => 10,
+ 'failure_wait_time' => 30,
+ 'failure_reset_time' => 1800,
+ 'storage_timeout' => 5
+ }
+ )
+ end
def value_from_redis(name)
Gitlab::Git::Storage.redis.with do |redis|
@@ -96,14 +117,14 @@ describe Gitlab::Git::Storage::CircuitBreaker, clean_gitlab_redis_shared_state:
end
describe '#circuit_broken?' do
- it 'is closed when there is no last failure' do
+ it 'is working when there is no last failure' do
set_in_redis(:last_failure, nil)
set_in_redis(:failure_count, 0)
expect(circuit_breaker.circuit_broken?).to be_falsey
end
- it 'is open when there was a recent failure' do
+ it 'is broken when there was a recent failure' do
Timecop.freeze do
set_in_redis(:last_failure, 1.second.ago.to_f)
set_in_redis(:failure_count, 1)
@@ -112,16 +133,34 @@ describe Gitlab::Git::Storage::CircuitBreaker, clean_gitlab_redis_shared_state:
end
end
- it 'is open when there are to many failures' do
+ it 'is broken when there are too many failures' do
set_in_redis(:last_failure, 1.day.ago.to_f)
set_in_redis(:failure_count, 200)
expect(circuit_breaker.circuit_broken?).to be_truthy
end
+
+ context 'the `failure_wait_time` is set to 0' do
+ before do
+ stub_storage_settings('default' => {
+ 'failure_wait_time' => 0,
+ 'path' => TestEnv.repos_path
+ })
+ end
+
+ it 'is working even when there is a recent failure' do
+ Timecop.freeze do
+ set_in_redis(:last_failure, 0.seconds.ago.to_f)
+ set_in_redis(:failure_count, 1)
+
+ expect(circuit_breaker.circuit_broken?).to be_falsey
+ end
+ end
+ end
end
describe "storage_available?" do
- context 'when the storage is available' do
+ context 'the storage is available' do
it 'tracks that the storage was accessible an raises the error' do
expect(circuit_breaker).to receive(:track_storage_accessible)
@@ -136,8 +175,8 @@ describe Gitlab::Git::Storage::CircuitBreaker, clean_gitlab_redis_shared_state:
end
end
- context 'when storage is not available' do
- let(:circuit_breaker) { described_class.new('broken') }
+ context 'storage is not available' do
+ let(:storage_name) { 'broken' }
it 'tracks that the storage was inaccessible' do
expect(circuit_breaker).to receive(:track_storage_inaccessible)
@@ -158,8 +197,8 @@ describe Gitlab::Git::Storage::CircuitBreaker, clean_gitlab_redis_shared_state:
end
end
- context 'when the storage is not available' do
- let(:circuit_breaker) { described_class.new('broken') }
+ context 'the storage is not available' do
+ let(:storage_name) { 'broken' }
it 'raises an error' do
expect(circuit_breaker).to receive(:track_storage_inaccessible)
diff --git a/spec/lib/gitlab/gpg/commit_spec.rb b/spec/lib/gitlab/gpg/commit_spec.rb
index ddb8dd9f0f4..e521fcc6dc1 100644
--- a/spec/lib/gitlab/gpg/commit_spec.rb
+++ b/spec/lib/gitlab/gpg/commit_spec.rb
@@ -1,13 +1,13 @@
require 'rails_helper'
-RSpec.describe Gitlab::Gpg::Commit do
+describe Gitlab::Gpg::Commit do
describe '#signature' do
let!(:project) { create :project, :repository, path: 'sample-project' }
let!(:commit_sha) { '0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33' }
- context 'unisgned commit' do
+ context 'unsigned commit' do
it 'returns nil' do
- expect(described_class.new(project.commit).signature).to be_nil
+ expect(described_class.new(project, commit_sha).signature).to be_nil
end
end
@@ -16,18 +16,19 @@ RSpec.describe Gitlab::Gpg::Commit do
create :gpg_key, key: GpgHelpers::User1.public_key, user: create(:user, email: GpgHelpers::User1.emails.first)
end
- let!(:commit) do
- raw_commit = double(:raw_commit, signature: [
- GpgHelpers::User1.signed_commit_signature,
- GpgHelpers::User1.signed_commit_base_data
- ], sha: commit_sha)
- allow(raw_commit).to receive :save!
-
- create :commit, git_commit: raw_commit, project: project
+ before do
+ allow(Rugged::Commit).to receive(:extract_signature)
+ .with(Rugged::Repository, commit_sha)
+ .and_return(
+ [
+ GpgHelpers::User1.signed_commit_signature,
+ GpgHelpers::User1.signed_commit_base_data
+ ]
+ )
end
it 'returns a valid signature' do
- expect(described_class.new(commit).signature).to have_attributes(
+ expect(described_class.new(project, commit_sha).signature).to have_attributes(
commit_sha: commit_sha,
project: project,
gpg_key: gpg_key,
@@ -39,7 +40,7 @@ RSpec.describe Gitlab::Gpg::Commit do
end
it 'returns the cached signature on second call' do
- gpg_commit = described_class.new(commit)
+ gpg_commit = described_class.new(project, commit_sha)
expect(gpg_commit).to receive(:using_keychain).and_call_original
gpg_commit.signature
@@ -53,18 +54,19 @@ RSpec.describe Gitlab::Gpg::Commit do
context 'known but unverified public key' do
let!(:gpg_key) { create :gpg_key, key: GpgHelpers::User1.public_key }
- let!(:commit) do
- raw_commit = double(:raw_commit, signature: [
- GpgHelpers::User1.signed_commit_signature,
- GpgHelpers::User1.signed_commit_base_data
- ], sha: commit_sha)
- allow(raw_commit).to receive :save!
-
- create :commit, git_commit: raw_commit, project: project
+ before do
+ allow(Rugged::Commit).to receive(:extract_signature)
+ .with(Rugged::Repository, commit_sha)
+ .and_return(
+ [
+ GpgHelpers::User1.signed_commit_signature,
+ GpgHelpers::User1.signed_commit_base_data
+ ]
+ )
end
it 'returns an invalid signature' do
- expect(described_class.new(commit).signature).to have_attributes(
+ expect(described_class.new(project, commit_sha).signature).to have_attributes(
commit_sha: commit_sha,
project: project,
gpg_key: gpg_key,
@@ -76,7 +78,7 @@ RSpec.describe Gitlab::Gpg::Commit do
end
it 'returns the cached signature on second call' do
- gpg_commit = described_class.new(commit)
+ gpg_commit = described_class.new(project, commit_sha)
expect(gpg_commit).to receive(:using_keychain).and_call_original
gpg_commit.signature
@@ -88,20 +90,19 @@ RSpec.describe Gitlab::Gpg::Commit do
end
context 'unknown public key' do
- let!(:commit) do
- raw_commit = double(:raw_commit, signature: [
- GpgHelpers::User1.signed_commit_signature,
- GpgHelpers::User1.signed_commit_base_data
- ], sha: commit_sha)
- allow(raw_commit).to receive :save!
-
- create :commit,
- git_commit: raw_commit,
- project: project
+ before do
+ allow(Rugged::Commit).to receive(:extract_signature)
+ .with(Rugged::Repository, commit_sha)
+ .and_return(
+ [
+ GpgHelpers::User1.signed_commit_signature,
+ GpgHelpers::User1.signed_commit_base_data
+ ]
+ )
end
it 'returns an invalid signature' do
- expect(described_class.new(commit).signature).to have_attributes(
+ expect(described_class.new(project, commit_sha).signature).to have_attributes(
commit_sha: commit_sha,
project: project,
gpg_key: nil,
@@ -113,7 +114,7 @@ RSpec.describe Gitlab::Gpg::Commit do
end
it 'returns the cached signature on second call' do
- gpg_commit = described_class.new(commit)
+ gpg_commit = described_class.new(project, commit_sha)
expect(gpg_commit).to receive(:using_keychain).and_call_original
gpg_commit.signature
diff --git a/spec/lib/gitlab/gpg/invalid_gpg_signature_updater_spec.rb b/spec/lib/gitlab/gpg/invalid_gpg_signature_updater_spec.rb
index c4e04ee46a2..4de4419de27 100644
--- a/spec/lib/gitlab/gpg/invalid_gpg_signature_updater_spec.rb
+++ b/spec/lib/gitlab/gpg/invalid_gpg_signature_updater_spec.rb
@@ -4,23 +4,16 @@ RSpec.describe Gitlab::Gpg::InvalidGpgSignatureUpdater do
describe '#run' do
let!(:commit_sha) { '0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33' }
let!(:project) { create :project, :repository, path: 'sample-project' }
- let!(:raw_commit) do
- raw_commit = double(:raw_commit, signature: [
- GpgHelpers::User1.signed_commit_signature,
- GpgHelpers::User1.signed_commit_base_data
- ], sha: commit_sha)
-
- allow(raw_commit).to receive :save!
-
- raw_commit
- end
-
- let!(:commit) do
- create :commit, git_commit: raw_commit, project: project
- end
before do
- allow_any_instance_of(Project).to receive(:commit).and_return(commit)
+ allow(Rugged::Commit).to receive(:extract_signature)
+ .with(Rugged::Repository, commit_sha)
+ .and_return(
+ [
+ GpgHelpers::User1.signed_commit_signature,
+ GpgHelpers::User1.signed_commit_base_data
+ ]
+ )
end
context 'gpg signature did have an associated gpg key which was removed later' do
diff --git a/spec/lib/gitlab/import_export/project.light.json b/spec/lib/gitlab/import_export/project.light.json
index a78836c3c34..2d8f3d4a566 100644
--- a/spec/lib/gitlab/import_export/project.light.json
+++ b/spec/lib/gitlab/import_export/project.light.json
@@ -2,6 +2,20 @@
"description": "Nisi et repellendus ut enim quo accusamus vel magnam.",
"visibility_level": 10,
"archived": false,
+ "milestones": [
+ {
+ "id": 1,
+ "title": "test milestone",
+ "project_id": 8,
+ "description": "test milestone",
+ "due_date": null,
+ "created_at": "2016-06-14T15:02:04.415Z",
+ "updated_at": "2016-06-14T15:02:04.415Z",
+ "state": "active",
+ "iid": 1,
+ "group_id": null
+ }
+ ],
"labels": [
{
"id": 2,
@@ -14,20 +28,6 @@
"description": "",
"type": "ProjectLabel",
"priorities": [
- ]
- },
- {
- "id": 3,
- "title": "test3",
- "color": "#428bca",
- "group_id": 8,
- "created_at": "2016-07-22T08:55:44.161Z",
- "updated_at": "2016-07-22T08:55:44.161Z",
- "template": false,
- "description": "",
- "project_id": null,
- "type": "GroupLabel",
- "priorities": [
{
"id": 1,
"project_id": 5,
@@ -39,10 +39,80 @@
]
}
],
+ "issues": [
+ {
+ "id": 1,
+ "title": "Fugiat est minima quae maxime non similique.",
+ "assignee_id": null,
+ "project_id": 8,
+ "author_id": 1,
+ "created_at": "2017-07-07T18:13:01.138Z",
+ "updated_at": "2017-08-15T18:37:40.807Z",
+ "branch_name": null,
+ "description": "Quam totam fuga numquam in eveniet.",
+ "state": "opened",
+ "iid": 20,
+ "updated_by_id": 1,
+ "confidential": false,
+ "deleted_at": null,
+ "due_date": null,
+ "moved_to_id": null,
+ "lock_version": null,
+ "time_estimate": 0,
+ "closed_at": null,
+ "last_edited_at": null,
+ "last_edited_by_id": null,
+ "group_milestone_id": null,
+ "label_links": [
+ {
+ "id": 11,
+ "label_id": 6,
+ "target_id": 1,
+ "target_type": "Issue",
+ "created_at": "2017-08-15T18:37:40.795Z",
+ "updated_at": "2017-08-15T18:37:40.795Z",
+ "label": {
+ "id": 6,
+ "title": "group label",
+ "color": "#A8D695",
+ "project_id": null,
+ "created_at": "2017-08-15T18:37:19.698Z",
+ "updated_at": "2017-08-15T18:37:19.698Z",
+ "template": false,
+ "description": "",
+ "group_id": 5,
+ "type": "GroupLabel",
+ "priorities": []
+ }
+ },
+ {
+ "id": 11,
+ "label_id": 2,
+ "target_id": 1,
+ "target_type": "Issue",
+ "created_at": "2017-08-15T18:37:40.795Z",
+ "updated_at": "2017-08-15T18:37:40.795Z",
+ "label": {
+ "id": 6,
+ "title": "project label",
+ "color": "#A8D695",
+ "project_id": null,
+ "created_at": "2017-08-15T18:37:19.698Z",
+ "updated_at": "2017-08-15T18:37:19.698Z",
+ "template": false,
+ "description": "",
+ "group_id": 5,
+ "type": "ProjectLabel",
+ "priorities": []
+ }
+ }
+ ]
+ }
+ ],
"snippets": [
],
"hooks": [
]
-} \ No newline at end of file
+}
diff --git a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb
index 7ee0e22f28d..956f1d56eb4 100644
--- a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb
+++ b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb
@@ -183,7 +183,8 @@ describe Gitlab::ImportExport::ProjectTreeRestorer do
let(:restored_project_json) { project_tree_restorer.restore }
before do
- allow(ImportExport).to receive(:project_filename).and_return('project.light.json')
+ project_tree_restorer.instance_variable_set(:@path, "spec/lib/gitlab/import_export/project.light.json")
+
allow(shared).to receive(:export_path).and_return('spec/lib/gitlab/import_export/')
end
@@ -195,7 +196,7 @@ describe Gitlab::ImportExport::ProjectTreeRestorer do
restored_project_json
- expect(shared.errors.first).not_to include('test')
+ expect(shared.errors.first).to be_nil
end
end
end
@@ -219,15 +220,42 @@ describe Gitlab::ImportExport::ProjectTreeRestorer do
end
before do
+ project_tree_restorer.instance_variable_set(:@path, "spec/lib/gitlab/import_export/project.light.json")
+
restored_project_json
end
- it 'has group labels' do
- expect(GroupLabel.count).to eq(1)
+ it 'correctly restores project' do
+ expect(restored_project_json).to be_truthy
+ expect(shared.errors).to be_empty
+ end
+
+ it 'has labels' do
+ expect(project.labels.count).to eq(2)
+ end
+
+ it 'creates group label' do
+ expect(project.group.labels.count).to eq(1)
end
it 'has label priorities' do
- expect(GroupLabel.first.priorities).not_to be_empty
+ expect(project.labels.first.priorities).not_to be_empty
+ end
+
+ it 'has milestones' do
+ expect(project.milestones.count).to eq(1)
+ end
+
+ it 'has issue' do
+ expect(project.issues.count).to eq(1)
+ expect(project.issues.first.labels.count).to eq(2)
+ end
+
+ it 'has issue with group label and project label' do
+ labels = project.issues.first.labels
+
+ expect(labels.where(type: "GroupLabel").count).to eq(1)
+ expect(labels.where(type: "ProjectLabel").count).to eq(1)
end
end
end
diff --git a/spec/lib/gitlab/key_fingerprint_spec.rb b/spec/lib/gitlab/key_fingerprint_spec.rb
index f5fd5a96bc9..d643dc5342d 100644
--- a/spec/lib/gitlab/key_fingerprint_spec.rb
+++ b/spec/lib/gitlab/key_fingerprint_spec.rb
@@ -30,8 +30,8 @@ describe Gitlab::KeyFingerprint, lib: true do
MD5_FINGERPRINTS = {
rsa: '06:b2:8a:92:df:0e:11:2c:ca:7b:8f:a4:ba:6e:4b:fd',
- ecdsa: '45:ff:5b:98:9a:b6:8a:41:13:c1:30:8b:09:5e:7b:4e',
- ed25519: '2e:65:6a:c8:cf:bf:b2:8b:9a:bd:6d:9f:11:5c:12:16',
+ ecdsa: '45:ff:5b:98:9a:b6:8a:41:13:c1:30:8b:09:5e:7b:4e',
+ ed25519: '2e:65:6a:c8:cf:bf:b2:8b:9a:bd:6d:9f:11:5c:12:16',
dss: '57:98:86:02:5f:9c:f4:9b:ad:5a:1e:51:92:0e:fd:2b'
}.freeze
diff --git a/spec/lib/gitlab/ldap/auth_hash_spec.rb b/spec/lib/gitlab/ldap/auth_hash_spec.rb
index 57a91193004..8370adf9211 100644
--- a/spec/lib/gitlab/ldap/auth_hash_spec.rb
+++ b/spec/lib/gitlab/ldap/auth_hash_spec.rb
@@ -4,8 +4,8 @@ describe Gitlab::LDAP::AuthHash do
let(:auth_hash) do
described_class.new(
OmniAuth::AuthHash.new(
- uid: '123456',
- provider: 'ldapmain',
+ uid: '123456',
+ provider: 'ldapmain',
info: info,
extra: {
raw_info: raw_info
@@ -33,11 +33,11 @@ describe Gitlab::LDAP::AuthHash do
context "without overridden attributes" do
it "has the correct username" do
- expect(auth_hash.username).to eq("123456")
+ expect(auth_hash.username).to eq("123456")
end
it "has the correct name" do
- expect(auth_hash.name).to eq("Smith, J.")
+ expect(auth_hash.name).to eq("Smith, J.")
end
end
@@ -54,11 +54,11 @@ describe Gitlab::LDAP::AuthHash do
end
it "has the correct username" do
- expect(auth_hash.username).to eq("johnsmith@example.com")
+ expect(auth_hash.username).to eq("johnsmith@example.com")
end
it "has the correct name" do
- expect(auth_hash.name).to eq("John Smith")
+ expect(auth_hash.name).to eq("John Smith")
end
end
end
diff --git a/spec/lib/gitlab/o_auth/user_spec.rb b/spec/lib/gitlab/o_auth/user_spec.rb
index 15edb820908..2cf0f7516de 100644
--- a/spec/lib/gitlab/o_auth/user_spec.rb
+++ b/spec/lib/gitlab/o_auth/user_spec.rb
@@ -481,7 +481,7 @@ describe Gitlab::OAuth::User do
email: 'admin@othermail.com'
}
end
-
+
it 'generates the username with a counter' do
expect(gl_user.username).to eq('admin1')
end
diff --git a/spec/lib/gitlab/prometheus/additional_metrics_parser_spec.rb b/spec/lib/gitlab/prometheus/additional_metrics_parser_spec.rb
index d7df4e35c31..5589db92b1d 100644
--- a/spec/lib/gitlab/prometheus/additional_metrics_parser_spec.rb
+++ b/spec/lib/gitlab/prometheus/additional_metrics_parser_spec.rb
@@ -24,7 +24,7 @@ describe Gitlab::Prometheus::AdditionalMetricsParser do
queries: [{ query_range: 'query_range_empty' }]
- group: group_b
priority: 1
- metrics:
+ metrics:
- title: title
required_metrics: ['metric_a']
weight: 1
@@ -148,7 +148,7 @@ describe Gitlab::Prometheus::AdditionalMetricsParser do
- group: group_a
priority: 1
metrics:
- - title:
+ - title:
required_metrics: []
weight: 1
queries: []
diff --git a/spec/migrations/README.md b/spec/migrations/README.md
index 05d4f35db72..45cf25b96de 100644
--- a/spec/migrations/README.md
+++ b/spec/migrations/README.md
@@ -28,6 +28,14 @@ The `after` hook will migrate the database **up** and reinstitutes the latest
schema version, so that the process does not affect subsequent specs and
ensures proper isolation.
+## Testing a class that is not an ActiveRecord::Migration
+
+In order to test a class that is not a migration itself, you will need to
+manually provide a required schema version. Please add a `schema` tag to a
+context that you want to switch the database schema within.
+
+Example: `describe SomeClass, :migration, schema: 20170608152748`.
+
## Available helpers
Use `table` helper to create a temporary `ActiveRecord::Base` derived model
@@ -80,8 +88,6 @@ end
## Best practices
-1. Use only one test example per migration unless there is a good reason to
-use more.
1. Note that this type of tests do not run within the transaction, we use
a truncation database cleanup strategy. Do not depend on transaction being
present.
diff --git a/spec/migrations/migrate_old_artifacts_spec.rb b/spec/migrations/migrate_old_artifacts_spec.rb
index cfe1ca481b2..81366d15b34 100644
--- a/spec/migrations/migrate_old_artifacts_spec.rb
+++ b/spec/migrations/migrate_old_artifacts_spec.rb
@@ -10,7 +10,7 @@ describe MigrateOldArtifacts do
before do
allow(Gitlab.config.artifacts).to receive(:path).and_return(directory)
end
-
+
after do
FileUtils.remove_entry_secure(directory)
end
@@ -95,7 +95,7 @@ describe MigrateOldArtifacts do
FileUtils.copy(
Rails.root.join('spec/fixtures/ci_build_artifacts.zip'),
File.join(legacy_path(build), "ci_build_artifacts.zip"))
-
+
FileUtils.copy(
Rails.root.join('spec/fixtures/ci_build_artifacts_metadata.gz'),
File.join(legacy_path(build), "ci_build_artifacts_metadata.gz"))
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index eba71ba2f72..5e60511f3a8 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -1610,8 +1610,7 @@ describe Project do
it 'imports a project' do
expect_any_instance_of(RepositoryImportWorker).to receive(:perform).and_call_original
- project.import_schedule
-
+ expect { project.import_schedule }.to change { project.import_jid }
expect(project.reload.import_status).to eq('finished')
end
end
@@ -1624,6 +1623,13 @@ describe Project do
allow(Projects::HousekeepingService).to receive(:new) { housekeeping_service }
end
+ it 'resets project import_error' do
+ error_message = 'Some error'
+ mirror = create(:project_empty_repo, :import_started, import_error: error_message)
+
+ expect { mirror.import_finish }.to change { mirror.import_error }.from(error_message).to(nil)
+ end
+
it 'performs housekeeping when an import of a fresh project is completed' do
project = create(:project_empty_repo, :import_started, import_type: :github)
@@ -1730,17 +1736,21 @@ describe Project do
end
describe '#add_import_job' do
+ let(:import_jid) { '123' }
+
context 'forked' do
let(:forked_project_link) { create(:forked_project_link, :forked_to_empty_project) }
let(:forked_from_project) { forked_project_link.forked_from_project }
let(:project) { forked_project_link.forked_to_project }
it 'schedules a RepositoryForkWorker job' do
- expect(RepositoryForkWorker).to receive(:perform_async)
- .with(project.id, forked_from_project.repository_storage_path,
- forked_from_project.disk_path, project.namespace.full_path)
+ expect(RepositoryForkWorker).to receive(:perform_async).with(
+ project.id,
+ forked_from_project.repository_storage_path,
+ forked_from_project.disk_path,
+ project.namespace.full_path).and_return(import_jid)
- project.add_import_job
+ expect(project.add_import_job).to eq(import_jid)
end
end
@@ -1748,9 +1758,8 @@ describe Project do
it 'schedules a RepositoryImportWorker job' do
project = create(:project, import_url: generate(:url))
- expect(RepositoryImportWorker).to receive(:perform_async).with(project.id)
-
- project.add_import_job
+ expect(RepositoryImportWorker).to receive(:perform_async).with(project.id).and_return(import_jid)
+ expect(project.add_import_job).to eq(import_jid)
end
end
end
@@ -2311,6 +2320,44 @@ describe Project do
end
end
+ describe '#remove_pages' do
+ let(:project) { create(:project) }
+ let(:namespace) { project.namespace }
+ let(:pages_path) { project.pages_path }
+
+ around do |example|
+ FileUtils.mkdir_p(pages_path)
+ begin
+ example.run
+ ensure
+ FileUtils.rm_rf(pages_path)
+ end
+ end
+
+ it 'removes the pages directory' do
+ expect_any_instance_of(Projects::UpdatePagesConfigurationService).to receive(:execute)
+ expect_any_instance_of(Gitlab::PagesTransfer).to receive(:rename_project).and_return(true)
+ expect(PagesWorker).to receive(:perform_in).with(5.minutes, :remove, namespace.full_path, anything)
+
+ project.remove_pages
+ end
+
+ it 'is a no-op when there is no namespace' do
+ project.update_column(:namespace_id, nil)
+
+ expect_any_instance_of(Projects::UpdatePagesConfigurationService).not_to receive(:execute)
+ expect_any_instance_of(Gitlab::PagesTransfer).not_to receive(:rename_project)
+
+ project.remove_pages
+ end
+
+ it 'is run when the project is destroyed' do
+ expect(project).to receive(:remove_pages).and_call_original
+
+ project.destroy
+ end
+ end
+
describe '#forks_count' do
it 'returns the number of forks' do
project = build(:project)
diff --git a/spec/models/protectable_dropdown_spec.rb b/spec/models/protectable_dropdown_spec.rb
index 5c5dcd9f5c9..d4433a88a15 100644
--- a/spec/models/protectable_dropdown_spec.rb
+++ b/spec/models/protectable_dropdown_spec.rb
@@ -4,6 +4,13 @@ describe ProtectableDropdown do
let(:project) { create(:project, :repository) }
let(:subject) { described_class.new(project, :branches) }
+ describe 'initialize' do
+ it 'raises ArgumentError for invalid ref type' do
+ expect { described_class.new(double, :foo) }
+ .to raise_error(ArgumentError, "invalid ref type `foo`")
+ end
+ end
+
describe '#protectable_ref_names' do
before do
project.protected_branches.create(name: 'master')
diff --git a/spec/requests/api/commits_spec.rb b/spec/requests/api/commits_spec.rb
index 992a6e8d76a..dafe3f466a2 100644
--- a/spec/requests/api/commits_spec.rb
+++ b/spec/requests/api/commits_spec.rb
@@ -16,11 +16,13 @@ describe API::Commits do
end
describe 'GET /projects/:id/repository/commits' do
- context 'authorized user' do
+ let(:route) { "/projects/#{project_id}/repository/commits" }
+
+ shared_examples_for 'project commits' do
it "returns project commits" do
commit = project.repository.commit
- get api("/projects/#{project_id}/repository/commits", user)
+ get api(route, current_user)
expect(response).to have_http_status(200)
expect(response).to match_response_schema('public_api/v4/commits')
@@ -32,7 +34,7 @@ describe API::Commits do
it 'include correct pagination headers' do
commit_count = project.repository.count_commits(ref: 'master').to_s
- get api("/projects/#{project_id}/repository/commits", user)
+ get api(route, current_user)
expect(response).to include_pagination_headers
expect(response.headers['X-Total']).to eq(commit_count)
@@ -40,140 +42,151 @@ describe API::Commits do
end
end
- context "unauthorized user" do
- it "does not return project commits" do
- get api("/projects/#{project_id}/repository/commits")
+ context 'when unauthenticated', 'and project is public' do
+ let(:project) { create(:project, :public, :repository) }
+
+ it_behaves_like 'project commits'
+ end
- expect(response).to have_http_status(404)
+ context 'when unauthenticated', 'and project is private' do
+ it_behaves_like '404 response' do
+ let(:request) { get api(route) }
+ let(:message) { '404 Project Not Found' }
end
end
- context "since optional parameter" do
- it "returns project commits since provided parameter" do
- commits = project.repository.commits("master")
- after = commits.second.created_at
+ context 'when authenticated', 'as a master' do
+ let(:current_user) { user }
- get api("/projects/#{project_id}/repository/commits?since=#{after.utc.iso8601}", user)
+ it_behaves_like 'project commits'
- expect(json_response.size).to eq 2
- expect(json_response.first["id"]).to eq(commits.first.id)
- expect(json_response.second["id"]).to eq(commits.second.id)
- end
+ context "since optional parameter" do
+ it "returns project commits since provided parameter" do
+ commits = project.repository.commits("master")
+ after = commits.second.created_at
- it 'include correct pagination headers' do
- commits = project.repository.commits("master")
- after = commits.second.created_at
- commit_count = project.repository.count_commits(ref: 'master', after: after).to_s
+ get api("/projects/#{project_id}/repository/commits?since=#{after.utc.iso8601}", user)
- get api("/projects/#{project_id}/repository/commits?since=#{after.utc.iso8601}", user)
+ expect(json_response.size).to eq 2
+ expect(json_response.first["id"]).to eq(commits.first.id)
+ expect(json_response.second["id"]).to eq(commits.second.id)
+ end
- expect(response).to include_pagination_headers
- expect(response.headers['X-Total']).to eq(commit_count)
- expect(response.headers['X-Page']).to eql('1')
+ it 'include correct pagination headers' do
+ commits = project.repository.commits("master")
+ after = commits.second.created_at
+ commit_count = project.repository.count_commits(ref: 'master', after: after).to_s
+
+ get api("/projects/#{project_id}/repository/commits?since=#{after.utc.iso8601}", user)
+
+ expect(response).to include_pagination_headers
+ expect(response.headers['X-Total']).to eq(commit_count)
+ expect(response.headers['X-Page']).to eql('1')
+ end
end
- end
- context "until optional parameter" do
- it "returns project commits until provided parameter" do
- commits = project.repository.commits("master")
- before = commits.second.created_at
+ context "until optional parameter" do
+ it "returns project commits until provided parameter" do
+ commits = project.repository.commits("master")
+ before = commits.second.created_at
- get api("/projects/#{project_id}/repository/commits?until=#{before.utc.iso8601}", user)
+ get api("/projects/#{project_id}/repository/commits?until=#{before.utc.iso8601}", user)
- if commits.size >= 20
- expect(json_response.size).to eq(20)
- else
- expect(json_response.size).to eq(commits.size - 1)
- end
+ 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
+ expect(json_response.first["id"]).to eq(commits.second.id)
+ expect(json_response.second["id"]).to eq(commits.third.id)
+ end
- it 'include correct pagination headers' do
- commits = project.repository.commits("master")
- before = commits.second.created_at
- commit_count = project.repository.count_commits(ref: 'master', before: before).to_s
+ it 'include correct pagination headers' do
+ commits = project.repository.commits("master")
+ before = commits.second.created_at
+ commit_count = project.repository.count_commits(ref: 'master', before: before).to_s
- get api("/projects/#{project_id}/repository/commits?until=#{before.utc.iso8601}", user)
+ get api("/projects/#{project_id}/repository/commits?until=#{before.utc.iso8601}", user)
- expect(response).to include_pagination_headers
- expect(response.headers['X-Total']).to eq(commit_count)
- expect(response.headers['X-Page']).to eql('1')
+ expect(response).to include_pagination_headers
+ expect(response.headers['X-Total']).to eq(commit_count)
+ expect(response.headers['X-Page']).to eql('1')
+ end
end
- end
- context "invalid xmlschema date parameters" do
- it "returns an invalid parameter error message" do
- get api("/projects/#{project_id}/repository/commits?since=invalid-date", user)
+ context "invalid xmlschema date parameters" do
+ it "returns an invalid parameter error message" do
+ get api("/projects/#{project_id}/repository/commits?since=invalid-date", user)
- expect(response).to have_http_status(400)
- expect(json_response['error']).to eq('since is invalid')
+ expect(response).to have_http_status(400)
+ expect(json_response['error']).to eq('since is invalid')
+ end
end
- end
- context "path optional parameter" do
- it "returns project commits matching provided path parameter" do
- path = 'files/ruby/popen.rb'
- commit_count = project.repository.count_commits(ref: 'master', path: path).to_s
+ context "path optional parameter" do
+ it "returns project commits matching provided path parameter" do
+ path = 'files/ruby/popen.rb'
+ commit_count = project.repository.count_commits(ref: 'master', path: path).to_s
- get api("/projects/#{project_id}/repository/commits?path=#{path}", user)
+ get api("/projects/#{project_id}/repository/commits?path=#{path}", user)
- expect(json_response.size).to eq(3)
- expect(json_response.first["id"]).to eq("570e7b2abdd848b95f2f578043fc23bd6f6fd24d")
- expect(response).to include_pagination_headers
- expect(response.headers['X-Total']).to eq(commit_count)
- end
+ expect(json_response.size).to eq(3)
+ expect(json_response.first["id"]).to eq("570e7b2abdd848b95f2f578043fc23bd6f6fd24d")
+ expect(response).to include_pagination_headers
+ expect(response.headers['X-Total']).to eq(commit_count)
+ end
- it 'include correct pagination headers' do
- path = 'files/ruby/popen.rb'
- commit_count = project.repository.count_commits(ref: 'master', path: path).to_s
+ it 'include correct pagination headers' do
+ path = 'files/ruby/popen.rb'
+ commit_count = project.repository.count_commits(ref: 'master', path: path).to_s
- get api("/projects/#{project_id}/repository/commits?path=#{path}", user)
+ get api("/projects/#{project_id}/repository/commits?path=#{path}", user)
- expect(response).to include_pagination_headers
- expect(response.headers['X-Total']).to eq(commit_count)
- expect(response.headers['X-Page']).to eql('1')
+ expect(response).to include_pagination_headers
+ expect(response.headers['X-Total']).to eq(commit_count)
+ expect(response.headers['X-Page']).to eql('1')
+ end
end
- end
- context 'with pagination params' do
- let(:page) { 1 }
- let(:per_page) { 5 }
- let(:ref_name) { 'master' }
- let!(:request) do
- get api("/projects/#{project_id}/repository/commits?page=#{page}&per_page=#{per_page}&ref_name=#{ref_name}", user)
- end
+ context 'with pagination params' do
+ let(:page) { 1 }
+ let(:per_page) { 5 }
+ let(:ref_name) { 'master' }
+ let!(:request) do
+ get api("/projects/#{project_id}/repository/commits?page=#{page}&per_page=#{per_page}&ref_name=#{ref_name}", user)
+ end
- it 'returns correct headers' do
- commit_count = project.repository.count_commits(ref: ref_name).to_s
+ it 'returns correct headers' do
+ commit_count = project.repository.count_commits(ref: ref_name).to_s
- expect(response).to include_pagination_headers
- expect(response.headers['X-Total']).to eq(commit_count)
- expect(response.headers['X-Page']).to eq('1')
- expect(response.headers['Link']).to match(/page=1&per_page=5/)
- expect(response.headers['Link']).to match(/page=2&per_page=5/)
- end
+ expect(response).to include_pagination_headers
+ expect(response.headers['X-Total']).to eq(commit_count)
+ expect(response.headers['X-Page']).to eq('1')
+ expect(response.headers['Link']).to match(/page=1&per_page=5/)
+ expect(response.headers['Link']).to match(/page=2&per_page=5/)
+ end
- context 'viewing the first page' do
- it 'returns the first 5 commits' do
- commit = project.repository.commit
+ context 'viewing the first page' do
+ it 'returns the first 5 commits' do
+ commit = project.repository.commit
- expect(json_response.size).to eq(per_page)
- expect(json_response.first['id']).to eq(commit.id)
- expect(response.headers['X-Page']).to eq('1')
+ expect(json_response.size).to eq(per_page)
+ expect(json_response.first['id']).to eq(commit.id)
+ expect(response.headers['X-Page']).to eq('1')
+ end
end
- end
- context 'viewing the third page' do
- let(:page) { 3 }
+ context 'viewing the third page' do
+ let(:page) { 3 }
- it 'returns the third 5 commits' do
- commit = project.repository.commits('HEAD', offset: (page - 1) * per_page).first
+ it 'returns the third 5 commits' do
+ commit = project.repository.commits('HEAD', offset: (page - 1) * per_page).first
- expect(json_response.size).to eq(per_page)
- expect(json_response.first['id']).to eq(commit.id)
- expect(response.headers['X-Page']).to eq('3')
+ expect(json_response.size).to eq(per_page)
+ expect(json_response.first['id']).to eq(commit.id)
+ expect(response.headers['X-Page']).to eq('3')
+ end
end
end
end
diff --git a/spec/requests/api/protected_branches_spec.rb b/spec/requests/api/protected_branches_spec.rb
index e4f9c47fb33..1aa8a95780e 100644
--- a/spec/requests/api/protected_branches_spec.rb
+++ b/spec/requests/api/protected_branches_spec.rb
@@ -96,7 +96,7 @@ describe API::ProtectedBranches do
describe 'POST /projects/:id/protected_branches' do
let(:branch_name) { 'new_branch' }
- context 'when authenticated as a master' do
+ context 'when authenticated as a master' do
before do
project.add_master(user)
end
@@ -221,7 +221,7 @@ describe API::ProtectedBranches do
context 'when branch has a wildcard in its name' do
let(:protected_name) { 'feature*' }
-
+
it "unprotects a wildcard branch" do
delete api("/projects/#{project.id}/protected_branches/#{branch_name}", user)
diff --git a/spec/requests/api/settings_spec.rb b/spec/requests/api/settings_spec.rb
index 97275b80d03..737c028ad53 100644
--- a/spec/requests/api/settings_spec.rb
+++ b/spec/requests/api/settings_spec.rb
@@ -45,7 +45,7 @@ describe API::Settings, 'Settings' do
help_page_hide_commercial_content: true,
help_page_support_url: 'http://example.com/help',
project_export_enabled: false
-
+
expect(response).to have_http_status(200)
expect(json_response['default_projects_limit']).to eq(3)
expect(json_response['password_authentication_enabled']).to be_falsey
diff --git a/spec/requests/ci/api/builds_spec.rb b/spec/requests/ci/api/builds_spec.rb
index ebd67eb1e94..7ccba4ba3ec 100644
--- a/spec/requests/ci/api/builds_spec.rb
+++ b/spec/requests/ci/api/builds_spec.rb
@@ -130,7 +130,7 @@ describe Ci::API::Builds do
register_builds info: { platform: :darwin }
expect(response).to have_http_status(201)
-
+
expect(json_response["options"]).to be_empty
end
end
diff --git a/spec/services/git_push_service_spec.rb b/spec/services/git_push_service_spec.rb
index 8485605b398..e3c1bdce300 100644
--- a/spec/services/git_push_service_spec.rb
+++ b/spec/services/git_push_service_spec.rb
@@ -688,10 +688,38 @@ describe GitPushService, services: true do
)
end
- it 'calls CreateGpgSignatureWorker.perform_async for each commit' do
- expect(CreateGpgSignatureWorker).to receive(:perform_async).with(sample_commit.id, project.id)
+ context 'when the commit has a signature' do
+ context 'when the signature is already cached' do
+ before do
+ create(:gpg_signature, commit_sha: sample_commit.id)
+ end
- execute_service(project, user, oldrev, newrev, ref)
+ it 'does not queue a CreateGpgSignatureWorker' do
+ expect(CreateGpgSignatureWorker).not_to receive(:perform_async).with(sample_commit.id, project.id)
+
+ execute_service(project, user, oldrev, newrev, ref)
+ end
+ end
+
+ context 'when the signature is not yet cached' do
+ it 'queues a CreateGpgSignatureWorker' do
+ expect(CreateGpgSignatureWorker).to receive(:perform_async).with(sample_commit.id, project.id)
+
+ execute_service(project, user, oldrev, newrev, ref)
+ end
+ end
+ end
+
+ context 'when the commit does not have a signature' do
+ before do
+ allow(Gitlab::Git::Commit).to receive(:shas_with_signatures).with(project.repository, [sample_commit.id]).and_return([])
+ end
+
+ it 'does not queue a CreateGpgSignatureWorker' do
+ expect(CreateGpgSignatureWorker).not_to receive(:perform_async).with(sample_commit.id, project.id)
+
+ execute_service(project, user, oldrev, newrev, ref)
+ end
end
end
diff --git a/spec/services/web_hook_service_spec.rb b/spec/services/web_hook_service_spec.rb
index 365cb6b8f09..0726e135b20 100644
--- a/spec/services/web_hook_service_spec.rb
+++ b/spec/services/web_hook_service_spec.rb
@@ -144,7 +144,7 @@ describe WebHookService do
describe '#async_execute' do
let(:system_hook) { create(:system_hook) }
-
+
it 'enqueue WebHookWorker' do
expect(Sidekiq::Client).to receive(:enqueue).with(WebHookWorker, project_hook.id, data, 'push_hooks')
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 3eea39d4bf4..c10197ff651 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -132,17 +132,12 @@ RSpec.configure do |config|
Sidekiq.redis(&:flushall)
end
- config.before(:example, :migration) do
- ActiveRecord::Migrator
- .migrate(migrations_paths, previous_migration.version)
-
- reset_column_in_migration_models
+ config.before(:each, :migration) do
+ schema_migrate_down!
end
- config.after(:example, :migration) do
- ActiveRecord::Migrator.migrate(migrations_paths)
-
- reset_column_in_migration_models
+ config.after(:context, :migration) do
+ schema_migrate_up!
end
config.around(:each, :nested_groups) do |example|
diff --git a/spec/support/db_cleaner.rb b/spec/support/db_cleaner.rb
index 7f5769209bb..b0f520d08e8 100644
--- a/spec/support/db_cleaner.rb
+++ b/spec/support/db_cleaner.rb
@@ -20,7 +20,7 @@ RSpec.configure do |config|
end
config.before(:each, :migration) do
- DatabaseCleaner.strategy = :truncation
+ DatabaseCleaner.strategy = :truncation, { cache_tables: false }
end
config.before(:each) do
diff --git a/spec/support/filtered_search_helpers.rb b/spec/support/filtered_search_helpers.rb
index d21c4324d9e..99b8b6b7ea4 100644
--- a/spec/support/filtered_search_helpers.rb
+++ b/spec/support/filtered_search_helpers.rb
@@ -54,8 +54,8 @@ module FilteredSearchHelpers
# Iterates through each visual token inside
# .tokens-container to make sure the correct names and values are rendered
def expect_tokens(tokens)
- page.find '.filtered-search-box .tokens-container' do
- page.all(:css, '.tokens-container li').each_with_index do |el, index|
+ page.within '.filtered-search-box .tokens-container' do
+ page.all(:css, '.tokens-container li .selectable').each_with_index do |el, index|
token_name = tokens[index][:name]
token_value = tokens[index][:value]
@@ -67,6 +67,28 @@ module FilteredSearchHelpers
end
end
+ def create_token(token_name, token_value = nil, symbol = nil)
+ { name: token_name, value: "#{symbol}#{token_value}" }
+ end
+
+ def author_token(author_name = nil)
+ create_token('Author', author_name)
+ end
+
+ def assignee_token(assignee_name = nil)
+ create_token('Assignee', assignee_name)
+ end
+
+ def milestone_token(milestone_name = nil, has_symbol = true)
+ symbol = has_symbol ? '%' : nil
+ create_token('Milestone', milestone_name, symbol)
+ end
+
+ def label_token(label_name = nil, has_symbol = true)
+ symbol = has_symbol ? '~' : nil
+ create_token('Label', label_name, symbol)
+ end
+
def default_placeholder
'Search or filter results...'
end
diff --git a/spec/support/generate-seed-repo-rb b/spec/support/generate-seed-repo-rb
index c89389b90ca..ef3c8e7087f 100755
--- a/spec/support/generate-seed-repo-rb
+++ b/spec/support/generate-seed-repo-rb
@@ -1,16 +1,16 @@
#!/usr/bin/env ruby
-#
+#
# # generate-seed-repo-rb
-#
+#
# This script generates the seed_repo.rb file used by lib/gitlab/git
# tests. The seed_repo.rb file needs to be updated anytime there is a
# Git push to https://gitlab.com/gitlab-org/gitlab-git-test.
-#
+#
# Usage:
-#
+#
# ./spec/support/generate-seed-repo-rb > spec/support/seed_repo.rb
-#
-#
+#
+#
require 'erb'
require 'tempfile'
diff --git a/spec/support/matchers/access_matchers_for_controller.rb b/spec/support/matchers/access_matchers_for_controller.rb
index ff60bd0c0ae..bb6b7c63ee9 100644
--- a/spec/support/matchers/access_matchers_for_controller.rb
+++ b/spec/support/matchers/access_matchers_for_controller.rb
@@ -1,6 +1,6 @@
# AccessMatchersForController
#
-# For testing authorize_xxx in controller.
+# For testing authorize_xxx in controller.
module AccessMatchersForController
extend RSpec::Matchers::DSL
include Warden::Test::Helpers
diff --git a/spec/support/migrations_helpers.rb b/spec/support/migrations_helpers.rb
index aabdad13047..255b3d96a62 100644
--- a/spec/support/migrations_helpers.rb
+++ b/spec/support/migrations_helpers.rb
@@ -31,6 +31,35 @@ module MigrationsHelpers
end
end
+ def migration_schema_version
+ self.class.metadata[:schema] || previous_migration.version
+ end
+
+ def schema_migrate_down!
+ disable_migrations_output do
+ ActiveRecord::Migrator.migrate(migrations_paths,
+ migration_schema_version)
+ end
+
+ reset_column_in_migration_models
+ end
+
+ def schema_migrate_up!
+ disable_migrations_output do
+ ActiveRecord::Migrator.migrate(migrations_paths)
+ end
+
+ reset_column_in_migration_models
+ end
+
+ def disable_migrations_output
+ ActiveRecord::Migration.verbose = false
+
+ yield
+ ensure
+ ActiveRecord::Migration.verbose = true
+ end
+
def migrate!
ActiveRecord::Migrator.up(migrations_paths) do |migration|
migration.name == described_class.name
diff --git a/spec/support/stub_configuration.rb b/spec/support/stub_configuration.rb
index 37c89d37aa0..45c10e78789 100644
--- a/spec/support/stub_configuration.rb
+++ b/spec/support/stub_configuration.rb
@@ -39,14 +39,17 @@ module StubConfiguration
end
def stub_storage_settings(messages)
+ # Default storage is always required
+ messages['default'] ||= Gitlab.config.repositories.storages.default
messages.each do |storage_name, storage_settings|
+ storage_settings['path'] ||= TestEnv.repos_path
storage_settings['failure_count_threshold'] ||= 10
storage_settings['failure_wait_time'] ||= 30
storage_settings['failure_reset_time'] ||= 1800
storage_settings['storage_timeout'] ||= 5
end
- allow(Gitlab.config.repositories).to receive(:storages).and_return(messages)
+ allow(Gitlab.config.repositories).to receive(:storages).and_return(Settingslogic.new(messages))
end
private
diff --git a/spec/tasks/gitlab/gitaly_rake_spec.rb b/spec/tasks/gitlab/gitaly_rake_spec.rb
index b29d63c7d67..1e9b20435ec 100644
--- a/spec/tasks/gitlab/gitaly_rake_spec.rb
+++ b/spec/tasks/gitlab/gitaly_rake_spec.rb
@@ -89,7 +89,7 @@ describe 'gitlab:gitaly namespace rake task' do
it 'calls make in the gitaly directory without BUNDLE_PATH' do
expect(main_object).to receive(:run_command!).with(command_preamble + ['make']).and_return(true)
-
+
run_rake_task('gitlab:gitaly:install', clone_path)
end
end
diff --git a/spec/workers/create_gpg_signature_worker_spec.rb b/spec/workers/create_gpg_signature_worker_spec.rb
index c6a17d77d73..54978baca88 100644
--- a/spec/workers/create_gpg_signature_worker_spec.rb
+++ b/spec/workers/create_gpg_signature_worker_spec.rb
@@ -1,34 +1,26 @@
require 'spec_helper'
describe CreateGpgSignatureWorker do
+ let(:project) { create(:project, :repository) }
+
context 'when GpgKey is found' do
- it 'calls Commit#signature' do
- commit_sha = '0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33'
- project = create :project
- commit = instance_double(Commit)
+ let(:commit_sha) { '0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33' }
- allow(Project).to receive(:find_by).with(id: project.id).and_return(project)
- allow(project).to receive(:commit).with(commit_sha).and_return(commit)
+ it 'calls Gitlab::Gpg::Commit#signature' do
+ expect(Gitlab::Gpg::Commit).to receive(:new).with(project, commit_sha).and_call_original
- expect(commit).to receive(:signature)
+ expect_any_instance_of(Gitlab::Gpg::Commit).to receive(:signature)
described_class.new.perform(commit_sha, project.id)
end
end
context 'when Commit is not found' do
- let(:nonexisting_commit_sha) { 'bogus' }
- let(:project) { create :project }
+ let(:nonexisting_commit_sha) { '0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a34' }
it 'does not raise errors' do
expect { described_class.new.perform(nonexisting_commit_sha, project.id) }.not_to raise_error
end
-
- it 'does not call Commit#signature' do
- expect_any_instance_of(Commit).not_to receive(:signature)
-
- described_class.new.perform(nonexisting_commit_sha, project.id)
- end
end
context 'when Project is not found' do
@@ -38,8 +30,8 @@ describe CreateGpgSignatureWorker do
expect { described_class.new.perform(anything, nonexisting_project_id) }.not_to raise_error
end
- it 'does not call Commit#signature' do
- expect_any_instance_of(Commit).not_to receive(:signature)
+ it 'does not call Gitlab::Gpg::Commit#signature' do
+ expect_any_instance_of(Gitlab::Gpg::Commit).not_to receive(:signature)
described_class.new.perform(anything, nonexisting_project_id)
end
diff --git a/spec/workers/repository_import_worker_spec.rb b/spec/workers/repository_import_worker_spec.rb
index ca904e512ac..100dfc32bbe 100644
--- a/spec/workers/repository_import_worker_spec.rb
+++ b/spec/workers/repository_import_worker_spec.rb
@@ -22,8 +22,8 @@ describe RepositoryImportWorker do
it 'hide the credentials that were used in the import URL' do
error = %q{remote: Not Found fatal: repository 'https://user:pass@test.com/root/repoC.git/' not found }
+ project.update_attributes(import_jid: '123')
expect_any_instance_of(Projects::ImportService).to receive(:execute).and_return({ status: :error, message: error })
- allow(subject).to receive(:jid).and_return('123')
expect do
subject.perform(project.id)
diff --git a/spec/workers/stuck_import_jobs_worker_spec.rb b/spec/workers/stuck_import_jobs_worker_spec.rb
index 2f5b685a332..a82eb54ffe4 100644
--- a/spec/workers/stuck_import_jobs_worker_spec.rb
+++ b/spec/workers/stuck_import_jobs_worker_spec.rb
@@ -8,29 +8,29 @@ describe StuckImportJobsWorker do
allow_any_instance_of(Gitlab::ExclusiveLease).to receive(:try_obtain).and_return(exclusive_lease_uuid)
end
- describe 'long running import' do
- let(:project) { create(:project, import_jid: '123', import_status: 'started') }
+ describe 'with started import_status' do
+ let(:project) { create(:project, :import_started, import_jid: '123') }
- before do
- allow(Gitlab::SidekiqStatus).to receive(:completed_jids).and_return(['123'])
- end
+ describe 'long running import' do
+ it 'marks the project as failed' do
+ allow(Gitlab::SidekiqStatus).to receive(:completed_jids).and_return(['123'])
- it 'marks the project as failed' do
- expect { worker.perform }.to change { project.reload.import_status }.to('failed')
+ expect { worker.perform }.to change { project.reload.import_status }.to('failed')
+ end
end
- end
- describe 'running import' do
- let(:project) { create(:project, import_jid: '123', import_status: 'started') }
-
- before do
- allow(Gitlab::SidekiqStatus).to receive(:completed_jids).and_return([])
- end
+ describe 'running import' do
+ it 'does not mark the project as failed' do
+ allow(Gitlab::SidekiqStatus).to receive(:completed_jids).and_return([])
- it 'does not mark the project as failed' do
- worker.perform
+ expect { worker.perform }.not_to change { project.reload.import_status }
+ end
- expect(project.reload.import_status).to eq('started')
+ describe 'import without import_jid' do
+ it 'marks the project as failed' do
+ expect { worker.perform }.to change { project.reload.import_status }.to('failed')
+ end
+ end
end
end
end