summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorClement Ho <clemmakesapps@gmail.com>2017-08-01 18:23:36 +0000
committerClement Ho <clemmakesapps@gmail.com>2017-08-01 18:23:36 +0000
commit9ba8685ea701a2f805d4bf6fb71ff52c49065c92 (patch)
tree13daac9aae82d3f733dd4ce46f3a28a806b27982
parentc4e7875d2909588e55c21a7cf19e31f60bce200f (diff)
parent0edf2b0d421144686ee399f44c26a2c4e1b2df05 (diff)
downloadgitlab-ce-docs-specific-review-examples.tar.gz
Merge branch 'master' into 'docs-specific-review-examples'docs-specific-review-examples
# Conflicts: # doc/development/code_review.md
-rw-r--r--.gitlab-ci.yml3
-rw-r--r--.rubocop.yml452
-rw-r--r--.rubocop_todo.yml225
-rw-r--r--CHANGELOG.md10
-rw-r--r--GITALY_SERVER_VERSION2
-rw-r--r--Gemfile4
-rw-r--r--Gemfile.lock14
-rw-r--r--PROCESS.md20
-rw-r--r--app/assets/javascripts/blob_edit/blob_bundle.js5
-rw-r--r--app/assets/javascripts/build.js19
-rw-r--r--app/assets/javascripts/dispatcher.js43
-rw-r--r--app/assets/javascripts/gl_dropdown.js6
-rw-r--r--app/assets/javascripts/graphs/graphs_bundle.js2
-rw-r--r--app/assets/javascripts/graphs/graphs_charts.js63
-rw-r--r--app/assets/javascripts/graphs/graphs_show.js21
-rw-r--r--app/assets/javascripts/pipelines/pipelines_charts.js38
-rw-r--r--app/assets/javascripts/pipelines/pipelines_times.js27
-rw-r--r--app/assets/javascripts/projects/project_new.js85
-rw-r--r--app/assets/javascripts/ref_select_dropdown.js3
-rw-r--r--app/assets/javascripts/two_factor_auth.js13
-rw-r--r--app/assets/javascripts/ui_development_kit.js22
-rw-r--r--app/assets/stylesheets/framework/avatar.scss4
-rw-r--r--app/assets/stylesheets/framework/filters.scss8
-rw-r--r--app/assets/stylesheets/framework/header.scss23
-rw-r--r--app/assets/stylesheets/framework/layout.scss4
-rw-r--r--app/assets/stylesheets/framework/nav.scss20
-rw-r--r--app/assets/stylesheets/framework/sidebar.scss4
-rw-r--r--app/assets/stylesheets/framework/variables.scss1
-rw-r--r--app/assets/stylesheets/new_nav.scss22
-rw-r--r--app/assets/stylesheets/new_sidebar.scss12
-rw-r--r--app/assets/stylesheets/pages/builds.scss19
-rw-r--r--app/assets/stylesheets/pages/cycle_analytics.scss22
-rw-r--r--app/assets/stylesheets/pages/issuable.scss8
-rw-r--r--app/assets/stylesheets/pages/merge_requests.scss4
-rw-r--r--app/assets/stylesheets/pages/tree.scss22
-rw-r--r--app/assets/stylesheets/performance_bar.scss14
-rw-r--r--app/controllers/projects/graphs_controller.rb18
-rw-r--r--app/helpers/application_helper.rb6
-rw-r--r--app/helpers/gitlab_routing_helper.rb14
-rw-r--r--app/helpers/merge_requests_helper.rb2
-rw-r--r--app/helpers/nav_helper.rb33
-rw-r--r--app/helpers/projects_helper.rb4
-rw-r--r--app/helpers/webpack_helper.rb4
-rw-r--r--app/mailers/notify.rb2
-rw-r--r--app/models/ci/pipeline.rb2
-rw-r--r--app/models/concerns/storage/legacy_namespace.rb102
-rw-r--r--app/models/concerns/storage/legacy_project.rb76
-rw-r--r--app/models/concerns/storage/legacy_project_wiki.rb9
-rw-r--r--app/models/concerns/storage/legacy_repository.rb7
-rw-r--r--app/models/merge_request.rb4
-rw-r--r--app/models/namespace.rb98
-rw-r--r--app/models/notification_setting.rb34
-rw-r--r--app/models/project.rb120
-rw-r--r--app/models/project_services/flowdock_service.rb6
-rw-r--r--app/models/project_services/jira_service.rb2
-rw-r--r--app/models/project_wiki.rb26
-rw-r--r--app/models/repository.rb22
-rw-r--r--app/policies/global_policy.rb2
-rw-r--r--app/services/git_operation_service.rb2
-rw-r--r--app/services/projects/destroy_service.rb6
-rw-r--r--app/services/projects/import_export/export_service.rb2
-rw-r--r--app/services/projects/import_service.rb4
-rw-r--r--app/services/projects/transfer_service.rb4
-rw-r--r--app/services/quick_actions/interpret_service.rb16
-rw-r--r--app/services/system_hooks_service.rb8
-rw-r--r--app/uploaders/file_uploader.rb2
-rw-r--r--app/views/admin/groups/show.html.haml4
-rw-r--r--app/views/help/ui.html.haml25
-rw-r--r--app/views/import/_githubish_status.html.haml2
-rw-r--r--app/views/import/base/create.js.haml2
-rw-r--r--app/views/import/bitbucket/status.html.haml2
-rw-r--r--app/views/import/fogbugz/status.html.haml2
-rw-r--r--app/views/import/gitlab/status.html.haml2
-rw-r--r--app/views/import/google_code/status.html.haml2
-rw-r--r--app/views/layouts/_google_analytics.html.haml1
-rw-r--r--app/views/layouts/_init_auto_complete.html.haml1
-rw-r--r--app/views/layouts/_page.html.haml4
-rw-r--r--app/views/layouts/_piwik.html.haml1
-rw-r--r--app/views/layouts/application.html.haml5
-rw-r--r--app/views/layouts/nav/_new_admin_sidebar.html.haml2
-rw-r--r--app/views/layouts/nav/_new_group_sidebar.html.haml4
-rw-r--r--app/views/layouts/project.html.haml1
-rw-r--r--app/views/layouts/snippets.html.haml1
-rw-r--r--app/views/peek/views/_host.html.haml2
-rw-r--r--app/views/profiles/personal_access_tokens/index.html.haml8
-rw-r--r--app/views/profiles/two_factor_auths/show.html.haml163
-rw-r--r--app/views/projects/_activity.html.haml6
-rw-r--r--app/views/projects/blob/_new_dir.html.haml3
-rw-r--r--app/views/projects/blob/_remove.html.haml3
-rw-r--r--app/views/projects/branches/new.html.haml6
-rw-r--r--app/views/projects/commit/_commit_box.html.haml5
-rw-r--r--app/views/projects/commits/_commit.html.haml2
-rw-r--r--app/views/projects/commits/show.html.haml52
-rw-r--r--app/views/projects/find_file/show.html.haml10
-rw-r--r--app/views/projects/graphs/charts.html.haml64
-rw-r--r--app/views/projects/graphs/show.html.haml28
-rw-r--r--app/views/projects/imports/show.html.haml2
-rw-r--r--app/views/projects/mattermosts/_team_selection.html.haml2
-rw-r--r--app/views/projects/merge_requests/creations/_new_compare.html.haml2
-rw-r--r--app/views/projects/merge_requests/dropdowns/_project.html.haml2
-rw-r--r--app/views/projects/new.html.haml45
-rw-r--r--app/views/projects/pipelines/charts/_pipeline_times.haml25
-rw-r--r--app/views/projects/pipelines/charts/_pipelines.haml36
-rw-r--r--app/views/projects/pipelines/new.html.haml5
-rw-r--r--app/views/projects/services/mattermost_slash_commands/_detailed_help.html.haml2
-rw-r--r--app/views/projects/services/slack_slash_commands/_help.html.haml2
-rw-r--r--app/views/projects/tags/new.html.haml5
-rw-r--r--app/views/projects/tree/_tree_content.html.haml8
-rw-r--r--app/views/projects/wikis/_sidebar.html.haml3
-rw-r--r--app/views/projects/wikis/git_access.html.haml2
-rw-r--r--app/views/shared/issuable/_sidebar.html.haml2
-rw-r--r--app/views/u2f/_register.html.haml4
-rw-r--r--app/workers/git_garbage_collect_worker.rb39
-rw-r--r--app/workers/irker_worker.rb4
-rw-r--r--app/workers/repository_import_worker.rb2
-rw-r--r--changelogs/unreleased/29385-add_shrug_command.yml4
-rw-r--r--changelogs/unreleased/33620-remove-events-from-notification_settings.yml4
-rw-r--r--changelogs/unreleased/34492-firefox-job.yml4
-rw-r--r--changelogs/unreleased/34519-extend-api-group-secret-variable.yml4
-rw-r--r--changelogs/unreleased/34869-bump-rubocop-to-0-49-1-and-rubocop-rspec-to-1-15-1.yml4
-rw-r--r--changelogs/unreleased/35044-projects-logo-are-not-centered-vertically-on-projects-page.yml4
-rw-r--r--changelogs/unreleased/35697-allow-logged-in-user-to-read-user-list.yml4
-rw-r--r--changelogs/unreleased/breadcrumbs-collapsed-title-width-fix.yml4
-rw-r--r--changelogs/unreleased/fix-group-milestone-link-in-issuable-sidebar.yml4
-rw-r--r--changelogs/unreleased/help-page-breadcrumb-title-fix.yml4
-rw-r--r--changelogs/unreleased/mk-fix-deploy-key-deletion.yml4
-rw-r--r--changelogs/unreleased/mk-fix-wiki-backup.yml4
-rw-r--r--changelogs/unreleased/pawel-ensure_temp_files_are_deleted_in_fs_metrics-35457.yml4
-rw-r--r--changelogs/unreleased/pawel-prometheus_client_pid_reuse_error.yml4
-rw-r--r--changelogs/unreleased/search-flickering.yml4
-rw-r--r--changelogs/unreleased/winh-derive-project-name.yml4
-rw-r--r--config/gitlab.yml.example2
-rw-r--r--config/webpack.config.js10
-rw-r--r--db/migrate/20161124141322_migrate_process_commit_worker_jobs.rb1
-rw-r--r--db/post_migrate/20170728101014_remove_events_from_notification_settings.rb9
-rw-r--r--db/schema.rb23
-rw-r--r--doc/README.md13
-rw-r--r--doc/administration/auth/ldap.md6
-rw-r--r--doc/administration/high_availability/nfs.md4
-rw-r--r--doc/administration/monitoring/performance/img/performance_bar.pngbin186116 -> 170256 bytes
-rw-r--r--doc/api/README.md3
-rw-r--r--doc/api/group_level_variables.md125
-rw-r--r--doc/api/project_level_variables.md (renamed from doc/api/build_variables.md)12
-rw-r--r--doc/api/users.md40
-rw-r--r--doc/articles/index.md115
-rw-r--r--doc/ci/README.md1
-rw-r--r--doc/development/code_review.md3
-rw-r--r--doc/integration/slash_commands.md21
-rw-r--r--doc/user/group/index.md2
-rw-r--r--doc/user/index.md2
-rw-r--r--doc/user/profile/index.md2
-rw-r--r--doc/user/project/index.md107
-rw-r--r--doc/user/project/koding.md2
-rw-r--r--doc/user/project/repository/index.md2
-rw-r--r--features/steps/project/forked_merge_requests.rb12
-rw-r--r--features/steps/project/redirects.rb4
-rw-r--r--features/steps/project/wiki.rb2
-rw-r--r--lib/api/api.rb1
-rw-r--r--lib/api/group_variables.rb96
-rw-r--r--lib/api/runner.rb4
-rw-r--r--lib/backup/repository.rb19
-rw-r--r--lib/banzai/filter/abstract_reference_filter.rb4
-rw-r--r--lib/banzai/filter/relative_link_filter.rb2
-rw-r--r--lib/banzai/filter/upload_link_filter.rb2
-rw-r--r--lib/ci/api/builds.rb4
-rw-r--r--lib/github/import.rb2
-rw-r--r--lib/gitlab/bitbucket_import/importer.rb2
-rw-r--r--lib/gitlab/diff/file.rb18
-rw-r--r--lib/gitlab/ee_compat_check.rb2
-rw-r--r--lib/gitlab/email/message/repository_push.rb2
-rw-r--r--lib/gitlab/git/repository.rb27
-rw-r--r--lib/gitlab/gitaly_client/commit_service.rb7
-rw-r--r--lib/gitlab/gitaly_client/repository_service.rb18
-rw-r--r--lib/gitlab/github_import/importer.rb2
-rw-r--r--lib/gitlab/github_import/wiki_formatter.rb4
-rw-r--r--lib/gitlab/import_export/repo_restorer.rb2
-rw-r--r--lib/gitlab/import_export/uploads_saver.rb1
-rw-r--r--lib/gitlab/ldap/authentication.rb2
-rw-r--r--lib/gitlab/quick_actions/dsl.rb29
-rw-r--r--lib/gitlab/quick_actions/extractor.rb22
-rw-r--r--lib/gitlab/quick_actions/substitution_definition.rb24
-rw-r--r--lib/gitlab/slash_commands/deploy.rb33
-rw-r--r--lib/gitlab/slash_commands/presenters/deploy.rb11
-rwxr-xr-xlib/support/init.d/gitlab6
-rw-r--r--lib/tasks/gitlab/check.rake2
-rw-r--r--lib/tasks/gitlab/gitaly.rake12
-rw-r--r--lib/tasks/gitlab/list_repos.rake2
-rw-r--r--lib/tasks/gitlab/shell.rake2
-rw-r--r--locale/pt_BR/gitlab.po14
-rw-r--r--rubocop/cop/migration/add_column_with_default_to_large_table.rb1
-rwxr-xr-xscripts/gitaly-test-build10
-rwxr-xr-xscripts/gitaly-test-spawn7
-rwxr-xr-xscripts/lint-conflicts.sh5
-rwxr-xr-xscripts/static-analysis3
-rw-r--r--spec/controllers/projects/blob_controller_spec.rb2
-rw-r--r--spec/controllers/projects/branches_controller_spec.rb6
-rw-r--r--spec/controllers/projects/graphs_controller_spec.rb33
-rw-r--r--spec/controllers/projects/merge_requests_controller_spec.rb2
-rw-r--r--spec/controllers/projects/tree_controller_spec.rb6
-rw-r--r--spec/factories/milestones.rb2
-rw-r--r--spec/factories/notification_settings.rb1
-rw-r--r--spec/factories/projects.rb4
-rw-r--r--spec/features/admin/admin_users_impersonation_tokens_spec.rb4
-rw-r--r--spec/features/commits_spec.rb4
-rw-r--r--spec/features/merge_requests/create_new_mr_spec.rb8
-rw-r--r--spec/features/projects/wiki/markdown_preview_spec.rb48
-rw-r--r--spec/features/projects/wiki/user_git_access_wiki_page_spec.rb2
-rw-r--r--spec/helpers/gitlab_routing_helper_spec.rb53
-rw-r--r--spec/helpers/labels_helper_spec.rb4
-rw-r--r--spec/helpers/markup_helper_spec.rb4
-rw-r--r--spec/helpers/merge_requests_helper_spec.rb4
-rw-r--r--spec/helpers/projects_helper_spec.rb4
-rw-r--r--spec/javascripts/projects/project_new_spec.js127
-rw-r--r--spec/lib/banzai/filter/abstract_reference_filter_spec.rb14
-rw-r--r--spec/lib/banzai/filter/commit_range_reference_filter_spec.rb10
-rw-r--r--spec/lib/banzai/filter/commit_reference_filter_spec.rb10
-rw-r--r--spec/lib/banzai/filter/issue_reference_filter_spec.rb12
-rw-r--r--spec/lib/banzai/filter/label_reference_filter_spec.rb10
-rw-r--r--spec/lib/banzai/filter/merge_request_reference_filter_spec.rb4
-rw-r--r--spec/lib/banzai/filter/milestone_reference_filter_spec.rb34
-rw-r--r--spec/lib/banzai/filter/relative_link_filter_spec.rb2
-rw-r--r--spec/lib/banzai/filter/snippet_reference_filter_spec.rb4
-rw-r--r--spec/lib/banzai/filter/upload_link_filter_spec.rb10
-rw-r--r--spec/lib/extracts_path_spec.rb4
-rw-r--r--spec/lib/gitlab/backup/repository_spec.rb54
-rw-r--r--spec/lib/gitlab/diff/file_spec.rb8
-rw-r--r--spec/lib/gitlab/email/message/repository_push_spec.rb2
-rw-r--r--spec/lib/gitlab/gfm/uploads_rewriter_spec.rb4
-rw-r--r--spec/lib/gitlab/git/blob_spec.rb10
-rw-r--r--spec/lib/gitlab/git/repository_spec.rb39
-rw-r--r--spec/lib/gitlab/gitaly_client/commit_service_spec.rb2
-rw-r--r--spec/lib/gitlab/gitaly_client/notification_service_spec.rb2
-rw-r--r--spec/lib/gitlab/gitaly_client/ref_service_spec.rb2
-rw-r--r--spec/lib/gitlab/github_import/wiki_formatter_spec.rb4
-rw-r--r--spec/lib/gitlab/import_export/fork_spec.rb2
-rw-r--r--spec/lib/gitlab/import_export/project_tree_saver_spec.rb2
-rw-r--r--spec/lib/gitlab/import_export/repo_restorer_spec.rb2
-rw-r--r--spec/lib/gitlab/import_export/repo_saver_spec.rb2
-rw-r--r--spec/lib/gitlab/import_export/wiki_repo_saver_spec.rb2
-rw-r--r--spec/lib/gitlab/quick_actions/dsl_spec.rb16
-rw-r--r--spec/lib/gitlab/quick_actions/extractor_spec.rb37
-rw-r--r--spec/lib/gitlab/quick_actions/substitution_definition_spec.rb42
-rw-r--r--spec/lib/gitlab/slash_commands/command_spec.rb2
-rw-r--r--spec/lib/gitlab/slash_commands/deploy_spec.rb56
-rw-r--r--spec/lib/gitlab/slash_commands/presenters/deploy_spec.rb20
-rw-r--r--spec/lib/gitlab/url_builder_spec.rb18
-rw-r--r--spec/migrations/cleanup_namespaceless_pending_delete_projects_spec.rb2
-rw-r--r--spec/migrations/migrate_process_commit_worker_jobs_spec.rb2
-rw-r--r--spec/migrations/turn_nested_groups_into_regular_groups_for_mysql_spec.rb2
-rw-r--r--spec/models/commit_spec.rb2
-rw-r--r--spec/models/merge_request_spec.rb2
-rw-r--r--spec/models/notification_setting_spec.rb12
-rw-r--r--spec/models/project_label_spec.rb4
-rw-r--r--spec/models/project_services/gitlab_issue_tracker_service_spec.rb12
-rw-r--r--spec/models/project_services/jira_service_spec.rb6
-rw-r--r--spec/models/project_spec.rb84
-rw-r--r--spec/models/project_wiki_spec.rb18
-rw-r--r--spec/models/repository_spec.rb2
-rw-r--r--spec/requests/api/group_variables_spec.rb221
-rw-r--r--spec/requests/api/internal_spec.rb4
-rw-r--r--spec/requests/api/notification_settings_spec.rb4
-rw-r--r--spec/requests/api/users_spec.rb40
-rw-r--r--spec/requests/git_http_spec.rb42
-rw-r--r--spec/requests/lfs_http_spec.rb2
-rw-r--r--spec/requests/request_profiler_spec.rb2
-rw-r--r--spec/services/auth/container_registry_authentication_service_spec.rb36
-rw-r--r--spec/services/ci/pipeline_trigger_service_spec.rb2
-rw-r--r--spec/services/git_push_service_spec.rb2
-rw-r--r--spec/services/notification_service_spec.rb47
-rw-r--r--spec/services/projects/import_service_spec.rb10
-rw-r--r--spec/services/projects/transfer_service_spec.rb24
-rw-r--r--spec/services/quick_actions/interpret_service_spec.rb42
-rw-r--r--spec/services/system_note_service_spec.rb4
-rw-r--r--spec/spec_helper.rb4
-rw-r--r--spec/support/notify_shared_examples.rb2
-rw-r--r--spec/support/project_hook_data_shared_example.rb4
-rw-r--r--spec/support/test_env.rb11
-rw-r--r--spec/tasks/gitlab/backup_rake_spec.rb6
-rw-r--r--spec/tasks/gitlab/gitaly_rake_spec.rb18
-rw-r--r--spec/workers/git_garbage_collect_worker_spec.rb52
-rw-r--r--spec/workers/namespaceless_project_destroy_worker_spec.rb2
281 files changed, 3371 insertions, 1615 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index adde3400107..27fdf6ca0b5 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -96,6 +96,7 @@ stages:
- export KNAPSACK_GENERATE_REPORT=true
- export CACHE_CLASSES=true
- cp ${KNAPSACK_RSPEC_SUITE_REPORT_PATH} ${KNAPSACK_REPORT_PATH}
+ - scripts/gitaly-test-spawn
- knapsack rspec "--color --format documentation"
artifacts:
expire_in: 31d
@@ -221,6 +222,7 @@ setup-test-env:
- bundle exec rake gettext:po_to_json
- bundle exec rake gitlab:assets:compile
- bundle exec ruby -Ispec -e 'require "spec_helper" ; TestEnv.init'
+ - scripts/gitaly-test-build # Do not use 'bundle exec' here
artifacts:
expire_in: 7d
paths:
@@ -486,6 +488,7 @@ karma:
BABEL_ENV: "coverage"
CHROME_LOG_FILE: "chrome_debug.log"
script:
+ - scripts/gitaly-test-spawn
- bundle exec rake gettext:po_to_json
- bundle exec rake karma
coverage: '/^Statements *: (\d+\.\d+%)/'
diff --git a/.rubocop.yml b/.rubocop.yml
index f661a29d9d1..3e4275c271d 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -6,6 +6,7 @@ inherit_from: .rubocop_todo.yml
AllCops:
TargetRubyVersion: 2.3
+ TargetRailsVersion: 4.2
# Cop names are not d§splayed in offense messages by default. Change behavior
# by overriding DisplayCopNames, or by giving the -D/--display-cop-names
# option.
@@ -29,34 +30,230 @@ AllCops:
Bundler/OrderedGems:
Enabled: false
-# Style #######################################################################
+# Layout ######################################################################
# Check indentation of private/protected visibility modifiers.
-Style/AccessModifierIndentation:
- Enabled: true
-
-# Check the naming of accessor methods for get_/set_.
-Style/AccessorMethodName:
- Enabled: false
-
-# Use alias_method instead of alias.
-Style/Alias:
- EnforcedStyle: prefer_alias_method
+Layout/AccessModifierIndentation:
Enabled: true
# Align the elements of an array literal if they span more than one line.
-Style/AlignArray:
+Layout/AlignArray:
Enabled: true
# Align the elements of a hash literal if they span more than one line.
-Style/AlignHash:
+Layout/AlignHash:
Enabled: true
# Here we check if the parameters on a multi-line method call or
# definition are aligned.
-Style/AlignParameters:
+Layout/AlignParameters:
+ Enabled: false
+
+# Put end statement of multiline block on its own line.
+Layout/BlockEndNewline:
+ Enabled: true
+
+# Indentation of when in a case/when/[else/]end.
+Layout/CaseIndentation:
+ Enabled: true
+
+# Indentation of comments.
+Layout/CommentIndentation:
+ Enabled: true
+
+# Multi-line method chaining should be done with leading dots.
+Layout/DotPosition:
+ Enabled: true
+ EnforcedStyle: leading
+
+# Align elses and elsifs correctly.
+Layout/ElseAlignment:
+ Enabled: true
+
+# Add an empty line after magic comments to separate them from code.
+Layout/EmptyLineAfterMagicComment:
+ Enabled: false
+
+# Use empty lines between defs.
+Layout/EmptyLineBetweenDefs:
+ Enabled: true
+
+# Don't use several empty lines in a row.
+Layout/EmptyLines:
+ Enabled: true
+
+# Keep blank lines around access modifiers.
+Layout/EmptyLinesAroundAccessModifier:
+ Enabled: true
+
+# Keeps track of empty lines around block bodies.
+Layout/EmptyLinesAroundBlockBody:
+ Enabled: true
+
+# Keeps track of empty lines around class bodies.
+Layout/EmptyLinesAroundClassBody:
+ Enabled: true
+
+# Keeps track of empty lines around exception handling keywords.
+Layout/EmptyLinesAroundExceptionHandlingKeywords:
+ Enabled: false
+
+# Keeps track of empty lines around method bodies.
+Layout/EmptyLinesAroundMethodBody:
+ Enabled: true
+
+# Keeps track of empty lines around module bodies.
+Layout/EmptyLinesAroundModuleBody:
+ Enabled: true
+
+# Use Unix-style line endings.
+Layout/EndOfLine:
+ Enabled: true
+
+# Checks for a line break before the first parameter in a multi-line method
+# parameter definition.
+Layout/FirstMethodParameterLineBreak:
+ Enabled: true
+
+# Keep indentation straight.
+Layout/IndentationConsistency:
+ Enabled: true
+
+# Use 2 spaces for indentation.
+Layout/IndentationWidth:
+ Enabled: true
+
+# Checks the indentation of the first line of the right-hand-side of a
+# multi-line assignment.
+Layout/IndentAssignment:
+ Enabled: true
+
+# This cops checks the indentation of the here document bodies.
+Layout/IndentHeredoc:
+ Enabled: false
+
+# Comments should start with a space.
+Layout/LeadingCommentSpace:
+ Enabled: true
+
+# Checks that the closing brace in an array literal is either on the same line
+# as the last array element, or a new line.
+Layout/MultilineArrayBraceLayout:
+ Enabled: true
+ EnforcedStyle: symmetrical
+
+# Ensures newlines after multiline block do statements.
+Layout/MultilineBlockLayout:
+ Enabled: true
+
+# Checks that the closing brace in a hash literal is either on the same line as
+# the last hash element, or a new line.
+Layout/MultilineHashBraceLayout:
+ Enabled: true
+ EnforcedStyle: symmetrical
+
+# Checks that the closing brace in a method call is either on the same line as
+# the last method argument, or a new line.
+Layout/MultilineMethodCallBraceLayout:
+ Enabled: false
+ EnforcedStyle: symmetrical
+
+# Checks indentation of method calls with the dot operator that span more than
+# one line.
+Layout/MultilineMethodCallIndentation:
+ Enabled: false
+
+# Checks that the closing brace in a method definition is symmetrical with
+# respect to the opening brace and the method parameters.
+Layout/MultilineMethodDefinitionBraceLayout:
+ Enabled: false
+
+# Checks indentation of binary operations that span more than one line.
+Layout/MultilineOperationIndentation:
+ Enabled: true
+ EnforcedStyle: indented
+
+# Use spaces after colons.
+Layout/SpaceAfterColon:
+ Enabled: true
+
+# Use spaces after commas.
+Layout/SpaceAfterComma:
+ Enabled: true
+
+# Do not put a space between a method name and the opening parenthesis in a
+# method definition.
+Layout/SpaceAfterMethodName:
+ Enabled: true
+
+# Tracks redundant space after the ! operator.
+Layout/SpaceAfterNot:
+ Enabled: true
+
+# Use spaces after semicolons.
+Layout/SpaceAfterSemicolon:
+ Enabled: true
+
+# Use space around equals in parameter default
+Layout/SpaceAroundEqualsInParameterDefault:
+ Enabled: true
+
+# Use a space around keywords if appropriate.
+Layout/SpaceAroundKeyword:
+ Enabled: true
+
+# Use a single space around operators.
+Layout/SpaceAroundOperators:
+ Enabled: true
+
+# No spaces before commas.
+Layout/SpaceBeforeComma:
+ Enabled: true
+
+# Checks for missing space between code and a comment on the same line.
+Layout/SpaceBeforeComment:
+ Enabled: true
+
+# No spaces before semicolons.
+Layout/SpaceBeforeSemicolon:
+ Enabled: true
+
+# Checks for spaces inside square brackets.
+Layout/SpaceInsideBrackets:
+ Enabled: true
+
+# Use spaces inside hash literal braces - or don't.
+Layout/SpaceInsideHashLiteralBraces:
+ Enabled: true
+
+# No spaces inside range literals.
+Layout/SpaceInsideRangeLiteral:
+ Enabled: true
+
+# Checks for padding/surrounding spaces inside string interpolation.
+Layout/SpaceInsideStringInterpolation:
+ EnforcedStyle: no_space
+ Enabled: true
+
+# No hard tabs.
+Layout/Tab:
+ Enabled: true
+
+# Checks trailing blank lines and final newline.
+Layout/TrailingBlankLines:
+ Enabled: true
+
+# Style #######################################################################
+
+# Check the naming of accessor methods for get_/set_.
+Style/AccessorMethodName:
Enabled: false
+# Use alias_method instead of alias.
+Style/Alias:
+ EnforcedStyle: prefer_alias_method
+ Enabled: true
+
# Whether `and` and `or` are banned only in conditionals (conditionals)
# or completely (always).
Style/AndOr:
@@ -91,10 +288,6 @@ Style/BlockComments:
Style/BlockDelimiters:
Enabled: true
-# Put end statement of multiline block on its own line.
-Style/BlockEndNewline:
- Enabled: true
-
# This cop checks for braces around the last parameter in a method call
# if the last parameter is a hash.
Style/BracesAroundHashParameters:
@@ -104,10 +297,6 @@ Style/BracesAroundHashParameters:
Style/CaseEquality:
Enabled: false
-# Indentation of when in a case/when/[else/]end.
-Style/CaseIndentation:
- Enabled: true
-
# Checks for uses of character literals.
Style/CharacterLiteral:
Enabled: true
@@ -142,10 +331,6 @@ Style/ColonMethodCall:
Style/CommentAnnotation:
Enabled: false
-# Indentation of comments.
-Style/CommentIndentation:
- Enabled: true
-
# Check for `if` and `case` statements where each branch is used for
# assignment to the same variable when using the return of the
# condition can be used instead.
@@ -164,57 +349,16 @@ Style/DefWithParentheses:
Style/Documentation:
Enabled: false
-# Multi-line method chaining should be done with leading dots.
-Style/DotPosition:
- Enabled: true
- EnforcedStyle: leading
-
# This cop checks for uses of double negation (!!) to convert something
# to a boolean value. As this is both cryptic and usually redundant, it
# should be avoided.
Style/DoubleNegation:
Enabled: false
-# Align elses and elsifs correctly.
-Style/ElseAlignment:
- Enabled: true
-
-# Use empty lines between defs.
-Style/EmptyLineBetweenDefs:
- Enabled: true
-
-# Don't use several empty lines in a row.
-Style/EmptyLines:
- Enabled: true
-
-# Keep blank lines around access modifiers.
-Style/EmptyLinesAroundAccessModifier:
- Enabled: true
-
-# Keeps track of empty lines around block bodies.
-Style/EmptyLinesAroundBlockBody:
- Enabled: true
-
-# Keeps track of empty lines around class bodies.
-Style/EmptyLinesAroundClassBody:
- Enabled: true
-
-# Keeps track of empty lines around method bodies.
-Style/EmptyLinesAroundMethodBody:
- Enabled: true
-
-# Keeps track of empty lines around module bodies.
-Style/EmptyLinesAroundModuleBody:
- Enabled: true
-
# Avoid the use of END blocks.
Style/EndBlock:
Enabled: true
-# Use Unix-style line endings.
-Style/EndOfLine:
- Enabled: true
-
# Favor the use of Fixnum#even? && Fixnum#odd?
Style/EvenOdd:
Enabled: true
@@ -223,11 +367,6 @@ Style/EvenOdd:
Style/FileName:
Enabled: true
-# Checks for a line break before the first parameter in a multi-line method
-# parameter definition.
-Style/FirstMethodParameterLineBreak:
- Enabled: true
-
# Checks for flip flops.
Style/FlipFlop:
Enabled: true
@@ -236,6 +375,10 @@ Style/FlipFlop:
Style/For:
Enabled: true
+# Use a consistent style for format string tokens.
+Style/FormatStringToken:
+ Enabled: false
+
# Checks if there is a magic comment to enforce string literals
Style/FrozenStringLiteralComment:
Enabled: false
@@ -261,31 +404,19 @@ Style/IdenticalConditionalBranches:
Style/IfWithSemicolon:
Enabled: true
-# Checks the indentation of the first line of the right-hand-side of a
-# multi-line assignment.
-Style/IndentAssignment:
- Enabled: true
-
-# Keep indentation straight.
-Style/IndentationConsistency:
- Enabled: true
-
-# Use 2 spaces for indentation.
-Style/IndentationWidth:
- Enabled: true
-
# Use Kernel#loop for infinite loops.
Style/InfiniteLoop:
Enabled: true
+# Use the inverse method instead of `!.method`
+# if an inverse method is defined.
+Style/InverseMethods:
+ Enabled: false
+
# Use lambda.call(...) instead of lambda.(...).
Style/LambdaCall:
Enabled: true
-# Comments should start with a space.
-Style/LeadingCommentSpace:
- Enabled: true
-
# Checks if the method definitions have or don't have parentheses.
Style/MethodDefParentheses:
Enabled: true
@@ -298,55 +429,23 @@ Style/MethodName:
Style/ModuleFunction:
Enabled: false
-# Checks that the closing brace in an array literal is either on the same line
-# as the last array element, or a new line.
-Style/MultilineArrayBraceLayout:
- Enabled: true
- EnforcedStyle: symmetrical
-
# Avoid multi-line chains of blocks.
Style/MultilineBlockChain:
Enabled: true
-# Ensures newlines after multiline block do statements.
-Style/MultilineBlockLayout:
- Enabled: true
-
-# Checks that the closing brace in a hash literal is either on the same line as
-# the last hash element, or a new line.
-Style/MultilineHashBraceLayout:
- Enabled: true
- EnforcedStyle: symmetrical
-
# Do not use then for multi-line if/unless.
Style/MultilineIfThen:
Enabled: true
-# Checks that the closing brace in a method call is either on the same line as
-# the last method argument, or a new line.
-Style/MultilineMethodCallBraceLayout:
- Enabled: false
- EnforcedStyle: symmetrical
-
-# Checks indentation of method calls with the dot operator that span more than
-# one line.
-Style/MultilineMethodCallIndentation:
- Enabled: false
-
-# Checks that the closing brace in a method definition is symmetrical with
-# respect to the opening brace and the method parameters.
-Style/MultilineMethodDefinitionBraceLayout:
- Enabled: false
-
-# Checks indentation of binary operations that span more than one line.
-Style/MultilineOperationIndentation:
- Enabled: true
- EnforcedStyle: indented
-
# Avoid multi-line `? :` (the ternary operator), use if/unless instead.
Style/MultilineTernaryOperator:
Enabled: true
+# Avoid comparing a variable with multiple items in a conditional,
+# use Array#include? instead.
+Style/MultipleComparison:
+ Enabled: false
+
# This cop checks whether some constant value isn't a
# mutable literal (e.g. array or hash).
Style/MutableConstant:
@@ -421,68 +520,6 @@ Style/SignalException:
EnforcedStyle: only_raise
Enabled: true
-# Use spaces after colons.
-Style/SpaceAfterColon:
- Enabled: true
-
-# Use spaces after commas.
-Style/SpaceAfterComma:
- Enabled: true
-
-# Do not put a space between a method name and the opening parenthesis in a
-# method definition.
-Style/SpaceAfterMethodName:
- Enabled: true
-
-# Tracks redundant space after the ! operator.
-Style/SpaceAfterNot:
- Enabled: true
-
-# Use spaces after semicolons.
-Style/SpaceAfterSemicolon:
- Enabled: true
-
-# Use space around equals in parameter default
-Style/SpaceAroundEqualsInParameterDefault:
- Enabled: true
-
-# Use a space around keywords if appropriate.
-Style/SpaceAroundKeyword:
- Enabled: true
-
-# Use a single space around operators.
-Style/SpaceAroundOperators:
- Enabled: true
-
-# No spaces before commas.
-Style/SpaceBeforeComma:
- Enabled: true
-
-# Checks for missing space between code and a comment on the same line.
-Style/SpaceBeforeComment:
- Enabled: true
-
-# No spaces before semicolons.
-Style/SpaceBeforeSemicolon:
- Enabled: true
-
-# Checks for spaces inside square brackets.
-Style/SpaceInsideBrackets:
- Enabled: true
-
-# Use spaces inside hash literal braces - or don't.
-Style/SpaceInsideHashLiteralBraces:
- Enabled: true
-
-# No spaces inside range literals.
-Style/SpaceInsideRangeLiteral:
- Enabled: true
-
-# Checks for padding/surrounding spaces inside string interpolation.
-Style/SpaceInsideStringInterpolation:
- EnforcedStyle: no_space
- Enabled: true
-
# Check for the usage of parentheses around stabby lambda arguments.
Style/StabbyLambdaParentheses:
EnforcedStyle: require_parentheses
@@ -498,13 +535,9 @@ Style/StringMethods:
intern: to_sym
Enabled: true
-# No hard tabs.
-Style/Tab:
- Enabled: true
-
-# Checks trailing blank lines and final newline.
-Style/TrailingBlankLines:
- Enabled: true
+# Use %i or %I for arrays of symbols.
+Style/SymbolArray:
+ Enabled: false
# This cop checks for trailing comma in array and hash literals.
Style/TrailingCommaInLiteral:
@@ -553,6 +586,10 @@ Style/WhileUntilModifier:
Style/WordArray:
Enabled: true
+# Do not use literals as the first operand of a comparison.
+Style/YodaCondition:
+ Enabled: false
+
# Use `proc` instead of `Proc.new`.
Style/Proc:
Enabled: true
@@ -608,6 +645,11 @@ Metrics/PerceivedComplexity:
# Lint ########################################################################
+# Checks for ambiguous block association with method when param passed without
+# parentheses.
+Lint/AmbiguousBlockAssociation:
+ Enabled: false
+
# Checks for ambiguous operators in the first argument of a method invocation
# without parentheses.
Lint/AmbiguousOperator:
@@ -809,6 +851,10 @@ Lint/Void:
# Performance #################################################################
+# Use `caller(n..n)` instead of `caller`.
+Performance/Caller:
+ Enabled: false
+
# Use `casecmp` rather than `downcase ==`.
Performance/Casecmp:
Enabled: true
@@ -883,6 +929,14 @@ Rails/ActionFilter:
Enabled: true
EnforcedStyle: action
+# Check that models subclass ApplicationRecord.
+Rails/ApplicationRecord:
+ Enabled: false
+
+# Enforce using `blank?` and `present?`.
+Rails/Blank:
+ Enabled: false
+
# Checks the correct usage of date aware methods, such as `Date.today`,
# `Date.current`, etc.
Rails/Date:
@@ -939,10 +993,18 @@ Rails/OutputSafety:
Rails/PluralizationGrammar:
Enabled: true
+# Enforce using `blank?` and `present?`.
+Rails/Present:
+ Enabled: false
+
# Checks for `read_attribute(:attr)` and `write_attribute(:attr, val)`.
Rails/ReadWriteAttribute:
Enabled: false
+# Do not assign relative date to constants.
+Rails/RelativeDateConstant:
+ Enabled: false
+
# Checks the arguments of ActiveRecord scopes.
Rails/ScopeArgs:
Enabled: true
diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml
index 2ec558e274f..9caef3bde08 100644
--- a/.rubocop_todo.yml
+++ b/.rubocop_todo.yml
@@ -1,26 +1,89 @@
# This configuration was generated by
# `rubocop --auto-gen-config --exclude-limit 0`
-# on 2017-04-07 20:17:35 -0400 using RuboCop version 0.47.1.
+# on 2017-07-10 01:48:30 +0900 using RuboCop version 0.49.1.
# The point is for the user to remove these configuration records
# one by one as the offenses are removed from the code base.
# Note that changes in the inspected code, or installation of new
# versions of RuboCop, may require this file to be generated again.
-# Offense count: 233
+# Offense count: 181
+# Cop supports --auto-correct.
+# Configuration parameters: AllowForAlignment, ForceEqualSignAlignment.
+Layout/ExtraSpacing:
+ Enabled: false
+
+# Offense count: 119
+# Cop supports --auto-correct.
+# Configuration parameters: EnforcedStyle, SupportedStyles, IndentationWidth.
+# SupportedStyles: special_inside_parentheses, consistent, align_brackets
+Layout/IndentArray:
+ Enabled: false
+
+# Offense count: 208
+# Cop supports --auto-correct.
+# Configuration parameters: EnforcedStyle, SupportedStyles, IndentationWidth.
+# SupportedStyles: special_inside_parentheses, consistent, align_braces
+Layout/IndentHash:
+ Enabled: false
+
+# Offense count: 174
+# Cop supports --auto-correct.
+# Configuration parameters: EnforcedStyle, SupportedStyles.
+# SupportedStyles: space, no_space
+Layout/SpaceBeforeBlockBraces:
+ Enabled: false
+
+# Offense count: 8
+# Cop supports --auto-correct.
+# Configuration parameters: AllowForAlignment.
+Layout/SpaceBeforeFirstArg:
+ Enabled: false
+
+# Offense count: 64
+# Cop supports --auto-correct.
+# Configuration parameters: EnforcedStyle, SupportedStyles.
+# SupportedStyles: require_no_space, require_space
+Layout/SpaceInLambdaLiteral:
+ Enabled: false
+
+# Offense count: 256
+# Cop supports --auto-correct.
+# Configuration parameters: EnforcedStyle, SupportedStyles, EnforcedStyleForEmptyBraces, SupportedStylesForEmptyBraces, SpaceBeforeBlockParameters.
+# SupportedStyles: space, no_space
+# SupportedStylesForEmptyBraces: space, no_space
+Layout/SpaceInsideBlockBraces:
+ Enabled: false
+
+# Offense count: 135
+# Cop supports --auto-correct.
+Layout/SpaceInsideParens:
+ Enabled: false
+
+# Offense count: 14
+# Cop supports --auto-correct.
+Layout/SpaceInsidePercentLiteralDelimiters:
+ Enabled: false
+
+# Offense count: 89
+# Cop supports --auto-correct.
+Layout/TrailingWhitespace:
+ Enabled: false
+
+# Offense count: 272
RSpec/EmptyLineAfterFinalLet:
Enabled: false
-# Offense count: 167
+# Offense count: 181
RSpec/EmptyLineAfterSubject:
Enabled: false
-# Offense count: 72
+# Offense count: 78
# Configuration parameters: EnforcedStyle, SupportedStyles.
# SupportedStyles: implicit, each, example
RSpec/HookArgument:
Enabled: false
-# Offense count: 11
+# Offense count: 9
# Configuration parameters: EnforcedStyle, SupportedStyles.
# SupportedStyles: it_behaves_like, it_should_behave_like
RSpec/ItBehavesLike:
@@ -30,19 +93,19 @@ RSpec/ItBehavesLike:
RSpec/IteratedExpectation:
Enabled: false
-# Offense count: 3
+# Offense count: 2
RSpec/OverwritingSetup:
Enabled: false
-# Offense count: 34
+# Offense count: 36
RSpec/RepeatedExample:
Enabled: false
-# Offense count: 43
+# Offense count: 86
RSpec/ScatteredLet:
Enabled: false
-# Offense count: 32
+# Offense count: 20
RSpec/ScatteredSetup:
Enabled: false
@@ -50,7 +113,7 @@ RSpec/ScatteredSetup:
RSpec/SharedContext:
Enabled: false
-# Offense count: 150
+# Offense count: 115
Rails/FilePath:
Enabled: false
@@ -60,90 +123,71 @@ Rails/FilePath:
Rails/ReversibleMigration:
Enabled: false
-# Offense count: 302
+# Offense count: 336
# Configuration parameters: Blacklist.
# Blacklist: decrement!, decrement_counter, increment!, increment_counter, toggle!, touch, update_all, update_attribute, update_column, update_columns, update_counters
Rails/SkipsModelValidations:
Enabled: false
-# Offense count: 7
+# Offense count: 11
# Cop supports --auto-correct.
Security/YAMLLoad:
Enabled: false
-# Offense count: 59
+# Offense count: 58
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, SupportedStyles.
# SupportedStyles: percent_q, bare_percent
Style/BarePercentLiterals:
Enabled: false
-# Offense count: 5
+# Offense count: 6
# Cop supports --auto-correct.
Style/EachWithObject:
Enabled: false
-# Offense count: 28
+# Offense count: 31
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, SupportedStyles.
# SupportedStyles: empty, nil, both
Style/EmptyElse:
Enabled: false
-# Offense count: 4
+# Offense count: 9
# Cop supports --auto-correct.
Style/EmptyLiteral:
Enabled: false
-# Offense count: 59
+# Offense count: 78
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, SupportedStyles.
# SupportedStyles: compact, expanded
Style/EmptyMethod:
Enabled: false
-# Offense count: 214
+# Offense count: 23
# Cop supports --auto-correct.
-# Configuration parameters: AllowForAlignment, ForceEqualSignAlignment.
-Style/ExtraSpacing:
- Enabled: false
-
-# Offense count: 9
# Configuration parameters: EnforcedStyle, SupportedStyles.
# SupportedStyles: format, sprintf, percent
Style/FormatString:
Enabled: false
-# Offense count: 285
+# Offense count: 301
# Configuration parameters: MinBodyLength.
Style/GuardClause:
Enabled: false
-# Offense count: 16
+# Offense count: 18
Style/IfInsideElse:
Enabled: false
-# Offense count: 186
+# Offense count: 182
# Cop supports --auto-correct.
# Configuration parameters: MaxLineLength.
Style/IfUnlessModifier:
Enabled: false
-# Offense count: 99
-# Cop supports --auto-correct.
-# Configuration parameters: EnforcedStyle, SupportedStyles, IndentationWidth.
-# SupportedStyles: special_inside_parentheses, consistent, align_brackets
-Style/IndentArray:
- Enabled: false
-
-# Offense count: 160
-# Cop supports --auto-correct.
-# Configuration parameters: EnforcedStyle, SupportedStyles, IndentationWidth.
-# SupportedStyles: special_inside_parentheses, consistent, align_braces
-Style/IndentHash:
- Enabled: false
-
-# Offense count: 50
+# Offense count: 52
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, SupportedStyles.
# SupportedStyles: line_count_dependent, lambda, literal
@@ -155,63 +199,63 @@ Style/Lambda:
Style/LineEndConcatenation:
Enabled: false
-# Offense count: 34
+# Offense count: 40
# Cop supports --auto-correct.
Style/MethodCallWithoutArgsParentheses:
Enabled: false
-# Offense count: 10
+# Offense count: 13
Style/MethodMissing:
Enabled: false
-# Offense count: 3
+# Offense count: 6
# Cop supports --auto-correct.
Style/MultilineIfModifier:
Enabled: false
-# Offense count: 24
+# Offense count: 26
# Cop supports --auto-correct.
Style/NestedParenthesizedCalls:
Enabled: false
-# Offense count: 18
+# Offense count: 20
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, MinBodyLength, SupportedStyles.
# SupportedStyles: skip_modifier_ifs, always
Style/Next:
Enabled: false
-# Offense count: 37
+# Offense count: 45
# Cop supports --auto-correct.
# Configuration parameters: EnforcedOctalStyle, SupportedOctalStyles.
# SupportedOctalStyles: zero_with_o, zero_only
Style/NumericLiteralPrefix:
Enabled: false
-# Offense count: 88
+# Offense count: 98
# Cop supports --auto-correct.
# Configuration parameters: AutoCorrect, EnforcedStyle, SupportedStyles.
# SupportedStyles: predicate, comparison
Style/NumericPredicate:
Enabled: false
-# Offense count: 36
+# Offense count: 42
# Cop supports --auto-correct.
Style/ParallelAssignment:
Enabled: false
-# Offense count: 570
+# Offense count: 800
# Cop supports --auto-correct.
# Configuration parameters: PreferredDelimiters.
Style/PercentLiteralDelimiters:
Enabled: false
-# Offense count: 14
+# Offense count: 15
# Cop supports --auto-correct.
Style/PerlBackrefs:
Enabled: false
-# Offense count: 83
+# Offense count: 105
# Configuration parameters: NamePrefix, NamePrefixBlacklist, NameWhitelist.
# NamePrefix: is_, has_, have_
# NamePrefixBlacklist: is_, has_, have_
@@ -219,47 +263,47 @@ Style/PerlBackrefs:
Style/PredicateName:
Enabled: false
-# Offense count: 65
+# Offense count: 58
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, SupportedStyles.
# SupportedStyles: compact, exploded
Style/RaiseArgs:
Enabled: false
-# Offense count: 5
+# Offense count: 6
# Cop supports --auto-correct.
Style/RedundantBegin:
Enabled: false
-# Offense count: 32
+# Offense count: 37
# Cop supports --auto-correct.
Style/RedundantFreeze:
Enabled: false
-# Offense count: 15
+# Offense count: 14
# Cop supports --auto-correct.
# Configuration parameters: AllowMultipleReturnValues.
Style/RedundantReturn:
Enabled: false
-# Offense count: 382
+# Offense count: 406
# Cop supports --auto-correct.
Style/RedundantSelf:
Enabled: false
-# Offense count: 111
+# Offense count: 115
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, SupportedStyles, AllowInnerSlashes.
# SupportedStyles: slashes, percent_r, mixed
Style/RegexpLiteral:
Enabled: false
-# Offense count: 24
+# Offense count: 29
# Cop supports --auto-correct.
Style/RescueModifier:
Enabled: false
-# Offense count: 7
+# Offense count: 8
# Cop supports --auto-correct.
Style/SelfAssignment:
Enabled: false
@@ -270,101 +314,58 @@ Style/SelfAssignment:
Style/SingleLineMethods:
Enabled: false
-# Offense count: 168
-# Cop supports --auto-correct.
-# Configuration parameters: EnforcedStyle, SupportedStyles.
-# SupportedStyles: space, no_space
-Style/SpaceBeforeBlockBraces:
- Enabled: false
-
-# Offense count: 8
-# Cop supports --auto-correct.
-# Configuration parameters: AllowForAlignment.
-Style/SpaceBeforeFirstArg:
- Enabled: false
-
-# Offense count: 46
-# Cop supports --auto-correct.
-# Configuration parameters: EnforcedStyle, SupportedStyles.
-# SupportedStyles: require_no_space, require_space
-Style/SpaceInLambdaLiteral:
- Enabled: false
-
-# Offense count: 229
-# Cop supports --auto-correct.
-# Configuration parameters: EnforcedStyle, SupportedStyles, EnforcedStyleForEmptyBraces, SupportedStylesForEmptyBraces, SpaceBeforeBlockParameters.
-# SupportedStyles: space, no_space
-# SupportedStylesForEmptyBraces: space, no_space
-Style/SpaceInsideBlockBraces:
- Enabled: false
-
-# Offense count: 116
-# Cop supports --auto-correct.
-Style/SpaceInsideParens:
- Enabled: false
-
-# Offense count: 12
-# Cop supports --auto-correct.
-Style/SpaceInsidePercentLiteralDelimiters:
- Enabled: false
-
-# Offense count: 57
+# Offense count: 64
# Cop supports --auto-correct.
# Configuration parameters: SupportedStyles.
# SupportedStyles: use_perl_names, use_english_names
Style/SpecialGlobalVars:
EnforcedStyle: use_perl_names
-# Offense count: 42
+# Offense count: 44
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, SupportedStyles.
# SupportedStyles: single_quotes, double_quotes
Style/StringLiteralsInInterpolation:
Enabled: false
-# Offense count: 64
+# Offense count: 84
# Cop supports --auto-correct.
# Configuration parameters: IgnoredMethods.
# IgnoredMethods: respond_to, define_method
Style/SymbolProc:
Enabled: false
-# Offense count: 6
+# Offense count: 8
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, SupportedStyles, AllowSafeAssignment.
# SupportedStyles: require_parentheses, require_no_parentheses, require_parentheses_when_complex
Style/TernaryParentheses:
Enabled: false
-# Offense count: 18
+# Offense count: 17
# Cop supports --auto-correct.
# Configuration parameters: AllowNamedUnderscoreVariables.
Style/TrailingUnderscoreVariable:
Enabled: false
-# Offense count: 78
-# Cop supports --auto-correct.
-Style/TrailingWhitespace:
- Enabled: false
-
-# Offense count: 3
+# Offense count: 4
# Cop supports --auto-correct.
# Configuration parameters: ExactNameMatch, AllowPredicates, AllowDSLWriters, IgnoreClassMethods, Whitelist.
# Whitelist: to_ary, to_a, to_c, to_enum, to_h, to_hash, to_i, to_int, to_io, to_open, to_path, to_proc, to_r, to_regexp, to_str, to_s, to_sym
Style/TrivialAccessors:
Enabled: false
-# Offense count: 6
+# Offense count: 5
# Cop supports --auto-correct.
Style/UnlessElse:
Enabled: false
-# Offense count: 24
+# Offense count: 28
# Cop supports --auto-correct.
Style/UnneededInterpolation:
Enabled: false
-# Offense count: 8
+# Offense count: 11
# Cop supports --auto-correct.
Style/ZeroLengthPredicate:
Enabled: false
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 412cd86bad6..706823ed693 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,16 @@
documentation](doc/development/changelog.md) for instructions on adding your own
entry.
+## 9.4.3 (2017-07-31)
+
+- Fix Prometheus client PID reuse bug. !13130
+- Improve deploy environment chatops slash command. !13150
+- Fix asynchronous javascript paths when GitLab is installed under a relative URL. !13165
+- Fix LDAP authentication to Git repository or container registry.
+- Fixed new navigation breadcrumb title on help pages.
+- Ensure filesystem metrics test files are deleted.
+- Properly affixes nav bar in job view in microsoft edge.
+
## 9.4.2 (2017-07-28)
- Fix job merge request link to a forked source project. !12965
diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION
index d21d277be51..4e8f395fa5e 100644
--- a/GITALY_SERVER_VERSION
+++ b/GITALY_SERVER_VERSION
@@ -1 +1 @@
-0.25.0
+0.26.0
diff --git a/Gemfile b/Gemfile
index afea07ee6ff..a9a1cbc144d 100644
--- a/Gemfile
+++ b/Gemfile
@@ -339,8 +339,8 @@ group :development, :test do
gem 'spring-commands-rspec', '~> 1.0.4'
gem 'spring-commands-spinach', '~> 1.1.0'
- gem 'rubocop', '~> 0.47.1', require: false
- gem 'rubocop-rspec', '~> 1.15.0', require: false
+ gem 'rubocop', '~> 0.49.1', require: false
+ gem 'rubocop-rspec', '~> 1.15.1', require: false
gem 'scss_lint', '~> 0.54.0', require: false
gem 'haml_lint', '~> 0.21.0', require: false
gem 'simplecov', '~> 0.14.0', require: false
diff --git a/Gemfile.lock b/Gemfile.lock
index 627750e2c1d..5a327a14c4a 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -323,7 +323,7 @@ GEM
multi_json (~> 1.10)
retriable (~> 1.4)
signet (~> 0.6)
- google-protobuf (3.2.0.2)
+ google-protobuf (3.3.0)
googleauth (0.5.1)
faraday (~> 0.9)
jwt (~> 1.4)
@@ -545,6 +545,7 @@ GEM
rubypants (~> 0.2)
orm_adapter (0.5.0)
os (0.9.6)
+ parallel (1.11.2)
paranoia (2.3.1)
activerecord (>= 4.0, < 5.2)
parser (2.4.0.0)
@@ -730,13 +731,14 @@ GEM
pg
rails
sqlite3
- rubocop (0.47.1)
+ rubocop (0.49.1)
+ parallel (~> 1.10)
parser (>= 2.3.3.1, < 3.0)
powerpack (~> 0.1)
rainbow (>= 1.99.1, < 3.0)
ruby-progressbar (~> 1.7)
unicode-display_width (~> 1.0, >= 1.0.1)
- rubocop-rspec (1.15.0)
+ rubocop-rspec (1.15.1)
rubocop (>= 0.42.0)
ruby-fogbugz (0.2.1)
crack (~> 0.4)
@@ -868,7 +870,7 @@ GEM
unf (0.1.4)
unf_ext
unf_ext (0.0.7.2)
- unicode-display_width (1.1.3)
+ unicode-display_width (1.3.0)
unicorn (5.1.0)
kgio (~> 2.6)
raindrops (~> 0.7)
@@ -1078,8 +1080,8 @@ DEPENDENCIES
rspec-retry (~> 0.4.5)
rspec-set (~> 0.1.3)
rspec_profiling (~> 0.0.5)
- rubocop (~> 0.47.1)
- rubocop-rspec (~> 1.15.0)
+ rubocop (~> 0.49.1)
+ rubocop-rspec (~> 1.15.1)
ruby-fogbugz (~> 0.2.1)
ruby-prof (~> 0.16.2)
ruby_parser (~> 3.8)
diff --git a/PROCESS.md b/PROCESS.md
index 3b97a4e8c75..2b3d142bf77 100644
--- a/PROCESS.md
+++ b/PROCESS.md
@@ -128,7 +128,7 @@ information, see
### After the 7th
-Once the stable branch is frozen, only fixes for regressions (bugs introduced in that same release)
+Once the stable branch is frozen, only fixes for [regressions](#regressions)
and security issues will be cherry-picked into the stable branch.
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.
@@ -158,6 +158,24 @@ release should have the correct milestone assigned _and_ have the label
Merge requests without a milestone and this label will
not be merged into any stable branches.
+### Regressions
+
+A regression for a particular monthly release is a bug that exists in that
+release, but wasn't present in the release before. This includes bugs in
+features that were only added in that monthly release. Every regression **must**
+have the milestone of the release it was introduced in - if a regression doesn't
+have a milestone, it might be 'just' a bug!
+
+For instance, if 10.5.0 adds a feature, and that feature doesn't work correctly,
+then this is a regression in 10.5. If 10.5.1 then fixes that, but 10.5.3 somehow
+reintroduces the bug, then this bug is still a regression in 10.5.
+
+Because GitLab.com runs release candidates of new releases, a regression can be
+reported in a release before its 'official' release date on the 22nd of the
+month. When we say 'the most recent monthly release', this can refer to either
+the version currently running on GitLab.com, or the most recent version
+available in the package repositories.
+
## Release retrospective and kickoff
### Retrospective
diff --git a/app/assets/javascripts/blob_edit/blob_bundle.js b/app/assets/javascripts/blob_edit/blob_bundle.js
index 1c64ccf536f..b5500ac116f 100644
--- a/app/assets/javascripts/blob_edit/blob_bundle.js
+++ b/app/assets/javascripts/blob_edit/blob_bundle.js
@@ -8,6 +8,7 @@ import BlobFileDropzone from '../blob/blob_file_dropzone';
$(() => {
const editBlobForm = $('.js-edit-blob-form');
const uploadBlobForm = $('.js-upload-blob-form');
+ const deleteBlobForm = $('.js-delete-blob-form');
if (editBlobForm.length) {
const urlRoot = editBlobForm.data('relative-url-root');
@@ -30,4 +31,8 @@ $(() => {
'.btn-upload-file',
);
}
+
+ if (deleteBlobForm.length) {
+ new NewCommitForm(deleteBlobForm);
+ }
});
diff --git a/app/assets/javascripts/build.js b/app/assets/javascripts/build.js
index 1dfa064acfd..b3d3bbcf84f 100644
--- a/app/assets/javascripts/build.js
+++ b/app/assets/javascripts/build.js
@@ -64,7 +64,7 @@ window.Build = (function () {
$(window)
.off('scroll')
.on('scroll', () => {
- const contentHeight = this.$buildTraceOutput.prop('scrollHeight');
+ const contentHeight = this.$buildTraceOutput.height();
if (contentHeight > this.windowSize) {
// means the user did not scroll, the content was updated.
this.windowSize = contentHeight;
@@ -105,16 +105,17 @@ window.Build = (function () {
};
Build.prototype.canScroll = function () {
- return document.body.scrollHeight > window.innerHeight;
+ return $(document).height() > $(window).height();
};
Build.prototype.toggleScroll = function () {
- const currentPosition = document.body.scrollTop;
- const windowHeight = window.innerHeight;
+ const currentPosition = $(document).scrollTop();
+ const scrollHeight = $(document).height();
+ const windowHeight = $(window).height();
if (this.canScroll()) {
if (currentPosition > 0 &&
- (document.body.scrollHeight - currentPosition !== windowHeight)) {
+ (scrollHeight - currentPosition !== windowHeight)) {
// User is in the middle of the log
this.toggleDisableButton(this.$scrollTopBtn, false);
@@ -124,7 +125,7 @@ window.Build = (function () {
this.toggleDisableButton(this.$scrollTopBtn, true);
this.toggleDisableButton(this.$scrollBottomBtn, false);
- } else if (document.body.scrollHeight - currentPosition === windowHeight) {
+ } else if (scrollHeight - currentPosition === windowHeight) {
// User is at the bottom of the build log.
this.toggleDisableButton(this.$scrollTopBtn, false);
@@ -137,7 +138,7 @@ window.Build = (function () {
};
Build.prototype.scrollDown = function () {
- document.body.scrollTop = document.body.scrollHeight;
+ $(document).scrollTop($(document).height());
};
Build.prototype.scrollToBottom = function () {
@@ -147,7 +148,7 @@ window.Build = (function () {
};
Build.prototype.scrollToTop = function () {
- document.body.scrollTop = 0;
+ $(document).scrollTop(0);
this.hasBeenScrolled = true;
this.toggleScroll();
};
@@ -178,7 +179,7 @@ window.Build = (function () {
this.state = log.state;
}
- this.windowSize = this.$buildTraceOutput.prop('scrollHeight');
+ this.windowSize = this.$buildTraceOutput.height();
if (log.append) {
this.$buildTraceOutput.append(log.html);
diff --git a/app/assets/javascripts/dispatcher.js b/app/assets/javascripts/dispatcher.js
index 9d706b5ba59..a2664c0301e 100644
--- a/app/assets/javascripts/dispatcher.js
+++ b/app/assets/javascripts/dispatcher.js
@@ -8,6 +8,8 @@
/* global LabelsSelect */
/* global MilestoneSelect */
/* global Commit */
+/* global CommitsList */
+/* global NewBranchForm */
/* global NotificationsForm */
/* global NotificationsDropdown */
/* global GroupAvatar */
@@ -18,15 +20,20 @@
/* global Search */
/* global Admin */
/* global NamespaceSelects */
+/* global NewCommitForm */
+/* global NewBranchForm */
/* global Project */
/* global ProjectAvatar */
/* global MergeRequest */
/* global Compare */
/* global CompareAutocomplete */
+/* global ProjectFindFile */
/* global ProjectNew */
/* global ProjectShow */
+/* global ProjectImport */
/* global Labels */
/* global Shortcuts */
+/* global ShortcutsFindFile */
/* global Sidebar */
/* global ShortcutsWiki */
@@ -194,7 +201,6 @@ import GpgBadges from './gpg_badges';
break;
case 'explore:groups:index':
new GroupsList();
-
const landingElement = document.querySelector('.js-explore-groups-landing');
if (!landingElement) break;
const exploreGroupsLanding = new Landing(
@@ -217,6 +223,10 @@ import GpgBadges from './gpg_badges';
case 'projects:compare:show':
new gl.Diff();
break;
+ case 'projects:branches:new':
+ case 'projects:branches:create':
+ new NewBranchForm($('.js-create-branch-form'), JSON.parse(document.getElementById('availableRefs').innerHTML));
+ break;
case 'projects:branches:index':
gl.AjaxLoadingSpinner.init();
new DeleteModal();
@@ -258,7 +268,7 @@ import GpgBadges from './gpg_badges';
case 'projects:tags:new':
new ZenMode();
new gl.GLForm($('.tag-form'), true);
- new RefSelectDropdown($('.js-branch-select'), window.gl.availableRefs);
+ new RefSelectDropdown($('.js-branch-select'));
break;
case 'projects:snippets:show':
initNotes();
@@ -304,18 +314,23 @@ import GpgBadges from './gpg_badges';
container: '.js-commit-pipeline-graph',
}).bindEvents();
initNotes();
+ $('.commit-info.branches').load(document.querySelector('.js-commit-box').dataset.commitPath);
break;
case 'projects:commit:pipelines':
new MiniPipelineGraph({
container: '.js-commit-pipeline-graph',
}).bindEvents();
+ $('.commit-info.branches').load(document.querySelector('.js-commit-box').dataset.commitPath);
break;
- case 'projects:commits:show':
+ case 'projects:activity':
+ new gl.Activities();
shortcut_handler = new ShortcutsNavigation();
- GpgBadges.fetch();
break;
- case 'projects:activity':
+ case 'projects:commits:show':
+ CommitsList.init(document.querySelector('.js-project-commits-show').dataset.commitsLimit);
+ new gl.Activities();
shortcut_handler = new ShortcutsNavigation();
+ GpgBadges.fetch();
break;
case 'projects:show':
shortcut_handler = new ShortcutsNavigation();
@@ -330,6 +345,12 @@ import GpgBadges from './gpg_badges';
case 'projects:edit':
setupProjectEdit();
break;
+ case 'projects:imports:show':
+ new ProjectImport();
+ break;
+ case 'projects:pipelines:new':
+ new NewBranchForm($('.js-new-pipeline-form'));
+ break;
case 'projects:pipelines:builds':
case 'projects:pipelines:failures':
case 'projects:pipelines:show':
@@ -383,8 +404,19 @@ import GpgBadges from './gpg_badges';
shortcut_handler = new ShortcutsNavigation();
new TreeView();
new BlobViewer();
+ new NewCommitForm($('.js-create-dir-form'));
+ $('#tree-slider').waitForImages(function() {
+ gl.utils.ajaxGet(document.querySelector('.js-tree-content').dataset.logsPath);
+ });
break;
case 'projects:find_file:show':
+ const findElement = document.querySelector('.js-file-finder');
+ const projectFindFile = new ProjectFindFile($(".file-finder-holder"), {
+ url: findElement.dataset.fileFindUrl,
+ treeUrl: findElement.dataset.findTreeUrl,
+ blobUrlTemplate: findElement.dataset.blobUrlTemplate,
+ });
+ new ShortcutsFindFile(projectFindFile);
shortcut_handler = true;
break;
case 'projects:blob:show':
@@ -540,6 +572,7 @@ import GpgBadges from './gpg_badges';
shortcut_handler = new ShortcutsWiki();
new ZenMode();
new gl.GLForm($('.wiki-form'), true);
+ new Sidebar();
break;
case 'snippets':
shortcut_handler = new ShortcutsNavigation();
diff --git a/app/assets/javascripts/gl_dropdown.js b/app/assets/javascripts/gl_dropdown.js
index 3babe273100..9475498e176 100644
--- a/app/assets/javascripts/gl_dropdown.js
+++ b/app/assets/javascripts/gl_dropdown.js
@@ -730,10 +730,10 @@ GitLabDropdown = (function() {
GitLabDropdown.prototype.focusTextInput = function(triggerFocus = false) {
if (this.options.filterable) {
- $(':focus').blur();
-
this.dropdown.one('transitionend', () => {
- this.filterInput.focus();
+ if (this.dropdown.is('.open')) {
+ this.filterInput.focus();
+ }
});
if (triggerFocus) {
diff --git a/app/assets/javascripts/graphs/graphs_bundle.js b/app/assets/javascripts/graphs/graphs_bundle.js
index a433c7ba8f0..534bc535bb6 100644
--- a/app/assets/javascripts/graphs/graphs_bundle.js
+++ b/app/assets/javascripts/graphs/graphs_bundle.js
@@ -1,6 +1,4 @@
import Chart from 'vendor/Chart';
-import ContributorsStatGraph from './stat_graph_contributors';
// export to global scope
window.Chart = Chart;
-window.ContributorsStatGraph = ContributorsStatGraph;
diff --git a/app/assets/javascripts/graphs/graphs_charts.js b/app/assets/javascripts/graphs/graphs_charts.js
new file mode 100644
index 00000000000..279ffef770f
--- /dev/null
+++ b/app/assets/javascripts/graphs/graphs_charts.js
@@ -0,0 +1,63 @@
+import Chart from 'vendor/Chart';
+
+document.addEventListener('DOMContentLoaded', () => {
+ const projectChartData = JSON.parse(document.getElementById('projectChartData').innerHTML);
+
+ const responsiveChart = (selector, data) => {
+ const options = {
+ scaleOverlay: true,
+ responsive: true,
+ pointHitDetectionRadius: 2,
+ maintainAspectRatio: false,
+ };
+ // get selector by context
+ const ctx = selector.get(0).getContext('2d');
+ // pointing parent container to make chart.js inherit its width
+ const container = $(selector).parent();
+ const generateChart = () => {
+ selector.attr('width', $(container).width());
+ if (window.innerWidth < 768) {
+ // Scale fonts if window width lower than 768px (iPad portrait)
+ options.scaleFontSize = 8;
+ }
+ return new Chart(ctx).Bar(data, options);
+ };
+ // enabling auto-resizing
+ $(window).resize(generateChart);
+ return generateChart();
+ };
+
+ const chartData = (keys, values) => {
+ const data = {
+ labels: keys,
+ datasets: [{
+ fillColor: 'rgba(220,220,220,0.5)',
+ strokeColor: 'rgba(220,220,220,1)',
+ barStrokeWidth: 1,
+ barValueSpacing: 1,
+ barDatasetSpacing: 1,
+ data: values,
+ }],
+ };
+ return data;
+ };
+
+ const hourData = chartData(projectChartData.hour.keys, projectChartData.hour.values);
+ responsiveChart($('#hour-chart'), hourData);
+
+ const dayData = chartData(projectChartData.weekDays.keys, projectChartData.weekDays.values);
+ responsiveChart($('#weekday-chart'), dayData);
+
+ const monthData = chartData(projectChartData.month.keys, projectChartData.month.values);
+ responsiveChart($('#month-chart'), monthData);
+
+ const data = projectChartData.languages;
+ const ctx = $('#languages-chart').get(0).getContext('2d');
+ const options = {
+ scaleOverlay: true,
+ responsive: true,
+ maintainAspectRatio: false,
+ };
+
+ new Chart(ctx).Pie(data, options);
+});
diff --git a/app/assets/javascripts/graphs/graphs_show.js b/app/assets/javascripts/graphs/graphs_show.js
new file mode 100644
index 00000000000..36bad6db3e1
--- /dev/null
+++ b/app/assets/javascripts/graphs/graphs_show.js
@@ -0,0 +1,21 @@
+import ContributorsStatGraph from './stat_graph_contributors';
+
+document.addEventListener('DOMContentLoaded', () => {
+ $.ajax({
+ type: 'GET',
+ url: document.querySelector('.js-graphs-show').dataset.projectGraphPath,
+ dataType: 'json',
+ success(data) {
+ const graph = new ContributorsStatGraph();
+ graph.init(data);
+
+ $('#brush_change').change(() => {
+ graph.change_date_header();
+ graph.redraw_authors();
+ });
+
+ $('.stat-graph').fadeIn();
+ $('.loading-graph').hide();
+ },
+ });
+});
diff --git a/app/assets/javascripts/pipelines/pipelines_charts.js b/app/assets/javascripts/pipelines/pipelines_charts.js
new file mode 100644
index 00000000000..001faf4be33
--- /dev/null
+++ b/app/assets/javascripts/pipelines/pipelines_charts.js
@@ -0,0 +1,38 @@
+import Chart from 'vendor/Chart';
+
+document.addEventListener('DOMContentLoaded', () => {
+ const chartData = JSON.parse(document.getElementById('pipelinesChartsData').innerHTML);
+ const buildChart = (chartScope) => {
+ const data = {
+ labels: chartScope.labels,
+ datasets: [{
+ fillColor: '#7f8fa4',
+ strokeColor: '#7f8fa4',
+ pointColor: '#7f8fa4',
+ pointStrokeColor: '#EEE',
+ data: chartScope.totalValues,
+ },
+ {
+ fillColor: '#44aa22',
+ strokeColor: '#44aa22',
+ pointColor: '#44aa22',
+ pointStrokeColor: '#fff',
+ data: chartScope.successValues,
+ },
+ ],
+ };
+ const ctx = $(`#${chartScope.scope}Chart`).get(0).getContext('2d');
+ const options = {
+ scaleOverlay: true,
+ responsive: true,
+ maintainAspectRatio: false,
+ };
+ if (window.innerWidth < 768) {
+ // Scale fonts if window width lower than 768px (iPad portrait)
+ options.scaleFontSize = 8;
+ }
+ new Chart(ctx).Line(data, options);
+ };
+
+ chartData.forEach(scope => buildChart(scope));
+});
diff --git a/app/assets/javascripts/pipelines/pipelines_times.js b/app/assets/javascripts/pipelines/pipelines_times.js
new file mode 100644
index 00000000000..b5e7a0e53d9
--- /dev/null
+++ b/app/assets/javascripts/pipelines/pipelines_times.js
@@ -0,0 +1,27 @@
+import Chart from 'vendor/Chart';
+
+document.addEventListener('DOMContentLoaded', () => {
+ const chartData = JSON.parse(document.getElementById('pipelinesTimesChartsData').innerHTML);
+ const data = {
+ labels: chartData.labels,
+ datasets: [{
+ fillColor: 'rgba(220,220,220,0.5)',
+ strokeColor: 'rgba(220,220,220,1)',
+ barStrokeWidth: 1,
+ barValueSpacing: 1,
+ barDatasetSpacing: 1,
+ data: chartData.values,
+ }],
+ };
+ const ctx = $('#build_timesChart').get(0).getContext('2d');
+ const options = {
+ scaleOverlay: true,
+ responsive: true,
+ maintainAspectRatio: false,
+ };
+ if (window.innerWidth < 768) {
+ // Scale fonts if window width lower than 768px (iPad portrait)
+ options.scaleFontSize = 8;
+ }
+ new Chart(ctx).Bar(data, options);
+});
diff --git a/app/assets/javascripts/projects/project_new.js b/app/assets/javascripts/projects/project_new.js
new file mode 100644
index 00000000000..1dc1dbf356d
--- /dev/null
+++ b/app/assets/javascripts/projects/project_new.js
@@ -0,0 +1,85 @@
+let hasUserDefinedProjectPath = false;
+
+const deriveProjectPathFromUrl = ($projectImportUrl, $projectPath) => {
+ if ($projectImportUrl.attr('disabled') || hasUserDefinedProjectPath) {
+ return;
+ }
+
+ let importUrl = $projectImportUrl.val().trim();
+ if (importUrl.length === 0) {
+ return;
+ }
+
+ /*
+ \/?: remove trailing slash
+ (\.git\/?)?: remove trailing .git (with optional trailing slash)
+ (\?.*)?: remove query string
+ (#.*)?: remove fragment identifier
+ */
+ importUrl = importUrl.replace(/\/?(\.git\/?)?(\?.*)?(#.*)?$/, '');
+
+ // extract everything after the last slash
+ const pathMatch = /\/([^/]+)$/.exec(importUrl);
+ if (pathMatch) {
+ $projectPath.val(pathMatch[1]);
+ }
+};
+
+const bindEvents = () => {
+ const $newProjectForm = $('#new_project');
+ const importBtnTooltip = 'Please enter a valid project name.';
+ const $importBtnWrapper = $('.import_gitlab_project');
+ const $projectImportUrl = $('#project_import_url');
+ const $projectPath = $('#project_path');
+
+ if ($newProjectForm.length !== 1) {
+ return;
+ }
+
+ $('.how_to_import_link').on('click', (e) => {
+ e.preventDefault();
+ $('.how_to_import_link').next('.modal').show();
+ });
+
+ $('.modal-header .close').on('click', () => {
+ $('.modal').hide();
+ });
+
+ $('.btn_import_gitlab_project').on('click', () => {
+ const importHref = $('a.btn_import_gitlab_project').attr('href');
+ $('.btn_import_gitlab_project').attr('href', `${importHref}?namespace_id=${$('#project_namespace_id').val()}&path=${$projectPath.val()}`);
+ });
+
+ $('.btn_import_gitlab_project').attr('disabled', !$projectPath.val().trim().length);
+ $importBtnWrapper.attr('title', importBtnTooltip);
+
+ $newProjectForm.on('submit', () => {
+ $projectPath.val($projectPath.val().trim());
+ });
+
+ $projectPath.on('keyup', () => {
+ hasUserDefinedProjectPath = $projectPath.val().trim().length > 0;
+ if (hasUserDefinedProjectPath) {
+ $('.btn_import_gitlab_project').attr('disabled', false);
+ $importBtnWrapper.attr('title', '');
+ $importBtnWrapper.removeClass('has-tooltip');
+ } else {
+ $('.btn_import_gitlab_project').attr('disabled', true);
+ $importBtnWrapper.addClass('has-tooltip');
+ }
+ });
+
+ $projectImportUrl.disable();
+ $projectImportUrl.keyup(() => deriveProjectPathFromUrl($projectImportUrl, $projectPath));
+
+ $('.import_git').on('click', () => {
+ $projectImportUrl.attr('disabled', !$projectImportUrl.attr('disabled'));
+ });
+};
+
+document.addEventListener('DOMContentLoaded', bindEvents);
+
+export default {
+ bindEvents,
+ deriveProjectPathFromUrl,
+};
diff --git a/app/assets/javascripts/ref_select_dropdown.js b/app/assets/javascripts/ref_select_dropdown.js
index 215cd6fbdfd..65e4101352c 100644
--- a/app/assets/javascripts/ref_select_dropdown.js
+++ b/app/assets/javascripts/ref_select_dropdown.js
@@ -1,7 +1,8 @@
class RefSelectDropdown {
constructor($dropdownButton, availableRefs) {
+ const availableRefsValue = availableRefs || JSON.parse(document.getElementById('availableRefs').innerHTML);
$dropdownButton.glDropdown({
- data: availableRefs,
+ data: availableRefsValue,
filterable: true,
filterByText: true,
remote: false,
diff --git a/app/assets/javascripts/two_factor_auth.js b/app/assets/javascripts/two_factor_auth.js
new file mode 100644
index 00000000000..d26f61562a5
--- /dev/null
+++ b/app/assets/javascripts/two_factor_auth.js
@@ -0,0 +1,13 @@
+/* global U2FRegister */
+document.addEventListener('DOMContentLoaded', () => {
+ const twoFactorNode = document.querySelector('.js-two-factor-auth');
+ const skippable = twoFactorNode.dataset.twoFactorSkippable === 'true';
+ if (skippable) {
+ const button = `<a class="btn btn-xs btn-warning pull-right" data-method="patch" href="${twoFactorNode.dataset.two_factor_skip_url}">Configure it later</a>`;
+ const flashAlert = document.querySelector('.flash-alert .container-fluid');
+ if (flashAlert) flashAlert.insertAdjacentHTML('beforeend', button);
+ }
+
+ const u2fRegister = new U2FRegister($('#js-register-u2f'), gon.u2f);
+ u2fRegister.start();
+});
diff --git a/app/assets/javascripts/ui_development_kit.js b/app/assets/javascripts/ui_development_kit.js
new file mode 100644
index 00000000000..f503076715c
--- /dev/null
+++ b/app/assets/javascripts/ui_development_kit.js
@@ -0,0 +1,22 @@
+import Api from './api';
+
+document.addEventListener('DOMContentLoaded', () => {
+ $('#js-project-dropdown').glDropdown({
+ data: (term, callback) => {
+ Api.projects(term, {
+ order_by: 'last_activity_at',
+ }, (data) => {
+ callback(data);
+ });
+ },
+ text: project => (project.name_with_namespace || project.name),
+ selectable: true,
+ fieldName: 'author_id',
+ filterable: true,
+ search: {
+ fields: ['name_with_namespace'],
+ },
+ id: data => data.id,
+ isSelected: data => (data.id === 2),
+ });
+});
diff --git a/app/assets/stylesheets/framework/avatar.scss b/app/assets/stylesheets/framework/avatar.scss
index 0dfa7a31d31..cb41df8a88d 100644
--- a/app/assets/stylesheets/framework/avatar.scss
+++ b/app/assets/stylesheets/framework/avatar.scss
@@ -88,6 +88,10 @@
overflow: hidden;
display: flex;
+ a {
+ display: flex;
+ }
+
.avatar {
border-radius: 0;
border: none;
diff --git a/app/assets/stylesheets/framework/filters.scss b/app/assets/stylesheets/framework/filters.scss
index ab2abaca33a..ec13a86ccf7 100644
--- a/app/assets/stylesheets/framework/filters.scss
+++ b/app/assets/stylesheets/framework/filters.scss
@@ -414,13 +414,16 @@
background-color: $dropdown-hover-color;
color: $white-light;
text-decoration: none;
+ outline: 0;
.avatar {
border-color: $white-light;
}
}
-.filter-dropdown-item {
+.droplab-dropdown .dropdown-menu .filter-dropdown-item {
+ padding: 0;
+
.btn {
border: none;
width: 100%;
@@ -455,14 +458,11 @@
}
.dropdown-user {
- display: -webkit-flex;
display: flex;
}
.dropdown-user-details {
- display: -webkit-flex;
display: flex;
- -webkit-flex-direction: column;
flex-direction: column;
> span {
diff --git a/app/assets/stylesheets/framework/header.scss b/app/assets/stylesheets/framework/header.scss
index 605f4284bb5..1c4238bc564 100644
--- a/app/assets/stylesheets/framework/header.scss
+++ b/app/assets/stylesheets/framework/header.scss
@@ -313,6 +313,29 @@ header {
.impersonation i {
color: $red-500;
}
+
+ // TODO: fallback to global style
+ .dropdown-menu,
+ .dropdown-menu-nav {
+ li {
+ padding: 0 1px;
+
+ a {
+ border-radius: 0;
+ padding: 8px 16px;
+
+ &:hover,
+ &:active,
+ &:focus {
+ background-color: $gray-darker;
+ }
+ }
+ }
+ }
+}
+
+.with-performance-bar header.navbar-gitlab {
+ top: $performance-bar-height;
}
.navbar-nav {
diff --git a/app/assets/stylesheets/framework/layout.scss b/app/assets/stylesheets/framework/layout.scss
index 4a9d41b4fda..67c3287ed74 100644
--- a/app/assets/stylesheets/framework/layout.scss
+++ b/app/assets/stylesheets/framework/layout.scss
@@ -120,3 +120,7 @@ of the body element here, we negate cascading side effects but allow momentum sc
.page-with-sidebar {
-webkit-overflow-scrolling: auto;
}
+
+.with-performance-bar .page-with-sidebar {
+ margin-top: $header-height + $performance-bar-height;
+}
diff --git a/app/assets/stylesheets/framework/nav.scss b/app/assets/stylesheets/framework/nav.scss
index 35b4d77a5ab..88e7ba117d5 100644
--- a/app/assets/stylesheets/framework/nav.scss
+++ b/app/assets/stylesheets/framework/nav.scss
@@ -347,6 +347,10 @@
}
}
+.with-performance-bar .layout-nav {
+ margin-top: $header-height + $performance-bar-height;
+}
+
.scrolling-tabs-container {
position: relative;
@@ -441,6 +445,22 @@
}
}
+.with-performance-bar .page-with-layout-nav {
+ .right-sidebar {
+ top: ($header-height + 1) * 2 + $performance-bar-height;
+ }
+
+ &.page-with-sub-nav {
+ .right-sidebar {
+ top: ($header-height + 1) * 3 + $performance-bar-height;
+
+ &.affix {
+ top: $header-height + $performance-bar-height;
+ }
+ }
+ }
+}
+
.nav-block {
&.activities {
border-bottom: 1px solid $border-color;
diff --git a/app/assets/stylesheets/framework/sidebar.scss b/app/assets/stylesheets/framework/sidebar.scss
index 49b2f0e43a4..09b60ad1676 100644
--- a/app/assets/stylesheets/framework/sidebar.scss
+++ b/app/assets/stylesheets/framework/sidebar.scss
@@ -89,6 +89,10 @@
}
}
+.with-performance-bar .right-sidebar.affix {
+ top: $header-height + $performance-bar-height;
+}
+
@mixin maintain-sidebar-dimensions {
display: block;
width: $gutter-width;
diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss
index cf0a1ad57d0..0df6f24bfe6 100644
--- a/app/assets/stylesheets/framework/variables.scss
+++ b/app/assets/stylesheets/framework/variables.scss
@@ -204,6 +204,7 @@ $divergence-graph-separator-bg: #ccc;
$general-hover-transition-duration: 100ms;
$general-hover-transition-curve: linear;
$highlight-changes-color: rgb(235, 255, 232);
+$performance-bar-height: 35px;
/*
diff --git a/app/assets/stylesheets/new_nav.scss b/app/assets/stylesheets/new_nav.scss
index 360ffda8d71..1c4a84de7ec 100644
--- a/app/assets/stylesheets/new_nav.scss
+++ b/app/assets/stylesheets/new_nav.scss
@@ -309,6 +309,25 @@ header.navbar-gitlab-new {
outline: 0;
}
}
+
+ // TODO: fallback to global style
+ .dropdown-menu {
+ li {
+ padding: 0 1px;
+
+ a {
+ border-radius: 0;
+ padding: 8px 16px;
+
+ &.is-focused,
+ &:hover,
+ &:active,
+ &:focus {
+ background-color: $gray-darker;
+ }
+ }
+ }
+ }
}
.breadcrumbs-container {
@@ -325,6 +344,7 @@ header.navbar-gitlab-new {
.breadcrumbs-links {
flex: 1;
+ min-width: 0;
align-self: center;
color: $gl-text-color-quaternary;
@@ -343,7 +363,7 @@ header.navbar-gitlab-new {
}
.title {
- white-space: nowrap;
+ display: inline-block;
> a {
&:last-of-type:not(:first-child) {
diff --git a/app/assets/stylesheets/new_sidebar.scss b/app/assets/stylesheets/new_sidebar.scss
index ae43197a1a6..54f3e8d882c 100644
--- a/app/assets/stylesheets/new_sidebar.scss
+++ b/app/assets/stylesheets/new_sidebar.scss
@@ -118,7 +118,7 @@ $new-sidebar-width: 220px;
z-index: 400;
width: $new-sidebar-width;
transition: left $sidebar-transition-duration;
- top: 50px;
+ top: $header-height;
bottom: 0;
left: 0;
overflow: auto;
@@ -163,6 +163,10 @@ $new-sidebar-width: 220px;
}
}
+.with-performance-bar .nav-sidebar {
+ top: $header-height + $performance-bar-height;
+}
+
.sidebar-sub-level-items {
display: none;
padding-bottom: 8px;
@@ -260,7 +264,7 @@ $new-sidebar-width: 220px;
// Make issue boards full-height now that sub-nav is gone
.boards-list {
- height: calc(100vh - 50px);
+ height: calc(100vh - #{$header-height});
@media (min-width: $screen-sm-min) {
height: 475px; // Needed for PhantomJS
@@ -270,6 +274,10 @@ $new-sidebar-width: 220px;
}
}
+.with-performance-bar .boards-list {
+ height: calc(100vh - #{$header-height} - #{$performance-bar-height});
+}
+
// Change color of all horizontal tabs to match the new indigo color
.nav-links li.active a {
diff --git a/app/assets/stylesheets/pages/builds.scss b/app/assets/stylesheets/pages/builds.scss
index b6fc628c02b..28c99d8e57c 100644
--- a/app/assets/stylesheets/pages/builds.scss
+++ b/app/assets/stylesheets/pages/builds.scss
@@ -64,10 +64,10 @@
color: $gl-text-color;
position: sticky;
position: -webkit-sticky;
- top: 50px;
+ top: $header-height;
&.affix {
- top: 50px;
+ top: $header-height;
}
// with sidebar
@@ -86,6 +86,7 @@
position: absolute;
right: 0;
left: 0;
+ top: 0;
}
.truncated-info {
@@ -171,6 +172,16 @@
}
}
+.with-performance-bar .build-page {
+ .top-bar {
+ top: $header-height + $performance-bar-height;
+
+ &.affix {
+ top: $header-height + $performance-bar-height;
+ }
+ }
+}
+
.build-header {
.ci-header-container,
.header-action-buttons {
@@ -300,9 +311,7 @@
}
.dropdown-menu {
- right: $gl-padding;
- left: $gl-padding;
- width: auto;
+ margin-top: -$gl-padding;
}
svg {
diff --git a/app/assets/stylesheets/pages/cycle_analytics.scss b/app/assets/stylesheets/pages/cycle_analytics.scss
index eeb90759f10..87b50c7687e 100644
--- a/app/assets/stylesheets/pages/cycle_analytics.scss
+++ b/app/assets/stylesheets/pages/cycle_analytics.scss
@@ -110,6 +110,10 @@
.js-ca-dropdown {
top: $gl-padding-top;
+
+ .dropdown-menu-align-right {
+ margin-top: 2px;
+ }
}
.content-list {
@@ -442,6 +446,24 @@
margin-bottom: 20px;
}
}
+
+ // TODO: fallback to global style
+ .dropdown-menu {
+ li {
+ padding: 0 1px;
+
+ a {
+ border-radius: 0;
+ padding: 8px 16px;
+
+ &:hover,
+ &:active,
+ &:focus {
+ background-color: $gray-darker;
+ }
+ }
+ }
+ }
}
.cycle-analytics-overview {
diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss
index eb269df46fe..6da14320914 100644
--- a/app/assets/stylesheets/pages/issuable.scss
+++ b/app/assets/stylesheets/pages/issuable.scss
@@ -445,6 +445,14 @@
}
}
+.with-performance-bar .right-sidebar {
+ top: $header-height + $performance-bar-height;
+
+ .issuable-sidebar {
+ height: calc(100% - #{$header-height} - #{$performance-bar-height});
+ }
+}
+
.detail-page-description {
padding: 16px 0;
diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss
index 2db967547dd..4693b2434c7 100644
--- a/app/assets/stylesheets/pages/merge_requests.scss
+++ b/app/assets/stylesheets/pages/merge_requests.scss
@@ -759,6 +759,10 @@
}
}
+.with-performance-bar .merge-request-tabs-holder {
+ top: $header-height + $performance-bar-height;
+}
+
.merge-request-tabs {
display: flex;
margin-bottom: 0;
diff --git a/app/assets/stylesheets/pages/tree.scss b/app/assets/stylesheets/pages/tree.scss
index dc88cf3e699..e0f46172769 100644
--- a/app/assets/stylesheets/pages/tree.scss
+++ b/app/assets/stylesheets/pages/tree.scss
@@ -202,6 +202,28 @@
}
}
}
+
+ // TODO: fallback to global style
+ .dropdown-menu:not(.dropdown-menu-selectable) {
+ li {
+ padding: 0 1px;
+
+ &.dropdown-header {
+ padding: 8px 16px;
+ }
+
+ a {
+ border-radius: 0;
+ padding: 8px 16px;
+
+ &:hover,
+ &:active,
+ &:focus {
+ background-color: $gray-darker;
+ }
+ }
+ }
+ }
}
.blob-commit-info {
diff --git a/app/assets/stylesheets/performance_bar.scss b/app/assets/stylesheets/performance_bar.scss
index 2890b6b1e49..6e539e39ca1 100644
--- a/app/assets/stylesheets/performance_bar.scss
+++ b/app/assets/stylesheets/performance_bar.scss
@@ -3,9 +3,16 @@
@import "peek/views/rblineprof";
#peek {
- height: 35px;
+ position: fixed;
+ left: 0;
+ top: 0;
+ width: 100%;
+ z-index: 2000;
+ overflow-x: hidden;
+
+ height: $performance-bar-height;
background: $black;
- line-height: 35px;
+ line-height: $performance-bar-height;
color: $perf-bar-text;
&.disabled {
@@ -25,7 +32,8 @@
}
.wrapper {
- width: 1000px;
+ width: 80%;
+ height: $performance-bar-height;
margin: 0 auto;
}
diff --git a/app/controllers/projects/graphs_controller.rb b/app/controllers/projects/graphs_controller.rb
index 57372f9e79d..475d4c86294 100644
--- a/app/controllers/projects/graphs_controller.rb
+++ b/app/controllers/projects/graphs_controller.rb
@@ -43,23 +43,7 @@ class Projects::GraphsController < Projects::ApplicationController
end
def get_languages
- @languages = Linguist::Repository.new(@repository.rugged, @repository.rugged.head.target_id).languages
- total = @languages.map(&:last).sum
-
- @languages = @languages.map do |language|
- name, share = language
- color = Linguist::Language[name].color || "##{Digest::SHA256.hexdigest(name)[0...6]}"
- {
- value: (share.to_f * 100 / total).round(2),
- label: name,
- color: color,
- highlight: color
- }
- end
-
- @languages.sort! do |x, y|
- y[:value] <=> x[:value]
- end
+ @languages = @project.repository.languages
end
def fetch_graph
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index 1c165700b19..14dc9bd9d62 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -264,7 +264,11 @@ module ApplicationHelper
end
def page_class
- "issue-boards-page" if current_controller?(:boards)
+ class_names = []
+ class_names << 'issue-boards-page' if current_controller?(:boards)
+ class_names << 'with-performance-bar' if performance_bar_enabled?
+
+ class_names
end
# Returns active css class when condition returns true
diff --git a/app/helpers/gitlab_routing_helper.rb b/app/helpers/gitlab_routing_helper.rb
index 0517a699ae0..1f7db9b2eb8 100644
--- a/app/helpers/gitlab_routing_helper.rb
+++ b/app/helpers/gitlab_routing_helper.rb
@@ -48,7 +48,11 @@ module GitlabRoutingHelper
end
def milestone_path(entity, *args)
- project_milestone_path(entity.project, entity, *args)
+ if entity.is_group_milestone?
+ group_milestone_path(entity.group, entity, *args)
+ elsif entity.is_project_milestone?
+ project_milestone_path(entity.project, entity, *args)
+ end
end
def issue_url(entity, *args)
@@ -63,6 +67,14 @@ module GitlabRoutingHelper
project_pipeline_url(pipeline.project, pipeline.id, *args)
end
+ def milestone_url(entity, *args)
+ if entity.is_group_milestone?
+ group_milestone_url(entity.group, entity, *args)
+ elsif entity.is_project_milestone?
+ project_milestone_url(entity.project, entity, *args)
+ end
+ end
+
def pipeline_job_url(pipeline, build, *args)
project_job_url(pipeline.project, build.id, *args)
end
diff --git a/app/helpers/merge_requests_helper.rb b/app/helpers/merge_requests_helper.rb
index 78cf7b26a31..c31023f2d9a 100644
--- a/app/helpers/merge_requests_helper.rb
+++ b/app/helpers/merge_requests_helper.rb
@@ -40,7 +40,7 @@ module MergeRequestsHelper
def merge_path_description(merge_request, separator)
if merge_request.for_fork?
- "Project:Branches: #{@merge_request.source_project_path}:#{@merge_request.source_branch} #{separator} #{@merge_request.target_project.path_with_namespace}:#{@merge_request.target_branch}"
+ "Project:Branches: #{@merge_request.source_project_path}:#{@merge_request.source_branch} #{separator} #{@merge_request.target_project.full_path}:#{@merge_request.target_branch}"
else
"Branches: #{@merge_request.source_branch} #{separator} #{@merge_request.target_branch}"
end
diff --git a/app/helpers/nav_helper.rb b/app/helpers/nav_helper.rb
index b769462abc2..b1205b8529b 100644
--- a/app/helpers/nav_helper.rb
+++ b/app/helpers/nav_helper.rb
@@ -1,38 +1,49 @@
module NavHelper
+ def page_with_sidebar_class
+ class_name = page_gutter_class
+ class_name << 'page-with-new-sidebar' if defined?(@new_sidebar) && @new_sidebar
+
+ class_name
+ end
+
def page_gutter_class
if current_path?('merge_requests#show') ||
current_path?('projects/merge_requests/conflicts#show') ||
current_path?('issues#show') ||
current_path?('milestones#show')
if cookies[:collapsed_gutter] == 'true'
- "page-gutter right-sidebar-collapsed"
+ %w[page-gutter right-sidebar-collapsed]
else
- "page-gutter right-sidebar-expanded"
+ %w[page-gutter right-sidebar-expanded]
end
elsif current_path?('jobs#show')
- "page-gutter build-sidebar right-sidebar-expanded"
+ %w[page-gutter build-sidebar right-sidebar-expanded]
elsif current_path?('wikis#show') ||
current_path?('wikis#edit') ||
current_path?('wikis#update') ||
current_path?('wikis#history') ||
current_path?('wikis#git_access')
- "page-gutter wiki-sidebar right-sidebar-expanded"
+ %w[page-gutter wiki-sidebar right-sidebar-expanded]
+ else
+ []
end
end
def nav_header_class
- class_name = ''
- class_name << " with-horizontal-nav" if defined?(nav) && nav
+ class_names = []
+ class_names << 'with-horizontal-nav' if defined?(nav) && nav
- class_name
+ class_names
end
def layout_nav_class
- class_name = ''
- class_name << " page-with-layout-nav" if defined?(nav) && nav
- class_name << " page-with-sub-nav" if content_for?(:sub_nav)
+ return [] if show_new_nav?
- class_name
+ class_names = []
+ class_names << 'page-with-layout-nav' if defined?(nav) && nav
+ class_names << 'page-with-sub-nav' if content_for?(:sub_nav)
+
+ class_names
end
def nav_control_class
diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb
index 9a8d296d514..34ff6107eab 100644
--- a/app/helpers/projects_helper.rb
+++ b/app/helpers/projects_helper.rb
@@ -398,7 +398,7 @@ module ProjectsHelper
if project
import_path = "/Home/Stacks/import"
- repo = project.path_with_namespace
+ repo = project.full_path
branch ||= project.default_branch
sha ||= project.commit.short_id
@@ -458,7 +458,7 @@ module ProjectsHelper
def readme_cache_key
sha = @project.commit.try(:sha) || 'nil'
- [@project.path_with_namespace, sha, "readme"].join('-')
+ [@project.full_path, sha, "readme"].join('-')
end
def current_ref
diff --git a/app/helpers/webpack_helper.rb b/app/helpers/webpack_helper.rb
index 0386df22374..33453dd178f 100644
--- a/app/helpers/webpack_helper.rb
+++ b/app/helpers/webpack_helper.rb
@@ -34,6 +34,8 @@ module WebpackHelper
end
def webpack_public_path
- "#{webpack_public_host}/#{Rails.application.config.webpack.public_path}/"
+ relative_path = Rails.application.config.relative_url_root
+ webpack_path = Rails.application.config.webpack.public_path
+ File.join(webpack_public_host.to_s, relative_path.to_s, webpack_path.to_s, '')
end
end
diff --git a/app/mailers/notify.rb b/app/mailers/notify.rb
index eaac6fcb548..9efabe3f44e 100644
--- a/app/mailers/notify.rb
+++ b/app/mailers/notify.rb
@@ -165,7 +165,7 @@ class Notify < BaseMailer
headers['X-GitLab-Project'] = @project.name
headers['X-GitLab-Project-Id'] = @project.id
- headers['X-GitLab-Project-Path'] = @project.path_with_namespace
+ headers['X-GitLab-Project-Path'] = @project.full_path
end
def add_unsubscription_headers_and_links
diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb
index d2abcf30034..ea7331cb27f 100644
--- a/app/models/ci/pipeline.rb
+++ b/app/models/ci/pipeline.rb
@@ -317,7 +317,7 @@ module Ci
return @config_processor if defined?(@config_processor)
@config_processor ||= begin
- Ci::GitlabCiYamlProcessor.new(ci_yaml_file, project.path_with_namespace)
+ Ci::GitlabCiYamlProcessor.new(ci_yaml_file, project.full_path)
rescue Ci::GitlabCiYamlProcessor::ValidationError, Psych::SyntaxError => e
self.yaml_errors = e.message
nil
diff --git a/app/models/concerns/storage/legacy_namespace.rb b/app/models/concerns/storage/legacy_namespace.rb
new file mode 100644
index 00000000000..5ab5c80a2f5
--- /dev/null
+++ b/app/models/concerns/storage/legacy_namespace.rb
@@ -0,0 +1,102 @@
+module Storage
+ module LegacyNamespace
+ extend ActiveSupport::Concern
+
+ def move_dir
+ if any_project_has_container_registry_tags?
+ raise Gitlab::UpdatePathError.new('Namespace cannot be moved, because at least one project has tags in container registry')
+ end
+
+ # Move the namespace directory in all storage paths used by member projects
+ repository_storage_paths.each do |repository_storage_path|
+ # Ensure old directory exists before moving it
+ gitlab_shell.add_namespace(repository_storage_path, full_path_was)
+
+ unless gitlab_shell.mv_namespace(repository_storage_path, full_path_was, full_path)
+ Rails.logger.error "Exception moving path #{repository_storage_path} from #{full_path_was} to #{full_path}"
+
+ # if we cannot move namespace directory we should rollback
+ # db changes in order to prevent out of sync between db and fs
+ raise Gitlab::UpdatePathError.new('namespace directory cannot be moved')
+ end
+ end
+
+ Gitlab::UploadsTransfer.new.rename_namespace(full_path_was, full_path)
+ Gitlab::PagesTransfer.new.rename_namespace(full_path_was, full_path)
+
+ remove_exports!
+
+ # If repositories moved successfully we need to
+ # send update instructions to users.
+ # However we cannot allow rollback since we moved namespace dir
+ # So we basically we mute exceptions in next actions
+ begin
+ send_update_instructions
+ true
+ rescue
+ # Returning false does not rollback after_* transaction but gives
+ # us information about failing some of tasks
+ false
+ end
+ end
+
+ # Hooks
+
+ # Save the storage paths before the projects are destroyed to use them on after destroy
+ def prepare_for_destroy
+ old_repository_storage_paths
+ end
+
+ private
+
+ def old_repository_storage_paths
+ @old_repository_storage_paths ||= repository_storage_paths
+ end
+
+ def repository_storage_paths
+ # We need to get the storage paths for all the projects, even the ones that are
+ # pending delete. Unscoping also get rids of the default order, which causes
+ # problems with SELECT DISTINCT.
+ Project.unscoped do
+ all_projects.select('distinct(repository_storage)').to_a.map(&:repository_storage_path)
+ end
+ end
+
+ def rm_dir
+ # Remove the namespace directory in all storages paths used by member projects
+ old_repository_storage_paths.each do |repository_storage_path|
+ # Move namespace directory into trash.
+ # We will remove it later async
+ new_path = "#{full_path}+#{id}+deleted"
+
+ if gitlab_shell.mv_namespace(repository_storage_path, full_path, new_path)
+ Gitlab::AppLogger.info %Q(Namespace directory "#{full_path}" moved to "#{new_path}")
+
+ # Remove namespace directroy async with delay so
+ # GitLab has time to remove all projects first
+ run_after_commit do
+ GitlabShellWorker.perform_in(5.minutes, :rm_namespace, repository_storage_path, new_path)
+ end
+ end
+ end
+
+ remove_exports!
+ end
+
+ def remove_exports!
+ Gitlab::Popen.popen(%W(find #{export_path} -not -path #{export_path} -delete))
+ end
+
+ def export_path
+ File.join(Gitlab::ImportExport.storage_path, full_path_was)
+ end
+
+ def full_path_was
+ if parent
+ parent.full_path + '/' + path_was
+ else
+ path_was
+ end
+ end
+ end
+end
diff --git a/app/models/concerns/storage/legacy_project.rb b/app/models/concerns/storage/legacy_project.rb
new file mode 100644
index 00000000000..815db712285
--- /dev/null
+++ b/app/models/concerns/storage/legacy_project.rb
@@ -0,0 +1,76 @@
+module Storage
+ module LegacyProject
+ extend ActiveSupport::Concern
+
+ def disk_path
+ full_path
+ end
+
+ def ensure_storage_path_exist
+ gitlab_shell.add_namespace(repository_storage_path, namespace.full_path)
+ end
+
+ def rename_repo
+ path_was = previous_changes['path'].first
+ old_path_with_namespace = File.join(namespace.full_path, path_was)
+ new_path_with_namespace = File.join(namespace.full_path, path)
+
+ Rails.logger.error "Attempting to rename #{old_path_with_namespace} -> #{new_path_with_namespace}"
+
+ if has_container_registry_tags?
+ Rails.logger.error "Project #{old_path_with_namespace} cannot be renamed because container registry tags are present!"
+
+ # we currently doesn't support renaming repository if it contains images in container registry
+ raise StandardError.new('Project cannot be renamed, because images are present in its container registry')
+ end
+
+ expire_caches_before_rename(old_path_with_namespace)
+
+ if gitlab_shell.mv_repository(repository_storage_path, old_path_with_namespace, new_path_with_namespace)
+ # If repository moved successfully we need to send update instructions to users.
+ # However we cannot allow rollback since we moved repository
+ # So we basically we mute exceptions in next actions
+ begin
+ gitlab_shell.mv_repository(repository_storage_path, "#{old_path_with_namespace}.wiki", "#{new_path_with_namespace}.wiki")
+ send_move_instructions(old_path_with_namespace)
+ expires_full_path_cache
+
+ @old_path_with_namespace = old_path_with_namespace
+
+ SystemHooksService.new.execute_hooks_for(self, :rename)
+
+ @repository = nil
+ rescue => e
+ Rails.logger.error "Exception renaming #{old_path_with_namespace} -> #{new_path_with_namespace}: #{e}"
+ # Returning false does not rollback after_* transaction but gives
+ # us information about failing some of tasks
+ false
+ end
+ else
+ Rails.logger.error "Repository could not be renamed: #{old_path_with_namespace} -> #{new_path_with_namespace}"
+
+ # if we cannot move namespace directory we should rollback
+ # db changes in order to prevent out of sync between db and fs
+ raise StandardError.new('repository cannot be renamed')
+ end
+
+ Gitlab::AppLogger.info "Project was renamed: #{old_path_with_namespace} -> #{new_path_with_namespace}"
+
+ Gitlab::UploadsTransfer.new.rename_project(path_was, path, namespace.full_path)
+ Gitlab::PagesTransfer.new.rename_project(path_was, path, namespace.full_path)
+ end
+
+ def create_repository(force: false)
+ # Forked import is handled asynchronously
+ return if forked? && !force
+
+ if gitlab_shell.add_repository(repository_storage_path, path_with_namespace)
+ repository.after_create
+ true
+ else
+ errors.add(:base, 'Failed to create repository via gitlab-shell')
+ false
+ end
+ end
+ end
+end
diff --git a/app/models/concerns/storage/legacy_project_wiki.rb b/app/models/concerns/storage/legacy_project_wiki.rb
new file mode 100644
index 00000000000..ff82cb0ffa9
--- /dev/null
+++ b/app/models/concerns/storage/legacy_project_wiki.rb
@@ -0,0 +1,9 @@
+module Storage
+ module LegacyProjectWiki
+ extend ActiveSupport::Concern
+
+ def disk_path
+ project.disk_path + '.wiki'
+ end
+ end
+end
diff --git a/app/models/concerns/storage/legacy_repository.rb b/app/models/concerns/storage/legacy_repository.rb
new file mode 100644
index 00000000000..593749bf019
--- /dev/null
+++ b/app/models/concerns/storage/legacy_repository.rb
@@ -0,0 +1,7 @@
+module Storage
+ module LegacyRepository
+ extend ActiveSupport::Concern
+
+ delegate :disk_path, to: :project
+ end
+end
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index 81e0776e79c..8ca850b6d96 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -630,7 +630,7 @@ class MergeRequest < ActiveRecord::Base
def target_project_path
if target_project
- target_project.path_with_namespace
+ target_project.full_path
else
"(removed)"
end
@@ -638,7 +638,7 @@ class MergeRequest < ActiveRecord::Base
def source_project_path
if source_project
- source_project.path_with_namespace
+ source_project.full_path
else
"(removed)"
end
diff --git a/app/models/namespace.rb b/app/models/namespace.rb
index 0bb04194bdb..6073fb94a3f 100644
--- a/app/models/namespace.rb
+++ b/app/models/namespace.rb
@@ -8,6 +8,7 @@ class Namespace < ActiveRecord::Base
include Gitlab::VisibilityLevel
include Routable
include AfterCommitQueue
+ include Storage::LegacyNamespace
# Prevent users from creating unreasonably deep level of nesting.
# The number 20 was taken based on maximum nesting level of
@@ -41,10 +42,11 @@ class Namespace < ActiveRecord::Base
delegate :name, to: :owner, allow_nil: true, prefix: true
- after_update :move_dir, if: :path_changed?
after_commit :refresh_access_of_projects_invited_groups, on: :update, if: -> { previous_changes.key?('share_with_group_lock') }
- # Save the storage paths before the projects are destroyed to use them on after destroy
+ # Legacy Storage specific hooks
+
+ after_update :move_dir, if: :path_changed?
before_destroy(prepend: true) { prepare_for_destroy }
after_destroy :rm_dir
@@ -118,43 +120,6 @@ class Namespace < ActiveRecord::Base
owner_name
end
- def move_dir
- if any_project_has_container_registry_tags?
- raise Gitlab::UpdatePathError.new('Namespace cannot be moved, because at least one project has tags in container registry')
- end
-
- # Move the namespace directory in all storages paths used by member projects
- repository_storage_paths.each do |repository_storage_path|
- # Ensure old directory exists before moving it
- gitlab_shell.add_namespace(repository_storage_path, full_path_was)
-
- unless gitlab_shell.mv_namespace(repository_storage_path, full_path_was, full_path)
- Rails.logger.error "Exception moving path #{repository_storage_path} from #{full_path_was} to #{full_path}"
-
- # if we cannot move namespace directory we should rollback
- # db changes in order to prevent out of sync between db and fs
- raise Gitlab::UpdatePathError.new('namespace directory cannot be moved')
- end
- end
-
- Gitlab::UploadsTransfer.new.rename_namespace(full_path_was, full_path)
- Gitlab::PagesTransfer.new.rename_namespace(full_path_was, full_path)
-
- remove_exports!
-
- # If repositories moved successfully we need to
- # send update instructions to users.
- # However we cannot allow rollback since we moved namespace dir
- # So we basically we mute exceptions in next actions
- begin
- send_update_instructions
- rescue
- # Returning false does not rollback after_* transaction but gives
- # us information about failing some of tasks
- false
- end
- end
-
def any_project_has_container_registry_tags?
all_projects.any?(&:has_container_registry_tags?)
end
@@ -206,14 +171,6 @@ class Namespace < ActiveRecord::Base
parent_id_changed?
end
- def prepare_for_destroy
- old_repository_storage_paths
- end
-
- def old_repository_storage_paths
- @old_repository_storage_paths ||= repository_storage_paths
- end
-
# Includes projects from this namespace and projects from all subgroups
# that belongs to this namespace
def all_projects
@@ -232,37 +189,6 @@ class Namespace < ActiveRecord::Base
private
- def repository_storage_paths
- # We need to get the storage paths for all the projects, even the ones that are
- # pending delete. Unscoping also get rids of the default order, which causes
- # problems with SELECT DISTINCT.
- Project.unscoped do
- all_projects.select('distinct(repository_storage)').to_a.map(&:repository_storage_path)
- end
- end
-
- def rm_dir
- # Remove the namespace directory in all storages paths used by member projects
- old_repository_storage_paths.each do |repository_storage_path|
- # Move namespace directory into trash.
- # We will remove it later async
- new_path = "#{full_path}+#{id}+deleted"
-
- if gitlab_shell.mv_namespace(repository_storage_path, full_path, new_path)
- message = "Namespace directory \"#{full_path}\" moved to \"#{new_path}\""
- Gitlab::AppLogger.info message
-
- # Remove namespace directroy async with delay so
- # GitLab has time to remove all projects first
- run_after_commit do
- GitlabShellWorker.perform_in(5.minutes, :rm_namespace, repository_storage_path, new_path)
- end
- end
- end
-
- remove_exports!
- end
-
def refresh_access_of_projects_invited_groups
Group
.joins(project_group_links: :project)
@@ -270,22 +196,6 @@ class Namespace < ActiveRecord::Base
.find_each(&:refresh_members_authorized_projects)
end
- def remove_exports!
- Gitlab::Popen.popen(%W(find #{export_path} -not -path #{export_path} -delete))
- end
-
- def export_path
- File.join(Gitlab::ImportExport.storage_path, full_path_was)
- end
-
- def full_path_was
- if parent
- parent.full_path + '/' + path_was
- else
- path_was
- end
- end
-
def nesting_level_allowed
if ancestors.count > Group::NUMBER_OF_ANCESTORS_ALLOWED
errors.add(:parent_id, "has too deep level of nesting")
diff --git a/app/models/notification_setting.rb b/app/models/notification_setting.rb
index 81844b1e2ca..9b1cac64c44 100644
--- a/app/models/notification_setting.rb
+++ b/app/models/notification_setting.rb
@@ -1,4 +1,8 @@
class NotificationSetting < ActiveRecord::Base
+ include IgnorableColumn
+
+ ignore_column :events
+
enum level: { global: 3, watch: 2, mention: 4, participating: 1, disabled: 0, custom: 5 }
default_value_for :level, NotificationSetting.levels[:global]
@@ -41,9 +45,6 @@ class NotificationSetting < ActiveRecord::Base
:success_pipeline
].freeze
- store :events, coder: JSON
- before_save :convert_events
-
def self.find_or_create_for(source)
setting = find_or_initialize_by(source: source)
@@ -54,42 +55,17 @@ class NotificationSetting < ActiveRecord::Base
setting
end
- # 1. Check if this event has a value stored in its database column.
- # 2. If it does, return that value.
- # 3. If it doesn't (the value is nil), return the value from the serialized
- # JSON hash in `events`.
- (EMAIL_EVENTS - [:failed_pipeline]).each do |event|
- define_method(event) do
- bool = super()
-
- bool.nil? ? !!events[event] : bool
- end
-
- alias_method :"#{event}?", event
- end
-
# Allow people to receive failed pipeline notifications if they already have
# custom notifications enabled, as these are more like mentions than the other
# custom settings.
def failed_pipeline
bool = super
- bool = events[:failed_pipeline] if bool.nil?
bool.nil? || bool
end
alias_method :failed_pipeline?, :failed_pipeline
def event_enabled?(event)
- respond_to?(event) && public_send(event)
- end
-
- def convert_events
- return if events_before_type_cast.nil?
-
- EMAIL_EVENTS.each do |event|
- write_attribute(event, public_send(event))
- end
-
- write_attribute(:events, nil)
+ respond_to?(event) && !!public_send(event)
end
end
diff --git a/app/models/project.rb b/app/models/project.rb
index d827bfaa806..d85782782aa 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -17,6 +17,7 @@ class Project < ActiveRecord::Base
include ProjectFeaturesCompatibility
include SelectForProjectAuthorization
include Routable
+ include Storage::LegacyProject
extend Gitlab::ConfigHelper
@@ -43,9 +44,8 @@ class Project < ActiveRecord::Base
default_value_for :snippets_enabled, gitlab_config_features.snippets
default_value_for :only_allow_merge_if_all_discussions_are_resolved, false
- after_create :ensure_dir_exist
+ after_create :ensure_storage_path_exist
after_create :create_project_feature, unless: :project_feature
- after_save :ensure_dir_exist, if: :namespace_id_changed?
after_save :update_project_statistics, if: :namespace_id_changed?
# set last_activity_at to the same as created_at
@@ -67,6 +67,10 @@ class Project < ActiveRecord::Base
after_validation :check_pending_delete
+ # Legacy Storage specific hooks
+
+ after_save :ensure_storage_path_exist, if: :namespace_id_changed?
+
acts_as_taggable
attr_accessor :new_default_branch
@@ -375,7 +379,7 @@ class Project < ActiveRecord::Base
begin
Projects::HousekeepingService.new(project).execute
rescue Projects::HousekeepingService::LeaseTaken => e
- Rails.logger.info("Could not perform housekeeping for project #{project.path_with_namespace} (#{project.id}): #{e}")
+ Rails.logger.info("Could not perform housekeeping for project #{project.full_path} (#{project.id}): #{e}")
end
end
end
@@ -476,12 +480,12 @@ class Project < ActiveRecord::Base
end
def repository
- @repository ||= Repository.new(path_with_namespace, self)
+ @repository ||= Repository.new(full_path, self, disk_path: disk_path)
end
def container_registry_url
if Gitlab.config.registry.enabled
- "#{Gitlab.config.registry.host_port}/#{path_with_namespace.downcase}"
+ "#{Gitlab.config.registry.host_port}/#{full_path.downcase}"
end
end
@@ -520,16 +524,16 @@ class Project < ActiveRecord::Base
job_id =
if forked?
RepositoryForkWorker.perform_async(id, forked_from_project.repository_storage_path,
- forked_from_project.path_with_namespace,
+ forked_from_project.full_path,
self.namespace.full_path)
else
RepositoryImportWorker.perform_async(self.id)
end
if job_id
- Rails.logger.info "Import job started for #{path_with_namespace} with job ID #{job_id}"
+ Rails.logger.info "Import job started for #{full_path} with job ID #{job_id}"
else
- Rails.logger.error "Import job failed to start for #{path_with_namespace}"
+ Rails.logger.error "Import job failed to start for #{full_path}"
end
end
@@ -690,7 +694,7 @@ class Project < ActiveRecord::Base
# `from` argument can be a Namespace or Project.
def to_reference(from = nil, full: false)
if full || cross_namespace_reference?(from)
- path_with_namespace
+ full_path
elsif cross_project_reference?(from)
path
end
@@ -714,7 +718,7 @@ class Project < ActiveRecord::Base
author.ensure_incoming_email_token!
Gitlab::IncomingEmail.reply_address(
- "#{path_with_namespace}+#{author.incoming_email_token}")
+ "#{full_path}+#{author.incoming_email_token}")
end
def build_commit_note(commit)
@@ -941,7 +945,7 @@ class Project < ActiveRecord::Base
end
def url_to_repo
- gitlab_shell.url_to_repo(path_with_namespace)
+ gitlab_shell.url_to_repo(full_path)
end
def repo_exists?
@@ -974,56 +978,6 @@ class Project < ActiveRecord::Base
!group
end
- def rename_repo
- path_was = previous_changes['path'].first
- old_path_with_namespace = File.join(namespace.full_path, path_was)
- new_path_with_namespace = File.join(namespace.full_path, path)
-
- Rails.logger.error "Attempting to rename #{old_path_with_namespace} -> #{new_path_with_namespace}"
-
- if has_container_registry_tags?
- Rails.logger.error "Project #{old_path_with_namespace} cannot be renamed because container registry tags are present!"
-
- # we currently doesn't support renaming repository if it contains images in container registry
- raise StandardError.new('Project cannot be renamed, because images are present in its container registry')
- end
-
- expire_caches_before_rename(old_path_with_namespace)
-
- if gitlab_shell.mv_repository(repository_storage_path, old_path_with_namespace, new_path_with_namespace)
- # If repository moved successfully we need to send update instructions to users.
- # However we cannot allow rollback since we moved repository
- # So we basically we mute exceptions in next actions
- begin
- gitlab_shell.mv_repository(repository_storage_path, "#{old_path_with_namespace}.wiki", "#{new_path_with_namespace}.wiki")
- send_move_instructions(old_path_with_namespace)
- expires_full_path_cache
-
- @old_path_with_namespace = old_path_with_namespace
-
- SystemHooksService.new.execute_hooks_for(self, :rename)
-
- @repository = nil
- rescue => e
- Rails.logger.error "Exception renaming #{old_path_with_namespace} -> #{new_path_with_namespace}: #{e}"
- # Returning false does not rollback after_* transaction but gives
- # us information about failing some of tasks
- false
- end
- else
- Rails.logger.error "Repository could not be renamed: #{old_path_with_namespace} -> #{new_path_with_namespace}"
-
- # if we cannot move namespace directory we should rollback
- # db changes in order to prevent out of sync between db and fs
- raise StandardError.new('repository cannot be renamed')
- end
-
- Gitlab::AppLogger.info "Project was renamed: #{old_path_with_namespace} -> #{new_path_with_namespace}"
-
- Gitlab::UploadsTransfer.new.rename_project(path_was, path, namespace.full_path)
- Gitlab::PagesTransfer.new.rename_project(path_was, path, namespace.full_path)
- end
-
# Expires various caches before a project is renamed.
def expire_caches_before_rename(old_path)
repo = Repository.new(old_path, self)
@@ -1048,7 +1002,7 @@ class Project < ActiveRecord::Base
git_http_url: http_url_to_repo,
namespace: namespace.name,
visibility_level: visibility_level,
- path_with_namespace: path_with_namespace,
+ path_with_namespace: full_path,
default_branch: default_branch,
ci_config_path: ci_config_path
}
@@ -1109,19 +1063,6 @@ class Project < ActiveRecord::Base
merge_requests.where(source_project_id: self.id)
end
- def create_repository(force: false)
- # Forked import is handled asynchronously
- return if forked? && !force
-
- if gitlab_shell.add_repository(repository_storage_path, path_with_namespace)
- repository.after_create
- true
- else
- errors.add(:base, 'Failed to create repository via gitlab-shell')
- false
- end
- end
-
def ensure_repository
create_repository(force: true) unless repository_exists?
end
@@ -1257,7 +1198,7 @@ class Project < ActiveRecord::Base
end
def pages_path
- File.join(Settings.pages.path, path_with_namespace)
+ File.join(Settings.pages.path, disk_path)
end
def public_pages_path
@@ -1265,9 +1206,21 @@ class Project < ActiveRecord::Base
end
def remove_private_deploy_keys
- deploy_keys.where(public: false).delete_all
+ exclude_keys_linked_to_other_projects = <<-SQL
+ NOT EXISTS (
+ SELECT 1
+ FROM deploy_keys_projects dkp2
+ WHERE dkp2.deploy_key_id = deploy_keys_projects.deploy_key_id
+ AND dkp2.project_id != deploy_keys_projects.project_id
+ )
+ SQL
+
+ deploy_keys.where(public: false)
+ .where(exclude_keys_linked_to_other_projects)
+ .delete_all
end
+ # TODO: what to do here when not using Legacy Storage? Do we still need to rename and delay removal?
def remove_pages
::Projects::UpdatePagesConfigurationService.new(self).execute
@@ -1315,7 +1268,7 @@ class Project < ActiveRecord::Base
end
def export_path
- File.join(Gitlab::ImportExport.storage_path, path_with_namespace)
+ File.join(Gitlab::ImportExport.storage_path, disk_path)
end
def export_project_path
@@ -1327,16 +1280,12 @@ class Project < ActiveRecord::Base
status.zero?
end
- def ensure_dir_exist
- gitlab_shell.add_namespace(repository_storage_path, namespace.full_path)
- end
-
def predefined_variables
[
{ key: 'CI_PROJECT_ID', value: id.to_s, public: true },
{ key: 'CI_PROJECT_NAME', value: path, public: true },
- { key: 'CI_PROJECT_PATH', value: path_with_namespace, public: true },
- { key: 'CI_PROJECT_PATH_SLUG', value: path_with_namespace.parameterize, public: true },
+ { key: 'CI_PROJECT_PATH', value: full_path, public: true },
+ { key: 'CI_PROJECT_PATH_SLUG', value: full_path.parameterize, public: true },
{ key: 'CI_PROJECT_NAMESPACE', value: namespace.full_path, public: true },
{ key: 'CI_PROJECT_URL', value: web_url, public: true }
]
@@ -1441,6 +1390,7 @@ class Project < ActiveRecord::Base
alias_method :name_with_namespace, :full_name
alias_method :human_name, :full_name
+ # @deprecated cannot remove yet because it has an index with its name in elasticsearch
alias_method :path_with_namespace, :full_path
private
@@ -1495,7 +1445,7 @@ class Project < ActiveRecord::Base
def pending_delete_twin
return false unless path
- Project.pending_delete.find_by_full_path(path_with_namespace)
+ Project.pending_delete.find_by_full_path(full_path)
end
##
diff --git a/app/models/project_services/flowdock_service.rb b/app/models/project_services/flowdock_service.rb
index 2db95b9aaa3..4d23a17a545 100644
--- a/app/models/project_services/flowdock_service.rb
+++ b/app/models/project_services/flowdock_service.rb
@@ -35,9 +35,9 @@ class FlowdockService < Service
data[:after],
token: token,
repo: project.repository.path_to_repo,
- repo_url: "#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}",
- commit_url: "#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}/commit/%s",
- diff_url: "#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}/compare/%s...%s"
+ repo_url: "#{Gitlab.config.gitlab.url}/#{project.full_path}",
+ commit_url: "#{Gitlab.config.gitlab.url}/#{project.full_path}/commit/%s",
+ diff_url: "#{Gitlab.config.gitlab.url}/#{project.full_path}/compare/%s...%s"
)
end
end
diff --git a/app/models/project_services/jira_service.rb b/app/models/project_services/jira_service.rb
index 2aa19443198..c2414885368 100644
--- a/app/models/project_services/jira_service.rb
+++ b/app/models/project_services/jira_service.rb
@@ -140,7 +140,7 @@ class JiraService < IssueTrackerService
url: resource_url(user_path(author))
},
project: {
- name: project.path_with_namespace,
+ name: project.full_path,
url: resource_url(namespace_project_path(project.namespace, project)) # rubocop:disable Cop/ProjectPathHelper
},
entity: {
diff --git a/app/models/project_wiki.rb b/app/models/project_wiki.rb
index dfca0031af8..e8929a35836 100644
--- a/app/models/project_wiki.rb
+++ b/app/models/project_wiki.rb
@@ -1,5 +1,6 @@
class ProjectWiki
include Gitlab::ShellAdapter
+ include Storage::LegacyProjectWiki
MARKUPS = {
'Markdown' => :markdown,
@@ -26,16 +27,19 @@ class ProjectWiki
@project.path + '.wiki'
end
- def path_with_namespace
- @project.path_with_namespace + ".wiki"
+ def full_path
+ @project.full_path + '.wiki'
end
+ # @deprecated use full_path when you need it for an URL route or disk_path when you want to point to the filesystem
+ alias_method :path_with_namespace, :full_path
+
def web_url
Gitlab::Routing.url_helpers.project_wiki_url(@project, :home)
end
def url_to_repo
- gitlab_shell.url_to_repo(path_with_namespace)
+ gitlab_shell.url_to_repo(full_path)
end
def ssh_url_to_repo
@@ -43,11 +47,11 @@ class ProjectWiki
end
def http_url_to_repo
- "#{Gitlab.config.gitlab.url}/#{path_with_namespace}.git"
+ "#{Gitlab.config.gitlab.url}/#{full_path}.git"
end
def wiki_base_path
- [Gitlab.config.gitlab.relative_url_root, "/", @project.path_with_namespace, "/wikis"].join('')
+ [Gitlab.config.gitlab.relative_url_root, '/', @project.full_path, '/wikis'].join('')
end
# Returns the Gollum::Wiki object.
@@ -134,7 +138,7 @@ class ProjectWiki
end
def repository
- @repository ||= Repository.new(path_with_namespace, @project)
+ @repository ||= Repository.new(full_path, @project, disk_path: disk_path)
end
def default_branch
@@ -142,7 +146,7 @@ class ProjectWiki
end
def create_repo!
- if init_repo(path_with_namespace)
+ if init_repo(disk_path)
wiki = Gollum::Wiki.new(path_to_repo)
else
raise CouldNotCreateWikiError
@@ -162,15 +166,15 @@ class ProjectWiki
web_url: web_url,
git_ssh_url: ssh_url_to_repo,
git_http_url: http_url_to_repo,
- path_with_namespace: path_with_namespace,
+ path_with_namespace: full_path,
default_branch: default_branch
}
end
private
- def init_repo(path_with_namespace)
- gitlab_shell.add_repository(project.repository_storage_path, path_with_namespace)
+ def init_repo(disk_path)
+ gitlab_shell.add_repository(project.repository_storage_path, disk_path)
end
def commit_details(action, message = nil, title = nil)
@@ -184,7 +188,7 @@ class ProjectWiki
end
def path_to_repo
- @path_to_repo ||= File.join(project.repository_storage_path, "#{path_with_namespace}.git")
+ @path_to_repo ||= File.join(project.repository_storage_path, "#{disk_path}.git")
end
def update_project_activity
diff --git a/app/models/repository.rb b/app/models/repository.rb
index 50b7a477904..7ea9f1459a0 100644
--- a/app/models/repository.rb
+++ b/app/models/repository.rb
@@ -4,7 +4,7 @@ class Repository
include Gitlab::ShellAdapter
include RepositoryMirroring
- attr_accessor :path_with_namespace, :project
+ attr_accessor :full_path, :disk_path, :project
delegate :ref_name_for_sha, to: :raw_repository
@@ -52,13 +52,14 @@ class Repository
end
end
- def initialize(path_with_namespace, project)
- @path_with_namespace = path_with_namespace
+ def initialize(full_path, project, disk_path: nil)
+ @full_path = full_path
+ @disk_path = disk_path || full_path
@project = project
end
def raw_repository
- return nil unless path_with_namespace
+ return nil unless full_path
@raw_repository ||= initialize_raw_repository
end
@@ -66,7 +67,7 @@ class Repository
# Return absolute path to repository
def path_to_repo
@path_to_repo ||= File.expand_path(
- File.join(repository_storage_path, path_with_namespace + ".git")
+ File.join(repository_storage_path, disk_path + '.git')
)
end
@@ -469,7 +470,7 @@ class Repository
# Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/314
def exists?
- return false unless path_with_namespace
+ return false unless full_path
Gitlab::GitalyClient.migrate(:repository_exists) do |enabled|
if enabled
@@ -1005,7 +1006,7 @@ class Repository
end
def fetch_remote(remote, forced: false, no_tags: false)
- gitlab_shell.fetch_remote(repository_storage_path, path_with_namespace, remote, forced: forced, no_tags: no_tags)
+ gitlab_shell.fetch_remote(repository_storage_path, disk_path, remote, forced: forced, no_tags: no_tags)
end
def fetch_ref(source_path, source_ref, target_ref)
@@ -1104,7 +1105,8 @@ class Repository
end
def cache
- @cache ||= RepositoryCache.new(path_with_namespace, @project.id)
+ # TODO: should we use UUIDs here? We could move repositories without clearing this cache
+ @cache ||= RepositoryCache.new(full_path, @project.id)
end
def tags_sorted_by_committed_date
@@ -1127,7 +1129,7 @@ class Repository
end
def repository_event(event, tags = {})
- Gitlab::Metrics.add_event(event, { path: path_with_namespace }.merge(tags))
+ Gitlab::Metrics.add_event(event, { path: full_path }.merge(tags))
end
def create_commit(params = {})
@@ -1141,6 +1143,6 @@ class Repository
end
def initialize_raw_repository
- Gitlab::Git::Repository.new(project.repository_storage, path_with_namespace + '.git')
+ Gitlab::Git::Repository.new(project.repository_storage, disk_path + '.git')
end
end
diff --git a/app/policies/global_policy.rb b/app/policies/global_policy.rb
index 1c91425f589..1be7bbe9953 100644
--- a/app/policies/global_policy.rb
+++ b/app/policies/global_policy.rb
@@ -44,7 +44,7 @@ class GlobalPolicy < BasePolicy
prevent :log_in
end
- rule { admin | ~restricted_public_level }.policy do
+ rule { ~(anonymous & restricted_public_level) }.policy do
enable :read_users_list
end
end
diff --git a/app/services/git_operation_service.rb b/app/services/git_operation_service.rb
index 32925e9c1f2..545ca0742e4 100644
--- a/app/services/git_operation_service.rb
+++ b/app/services/git_operation_service.rb
@@ -60,7 +60,7 @@ class GitOperationService
start_branch_name = nil if start_repository.empty_repo?
if start_branch_name && !start_repository.branch_exists?(start_branch_name)
- raise ArgumentError, "Cannot find branch #{start_branch_name} in #{start_repository.path_with_namespace}"
+ raise ArgumentError, "Cannot find branch #{start_branch_name} in #{start_repository.full_path}"
end
update_branch_with_hooks(branch_name) do
diff --git a/app/services/projects/destroy_service.rb b/app/services/projects/destroy_service.rb
index f6e8b6655f2..11ad4838471 100644
--- a/app/services/projects/destroy_service.rb
+++ b/app/services/projects/destroy_service.rb
@@ -9,7 +9,7 @@ module Projects
def async_execute
project.update_attribute(:pending_delete, true)
job_id = ProjectDestroyWorker.perform_async(project.id, current_user.id, params)
- Rails.logger.info("User #{current_user.id} scheduled destruction of project #{project.path_with_namespace} with job ID #{job_id}")
+ Rails.logger.info("User #{current_user.id} scheduled destruction of project #{project.full_path} with job ID #{job_id}")
end
def execute
@@ -40,7 +40,7 @@ module Projects
private
def repo_path
- project.path_with_namespace
+ project.disk_path
end
def wiki_path
@@ -127,7 +127,7 @@ module Projects
def flush_caches(project)
project.repository.before_delete
- Repository.new(wiki_path, project).before_delete
+ Repository.new(wiki_path, project, disk_path: repo_path).before_delete
end
end
end
diff --git a/app/services/projects/import_export/export_service.rb b/app/services/projects/import_export/export_service.rb
index 535da706159..fe4e8ea10bf 100644
--- a/app/services/projects/import_export/export_service.rb
+++ b/app/services/projects/import_export/export_service.rb
@@ -2,7 +2,7 @@ module Projects
module ImportExport
class ExportService < BaseService
def execute(_options = {})
- @shared = Gitlab::ImportExport::Shared.new(relative_path: File.join(project.path_with_namespace, 'work'))
+ @shared = Gitlab::ImportExport::Shared.new(relative_path: File.join(project.disk_path, 'work'))
save_all
end
diff --git a/app/services/projects/import_service.rb b/app/services/projects/import_service.rb
index eea17e24903..50ec3651515 100644
--- a/app/services/projects/import_service.rb
+++ b/app/services/projects/import_service.rb
@@ -11,7 +11,7 @@ module Projects
success
rescue => e
- error("Error importing repository #{project.import_url} into #{project.path_with_namespace} - #{e.message}")
+ error("Error importing repository #{project.import_url} into #{project.full_path} - #{e.message}")
end
private
@@ -51,7 +51,7 @@ module Projects
end
def clone_repository
- gitlab_shell.import_repository(project.repository_storage_path, project.path_with_namespace, project.import_url)
+ gitlab_shell.import_repository(project.repository_storage_path, project.disk_path, project.import_url)
end
def fetch_repository
diff --git a/app/services/projects/transfer_service.rb b/app/services/projects/transfer_service.rb
index 4bb98e5cb4e..5957f612e84 100644
--- a/app/services/projects/transfer_service.rb
+++ b/app/services/projects/transfer_service.rb
@@ -34,7 +34,7 @@ module Projects
private
def transfer(project)
- @old_path = project.path_with_namespace
+ @old_path = project.full_path
@old_group = project.group
@new_path = File.join(@new_namespace.try(:full_path) || '', project.path)
@old_namespace = project.namespace
@@ -61,11 +61,13 @@ module Projects
project.send_move_instructions(@old_path)
# Move main repository
+ # TODO: check storage type and NOOP when not using Legacy
unless move_repo_folder(@old_path, @new_path)
raise TransferError.new('Cannot move project')
end
# Move wiki repo also if present
+ # TODO: check storage type and NOOP when not using Legacy
move_repo_folder("#{@old_path}.wiki", "#{@new_path}.wiki")
# Move missing group labels to project
diff --git a/app/services/quick_actions/interpret_service.rb b/app/services/quick_actions/interpret_service.rb
index 5dc1b91d2c0..c22bf7498bb 100644
--- a/app/services/quick_actions/interpret_service.rb
+++ b/app/services/quick_actions/interpret_service.rb
@@ -4,6 +4,9 @@ module QuickActions
attr_reader :issuable
+ SHRUG = '¯\\_(ツ)_/¯'.freeze
+ TABLEFLIP = '(╯°□°)╯︵ ┻━┻'.freeze
+
# Takes a text and interprets the commands that are extracted from it.
# Returns the content without commands, and hash of changes to be applied to a record.
def execute(content, issuable)
@@ -14,6 +17,7 @@ module QuickActions
content, commands = extractor.extract_commands(content, context)
extract_updates(commands, context)
+
[content, @updates]
end
@@ -423,6 +427,18 @@ module QuickActions
@updates[:spend_time] = { duration: :reset, user: current_user }
end
+ desc "Append the comment with #{SHRUG}"
+ params '<Comment>'
+ substitution :shrug do |comment|
+ "#{comment} #{SHRUG}"
+ end
+
+ desc "Append the comment with #{TABLEFLIP}"
+ params '<Comment>'
+ substitution :tableflip do |comment|
+ "#{comment} #{TABLEFLIP}"
+ end
+
# This is a dummy command, so that it appears in the autocomplete commands
desc 'CC'
params '@user'
diff --git a/app/services/system_hooks_service.rb b/app/services/system_hooks_service.rb
index bd58a54592f..cbcd4478af6 100644
--- a/app/services/system_hooks_service.rb
+++ b/app/services/system_hooks_service.rb
@@ -24,7 +24,7 @@ class SystemHooksService
key: model.key,
id: model.id
)
-
+
if model.user
data[:username] = model.user.username
end
@@ -56,7 +56,7 @@ class SystemHooksService
when GroupMember
data.merge!(group_member_data(model))
end
-
+
data
end
@@ -79,7 +79,7 @@ class SystemHooksService
{
name: model.name,
path: model.path,
- path_with_namespace: model.path_with_namespace,
+ path_with_namespace: model.full_path,
project_id: model.id,
owner_name: owner.name,
owner_email: owner.respond_to?(:email) ? owner.email : "",
@@ -93,7 +93,7 @@ class SystemHooksService
{
project_name: project.name,
project_path: project.path,
- project_path_with_namespace: project.path_with_namespace,
+ project_path_with_namespace: project.full_path,
project_id: project.id,
user_username: model.user.username,
user_name: model.user.name,
diff --git a/app/uploaders/file_uploader.rb b/app/uploaders/file_uploader.rb
index 652277e3b78..7027ac4b5db 100644
--- a/app/uploaders/file_uploader.rb
+++ b/app/uploaders/file_uploader.rb
@@ -30,7 +30,7 @@ class FileUploader < GitlabUploader
#
# Returns a String without a trailing slash
def self.dynamic_path_segment(model)
- File.join(CarrierWave.root, base_dir, model.path_with_namespace)
+ File.join(CarrierWave.root, base_dir, model.full_path)
end
attr_accessor :model
diff --git a/app/views/admin/groups/show.html.haml b/app/views/admin/groups/show.html.haml
index 843c71af466..2aadc071c75 100644
--- a/app/views/admin/groups/show.html.haml
+++ b/app/views/admin/groups/show.html.haml
@@ -70,7 +70,7 @@
%span.badge
= storage_counter(project.statistics.storage_size)
%span.pull-right.light
- %span.monospace= project.path_with_namespace + ".git"
+ %span.monospace= project.full_path + '.git'
.panel-footer
= paginate @projects, param_name: 'projects_page', theme: 'gitlab'
@@ -88,7 +88,7 @@
%span.badge
= storage_counter(project.statistics.storage_size)
%span.pull-right.light
- %span.monospace= project.path_with_namespace + ".git"
+ %span.monospace= project.full_path + '.git'
.col-md-6
- if can?(current_user, :admin_group_member, @group)
diff --git a/app/views/help/ui.html.haml b/app/views/help/ui.html.haml
index 48edbb8c16f..f18c3a74120 100644
--- a/app/views/help/ui.html.haml
+++ b/app/views/help/ui.html.haml
@@ -1,5 +1,7 @@
- page_title "UI Development Kit", "Help"
- lorem = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed fermentum nisi sapien, non consequat lectus aliquam ultrices. Suspendisse sodales est euismod nunc condimentum, a consectetur diam ornare."
+- content_for :page_specific_javascripts do
+ = webpack_bundle_tag('ui_development_kit')
.gitlab-ui-dev-kit
%h1 GitLab UI development kit
@@ -407,29 +409,6 @@
.dropdown-content
.dropdown-loading
= icon('spinner spin')
- :javascript
- $('#js-project-dropdown').glDropdown({
- data: function (term, callback) {
- Api.projects(term, { order_by: 'last_activity_at' }, function (data) {
- callback(data);
- });
- },
- text: function (project) {
- return project.name_with_namespace || project.name;
- },
- selectable: true,
- fieldName: "author_id",
- filterable: true,
- search: {
- fields: ['name_with_namespace']
- },
- id: function (data) {
- return data.id;
- },
- isSelected: function (data) {
- return data.id === 2;
- }
- })
.example
%div
diff --git a/app/views/import/_githubish_status.html.haml b/app/views/import/_githubish_status.html.haml
index 0e7f0b5ed4f..e9a04e6c122 100644
--- a/app/views/import/_githubish_status.html.haml
+++ b/app/views/import/_githubish_status.html.haml
@@ -25,7 +25,7 @@
%td
= provider_project_link(provider, project.import_source)
%td
- = link_to project.path_with_namespace, [project.namespace.becomes(Namespace), project]
+ = link_to project.full_path, [project.namespace.becomes(Namespace), project]
%td.job-status
- if project.import_status == 'finished'
%span
diff --git a/app/views/import/base/create.js.haml b/app/views/import/base/create.js.haml
index fde671e25a9..4dc3a4a0acf 100644
--- a/app/views/import/base/create.js.haml
+++ b/app/views/import/base/create.js.haml
@@ -4,7 +4,7 @@
job.attr("id", "project_#{@project.id}")
target_field = job.find(".import-target")
target_field.empty()
- target_field.append('#{link_to @project.path_with_namespace, project_path(@project)}')
+ target_field.append('#{link_to @project.full_path, project_path(@project)}')
$("table.import-jobs tbody").prepend(job)
job.addClass("active").find(".import-actions").html("<i class='fa fa-spinner fa-spin'></i> started")
- else
diff --git a/app/views/import/bitbucket/status.html.haml b/app/views/import/bitbucket/status.html.haml
index e6058617ac9..9589e0956f4 100644
--- a/app/views/import/bitbucket/status.html.haml
+++ b/app/views/import/bitbucket/status.html.haml
@@ -35,7 +35,7 @@
%td
= link_to project.import_source, "https://bitbucket.org/#{project.import_source}", target: '_blank', rel: 'noopener noreferrer'
%td
- = link_to project.path_with_namespace, [project.namespace.becomes(Namespace), project]
+ = link_to project.full_path, [project.namespace.becomes(Namespace), project]
%td.job-status
- if project.import_status == 'finished'
%span
diff --git a/app/views/import/fogbugz/status.html.haml b/app/views/import/fogbugz/status.html.haml
index 5de5da5e6a2..7b832c6a23a 100644
--- a/app/views/import/fogbugz/status.html.haml
+++ b/app/views/import/fogbugz/status.html.haml
@@ -33,7 +33,7 @@
%td
= project.import_source
%td
- = link_to project.path_with_namespace, [project.namespace.becomes(Namespace), project]
+ = link_to project.full_path, [project.namespace.becomes(Namespace), project]
%td.job-status
- if project.import_status == 'finished'
%span
diff --git a/app/views/import/gitlab/status.html.haml b/app/views/import/gitlab/status.html.haml
index 7456799ca0e..37734414835 100644
--- a/app/views/import/gitlab/status.html.haml
+++ b/app/views/import/gitlab/status.html.haml
@@ -28,7 +28,7 @@
%td
= link_to project.import_source, "https://gitlab.com/#{project.import_source}", target: "_blank"
%td
- = link_to project.path_with_namespace, [project.namespace.becomes(Namespace), project]
+ = link_to project.full_path, [project.namespace.becomes(Namespace), project]
%td.job-status
- if project.import_status == 'finished'
%span
diff --git a/app/views/import/google_code/status.html.haml b/app/views/import/google_code/status.html.haml
index 60de6bfe816..bc61aeece72 100644
--- a/app/views/import/google_code/status.html.haml
+++ b/app/views/import/google_code/status.html.haml
@@ -38,7 +38,7 @@
%td
= link_to project.import_source, "https://code.google.com/p/#{project.import_source}", target: "_blank", rel: 'noopener noreferrer'
%td
- = link_to project.path_with_namespace, [project.namespace.becomes(Namespace), project]
+ = link_to project.full_path, [project.namespace.becomes(Namespace), project]
%td.job-status
- if project.import_status == 'finished'
%span
diff --git a/app/views/layouts/_google_analytics.html.haml b/app/views/layouts/_google_analytics.html.haml
index 81e03c7eff2..98ea96b0b77 100644
--- a/app/views/layouts/_google_analytics.html.haml
+++ b/app/views/layouts/_google_analytics.html.haml
@@ -1,3 +1,4 @@
+-# haml-lint:disable InlineJavaScript
:javascript
var _gaq = _gaq || [];
_gaq.push(['_setAccount', '#{extra_config.google_analytics_id}']);
diff --git a/app/views/layouts/_init_auto_complete.html.haml b/app/views/layouts/_init_auto_complete.html.haml
index 4bb0dfc73fd..9704c9ec624 100644
--- a/app/views/layouts/_init_auto_complete.html.haml
+++ b/app/views/layouts/_init_auto_complete.html.haml
@@ -2,6 +2,7 @@
- noteable_type = @noteable.class if @noteable.present?
- if project
+ -# haml-lint:disable InlineJavaScript
:javascript
gl.GfmAutoComplete = gl.GfmAutoComplete || {};
gl.GfmAutoComplete.dataSources = {
diff --git a/app/views/layouts/_page.html.haml b/app/views/layouts/_page.html.haml
index 873220cc73d..c4f8cd71395 100644
--- a/app/views/layouts/_page.html.haml
+++ b/app/views/layouts/_page.html.haml
@@ -1,4 +1,4 @@
-.page-with-sidebar{ class: "#{('page-with-new-sidebar' if defined?(@new_sidebar) && @new_sidebar)} #{page_gutter_class}" }
+.page-with-sidebar{ class: page_with_sidebar_class }
- if show_new_nav?
- if defined?(nav) && nav
= render "layouts/nav/#{nav}"
@@ -9,7 +9,7 @@
= render "layouts/nav/#{nav}"
- if content_for?(:sub_nav)
= yield :sub_nav
- .content-wrapper{ class: "#{(layout_nav_class unless show_new_nav?)}" }
+ .content-wrapper{ class: layout_nav_class }
- if show_new_nav?
.mobile-overlay
.alert-wrapper
diff --git a/app/views/layouts/_piwik.html.haml b/app/views/layouts/_piwik.html.haml
index 259b4f7cdfc..a888e8ae187 100644
--- a/app/views/layouts/_piwik.html.haml
+++ b/app/views/layouts/_piwik.html.haml
@@ -1,4 +1,5 @@
<!-- Piwik -->
+-# haml-lint:disable InlineJavaScript
:javascript
var _paq = _paq || [];
_paq.push(['trackPageView']);
diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml
index 38b95d11fd4..b53f382fa3d 100644
--- a/app/views/layouts/application.html.haml
+++ b/app/views/layouts/application.html.haml
@@ -1,8 +1,9 @@
!!! 5
-%html{ lang: I18n.locale, class: "#{page_class}" }
+%html{ lang: I18n.locale, class: page_class }
= render "layouts/head"
%body{ class: @body_class, data: { page: body_data_page, project: "#{@project.path if @project}", group: "#{@group.path if @group}", find_file: find_file_path } }
= render "layouts/init_auto_complete" if @gfm_form
+ = render 'peek/bar'
- if show_new_nav?
= render "layouts/header/new"
- else
@@ -10,5 +11,3 @@
= render 'layouts/page', sidebar: sidebar, nav: nav
= yield :scripts_body
-
- = render 'peek/bar'
diff --git a/app/views/layouts/nav/_new_admin_sidebar.html.haml b/app/views/layouts/nav/_new_admin_sidebar.html.haml
index 95443de40c2..8db3e69aed4 100644
--- a/app/views/layouts/nav/_new_admin_sidebar.html.haml
+++ b/app/views/layouts/nav/_new_admin_sidebar.html.haml
@@ -91,8 +91,8 @@
= nav_link(controller: :abuse_reports) do
= link_to admin_abuse_reports_path, title: "Abuse Reports" do
%span
- Abuse Reports
%span.badge.count= number_with_delimiter(AbuseReport.count(:all))
+ Abuse Reports
- if akismet_enabled?
= nav_link(controller: :spam_logs) do
diff --git a/app/views/layouts/nav/_new_group_sidebar.html.haml b/app/views/layouts/nav/_new_group_sidebar.html.haml
index a7897c09e79..4fd9e213ead 100644
--- a/app/views/layouts/nav/_new_group_sidebar.html.haml
+++ b/app/views/layouts/nav/_new_group_sidebar.html.haml
@@ -28,9 +28,9 @@
= nav_link(path: ['groups#issues', 'labels#index', 'milestones#index']) do
= link_to issues_group_path(@group), title: 'Issues' do
%span
- Issues
- issues = IssuesFinder.new(current_user, group_id: @group.id, state: 'opened').execute
%span.badge.count= number_with_delimiter(issues.count)
+ Issues
%ul.sidebar-sub-level-items
= nav_link(path: 'groups#issues', html_options: { class: 'home' }) do
@@ -51,9 +51,9 @@
= nav_link(path: 'groups#merge_requests') do
= link_to merge_requests_group_path(@group), title: 'Merge Requests' do
%span
- Merge Requests
- merge_requests = MergeRequestsFinder.new(current_user, group_id: @group.id, state: 'opened', non_archived: true).execute
%span.badge.count= number_with_delimiter(merge_requests.count)
+ Merge Requests
= nav_link(path: 'group_members#index') do
= link_to group_group_members_path(@group), title: 'Members' do
%span
diff --git a/app/views/layouts/project.html.haml b/app/views/layouts/project.html.haml
index 99adb83cd1f..54d56e9b873 100644
--- a/app/views/layouts/project.html.haml
+++ b/app/views/layouts/project.html.haml
@@ -10,6 +10,7 @@
- content_for :project_javascripts do
- project = @target_project || @project
- if current_user
+ -# haml-lint:disable InlineJavaScript
:javascript
window.uploads_path = "#{project_uploads_path(project)}";
diff --git a/app/views/layouts/snippets.html.haml b/app/views/layouts/snippets.html.haml
index 57971205e0e..849075a0ba5 100644
--- a/app/views/layouts/snippets.html.haml
+++ b/app/views/layouts/snippets.html.haml
@@ -2,6 +2,7 @@
- content_for :page_specific_javascripts do
- if @snippet && current_user
+ -# haml-lint:disable InlineJavaScript
:javascript
window.uploads_path = "#{upload_path('personal_snippet', id: @snippet.id)}";
diff --git a/app/views/peek/views/_host.html.haml b/app/views/peek/views/_host.html.haml
new file mode 100644
index 00000000000..40769b5c6f6
--- /dev/null
+++ b/app/views/peek/views/_host.html.haml
@@ -0,0 +1,2 @@
+%span.current-host
+ = truncate(view.hostname)
diff --git a/app/views/profiles/personal_access_tokens/index.html.haml b/app/views/profiles/personal_access_tokens/index.html.haml
index cf750378e25..2216708d354 100644
--- a/app/views/profiles/personal_access_tokens/index.html.haml
+++ b/app/views/profiles/personal_access_tokens/index.html.haml
@@ -1,5 +1,6 @@
- page_title "Personal Access Tokens"
- @content_class = "limit-container-width" unless fluid_layout
+
= render 'profiles/head'
.row.prepend-top-default
@@ -19,7 +20,7 @@
%h5.prepend-top-0
Your New Personal Access Token
.form-group
- = text_field_tag 'created-personal-access-token', flash[:personal_access_token], readonly: true, class: "form-control", 'aria-describedby' => "created-personal-access-token-help-block"
+ = text_field_tag 'created-personal-access-token', flash[:personal_access_token], readonly: true, class: "form-control js-select-on-focus", 'aria-describedby' => "created-personal-access-token-help-block"
= clipboard_button(text: flash[:personal_access_token], title: "Copy personal access token to clipboard", placement: "left")
%span#created-personal-access-token-help-block.help-block.text-danger Make sure you save it - you won't be able to access it again.
@@ -28,8 +29,3 @@
= render "shared/personal_access_tokens_form", path: profile_personal_access_tokens_path, impersonation: false, token: @personal_access_token, scopes: @scopes
= render "shared/personal_access_tokens_table", impersonation: false, active_tokens: @active_personal_access_tokens, inactive_tokens: @inactive_personal_access_tokens
-
-:javascript
- $("#created-personal-access-token").click(function() {
- this.select();
- });
diff --git a/app/views/profiles/two_factor_auths/show.html.haml b/app/views/profiles/two_factor_auths/show.html.haml
index 037cb30efb9..33e062c1c9c 100644
--- a/app/views/profiles/two_factor_auths/show.html.haml
+++ b/app/views/profiles/two_factor_auths/show.html.haml
@@ -7,97 +7,92 @@
= render 'profiles/head'
-- if inject_u2f_api?
- - content_for :page_specific_javascripts do
+- content_for :page_specific_javascripts do
+ - if inject_u2f_api?
= page_specific_javascript_bundle_tag('u2f')
+ = page_specific_javascript_bundle_tag('two_factor_auth')
-.row.prepend-top-default
- .col-lg-4
- %h4.prepend-top-0
- Register Two-Factor Authentication App
- %p
- Use an app on your mobile device to enable two-factor authentication (2FA).
- .col-lg-8
- - if current_user.two_factor_otp_enabled?
- = icon "check inverse", base: "circle", class: "text-success", text: "You've already enabled two-factor authentication using mobile authenticator applications. You can disable it from your account settings page."
- - else
+.js-two-factor-auth{ 'data-two-factor-skippable' => "#{two_factor_skippable?}", 'data-two_factor_skip_url' => skip_profile_two_factor_auth_path }
+ .row.prepend-top-default
+ .col-lg-4
+ %h4.prepend-top-0
+ Register Two-Factor Authentication App
%p
- Download the Google Authenticator application from App Store or Google Play Store and scan this code.
- More information is available in the #{link_to('documentation', help_page_path('profile/two_factor_authentication'))}.
- .row.append-bottom-10
- .col-md-4
- = raw @qr_code
- .col-md-8
- .account-well
- %p.prepend-top-0.append-bottom-0
- Can't scan the code?
- %p.prepend-top-0.append-bottom-0
- To add the entry manually, provide the following details to the application on your phone.
- %p.prepend-top-0.append-bottom-0
- Account:
- = @account_string
- %p.prepend-top-0.append-bottom-0
- Key:
- = current_user.otp_secret.scan(/.{4}/).join(' ')
- %p.two-factor-new-manual-content
- Time based: Yes
- = form_tag profile_two_factor_auth_path, method: :post do |f|
- - if @error
- .alert.alert-danger
- = @error
- .form-group
- = label_tag :pin_code, nil, class: "label-light"
- = text_field_tag :pin_code, nil, class: "form-control", required: true
- .prepend-top-default
- = submit_tag 'Register with two-factor app', class: 'btn btn-success'
+ Use an app on your mobile device to enable two-factor authentication (2FA).
+ .col-lg-8
+ - if current_user.two_factor_otp_enabled?
+ = icon "check inverse", base: "circle", class: "text-success", text: "You've already enabled two-factor authentication using mobile authenticator applications. You can disable it from your account settings page."
+ - else
+ %p
+ Download the Google Authenticator application from App Store or Google Play Store and scan this code.
+ More information is available in the #{link_to('documentation', help_page_path('profile/two_factor_authentication'))}.
+ .row.append-bottom-10
+ .col-md-4
+ = raw @qr_code
+ .col-md-8
+ .account-well
+ %p.prepend-top-0.append-bottom-0
+ Can't scan the code?
+ %p.prepend-top-0.append-bottom-0
+ To add the entry manually, provide the following details to the application on your phone.
+ %p.prepend-top-0.append-bottom-0
+ Account:
+ = @account_string
+ %p.prepend-top-0.append-bottom-0
+ Key:
+ = current_user.otp_secret.scan(/.{4}/).join(' ')
+ %p.two-factor-new-manual-content
+ Time based: Yes
+ = form_tag profile_two_factor_auth_path, method: :post do |f|
+ - if @error
+ .alert.alert-danger
+ = @error
+ .form-group
+ = label_tag :pin_code, nil, class: "label-light"
+ = text_field_tag :pin_code, nil, class: "form-control", required: true
+ .prepend-top-default
+ = submit_tag 'Register with two-factor app', class: 'btn btn-success'
-%hr
+ %hr
-.row.prepend-top-default
-
- .col-lg-4
- %h4.prepend-top-0
- Register Universal Two-Factor (U2F) Device
- %p
- Use a hardware device to add the second factor of authentication.
- %p
- As U2F devices are only supported by a few browsers, we require that you set up a
- two-factor authentication app before a U2F device. That way you'll always be able to
- log in - even when you're using an unsupported browser.
- .col-lg-8
- - if @u2f_registration.errors.present?
- = form_errors(@u2f_registration)
- = render "u2f/register"
+ .row.prepend-top-default
+ .col-lg-4
+ %h4.prepend-top-0
+ Register Universal Two-Factor (U2F) Device
+ %p
+ Use a hardware device to add the second factor of authentication.
+ %p
+ As U2F devices are only supported by a few browsers, we require that you set up a
+ two-factor authentication app before a U2F device. That way you'll always be able to
+ log in - even when you're using an unsupported browser.
+ .col-lg-8
+ - if @u2f_registration.errors.present?
+ = form_errors(@u2f_registration)
+ = render "u2f/register"
- %hr
+ %hr
- %h5 U2F Devices (#{@u2f_registrations.length})
+ %h5 U2F Devices (#{@u2f_registrations.length})
- - if @u2f_registrations.present?
- .table-responsive
- %table.table.table-bordered.u2f-registrations
- %colgroup
- %col{ width: "50%" }
- %col{ width: "30%" }
- %col{ width: "20%" }
- %thead
- %tr
- %th Name
- %th Registered On
- %th
- %tbody
- - @u2f_registrations.each do |registration|
+ - if @u2f_registrations.present?
+ .table-responsive
+ %table.table.table-bordered.u2f-registrations
+ %colgroup
+ %col{ width: "50%" }
+ %col{ width: "30%" }
+ %col{ width: "20%" }
+ %thead
%tr
- %td= registration.name.presence || "<no name set>"
- %td= registration.created_at.to_date.to_s(:medium)
- %td= link_to "Delete", profile_u2f_registration_path(registration), method: :delete, class: "btn btn-danger pull-right", data: { confirm: "Are you sure you want to delete this device? This action cannot be undone." }
-
- - else
- .settings-message.text-center
- You don't have any U2F devices registered yet.
-
+ %th Name
+ %th Registered On
+ %th
+ %tbody
+ - @u2f_registrations.each do |registration|
+ %tr
+ %td= registration.name.presence || "<no name set>"
+ %td= registration.created_at.to_date.to_s(:medium)
+ %td= link_to "Delete", profile_u2f_registration_path(registration), method: :delete, class: "btn btn-danger pull-right", data: { confirm: "Are you sure you want to delete this device? This action cannot be undone." }
-- if two_factor_skippable?
- :javascript
- var button = "<a class='btn btn-xs btn-warning pull-right' data-method='patch' href='#{skip_profile_two_factor_auth_path}'>Configure it later</a>";
- $(".flash-alert").append(button);
+ - else
+ .settings-message.text-center
+ You don't have any U2F devices registered yet.
diff --git a/app/views/projects/_activity.html.haml b/app/views/projects/_activity.html.haml
index ecc966ed453..ad63f5e73ae 100644
--- a/app/views/projects/_activity.html.haml
+++ b/app/views/projects/_activity.html.haml
@@ -8,9 +8,3 @@
.content_list.project-activity{ :"data-href" => activity_project_path(@project) }
= spinner
-
-:javascript
- var activity = new gl.Activities();
- $(document).on('page:restore', function (event) {
- activity.reloadActivities()
- })
diff --git a/app/views/projects/blob/_new_dir.html.haml b/app/views/projects/blob/_new_dir.html.haml
index b2959ef6d31..03ab1bb59e4 100644
--- a/app/views/projects/blob/_new_dir.html.haml
+++ b/app/views/projects/blob/_new_dir.html.haml
@@ -20,6 +20,3 @@
- unless can?(current_user, :push_code, @project)
.inline.prepend-left-10
= commit_in_fork_help
-
-:javascript
- new NewCommitForm($('.js-create-dir-form'))
diff --git a/app/views/projects/blob/_remove.html.haml b/app/views/projects/blob/_remove.html.haml
index 6a4a657fa8c..750bdef3308 100644
--- a/app/views/projects/blob/_remove.html.haml
+++ b/app/views/projects/blob/_remove.html.haml
@@ -13,6 +13,3 @@
.col-sm-offset-2.col-sm-10
= button_tag 'Delete file', class: 'btn btn-remove btn-remove-file'
= link_to "Cancel", '#', class: "btn btn-cancel", "data-dismiss" => "modal"
-
-:javascript
- new NewCommitForm($('.js-delete-blob-form'))
diff --git a/app/views/projects/branches/new.html.haml b/app/views/projects/branches/new.html.haml
index 03eefcc2b4d..2baaaf6ac5b 100644
--- a/app/views/projects/branches/new.html.haml
+++ b/app/views/projects/branches/new.html.haml
@@ -28,8 +28,4 @@
.form-actions
= button_tag 'Create branch', class: 'btn btn-create', tabindex: 3
= link_to 'Cancel', project_branches_path(@project), class: 'btn btn-cancel'
-
-:javascript
- var availableRefs = #{@project.repository.ref_names.to_json};
-
- new NewBranchForm($('.js-create-branch-form'), availableRefs)
+%script#availableRefs{ type: "application/json" }= @project.repository.ref_names.to_json.html_safe
diff --git a/app/views/projects/commit/_commit_box.html.haml b/app/views/projects/commit/_commit_box.html.haml
index 419fbe99af8..09bcd187e59 100644
--- a/app/views/projects/commit/_commit_box.html.haml
+++ b/app/views/projects/commit/_commit_box.html.haml
@@ -1,4 +1,4 @@
-.page-content-header
+.page-content-header.js-commit-box{ 'data-commit-path' => branches_project_commit_path(@project, @commit.id) }
.header-main-content
= render partial: 'signature', object: @commit.signature
%strong
@@ -79,6 +79,3 @@
= render 'shared/mini_pipeline_graph', pipeline: last_pipeline, klass: 'js-commit-pipeline-graph'
in
= time_interval_in_words last_pipeline.duration
-
-:javascript
- $(".commit-info.branches").load("#{branches_project_commit_path(@project, @commit.id)}");
diff --git a/app/views/projects/commits/_commit.html.haml b/app/views/projects/commits/_commit.html.haml
index 12b73ecdf13..e7da47032be 100644
--- a/app/views/projects/commits/_commit.html.haml
+++ b/app/views/projects/commits/_commit.html.haml
@@ -5,7 +5,7 @@
- notes = commit.notes
- note_count = notes.user.count
-- cache_key = [project.path_with_namespace, commit.id, current_application_settings, note_count, @path.presence, current_controller?(:commits)]
+- cache_key = [project.full_path, commit.id, current_application_settings, note_count, @path.presence, current_controller?(:commits)]
- cache_key.push(commit.status(ref)) if commit.status(ref)
= cache(cache_key, expires_in: 1.day) do
diff --git a/app/views/projects/commits/show.html.haml b/app/views/projects/commits/show.html.haml
index bd2d900997e..7ae56086177 100644
--- a/app/views/projects/commits/show.html.haml
+++ b/app/views/projects/commits/show.html.haml
@@ -11,34 +11,32 @@
= content_for :sub_nav do
= render "head"
-%div{ class: container_class }
- .tree-holder
- .nav-block
- .tree-ref-container
- .tree-ref-holder
- = render 'shared/ref_switcher', destination: 'commits'
+.js-project-commits-show{ 'data-commits-limit' => @limit }
+ %div{ class: container_class }
+ .tree-holder
+ .nav-block
+ .tree-ref-container
+ .tree-ref-holder
+ = render 'shared/ref_switcher', destination: 'commits'
+
+ %ul.breadcrumb.repo-breadcrumb
+ = commits_breadcrumbs
+ .tree-controls.hidden-xs.hidden-sm
+ - if @merge_request.present?
+ .control
+ = link_to _("View open merge request"), project_merge_request_path(@project, @merge_request), class: 'btn'
+ - elsif create_mr_button?(@repository.root_ref, @ref)
+ .control
+ = link_to _("Create merge request"), create_mr_path(@repository.root_ref, @ref), class: 'btn btn-success'
- %ul.breadcrumb.repo-breadcrumb
- = commits_breadcrumbs
- .tree-controls.hidden-xs.hidden-sm
- - if @merge_request.present?
.control
- = link_to _("View open merge request"), project_merge_request_path(@project, @merge_request), class: 'btn'
- - elsif create_mr_button?(@repository.root_ref, @ref)
+ = form_tag(project_commits_path(@project, @id), method: :get, class: 'commits-search-form', data: { 'signatures-path' => namespace_project_signatures_path }) do
+ = search_field_tag :search, params[:search], { placeholder: _('Filter by commit message'), id: 'commits-search', class: 'form-control search-text-input input-short', spellcheck: false }
.control
- = link_to _("Create merge request"), create_mr_path(@repository.root_ref, @ref), class: 'btn btn-success'
+ = link_to project_commits_path(@project, @ref, rss_url_options), title: _("Commits feed"), class: 'btn' do
+ = icon("rss")
- .control
- = form_tag(project_commits_path(@project, @id), method: :get, class: 'commits-search-form', data: { 'signatures-path' => namespace_project_signatures_path }) do
- = search_field_tag :search, params[:search], { placeholder: _('Filter by commit message'), id: 'commits-search', class: 'form-control search-text-input input-short', spellcheck: false }
- .control
- = link_to project_commits_path(@project, @ref, rss_url_options), title: _("Commits feed"), class: 'btn' do
- = icon("rss")
-
- %div{ id: dom_id(@project) }
- %ol#commits-list.list-unstyled.content_list
- = render 'commits', project: @project, ref: @ref
- = spinner
-
-:javascript
- CommitsList.init(#{@limit});
+ %div{ id: dom_id(@project) }
+ %ol#commits-list.list-unstyled.content_list
+ = render 'commits', project: @project, ref: @ref
+ = spinner
diff --git a/app/views/projects/find_file/show.html.haml b/app/views/projects/find_file/show.html.haml
index e3bf48ee47f..021575160ea 100644
--- a/app/views/projects/find_file/show.html.haml
+++ b/app/views/projects/find_file/show.html.haml
@@ -1,7 +1,7 @@
- page_title "Find File", @ref
= render "projects/commits/head"
-.file-finder-holder.tree-holder.clearfix
+.file-finder-holder.tree-holder.clearfix.js-file-finder{ 'data-file-find-url': "#{escape_javascript(project_files_path(@project, @ref, @options.merge(format: :json)))}", 'data-find-tree-url': escape_javascript(project_tree_path(@project, @ref)), 'data-blob-url-template': escape_javascript(project_blob_path(@project, @id || @commit.id)) }
.nav-block
.tree-ref-holder
= render 'shared/ref_switcher', destination: 'find_file', path: @path
@@ -17,11 +17,3 @@
%table.table.files-slider{ class: "table_#{@hex_path} tree-table table-striped" }
%tbody
= spinner nil, true
-
-:javascript
- var projectFindFile = new ProjectFindFile($(".file-finder-holder"), {
- url: "#{escape_javascript(project_files_path(@project, @ref, @options.merge(format: :json)))}",
- treeUrl: "#{escape_javascript(project_tree_path(@project, @ref))}",
- blobUrlTemplate: "#{escape_javascript(project_blob_path(@project, @id || @commit.id))}"
- });
- new ShortcutsFindFile(projectFindFile);
diff --git a/app/views/projects/graphs/charts.html.haml b/app/views/projects/graphs/charts.html.haml
index 249b9d82ad9..228c8c84792 100644
--- a/app/views/projects/graphs/charts.html.haml
+++ b/app/views/projects/graphs/charts.html.haml
@@ -3,8 +3,9 @@
- if show_new_nav?
- add_to_breadcrumbs("Repository", project_tree_path(@project))
- content_for :page_specific_javascripts do
- = page_specific_javascript_bundle_tag('common_d3')
- = page_specific_javascript_bundle_tag('graphs')
+ = webpack_bundle_tag('common_d3')
+ = webpack_bundle_tag('graphs')
+ = webpack_bundle_tag('graphs_charts')
= render "projects/commits/head"
.repo-charts{ class: container_class }
@@ -75,55 +76,10 @@
Commits per day hour (UTC)
%canvas#hour-chart
-:javascript
- var responsiveChart = function (selector, data) {
- var options = { "scaleOverlay": true, responsive: true, pointHitDetectionRadius: 2, maintainAspectRatio: false };
- // get selector by context
- var ctx = selector.get(0).getContext("2d");
- // pointing parent container to make chart.js inherit its width
- var container = $(selector).parent();
- var generateChart = function() {
- selector.attr('width', $(container).width());
- if (window.innerWidth < 768) {
- // Scale fonts if window width lower than 768px (iPad portrait)
- options.scaleFontSize = 8
- }
- return new Chart(ctx).Bar(data, options);
- };
- // enabling auto-resizing
- $(window).resize(generateChart);
- return generateChart();
- };
-
- var chartData = function (keys, values) {
- var data = {
- labels : keys,
- datasets : [{
- fillColor : "rgba(220,220,220,0.5)",
- strokeColor : "rgba(220,220,220,1)",
- barStrokeWidth: 1,
- barValueSpacing: 1,
- barDatasetSpacing: 1,
- data : values
- }]
- };
- return data;
- };
-
- var hourData = chartData(#{@commits_per_time.keys.to_json}, #{@commits_per_time.values.to_json});
- responsiveChart($('#hour-chart'), hourData);
-
- var dayData = chartData(#{@commits_per_week_days.keys.to_json}, #{@commits_per_week_days.values.to_json});
- responsiveChart($('#weekday-chart'), dayData);
-
- var monthData = chartData(#{@commits_per_month.keys.to_json}, #{@commits_per_month.values.to_json});
- responsiveChart($('#month-chart'), monthData);
-
- var data = #{@languages.to_json};
- var ctx = $("#languages-chart").get(0).getContext("2d");
- var options = {
- scaleOverlay: true,
- responsive: true,
- maintainAspectRatio: false
- }
- var myPieChart = new Chart(ctx).Pie(data, options);
+%script#projectChartData{ type: "application/json" }
+ - projectChartData = {};
+ - projectChartData['hour'] = { 'keys' => @commits_per_time.keys, 'values' => @commits_per_time.values }
+ - projectChartData['weekDays'] = { 'keys' => @commits_per_week_days.keys, 'values' => @commits_per_week_days.values }
+ - projectChartData['month'] = { 'keys' => @commits_per_month.keys, 'values' => @commits_per_month.values }
+ - projectChartData['languages'] = @languages
+ = projectChartData.to_json.html_safe
diff --git a/app/views/projects/graphs/show.html.haml b/app/views/projects/graphs/show.html.haml
index 4256a8c4d7e..f41a0d8293b 100644
--- a/app/views/projects/graphs/show.html.haml
+++ b/app/views/projects/graphs/show.html.haml
@@ -1,15 +1,16 @@
- @no_container = true
- page_title "Contributors"
- content_for :page_specific_javascripts do
- = page_specific_javascript_bundle_tag('common_d3')
- = page_specific_javascript_bundle_tag('graphs')
+ = webpack_bundle_tag('common_d3')
+ = webpack_bundle_tag('graphs')
+ = webpack_bundle_tag('graphs_show')
- if show_new_nav?
- add_to_breadcrumbs("Repository", project_tree_path(@project))
= render 'projects/commits/head'
-%div{ class: container_class }
+.js-graphs-show{ class: container_class, 'data-project-graph-path': project_graph_path(@project, current_ref, format: :json) }
.sub-header-block
.tree-ref-holder
= render 'shared/ref_switcher', destination: 'graphs'
@@ -33,24 +34,3 @@
#contributors-master
#contributors.clearfix
%ol.contributors-list.clearfix
-
-
-
-:javascript
- $.ajax({
- type: "GET",
- url: "#{project_graph_path(@project, current_ref, format: :json)}",
- dataType: "json",
- success: function (data) {
- var graph = new ContributorsStatGraph();
- graph.init(data);
-
- $("#brush_change").change(function(){
- graph.change_date_header();
- graph.redraw_authors();
- });
-
- $(".stat-graph").fadeIn();
- $(".loading-graph").hide();
- }
- });
diff --git a/app/views/projects/imports/show.html.haml b/app/views/projects/imports/show.html.haml
index c52b3860636..8c490773a56 100644
--- a/app/views/projects/imports/show.html.haml
+++ b/app/views/projects/imports/show.html.haml
@@ -10,5 +10,3 @@
- if @project.external_import?
%p.monospace git clone --bare #{@project.safe_import_url}
%p Please wait while we import the repository for you. Refresh at will.
- :javascript
- new ProjectImport();
diff --git a/app/views/projects/mattermosts/_team_selection.html.haml b/app/views/projects/mattermosts/_team_selection.html.haml
index 3bdb5d0adc4..20acd476f73 100644
--- a/app/views/projects/mattermosts/_team_selection.html.haml
+++ b/app/views/projects/mattermosts/_team_selection.html.haml
@@ -33,7 +33,7 @@
Suggestions:
%code= 'gitlab'
%code= @project.path # Path contains no spaces, but dashes
- %code= @project.path_with_namespace
+ %code= @project.full_path
%p
Reserved:
= link_to 'https://docs.mattermost.com/help/messaging/executing-commands.html#built-in-commands', target: '__blank' do
diff --git a/app/views/projects/merge_requests/creations/_new_compare.html.haml b/app/views/projects/merge_requests/creations/_new_compare.html.haml
index 8958b2cf5e1..9d5cebdda53 100644
--- a/app/views/projects/merge_requests/creations/_new_compare.html.haml
+++ b/app/views/projects/merge_requests/creations/_new_compare.html.haml
@@ -41,7 +41,7 @@
- projects = target_projects(@project)
.merge-request-select.dropdown
= f.hidden_field :target_project_id
- = dropdown_toggle f.object.target_project.path_with_namespace, { toggle: "dropdown", field_name: "#{f.object_name}[target_project_id]", disabled: @merge_request.persisted? }, { toggle_class: "js-compare-dropdown js-target-project" }
+ = dropdown_toggle f.object.target_project.full_path, { toggle: "dropdown", field_name: "#{f.object_name}[target_project_id]", disabled: @merge_request.persisted? }, { toggle_class: "js-compare-dropdown js-target-project" }
.dropdown-menu.dropdown-menu-selectable.dropdown-target-project
= dropdown_title("Select target project")
= dropdown_filter("Search projects")
diff --git a/app/views/projects/merge_requests/dropdowns/_project.html.haml b/app/views/projects/merge_requests/dropdowns/_project.html.haml
index 25d5dc92f8a..aaf1ab00eeb 100644
--- a/app/views/projects/merge_requests/dropdowns/_project.html.haml
+++ b/app/views/projects/merge_requests/dropdowns/_project.html.haml
@@ -2,4 +2,4 @@
- projects.each do |project|
%li
%a{ href: "#", class: "#{('is-active' if selected == project.id)}", data: { id: project.id } }
- = project.path_with_namespace
+ = project.full_path
diff --git a/app/views/projects/new.html.haml b/app/views/projects/new.html.haml
index 87cc23fc649..25109f0f414 100644
--- a/app/views/projects/new.html.haml
+++ b/app/views/projects/new.html.haml
@@ -4,6 +4,8 @@
- page_title 'New Project'
- header_title "Projects", dashboard_projects_path
- visibility_level = params.dig(:project, :visibility_level) || default_project_visibility
+- content_for :page_specific_javascripts do
+ = webpack_bundle_tag 'project_new'
.project-edit-container
.project-edit-errors
@@ -111,46 +113,3 @@
%i.fa.fa-spinner.fa-spin
Creating project &amp; repository.
%p Please wait a moment, this page will automatically refresh when ready.
-
-:javascript
- var importBtnTooltip = "Please enter a valid project name.";
- var $importBtnWrapper = $('.import_gitlab_project');
-
- $('.how_to_import_link').bind('click', function (e) {
- e.preventDefault();
- var import_modal = $(this).next(".modal").show();
- });
-
- $('.modal-header .close').bind('click', function() {
- $(".modal").hide();
- });
-
- $('.btn_import_gitlab_project').bind('click', function() {
- var _href = $("a.btn_import_gitlab_project").attr("href");
- $(".btn_import_gitlab_project").attr("href", _href + '?namespace_id=' + $("#project_namespace_id").val() + '&path=' + $("#project_path").val());
- });
-
- $('.btn_import_gitlab_project').attr('disabled', $('#project_path').val().trim().length === 0);
- $importBtnWrapper.attr('title', importBtnTooltip);
-
- $('#new_project').submit(function(){
- var $path = $('#project_path');
- $path.val($path.val().trim());
- });
-
- $('#project_path').keyup(function(){
- if($(this).val().trim().length !== 0) {
- $('.btn_import_gitlab_project').attr('disabled', false);
- $importBtnWrapper.attr('title','');
- $importBtnWrapper.removeClass('has-tooltip');
- } else {
- $('.btn_import_gitlab_project').attr('disabled',true);
- $importBtnWrapper.addClass('has-tooltip');
- }
- });
-
- $('#project_import_url').disable();
- $('.import_git').click(function( event ) {
- $projectImportUrl = $('#project_import_url');
- $projectImportUrl.attr('disabled', !$projectImportUrl.attr('disabled'));
- });
diff --git a/app/views/projects/pipelines/charts/_pipeline_times.haml b/app/views/projects/pipelines/charts/_pipeline_times.haml
index 1292f580a81..a5dbd1b1532 100644
--- a/app/views/projects/pipelines/charts/_pipeline_times.haml
+++ b/app/views/projects/pipelines/charts/_pipeline_times.haml
@@ -1,27 +1,10 @@
+- content_for :page_specific_javascripts do
+ = webpack_bundle_tag('pipelines_times')
+
%div
%p.light
= _("Commit duration in minutes for last 30 commits")
%canvas#build_timesChart{ height: 200 }
-:javascript
- var data = {
- labels : #{@charts[:pipeline_times].labels.to_json},
- datasets : [
- {
- fillColor : "rgba(220,220,220,0.5)",
- strokeColor : "rgba(220,220,220,1)",
- barStrokeWidth: 1,
- barValueSpacing: 1,
- barDatasetSpacing: 1,
- data : #{@charts[:pipeline_times].pipeline_times.to_json}
- }
- ]
- }
- var ctx = $("#build_timesChart").get(0).getContext("2d");
- var options = { scaleOverlay: true, responsive: true, maintainAspectRatio: false };
- if (window.innerWidth < 768) {
- // Scale fonts if window width lower than 768px (iPad portrait)
- options.scaleFontSize = 8
- }
- new Chart(ctx).Bar(data, options);
+%script#pipelinesTimesChartsData{ type: "application/json" }= { :labels => @charts[:pipeline_times].labels, :values => @charts[:pipeline_times].pipeline_times }.to_json.html_safe
diff --git a/app/views/projects/pipelines/charts/_pipelines.haml b/app/views/projects/pipelines/charts/_pipelines.haml
index be884448087..02f1ef4b6da 100644
--- a/app/views/projects/pipelines/charts/_pipelines.haml
+++ b/app/views/projects/pipelines/charts/_pipelines.haml
@@ -1,3 +1,6 @@
+- content_for :page_specific_javascripts do
+ = webpack_bundle_tag('pipelines_charts')
+
%h4= _("Pipelines charts")
%p
&nbsp;
@@ -26,31 +29,8 @@
= _("Jobs for last year")
%canvas#yearChart.padded{ height: 250 }
-- [:week, :month, :year].each do |scope|
- :javascript
- var data = {
- labels : #{@charts[scope].labels.to_json},
- datasets : [
- {
- fillColor : "#7f8fa4",
- strokeColor : "#7f8fa4",
- pointColor : "#7f8fa4",
- pointStrokeColor : "#EEE",
- data : #{@charts[scope].total.to_json}
- },
- {
- fillColor : "#44aa22",
- strokeColor : "#44aa22",
- pointColor : "#44aa22",
- pointStrokeColor : "#fff",
- data : #{@charts[scope].success.to_json}
- }
- ]
- }
- var ctx = $("##{scope}Chart").get(0).getContext("2d");
- var options = { scaleOverlay: true, responsive: true, maintainAspectRatio: false };
- if (window.innerWidth < 768) {
- // Scale fonts if window width lower than 768px (iPad portrait)
- options.scaleFontSize = 8
- }
- new Chart(ctx).Line(data, options);
+%script#pipelinesChartsData{ type: "application/json" }
+ - chartData = []
+ - [:week, :month, :year].each do |scope|
+ - chartData.push({ 'scope' => scope, 'labels' => @charts[scope].labels, 'totalValues' => @charts[scope].total, 'successValues' => @charts[scope].success })
+ = chartData.to_json.html_safe
diff --git a/app/views/projects/pipelines/new.html.haml b/app/views/projects/pipelines/new.html.haml
index c966df62856..4ad37d0e882 100644
--- a/app/views/projects/pipelines/new.html.haml
+++ b/app/views/projects/pipelines/new.html.haml
@@ -20,7 +20,4 @@
= f.submit 'Create pipeline', class: 'btn btn-create', tabindex: 3
= link_to 'Cancel', project_pipelines_path(@project), class: 'btn btn-cancel'
-:javascript
- var availableRefs = #{@project.repository.ref_names.to_json};
-
- new NewBranchForm($('.js-new-pipeline-form'), availableRefs)
+%script#availableRefs{ type: "application/json" }= @project.repository.ref_names.to_json.html_safe
diff --git a/app/views/projects/services/mattermost_slash_commands/_detailed_help.html.haml b/app/views/projects/services/mattermost_slash_commands/_detailed_help.html.haml
index ef3599460f1..5dbcbf7eba6 100644
--- a/app/views/projects/services/mattermost_slash_commands/_detailed_help.html.haml
+++ b/app/views/projects/services/mattermost_slash_commands/_detailed_help.html.haml
@@ -39,7 +39,7 @@
Suggestions:
%code= 'gitlab'
%code= @project.path # Path contains no spaces, but dashes
- %code= @project.path_with_namespace
+ %code= @project.full_path
.form-group
= label_tag :request_url, 'Request URL', class: 'col-sm-2 col-xs-12 control-label'
diff --git a/app/views/projects/services/slack_slash_commands/_help.html.haml b/app/views/projects/services/slack_slash_commands/_help.html.haml
index 73b99453a4b..c31c95608c6 100644
--- a/app/views/projects/services/slack_slash_commands/_help.html.haml
+++ b/app/views/projects/services/slack_slash_commands/_help.html.haml
@@ -33,7 +33,7 @@
Suggestions:
%code= 'gitlab'
%code= @project.path # Path contains no spaces, but dashes
- %code= @project.path_with_namespace
+ %code= @project.full_path
.form-group
= label_tag :url, 'URL', class: 'col-sm-2 col-xs-12 control-label'
diff --git a/app/views/projects/tags/new.html.haml b/app/views/projects/tags/new.html.haml
index f1bbaf40387..521b4d927bc 100644
--- a/app/views/projects/tags/new.html.haml
+++ b/app/views/projects/tags/new.html.haml
@@ -40,7 +40,4 @@
.form-actions
= button_tag 'Create tag', class: 'btn btn-create', tabindex: 3
= link_to 'Cancel', project_tags_path(@project), class: 'btn btn-cancel'
-
-:javascript
- window.gl = window.gl || { };
- window.gl.availableRefs = #{@project.repository.ref_names.to_json};
+%script#availableRefs{ type: "application/json" }= @project.repository.ref_names.to_json.html_safe
diff --git a/app/views/projects/tree/_tree_content.html.haml b/app/views/projects/tree/_tree_content.html.haml
index 6560bd5ab3f..820b947804e 100644
--- a/app/views/projects/tree/_tree_content.html.haml
+++ b/app/views/projects/tree/_tree_content.html.haml
@@ -1,4 +1,4 @@
-.tree-content-holder
+.tree-content-holder.js-tree-content{ 'data-logs-path': @logs_path }
.table-holder
%table.table#tree-slider{ class: "table_#{@hex_path} tree-table" }
%thead
@@ -22,9 +22,3 @@
- if can_edit_tree?
= render 'projects/blob/upload', title: _('Upload New File'), placeholder: _('Upload New File'), button_title: _('Upload file'), form_path: project_create_blob_path(@project, @id), method: :post
= render 'projects/blob/new_dir'
-
-:javascript
- // Load last commit log for each file in tree
- $('#tree-slider').waitForImages(function() {
- gl.utils.ajaxGet("#{escape_javascript(@logs_path)}");
- });
diff --git a/app/views/projects/wikis/_sidebar.html.haml b/app/views/projects/wikis/_sidebar.html.haml
index 62873d3aa66..e71ce1f357f 100644
--- a/app/views/projects/wikis/_sidebar.html.haml
+++ b/app/views/projects/wikis/_sidebar.html.haml
@@ -19,6 +19,3 @@
More Pages
= render 'projects/wikis/new'
-
-:javascript
- new Sidebar();
diff --git a/app/views/projects/wikis/git_access.html.haml b/app/views/projects/wikis/git_access.html.haml
index e64dd6085fe..e740fb93ea4 100644
--- a/app/views/projects/wikis/git_access.html.haml
+++ b/app/views/projects/wikis/git_access.html.haml
@@ -7,7 +7,7 @@
.git-access-header
Clone repository
- %strong= @project_wiki.path_with_namespace
+ %strong= @project_wiki.full_path
= render "shared/clone_panel", project: @project_wiki
diff --git a/app/views/shared/issuable/_sidebar.html.haml b/app/views/shared/issuable/_sidebar.html.haml
index b08267357e5..e7510c1d1ec 100644
--- a/app/views/shared/issuable/_sidebar.html.haml
+++ b/app/views/shared/issuable/_sidebar.html.haml
@@ -37,7 +37,7 @@
= link_to 'Edit', '#', class: 'edit-link pull-right'
.value.hide-collapsed
- if issuable.milestone
- = link_to issuable.milestone.title, project_milestone_path(@project, issuable.milestone), class: "bold has-tooltip", title: milestone_remaining_days(issuable.milestone), data: { container: "body", html: 1 }
+ = link_to issuable.milestone.title, milestone_path(issuable.milestone), class: "bold has-tooltip", title: milestone_remaining_days(issuable.milestone), data: { container: "body", html: 1 }
- else
%span.no-value None
diff --git a/app/views/u2f/_register.html.haml b/app/views/u2f/_register.html.haml
index 00788e77b6b..093b2d82813 100644
--- a/app/views/u2f/_register.html.haml
+++ b/app/views/u2f/_register.html.haml
@@ -37,7 +37,3 @@
.col-md-3
= hidden_field_tag 'u2f_registration[device_response]', nil, class: 'form-control', required: true, id: "js-device-response"
= submit_tag "Register U2F device", class: "btn btn-success"
-
-:javascript
- var u2fRegister = new U2FRegister($("#js-register-u2f"), gon.u2f);
- u2fRegister.start();
diff --git a/app/workers/git_garbage_collect_worker.rb b/app/workers/git_garbage_collect_worker.rb
index d369b639ae9..c95497dfaba 100644
--- a/app/workers/git_garbage_collect_worker.rb
+++ b/app/workers/git_garbage_collect_worker.rb
@@ -5,6 +5,12 @@ class GitGarbageCollectWorker
sidekiq_options retry: false
+ GITALY_MIGRATED_TASKS = {
+ gc: :garbage_collect,
+ full_repack: :repack_full,
+ incremental_repack: :repack_incremental
+ }.freeze
+
def perform(project_id, task = :gc, lease_key = nil, lease_uuid = nil)
project = Project.find(project_id)
task = task.to_sym
@@ -15,8 +21,14 @@ class GitGarbageCollectWorker
Gitlab::GitLogger.info(description)
- output, status = Gitlab::Popen.popen(cmd, repo_path)
- Gitlab::GitLogger.error("#{description} failed:\n#{output}") unless status.zero?
+ gitaly_migrate(GITALY_MIGRATED_TASKS[task]) do |is_enabled|
+ if is_enabled
+ gitaly_call(task, project.repository.raw_repository)
+ else
+ output, status = Gitlab::Popen.popen(cmd, repo_path)
+ Gitlab::GitLogger.error("#{description} failed:\n#{output}") unless status.zero?
+ end
+ end
# Refresh the branch cache in case garbage collection caused a ref lookup to fail
flush_ref_caches(project) if task == :gc
@@ -26,6 +38,19 @@ class GitGarbageCollectWorker
private
+ ## `repository` has to be a Gitlab::Git::Repository
+ def gitaly_call(task, repository)
+ client = Gitlab::GitalyClient::RepositoryService.new(repository)
+ case task
+ when :gc
+ client.garbage_collect(bitmaps_enabled?)
+ when :full_repack
+ client.repack_full(bitmaps_enabled?)
+ when :incremental_repack
+ client.repack_incremental
+ end
+ end
+
def command(task)
case task
when :gc
@@ -55,4 +80,14 @@ class GitGarbageCollectWorker
config_value = write_bitmaps ? 'true' : 'false'
%W[git -c repack.writeBitmaps=#{config_value}]
end
+
+ def gitaly_migrate(method, &block)
+ Gitlab::GitalyClient.migrate(method, &block)
+ rescue GRPC::NotFound => e
+ Gitlab::GitLogger.error("#{method} failed:\nRepository not found")
+ raise Gitlab::Git::Repository::NoRepository.new(e)
+ rescue GRPC::BadStatus => e
+ Gitlab::GitLogger.error("#{method} failed:\n#{e}")
+ raise Gitlab::Git::CommandError.new(e)
+ end
end
diff --git a/app/workers/irker_worker.rb b/app/workers/irker_worker.rb
index 22f67fa9e9f..3dd14466994 100644
--- a/app/workers/irker_worker.rb
+++ b/app/workers/irker_worker.rb
@@ -66,7 +66,7 @@ class IrkerWorker
end
def send_new_branch(project, repo_name, committer, branch)
- repo_path = project.path_with_namespace
+ repo_path = project.full_path
newbranch = "#{Gitlab.config.gitlab.url}/#{repo_path}/branches"
newbranch = "\x0302\x1f#{newbranch}\x0f" if @colors
@@ -109,7 +109,7 @@ class IrkerWorker
end
def send_commits_count(data, project, repo, committer, branch)
- url = compare_url data, project.path_with_namespace
+ url = compare_url data, project.full_path
commits = colorize_commits data['total_commits_count']
new_commits = 'new commit'
diff --git a/app/workers/repository_import_worker.rb b/app/workers/repository_import_worker.rb
index 625476b7e01..6be541abd3e 100644
--- a/app/workers/repository_import_worker.rb
+++ b/app/workers/repository_import_worker.rb
@@ -16,7 +16,7 @@ class RepositoryImportWorker
Gitlab::Metrics.add_event(:import_repository,
import_url: @project.import_url,
- path: @project.path_with_namespace)
+ path: @project.full_path)
project.update_columns(import_jid: self.jid, import_error: nil)
diff --git a/changelogs/unreleased/29385-add_shrug_command.yml b/changelogs/unreleased/29385-add_shrug_command.yml
new file mode 100644
index 00000000000..14b8f486d5c
--- /dev/null
+++ b/changelogs/unreleased/29385-add_shrug_command.yml
@@ -0,0 +1,4 @@
+---
+title: Add /shrug and /tableflip commands
+merge_request: 10068
+author: Alex Ives
diff --git a/changelogs/unreleased/33620-remove-events-from-notification_settings.yml b/changelogs/unreleased/33620-remove-events-from-notification_settings.yml
new file mode 100644
index 00000000000..f5f3ef3fb82
--- /dev/null
+++ b/changelogs/unreleased/33620-remove-events-from-notification_settings.yml
@@ -0,0 +1,4 @@
+---
+title: Remove events column from notification settings table
+merge_request:
+author:
diff --git a/changelogs/unreleased/34492-firefox-job.yml b/changelogs/unreleased/34492-firefox-job.yml
new file mode 100644
index 00000000000..881b8f649ea
--- /dev/null
+++ b/changelogs/unreleased/34492-firefox-job.yml
@@ -0,0 +1,4 @@
+---
+title: Use jQuery to control scroll behavior in job log for cross browser consistency
+merge_request:
+author:
diff --git a/changelogs/unreleased/34519-extend-api-group-secret-variable.yml b/changelogs/unreleased/34519-extend-api-group-secret-variable.yml
new file mode 100644
index 00000000000..e0b625c392f
--- /dev/null
+++ b/changelogs/unreleased/34519-extend-api-group-secret-variable.yml
@@ -0,0 +1,4 @@
+---
+title: Extend API for Group Secret Variable
+merge_request: 12936
+author:
diff --git a/changelogs/unreleased/34869-bump-rubocop-to-0-49-1-and-rubocop-rspec-to-1-15-1.yml b/changelogs/unreleased/34869-bump-rubocop-to-0-49-1-and-rubocop-rspec-to-1-15-1.yml
new file mode 100644
index 00000000000..0eb2d069719
--- /dev/null
+++ b/changelogs/unreleased/34869-bump-rubocop-to-0-49-1-and-rubocop-rspec-to-1-15-1.yml
@@ -0,0 +1,4 @@
+---
+title: Bump rubocop to 0.49.1 and rubocop-rspec to 1.15.1
+merge_request:
+author: Takuya Noguchi
diff --git a/changelogs/unreleased/35044-projects-logo-are-not-centered-vertically-on-projects-page.yml b/changelogs/unreleased/35044-projects-logo-are-not-centered-vertically-on-projects-page.yml
new file mode 100644
index 00000000000..9de4dbefd35
--- /dev/null
+++ b/changelogs/unreleased/35044-projects-logo-are-not-centered-vertically-on-projects-page.yml
@@ -0,0 +1,4 @@
+---
+title: Fix project logos that are not centered vertically on list pages
+merge_request: 13124
+author: Florian Lemaitre
diff --git a/changelogs/unreleased/35697-allow-logged-in-user-to-read-user-list.yml b/changelogs/unreleased/35697-allow-logged-in-user-to-read-user-list.yml
new file mode 100644
index 00000000000..54b2e71bef9
--- /dev/null
+++ b/changelogs/unreleased/35697-allow-logged-in-user-to-read-user-list.yml
@@ -0,0 +1,4 @@
+---
+title: Allow any logged in users to read_users_list even if it's restricted
+merge_request: 13201
+author:
diff --git a/changelogs/unreleased/breadcrumbs-collapsed-title-width-fix.yml b/changelogs/unreleased/breadcrumbs-collapsed-title-width-fix.yml
new file mode 100644
index 00000000000..988fdacb5fd
--- /dev/null
+++ b/changelogs/unreleased/breadcrumbs-collapsed-title-width-fix.yml
@@ -0,0 +1,4 @@
+---
+title: Fixed breadcrumbs title aggressively collapsing
+merge_request:
+author:
diff --git a/changelogs/unreleased/fix-group-milestone-link-in-issuable-sidebar.yml b/changelogs/unreleased/fix-group-milestone-link-in-issuable-sidebar.yml
new file mode 100644
index 00000000000..1558e575e6d
--- /dev/null
+++ b/changelogs/unreleased/fix-group-milestone-link-in-issuable-sidebar.yml
@@ -0,0 +1,4 @@
+---
+title: Fix links to group milestones from issue and merge request sidebar
+merge_request:
+author:
diff --git a/changelogs/unreleased/help-page-breadcrumb-title-fix.yml b/changelogs/unreleased/help-page-breadcrumb-title-fix.yml
deleted file mode 100644
index 040fe4b9916..00000000000
--- a/changelogs/unreleased/help-page-breadcrumb-title-fix.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Fixed new navigation breadcrumb title on help pages
-merge_request:
-author:
diff --git a/changelogs/unreleased/mk-fix-deploy-key-deletion.yml b/changelogs/unreleased/mk-fix-deploy-key-deletion.yml
new file mode 100644
index 00000000000..9ff2e49b14c
--- /dev/null
+++ b/changelogs/unreleased/mk-fix-deploy-key-deletion.yml
@@ -0,0 +1,4 @@
+---
+title: Fix deletion of deploy keys linked to other projects
+merge_request: 13162
+author:
diff --git a/changelogs/unreleased/mk-fix-wiki-backup.yml b/changelogs/unreleased/mk-fix-wiki-backup.yml
new file mode 100644
index 00000000000..ba9c1e85955
--- /dev/null
+++ b/changelogs/unreleased/mk-fix-wiki-backup.yml
@@ -0,0 +1,4 @@
+---
+title: Fix improperly skipped backups of wikis.
+merge_request: 13096
+author:
diff --git a/changelogs/unreleased/pawel-ensure_temp_files_are_deleted_in_fs_metrics-35457.yml b/changelogs/unreleased/pawel-ensure_temp_files_are_deleted_in_fs_metrics-35457.yml
deleted file mode 100644
index 1186dc59dc7..00000000000
--- a/changelogs/unreleased/pawel-ensure_temp_files_are_deleted_in_fs_metrics-35457.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Ensure filesystem metrics test files are deleted
-merge_request:
-author:
diff --git a/changelogs/unreleased/pawel-prometheus_client_pid_reuse_error.yml b/changelogs/unreleased/pawel-prometheus_client_pid_reuse_error.yml
deleted file mode 100644
index dfff4c23308..00000000000
--- a/changelogs/unreleased/pawel-prometheus_client_pid_reuse_error.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Fix Prometheus client PID reuse bug
-merge_request: 13130
-author:
diff --git a/changelogs/unreleased/search-flickering.yml b/changelogs/unreleased/search-flickering.yml
new file mode 100644
index 00000000000..951a5a0292a
--- /dev/null
+++ b/changelogs/unreleased/search-flickering.yml
@@ -0,0 +1,4 @@
+---
+title: Fix search box losing focus when typing
+merge_request:
+author:
diff --git a/changelogs/unreleased/winh-derive-project-name.yml b/changelogs/unreleased/winh-derive-project-name.yml
new file mode 100644
index 00000000000..2244d21d768
--- /dev/null
+++ b/changelogs/unreleased/winh-derive-project-name.yml
@@ -0,0 +1,4 @@
+---
+title: Derive project path from import URL
+merge_request: 13131
+author:
diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example
index e9bf2df490f..73a68c6da1b 100644
--- a/config/gitlab.yml.example
+++ b/config/gitlab.yml.example
@@ -282,7 +282,7 @@ production: &base
#
# Example: '/etc/ca.pem'
#
- ca_cert: ''
+ ca_file: ''
# Specifies the SSL version for OpenSSL to use, if the OpenSSL default
# is not appropriate.
diff --git a/config/webpack.config.js b/config/webpack.config.js
index 41d3ed12b14..8b0c64f9289 100644
--- a/config/webpack.config.js
+++ b/config/webpack.config.js
@@ -39,6 +39,8 @@ var config = {
environments_folder: './environments/folder/environments_folder_bundle.js',
filtered_search: './filtered_search/filtered_search_bundle.js',
graphs: './graphs/graphs_bundle.js',
+ graphs_charts: './graphs/graphs_charts.js',
+ graphs_show: './graphs/graphs_show.js',
group: './group.js',
groups: './groups/index.js',
groups_list: './groups_list.js',
@@ -54,8 +56,11 @@ var config = {
notebook_viewer: './blob/notebook_viewer.js',
pdf_viewer: './blob/pdf_viewer.js',
pipelines: './pipelines/pipelines_bundle.js',
- pipelines_details: './pipelines/pipeline_details_bundle.js',
+ pipelines_charts: './pipelines/pipelines_charts.js',
+ pipelines_details: './pipelines/pipeline_details_bundle.js',
+ pipelines_times: './pipelines/pipelines_times.js',
profile: './profile/profile_bundle.js',
+ project_new: './projects/project_new.js',
prometheus_metrics: './prometheus_metrics',
protected_branches: './protected_branches',
protected_tags: './protected_tags',
@@ -67,9 +72,12 @@ var config = {
stl_viewer: './blob/stl_viewer.js',
terminal: './terminal/terminal_bundle.js',
u2f: ['vendor/u2f'],
+ ui_development_kit: './ui_development_kit.js',
+ users: './users/index.js',
raven: './raven/index.js',
vue_merge_request_widget: './vue_merge_request_widget/index.js',
test: './test.js',
+ two_factor_auth: './two_factor_auth.js',
performance_bar: './performance_bar.js',
webpack_runtime: './webpack.js',
},
diff --git a/db/migrate/20161124141322_migrate_process_commit_worker_jobs.rb b/db/migrate/20161124141322_migrate_process_commit_worker_jobs.rb
index c0cb9d78748..bcdae272209 100644
--- a/db/migrate/20161124141322_migrate_process_commit_worker_jobs.rb
+++ b/db/migrate/20161124141322_migrate_process_commit_worker_jobs.rb
@@ -16,6 +16,7 @@ class MigrateProcessCommitWorkerJobs < ActiveRecord::Migration
end
def repository_path
+ # TODO: review if the change from Legacy storage needs to reflect here as well.
File.join(repository_storage_path, read_attribute(:path_with_namespace) + '.git')
end
diff --git a/db/post_migrate/20170728101014_remove_events_from_notification_settings.rb b/db/post_migrate/20170728101014_remove_events_from_notification_settings.rb
new file mode 100644
index 00000000000..cd533391d8d
--- /dev/null
+++ b/db/post_migrate/20170728101014_remove_events_from_notification_settings.rb
@@ -0,0 +1,9 @@
+class RemoveEventsFromNotificationSettings < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ def change
+ remove_column :notification_settings, :events, :text
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 0abdb987b77..4ba218e1e0d 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -11,7 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema.define(version: 20170725145659) do
+ActiveRecord::Schema.define(version: 20170728101014) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -254,32 +254,32 @@ ActiveRecord::Schema.define(version: 20170725145659) do
add_index "ci_builds", ["updated_at"], name: "index_ci_builds_on_updated_at", using: :btree
add_index "ci_builds", ["user_id"], name: "index_ci_builds_on_user_id", using: :btree
- create_table "ci_pipeline_schedule_variables", force: :cascade do |t|
+ create_table "ci_group_variables", force: :cascade do |t|
t.string "key", null: false
t.text "value"
t.text "encrypted_value"
t.string "encrypted_value_salt"
t.string "encrypted_value_iv"
- t.integer "pipeline_schedule_id", null: false
+ t.integer "group_id", null: false
+ t.boolean "protected", default: false, null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
- add_index "ci_pipeline_schedule_variables", ["pipeline_schedule_id", "key"], name: "index_ci_pipeline_schedule_variables_on_schedule_id_and_key", unique: true, using: :btree
+ add_index "ci_group_variables", ["group_id", "key"], name: "index_ci_group_variables_on_group_id_and_key", unique: true, using: :btree
- create_table "ci_group_variables", force: :cascade do |t|
+ create_table "ci_pipeline_schedule_variables", force: :cascade do |t|
t.string "key", null: false
t.text "value"
t.text "encrypted_value"
t.string "encrypted_value_salt"
t.string "encrypted_value_iv"
- t.integer "group_id", null: false
- t.boolean "protected", default: false, null: false
- t.datetime "created_at", null: false
- t.datetime "updated_at", null: false
+ t.integer "pipeline_schedule_id", null: false
+ t.datetime "created_at"
+ t.datetime "updated_at"
end
- add_index "ci_group_variables", ["group_id", "key"], name: "index_ci_group_variables_on_group_id_and_key", unique: true, using: :btree
+ add_index "ci_pipeline_schedule_variables", ["pipeline_schedule_id", "key"], name: "index_ci_pipeline_schedule_variables_on_schedule_id_and_key", unique: true, using: :btree
create_table "ci_pipeline_schedules", force: :cascade do |t|
t.string "description"
@@ -981,7 +981,6 @@ ActiveRecord::Schema.define(version: 20170725145659) do
t.integer "level", default: 0, null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
- t.text "events"
t.boolean "new_note"
t.boolean "new_issue"
t.boolean "reopen_issue"
@@ -1624,8 +1623,8 @@ ActiveRecord::Schema.define(version: 20170725145659) do
add_foreign_key "ci_builds", "ci_pipelines", column: "auto_canceled_by_id", name: "fk_a2141b1522", on_delete: :nullify
add_foreign_key "ci_builds", "ci_stages", column: "stage_id", name: "fk_3a9eaa254d", on_delete: :cascade
add_foreign_key "ci_builds", "projects", name: "fk_befce0568a", on_delete: :cascade
- add_foreign_key "ci_pipeline_schedule_variables", "ci_pipeline_schedules", column: "pipeline_schedule_id", name: "fk_41c35fda51", on_delete: :cascade
add_foreign_key "ci_group_variables", "namespaces", column: "group_id", name: "fk_33ae4d58d8", on_delete: :cascade
+ add_foreign_key "ci_pipeline_schedule_variables", "ci_pipeline_schedules", column: "pipeline_schedule_id", name: "fk_41c35fda51", on_delete: :cascade
add_foreign_key "ci_pipeline_schedules", "projects", name: "fk_8ead60fcc4", on_delete: :cascade
add_foreign_key "ci_pipeline_schedules", "users", column: "owner_id", name: "fk_9ea99f58d2", on_delete: :nullify
add_foreign_key "ci_pipeline_variables", "ci_pipelines", column: "pipeline_id", name: "fk_f29c5f4380", on_delete: :cascade
diff --git a/doc/README.md b/doc/README.md
index cc63ecb7eab..8bb8e147cd1 100644
--- a/doc/README.md
+++ b/doc/README.md
@@ -44,16 +44,17 @@ Shortcuts to GitLab's most visited docs:
### Projects and groups
-- [Create a project](gitlab-basics/create-project.md)
-- [Fork a project](gitlab-basics/fork-project.md)
-- [Importing and exporting projects between instances](user/project/settings/import_export.md).
-- [Project access](public_access/public_access.md): Setting up your project's visibility to public, internal, or private.
+- [Projects](user/project/index.md):
+ - [Create a project](gitlab-basics/create-project.md)
+ - [Fork a project](gitlab-basics/fork-project.md)
+ - [Importing and exporting projects between instances](user/project/settings/import_export.md).
+ - [Project access](public_access/public_access.md): Setting up your project's visibility to public, internal, or private.
+ - [GitLab Pages](user/project/pages/index.md): Build, test, and deploy your static website with GitLab Pages.
- [Groups](user/group/index.md): Organize your projects in groups.
- - [GitLab Subgroups](user/group/subgroups/index.md)
+ - [Subgroups](user/group/subgroups/index.md)
- [Search through GitLab](user/search/index.md): Search for issues, merge requests, projects, groups, todos, and issues in Issue Boards.
- [Snippets](user/snippets.md): Snippets allow you to create little bits of code.
- [Wikis](user/project/wiki/index.md): Enhance your repository documentation with built-in wikis.
-- [GitLab Pages](user/project/pages/index.md): Build, test, and deploy your static website with GitLab Pages.
### Repository
diff --git a/doc/administration/auth/ldap.md b/doc/administration/auth/ldap.md
index a7395e03d1c..425c924cdf2 100644
--- a/doc/administration/auth/ldap.md
+++ b/doc/administration/auth/ldap.md
@@ -96,7 +96,7 @@ main: # 'main' is the GitLab 'provider ID' of this LDAP server
#
# Example: '/etc/ca.pem'
#
- ca_cert: ''
+ ca_file: ''
# Specifies the SSL version for OpenSSL to use, if the OpenSSL default
# is not appropriate.
@@ -259,9 +259,9 @@ group you can use the following syntax:
(memberOf:1.2.840.113556.1.4.1941:=CN=My Group,DC=Example,DC=com)
```
-Find more information about this "LDAP_MATCHING_RULE_IN_CHAIN" filter at
+Find more information about this "LDAP_MATCHING_RULE_IN_CHAIN" filter at
https://msdn.microsoft.com/en-us/library/aa746475(v=vs.85).aspx. Support for
-nested members in the user filter should not be confused with
+nested members in the user filter should not be confused with
[group sync nested groups support (EE only)](https://docs.gitlab.com/ee/administration/auth/ldap-ee.html#supported-ldap-group-types-attributes).
Please note that GitLab does not support the custom filter syntax used by
diff --git a/doc/administration/high_availability/nfs.md b/doc/administration/high_availability/nfs.md
index bd6b7327aed..90a2e9298bf 100644
--- a/doc/administration/high_availability/nfs.md
+++ b/doc/administration/high_availability/nfs.md
@@ -46,6 +46,10 @@ GitLab does not recommend using EFS with GitLab.
many small files are written in a serialized manner are not well-suited for EFS.
EBS with an NFS server on top will perform much better.
+In addition, avoid storing GitLab log files (e.g. those in `/var/log/gitlab`)
+because this will also affect performance. We recommend that the log files be
+stored on a local volume.
+
For more details on another person's experience with EFS, see
[Amazon's Elastic File System: Burst Credits](https://www.rawkode.io/2017/04/amazons-elastic-file-system-burst-credits/)
diff --git a/doc/administration/monitoring/performance/img/performance_bar.png b/doc/administration/monitoring/performance/img/performance_bar.png
index d38293d2ed6..b3c6bc474e3 100644
--- a/doc/administration/monitoring/performance/img/performance_bar.png
+++ b/doc/administration/monitoring/performance/img/performance_bar.png
Binary files differ
diff --git a/doc/api/README.md b/doc/api/README.md
index fe29563eaca..1d2226e2ae8 100644
--- a/doc/api/README.md
+++ b/doc/api/README.md
@@ -11,7 +11,8 @@ following locations:
- [Award Emoji](award_emoji.md)
- [Branches](branches.md)
- [Broadcast Messages](broadcast_messages.md)
-- [Build Variables](build_variables.md)
+- [Project-level Variables](project_level_variables.md)
+- [Group-level Variables](group_level_variables.md)
- [Commits](commits.md)
- [Deployments](deployments.md)
- [Deploy Keys](deploy_keys.md)
diff --git a/doc/api/group_level_variables.md b/doc/api/group_level_variables.md
new file mode 100644
index 00000000000..e19be7b35c4
--- /dev/null
+++ b/doc/api/group_level_variables.md
@@ -0,0 +1,125 @@
+# Group-level Variables API
+
+## List group variables
+
+Get list of a group's variables.
+
+```
+GET /groups/:id/variables
+```
+
+| Attribute | Type | required | Description |
+|-----------|---------|----------|---------------------|
+| `id` | integer/string | yes | The ID of a group or [URL-encoded path of the group](README.md#namespaced-path-encoding) owned by the authenticated user |
+
+```
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/groups/1/variables"
+```
+
+```json
+[
+ {
+ "key": "TEST_VARIABLE_1",
+ "value": "TEST_1"
+ },
+ {
+ "key": "TEST_VARIABLE_2",
+ "value": "TEST_2"
+ }
+]
+```
+
+## Show variable details
+
+Get the details of a group's specific variable.
+
+```
+GET /groups/:id/variables/:key
+```
+
+| Attribute | Type | required | Description |
+|-----------|---------|----------|-----------------------|
+| `id` | integer/string | yes | The ID of a group or [URL-encoded path of the group](README.md#namespaced-path-encoding) owned by the authenticated user |
+| `key` | string | yes | The `key` of a variable |
+
+```
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/groups/1/variables/TEST_VARIABLE_1"
+```
+
+```json
+{
+ "key": "TEST_VARIABLE_1",
+ "value": "TEST_1"
+}
+```
+
+## Create variable
+
+Create a new variable.
+
+```
+POST /groups/:id/variables
+```
+
+| Attribute | Type | required | Description |
+|-------------|---------|----------|-----------------------|
+| `id` | integer/string | yes | The ID of a group or [URL-encoded path of the group](README.md#namespaced-path-encoding) owned by the authenticated user |
+| `key` | string | yes | The `key` of a variable; must have no more than 255 characters; only `A-Z`, `a-z`, `0-9`, and `_` are allowed |
+| `value` | string | yes | The `value` of a variable |
+| `protected` | boolean | no | Whether the variable is protected |
+
+```
+curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/groups/1/variables" --form "key=NEW_VARIABLE" --form "value=new value"
+```
+
+```json
+{
+ "key": "NEW_VARIABLE",
+ "value": "new value",
+ "protected": false
+}
+```
+
+## Update variable
+
+Update a group's variable.
+
+```
+PUT /groups/:id/variables/:key
+```
+
+| Attribute | Type | required | Description |
+|-------------|---------|----------|-------------------------|
+| `id` | integer/string | yes | The ID of a group or [URL-encoded path of the group](README.md#namespaced-path-encoding) owned by the authenticated user |
+| `key` | string | yes | The `key` of a variable |
+| `value` | string | yes | The `value` of a variable |
+| `protected` | boolean | no | Whether the variable is protected |
+
+```
+curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/groups/1/variables/NEW_VARIABLE" --form "value=updated value"
+```
+
+```json
+{
+ "key": "NEW_VARIABLE",
+ "value": "updated value",
+ "protected": true
+}
+```
+
+## Remove variable
+
+Remove a group's variable.
+
+```
+DELETE /groups/:id/variables/:key
+```
+
+| Attribute | Type | required | Description |
+|-----------|---------|----------|-------------------------|
+| `id` | integer/string | yes | The ID of a group or [URL-encoded path of the group](README.md#namespaced-path-encoding) owned by the authenticated user |
+| `key` | string | yes | The `key` of a variable |
+
+```
+curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/groups/1/variables/VARIABLE_1"
+```
diff --git a/doc/api/build_variables.md b/doc/api/project_level_variables.md
index d4f00256ed3..82ac0b09027 100644
--- a/doc/api/build_variables.md
+++ b/doc/api/project_level_variables.md
@@ -1,8 +1,8 @@
-# Build Variables API
+# Project-level Variables API
## List project variables
-Get list of a project's build variables.
+Get list of a project's variables.
```
GET /projects/:id/variables
@@ -31,7 +31,7 @@ curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/
## Show variable details
-Get the details of a project's specific build variable.
+Get the details of a project's specific variable.
```
GET /projects/:id/variables/:key
@@ -55,7 +55,7 @@ curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/
## Create variable
-Create a new build variable.
+Create a new variable.
```
POST /projects/:id/variables
@@ -82,7 +82,7 @@ curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitl
## Update variable
-Update a project's build variable.
+Update a project's variable.
```
PUT /projects/:id/variables/:key
@@ -109,7 +109,7 @@ curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitla
## Remove variable
-Remove a project's build variable.
+Remove a project's variable.
```
DELETE /projects/:id/variables/:key
diff --git a/doc/api/users.md b/doc/api/users.md
index 6e5ec3231c5..57a13eb477d 100644
--- a/doc/api/users.md
+++ b/doc/api/users.md
@@ -241,26 +241,26 @@ POST /users
Parameters:
-- `email` (required) - Email
-- `password` (optional) - Password
-- `reset_password` (optional) - Send user password reset link - true or false(default)
-- `username` (required) - Username
-- `name` (required) - Name
-- `skype` (optional) - Skype ID
-- `linkedin` (optional) - LinkedIn
-- `twitter` (optional) - Twitter account
-- `website_url` (optional) - Website URL
-- `organization` (optional) - Organization name
-- `projects_limit` (optional) - Number of projects user can create
-- `extern_uid` (optional) - External UID
-- `provider` (optional) - External provider name
-- `bio` (optional) - User's biography
-- `location` (optional) - User's location
-- `admin` (optional) - User is admin - true or false (default)
-- `can_create_group` (optional) - User can create groups - true or false
-- `confirm` (optional) - Require confirmation - true (default) or false
-- `external` (optional) - Flags the user as external - true or false(default)
-- `avatar` (optional) - Image file for user's avatar
+- `email` (required) - Email
+- `password` (optional) - Password
+- `reset_password` (optional) - Send user password reset link - true or false(default)
+- `username` (required) - Username
+- `name` (required) - Name
+- `skype` (optional) - Skype ID
+- `linkedin` (optional) - LinkedIn
+- `twitter` (optional) - Twitter account
+- `website_url` (optional) - Website URL
+- `organization` (optional) - Organization name
+- `projects_limit` (optional) - Number of projects user can create
+- `extern_uid` (optional) - External UID
+- `provider` (optional) - External provider name
+- `bio` (optional) - User's biography
+- `location` (optional) - User's location
+- `admin` (optional) - User is admin - true or false (default)
+- `can_create_group` (optional) - User can create groups - true or false
+- `skip_confirmation` (optional) - Skip confirmation - true or false (default)
+- `external` (optional) - Flags the user as external - true or false(default)
+- `avatar` (optional) - Image file for user's avatar
## User modification
diff --git a/doc/articles/index.md b/doc/articles/index.md
index a4e41517d83..9d2e5956029 100644
--- a/doc/articles/index.md
+++ b/doc/articles/index.md
@@ -7,40 +7,107 @@ to provide the community with guidance on specific processes to achieve certain
They are written by members of the GitLab Team and by
[Community Writers](https://about.gitlab.com/handbook/product/technical-writing/community-writers/).
+Part of the articles listed below link to the [GitLab Blog](https://about.gitlab.com/blog/),
+where they were originally published.
+
## Authentication
-- **LDAP**
- - [How to configure LDAP with GitLab CE](how_to_configure_ldap_gitlab_ce/index.md)
- - [How to configure LDAP with GitLab EE](https://docs.gitlab.com/ee/articles/how_to_configure_ldap_gitlab_ee/)
+Explore GitLab's supported [authentications methods](../topics/authentication/index.md):
+
+| 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 |
+
+## Build, test, and deploy with GitLab CI/CD
+
+Build, test, and deploy the software you develop with [GitLab CI/CD](../ci/README.md):
+
+| 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 |
+| [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
-- [How to install Git](how_to_install_git/index.md)
+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 |
## GitLab Pages
-- **GitLab Pages from A to Z**
- - [Part 1: Static sites and GitLab Pages domains](../user/project/pages/getting_started_part_one.md)
- - [Part 2: Quick start guide - Setting up GitLab Pages](../user/project/pages/getting_started_part_two.md)
- - [Part 3: Setting Up Custom Domains - DNS Records and SSL/TLS Certificates](../user/project/pages/getting_started_part_three.md)
- - [Part 4: Creating and tweaking `.gitlab-ci.yml` for GitLab Pages](../user/project/pages/getting_started_part_four.md)
-- [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/)
-- [GitLab CI: Deployment & Environments](https://about.gitlab.com/2016/08/26/ci-deployment-and-environments/)
+Learn how to deploy a static website with [GitLab Pages](../user/project/pages/index.md#getting-started):
-## Sofware development
+| 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 |
+| **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 |
-- [In 13 minutes from Kubernetes to a complete application development tool](https://about.gitlab.com/2016/11/14/idea-to-production/)
-- [Making CI Easier with GitLab](https://about.gitlab.com/2017/07/13/making-ci-easier-with-gitlab/)
-- [Fast and Natural Continuous Integration with GitLab CI](https://about.gitlab.com/2017/05/22/fast-and-natural-continuous-integration-with-gitlab-ci/)
-- [GitLab Workflow, an Overview](https://about.gitlab.com/2016/10/25/gitlab-workflow-an-overview/)
-- [Continuous Integration, Delivery, and Deployment with GitLab](https://about.gitlab.com/2016/08/05/continuous-integration-delivery-and-deployment-with-gitlab/)
+## Install and maintain GitLab
-## Build, test, and deploy with GitLab CI/CD
+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](https://about.gitlab.com/2016/06/28/get-started-with-openshift-origin-3-and-gitlab/) | 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
+
+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 |
-**Build, test, and deploy** the software you develop with **[GitLab CI/CD](../ci/README.md)**
+## Technologies
-- [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/)
-- [Automated Debian Package Build with GitLab CI](https://about.gitlab.com/2016/10/12/automated-debian-package-build-with-gitlab-ci/)
-- [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/)
-- [Setting up GitLab CI for Android projects](https://about.gitlab.com/2016/11/30/setting-up-gitlab-ci-for-android-projects/)
-- [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/)
+| 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 |
diff --git a/doc/ci/README.md b/doc/ci/README.md
index ca7266ac68f..10ea9467942 100644
--- a/doc/ci/README.md
+++ b/doc/ci/README.md
@@ -23,6 +23,7 @@ The first steps towards your GitLab CI journey.
- [Setting up GitLab Runner For Continuous Integration](https://about.gitlab.com/2016/03/01/gitlab-runner-with-docker/)
- [GitLab CI: Deployment & environments](https://about.gitlab.com/2016/08/26/ci-deployment-and-environments/)
- **Videos:**
+ - [Demo (Streamed live on Jul 17, 2017): GitLab CI/CD Deep Dive](https://youtu.be/pBe4t1CD8Fc?t=195)
- [Demo (March, 2017): how to get started using CI/CD with GitLab](https://about.gitlab.com/2017/03/13/ci-cd-demo/)
- [Webcast (April, 2016): getting started with CI in GitLab](https://about.gitlab.com/2016/04/20/webcast-recording-and-slides-introduction-to-ci-in-gitlab/)
- **Third-party videos:**
diff --git a/doc/development/code_review.md b/doc/development/code_review.md
index 9fc6759b58a..7165b8062a7 100644
--- a/doc/development/code_review.md
+++ b/doc/development/code_review.md
@@ -21,6 +21,8 @@ There are a few rules to get your merge request accepted:
be **approved by a [UX team member and a frontend maintainer][team]**.
1. If your merge request includes UX, frontend and backend changes [^1], it must
be **approved by a [UX team member, a frontend and a backend maintainer][team]**.
+ 1. If your merge request includes a new dependency or a filesystem change, it must
+ be **approved by a [Build team member][team]**. See [how to work with the Build team][build handbook] for more details.
1. To lower the amount of merge requests maintainers need to review, you can
ask or assign any [reviewers][projects] for a first review.
1. If you need some guidance (e.g. it's your first merge request), feel free
@@ -204,3 +206,4 @@ Largely based on the [thoughtbot code review guide].
[projects]: https://about.gitlab.com/handbook/engineering/projects/
[team]: https://about.gitlab.com/team/
+[build handbook]: https://about.gitlab.com/handbook/build/handbook/build#how-to-work-with-build
diff --git a/doc/integration/slash_commands.md b/doc/integration/slash_commands.md
index 5d880ba785c..aa52b5415cf 100644
--- a/doc/integration/slash_commands.md
+++ b/doc/integration/slash_commands.md
@@ -2,7 +2,11 @@
Slash commands in Mattermost and Slack allow you to control GitLab and view GitLab content right inside your chat client, without having to leave it. For Slack, this requires a [project service configuration](../user/project/integrations/slack_slash_commands.md). Simply type the command as a message in your chat client to activate it.
-Commands are scoped to a project, with a trigger term that is specified during configuration. (We suggest you use the project name as the trigger term for simplicty and clarity.) Taking the trigger term as `project-name`, the commands are:
+Commands are scoped to a project, with a trigger term that is specified during configuration.
+
+We suggest you use the project name as the trigger term for simplicity and clarity.
+
+Taking the trigger term as `project-name`, the commands are:
| Command | Effect |
@@ -12,3 +16,18 @@ Commands are scoped to a project, with a trigger term that is specified during c
| `/project-name issue show <id>` | Shows the issue with id `<id>` |
| `/project-name issue search <query>` | Shows up to 5 issues matching `<query>` |
| `/project-name deploy <from> to <to>` | Deploy from the `<from>` environment to the `<to>` environment |
+
+## Issue commands
+
+It is possible to create new issue, display issue details and search up to 5 issues.
+
+## Deploy command
+
+In order to deploy to an environment, GitLab will try to find a deployment
+manual action in the pipeline.
+
+If there is only one action for a given environment, it is going to be triggered.
+If there is more than one action defined, GitLab will try to find an action
+which name equals the environment name we want to deploy to.
+
+Command will return an error when no matching action has been found.
diff --git a/doc/user/group/index.md b/doc/user/group/index.md
index 2691cf7d671..08da721c71d 100644
--- a/doc/user/group/index.md
+++ b/doc/user/group/index.md
@@ -42,7 +42,7 @@ In GitLab, a namespace is a unique name to be used as a user name, a group name,
For example, consider a user called John:
-1. John creates his account on GitLab.com with the username `jonh`;
+1. John creates his account on GitLab.com with the username `john`;
his profile will be accessed under `https://gitlab.example.com/john`
1. John creates a group for his team with the groupname `john-team`;
his group and its projects will be accessed under `https://gitlab.example.com/john-team`
diff --git a/doc/user/index.md b/doc/user/index.md
index 522cf932349..1281cc6e4f0 100644
--- a/doc/user/index.md
+++ b/doc/user/index.md
@@ -66,7 +66,7 @@ For more use cases please check our [Technical Articles](../articles/index.md).
## Projects
-In GitLab, you can create projects for numerous reasons, such as, host
+In GitLab, you can create [projects](project/index.md) for numerous reasons, such as, host
your code, use it as an issue tracker, collaborate on code, and continuously
build, test, and deploy your app with built-in GitLab CI/CD. Or, you can do
it all at once, from one single project.
diff --git a/doc/user/profile/index.md b/doc/user/profile/index.md
index 0e3747817d2..7d25970fcb1 100644
--- a/doc/user/profile/index.md
+++ b/doc/user/profile/index.md
@@ -23,7 +23,7 @@ On your profile page, you will see the following information:
- Personal information
- Activity stream: see your activity streamline and the history of your contributions
- Groups: [groups](../group/index.md) you're a member of
-- Contributed projects: projects you contributed to
+- Contributed projects: [projects](../project/index.md) you contributed to
- Personal projects: your personal projects (respecting the project's visibility level)
- Snippets: your personal code [snippets](../snippets.md#personal-snippets)
diff --git a/doc/user/project/index.md b/doc/user/project/index.md
new file mode 100644
index 00000000000..91a19600951
--- /dev/null
+++ b/doc/user/project/index.md
@@ -0,0 +1,107 @@
+# Projects
+
+In GitLab, you can create projects for hosting
+your codebase, use it as an issue tracker, collaborate on code, and continuously
+build, test, and deploy your app with built-in GitLab CI/CD.
+
+Your projects can be [available](../../public_access/public_access.md)
+publicly, internally, or privately, at your choice. GitLab does not limit
+the number of private projects you create.
+
+## Project's features
+
+When you create a project in GitLab, you'll have access to a large number of
+[features](https://about.gitlab.com/features/):
+
+**Issues and merge requests:**
+
+- [Issue tracker](issues/index.md): Discuss implementations with your team within issues
+ - [Issue Boards](issue_board.md): Organize and prioritize your workflow
+ - [Multiple Issue Boards](https://docs.gitlab.com/ee/user/project/issue_board.html#multiple-issue-boards) (**EES/EEP**): Allow your teams to create their own workflows (Issue Boards) for the same project
+- [Repositories](repository/index.md): Host your code in a fully
+integrated platform
+ - [Protected branches](protected_branches.md): Prevent collaborators
+ from messing with history or pushing code without review
+ - [Protected tags](protected_tags.md): Control over who has
+ permission to create tags, and prevent accidental update or deletion
+- [Merge Requests](merge_requests/index.md): Apply your branching
+strategy and get reviewed by your team
+ - [Merge Request Approvals](https://docs.gitlab.com/ee/user/project/merge_requests/merge_request_approvals.html) (**EES/EEP**): Ask for approval before
+ implementing a change
+ - [Fix merge conflicts from the UI](merge_requests/resolve_conflicts.md):
+ Your Git diff tool right from GitLab's UI
+ - [Review Apps](../../ci/review_apps/index.md): Live preview the results
+ of the changes proposed in a merge request in a per-branch basis
+- [Labels](labels.md): Organize issues and merge requests by labels
+- [Time Tracking](../../workflow/time_tracking.md): Track estimate time
+and time spent on
+ the conclusion of an issue or merge request
+- [Milestones](milestones/index.md): Work towards a target date
+- [Description templates](description_templates.md): Define context-specific
+templates for issue and merge request description fields for your project
+- [Slash commands (quick actions)](quick_actions.md): Textual shortcuts for
+common actions on issues or merge requests
+
+**GitLab CI/CD:**
+
+- [GitLab CI/CD](../../ci/README.md): GitLab's built-in [Continuous Integration, Delivery, and Deployment](https://about.gitlab.com/2016/08/05/continuous-integration-delivery-and-deployment-with-gitlab/) tool
+ - [Container Registry](container_registry.md): Build and push Docker
+ images out-of-the-box
+ - [Auto Deploy](../../ci/autodeploy/index.md): Configure GitLab CI/CD
+ to automatically set up your app's deployment
+ - [Enable and disable GitLab CI](../../ci/enable_or_disable_ci.md)
+ - [Pipelines](../../ci/pipelines.md#pipelines): Configure and visualize
+ your GitLab CI/CD pipelines from the UI
+ - [Scheduled Pipelines](pipelines/schedules.md): Schedule a pipeline
+ to start at a chosen time
+ - [Pipeline Graphs](../../ci/pipelines.md#pipeline-graphs): View your
+ entire pipeline from the UI
+ - [Job artifacts](pipelines/job_artifacts.md): Define,
+ browse, and download job artifacts
+ - [Pipeline settings](pipelines/settings.md): Set up Git strategy (choose the default way your repository is fetched from GitLab in a job),
+ timeout (defines the maximum amount of time in minutes that a job is able run), custom path for `.gitlab-ci.yml`, test coverage parsing, pipeline's visibility, and much more
+- [GitLab Pages](pages/index.md): Build, test, and deploy your static
+website with GitLab Pages
+
+**Other features:**
+
+- [Cycle Analytics](cycle_analytics.md): Review your development lifecycle
+- [Koding integration](koding.md) (not available on GitLab.com): Integrate
+with Koding to have access to a web terminal right from the GitLab UI
+- [Syntax highlighting](highlighting.md): An alternative to customize
+your code blocks, overriding GitLab's default choice of language
+
+### Project's integrations
+
+[Integrate your project](integrations/index.md) with Jira, Mattermost,
+Kubernetes, Slack, and a lot more.
+
+## New project
+
+Learn how to [create a new project](../../gitlab-basics/create-project.md) in GitLab.
+
+### Fork a project
+
+You can [fork a project](../../gitlab-basics/fork-project.md) in order to:
+
+- Collaborate on code by forking a project and creating a merge request
+from your fork to the upstream project
+- Fork a sample project to work on the top of that
+
+## 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)
+- [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)
+
+## Leave a project
+
+**Leave project** will only display on the project's dashboard
+when a project is part of a group (under a
+[group namespace](../group/index.md#namespaces)).
+If you choose to leave a project you will no longer be a project
+member, therefore, unable to contribute.
diff --git a/doc/user/project/koding.md b/doc/user/project/koding.md
index c56a1efe3c2..455e2ee47b4 100644
--- a/doc/user/project/koding.md
+++ b/doc/user/project/koding.md
@@ -1,4 +1,4 @@
-# Koding & GitLab
+# Koding integration
> [Introduced][ce-5909] in GitLab 8.11.
diff --git a/doc/user/project/repository/index.md b/doc/user/project/repository/index.md
index bb41eb41795..4b2c435a120 100644
--- a/doc/user/project/repository/index.md
+++ b/doc/user/project/repository/index.md
@@ -2,7 +2,7 @@
A [repository](https://git-scm.com/book/en/v2/Git-Basics-Getting-a-Git-Repository)
is what you use to store your codebase in GitLab and change it with version control.
-A repository is part of a project, which has a lot of other features.
+A repository is part of a [project](../index.md), which has a lot of other features.
## Create a repository
diff --git a/features/steps/project/forked_merge_requests.rb b/features/steps/project/forked_merge_requests.rb
index c6cabace25b..420ac8a695a 100644
--- a/features/steps/project/forked_merge_requests.rb
+++ b/features/steps/project/forked_merge_requests.rb
@@ -30,8 +30,8 @@ class Spinach::Features::ProjectForkedMergeRequests < Spinach::FeatureSteps
expect(@merge_request.source_project).to eq @forked_project
expect(@merge_request.source_branch).to eq "fix"
expect(@merge_request.target_branch).to eq "master"
- expect(page).to have_content @forked_project.path_with_namespace
- expect(page).to have_content @project.path_with_namespace
+ expect(page).to have_content @forked_project.full_path
+ expect(page).to have_content @project.full_path
expect(page).to have_content @merge_request.source_branch
expect(page).to have_content @merge_request.target_branch
@@ -43,10 +43,10 @@ class Spinach::Features::ProjectForkedMergeRequests < Spinach::FeatureSteps
expect(page).to have_content('Target branch')
first('.js-source-project').click
- first('.dropdown-source-project a', text: @forked_project.path_with_namespace)
+ first('.dropdown-source-project a', text: @forked_project.full_path)
first('.js-target-project').click
- first('.dropdown-target-project a', text: @project.path_with_namespace)
+ first('.dropdown-target-project a', text: @project.full_path)
first('.js-source-branch').click
wait_for_requests
@@ -81,8 +81,8 @@ class Spinach::Features::ProjectForkedMergeRequests < Spinach::FeatureSteps
expect(@merge_request.source_project).to eq @forked_project
expect(@merge_request.source_branch).to eq "fix"
expect(@merge_request.target_branch).to eq "master"
- expect(page).to have_content @forked_project.path_with_namespace
- expect(page).to have_content @project.path_with_namespace
+ expect(page).to have_content @forked_project.full_path
+ expect(page).to have_content @project.full_path
expect(page).to have_content @merge_request.source_branch
expect(page).to have_content @merge_request.target_branch
end
diff --git a/features/steps/project/redirects.rb b/features/steps/project/redirects.rb
index b2ceb8dd9a8..cbe6f93f87e 100644
--- a/features/steps/project/redirects.rb
+++ b/features/steps/project/redirects.rb
@@ -47,7 +47,7 @@ class Spinach::Features::ProjectRedirects < Spinach::FeatureSteps
step 'I should be redirected to "Community" page' do
project = Project.find_by(name: 'Community')
- expect(current_path).to eq "/#{project.path_with_namespace}"
+ expect(current_path).to eq "/#{project.full_path}"
expect(status_code).to eq 200
end
@@ -61,7 +61,7 @@ class Spinach::Features::ProjectRedirects < Spinach::FeatureSteps
step 'I should be redirected to "Enterprise" page' do
project = Project.find_by(name: 'Enterprise')
- expect(current_path).to eq "/#{project.path_with_namespace}"
+ expect(current_path).to eq "/#{project.full_path}"
expect(status_code).to eq 200
end
end
diff --git a/features/steps/project/wiki.rb b/features/steps/project/wiki.rb
index 6a478c50e5e..2b8da2a6f19 100644
--- a/features/steps/project/wiki.rb
+++ b/features/steps/project/wiki.rb
@@ -142,7 +142,7 @@ class Spinach::Features::ProjectWiki < Spinach::FeatureSteps
end
step 'I should see non-escaped link in the pages list' do
- expect(page).to have_xpath("//a[@href='/#{project.path_with_namespace}/wikis/one/two/three-test']")
+ expect(page).to have_xpath("//a[@href='/#{project.full_path}/wikis/one/two/three-test']")
end
step 'I edit the Wiki page with a path' do
diff --git a/lib/api/api.rb b/lib/api/api.rb
index 045a0db1842..ae10da2d85f 100644
--- a/lib/api/api.rb
+++ b/lib/api/api.rb
@@ -139,6 +139,7 @@ module API
mount ::API::Triggers
mount ::API::Users
mount ::API::Variables
+ mount ::API::GroupVariables
mount ::API::Version
route :any, '*path' do
diff --git a/lib/api/group_variables.rb b/lib/api/group_variables.rb
new file mode 100644
index 00000000000..f64da4ab77b
--- /dev/null
+++ b/lib/api/group_variables.rb
@@ -0,0 +1,96 @@
+module API
+ class GroupVariables < Grape::API
+ include PaginationParams
+
+ before { authenticate! }
+ before { authorize! :admin_build, user_group }
+
+ params do
+ requires :id, type: String, desc: 'The ID of a group'
+ end
+
+ resource :groups, requirements: { id: %r{[^/]+} } do
+ desc 'Get group-level variables' do
+ success Entities::Variable
+ end
+ params do
+ use :pagination
+ end
+ get ':id/variables' do
+ variables = user_group.variables
+ present paginate(variables), with: Entities::Variable
+ end
+
+ desc 'Get a specific variable from a group' do
+ success Entities::Variable
+ end
+ params do
+ requires :key, type: String, desc: 'The key of the variable'
+ end
+ get ':id/variables/:key' do
+ key = params[:key]
+ variable = user_group.variables.find_by(key: key)
+
+ return not_found!('GroupVariable') unless variable
+
+ present variable, with: Entities::Variable
+ end
+
+ desc 'Create a new variable in a group' do
+ success Entities::Variable
+ end
+ params do
+ requires :key, type: String, desc: 'The key of the variable'
+ requires :value, type: String, desc: 'The value of the variable'
+ optional :protected, type: String, desc: 'Whether the variable is protected'
+ end
+ post ':id/variables' do
+ variable_params = declared_params(include_missing: false)
+
+ variable = user_group.variables.create(variable_params)
+
+ if variable.valid?
+ present variable, with: Entities::Variable
+ else
+ render_validation_error!(variable)
+ end
+ end
+
+ desc 'Update an existing variable from a group' do
+ success Entities::Variable
+ end
+ params do
+ optional :key, type: String, desc: 'The key of the variable'
+ optional :value, type: String, desc: 'The value of the variable'
+ optional :protected, type: String, desc: 'Whether the variable is protected'
+ end
+ put ':id/variables/:key' do
+ variable = user_group.variables.find_by(key: params[:key])
+
+ return not_found!('GroupVariable') unless variable
+
+ variable_params = declared_params(include_missing: false).except(:key)
+
+ if variable.update(variable_params)
+ present variable, with: Entities::Variable
+ else
+ render_validation_error!(variable)
+ end
+ end
+
+ desc 'Delete an existing variable from a group' do
+ success Entities::Variable
+ end
+ params do
+ requires :key, type: String, desc: 'The key of the variable'
+ end
+ delete ':id/variables/:key' do
+ variable = user_group.variables.find_by(key: params[:key])
+ not_found!('GroupVariable') unless variable
+
+ status 204
+ variable.destroy
+ end
+ end
+ end
+end
diff --git a/lib/api/runner.rb b/lib/api/runner.rb
index 405d25ca3c1..88fc62d33df 100644
--- a/lib/api/runner.rb
+++ b/lib/api/runner.rb
@@ -90,7 +90,7 @@ module API
if result.valid?
if result.build
Gitlab::Metrics.add_event(:build_found,
- project: result.build.project.path_with_namespace)
+ project: result.build.project.full_path)
present result.build, with: Entities::JobRequest::Response
else
Gitlab::Metrics.add_event(:build_not_found)
@@ -119,7 +119,7 @@ module API
job.trace.set(params[:trace]) if params[:trace]
Gitlab::Metrics.add_event(:update_build,
- project: job.project.path_with_namespace)
+ project: job.project.full_path)
case params[:state].to_s
when 'success'
diff --git a/lib/backup/repository.rb b/lib/backup/repository.rb
index a1685c77916..88821ae56e0 100644
--- a/lib/backup/repository.rb
+++ b/lib/backup/repository.rb
@@ -7,7 +7,7 @@ module Backup
prepare
Project.find_each(batch_size: 1000) do |project|
- progress.print " * #{project.path_with_namespace} ... "
+ progress.print " * #{project.full_path} ... "
path_to_project_repo = path_to_repo(project)
path_to_project_bundle = path_to_bundle(project)
@@ -42,7 +42,7 @@ module Backup
path_to_wiki_bundle = path_to_bundle(wiki)
if File.exist?(path_to_wiki_repo)
- progress.print " * #{wiki.path_with_namespace} ... "
+ progress.print " * #{wiki.full_path} ... "
if empty_repo?(wiki)
progress.puts " [SKIPPED]".color(:cyan)
else
@@ -71,11 +71,11 @@ module Backup
end
Project.find_each(batch_size: 1000) do |project|
- progress.print " * #{project.path_with_namespace} ... "
+ progress.print " * #{project.full_path} ... "
path_to_project_repo = path_to_repo(project)
path_to_project_bundle = path_to_bundle(project)
- project.ensure_dir_exist
+ project.ensure_storage_path_exist
cmd = if File.exist?(path_to_project_bundle)
%W(#{Gitlab.config.git.bin_path} clone --bare #{path_to_project_bundle} #{path_to_project_repo})
@@ -104,7 +104,7 @@ module Backup
path_to_wiki_bundle = path_to_bundle(wiki)
if File.exist?(path_to_wiki_bundle)
- progress.print " * #{wiki.path_with_namespace} ... "
+ progress.print " * #{wiki.full_path} ... "
# If a wiki bundle exists, first remove the empty repo
# that was initialized with ProjectWiki.new() and then
@@ -142,11 +142,11 @@ module Backup
end
def path_to_bundle(project)
- File.join(backup_repos_path, project.path_with_namespace + '.bundle')
+ File.join(backup_repos_path, project.disk_path + '.bundle')
end
def path_to_tars(project, dir = nil)
- path = File.join(backup_repos_path, project.path_with_namespace)
+ path = File.join(backup_repos_path, project.disk_path)
if dir
File.join(path, "#{dir}.tar")
@@ -185,13 +185,14 @@ module Backup
def progress_warn(project, cmd, output)
progress.puts "[WARNING] Executing #{cmd}".color(:orange)
- progress.puts "Ignoring error on #{project.path_with_namespace} - #{output}".color(:orange)
+ progress.puts "Ignoring error on #{project.full_path} - #{output}".color(:orange)
end
def empty_repo?(project_or_wiki)
+ project_or_wiki.repository.expire_exists_cache # protect backups from stale cache
project_or_wiki.repository.empty_repo?
rescue => e
- progress.puts "Ignoring repository error and continuing backing up project: #{project_or_wiki.path_with_namespace} - #{e.message}".color(:orange)
+ progress.puts "Ignoring repository error and continuing backing up project: #{project_or_wiki.full_path} - #{e.message}".color(:orange)
false
end
diff --git a/lib/banzai/filter/abstract_reference_filter.rb b/lib/banzai/filter/abstract_reference_filter.rb
index 7a262dd025c..685b43605ae 100644
--- a/lib/banzai/filter/abstract_reference_filter.rb
+++ b/lib/banzai/filter/abstract_reference_filter.rb
@@ -259,7 +259,7 @@ module Banzai
found = []
projects.each do |project|
- ref = project.path_with_namespace
+ ref = project.full_path
get_or_set_cache(cache, ref) { project }
found << ref
end
@@ -277,7 +277,7 @@ module Banzai
end
def current_project_path
- @current_project_path ||= project.path_with_namespace
+ @current_project_path ||= project.full_path
end
def current_project_namespace_path
diff --git a/lib/banzai/filter/relative_link_filter.rb b/lib/banzai/filter/relative_link_filter.rb
index c2fed57a0d8..758f15c8a67 100644
--- a/lib/banzai/filter/relative_link_filter.rb
+++ b/lib/banzai/filter/relative_link_filter.rb
@@ -51,7 +51,7 @@ module Banzai
uri.path = [
relative_url_root,
- context[:project].path_with_namespace,
+ context[:project].full_path,
uri_type(file_path),
Addressable::URI.escape(ref),
Addressable::URI.escape(file_path)
diff --git a/lib/banzai/filter/upload_link_filter.rb b/lib/banzai/filter/upload_link_filter.rb
index 45bb66dc99f..09844931be5 100644
--- a/lib/banzai/filter/upload_link_filter.rb
+++ b/lib/banzai/filter/upload_link_filter.rb
@@ -28,7 +28,7 @@ module Banzai
end
def build_url(uri)
- File.join(Gitlab.config.gitlab.url, project.path_with_namespace, uri)
+ File.join(Gitlab.config.gitlab.url, project.full_path, uri)
end
def project
diff --git a/lib/ci/api/builds.rb b/lib/ci/api/builds.rb
index e2e91ce99cd..79058c02ce5 100644
--- a/lib/ci/api/builds.rb
+++ b/lib/ci/api/builds.rb
@@ -29,7 +29,7 @@ module Ci
if result.valid?
if result.build
Gitlab::Metrics.add_event(:build_found,
- project: result.build.project.path_with_namespace)
+ project: result.build.project.full_path)
present result.build, with: Entities::BuildDetails
else
@@ -64,7 +64,7 @@ module Ci
build.trace.set(params[:trace]) if params[:trace]
Gitlab::Metrics.add_event(:update_build,
- project: build.project.path_with_namespace)
+ project: build.project.full_path)
case params[:state].to_s
when 'success'
diff --git a/lib/github/import.rb b/lib/github/import.rb
index ff5d7db2705..cea4be5460b 100644
--- a/lib/github/import.rb
+++ b/lib/github/import.rb
@@ -93,7 +93,7 @@ module Github
def fetch_wiki_repository
wiki_url = "https://#{options.fetch(:token)}@github.com/#{repo}.wiki.git"
- wiki_path = "#{project.path_with_namespace}.wiki"
+ wiki_path = "#{project.full_path}.wiki"
unless project.wiki.repository_exists?
gitlab_shell.import_repository(project.repository_storage_path, wiki_path, wiki_url)
diff --git a/lib/gitlab/bitbucket_import/importer.rb b/lib/gitlab/bitbucket_import/importer.rb
index 5a6d9ae99a0..28bbf3b384e 100644
--- a/lib/gitlab/bitbucket_import/importer.rb
+++ b/lib/gitlab/bitbucket_import/importer.rb
@@ -61,7 +61,7 @@ module Gitlab
def import_wiki
return if project.wiki.repository_exists?
- path_with_namespace = "#{project.path_with_namespace}.wiki"
+ path_with_namespace = "#{project.full_path}.wiki"
import_url = project.import_url.sub(/\.git\z/, ".git/wiki")
gitlab_shell.import_repository(project.repository_storage_path, path_with_namespace, import_url)
rescue StandardError => e
diff --git a/lib/gitlab/diff/file.rb b/lib/gitlab/diff/file.rb
index d2863a4da71..6d7de52cb80 100644
--- a/lib/gitlab/diff/file.rb
+++ b/lib/gitlab/diff/file.rb
@@ -79,13 +79,6 @@ module Gitlab
@new_content_sha = refs&.head_sha
end
- def new_content_commit
- return @new_content_commit if defined?(@new_content_commit)
-
- sha = new_content_commit
- @new_content_commit = repository.commit(sha) if sha
- end
-
def old_content_sha
return if new_file?
return @old_content_sha if defined?(@old_content_sha)
@@ -94,13 +87,6 @@ module Gitlab
@old_content_sha = refs&.base_sha
end
- def old_content_commit
- return @old_content_commit if defined?(@old_content_commit)
-
- sha = old_content_sha
- @old_content_commit = repository.commit(sha) if sha
- end
-
def new_blob
return @new_blob if defined?(@new_blob)
@@ -123,10 +109,6 @@ module Gitlab
new_content_sha || old_content_sha
end
- def content_commit
- new_content_commit || old_content_commit
- end
-
def blob
new_blob || old_blob
end
diff --git a/lib/gitlab/ee_compat_check.rb b/lib/gitlab/ee_compat_check.rb
index 85e6db0a689..72d7d4f84d1 100644
--- a/lib/gitlab/ee_compat_check.rb
+++ b/lib/gitlab/ee_compat_check.rb
@@ -181,8 +181,6 @@ module Gitlab
end
def find_merge_base_with_master(branch:)
- return if merge_base_found?
-
# Start with (Math.exp(3).to_i = 20) until (Math.exp(6).to_i = 403)
# In total we go (20 + 54 + 148 + 403 = 625) commits deeper
depth = 20
diff --git a/lib/gitlab/email/message/repository_push.rb b/lib/gitlab/email/message/repository_push.rb
index dd1d9dcd555..cd9d3a6483f 100644
--- a/lib/gitlab/email/message/repository_push.rb
+++ b/lib/gitlab/email/message/repository_push.rb
@@ -117,7 +117,7 @@ module Gitlab
def subject
subject_text = '[Git]'
- subject_text << "[#{project.path_with_namespace}]"
+ subject_text << "[#{project.full_path}]"
subject_text << "[#{ref_name}]" if @action == :push
subject_text << ' '
diff --git a/lib/gitlab/git/repository.rb b/lib/gitlab/git/repository.rb
index a3bc79109f8..88529ba2c47 100644
--- a/lib/gitlab/git/repository.rb
+++ b/lib/gitlab/git/repository.rb
@@ -636,6 +636,33 @@ module Gitlab
@attributes.attributes(path)
end
+ def languages(ref = nil)
+ Gitlab::GitalyClient.migrate(:commit_languages) do |is_enabled|
+ if is_enabled
+ gitaly_commit_client.languages(ref)
+ else
+ ref ||= rugged.head.target_id
+ languages = Linguist::Repository.new(rugged, ref).languages
+ total = languages.map(&:last).sum
+
+ languages = languages.map do |language|
+ name, share = language
+ color = Linguist::Language[name].color || "##{Digest::SHA256.hexdigest(name)[0...6]}"
+ {
+ value: (share.to_f * 100 / total).round(2),
+ label: name,
+ color: color,
+ highlight: color
+ }
+ end
+
+ languages.sort do |x, y|
+ y[:value] <=> x[:value]
+ end
+ end
+ end
+ end
+
def gitaly_repository
Gitlab::GitalyClient::Util.repository(@storage, @relative_path)
end
diff --git a/lib/gitlab/gitaly_client/commit_service.rb b/lib/gitlab/gitaly_client/commit_service.rb
index c6e52b530b3..a834781b1f1 100644
--- a/lib/gitlab/gitaly_client/commit_service.rb
+++ b/lib/gitlab/gitaly_client/commit_service.rb
@@ -118,6 +118,13 @@ module Gitlab
consume_commits_response(response)
end
+ def languages(ref = nil)
+ request = Gitaly::CommitLanguagesRequest.new(repository: @gitaly_repo, revision: ref || '')
+ response = GitalyClient.call(@repository.storage, :commit_service, :commit_languages, request)
+
+ response.languages.map { |l| { value: l.share.round(2), label: l.name, color: l.color, highlight: l.color } }
+ end
+
private
def commit_diff_request_params(commit, options = {})
diff --git a/lib/gitlab/gitaly_client/repository_service.rb b/lib/gitlab/gitaly_client/repository_service.rb
index f5d84ea8762..13e75b256a7 100644
--- a/lib/gitlab/gitaly_client/repository_service.rb
+++ b/lib/gitlab/gitaly_client/repository_service.rb
@@ -4,12 +4,28 @@ module Gitlab
def initialize(repository)
@repository = repository
@gitaly_repo = repository.gitaly_repository
+ @storage = repository.storage
end
def exists?
request = Gitaly::RepositoryExistsRequest.new(repository: @gitaly_repo)
- GitalyClient.call(@repository.storage, :repository_service, :exists, request).exists
+ GitalyClient.call(@storage, :repository_service, :exists, request).exists
+ end
+
+ def garbage_collect(create_bitmap)
+ request = Gitaly::GarbageCollectRequest.new(repository: @gitaly_repo, create_bitmap: create_bitmap)
+ GitalyClient.call(@storage, :repository_service, :garbage_collect, request)
+ end
+
+ def repack_full(create_bitmap)
+ request = Gitaly::RepackFullRequest.new(repository: @gitaly_repo, create_bitmap: create_bitmap)
+ GitalyClient.call(@storage, :repository_service, :repack_full, request)
+ end
+
+ def repack_incremental
+ request = Gitaly::RepackIncrementalRequest.new(repository: @gitaly_repo)
+ GitalyClient.call(@storage, :repository_service, :repack_incremental, request)
end
end
end
diff --git a/lib/gitlab/github_import/importer.rb b/lib/gitlab/github_import/importer.rb
index a8c0b47e786..266b1a6fece 100644
--- a/lib/gitlab/github_import/importer.rb
+++ b/lib/gitlab/github_import/importer.rb
@@ -254,7 +254,7 @@ module Gitlab
def import_wiki
unless project.wiki.repository_exists?
wiki = WikiFormatter.new(project)
- gitlab_shell.import_repository(project.repository_storage_path, wiki.path_with_namespace, wiki.import_url)
+ gitlab_shell.import_repository(project.repository_storage_path, wiki.disk_path, wiki.import_url)
end
rescue Gitlab::Shell::Error => e
# GitHub error message when the wiki repo has not been created,
diff --git a/lib/gitlab/github_import/wiki_formatter.rb b/lib/gitlab/github_import/wiki_formatter.rb
index 6c592ff469c..0396122eeb9 100644
--- a/lib/gitlab/github_import/wiki_formatter.rb
+++ b/lib/gitlab/github_import/wiki_formatter.rb
@@ -7,8 +7,8 @@ module Gitlab
@project = project
end
- def path_with_namespace
- "#{project.path_with_namespace}.wiki"
+ def disk_path
+ "#{project.disk_path}.wiki"
end
def import_url
diff --git a/lib/gitlab/import_export/repo_restorer.rb b/lib/gitlab/import_export/repo_restorer.rb
index c824d3ea9fc..32ca2809b2f 100644
--- a/lib/gitlab/import_export/repo_restorer.rb
+++ b/lib/gitlab/import_export/repo_restorer.rb
@@ -13,7 +13,7 @@ module Gitlab
def restore
return true unless File.exist?(@path_to_bundle)
- gitlab_shell.import_repository(@project.repository_storage_path, @project.path_with_namespace, @path_to_bundle)
+ gitlab_shell.import_repository(@project.repository_storage_path, @project.disk_path, @path_to_bundle)
rescue => e
@shared.error(e)
false
diff --git a/lib/gitlab/import_export/uploads_saver.rb b/lib/gitlab/import_export/uploads_saver.rb
index 62a2553675c..f9ae5079d7c 100644
--- a/lib/gitlab/import_export/uploads_saver.rb
+++ b/lib/gitlab/import_export/uploads_saver.rb
@@ -24,6 +24,7 @@ module Gitlab
end
def uploads_path
+ # TODO: decide what to do with uploads. We will use UUIDs here too?
File.join(Rails.root.join('public/uploads'), @project.path_with_namespace)
end
end
diff --git a/lib/gitlab/ldap/authentication.rb b/lib/gitlab/ldap/authentication.rb
index 4745311402c..ed1de73f8c6 100644
--- a/lib/gitlab/ldap/authentication.rb
+++ b/lib/gitlab/ldap/authentication.rb
@@ -42,7 +42,7 @@ module Gitlab
end
def adapter
- OmniAuth::LDAP::Adaptor.new(config.options.symbolize_keys)
+ OmniAuth::LDAP::Adaptor.new(config.omniauth_options)
end
def config
diff --git a/lib/gitlab/quick_actions/dsl.rb b/lib/gitlab/quick_actions/dsl.rb
index a4a97236ffc..536765305e1 100644
--- a/lib/gitlab/quick_actions/dsl.rb
+++ b/lib/gitlab/quick_actions/dsl.rb
@@ -105,9 +105,32 @@ module Gitlab
# # Awesome code block
# end
def command(*command_names, &block)
+ define_command(CommandDefinition, *command_names, &block)
+ end
+
+ # Registers a new substitution which is recognizable from body of email or
+ # comment.
+ # It accepts aliases and takes a block with the formatted content.
+ #
+ # Example:
+ #
+ # command :my_substitution, :alias_for_my_substitution do |text|
+ # "#{text} MY AWESOME SUBSTITUTION"
+ # end
+ def substitution(*substitution_names, &block)
+ define_command(SubstitutionDefinition, *substitution_names, &block)
+ end
+
+ def definition_by_name(name)
+ command_definitions_by_name[name.to_sym]
+ end
+
+ private
+
+ def define_command(klass, *command_names, &block)
name, *aliases = command_names
- definition = CommandDefinition.new(
+ definition = klass.new(
name,
aliases: aliases,
description: @description,
@@ -130,10 +153,6 @@ module Gitlab
@condition_block = nil
@parse_params_block = nil
end
-
- def definition_by_name(name)
- command_definitions_by_name[name.to_sym]
- end
end
end
end
diff --git a/lib/gitlab/quick_actions/extractor.rb b/lib/gitlab/quick_actions/extractor.rb
index 09576be7156..3ebfa3bd4b8 100644
--- a/lib/gitlab/quick_actions/extractor.rb
+++ b/lib/gitlab/quick_actions/extractor.rb
@@ -46,6 +46,8 @@ module Gitlab
end
end
+ content, commands = perform_substitutions(content, commands)
+
[content.strip, commands]
end
@@ -110,6 +112,26 @@ module Gitlab
}mx
end
+ def perform_substitutions(content, commands)
+ return unless content
+
+ substitution_definitions = self.command_definitions.select do |definition|
+ definition.is_a?(Gitlab::QuickActions::SubstitutionDefinition)
+ end
+
+ substitution_definitions.each do |substitution|
+ match_data = substitution.match(content)
+ if match_data
+ command = [substitution.name.to_s]
+ command << match_data[1] unless match_data[1].empty?
+ commands << command
+ end
+ content = substitution.perform_substitution(self, content)
+ end
+
+ [content, commands]
+ end
+
def command_names(opts)
command_definitions.flat_map do |command|
next if command.noop?
diff --git a/lib/gitlab/quick_actions/substitution_definition.rb b/lib/gitlab/quick_actions/substitution_definition.rb
new file mode 100644
index 00000000000..032c49ed159
--- /dev/null
+++ b/lib/gitlab/quick_actions/substitution_definition.rb
@@ -0,0 +1,24 @@
+module Gitlab
+ module QuickActions
+ class SubstitutionDefinition < CommandDefinition
+ # noop?=>true means these won't get extracted or removed by Gitlab::QuickActions::Extractor#extract_commands
+ # QuickActions::InterpretService#perform_substitutions handles them separately
+ def noop?
+ true
+ end
+
+ def match(content)
+ content.match %r{^/#{all_names.join('|')} ?(.*)$}
+ end
+
+ def perform_substitution(context, content)
+ return unless content
+
+ all_names.each do |a_name|
+ content.gsub!(%r{/#{a_name} ?(.*)$}, execute_block(action_block, context, '\1'))
+ end
+ content
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/slash_commands/deploy.rb b/lib/gitlab/slash_commands/deploy.rb
index e71eb15d604..93e00ab75a1 100644
--- a/lib/gitlab/slash_commands/deploy.rb
+++ b/lib/gitlab/slash_commands/deploy.rb
@@ -21,29 +21,34 @@ module Gitlab
from = match[:from]
to = match[:to]
- actions = find_actions(from, to)
+ action = find_action(from, to)
- if actions.none?
- Gitlab::SlashCommands::Presenters::Deploy.new(nil).no_actions
- elsif actions.one?
- action = play!(from, to, actions.first)
- Gitlab::SlashCommands::Presenters::Deploy.new(action).present(from, to)
+ if action.nil?
+ Gitlab::SlashCommands::Presenters::Deploy
+ .new(action).action_not_found
else
- Gitlab::SlashCommands::Presenters::Deploy.new(actions).too_many_actions
+ deployment = action.play(current_user)
+
+ Gitlab::SlashCommands::Presenters::Deploy
+ .new(deployment).present(from, to)
end
end
private
- def play!(from, to, action)
- action.play(current_user)
- end
-
- def find_actions(from, to)
+ def find_action(from, to)
environment = project.environments.find_by(name: from)
- return [] unless environment
+ return unless environment
- environment.actions_for(to).select(&:starts_environment?)
+ actions = environment.actions_for(to).select do |action|
+ action.starts_environment?
+ end
+
+ if actions.many?
+ actions.find { |action| action.name == to.to_s }
+ else
+ actions.first
+ end
end
end
end
diff --git a/lib/gitlab/slash_commands/presenters/deploy.rb b/lib/gitlab/slash_commands/presenters/deploy.rb
index b8dc77bd37b..ebae0f57f9b 100644
--- a/lib/gitlab/slash_commands/presenters/deploy.rb
+++ b/lib/gitlab/slash_commands/presenters/deploy.rb
@@ -3,17 +3,14 @@ module Gitlab
module Presenters
class Deploy < Presenters::Base
def present(from, to)
- message = "Deployment started from #{from} to #{to}. [Follow its progress](#{resource_url})."
+ message = "Deployment started from #{from} to #{to}. " \
+ "[Follow its progress](#{resource_url})."
in_channel_response(text: message)
end
- def no_actions
- ephemeral_response(text: "No action found to be executed")
- end
-
- def too_many_actions
- ephemeral_response(text: "Too many actions defined")
+ def action_not_found
+ ephemeral_response(text: "Couldn't find a deployment manual action.")
end
end
end
diff --git a/lib/support/init.d/gitlab b/lib/support/init.d/gitlab
index c5f93336346..2f2de083dc0 100755
--- a/lib/support/init.d/gitlab
+++ b/lib/support/init.d/gitlab
@@ -291,7 +291,7 @@ start_gitlab() {
fi
if [ "$gitlab_workhorse_status" = "0" ]; then
- echo "The GitLab Workhorse is already running with pid $spid, not restarting"
+ echo "The GitLab Workhorse is already running with pid $hpid, not restarting"
else
# No need to remove a socket, gitlab-workhorse does this itself.
# Because gitlab-workhorse has multiple executables we need to fix
@@ -313,7 +313,7 @@ start_gitlab() {
if [ "$gitlab_pages_enabled" = true ]; then
if [ "$gitlab_pages_status" = "0" ]; then
- echo "The GitLab Pages is already running with pid $spid, not restarting"
+ echo "The GitLab Pages is already running with pid $gppid, not restarting"
else
$app_root/bin/daemon_with_pidfile $gitlab_pages_pid_path \
$gitlab_pages_dir/gitlab-pages $gitlab_pages_options \
@@ -421,7 +421,7 @@ print_status() {
fi
if [ "$gitlab_pages_enabled" = true ]; then
if [ "$gitlab_pages_status" = "0" ]; then
- echo "The GitLab Pages with pid $mpid is running."
+ echo "The GitLab Pages with pid $gppid is running."
else
printf "The GitLab Pages is \033[31mnot running\033[0m.\n"
fi
diff --git a/lib/tasks/gitlab/check.rake b/lib/tasks/gitlab/check.rake
index 858f1cd7b34..dbb3b827b9a 100644
--- a/lib/tasks/gitlab/check.rake
+++ b/lib/tasks/gitlab/check.rake
@@ -527,7 +527,7 @@ namespace :gitlab do
repo_dirs = user.authorized_projects.map do |p|
File.join(
p.repository_storage_path,
- "#{p.path_with_namespace}.git"
+ "#{p.disk_path}.git"
)
end
diff --git a/lib/tasks/gitlab/gitaly.rake b/lib/tasks/gitlab/gitaly.rake
index 9df07ea8d83..680e76af471 100644
--- a/lib/tasks/gitlab/gitaly.rake
+++ b/lib/tasks/gitlab/gitaly.rake
@@ -19,7 +19,10 @@ namespace :gitlab do
Dir.chdir(args.dir) do
create_gitaly_configuration
- Bundler.with_original_env { run_command!([command]) }
+ # In CI we run scripts/gitaly-test-build instead of this command
+ unless ENV['CI'].present?
+ Bundler.with_original_env { run_command!(%w[/usr/bin/env -u BUNDLE_GEMFILE] + [command]) }
+ end
end
end
@@ -30,7 +33,9 @@ namespace :gitlab do
puts "# Gitaly storage configuration generated from #{Gitlab.config.source} on #{Time.current.to_s(:long)}"
puts "# This is in TOML format suitable for use in Gitaly's config.toml file."
- puts gitaly_configuration_toml
+ # Exclude gitaly-ruby configuration because that depends on the gitaly
+ # installation directory.
+ puts gitaly_configuration_toml(gitaly_ruby: false)
end
private
@@ -41,7 +46,7 @@ namespace :gitlab do
# only generate a configuration for the most common and simplest case: when
# we have exactly one Gitaly process and we are sure it is running locally
# because it uses a Unix socket.
- def gitaly_configuration_toml
+ def gitaly_configuration_toml(gitaly_ruby: true)
storages = []
address = nil
@@ -60,6 +65,7 @@ namespace :gitlab do
end
config = { socket_path: address.sub(%r{\Aunix:}, ''), storage: storages }
config[:auth] = { token: 'secret' } if Rails.env.test?
+ config[:'gitaly-ruby'] = { dir: File.join(Dir.pwd, 'ruby') } if gitaly_ruby
TOML.dump(config)
end
diff --git a/lib/tasks/gitlab/list_repos.rake b/lib/tasks/gitlab/list_repos.rake
index ffcc76e5498..b732db9db6e 100644
--- a/lib/tasks/gitlab/list_repos.rake
+++ b/lib/tasks/gitlab/list_repos.rake
@@ -9,7 +9,7 @@ namespace :gitlab do
scope = scope.where('id IN (?) OR namespace_id in (?)', project_ids, namespace_ids)
end
scope.find_each do |project|
- base = File.join(project.repository_storage_path, project.path_with_namespace)
+ base = File.join(project.repository_storage_path, project.disk_path)
puts base + '.git'
puts base + '.wiki.git'
end
diff --git a/lib/tasks/gitlab/shell.rake b/lib/tasks/gitlab/shell.rake
index ee2cdcdea1b..42825f29e32 100644
--- a/lib/tasks/gitlab/shell.rake
+++ b/lib/tasks/gitlab/shell.rake
@@ -80,7 +80,7 @@ namespace :gitlab do
print '-'
else
if Gitlab::Shell.new.add_repository(project.repository_storage_path,
- project.path_with_namespace)
+ project.disk_path)
print '.'
else
print 'F'
diff --git a/locale/pt_BR/gitlab.po b/locale/pt_BR/gitlab.po
index 78cf6d2dacc..ee00b816b84 100644
--- a/locale/pt_BR/gitlab.po
+++ b/locale/pt_BR/gitlab.po
@@ -413,14 +413,14 @@ msgstr[0] "Fork"
msgstr[1] "Forks"
msgid "ForkedFromProjectPath|Forked from"
-msgstr "Forked de"
+msgstr "Fork criado a partir de"
msgid "From issue creation until deploy to production"
msgstr "Da abertura de tarefas até a implantação para a produção"
msgid "From merge request merge until deploy to production"
msgstr ""
-"Da aceitação da solicitação de incorporação até a implantação em produção"
+"Do merge request até a implantação em produção"
msgid "Go to your fork"
msgstr "Ir para seu fork"
@@ -576,7 +576,7 @@ msgid "NotificationEvent|Successful pipeline"
msgstr "Pipeline bem sucedido"
msgid "NotificationLevel|Custom"
-msgstr "Personalizar"
+msgstr "Personalizado"
msgid "NotificationLevel|Disabled"
msgstr "Desabilitado"
@@ -881,9 +881,9 @@ msgid ""
"the issue to a milestone, or add the issue to a list on your Issue Board. "
"Begin creating issues to see data for this stage."
msgstr ""
-"A etapa de relatos mostra o tempo que leva desde a criação de uma issue até "
-"sua atribuição a um marco, ou sua adição a uma lista no seu Issue Board. "
-"Comece a criar issues para ver dados para esta etapa."
+"A etapa de planejamento mostra o tempo que se leva desde a criação de uma "
+"issue até sua atribuição à um milestone, ou sua adição a uma lista no seu "
+"Issue Board. Comece a criar issues para ver dados para esta etapa."
msgid "The phase of the development lifecycle."
msgstr "A fase do ciclo de vida do desenvolvimento."
@@ -974,7 +974,7 @@ msgid "Time before an issue gets scheduled"
msgstr "Tempo até que uma issue seja agendada"
msgid "Time before an issue starts implementation"
-msgstr "Tempo até que uma issue comece a ser implementado"
+msgstr "Tempo até que uma issue comece a ser implementada"
msgid "Time between merge request creation and merge/close"
msgstr ""
diff --git a/rubocop/cop/migration/add_column_with_default_to_large_table.rb b/rubocop/cop/migration/add_column_with_default_to_large_table.rb
index 87788b0d9c2..fb363f95b56 100644
--- a/rubocop/cop/migration/add_column_with_default_to_large_table.rb
+++ b/rubocop/cop/migration/add_column_with_default_to_large_table.rb
@@ -20,6 +20,7 @@ module RuboCop
'necessary'.freeze
LARGE_TABLES = %i[
+ ci_pipelines
ci_builds
events
issues
diff --git a/scripts/gitaly-test-build b/scripts/gitaly-test-build
new file mode 100755
index 00000000000..44d314009e2
--- /dev/null
+++ b/scripts/gitaly-test-build
@@ -0,0 +1,10 @@
+#!/usr/bin/env ruby
+
+# This script assumes tmp/tests/gitaly already contains the correct
+# Gitaly version. We just have to compile it and run its 'bundle
+# install'. We have this separate script for that because weird things
+# were happening in CI when we have a 'bundle exec' process that later
+# called 'bundle install' using a different Gemfile, as happens with
+# gitlab-ce and gitaly.
+
+abort 'gitaly build failed' unless system('make', chdir: 'tmp/tests/gitaly')
diff --git a/scripts/gitaly-test-spawn b/scripts/gitaly-test-spawn
new file mode 100755
index 00000000000..dd603eec7f6
--- /dev/null
+++ b/scripts/gitaly-test-spawn
@@ -0,0 +1,7 @@
+#!/usr/bin/env ruby
+
+gitaly_dir = 'tmp/tests/gitaly'
+args = %W[#{gitaly_dir}/gitaly #{gitaly_dir}/config.toml]
+
+# Print the PID of the spawned process
+puts spawn(*args, [:out, :err] => 'log/gitaly-test.log')
diff --git a/scripts/lint-conflicts.sh b/scripts/lint-conflicts.sh
new file mode 100755
index 00000000000..f3877600c8c
--- /dev/null
+++ b/scripts/lint-conflicts.sh
@@ -0,0 +1,5 @@
+#!/bin/sh
+
+output=`git ls-files -z | grep -zvE '\.(rb|js|haml)$' | xargs -0n1 grep -HEn '^<<<<<<< '`
+echo $output
+test -z "$output"
diff --git a/scripts/static-analysis b/scripts/static-analysis
index 6d35684b97f..e4f80e8fc6f 100755
--- a/scripts/static-analysis
+++ b/scripts/static-analysis
@@ -11,7 +11,8 @@ tasks = [
%w[bundle exec rake brakeman],
%w[bundle exec license_finder],
%w[yarn run eslint],
- %w[bundle exec rubocop --require rubocop-rspec]
+ %w[bundle exec rubocop --require rubocop-rspec],
+ %w[scripts/lint-conflicts.sh]
]
failed_tasks = tasks.reduce({}) do |failures, task|
diff --git a/spec/controllers/projects/blob_controller_spec.rb b/spec/controllers/projects/blob_controller_spec.rb
index 02bbc48dc59..19ee5e3bb7e 100644
--- a/spec/controllers/projects/blob_controller_spec.rb
+++ b/spec/controllers/projects/blob_controller_spec.rb
@@ -48,7 +48,7 @@ describe Projects::BlobController do
let(:id) { 'markdown/doc' }
it 'redirects' do
expect(subject)
- .to redirect_to("/#{project.path_with_namespace}/tree/markdown/doc")
+ .to redirect_to("/#{project.full_path}/tree/markdown/doc")
end
end
end
diff --git a/spec/controllers/projects/branches_controller_spec.rb b/spec/controllers/projects/branches_controller_spec.rb
index 9cd4e9dbf84..24defb4d657 100644
--- a/spec/controllers/projects/branches_controller_spec.rb
+++ b/spec/controllers/projects/branches_controller_spec.rb
@@ -33,7 +33,7 @@ describe Projects::BranchesController do
let(:ref) { "master" }
it 'redirects' do
expect(subject)
- .to redirect_to("/#{project.path_with_namespace}/tree/merge_branch")
+ .to redirect_to("/#{project.full_path}/tree/merge_branch")
end
end
@@ -42,7 +42,7 @@ describe Projects::BranchesController do
let(:ref) { "master" }
it 'redirects' do
expect(subject)
- .to redirect_to("/#{project.path_with_namespace}/tree/alert('merge');")
+ .to redirect_to("/#{project.full_path}/tree/alert('merge');")
end
end
@@ -82,7 +82,7 @@ describe Projects::BranchesController do
issue_iid: issue.iid
expect(subject)
- .to redirect_to("/#{project.path_with_namespace}/tree/1-feature-branch")
+ .to redirect_to("/#{project.full_path}/tree/1-feature-branch")
end
it 'posts a system note' do
diff --git a/spec/controllers/projects/graphs_controller_spec.rb b/spec/controllers/projects/graphs_controller_spec.rb
index e0de62e4454..5af03ae118c 100644
--- a/spec/controllers/projects/graphs_controller_spec.rb
+++ b/spec/controllers/projects/graphs_controller_spec.rb
@@ -24,37 +24,4 @@ describe Projects::GraphsController do
expect(response).to redirect_to action: :charts
end
end
-
- describe 'GET charts' do
- let(:linguist_repository) do
- double(languages: {
- 'Ruby' => 1000,
- 'CoffeeScript' => 350,
- 'NSIS' => 15
- })
- end
-
- let(:expected_values) do
- nsis_color = "##{Digest::SHA256.hexdigest('NSIS')[0...6]}"
- [
- # colors from Linguist:
- { label: "Ruby", color: "#701516", highlight: "#701516" },
- { label: "CoffeeScript", color: "#244776", highlight: "#244776" },
- # colors from SHA256 fallback:
- { label: "NSIS", color: nsis_color, highlight: nsis_color }
- ]
- end
-
- before do
- allow(Linguist::Repository).to receive(:new).and_return(linguist_repository)
- end
-
- it 'sets the correct colour according to language' do
- get(:charts, namespace_id: project.namespace, project_id: project, id: 'master')
-
- expected_values.each do |val|
- expect(assigns(:languages)).to include(a_hash_including(val))
- end
- end
- end
end
diff --git a/spec/controllers/projects/merge_requests_controller_spec.rb b/spec/controllers/projects/merge_requests_controller_spec.rb
index 2fce4b7a85f..0bfe83f1d98 100644
--- a/spec/controllers/projects/merge_requests_controller_spec.rb
+++ b/spec/controllers/projects/merge_requests_controller_spec.rb
@@ -609,7 +609,7 @@ describe Projects::MergeRequestsController do
end
it 'links to the environment on that project' do
- expect(json_response.first['url']).to match /#{forked.path_with_namespace}/
+ expect(json_response.first['url']).to match /#{forked.full_path}/
end
end
end
diff --git a/spec/controllers/projects/tree_controller_spec.rb b/spec/controllers/projects/tree_controller_spec.rb
index 16cd2e076e5..775f3998f5d 100644
--- a/spec/controllers/projects/tree_controller_spec.rb
+++ b/spec/controllers/projects/tree_controller_spec.rb
@@ -81,7 +81,7 @@ describe Projects::TreeController do
context 'redirect to blob' do
let(:id) { 'master/README.md' }
it 'redirects' do
- redirect_url = "/#{project.path_with_namespace}/blob/master/README.md"
+ redirect_url = "/#{project.full_path}/blob/master/README.md"
expect(subject)
.to redirect_to(redirect_url)
end
@@ -107,7 +107,7 @@ describe Projects::TreeController do
it 'redirects to the new directory' do
expect(subject)
- .to redirect_to("/#{project.path_with_namespace}/tree/#{branch_name}/#{path}")
+ .to redirect_to("/#{project.full_path}/tree/#{branch_name}/#{path}")
expect(flash[:notice]).to eq('The directory has been successfully created.')
end
end
@@ -118,7 +118,7 @@ describe Projects::TreeController do
it 'does not allow overwriting of existing files' do
expect(subject)
- .to redirect_to("/#{project.path_with_namespace}/tree/master")
+ .to redirect_to("/#{project.full_path}/tree/master")
expect(flash[:alert]).to eq('A file with this name already exists')
end
end
diff --git a/spec/factories/milestones.rb b/spec/factories/milestones.rb
index 113665ff11b..b5e2ec60072 100644
--- a/spec/factories/milestones.rb
+++ b/spec/factories/milestones.rb
@@ -17,7 +17,7 @@ FactoryGirl.define do
state "closed"
end
- after(:build) do |milestone, evaluator|
+ after(:build, :stub) do |milestone, evaluator|
if evaluator.group
milestone.group = evaluator.group
elsif evaluator.group_id
diff --git a/spec/factories/notification_settings.rb b/spec/factories/notification_settings.rb
index b5e96d18b8f..ee41997e41a 100644
--- a/spec/factories/notification_settings.rb
+++ b/spec/factories/notification_settings.rb
@@ -3,6 +3,5 @@ FactoryGirl.define do
source factory: :empty_project
user
level 3
- events []
end
end
diff --git a/spec/factories/projects.rb b/spec/factories/projects.rb
index 485ed48d2de..a04befe809a 100644
--- a/spec/factories/projects.rb
+++ b/spec/factories/projects.rb
@@ -64,7 +64,7 @@ FactoryGirl.define do
# We delete hooks so that gitlab-shell will not try to authenticate with
# an API that isn't running
- FileUtils.rm_r(File.join(project.repository_storage_path, "#{project.path_with_namespace}.git", 'hooks'))
+ FileUtils.rm_r(File.join(project.repository_storage_path, "#{project.disk_path}.git", 'hooks'))
end
end
@@ -72,7 +72,7 @@ FactoryGirl.define do
after(:create) do |project|
raise "Failed to create repository!" unless project.create_repository
- FileUtils.rm_r(File.join(project.repository_storage_path, "#{project.path_with_namespace}.git", 'refs'))
+ FileUtils.rm_r(File.join(project.repository_storage_path, "#{project.disk_path}.git", 'refs'))
end
end
diff --git a/spec/features/admin/admin_users_impersonation_tokens_spec.rb b/spec/features/admin/admin_users_impersonation_tokens_spec.rb
index 97ffc54415c..034682dae27 100644
--- a/spec/features/admin/admin_users_impersonation_tokens_spec.rb
+++ b/spec/features/admin/admin_users_impersonation_tokens_spec.rb
@@ -32,11 +32,13 @@ describe 'Admin > Users > Impersonation Tokens', js: true do
check "api"
check "read_user"
- expect { click_on "Create impersonation token" }.to change { PersonalAccessTokensFinder.new(impersonation: true).execute.count }
+ click_on "Create impersonation token"
+
expect(active_impersonation_tokens).to have_text(name)
expect(active_impersonation_tokens).to have_text('In')
expect(active_impersonation_tokens).to have_text('api')
expect(active_impersonation_tokens).to have_text('read_user')
+ expect(PersonalAccessTokensFinder.new(impersonation: true).execute.count).to equal(1)
end
end
diff --git a/spec/features/commits_spec.rb b/spec/features/commits_spec.rb
index 15ec6f20763..0c9fcc60d30 100644
--- a/spec/features/commits_spec.rb
+++ b/spec/features/commits_spec.rb
@@ -282,7 +282,7 @@ describe 'Commits' do
end
# verified and the gpg user has a gitlab profile
- click_on 'Verified'
+ click_on 'Verified', match: :first
within '.popover' do
expect(page).to have_content 'This commit was signed with a verified signature.'
expect(page).to have_content 'Nannie Bernhard'
@@ -295,7 +295,7 @@ describe 'Commits' do
visit project_commits_path(project, :'signed-commits')
- click_on 'Verified'
+ click_on 'Verified', match: :first
within '.popover' do
expect(page).to have_content 'This commit was signed with a verified signature.'
expect(page).to have_content 'Nannie Bernhard'
diff --git a/spec/features/merge_requests/create_new_mr_spec.rb b/spec/features/merge_requests/create_new_mr_spec.rb
index 11a74276898..d7f3d91e625 100644
--- a/spec/features/merge_requests/create_new_mr_spec.rb
+++ b/spec/features/merge_requests/create_new_mr_spec.rb
@@ -67,8 +67,8 @@ feature 'Create New Merge Request', js: true do
visit project_new_merge_request_path(project, merge_request: { target_project_id: private_project.id })
- expect(page).not_to have_content private_project.path_with_namespace
- expect(page).to have_content project.path_with_namespace
+ expect(page).not_to have_content private_project.full_path
+ expect(page).to have_content project.full_path
end
end
@@ -78,8 +78,8 @@ feature 'Create New Merge Request', js: true do
visit project_new_merge_request_path(project, merge_request: { source_project_id: private_project.id })
- expect(page).not_to have_content private_project.path_with_namespace
- expect(page).to have_content project.path_with_namespace
+ expect(page).not_to have_content private_project.full_path
+ expect(page).to have_content project.full_path
end
end
diff --git a/spec/features/projects/wiki/markdown_preview_spec.rb b/spec/features/projects/wiki/markdown_preview_spec.rb
index dbe98a38197..1dc89665020 100644
--- a/spec/features/projects/wiki/markdown_preview_spec.rb
+++ b/spec/features/projects/wiki/markdown_preview_spec.rb
@@ -38,10 +38,10 @@ feature 'Projects > Wiki > User previews markdown changes', js: true do
expect(page).to have_content("regular link")
- expect(page.html).to include("<a href=\"/#{project.path_with_namespace}/wikis/regular\">regular link</a>")
- expect(page.html).to include("<a href=\"/#{project.path_with_namespace}/wikis/a/b/relative\">relative link 1</a>")
- expect(page.html).to include("<a href=\"/#{project.path_with_namespace}/wikis/a/b/c/relative\">relative link 2</a>")
- expect(page.html).to include("<a href=\"/#{project.path_with_namespace}/wikis/a/b/c/e/f/relative\">relative link 3</a>")
+ expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/regular\">regular link</a>")
+ expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a/b/relative\">relative link 1</a>")
+ expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a/b/c/relative\">relative link 2</a>")
+ expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a/b/c/e/f/relative\">relative link 3</a>")
end
end
@@ -60,10 +60,10 @@ feature 'Projects > Wiki > User previews markdown changes', js: true do
expect(page).to have_content("regular link")
- expect(page.html).to include("<a href=\"/#{project.path_with_namespace}/wikis/regular\">regular link</a>")
- expect(page.html).to include("<a href=\"/#{project.path_with_namespace}/wikis/a-page/b-page/relative\">relative link 1</a>")
- expect(page.html).to include("<a href=\"/#{project.path_with_namespace}/wikis/a-page/b-page/c-page/relative\">relative link 2</a>")
- expect(page.html).to include("<a href=\"/#{project.path_with_namespace}/wikis/a-page/b-page/c-page/e/f/relative\">relative link 3</a>")
+ expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/regular\">regular link</a>")
+ expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a-page/b-page/relative\">relative link 1</a>")
+ expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a-page/b-page/c-page/relative\">relative link 2</a>")
+ expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a-page/b-page/c-page/e/f/relative\">relative link 3</a>")
end
end
@@ -82,10 +82,10 @@ feature 'Projects > Wiki > User previews markdown changes', js: true do
expect(page).to have_content("regular link")
- expect(page.html).to include("<a href=\"/#{project.path_with_namespace}/wikis/regular\">regular link</a>")
- expect(page.html).to include("<a href=\"/#{project.path_with_namespace}/wikis/a-page/b-page/relative\">relative link 1</a>")
- expect(page.html).to include("<a href=\"/#{project.path_with_namespace}/wikis/a-page/b-page/c-page/relative\">relative link 2</a>")
- expect(page.html).to include("<a href=\"/#{project.path_with_namespace}/wikis/a-page/b-page/c-page/e/f/relative\">relative link 3</a>")
+ expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/regular\">regular link</a>")
+ expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a-page/b-page/relative\">relative link 1</a>")
+ expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a-page/b-page/c-page/relative\">relative link 2</a>")
+ expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a-page/b-page/c-page/e/f/relative\">relative link 3</a>")
end
end
end
@@ -115,10 +115,10 @@ feature 'Projects > Wiki > User previews markdown changes', js: true do
expect(page).to have_content("regular link")
- expect(page.html).to include("<a href=\"/#{project.path_with_namespace}/wikis/regular\">regular link</a>")
- expect(page.html).to include("<a href=\"/#{project.path_with_namespace}/wikis/a/b/relative\">relative link 1</a>")
- expect(page.html).to include("<a href=\"/#{project.path_with_namespace}/wikis/a/b/c/relative\">relative link 2</a>")
- expect(page.html).to include("<a href=\"/#{project.path_with_namespace}/wikis/a/b/c/e/f/relative\">relative link 3</a>")
+ expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/regular\">regular link</a>")
+ expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a/b/relative\">relative link 1</a>")
+ expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a/b/c/relative\">relative link 2</a>")
+ expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a/b/c/e/f/relative\">relative link 3</a>")
end
end
@@ -132,10 +132,10 @@ feature 'Projects > Wiki > User previews markdown changes', js: true do
expect(page).to have_content("regular link")
- expect(page.html).to include("<a href=\"/#{project.path_with_namespace}/wikis/regular\">regular link</a>")
- expect(page.html).to include("<a href=\"/#{project.path_with_namespace}/wikis/a-page/b-page/relative\">relative link 1</a>")
- expect(page.html).to include("<a href=\"/#{project.path_with_namespace}/wikis/a-page/b-page/c-page/relative\">relative link 2</a>")
- expect(page.html).to include("<a href=\"/#{project.path_with_namespace}/wikis/a-page/b-page/c-page/e/f/relative\">relative link 3</a>")
+ expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/regular\">regular link</a>")
+ expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a-page/b-page/relative\">relative link 1</a>")
+ expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a-page/b-page/c-page/relative\">relative link 2</a>")
+ expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a-page/b-page/c-page/e/f/relative\">relative link 3</a>")
end
end
@@ -149,10 +149,10 @@ feature 'Projects > Wiki > User previews markdown changes', js: true do
expect(page).to have_content("regular link")
- expect(page.html).to include("<a href=\"/#{project.path_with_namespace}/wikis/regular\">regular link</a>")
- expect(page.html).to include("<a href=\"/#{project.path_with_namespace}/wikis/a-page/b-page/relative\">relative link 1</a>")
- expect(page.html).to include("<a href=\"/#{project.path_with_namespace}/wikis/a-page/b-page/c-page/relative\">relative link 2</a>")
- expect(page.html).to include("<a href=\"/#{project.path_with_namespace}/wikis/a-page/b-page/c-page/e/f/relative\">relative link 3</a>")
+ expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/regular\">regular link</a>")
+ expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a-page/b-page/relative\">relative link 1</a>")
+ expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a-page/b-page/c-page/relative\">relative link 2</a>")
+ expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a-page/b-page/c-page/e/f/relative\">relative link 3</a>")
end
end
end
diff --git a/spec/features/projects/wiki/user_git_access_wiki_page_spec.rb b/spec/features/projects/wiki/user_git_access_wiki_page_spec.rb
index 9c58e336605..f1e7f5f2be8 100644
--- a/spec/features/projects/wiki/user_git_access_wiki_page_spec.rb
+++ b/spec/features/projects/wiki/user_git_access_wiki_page_spec.rb
@@ -20,7 +20,7 @@ describe 'Projects > Wiki > User views Git access wiki page' do
visit project_wiki_path(project, wiki_page)
click_link 'Clone repository'
- expect(page).to have_text("Clone repository #{project.wiki.path_with_namespace}")
+ expect(page).to have_text("Clone repository #{project.wiki.full_path}")
expect(page).to have_text(project.wiki.http_url_to_repo)
end
end
diff --git a/spec/helpers/gitlab_routing_helper_spec.rb b/spec/helpers/gitlab_routing_helper_spec.rb
index 717ac1962d1..9aaed0edf87 100644
--- a/spec/helpers/gitlab_routing_helper_spec.rb
+++ b/spec/helpers/gitlab_routing_helper_spec.rb
@@ -1,6 +1,9 @@
require 'spec_helper'
describe GitlabRoutingHelper do
+ let(:project) { build_stubbed(:empty_project) }
+ let(:group) { build_stubbed(:group) }
+
describe 'Project URL helpers' do
describe '#project_member_path' do
let(:project_member) { create(:project_member) }
@@ -9,14 +12,10 @@ describe GitlabRoutingHelper do
end
describe '#request_access_project_members_path' do
- let(:project) { build_stubbed(:empty_project) }
-
it { expect(request_access_project_members_path(project)).to eq request_access_project_project_members_path(project) }
end
describe '#leave_project_members_path' do
- let(:project) { build_stubbed(:empty_project) }
-
it { expect(leave_project_members_path(project)).to eq leave_project_project_members_path(project) }
end
@@ -35,8 +34,6 @@ describe GitlabRoutingHelper do
describe 'Group URL helpers' do
describe '#group_members_url' do
- let(:group) { build_stubbed(:group) }
-
it { expect(group_members_url(group)).to eq group_group_members_url(group) }
end
@@ -47,14 +44,10 @@ describe GitlabRoutingHelper do
end
describe '#request_access_group_members_path' do
- let(:group) { build_stubbed(:group) }
-
it { expect(request_access_group_members_path(group)).to eq request_access_group_group_members_path(group) }
end
describe '#leave_group_members_path' do
- let(:group) { build_stubbed(:group) }
-
it { expect(leave_group_members_path(group)).to eq leave_group_group_members_path(group) }
end
@@ -70,4 +63,44 @@ describe GitlabRoutingHelper do
it { expect(resend_invite_group_member_path(group_member)).to eq resend_invite_group_group_member_path(group_member.source, group_member) }
end
end
+
+ describe '#milestone_path' do
+ context 'for a group milestone' do
+ let(:milestone) { build_stubbed(:milestone, group: group, iid: 1) }
+
+ it 'links to the group milestone page' do
+ expect(milestone_path(milestone))
+ .to eq(group_milestone_path(group, milestone))
+ end
+ end
+
+ context 'for a project milestone' do
+ let(:milestone) { build_stubbed(:milestone, project: project, iid: 1) }
+
+ it 'links to the project milestone page' do
+ expect(milestone_path(milestone))
+ .to eq(project_milestone_path(project, milestone))
+ end
+ end
+ end
+
+ describe '#milestone_url' do
+ context 'for a group milestone' do
+ let(:milestone) { build_stubbed(:milestone, group: group, iid: 1) }
+
+ it 'links to the group milestone page' do
+ expect(milestone_url(milestone))
+ .to eq(group_milestone_url(group, milestone))
+ end
+ end
+
+ context 'for a project milestone' do
+ let(:milestone) { build_stubbed(:milestone, project: project, iid: 1) }
+
+ it 'links to the project milestone page' do
+ expect(milestone_url(milestone))
+ .to eq(project_milestone_url(project, milestone))
+ end
+ end
+ end
end
diff --git a/spec/helpers/labels_helper_spec.rb b/spec/helpers/labels_helper_spec.rb
index a8d6044fda7..8fc94ce09db 100644
--- a/spec/helpers/labels_helper_spec.rb
+++ b/spec/helpers/labels_helper_spec.rb
@@ -7,7 +7,7 @@ describe LabelsHelper do
context 'without subject' do
it "uses the label's project" do
- expect(link_to_label(label)).to match %r{<a href="/#{label.project.path_with_namespace}/issues\?label_name%5B%5D=#{label.name}">.*</a>}
+ expect(link_to_label(label)).to match %r{<a href="/#{label.project.full_path}/issues\?label_name%5B%5D=#{label.name}">.*</a>}
end
end
@@ -32,7 +32,7 @@ describe LabelsHelper do
['issue', :issue, 'merge_request', :merge_request].each do |type|
context "set to #{type}" do
it 'links to correct page' do
- expect(link_to_label(label, type: type)).to match %r{<a href="/#{label.project.path_with_namespace}/#{type.to_s.pluralize}\?label_name%5B%5D=#{label.name}">.*</a>}
+ expect(link_to_label(label, type: type)).to match %r{<a href="/#{label.project.full_path}/#{type.to_s.pluralize}\?label_name%5B%5D=#{label.name}">.*</a>}
end
end
end
diff --git a/spec/helpers/markup_helper_spec.rb b/spec/helpers/markup_helper_spec.rb
index 4b6a351cf70..70eb01c9c44 100644
--- a/spec/helpers/markup_helper_spec.rb
+++ b/spec/helpers/markup_helper_spec.rb
@@ -210,11 +210,11 @@ describe MarkupHelper do
describe '#cross_project_reference' do
it 'shows the full MR reference' do
- expect(helper.cross_project_reference(project, merge_request)).to include(project.path_with_namespace)
+ expect(helper.cross_project_reference(project, merge_request)).to include(project.full_path)
end
it 'shows the full issue reference' do
- expect(helper.cross_project_reference(project, issue)).to include(project.path_with_namespace)
+ expect(helper.cross_project_reference(project, issue)).to include(project.full_path)
end
end
end
diff --git a/spec/helpers/merge_requests_helper_spec.rb b/spec/helpers/merge_requests_helper_spec.rb
index 493a4ff9a93..b2a9e277a1c 100644
--- a/spec/helpers/merge_requests_helper_spec.rb
+++ b/spec/helpers/merge_requests_helper_spec.rb
@@ -34,8 +34,8 @@ describe MergeRequestsHelper do
let(:fork_project) { create(:empty_project, forked_from_project: project) }
let(:merge_request) { create(:merge_request, source_project: fork_project, target_project: project) }
subject { format_mr_branch_names(merge_request) }
- let(:source_title) { "#{fork_project.path_with_namespace}:#{merge_request.source_branch}" }
- let(:target_title) { "#{project.path_with_namespace}:#{merge_request.target_branch}" }
+ let(:source_title) { "#{fork_project.full_path}:#{merge_request.source_branch}" }
+ let(:target_title) { "#{project.full_path}:#{merge_request.target_branch}" }
it { is_expected.to eq([source_title, target_title]) }
end
diff --git a/spec/helpers/projects_helper_spec.rb b/spec/helpers/projects_helper_spec.rb
index 45066a60f50..faf26931efc 100644
--- a/spec/helpers/projects_helper_spec.rb
+++ b/spec/helpers/projects_helper_spec.rb
@@ -53,13 +53,13 @@ describe ProjectsHelper do
end
it "returns a valid cach key" do
- expect(helper.send(:readme_cache_key)).to eq("#{project.path_with_namespace}-#{project.commit.id}-readme")
+ expect(helper.send(:readme_cache_key)).to eq("#{project.full_path}-#{project.commit.id}-readme")
end
it "returns a valid cache key if HEAD does not exist" do
allow(project).to receive(:commit) { nil }
- expect(helper.send(:readme_cache_key)).to eq("#{project.path_with_namespace}-nil-readme")
+ expect(helper.send(:readme_cache_key)).to eq("#{project.full_path}-nil-readme")
end
end
diff --git a/spec/javascripts/projects/project_new_spec.js b/spec/javascripts/projects/project_new_spec.js
new file mode 100644
index 00000000000..850768f0e4f
--- /dev/null
+++ b/spec/javascripts/projects/project_new_spec.js
@@ -0,0 +1,127 @@
+import projectNew from '~/projects/project_new';
+
+describe('New Project', () => {
+ let $projectImportUrl;
+ let $projectPath;
+
+ beforeEach(() => {
+ setFixtures(`
+ <input id="project_import_url" />
+ <input id="project_path" />
+ `);
+
+ $projectImportUrl = $('#project_import_url');
+ $projectPath = $('#project_path');
+ });
+
+ describe('deriveProjectPathFromUrl', () => {
+ const dummyImportUrl = `${gl.TEST_HOST}/dummy/import/url.git`;
+
+ beforeEach(() => {
+ projectNew.bindEvents();
+ $projectPath.val('').keyup().val(dummyImportUrl);
+ });
+
+ it('does not change project path for disabled $projectImportUrl', () => {
+ $projectImportUrl.attr('disabled', true);
+
+ projectNew.deriveProjectPathFromUrl($projectImportUrl, $projectPath);
+
+ expect($projectPath.val()).toEqual(dummyImportUrl);
+ });
+
+ describe('for enabled $projectImportUrl', () => {
+ beforeEach(() => {
+ $projectImportUrl.attr('disabled', false);
+ });
+
+ it('does not change project path if it is set by user', () => {
+ $projectPath.keyup();
+
+ projectNew.deriveProjectPathFromUrl($projectImportUrl, $projectPath);
+
+ expect($projectPath.val()).toEqual(dummyImportUrl);
+ });
+
+ it('does not change project path for empty $projectImportUrl', () => {
+ $projectImportUrl.val('');
+
+ projectNew.deriveProjectPathFromUrl($projectImportUrl, $projectPath);
+
+ expect($projectPath.val()).toEqual(dummyImportUrl);
+ });
+
+ it('does not change project path for whitespace $projectImportUrl', () => {
+ $projectImportUrl.val(' ');
+
+ projectNew.deriveProjectPathFromUrl($projectImportUrl, $projectPath);
+
+ expect($projectPath.val()).toEqual(dummyImportUrl);
+ });
+
+ it('does not change project path for $projectImportUrl without slashes', () => {
+ $projectImportUrl.val('has-no-slash');
+
+ projectNew.deriveProjectPathFromUrl($projectImportUrl, $projectPath);
+
+ expect($projectPath.val()).toEqual(dummyImportUrl);
+ });
+
+ it('changes project path to last $projectImportUrl component', () => {
+ $projectImportUrl.val('/this/is/last');
+
+ projectNew.deriveProjectPathFromUrl($projectImportUrl, $projectPath);
+
+ expect($projectPath.val()).toEqual('last');
+ });
+
+ it('ignores trailing slashes in $projectImportUrl', () => {
+ $projectImportUrl.val('/has/trailing/slash/');
+
+ projectNew.deriveProjectPathFromUrl($projectImportUrl, $projectPath);
+
+ expect($projectPath.val()).toEqual('slash');
+ });
+
+ it('ignores fragment identifier in $projectImportUrl', () => {
+ $projectImportUrl.val('/this/has/a#fragment-identifier/');
+
+ projectNew.deriveProjectPathFromUrl($projectImportUrl, $projectPath);
+
+ expect($projectPath.val()).toEqual('a');
+ });
+
+ it('ignores query string in $projectImportUrl', () => {
+ $projectImportUrl.val('/url/with?query=string');
+
+ projectNew.deriveProjectPathFromUrl($projectImportUrl, $projectPath);
+
+ expect($projectPath.val()).toEqual('with');
+ });
+
+ it('ignores trailing .git in $projectImportUrl', () => {
+ $projectImportUrl.val('/repository.git');
+
+ projectNew.deriveProjectPathFromUrl($projectImportUrl, $projectPath);
+
+ expect($projectPath.val()).toEqual('repository');
+ });
+
+ it('changes project path for HTTPS URL in $projectImportUrl', () => {
+ $projectImportUrl.val('https://username:password@gitlab.company.com/group/project.git');
+
+ projectNew.deriveProjectPathFromUrl($projectImportUrl, $projectPath);
+
+ expect($projectPath.val()).toEqual('project');
+ });
+
+ it('changes project path for SSH URL in $projectImportUrl', () => {
+ $projectImportUrl.val('git@gitlab.com:gitlab-org/gitlab-ce.git');
+
+ projectNew.deriveProjectPathFromUrl($projectImportUrl, $projectPath);
+
+ expect($projectPath.val()).toEqual('gitlab-ce');
+ });
+ });
+ });
+});
diff --git a/spec/lib/banzai/filter/abstract_reference_filter_spec.rb b/spec/lib/banzai/filter/abstract_reference_filter_spec.rb
index 27532f96f56..32d027b026b 100644
--- a/spec/lib/banzai/filter/abstract_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/abstract_reference_filter_spec.rb
@@ -5,7 +5,7 @@ describe Banzai::Filter::AbstractReferenceFilter do
describe '#references_per_project' do
it 'returns a Hash containing references grouped per project paths' do
- doc = Nokogiri::HTML.fragment("#1 #{project.path_with_namespace}#2")
+ doc = Nokogiri::HTML.fragment("#1 #{project.full_path}#2")
filter = described_class.new(doc, project: project)
expect(filter).to receive(:object_class).exactly(4).times.and_return(Issue)
@@ -14,7 +14,7 @@ describe Banzai::Filter::AbstractReferenceFilter do
refs = filter.references_per_project
expect(refs).to be_an_instance_of(Hash)
- expect(refs[project.path_with_namespace]).to eq(Set.new(%w[1 2]))
+ expect(refs[project.full_path]).to eq(Set.new(%w[1 2]))
end
end
@@ -24,10 +24,10 @@ describe Banzai::Filter::AbstractReferenceFilter do
filter = described_class.new(doc, project: project)
expect(filter).to receive(:references_per_project)
- .and_return({ project.path_with_namespace => Set.new(%w[1]) })
+ .and_return({ project.full_path => Set.new(%w[1]) })
expect(filter.projects_per_reference)
- .to eq({ project.path_with_namespace => project })
+ .to eq({ project.full_path => project })
end
end
@@ -37,7 +37,7 @@ describe Banzai::Filter::AbstractReferenceFilter do
context 'with RequestStore disabled' do
it 'returns a list of Projects for a list of paths' do
- expect(filter.find_projects_for_paths([project.path_with_namespace]))
+ expect(filter.find_projects_for_paths([project.full_path]))
.to eq([project])
end
@@ -49,7 +49,7 @@ describe Banzai::Filter::AbstractReferenceFilter do
context 'with RequestStore enabled', :request_store do
it 'returns a list of Projects for a list of paths' do
- expect(filter.find_projects_for_paths([project.path_with_namespace]))
+ expect(filter.find_projects_for_paths([project.full_path]))
.to eq([project])
end
@@ -88,7 +88,7 @@ describe Banzai::Filter::AbstractReferenceFilter do
doc = Nokogiri::HTML.fragment('')
filter = described_class.new(doc, project: project)
- expect(filter.current_project_path).to eq(project.path_with_namespace)
+ expect(filter.current_project_path).to eq(project.full_path)
end
end
end
diff --git a/spec/lib/banzai/filter/commit_range_reference_filter_spec.rb b/spec/lib/banzai/filter/commit_range_reference_filter_spec.rb
index 11d48387544..935146c17fc 100644
--- a/spec/lib/banzai/filter/commit_range_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/commit_range_reference_filter_spec.rb
@@ -100,7 +100,7 @@ describe Banzai::Filter::CommitRangeReferenceFilter do
context 'cross-project / cross-namespace complete reference' do
let(:project2) { create(:project, :public, :repository) }
- let(:reference) { "#{project2.path_with_namespace}@#{commit1.id}...#{commit2.id}" }
+ let(:reference) { "#{project2.full_path}@#{commit1.id}...#{commit2.id}" }
it 'links to a valid reference' do
doc = reference_filter("See #{reference}")
@@ -113,20 +113,20 @@ describe Banzai::Filter::CommitRangeReferenceFilter do
doc = reference_filter("Fixed (#{reference}.)")
expect(doc.css('a').first.text)
- .to eql("#{project2.path_with_namespace}@#{commit1.short_id}...#{commit2.short_id}")
+ .to eql("#{project2.full_path}@#{commit1.short_id}...#{commit2.short_id}")
end
it 'has valid text' do
doc = reference_filter("Fixed (#{reference}.)")
- expect(doc.text).to eql("Fixed (#{project2.path_with_namespace}@#{commit1.short_id}...#{commit2.short_id}.)")
+ expect(doc.text).to eql("Fixed (#{project2.full_path}@#{commit1.short_id}...#{commit2.short_id}.)")
end
it 'ignores invalid commit IDs on the referenced project' do
- exp = act = "Fixed #{project2.path_with_namespace}@#{commit1.id.reverse}...#{commit2.id}"
+ exp = act = "Fixed #{project2.full_path}@#{commit1.id.reverse}...#{commit2.id}"
expect(reference_filter(act).to_html).to eq exp
- exp = act = "Fixed #{project2.path_with_namespace}@#{commit1.id}...#{commit2.id.reverse}"
+ exp = act = "Fixed #{project2.full_path}@#{commit1.id}...#{commit2.id.reverse}"
expect(reference_filter(act).to_html).to eq exp
end
end
diff --git a/spec/lib/banzai/filter/commit_reference_filter_spec.rb b/spec/lib/banzai/filter/commit_reference_filter_spec.rb
index fb2a36d1ba1..c7cf1c1d582 100644
--- a/spec/lib/banzai/filter/commit_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/commit_reference_filter_spec.rb
@@ -98,18 +98,18 @@ describe Banzai::Filter::CommitReferenceFilter do
let(:namespace) { create(:namespace) }
let(:project2) { create(:project, :public, :repository, namespace: namespace) }
let(:commit) { project2.commit }
- let(:reference) { "#{project2.path_with_namespace}@#{commit.short_id}" }
+ let(:reference) { "#{project2.full_path}@#{commit.short_id}" }
it 'link has valid text' do
doc = reference_filter("See (#{reference}.)")
- expect(doc.css('a').first.text).to eql("#{project2.path_with_namespace}@#{commit.short_id}")
+ expect(doc.css('a').first.text).to eql("#{project2.full_path}@#{commit.short_id}")
end
it 'has valid text' do
doc = reference_filter("See (#{reference}.)")
- expect(doc.text).to eql("See (#{project2.path_with_namespace}@#{commit.short_id}.)")
+ expect(doc.text).to eql("See (#{project2.full_path}@#{commit.short_id}.)")
end
it 'ignores invalid commit IDs on the referenced project' do
@@ -124,7 +124,7 @@ describe Banzai::Filter::CommitReferenceFilter do
let(:project) { create(:empty_project, namespace: namespace) }
let(:project2) { create(:project, :public, :repository, namespace: namespace) }
let(:commit) { project2.commit }
- let(:reference) { "#{project2.path_with_namespace}@#{commit.short_id}" }
+ let(:reference) { "#{project2.full_path}@#{commit.short_id}" }
it 'link has valid text' do
doc = reference_filter("See (#{reference}.)")
@@ -150,7 +150,7 @@ describe Banzai::Filter::CommitReferenceFilter do
let(:project) { create(:empty_project, namespace: namespace) }
let(:project2) { create(:project, :public, :repository, namespace: namespace) }
let(:commit) { project2.commit }
- let(:reference) { "#{project2.path_with_namespace}@#{commit.short_id}" }
+ let(:reference) { "#{project2.full_path}@#{commit.short_id}" }
it 'link has valid text' do
doc = reference_filter("See (#{reference}.)")
diff --git a/spec/lib/banzai/filter/issue_reference_filter_spec.rb b/spec/lib/banzai/filter/issue_reference_filter_spec.rb
index 045bf3e0cc9..024a5cafb41 100644
--- a/spec/lib/banzai/filter/issue_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/issue_reference_filter_spec.rb
@@ -127,7 +127,7 @@ describe Banzai::Filter::IssueReferenceFilter do
let(:project2) { create(:empty_project, :public) }
let(:issue) { create(:issue, project: project2) }
- let(:reference) { "#{project2.path_with_namespace}##{issue.iid}" }
+ let(:reference) { "#{project2.full_path}##{issue.iid}" }
it 'ignores valid references when cross-reference project uses external tracker' do
expect_any_instance_of(described_class).to receive(:find_object)
@@ -148,13 +148,13 @@ describe Banzai::Filter::IssueReferenceFilter do
it 'link has valid text' do
doc = reference_filter("Fixed (#{reference}.)")
- expect(doc.css('a').first.text).to eql("#{project2.path_with_namespace}##{issue.iid}")
+ expect(doc.css('a').first.text).to eql("#{project2.full_path}##{issue.iid}")
end
it 'has valid text' do
doc = reference_filter("Fixed (#{reference}.)")
- expect(doc.text).to eq("Fixed (#{project2.path_with_namespace}##{issue.iid}.)")
+ expect(doc.text).to eq("Fixed (#{project2.full_path}##{issue.iid}.)")
end
it 'ignores invalid issue IDs on the referenced project' do
@@ -171,7 +171,7 @@ describe Banzai::Filter::IssueReferenceFilter do
let(:project) { create(:empty_project, :public, namespace: namespace) }
let(:project2) { create(:empty_project, :public, namespace: namespace) }
let(:issue) { create(:issue, project: project2) }
- let(:reference) { "#{project2.path_with_namespace}##{issue.iid}" }
+ let(:reference) { "#{project2.full_path}##{issue.iid}" }
it 'ignores valid references when cross-reference project uses external tracker' do
expect_any_instance_of(described_class).to receive(:find_object)
@@ -324,10 +324,10 @@ describe Banzai::Filter::IssueReferenceFilter do
filter = described_class.new(doc, project: project)
expect(filter).to receive(:projects_per_reference)
- .and_return({ project.path_with_namespace => project })
+ .and_return({ project.full_path => project })
expect(filter).to receive(:references_per_project)
- .and_return({ project.path_with_namespace => Set.new([issue.iid]) })
+ .and_return({ project.full_path => Set.new([issue.iid]) })
expect(filter.issues_per_project)
.to eq({ project => { issue.iid => issue } })
diff --git a/spec/lib/banzai/filter/label_reference_filter_spec.rb b/spec/lib/banzai/filter/label_reference_filter_spec.rb
index 1daa6ac7f9e..dfd4c7a7279 100644
--- a/spec/lib/banzai/filter/label_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/label_reference_filter_spec.rb
@@ -368,7 +368,7 @@ describe Banzai::Filter::LabelReferenceFilter do
describe 'cross-project / cross-namespace complete reference' do
let(:project2) { create(:empty_project) }
let(:label) { create(:label, project: project2, color: '#00ff00') }
- let(:reference) { "#{project2.path_with_namespace}~#{label.name}" }
+ let(:reference) { "#{project2.full_path}~#{label.name}" }
let!(:result) { reference_filter("See #{reference}") }
it 'links to a valid reference' do
@@ -400,7 +400,7 @@ describe Banzai::Filter::LabelReferenceFilter do
let(:project) { create(:empty_project, namespace: namespace) }
let(:project2) { create(:empty_project, namespace: namespace) }
let(:label) { create(:label, project: project2, color: '#00ff00') }
- let(:reference) { "#{project2.path_with_namespace}~#{label.name}" }
+ let(:reference) { "#{project2.full_path}~#{label.name}" }
let!(:result) { reference_filter("See #{reference}") }
it 'links to a valid reference' do
@@ -466,7 +466,7 @@ describe Banzai::Filter::LabelReferenceFilter do
let(:another_group) { create(:group) }
let(:another_project) { create(:empty_project, :public, namespace: another_group) }
let(:group_label) { create(:group_label, group: another_group, color: '#00ff00') }
- let(:reference) { "#{another_project.path_with_namespace}~#{group_label.name}" }
+ let(:reference) { "#{another_project.full_path}~#{group_label.name}" }
let!(:result) { reference_filter("See #{reference}", project: project) }
it 'points to referenced project issues page' do
@@ -501,7 +501,7 @@ describe Banzai::Filter::LabelReferenceFilter do
let(:project) { create(:empty_project, :public, namespace: group) }
let(:another_project) { create(:empty_project, :public, namespace: group) }
let(:group_label) { create(:group_label, group: group, color: '#00ff00') }
- let(:reference) { "#{another_project.path_with_namespace}~#{group_label.name}" }
+ let(:reference) { "#{another_project.full_path}~#{group_label.name}" }
let!(:result) { reference_filter("See #{reference}", project: project) }
it 'points to referenced project issues page' do
@@ -535,7 +535,7 @@ describe Banzai::Filter::LabelReferenceFilter do
let(:group) { create(:group) }
let(:project) { create(:empty_project, :public, namespace: group) }
let(:group_label) { create(:group_label, group: group, color: '#00ff00') }
- let(:reference) { "#{project.path_with_namespace}~#{group_label.name}" }
+ let(:reference) { "#{project.full_path}~#{group_label.name}" }
let!(:result) { reference_filter("See #{reference}", project: project) }
it 'points to referenced project issues page' do
diff --git a/spec/lib/banzai/filter/merge_request_reference_filter_spec.rb b/spec/lib/banzai/filter/merge_request_reference_filter_spec.rb
index 683972a3112..b693ae3eca2 100644
--- a/spec/lib/banzai/filter/merge_request_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/merge_request_reference_filter_spec.rb
@@ -102,7 +102,7 @@ describe Banzai::Filter::MergeRequestReferenceFilter do
context 'cross-project / cross-namespace complete reference' do
let(:project2) { create(:empty_project, :public) }
let(:merge) { create(:merge_request, source_project: project2) }
- let(:reference) { "#{project2.path_with_namespace}!#{merge.iid}" }
+ let(:reference) { "#{project2.full_path}!#{merge.iid}" }
it 'links to a valid reference' do
doc = reference_filter("See #{reference}")
@@ -135,7 +135,7 @@ describe Banzai::Filter::MergeRequestReferenceFilter do
let(:project) { create(:empty_project, :public, namespace: namespace) }
let(:project2) { create(:empty_project, :public, namespace: namespace) }
let!(:merge) { create(:merge_request, source_project: project2) }
- let(:reference) { "#{project2.path_with_namespace}!#{merge.iid}" }
+ let(:reference) { "#{project2.full_path}!#{merge.iid}" }
it 'links to a valid reference' do
doc = reference_filter("See #{reference}")
diff --git a/spec/lib/banzai/filter/milestone_reference_filter_spec.rb b/spec/lib/banzai/filter/milestone_reference_filter_spec.rb
index 8fe05dc2e53..79ff9419e4b 100644
--- a/spec/lib/banzai/filter/milestone_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/milestone_reference_filter_spec.rb
@@ -152,7 +152,7 @@ describe Banzai::Filter::MilestoneReferenceFilter do
let(:namespace) { create(:namespace) }
let(:another_project) { create(:empty_project, :public, namespace: namespace) }
let(:milestone) { create(:milestone, project: another_project) }
- let(:reference) { "#{another_project.path_with_namespace}%#{milestone.iid}" }
+ let(:reference) { "#{another_project.full_path}%#{milestone.iid}" }
let!(:result) { reference_filter("See #{reference}") }
it 'points to referenced project milestone page' do
@@ -164,14 +164,14 @@ describe Banzai::Filter::MilestoneReferenceFilter do
doc = reference_filter("See (#{reference}.)")
expect(doc.css('a').first.text)
- .to eq("#{milestone.name} in #{another_project.path_with_namespace}")
+ .to eq("#{milestone.name} in #{another_project.full_path}")
end
it 'has valid text' do
doc = reference_filter("See (#{reference}.)")
expect(doc.text)
- .to eq("See (#{milestone.name} in #{another_project.path_with_namespace}.)")
+ .to eq("See (#{milestone.name} in #{another_project.full_path}.)")
end
it 'escapes the name attribute' do
@@ -180,7 +180,7 @@ describe Banzai::Filter::MilestoneReferenceFilter do
doc = reference_filter("See #{reference}")
expect(doc.css('a').first.text)
- .to eq "#{milestone.name} in #{another_project.path_with_namespace}"
+ .to eq "#{milestone.name} in #{another_project.full_path}"
end
end
@@ -189,7 +189,7 @@ describe Banzai::Filter::MilestoneReferenceFilter do
let(:project) { create(:empty_project, :public, namespace: namespace) }
let(:another_project) { create(:empty_project, :public, namespace: namespace) }
let(:milestone) { create(:milestone, project: another_project) }
- let(:reference) { "#{another_project.path_with_namespace}%#{milestone.iid}" }
+ let(:reference) { "#{another_project.full_path}%#{milestone.iid}" }
let!(:result) { reference_filter("See #{reference}") }
it 'points to referenced project milestone page' do
@@ -257,4 +257,28 @@ describe Banzai::Filter::MilestoneReferenceFilter do
.to eq "#{milestone.name} in #{another_project.path}"
end
end
+
+ describe 'cross project milestone references' do
+ let(:another_project) { create(:empty_project, :public) }
+ let(:project_path) { another_project.full_path }
+ let(:milestone) { create(:milestone, project: another_project) }
+ let(:reference) { milestone.to_reference(project) }
+
+ let!(:result) { reference_filter("See #{reference}") }
+
+ it 'points to referenced project milestone page' do
+ expect(result.css('a').first.attr('href')).to eq urls
+ .project_milestone_url(another_project, milestone)
+ end
+
+ it 'contains cross project content' do
+ expect(result.css('a').first.text).to eq "#{milestone.name} in #{project_path}"
+ end
+
+ it 'escapes the name attribute' do
+ allow_any_instance_of(Milestone).to receive(:title).and_return(%{"></a>whatever<a title="})
+ doc = reference_filter("See #{reference}")
+ expect(doc.css('a').first.text).to eq "#{milestone.name} in #{project_path}"
+ end
+ end
end
diff --git a/spec/lib/banzai/filter/relative_link_filter_spec.rb b/spec/lib/banzai/filter/relative_link_filter_spec.rb
index ddebf2264d9..e97199672f3 100644
--- a/spec/lib/banzai/filter/relative_link_filter_spec.rb
+++ b/spec/lib/banzai/filter/relative_link_filter_spec.rb
@@ -26,7 +26,7 @@ describe Banzai::Filter::RelativeLinkFilter do
end
let(:project) { create(:project, :repository) }
- let(:project_path) { project.path_with_namespace }
+ let(:project_path) { project.full_path }
let(:ref) { 'markdown' }
let(:commit) { project.commit(ref) }
let(:project_wiki) { nil }
diff --git a/spec/lib/banzai/filter/snippet_reference_filter_spec.rb b/spec/lib/banzai/filter/snippet_reference_filter_spec.rb
index 5f548888223..a7eee7a4515 100644
--- a/spec/lib/banzai/filter/snippet_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/snippet_reference_filter_spec.rb
@@ -83,7 +83,7 @@ describe Banzai::Filter::SnippetReferenceFilter do
let(:namespace) { create(:namespace) }
let(:project2) { create(:empty_project, :public, namespace: namespace) }
let!(:snippet) { create(:project_snippet, project: project2) }
- let(:reference) { "#{project2.path_with_namespace}$#{snippet.id}" }
+ let(:reference) { "#{project2.full_path}$#{snippet.id}" }
it 'links to a valid reference' do
doc = reference_filter("See #{reference}")
@@ -116,7 +116,7 @@ describe Banzai::Filter::SnippetReferenceFilter do
let(:project) { create(:empty_project, :public, namespace: namespace) }
let(:project2) { create(:empty_project, :public, namespace: namespace) }
let!(:snippet) { create(:project_snippet, project: project2) }
- let(:reference) { "#{project2.path_with_namespace}$#{snippet.id}" }
+ let(:reference) { "#{project2.full_path}$#{snippet.id}" }
it 'links to a valid reference' do
doc = reference_filter("See #{reference}")
diff --git a/spec/lib/banzai/filter/upload_link_filter_spec.rb b/spec/lib/banzai/filter/upload_link_filter_spec.rb
index 3bc9635b50e..74a23a9ab5e 100644
--- a/spec/lib/banzai/filter/upload_link_filter_spec.rb
+++ b/spec/lib/banzai/filter/upload_link_filter_spec.rb
@@ -52,21 +52,21 @@ describe Banzai::Filter::UploadLinkFilter do
it 'rebuilds relative URL for a link' do
doc = filter(link('/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg'))
expect(doc.at_css('a')['href'])
- .to eq "#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg"
+ .to eq "#{Gitlab.config.gitlab.url}/#{project.full_path}/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg"
doc = filter(nested_link('/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg'))
expect(doc.at_css('a')['href'])
- .to eq "#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg"
+ .to eq "#{Gitlab.config.gitlab.url}/#{project.full_path}/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg"
end
it 'rebuilds relative URL for an image' do
doc = filter(image('/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg'))
expect(doc.at_css('img')['src'])
- .to eq "#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg"
+ .to eq "#{Gitlab.config.gitlab.url}/#{project.full_path}/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg"
doc = filter(nested_image('/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg'))
expect(doc.at_css('img')['src'])
- .to eq "#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg"
+ .to eq "#{Gitlab.config.gitlab.url}/#{project.full_path}/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg"
end
it 'does not modify absolute URL' do
@@ -85,7 +85,7 @@ describe Banzai::Filter::UploadLinkFilter do
.to receive(:image?).with(path).and_return(true)
doc = filter(image(escaped))
- expect(doc.at_css('img')['src']).to match "#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}/uploads/%ED%95%9C%EA%B8%80.png"
+ expect(doc.at_css('img')['src']).to match "#{Gitlab.config.gitlab.url}/#{project.full_path}/uploads/%ED%95%9C%EA%B8%80.png"
end
end
diff --git a/spec/lib/extracts_path_spec.rb b/spec/lib/extracts_path_spec.rb
index 9b89f47ae7e..867f7d55af7 100644
--- a/spec/lib/extracts_path_spec.rb
+++ b/spec/lib/extracts_path_spec.rb
@@ -14,7 +14,7 @@ describe ExtractsPath do
repo = double(ref_names: ['master', 'foo/bar/baz', 'v1.0.0', 'v2.0.0',
'release/app', 'release/app/v1.0.0'])
allow(project).to receive(:repository).and_return(repo)
- allow(project).to receive(:path_with_namespace)
+ allow(project).to receive(:full_path)
.and_return('gitlab/gitlab-ci')
allow(request).to receive(:format=)
end
@@ -29,7 +29,7 @@ describe ExtractsPath do
it "log tree path has no escape sequences" do
assign_ref_vars
- expect(@logs_path).to eq("/#{@project.path_with_namespace}/refs/#{ref}/logs_tree/files/ruby/popen.rb")
+ expect(@logs_path).to eq("/#{@project.full_path}/refs/#{ref}/logs_tree/files/ruby/popen.rb")
end
context 'ref contains %20' do
diff --git a/spec/lib/gitlab/backup/repository_spec.rb b/spec/lib/gitlab/backup/repository_spec.rb
index db860b01ba4..3af69daa585 100644
--- a/spec/lib/gitlab/backup/repository_spec.rb
+++ b/spec/lib/gitlab/backup/repository_spec.rb
@@ -60,4 +60,58 @@ describe Backup::Repository do
end
end
end
+
+ describe '#empty_repo?' do
+ context 'for a wiki' do
+ let(:wiki) { create(:project_wiki) }
+
+ context 'wiki repo has content' do
+ let!(:wiki_page) { create(:wiki_page, wiki: wiki) }
+
+ before do
+ wiki.repository.exists? # initial cache
+ end
+
+ context '`repository.exists?` is incorrectly cached as false' do
+ before do
+ repo = wiki.repository
+ repo.send(:cache).expire(:exists?)
+ repo.send(:cache).fetch(:exists?) { false }
+ repo.send(:instance_variable_set, :@exists, false)
+ end
+
+ it 'returns false, regardless of bad cache value' do
+ expect(described_class.new.send(:empty_repo?, wiki)).to be_falsey
+ end
+ end
+
+ context '`repository.exists?` is correctly cached as true' do
+ it 'returns false' do
+ expect(described_class.new.send(:empty_repo?, wiki)).to be_falsey
+ end
+ end
+ end
+
+ context 'wiki repo does not have content' do
+ context '`repository.exists?` is incorrectly cached as true' do
+ before do
+ repo = wiki.repository
+ repo.send(:cache).expire(:exists?)
+ repo.send(:cache).fetch(:exists?) { true }
+ repo.send(:instance_variable_set, :@exists, true)
+ end
+
+ it 'returns true, regardless of bad cache value' do
+ expect(described_class.new.send(:empty_repo?, wiki)).to be_truthy
+ end
+ end
+
+ context '`repository.exists?` is correctly cached as false' do
+ it 'returns true' do
+ expect(described_class.new.send(:empty_repo?, wiki)).to be_truthy
+ end
+ end
+ end
+ end
+ end
end
diff --git a/spec/lib/gitlab/diff/file_spec.rb b/spec/lib/gitlab/diff/file_spec.rb
index cd2fa98b14c..d3d841b0668 100644
--- a/spec/lib/gitlab/diff/file_spec.rb
+++ b/spec/lib/gitlab/diff/file_spec.rb
@@ -47,14 +47,6 @@ describe Gitlab::Diff::File do
end
end
- describe '#old_content_commit' do
- it 'returns base commit' do
- old_content_commit = diff_file.old_content_commit
-
- expect(old_content_commit.id).to eq('6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9')
- end
- end
-
describe '#old_blob' do
it 'returns blob of commit of base commit' do
old_data = diff_file.old_blob.data
diff --git a/spec/lib/gitlab/email/message/repository_push_spec.rb b/spec/lib/gitlab/email/message/repository_push_spec.rb
index 7b3291b8315..83c4d177cae 100644
--- a/spec/lib/gitlab/email/message/repository_push_spec.rb
+++ b/spec/lib/gitlab/email/message/repository_push_spec.rb
@@ -117,7 +117,7 @@ describe Gitlab::Email::Message::RepositoryPush do
describe '#subject' do
subject { message.subject }
- it { is_expected.to include "[Git][#{project.path_with_namespace}]" }
+ it { is_expected.to include "[Git][#{project.full_path}]" }
it { is_expected.to include "#{compare.commits.length} commits" }
it { is_expected.to include compare.commits.first.message.split("\n").first }
end
diff --git a/spec/lib/gitlab/gfm/uploads_rewriter_spec.rb b/spec/lib/gitlab/gfm/uploads_rewriter_spec.rb
index c3016f63ebf..fef456eb416 100644
--- a/spec/lib/gitlab/gfm/uploads_rewriter_spec.rb
+++ b/spec/lib/gitlab/gfm/uploads_rewriter_spec.rb
@@ -39,8 +39,8 @@ describe Gitlab::Gfm::UploadsRewriter do
it 'copies files' do
expect(new_files).to all(exist)
expect(old_paths).not_to match_array new_paths
- expect(old_paths).to all(include(old_project.path_with_namespace))
- expect(new_paths).to all(include(new_project.path_with_namespace))
+ expect(old_paths).to all(include(old_project.full_path))
+ expect(new_paths).to all(include(new_project.full_path))
end
it 'does not remove old files' do
diff --git a/spec/lib/gitlab/git/blob_spec.rb b/spec/lib/gitlab/git/blob_spec.rb
index 3c784eda4f8..18320bb23b9 100644
--- a/spec/lib/gitlab/git/blob_spec.rb
+++ b/spec/lib/gitlab/git/blob_spec.rb
@@ -78,12 +78,18 @@ describe Gitlab::Git::Blob, seed_helper: true do
context 'large file' do
let(:blob) { Gitlab::Git::Blob.find(repository, SeedRepo::Commit::ID, 'files/images/6049019_460s.jpg') }
let(:blob_size) { 111803 }
+ let(:stub_limit) { 1000 }
+
+ before do
+ stub_const('Gitlab::Git::Blob::MAX_DATA_DISPLAY_SIZE', stub_limit)
+ end
it { expect(blob.size).to eq(blob_size) }
- it { expect(blob.data.length).to eq(blob_size) }
+ it { expect(blob.data.length).to eq(stub_limit) }
it 'check that this test is sane' do
- expect(blob.size).to be <= Gitlab::Git::Blob::MAX_DATA_DISPLAY_SIZE
+ # It only makes sense to test limiting if the blob is larger than the limit.
+ expect(blob.size).to be > Gitlab::Git::Blob::MAX_DATA_DISPLAY_SIZE
end
it 'can load all data' do
diff --git a/spec/lib/gitlab/git/repository_spec.rb b/spec/lib/gitlab/git/repository_spec.rb
index 50736d353ad..8e4a1f31ced 100644
--- a/spec/lib/gitlab/git/repository_spec.rb
+++ b/spec/lib/gitlab/git/repository_spec.rb
@@ -1127,6 +1127,45 @@ describe Gitlab::Git::Repository, seed_helper: true do
end
end
+ describe '#languages' do
+ shared_examples 'languages' do
+ it 'returns exactly the expected results' do
+ languages = repository.languages('4b4918a572fa86f9771e5ba40fbd48e1eb03e2c6')
+ expected_languages = [
+ { value: 66.63, label: "Ruby", color: "#701516", highlight: "#701516" },
+ { value: 22.96, label: "JavaScript", color: "#f1e05a", highlight: "#f1e05a" },
+ { value: 7.9, label: "HTML", color: "#e44b23", highlight: "#e44b23" },
+ { value: 2.51, label: "CoffeeScript", color: "#244776", highlight: "#244776" }
+ ]
+
+ expect(languages.size).to eq(expected_languages.size)
+
+ expected_languages.size.times do |i|
+ a = expected_languages[i]
+ b = languages[i]
+
+ expect(a.keys.sort).to eq(b.keys.sort)
+ expect(a[:value]).to be_within(0.1).of(b[:value])
+
+ non_float_keys = a.keys - [:value]
+ expect(a.values_at(*non_float_keys)).to eq(b.values_at(*non_float_keys))
+ end
+ end
+
+ it "uses the repository's HEAD when no ref is passed" do
+ lang = repository.languages.first
+
+ expect(lang[:label]).to eq('Ruby')
+ end
+ end
+
+ it_behaves_like 'languages'
+
+ context 'with rugged', skip_gitaly_mock: true do
+ it_behaves_like 'languages'
+ end
+ end
+
def create_remote_branch(repository, remote_name, branch_name, source_branch_name)
source_branch = repository.branches.find { |branch| branch.name == source_branch_name }
rugged = repository.rugged
diff --git a/spec/lib/gitlab/gitaly_client/commit_service_spec.rb b/spec/lib/gitlab/gitaly_client/commit_service_spec.rb
index 0868c793a33..d71e0f84c65 100644
--- a/spec/lib/gitlab/gitaly_client/commit_service_spec.rb
+++ b/spec/lib/gitlab/gitaly_client/commit_service_spec.rb
@@ -3,7 +3,7 @@ require 'spec_helper'
describe Gitlab::GitalyClient::CommitService do
let(:project) { create(:project, :repository) }
let(:storage_name) { project.repository_storage }
- let(:relative_path) { project.path_with_namespace + '.git' }
+ let(:relative_path) { project.disk_path + '.git' }
let(:repository) { project.repository }
let(:repository_message) { repository.gitaly_repository }
let(:revision) { '913c66a37b4a45b9769037c55c2d238bd0942d2e' }
diff --git a/spec/lib/gitlab/gitaly_client/notification_service_spec.rb b/spec/lib/gitlab/gitaly_client/notification_service_spec.rb
index d9597c4aa78..1bcdd5e5497 100644
--- a/spec/lib/gitlab/gitaly_client/notification_service_spec.rb
+++ b/spec/lib/gitlab/gitaly_client/notification_service_spec.rb
@@ -4,7 +4,7 @@ describe Gitlab::GitalyClient::NotificationService do
describe '#post_receive' do
let(:project) { create(:empty_project) }
let(:storage_name) { project.repository_storage }
- let(:relative_path) { project.path_with_namespace + '.git' }
+ let(:relative_path) { project.disk_path + '.git' }
subject { described_class.new(project.repository) }
it 'sends a post_receive message' do
diff --git a/spec/lib/gitlab/gitaly_client/ref_service_spec.rb b/spec/lib/gitlab/gitaly_client/ref_service_spec.rb
index 0b1c890f956..2ad24119476 100644
--- a/spec/lib/gitlab/gitaly_client/ref_service_spec.rb
+++ b/spec/lib/gitlab/gitaly_client/ref_service_spec.rb
@@ -3,7 +3,7 @@ require 'spec_helper'
describe Gitlab::GitalyClient::RefService do
let(:project) { create(:empty_project) }
let(:storage_name) { project.repository_storage }
- let(:relative_path) { project.path_with_namespace + '.git' }
+ let(:relative_path) { project.disk_path + '.git' }
let(:client) { described_class.new(project.repository) }
describe '#branches' do
diff --git a/spec/lib/gitlab/github_import/wiki_formatter_spec.rb b/spec/lib/gitlab/github_import/wiki_formatter_spec.rb
index de50265bc14..fcd90fab547 100644
--- a/spec/lib/gitlab/github_import/wiki_formatter_spec.rb
+++ b/spec/lib/gitlab/github_import/wiki_formatter_spec.rb
@@ -9,9 +9,9 @@ describe Gitlab::GithubImport::WikiFormatter do
subject(:wiki) { described_class.new(project) }
- describe '#path_with_namespace' do
+ describe '#disk_path' do
it 'appends .wiki to project path' do
- expect(wiki.path_with_namespace).to eq 'gitlabhq/gitlabhq.wiki'
+ expect(wiki.disk_path).to eq project.disk_path + '.wiki'
end
end
diff --git a/spec/lib/gitlab/import_export/fork_spec.rb b/spec/lib/gitlab/import_export/fork_spec.rb
index 08588a76fe6..0ff64cbe880 100644
--- a/spec/lib/gitlab/import_export/fork_spec.rb
+++ b/spec/lib/gitlab/import_export/fork_spec.rb
@@ -5,7 +5,7 @@ describe 'forked project import' do
let!(:project_with_repo) { create(:project, name: 'test-repo-restorer', path: 'test-repo-restorer') }
let!(:project) { create(:empty_project, name: 'test-repo-restorer-no-repo', path: 'test-repo-restorer-no-repo') }
let(:export_path) { "#{Dir.tmpdir}/project_tree_saver_spec" }
- let(:shared) { Gitlab::ImportExport::Shared.new(relative_path: project.path_with_namespace) }
+ let(:shared) { Gitlab::ImportExport::Shared.new(relative_path: project.full_path) }
let(:forked_from_project) { create(:project) }
let(:fork_link) { create(:forked_project_link, forked_from_project: project_with_repo) }
let(:repo_saver) { Gitlab::ImportExport::RepoSaver.new(project: project_with_repo, shared: shared) }
diff --git a/spec/lib/gitlab/import_export/project_tree_saver_spec.rb b/spec/lib/gitlab/import_export/project_tree_saver_spec.rb
index 0c7e733b01f..a278f89c1a1 100644
--- a/spec/lib/gitlab/import_export/project_tree_saver_spec.rb
+++ b/spec/lib/gitlab/import_export/project_tree_saver_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
describe Gitlab::ImportExport::ProjectTreeSaver do
describe 'saves the project tree into a json object' do
- let(:shared) { Gitlab::ImportExport::Shared.new(relative_path: project.path_with_namespace) }
+ let(:shared) { Gitlab::ImportExport::Shared.new(relative_path: project.full_path) }
let(:project_tree_saver) { described_class.new(project: project, current_user: user, shared: shared) }
let(:export_path) { "#{Dir.tmpdir}/project_tree_saver_spec" }
let(:user) { create(:user) }
diff --git a/spec/lib/gitlab/import_export/repo_restorer_spec.rb b/spec/lib/gitlab/import_export/repo_restorer_spec.rb
index d3be2965bf4..82935af2670 100644
--- a/spec/lib/gitlab/import_export/repo_restorer_spec.rb
+++ b/spec/lib/gitlab/import_export/repo_restorer_spec.rb
@@ -6,7 +6,7 @@ describe Gitlab::ImportExport::RepoRestorer do
let!(:project_with_repo) { create(:project, name: 'test-repo-restorer', path: 'test-repo-restorer') }
let!(:project) { create(:empty_project) }
let(:export_path) { "#{Dir.tmpdir}/project_tree_saver_spec" }
- let(:shared) { Gitlab::ImportExport::Shared.new(relative_path: project.path_with_namespace) }
+ let(:shared) { Gitlab::ImportExport::Shared.new(relative_path: project.full_path) }
let(:bundler) { Gitlab::ImportExport::RepoSaver.new(project: project_with_repo, shared: shared) }
let(:bundle_path) { File.join(shared.export_path, Gitlab::ImportExport.project_bundle_filename) }
let(:restorer) do
diff --git a/spec/lib/gitlab/import_export/repo_saver_spec.rb b/spec/lib/gitlab/import_export/repo_saver_spec.rb
index 87af13e0beb..0ba199bbb05 100644
--- a/spec/lib/gitlab/import_export/repo_saver_spec.rb
+++ b/spec/lib/gitlab/import_export/repo_saver_spec.rb
@@ -5,7 +5,7 @@ describe Gitlab::ImportExport::RepoSaver do
let(:user) { create(:user) }
let!(:project) { create(:empty_project, :public, name: 'searchable_project') }
let(:export_path) { "#{Dir.tmpdir}/project_tree_saver_spec" }
- let(:shared) { Gitlab::ImportExport::Shared.new(relative_path: project.path_with_namespace) }
+ let(:shared) { Gitlab::ImportExport::Shared.new(relative_path: project.full_path) }
let(:bundler) { described_class.new(project: project, shared: shared) }
before do
diff --git a/spec/lib/gitlab/import_export/wiki_repo_saver_spec.rb b/spec/lib/gitlab/import_export/wiki_repo_saver_spec.rb
index 78137aeff5e..caf08e674d3 100644
--- a/spec/lib/gitlab/import_export/wiki_repo_saver_spec.rb
+++ b/spec/lib/gitlab/import_export/wiki_repo_saver_spec.rb
@@ -5,7 +5,7 @@ describe Gitlab::ImportExport::WikiRepoSaver do
let(:user) { create(:user) }
let!(:project) { create(:empty_project, :public, name: 'searchable_project') }
let(:export_path) { "#{Dir.tmpdir}/project_tree_saver_spec" }
- let(:shared) { Gitlab::ImportExport::Shared.new(relative_path: project.path_with_namespace) }
+ let(:shared) { Gitlab::ImportExport::Shared.new(relative_path: project.full_path) }
let(:wiki_bundler) { described_class.new(project: project, shared: shared) }
let!(:project_wiki) { ProjectWiki.new(project, user) }
diff --git a/spec/lib/gitlab/quick_actions/dsl_spec.rb b/spec/lib/gitlab/quick_actions/dsl_spec.rb
index a4bb3f911d7..ff59dc48bcb 100644
--- a/spec/lib/gitlab/quick_actions/dsl_spec.rb
+++ b/spec/lib/gitlab/quick_actions/dsl_spec.rb
@@ -42,13 +42,18 @@ describe Gitlab::QuickActions::Dsl do
command :with_params_parsing do |parsed|
parsed
end
+
+ params '<Comment>'
+ substitution :something do |text|
+ "#{text} Some complicated thing you want in here"
+ end
end
end
describe '.command_definitions' do
it 'returns an array with commands definitions' do
no_args_def, explanation_with_aliases_def, dynamic_description_def,
- cc_def, cond_action_def, with_params_parsing_def =
+ cc_def, cond_action_def, with_params_parsing_def, substitution_def =
DummyClass.command_definitions
expect(no_args_def.name).to eq(:no_args)
@@ -104,6 +109,15 @@ describe Gitlab::QuickActions::Dsl do
expect(with_params_parsing_def.condition_block).to be_nil
expect(with_params_parsing_def.action_block).to be_a_kind_of(Proc)
expect(with_params_parsing_def.parse_params_block).to be_a_kind_of(Proc)
+
+ expect(substitution_def.name).to eq(:something)
+ expect(substitution_def.aliases).to eq([])
+ expect(substitution_def.description).to eq('')
+ expect(substitution_def.explanation).to eq('')
+ expect(substitution_def.params).to eq(['<Comment>'])
+ expect(substitution_def.condition_block).to be_nil
+ expect(substitution_def.action_block.call('text')).to eq('text Some complicated thing you want in here')
+ expect(substitution_def.parse_params_block).to be_nil
end
end
end
diff --git a/spec/lib/gitlab/quick_actions/extractor_spec.rb b/spec/lib/gitlab/quick_actions/extractor_spec.rb
index 9d32938e155..f7c288f2393 100644
--- a/spec/lib/gitlab/quick_actions/extractor_spec.rb
+++ b/spec/lib/gitlab/quick_actions/extractor_spec.rb
@@ -9,6 +9,11 @@ describe Gitlab::QuickActions::Extractor do
command(:assign) { }
command(:labels) { }
command(:power) { }
+ command(:noop_command)
+ substitution(:substitution) { 'foo' }
+ substitution :shrug do |comment|
+ "#{comment} SHRUG"
+ end
end.command_definitions
end
@@ -177,6 +182,38 @@ describe Gitlab::QuickActions::Extractor do
expect(msg).to eq "hello\nworld"
end
+ it 'does not extract noop commands' do
+ msg = %(hello\nworld\n/reopen\n/noop_command)
+ msg, commands = extractor.extract_commands(msg)
+
+ expect(commands).to eq [['reopen']]
+ expect(msg).to eq "hello\nworld\n/noop_command"
+ end
+
+ it 'extracts and performs substitution commands' do
+ msg = %(hello\nworld\n/reopen\n/substitution)
+ msg, commands = extractor.extract_commands(msg)
+
+ expect(commands).to eq [['reopen'], ['substitution']]
+ expect(msg).to eq "hello\nworld\nfoo"
+ end
+
+ it 'extracts and performs substitution commands' do
+ msg = %(hello\nworld\n/reopen\n/shrug this is great?)
+ msg, commands = extractor.extract_commands(msg)
+
+ expect(commands).to eq [['reopen'], ['shrug', 'this is great?']]
+ expect(msg).to eq "hello\nworld\nthis is great? SHRUG"
+ end
+
+ it 'extracts and performs substitution commands with comments' do
+ msg = %(hello\nworld\n/reopen\n/substitution wow this is a thing.)
+ msg, commands = extractor.extract_commands(msg)
+
+ expect(commands).to eq [['reopen'], ['substitution', 'wow this is a thing.']]
+ expect(msg).to eq "hello\nworld\nfoo"
+ end
+
it 'extracts multiple commands' do
msg = %(hello\n/power @user.name %9.10 ~"bar baz.2" label\nworld\n/reopen)
msg, commands = extractor.extract_commands(msg)
diff --git a/spec/lib/gitlab/quick_actions/substitution_definition_spec.rb b/spec/lib/gitlab/quick_actions/substitution_definition_spec.rb
new file mode 100644
index 00000000000..1bb8bc51c96
--- /dev/null
+++ b/spec/lib/gitlab/quick_actions/substitution_definition_spec.rb
@@ -0,0 +1,42 @@
+require 'spec_helper'
+
+describe Gitlab::QuickActions::SubstitutionDefinition do
+ let(:content) do
+ <<EOF
+Hello! Let's do this!
+/sub_name I like this stuff
+EOF
+ end
+ subject do
+ described_class.new(:sub_name, action_block: proc { |text| "#{text} foo" })
+ end
+
+ describe '#perform_substitution!' do
+ it 'returns nil if content is nil' do
+ expect(subject.perform_substitution(self, nil)).to be_nil
+ end
+
+ it 'performs the substitution by default' do
+ expect(subject.perform_substitution(self, content)).to eq <<EOF
+Hello! Let's do this!
+I like this stuff foo
+EOF
+ end
+ end
+
+ describe '#match' do
+ it 'checks the content for the command' do
+ expect(subject.match(content)).to be_truthy
+ end
+
+ it 'returns the match data' do
+ data = subject.match(content)
+ expect(data).to be_a(MatchData)
+ expect(data[1]).to eq('I like this stuff')
+ end
+
+ it 'is nil if content does not have the command' do
+ expect(subject.match('blah')).to be_falsey
+ end
+ end
+end
diff --git a/spec/lib/gitlab/slash_commands/command_spec.rb b/spec/lib/gitlab/slash_commands/command_spec.rb
index f0ecf59406a..88f73bf90cd 100644
--- a/spec/lib/gitlab/slash_commands/command_spec.rb
+++ b/spec/lib/gitlab/slash_commands/command_spec.rb
@@ -80,7 +80,7 @@ describe Gitlab::SlashCommands::Command do
it 'returns error' do
expect(subject[:response_type]).to be(:ephemeral)
- expect(subject[:text]).to include('Too many actions defined')
+ expect(subject[:text]).to include("Couldn't find a deployment manual action.")
end
end
end
diff --git a/spec/lib/gitlab/slash_commands/deploy_spec.rb b/spec/lib/gitlab/slash_commands/deploy_spec.rb
index e52aaed7328..c3fb7d5adea 100644
--- a/spec/lib/gitlab/slash_commands/deploy_spec.rb
+++ b/spec/lib/gitlab/slash_commands/deploy_spec.rb
@@ -22,7 +22,7 @@ describe Gitlab::SlashCommands::Deploy do
context 'if no environment is defined' do
it 'does not execute an action' do
expect(subject[:response_type]).to be(:ephemeral)
- expect(subject[:text]).to eq("No action found to be executed")
+ expect(subject[:text]).to eq "Couldn't find a deployment manual action."
end
end
@@ -35,12 +35,12 @@ describe Gitlab::SlashCommands::Deploy do
context 'without actions' do
it 'does not execute an action' do
expect(subject[:response_type]).to be(:ephemeral)
- expect(subject[:text]).to eq("No action found to be executed")
+ expect(subject[:text]).to eq "Couldn't find a deployment manual action."
end
end
- context 'with action' do
- let!(:manual1) do
+ context 'when single action has been matched' do
+ before do
create(:ci_build, :manual, pipeline: pipeline,
name: 'first',
environment: 'production')
@@ -48,31 +48,61 @@ describe Gitlab::SlashCommands::Deploy do
it 'returns success result' do
expect(subject[:response_type]).to be(:in_channel)
- expect(subject[:text]).to start_with('Deployment started from staging to production')
+ expect(subject[:text])
+ .to start_with('Deployment started from staging to production')
end
+ end
+
+ context 'when more than one action has been matched' do
+ context 'when there is no specific actions with a environment name' do
+ before do
+ create(:ci_build, :manual, pipeline: pipeline,
+ name: 'first',
+ environment: 'production')
- context 'when duplicate action exists' do
- let!(:manual2) do
create(:ci_build, :manual, pipeline: pipeline,
name: 'second',
environment: 'production')
end
- it 'returns error' do
+ it 'returns error about too many actions defined' do
+ expect(subject[:text]).to eq("Couldn't find a deployment manual action.")
expect(subject[:response_type]).to be(:ephemeral)
- expect(subject[:text]).to eq('Too many actions defined')
end
end
- context 'when teardown action exists' do
- let!(:teardown) do
+ context 'when one of the actions is environement specific action' do
+ before do
+ create(:ci_build, :manual, pipeline: pipeline,
+ name: 'first',
+ environment: 'production')
+
+ create(:ci_build, :manual, pipeline: pipeline,
+ name: 'production',
+ environment: 'production')
+ end
+
+ it 'deploys to production' do
+ expect(subject[:text])
+ .to start_with('Deployment started from staging to production')
+ expect(subject[:response_type]).to be(:in_channel)
+ end
+ end
+
+ context 'when one of the actions is a teardown action' do
+ before do
+ create(:ci_build, :manual, pipeline: pipeline,
+ name: 'first',
+ environment: 'production')
+
create(:ci_build, :manual, :teardown_environment,
pipeline: pipeline, name: 'teardown', environment: 'production')
end
- it 'returns the success message' do
+ it 'deploys to production' do
+ expect(subject[:text])
+ .to start_with('Deployment started from staging to production')
expect(subject[:response_type]).to be(:in_channel)
- expect(subject[:text]).to start_with('Deployment started from staging to production')
end
end
end
diff --git a/spec/lib/gitlab/slash_commands/presenters/deploy_spec.rb b/spec/lib/gitlab/slash_commands/presenters/deploy_spec.rb
index dee3c77db27..d16d122c64e 100644
--- a/spec/lib/gitlab/slash_commands/presenters/deploy_spec.rb
+++ b/spec/lib/gitlab/slash_commands/presenters/deploy_spec.rb
@@ -17,8 +17,8 @@ describe Gitlab::SlashCommands::Presenters::Deploy do
end
end
- describe '#no_actions' do
- subject { described_class.new(nil).no_actions }
+ describe '#action_not_found' do
+ subject { described_class.new(nil).action_not_found }
it { is_expected.to have_key(:text) }
it { is_expected.to have_key(:response_type) }
@@ -27,21 +27,7 @@ describe Gitlab::SlashCommands::Presenters::Deploy do
it 'tells the user there is no action' do
expect(subject[:response_type]).to be(:ephemeral)
- expect(subject[:text]).to eq("No action found to be executed")
- end
- end
-
- describe '#too_many_actions' do
- subject { described_class.new([]).too_many_actions }
-
- it { is_expected.to have_key(:text) }
- it { is_expected.to have_key(:response_type) }
- it { is_expected.to have_key(:status) }
- it { is_expected.not_to have_key(:attachments) }
-
- it 'tells the user there is no action' do
- expect(subject[:response_type]).to be(:ephemeral)
- expect(subject[:text]).to eq("Too many actions defined")
+ expect(subject[:text]).to eq "Couldn't find a deployment manual action."
end
end
end
diff --git a/spec/lib/gitlab/url_builder_spec.rb b/spec/lib/gitlab/url_builder_spec.rb
index 50422020823..7412f22640c 100644
--- a/spec/lib/gitlab/url_builder_spec.rb
+++ b/spec/lib/gitlab/url_builder_spec.rb
@@ -8,7 +8,7 @@ describe Gitlab::UrlBuilder do
url = described_class.build(commit)
- expect(url).to eq "#{Settings.gitlab['url']}/#{commit.project.path_with_namespace}/commit/#{commit.id}"
+ expect(url).to eq "#{Settings.gitlab['url']}/#{commit.project.full_path}/commit/#{commit.id}"
end
end
@@ -18,7 +18,7 @@ describe Gitlab::UrlBuilder do
url = described_class.build(issue)
- expect(url).to eq "#{Settings.gitlab['url']}/#{issue.project.path_with_namespace}/issues/#{issue.iid}"
+ expect(url).to eq "#{Settings.gitlab['url']}/#{issue.project.full_path}/issues/#{issue.iid}"
end
end
@@ -28,7 +28,7 @@ describe Gitlab::UrlBuilder do
url = described_class.build(merge_request)
- expect(url).to eq "#{Settings.gitlab['url']}/#{merge_request.project.path_with_namespace}/merge_requests/#{merge_request.iid}"
+ expect(url).to eq "#{Settings.gitlab['url']}/#{merge_request.project.full_path}/merge_requests/#{merge_request.iid}"
end
end
@@ -39,7 +39,7 @@ describe Gitlab::UrlBuilder do
url = described_class.build(note)
- expect(url).to eq "#{Settings.gitlab['url']}/#{note.project.path_with_namespace}/commit/#{note.commit_id}#note_#{note.id}"
+ expect(url).to eq "#{Settings.gitlab['url']}/#{note.project.full_path}/commit/#{note.commit_id}#note_#{note.id}"
end
end
@@ -49,7 +49,7 @@ describe Gitlab::UrlBuilder do
url = described_class.build(note)
- expect(url).to eq "#{Settings.gitlab['url']}/#{note.project.path_with_namespace}/commit/#{note.commit_id}#note_#{note.id}"
+ expect(url).to eq "#{Settings.gitlab['url']}/#{note.project.full_path}/commit/#{note.commit_id}#note_#{note.id}"
end
end
@@ -60,7 +60,7 @@ describe Gitlab::UrlBuilder do
url = described_class.build(note)
- expect(url).to eq "#{Settings.gitlab['url']}/#{issue.project.path_with_namespace}/issues/#{issue.iid}#note_#{note.id}"
+ expect(url).to eq "#{Settings.gitlab['url']}/#{issue.project.full_path}/issues/#{issue.iid}#note_#{note.id}"
end
end
@@ -71,7 +71,7 @@ describe Gitlab::UrlBuilder do
url = described_class.build(note)
- expect(url).to eq "#{Settings.gitlab['url']}/#{merge_request.project.path_with_namespace}/merge_requests/#{merge_request.iid}#note_#{note.id}"
+ expect(url).to eq "#{Settings.gitlab['url']}/#{merge_request.project.full_path}/merge_requests/#{merge_request.iid}#note_#{note.id}"
end
end
@@ -82,7 +82,7 @@ describe Gitlab::UrlBuilder do
url = described_class.build(note)
- expect(url).to eq "#{Settings.gitlab['url']}/#{merge_request.project.path_with_namespace}/merge_requests/#{merge_request.iid}#note_#{note.id}"
+ expect(url).to eq "#{Settings.gitlab['url']}/#{merge_request.project.full_path}/merge_requests/#{merge_request.iid}#note_#{note.id}"
end
end
@@ -93,7 +93,7 @@ describe Gitlab::UrlBuilder do
url = described_class.build(note)
- expect(url).to eq "#{Settings.gitlab['url']}/#{project_snippet.project.path_with_namespace}/snippets/#{note.noteable_id}#note_#{note.id}"
+ expect(url).to eq "#{Settings.gitlab['url']}/#{project_snippet.project.full_path}/snippets/#{note.noteable_id}#note_#{note.id}"
end
end
diff --git a/spec/migrations/cleanup_namespaceless_pending_delete_projects_spec.rb b/spec/migrations/cleanup_namespaceless_pending_delete_projects_spec.rb
index 49e750a3f4d..dd634f2c024 100644
--- a/spec/migrations/cleanup_namespaceless_pending_delete_projects_spec.rb
+++ b/spec/migrations/cleanup_namespaceless_pending_delete_projects_spec.rb
@@ -4,7 +4,7 @@ require Rails.root.join('db', 'post_migrate', '20170502101023_cleanup_namespacel
describe CleanupNamespacelessPendingDeleteProjects do
before do
# Stub after_save callbacks that will fail when Project has no namespace
- allow_any_instance_of(Project).to receive(:ensure_dir_exist).and_return(nil)
+ allow_any_instance_of(Project).to receive(:ensure_storage_path_exist).and_return(nil)
allow_any_instance_of(Project).to receive(:update_project_statistics).and_return(nil)
end
diff --git a/spec/migrations/migrate_process_commit_worker_jobs_spec.rb b/spec/migrations/migrate_process_commit_worker_jobs_spec.rb
index 5b633dd349b..cf2d5827306 100644
--- a/spec/migrations/migrate_process_commit_worker_jobs_spec.rb
+++ b/spec/migrations/migrate_process_commit_worker_jobs_spec.rb
@@ -20,7 +20,7 @@ describe MigrateProcessCommitWorkerJobs do
.find_including_path(project.id)
expect(migration_project[:path_with_namespace])
- .to eq(project.path_with_namespace)
+ .to eq(project.full_path)
end
end
diff --git a/spec/migrations/turn_nested_groups_into_regular_groups_for_mysql_spec.rb b/spec/migrations/turn_nested_groups_into_regular_groups_for_mysql_spec.rb
index 42109fd0743..6f7a730edff 100644
--- a/spec/migrations/turn_nested_groups_into_regular_groups_for_mysql_spec.rb
+++ b/spec/migrations/turn_nested_groups_into_regular_groups_for_mysql_spec.rb
@@ -44,7 +44,7 @@ describe TurnNestedGroupsIntoRegularGroupsForMysql do
end
it 'renames projects of the nested group' do
- expect(updated_project.path_with_namespace)
+ expect(updated_project.full_path)
.to eq("#{parent_group.name}-#{child_group.name}/#{updated_project.path}")
end
diff --git a/spec/models/commit_spec.rb b/spec/models/commit_spec.rb
index 2285c338599..595c54890ab 100644
--- a/spec/models/commit_spec.rb
+++ b/spec/models/commit_spec.rb
@@ -161,7 +161,7 @@ eos
end
it 'detects issues that this commit is marked as closing' do
- ext_ref = "#{other_project.path_with_namespace}##{other_issue.iid}"
+ ext_ref = "#{other_project.full_path}##{other_issue.iid}"
allow(commit).to receive_messages(
safe_message: "Fixes ##{issue.iid} and #{ext_ref}",
diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb
index b2dd02553c1..d9903b9b951 100644
--- a/spec/models/merge_request_spec.rb
+++ b/spec/models/merge_request_spec.rb
@@ -250,7 +250,7 @@ describe MergeRequest do
end
it 'returns a String reference with the full path' do
- expect(merge_request.to_reference(full: true)).to eq(project.path_with_namespace + '!1')
+ expect(merge_request.to_reference(full: true)).to eq(project.full_path + '!1')
end
end
diff --git a/spec/models/notification_setting_spec.rb b/spec/models/notification_setting_spec.rb
index 07e296424ca..2a0d102d3fe 100644
--- a/spec/models/notification_setting_spec.rb
+++ b/spec/models/notification_setting_spec.rb
@@ -63,24 +63,20 @@ RSpec.describe NotificationSetting do
end
end
- describe 'event_enabled?' do
+ describe '#event_enabled?' do
before do
subject.update!(user: create(:user))
end
context 'for an event with a matching column name' do
- before do
- subject.update!(events: { new_note: true }.to_json)
- end
-
it 'returns the value of the column' do
- subject.update!(new_note: false)
+ subject.update!(new_note: true)
- expect(subject.event_enabled?(:new_note)).to be(false)
+ expect(subject.event_enabled?(:new_note)).to be(true)
end
context 'when the column has a nil value' do
- it 'returns the value from the events hash' do
+ it 'returns false' do
expect(subject.event_enabled?(:new_note)).to be(false)
end
end
diff --git a/spec/models/project_label_spec.rb b/spec/models/project_label_spec.rb
index add7e85f388..c47f32d8d77 100644
--- a/spec/models/project_label_spec.rb
+++ b/spec/models/project_label_spec.rb
@@ -105,14 +105,14 @@ describe ProjectLabel do
context 'using name' do
it 'returns cross reference with label name' do
expect(label.to_reference(project, format: :name))
- .to eq %Q(#{label.project.path_with_namespace}~"#{label.name}")
+ .to eq %Q(#{label.project.full_path}~"#{label.name}")
end
end
context 'using id' do
it 'returns cross reference with label id' do
expect(label.to_reference(project, format: :id))
- .to eq %Q(#{label.project.path_with_namespace}~#{label.id})
+ .to eq %Q(#{label.project.full_path}~#{label.id})
end
end
end
diff --git a/spec/models/project_services/gitlab_issue_tracker_service_spec.rb b/spec/models/project_services/gitlab_issue_tracker_service_spec.rb
index d19dab8fd39..45cc78c8ff4 100644
--- a/spec/models/project_services/gitlab_issue_tracker_service_spec.rb
+++ b/spec/models/project_services/gitlab_issue_tracker_service_spec.rb
@@ -31,9 +31,9 @@ describe GitlabIssueTrackerService do
end
it 'gives the correct path' do
- expect(service.project_url).to eq("http://#{Gitlab.config.gitlab.host}/gitlab/root/#{project.path_with_namespace}/issues")
- expect(service.new_issue_url).to eq("http://#{Gitlab.config.gitlab.host}/gitlab/root/#{project.path_with_namespace}/issues/new")
- expect(service.issue_url(432)).to eq("http://#{Gitlab.config.gitlab.host}/gitlab/root/#{project.path_with_namespace}/issues/432")
+ expect(service.project_url).to eq("http://#{Gitlab.config.gitlab.host}/gitlab/root/#{project.full_path}/issues")
+ expect(service.new_issue_url).to eq("http://#{Gitlab.config.gitlab.host}/gitlab/root/#{project.full_path}/issues/new")
+ expect(service.issue_url(432)).to eq("http://#{Gitlab.config.gitlab.host}/gitlab/root/#{project.full_path}/issues/432")
end
end
@@ -43,9 +43,9 @@ describe GitlabIssueTrackerService do
end
it 'gives the correct path' do
- expect(service.issue_tracker_path).to eq("/gitlab/root/#{project.path_with_namespace}/issues")
- expect(service.new_issue_path).to eq("/gitlab/root/#{project.path_with_namespace}/issues/new")
- expect(service.issue_path(432)).to eq("/gitlab/root/#{project.path_with_namespace}/issues/432")
+ expect(service.issue_tracker_path).to eq("/gitlab/root/#{project.full_path}/issues")
+ expect(service.new_issue_path).to eq("/gitlab/root/#{project.full_path}/issues/new")
+ expect(service.issue_path(432)).to eq("/gitlab/root/#{project.full_path}/issues/432")
end
end
end
diff --git a/spec/models/project_services/jira_service_spec.rb b/spec/models/project_services/jira_service_spec.rb
index 8f34b44930e..bc9374d6dbb 100644
--- a/spec/models/project_services/jira_service_spec.rb
+++ b/spec/models/project_services/jira_service_spec.rb
@@ -135,7 +135,7 @@ describe JiraService do
body: hash_including(
GlobalID: "GitLab",
object: {
- url: "#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}/commit/#{merge_request.diff_head_sha}",
+ url: "#{Gitlab.config.gitlab.url}/#{project.full_path}/commit/#{merge_request.diff_head_sha}",
title: "GitLab: Solved by commit #{merge_request.diff_head_sha}.",
icon: { title: "GitLab", url16x16: "https://gitlab.com/favicon.ico" },
status: { resolved: true }
@@ -159,7 +159,7 @@ describe JiraService do
@jira_service.close_issue(merge_request, ExternalIssue.new("JIRA-123", project))
expect(WebMock).to have_requested(:post, @comment_url).with(
- body: /#{custom_base_url}\/#{project.path_with_namespace}\/commit\/#{merge_request.diff_head_sha}/
+ body: /#{custom_base_url}\/#{project.full_path}\/commit\/#{merge_request.diff_head_sha}/
).once
end
@@ -174,7 +174,7 @@ describe JiraService do
@jira_service.close_issue(merge_request, ExternalIssue.new("JIRA-123", project))
expect(WebMock).to have_requested(:post, @comment_url).with(
- body: /#{Gitlab.config.gitlab.url}\/#{project.path_with_namespace}\/commit\/#{merge_request.diff_head_sha}/
+ body: /#{Gitlab.config.gitlab.url}\/#{project.full_path}\/commit\/#{merge_request.diff_head_sha}/
).once
end
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index 473b7a88d61..29cc9d35fc9 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -306,6 +306,7 @@ describe Project do
it { is_expected.to respond_to(:execute_hooks) }
it { is_expected.to respond_to(:owner) }
it { is_expected.to respond_to(:path_with_namespace) }
+ it { is_expected.to respond_to(:full_path) }
end
describe 'delegation' do
@@ -460,7 +461,7 @@ describe Project do
end
it 'returns the address to create a new issue' do
- address = "p+#{project.path_with_namespace}+#{user.incoming_email_token}@gl.ab"
+ address = "p+#{project.full_path}+#{user.incoming_email_token}@gl.ab"
expect(project.new_issue_address(user)).to eq(address)
end
@@ -1155,6 +1156,33 @@ describe Project do
end
end
+ describe '#pages_url' do
+ let(:group) { create :group, name: group_name }
+ let(:project) { create :empty_project, namespace: group, name: project_name }
+ let(:domain) { 'Example.com' }
+
+ subject { project.pages_url }
+
+ before do
+ allow(Settings.pages).to receive(:host).and_return(domain)
+ allow(Gitlab.config.pages).to receive(:url).and_return('http://example.com')
+ end
+
+ context 'group page' do
+ let(:group_name) { 'Group' }
+ let(:project_name) { 'group.example.com' }
+
+ it { is_expected.to eq("http://group.example.com") }
+ end
+
+ context 'project page' do
+ let(:group_name) { 'Group' }
+ let(:project_name) { 'Project' }
+
+ it { is_expected.to eq("http://group.example.com/project") }
+ end
+ end
+
describe '.search' do
let(:project) { create(:empty_project, description: 'kitten mittens') }
@@ -1341,7 +1369,7 @@ describe Project do
context 'using a regular repository' do
it 'creates the repository' do
expect(shell).to receive(:add_repository)
- .with(project.repository_storage_path, project.path_with_namespace)
+ .with(project.repository_storage_path, project.disk_path)
.and_return(true)
expect(project.repository).to receive(:after_create)
@@ -1351,7 +1379,7 @@ describe Project do
it 'adds an error if the repository could not be created' do
expect(shell).to receive(:add_repository)
- .with(project.repository_storage_path, project.path_with_namespace)
+ .with(project.repository_storage_path, project.disk_path)
.and_return(false)
expect(project.repository).not_to receive(:after_create)
@@ -1384,7 +1412,7 @@ describe Project do
.and_return(false)
allow(shell).to receive(:add_repository)
- .with(project.repository_storage_path, project.path_with_namespace)
+ .with(project.repository_storage_path, project.disk_path)
.and_return(true)
expect(project).to receive(:create_repository).with(force: true)
@@ -1408,7 +1436,7 @@ describe Project do
.and_return(false)
expect(shell).to receive(:add_repository)
- .with(project.repository_storage_path, project.path_with_namespace)
+ .with(project.repository_storage_path, project.disk_path)
.and_return(true)
project.ensure_repository
@@ -1572,7 +1600,7 @@ describe Project do
before do
allow_any_instance_of(Gitlab::Shell).to receive(:import_repository)
- .with(project.repository_storage_path, project.path_with_namespace, project.import_url)
+ .with(project.repository_storage_path, project.disk_path, project.import_url)
.and_return(true)
expect_any_instance_of(Repository).to receive(:after_import)
@@ -1710,7 +1738,7 @@ describe Project do
it 'schedules a RepositoryForkWorker job' do
expect(RepositoryForkWorker).to receive(:perform_async)
.with(project.id, forked_from_project.repository_storage_path,
- forked_from_project.path_with_namespace, project.namespace.full_path)
+ forked_from_project.disk_path, project.namespace.full_path)
project.add_import_job
end
@@ -2238,19 +2266,43 @@ describe Project do
end
describe '#remove_private_deploy_keys' do
- it 'removes the private deploy keys of a project' do
- project = create(:empty_project)
+ let!(:project) { create(:empty_project) }
+
+ context 'for a private deploy key' do
+ let!(:key) { create(:deploy_key, public: false) }
+ let!(:deploy_keys_project) { create(:deploy_keys_project, deploy_key: key, project: project) }
+
+ context 'when the key is not linked to another project' do
+ it 'removes the key' do
+ project.remove_private_deploy_keys
- private_key = create(:deploy_key, public: false)
- public_key = create(:deploy_key, public: true)
+ expect(project.deploy_keys).not_to include(key)
+ end
+ end
- create(:deploy_keys_project, deploy_key: private_key, project: project)
- create(:deploy_keys_project, deploy_key: public_key, project: project)
+ context 'when the key is linked to another project' do
+ before do
+ another_project = create(:empty_project)
+ create(:deploy_keys_project, deploy_key: key, project: another_project)
+ end
- project.remove_private_deploy_keys
+ it 'does not remove the key' do
+ project.remove_private_deploy_keys
- expect(project.deploy_keys.where(public: false).any?).to eq(false)
- expect(project.deploy_keys.where(public: true).any?).to eq(true)
+ expect(project.deploy_keys).to include(key)
+ end
+ end
+ end
+
+ context 'for a public deploy key' do
+ let!(:key) { create(:deploy_key, public: true) }
+ let!(:deploy_keys_project) { create(:deploy_keys_project, deploy_key: key, project: project) }
+
+ it 'does not remove the key' do
+ project.remove_private_deploy_keys
+
+ expect(project.deploy_keys).to include(key)
+ end
end
end
end
diff --git a/spec/models/project_wiki_spec.rb b/spec/models/project_wiki_spec.rb
index c6ceb092810..484fcfc88a3 100644
--- a/spec/models/project_wiki_spec.rb
+++ b/spec/models/project_wiki_spec.rb
@@ -15,19 +15,23 @@ describe ProjectWiki do
describe "#path_with_namespace" do
it "returns the project path with namespace with the .wiki extension" do
- expect(subject.path_with_namespace).to eq(project.path_with_namespace + ".wiki")
+ expect(subject.path_with_namespace).to eq(project.full_path + '.wiki')
+ end
+
+ it 'returns the same value as #full_path' do
+ expect(subject.path_with_namespace).to eq(subject.full_path)
end
end
describe '#web_url' do
it 'returns the full web URL to the wiki' do
- expect(subject.web_url).to eq("#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}/wikis/home")
+ expect(subject.web_url).to eq("#{Gitlab.config.gitlab.url}/#{project.full_path}/wikis/home")
end
end
describe "#url_to_repo" do
it "returns the correct ssh url to the repo" do
- expect(subject.url_to_repo).to eq(gitlab_shell.url_to_repo(subject.path_with_namespace))
+ expect(subject.url_to_repo).to eq(gitlab_shell.url_to_repo(subject.full_path))
end
end
@@ -41,7 +45,7 @@ describe ProjectWiki do
let(:project) { create :empty_project }
it 'returns the full http url to the repo' do
- expected_url = "#{Gitlab.config.gitlab.url}/#{subject.path_with_namespace}.git"
+ expected_url = "#{Gitlab.config.gitlab.url}/#{subject.full_path}.git"
expect(project_wiki.http_url_to_repo).to eq(expected_url)
expect(project_wiki.http_url_to_repo).not_to include('@')
@@ -50,7 +54,7 @@ describe ProjectWiki do
describe "#wiki_base_path" do
it "returns the wiki base path" do
- wiki_base_path = "#{Gitlab.config.gitlab.relative_url_root}/#{project.path_with_namespace}/wikis"
+ wiki_base_path = "#{Gitlab.config.gitlab.relative_url_root}/#{project.full_path}/wikis"
expect(subject.wiki_base_path).to eq(wiki_base_path)
end
@@ -77,7 +81,7 @@ describe ProjectWiki do
allow_any_instance_of(Gitlab::Shell).to receive(:add_repository) do
create_temp_repo("#{Rails.root}/tmp/test-git-base-path/non-existant.wiki.git")
end
- allow(project).to receive(:path_with_namespace).and_return("non-existant")
+ allow(project).to receive(:full_path).and_return("non-existant")
end
describe '#empty?' do
@@ -269,7 +273,7 @@ describe ProjectWiki do
describe '#create_repo!' do
it 'creates a repository' do
expect(subject).to receive(:init_repo)
- .with(subject.path_with_namespace)
+ .with(subject.full_path)
.and_return(true)
expect(subject.repository).to receive(:after_create)
diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb
index 07ed66e127a..0fd3a4d622a 100644
--- a/spec/models/repository_spec.rb
+++ b/spec/models/repository_spec.rb
@@ -962,7 +962,7 @@ describe Repository do
end
it 'returns false if no full path can be constructed' do
- allow(repository).to receive(:path_with_namespace).and_return(nil)
+ allow(repository).to receive(:full_path).and_return(nil)
expect(repository.exists?).to eq(false)
end
diff --git a/spec/requests/api/group_variables_spec.rb b/spec/requests/api/group_variables_spec.rb
new file mode 100644
index 00000000000..402ea057cc5
--- /dev/null
+++ b/spec/requests/api/group_variables_spec.rb
@@ -0,0 +1,221 @@
+require 'spec_helper'
+
+describe API::GroupVariables do
+ let(:group) { create(:group) }
+ let(:user) { create(:user) }
+
+ describe 'GET /groups/:id/variables' do
+ let!(:variable) { create(:ci_group_variable, group: group) }
+
+ context 'authorized user with proper permissions' do
+ before do
+ group.add_master(user)
+ end
+
+ it 'returns group variables' do
+ get api("/groups/#{group.id}/variables", user)
+
+ expect(response).to have_http_status(200)
+ expect(json_response).to be_a(Array)
+ end
+ end
+
+ context 'authorized user with invalid permissions' do
+ it 'does not return group variables' do
+ get api("/groups/#{group.id}/variables", user)
+
+ expect(response).to have_http_status(403)
+ end
+ end
+
+ context 'unauthorized user' do
+ it 'does not return group variables' do
+ get api("/groups/#{group.id}/variables")
+
+ expect(response).to have_http_status(401)
+ end
+ end
+ end
+
+ describe 'GET /groups/:id/variables/:key' do
+ let!(:variable) { create(:ci_group_variable, group: group) }
+
+ context 'authorized user with proper permissions' do
+ before do
+ group.add_master(user)
+ end
+
+ it 'returns group variable details' do
+ get api("/groups/#{group.id}/variables/#{variable.key}", user)
+
+ expect(response).to have_http_status(200)
+ expect(json_response['value']).to eq(variable.value)
+ expect(json_response['protected']).to eq(variable.protected?)
+ end
+
+ it 'responds with 404 Not Found if requesting non-existing variable' do
+ get api("/groups/#{group.id}/variables/non_existing_variable", user)
+
+ expect(response).to have_http_status(404)
+ end
+ end
+
+ context 'authorized user with invalid permissions' do
+ it 'does not return group variable details' do
+ get api("/groups/#{group.id}/variables/#{variable.key}", user)
+
+ expect(response).to have_http_status(403)
+ end
+ end
+
+ context 'unauthorized user' do
+ it 'does not return group variable details' do
+ get api("/groups/#{group.id}/variables/#{variable.key}")
+
+ expect(response).to have_http_status(401)
+ end
+ end
+ end
+
+ describe 'POST /groups/:id/variables' do
+ context 'authorized user with proper permissions' do
+ let!(:variable) { create(:ci_group_variable, group: group) }
+
+ before do
+ group.add_master(user)
+ end
+
+ it 'creates variable' do
+ expect do
+ post api("/groups/#{group.id}/variables", user), key: 'TEST_VARIABLE_2', value: 'VALUE_2', protected: true
+ end.to change{group.variables.count}.by(1)
+
+ expect(response).to have_http_status(201)
+ expect(json_response['key']).to eq('TEST_VARIABLE_2')
+ expect(json_response['value']).to eq('VALUE_2')
+ expect(json_response['protected']).to be_truthy
+ end
+
+ it 'creates variable with optional attributes' do
+ expect do
+ post api("/groups/#{group.id}/variables", user), key: 'TEST_VARIABLE_2', value: 'VALUE_2'
+ end.to change{group.variables.count}.by(1)
+
+ expect(response).to have_http_status(201)
+ expect(json_response['key']).to eq('TEST_VARIABLE_2')
+ expect(json_response['value']).to eq('VALUE_2')
+ expect(json_response['protected']).to be_falsey
+ end
+
+ it 'does not allow to duplicate variable key' do
+ expect do
+ post api("/groups/#{group.id}/variables", user), key: variable.key, value: 'VALUE_2'
+ end.to change{group.variables.count}.by(0)
+
+ expect(response).to have_http_status(400)
+ end
+ end
+
+ context 'authorized user with invalid permissions' do
+ it 'does not create variable' do
+ post api("/groups/#{group.id}/variables", user)
+
+ expect(response).to have_http_status(403)
+ end
+ end
+
+ context 'unauthorized user' do
+ it 'does not create variable' do
+ post api("/groups/#{group.id}/variables")
+
+ expect(response).to have_http_status(401)
+ end
+ end
+ end
+
+ describe 'PUT /groups/:id/variables/:key' do
+ let!(:variable) { create(:ci_group_variable, group: group) }
+
+ context 'authorized user with proper permissions' do
+ before do
+ group.add_master(user)
+ end
+
+ it 'updates variable data' do
+ initial_variable = group.variables.first
+ value_before = initial_variable.value
+
+ put api("/groups/#{group.id}/variables/#{variable.key}", user), value: 'VALUE_1_UP', protected: true
+
+ updated_variable = group.variables.first
+
+ expect(response).to have_http_status(200)
+ expect(value_before).to eq(variable.value)
+ expect(updated_variable.value).to eq('VALUE_1_UP')
+ expect(updated_variable).to be_protected
+ end
+
+ it 'responds with 404 Not Found if requesting non-existing variable' do
+ put api("/groups/#{group.id}/variables/non_existing_variable", user)
+
+ expect(response).to have_http_status(404)
+ end
+ end
+
+ context 'authorized user with invalid permissions' do
+ it 'does not update variable' do
+ put api("/groups/#{group.id}/variables/#{variable.key}", user)
+
+ expect(response).to have_http_status(403)
+ end
+ end
+
+ context 'unauthorized user' do
+ it 'does not update variable' do
+ put api("/groups/#{group.id}/variables/#{variable.key}")
+
+ expect(response).to have_http_status(401)
+ end
+ end
+ end
+
+ describe 'DELETE /groups/:id/variables/:key' do
+ let!(:variable) { create(:ci_group_variable, group: group) }
+
+ context 'authorized user with proper permissions' do
+ before do
+ group.add_master(user)
+ end
+
+ it 'deletes variable' do
+ expect do
+ delete api("/groups/#{group.id}/variables/#{variable.key}", user)
+
+ expect(response).to have_http_status(204)
+ end.to change{group.variables.count}.by(-1)
+ end
+
+ it 'responds with 404 Not Found if requesting non-existing variable' do
+ delete api("/groups/#{group.id}/variables/non_existing_variable", user)
+
+ expect(response).to have_http_status(404)
+ end
+ end
+
+ context 'authorized user with invalid permissions' do
+ it 'does not delete variable' do
+ delete api("/groups/#{group.id}/variables/#{variable.key}", user)
+
+ expect(response).to have_http_status(403)
+ end
+ end
+
+ context 'unauthorized user' do
+ it 'does not delete variable' do
+ delete api("/groups/#{group.id}/variables/#{variable.key}")
+
+ expect(response).to have_http_status(401)
+ end
+ end
+ end
+end
diff --git a/spec/requests/api/internal_spec.rb b/spec/requests/api/internal_spec.rb
index ce9b9ac1eb3..fb312d3cb7d 100644
--- a/spec/requests/api/internal_spec.rb
+++ b/spec/requests/api/internal_spec.rb
@@ -301,7 +301,7 @@ describe API::Internal do
context 'project as /namespace/project' do
it do
- pull(key, project_with_repo_path('/' + project.path_with_namespace))
+ pull(key, project_with_repo_path('/' + project.full_path))
expect(response).to have_http_status(200)
expect(json_response["status"]).to be_truthy
@@ -312,7 +312,7 @@ describe API::Internal do
context 'project as namespace/project' do
it do
- pull(key, project_with_repo_path(project.path_with_namespace))
+ pull(key, project_with_repo_path(project.full_path))
expect(response).to have_http_status(200)
expect(json_response["status"]).to be_truthy
diff --git a/spec/requests/api/notification_settings_spec.rb b/spec/requests/api/notification_settings_spec.rb
index f619b7e6eaf..d0e7a82e607 100644
--- a/spec/requests/api/notification_settings_spec.rb
+++ b/spec/requests/api/notification_settings_spec.rb
@@ -72,8 +72,8 @@ describe API::NotificationSettings do
expect(response).to have_http_status(200)
expect(json_response['level']).to eq(user.reload.notification_settings_for(project).level)
- expect(json_response['events']['new_note']).to eq(true)
- expect(json_response['events']['new_issue']).to eq(false)
+ expect(json_response['events']['new_note']).to be_truthy
+ expect(json_response['events']['new_issue']).to be_falsey
end
end
diff --git a/spec/requests/api/users_spec.rb b/spec/requests/api/users_spec.rb
index 66b165b438b..2dc7be22f8f 100644
--- a/spec/requests/api/users_spec.rb
+++ b/spec/requests/api/users_spec.rb
@@ -16,38 +16,44 @@ describe API::Users do
it "returns authorization error when the `username` parameter is not passed" do
get api("/users")
- expect(response).to have_http_status(403)
+ expect(response).to have_gitlab_http_status(403)
end
it "returns the user when a valid `username` parameter is passed" do
- user = create(:user)
-
get api("/users"), username: user.username
- expect(response).to have_http_status(200)
+ expect(response).to have_gitlab_http_status(200)
expect(json_response).to be_an Array
expect(json_response.size).to eq(1)
expect(json_response[0]['id']).to eq(user.id)
expect(json_response[0]['username']).to eq(user.username)
end
- it "returns authorization error when the `username` parameter refers to an inaccessible user" do
- user = create(:user)
-
- stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::PUBLIC])
-
- get api("/users"), username: user.username
-
- expect(response).to have_http_status(403)
- end
-
it "returns an empty response when an invalid `username` parameter is passed" do
get api("/users"), username: 'invalid'
- expect(response).to have_http_status(200)
+ expect(response).to have_gitlab_http_status(200)
expect(json_response).to be_an Array
expect(json_response.size).to eq(0)
end
+
+ context "when public level is restricted" do
+ before do
+ stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::PUBLIC])
+ end
+
+ it "returns authorization error when the `username` parameter refers to an inaccessible user" do
+ get api("/users"), username: user.username
+
+ expect(response).to have_gitlab_http_status(403)
+ end
+
+ it "returns authorization error when the `username` parameter is not passed" do
+ get api("/users")
+
+ expect(response).to have_gitlab_http_status(403)
+ end
+ end
end
context "when authenticated" do
@@ -58,10 +64,10 @@ describe API::Users do
end
context 'when authenticate as a regular user' do
- it "renders 403" do
+ it "renders 200" do
get api("/users", user)
- expect(response).to have_gitlab_http_status(403)
+ expect(response).to have_gitlab_http_status(200)
end
end
diff --git a/spec/requests/git_http_spec.rb b/spec/requests/git_http_spec.rb
index d4a3e8b13e1..d5c16d8f601 100644
--- a/spec/requests/git_http_spec.rb
+++ b/spec/requests/git_http_spec.rb
@@ -123,7 +123,7 @@ describe 'Git HTTP requests' do
context "when requesting the Wiki" do
let(:wiki) { ProjectWiki.new(project) }
- let(:path) { "/#{wiki.repository.path_with_namespace}.git" }
+ let(:path) { "/#{wiki.repository.full_path}.git" }
context "when the project is public" do
let(:project) { create(:project, :repository, :public, :wiki_enabled) }
@@ -139,7 +139,7 @@ describe 'Git HTTP requests' do
download(path) do |response|
json_body = ActiveSupport::JSON.decode(response.body)
- expect(json_body['RepoPath']).to include(wiki.repository.path_with_namespace)
+ expect(json_body['RepoPath']).to include(wiki.repository.full_path)
end
end
end
@@ -222,7 +222,7 @@ describe 'Git HTTP requests' do
end
context "when the project exists" do
- let(:path) { "#{project.path_with_namespace}.git" }
+ let(:path) { "#{project.full_path}.git" }
context "when the project is public" do
let(:project) { create(:project, :repository, :public) }
@@ -286,7 +286,7 @@ describe 'Git HTTP requests' do
context 'when the request is not from gitlab-workhorse' do
it 'raises an exception' do
expect do
- get("/#{project.path_with_namespace}.git/info/refs?service=git-upload-pack")
+ get("/#{project.full_path}.git/info/refs?service=git-upload-pack")
end.to raise_error(JWT::DecodeError)
end
end
@@ -294,7 +294,7 @@ describe 'Git HTTP requests' do
context 'when the repo is public' do
context 'but the repo is disabled' do
let(:project) { create(:project, :public, :repository, :repository_disabled) }
- let(:path) { "#{project.path_with_namespace}.git" }
+ let(:path) { "#{project.full_path}.git" }
let(:env) { {} }
it_behaves_like 'pulls require Basic HTTP Authentication'
@@ -303,7 +303,7 @@ describe 'Git HTTP requests' do
context 'but the repo is enabled' do
let(:project) { create(:project, :public, :repository, :repository_enabled) }
- let(:path) { "#{project.path_with_namespace}.git" }
+ let(:path) { "#{project.full_path}.git" }
let(:env) { {} }
it_behaves_like 'pulls are allowed'
@@ -421,7 +421,7 @@ describe 'Git HTTP requests' do
@token = Doorkeeper::AccessToken.create!(application_id: application.id, resource_owner_id: user.id, scopes: "api")
end
- let(:path) { "#{project.path_with_namespace}.git" }
+ let(:path) { "#{project.full_path}.git" }
let(:env) { { user: 'oauth2', password: @token.token } }
it_behaves_like 'pulls are allowed'
@@ -431,7 +431,7 @@ describe 'Git HTTP requests' do
context 'when user has 2FA enabled' do
let(:user) { create(:user, :two_factor) }
let(:access_token) { create(:personal_access_token, user: user) }
- let(:path) { "#{project.path_with_namespace}.git" }
+ let(:path) { "#{project.full_path}.git" }
before do
project.team << [user, :master]
@@ -580,7 +580,7 @@ describe 'Git HTTP requests' do
end
context 'when build created by system is authenticated' do
- let(:path) { "#{project.path_with_namespace}.git" }
+ let(:path) { "#{project.full_path}.git" }
let(:env) { { user: 'gitlab-ci-token', password: build.token } }
it_behaves_like 'pulls are allowed'
@@ -602,7 +602,7 @@ describe 'Git HTTP requests' do
# We are "authenticated" as CI using a valid token here. But we are
# not authorized to see any other project, so return "not found".
it "rejects pulls for other project with 404 Not Found" do
- clone_get("#{other_project.path_with_namespace}.git", env)
+ clone_get("#{other_project.full_path}.git", env)
expect(response).to have_http_status(:not_found)
expect(response.body).to eq(git_access_error(:project_not_found))
@@ -616,7 +616,7 @@ describe 'Git HTTP requests' do
end
shared_examples 'can download code only' do
- let(:path) { "#{project.path_with_namespace}.git" }
+ let(:path) { "#{project.full_path}.git" }
let(:env) { { user: 'gitlab-ci-token', password: build.token } }
it_behaves_like 'pulls are allowed'
@@ -646,7 +646,7 @@ describe 'Git HTTP requests' do
it_behaves_like 'can download code only'
it 'downloads from other project get status 403' do
- clone_get "#{other_project.path_with_namespace}.git", user: 'gitlab-ci-token', password: build.token
+ clone_get "#{other_project.full_path}.git", user: 'gitlab-ci-token', password: build.token
expect(response).to have_http_status(:forbidden)
end
@@ -658,7 +658,7 @@ describe 'Git HTTP requests' do
it_behaves_like 'can download code only'
it 'downloads from other project get status 404' do
- clone_get "#{other_project.path_with_namespace}.git", user: 'gitlab-ci-token', password: build.token
+ clone_get "#{other_project.full_path}.git", user: 'gitlab-ci-token', password: build.token
expect(response).to have_http_status(:not_found)
end
@@ -671,7 +671,7 @@ describe 'Git HTTP requests' do
let(:project) { create(:project, :repository, :public, path: 'project.git-project') }
context "GET info/refs" do
- let(:path) { "/#{project.path_with_namespace}/info/refs" }
+ let(:path) { "/#{project.full_path}/info/refs" }
context "when no params are added" do
before do
@@ -679,7 +679,7 @@ describe 'Git HTTP requests' do
end
it "redirects to the .git suffix version" do
- expect(response).to redirect_to("/#{project.path_with_namespace}.git/info/refs")
+ expect(response).to redirect_to("/#{project.full_path}.git/info/refs")
end
end
@@ -691,7 +691,7 @@ describe 'Git HTTP requests' do
end
it "redirects to the .git suffix version" do
- expect(response).to redirect_to("/#{project.path_with_namespace}.git/info/refs?service=#{params[:service]}")
+ expect(response).to redirect_to("/#{project.full_path}.git/info/refs?service=#{params[:service]}")
end
end
@@ -703,7 +703,7 @@ describe 'Git HTTP requests' do
end
it "redirects to the .git suffix version" do
- expect(response).to redirect_to("/#{project.path_with_namespace}.git/info/refs?service=#{params[:service]}")
+ expect(response).to redirect_to("/#{project.full_path}.git/info/refs?service=#{params[:service]}")
end
end
@@ -722,13 +722,13 @@ describe 'Git HTTP requests' do
context "POST git-upload-pack" do
it "fails to find a route" do
- expect { clone_post(project.path_with_namespace) }.to raise_error(ActionController::RoutingError)
+ expect { clone_post(project.full_path) }.to raise_error(ActionController::RoutingError)
end
end
context "POST git-receive-pack" do
it "fails to find a route" do
- expect { push_post(project.path_with_namespace) }.to raise_error(ActionController::RoutingError)
+ expect { push_post(project.full_path) }.to raise_error(ActionController::RoutingError)
end
end
end
@@ -744,7 +744,7 @@ describe 'Git HTTP requests' do
Blob.decorate(Gitlab::Git::Blob.find(project.repository, 'master', 'bar/branch-test.txt'), project)
end
- get "/#{project.path_with_namespace}/blob/master/info/refs"
+ get "/#{project.full_path}/blob/master/info/refs"
end
it "returns the file" do
@@ -754,7 +754,7 @@ describe 'Git HTTP requests' do
context "when the file does not exist" do
before do
- get "/#{project.path_with_namespace}/blob/master/info/refs"
+ get "/#{project.full_path}/blob/master/info/refs"
end
it "returns not found" do
diff --git a/spec/requests/lfs_http_spec.rb b/spec/requests/lfs_http_spec.rb
index 697b150ab34..4f1a90750b3 100644
--- a/spec/requests/lfs_http_spec.rb
+++ b/spec/requests/lfs_http_spec.rb
@@ -701,7 +701,7 @@ describe 'Git LFS API and storage' do
expect(json_response['objects']).to be_kind_of(Array)
expect(json_response['objects'].first['oid']).to eq("91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897")
expect(json_response['objects'].first['size']).to eq(1575078)
- expect(json_response['objects'].first['actions']['upload']['href']).to eq("#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}.git/gitlab-lfs/objects/91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897/1575078")
+ expect(json_response['objects'].first['actions']['upload']['href']).to eq("#{Gitlab.config.gitlab.url}/#{project.full_path}.git/gitlab-lfs/objects/91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897/1575078")
expect(json_response['objects'].first['actions']['upload']['header']).to eq('Authorization' => authorization)
end
end
diff --git a/spec/requests/request_profiler_spec.rb b/spec/requests/request_profiler_spec.rb
index 51fbfecec4b..9afeb2983b0 100644
--- a/spec/requests/request_profiler_spec.rb
+++ b/spec/requests/request_profiler_spec.rb
@@ -15,7 +15,7 @@ describe 'Request Profiler' do
it 'creates a profile of the request' do
project = create(:project, namespace: user.namespace)
time = Time.now
- path = "/#{project.path_with_namespace}"
+ path = "/#{project.full_path}"
Timecop.freeze(time) do
get path, nil, 'X-Profile-Token' => Gitlab::RequestProfiler.profile_token
diff --git a/spec/services/auth/container_registry_authentication_service_spec.rb b/spec/services/auth/container_registry_authentication_service_spec.rb
index 66a8a93b168..0ae839ce0b3 100644
--- a/spec/services/auth/container_registry_authentication_service_spec.rb
+++ b/spec/services/auth/container_registry_authentication_service_spec.rb
@@ -46,7 +46,7 @@ describe Auth::ContainerRegistryAuthenticationService do
shared_examples 'an accessible' do
let(:access) do
[{ 'type' => 'repository',
- 'name' => project.path_with_namespace,
+ 'name' => project.full_path,
'actions' => actions }]
end
@@ -97,7 +97,7 @@ describe Auth::ContainerRegistryAuthenticationService do
describe '#full_access_token' do
let(:project) { create(:empty_project) }
- let(:token) { described_class.full_access_token(project.path_with_namespace) }
+ let(:token) { described_class.full_access_token(project.full_path) }
subject { { token: token } }
@@ -124,7 +124,7 @@ describe Auth::ContainerRegistryAuthenticationService do
end
let(:current_params) do
- { scope: "repository:#{project.path_with_namespace}:push" }
+ { scope: "repository:#{project.full_path}:push" }
end
it_behaves_like 'a pushable'
@@ -138,7 +138,7 @@ describe Auth::ContainerRegistryAuthenticationService do
context 'when pulling from root level repository' do
let(:current_params) do
- { scope: "repository:#{project.path_with_namespace}:pull" }
+ { scope: "repository:#{project.full_path}:pull" }
end
it_behaves_like 'a pullable'
@@ -152,7 +152,7 @@ describe Auth::ContainerRegistryAuthenticationService do
end
let(:current_params) do
- { scope: "repository:#{project.path_with_namespace}:push,pull" }
+ { scope: "repository:#{project.full_path}:push,pull" }
end
it_behaves_like 'a pullable'
@@ -165,7 +165,7 @@ describe Auth::ContainerRegistryAuthenticationService do
end
let(:current_params) do
- { scope: "repository:#{project.path_with_namespace}:pull,push" }
+ { scope: "repository:#{project.full_path}:pull,push" }
end
it_behaves_like 'an inaccessible'
@@ -178,7 +178,7 @@ describe Auth::ContainerRegistryAuthenticationService do
context 'allow anyone to pull images' do
let(:current_params) do
- { scope: "repository:#{project.path_with_namespace}:pull" }
+ { scope: "repository:#{project.full_path}:pull" }
end
it_behaves_like 'a pullable'
@@ -187,7 +187,7 @@ describe Auth::ContainerRegistryAuthenticationService do
context 'disallow anyone to push images' do
let(:current_params) do
- { scope: "repository:#{project.path_with_namespace}:push" }
+ { scope: "repository:#{project.full_path}:push" }
end
it_behaves_like 'an inaccessible'
@@ -210,7 +210,7 @@ describe Auth::ContainerRegistryAuthenticationService do
context 'for internal user' do
context 'allow anyone to pull images' do
let(:current_params) do
- { scope: "repository:#{project.path_with_namespace}:pull" }
+ { scope: "repository:#{project.full_path}:pull" }
end
it_behaves_like 'a pullable'
@@ -219,7 +219,7 @@ describe Auth::ContainerRegistryAuthenticationService do
context 'disallow anyone to push images' do
let(:current_params) do
- { scope: "repository:#{project.path_with_namespace}:push" }
+ { scope: "repository:#{project.full_path}:push" }
end
it_behaves_like 'an inaccessible'
@@ -230,7 +230,7 @@ describe Auth::ContainerRegistryAuthenticationService do
context 'for external user' do
let(:current_user) { create(:user, external: true) }
let(:current_params) do
- { scope: "repository:#{project.path_with_namespace}:pull,push" }
+ { scope: "repository:#{project.full_path}:pull,push" }
end
it_behaves_like 'an inaccessible'
@@ -255,7 +255,7 @@ describe Auth::ContainerRegistryAuthenticationService do
context 'allow to pull and push images' do
let(:current_params) do
- { scope: "repository:#{current_project.path_with_namespace}:pull,push" }
+ { scope: "repository:#{current_project.full_path}:pull,push" }
end
it_behaves_like 'a pullable and pushable' do
@@ -270,7 +270,7 @@ describe Auth::ContainerRegistryAuthenticationService do
context 'for other projects' do
context 'when pulling' do
let(:current_params) do
- { scope: "repository:#{project.path_with_namespace}:pull" }
+ { scope: "repository:#{project.full_path}:pull" }
end
context 'allow for public' do
@@ -337,7 +337,7 @@ describe Auth::ContainerRegistryAuthenticationService do
context 'when pushing' do
let(:current_params) do
- { scope: "repository:#{project.path_with_namespace}:push" }
+ { scope: "repository:#{project.full_path}:push" }
end
context 'disallow for all' do
@@ -371,7 +371,7 @@ describe Auth::ContainerRegistryAuthenticationService do
context 'disallow when pulling' do
let(:current_params) do
- { scope: "repository:#{project.path_with_namespace}:pull" }
+ { scope: "repository:#{project.full_path}:pull" }
end
it_behaves_like 'an inaccessible'
@@ -399,7 +399,7 @@ describe Auth::ContainerRegistryAuthenticationService do
let(:project) { create(:empty_project, :private) }
let(:current_params) do
- { scope: "repository:#{project.path_with_namespace}:pull" }
+ { scope: "repository:#{project.full_path}:pull" }
end
it_behaves_like 'a forbidden'
@@ -410,7 +410,7 @@ describe Auth::ContainerRegistryAuthenticationService do
context 'when pulling and pushing' do
let(:current_params) do
- { scope: "repository:#{project.path_with_namespace}:pull,push" }
+ { scope: "repository:#{project.full_path}:pull,push" }
end
it_behaves_like 'a pullable'
@@ -419,7 +419,7 @@ describe Auth::ContainerRegistryAuthenticationService do
context 'when pushing' do
let(:current_params) do
- { scope: "repository:#{project.path_with_namespace}:push" }
+ { scope: "repository:#{project.full_path}:push" }
end
it_behaves_like 'a forbidden'
diff --git a/spec/services/ci/pipeline_trigger_service_spec.rb b/spec/services/ci/pipeline_trigger_service_spec.rb
index 945a2fe1a6b..e9188a83b89 100644
--- a/spec/services/ci/pipeline_trigger_service_spec.rb
+++ b/spec/services/ci/pipeline_trigger_service_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Ci::PipelineTriggerService, services: true do
+describe Ci::PipelineTriggerService do
let(:project) { create(:project, :repository) }
before do
diff --git a/spec/services/git_push_service_spec.rb b/spec/services/git_push_service_spec.rb
index 3fb677b65be..82724ccd281 100644
--- a/spec/services/git_push_service_spec.rb
+++ b/spec/services/git_push_service_spec.rb
@@ -453,7 +453,7 @@ describe GitPushService, services: true do
let(:message) { "this is some work.\n\ncloses JIRA-1" }
let(:comment_body) do
{
- body: "Issue solved with [#{closing_commit.id}|http://#{Gitlab.config.gitlab.host}/#{project.path_with_namespace}/commit/#{closing_commit.id}]."
+ body: "Issue solved with [#{closing_commit.id}|http://#{Gitlab.config.gitlab.host}/#{project.full_path}/commit/#{closing_commit.id}]."
}.to_json
end
diff --git a/spec/services/notification_service_spec.rb b/spec/services/notification_service_spec.rb
index 49d6fc7853f..5b69426cbaa 100644
--- a/spec/services/notification_service_spec.rb
+++ b/spec/services/notification_service_spec.rb
@@ -694,17 +694,6 @@ describe NotificationService do
let!(:subscriber_to_label_1) { create(:user) { |u| label_1.toggle_subscription(u, project) } }
let!(:subscriber_to_label_2) { create(:user) { |u| label_2.toggle_subscription(u, project) } }
- it "emails subscribers of the issue's added labels only" do
- notification.relabeled_issue(issue, [group_label_2, label_2], @u_disabled)
-
- should_not_email(subscriber_to_label_1)
- should_not_email(subscriber_to_group_label_1)
- should_not_email(subscriber_to_group_label_2_on_another_project)
- should_email(subscriber_1_to_group_label_2)
- should_email(subscriber_2_to_group_label_2)
- should_email(subscriber_to_label_2)
- end
-
it "emails the current user if they've opted into notifications about their activity" do
subscriber_to_label_2.notified_of_own_activity = true
notification.relabeled_issue(issue, [group_label_2, label_2], subscriber_to_label_2)
@@ -721,6 +710,12 @@ describe NotificationService do
it "doesn't send email to anyone but subscribers of the given labels" do
notification.relabeled_issue(issue, [group_label_2, label_2], @u_disabled)
+ should_not_email(subscriber_to_label_1)
+ should_not_email(subscriber_to_group_label_1)
+ should_not_email(subscriber_to_group_label_2_on_another_project)
+ should_email(subscriber_1_to_group_label_2)
+ should_email(subscriber_2_to_group_label_2)
+ should_email(subscriber_to_label_2)
should_not_email(issue.assignees.first)
should_not_email(issue.author)
should_not_email(@u_watcher)
@@ -730,12 +725,6 @@ describe NotificationService do
should_not_email(@watcher_and_subscriber)
should_not_email(@unsubscriber)
should_not_email(@u_participating)
- should_not_email(subscriber_to_label_1)
- should_not_email(subscriber_to_group_label_1)
- should_not_email(subscriber_to_group_label_2_on_another_project)
- should_email(subscriber_1_to_group_label_2)
- should_email(subscriber_2_to_group_label_2)
- should_email(subscriber_to_label_2)
end
context 'confidential issues' do
@@ -878,11 +867,6 @@ describe NotificationService do
end
describe '#new_merge_request' do
- before do
- update_custom_notification(:new_merge_request, @u_guest_custom, resource: project)
- update_custom_notification(:new_merge_request, @u_custom_global)
- end
-
it do
notification.new_merge_request(merge_request, @u_disabled)
@@ -1008,7 +992,7 @@ describe NotificationService do
let!(:subscriber_to_label_1) { create(:user) { |u| label_1.toggle_subscription(u, project) } }
let!(:subscriber_to_label_2) { create(:user) { |u| label_2.toggle_subscription(u, project) } }
- it "emails subscribers of the merge request's added labels only" do
+ it "doesn't send email to anyone but subscribers of the given labels" do
notification.relabeled_merge_request(merge_request, [group_label_2, label_2], @u_disabled)
should_not_email(subscriber_to_label_1)
@@ -1017,11 +1001,6 @@ describe NotificationService do
should_email(subscriber_1_to_group_label_2)
should_email(subscriber_2_to_group_label_2)
should_email(subscriber_to_label_2)
- end
-
- it "doesn't send email to anyone but subscribers of the given labels" do
- notification.relabeled_merge_request(merge_request, [group_label_2, label_2], @u_disabled)
-
should_not_email(merge_request.assignee)
should_not_email(merge_request.author)
should_not_email(@u_watcher)
@@ -1031,12 +1010,6 @@ describe NotificationService do
should_not_email(@unsubscriber)
should_not_email(@u_participating)
should_not_email(@u_lazy_participant)
- should_not_email(subscriber_to_label_1)
- should_not_email(subscriber_to_group_label_1)
- should_not_email(subscriber_to_group_label_2_on_another_project)
- should_email(subscriber_1_to_group_label_2)
- should_email(subscriber_2_to_group_label_2)
- should_email(subscriber_to_label_2)
end
end
@@ -1081,12 +1054,12 @@ describe NotificationService do
should_email(merge_request.assignee)
should_email(@u_watcher)
+ should_email(@u_guest_watcher)
+ should_email(@u_guest_custom)
+ should_email(@u_custom_global)
should_email(@u_participant_mentioned)
should_email(@subscriber)
should_email(@watcher_and_subscriber)
- should_email(@u_guest_watcher)
- should_email(@u_custom_global)
- should_email(@u_guest_custom)
should_not_email(@unsubscriber)
should_not_email(@u_participating)
should_not_email(@u_disabled)
diff --git a/spec/services/projects/import_service_spec.rb b/spec/services/projects/import_service_spec.rb
index 67fe610f92a..c2a6cafa4c8 100644
--- a/spec/services/projects/import_service_spec.rb
+++ b/spec/services/projects/import_service_spec.rb
@@ -26,7 +26,7 @@ describe Projects::ImportService do
result = subject.execute
expect(result[:status]).to eq :error
- expect(result[:message]).to eq "Error importing repository #{project.import_url} into #{project.path_with_namespace} - The repository could not be created."
+ expect(result[:message]).to eq "Error importing repository #{project.import_url} into #{project.full_path} - The repository could not be created."
end
end
@@ -52,7 +52,7 @@ describe Projects::ImportService do
result = subject.execute
expect(result[:status]).to eq :error
- expect(result[:message]).to eq "Error importing repository #{project.import_url} into #{project.path_with_namespace} - Failed to import the repository"
+ expect(result[:message]).to eq "Error importing repository #{project.import_url} into #{project.full_path} - Failed to import the repository"
end
it 'does not remove the GitHub remote' do
@@ -86,7 +86,7 @@ describe Projects::ImportService do
result = subject.execute
expect(result[:status]).to eq :error
- expect(result[:message]).to eq "Error importing repository #{project.import_url} into #{project.path_with_namespace} - Failed to import the repository"
+ expect(result[:message]).to eq "Error importing repository #{project.import_url} into #{project.full_path} - Failed to import the repository"
end
end
end
@@ -129,7 +129,7 @@ describe Projects::ImportService do
result = subject.execute
expect(result[:status]).to eq :error
- expect(result[:message]).to eq "Error importing repository #{project.import_url} into #{project.path_with_namespace} - The remote data could not be imported."
+ expect(result[:message]).to eq "Error importing repository #{project.import_url} into #{project.full_path} - The remote data could not be imported."
end
it 'fails if importer raise an error' do
@@ -139,7 +139,7 @@ describe Projects::ImportService do
result = subject.execute
expect(result[:status]).to eq :error
- expect(result[:message]).to eq "Error importing repository #{project.import_url} into #{project.path_with_namespace} - Github: failed to connect API"
+ expect(result[:message]).to eq "Error importing repository #{project.import_url} into #{project.full_path} - Github: failed to connect API"
end
it 'expires content cache after error' do
diff --git a/spec/services/projects/transfer_service_spec.rb b/spec/services/projects/transfer_service_spec.rb
index ae32e85b2a7..2cb60cbcfc4 100644
--- a/spec/services/projects/transfer_service_spec.rb
+++ b/spec/services/projects/transfer_service_spec.rb
@@ -37,18 +37,18 @@ describe Projects::TransferService do
end
it 'executes system hooks' do
- expect_any_instance_of(described_class).to receive(:execute_system_hooks)
-
- transfer_project(project, user, group)
+ transfer_project(project, user, group) do |service|
+ expect(service).to receive(:execute_system_hooks)
+ end
end
end
context 'when transfer fails' do
let!(:original_path) { project_path(project) }
- def attempt_project_transfer
+ def attempt_project_transfer(&block)
expect do
- transfer_project(project, user, group)
+ transfer_project(project, user, group, &block)
end.to raise_error(ActiveRecord::ActiveRecordError)
end
@@ -59,7 +59,7 @@ describe Projects::TransferService do
end
def project_path(project)
- File.join(project.repository_storage_path, "#{project.path_with_namespace}.git")
+ File.join(project.repository_storage_path, "#{project.disk_path}.git")
end
def current_path
@@ -80,9 +80,9 @@ describe Projects::TransferService do
end
it "doesn't run system hooks" do
- expect_any_instance_of(described_class).not_to receive(:execute_system_hooks)
-
- attempt_project_transfer
+ attempt_project_transfer do |service|
+ expect(service).not_to receive(:execute_system_hooks)
+ end
end
end
@@ -120,7 +120,11 @@ describe Projects::TransferService do
end
def transfer_project(project, user, new_namespace)
- Projects::TransferService.new(project, user).execute(new_namespace)
+ service = Projects::TransferService.new(project, user)
+
+ yield(service) if block_given?
+
+ service.execute(new_namespace)
end
context 'visibility level' do
diff --git a/spec/services/quick_actions/interpret_service_spec.rb b/spec/services/quick_actions/interpret_service_spec.rb
index 42a4a2591ea..92bde2c92bf 100644
--- a/spec/services/quick_actions/interpret_service_spec.rb
+++ b/spec/services/quick_actions/interpret_service_spec.rb
@@ -9,13 +9,13 @@ describe QuickActions::InterpretService do
let(:inprogress) { create(:label, project: project, title: 'In Progress') }
let(:bug) { create(:label, project: project, title: 'Bug') }
let(:note) { build(:note, commit_id: merge_request.diff_head_sha) }
+ let(:service) { described_class.new(project, developer) }
before do
project.team << [developer, :developer]
end
describe '#execute' do
- let(:service) { described_class.new(project, developer) }
let(:merge_request) { create(:merge_request, source_project: project) }
shared_examples 'reopen command' do
@@ -270,6 +270,22 @@ describe QuickActions::InterpretService do
end
end
+ shared_examples 'shrug command' do
+ it 'appends ¯\_(ツ)_/¯ to the comment' do
+ new_content, _ = service.execute(content, issuable)
+
+ expect(new_content).to end_with(described_class::SHRUG)
+ end
+ end
+
+ shared_examples 'tableflip command' do
+ it 'appends (╯°□°)╯︵ ┻━┻ to the comment' do
+ new_content, _ = service.execute(content, issuable)
+
+ expect(new_content).to end_with(described_class::TABLEFLIP)
+ end
+ end
+
it_behaves_like 'reopen command' do
let(:content) { '/reopen' }
let(:issuable) { issue }
@@ -775,6 +791,30 @@ describe QuickActions::InterpretService do
end
end
+ context '/shrug command' do
+ it_behaves_like 'shrug command' do
+ let(:content) { '/shrug people are people' }
+ let(:issuable) { issue }
+ end
+
+ it_behaves_like 'shrug command' do
+ let(:content) { '/shrug' }
+ let(:issuable) { issue }
+ end
+ end
+
+ context '/tableflip command' do
+ it_behaves_like 'tableflip command' do
+ let(:content) { '/tableflip curse your sudden but enviable betrayal' }
+ let(:issuable) { issue }
+ end
+
+ it_behaves_like 'tableflip command' do
+ let(:content) { '/tableflip' }
+ let(:issuable) { issue }
+ end
+ end
+
context '/target_branch command' do
let(:non_empty_project) { create(:project, :repository) }
let(:another_merge_request) { create(:merge_request, author: developer, source_project: non_empty_project) }
diff --git a/spec/services/system_note_service_spec.rb b/spec/services/system_note_service_spec.rb
index cb59493c343..5c20263a532 100644
--- a/spec/services/system_note_service_spec.rb
+++ b/spec/services/system_note_service_spec.rb
@@ -667,7 +667,7 @@ describe SystemNoteService do
end
it 'mentions referenced project' do
- expect(subject.note).to include new_project.path_with_namespace
+ expect(subject.note).to include new_project.full_path
end
end
@@ -873,7 +873,7 @@ describe SystemNoteService do
describe "existing reference" do
before do
allow(JIRA::Resource::Remotelink).to receive(:all).and_return([])
- message = "[#{author.name}|http://localhost/#{author.username}] mentioned this issue in [a commit of #{project.path_with_namespace}|http://localhost/#{project.path_with_namespace}/commit/#{commit.id}]:\n'#{commit.title.chomp}'"
+ message = "[#{author.name}|http://localhost/#{author.username}] mentioned this issue in [a commit of #{project.full_path}|http://localhost/#{project.full_path}/commit/#{commit.id}]:\n'#{commit.title.chomp}'"
allow_any_instance_of(JIRA::Resource::Issue).to receive(:comments).and_return([OpenStruct.new(body: message)])
end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 85335643921..609998d6e9c 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -129,10 +129,14 @@ RSpec.configure do |config|
config.before(:example, :migration) do
ActiveRecord::Migrator
.migrate(migrations_paths, previous_migration.version)
+
+ ActiveRecord::Base.descendants.each(&:reset_column_information)
end
config.after(:example, :migration) do
ActiveRecord::Migrator.migrate(migrations_paths)
+
+ ActiveRecord::Base.descendants.each(&:reset_column_information)
end
config.around(:each, :nested_groups) do |example|
diff --git a/spec/support/notify_shared_examples.rb b/spec/support/notify_shared_examples.rb
index 76411065265..70799bce721 100644
--- a/spec/support/notify_shared_examples.rb
+++ b/spec/support/notify_shared_examples.rb
@@ -50,7 +50,7 @@ shared_examples 'an email with X-GitLab headers containing project details' do
aggregate_failures do
is_expected.to have_header('X-GitLab-Project', /#{project.name}/)
is_expected.to have_header('X-GitLab-Project-Id', /#{project.id}/)
- is_expected.to have_header('X-GitLab-Project-Path', /#{project.path_with_namespace}/)
+ is_expected.to have_header('X-GitLab-Project-Path', /#{project.full_path}/)
end
end
end
diff --git a/spec/support/project_hook_data_shared_example.rb b/spec/support/project_hook_data_shared_example.rb
index 7dbaa6a6459..1eb405d4be8 100644
--- a/spec/support/project_hook_data_shared_example.rb
+++ b/spec/support/project_hook_data_shared_example.rb
@@ -8,7 +8,7 @@ RSpec.shared_examples 'project hook data with deprecateds' do |project_key: :pro
expect(data[project_key][:git_ssh_url]).to eq(project.ssh_url_to_repo)
expect(data[project_key][:namespace]).to eq(project.namespace.name)
expect(data[project_key][:visibility_level]).to eq(project.visibility_level)
- expect(data[project_key][:path_with_namespace]).to eq(project.path_with_namespace)
+ expect(data[project_key][:path_with_namespace]).to eq(project.full_path)
expect(data[project_key][:default_branch]).to eq(project.default_branch)
expect(data[project_key][:homepage]).to eq(project.web_url)
expect(data[project_key][:url]).to eq(project.url_to_repo)
@@ -27,7 +27,7 @@ RSpec.shared_examples 'project hook data' do |project_key: :project|
expect(data[project_key][:git_ssh_url]).to eq(project.ssh_url_to_repo)
expect(data[project_key][:namespace]).to eq(project.namespace.name)
expect(data[project_key][:visibility_level]).to eq(project.visibility_level)
- expect(data[project_key][:path_with_namespace]).to eq(project.path_with_namespace)
+ expect(data[project_key][:path_with_namespace]).to eq(project.full_path)
expect(data[project_key][:default_branch]).to eq(project.default_branch)
end
end
diff --git a/spec/support/test_env.rb b/spec/support/test_env.rb
index 86f9568c12e..f0603dfadde 100644
--- a/spec/support/test_env.rb
+++ b/spec/support/test_env.rb
@@ -144,10 +144,13 @@ module TestEnv
end
def start_gitaly(gitaly_dir)
- gitaly_exec = File.join(gitaly_dir, 'gitaly')
- gitaly_config = File.join(gitaly_dir, 'config.toml')
- log_file = Rails.root.join('log/gitaly-test.log').to_s
- @gitaly_pid = Bundler.with_original_env { spawn(gitaly_exec, gitaly_config, [:out, :err] => log_file) }
+ if ENV['CI'].present?
+ # Gitaly has been spawned outside this process already
+ return
+ end
+
+ spawn_script = Rails.root.join('scripts/gitaly-test-spawn').to_s
+ @gitaly_pid = Bundler.with_original_env { IO.popen([spawn_script], &:read).to_i }
end
def stop_gitaly
diff --git a/spec/tasks/gitlab/backup_rake_spec.rb b/spec/tasks/gitlab/backup_rake_spec.rb
index 71580a788d0..fae92451b46 100644
--- a/spec/tasks/gitlab/backup_rake_spec.rb
+++ b/spec/tasks/gitlab/backup_rake_spec.rb
@@ -117,7 +117,7 @@ describe 'gitlab:app namespace rake task' do
describe 'backup creation and deletion using custom_hooks' do
let(:project) { create(:project, :repository) }
- let(:user_backup_path) { "repositories/#{project.path_with_namespace}" }
+ let(:user_backup_path) { "repositories/#{project.disk_path}" }
before(:each) do
@origin_cd = Dir.pwd
@@ -261,8 +261,8 @@ describe 'gitlab:app namespace rake task' do
%W{tar -tvf #{@backup_tar} repositories}
)
expect(exit_status).to eq(0)
- expect(tar_contents).to match("repositories/#{project_a.path_with_namespace}.bundle")
- expect(tar_contents).to match("repositories/#{project_b.path_with_namespace}.bundle")
+ expect(tar_contents).to match("repositories/#{project_a.disk_path}.bundle")
+ expect(tar_contents).to match("repositories/#{project_b.disk_path}.bundle")
end
end
end # backup_create task
diff --git a/spec/tasks/gitlab/gitaly_rake_spec.rb b/spec/tasks/gitlab/gitaly_rake_spec.rb
index d42d2423f15..695231c7d15 100644
--- a/spec/tasks/gitlab/gitaly_rake_spec.rb
+++ b/spec/tasks/gitlab/gitaly_rake_spec.rb
@@ -41,6 +41,16 @@ describe 'gitlab:gitaly namespace rake task' do
end
describe 'gmake/make' do
+ let(:command_preamble) { %w[/usr/bin/env -u BUNDLE_GEMFILE] }
+
+ before(:all) do
+ @old_env_ci = ENV.delete('CI')
+ end
+
+ after(:all) do
+ ENV['CI'] = @old_env_ci if @old_env_ci
+ end
+
before do
FileUtils.mkdir_p(clone_path)
expect(Dir).to receive(:chdir).with(clone_path).and_call_original
@@ -49,12 +59,12 @@ describe 'gitlab:gitaly namespace rake task' do
context 'gmake is available' do
before do
expect_any_instance_of(Object).to receive(:checkout_or_clone_version)
- allow_any_instance_of(Object).to receive(:run_command!).with(['gmake']).and_return(true)
+ allow_any_instance_of(Object).to receive(:run_command!).with(command_preamble + ['gmake']).and_return(true)
end
it 'calls gmake in the gitaly directory' do
expect(Gitlab::Popen).to receive(:popen).with(%w[which gmake]).and_return(['/usr/bin/gmake', 0])
- expect_any_instance_of(Object).to receive(:run_command!).with(['gmake']).and_return(true)
+ expect_any_instance_of(Object).to receive(:run_command!).with(command_preamble + ['gmake']).and_return(true)
run_rake_task('gitlab:gitaly:install', clone_path)
end
@@ -63,12 +73,12 @@ describe 'gitlab:gitaly namespace rake task' do
context 'gmake is not available' do
before do
expect_any_instance_of(Object).to receive(:checkout_or_clone_version)
- allow_any_instance_of(Object).to receive(:run_command!).with(['make']).and_return(true)
+ allow_any_instance_of(Object).to receive(:run_command!).with(command_preamble + ['make']).and_return(true)
end
it 'calls make in the gitaly directory' do
expect(Gitlab::Popen).to receive(:popen).with(%w[which gmake]).and_return(['', 42])
- expect_any_instance_of(Object).to receive(:run_command!).with(['make']).and_return(true)
+ expect_any_instance_of(Object).to receive(:run_command!).with(command_preamble + ['make']).and_return(true)
run_rake_task('gitlab:gitaly:install', clone_path)
end
diff --git a/spec/workers/git_garbage_collect_worker_spec.rb b/spec/workers/git_garbage_collect_worker_spec.rb
index 309b3172da1..05f971dfd13 100644
--- a/spec/workers/git_garbage_collect_worker_spec.rb
+++ b/spec/workers/git_garbage_collect_worker_spec.rb
@@ -9,17 +9,51 @@ describe GitGarbageCollectWorker do
subject { described_class.new }
describe "#perform" do
- it "flushes ref caches when the task is 'gc'" do
- expect(subject).to receive(:command).with(:gc).and_return([:the, :command])
- expect(Gitlab::Popen).to receive(:popen)
- .with([:the, :command], project.repository.path_to_repo).and_return(["", 0])
+ shared_examples 'flushing ref caches' do |gitaly|
+ it "flushes ref caches when the task if 'gc'" do
+ expect(subject).to receive(:command).with(:gc).and_return([:the, :command])
+
+ if gitaly
+ expect_any_instance_of(Gitlab::GitalyClient::RepositoryService).to receive(:garbage_collect)
+ .and_return(nil)
+ else
+ expect(Gitlab::Popen).to receive(:popen)
+ .with([:the, :command], project.repository.path_to_repo).and_return(["", 0])
+ end
+
+ expect_any_instance_of(Repository).to receive(:after_create_branch).and_call_original
+ expect_any_instance_of(Repository).to receive(:branch_names).and_call_original
+ expect_any_instance_of(Repository).to receive(:branch_count).and_call_original
+ expect_any_instance_of(Repository).to receive(:has_visible_content?).and_call_original
+
+ subject.perform(project.id)
+ end
+ end
+
+ context "with Gitaly turned on" do
+ it_should_behave_like 'flushing ref caches', true
+ end
+
+ context "with Gitaly turned off", skip_gitaly_mock: true do
+ it_should_behave_like 'flushing ref caches', false
+ end
- expect_any_instance_of(Repository).to receive(:after_create_branch).and_call_original
- expect_any_instance_of(Repository).to receive(:branch_names).and_call_original
- expect_any_instance_of(Repository).to receive(:branch_count).and_call_original
- expect_any_instance_of(Repository).to receive(:has_visible_content?).and_call_original
+ context "repack_full" do
+ it "calls Gitaly" do
+ expect_any_instance_of(Gitlab::GitalyClient::RepositoryService).to receive(:repack_full)
+ .and_return(nil)
- subject.perform(project.id)
+ subject.perform(project.id, :full_repack)
+ end
+ end
+
+ context "repack_incremental" do
+ it "calls Gitaly" do
+ expect_any_instance_of(Gitlab::GitalyClient::RepositoryService).to receive(:repack_incremental)
+ .and_return(nil)
+
+ subject.perform(project.id, :incremental_repack)
+ end
end
shared_examples 'gc tasks' do
diff --git a/spec/workers/namespaceless_project_destroy_worker_spec.rb b/spec/workers/namespaceless_project_destroy_worker_spec.rb
index 8533b7b85e9..f9e23d648ec 100644
--- a/spec/workers/namespaceless_project_destroy_worker_spec.rb
+++ b/spec/workers/namespaceless_project_destroy_worker_spec.rb
@@ -5,7 +5,7 @@ describe NamespacelessProjectDestroyWorker do
before do
# Stub after_save callbacks that will fail when Project has no namespace
- allow_any_instance_of(Project).to receive(:ensure_dir_exist).and_return(nil)
+ allow_any_instance_of(Project).to receive(:ensure_storage_path_exist).and_return(nil)
allow_any_instance_of(Project).to receive(:update_project_statistics).and_return(nil)
end