summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitlab-ci.yml47
-rw-r--r--.gitlab/merge_request_templates/Database changes.md22
-rw-r--r--.gitlab/merge_request_templates/Documentation.md14
-rw-r--r--app/assets/javascripts/diffs/components/app.vue5
-rw-r--r--app/assets/javascripts/diffs/components/diff_file.vue6
-rw-r--r--app/assets/javascripts/diffs/components/diff_file_header.vue6
-rw-r--r--app/assets/javascripts/diffs/components/diff_line_note_form.vue12
-rw-r--r--app/assets/javascripts/diffs/components/diff_table_cell.vue12
-rw-r--r--app/assets/javascripts/diffs/components/edit_button.vue8
-rw-r--r--app/assets/javascripts/diffs/components/inline_diff_comment_row.vue13
-rw-r--r--app/assets/javascripts/diffs/components/inline_diff_table_row.vue16
-rw-r--r--app/assets/javascripts/diffs/components/inline_diff_view.vue6
-rw-r--r--app/assets/javascripts/diffs/components/parallel_diff_comment_row.vue18
-rw-r--r--app/assets/javascripts/diffs/components/parallel_diff_table_row.vue14
-rw-r--r--app/assets/javascripts/diffs/components/parallel_diff_view.vue8
-rw-r--r--app/assets/javascripts/diffs/store/getters.js4
-rw-r--r--app/assets/javascripts/ide/components/new_dropdown/button.vue1
-rw-r--r--app/assets/javascripts/notes/components/diff_with_note.vue162
-rw-r--r--app/assets/javascripts/pipelines/components/stage.vue1
-rw-r--r--app/assets/javascripts/vue_shared/components/memory_graph.vue2
-rw-r--r--app/assets/stylesheets/bootstrap_migration.scss5
-rw-r--r--app/assets/stylesheets/framework/gfm.scss8
-rw-r--r--app/assets/stylesheets/pages/merge_requests.scss1
-rw-r--r--app/controllers/projects/wikis_controller.rb7
-rw-r--r--app/finders/projects_finder.rb3
-rw-r--r--app/helpers/projects_helper.rb14
-rw-r--r--app/helpers/submodule_helper.rb8
-rw-r--r--app/models/deployment.rb4
-rw-r--r--app/models/environment.rb2
-rw-r--r--app/models/note.rb2
-rw-r--r--app/models/project.rb11
-rw-r--r--app/models/project_wiki.rb7
-rw-r--r--app/models/wiki_page.rb2
-rw-r--r--app/policies/environment_policy.rb5
-rw-r--r--app/serializers/environment_entity.rb2
-rw-r--r--app/services/access_token_validation_service.rb2
-rw-r--r--app/services/after_branch_delete_service.rb2
-rw-r--r--app/services/akismet_service.rb2
-rw-r--r--app/services/application_settings/base_service.rb2
-rw-r--r--app/services/application_settings/update_service.rb2
-rw-r--r--app/services/applications/create_service.rb2
-rw-r--r--app/services/audit_event_service.rb2
-rw-r--r--app/services/auth/container_registry_authentication_service.rb2
-rw-r--r--app/services/badges/base_service.rb2
-rw-r--r--app/services/badges/build_service.rb2
-rw-r--r--app/services/badges/create_service.rb2
-rw-r--r--app/services/badges/update_service.rb2
-rw-r--r--app/services/base_count_service.rb2
-rw-r--r--app/services/base_renderer.rb2
-rw-r--r--app/services/base_service.rb2
-rw-r--r--app/services/boards/base_service.rb2
-rw-r--r--app/services/boards/create_service.rb2
-rw-r--r--app/services/boards/issues/create_service.rb2
-rw-r--r--app/services/boards/issues/list_service.rb2
-rw-r--r--app/services/boards/issues/move_service.rb2
-rw-r--r--app/services/boards/list_service.rb2
-rw-r--r--app/services/boards/lists/create_service.rb2
-rw-r--r--app/services/boards/lists/destroy_service.rb2
-rw-r--r--app/services/boards/lists/generate_service.rb2
-rw-r--r--app/services/boards/lists/list_service.rb2
-rw-r--r--app/services/boards/lists/move_service.rb2
-rw-r--r--app/services/chat_names/authorize_user_service.rb2
-rw-r--r--app/services/chat_names/find_user_service.rb2
-rw-r--r--app/services/ci/create_pipeline_schedule_service.rb2
-rw-r--r--app/services/ci/create_pipeline_service.rb2
-rw-r--r--app/services/ci/ensure_stage_service.rb2
-rw-r--r--app/services/ci/extract_sections_from_build_trace_service.rb2
-rw-r--r--app/services/ci/fetch_kubernetes_token_service.rb2
-rw-r--r--app/services/ci/pipeline_trigger_service.rb2
-rw-r--r--app/services/ci/play_build_service.rb2
-rw-r--r--app/services/ci/process_pipeline_service.rb2
-rw-r--r--app/services/ci/register_job_service.rb2
-rw-r--r--app/services/ci/retry_build_service.rb2
-rw-r--r--app/services/ci/retry_pipeline_service.rb2
-rw-r--r--app/services/ci/stop_environments_service.rb4
-rw-r--r--app/services/ci/update_build_queue_service.rb2
-rw-r--r--app/services/ci/update_runner_service.rb2
-rw-r--r--app/services/clusters/applications/base_helm_service.rb2
-rw-r--r--app/services/clusters/applications/check_ingress_ip_address_service.rb2
-rw-r--r--app/services/clusters/applications/check_installation_progress_service.rb2
-rw-r--r--app/services/clusters/applications/install_service.rb2
-rw-r--r--app/services/clusters/applications/schedule_installation_service.rb2
-rw-r--r--app/services/clusters/create_service.rb2
-rw-r--r--app/services/clusters/gcp/fetch_operation_service.rb2
-rw-r--r--app/services/clusters/gcp/finalize_creation_service.rb2
-rw-r--r--app/services/clusters/gcp/provision_service.rb2
-rw-r--r--app/services/clusters/gcp/verify_provision_status_service.rb2
-rw-r--r--app/services/clusters/update_service.rb2
-rw-r--r--app/services/cohorts_service.rb2
-rw-r--r--app/services/commits/change_service.rb2
-rw-r--r--app/services/commits/cherry_pick_service.rb2
-rw-r--r--app/services/commits/create_service.rb2
-rw-r--r--app/services/commits/revert_service.rb2
-rw-r--r--app/services/compare_service.rb2
-rw-r--r--app/services/concerns/exclusive_lease_guard.rb2
-rw-r--r--app/services/concerns/issues/resolve_discussions.rb2
-rw-r--r--app/services/concerns/update_visibility_level.rb2
-rw-r--r--app/services/concerns/users/new_user_notifier.rb2
-rw-r--r--app/services/concerns/users/participable_service.rb2
-rw-r--r--app/services/create_branch_service.rb2
-rw-r--r--app/services/create_deployment_service.rb2
-rw-r--r--app/services/create_release_service.rb2
-rw-r--r--app/services/create_snippet_service.rb2
-rw-r--r--app/services/delete_branch_service.rb2
-rw-r--r--app/services/delete_merged_branches_service.rb2
-rw-r--r--app/services/deploy_keys/create_service.rb2
-rw-r--r--app/services/deploy_tokens/create_service.rb2
-rw-r--r--app/services/discussions/base_service.rb2
-rw-r--r--app/services/discussions/resolve_service.rb2
-rw-r--r--app/services/discussions/update_diff_position_service.rb2
-rw-r--r--app/services/emails/base_service.rb2
-rw-r--r--app/services/emails/confirm_service.rb2
-rw-r--r--app/services/emails/create_service.rb2
-rw-r--r--app/services/emails/destroy_service.rb2
-rw-r--r--app/services/event_create_service.rb2
-rw-r--r--app/services/events/render_service.rb2
-rw-r--r--app/services/files/base_service.rb2
-rw-r--r--app/services/files/create_dir_service.rb2
-rw-r--r--app/services/files/create_service.rb2
-rw-r--r--app/services/files/delete_service.rb2
-rw-r--r--app/services/files/multi_service.rb2
-rw-r--r--app/services/files/update_service.rb2
-rw-r--r--app/services/git_push_service.rb2
-rw-r--r--app/services/git_tag_push_service.rb2
-rw-r--r--app/services/gpg_keys/create_service.rb2
-rw-r--r--app/services/gravatar_service.rb2
-rw-r--r--app/services/groups/base_service.rb2
-rw-r--r--app/services/groups/create_service.rb2
-rw-r--r--app/services/groups/destroy_service.rb2
-rw-r--r--app/services/groups/nested_create_service.rb2
-rw-r--r--app/services/groups/transfer_service.rb2
-rw-r--r--app/services/groups/update_service.rb2
-rw-r--r--app/services/ham_service.rb2
-rw-r--r--app/services/import_export_clean_up_service.rb2
-rw-r--r--app/services/issuable/bulk_update_service.rb2
-rw-r--r--app/services/issuable/common_system_notes_service.rb2
-rw-r--r--app/services/issuable/destroy_service.rb2
-rw-r--r--app/services/issuable_base_service.rb2
-rw-r--r--app/services/issues/base_service.rb2
-rw-r--r--app/services/issues/build_service.rb8
-rw-r--r--app/services/issues/close_service.rb2
-rw-r--r--app/services/issues/create_service.rb2
-rw-r--r--app/services/issues/duplicate_service.rb2
-rw-r--r--app/services/issues/fetch_referenced_merge_requests_service.rb2
-rw-r--r--app/services/issues/move_service.rb2
-rw-r--r--app/services/issues/reopen_service.rb2
-rw-r--r--app/services/issues/update_service.rb2
-rw-r--r--app/services/keys/base_service.rb2
-rw-r--r--app/services/keys/create_service.rb2
-rw-r--r--app/services/keys/destroy_service.rb2
-rw-r--r--app/services/keys/last_used_service.rb2
-rw-r--r--app/services/labels/base_service.rb2
-rw-r--r--app/services/labels/create_service.rb2
-rw-r--r--app/services/labels/find_or_create_service.rb2
-rw-r--r--app/services/labels/promote_service.rb2
-rw-r--r--app/services/labels/transfer_service.rb2
-rw-r--r--app/services/labels/update_service.rb2
-rw-r--r--app/services/merge_request_metrics_service.rb2
-rw-r--r--app/services/metrics_service.rb2
-rw-r--r--app/services/note_summary.rb2
-rw-r--r--app/services/notification_recipient_service.rb2
-rw-r--r--app/services/notification_service.rb2
-rw-r--r--app/services/preview_markdown_service.rb2
-rw-r--r--app/services/push_event_payload_service.rb2
-rw-r--r--app/services/repair_ldap_blocked_user_service.rb2
-rw-r--r--app/services/repository_archive_clean_up_service.rb2
-rw-r--r--app/services/reset_project_cache_service.rb2
-rw-r--r--app/services/search_service.rb2
-rw-r--r--app/services/spam_check_service.rb2
-rw-r--r--app/services/spam_service.rb2
-rw-r--r--app/services/submit_usage_ping_service.rb2
-rw-r--r--app/services/system_hooks_service.rb2
-rw-r--r--app/services/system_note_service.rb35
-rw-r--r--app/services/todo_service.rb2
-rw-r--r--app/services/update_release_service.rb2
-rw-r--r--app/services/update_snippet_service.rb2
-rw-r--r--app/services/upload_service.rb8
-rw-r--r--app/services/user_agent_detail_service.rb2
-rw-r--r--app/services/user_project_access_changed_service.rb2
-rw-r--r--app/services/validate_new_branch_service.rb2
-rw-r--r--app/services/verify_pages_domain_service.rb2
-rw-r--r--app/services/web_hook_service.rb2
-rw-r--r--app/uploaders/file_uploader.rb8
-rw-r--r--app/views/ci/runner/_how_to_setup_runner.html.haml7
-rw-r--r--app/views/doorkeeper/applications/_delete_form.html.haml6
-rw-r--r--app/views/doorkeeper/applications/_form.html.haml8
-rw-r--r--app/views/doorkeeper/applications/edit.html.haml4
-rw-r--r--app/views/doorkeeper/applications/index.html.haml35
-rw-r--r--app/views/doorkeeper/applications/new.html.haml4
-rw-r--r--app/views/doorkeeper/applications/show.html.haml14
-rw-r--r--app/views/doorkeeper/authorizations/error.html.haml2
-rw-r--r--app/views/doorkeeper/authorizations/new.html.haml28
-rw-r--r--app/views/doorkeeper/authorizations/show.html.haml2
-rw-r--r--app/views/doorkeeper/authorized_applications/_delete_form.html.haml2
-rw-r--r--app/views/doorkeeper/authorized_applications/index.html.haml6
-rw-r--r--app/views/import/bitbucket/deploy_key.js.haml2
-rw-r--r--app/views/import/bitbucket/status.html.haml37
-rw-r--r--app/views/import/fogbugz/new.html.haml18
-rw-r--r--app/views/import/fogbugz/new_user_map.html.haml34
-rw-r--r--app/views/import/fogbugz/status.html.haml27
-rw-r--r--app/views/import/gitea/new.html.haml17
-rw-r--r--app/views/import/gitea/status.html.haml6
-rw-r--r--app/views/import/github/new.html.haml2
-rw-r--r--app/views/import/github/status.html.haml2
-rw-r--r--app/views/import/gitlab/status.html.haml22
-rw-r--r--app/views/import/gitlab_projects/new.html.haml16
-rw-r--r--app/views/import/google_code/new.html.haml42
-rw-r--r--app/views/import/google_code/new_user_map.html.haml36
-rw-r--r--app/views/import/google_code/status.html.haml40
-rw-r--r--app/views/projects/commit/_change.html.haml2
-rw-r--r--app/views/projects/environments/show.html.haml2
-rw-r--r--app/views/projects/imports/new.html.haml2
-rw-r--r--app/views/projects/protected_branches/_update_protected_branch.html.haml2
-rw-r--r--app/views/projects/protected_branches/shared/_protected_branch.html.haml2
-rw-r--r--app/views/projects/wikis/_sidebar.html.haml8
-rw-r--r--changelogs/custom_wiki_sidebar.yml5
-rw-r--r--changelogs/unreleased/48745-project-exports-fail-when-uploads-have-been-migrated-to-object-storage.yml5
-rw-r--r--changelogs/unreleased/49291-fix-memory-graph-component-typo.yml5
-rw-r--r--changelogs/unreleased/add-merge-request-header-branch-details-right-margin.yml5
-rw-r--r--changelogs/unreleased/close-revert-and-cherry-pick-modal-on-escape-keypress.yml5
-rw-r--r--changelogs/unreleased/feature-gb-email-delivery-metrics.yml5
-rw-r--r--changelogs/unreleased/fix-project-api-archived.yml5
-rw-r--r--changelogs/unreleased/frozen-string-enable-app-services.yml5
-rw-r--r--changelogs/unreleased/frozen-string-enable-apps-services-inner.yml5
-rw-r--r--changelogs/unreleased/satishperala-gitlab-ce-20720_webhooks_full_image_url.yml5
-rw-r--r--changelogs/unreleased/tweak-sql-buckets.yml5
-rw-r--r--changelogs/unreleased/update-issue-closing-pattern.yml5
-rw-r--r--changelogs/unreleased/update-specific-runners-help-url.yml5
-rw-r--r--config/initializers/1_settings.rb2
-rw-r--r--config/initializers/action_mailer_hooks.rb12
-rw-r--r--config/initializers/additional_headers_interceptor.rb1
-rw-r--r--config/initializers/disable_email_interceptor.rb5
-rw-r--r--config/initializers/email_template_interceptor.rb2
-rw-r--r--danger/changelog/Dangerfile20
-rw-r--r--danger/specs/Dangerfile14
-rw-r--r--doc/api/pipelines.md4
-rw-r--r--doc/api/users.md30
-rw-r--r--doc/development/architecture.md2
-rw-r--r--doc/development/sql.md42
-rw-r--r--doc/install/installation.md6
-rw-r--r--doc/topics/autodevops/index.md2
-rw-r--r--doc/update/10.6-to-10.7.md2
-rw-r--r--doc/user/project/import/manifest.md3
-rw-r--r--doc/user/project/integrations/webhooks.md28
-rw-r--r--doc/user/project/wiki/index.md7
-rw-r--r--lib/additional_email_headers_interceptor.rb6
-rw-r--r--lib/api/helpers.rb8
-rw-r--r--lib/api/projects.rb2
-rw-r--r--lib/banzai/filter/blockquote_fence_filter.rb22
-rw-r--r--lib/banzai/pipeline/gfm_pipeline.rb18
-rw-r--r--lib/banzai/pipeline/post_process_pipeline.rb12
-rw-r--r--lib/banzai/pipeline/single_line_pipeline.rb8
-rw-r--r--lib/disable_email_interceptor.rb7
-rw-r--r--lib/email_template_interceptor.rb11
-rw-r--r--lib/gitlab/email/hook/additional_headers_interceptor.rb12
-rw-r--r--lib/gitlab/email/hook/delivery_metrics_observer.rb31
-rw-r--r--lib/gitlab/email/hook/disable_email_interceptor.rb13
-rw-r--r--lib/gitlab/email/hook/email_template_interceptor.rb18
-rw-r--r--lib/gitlab/git/repository.rb254
-rw-r--r--lib/gitlab/hook_data/base_builder.rb38
-rw-r--r--lib/gitlab/hook_data/issuable_builder.rb8
-rw-r--r--lib/gitlab/hook_data/issue_builder.rb9
-rw-r--r--lib/gitlab/hook_data/merge_request_builder.rb9
-rw-r--r--lib/gitlab/hook_data/note_builder.rb43
-rw-r--r--lib/gitlab/hook_data/wiki_page_builder.rb15
-rw-r--r--lib/gitlab/import_export/avatar_saver.rb11
-rw-r--r--lib/gitlab/import_export/uploads_manager.rb101
-rw-r--r--lib/gitlab/import_export/uploads_restorer.rb21
-rw-r--r--lib/gitlab/import_export/uploads_saver.rb15
-rw-r--r--lib/gitlab/metrics/subscribers/active_record.rb2
-rw-r--r--lib/gitlab/regex.rb26
-rw-r--r--locale/gitlab.pot363
-rw-r--r--qa/qa.rb10
-rw-r--r--qa/qa/factory/resource/branch.rb21
-rw-r--r--qa/qa/factory/resource/file.rb34
-rw-r--r--qa/qa/page/file/form.rb40
-rw-r--r--qa/qa/page/file/shared/commit_message.rb19
-rw-r--r--qa/qa/page/file/show.rb30
-rw-r--r--qa/qa/page/main/oauth.rb2
-rw-r--r--qa/qa/page/project/settings/protected_branches.rb17
-rw-r--r--qa/qa/page/project/show.rb8
-rw-r--r--qa/qa/page/view.rb4
-rw-r--r--qa/qa/specs/features/project/file_spec.rb54
-rw-r--r--qa/qa/specs/features/repository/protected_branches_spec.rb5
-rw-r--r--spec/factories/uploads.rb7
-rw-r--r--spec/features/merge_request/user_sees_check_out_branch_modal_spec.rb4
-rw-r--r--spec/features/merge_request/user_sees_cherry_pick_modal_spec.rb (renamed from spec/features/merge_request/user_cherry_picks_spec.rb)22
-rw-r--r--spec/features/projects/wiki/user_creates_wiki_page_spec.rb46
-rw-r--r--spec/features/user_sees_revert_modal_spec.rb25
-rw-r--r--spec/helpers/projects_helper_spec.rb27
-rw-r--r--spec/helpers/submodule_helper_spec.rb21
-rw-r--r--spec/javascripts/diffs/components/diff_file_header_spec.js2
-rw-r--r--spec/javascripts/diffs/components/diff_file_spec.js2
-rw-r--r--spec/javascripts/diffs/components/diff_line_note_form_spec.js2
-rw-r--r--spec/javascripts/diffs/store/getters_spec.js19
-rw-r--r--spec/javascripts/environments/environment_item_spec.js2
-rw-r--r--spec/javascripts/environments/mock_data.js12
-rw-r--r--spec/javascripts/vue_shared/components/memory_graph_spec.js2
-rw-r--r--spec/lib/gitlab/closing_issue_extractor_spec.rb14
-rw-r--r--spec/lib/gitlab/email/hook/additional_headers_interceptor_spec.rb (renamed from spec/lib/additional_email_headers_interceptor_spec.rb)2
-rw-r--r--spec/lib/gitlab/email/hook/delivery_metrics_observer_spec.rb35
-rw-r--r--spec/lib/gitlab/email/hook/disable_email_interceptor_spec.rb (renamed from spec/lib/disable_email_interceptor_spec.rb)2
-rw-r--r--spec/lib/gitlab/git/diff_spec.rb2
-rw-r--r--spec/lib/gitlab/git/index_spec.rb16
-rw-r--r--spec/lib/gitlab/git/repository_spec.rb84
-rw-r--r--spec/lib/gitlab/hook_data/base_builder_spec.rb64
-rw-r--r--spec/lib/gitlab/hook_data/issue_builder_spec.rb9
-rw-r--r--spec/lib/gitlab/hook_data/merge_request_builder_spec.rb9
-rw-r--r--spec/lib/gitlab/import_export/avatar_saver_spec.rb1
-rw-r--r--spec/lib/gitlab/import_export/uploads_manager_spec.rb80
-rw-r--r--spec/lib/gitlab/import_export/uploads_saver_spec.rb5
-rw-r--r--spec/mailers/notify_spec.rb6
-rw-r--r--spec/models/deployment_spec.rb18
-rw-r--r--spec/models/environment_spec.rb17
-rw-r--r--spec/models/project_wiki_spec.rb16
-rw-r--r--spec/models/wiki_page_spec.rb10
-rw-r--r--spec/requests/api/projects_spec.rb33
-rw-r--r--spec/uploaders/file_uploader_spec.rb9
318 files changed, 2346 insertions, 1097 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 137c26d7dae..afe9da08495 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -1,4 +1,4 @@
-image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.4.4-golang-1.9-git-2.17-chrome-67.0-node-8.x-yarn-1.2-postgresql-9.6-graphicsmagick-1.3.29"
+image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.4.4-golang-1.9-git-2.18-chrome-67.0-node-8.x-yarn-1.2-postgresql-9.6-graphicsmagick-1.3.29"
.dedicated-runner: &dedicated-runner
retry: 1
@@ -86,7 +86,9 @@ stages:
.rails5: &rails5
allow_failure: true
only:
- - /rails5/
+ variables:
+ - $CI_COMMIT_REF_NAME =~ /rails5/
+ - $RAILS5_ENABLED
variables:
BUNDLE_GEMFILE: "Gemfile.rails5"
RAILS5: "true"
@@ -327,7 +329,7 @@ cloud-native-image:
cache: {}
script:
- gem install gitlab --no-ri --no-rdoc
- - ./scripts/trigger-build cng
+ - BUILD_TRIGGER_TOKEN=$CI_JOB_TOKEN scripts/trigger-build cng
only:
- tags@gitlab-org/gitlab-ce
- tags@gitlab-org/gitlab-ee
@@ -348,24 +350,6 @@ retrieve-tests-metadata:
- wget -O $FLAKY_RSPEC_SUITE_REPORT_PATH http://${TESTS_METADATA_S3_BUCKET}.s3.amazonaws.com/$FLAKY_RSPEC_SUITE_REPORT_PATH || rm $FLAKY_RSPEC_SUITE_REPORT_PATH
- '[[ -f $FLAKY_RSPEC_SUITE_REPORT_PATH ]] || echo "{}" > ${FLAKY_RSPEC_SUITE_REPORT_PATH}'
-danger-review:
- image: registry.gitlab.com/gitlab-org/gitaly/dangercontainer:latest
- stage: prepare
- before_script:
- - source scripts/utils.sh
- - retry gem install danger --no-ri --no-rdoc
- cache: {}
- only:
- refs:
- - branches@gitlab-org/gitlab-ce
- - branches@gitlab-org/gitlab-ee
- except:
- variables:
- - $CI_COMMIT_REF_NAME =~ /^ce-to-ee-.*/
- script:
- - git version
- - danger --fail-on-errors=true
-
update-tests-metadata:
<<: *tests-metadata-state
<<: *only-canonical-masters
@@ -454,6 +438,27 @@ setup-test-env:
- config/secrets.yml
- vendor/gitaly-ruby
+danger-review:
+ image: registry.gitlab.com/gitlab-org/gitaly/dangercontainer:latest
+ stage: test
+ allow_failure: true
+ before_script:
+ - source scripts/utils.sh
+ - retry gem install danger --no-ri --no-rdoc
+ cache: {}
+ only:
+ refs:
+ - branches@gitlab-org/gitlab-ce
+ - branches@gitlab-org/gitlab-ee
+ except:
+ refs:
+ - master
+ variables:
+ - $CI_COMMIT_REF_NAME =~ /^ce-to-ee-.*/
+ script:
+ - git version
+ - danger --fail-on-errors=true
+
rspec-pg 0 30: *rspec-metadata-pg
rspec-pg 1 30: *rspec-metadata-pg
rspec-pg 2 30: *rspec-metadata-pg
diff --git a/.gitlab/merge_request_templates/Database changes.md b/.gitlab/merge_request_templates/Database changes.md
index d14d52e1b6b..e636ec313df 100644
--- a/.gitlab/merge_request_templates/Database changes.md
+++ b/.gitlab/merge_request_templates/Database changes.md
@@ -34,17 +34,17 @@ When removing columns, tables, indexes or other structures:
## General checklist
- [ ] [Changelog entry](https://docs.gitlab.com/ee/development/changelog.html) added, if necessary
-- [ ] [Documentation created/updated](https://docs.gitlab.com/ee/development/doc_styleguide.html)
-- [ ] API support added
-- [ ] Tests added for this feature/bug
-- Conform by the [code review guidelines](https://docs.gitlab.com/ee/development/code_review.html)
- - [ ] Has been reviewed by a Backend maintainer
- - [ ] Has been reviewed by a Database specialist
-- [ ] Conform by the [merge request performance guides](https://docs.gitlab.com/ee/development/merge_request_performance_guidelines.html)
-- [ ] Conform by the [style guides](https://gitlab.com/gitlab-org/gitlab-ee/blob/master/CONTRIBUTING.md#style-guides)
+- [ ] [Documentation created/updated](https://docs.gitlab.com/ee/development/documentation/index.html#contributing-to-docs)
+- [ ] [API support added](https://docs.gitlab.com/ee/development/api_styleguide.html)
+- [ ] [Tests added for this feature/bug](https://docs.gitlab.com/ee/development/testing_guide/index.html)
+- Conforms to the [code review guidelines](https://docs.gitlab.com/ee/development/code_review.html)
+ - [ ] Has been reviewed by a Backend [maintainer](https://about.gitlab.com/handbook/engineering/#maintainer)
+ - [ ] Has been reviewed by a Database [specialist](https://about.gitlab.com/team/structure/#specialist)
+- [ ] Conforms to the [merge request performance guidelines](https://docs.gitlab.com/ee/development/merge_request_performance_guidelines.html)
+- [ ] Conforms to the [style guides](https://gitlab.com/gitlab-org/gitlab-ee/blob/master/CONTRIBUTING.md#style-guides)
- [ ] If you have multiple commits, please combine them into a few logically organized commits by [squashing them](https://git-scm.com/book/en/Git-Tools-Rewriting-History#Squashing-Commits)
-- [ ] Internationalization required/considered
-- [ ] If paid feature, have we considered GitLab.com plan and how it works for groups and is there a design for promoting it to users who aren't on the correct plan
-- [ ] End-to-end tests pass (`package-and-qa` manual pipeline job)
+- [ ] [Internationalization required/considered](https://docs.gitlab.com/ee/development/i18n/index.html)
+- [ ] For a paid feature, have we considered GitLab.com plans, how it works for groups, and is there a design for promoting it to users who aren't on the correct plan?
+- [ ] [End-to-end tests](https://docs.gitlab.com/ee/development/testing_guide/end_to_end_tests.html#testing-code-in-merge-requests) pass (`package-and-qa` manual pipeline job)
/label ~database
diff --git a/.gitlab/merge_request_templates/Documentation.md b/.gitlab/merge_request_templates/Documentation.md
index da38a703c3c..531035b3766 100644
--- a/.gitlab/merge_request_templates/Documentation.md
+++ b/.gitlab/merge_request_templates/Documentation.md
@@ -1,4 +1,4 @@
-<!--See the general Documentation guidelines https://docs.gitlab.com/ce/development/writing_documentation.html -->
+<!--See the general Documentation guidelines https://docs.gitlab.com/ee/development/documentation/index.html -->
## What does this MR do?
@@ -13,17 +13,17 @@ Closes
## Moving docs to a new location?
Read the guidelines:
-https://docs.gitlab.com/ce/development/writing_documentation.html#changing-document-location
+https://docs.gitlab.com/ee/development/documentation/#changing-document-location
- [ ] Make sure the old link is not removed and has its contents replaced with
a link to the new location.
- [ ] Make sure internal links pointing to the document in question are not broken.
-- [ ] Search and replace any links referring to old docs in GitLab Rails app,
- specifically under the `app/views/` and `ee/app/views` (for GitLab EE) directories.
-- [ ] Make sure to add [`redirect_from`](https://docs.gitlab.com/ce/development/writing_documentation.html#redirections-for-pages-with-disqus-comments)
+- [ ] Search and replace any links referring to the old docs in the GitLab Rails app,
+ specifically under the `app/views/` and `ee/app/views` (for GitLab EE) directories.
+- [ ] Make sure to add [`redirect_from`](https://docs.gitlab.com/ee/development/documentation/index.html#redirections-for-pages-with-disqus-comments)
to the new document if there are any Disqus comments on the old document thread.
-- [ ] If working on CE and the `ee-compat-check` jobs fails, submit an MR to EE
- with the changes as well (https://docs.gitlab.com/ce/development/writing_documentation.html#cherry-picking-from-ce-to-ee).
+- [ ] If working on CE and the `ee-compat-check` jobs fails, [submit an MR to EE
+ with the changes](https://docs.gitlab.com/ee/development/documentation/index.html#cherry-picking-from-ce-to-ee) as well.
- [ ] Ping one of the technical writers for review.
/label ~Documentation
diff --git a/app/assets/javascripts/diffs/components/app.vue b/app/assets/javascripts/diffs/components/app.vue
index 1d1415fe6ca..7cc4e6a2c3a 100644
--- a/app/assets/javascripts/diffs/components/app.vue
+++ b/app/assets/javascripts/diffs/components/app.vue
@@ -85,6 +85,9 @@ export default {
}
return __('Show latest version');
},
+ canCurrentUserFork() {
+ return this.currentUser.canFork === true && this.currentUser.canCreateMergeRequest;
+ },
},
watch: {
diffViewType() {
@@ -192,7 +195,7 @@ export default {
v-for="file in diffFiles"
:key="file.newPath"
:file="file"
- :current-user="currentUser"
+ :can-current-user-fork="canCurrentUserFork"
/>
</div>
<no-changes v-else />
diff --git a/app/assets/javascripts/diffs/components/diff_file.vue b/app/assets/javascripts/diffs/components/diff_file.vue
index 944084f05c9..7e7058d8d08 100644
--- a/app/assets/javascripts/diffs/components/diff_file.vue
+++ b/app/assets/javascripts/diffs/components/diff_file.vue
@@ -18,8 +18,8 @@ export default {
type: Object,
required: true,
},
- currentUser: {
- type: Object,
+ canCurrentUserFork: {
+ type: Boolean,
required: true,
},
},
@@ -87,7 +87,7 @@ export default {
class="diff-file file-holder"
>
<diff-file-header
- :current-user="currentUser"
+ :can-current-user-fork="canCurrentUserFork"
:diff-file="file"
:collapsible="true"
:expanded="!isCollapsed"
diff --git a/app/assets/javascripts/diffs/components/diff_file_header.vue b/app/assets/javascripts/diffs/components/diff_file_header.vue
index c5abd0a9568..c494d3bcd3e 100644
--- a/app/assets/javascripts/diffs/components/diff_file_header.vue
+++ b/app/assets/javascripts/diffs/components/diff_file_header.vue
@@ -39,8 +39,8 @@ export default {
required: false,
default: true,
},
- currentUser: {
- type: Object,
+ canCurrentUserFork: {
+ type: Boolean,
required: true,
},
},
@@ -228,7 +228,7 @@ export default {
<edit-button
v-if="!diffFile.deletedFile"
- :current-user="currentUser"
+ :can-current-user-fork="canCurrentUserFork"
:edit-path="diffFile.editPath"
:can-modify-blob="diffFile.canModifyBlob"
@showForkMessage="showForkMessage"
diff --git a/app/assets/javascripts/diffs/components/diff_line_note_form.vue b/app/assets/javascripts/diffs/components/diff_line_note_form.vue
index db380e68bd1..32f9516d332 100644
--- a/app/assets/javascripts/diffs/components/diff_line_note_form.vue
+++ b/app/assets/javascripts/diffs/components/diff_line_note_form.vue
@@ -13,12 +13,8 @@ export default {
noteForm,
},
props: {
- diffFile: {
- type: Object,
- required: true,
- },
- diffLines: {
- type: Array,
+ diffFileHash: {
+ type: String,
required: true,
},
line: {
@@ -40,6 +36,7 @@ export default {
noteableData: state => state.notes.noteableData,
diffViewType: state => state.diffs.diffViewType,
}),
+ ...mapGetters('diffs', ['getDiffFileByHash']),
...mapGetters(['isLoggedIn', 'noteableType', 'getNoteableData', 'getNotesDataByProp']),
},
mounted() {
@@ -68,13 +65,14 @@ export default {
});
},
handleSaveNote(note) {
+ const selectedDiffFile = this.getDiffFileByHash(this.diffFileHash);
const postData = getNoteFormData({
note,
noteableData: this.noteableData,
noteableType: this.noteableType,
noteTargetLine: this.noteTargetLine,
diffViewType: this.diffViewType,
- diffFile: this.diffFile,
+ diffFile: selectedDiffFile,
linePosition: this.position,
});
diff --git a/app/assets/javascripts/diffs/components/diff_table_cell.vue b/app/assets/javascripts/diffs/components/diff_table_cell.vue
index bd02b45a63c..5962f30d9bb 100644
--- a/app/assets/javascripts/diffs/components/diff_table_cell.vue
+++ b/app/assets/javascripts/diffs/components/diff_table_cell.vue
@@ -24,8 +24,12 @@ export default {
type: Object,
required: true,
},
- diffFile: {
- type: Object,
+ fileHash: {
+ type: String,
+ required: true,
+ },
+ contextLinesPath: {
+ type: String,
required: true,
},
diffViewType: {
@@ -120,14 +124,14 @@ export default {
:class="classNameMap"
>
<diff-line-gutter-content
- :file-hash="diffFile.fileHash"
+ :file-hash="fileHash"
+ :context-lines-path="contextLinesPath"
:line-type="normalizedLine.type"
:line-code="normalizedLine.lineCode"
:line-position="linePosition"
:line-number="lineNumber"
:meta-data="normalizedLine.metaData"
:show-comment-button="showCommentButton"
- :context-lines-path="diffFile.contextLinesPath"
:is-bottom="isBottom"
:is-match-line="isMatchLine"
:is-context-line="isContentLine"
diff --git a/app/assets/javascripts/diffs/components/edit_button.vue b/app/assets/javascripts/diffs/components/edit_button.vue
index ebf90631d76..2fb85ca2f07 100644
--- a/app/assets/javascripts/diffs/components/edit_button.vue
+++ b/app/assets/javascripts/diffs/components/edit_button.vue
@@ -5,8 +5,8 @@ export default {
type: String,
required: true,
},
- currentUser: {
- type: Object,
+ canCurrentUserFork: {
+ type: Boolean,
required: true,
},
canModifyBlob: {
@@ -17,12 +17,12 @@ export default {
},
methods: {
handleEditClick(evt) {
- if (!this.currentUser || this.canModifyBlob) {
+ if (!this.canCurrentUserFork || this.canModifyBlob) {
// if we can Edit, do default Edit button behavior
return;
}
- if (this.currentUser.canFork && this.currentUser.canCreateMergeRequest) {
+ if (this.canCurrentUserFork) {
evt.preventDefault();
this.$emit('showForkMessage');
}
diff --git a/app/assets/javascripts/diffs/components/inline_diff_comment_row.vue b/app/assets/javascripts/diffs/components/inline_diff_comment_row.vue
index 1e8f2eecd76..ca265dd892c 100644
--- a/app/assets/javascripts/diffs/components/inline_diff_comment_row.vue
+++ b/app/assets/javascripts/diffs/components/inline_diff_comment_row.vue
@@ -13,12 +13,8 @@ export default {
type: Object,
required: true,
},
- diffFile: {
- type: Object,
- required: true,
- },
- diffLines: {
- type: Array,
+ diffFileHash: {
+ type: String,
required: true,
},
lineIndex: {
@@ -58,10 +54,9 @@ export default {
/>
<diff-line-note-form
v-if="diffLineCommentForms[line.lineCode]"
- :diff-file="diffFile"
- :diff-lines="diffLines"
+ :diff-file-hash="diffFileHash"
:line="line"
- :note-target-line="diffLines[lineIndex]"
+ :note-target-line="line"
/>
</div>
</td>
diff --git a/app/assets/javascripts/diffs/components/inline_diff_table_row.vue b/app/assets/javascripts/diffs/components/inline_diff_table_row.vue
index 8e4715c9862..0197a510ef1 100644
--- a/app/assets/javascripts/diffs/components/inline_diff_table_row.vue
+++ b/app/assets/javascripts/diffs/components/inline_diff_table_row.vue
@@ -16,8 +16,12 @@ export default {
DiffTableCell,
},
props: {
- diffFile: {
- type: Object,
+ fileHash: {
+ type: String,
+ required: true,
+ },
+ contextLinesPath: {
+ type: String,
required: true,
},
line: {
@@ -50,7 +54,7 @@ export default {
inlineRowId() {
const { lineCode, oldLine, newLine } = this.line;
- return lineCode || `${this.diffFile.fileHash}_${oldLine}_${newLine}`;
+ return lineCode || `${this.fileHash}_${oldLine}_${newLine}`;
},
},
created() {
@@ -78,7 +82,8 @@ export default {
@mouseout="handleMouseMove"
>
<diff-table-cell
- :diff-file="diffFile"
+ :file-hash="fileHash"
+ :context-lines-path="contextLinesPath"
:line="line"
:line-type="oldLineType"
:is-bottom="isBottom"
@@ -87,7 +92,8 @@ export default {
class="diff-line-num old_line"
/>
<diff-table-cell
- :diff-file="diffFile"
+ :file-hash="fileHash"
+ :context-lines-path="contextLinesPath"
:line="line"
:line-type="newLineType"
:is-bottom="isBottom"
diff --git a/app/assets/javascripts/diffs/components/inline_diff_view.vue b/app/assets/javascripts/diffs/components/inline_diff_view.vue
index 9c1359f7c89..9fd19b74cd7 100644
--- a/app/assets/javascripts/diffs/components/inline_diff_view.vue
+++ b/app/assets/javascripts/diffs/components/inline_diff_view.vue
@@ -60,15 +60,15 @@ export default {
v-for="(line, index) in normalizedDiffLines"
>
<inline-diff-table-row
- :diff-file="diffFile"
+ :file-hash="diffFile.fileHash"
+ :context-lines-path="diffFile.contextLinesPath"
:line="line"
:is-bottom="index + 1 === diffLinesLength"
:key="line.lineCode"
/>
<inline-diff-comment-row
v-if="shouldRenderCommentRow(line)"
- :diff-file="diffFile"
- :diff-lines="normalizedDiffLines"
+ :diff-file-hash="diffFile.fileHash"
:line="line"
:line-index="index"
:key="index"
diff --git a/app/assets/javascripts/diffs/components/parallel_diff_comment_row.vue b/app/assets/javascripts/diffs/components/parallel_diff_comment_row.vue
index 1e20792b647..cc5248c25d9 100644
--- a/app/assets/javascripts/diffs/components/parallel_diff_comment_row.vue
+++ b/app/assets/javascripts/diffs/components/parallel_diff_comment_row.vue
@@ -13,12 +13,8 @@ export default {
type: Object,
required: true,
},
- diffFile: {
- type: Object,
- required: true,
- },
- diffLines: {
- type: Array,
+ diffFileHash: {
+ type: String,
required: true,
},
lineIndex: {
@@ -91,10 +87,9 @@ export default {
<diff-line-note-form
v-if="diffLineCommentForms[leftLineCode] &&
diffLineCommentForms[leftLineCode]"
- :diff-file="diffFile"
- :diff-lines="diffLines"
+ :diff-file-hash="diffFileHash"
:line="line.left"
- :note-target-line="diffLines[lineIndex].left"
+ :note-target-line="line.left"
position="left"
/>
</td>
@@ -112,10 +107,9 @@ export default {
<diff-line-note-form
v-if="diffLineCommentForms[rightLineCode] &&
diffLineCommentForms[rightLineCode] && line.right.type"
- :diff-file="diffFile"
- :diff-lines="diffLines"
+ :diff-file-hash="diffFileHash"
:line="line.right"
- :note-target-line="diffLines[lineIndex].right"
+ :note-target-line="line.right"
position="right"
/>
</td>
diff --git a/app/assets/javascripts/diffs/components/parallel_diff_table_row.vue b/app/assets/javascripts/diffs/components/parallel_diff_table_row.vue
index b76fc63205b..ee5bb4d8d05 100644
--- a/app/assets/javascripts/diffs/components/parallel_diff_table_row.vue
+++ b/app/assets/javascripts/diffs/components/parallel_diff_table_row.vue
@@ -19,8 +19,12 @@ export default {
DiffTableCell,
},
props: {
- diffFile: {
- type: Object,
+ fileHash: {
+ type: String,
+ required: true,
+ },
+ contextLinesPath: {
+ type: String,
required: true,
},
line: {
@@ -103,7 +107,8 @@ export default {
@mouseout="handleMouseMove"
>
<diff-table-cell
- :diff-file="diffFile"
+ :file-hash="fileHash"
+ :context-lines-path="contextLinesPath"
:line="line"
:line-type="oldLineType"
:line-position="linePositionLeft"
@@ -123,7 +128,8 @@ export default {
>
</td>
<diff-table-cell
- :diff-file="diffFile"
+ :file-hash="fileHash"
+ :context-lines-path="contextLinesPath"
:line="line"
:line-type="newLineType"
:line-position="linePositionRight"
diff --git a/app/assets/javascripts/diffs/components/parallel_diff_view.vue b/app/assets/javascripts/diffs/components/parallel_diff_view.vue
index 216865474a6..32528c9e7ab 100644
--- a/app/assets/javascripts/diffs/components/parallel_diff_view.vue
+++ b/app/assets/javascripts/diffs/components/parallel_diff_view.vue
@@ -93,17 +93,17 @@ export default {
v-for="(line, index) in parallelDiffLines"
>
<parallel-diff-table-row
- :diff-file="diffFile"
+ :file-hash="diffFile.fileHash"
+ :context-lines-path="diffFile.contextLinesPath"
:line="line"
:is-bottom="index + 1 === diffLinesLength"
:key="index"
/>
<parallel-diff-comment-row
v-if="shouldRenderCommentRow(line)"
- :key="line.left.lineCode || line.right.lineCode"
+ :key="`dcr-${index}`"
:line="line"
- :diff-file="diffFile"
- :diff-lines="parallelDiffLines"
+ :diff-file-hash="diffFile.fileHash"
:line-index="index"
/>
</template>
diff --git a/app/assets/javascripts/diffs/store/getters.js b/app/assets/javascripts/diffs/store/getters.js
index f89acb73ed8..855de79adf8 100644
--- a/app/assets/javascripts/diffs/store/getters.js
+++ b/app/assets/javascripts/diffs/store/getters.js
@@ -57,4 +57,8 @@ export const getDiffFileDiscussions = (state, getters, rootState, rootGetters) =
) || [];
// prevent babel-plugin-rewire from generating an invalid default during karma∂ tests
+export const getDiffFileByHash = state => fileHash =>
+ state.diffFiles.find(file => file.fileHash === fileHash);
+
+// prevent babel-plugin-rewire from generating an invalid default during karma tests
export default () => {};
diff --git a/app/assets/javascripts/ide/components/new_dropdown/button.vue b/app/assets/javascripts/ide/components/new_dropdown/button.vue
index 7682b34ce4d..ff114e47741 100644
--- a/app/assets/javascripts/ide/components/new_dropdown/button.vue
+++ b/app/assets/javascripts/ide/components/new_dropdown/button.vue
@@ -38,6 +38,7 @@ export default {
<button
:aria-label="label"
type="button"
+ class="btn-blank"
@click.stop.prevent="clicked"
>
<icon
diff --git a/app/assets/javascripts/notes/components/diff_with_note.vue b/app/assets/javascripts/notes/components/diff_with_note.vue
index 9c2908c477e..27ff7dea909 100644
--- a/app/assets/javascripts/notes/components/diff_with_note.vue
+++ b/app/assets/javascripts/notes/components/diff_with_note.vue
@@ -1,94 +1,90 @@
<script>
- import { mapState, mapActions } from 'vuex';
- import imageDiffHelper from '~/image_diff/helpers/index';
- import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
- import DiffFileHeader from '~/diffs/components/diff_file_header.vue';
- import SkeletonLoadingContainer from '~/vue_shared/components/skeleton_loading_container.vue';
- import { trimFirstCharOfLineContent } from '~/diffs/store/utils';
+import { mapState, mapActions } from 'vuex';
+import imageDiffHelper from '~/image_diff/helpers/index';
+import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
+import DiffFileHeader from '~/diffs/components/diff_file_header.vue';
+import SkeletonLoadingContainer from '~/vue_shared/components/skeleton_loading_container.vue';
+import { trimFirstCharOfLineContent } from '~/diffs/store/utils';
- export default {
- components: {
- DiffFileHeader,
- SkeletonLoadingContainer,
+export default {
+ components: {
+ DiffFileHeader,
+ SkeletonLoadingContainer,
+ },
+ props: {
+ discussion: {
+ type: Object,
+ required: true,
},
- props: {
- discussion: {
- type: Object,
- required: true,
- },
+ },
+ data() {
+ return {
+ error: false,
+ };
+ },
+ computed: {
+ ...mapState({
+ noteableData: state => state.notes.noteableData,
+ }),
+ hasTruncatedDiffLines() {
+ return this.discussion.truncatedDiffLines && this.discussion.truncatedDiffLines.length !== 0;
},
- data() {
- return {
- error: false,
- };
+ isDiscussionsExpanded() {
+ return true; // TODO: @fatihacet - Fix this.
},
- computed: {
- ...mapState({
- noteableData: state => state.notes.noteableData,
- }),
- hasTruncatedDiffLines() {
- return this.discussion.truncatedDiffLines &&
- this.discussion.truncatedDiffLines.length !== 0;
- },
- isDiscussionsExpanded() {
- return true; // TODO: @fatihacet - Fix this.
- },
- isCollapsed() {
- return this.diffFile.collapsed || false;
- },
- isImageDiff() {
- return !this.diffFile.text;
- },
- diffFileClass() {
- const { text } = this.diffFile;
- return text ? 'text-file' : 'js-image-file';
- },
- diffFile() {
- return convertObjectPropsToCamelCase(this.discussion.diffFile, { deep: true });
- },
- imageDiffHtml() {
- return this.discussion.imageDiffHtml;
- },
- currentUser() {
- return this.noteableData.current_user;
- },
- userColorScheme() {
- return window.gon.user_color_scheme;
- },
- normalizedDiffLines() {
- if (this.discussion.truncatedDiffLines) {
- return this.discussion.truncatedDiffLines.map(line =>
- trimFirstCharOfLineContent(convertObjectPropsToCamelCase(line)),
- );
- }
-
- return [];
- },
+ isCollapsed() {
+ return this.diffFile.collapsed || false;
+ },
+ isImageDiff() {
+ return !this.diffFile.text;
+ },
+ diffFileClass() {
+ const { text } = this.diffFile;
+ return text ? 'text-file' : 'js-image-file';
+ },
+ diffFile() {
+ return convertObjectPropsToCamelCase(this.discussion.diffFile, { deep: true });
},
- mounted() {
- if (this.isImageDiff) {
- const canCreateNote = false;
- const renderCommentBadge = true;
- imageDiffHelper.initImageDiff(this.$refs.fileHolder, canCreateNote, renderCommentBadge);
- } else if (!this.hasTruncatedDiffLines) {
- this.fetchDiff();
+ imageDiffHtml() {
+ return this.discussion.imageDiffHtml;
+ },
+ userColorScheme() {
+ return window.gon.user_color_scheme;
+ },
+ normalizedDiffLines() {
+ if (this.discussion.truncatedDiffLines) {
+ return this.discussion.truncatedDiffLines.map(line =>
+ trimFirstCharOfLineContent(convertObjectPropsToCamelCase(line)),
+ );
}
+
+ return [];
+ },
+ },
+ mounted() {
+ if (this.isImageDiff) {
+ const canCreateNote = false;
+ const renderCommentBadge = true;
+ imageDiffHelper.initImageDiff(this.$refs.fileHolder, canCreateNote, renderCommentBadge);
+ } else if (!this.hasTruncatedDiffLines) {
+ this.fetchDiff();
+ }
+ },
+ methods: {
+ ...mapActions(['fetchDiscussionDiffLines']),
+ rowTag(html) {
+ return html.outerHTML ? 'tr' : 'template';
},
- methods: {
- ...mapActions(['fetchDiscussionDiffLines']),
- rowTag(html) {
- return html.outerHTML ? 'tr' : 'template';
- },
- fetchDiff() {
- this.error = false;
- this.fetchDiscussionDiffLines(this.discussion)
- .then(this.highlight)
- .catch(() => {
- this.error = true;
- });
- },
+ fetchDiff() {
+ this.error = false;
+ this.fetchDiscussionDiffLines(this.discussion)
+ .then(this.highlight)
+ .catch(() => {
+ this.error = true;
+ });
},
- };
+ },
+};
</script>
<template>
@@ -99,7 +95,7 @@
>
<diff-file-header
:diff-file="diffFile"
- :current-user="currentUser"
+ :can-current-user-fork="false"
:discussions-expanded="isDiscussionsExpanded"
:expanded="!isCollapsed"
/>
diff --git a/app/assets/javascripts/pipelines/components/stage.vue b/app/assets/javascripts/pipelines/components/stage.vue
index 56fdb858088..c7df69c69ed 100644
--- a/app/assets/javascripts/pipelines/components/stage.vue
+++ b/app/assets/javascripts/pipelines/components/stage.vue
@@ -175,6 +175,7 @@ export default {
<span
:aria-label="stage.title"
aria-hidden="true"
+ class="no-pointer-events"
>
<icon :name="borderlessIcon" />
</span>
diff --git a/app/assets/javascripts/vue_shared/components/memory_graph.vue b/app/assets/javascripts/vue_shared/components/memory_graph.vue
index 522091ea889..552a92541be 100644
--- a/app/assets/javascripts/vue_shared/components/memory_graph.vue
+++ b/app/assets/javascripts/vue_shared/components/memory_graph.vue
@@ -126,7 +126,7 @@ export default {
:cx="dotX"
:cy="dotY"
r="1.5"
- tranform="translate(0 -1)"
+ transform="translate(0 -1)"
/>
</svg>
</div>
diff --git a/app/assets/stylesheets/bootstrap_migration.scss b/app/assets/stylesheets/bootstrap_migration.scss
index ded33e8b151..d28ad407734 100644
--- a/app/assets/stylesheets/bootstrap_migration.scss
+++ b/app/assets/stylesheets/bootstrap_migration.scss
@@ -110,7 +110,7 @@ code {
padding: 2px 4px;
color: $red-600;
background-color: $red-100;
- border-radius: 3px;
+ border-radius: $border-radius-default;
.code > & {
background-color: inherit;
@@ -128,7 +128,8 @@ table {
border-spacing: 0;
}
-.tooltip {
+.tooltip,
+.no-pointer-events {
// Fix bootstrap4 bug whereby tooltips flicker when they are hovered over their borders
pointer-events: none;
}
diff --git a/app/assets/stylesheets/framework/gfm.scss b/app/assets/stylesheets/framework/gfm.scss
index 1cf12b1a015..d2ba76f5160 100644
--- a/app/assets/stylesheets/framework/gfm.scss
+++ b/app/assets/stylesheets/framework/gfm.scss
@@ -9,12 +9,8 @@
.gfm-project_member {
padding: 0 2px;
- border-radius: #{$border-radius-default / 2};
- background-color: $user-mention-bg;
-
- &:hover {
- background-color: $user-mention-bg-hover;
- }
+ background-color: $blue-100;
+ border-radius: $border-radius-default;
}
.gfm-color_chip {
diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss
index 5835b8b8c9b..c8349a4ef79 100644
--- a/app/assets/stylesheets/pages/merge_requests.scss
+++ b/app/assets/stylesheets/pages/merge_requests.scss
@@ -551,6 +551,7 @@
@include media-breakpoint-up(lg) {
.branch-actions {
align-self: center;
+ margin-left: $gl-padding;
}
}
}
diff --git a/app/controllers/projects/wikis_controller.rb b/app/controllers/projects/wikis_controller.rb
index c01066c688a..9dc0c31be49 100644
--- a/app/controllers/projects/wikis_controller.rb
+++ b/app/controllers/projects/wikis_controller.rb
@@ -116,7 +116,12 @@ class Projects::WikisController < Projects::ApplicationController
# Call #wiki to make sure the Wiki Repo is initialized
@project_wiki.wiki
- @sidebar_wiki_entries = WikiPage.group_by_directory(@project_wiki.pages(limit: 15))
+
+ @sidebar_page = @project_wiki.find_sidebar(params[:version_id])
+
+ unless @sidebar_page # Fallback to default sidebar
+ @sidebar_wiki_entries = WikiPage.group_by_directory(@project_wiki.pages(limit: 15))
+ end
rescue ProjectWiki::CouldNotCreateWikiError
flash[:notice] = "Could not create Wiki Repository at this time. Please try again later."
redirect_to project_path(@project)
diff --git a/app/finders/projects_finder.rb b/app/finders/projects_finder.rb
index c7d6bc6cfdc..b06595081e7 100644
--- a/app/finders/projects_finder.rb
+++ b/app/finders/projects_finder.rb
@@ -16,6 +16,7 @@
# personal: boolean
# search: string
# non_archived: boolean
+# archived: 'only' or boolean
#
class ProjectsFinder < UnionFinder
include CustomAttributesFilter
@@ -130,7 +131,7 @@ class ProjectsFinder < UnionFinder
def by_archived(projects)
if params[:non_archived]
projects.non_archived
- elsif params.key?(:archived) # Back-compatibility with the places where `params[:archived]` can be set explicitly to `false`
+ elsif params.key?(:archived)
if params[:archived] == 'only'
projects.archived
elsif Gitlab::Utils.to_boolean(params[:archived])
diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb
index b0f381db5ab..221f1aa9dd8 100644
--- a/app/helpers/projects_helper.rb
+++ b/app/helpers/projects_helper.rb
@@ -413,20 +413,6 @@ module ProjectsHelper
@ref || @repository.try(:root_ref)
end
- # Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/1235
- def sanitize_repo_path(project, message)
- return '' unless message.present?
-
- exports_path = File.join(Settings.shared['path'], 'tmp/project_exports')
- filtered_message = message.strip.gsub(exports_path, "[REPO EXPORT PATH]")
-
- disk_path = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- Gitlab.config.repositories.storages[project.repository_storage].legacy_disk_path
- end
-
- filtered_message.gsub(disk_path.chomp('/'), "[REPOS PATH]")
- end
-
def project_child_container_class(view_path)
view_path == "projects/issues/issues" ? "prepend-top-default" : "project-show-#{view_path}"
end
diff --git a/app/helpers/submodule_helper.rb b/app/helpers/submodule_helper.rb
index 9151543dfdc..ebfde993456 100644
--- a/app/helpers/submodule_helper.rb
+++ b/app/helpers/submodule_helper.rb
@@ -8,7 +8,7 @@ module SubmoduleHelper
url = repository.submodule_url_for(ref, submodule_item.path)
if url == '.' || url == './'
- url = File.join(Gitlab.config.gitlab.url, @project.full_path)
+ url = File.join(Gitlab.config.gitlab.url, repository.project.full_path)
end
if url =~ %r{([^/:]+)/([^/]+(?:\.git)?)\Z}
@@ -31,7 +31,7 @@ module SubmoduleHelper
[namespace_project_path(namespace, project),
namespace_project_tree_path(namespace, project, submodule_item.id)]
elsif relative_self_url?(url)
- relative_self_links(url, submodule_item.id)
+ relative_self_links(url, submodule_item.id, repository.project)
elsif github_dot_com_url?(url)
standard_links('github.com', namespace, project, submodule_item.id)
elsif gitlab_dot_com_url?(url)
@@ -73,7 +73,7 @@ module SubmoduleHelper
[base, [base, '/tree/', commit].join('')]
end
- def relative_self_links(url, commit)
+ def relative_self_links(url, commit, project)
url.rstrip!
# Map relative links to a namespace and project
# For example:
@@ -85,7 +85,7 @@ module SubmoduleHelper
namespace = components.pop.gsub(/^\.\.$/, '')
if namespace.empty?
- namespace = @project.namespace.full_path
+ namespace = project.namespace.full_path
end
begin
diff --git a/app/models/deployment.rb b/app/models/deployment.rb
index ac86e9e8de0..687246b47b2 100644
--- a/app/models/deployment.rb
+++ b/app/models/deployment.rb
@@ -92,10 +92,6 @@ class Deployment < ActiveRecord::Base
@stop_action ||= manual_actions.find_by(name: on_stop)
end
- def stop_action?
- stop_action.present?
- end
-
def formatted_deployment_time
created_at.to_time.in_time_zone.to_s(:medium)
end
diff --git a/app/models/environment.rb b/app/models/environment.rb
index 8d523dae324..4856d313318 100644
--- a/app/models/environment.rb
+++ b/app/models/environment.rb
@@ -117,7 +117,7 @@ class Environment < ActiveRecord::Base
external_url.gsub(%r{\A.*?://}, '')
end
- def stop_action?
+ def stop_action_available?
available? && stop_action.present?
end
diff --git a/app/models/note.rb b/app/models/note.rb
index abc40d9016e..fe3507adcb3 100644
--- a/app/models/note.rb
+++ b/app/models/note.rb
@@ -202,7 +202,7 @@ class Note < ActiveRecord::Base
end
def hook_attrs
- attributes
+ Gitlab::HookData::NoteBuilder.new(self).build
end
def for_commit?
diff --git a/app/models/project.rb b/app/models/project.rb
index 4606cdc70a8..5f582dfa5ee 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -2173,10 +2173,13 @@ class Project < ActiveRecord::Base
merge_requests = source_of_merge_requests.opened
.where(allow_collaboration: true)
- if branch_name
- merge_requests.find_by(source_branch: branch_name)&.can_be_merged_by?(user)
- else
- merge_requests.any? { |merge_request| merge_request.can_be_merged_by?(user) }
+ # Issue for N+1: https://gitlab.com/gitlab-org/gitlab-ce/issues/49322
+ Gitlab::GitalyClient.allow_n_plus_1_calls do
+ if branch_name
+ merge_requests.find_by(source_branch: branch_name)&.can_be_merged_by?(user)
+ else
+ merge_requests.any? { |merge_request| merge_request.can_be_merged_by?(user) }
+ end
end
end
diff --git a/app/models/project_wiki.rb b/app/models/project_wiki.rb
index 9ae2fb0013a..3aa56b3983f 100644
--- a/app/models/project_wiki.rb
+++ b/app/models/project_wiki.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ProjectWiki
include Gitlab::ShellAdapter
include Storage::LegacyProjectWiki
@@ -9,6 +11,7 @@ class ProjectWiki
}.freeze unless defined?(MARKUPS)
CouldNotCreateWikiError = Class.new(StandardError)
+ SIDEBAR = '_sidebar'
# Returns a string describing what went wrong after
# an operation fails.
@@ -98,6 +101,10 @@ class ProjectWiki
end
end
+ def find_sidebar(version = nil)
+ find_page(SIDEBAR, version)
+ end
+
def find_file(name, version = nil)
wiki.file(name, version)
end
diff --git a/app/models/wiki_page.rb b/app/models/wiki_page.rb
index 4b49edb01a5..55243136140 100644
--- a/app/models/wiki_page.rb
+++ b/app/models/wiki_page.rb
@@ -60,7 +60,7 @@ class WikiPage
attr_accessor :attributes
def hook_attrs
- attributes
+ Gitlab::HookData::WikiPageBuilder.new(self).build
end
def initialize(wiki, page = nil, persisted = false)
diff --git a/app/policies/environment_policy.rb b/app/policies/environment_policy.rb
index 978dc3a7c81..2d07311db72 100644
--- a/app/policies/environment_policy.rb
+++ b/app/policies/environment_policy.rb
@@ -2,11 +2,12 @@ class EnvironmentPolicy < BasePolicy
delegate { @subject.project }
condition(:stop_with_deployment_allowed) do
- @subject.stop_action? && can?(:create_deployment) && can?(:update_build, @subject.stop_action)
+ @subject.stop_action_available? &&
+ can?(:create_deployment) && can?(:update_build, @subject.stop_action)
end
condition(:stop_with_update_allowed) do
- !@subject.stop_action? && can?(:update_environment, @subject)
+ !@subject.stop_action_available? && can?(:update_environment, @subject)
end
rule { stop_with_deployment_allowed | stop_with_update_allowed }.enable :stop_environment
diff --git a/app/serializers/environment_entity.rb b/app/serializers/environment_entity.rb
index 0fc3f92b151..83558fc6659 100644
--- a/app/serializers/environment_entity.rb
+++ b/app/serializers/environment_entity.rb
@@ -7,7 +7,7 @@ class EnvironmentEntity < Grape::Entity
expose :external_url
expose :environment_type
expose :last_deployment, using: DeploymentEntity
- expose :stop_action?, as: :has_stop_action
+ expose :stop_action_available?, as: :has_stop_action
expose :metrics_path, if: -> (environment, _) { environment.has_metrics? } do |environment|
metrics_project_environment_path(environment.project, environment)
diff --git a/app/services/access_token_validation_service.rb b/app/services/access_token_validation_service.rb
index 46e19230328..2a337918d21 100644
--- a/app/services/access_token_validation_service.rb
+++ b/app/services/access_token_validation_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class AccessTokenValidationService
# Results:
VALID = :valid
diff --git a/app/services/after_branch_delete_service.rb b/app/services/after_branch_delete_service.rb
index 227e9ea9c6d..e7eb74d3e7d 100644
--- a/app/services/after_branch_delete_service.rb
+++ b/app/services/after_branch_delete_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
##
# Branch can be deleted either by DeleteBranchService
# or by GitPushService.
diff --git a/app/services/akismet_service.rb b/app/services/akismet_service.rb
index 0521393dd27..82ae66ab0f5 100644
--- a/app/services/akismet_service.rb
+++ b/app/services/akismet_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class AkismetService
attr_accessor :owner, :text, :options
diff --git a/app/services/application_settings/base_service.rb b/app/services/application_settings/base_service.rb
index 2bcc7d7c08b..ebe067536ca 100644
--- a/app/services/application_settings/base_service.rb
+++ b/app/services/application_settings/base_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ApplicationSettings
class BaseService < ::BaseService
def initialize(application_setting, user, params = {})
diff --git a/app/services/application_settings/update_service.rb b/app/services/application_settings/update_service.rb
index 7bcb8f49d0d..19cf34e2ac4 100644
--- a/app/services/application_settings/update_service.rb
+++ b/app/services/application_settings/update_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ApplicationSettings
class UpdateService < ApplicationSettings::BaseService
attr_reader :params, :application_setting
diff --git a/app/services/applications/create_service.rb b/app/services/applications/create_service.rb
index 94a434b95dd..7db90c0b3c6 100644
--- a/app/services/applications/create_service.rb
+++ b/app/services/applications/create_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Applications
class CreateService
def initialize(current_user, params)
diff --git a/app/services/audit_event_service.rb b/app/services/audit_event_service.rb
index 5ad9a50687c..4c5e22bdd7e 100644
--- a/app/services/audit_event_service.rb
+++ b/app/services/audit_event_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class AuditEventService
def initialize(author, entity, details = {})
@author, @entity, @details = author, entity, details
diff --git a/app/services/auth/container_registry_authentication_service.rb b/app/services/auth/container_registry_authentication_service.rb
index f28cddb2af3..81857d0cb4c 100644
--- a/app/services/auth/container_registry_authentication_service.rb
+++ b/app/services/auth/container_registry_authentication_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Auth
class ContainerRegistryAuthenticationService < BaseService
AUDIENCE = 'container_registry'.freeze
diff --git a/app/services/badges/base_service.rb b/app/services/badges/base_service.rb
index 4f87426bd38..45fc9ac4373 100644
--- a/app/services/badges/base_service.rb
+++ b/app/services/badges/base_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Badges
class BaseService
protected
diff --git a/app/services/badges/build_service.rb b/app/services/badges/build_service.rb
index 6267e571838..e5ede1586b6 100644
--- a/app/services/badges/build_service.rb
+++ b/app/services/badges/build_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Badges
class BuildService < Badges::BaseService
# returns the created badge
diff --git a/app/services/badges/create_service.rb b/app/services/badges/create_service.rb
index aafb87f7dcd..4a55a00daeb 100644
--- a/app/services/badges/create_service.rb
+++ b/app/services/badges/create_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Badges
class CreateService < Badges::BaseService
# returns the created badge
diff --git a/app/services/badges/update_service.rb b/app/services/badges/update_service.rb
index 495a4a2c99d..a653b7903dd 100644
--- a/app/services/badges/update_service.rb
+++ b/app/services/badges/update_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Badges
class UpdateService < Badges::BaseService
# returns the updated badge
diff --git a/app/services/base_count_service.rb b/app/services/base_count_service.rb
index 975e288301c..ad1647842b8 100644
--- a/app/services/base_count_service.rb
+++ b/app/services/base_count_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Base class for services that count a single resource such as the number of
# issues for a project.
class BaseCountService
diff --git a/app/services/base_renderer.rb b/app/services/base_renderer.rb
index d6e30bd7008..30a6e8c62dd 100644
--- a/app/services/base_renderer.rb
+++ b/app/services/base_renderer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class BaseRenderer
attr_reader :current_user
diff --git a/app/services/base_service.rb b/app/services/base_service.rb
index 3519b7c5e7d..3e968c8f707 100644
--- a/app/services/base_service.rb
+++ b/app/services/base_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class BaseService
include Gitlab::Allowable
diff --git a/app/services/boards/base_service.rb b/app/services/boards/base_service.rb
index 72822ffffa1..205db47888e 100644
--- a/app/services/boards/base_service.rb
+++ b/app/services/boards/base_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Boards
class BaseService < ::BaseService
# Parent can either a group or a project
diff --git a/app/services/boards/create_service.rb b/app/services/boards/create_service.rb
index bd0bb387662..4caf5ffa3cb 100644
--- a/app/services/boards/create_service.rb
+++ b/app/services/boards/create_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Boards
class CreateService < Boards::BaseService
def execute
diff --git a/app/services/boards/issues/create_service.rb b/app/services/boards/issues/create_service.rb
index 3025029755c..bd045e18b8d 100644
--- a/app/services/boards/issues/create_service.rb
+++ b/app/services/boards/issues/create_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Boards
module Issues
class CreateService < Boards::BaseService
diff --git a/app/services/boards/issues/list_service.rb b/app/services/boards/issues/list_service.rb
index b1dbe73cdf7..50c11be0d15 100644
--- a/app/services/boards/issues/list_service.rb
+++ b/app/services/boards/issues/list_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Boards
module Issues
class ListService < Boards::BaseService
diff --git a/app/services/boards/issues/move_service.rb b/app/services/boards/issues/move_service.rb
index ee3112c7571..6fd8a23b2a1 100644
--- a/app/services/boards/issues/move_service.rb
+++ b/app/services/boards/issues/move_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Boards
module Issues
class MoveService < Boards::BaseService
diff --git a/app/services/boards/list_service.rb b/app/services/boards/list_service.rb
index 9269b8d2620..edd1cc7c2e1 100644
--- a/app/services/boards/list_service.rb
+++ b/app/services/boards/list_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Boards
class ListService < Boards::BaseService
def execute
diff --git a/app/services/boards/lists/create_service.rb b/app/services/boards/lists/create_service.rb
index 6fd9885d4f3..48d2d5abaec 100644
--- a/app/services/boards/lists/create_service.rb
+++ b/app/services/boards/lists/create_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Boards
module Lists
class CreateService < Boards::BaseService
diff --git a/app/services/boards/lists/destroy_service.rb b/app/services/boards/lists/destroy_service.rb
index d75c5fd3dc6..e12d4f46e19 100644
--- a/app/services/boards/lists/destroy_service.rb
+++ b/app/services/boards/lists/destroy_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Boards
module Lists
class DestroyService < Boards::BaseService
diff --git a/app/services/boards/lists/generate_service.rb b/app/services/boards/lists/generate_service.rb
index 05d4ab5dbcc..4fbf1026019 100644
--- a/app/services/boards/lists/generate_service.rb
+++ b/app/services/boards/lists/generate_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Boards
module Lists
class GenerateService < Boards::BaseService
diff --git a/app/services/boards/lists/list_service.rb b/app/services/boards/lists/list_service.rb
index e57c95294af..e10eb52e041 100644
--- a/app/services/boards/lists/list_service.rb
+++ b/app/services/boards/lists/list_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Boards
module Lists
class ListService < Boards::BaseService
diff --git a/app/services/boards/lists/move_service.rb b/app/services/boards/lists/move_service.rb
index 7d0730e8332..27a36051662 100644
--- a/app/services/boards/lists/move_service.rb
+++ b/app/services/boards/lists/move_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Boards
module Lists
class MoveService < Boards::BaseService
diff --git a/app/services/chat_names/authorize_user_service.rb b/app/services/chat_names/authorize_user_service.rb
index 7256466c9e8..78b53cb3637 100644
--- a/app/services/chat_names/authorize_user_service.rb
+++ b/app/services/chat_names/authorize_user_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ChatNames
class AuthorizeUserService
include Gitlab::Routing
diff --git a/app/services/chat_names/find_user_service.rb b/app/services/chat_names/find_user_service.rb
index d458b814183..854b191c45c 100644
--- a/app/services/chat_names/find_user_service.rb
+++ b/app/services/chat_names/find_user_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ChatNames
class FindUserService
def initialize(service, params)
diff --git a/app/services/ci/create_pipeline_schedule_service.rb b/app/services/ci/create_pipeline_schedule_service.rb
index cd40deb6187..0d5f50c26a1 100644
--- a/app/services/ci/create_pipeline_schedule_service.rb
+++ b/app/services/ci/create_pipeline_schedule_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Ci
class CreatePipelineScheduleService < BaseService
def execute
diff --git a/app/services/ci/create_pipeline_service.rb b/app/services/ci/create_pipeline_service.rb
index 17a53b6a8fd..85df8bcff8c 100644
--- a/app/services/ci/create_pipeline_service.rb
+++ b/app/services/ci/create_pipeline_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Ci
class CreatePipelineService < BaseService
attr_reader :pipeline
diff --git a/app/services/ci/ensure_stage_service.rb b/app/services/ci/ensure_stage_service.rb
index b8c7be2d350..3d0e39d1b9f 100644
--- a/app/services/ci/ensure_stage_service.rb
+++ b/app/services/ci/ensure_stage_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Ci
##
# We call this service everytime we persist a CI/CD job.
diff --git a/app/services/ci/extract_sections_from_build_trace_service.rb b/app/services/ci/extract_sections_from_build_trace_service.rb
index 75f9e0f897d..693f6d55be3 100644
--- a/app/services/ci/extract_sections_from_build_trace_service.rb
+++ b/app/services/ci/extract_sections_from_build_trace_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Ci
class ExtractSectionsFromBuildTraceService < BaseService
def execute(build)
diff --git a/app/services/ci/fetch_kubernetes_token_service.rb b/app/services/ci/fetch_kubernetes_token_service.rb
index bca883ec0a0..15eda56cac6 100644
--- a/app/services/ci/fetch_kubernetes_token_service.rb
+++ b/app/services/ci/fetch_kubernetes_token_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
##
# TODO:
# Almost components in this class were copied from app/models/project_services/kubernetes_service.rb
diff --git a/app/services/ci/pipeline_trigger_service.rb b/app/services/ci/pipeline_trigger_service.rb
index 85533a1cbdb..f54574b026b 100644
--- a/app/services/ci/pipeline_trigger_service.rb
+++ b/app/services/ci/pipeline_trigger_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Ci
class PipelineTriggerService < BaseService
include Gitlab::Utils::StrongMemoize
diff --git a/app/services/ci/play_build_service.rb b/app/services/ci/play_build_service.rb
index e24f48c2d16..eb0b070657d 100644
--- a/app/services/ci/play_build_service.rb
+++ b/app/services/ci/play_build_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Ci
class PlayBuildService < ::BaseService
def execute(build)
diff --git a/app/services/ci/process_pipeline_service.rb b/app/services/ci/process_pipeline_service.rb
index 55af193d717..cda9bbff3b4 100644
--- a/app/services/ci/process_pipeline_service.rb
+++ b/app/services/ci/process_pipeline_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Ci
class ProcessPipelineService < BaseService
attr_reader :pipeline
diff --git a/app/services/ci/register_job_service.rb b/app/services/ci/register_job_service.rb
index 6eb1c4f52de..f7ccec3a700 100644
--- a/app/services/ci/register_job_service.rb
+++ b/app/services/ci/register_job_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Ci
# This class responsible for assigning
# proper pending build to runner on runner API request
diff --git a/app/services/ci/retry_build_service.rb b/app/services/ci/retry_build_service.rb
index 6128b2a8fbb..6ceb59e4780 100644
--- a/app/services/ci/retry_build_service.rb
+++ b/app/services/ci/retry_build_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Ci
class RetryBuildService < ::BaseService
CLONE_ACCESSORS = %i[pipeline project ref tag options commands name
diff --git a/app/services/ci/retry_pipeline_service.rb b/app/services/ci/retry_pipeline_service.rb
index c5a43869990..42a13367a99 100644
--- a/app/services/ci/retry_pipeline_service.rb
+++ b/app/services/ci/retry_pipeline_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Ci
class RetryPipelineService < ::BaseService
include Gitlab::OptimisticLocking
diff --git a/app/services/ci/stop_environments_service.rb b/app/services/ci/stop_environments_service.rb
index 43c9a065fcf..973ae5ce5aa 100644
--- a/app/services/ci/stop_environments_service.rb
+++ b/app/services/ci/stop_environments_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Ci
class StopEnvironmentsService < BaseService
attr_reader :ref
@@ -8,7 +10,7 @@ module Ci
return unless @ref.present?
environments.each do |environment|
- next unless environment.stop_action?
+ next unless environment.stop_action_available?
next unless can?(current_user, :stop_environment, environment)
environment.stop_with_action!(current_user)
diff --git a/app/services/ci/update_build_queue_service.rb b/app/services/ci/update_build_queue_service.rb
index 41b1c144c3e..9c589d910eb 100644
--- a/app/services/ci/update_build_queue_service.rb
+++ b/app/services/ci/update_build_queue_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Ci
class UpdateBuildQueueService
def execute(build)
diff --git a/app/services/ci/update_runner_service.rb b/app/services/ci/update_runner_service.rb
index 450ee7da1c9..e4117a51fe6 100644
--- a/app/services/ci/update_runner_service.rb
+++ b/app/services/ci/update_runner_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Ci
class UpdateRunnerService
attr_reader :runner
diff --git a/app/services/clusters/applications/base_helm_service.rb b/app/services/clusters/applications/base_helm_service.rb
index cba1b920f7c..270a8eb24f4 100644
--- a/app/services/clusters/applications/base_helm_service.rb
+++ b/app/services/clusters/applications/base_helm_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Clusters
module Applications
class BaseHelmService
diff --git a/app/services/clusters/applications/check_ingress_ip_address_service.rb b/app/services/clusters/applications/check_ingress_ip_address_service.rb
index e572b1e5d99..f32e73e8b1c 100644
--- a/app/services/clusters/applications/check_ingress_ip_address_service.rb
+++ b/app/services/clusters/applications/check_ingress_ip_address_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Clusters
module Applications
class CheckIngressIpAddressService < BaseHelmService
diff --git a/app/services/clusters/applications/check_installation_progress_service.rb b/app/services/clusters/applications/check_installation_progress_service.rb
index 90393e951a4..4640c5a2d4b 100644
--- a/app/services/clusters/applications/check_installation_progress_service.rb
+++ b/app/services/clusters/applications/check_installation_progress_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Clusters
module Applications
class CheckInstallationProgressService < BaseHelmService
diff --git a/app/services/clusters/applications/install_service.rb b/app/services/clusters/applications/install_service.rb
index 7ec3a9baa6e..7e3c0e77a83 100644
--- a/app/services/clusters/applications/install_service.rb
+++ b/app/services/clusters/applications/install_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Clusters
module Applications
class InstallService < BaseHelmService
diff --git a/app/services/clusters/applications/schedule_installation_service.rb b/app/services/clusters/applications/schedule_installation_service.rb
index 9c5461e85e1..4ead4f619c8 100644
--- a/app/services/clusters/applications/schedule_installation_service.rb
+++ b/app/services/clusters/applications/schedule_installation_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Clusters
module Applications
class ScheduleInstallationService < ::BaseService
diff --git a/app/services/clusters/create_service.rb b/app/services/clusters/create_service.rb
index 418888e3293..e3e0cfa462c 100644
--- a/app/services/clusters/create_service.rb
+++ b/app/services/clusters/create_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Clusters
class CreateService < BaseService
attr_reader :access_token
diff --git a/app/services/clusters/gcp/fetch_operation_service.rb b/app/services/clusters/gcp/fetch_operation_service.rb
index a4cd3ca5c11..02c96a1e286 100644
--- a/app/services/clusters/gcp/fetch_operation_service.rb
+++ b/app/services/clusters/gcp/fetch_operation_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Clusters
module Gcp
class FetchOperationService
diff --git a/app/services/clusters/gcp/finalize_creation_service.rb b/app/services/clusters/gcp/finalize_creation_service.rb
index 84944e95542..264419501dc 100644
--- a/app/services/clusters/gcp/finalize_creation_service.rb
+++ b/app/services/clusters/gcp/finalize_creation_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Clusters
module Gcp
class FinalizeCreationService
diff --git a/app/services/clusters/gcp/provision_service.rb b/app/services/clusters/gcp/provision_service.rb
index 8beea5a8cfb..ab1bf9c64f6 100644
--- a/app/services/clusters/gcp/provision_service.rb
+++ b/app/services/clusters/gcp/provision_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Clusters
module Gcp
class ProvisionService
diff --git a/app/services/clusters/gcp/verify_provision_status_service.rb b/app/services/clusters/gcp/verify_provision_status_service.rb
index 7cc4324677e..b24246f5c4b 100644
--- a/app/services/clusters/gcp/verify_provision_status_service.rb
+++ b/app/services/clusters/gcp/verify_provision_status_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Clusters
module Gcp
class VerifyProvisionStatusService
diff --git a/app/services/clusters/update_service.rb b/app/services/clusters/update_service.rb
index 989218e32a2..98fdeec4fb1 100644
--- a/app/services/clusters/update_service.rb
+++ b/app/services/clusters/update_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Clusters
class UpdateService < BaseService
def execute(cluster)
diff --git a/app/services/cohorts_service.rb b/app/services/cohorts_service.rb
index 6781533af28..7a14e97f749 100644
--- a/app/services/cohorts_service.rb
+++ b/app/services/cohorts_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class CohortsService
MONTHS_INCLUDED = 12
diff --git a/app/services/commits/change_service.rb b/app/services/commits/change_service.rb
index 1ce6ab36cbf..2fbd442fc2e 100644
--- a/app/services/commits/change_service.rb
+++ b/app/services/commits/change_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Commits
class ChangeService < Commits::CreateService
def initialize(*args)
diff --git a/app/services/commits/cherry_pick_service.rb b/app/services/commits/cherry_pick_service.rb
index 320e229560d..4c5b15b2f95 100644
--- a/app/services/commits/cherry_pick_service.rb
+++ b/app/services/commits/cherry_pick_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Commits
class CherryPickService < ChangeService
def create_commit!
diff --git a/app/services/commits/create_service.rb b/app/services/commits/create_service.rb
index 4d0578becbe..3ce9acc833c 100644
--- a/app/services/commits/create_service.rb
+++ b/app/services/commits/create_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Commits
class CreateService < ::BaseService
ValidationError = Class.new(StandardError)
diff --git a/app/services/commits/revert_service.rb b/app/services/commits/revert_service.rb
index dc27399e047..dddb8b24eac 100644
--- a/app/services/commits/revert_service.rb
+++ b/app/services/commits/revert_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Commits
class RevertService < ChangeService
def create_commit!
diff --git a/app/services/compare_service.rb b/app/services/compare_service.rb
index 2a69a205629..3adf8a0c1a1 100644
--- a/app/services/compare_service.rb
+++ b/app/services/compare_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'securerandom'
# Compare 2 refs for one repo or between repositories
diff --git a/app/services/concerns/exclusive_lease_guard.rb b/app/services/concerns/exclusive_lease_guard.rb
index f45436370c1..f102e00d150 100644
--- a/app/services/concerns/exclusive_lease_guard.rb
+++ b/app/services/concerns/exclusive_lease_guard.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
#
# Concern that helps with getting an exclusive lease for running a block
# of code.
diff --git a/app/services/concerns/issues/resolve_discussions.rb b/app/services/concerns/issues/resolve_discussions.rb
index 455f761ca9b..1563ed965df 100644
--- a/app/services/concerns/issues/resolve_discussions.rb
+++ b/app/services/concerns/issues/resolve_discussions.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Issues
module ResolveDiscussions
include Gitlab::Utils::StrongMemoize
diff --git a/app/services/concerns/update_visibility_level.rb b/app/services/concerns/update_visibility_level.rb
index 536fcc6acce..b7a161f5089 100644
--- a/app/services/concerns/update_visibility_level.rb
+++ b/app/services/concerns/update_visibility_level.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module UpdateVisibilityLevel
def valid_visibility_level_change?(target, new_visibility)
# check that user is allowed to set specified visibility_level
diff --git a/app/services/concerns/users/new_user_notifier.rb b/app/services/concerns/users/new_user_notifier.rb
index 231693ce7a9..11547e4a5b6 100644
--- a/app/services/concerns/users/new_user_notifier.rb
+++ b/app/services/concerns/users/new_user_notifier.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Users
module NewUserNotifier
def notify_new_user(user, reset_token)
diff --git a/app/services/concerns/users/participable_service.rb b/app/services/concerns/users/participable_service.rb
index bf60b96938d..5b408bd96c7 100644
--- a/app/services/concerns/users/participable_service.rb
+++ b/app/services/concerns/users/participable_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Users
module ParticipableService
extend ActiveSupport::Concern
diff --git a/app/services/create_branch_service.rb b/app/services/create_branch_service.rb
index 9b1a4d960e2..65208b07e27 100644
--- a/app/services/create_branch_service.rb
+++ b/app/services/create_branch_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class CreateBranchService < BaseService
def execute(branch_name, ref)
create_master_branch if project.empty_repo?
diff --git a/app/services/create_deployment_service.rb b/app/services/create_deployment_service.rb
index 7e5a77fb056..bb3f605da28 100644
--- a/app/services/create_deployment_service.rb
+++ b/app/services/create_deployment_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class CreateDeploymentService
attr_reader :job
diff --git a/app/services/create_release_service.rb b/app/services/create_release_service.rb
index 54ff1f74126..09c68390007 100644
--- a/app/services/create_release_service.rb
+++ b/app/services/create_release_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class CreateReleaseService < BaseService
def execute(tag_name, release_description)
repository = project.repository
diff --git a/app/services/create_snippet_service.rb b/app/services/create_snippet_service.rb
index 40286dbf3bf..6f1fce4989e 100644
--- a/app/services/create_snippet_service.rb
+++ b/app/services/create_snippet_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class CreateSnippetService < BaseService
include SpamCheckService
diff --git a/app/services/delete_branch_service.rb b/app/services/delete_branch_service.rb
index e1499dcee64..44252f7b0a6 100644
--- a/app/services/delete_branch_service.rb
+++ b/app/services/delete_branch_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class DeleteBranchService < BaseService
def execute(branch_name)
repository = project.repository
diff --git a/app/services/delete_merged_branches_service.rb b/app/services/delete_merged_branches_service.rb
index c98d1e3c540..ff3e4783fe3 100644
--- a/app/services/delete_merged_branches_service.rb
+++ b/app/services/delete_merged_branches_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class DeleteMergedBranchesService < BaseService
def async_execute
DeleteMergedBranchesWorker.perform_async(project.id, current_user.id)
diff --git a/app/services/deploy_keys/create_service.rb b/app/services/deploy_keys/create_service.rb
index 16de3d08df2..a25e73666f8 100644
--- a/app/services/deploy_keys/create_service.rb
+++ b/app/services/deploy_keys/create_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module DeployKeys
class CreateService < Keys::BaseService
def execute
diff --git a/app/services/deploy_tokens/create_service.rb b/app/services/deploy_tokens/create_service.rb
index 52f545947af..dc0122002e9 100644
--- a/app/services/deploy_tokens/create_service.rb
+++ b/app/services/deploy_tokens/create_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module DeployTokens
class CreateService < BaseService
def execute
diff --git a/app/services/discussions/base_service.rb b/app/services/discussions/base_service.rb
index e4dfe6e71bb..86b8310f0a6 100644
--- a/app/services/discussions/base_service.rb
+++ b/app/services/discussions/base_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Discussions
class BaseService < ::BaseService
end
diff --git a/app/services/discussions/resolve_service.rb b/app/services/discussions/resolve_service.rb
index 0437195f588..816cd45b07a 100644
--- a/app/services/discussions/resolve_service.rb
+++ b/app/services/discussions/resolve_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Discussions
class ResolveService < Discussions::BaseService
def execute(one_or_more_discussions)
diff --git a/app/services/discussions/update_diff_position_service.rb b/app/services/discussions/update_diff_position_service.rb
index 746f209e20f..c61437fb2e3 100644
--- a/app/services/discussions/update_diff_position_service.rb
+++ b/app/services/discussions/update_diff_position_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Discussions
class UpdateDiffPositionService < BaseService
def execute(discussion)
diff --git a/app/services/emails/base_service.rb b/app/services/emails/base_service.rb
index 5bbceeb3b3f..ba7b689a9af 100644
--- a/app/services/emails/base_service.rb
+++ b/app/services/emails/base_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Emails
class BaseService
def initialize(current_user, params = {})
diff --git a/app/services/emails/confirm_service.rb b/app/services/emails/confirm_service.rb
index b5301bf2b82..38204e011dd 100644
--- a/app/services/emails/confirm_service.rb
+++ b/app/services/emails/confirm_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Emails
class ConfirmService < ::Emails::BaseService
def execute(email)
diff --git a/app/services/emails/create_service.rb b/app/services/emails/create_service.rb
index 94a841af7c3..acf575e24e5 100644
--- a/app/services/emails/create_service.rb
+++ b/app/services/emails/create_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Emails
class CreateService < ::Emails::BaseService
def execute(extra_params = {})
diff --git a/app/services/emails/destroy_service.rb b/app/services/emails/destroy_service.rb
index 1ed131fe326..9ca1a03e172 100644
--- a/app/services/emails/destroy_service.rb
+++ b/app/services/emails/destroy_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Emails
class DestroyService < ::Emails::BaseService
def execute(email)
diff --git a/app/services/event_create_service.rb b/app/services/event_create_service.rb
index 44dc90b3462..e7464fd9d5f 100644
--- a/app/services/event_create_service.rb
+++ b/app/services/event_create_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# EventCreateService class
#
# Used for creating events feed on dashboard after certain user action
diff --git a/app/services/events/render_service.rb b/app/services/events/render_service.rb
index bb72d7685dd..50429683902 100644
--- a/app/services/events/render_service.rb
+++ b/app/services/events/render_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Events
class RenderService < BaseRenderer
def execute(events, atom_request: false)
diff --git a/app/services/files/base_service.rb b/app/services/files/base_service.rb
index 8d4b9f14780..025f093a428 100644
--- a/app/services/files/base_service.rb
+++ b/app/services/files/base_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Files
class BaseService < Commits::CreateService
FileChangedError = Class.new(StandardError)
diff --git a/app/services/files/create_dir_service.rb b/app/services/files/create_dir_service.rb
index 8ecac6115bd..362b80071ba 100644
--- a/app/services/files/create_dir_service.rb
+++ b/app/services/files/create_dir_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Files
class CreateDirService < Files::BaseService
def create_commit!
diff --git a/app/services/files/create_service.rb b/app/services/files/create_service.rb
index a954564946b..fd5442a6c28 100644
--- a/app/services/files/create_service.rb
+++ b/app/services/files/create_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Files
class CreateService < Files::BaseService
def create_commit!
diff --git a/app/services/files/delete_service.rb b/app/services/files/delete_service.rb
index 32a57484d4e..0ec1f79d396 100644
--- a/app/services/files/delete_service.rb
+++ b/app/services/files/delete_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Files
class DeleteService < Files::BaseService
def create_commit!
diff --git a/app/services/files/multi_service.rb b/app/services/files/multi_service.rb
index 13a1dee4173..08088f8c592 100644
--- a/app/services/files/multi_service.rb
+++ b/app/services/files/multi_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Files
class MultiService < Files::BaseService
UPDATE_FILE_ACTIONS = %w(update move delete).freeze
diff --git a/app/services/files/update_service.rb b/app/services/files/update_service.rb
index 1902d1cea72..2b3e96e6c53 100644
--- a/app/services/files/update_service.rb
+++ b/app/services/files/update_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Files
class UpdateService < Files::BaseService
def create_commit!
diff --git a/app/services/git_push_service.rb b/app/services/git_push_service.rb
index f3bfc53dcd3..29c8ce5fea3 100644
--- a/app/services/git_push_service.rb
+++ b/app/services/git_push_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class GitPushService < BaseService
attr_accessor :push_data, :push_commits
include Gitlab::Access
diff --git a/app/services/git_tag_push_service.rb b/app/services/git_tag_push_service.rb
index 9917a39b795..3ff2d1d107d 100644
--- a/app/services/git_tag_push_service.rb
+++ b/app/services/git_tag_push_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class GitTagPushService < BaseService
attr_accessor :push_data
diff --git a/app/services/gpg_keys/create_service.rb b/app/services/gpg_keys/create_service.rb
index e822a89c4d3..e41444b2a82 100644
--- a/app/services/gpg_keys/create_service.rb
+++ b/app/services/gpg_keys/create_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module GpgKeys
class CreateService < Keys::BaseService
def execute
diff --git a/app/services/gravatar_service.rb b/app/services/gravatar_service.rb
index c6e52c3bb91..2a7a5dae291 100644
--- a/app/services/gravatar_service.rb
+++ b/app/services/gravatar_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class GravatarService
def execute(email, size = nil, scale = 2, username: nil)
return unless Gitlab::CurrentSettings.gravatar_enabled?
diff --git a/app/services/groups/base_service.rb b/app/services/groups/base_service.rb
index a8fa098246a..8c8acce5ca5 100644
--- a/app/services/groups/base_service.rb
+++ b/app/services/groups/base_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Groups
class BaseService < ::BaseService
attr_accessor :group, :current_user, :params
diff --git a/app/services/groups/create_service.rb b/app/services/groups/create_service.rb
index 70e50aa0f12..24d8400c625 100644
--- a/app/services/groups/create_service.rb
+++ b/app/services/groups/create_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Groups
class CreateService < Groups::BaseService
def initialize(user, params = {})
diff --git a/app/services/groups/destroy_service.rb b/app/services/groups/destroy_service.rb
index 58e88688dfa..c4554ce45fb 100644
--- a/app/services/groups/destroy_service.rb
+++ b/app/services/groups/destroy_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Groups
class DestroyService < Groups::BaseService
def async_execute
diff --git a/app/services/groups/nested_create_service.rb b/app/services/groups/nested_create_service.rb
index c2dfbac5414..50d34d8cb91 100644
--- a/app/services/groups/nested_create_service.rb
+++ b/app/services/groups/nested_create_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Groups
class NestedCreateService < Groups::BaseService
attr_reader :group_path, :visibility_level
diff --git a/app/services/groups/transfer_service.rb b/app/services/groups/transfer_service.rb
index e591c820cff..ea7576077f3 100644
--- a/app/services/groups/transfer_service.rb
+++ b/app/services/groups/transfer_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Groups
class TransferService < Groups::BaseService
ERROR_MESSAGES = {
diff --git a/app/services/groups/update_service.rb b/app/services/groups/update_service.rb
index 08e3efb96e3..436a6b18cb1 100644
--- a/app/services/groups/update_service.rb
+++ b/app/services/groups/update_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Groups
class UpdateService < Groups::BaseService
include UpdateVisibilityLevel
diff --git a/app/services/ham_service.rb b/app/services/ham_service.rb
index b0e1799b489..794eb34d9ca 100644
--- a/app/services/ham_service.rb
+++ b/app/services/ham_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class HamService
attr_accessor :spam_log
diff --git a/app/services/import_export_clean_up_service.rb b/app/services/import_export_clean_up_service.rb
index 3702c3742ef..e75a951944e 100644
--- a/app/services/import_export_clean_up_service.rb
+++ b/app/services/import_export_clean_up_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ImportExportCleanUpService
LAST_MODIFIED_TIME_IN_MINUTES = 1440
diff --git a/app/services/issuable/bulk_update_service.rb b/app/services/issuable/bulk_update_service.rb
index 5d42a89fced..051d5ba881d 100644
--- a/app/services/issuable/bulk_update_service.rb
+++ b/app/services/issuable/bulk_update_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Issuable
class BulkUpdateService < IssuableBaseService
def execute(type)
diff --git a/app/services/issuable/common_system_notes_service.rb b/app/services/issuable/common_system_notes_service.rb
index 3da21bd8b8f..028b350ca07 100644
--- a/app/services/issuable/common_system_notes_service.rb
+++ b/app/services/issuable/common_system_notes_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Issuable
class CommonSystemNotesService < ::BaseService
attr_reader :issuable
diff --git a/app/services/issuable/destroy_service.rb b/app/services/issuable/destroy_service.rb
index 0b1a33518c6..4c64655a622 100644
--- a/app/services/issuable/destroy_service.rb
+++ b/app/services/issuable/destroy_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Issuable
class DestroyService < IssuableBaseService
def execute(issuable)
diff --git a/app/services/issuable_base_service.rb b/app/services/issuable_base_service.rb
index 5e06e0c61cf..7d60c65bb79 100644
--- a/app/services/issuable_base_service.rb
+++ b/app/services/issuable_base_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class IssuableBaseService < BaseService
private
diff --git a/app/services/issues/base_service.rb b/app/services/issues/base_service.rb
index cbfef175af0..25389a946bb 100644
--- a/app/services/issues/base_service.rb
+++ b/app/services/issues/base_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Issues
class BaseService < ::IssuableBaseService
def hook_data(issue, action, old_associations: {})
diff --git a/app/services/issues/build_service.rb b/app/services/issues/build_service.rb
index 3a4f7b159f1..52b45f1b2ce 100644
--- a/app/services/issues/build_service.rb
+++ b/app/services/issues/build_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Issues
class BuildService < Issues::BaseService
include ResolveDiscussions
@@ -44,14 +46,14 @@ module Issues
other_note_count = discussion.notes.size - 1
- discussion_info = "- [ ] #{first_note_to_resolve.author.to_reference} #{action} a [discussion](#{note_url}): "
- discussion_info << " (+#{other_note_count} #{'comment'.pluralize(other_note_count)})" if other_note_count > 0
+ discussion_info = ["- [ ] #{first_note_to_resolve.author.to_reference} #{action} a [discussion](#{note_url}): "]
+ discussion_info << "(+#{other_note_count} #{'comment'.pluralize(other_note_count)})" if other_note_count > 0
note_without_block_quotes = Banzai::Filter::BlockquoteFenceFilter.new(first_note_to_resolve.note).call
spaces = ' ' * 4
quote = note_without_block_quotes.lines.map { |line| "#{spaces}> #{line}" }.join
- [discussion_info, quote].join("\n\n")
+ [discussion_info.join(' '), quote].join("\n\n")
end
def issue_params
diff --git a/app/services/issues/close_service.rb b/app/services/issues/close_service.rb
index 4a99367c575..e5cc12e6082 100644
--- a/app/services/issues/close_service.rb
+++ b/app/services/issues/close_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Issues
class CloseService < Issues::BaseService
# Closes the supplied issue if the current user is able to do so.
diff --git a/app/services/issues/create_service.rb b/app/services/issues/create_service.rb
index 0307634c0b6..5793a15e1bc 100644
--- a/app/services/issues/create_service.rb
+++ b/app/services/issues/create_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Issues
class CreateService < Issues::BaseService
include SpamCheckService
diff --git a/app/services/issues/duplicate_service.rb b/app/services/issues/duplicate_service.rb
index 5c0854e664d..9b22f5e7914 100644
--- a/app/services/issues/duplicate_service.rb
+++ b/app/services/issues/duplicate_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Issues
class DuplicateService < Issues::BaseService
def execute(duplicate_issue, canonical_issue)
diff --git a/app/services/issues/fetch_referenced_merge_requests_service.rb b/app/services/issues/fetch_referenced_merge_requests_service.rb
index 39c8ded9df4..5e84f3c81c9 100644
--- a/app/services/issues/fetch_referenced_merge_requests_service.rb
+++ b/app/services/issues/fetch_referenced_merge_requests_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Issues
class FetchReferencedMergeRequestsService < Issues::BaseService
def execute(issue)
diff --git a/app/services/issues/move_service.rb b/app/services/issues/move_service.rb
index 6e5c29a5c40..841bce9949e 100644
--- a/app/services/issues/move_service.rb
+++ b/app/services/issues/move_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Issues
class MoveService < Issues::BaseService
MoveError = Class.new(StandardError)
diff --git a/app/services/issues/reopen_service.rb b/app/services/issues/reopen_service.rb
index 02224f3357a..3bd53f9ccdc 100644
--- a/app/services/issues/reopen_service.rb
+++ b/app/services/issues/reopen_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Issues
class ReopenService < Issues::BaseService
def execute(issue)
diff --git a/app/services/issues/update_service.rb b/app/services/issues/update_service.rb
index 1000e1842b6..c02dddf67b2 100644
--- a/app/services/issues/update_service.rb
+++ b/app/services/issues/update_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Issues
class UpdateService < Issues::BaseService
include SpamCheckService
diff --git a/app/services/keys/base_service.rb b/app/services/keys/base_service.rb
index df8e82f5f60..113e22b01ce 100644
--- a/app/services/keys/base_service.rb
+++ b/app/services/keys/base_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Keys
class BaseService
attr_accessor :user, :params
diff --git a/app/services/keys/create_service.rb b/app/services/keys/create_service.rb
index e2e5a6c46c5..d9fa69a88d7 100644
--- a/app/services/keys/create_service.rb
+++ b/app/services/keys/create_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Keys
class CreateService < ::Keys::BaseService
def execute
diff --git a/app/services/keys/destroy_service.rb b/app/services/keys/destroy_service.rb
index 785cfa3a1d8..e2ae4047941 100644
--- a/app/services/keys/destroy_service.rb
+++ b/app/services/keys/destroy_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Keys
class DestroyService < ::Keys::BaseService
def execute(key)
diff --git a/app/services/keys/last_used_service.rb b/app/services/keys/last_used_service.rb
index dbd79f7da55..daef544bac0 100644
--- a/app/services/keys/last_used_service.rb
+++ b/app/services/keys/last_used_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Keys
class LastUsedService
TIMEOUT = 1.day.to_i
diff --git a/app/services/labels/base_service.rb b/app/services/labels/base_service.rb
index 91d72a57b4e..ead7f2ea607 100644
--- a/app/services/labels/base_service.rb
+++ b/app/services/labels/base_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Labels
class BaseService < ::BaseService
COLOR_NAME_TO_HEX = {
diff --git a/app/services/labels/create_service.rb b/app/services/labels/create_service.rb
index 6c399c92377..fe34be41ac1 100644
--- a/app/services/labels/create_service.rb
+++ b/app/services/labels/create_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Labels
class CreateService < Labels::BaseService
def initialize(params = {})
diff --git a/app/services/labels/find_or_create_service.rb b/app/services/labels/find_or_create_service.rb
index a72da3c637f..e4486764a4d 100644
--- a/app/services/labels/find_or_create_service.rb
+++ b/app/services/labels/find_or_create_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Labels
class FindOrCreateService
def initialize(current_user, parent, params = {})
diff --git a/app/services/labels/promote_service.rb b/app/services/labels/promote_service.rb
index 74a85e5c9f0..c0463052821 100644
--- a/app/services/labels/promote_service.rb
+++ b/app/services/labels/promote_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Labels
class PromoteService < BaseService
BATCH_SIZE = 1000
diff --git a/app/services/labels/transfer_service.rb b/app/services/labels/transfer_service.rb
index 9b7486cf53b..1bd8d9fc325 100644
--- a/app/services/labels/transfer_service.rb
+++ b/app/services/labels/transfer_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Labels::TransferService class
#
# User for recreate the missing group labels at project level
diff --git a/app/services/labels/update_service.rb b/app/services/labels/update_service.rb
index 28dcabf9541..c3a720a1c66 100644
--- a/app/services/labels/update_service.rb
+++ b/app/services/labels/update_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Labels
class UpdateService < Labels::BaseService
def initialize(params = {})
diff --git a/app/services/merge_request_metrics_service.rb b/app/services/merge_request_metrics_service.rb
index 9248de14a53..4e88b77c855 100644
--- a/app/services/merge_request_metrics_service.rb
+++ b/app/services/merge_request_metrics_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class MergeRequestMetricsService
delegate :update!, to: :@merge_request_metrics
diff --git a/app/services/metrics_service.rb b/app/services/metrics_service.rb
index c237d2ae8c9..222a5c8c79c 100644
--- a/app/services/metrics_service.rb
+++ b/app/services/metrics_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'prometheus/client/formats/text'
class MetricsService
diff --git a/app/services/note_summary.rb b/app/services/note_summary.rb
index a6f6320d573..81f6f92f75c 100644
--- a/app/services/note_summary.rb
+++ b/app/services/note_summary.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class NoteSummary
attr_reader :note
attr_reader :metadata
diff --git a/app/services/notification_recipient_service.rb b/app/services/notification_recipient_service.rb
index d9834fd0ccc..4389fd89538 100644
--- a/app/services/notification_recipient_service.rb
+++ b/app/services/notification_recipient_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
#
# Used by NotificationService to determine who should receive notification
#
diff --git a/app/services/notification_service.rb b/app/services/notification_service.rb
index d7be9a925b5..4511c500fca 100644
--- a/app/services/notification_service.rb
+++ b/app/services/notification_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# rubocop:disable GitlabSecurity/PublicSend
# NotificationService class
diff --git a/app/services/preview_markdown_service.rb b/app/services/preview_markdown_service.rb
index 6da4d9523cf..a15ee4911ef 100644
--- a/app/services/preview_markdown_service.rb
+++ b/app/services/preview_markdown_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class PreviewMarkdownService < BaseService
def execute
text, commands = explain_quick_actions(params[:text])
diff --git a/app/services/push_event_payload_service.rb b/app/services/push_event_payload_service.rb
index b0a389c85f9..bb1259787af 100644
--- a/app/services/push_event_payload_service.rb
+++ b/app/services/push_event_payload_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Service class for creating push event payloads as stored in the
# "push_event_payloads" table.
#
diff --git a/app/services/repair_ldap_blocked_user_service.rb b/app/services/repair_ldap_blocked_user_service.rb
index 863cef7ff61..6ed42054ac3 100644
--- a/app/services/repair_ldap_blocked_user_service.rb
+++ b/app/services/repair_ldap_blocked_user_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class RepairLdapBlockedUserService
attr_accessor :user
diff --git a/app/services/repository_archive_clean_up_service.rb b/app/services/repository_archive_clean_up_service.rb
index ba7be4b3f89..99a9c834352 100644
--- a/app/services/repository_archive_clean_up_service.rb
+++ b/app/services/repository_archive_clean_up_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class RepositoryArchiveCleanUpService
LAST_MODIFIED_TIME_IN_MINUTES = 120
diff --git a/app/services/reset_project_cache_service.rb b/app/services/reset_project_cache_service.rb
index a162a6eedb9..676d367a1c1 100644
--- a/app/services/reset_project_cache_service.rb
+++ b/app/services/reset_project_cache_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ResetProjectCacheService < BaseService
def execute
@project.increment!(:jobs_cache_index)
diff --git a/app/services/search_service.rb b/app/services/search_service.rb
index 1d4d03a8b7d..1b707d79b43 100644
--- a/app/services/search_service.rb
+++ b/app/services/search_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class SearchService
include Gitlab::Allowable
diff --git a/app/services/spam_check_service.rb b/app/services/spam_check_service.rb
index d4ade869777..895261925ba 100644
--- a/app/services/spam_check_service.rb
+++ b/app/services/spam_check_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# SpamCheckService
#
# Provide helper methods for checking if a given spammable object has
diff --git a/app/services/spam_service.rb b/app/services/spam_service.rb
index 73ea3018fbd..f2f133dae28 100644
--- a/app/services/spam_service.rb
+++ b/app/services/spam_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class SpamService
attr_accessor :spammable, :request, :options
attr_reader :spam_log
diff --git a/app/services/submit_usage_ping_service.rb b/app/services/submit_usage_ping_service.rb
index ac029fad7ea..93c2e222963 100644
--- a/app/services/submit_usage_ping_service.rb
+++ b/app/services/submit_usage_ping_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class SubmitUsagePingService
URL = 'https://version.gitlab.com/usage_data'.freeze
diff --git a/app/services/system_hooks_service.rb b/app/services/system_hooks_service.rb
index ba7946fd23c..bd3907cdf8e 100644
--- a/app/services/system_hooks_service.rb
+++ b/app/services/system_hooks_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class SystemHooksService
def execute_hooks_for(model, event)
data = build_event_data(model, event)
diff --git a/app/services/system_note_service.rb b/app/services/system_note_service.rb
index 00bf5434b7f..77494295f14 100644
--- a/app/services/system_note_service.rb
+++ b/app/services/system_note_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# SystemNoteService
#
# Used for creating system notes (e.g., when a user references a merge request
@@ -21,9 +23,11 @@ module SystemNoteService
total_count = new_commits.length + existing_commits.length
commits_text = "#{total_count} commit".pluralize(total_count)
- body = "added #{commits_text}\n\n"
- body << commits_list(noteable, new_commits, existing_commits, oldrev)
- body << "\n\n[Compare with previous version](#{diff_comparison_url(noteable, project, oldrev)})"
+ text_parts = ["added #{commits_text}"]
+ text_parts << commits_list(noteable, new_commits, existing_commits, oldrev)
+ text_parts << "[Compare with previous version](#{diff_comparison_url(noteable, project, oldrev)})"
+
+ body = text_parts.join("\n\n")
create_note(NoteSummary.new(noteable, project, author, body, action: 'commit', commit_count: total_count))
end
@@ -103,18 +107,19 @@ module SystemNoteService
added_labels = added_labels.map(&references).join(' ')
removed_labels = removed_labels.map(&references).join(' ')
- body = ''
+ text_parts = []
if added_labels.present?
- body << "added #{added_labels}"
- body << ' and ' if removed_labels.present?
+ text_parts << "added #{added_labels}"
+ text_parts << 'and' if removed_labels.present?
end
if removed_labels.present?
- body << "removed #{removed_labels}"
+ text_parts << "removed #{removed_labels}"
end
- body << ' ' << 'label'.pluralize(labels_count)
+ text_parts << 'label'.pluralize(labels_count)
+ body = text_parts.join(' ')
create_note(NoteSummary.new(noteable, project, author, body, action: 'label'))
end
@@ -188,8 +193,10 @@ module SystemNoteService
spent_at = noteable.spent_at
parsed_time = Gitlab::TimeTrackingFormatter.output(time_spent.abs)
action = time_spent > 0 ? 'added' : 'subtracted'
- body = "#{action} #{parsed_time} of time spent"
- body << " at #{spent_at}" if spent_at
+
+ text_parts = ["#{action} #{parsed_time} of time spent"]
+ text_parts << "at #{spent_at}" if spent_at
+ body = text_parts.join(' ')
end
create_note(NoteSummary.new(noteable, project, author, body, action: 'time_tracking'))
@@ -268,17 +275,19 @@ module SystemNoteService
diff_refs = change_position.diff_refs
version_index = merge_request.merge_request_diffs.viewable.count
- body = "changed this line in"
+ text_parts = ["changed this line in"]
if version_params = merge_request.version_params_for(diff_refs)
line_code = change_position.line_code(project.repository)
url = url_helpers.diffs_project_merge_request_url(project, merge_request, version_params.merge(anchor: line_code))
- body << " [version #{version_index} of the diff](#{url})"
+ text_parts << "[version #{version_index} of the diff](#{url})"
else
- body << " version #{version_index} of the diff"
+ text_parts << "version #{version_index} of the diff"
end
+ body = text_parts.join(' ')
note_attributes = discussion.reply_attributes.merge(project: project, author: author, note: body)
+
note = Note.create(note_attributes.merge(system: true))
note.system_note_metadata = SystemNoteMetadata.new(action: 'outdated')
diff --git a/app/services/todo_service.rb b/app/services/todo_service.rb
index f91cd03bf5c..0bcd53c76a9 100644
--- a/app/services/todo_service.rb
+++ b/app/services/todo_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# TodoService class
#
# Used for creating/updating todos after certain user actions
diff --git a/app/services/update_release_service.rb b/app/services/update_release_service.rb
index dc696e9c440..422ba668e35 100644
--- a/app/services/update_release_service.rb
+++ b/app/services/update_release_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class UpdateReleaseService < BaseService
def execute(tag_name, release_description)
repository = project.repository
diff --git a/app/services/update_snippet_service.rb b/app/services/update_snippet_service.rb
index 358bca73aec..15bc1046a4e 100644
--- a/app/services/update_snippet_service.rb
+++ b/app/services/update_snippet_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class UpdateSnippetService < BaseService
include SpamCheckService
diff --git a/app/services/upload_service.rb b/app/services/upload_service.rb
index d5a9b344905..39909ee4f82 100644
--- a/app/services/upload_service.rb
+++ b/app/services/upload_service.rb
@@ -1,12 +1,14 @@
+# frozen_string_literal: true
+
class UploadService
- def initialize(model, file, uploader_class = FileUploader)
- @model, @file, @uploader_class = model, file, uploader_class
+ def initialize(model, file, uploader_class = FileUploader, **uploader_context)
+ @model, @file, @uploader_class, @uploader_context = model, file, uploader_class, uploader_context
end
def execute
return nil unless @file && @file.size <= max_attachment_size
- uploader = @uploader_class.new(@model)
+ uploader = @uploader_class.new(@model, nil, @uploader_context)
uploader.store!(@file)
uploader.to_h
diff --git a/app/services/user_agent_detail_service.rb b/app/services/user_agent_detail_service.rb
index a1ee3df5fe1..5cb42e879a0 100644
--- a/app/services/user_agent_detail_service.rb
+++ b/app/services/user_agent_detail_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class UserAgentDetailService
attr_accessor :spammable, :request
diff --git a/app/services/user_project_access_changed_service.rb b/app/services/user_project_access_changed_service.rb
index 8630e572624..adca43660e8 100644
--- a/app/services/user_project_access_changed_service.rb
+++ b/app/services/user_project_access_changed_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class UserProjectAccessChangedService
def initialize(user_ids)
@user_ids = Array.wrap(user_ids)
diff --git a/app/services/validate_new_branch_service.rb b/app/services/validate_new_branch_service.rb
index 643f2ce1481..c19e2ec2043 100644
--- a/app/services/validate_new_branch_service.rb
+++ b/app/services/validate_new_branch_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require_relative 'base_service'
class ValidateNewBranchService < BaseService
diff --git a/app/services/verify_pages_domain_service.rb b/app/services/verify_pages_domain_service.rb
index 13cb53dee01..07f7391f877 100644
--- a/app/services/verify_pages_domain_service.rb
+++ b/app/services/verify_pages_domain_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'resolv'
class VerifyPagesDomainService < BaseService
diff --git a/app/services/web_hook_service.rb b/app/services/web_hook_service.rb
index 8a86e47f0ea..34724e0250d 100644
--- a/app/services/web_hook_service.rb
+++ b/app/services/web_hook_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class WebHookService
class InternalErrorResponse
attr_reader :body, :headers, :code
diff --git a/app/uploaders/file_uploader.rb b/app/uploaders/file_uploader.rb
index 21292ddcf44..83f7b99d2a5 100644
--- a/app/uploaders/file_uploader.rb
+++ b/app/uploaders/file_uploader.rb
@@ -15,7 +15,7 @@ class FileUploader < GitlabUploader
prepend ObjectStorage::Extension::RecordsUploads
MARKDOWN_PATTERN = %r{\!?\[.*?\]\(/uploads/(?<secret>[0-9a-f]{32})/(?<file>.*?)\)}
- DYNAMIC_PATH_PATTERN = %r{(?<secret>\h{32})/(?<identifier>.*)}
+ DYNAMIC_PATH_PATTERN = %r{.*(?<secret>\h{32})/(?<identifier>.*)}
after :remove, :prune_store_dir
@@ -67,6 +67,10 @@ class FileUploader < GitlabUploader
SecureRandom.hex
end
+ def self.extract_dynamic_path(path)
+ DYNAMIC_PATH_PATTERN.match(path)
+ end
+
def upload_paths(identifier)
[
File.join(secret, identifier),
@@ -143,7 +147,7 @@ class FileUploader < GitlabUploader
return if apply_context!(value.uploader_context)
# fallback to the regex based extraction
- if matches = DYNAMIC_PATH_PATTERN.match(value.path)
+ if matches = self.class.extract_dynamic_path(value.path)
@secret = matches[:secret]
@identifier = matches[:identifier]
end
diff --git a/app/views/ci/runner/_how_to_setup_runner.html.haml b/app/views/ci/runner/_how_to_setup_runner.html.haml
index 3ae9ce6c11f..13f96b9747c 100644
--- a/app/views/ci/runner/_how_to_setup_runner.html.haml
+++ b/app/views/ci/runner/_how_to_setup_runner.html.haml
@@ -1,16 +1,17 @@
-- link = link_to _("GitLab Runner section"), 'https://about.gitlab.com/gitlab-ci/#gitlab-runner', target: '_blank'
+- link = link_to _("Install GitLab Runner"), 'https://docs.gitlab.com/runner/install/', target: '_blank'
.append-bottom-10
%h4= _("Setup a #{type} Runner manually")
%ol
%li
- = _("Install a Runner compatible with GitLab CI")
- = (_("(check out the %{link} for information on how to install it).") % { link: link }).html_safe
+ = link.html_safe
%li
= _("Specify the following URL during the Runner setup:")
%code#coordinator_address= root_url(only_path: false)
+ = clipboard_button(target: '#coordinator_address', title: _("Copy URL to clipboard"), class: "btn-transparent btn-clipboard")
%li
= _("Use the following registration token during setup:")
%code#registration_token= registration_token
+ = clipboard_button(target: '#registration_token', title: _("Copy token to clipboard"), class: "btn-transparent btn-clipboard")
%li
= _("Start the Runner!")
diff --git a/app/views/doorkeeper/applications/_delete_form.html.haml b/app/views/doorkeeper/applications/_delete_form.html.haml
index 84b4ce5b606..ac5cac50699 100644
--- a/app/views/doorkeeper/applications/_delete_form.html.haml
+++ b/app/views/doorkeeper/applications/_delete_form.html.haml
@@ -2,9 +2,9 @@
= form_tag oauth_application_path(application) do
%input{ :name => "_method", :type => "hidden", :value => "delete" }/
- if defined? small
- = button_tag type: "submit", class: "btn btn-transparent", data: { confirm: "Are you sure?" } do
+ = button_tag type: "submit", class: "btn btn-transparent", data: { confirm: _("Are you sure?") } do
%span.sr-only
- Destroy
+ = _('Destroy')
= icon('trash')
- else
- = submit_tag 'Destroy', data: { confirm: "Are you sure?" }, class: submit_btn_css
+ = submit_tag _('Destroy'), data: { confirm: _("Are you sure?") }, class: submit_btn_css
diff --git a/app/views/doorkeeper/applications/_form.html.haml b/app/views/doorkeeper/applications/_form.html.haml
index be0935b8313..1ddd0df54cd 100644
--- a/app/views/doorkeeper/applications/_form.html.haml
+++ b/app/views/doorkeeper/applications/_form.html.haml
@@ -10,16 +10,14 @@
= f.text_area :redirect_uri, class: 'form-control', required: true
%span.form-text.text-muted
- Use one line per URI
+ = _('Use one line per URI')
- if Doorkeeper.configuration.native_redirect_uri
%span.form-text.text-muted
- Use
- %code= Doorkeeper.configuration.native_redirect_uri
- for local tests
+ = _('Use <code>%{native_redirect_uri}</code> for local tests').html_safe % { native_redirect_uri: Doorkeeper.configuration.native_redirect_uri }
.form-group
= f.label :scopes, class: 'label-light'
= render 'shared/tokens/scopes_form', prefix: 'doorkeeper_application', token: application, scopes: @scopes
.prepend-top-default
- = f.submit 'Save application', class: "btn btn-create"
+ = f.submit _('Save application'), class: "btn btn-create"
diff --git a/app/views/doorkeeper/applications/edit.html.haml b/app/views/doorkeeper/applications/edit.html.haml
index 49f90298a50..aad4200f240 100644
--- a/app/views/doorkeeper/applications/edit.html.haml
+++ b/app/views/doorkeeper/applications/edit.html.haml
@@ -1,4 +1,4 @@
-- page_title "Edit", @application.name, "Applications"
+- page_title _("Edit"), @application.name, _("Applications")
- @content_class = "limit-container-width" unless fluid_layout
-%h3.page-title Edit application
+%h3.page-title= _('Edit application')
= render 'form', application: @application
diff --git a/app/views/doorkeeper/applications/index.html.haml b/app/views/doorkeeper/applications/index.html.haml
index cdf3ff81bd9..ab3a1b100ce 100644
--- a/app/views/doorkeeper/applications/index.html.haml
+++ b/app/views/doorkeeper/applications/index.html.haml
@@ -1,4 +1,4 @@
-- page_title "Applications"
+- page_title _("Applications")
- @content_class = "limit-container-width" unless fluid_layout
.row.prepend-top-default
@@ -7,28 +7,27 @@
= page_title
%p
- if user_oauth_applications?
- Manage applications that can use GitLab as an OAuth provider,
- and applications that you've authorized to use your account.
+ = _("Manage applications that can use GitLab as an OAuth provider, and applications that you've authorized to use your account.")
- else
- Manage applications that you've authorized to use your account.
+ = _("Manage applications that you've authorized to use your account.")
.col-lg-8
- if user_oauth_applications?
%h5.prepend-top-0
- Add new application
+ = _('Add new application')
= render 'form', application: @application
%hr
- if user_oauth_applications?
.oauth-applications
%h5
- Your applications (#{@applications.size})
+ = _("Your applications (%{size})") % { size: @applications.size }
- if @applications.any?
.table-responsive
%table.table
%thead
%tr
- %th Name
- %th Callback URL
- %th Clients
+ %th= _('Name')
+ %th= _('Callback URL')
+ %th= _('Clients')
%th.last-heading
%tbody
- @applications.each do |application|
@@ -41,25 +40,25 @@
%td
= link_to edit_oauth_application_path(application), class: "btn btn-transparent append-right-5" do
%span.sr-only
- Edit
+ = _('Edit')
= icon('pencil')
= render 'delete_form', application: application, small: true
- else
.settings-message.text-center
- You don't have any applications
+ = _("You don't have any applications")
.oauth-authorized-applications.prepend-top-20.append-bottom-default
- if user_oauth_applications?
%h5
- Authorized applications (#{@authorized_tokens.size})
+ = _("Authorized applications (%{size})") % { size: @authorized_tokens.size }
- if @authorized_tokens.any?
.table-responsive
%table.table.table-striped
%thead
%tr
- %th Name
- %th Authorized At
- %th Scope
+ %th= _('Name')
+ %th= _('Authorized At')
+ %th= _('Scope')
%th
%tbody
- @authorized_apps.each do |app|
@@ -72,12 +71,12 @@
- @authorized_anonymous_tokens.each do |token|
%tr
%td
- Anonymous
+ = _('Anonymous')
.form-text.text-muted
- %em Authorization was granted by entering your username and password in the application.
+ %em= _("Authorization was granted by entering your username and password in the application.")
%td= token.created_at
%td= token.scopes
%td= render 'doorkeeper/authorized_applications/delete_form', token: token
- else
.settings-message.text-center
- You don't have any authorized applications
+ = _("You don't have any authorized applications")
diff --git a/app/views/doorkeeper/applications/new.html.haml b/app/views/doorkeeper/applications/new.html.haml
index d3692d1f759..a66fab20d7c 100644
--- a/app/views/doorkeeper/applications/new.html.haml
+++ b/app/views/doorkeeper/applications/new.html.haml
@@ -1,6 +1,6 @@
-- page_title "New Application"
+- page_title _("New Application")
-%h3.page-title New Application
+%h3.page-title= _("New Application")
%hr
diff --git a/app/views/doorkeeper/applications/show.html.haml b/app/views/doorkeeper/applications/show.html.haml
index 89ad626f73f..bb76ac6d5f6 100644
--- a/app/views/doorkeeper/applications/show.html.haml
+++ b/app/views/doorkeeper/applications/show.html.haml
@@ -1,27 +1,27 @@
-- add_to_breadcrumbs "Applications", oauth_applications_path
+- add_to_breadcrumbs _("Applications"), oauth_applications_path
- breadcrumb_title @application.name
-- page_title @application.name, "Applications"
+- page_title @application.name, _("Applications")
- @content_class = "limit-container-width" unless fluid_layout
%h3.page-title
- Application: #{@application.name}
+ = _("Application: %{name}") % { name: @application.name }
.table-holder.oauth-application-show
%table.table
%tr
%td
- Application Id
+ = _('Application Id')
%td
%code#application_id= @application.uid
%tr
%td
- Secret:
+ = _('Secret:')
%td
%code#secret= @application.secret
%tr
%td
- Callback url
+ = _('Callback url')
%td
- @application.redirect_uri.split.each do |uri|
%div
@@ -30,5 +30,5 @@
= render "shared/tokens/scopes_list", token: @application
.form-actions
- = link_to 'Edit', edit_oauth_application_path(@application), class: 'btn btn-primary wide float-left'
+ = link_to _('Edit'), edit_oauth_application_path(@application), class: 'btn btn-primary wide float-left'
= render 'delete_form', application: @application, submit_btn_css: 'btn btn-danger prepend-left-10'
diff --git a/app/views/doorkeeper/authorizations/error.html.haml b/app/views/doorkeeper/authorizations/error.html.haml
index 6117b00149f..32b4ccb0fe6 100644
--- a/app/views/doorkeeper/authorizations/error.html.haml
+++ b/app/views/doorkeeper/authorizations/error.html.haml
@@ -1,3 +1,3 @@
-%h3.page-title An error has occurred
+%h3.page-title= _("An error has occurred")
%main{ :role => "main" }
%pre= @pre_auth.error_response.body[:error_description]
diff --git a/app/views/doorkeeper/authorizations/new.html.haml b/app/views/doorkeeper/authorizations/new.html.haml
index 28cdc7607e0..ca62a59d909 100644
--- a/app/views/doorkeeper/authorizations/new.html.haml
+++ b/app/views/doorkeeper/authorizations/new.html.haml
@@ -3,34 +3,28 @@
.modal-content
.modal-header
%h3.page-title
- Authorize
- = link_to @pre_auth.client.name, @pre_auth.redirect_uri, target: '_blank', rel: 'noopener noreferrer'
- to use your account?
+ - link_to_client = link_to(@pre_auth.client.name, @pre_auth.redirect_uri, target: '_blank', rel: 'noopener noreferrer')
+ = _("Authorize %{link_to_client} to use your account?")
.modal-body
- if current_user.admin?
.text-warning
%p
= icon("exclamation-triangle fw")
- You are an admin, which means granting access to
- %strong= @pre_auth.client.name
- will allow them to interact with GitLab as an admin as well. Proceed with caution.
+ = _('You are an admin, which means granting access to <strong>%{client_name}</strong> will allow them to interact with GitLab as an admin as well. Proceed with caution.').html_safe % { client_name: @pre_auth.client.name }
%p
- An application called
- = link_to @pre_auth.client.name, @pre_auth.redirect_uri, target: '_blank', rel: 'noopener noreferrer'
- is requesting access to your GitLab account.
+ - link_to_client = link_to(@pre_auth.client.name, @pre_auth.redirect_uri, target: '_blank', rel: 'noopener noreferrer')
+ = _("An application called %{link_to_client} is requesting access to your GitLab account.").html_safe % { link_to_client: link_to_client }
- auth_app_owner = @pre_auth.client.application.owner
- if auth_app_owner
- This application was created by
- = succeed "." do
- = link_to auth_app_owner.name, user_path(auth_app_owner)
+ - link_to_owner = link_to(auth_app_owner.name, user_path(auth_app_owner))
+ = _("This application was created by %{link_to_owner}.").html_safe % { link_to_owner: link_to_owner }
- Please note that this application is not provided by GitLab and you should verify its authenticity before
- allowing access.
+ = _("Please note that this application is not provided by GitLab and you should verify its authenticity before allowing access.")
- if @pre_auth.scopes
%p
- This application will be able to:
+ = _("This application will be able to:")
%ul
- @pre_auth.scopes.each do |scope|
%li
@@ -44,7 +38,7 @@
= hidden_field_tag :response_type, @pre_auth.response_type
= hidden_field_tag :scope, @pre_auth.scope
= hidden_field_tag :nonce, @pre_auth.nonce
- = submit_tag "Deny", class: "btn btn-danger"
+ = submit_tag _("Deny"), class: "btn btn-danger"
= form_tag oauth_authorization_path, method: :post, class: 'inline' do
= hidden_field_tag :client_id, @pre_auth.client.uid
= hidden_field_tag :redirect_uri, @pre_auth.redirect_uri
@@ -52,4 +46,4 @@
= hidden_field_tag :response_type, @pre_auth.response_type
= hidden_field_tag :scope, @pre_auth.scope
= hidden_field_tag :nonce, @pre_auth.nonce
- = submit_tag "Authorize", class: "btn btn-success prepend-left-10"
+ = submit_tag _("Authorize"), class: "btn btn-success prepend-left-10"
diff --git a/app/views/doorkeeper/authorizations/show.html.haml b/app/views/doorkeeper/authorizations/show.html.haml
index 44e868e6782..e4bfd69e7f8 100644
--- a/app/views/doorkeeper/authorizations/show.html.haml
+++ b/app/views/doorkeeper/authorizations/show.html.haml
@@ -1,3 +1,3 @@
-%h3.page-title Authorization code:
+%h3.page-title= _("Authorization code:")
%main{ :role => "main" }
%code#authorization_code= params[:code]
diff --git a/app/views/doorkeeper/authorized_applications/_delete_form.html.haml b/app/views/doorkeeper/authorized_applications/_delete_form.html.haml
index 11c1e67878e..08f2442f025 100644
--- a/app/views/doorkeeper/authorized_applications/_delete_form.html.haml
+++ b/app/views/doorkeeper/authorized_applications/_delete_form.html.haml
@@ -6,4 +6,4 @@
= form_tag path do
%input{ :name => "_method", :type => "hidden", :value => "delete" }/
- = submit_tag 'Revoke', onclick: "return confirm('Are you sure?')", class: 'btn btn-remove btn-sm'
+ = submit_tag _('Revoke'), onclick: "return confirm('#{_('Are you sure?')}')", class: 'btn btn-remove btn-sm'
diff --git a/app/views/doorkeeper/authorized_applications/index.html.haml b/app/views/doorkeeper/authorized_applications/index.html.haml
index 30c9d02b72e..8e73298b250 100644
--- a/app/views/doorkeeper/authorized_applications/index.html.haml
+++ b/app/views/doorkeeper/authorized_applications/index.html.haml
@@ -1,12 +1,12 @@
%header
- %h1 Your authorized applications
+ %h1= _("Your authorized applications")
%main{ :role => "main" }
.table-holder
%table.table.table-striped
%thead
%tr
- %th Application
- %th Created At
+ %th= _('Application')
+ %th= _('Created At')
%th
%th
%tbody
diff --git a/app/views/import/bitbucket/deploy_key.js.haml b/app/views/import/bitbucket/deploy_key.js.haml
index 81b34ab5c9d..99e8ac1afa1 100644
--- a/app/views/import/bitbucket/deploy_key.js.haml
+++ b/app/views/import/bitbucket/deploy_key.js.haml
@@ -1,3 +1,3 @@
:plain
job = $("tr#repo_#{@repo_id}")
- job.find(".import-actions").html("<p class='alert alert-danger'>Access denied! Please verify you can add deploy keys to this repository.</p>")
+ job.find(".import-actions").html("<p class='alert alert-danger'>#{_('Access denied! Please verify you can add deploy keys to this repository.')}</p>")
diff --git a/app/views/import/bitbucket/status.html.haml b/app/views/import/bitbucket/status.html.haml
index 4e8f715db4f..a75b7aa9dd2 100644
--- a/app/views/import/bitbucket/status.html.haml
+++ b/app/views/import/bitbucket/status.html.haml
@@ -1,22 +1,22 @@
-- page_title 'Bitbucket import'
-- header_title 'Projects', root_path
+- page_title _('Bitbucket import')
+- header_title _('Projects'), root_path
%h3.page-title
%i.fa.fa-bitbucket
- Import projects from Bitbucket
+ = _('Import projects from Bitbucket')
- if @repos.any?
%p.light
- Select projects you want to import.
+ = _('Select projects you want to import.')
%hr
%p
- if @incompatible_repos.any?
= button_tag class: 'btn btn-import btn-success js-import-all' do
- Import all compatible projects
+ = _('Import all compatible projects')
= icon('spinner spin', class: 'loading-icon')
- else
= button_tag class: 'btn btn-import btn-success js-import-all' do
- Import all projects
+ = _('Import all projects')
= icon('spinner spin', class: 'loading-icon')
.table-responsive
@@ -26,9 +26,9 @@
%colgroup.import-jobs-status-col
%thead
%tr
- %th From Bitbucket
- %th To GitLab
- %th Status
+ %th= _('From Bitbucket')
+ %th= _('To GitLab')
+ %th= _('Status')
%tbody
- @already_added_projects.each do |project|
%tr{ id: "project_#{project.id}", class: "#{project_status_css_class(project.import_status)}" }
@@ -40,10 +40,10 @@
- if project.import_status == 'finished'
%span
%i.fa.fa-check
- done
+ = _('done')
- elsif project.import_status == 'started'
%i.fa.fa-spinner.fa-spin
- started
+ = _('started')
- else
= project.human_import_status_name
@@ -66,7 +66,7 @@
= text_field_tag :path, repo.name, class: "input-mini form-control", tabindex: 2, autofocus: true, required: true
%td.import-actions.job-status
= button_tag class: 'btn btn-import js-add-to-import' do
- Import
+ = _('Import')
= icon('spinner spin', class: 'loading-icon')
- @incompatible_repos.each do |repo|
%tr{ id: "repo_#{repo.owner}___#{repo.slug}" }
@@ -74,16 +74,13 @@
= link_to repo.full_name, "https://bitbucket.org/#{repo.full_name}", target: '_blank', rel: 'noopener noreferrer'
%td.import-target
%td.import-actions-job-status
- = label_tag 'Incompatible Project', nil, class: 'label badge-danger'
+ = label_tag _('Incompatible Project'), nil, class: 'label badge-danger'
- if @incompatible_repos.any?
%p
- One or more of your Bitbucket projects cannot be imported into GitLab
- directly because they use Subversion or Mercurial for version control,
- rather than Git. Please convert
- = link_to 'them to Git,', 'https://www.atlassian.com/git/tutorials/migrating-overview'
- and go through the
- = link_to 'import flow', status_import_bitbucket_path
- again.
+ = _("One or more of your Bitbucket projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git.")
+ - link_to_git = link_to(_('Git'), 'https://www.atlassian.com/git/tutorials/migrating-overview')
+ - link_to_import_flow = link_to(_('import flow'), status_import_bitbucket_path)
+ = _("Please convert them to %{link_to_git}, and go through the %{link_to_import_flow} again.").html_safe % { link_to_git: link_to_git, link_to_import_flow: link_to_import_flow }
.js-importer-status{ data: { jobs_import_path: "#{jobs_import_bitbucket_path}", import_path: "#{import_bitbucket_path}" } }
diff --git a/app/views/import/fogbugz/new.html.haml b/app/views/import/fogbugz/new.html.haml
index 74d686b6703..b54b1af1e0c 100644
--- a/app/views/import/fogbugz/new.html.haml
+++ b/app/views/import/fogbugz/new.html.haml
@@ -1,26 +1,24 @@
-- page_title "FogBugz Import"
-- header_title "Projects", root_path
+- page_title _("FogBugz Import")
+- header_title _("Projects"), root_path
%h3.page-title
%i.fa.fa-bug
- Import projects from FogBugz
+ = _('Import projects from FogBugz')
%hr
= form_tag callback_import_fogbugz_path do
%p
- To get started you enter your FogBugz URL and login information below.
- In the next steps, you'll be able to map users and select the projects
- you want to import.
+ = _("To get started you enter your FogBugz URL and login information below. In the next steps, you'll be able to map users and select the projects you want to import.")
.form-group.row
- = label_tag :uri, 'FogBugz URL', class: 'col-form-label col-md-2'
+ = label_tag :uri, _('FogBugz URL'), class: 'col-form-label col-md-2'
.col-md-4
= text_field_tag :uri, nil, placeholder: 'https://mycompany.fogbugz.com', class: 'form-control'
.form-group.row
- = label_tag :email, 'FogBugz Email', class: 'col-form-label col-md-2'
+ = label_tag :email, _('FogBugz Email'), class: 'col-form-label col-md-2'
.col-md-4
= text_field_tag :email, nil, class: 'form-control'
.form-group.row
- = label_tag :password, 'FogBugz Password', class: 'col-form-label col-md-2'
+ = label_tag :password, _('FogBugz Password'), class: 'col-form-label col-md-2'
.col-md-4
= password_field_tag :password, nil, class: 'form-control'
.form-actions
- = submit_tag 'Continue to the next step', class: 'btn btn-create'
+ = submit_tag _('Continue to the next step'), class: 'btn btn-create'
diff --git a/app/views/import/fogbugz/new_user_map.html.haml b/app/views/import/fogbugz/new_user_map.html.haml
index d27c5d3c36d..ff2f989c509 100644
--- a/app/views/import/fogbugz/new_user_map.html.haml
+++ b/app/views/import/fogbugz/new_user_map.html.haml
@@ -1,39 +1,33 @@
-- page_title 'User map', 'FogBugz import'
-- header_title "Projects", root_path
+- page_title _('User map'), _('FogBugz import')
+- header_title _("Projects"), root_path
%h3.page-title
%i.fa.fa-bug
- Import projects from FogBugz
+ = _('Import projects from FogBugz')
%hr
= form_tag create_user_map_import_fogbugz_path do
%p
- Customize how FogBugz email addresses and usernames are imported into GitLab.
- In the next step, you'll be able to select the projects you want to import.
+ = _("Customize how FogBugz email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import.")
%p
- The user map is a mapping of the FogBugz users that participated on your projects to the way their email address and usernames will be imported into GitLab. You can change this by populating the table below.
+ = _("The user map is a mapping of the FogBugz users that participated on your projects to the way their email address and usernames will be imported into GitLab. You can change this by populating the table below.")
%ul
%li
- %strong Default: Map a FogBugz account ID to a full name
+ %strong= _("Default: Map a FogBugz account ID to a full name")
%p
- An empty GitLab User field will add the FogBugz user's full name
- (e.g. "By John Smith") in the description of all issues and comments.
- It will also associate and/or assign these issues and comments with
- the project creator.
+ = _("An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator.")
%li
- %strong Map a FogBugz account ID to a GitLab user
+ %strong= _("Map a FogBugz account ID to a GitLab user")
%p
- Selecting a GitLab user will add a link to the GitLab user in the descriptions
- of issues and comments (e.g. "By <a href="#">@johnsmith</a>"). It will also
- associate and/or assign these issues and comments with the selected user.
+ = _('Selecting a GitLab user will add a link to the GitLab user in the descriptions of issues and comments (e.g. "By <a href="#">@johnsmith</a>"). It will also associate and/or assign these issues and comments with the selected user.').html_safe
.table-holder
%table.table
%thead
%tr
- %th ID
- %th Name
- %th Email
- %th GitLab User
+ %th= _("ID")
+ %th= _("Name")
+ %th= _("Email")
+ %th= _("GitLab User")
%tbody
- @user_map.each do |id, user|
%tr
@@ -45,4 +39,4 @@
scope: :all, email_user: true, selected: user[:gitlab_user])
.form-actions
- = submit_tag 'Continue to the next step', class: 'btn btn-create'
+ = submit_tag _('Continue to the next step'), class: 'btn btn-create'
diff --git a/app/views/import/fogbugz/status.html.haml b/app/views/import/fogbugz/status.html.haml
index 7b832c6a23a..830d141ebea 100644
--- a/app/views/import/fogbugz/status.html.haml
+++ b/app/views/import/fogbugz/status.html.haml
@@ -1,20 +1,19 @@
-- page_title "FogBugz import"
-- header_title "Projects", root_path
+- page_title _("FogBugz import")
+- header_title _("Projects"), root_path
%h3.page-title
%i.fa.fa-bug
- Import projects from FogBugz
+ = _('Import projects from FogBugz')
- if @repos.any?
%p.light
- Select projects you want to import.
+ = _('Select projects you want to import.')
%p.light
- Optionally, you can
- = link_to 'customize', new_user_map_import_fogbugz_path
- how FogBugz email addresses and usernames are imported into GitLab.
+ - link_to_customize = link_to('customize', new_user_map_import_fogbugz_path)
+ = _('Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab.').html_safe % { link_to_customize: link_to_customize }
%hr
%p
= button_tag class: 'btn btn-import btn-success js-import-all' do
- Import all projects
+ = _('Import all projects')
= icon("spinner spin", class: "loading-icon")
.table-responsive
@@ -24,9 +23,9 @@
%colgroup.import-jobs-status-col
%thead
%tr
- %th From FogBugz
- %th To GitLab
- %th Status
+ %th= _("From FogBugz")
+ %th= _("To GitLab")
+ %th= _("Status")
%tbody
- @already_added_projects.each do |project|
%tr{ id: "project_#{project.id}", class: "#{project_status_css_class(project.import_status)}" }
@@ -38,10 +37,10 @@
- if project.import_status == 'finished'
%span
%i.fa.fa-check
- done
+ = _("done")
- elsif project.import_status == 'started'
%i.fa.fa-spinner.fa-spin
- started
+ = _("started")
- else
= project.human_import_status_name
@@ -53,7 +52,7 @@
#{current_user.username}/#{repo.name}
%td.import-actions.job-status
= button_tag class: "btn btn-import js-add-to-import" do
- Import
+ = _("Import")
= icon("spinner spin", class: "loading-icon")
.js-importer-status{ data: { jobs_import_path: "#{jobs_import_fogbugz_path}", import_path: "#{import_fogbugz_path}" } }
diff --git a/app/views/import/gitea/new.html.haml b/app/views/import/gitea/new.html.haml
index 581576a8a3d..2b3102f9af9 100644
--- a/app/views/import/gitea/new.html.haml
+++ b/app/views/import/gitea/new.html.haml
@@ -1,23 +1,22 @@
-- page_title "Gitea Import"
-- header_title "Projects", root_path
+- page_title _("Gitea Import")
+- header_title _("Projects"), root_path
%h3.page-title
= custom_icon('go_logo')
- Import Projects from Gitea
+ = _('Import Projects from Gitea')
%p
- To get started, please enter your Gitea Host URL and a
- = succeed '.' do
- = link_to 'Personal Access Token', 'https://github.com/gogits/go-gogs-client/wiki#access-token'
+ - link_to_personal_token = link_to(_('Personal Access Token'), 'https://github.com/gogits/go-gogs-client/wiki#access-token')
+ = _('To get started, please enter your Gitea Host URL and a %{link_to_personal_token}.').html_safe % { link_to_personal_token: link_to_personal_token }
= form_tag personal_access_token_import_gitea_path do
.form-group.row
- = label_tag :gitea_host_url, 'Gitea Host URL', class: 'col-form-label col-sm-2'
+ = label_tag :gitea_host_url, _('Gitea Host URL'), class: 'col-form-label col-sm-2'
.col-sm-4
= text_field_tag :gitea_host_url, nil, placeholder: 'https://try.gitea.io', class: 'form-control'
.form-group.row
- = label_tag :personal_access_token, 'Personal Access Token', class: 'col-form-label col-sm-2'
+ = label_tag :personal_access_token, _('Personal Access Token'), class: 'col-form-label col-sm-2'
.col-sm-4
= text_field_tag :personal_access_token, nil, class: 'form-control'
.form-actions
- = submit_tag 'List Your Gitea Repositories', class: 'btn btn-create'
+ = submit_tag _('List Your Gitea Repositories'), class: 'btn btn-create'
diff --git a/app/views/import/gitea/status.html.haml b/app/views/import/gitea/status.html.haml
index 589ca27e45d..88244fde16b 100644
--- a/app/views/import/gitea/status.html.haml
+++ b/app/views/import/gitea/status.html.haml
@@ -1,7 +1,7 @@
-- page_title "Gitea Import"
-- header_title "Projects", root_path
+- page_title _("Gitea Import")
+- header_title _("Projects"), root_path
%h3.page-title
= custom_icon('go_logo')
- Import Projects from Gitea
+ = _('Import Projects from Gitea')
= render 'import/githubish_status', provider: 'gitea'
diff --git a/app/views/import/github/new.html.haml b/app/views/import/github/new.html.haml
index b9ebb1a39d9..6ff25f2c842 100644
--- a/app/views/import/github/new.html.haml
+++ b/app/views/import/github/new.html.haml
@@ -1,7 +1,7 @@
- title = has_ci_cd_only_params? ? _('Connect repositories from GitHub') : _('GitHub import')
- page_title title
- breadcrumb_title title
-- header_title "Projects", root_path
+- header_title _("Projects"), root_path
%h3.page-title
= icon 'github', text: import_github_title
diff --git a/app/views/import/github/status.html.haml b/app/views/import/github/status.html.haml
index b00b972d9c9..be057be6d1a 100644
--- a/app/views/import/github/status.html.haml
+++ b/app/views/import/github/status.html.haml
@@ -1,7 +1,7 @@
- title = has_ci_cd_only_params? ? _('Connect repositories from GitHub') : _('GitHub import')
- page_title title
- breadcrumb_title title
-- header_title "Projects", root_path
+- header_title _("Projects"), root_path
%h3.page-title
= icon 'github', text: import_github_title
diff --git a/app/views/import/gitlab/status.html.haml b/app/views/import/gitlab/status.html.haml
index 37734414835..b7bfbae5edf 100644
--- a/app/views/import/gitlab/status.html.haml
+++ b/app/views/import/gitlab/status.html.haml
@@ -1,15 +1,15 @@
-- page_title "GitLab.com import"
-- header_title "Projects", root_path
+- page_title _("GitLab.com import")
+- header_title _("Projects"), root_path
%h3.page-title
%i.fa.fa-heart
- Import projects from GitLab.com
+ = _('Import projects from GitLab.com')
%p.light
- Select projects you want to import.
+ = _('Select projects you want to import.')
%hr
%p
= button_tag class: "btn btn-import btn-success js-import-all" do
- Import all projects
+ = _('Import all projects')
= icon("spinner spin", class: "loading-icon")
.table-responsive
@@ -19,9 +19,9 @@
%colgroup.import-jobs-status-col
%thead
%tr
- %th From GitLab.com
- %th To this GitLab instance
- %th Status
+ %th= _('From GitLab.com')
+ %th= _('To this GitLab instance')
+ %th= _('Status')
%tbody
- @already_added_projects.each do |project|
%tr{ id: "project_#{project.id}", class: "#{project_status_css_class(project.import_status)}" }
@@ -33,10 +33,10 @@
- if project.import_status == 'finished'
%span
%i.fa.fa-check
- done
+ = _('done')
- elsif project.import_status == 'started'
%i.fa.fa-spinner.fa-spin
- started
+ = _('started')
- else
= project.human_import_status_name
@@ -48,7 +48,7 @@
= import_project_target(repo['namespace']['path'], repo['name'])
%td.import-actions.job-status
= button_tag class: "btn btn-import js-add-to-import" do
- Import
+ = _('Import')
= icon("spinner spin", class: "loading-icon")
.js-importer-status{ data: { jobs_import_path: "#{jobs_import_gitlab_path}", import_path: "#{import_gitlab_path}" } }
diff --git a/app/views/import/gitlab_projects/new.html.haml b/app/views/import/gitlab_projects/new.html.haml
index cc672a5ea7c..a258fc64b1e 100644
--- a/app/views/import/gitlab_projects/new.html.haml
+++ b/app/views/import/gitlab_projects/new.html.haml
@@ -1,9 +1,9 @@
-- page_title "GitLab Import"
-- header_title "Projects", root_path
+- page_title _("GitLab Import")
+- header_title _("Projects"), root_path
%h3.page-title
= icon('gitlab')
- Import an exported GitLab project
+ = _('Import an exported GitLab project')
%hr
= form_tag import_gitlab_project_path, class: 'new_project', multipart: true do
@@ -24,19 +24,19 @@
#{user_url(current_user.username)}/
= hidden_field_tag :namespace_id, value: current_user.namespace_id
.form-group.col-12.col-sm-6.project-path
- = label_tag :path, 'Project name', class: 'label-light'
+ = label_tag :path, _('Project name'), class: 'label-light'
= text_field_tag :path, @path, placeholder: "my-awesome-project", class: "js-path-name form-control", tabindex: 2, autofocus: true, required: true
.row
.form-group.col-md-12
- To move or copy an entire GitLab project from another GitLab installation to this one, navigate to the original project's settings page, generate an export file, and upload it here.
+ = _("To move or copy an entire GitLab project from another GitLab installation to this one, navigate to the original project's settings page, generate an export file, and upload it here.")
.row
.form-group.col-sm-12
= hidden_field_tag :namespace_id, @namespace.id
- = label_tag :file, 'GitLab project export', class: 'label-light'
+ = label_tag :file, _('GitLab project export'), class: 'label-light'
.form-group
= file_field_tag :file, class: ''
.row
.form-actions.col-sm-12
- = submit_tag 'Import project', class: 'btn btn-create'
- = link_to 'Cancel', new_project_path, class: 'btn btn-cancel'
+ = submit_tag _('Import project'), class: 'btn btn-create'
+ = link_to _('Cancel'), new_project_path, class: 'btn btn-cancel'
diff --git a/app/views/import/google_code/new.html.haml b/app/views/import/google_code/new.html.haml
index 2f1fb8d9c56..fd6e4726fc5 100644
--- a/app/views/import/google_code/new.html.haml
+++ b/app/views/import/google_code/new.html.haml
@@ -1,62 +1,62 @@
-- page_title "Google Code import"
-- header_title "Projects", root_path
+- page_title _("Google Code import")
+- header_title _("Projects"), root_path
%h3.page-title
%i.fa.fa-google
- Import projects from Google Code
+ = _('Import projects from Google Code')
%hr
= form_tag callback_import_google_code_path, multipart: true do
%p
- Follow the steps below to export your Google Code project data.
- In the next step, you'll be able to select the projects you want to import.
+ = _('Follow the steps below to export your Google Code project data.')
+ = _("In the next step, you'll be able to select the projects you want to import.")
%ol
%li
%p
- Go to
- #{link_to "Google Takeout", "https://www.google.com/settings/takeout", target: '_blank', rel: 'noopener noreferrer'}.
+ - link_to_google_takeout = link_to(_("Google Takeout"), "https://www.google.com/settings/takeout", target: '_blank', rel: 'noopener noreferrer')
+ = _("Go to %{link_to_google_takeout}.").html_safe % { link_to_google_takeout: link_to_google_takeout }
%li
%p
- Make sure you're logged into the account that owns the projects you'd like to import.
+ = _("Make sure you're logged into the account that owns the projects you'd like to import.")
%li
%p
- Click the <strong>Select none</strong> button on the right, since we only need "Google Code Project Hosting".
+ = _('Click the <strong>Select none</strong> button on the right, since we only need "Google Code Project Hosting".').html_safe
%li
%p
- Scroll down to <strong>Google Code Project Hosting</strong> and enable the switch on the right.
+ = _('Scroll down to <strong>Google Code Project Hosting</strong> and enable the switch on the right.').html_safe
%li
%p
- Choose <strong>Next</strong> at the bottom of the page.
+ = _('Choose <strong>Next</strong> at the bottom of the page.').html_safe
%li
%p
- Leave the "File type" and "Delivery method" options on their default values.
+ = _('Leave the "File type" and "Delivery method" options on their default values.')
%li
%p
- Choose <strong>Create archive</strong> and wait for archiving to complete.
+ = _('Choose <strong>Create archive</strong> and wait for archiving to complete.').html_safe
%li
%p
- Click the <strong>Download</strong> button and wait for downloading to complete.
+ = _('Click the <strong>Download</strong> button and wait for downloading to complete.').html_safe
%li
%p
- Find the downloaded ZIP file and decompress it.
+ = _('Find the downloaded ZIP file and decompress it.')
%li
%p
- Find the newly extracted <code>Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json</code> file.
+ = _('Find the newly extracted <code>Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json</code> file.').html_safe
%li
%p
- Upload <code>GoogleCodeProjectHosting.json</code> here:
+ = _('Upload <code>GoogleCodeProjectHosting.json</code> here:').html_safe
%p
%input{ type: "file", name: "dump_file", id: "dump_file" }
%li
%p
- Do you want to customize how Google Code email addresses and usernames are imported into GitLab?
+ = _('Do you want to customize how Google Code email addresses and usernames are imported into GitLab?')
%p
= label_tag :create_user_map_0 do
= radio_button_tag :create_user_map, 0, true
- No, directly import the existing email addresses and usernames.
+ = _('No, directly import the existing email addresses and usernames.')
%p
= label_tag :create_user_map_1 do
= radio_button_tag :create_user_map, 1, false
- Yes, let me map Google Code users to full names or GitLab users.
+ = _('Yes, let me map Google Code users to full names or GitLab users.')
%li
%p
- = submit_tag 'Continue to the next step', class: "btn btn-create"
+ = submit_tag _('Continue to the next step'), class: "btn btn-create"
diff --git a/app/views/import/google_code/new_user_map.html.haml b/app/views/import/google_code/new_user_map.html.haml
index 91c774f575c..baaaf6bdc63 100644
--- a/app/views/import/google_code/new_user_map.html.haml
+++ b/app/views/import/google_code/new_user_map.html.haml
@@ -1,44 +1,36 @@
-- page_title "User map", "Google Code import"
-- header_title "Projects", root_path
+- page_title _("User map"), _("Google Code import")
+- header_title _("Projects"), root_path
%h3.page-title
%i.fa.fa-google
- Import projects from Google Code
+ = _('Import projects from Google Code')
%hr
= form_tag create_user_map_import_google_code_path do
%p
- Customize how Google Code email addresses and usernames are imported into GitLab.
- In the next step, you'll be able to select the projects you want to import.
+ = _("Customize how Google Code email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import.")
%p
- The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of <code>:</code>. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side.
+ = _("The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of <code>:</code>. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side.").html_safe
%ul
%li
- %strong Default: Directly import the Google Code email address or username
+ %strong= _("Default: Directly import the Google Code email address or username")
%p
- <code>"johnsmith@example.com": "johnsm...@example.com"</code>
- will add "By johnsm...@example.com" to all issues and comments originally created by johnsmith@example.com.
- The email address or username is masked to ensure the user's privacy.
+ = _('<code>"johnsmith@example.com": "johnsm...@example.com"</code> will add "By johnsm...@example.com" to all issues and comments originally created by johnsmith@example.com. The email address or username is masked to ensure the user\'s privacy.').html_safe
%li
- %strong Map a Google Code user to a GitLab user
+ %strong= _("Map a Google Code user to a GitLab user")
%p
- <code>"johnsmith@example.com": "@johnsmith"</code>
- will add "By <a href="#">@johnsmith</a>" to all issues and comments originally created by johnsmith@example.com,
- and will set <a href="#">@johnsmith</a> as the assignee on all issues originally assigned to johnsmith@example.com.
+ = _('<code>"johnsmith@example.com": "@johnsmith"</code> will add "By <a href="#">@johnsmith</a>" to all issues and comments originally created by johnsmith@example.com, and will set <a href="#">@johnsmith</a> as the assignee on all issues originally assigned to johnsmith@example.com.').html_safe
%li
- %strong Map a Google Code user to a full name
+ %strong= _("Map a Google Code user to a full name")
%p
- <code>"johnsmith@example.com": "John Smith"</code>
- will add "By John Smith" to all issues and comments originally created by johnsmith@example.com.
+ = _('<code>"johnsmith@example.com": "John Smith"</code> will add "By John Smith" to all issues and comments originally created by johnsmith@example.com.').html_safe
%li
- %strong Map a Google Code user to a full email address
+ %strong= _("Map a Google Code user to a full email address")
%p
- <code>"johnsmith@example.com": "johnsmith@example.com"</code>
- will add "By <a href="#">johnsmith@example.com</a>" to all issues and comments originally created by johnsmith@example.com.
- By default, the email address or username is masked to ensure the user's privacy. Use this option if you want to show the full email address.
+ = _('<code>"johnsmith@example.com": "johnsmith@example.com"</code> will add "By <a href="#">johnsmith@example.com</a>" to all issues and comments originally created by johnsmith@example.com. By default, the email address or username is masked to ensure the user\'s privacy. Use this option if you want to show the full email address.').html_safe
.form-group.row
.col-sm-12
= text_area_tag :user_map, JSON.pretty_generate(@user_map), class: 'form-control', rows: 15
.form-actions
- = submit_tag 'Continue to the next step', class: "btn btn-create"
+ = submit_tag _('Continue to the next step'), class: "btn btn-create"
diff --git a/app/views/import/google_code/status.html.haml b/app/views/import/google_code/status.html.haml
index acf7a108cb0..347e2820f94 100644
--- a/app/views/import/google_code/status.html.haml
+++ b/app/views/import/google_code/status.html.haml
@@ -1,25 +1,24 @@
-- page_title "Google Code import"
-- header_title "Projects", root_path
+- page_title _("Google Code import")
+- header_title _("Projects"), root_path
%h3.page-title
%i.fa.fa-google
- Import projects from Google Code
+ = _('Import projects from Google Code')
- if @repos.any?
%p.light
- Select projects you want to import.
+ = _('Select projects you want to import.')
%p.light
- Optionally, you can
- = link_to "customize", new_user_map_import_google_code_path
- how Google Code email addresses and usernames are imported into GitLab.
+ - link_to_customize = link_to(_("customize"), new_user_map_import_google_code_path)
+ = _("Optionally, you can %{link_to_customize} how Google Code email addresses and usernames are imported into GitLab.").html_safe % { link_to_customize: link_to_customize }
%hr
%p
- if @incompatible_repos.any?
= button_tag class: "btn btn-import btn-success js-import-all" do
- Import all compatible projects
+ = _("Import all compatible projects")
= icon("spinner spin", class: "loading-icon")
- else
= button_tag class: "btn btn-import btn-success js-import-all" do
- Import all projects
+ = _("Import all projects")
= icon("spinner spin", class: "loading-icon")
.table-responsive
@@ -29,9 +28,9 @@
%colgroup.import-jobs-status-col
%thead
%tr
- %th From Google Code
- %th To GitLab
- %th Status
+ %th= _("From Google Code")
+ %th= _("To GitLab")
+ %th= _("Status")
%tbody
- @already_added_projects.each do |project|
%tr{ id: "project_#{project.id}", class: "#{project_status_css_class(project.import_status)}" }
@@ -43,10 +42,10 @@
- if project.import_status == 'finished'
%span
%i.fa.fa-check
- done
+ = _("done")
- elsif project.import_status == 'started'
%i.fa.fa-spinner.fa-spin
- started
+ = _("started")
- else
= project.human_import_status_name
@@ -58,7 +57,7 @@
#{current_user.username}/#{repo.name}
%td.import-actions.job-status
= button_tag class: "btn btn-import js-add-to-import" do
- Import
+ = _("Import")
= icon("spinner spin", class: "loading-icon")
- @incompatible_repos.each do |repo|
%tr{ id: "repo_#{repo.id}" }
@@ -66,15 +65,12 @@
= link_to repo.name, "https://code.google.com/p/#{repo.name}", target: "_blank", rel: 'noopener noreferrer'
%td.import-target
%td.import-actions-job-status
- = label_tag "Incompatible Project", nil, class: "label badge-danger"
+ = label_tag _("Incompatible Project"), nil, class: "label badge-danger"
- if @incompatible_repos.any?
%p
- One or more of your Google Code projects cannot be imported into GitLab
- directly because they use Subversion or Mercurial for version control,
- rather than Git. Please convert them to Git on Google Code, and go
- through the
- = link_to "import flow", new_import_google_code_path
- again.
+ = _("One or more of your Google Code projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git.")
+ - link_to_import_flow = link_to(_("import flow"), new_import_google_code_path)
+ = _("Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again.").html_safe % { link_to_import_flow: link_to_import_flow }
.js-importer-status{ data: { jobs_import_path: "#{jobs_import_google_code_path}", import_path: "#{import_google_code_path}" } }
diff --git a/app/views/projects/commit/_change.html.haml b/app/views/projects/commit/_change.html.haml
index 3d97e93c9e9..14a7e84394a 100644
--- a/app/views/projects/commit/_change.html.haml
+++ b/app/views/projects/commit/_change.html.haml
@@ -11,7 +11,7 @@
- branch_label = s_('ChangeTypeActionLabel|Pick into branch')
- title = commit.merged_merge_request(current_user) ? _('Cherry-pick this merge request') : _('Cherry-pick this commit')
-.modal{ id: "modal-#{type}-commit" }
+.modal{ id: "modal-#{type}-commit", tabindex: -1 }
.modal-dialog
.modal-content
.modal-header
diff --git a/app/views/projects/environments/show.html.haml b/app/views/projects/environments/show.html.haml
index a33bc9d4ce6..c7890b37381 100644
--- a/app/views/projects/environments/show.html.haml
+++ b/app/views/projects/environments/show.html.haml
@@ -16,7 +16,7 @@
?
.modal-body
%p= s_('Environments|Are you sure you want to stop this environment?')
- - unless @environment.stop_action?
+ - unless @environment.stop_action_available?
.warning_message
%p= s_('Environments|Note that this action will stop the environment, but it will %{emphasis_start}not%{emphasis_end} have an effect on any existing deployment due to no “stop environment action” being defined in the %{ci_config_link_start}.gitlab-ci.yml%{ci_config_link_end} file.').html_safe % { emphasis_start: '<strong>'.html_safe,
emphasis_end: '</strong>'.html_safe,
diff --git a/app/views/projects/imports/new.html.haml b/app/views/projects/imports/new.html.haml
index 16c4f21279d..ca82054d799 100644
--- a/app/views/projects/imports/new.html.haml
+++ b/app/views/projects/imports/new.html.haml
@@ -10,7 +10,7 @@
.card-body
%pre
:preserve
- #{h(sanitize_repo_path(@project, @project.import_error))}
+ #{h(@project.import_error)}
= form_for @project, url: project_import_path(@project), method: :post do |f|
= render "shared/import_form", f: f
diff --git a/app/views/projects/protected_branches/_update_protected_branch.html.haml b/app/views/projects/protected_branches/_update_protected_branch.html.haml
index f242459f69b..74bfaa9ff80 100644
--- a/app/views/projects/protected_branches/_update_protected_branch.html.haml
+++ b/app/views/projects/protected_branches/_update_protected_branch.html.haml
@@ -6,5 +6,5 @@
%td
= hidden_field_tag "allowed_to_push_#{protected_branch.id}", protected_branch.push_access_levels.first.access_level
= dropdown_tag( (protected_branch.push_access_levels.first.humanize || 'Select') ,
- options: { toggle_class: 'js-allowed-to-push qa-allowed-to-push', dropdown_class: 'dropdown-menu-selectable js-allowed-to-push-container capitalize-header',
+ options: { toggle_class: 'js-allowed-to-push', dropdown_class: 'dropdown-menu-selectable js-allowed-to-push-container capitalize-header',
data: { field_name: "allowed_to_push_#{protected_branch.id}", access_level_id: protected_branch.push_access_levels.first.id }})
diff --git a/app/views/projects/protected_branches/shared/_protected_branch.html.haml b/app/views/projects/protected_branches/shared/_protected_branch.html.haml
index 82ef08272d3..05cee483c0e 100644
--- a/app/views/projects/protected_branches/shared/_protected_branch.html.haml
+++ b/app/views/projects/protected_branches/shared/_protected_branch.html.haml
@@ -2,7 +2,7 @@
%tr.js-protected-branch-edit-form{ data: { url: namespace_project_protected_branch_path(@project.namespace, @project, protected_branch) } }
%td
- %span.ref-name.qa-protected-branch-name= protected_branch.name
+ %span.ref-name= protected_branch.name
- if @project.root_ref?(protected_branch.name)
%span.badge.badge-info.prepend-left-5 default
diff --git a/app/views/projects/wikis/_sidebar.html.haml b/app/views/projects/wikis/_sidebar.html.haml
index a23396dc0d8..28353927135 100644
--- a/app/views/projects/wikis/_sidebar.html.haml
+++ b/app/views/projects/wikis/_sidebar.html.haml
@@ -11,9 +11,11 @@
.blocks-container
.block.block-first
- %ul.wiki-pages
- = render @sidebar_wiki_entries, context: 'sidebar'
-
+ - if @sidebar_page
+ = render_wiki_content(@sidebar_page)
+ - else
+ %ul.wiki-pages
+ = render @sidebar_wiki_entries, context: 'sidebar'
.block
= link_to project_wikis_pages_path(@project), class: 'btn btn-block' do
= s_("Wiki|More Pages")
diff --git a/changelogs/custom_wiki_sidebar.yml b/changelogs/custom_wiki_sidebar.yml
new file mode 100644
index 00000000000..988fccc929c
--- /dev/null
+++ b/changelogs/custom_wiki_sidebar.yml
@@ -0,0 +1,5 @@
+---
+title: "Custom Wiki Sidebar Support Issue 14995"
+merge_request:
+author: Josh Sooter
+type: added
diff --git a/changelogs/unreleased/48745-project-exports-fail-when-uploads-have-been-migrated-to-object-storage.yml b/changelogs/unreleased/48745-project-exports-fail-when-uploads-have-been-migrated-to-object-storage.yml
new file mode 100644
index 00000000000..7552e0d3878
--- /dev/null
+++ b/changelogs/unreleased/48745-project-exports-fail-when-uploads-have-been-migrated-to-object-storage.yml
@@ -0,0 +1,5 @@
+---
+title: Add uploader support to Import/Export uploads
+merge_request: 20484
+author:
+type: added
diff --git a/changelogs/unreleased/49291-fix-memory-graph-component-typo.yml b/changelogs/unreleased/49291-fix-memory-graph-component-typo.yml
new file mode 100644
index 00000000000..f21bd454e84
--- /dev/null
+++ b/changelogs/unreleased/49291-fix-memory-graph-component-typo.yml
@@ -0,0 +1,5 @@
+---
+title: Fix typo in CSS transform property for Memory Graph component
+merge_request: 20650
+author:
+type: fixed
diff --git a/changelogs/unreleased/add-merge-request-header-branch-details-right-margin.yml b/changelogs/unreleased/add-merge-request-header-branch-details-right-margin.yml
new file mode 100644
index 00000000000..4f9a551d13e
--- /dev/null
+++ b/changelogs/unreleased/add-merge-request-header-branch-details-right-margin.yml
@@ -0,0 +1,5 @@
+---
+title: Add merge request header branch actions left margin
+merge_request: 20643
+author: George Tsiolis
+type: changed
diff --git a/changelogs/unreleased/close-revert-and-cherry-pick-modal-on-escape-keypress.yml b/changelogs/unreleased/close-revert-and-cherry-pick-modal-on-escape-keypress.yml
new file mode 100644
index 00000000000..49648cdfcfc
--- /dev/null
+++ b/changelogs/unreleased/close-revert-and-cherry-pick-modal-on-escape-keypress.yml
@@ -0,0 +1,5 @@
+---
+title: Close revert and cherry pick modal on escape keypress
+merge_request: 20341
+author: George Tsiolis
+type: changed
diff --git a/changelogs/unreleased/feature-gb-email-delivery-metrics.yml b/changelogs/unreleased/feature-gb-email-delivery-metrics.yml
new file mode 100644
index 00000000000..9d0d08a471d
--- /dev/null
+++ b/changelogs/unreleased/feature-gb-email-delivery-metrics.yml
@@ -0,0 +1,5 @@
+---
+title: Add emails delivery Prometheus metrics
+merge_request: 20638
+author:
+type: added
diff --git a/changelogs/unreleased/fix-project-api-archived.yml b/changelogs/unreleased/fix-project-api-archived.yml
new file mode 100644
index 00000000000..9d119fd3429
--- /dev/null
+++ b/changelogs/unreleased/fix-project-api-archived.yml
@@ -0,0 +1,5 @@
+---
+title: Fix archived parameter for projects API
+merge_request: 20566
+author: Peter Marko
+type: fixed
diff --git a/changelogs/unreleased/frozen-string-enable-app-services.yml b/changelogs/unreleased/frozen-string-enable-app-services.yml
new file mode 100644
index 00000000000..cfc1f356e3a
--- /dev/null
+++ b/changelogs/unreleased/frozen-string-enable-app-services.yml
@@ -0,0 +1,5 @@
+---
+title: Enable frozen string in apps/uploaders/*.rb
+merge_request: 20401
+author: gfyoung
+type: other
diff --git a/changelogs/unreleased/frozen-string-enable-apps-services-inner.yml b/changelogs/unreleased/frozen-string-enable-apps-services-inner.yml
new file mode 100644
index 00000000000..16b8ec3908f
--- /dev/null
+++ b/changelogs/unreleased/frozen-string-enable-apps-services-inner.yml
@@ -0,0 +1,5 @@
+---
+title: Enable frozen string in app/services/**/*.rb
+merge_request: 20656
+author: gfyoung
+type: performance
diff --git a/changelogs/unreleased/satishperala-gitlab-ce-20720_webhooks_full_image_url.yml b/changelogs/unreleased/satishperala-gitlab-ce-20720_webhooks_full_image_url.yml
new file mode 100644
index 00000000000..7bfe1b5778f
--- /dev/null
+++ b/changelogs/unreleased/satishperala-gitlab-ce-20720_webhooks_full_image_url.yml
@@ -0,0 +1,5 @@
+---
+title: Include full image URL in webhooks for uploaded images
+merge_request: 18109
+author: Satish Perala
+type: changed
diff --git a/changelogs/unreleased/tweak-sql-buckets.yml b/changelogs/unreleased/tweak-sql-buckets.yml
new file mode 100644
index 00000000000..00a0f733ee1
--- /dev/null
+++ b/changelogs/unreleased/tweak-sql-buckets.yml
@@ -0,0 +1,5 @@
+---
+title: Add a 10 ms bucket for SQL timings
+merge_request:
+author:
+type: changed
diff --git a/changelogs/unreleased/update-issue-closing-pattern.yml b/changelogs/unreleased/update-issue-closing-pattern.yml
new file mode 100644
index 00000000000..95488adf449
--- /dev/null
+++ b/changelogs/unreleased/update-issue-closing-pattern.yml
@@ -0,0 +1,5 @@
+---
+title: Update issue closing pattern
+merge_request: 20554
+author: George Tsiolis
+type: changed
diff --git a/changelogs/unreleased/update-specific-runners-help-url.yml b/changelogs/unreleased/update-specific-runners-help-url.yml
new file mode 100644
index 00000000000..0ccbc3b2d65
--- /dev/null
+++ b/changelogs/unreleased/update-specific-runners-help-url.yml
@@ -0,0 +1,5 @@
+---
+title: Update specific runners help URL
+merge_request: 20213
+author: George Tsiolis
+type: other
diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb
index 693a2934a1b..4b9cc59ec45 100644
--- a/config/initializers/1_settings.rb
+++ b/config/initializers/1_settings.rb
@@ -135,7 +135,7 @@ Settings.gitlab['signup_enabled'] ||= true if Settings.gitlab['signup_enabled'].
Settings.gitlab['signin_enabled'] ||= true if Settings.gitlab['signin_enabled'].nil?
Settings.gitlab['restricted_visibility_levels'] = Settings.__send__(:verify_constant_array, Gitlab::VisibilityLevel, Settings.gitlab['restricted_visibility_levels'], [])
Settings.gitlab['username_changing_enabled'] = true if Settings.gitlab['username_changing_enabled'].nil?
-Settings.gitlab['issue_closing_pattern'] = '((?:[Cc]los(?:e[sd]?|ing)|[Ff]ix(?:e[sd]|ing)?|[Rr]esolv(?:e[sd]?|ing)|[Ii]mplement(?:s|ed|ing)?)(:?) +(?:(?:issues? +)?%{issue_ref}(?:(?: *,? +and +| *, *)?)|([A-Z][A-Z0-9_]+-\d+))+)' if Settings.gitlab['issue_closing_pattern'].nil?
+Settings.gitlab['issue_closing_pattern'] = '((?:[Cc]los(?:e[sd]?|ing)|[Ff]ix(?:e[sd]|ing)?|[Rr]esolv(?:e[sd]?|ing)|[Ii]mplement(?:s|ed|ing)?)(:?) +(?:(?:issues? +)?%{issue_ref}(?:(?: *,? +and +| *,? *)?)|([A-Z][A-Z0-9_]+-\d+))+)' if Settings.gitlab['issue_closing_pattern'].nil?
Settings.gitlab['default_projects_features'] ||= {}
Settings.gitlab['webhook_timeout'] ||= 10
Settings.gitlab['max_attachment_size'] ||= 10
diff --git a/config/initializers/action_mailer_hooks.rb b/config/initializers/action_mailer_hooks.rb
new file mode 100644
index 00000000000..f1b3c1f8ae8
--- /dev/null
+++ b/config/initializers/action_mailer_hooks.rb
@@ -0,0 +1,12 @@
+unless Gitlab.config.gitlab.email_enabled
+ ActionMailer::Base.register_interceptor(::Gitlab::Email::Hook::DisableEmailInterceptor)
+ ActionMailer::Base.logger = nil
+end
+
+ActionMailer::Base.register_interceptors(
+ ::Gitlab::Email::Hook::AdditionalHeadersInterceptor,
+ ::Gitlab::Email::Hook::EmailTemplateInterceptor,
+ ::Gitlab::Email::Hook::DeliveryMetricsObserver
+)
+
+ActionMailer::Base.register_observer(::Gitlab::Email::Hook::DeliveryMetricsObserver)
diff --git a/config/initializers/additional_headers_interceptor.rb b/config/initializers/additional_headers_interceptor.rb
deleted file mode 100644
index b9159e7c06c..00000000000
--- a/config/initializers/additional_headers_interceptor.rb
+++ /dev/null
@@ -1 +0,0 @@
-ActionMailer::Base.register_interceptor(AdditionalEmailHeadersInterceptor)
diff --git a/config/initializers/disable_email_interceptor.rb b/config/initializers/disable_email_interceptor.rb
deleted file mode 100644
index e8770c8d460..00000000000
--- a/config/initializers/disable_email_interceptor.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-# Interceptor in lib/disable_email_interceptor.rb
-unless Gitlab.config.gitlab.email_enabled
- ActionMailer::Base.register_interceptor(DisableEmailInterceptor)
- ActionMailer::Base.logger = nil
-end
diff --git a/config/initializers/email_template_interceptor.rb b/config/initializers/email_template_interceptor.rb
deleted file mode 100644
index f195ca9bcd6..00000000000
--- a/config/initializers/email_template_interceptor.rb
+++ /dev/null
@@ -1,2 +0,0 @@
-# Interceptor in lib/email_template_interceptor.rb
-ActionMailer::Base.register_interceptor(EmailTemplateInterceptor)
diff --git a/danger/changelog/Dangerfile b/danger/changelog/Dangerfile
index 2424e650d07..a1f94dc6004 100644
--- a/danger/changelog/Dangerfile
+++ b/danger/changelog/Dangerfile
@@ -2,15 +2,13 @@
require 'yaml'
-NO_CHANGELOG_LABELS = %w[backstage QA test].freeze
+NO_CHANGELOG_LABELS = %w[backstage Documentation QA test].freeze
SEE_DOC = "See [the documentation](https://docs.gitlab.com/ce/development/changelog.html).".freeze
-MISSING_CHANGELOG_MESSAGE = <<~MSG.freeze
-**[CHANGELOG missing](https://docs.gitlab.com/ce/development/changelog.html).**
-
+CREATE_CHANGELOG_MESSAGE = <<~MSG.freeze
You can create one with:
```
-bin/changelog -m %<mr_iid>s
+bin/changelog -m %<mr_iid>s "%<mr_title>s"
```
If your merge request doesn't warrant a CHANGELOG entry,
@@ -38,12 +36,14 @@ def check_changelog(path)
if yaml["merge_request"].nil?
message "Consider setting `merge_request` to #{gitlab.mr_json["iid"]} in #{gitlab.html_link(path)}. #{SEE_DOC}"
- elsif yaml["merge_request"] != gitlab.mr_json["iid"] && !ce_port_changelog?(changelog_path)
+ elsif yaml["merge_request"] != gitlab.mr_json["iid"] && !ce_port_changelog?(path)
fail "Merge request ID was not set to #{gitlab.mr_json["iid"]}! #{SEE_DOC}"
end
-rescue StandardError
+rescue Psych::SyntaxError, Psych::DisallowedClass, Psych::BadAlias
# YAML could not be parsed, fail the build.
fail "#{gitlab.html_link(path)} isn't valid YAML! #{SEE_DOC}"
+rescue StandardError => e
+ warn "There was a problem trying to check the Changelog. Exception: #{e.name} - #{e.message}"
end
def presented_no_changelog_labels
@@ -54,13 +54,15 @@ changelog_needed = (gitlab.mr_labels & NO_CHANGELOG_LABELS).empty?
changelog_found = git.added_files.find { |path| path =~ %r{\A(ee/)?(changelogs/unreleased)(-ee)?/} }
if git.modified_files.include?("CHANGELOG.md")
- fail "CHANGELOG.md was edited. Please remove the additions and create an entry with `bin/changelog -m #{gitlab.mr_json["iid"]}` instead."
+ fail "**CHANGELOG.md was edited.** Please remove the additions and create a CHANGELOG entry.\n\n" +
+ format(CREATE_CHANGELOG_MESSAGE, mr_iid: gitlab.mr_json["iid"], mr_title: gitlab.mr_json["title"], labels: presented_no_changelog_labels)
end
if changelog_needed
if changelog_found
check_changelog(changelog_found)
else
- warn format(MISSING_CHANGELOG_MESSAGE, mr_iid: gitlab.mr_json["iid"], labels: presented_no_changelog_labels)
+ warn "**[CHANGELOG missing](https://docs.gitlab.com/ce/development/changelog.html).**\n\n" +
+ format(CREATE_CHANGELOG_MESSAGE, mr_iid: gitlab.mr_json["iid"], mr_title: gitlab.mr_json["title"], labels: presented_no_changelog_labels)
end
end
diff --git a/danger/specs/Dangerfile b/danger/specs/Dangerfile
index 934ea0beadb..97188df8785 100644
--- a/danger/specs/Dangerfile
+++ b/danger/specs/Dangerfile
@@ -1,12 +1,18 @@
+NO_SPECS_LABELS = %w[backstage Documentation QA].freeze
NO_NEW_SPEC_MESSAGE = <<~MSG.freeze
You've made some app changes, but didn't add any tests.
That's OK as long as you're refactoring existing code,
-but please consider adding the ~backstage label in that case.
+but please consider adding any of the %<labels>s labels.
MSG
+def presented_no_changelog_labels
+ NO_SPECS_LABELS.map { |label| "~#{label}" }.join(', ')
+end
+
has_app_changes = !git.modified_files.grep(%r{\A(ee/)?(app|lib|db/(geo/)?(post_)?migrate)/}).empty?
-has_spec_changes = !git.modified_files.grep(/spec/).empty?
+has_spec_changes = !git.modified_files.grep(%r{\A(ee/)?spec/}).empty?
+new_specs_needed = (gitlab.mr_labels & NO_SPECS_LABELS).empty?
-if has_app_changes && !has_spec_changes
- warn NO_NEW_SPEC_MESSAGE, sticky: false
+if has_app_changes && !has_spec_changes && new_specs_needed
+ warn format(NO_NEW_SPEC_MESSAGE, labels: presented_no_changelog_labels), sticky: false
end
diff --git a/doc/api/pipelines.md b/doc/api/pipelines.md
index ebae68fe389..22cf9afbcd2 100644
--- a/doc/api/pipelines.md
+++ b/doc/api/pipelines.md
@@ -151,7 +151,7 @@ POST /projects/:id/pipelines/:pipeline_id/retry
| `pipeline_id` | integer | yes | The ID of a pipeline |
```
-curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/1/pipelines/46/retry"
+curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/1/pipelines/46/retry"
```
Response:
@@ -197,7 +197,7 @@ POST /projects/:id/pipelines/:pipeline_id/cancel
| `pipeline_id` | integer | yes | The ID of a pipeline |
```
-curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/1/pipelines/46/cancel"
+curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/1/pipelines/46/cancel"
```
Response:
diff --git a/doc/api/users.md b/doc/api/users.md
index ca5afa04687..72fdaaa2c74 100644
--- a/doc/api/users.md
+++ b/doc/api/users.md
@@ -33,6 +33,20 @@ GET /users
]
```
+You can also search for users by email or username with: `/users?search=John`
+
+In addition, you can lookup users by username:
+
+```
+GET /users?username=:username
+```
+
+For example:
+
+```
+GET /users?username=jack_smith
+```
+
In addition, you can filter users based on states eg. `blocked`, `active`
This works only to filter users who are `blocked` or `active`.
It does not support `active=false` or `blocked=false`.
@@ -126,21 +140,7 @@ GET /users
]
```
-You can search for users by email or username with: `/users?search=John`
-
-In addition, you can lookup users by username:
-
-```
-GET /users?username=:username
-```
-
-For example:
-
-```
-GET /users?username=jack_smith
-```
-
-You can also lookup users by external UID and provider:
+You can lookup users by external UID and provider:
```
GET /users?extern_uid=:extern_uid&provider=:provider
diff --git a/doc/development/architecture.md b/doc/development/architecture.md
index 6ca3e9e5a0a..3e417a44ec1 100644
--- a/doc/development/architecture.md
+++ b/doc/development/architecture.md
@@ -197,4 +197,4 @@ Note: It is recommended to log into the `git` user using `sudo -i -u git` or `su
## GitLab.com
-We've also detailed [our architecture of GitLab.com](https://about.gitlab.com/handbook/infrastructure/production-architecture/) but this is probably over the top unless you have millions of users.
+We've also detailed [our architecture of GitLab.com](https://about.gitlab.com/handbook/engineering/infrastructure/production-architecture/) but this is probably over the top unless you have millions of users.
diff --git a/doc/development/sql.md b/doc/development/sql.md
index 974b1d99dff..e1e1d31a85f 100644
--- a/doc/development/sql.md
+++ b/doc/development/sql.md
@@ -243,3 +243,45 @@ WHERE EXISTS (
```
[gin-index]: http://www.postgresql.org/docs/current/static/gin.html
+
+## `.find_or_create_by` is not atomic
+
+The inherent pattern with methods like `.find_or_create_by` and
+`.first_or_create` and others is that they are not atomic. This means,
+it first runs a `SELECT`, and if there are no results an `INSERT` is
+performed. With concurrent processes in mind, there is a race condition
+which may lead to trying to insert two similar records. This may not be
+desired, or may cause one of the queries to fail due to a constraint
+violation, for example.
+
+Using transactions does not solve this problem.
+
+The following pattern should be used to avoid the problem:
+
+```ruby
+Project.transaction do
+ begin
+ User.find_or_create_by(username: "foo")
+ rescue ActiveRecord::RecordNotUnique
+ retry
+ end
+end
+```
+
+If the above block is run inside a transaction and hits the race
+condition, the transaction is aborted and we cannot simply retry (any
+further queries inside the aborted transaction are going to fail). We
+can employ [nested transactions](http://api.rubyonrails.org/classes/ActiveRecord/Transactions/ClassMethods.html#module-ActiveRecord::Transactions::ClassMethods-label-Nested+transactions)
+here to only rollback the "inner transaction". Note that `requires_new: true` is required here.
+
+```ruby
+Project.transaction do
+ begin
+ User.transaction(requires_new: true) do
+ User.find_or_create_by(username: "foo")
+ end
+ rescue ActiveRecord::RecordNotUnique
+ retry
+ end
+end
+```
diff --git a/doc/install/installation.md b/doc/install/installation.md
index 4b68090f8d3..8c7f80fd8e8 100644
--- a/doc/install/installation.md
+++ b/doc/install/installation.md
@@ -92,9 +92,9 @@ Is the system packaged Git too old? Remove it and compile from source.
# Download and compile from source
cd /tmp
- curl --remote-name --progress https://www.kernel.org/pub/software/scm/git/git-2.16.3.tar.gz
- echo 'dda229e9c73f4fbb7d4324e0d993e11311673df03f73b194c554c2e9451e17cd git-2.16.3.tar.gz' | shasum -a256 -c - && tar -xzf git-2.16.3.tar.gz
- cd git-2.16.3/
+ curl --remote-name --progress https://www.kernel.org/pub/software/scm/git/git-2.18.0.tar.gz
+ echo '94faf2c0b02a7920b0b46f4961d8e9cad08e81418614102898a55f980fa3e7e4 git-2.18.0.tar.gz' | shasum -a256 -c - && tar -xzf git-2.18.0.tar.gz
+ cd git-2.18.0/
./configure
make prefix=/usr/local all
diff --git a/doc/topics/autodevops/index.md b/doc/topics/autodevops/index.md
index d04829eaeb8..de1d366adc3 100644
--- a/doc/topics/autodevops/index.md
+++ b/doc/topics/autodevops/index.md
@@ -527,7 +527,7 @@ repo or by specifying a project variable:
- **Bundled chart** - If your project has a `./chart` directory with a `Chart.yaml`
file in it, Auto DevOps will detect the chart and use it instead of the [default
- one](https://gitlab.com/charts/charts.gitlab.io/tree/master/charts/auto-deploy-app).
+ one](https://gitlab.com/charts/auto-deploy-app).
This can be a great way to control exactly how your application is deployed.
- **Project variable** - Create a [project variable](../../ci/variables/README.md#secret-variables)
`AUTO_DEVOPS_CHART` with the URL of a custom chart to use.
diff --git a/doc/update/10.6-to-10.7.md b/doc/update/10.6-to-10.7.md
index 4efbb8c65cf..b9c14395a3a 100644
--- a/doc/update/10.6-to-10.7.md
+++ b/doc/update/10.6-to-10.7.md
@@ -340,7 +340,7 @@ sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production
If all items are green, then congratulations, the upgrade is complete!
-## Things went south? Revert to previous version (10.5)
+## Things went south? Revert to previous version (10.6)
### 1. Revert the code to the previous version
diff --git a/doc/user/project/import/manifest.md b/doc/user/project/import/manifest.md
index 812ecf05faf..06171f11e12 100644
--- a/doc/user/project/import/manifest.md
+++ b/doc/user/project/import/manifest.md
@@ -1,7 +1,8 @@
# Import multiple repositories by uploading a manifest file
GitLab allows you to import all the required git repositories
-based a manifest file like the one used by the Android repository.
+based a manifest file like the one used by the [Android repository](https://android.googlesource.com/platform/manifest/+/2d6f081a3b05d8ef7a2b1b52b0d536b2b74feab4/default.xml).
+This feature can be very handy when you need to import a project with many repositories like Android Open Source Project (AOSP).
>**Note:**
diff --git a/doc/user/project/integrations/webhooks.md b/doc/user/project/integrations/webhooks.md
index 8c09927e2df..8e486318980 100644
--- a/doc/user/project/integrations/webhooks.md
+++ b/doc/user/project/integrations/webhooks.md
@@ -10,6 +10,13 @@ Starting from GitLab 8.5:
Starting from GitLab 11.1, the logs of web hooks are automatically removed after
one month.
+>**Note**
+Starting from GitLab 11.2:
+- The `description` field for issues, merge requests, comments, and wiki pages
+ is rewritten so that simple Markdown image references (like
+ `![](/uploads/...)`) have their target URL changed to an absolute URL. See
+ [image URL rewriting](#image-url-rewriting) for more details.
+
Project webhooks allow you to trigger a URL if for example new code is pushed or
a new issue is created. You can configure webhooks to listen for specific events
like pushes, issues or merge requests. GitLab will send a POST request with data
@@ -1125,6 +1132,27 @@ X-Gitlab-Event: Build Hook
}
```
+## Image URL rewriting
+
+From GitLab 11.2, simple image references are rewritten to use an absolute URL
+in webhooks. So if an image, merge request, comment, or wiki page has this in
+its description:
+
+```markdown
+![image](/uploads/$sha/image.png)
+```
+
+It will appear in the webhook body as the below (assuming that GitLab is
+installed at gitlab.example.com):
+
+```markdown
+![image](https://gitlab.example.com/uploads/$sha/image.png)
+```
+
+This will not rewrite URLs that already are pointing to HTTP, HTTPS, or
+protocol-relative URLs. It will also not rewrite image URLs using advanced
+Markdown features, like link labels.
+
## Testing webhooks
You can trigger the webhook manually. Sample data from the project will be used.Sample data will take from the project.
diff --git a/doc/user/project/wiki/index.md b/doc/user/project/wiki/index.md
index d084ee41d8a..ad0ef60373c 100644
--- a/doc/user/project/wiki/index.md
+++ b/doc/user/project/wiki/index.md
@@ -107,3 +107,10 @@ On the right sidebar, click on **Clone repository** and follow the on-screen
instructions.
[permissions]: ../../permissions.md
+
+## Customizing sidebar
+
+By default, the wiki would render a sidebar which lists all the pages for the
+wiki. You could as well provide a `_sidebar` page to replace this default
+sidebar. When this customized sidebar page is provided, the default sidebar
+would not be rendered, but the customized one.
diff --git a/lib/additional_email_headers_interceptor.rb b/lib/additional_email_headers_interceptor.rb
deleted file mode 100644
index 3cb1694b9f1..00000000000
--- a/lib/additional_email_headers_interceptor.rb
+++ /dev/null
@@ -1,6 +0,0 @@
-class AdditionalEmailHeadersInterceptor
- def self.delivering_email(message)
- message.header['Auto-Submitted'] ||= 'auto-generated'
- message.header['X-Auto-Response-Suppress'] ||= 'All'
- end
-end
diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb
index 9c53b7c3fe7..f7737468148 100644
--- a/lib/api/helpers.rb
+++ b/lib/api/helpers.rb
@@ -385,7 +385,7 @@ module API
finder_params[:non_public] = true if params[:membership].present?
finder_params[:starred] = true if params[:starred].present?
finder_params[:visibility_level] = Gitlab::VisibilityLevel.level_value(params[:visibility]) if params[:visibility]
- finder_params[:archived] = params[:archived]
+ finder_params[:archived] = archived_param unless params[:archived].nil?
finder_params[:search] = params[:search] if params[:search]
finder_params[:user] = params.delete(:user) if params[:user]
finder_params[:custom_attributes] = params[:custom_attributes] if params[:custom_attributes]
@@ -496,5 +496,11 @@ module API
exception.status == 500
end
+
+ def archived_param
+ return 'only' if params[:archived]
+
+ params[:archived]
+ end
end
end
diff --git a/lib/api/projects.rb b/lib/api/projects.rb
index 8273abe48c9..0888e3befac 100644
--- a/lib/api/projects.rb
+++ b/lib/api/projects.rb
@@ -30,7 +30,7 @@ module API
end
params :filter_params do
- optional :archived, type: Boolean, default: false, desc: 'Limit by archived status'
+ optional :archived, type: Boolean, desc: 'Limit by archived status'
optional :visibility, type: String, values: Gitlab::VisibilityLevel.string_values,
desc: 'Limit by visibility'
optional :search, type: String, desc: 'Return list of projects matching the search criteria'
diff --git a/lib/banzai/filter/blockquote_fence_filter.rb b/lib/banzai/filter/blockquote_fence_filter.rb
index fbfcd72c916..7108e828c6d 100644
--- a/lib/banzai/filter/blockquote_fence_filter.rb
+++ b/lib/banzai/filter/blockquote_fence_filter.rb
@@ -2,27 +2,7 @@ module Banzai
module Filter
class BlockquoteFenceFilter < HTML::Pipeline::TextFilter
REGEX = %r{
- (?<code>
- # Code blocks:
- # ```
- # Anything, including `>>>` blocks which are ignored by this filter
- # ```
-
- ^```
- .+?
- \n```\ *$
- )
- |
- (?<html>
- # HTML block:
- # <tag>
- # Anything, including `>>>` blocks which are ignored by this filter
- # </tag>
-
- ^<[^>]+?>\ *\n
- .+?
- \n<\/[^>]+?>\ *$
- )
+ #{::Gitlab::Regex.markdown_code_or_html_blocks}
|
(?:
# Blockquote:
diff --git a/lib/banzai/pipeline/gfm_pipeline.rb b/lib/banzai/pipeline/gfm_pipeline.rb
index 0d9b874ef85..5dab80dd3eb 100644
--- a/lib/banzai/pipeline/gfm_pipeline.rb
+++ b/lib/banzai/pipeline/gfm_pipeline.rb
@@ -24,6 +24,17 @@ module Banzai
Filter::AutolinkFilter,
Filter::ExternalLinkFilter,
+ *reference_filters,
+
+ Filter::TaskListFilter,
+ Filter::InlineDiffFilter,
+
+ Filter::SetDirectionFilter
+ ]
+ end
+
+ def self.reference_filters
+ [
Filter::UserReferenceFilter,
Filter::IssueReferenceFilter,
Filter::ExternalIssueReferenceFilter,
@@ -32,12 +43,7 @@ module Banzai
Filter::CommitRangeReferenceFilter,
Filter::CommitReferenceFilter,
Filter::LabelReferenceFilter,
- Filter::MilestoneReferenceFilter,
-
- Filter::TaskListFilter,
- Filter::InlineDiffFilter,
-
- Filter::SetDirectionFilter
+ Filter::MilestoneReferenceFilter
]
end
diff --git a/lib/banzai/pipeline/post_process_pipeline.rb b/lib/banzai/pipeline/post_process_pipeline.rb
index dcd52bc03c7..0b2e584ef16 100644
--- a/lib/banzai/pipeline/post_process_pipeline.rb
+++ b/lib/banzai/pipeline/post_process_pipeline.rb
@@ -2,11 +2,17 @@ module Banzai
module Pipeline
class PostProcessPipeline < BasePipeline
def self.filters
- FilterArray[
+ @filters ||= FilterArray[
+ *internal_link_filters,
+ Filter::AbsoluteLinkFilter
+ ]
+ end
+
+ def self.internal_link_filters
+ [
Filter::RedactorFilter,
Filter::RelativeLinkFilter,
- Filter::IssuableStateFilter,
- Filter::AbsoluteLinkFilter
+ Filter::IssuableStateFilter
]
end
diff --git a/lib/banzai/pipeline/single_line_pipeline.rb b/lib/banzai/pipeline/single_line_pipeline.rb
index 1929099931b..cd5a6c8875c 100644
--- a/lib/banzai/pipeline/single_line_pipeline.rb
+++ b/lib/banzai/pipeline/single_line_pipeline.rb
@@ -10,13 +10,19 @@ module Banzai
Filter::AutolinkFilter,
Filter::ExternalLinkFilter,
+ *reference_filters
+ ]
+ end
+
+ def self.reference_filters
+ [
Filter::UserReferenceFilter,
Filter::IssueReferenceFilter,
Filter::ExternalIssueReferenceFilter,
Filter::MergeRequestReferenceFilter,
Filter::SnippetReferenceFilter,
Filter::CommitRangeReferenceFilter,
- Filter::CommitReferenceFilter,
+ Filter::CommitReferenceFilter
]
end
end
diff --git a/lib/disable_email_interceptor.rb b/lib/disable_email_interceptor.rb
deleted file mode 100644
index cee664b8951..00000000000
--- a/lib/disable_email_interceptor.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-# Read about interceptors in http://guides.rubyonrails.org/action_mailer_basics.html#intercepting-emails
-class DisableEmailInterceptor
- def self.delivering_email(message)
- message.perform_deliveries = false
- Rails.logger.info "Emails disabled! Interceptor prevented sending mail #{message.subject}"
- end
-end
diff --git a/lib/email_template_interceptor.rb b/lib/email_template_interceptor.rb
deleted file mode 100644
index 3978a6d9fe4..00000000000
--- a/lib/email_template_interceptor.rb
+++ /dev/null
@@ -1,11 +0,0 @@
-# Read about interceptors in http://guides.rubyonrails.org/action_mailer_basics.html#intercepting-emails
-class EmailTemplateInterceptor
- def self.delivering_email(message)
- # Remove HTML part if HTML emails are disabled.
- unless Gitlab::CurrentSettings.html_emails_enabled
- message.parts.delete_if do |part|
- part.content_type.start_with?('text/html')
- end
- end
- end
-end
diff --git a/lib/gitlab/email/hook/additional_headers_interceptor.rb b/lib/gitlab/email/hook/additional_headers_interceptor.rb
new file mode 100644
index 00000000000..064cb5e659a
--- /dev/null
+++ b/lib/gitlab/email/hook/additional_headers_interceptor.rb
@@ -0,0 +1,12 @@
+module Gitlab
+ module Email
+ module Hook
+ class AdditionalHeadersInterceptor
+ def self.delivering_email(message)
+ message.header['Auto-Submitted'] ||= 'auto-generated'
+ message.header['X-Auto-Response-Suppress'] ||= 'All'
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/email/hook/delivery_metrics_observer.rb b/lib/gitlab/email/hook/delivery_metrics_observer.rb
new file mode 100644
index 00000000000..1c2985f6045
--- /dev/null
+++ b/lib/gitlab/email/hook/delivery_metrics_observer.rb
@@ -0,0 +1,31 @@
+module Gitlab
+ module Email
+ module Hook
+ class DeliveryMetricsObserver
+ extend Gitlab::Utils::StrongMemoize
+
+ def self.delivering_email(_message)
+ delivery_attempts_counter.increment
+ end
+
+ def self.delivered_email(_message)
+ delivered_emails_counter.increment
+ end
+
+ def self.delivery_attempts_counter
+ strong_memoize(:delivery_attempts_counter) do
+ Gitlab::Metrics.counter(:gitlab_emails_delivery_attempts_total,
+ 'Counter of total emails delivery attempts')
+ end
+ end
+
+ def self.delivered_emails_counter
+ strong_memoize(:delivered_emails_counter) do
+ Gitlab::Metrics.counter(:gitlab_emails_delivered_total,
+ 'Counter of total emails delievered')
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/email/hook/disable_email_interceptor.rb b/lib/gitlab/email/hook/disable_email_interceptor.rb
new file mode 100644
index 00000000000..7bb8b53f0c8
--- /dev/null
+++ b/lib/gitlab/email/hook/disable_email_interceptor.rb
@@ -0,0 +1,13 @@
+module Gitlab
+ module Email
+ module Hook
+ class DisableEmailInterceptor
+ def self.delivering_email(message)
+ message.perform_deliveries = false
+
+ Rails.logger.info "Emails disabled! Interceptor prevented sending mail #{message.subject}"
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/email/hook/email_template_interceptor.rb b/lib/gitlab/email/hook/email_template_interceptor.rb
new file mode 100644
index 00000000000..be0c4dd862e
--- /dev/null
+++ b/lib/gitlab/email/hook/email_template_interceptor.rb
@@ -0,0 +1,18 @@
+module Gitlab
+ module Email
+ module Hook
+ class EmailTemplateInterceptor
+ ##
+ # Remove HTML part if HTML emails are disabled.
+ #
+ def self.delivering_email(message)
+ unless Gitlab::CurrentSettings.html_emails_enabled
+ message.parts.delete_if do |part|
+ part.content_type.start_with?('text/html')
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/git/repository.rb b/lib/gitlab/git/repository.rb
index fc4711751b1..2cbd9c218d4 100644
--- a/lib/gitlab/git/repository.rb
+++ b/lib/gitlab/git/repository.rb
@@ -280,12 +280,6 @@ module Gitlab
end.map(&:name)
end
- def rugged_head
- rugged.head
- rescue Rugged::ReferenceError
- nil
- end
-
def archive_metadata(ref, storage_path, project_path, format = "tar.gz", append_sha:)
ref ||= root_ref
commit = Gitlab::Git::Commit.find(self, ref)
@@ -449,12 +443,8 @@ module Gitlab
# Returns the SHA of the most recent common ancestor of +from+ and +to+
def merge_base(from, to)
- gitaly_migrate(:merge_base) do |is_enabled|
- if is_enabled
- gitaly_repository_client.find_merge_base(from, to)
- else
- rugged_merge_base(from, to)
- end
+ wrapped_gitaly_errors do
+ gitaly_repository_client.find_merge_base(from, to)
end
end
@@ -470,12 +460,8 @@ module Gitlab
return [] unless root_sha
- branches = gitaly_migrate(:merged_branch_names) do |is_enabled|
- if is_enabled
- gitaly_merged_branch_names(branch_names, root_sha)
- else
- git_merged_branch_names(branch_names, root_sha)
- end
+ branches = wrapped_gitaly_errors do
+ gitaly_merged_branch_names(branch_names, root_sha)
end
Set.new(branches)
@@ -515,11 +501,6 @@ module Gitlab
@refs_hash
end
- # Lookup for rugged object by oid or ref name
- def lookup(oid_or_ref_name)
- rugged.rev_parse(oid_or_ref_name)
- end
-
# Returns url for submodule
#
# Ex.
@@ -859,12 +840,8 @@ module Gitlab
def write_ref(ref_path, ref, old_ref: nil, shell: true)
ref_path = "#{Gitlab::Git::BRANCH_REF_PREFIX}#{ref_path}" unless ref_path.start_with?("refs/") || ref_path == "HEAD"
- gitaly_migrate(:write_ref) do |is_enabled|
- if is_enabled
- gitaly_repository_client.write_ref(ref_path, ref, old_ref, shell)
- else
- local_write_ref(ref_path, ref, old_ref: old_ref, shell: shell)
- end
+ wrapped_gitaly_errors do
+ gitaly_repository_client.write_ref(ref_path, ref, old_ref, shell)
end
end
@@ -916,20 +893,6 @@ module Gitlab
Gitlab::Git::Blob.batch(self, items, blob_size_limit: blob_size_limit)
end
- def commit_index(user, branch_name, index, options)
- committer = user_to_committer(user)
-
- OperationService.new(user, self).with_branch(branch_name) do
- commit_params = options.merge(
- tree: index.write_tree(rugged),
- author: committer,
- committer: committer
- )
-
- create_commit(commit_params)
- end
- end
-
def fsck
msg, status = gitaly_repository_client.fsck
@@ -1213,37 +1176,6 @@ module Gitlab
end
end
- def local_write_ref(ref_path, ref, old_ref: nil, shell: true)
- if shell
- shell_write_ref(ref_path, ref, old_ref)
- else
- rugged_write_ref(ref_path, ref)
- end
- end
-
- def rugged_write_config(full_path:)
- rugged.config['gitlab.fullpath'] = full_path
- end
-
- def shell_write_ref(ref_path, ref, old_ref)
- raise ArgumentError, "invalid ref_path #{ref_path.inspect}" if ref_path.include?(' ')
- raise ArgumentError, "invalid ref #{ref.inspect}" if ref.include?("\x00")
- raise ArgumentError, "invalid old_ref #{old_ref.inspect}" if !old_ref.nil? && old_ref.include?("\x00")
-
- input = "update #{ref_path}\x00#{ref}\x00#{old_ref}\x00"
- run_git!(%w[update-ref --stdin -z]) { |stdin| stdin.write(input) }
- end
-
- def rugged_write_ref(ref_path, ref)
- rugged.references.create(ref_path, ref, force: true)
- rescue Rugged::ReferenceError => ex
- Rails.logger.error "Unable to create #{ref_path} reference for repository #{path}: #{ex}"
- rescue Rugged::OSError => ex
- raise unless ex.message =~ /Failed to create locked file/ && ex.message =~ /File exists/
-
- Rails.logger.error "Unable to create #{ref_path} reference for repository #{path}: #{ex}"
- end
-
def run_git(args, chdir: path, env: {}, nice: false, lazy_block: nil, &block)
cmd = [Gitlab.config.git.bin_path, *args]
cmd.unshift("nice") if nice
@@ -1314,20 +1246,6 @@ module Gitlab
}
end
- def git_merged_branch_names(branch_names, root_sha)
- git_arguments =
- %W[branch --merged #{root_sha}
- --format=%(refname:short)\ %(objectname)] + branch_names
-
- lines = run_git(git_arguments).first.lines
-
- lines.each_with_object([]) do |line, branches|
- name, sha = line.strip.split(' ', 2)
-
- branches << name if sha != root_sha
- end
- end
-
def gitaly_merged_branch_names(branch_names, root_sha)
qualified_branch_names = branch_names.map { |b| "refs/heads/#{b}" }
@@ -1356,23 +1274,6 @@ module Gitlab
end
end
- # We are trying to deprecate this method because it does a lot of work
- # but it seems to be used only to look up submodule URL's.
- # https://gitlab.com/gitlab-org/gitaly/issues/329
- def submodules(ref)
- commit = rev_parse_target(ref)
- return {} unless commit
-
- begin
- content = blob_content(commit, ".gitmodules")
- rescue InvalidBlobName
- return {}
- end
-
- parser = GitmodulesParser.new(content)
- fill_submodule_ids(commit, parser.parse)
- end
-
def gitaly_submodule_url_for(ref, path)
# We don't care about the contents so 1 byte is enough. Can't request 0 bytes, 0 means unlimited.
commit_object = gitaly_commit_client.tree_entry(ref, path, 1)
@@ -1395,68 +1296,6 @@ module Gitlab
Gitlab::Git::HookEnv.all(gl_repository).values_at(*ALLOWED_OBJECT_RELATIVE_DIRECTORIES_VARIABLES).flatten.compact
end
- # Get the content of a blob for a given commit. If the blob is a commit
- # (for submodules) then return the blob's OID.
- def blob_content(commit, blob_name)
- blob_entry = tree_entry(commit, blob_name)
-
- unless blob_entry
- raise InvalidBlobName.new("Invalid blob name: #{blob_name}")
- end
-
- case blob_entry[:type]
- when :commit
- blob_entry[:oid]
- when :tree
- raise InvalidBlobName.new("#{blob_name} is a tree, not a blob")
- when :blob
- rugged.lookup(blob_entry[:oid]).content
- end
- end
-
- # Fill in the 'id' field of a submodule hash from its values
- # as-of +commit+. Return a Hash consisting only of entries
- # from the submodule hash for which the 'id' field is filled.
- def fill_submodule_ids(commit, submodule_data)
- submodule_data.each do |path, data|
- id = begin
- blob_content(commit, path)
- rescue InvalidBlobName
- nil
- end
- data['id'] = id
- end
- submodule_data.select { |path, data| data['id'] }
- end
-
- # Find the entry for +path+ in the tree for +commit+
- def tree_entry(commit, path)
- pathname = Pathname.new(path)
- first = true
- tmp_entry = nil
-
- pathname.each_filename do |dir|
- if first
- tmp_entry = commit.tree[dir]
- first = false
- elsif tmp_entry.nil?
- return nil
- else
- begin
- tmp_entry = rugged.lookup(tmp_entry[:oid])
- rescue Rugged::OdbError, Rugged::InvalidError, Rugged::ReferenceError
- return nil
- end
-
- return nil unless tmp_entry.type == :tree
-
- tmp_entry = tmp_entry[dir]
- end
- end
-
- tmp_entry
- end
-
# Return the Rugged patches for the diff between +from+ and +to+.
def diff_patches(from, to, options = {}, *paths)
options ||= {}
@@ -1496,75 +1335,6 @@ module Gitlab
gitaly_repository_client.apply_gitattributes(revision)
end
- def rugged_copy_gitattributes(ref)
- begin
- commit = lookup(ref)
- rescue Rugged::ReferenceError
- raise InvalidRef.new("Ref #{ref} is invalid")
- end
-
- # Create the paths
- info_dir_path = File.join(path, 'info')
- info_attributes_path = File.join(info_dir_path, 'attributes')
-
- begin
- # Retrieve the contents of the blob
- gitattributes_content = blob_content(commit, '.gitattributes')
- rescue InvalidBlobName
- # No .gitattributes found. Should now remove any info/attributes and return
- File.delete(info_attributes_path) if File.exist?(info_attributes_path)
- return
- end
-
- # Create the info directory if needed
- Dir.mkdir(info_dir_path) unless File.directory?(info_dir_path)
-
- # Write the contents of the .gitattributes file to info/attributes
- # Use binary mode to prevent Rails from converting ASCII-8BIT to UTF-8
- File.open(info_attributes_path, "wb") do |file|
- file.write(gitattributes_content)
- end
- end
-
- def rugged_cherry_pick(user:, commit:, branch_name:, message:, start_branch_name:, start_repository:)
- OperationService.new(user, self).with_branch(
- branch_name,
- start_branch_name: start_branch_name,
- start_repository: start_repository
- ) do |start_commit|
-
- Gitlab::Git.check_namespace!(commit, start_repository)
-
- cherry_pick_tree_id = check_cherry_pick_content(commit, start_commit.sha)
- raise CreateTreeError unless cherry_pick_tree_id
-
- committer = user_to_committer(user)
-
- create_commit(message: message,
- author: {
- email: commit.author_email,
- name: commit.author_name,
- time: commit.authored_date
- },
- committer: committer,
- tree: cherry_pick_tree_id,
- parents: [start_commit.sha])
- end
- end
-
- def check_cherry_pick_content(target_commit, source_sha)
- args = [target_commit.sha, source_sha]
- args << 1 if target_commit.merge_commit?
-
- cherry_pick_index = rugged.cherrypick_commit(*args)
- return false if cherry_pick_index.conflicts?
-
- tree_id = cherry_pick_index.write_tree(rugged)
- return false unless diff_exists?(source_sha, tree_id)
-
- tree_id
- end
-
def local_fetch_ref(source_path, source_ref:, target_ref:)
args = %W(fetch --no-tags -f #{source_path} #{source_ref}:#{target_ref})
run_git(args)
@@ -1621,12 +1391,6 @@ module Gitlab
raise CommandError, @gitlab_projects.output
end
- def rugged_merge_base(from, to)
- rugged.merge_base(from, to)
- rescue Rugged::ReferenceError
- nil
- end
-
def rev_list_param(spec)
spec == :all ? ['--all'] : spec
end
@@ -1634,12 +1398,6 @@ module Gitlab
def sha_from_ref(ref)
rev_parse_target(ref).oid
end
-
- def create_commit(params = {})
- params[:message].delete!("\r")
-
- Rugged::Commit.create(rugged, params)
- end
end
end
end
diff --git a/lib/gitlab/hook_data/base_builder.rb b/lib/gitlab/hook_data/base_builder.rb
new file mode 100644
index 00000000000..4ffca356b29
--- /dev/null
+++ b/lib/gitlab/hook_data/base_builder.rb
@@ -0,0 +1,38 @@
+module Gitlab
+ module HookData
+ class BaseBuilder
+ attr_accessor :object
+
+ MARKDOWN_SIMPLE_IMAGE = %r{
+ #{::Gitlab::Regex.markdown_code_or_html_blocks}
+ |
+ (?<image>
+ !
+ \[(?<title>[^\n]*?)\]
+ \((?<url>(?!(https?://|//))[^\n]+?)\)
+ )
+ }mx.freeze
+
+ def initialize(object)
+ @object = object
+ end
+
+ private
+
+ def absolute_image_urls(markdown_text)
+ return markdown_text unless markdown_text.present?
+
+ markdown_text.gsub(MARKDOWN_SIMPLE_IMAGE) do
+ if $~[:image]
+ url = $~[:url]
+ url = "/#{url}" unless url.start_with?('/')
+
+ "![#{$~[:title]}](#{Gitlab.config.gitlab.url}#{url})"
+ else
+ $~[0]
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/hook_data/issuable_builder.rb b/lib/gitlab/hook_data/issuable_builder.rb
index 6ab36676127..f2eda398b8f 100644
--- a/lib/gitlab/hook_data/issuable_builder.rb
+++ b/lib/gitlab/hook_data/issuable_builder.rb
@@ -1,13 +1,9 @@
module Gitlab
module HookData
- class IssuableBuilder
+ class IssuableBuilder < BaseBuilder
CHANGES_KEYS = %i[previous current].freeze
- attr_accessor :issuable
-
- def initialize(issuable)
- @issuable = issuable
- end
+ alias_method :issuable, :object
def build(user: nil, changes: {})
hook_data = {
diff --git a/lib/gitlab/hook_data/issue_builder.rb b/lib/gitlab/hook_data/issue_builder.rb
index f9b1a3caf5e..0d71c748dc6 100644
--- a/lib/gitlab/hook_data/issue_builder.rb
+++ b/lib/gitlab/hook_data/issue_builder.rb
@@ -1,6 +1,6 @@
module Gitlab
module HookData
- class IssueBuilder
+ class IssueBuilder < BaseBuilder
SAFE_HOOK_ATTRIBUTES = %i[
assignee_id
author_id
@@ -30,14 +30,11 @@ module Gitlab
total_time_spent
].freeze
- attr_accessor :issue
-
- def initialize(issue)
- @issue = issue
- end
+ alias_method :issue, :object
def build
attrs = {
+ description: absolute_image_urls(issue.description),
url: Gitlab::UrlBuilder.build(issue),
total_time_spent: issue.total_time_spent,
human_total_time_spent: issue.human_total_time_spent,
diff --git a/lib/gitlab/hook_data/merge_request_builder.rb b/lib/gitlab/hook_data/merge_request_builder.rb
index aff786864f2..dfbed0597ed 100644
--- a/lib/gitlab/hook_data/merge_request_builder.rb
+++ b/lib/gitlab/hook_data/merge_request_builder.rb
@@ -1,6 +1,6 @@
module Gitlab
module HookData
- class MergeRequestBuilder
+ class MergeRequestBuilder < BaseBuilder
SAFE_HOOK_ATTRIBUTES = %i[
assignee_id
author_id
@@ -35,14 +35,11 @@ module Gitlab
total_time_spent
].freeze
- attr_accessor :merge_request
-
- def initialize(merge_request)
- @merge_request = merge_request
- end
+ alias_method :merge_request, :object
def build
attrs = {
+ description: absolute_image_urls(merge_request.description),
url: Gitlab::UrlBuilder.build(merge_request),
source: merge_request.source_project.try(:hook_attrs),
target: merge_request.target_project.hook_attrs,
diff --git a/lib/gitlab/hook_data/note_builder.rb b/lib/gitlab/hook_data/note_builder.rb
new file mode 100644
index 00000000000..81873e345d5
--- /dev/null
+++ b/lib/gitlab/hook_data/note_builder.rb
@@ -0,0 +1,43 @@
+module Gitlab
+ module HookData
+ class NoteBuilder < BaseBuilder
+ SAFE_HOOK_ATTRIBUTES = %i[
+ attachment
+ author_id
+ change_position
+ commit_id
+ created_at
+ discussion_id
+ id
+ line_code
+ note
+ noteable_id
+ noteable_type
+ original_position
+ position
+ project_id
+ resolved_at
+ resolved_by_id
+ resolved_by_push
+ st_diff
+ system
+ type
+ updated_at
+ updated_by_id
+ ].freeze
+
+ alias_method :note, :object
+
+ def build
+ note
+ .attributes
+ .with_indifferent_access
+ .slice(*SAFE_HOOK_ATTRIBUTES)
+ .merge(
+ description: absolute_image_urls(note.note),
+ url: Gitlab::UrlBuilder.build(note)
+ )
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/hook_data/wiki_page_builder.rb b/lib/gitlab/hook_data/wiki_page_builder.rb
new file mode 100644
index 00000000000..59c94a61cf2
--- /dev/null
+++ b/lib/gitlab/hook_data/wiki_page_builder.rb
@@ -0,0 +1,15 @@
+module Gitlab
+ module HookData
+ class WikiPageBuilder < BaseBuilder
+ alias_method :wiki_page, :object
+
+ def build
+ wiki_page
+ .attributes
+ .merge(
+ 'content' => absolute_image_urls(wiki_page.content)
+ )
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/import_export/avatar_saver.rb b/lib/gitlab/import_export/avatar_saver.rb
index 998c21e2586..31ef0490cb3 100644
--- a/lib/gitlab/import_export/avatar_saver.rb
+++ b/lib/gitlab/import_export/avatar_saver.rb
@@ -11,7 +11,12 @@ module Gitlab
def save
return true unless @project.avatar.exists?
- copy_files(avatar_path, avatar_export_path)
+ Gitlab::ImportExport::UploadsManager.new(
+ project: @project,
+ shared: @shared,
+ relative_export_path: 'avatar',
+ from: avatar_path
+ ).save
rescue => e
@shared.error(e)
false
@@ -19,10 +24,6 @@ module Gitlab
private
- def avatar_export_path
- File.join(@shared.export_path, 'avatar', @project.avatar_identifier)
- end
-
def avatar_path
@project.avatar.path
end
diff --git a/lib/gitlab/import_export/uploads_manager.rb b/lib/gitlab/import_export/uploads_manager.rb
new file mode 100644
index 00000000000..1110149712d
--- /dev/null
+++ b/lib/gitlab/import_export/uploads_manager.rb
@@ -0,0 +1,101 @@
+module Gitlab
+ module ImportExport
+ class UploadsManager
+ include Gitlab::ImportExport::CommandLineUtil
+
+ UPLOADS_BATCH_SIZE = 100
+
+ def initialize(project:, shared:, relative_export_path: 'uploads', from: nil)
+ @project = project
+ @shared = shared
+ @relative_export_path = relative_export_path
+ @from = from || default_uploads_path
+ end
+
+ def save
+ copy_files(@from, uploads_export_path) if File.directory?(@from)
+
+ if File.file?(@from) && @relative_export_path == 'avatar'
+ copy_files(@from, File.join(uploads_export_path, @project.avatar.filename))
+ end
+
+ copy_from_object_storage
+
+ true
+ rescue => e
+ @shared.error(e)
+ false
+ end
+
+ def restore
+ Dir["#{uploads_export_path}/**/*"].each do |upload|
+ next if File.directory?(upload)
+
+ add_upload(upload)
+ end
+
+ true
+ rescue => e
+ @shared.error(e)
+ false
+ end
+
+ private
+
+ def add_upload(upload)
+ uploader_context = FileUploader.extract_dynamic_path(upload).named_captures.symbolize_keys
+
+ UploadService.new(@project, File.open(upload, 'r'), FileUploader, uploader_context).execute
+ end
+
+ def copy_from_object_storage
+ return unless Gitlab::ImportExport.object_storage?
+
+ each_uploader do |uploader|
+ next unless uploader.file
+ next if uploader.upload.local? # Already copied, using the old method
+
+ download_and_copy(uploader)
+ end
+ end
+
+ def default_uploads_path
+ FileUploader.absolute_base_dir(@project)
+ end
+
+ def uploads_export_path
+ @uploads_export_path ||= File.join(@shared.export_path, @relative_export_path)
+ end
+
+ def each_uploader
+ avatar_path = @project.avatar&.upload&.path
+
+ if @relative_export_path == 'avatar'
+ yield(@project.avatar)
+ else
+ project_uploads_except_avatar(avatar_path).find_each(batch_size: UPLOADS_BATCH_SIZE) do |upload|
+ yield(upload.build_uploader)
+ end
+ end
+ end
+
+ def project_uploads_except_avatar(avatar_path)
+ return @project.uploads unless avatar_path
+
+ @project.uploads.where("path != ?", avatar_path)
+ end
+
+ def download_and_copy(upload)
+ secret = upload.try(:secret) || ''
+ upload_path = File.join(uploads_export_path, secret, upload.filename)
+
+ mkdir_p(File.join(uploads_export_path, secret))
+
+ File.open(upload_path, 'w') do |file|
+ # Download (stream) file from the uploader's location
+ IO.copy_stream(URI.parse(upload.file.url).open, file)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/import_export/uploads_restorer.rb b/lib/gitlab/import_export/uploads_restorer.rb
index df19354b76e..25f85936227 100644
--- a/lib/gitlab/import_export/uploads_restorer.rb
+++ b/lib/gitlab/import_export/uploads_restorer.rb
@@ -2,13 +2,30 @@ module Gitlab
module ImportExport
class UploadsRestorer < UploadsSaver
def restore
- return true unless File.directory?(uploads_export_path)
+ if Gitlab::ImportExport.object_storage?
+ Gitlab::ImportExport::UploadsManager.new(
+ project: @project,
+ shared: @shared
+ ).restore
+ elsif File.directory?(uploads_export_path)
+ copy_files(uploads_export_path, uploads_path)
- copy_files(uploads_export_path, uploads_path)
+ true
+ else
+ true # Proceed without uploads
+ end
rescue => e
@shared.error(e)
false
end
+
+ def uploads_path
+ FileUploader.absolute_base_dir(@project)
+ end
+
+ def uploads_export_path
+ @uploads_export_path ||= File.join(@shared.export_path, 'uploads')
+ end
end
end
end
diff --git a/lib/gitlab/import_export/uploads_saver.rb b/lib/gitlab/import_export/uploads_saver.rb
index 2f08dda55fd..b3f17af5661 100644
--- a/lib/gitlab/import_export/uploads_saver.rb
+++ b/lib/gitlab/import_export/uploads_saver.rb
@@ -9,21 +9,14 @@ module Gitlab
end
def save
- return true unless File.directory?(uploads_path)
-
- copy_files(uploads_path, uploads_export_path)
+ Gitlab::ImportExport::UploadsManager.new(
+ project: @project,
+ shared: @shared
+ ).save
rescue => e
@shared.error(e)
false
end
-
- def uploads_path
- FileUploader.absolute_base_dir(@project)
- end
-
- def uploads_export_path
- File.join(@shared.export_path, 'uploads')
- end
end
end
end
diff --git a/lib/gitlab/metrics/subscribers/active_record.rb b/lib/gitlab/metrics/subscribers/active_record.rb
index 38f119cf06d..c205f348023 100644
--- a/lib/gitlab/metrics/subscribers/active_record.rb
+++ b/lib/gitlab/metrics/subscribers/active_record.rb
@@ -20,7 +20,7 @@ module Gitlab
define_histogram :gitlab_sql_duration_seconds do
docstring 'SQL time'
base_labels Transaction::BASE_LABELS
- buckets [0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0]
+ buckets [0.01, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0]
end
def current_transaction
diff --git a/lib/gitlab/regex.rb b/lib/gitlab/regex.rb
index ac3de2a8f71..e1a958c508a 100644
--- a/lib/gitlab/regex.rb
+++ b/lib/gitlab/regex.rb
@@ -73,5 +73,31 @@ module Gitlab
def build_trace_section_regex
@build_trace_section_regexp ||= /section_((?:start)|(?:end)):(\d+):([a-zA-Z0-9_.-]+)\r\033\[0K/.freeze
end
+
+ def markdown_code_or_html_blocks
+ @markdown_code_or_html_blocks ||= %r{
+ (?<code>
+ # Code blocks:
+ # ```
+ # Anything, including `>>>` blocks which are ignored by this filter
+ # ```
+
+ ^```
+ .+?
+ \n```\ *$
+ )
+ |
+ (?<html>
+ # HTML block:
+ # <tag>
+ # Anything, including `>>>` blocks which are ignored by this filter
+ # </tag>
+
+ ^<[^>]+?>\ *\n
+ .+?
+ \n<\/[^>]+?>\ *$
+ )
+ }mx
+ end
end
end
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index ab488218288..8e23383609f 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -129,9 +129,6 @@ msgstr ""
msgid "%{unstaged} unstaged and %{staged} staged changes"
msgstr ""
-msgid "(check out the %{link} for information on how to install it)."
-msgstr ""
-
msgid "+ %{moreCount} more"
msgstr ""
@@ -205,6 +202,18 @@ msgstr ""
msgid "404|Please contact your GitLab administrator if you think this is a mistake."
msgstr ""
+msgid "<code>\"johnsmith@example.com\": \"@johnsmith\"</code> will add \"By <a href=\"#\">@johnsmith</a>\" to all issues and comments originally created by johnsmith@example.com, and will set <a href=\"#\">@johnsmith</a> as the assignee on all issues originally assigned to johnsmith@example.com."
+msgstr ""
+
+msgid "<code>\"johnsmith@example.com\": \"John Smith\"</code> will add \"By John Smith\" to all issues and comments originally created by johnsmith@example.com."
+msgstr ""
+
+msgid "<code>\"johnsmith@example.com\": \"johnsm...@example.com\"</code> will add \"By johnsm...@example.com\" to all issues and comments originally created by johnsmith@example.com. The email address or username is masked to ensure the user's privacy."
+msgstr ""
+
+msgid "<code>\"johnsmith@example.com\": \"johnsmith@example.com\"</code> will add \"By <a href=\"#\">johnsmith@example.com</a>\" to all issues and comments originally created by johnsmith@example.com. By default, the email address or username is masked to ensure the user's privacy. Use this option if you want to show the full email address."
+msgstr ""
+
msgid "<strong>%{group_name}</strong> group members"
msgstr ""
@@ -244,6 +253,9 @@ msgstr ""
msgid "Access Tokens"
msgstr ""
+msgid "Access denied! Please verify you can add deploy keys to this repository."
+msgstr ""
+
msgid "Access to failing storages has been temporarily disabled to allow the mount to recover. Reset storage information after the issue has been resolved to allow access again."
msgstr ""
@@ -280,6 +292,9 @@ msgstr ""
msgid "Add Readme"
msgstr ""
+msgid "Add new application"
+msgstr ""
+
msgid "Add new directory"
msgstr ""
@@ -379,9 +394,18 @@ msgstr ""
msgid "Alternatively, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
msgstr ""
+msgid "An application called %{link_to_client} is requesting access to your GitLab account."
+msgstr ""
+
+msgid "An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator."
+msgstr ""
+
msgid "An error accured whilst committing your changes."
msgstr ""
+msgid "An error has occurred"
+msgstr ""
+
msgid "An error occured creating the new branch."
msgstr ""
@@ -475,12 +499,24 @@ msgstr ""
msgid "An error occurred. Please try again."
msgstr ""
+msgid "Anonymous"
+msgstr ""
+
msgid "Any"
msgstr ""
msgid "Appearance"
msgstr ""
+msgid "Application"
+msgstr ""
+
+msgid "Application Id"
+msgstr ""
+
+msgid "Application: %{name}"
+msgstr ""
+
msgid "Applications"
msgstr ""
@@ -562,6 +598,24 @@ msgstr ""
msgid "Author"
msgstr ""
+msgid "Authorization code:"
+msgstr ""
+
+msgid "Authorization was granted by entering your username and password in the application."
+msgstr ""
+
+msgid "Authorize"
+msgstr ""
+
+msgid "Authorize %{link_to_client} to use your account?"
+msgstr ""
+
+msgid "Authorized At"
+msgstr ""
+
+msgid "Authorized applications (%{size})"
+msgstr ""
+
msgid "Authors: %{authors}"
msgstr ""
@@ -706,6 +760,9 @@ msgstr ""
msgid "Below you will find all the groups that are public."
msgstr ""
+msgid "Bitbucket import"
+msgstr ""
+
msgid "Boards"
msgstr ""
@@ -921,6 +978,12 @@ msgstr ""
msgid "CICD|You need to specify a domain if you want to use Auto Review Apps and Auto Deploy stages."
msgstr ""
+msgid "Callback URL"
+msgstr ""
+
+msgid "Callback url"
+msgstr ""
+
msgid "Can't find HEAD commit for this branch"
msgstr ""
@@ -981,6 +1044,12 @@ msgstr ""
msgid "Cherry-pick this merge request"
msgstr ""
+msgid "Choose <strong>Create archive</strong> and wait for archiving to complete."
+msgstr ""
+
+msgid "Choose <strong>Next</strong> at the bottom of the page."
+msgstr ""
+
msgid "Choose File ..."
msgstr ""
@@ -1092,9 +1161,15 @@ msgstr ""
msgid "Click any <strong>project name</strong> in the project list below to navigate to the project milestone."
msgstr ""
+msgid "Click the <strong>Download</strong> button and wait for downloading to complete."
+msgstr ""
+
msgid "Click the <strong>Promote</strong> button in the top right corner to promote it to a group milestone."
msgstr ""
+msgid "Click the <strong>Select none</strong> button on the right, since we only need \"Google Code Project Hosting\"."
+msgstr ""
+
msgid "Click the button below to begin the install process by navigating to the Kubernetes page"
msgstr ""
@@ -1104,6 +1179,9 @@ msgstr ""
msgid "Click to expand text"
msgstr ""
+msgid "Clients"
+msgstr ""
+
msgid "Clone repository"
msgstr ""
@@ -1639,6 +1717,9 @@ msgstr ""
msgid "Continue"
msgstr ""
+msgid "Continue to the next step"
+msgstr ""
+
msgid "Continuous Integration and Deployment"
msgstr ""
@@ -1690,6 +1771,9 @@ msgstr ""
msgid "Copy to clipboard"
msgstr ""
+msgid "Copy token to clipboard"
+msgstr ""
+
msgid "Create"
msgstr ""
@@ -1768,6 +1852,9 @@ msgstr ""
msgid "Created"
msgstr ""
+msgid "Created At"
+msgstr ""
+
msgid "Created by me"
msgstr ""
@@ -1795,6 +1882,12 @@ msgstr ""
msgid "Custom notification levels are the same as participating levels. With custom notification levels you will also receive notifications for select events. To find out more, check out %{notification_link}."
msgstr ""
+msgid "Customize how FogBugz email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
+msgstr ""
+
+msgid "Customize how Google Code email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
+msgstr ""
+
msgid "Cycle Analytics"
msgstr ""
@@ -1834,6 +1927,12 @@ msgstr ""
msgid "Decline and sign out"
msgstr ""
+msgid "Default: Directly import the Google Code email address or username"
+msgstr ""
+
+msgid "Default: Map a FogBugz account ID to a full name"
+msgstr ""
+
msgid "Define a custom pattern with cron syntax"
msgstr ""
@@ -1846,6 +1945,9 @@ msgstr ""
msgid "Delete list"
msgstr ""
+msgid "Deny"
+msgstr ""
+
msgid "Deploy"
msgid_plural "Deploys"
msgstr[0] ""
@@ -1983,6 +2085,9 @@ msgstr ""
msgid "Description:"
msgstr ""
+msgid "Destroy"
+msgstr ""
+
msgid "Details"
msgstr ""
@@ -2019,6 +2124,9 @@ msgstr ""
msgid "Dismiss Cycle Analytics introduction box"
msgstr ""
+msgid "Do you want to customize how Google Code email addresses and usernames are imported into GitLab?"
+msgstr ""
+
msgid "Domain"
msgstr ""
@@ -2076,6 +2184,9 @@ msgstr ""
msgid "Edit Snippet"
msgstr ""
+msgid "Edit application"
+msgstr ""
+
msgid "Edit files in the editor and commit changes here"
msgstr ""
@@ -2355,6 +2466,12 @@ msgstr ""
msgid "Find file"
msgstr ""
+msgid "Find the downloaded ZIP file and decompress it."
+msgstr ""
+
+msgid "Find the newly extracted <code>Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json</code> file."
+msgstr ""
+
msgid "Finished"
msgstr ""
@@ -2364,6 +2481,24 @@ msgstr ""
msgid "FirstPushedBy|pushed by"
msgstr ""
+msgid "FogBugz Email"
+msgstr ""
+
+msgid "FogBugz Import"
+msgstr ""
+
+msgid "FogBugz Password"
+msgstr ""
+
+msgid "FogBugz URL"
+msgstr ""
+
+msgid "FogBugz import"
+msgstr ""
+
+msgid "Follow the steps below to export your Google Code project data."
+msgstr ""
+
msgid "For internal projects, any logged in user can view pipelines and access job details (output logs and artifacts)"
msgstr ""
@@ -2396,6 +2531,18 @@ msgstr ""
msgid "From %{provider_title}"
msgstr ""
+msgid "From Bitbucket"
+msgstr ""
+
+msgid "From FogBugz"
+msgstr ""
+
+msgid "From GitLab.com"
+msgstr ""
+
+msgid "From Google Code"
+msgstr ""
+
msgid "From issue creation until deploy to production"
msgstr ""
@@ -2417,6 +2564,9 @@ msgstr ""
msgid "Generate a default set of labels"
msgstr ""
+msgid "Git"
+msgstr ""
+
msgid "Git repository URL"
msgstr ""
@@ -2441,7 +2591,16 @@ msgstr ""
msgid "GitLab Group Runners can execute code for all the projects in this group."
msgstr ""
-msgid "GitLab Runner section"
+msgid "GitLab Import"
+msgstr ""
+
+msgid "GitLab User"
+msgstr ""
+
+msgid "GitLab project export"
+msgstr ""
+
+msgid "GitLab.com import"
msgstr ""
msgid "Gitaly"
@@ -2453,18 +2612,33 @@ msgstr ""
msgid "Gitaly|Address"
msgstr ""
+msgid "Gitea Host URL"
+msgstr ""
+
+msgid "Gitea Import"
+msgstr ""
+
msgid "Go Back"
msgstr ""
msgid "Go back"
msgstr ""
+msgid "Go to %{link_to_google_takeout}."
+msgstr ""
+
msgid "Go to your fork"
msgstr ""
msgid "GoToYourFork|Fork"
msgstr ""
+msgid "Google Code import"
+msgstr ""
+
+msgid "Google Takeout"
+msgstr ""
+
msgid "Google authentication is not %{link_to_documentation}. Ask your GitLab administrator if you want to use this service."
msgstr ""
@@ -2686,31 +2860,64 @@ msgstr ""
msgid "Import"
msgstr ""
+msgid "Import Projects from Gitea"
+msgstr ""
+
+msgid "Import all compatible projects"
+msgstr ""
+
+msgid "Import all projects"
+msgstr ""
+
msgid "Import all repositories"
msgstr ""
+msgid "Import an exported GitLab project"
+msgstr ""
+
msgid "Import in progress"
msgstr ""
msgid "Import multiple repositories by uploading a manifest file."
msgstr ""
+msgid "Import project"
+msgstr ""
+
+msgid "Import projects from Bitbucket"
+msgstr ""
+
+msgid "Import projects from FogBugz"
+msgstr ""
+
+msgid "Import projects from GitLab.com"
+msgstr ""
+
+msgid "Import projects from Google Code"
+msgstr ""
+
msgid "Import repositories from GitHub"
msgstr ""
msgid "Import repository"
msgstr ""
+msgid "In the next step, you'll be able to select the projects you want to import."
+msgstr ""
+
msgid "Include a Terms of Service agreement and Privacy Policy that all users must accept."
msgstr ""
+msgid "Incompatible Project"
+msgstr ""
+
msgid "Inline"
msgstr ""
-msgid "Install Runner on Kubernetes"
+msgid "Install GitLab Runner"
msgstr ""
-msgid "Install a Runner compatible with GitLab CI"
+msgid "Install Runner on Kubernetes"
msgstr ""
msgid "Instance does not support multiple Kubernetes clusters"
@@ -2901,9 +3108,15 @@ msgstr ""
msgid "Leave project"
msgstr ""
+msgid "Leave the \"File type\" and \"Delivery method\" options on their default values."
+msgstr ""
+
msgid "List"
msgstr ""
+msgid "List Your Gitea Repositories"
+msgstr ""
+
msgid "List available repositories"
msgstr ""
@@ -2934,12 +3147,21 @@ msgstr ""
msgid "Locked to current projects"
msgstr ""
+msgid "Make sure you're logged into the account that owns the projects you'd like to import."
+msgstr ""
+
msgid "Manage access"
msgstr ""
msgid "Manage all notifications"
msgstr ""
+msgid "Manage applications that can use GitLab as an OAuth provider, and applications that you've authorized to use your account."
+msgstr ""
+
+msgid "Manage applications that you've authorized to use your account."
+msgstr ""
+
msgid "Manage group labels"
msgstr ""
@@ -2955,6 +3177,18 @@ msgstr ""
msgid "Manifest file import"
msgstr ""
+msgid "Map a FogBugz account ID to a GitLab user"
+msgstr ""
+
+msgid "Map a Google Code user to a GitLab user"
+msgstr ""
+
+msgid "Map a Google Code user to a full email address"
+msgstr ""
+
+msgid "Map a Google Code user to a full name"
+msgstr ""
+
msgid "Mar"
msgstr ""
@@ -3141,6 +3375,9 @@ msgstr ""
msgid "New"
msgstr ""
+msgid "New Application"
+msgstr ""
+
msgid "New Group"
msgstr ""
@@ -3251,6 +3488,9 @@ msgstr ""
msgid "No schedules"
msgstr ""
+msgid "No, directly import the existing email addresses and usernames."
+msgstr ""
+
msgid "None"
msgstr ""
@@ -3362,6 +3602,12 @@ msgstr ""
msgid "OfSearchInADropdown|Filter"
msgstr ""
+msgid "One or more of your Bitbucket projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
+msgstr ""
+
+msgid "One or more of your Google Code projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
+msgstr ""
+
msgid "Online IDE integration settings."
msgstr ""
@@ -3386,6 +3632,12 @@ msgstr ""
msgid "Operations"
msgstr ""
+msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
+msgstr ""
+
+msgid "Optionally, you can %{link_to_customize} how Google Code email addresses and usernames are imported into GitLab."
+msgstr ""
+
msgid "Options"
msgstr ""
@@ -3620,6 +3872,15 @@ msgstr ""
msgid "Please accept the Terms of Service before continuing."
msgstr ""
+msgid "Please convert them to %{link_to_git}, and go through the %{link_to_import_flow} again."
+msgstr ""
+
+msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
+msgstr ""
+
+msgid "Please note that this application is not provided by GitLab and you should verify its authenticity before allowing access."
+msgstr ""
+
msgid "Please select at least one filter to see results"
msgstr ""
@@ -3779,6 +4040,9 @@ msgstr ""
msgid "Project export started. A download link will be sent by email."
msgstr ""
+msgid "Project name"
+msgstr ""
+
msgid "ProjectActivityRSS|Subscribe"
msgstr ""
@@ -4060,6 +4324,9 @@ msgstr ""
msgid "Reviewing (merge request !%{mergeRequestId})"
msgstr ""
+msgid "Revoke"
+msgstr ""
+
msgid "Runner token"
msgstr ""
@@ -4084,6 +4351,9 @@ msgstr ""
msgid "Save"
msgstr ""
+msgid "Save application"
+msgstr ""
+
msgid "Save changes"
msgstr ""
@@ -4105,6 +4375,12 @@ msgstr ""
msgid "Scheduling Pipelines"
msgstr ""
+msgid "Scope"
+msgstr ""
+
+msgid "Scroll down to <strong>Google Code Project Hosting</strong> and enable the switch on the right."
+msgstr ""
+
msgid "Scroll to bottom"
msgstr ""
@@ -4144,6 +4420,9 @@ msgstr ""
msgid "Seconds to wait for a storage access attempt"
msgstr ""
+msgid "Secret:"
+msgstr ""
+
msgid "Select"
msgstr ""
@@ -4174,12 +4453,18 @@ msgstr ""
msgid "Select project to choose zone"
msgstr ""
+msgid "Select projects you want to import."
+msgstr ""
+
msgid "Select source branch"
msgstr ""
msgid "Select target branch"
msgstr ""
+msgid "Selecting a GitLab user will add a link to the GitLab user in the descriptions of issues and comments (e.g. \"By <a href=\"#\">@johnsmith</a>\"). It will also associate and/or assign these issues and comments with the selected user."
+msgstr ""
+
msgid "Send email"
msgstr ""
@@ -4688,6 +4973,12 @@ msgstr ""
msgid "The time taken by each data entry gathered by that stage."
msgstr ""
+msgid "The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of <code>:</code>. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side."
+msgstr ""
+
+msgid "The user map is a mapping of the FogBugz users that participated on your projects to the way their email address and usernames will be imported into GitLab. You can change this by populating the table below."
+msgstr ""
+
msgid "The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6."
msgstr ""
@@ -4730,6 +5021,12 @@ msgstr ""
msgid "This GitLab instance does not provide any shared Runners yet. Instance administrators can register shared Runners in the admin area."
msgstr ""
+msgid "This application was created by %{link_to_owner}."
+msgstr ""
+
+msgid "This application will be able to:"
+msgstr ""
+
msgid "This diff is collapsed."
msgstr ""
@@ -4995,6 +5292,12 @@ msgstr ""
msgid "To add an SSH key you need to %{generate_link_start}generate one%{link_end} or use an %{existing_link_start}existing key%{link_end}."
msgstr ""
+msgid "To get started you enter your FogBugz URL and login information below. In the next steps, you'll be able to map users and select the projects you want to import."
+msgstr ""
+
+msgid "To get started, please enter your Gitea Host URL and a %{link_to_personal_token}."
+msgstr ""
+
msgid "To import GitHub repositories, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
msgstr ""
@@ -5004,9 +5307,15 @@ msgstr ""
msgid "To import an SVN repository, check out %{svn_link}."
msgstr ""
+msgid "To move or copy an entire GitLab project from another GitLab installation to this one, navigate to the original project's settings page, generate an export file, and upload it here."
+msgstr ""
+
msgid "To start serving your jobs you can add Runners to your group"
msgstr ""
+msgid "To this GitLab instance"
+msgstr ""
+
msgid "To validate your GitLab CI configurations, go to 'CI/CD → Pipelines' inside your project, and click on the 'CI Lint' button."
msgstr ""
@@ -5108,6 +5417,9 @@ msgstr[1] ""
msgid "Update your group name, description, avatar, and other general settings."
msgstr ""
+msgid "Upload <code>GoogleCodeProjectHosting.json</code> here:"
+msgstr ""
+
msgid "Upload New File"
msgstr ""
@@ -5126,9 +5438,15 @@ msgstr ""
msgid "Usage statistics"
msgstr ""
+msgid "Use <code>%{native_redirect_uri}</code> for local tests"
+msgstr ""
+
msgid "Use group milestones to manage issues from multiple projects in the same milestone."
msgstr ""
+msgid "Use one line per URI"
+msgstr ""
+
msgid "Use the following registration token during setup:"
msgstr ""
@@ -5138,6 +5456,9 @@ msgstr ""
msgid "User and IP Rate Limits"
msgstr ""
+msgid "User map"
+msgstr ""
+
msgid "Users"
msgstr ""
@@ -5369,6 +5690,12 @@ msgstr ""
msgid "Yes, add it"
msgstr ""
+msgid "Yes, let me map Google Code users to full names or GitLab users."
+msgstr ""
+
+msgid "You are an admin, which means granting access to <strong>%{client_name}</strong> will allow them to interact with GitLab as an admin as well. Proceed with caution."
+msgstr ""
+
msgid "You are going to remove %{group_name}. Removed groups CANNOT be restored! Are you ABSOLUTELY sure?"
msgstr ""
@@ -5420,6 +5747,12 @@ msgstr ""
msgid "You do not have any assigned merge requests"
msgstr ""
+msgid "You don't have any applications"
+msgstr ""
+
+msgid "You don't have any authorized applications"
+msgstr ""
+
msgid "You have no permissions"
msgstr ""
@@ -5486,6 +5819,12 @@ msgstr ""
msgid "Your Todos"
msgstr ""
+msgid "Your applications (%{size})"
+msgstr ""
+
+msgid "Your authorized applications"
+msgstr ""
+
msgid "Your changes can be committed to %{branch_name} because a merge request is open."
msgstr ""
@@ -5522,6 +5861,9 @@ msgstr ""
msgid "connecting"
msgstr ""
+msgid "customize"
+msgstr ""
+
msgid "day"
msgid_plural "days"
msgstr[0] ""
@@ -5533,6 +5875,9 @@ msgstr ""
msgid "disabled"
msgstr ""
+msgid "done"
+msgstr ""
+
msgid "enabled"
msgstr ""
@@ -5545,6 +5890,9 @@ msgstr ""
msgid "here"
msgstr ""
+msgid "import flow"
+msgstr ""
+
msgid "importing"
msgstr ""
@@ -5756,6 +6104,9 @@ msgstr ""
msgid "spendCommand|%{slash_command} will update the sum of the time spent."
msgstr ""
+msgid "started"
+msgstr ""
+
msgid "this document"
msgstr ""
diff --git a/qa/qa.rb b/qa/qa.rb
index ef83722de90..0b48cf58766 100644
--- a/qa/qa.rb
+++ b/qa/qa.rb
@@ -53,6 +53,7 @@ module QA
autoload :User, 'qa/factory/resource/user'
autoload :ProjectMilestone, 'qa/factory/resource/project_milestone'
autoload :Wiki, 'qa/factory/resource/wiki'
+ autoload :File, 'qa/factory/resource/file'
autoload :Fork, 'qa/factory/resource/fork'
end
@@ -136,6 +137,15 @@ module QA
autoload :Show, 'qa/page/group/show'
end
+ module File
+ autoload :Form, 'qa/page/file/form'
+ autoload :Show, 'qa/page/file/show'
+
+ module Shared
+ autoload :CommitMessage, 'qa/page/file/shared/commit_message'
+ end
+ end
+
module Project
autoload :New, 'qa/page/project/new'
autoload :Show, 'qa/page/project/show'
diff --git a/qa/qa/factory/resource/branch.rb b/qa/qa/factory/resource/branch.rb
index 7fb0633ec90..bc252bf3148 100644
--- a/qa/qa/factory/resource/branch.rb
+++ b/qa/qa/factory/resource/branch.rb
@@ -9,18 +9,6 @@ module QA
project.name = 'protected-branch-project'
end
- product :name do
- Page::Project::Settings::Repository.act do
- expand_protected_branches(&:last_branch_name)
- end
- end
-
- product :push_allowance do
- Page::Project::Settings::Repository.act do
- expand_protected_branches(&:last_push_allowance)
- end
- end
-
def initialize
@branch_name = 'test/branch'
@allow_to_push = true
@@ -80,15 +68,6 @@ module QA
end
page.protect_branch
-
- # Avoid Selenium::WebDriver::Error::StaleElementReferenceError
- # without sleeping. I.e. this completes fast on fast machines.
- page.refresh
-
- # It is possible for the protected branch row to "disappear" at first
- page.wait do
- page.has_content?(branch_name)
- end
end
end
end
diff --git a/qa/qa/factory/resource/file.rb b/qa/qa/factory/resource/file.rb
new file mode 100644
index 00000000000..2016d10ddae
--- /dev/null
+++ b/qa/qa/factory/resource/file.rb
@@ -0,0 +1,34 @@
+module QA
+ module Factory
+ module Resource
+ class File < Factory::Base
+ attr_accessor :name,
+ :content,
+ :commit_message
+
+ dependency Factory::Resource::Project, as: :project do |project|
+ project.name = 'project-with-new-file'
+ end
+
+ def initialize
+ @name = 'QA Test - File name'
+ @content = 'QA Test - File content'
+ @commit_message = 'QA Test - Commit message'
+ end
+
+ def fabricate!
+ project.visit!
+
+ Page::Project::Show.act { go_to_new_file! }
+
+ Page::File::Form.perform do |page|
+ page.add_name(@name)
+ page.add_content(@content)
+ page.add_commit_message(@commit_message)
+ page.commit_changes
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/page/file/form.rb b/qa/qa/page/file/form.rb
new file mode 100644
index 00000000000..f6e502f500b
--- /dev/null
+++ b/qa/qa/page/file/form.rb
@@ -0,0 +1,40 @@
+module QA
+ module Page
+ module File
+ class Form < Page::Base
+ include Shared::CommitMessage
+
+ view 'app/views/projects/blob/_editor.html.haml' do
+ element :file_name, "text_field_tag 'file_name'"
+ element :editor, '#editor'
+ end
+
+ view 'app/views/projects/_commit_button.html.haml' do
+ element :commit_changes, "button_tag 'Commit changes'"
+ end
+
+ def add_name(name)
+ fill_in 'file_name', with: name
+ end
+
+ def add_content(content)
+ text_area.set content
+ end
+
+ def remove_content
+ text_area.send_keys([:command, 'a'], :backspace)
+ end
+
+ def commit_changes
+ click_on 'Commit changes'
+ end
+
+ private
+
+ def text_area
+ find('#editor>textarea', visible: false)
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/page/file/shared/commit_message.rb b/qa/qa/page/file/shared/commit_message.rb
new file mode 100644
index 00000000000..5af1a55e2ef
--- /dev/null
+++ b/qa/qa/page/file/shared/commit_message.rb
@@ -0,0 +1,19 @@
+module QA
+ module Page
+ module File
+ module Shared
+ module CommitMessage
+ def self.included(base)
+ base.view 'app/views/shared/_commit_message_container.html.haml' do
+ element :commit_message, "text_area_tag 'commit_message'"
+ end
+ end
+
+ def add_commit_message(message)
+ fill_in 'commit_message', with: message
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/page/file/show.rb b/qa/qa/page/file/show.rb
new file mode 100644
index 00000000000..99f5924b67f
--- /dev/null
+++ b/qa/qa/page/file/show.rb
@@ -0,0 +1,30 @@
+module QA
+ module Page
+ module File
+ class Show < Page::Base
+ include Shared::CommitMessage
+
+ view 'app/helpers/blob_helper.rb' do
+ element :edit_button, "_('Edit')"
+ element :delete_button, /label:\s+"Delete"/
+ end
+
+ view 'app/views/projects/blob/_remove.html.haml' do
+ element :delete_file_button, "button_tag 'Delete file'"
+ end
+
+ def click_edit
+ click_on 'Edit'
+ end
+
+ def click_delete
+ click_on 'Delete'
+ end
+
+ def click_delete_file
+ click_on 'Delete file'
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/page/main/oauth.rb b/qa/qa/page/main/oauth.rb
index 6f548148363..618f114e058 100644
--- a/qa/qa/page/main/oauth.rb
+++ b/qa/qa/page/main/oauth.rb
@@ -3,7 +3,7 @@ module QA
module Main
class OAuth < Page::Base
view 'app/views/doorkeeper/authorizations/new.html.haml' do
- element :authorization_button, 'submit_tag "Authorize"'
+ element :authorization_button, 'submit_tag _("Authorize")'
end
def needs_authorization?
diff --git a/qa/qa/page/project/settings/protected_branches.rb b/qa/qa/page/project/settings/protected_branches.rb
index e572ae12132..76591a4e3fe 100644
--- a/qa/qa/page/project/settings/protected_branches.rb
+++ b/qa/qa/page/project/settings/protected_branches.rb
@@ -16,7 +16,6 @@ module QA
end
view 'app/views/projects/protected_branches/_update_protected_branch.html.haml' do
- element :allowed_to_push
element :allowed_to_merge
end
@@ -24,10 +23,6 @@ module QA
element :protected_branches_list
end
- view 'app/views/projects/protected_branches/shared/_protected_branch.html.haml' do
- element :protected_branch_name
- end
-
def select_branch(branch_name)
click_element :protected_branch_select
@@ -62,18 +57,6 @@ module QA
click_on 'Protect'
end
- def last_branch_name
- within_element(:protected_branches_list) do
- all('.qa-protected-branch-name').last
- end
- end
-
- def last_push_allowance
- within_element(:protected_branches_list) do
- all('.qa-allowed-to-push').last
- end
- end
-
private
def click_allow(action, text)
diff --git a/qa/qa/page/project/show.rb b/qa/qa/page/project/show.rb
index 88861d5772d..1d3dad4cda0 100644
--- a/qa/qa/page/project/show.rb
+++ b/qa/qa/page/project/show.rb
@@ -31,10 +31,18 @@ module QA
element :tree_holder, '.tree-holder'
end
+ view 'app/presenters/project_presenter.rb' do
+ element :new_file_button, "label: _('New file'),"
+ end
+
def project_name
find('.qa-project-name').text
end
+ def go_to_new_file!
+ click_on 'New file'
+ end
+
def switch_to_branch(branch_name)
find_element(:branches_select).click
diff --git a/qa/qa/page/view.rb b/qa/qa/page/view.rb
index 6635e1ce039..b2a2da4dbf3 100644
--- a/qa/qa/page/view.rb
+++ b/qa/qa/page/view.rb
@@ -9,7 +9,7 @@ module QA
end
def pathname
- @pathname ||= Pathname.new(File.join(__dir__, '../../../', @path))
+ @pathname ||= Pathname.new(::File.join(__dir__, '../../../', @path))
.cleanpath.expand_path
end
@@ -23,7 +23,7 @@ module QA
# elements' existence.
#
@missing ||= @elements.dup.tap do |elements|
- File.foreach(pathname.to_s) do |line|
+ ::File.foreach(pathname.to_s) do |line|
elements.reject! { |element| element.matches?(line) }
end
end
diff --git a/qa/qa/specs/features/project/file_spec.rb b/qa/qa/specs/features/project/file_spec.rb
new file mode 100644
index 00000000000..5659784cd5c
--- /dev/null
+++ b/qa/qa/specs/features/project/file_spec.rb
@@ -0,0 +1,54 @@
+module QA
+ describe 'File Functionality', :core do
+ it 'lets a user create, edit and delete a file via WebUI' do
+ Runtime::Browser.visit(:gitlab, Page::Main::Login)
+ Page::Main::Login.act { sign_in_using_credentials }
+
+ # Create
+ file_name = 'QA Test - File name'
+ file_content = 'QA Test - File content'
+ commit_message_for_create = 'QA Test - Create new file'
+
+ Factory::Resource::File.fabricate! do |file|
+ file.name = file_name
+ file.content = file_content
+ file.commit_message = commit_message_for_create
+ end
+
+ expect(page).to have_content('The file has been successfully created.')
+ expect(page).to have_content(file_name)
+ expect(page).to have_content(file_content)
+ expect(page).to have_content(commit_message_for_create)
+
+ # Edit
+ updated_file_content = 'QA Test - Updated file content'
+ commit_message_for_update = 'QA Test - Update file'
+
+ Page::File::Show.act { click_edit }
+
+ Page::File::Form.act do
+ remove_content
+ add_content(updated_file_content)
+ add_commit_message(commit_message_for_update)
+ commit_changes
+ end
+
+ expect(page).to have_content('Your changes have been successfully committed.')
+ expect(page).to have_content(updated_file_content)
+ expect(page).to have_content(commit_message_for_update)
+
+ # Delete
+ commit_message_for_delete = 'QA Test - Delete file'
+
+ Page::File::Show.act do
+ click_delete
+ add_commit_message(commit_message_for_delete)
+ click_delete_file
+ end
+
+ expect(page).to have_content('The file has been successfully deleted.')
+ expect(page).to have_content(commit_message_for_delete)
+ expect(page).to have_no_content(file_name)
+ end
+ end
+end
diff --git a/qa/qa/specs/features/repository/protected_branches_spec.rb b/qa/qa/specs/features/repository/protected_branches_spec.rb
index 4e593a69aae..c2de94516d9 100644
--- a/qa/qa/specs/features/repository/protected_branches_spec.rb
+++ b/qa/qa/specs/features/repository/protected_branches_spec.rb
@@ -21,11 +21,8 @@ module QA
end
context 'when developers and maintainers are allowed to push to a protected branch' do
- let!(:protected_branch) { create_protected_branch(allow_to_push: true) }
-
it 'user with push rights successfully pushes to the protected branch' do
- expect(protected_branch.name).to have_content(branch_name)
- expect(protected_branch.push_allowance).to have_content('Developers + Maintainers')
+ create_protected_branch(allow_to_push: true)
push = push_new_file(branch_name)
diff --git a/spec/factories/uploads.rb b/spec/factories/uploads.rb
index b45f6f30e40..a81b2169b89 100644
--- a/spec/factories/uploads.rb
+++ b/spec/factories/uploads.rb
@@ -28,6 +28,13 @@ FactoryBot.define do
secret SecureRandom.hex
end
+ trait :with_file do
+ after(:create) do |upload|
+ FileUtils.mkdir_p(File.dirname(upload.absolute_path))
+ FileUtils.touch(upload.absolute_path)
+ end
+ end
+
trait :object_storage do
store ObjectStorage::Store::REMOTE
end
diff --git a/spec/features/merge_request/user_sees_check_out_branch_modal_spec.rb b/spec/features/merge_request/user_sees_check_out_branch_modal_spec.rb
index c40c720d168..86086a58f18 100644
--- a/spec/features/merge_request/user_sees_check_out_branch_modal_spec.rb
+++ b/spec/features/merge_request/user_sees_check_out_branch_modal_spec.rb
@@ -1,6 +1,6 @@
require 'rails_helper'
-describe 'Merge request > User sees Check out branch modal', :js do
+describe 'Merge request > User sees check out branch modal', :js do
let(:project) { create(:project, :public, :repository) }
let(:user) { project.creator }
let(:merge_request) { create(:merge_request, source_project: project) }
@@ -16,7 +16,7 @@ describe 'Merge request > User sees Check out branch modal', :js do
expect(page).to have_content('Check out, review, and merge locally')
end
- it 'closes the check out branch model with Escape keypress' do
+ it 'closes the check out branch modal with escape keypress' do
find('#modal_merge_info').send_keys(:escape)
expect(page).not_to have_content('Check out, review, and merge locally')
diff --git a/spec/features/merge_request/user_cherry_picks_spec.rb b/spec/features/merge_request/user_sees_cherry_pick_modal_spec.rb
index c6ec3f08cc5..aa499493dbe 100644
--- a/spec/features/merge_request/user_cherry_picks_spec.rb
+++ b/spec/features/merge_request/user_sees_cherry_pick_modal_spec.rb
@@ -21,7 +21,7 @@ describe 'Merge request > User cherry-picks', :js do
end
# Fast-forward merge, or merged before GitLab 8.5.
- context 'Without a merge commit' do
+ context 'without a merge commit' do
before do
merge_request.merge_commit_sha = nil
merge_request.save
@@ -34,7 +34,7 @@ describe 'Merge request > User cherry-picks', :js do
end
end
- context 'With a merge commit' do
+ context 'with a merge commit' do
it 'shows a Cherry-pick button' do
visit project_merge_request_path(project, merge_request)
@@ -49,5 +49,23 @@ describe 'Merge request > User cherry-picks', :js do
expect(page).not_to have_link 'Cherry-pick'
end
end
+
+ context 'and seeing the cherry-pick modal' do
+ before do
+ visit project_merge_request_path(project, merge_request)
+
+ click_link('Cherry-pick')
+ end
+
+ it 'shows the cherry-pick modal' do
+ expect(page).to have_content('Cherry-pick this merge request')
+ end
+
+ it 'closes the cherry-pick modal with escape keypress' do
+ find('#modal-cherry-pick-commit').send_keys(:escape)
+
+ expect(page).not_to have_content('Start a new merge request with these changes')
+ end
+ end
end
end
diff --git a/spec/features/projects/wiki/user_creates_wiki_page_spec.rb b/spec/features/projects/wiki/user_creates_wiki_page_spec.rb
index 830565620d6..149eeb4f9ba 100644
--- a/spec/features/projects/wiki/user_creates_wiki_page_spec.rb
+++ b/spec/features/projects/wiki/user_creates_wiki_page_spec.rb
@@ -2,16 +2,22 @@ require "spec_helper"
describe "User creates wiki page" do
let(:user) { create(:user) }
+ let(:wiki) { ProjectWiki.new(project, user) }
+ let(:project) { create(:project) }
before do
project.add_maintainer(user)
- sign_in(user)
- visit(project_wikis_path(project))
- click_link "Create your first page"
+ sign_in(user)
end
context "when wiki is empty" do
+ before do
+ visit(project_wikis_path(project))
+
+ click_link "Create your first page"
+ end
+
context "in a user namespace" do
let(:project) { create(:project, :wiki_repo, namespace: user.namespace) }
@@ -165,7 +171,9 @@ describe "User creates wiki page" do
context "when wiki is not empty", :js do
before do
- create(:wiki_page, wiki: create(:project, :wiki_repo, namespace: user.namespace).wiki, attrs: { title: "home", content: "Home page" })
+ create(:wiki_page, wiki: wiki, attrs: { title: 'home', content: 'Home page' })
+
+ visit(project_wikis_path(project))
end
context "in a user namespace" do
@@ -290,4 +298,34 @@ describe "User creates wiki page" do
end
end
end
+
+ describe 'sidebar feature' do
+ context 'when there are some existing pages' do
+ before do
+ create(:wiki_page, wiki: wiki, attrs: { title: 'home', content: 'home' })
+ create(:wiki_page, wiki: wiki, attrs: { title: 'another', content: 'another' })
+ end
+
+ it 'renders a default sidebar when there is no customized sidebar' do
+ visit(project_wikis_path(project))
+
+ expect(page).to have_content('Another')
+ expect(page).to have_content('More Pages')
+ end
+
+ context 'when there is a customized sidebar' do
+ before do
+ create(:wiki_page, wiki: wiki, attrs: { title: '_sidebar', content: 'My customized sidebar' })
+ end
+
+ it 'renders my customized sidebar instead of the default one' do
+ visit(project_wikis_path(project))
+
+ expect(page).to have_content('My customized sidebar')
+ expect(page).to have_content('More Pages')
+ expect(page).not_to have_content('Another')
+ end
+ end
+ end
+ end
end
diff --git a/spec/features/user_sees_revert_modal_spec.rb b/spec/features/user_sees_revert_modal_spec.rb
new file mode 100644
index 00000000000..11a9e470f76
--- /dev/null
+++ b/spec/features/user_sees_revert_modal_spec.rb
@@ -0,0 +1,25 @@
+require 'rails_helper'
+
+describe 'Merge request > User sees revert modal', :js do
+ let(:project) { create(:project, :public, :repository) }
+ let(:user) { project.creator }
+ let(:merge_request) { create(:merge_request, source_project: project) }
+
+ before do
+ sign_in(user)
+ visit(project_merge_request_path(project, merge_request))
+ click_button('Merge')
+ visit(merge_request_path(merge_request))
+ click_link('Revert')
+ end
+
+ it 'shows the revert modal' do
+ expect(page).to have_content('Revert this merge request')
+ end
+
+ it 'closes the revert modal with escape keypress' do
+ find('#modal-revert-commit').send_keys(:escape)
+
+ expect(page).not_to have_content('Revert this merge request')
+ end
+end
diff --git a/spec/helpers/projects_helper_spec.rb b/spec/helpers/projects_helper_spec.rb
index beb6e8ea273..cbd4ff0fb4a 100644
--- a/spec/helpers/projects_helper_spec.rb
+++ b/spec/helpers/projects_helper_spec.rb
@@ -290,33 +290,6 @@ describe ProjectsHelper do
end
end
- describe '#sanitizerepo_repo_path' do
- let(:project) { create(:project, :repository) }
- let(:storage_path) do
- Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- Gitlab.config.repositories.storages.default.legacy_disk_path
- end
- end
-
- before do
- allow(Settings.shared).to receive(:[]).with('path').and_return('/base/repo/export/path')
- end
-
- it 'removes the repo path' do
- repo = File.join(storage_path, 'namespace/test.git')
- import_error = "Could not clone #{repo}\n"
-
- expect(sanitize_repo_path(project, import_error)).to eq('Could not clone [REPOS PATH]/namespace/test.git')
- end
-
- it 'removes the temporary repo path used for uploads/exports' do
- repo = '/base/repo/export/path/tmp/project_exports/uploads/test.tar.gz'
- import_error = "Unable to decompress #{repo}\n"
-
- expect(sanitize_repo_path(project, import_error)).to eq('Unable to decompress [REPO EXPORT PATH]/uploads/test.tar.gz')
- end
- end
-
describe '#last_push_event' do
let(:user) { double(:user, fork_of: nil) }
let(:project) { double(:project, id: 1) }
diff --git a/spec/helpers/submodule_helper_spec.rb b/spec/helpers/submodule_helper_spec.rb
index 5a2e4b34069..a64f8a11ef2 100644
--- a/spec/helpers/submodule_helper_spec.rb
+++ b/spec/helpers/submodule_helper_spec.rb
@@ -92,11 +92,10 @@ describe SubmoduleHelper do
context 'in-repository submodule' do
let(:group) { create(:group, name: "Master Project", path: "master-project") }
let(:project) { create(:project, group: group) }
- before do
- self.instance_variable_set(:@project, project)
- end
it 'in-repository' do
+ allow(repo).to receive(:project).and_return(project)
+
stub_url('./')
expect(submodule_links(submodule_item)).to eq(["/master-project/#{project.path}", "/master-project/#{project.path}/tree/hash"])
end
@@ -167,32 +166,28 @@ describe SubmoduleHelper do
let(:project) { create(:project, group: group) }
let(:commit_id) { sample_commit[:id] }
- before do
- self.instance_variable_set(:@project, project)
- end
-
it 'one level down' do
- result = relative_self_links('../test.git', commit_id)
+ result = relative_self_links('../test.git', commit_id, project)
expect(result).to eq(["/#{group.path}/test", "/#{group.path}/test/tree/#{commit_id}"])
end
it 'with trailing whitespace' do
- result = relative_self_links('../test.git ', commit_id)
+ result = relative_self_links('../test.git ', commit_id, project)
expect(result).to eq(["/#{group.path}/test", "/#{group.path}/test/tree/#{commit_id}"])
end
it 'two levels down' do
- result = relative_self_links('../../test.git', commit_id)
+ result = relative_self_links('../../test.git', commit_id, project)
expect(result).to eq(["/#{group.path}/test", "/#{group.path}/test/tree/#{commit_id}"])
end
it 'one level down with namespace and repo' do
- result = relative_self_links('../foobar/test.git', commit_id)
+ result = relative_self_links('../foobar/test.git', commit_id, project)
expect(result).to eq(["/foobar/test", "/foobar/test/tree/#{commit_id}"])
end
it 'two levels down with namespace and repo' do
- result = relative_self_links('../foobar/baz/test.git', commit_id)
+ result = relative_self_links('../foobar/baz/test.git', commit_id, project)
expect(result).to eq(["/baz/test", "/baz/test/tree/#{commit_id}"])
end
@@ -201,7 +196,7 @@ describe SubmoduleHelper do
let(:project) { create(:project, namespace: user.namespace) }
it 'one level down with personal project' do
- result = relative_self_links('../test.git', commit_id)
+ result = relative_self_links('../test.git', commit_id, project)
expect(result).to eq(["/#{user.username}/test", "/#{user.username}/test/tree/#{commit_id}"])
end
end
diff --git a/spec/javascripts/diffs/components/diff_file_header_spec.js b/spec/javascripts/diffs/components/diff_file_header_spec.js
index 0f3a95da5bf..241ff07026e 100644
--- a/spec/javascripts/diffs/components/diff_file_header_spec.js
+++ b/spec/javascripts/diffs/components/diff_file_header_spec.js
@@ -24,7 +24,7 @@ describe('diff_file_header', () => {
const diffFile = convertObjectPropsToCamelCase(diffDiscussionMock.diff_file, { deep: true });
props = {
diffFile,
- currentUser: {},
+ canCurrentUserFork: false,
};
});
diff --git a/spec/javascripts/diffs/components/diff_file_spec.js b/spec/javascripts/diffs/components/diff_file_spec.js
index 9b994543e19..7a4616ec8eb 100644
--- a/spec/javascripts/diffs/components/diff_file_spec.js
+++ b/spec/javascripts/diffs/components/diff_file_spec.js
@@ -11,7 +11,7 @@ describe('DiffFile', () => {
beforeEach(() => {
vm = createComponentWithStore(Vue.extend(DiffFileComponent), store, {
file: getDiffFileMock(),
- currentUser: {},
+ canCurrentUserFork: false,
}).$mount();
});
diff --git a/spec/javascripts/diffs/components/diff_line_note_form_spec.js b/spec/javascripts/diffs/components/diff_line_note_form_spec.js
index 81cd4f9769a..4600aaea70b 100644
--- a/spec/javascripts/diffs/components/diff_line_note_form_spec.js
+++ b/spec/javascripts/diffs/components/diff_line_note_form_spec.js
@@ -15,7 +15,7 @@ describe('DiffLineNoteForm', () => {
diffLines = diffFile.highlightedDiffLines;
component = createComponentWithStore(Vue.extend(DiffLineNoteForm), store, {
- diffFile,
+ diffFileHash: diffFile.fileHash,
diffLines,
line: diffLines[0],
noteTargetLine: diffLines[0],
diff --git a/spec/javascripts/diffs/store/getters_spec.js b/spec/javascripts/diffs/store/getters_spec.js
index 919b612bb6a..6210d4a7124 100644
--- a/spec/javascripts/diffs/store/getters_spec.js
+++ b/spec/javascripts/diffs/store/getters_spec.js
@@ -184,4 +184,23 @@ describe('Diffs Module Getters', () => {
).toEqual(0);
});
});
+
+ describe('getDiffFileByHash', () => {
+ it('returns file by hash', () => {
+ const fileA = {
+ fileHash: '123',
+ };
+ const fileB = {
+ fileHash: '456',
+ };
+ localState.diffFiles = [fileA, fileB];
+
+ expect(getters.getDiffFileByHash(localState)('456')).toEqual(fileB);
+ });
+
+ it('returns null if no matching file is found', () => {
+ localState.diffFiles = [];
+ expect(getters.getDiffFileByHash(localState)('123')).toBeUndefined();
+ });
+ });
});
diff --git a/spec/javascripts/environments/environment_item_spec.js b/spec/javascripts/environments/environment_item_spec.js
index 7a34126eef7..0b933dda431 100644
--- a/spec/javascripts/environments/environment_item_spec.js
+++ b/spec/javascripts/environments/environment_item_spec.js
@@ -104,7 +104,7 @@ describe('Environment item', () => {
},
],
},
- 'stop_action?': true,
+ has_stop_action: true,
environment_path: 'root/ci-folders/environments/31',
created_at: '2016-11-07T11:11:16.525Z',
updated_at: '2016-11-10T15:55:58.778Z',
diff --git a/spec/javascripts/environments/mock_data.js b/spec/javascripts/environments/mock_data.js
index 8a1e26935d9..7bb57f938b8 100644
--- a/spec/javascripts/environments/mock_data.js
+++ b/spec/javascripts/environments/mock_data.js
@@ -7,7 +7,7 @@ export const environmentsList = [
external_url: null,
environment_type: null,
last_deployment: null,
- 'stop_action?': false,
+ has_stop_action: false,
environment_path: '/root/review-app/environments/7',
stop_path: '/root/review-app/environments/7/stop',
created_at: '2017-01-31T10:53:46.894Z',
@@ -22,7 +22,7 @@ export const environmentsList = [
external_url: null,
environment_type: 'build',
last_deployment: null,
- 'stop_action?': false,
+ has_stop_action: false,
environment_path: '/root/review-app/environments/12',
stop_path: '/root/review-app/environments/12/stop',
created_at: '2017-02-01T19:42:18.400Z',
@@ -41,7 +41,7 @@ export const serverData = [
external_url: null,
environment_type: null,
last_deployment: null,
- 'stop_action?': false,
+ has_stop_action: false,
environment_path: '/root/review-app/environments/7',
stop_path: '/root/review-app/environments/7/stop',
created_at: '2017-01-31T10:53:46.894Z',
@@ -58,7 +58,7 @@ export const serverData = [
external_url: null,
environment_type: 'build',
last_deployment: null,
- 'stop_action?': false,
+ has_stop_action: false,
environment_path: '/root/review-app/environments/12',
stop_path: '/root/review-app/environments/12/stop',
created_at: '2017-02-01T19:42:18.400Z',
@@ -77,7 +77,7 @@ export const environment = {
external_url: null,
environment_type: null,
last_deployment: null,
- 'stop_action?': false,
+ has_stop_action: false,
environment_path: '/root/review-app/environments/7',
stop_path: '/root/review-app/environments/7/stop',
created_at: '2017-01-31T10:53:46.894Z',
@@ -95,7 +95,7 @@ export const folder = {
external_url: null,
environment_type: 'build',
last_deployment: null,
- 'stop_action?': false,
+ has_stop_action: false,
environment_path: '/root/review-app/environments/12',
stop_path: '/root/review-app/environments/12/stop',
created_at: '2017-02-01T19:42:18.400Z',
diff --git a/spec/javascripts/vue_shared/components/memory_graph_spec.js b/spec/javascripts/vue_shared/components/memory_graph_spec.js
index 73a69df019e..65d8ed39ade 100644
--- a/spec/javascripts/vue_shared/components/memory_graph_spec.js
+++ b/spec/javascripts/vue_shared/components/memory_graph_spec.js
@@ -113,7 +113,7 @@ describe('MemoryGraph', () => {
const circleEl = el.querySelector('circle');
expect(circleEl).toBeDefined();
expect(circleEl.getAttribute('r')).toBe('1.5');
- expect(circleEl.getAttribute('tranform')).toBe('translate(0 -1)');
+ expect(circleEl.getAttribute('transform')).toBe('translate(0 -1)');
expect(circleEl.getAttribute('cx')).toBe(`${dotX}`);
expect(circleEl.getAttribute('cy')).toBe(`${dotY}`);
done();
diff --git a/spec/lib/gitlab/closing_issue_extractor_spec.rb b/spec/lib/gitlab/closing_issue_extractor_spec.rb
index 3a0688b7cbb..1f35d1e4880 100644
--- a/spec/lib/gitlab/closing_issue_extractor_spec.rb
+++ b/spec/lib/gitlab/closing_issue_extractor_spec.rb
@@ -379,6 +379,20 @@ describe Gitlab::ClosingIssueExtractor do
.to match_array([issue, other_issue, third_issue])
end
+ it 'allows non-comma-separated issue numbers in single line message' do
+ message = "Closes #{reference} #{reference2} #{reference3}"
+
+ expect(subject.closed_by_message(message))
+ .to match_array([issue, other_issue, third_issue])
+ end
+
+ it 'allows mixed comma-separated and non-comma-separated issue numbers in single line message' do
+ message = "Closes #{reference}, #{reference2} and #{reference3}"
+
+ expect(subject.closed_by_message(message))
+ .to match_array([issue, other_issue, third_issue])
+ end
+
it 'fetches issues in multi-line message' do
message = "Awesome commit (closes #{reference})\nAlso fixes #{reference2}"
diff --git a/spec/lib/additional_email_headers_interceptor_spec.rb b/spec/lib/gitlab/email/hook/additional_headers_interceptor_spec.rb
index b5c1a360ba9..ae61ece8029 100644
--- a/spec/lib/additional_email_headers_interceptor_spec.rb
+++ b/spec/lib/gitlab/email/hook/additional_headers_interceptor_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe AdditionalEmailHeadersInterceptor do
+describe Gitlab::Email::Hook::AdditionalHeadersInterceptor do
let(:mail) do
ActionMailer::Base.mail(to: 'test@mail.com', from: 'info@mail.com', body: 'hello')
end
diff --git a/spec/lib/gitlab/email/hook/delivery_metrics_observer_spec.rb b/spec/lib/gitlab/email/hook/delivery_metrics_observer_spec.rb
new file mode 100644
index 00000000000..4497d4002da
--- /dev/null
+++ b/spec/lib/gitlab/email/hook/delivery_metrics_observer_spec.rb
@@ -0,0 +1,35 @@
+require 'spec_helper'
+
+describe Gitlab::Email::Hook::DeliveryMetricsObserver do
+ let(:email) do
+ ActionMailer::Base.mail(to: 'test@example.com',
+ from: 'info@example.com',
+ body: 'hello')
+ end
+
+ context 'when email has been delivered' do
+ it 'increments both email delivery metrics' do
+ expect(described_class.delivery_attempts_counter).to receive(:increment)
+ expect(described_class.delivered_emails_counter).to receive(:increment)
+
+ email.deliver_now
+ end
+ end
+
+ context 'when email has not been delivered due to an error' do
+ before do
+ allow(email.delivery_method).to receive(:deliver!)
+ .and_raise(StandardError, 'Some SMTP error')
+ end
+
+ it 'increments only delivery attempt metric' do
+ expect(described_class.delivery_attempts_counter)
+ .to receive(:increment)
+ expect(described_class.delivered_emails_counter)
+ .not_to receive(:increment)
+
+ expect { email.deliver_now }
+ .to raise_error(StandardError, 'Some SMTP error')
+ end
+ end
+end
diff --git a/spec/lib/disable_email_interceptor_spec.rb b/spec/lib/gitlab/email/hook/disable_email_interceptor_spec.rb
index 3652d928c43..91aa3bc7c2e 100644
--- a/spec/lib/disable_email_interceptor_spec.rb
+++ b/spec/lib/gitlab/email/hook/disable_email_interceptor_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe DisableEmailInterceptor do
+describe Gitlab::Email::Hook::DisableEmailInterceptor do
before do
Mail.register_interceptor(described_class)
end
diff --git a/spec/lib/gitlab/git/diff_spec.rb b/spec/lib/gitlab/git/diff_spec.rb
index 7c3d2af819b..11ab376ab8f 100644
--- a/spec/lib/gitlab/git/diff_spec.rb
+++ b/spec/lib/gitlab/git/diff_spec.rb
@@ -269,7 +269,7 @@ EOT
before do
# TODO use a Gitaly diff object instead
rugged_commit = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- repository.lookup('5937ac0a7beb003549fc5fd26fc247adbce4a52e')
+ repository.rugged.rev_parse('5937ac0a7beb003549fc5fd26fc247adbce4a52e')
end
@diffs = rugged_commit.parents[0].diff(rugged_commit).patches
diff --git a/spec/lib/gitlab/git/index_spec.rb b/spec/lib/gitlab/git/index_spec.rb
index 16e6bd35449..e51b875be11 100644
--- a/spec/lib/gitlab/git/index_spec.rb
+++ b/spec/lib/gitlab/git/index_spec.rb
@@ -5,7 +5,7 @@ describe Gitlab::Git::Index, seed_helper: true do
let(:index) { described_class.new(repository) }
before do
- index.read_tree(repository.lookup('master').tree)
+ index.read_tree(lookup('master').tree)
end
around do |example|
@@ -30,7 +30,7 @@ describe Gitlab::Git::Index, seed_helper: true do
entry = index.get(options[:file_path])
expect(entry).not_to be_nil
- expect(repository.lookup(entry[:oid]).content).to eq(options[:content])
+ expect(lookup(entry[:oid]).content).to eq(options[:content])
end
end
@@ -54,7 +54,7 @@ describe Gitlab::Git::Index, seed_helper: true do
index.create(options)
entry = index.get(options[:file_path])
- expect(repository.lookup(entry[:oid]).content).to eq(Base64.decode64(options[:content]))
+ expect(lookup(entry[:oid]).content).to eq(Base64.decode64(options[:content]))
end
end
@@ -68,7 +68,7 @@ describe Gitlab::Git::Index, seed_helper: true do
index.create(options)
entry = index.get(options[:file_path])
- expect(repository.lookup(entry[:oid]).content).to eq("Hello,\nWorld")
+ expect(lookup(entry[:oid]).content).to eq("Hello,\nWorld")
end
end
end
@@ -135,7 +135,7 @@ describe Gitlab::Git::Index, seed_helper: true do
entry = index.get(options[:file_path])
- expect(repository.lookup(entry[:oid]).content).to eq(options[:content])
+ expect(lookup(entry[:oid]).content).to eq(options[:content])
end
it 'preserves file mode' do
@@ -190,7 +190,7 @@ describe Gitlab::Git::Index, seed_helper: true do
entry = index.get(options[:file_path])
expect(entry).not_to be_nil
- expect(repository.lookup(entry[:oid]).content).to eq(options[:content])
+ expect(lookup(entry[:oid]).content).to eq(options[:content])
end
it 'preserves file mode' do
@@ -232,4 +232,8 @@ describe Gitlab::Git::Index, seed_helper: true do
end
end
end
+
+ def lookup(revision)
+ repository.rugged.rev_parse(revision)
+ end
end
diff --git a/spec/lib/gitlab/git/repository_spec.rb b/spec/lib/gitlab/git/repository_spec.rb
index 64b08dd9c4b..6480f6c407d 100644
--- a/spec/lib/gitlab/git/repository_spec.rb
+++ b/spec/lib/gitlab/git/repository_spec.rb
@@ -321,90 +321,6 @@ describe Gitlab::Git::Repository, seed_helper: true do
end
end
- context '#submodules' do
- around do |example|
- # TODO #submodules will be removed, has been migrated to gitaly
- Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- example.run
- end
- end
-
- let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '') }
-
- context 'where repo has submodules' do
- let(:submodules) { repository.send(:submodules, 'master') }
- let(:submodule) { submodules.first }
-
- it { expect(submodules).to be_kind_of Hash }
- it { expect(submodules.empty?).to be_falsey }
-
- it 'should have valid data' do
- expect(submodule).to eq([
- "six", {
- "id" => "409f37c4f05865e4fb208c771485f211a22c4c2d",
- "name" => "six",
- "url" => "git://github.com/randx/six.git"
- }
- ])
- end
-
- it 'should handle nested submodules correctly' do
- nested = submodules['nested/six']
- expect(nested['name']).to eq('nested/six')
- expect(nested['url']).to eq('git://github.com/randx/six.git')
- expect(nested['id']).to eq('24fb71c79fcabc63dfd8832b12ee3bf2bf06b196')
- end
-
- it 'should handle deeply nested submodules correctly' do
- nested = submodules['deeper/nested/six']
- expect(nested['name']).to eq('deeper/nested/six')
- expect(nested['url']).to eq('git://github.com/randx/six.git')
- expect(nested['id']).to eq('24fb71c79fcabc63dfd8832b12ee3bf2bf06b196')
- end
-
- it 'should not have an entry for an invalid submodule' do
- expect(submodules).not_to have_key('invalid/path')
- end
-
- it 'should not have an entry for an uncommited submodule dir' do
- submodules = repository.send(:submodules, 'fix-existing-submodule-dir')
- expect(submodules).not_to have_key('submodule-existing-dir')
- end
-
- it 'should handle tags correctly' do
- submodules = repository.send(:submodules, 'v1.2.1')
-
- expect(submodules.first).to eq([
- "six", {
- "id" => "409f37c4f05865e4fb208c771485f211a22c4c2d",
- "name" => "six",
- "url" => "git://github.com/randx/six.git"
- }
- ])
- end
-
- it 'should not break on invalid syntax' do
- allow(repository).to receive(:blob_content).and_return(<<-GITMODULES.strip_heredoc)
- [submodule "six"]
- path = six
- url = git://github.com/randx/six.git
-
- [submodule]
- foo = bar
- GITMODULES
-
- expect(submodules).to have_key('six')
- end
- end
-
- context 'where repo doesn\'t have submodules' do
- let(:submodules) { repository.send(:submodules, '6d39438') }
- it 'should return an empty hash' do
- expect(submodules).to be_empty
- end
- end
- end
-
describe '#commit_count' do
shared_examples 'simple commit counting' do
it { expect(repository.commit_count("master")).to eq(25) }
diff --git a/spec/lib/gitlab/hook_data/base_builder_spec.rb b/spec/lib/gitlab/hook_data/base_builder_spec.rb
new file mode 100644
index 00000000000..a921dd766c3
--- /dev/null
+++ b/spec/lib/gitlab/hook_data/base_builder_spec.rb
@@ -0,0 +1,64 @@
+require 'spec_helper'
+
+describe Gitlab::HookData::BaseBuilder do
+ describe '#absolute_image_urls' do
+ let(:subclass) do
+ Class.new(described_class) do
+ public :absolute_image_urls
+ end
+ end
+
+ subject { subclass.new(nil) }
+
+ using RSpec::Parameterized::TableSyntax
+
+ where do
+ {
+ 'relative image URL' => {
+ input: '![an image](foo.png)',
+ output: "![an image](#{Gitlab.config.gitlab.url}/foo.png)"
+ },
+ 'HTTP URL' => {
+ input: '![an image](http://example.com/foo.png)',
+ output: '![an image](http://example.com/foo.png)'
+ },
+ 'HTTPS URL' => {
+ input: '![an image](https://example.com/foo.png)',
+ output: '![an image](https://example.com/foo.png)'
+ },
+ 'protocol-relative URL' => {
+ input: '![an image](//example.com/foo.png)',
+ output: '![an image](//example.com/foo.png)'
+ },
+ 'URL reference by title' => {
+ input: "![foo]\n\n[foo]: foo.png",
+ output: "![foo]\n\n[foo]: foo.png"
+ },
+ 'URL reference by label' => {
+ input: "![][foo]\n\n[foo]: foo.png",
+ output: "![][foo]\n\n[foo]: foo.png"
+ },
+ 'in Markdown inline code block' => {
+ input: '`![an image](foo.png)`',
+ output: "`![an image](#{Gitlab.config.gitlab.url}/foo.png)`"
+ },
+ 'in HTML tag on the same line' => {
+ input: '<p>![an image](foo.png)</p>',
+ output: "<p>![an image](#{Gitlab.config.gitlab.url}/foo.png)</p>"
+ },
+ 'in Markdown multi-line code block' => {
+ input: "```\n![an image](foo.png)\n```",
+ output: "```\n![an image](foo.png)\n```"
+ },
+ 'in HTML tag on different lines' => {
+ input: "<p>\n![an image](foo.png)\n</p>",
+ output: "<p>\n![an image](foo.png)\n</p>"
+ }
+ }
+ end
+
+ with_them do
+ it { expect(subject.absolute_image_urls(input)).to eq(output) }
+ end
+ end
+end
diff --git a/spec/lib/gitlab/hook_data/issue_builder_spec.rb b/spec/lib/gitlab/hook_data/issue_builder_spec.rb
index 506b2c0be20..60093474f8a 100644
--- a/spec/lib/gitlab/hook_data/issue_builder_spec.rb
+++ b/spec/lib/gitlab/hook_data/issue_builder_spec.rb
@@ -40,5 +40,14 @@ describe Gitlab::HookData::IssueBuilder do
expect(data).to include(:human_total_time_spent)
expect(data).to include(:assignee_ids)
end
+
+ context 'when the issue has an image in the description' do
+ let(:issue_with_description) { create(:issue, description: 'test![Issue_Image](/uploads/abc/Issue_Image.png)') }
+ let(:builder) { described_class.new(issue_with_description) }
+
+ it 'sets the image to use an absolute URL' do
+ expect(data[:description]).to eq("test![Issue_Image](#{Settings.gitlab.url}/uploads/abc/Issue_Image.png)")
+ end
+ end
end
end
diff --git a/spec/lib/gitlab/hook_data/merge_request_builder_spec.rb b/spec/lib/gitlab/hook_data/merge_request_builder_spec.rb
index b61614e4790..dd586af6118 100644
--- a/spec/lib/gitlab/hook_data/merge_request_builder_spec.rb
+++ b/spec/lib/gitlab/hook_data/merge_request_builder_spec.rb
@@ -56,5 +56,14 @@ describe Gitlab::HookData::MergeRequestBuilder do
expect(data).to include(:human_time_estimate)
expect(data).to include(:human_total_time_spent)
end
+
+ context 'when the MR has an image in the description' do
+ let(:mr_with_description) { create(:merge_request, description: 'test![Issue_Image](/uploads/abc/Issue_Image.png)') }
+ let(:builder) { described_class.new(mr_with_description) }
+
+ it 'sets the image to use an absolute URL' do
+ expect(data[:description]).to eq("test![Issue_Image](#{Settings.gitlab.url}/uploads/abc/Issue_Image.png)")
+ end
+ end
end
end
diff --git a/spec/lib/gitlab/import_export/avatar_saver_spec.rb b/spec/lib/gitlab/import_export/avatar_saver_spec.rb
index 2223f163177..90e6d653d34 100644
--- a/spec/lib/gitlab/import_export/avatar_saver_spec.rb
+++ b/spec/lib/gitlab/import_export/avatar_saver_spec.rb
@@ -9,6 +9,7 @@ describe Gitlab::ImportExport::AvatarSaver do
before do
FileUtils.mkdir_p("#{shared.export_path}/avatar/")
allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path)
+ stub_feature_flags(import_export_object_storage: false)
end
after do
diff --git a/spec/lib/gitlab/import_export/uploads_manager_spec.rb b/spec/lib/gitlab/import_export/uploads_manager_spec.rb
new file mode 100644
index 00000000000..9c3870a0af8
--- /dev/null
+++ b/spec/lib/gitlab/import_export/uploads_manager_spec.rb
@@ -0,0 +1,80 @@
+require 'spec_helper'
+
+describe Gitlab::ImportExport::UploadsManager do
+ let(:shared) { project.import_export_shared }
+ let(:export_path) { "#{Dir.tmpdir}/project_tree_saver_spec" }
+ let(:project) { create(:project) }
+ let(:exported_file_path) { "#{shared.export_path}/uploads/#{upload.secret}/#{File.basename(upload.path)}" }
+
+ subject(:manager) { described_class.new(project: project, shared: shared) }
+
+ before do
+ allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path)
+ FileUtils.mkdir_p(shared.export_path)
+ end
+
+ after do
+ FileUtils.rm_rf(shared.export_path)
+ end
+
+ describe '#save' do
+ context 'when the project has uploads locally stored' do
+ let(:upload) { create(:upload, :issuable_upload, :with_file, model: project) }
+
+ before do
+ project.uploads << upload
+ end
+
+ it 'does not cause errors' do
+ manager.save
+
+ expect(shared.errors).to be_empty
+ end
+
+ it 'copies the file in the correct location when there is an upload' do
+ manager.save
+
+ expect(File).to exist(exported_file_path)
+ end
+ end
+
+ context 'using object storage' do
+ let!(:upload) { create(:upload, :issuable_upload, :object_storage, model: project) }
+
+ before do
+ stub_feature_flags(import_export_object_storage: true)
+ stub_uploads_object_storage(FileUploader)
+ end
+
+ it 'saves the file' do
+ fake_uri = double
+
+ expect(fake_uri).to receive(:open).and_return(StringIO.new('File content'))
+ expect(URI).to receive(:parse).and_return(fake_uri)
+
+ manager.save
+
+ expect(File.read(exported_file_path)).to eq('File content')
+ end
+ end
+
+ describe '#restore' do
+ context 'using object storage' do
+ before do
+ stub_feature_flags(import_export_object_storage: true)
+ stub_uploads_object_storage(FileUploader)
+
+ FileUtils.mkdir_p(File.join(shared.export_path, 'uploads/72a497a02fe3ee09edae2ed06d390038'))
+ FileUtils.touch(File.join(shared.export_path, 'uploads/72a497a02fe3ee09edae2ed06d390038', "dummy.txt"))
+ end
+
+ it 'restores the file' do
+ manager.restore
+
+ expect(project.uploads.size).to eq(1)
+ expect(project.uploads.first.build_uploader.filename).to eq('dummy.txt')
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/import_export/uploads_saver_spec.rb b/spec/lib/gitlab/import_export/uploads_saver_spec.rb
index 095687fa89d..c716edd9397 100644
--- a/spec/lib/gitlab/import_export/uploads_saver_spec.rb
+++ b/spec/lib/gitlab/import_export/uploads_saver_spec.rb
@@ -7,6 +7,7 @@ describe Gitlab::ImportExport::UploadsSaver do
let(:shared) { project.import_export_shared }
before do
+ stub_feature_flags(import_export_object_storage: false)
allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path)
end
@@ -30,7 +31,7 @@ describe Gitlab::ImportExport::UploadsSaver do
it 'copies the uploads to the export path' do
saver.save
- uploads = Dir.glob(File.join(saver.uploads_export_path, '**/*')).map { |file| File.basename(file) }
+ uploads = Dir.glob(File.join(shared.export_path, 'uploads/**/*')).map { |file| File.basename(file) }
expect(uploads).to include('banana_sample.gif')
end
@@ -52,7 +53,7 @@ describe Gitlab::ImportExport::UploadsSaver do
it 'copies the uploads to the export path' do
saver.save
- uploads = Dir.glob(File.join(saver.uploads_export_path, '**/*')).map { |file| File.basename(file) }
+ uploads = Dir.glob(File.join(shared.export_path, 'uploads/**/*')).map { |file| File.basename(file) }
expect(uploads).to include('banana_sample.gif')
end
diff --git a/spec/mailers/notify_spec.rb b/spec/mailers/notify_spec.rb
index 581132b6672..ff1a5aa2536 100644
--- a/spec/mailers/notify_spec.rb
+++ b/spec/mailers/notify_spec.rb
@@ -1366,7 +1366,8 @@ describe Notify do
it 'only sends the text template' do
stub_application_setting(html_emails_enabled: false)
- EmailTemplateInterceptor.delivering_email(multipart_mail)
+ Gitlab::Email::Hook::EmailTemplateInterceptor
+ .delivering_email(multipart_mail)
expect(multipart_mail).to have_part_with('text/plain')
expect(multipart_mail).not_to have_part_with('text/html')
@@ -1377,7 +1378,8 @@ describe Notify do
it 'sends a multipart message' do
stub_application_setting(html_emails_enabled: true)
- EmailTemplateInterceptor.delivering_email(multipart_mail)
+ Gitlab::Email::Hook::EmailTemplateInterceptor
+ .delivering_email(multipart_mail)
expect(multipart_mail).to have_part_with('text/plain')
expect(multipart_mail).to have_part_with('text/html')
diff --git a/spec/models/deployment_spec.rb b/spec/models/deployment_spec.rb
index e01906f4b6c..b335e0fbeb3 100644
--- a/spec/models/deployment_spec.rb
+++ b/spec/models/deployment_spec.rb
@@ -157,22 +157,4 @@ describe Deployment do
end
end
end
-
- describe '#stop_action?' do
- subject { deployment.stop_action? }
-
- context 'when no other actions' do
- let(:deployment) { build(:deployment) }
-
- it { is_expected.to be_falsey }
- end
-
- context 'when matching action is defined' do
- let(:build) { create(:ci_build) }
- let(:deployment) { FactoryBot.build(:deployment, deployable: build, on_stop: 'close_app') }
- let!(:close_action) { create(:ci_build, :manual, pipeline: build.pipeline, name: 'close_app') }
-
- it { is_expected.to be_truthy }
- end
- end
end
diff --git a/spec/models/environment_spec.rb b/spec/models/environment_spec.rb
index 4bded9efe91..c65e0b81451 100644
--- a/spec/models/environment_spec.rb
+++ b/spec/models/environment_spec.rb
@@ -170,8 +170,8 @@ describe Environment do
end
end
- describe '#stop_action?' do
- subject { environment.stop_action? }
+ describe '#stop_action_available?' do
+ subject { environment.stop_action_available? }
context 'when no other actions' do
it { is_expected.to be_falsey }
@@ -179,8 +179,17 @@ describe Environment do
context 'when matching action is defined' do
let(:build) { create(:ci_build) }
- let!(:deployment) { create(:deployment, environment: environment, deployable: build, on_stop: 'close_app') }
- let!(:close_action) { create(:ci_build, :manual, pipeline: build.pipeline, name: 'close_app') }
+
+ let!(:deployment) do
+ create(:deployment, environment: environment,
+ deployable: build,
+ on_stop: 'close_app')
+ end
+
+ let!(:close_action) do
+ create(:ci_build, :manual, pipeline: build.pipeline,
+ name: 'close_app')
+ end
context 'when environment is available' do
before do
diff --git a/spec/models/project_wiki_spec.rb b/spec/models/project_wiki_spec.rb
index a544940800a..528f5b610d7 100644
--- a/spec/models/project_wiki_spec.rb
+++ b/spec/models/project_wiki_spec.rb
@@ -189,6 +189,22 @@ describe ProjectWiki do
end
end
+ describe '#find_sidebar' do
+ before do
+ create_page(described_class::SIDEBAR, 'This is an awesome Sidebar')
+ end
+
+ after do
+ subject.pages.each { |page| destroy_page(page.page) }
+ end
+
+ it 'finds the page defined as _sidebar' do
+ page = subject.find_page('_sidebar')
+
+ expect(page.content).to eq('This is an awesome Sidebar')
+ end
+ end
+
describe '#find_file' do
shared_examples 'finding a wiki file' do
let(:image) { File.open(Rails.root.join('spec', 'fixtures', 'big-image.png')) }
diff --git a/spec/models/wiki_page_spec.rb b/spec/models/wiki_page_spec.rb
index 1c765ceac2f..63850939be1 100644
--- a/spec/models/wiki_page_spec.rb
+++ b/spec/models/wiki_page_spec.rb
@@ -554,6 +554,16 @@ describe WikiPage do
end
end
+ describe '#hook_attrs' do
+ it 'adds absolute urls for images in the content' do
+ create_page("test page", "test![WikiPage_Image](/uploads/abc/WikiPage_Image.png)")
+ page = wiki.wiki.page(title: "test page")
+ wiki_page = described_class.new(wiki, page, true)
+
+ expect(wiki_page.hook_attrs['content']).to eq("test![WikiPage_Image](#{Settings.gitlab.url}/uploads/abc/WikiPage_Image.png)")
+ end
+ end
+
private
def remove_temp_repo(path)
diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb
index 8389cb7cf9c..f72c01561d8 100644
--- a/spec/requests/api/projects_spec.rb
+++ b/spec/requests/api/projects_spec.rb
@@ -237,6 +237,39 @@ describe API::Projects do
end
end
+ context 'and using archived' do
+ let!(:archived_project) { create(:project, creator_id: user.id, namespace: user.namespace, archived: true) }
+
+ it 'returns archived projects' do
+ get api('/projects?archived=true', user)
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response).to include_pagination_headers
+ expect(json_response).to be_an Array
+ expect(json_response.length).to eq(Project.public_or_visible_to_user(user).where(archived: true).size)
+ expect(json_response.map { |project| project['id'] }).to include(archived_project.id)
+ end
+
+ it 'returns non-archived projects' do
+ get api('/projects?archived=false', user)
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response).to include_pagination_headers
+ expect(json_response).to be_an Array
+ expect(json_response.length).to eq(Project.public_or_visible_to_user(user).where(archived: false).size)
+ expect(json_response.map { |project| project['id'] }).not_to include(archived_project.id)
+ end
+
+ it 'returns every project' do
+ get api('/projects', user)
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response).to include_pagination_headers
+ expect(json_response).to be_an Array
+ expect(json_response.map { |project| project['id'] }).to contain_exactly(*Project.public_or_visible_to_user(user).pluck(:id))
+ end
+ end
+
context 'and using search' do
it_behaves_like 'projects response' do
let(:filter) { { search: project.name } }
diff --git a/spec/uploaders/file_uploader_spec.rb b/spec/uploaders/file_uploader_spec.rb
index 7ba28b4fc1f..3efe512a59c 100644
--- a/spec/uploaders/file_uploader_spec.rb
+++ b/spec/uploaders/file_uploader_spec.rb
@@ -124,6 +124,15 @@ describe FileUploader do
end
end
+ describe '.extract_dynamic_path' do
+ it 'works with hashed storage' do
+ path = 'export/4b227777d4dd1fc61c6f884f48641d02b4d121d3fd328cb08b5531fcacdabf8a/test/uploads/72a497a02fe3ee09edae2ed06d390038/dummy.txt'
+
+ expect(described_class.extract_dynamic_path(path)[:identifier]).to eq('dummy.txt')
+ expect(described_class.extract_dynamic_path(path)[:secret]).to eq('72a497a02fe3ee09edae2ed06d390038')
+ end
+ end
+
describe '#secret' do
it 'generates a secret if none is provided' do
expect(described_class).to receive(:generate_secret).and_return('secret')