summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md12
-rw-r--r--CONTRIBUTING.md6
-rw-r--r--Gemfile5
-rw-r--r--Gemfile.lock8
-rw-r--r--Gemfile.rails5.lock4
-rw-r--r--app/assets/javascripts/diffs/components/diff_line_gutter_content.vue19
-rw-r--r--app/assets/javascripts/diffs/components/diff_table_cell.vue6
-rw-r--r--app/assets/javascripts/diffs/components/inline_diff_comment_row.vue11
-rw-r--r--app/assets/javascripts/diffs/components/inline_diff_table_row.vue7
-rw-r--r--app/assets/javascripts/diffs/components/inline_diff_view.vue23
-rw-r--r--app/assets/javascripts/diffs/components/parallel_diff_comment_row.vue53
-rw-r--r--app/assets/javascripts/diffs/components/parallel_diff_table_row.vue12
-rw-r--r--app/assets/javascripts/diffs/components/parallel_diff_view.vue38
-rw-r--r--app/assets/javascripts/diffs/store/getters.js63
-rw-r--r--app/assets/javascripts/gpg_badges.js31
-rw-r--r--app/assets/javascripts/lib/utils/common_utils.js20
-rw-r--r--app/assets/javascripts/lib/utils/poll.js11
-rw-r--r--app/assets/javascripts/notes/stores/mutations.js19
-rw-r--r--app/assets/javascripts/notes/stores/utils.js9
-rw-r--r--app/assets/javascripts/pages/projects/blob/show/index.js3
-rw-r--r--app/assets/javascripts/pages/projects/show/index.js3
-rw-r--r--app/assets/javascripts/pages/projects/tree/show/index.js6
-rw-r--r--app/assets/javascripts/projects/tree/components/commit_pipeline_status_component.vue162
-rw-r--r--app/assets/javascripts/reports/store/actions.js67
-rw-r--r--app/assets/javascripts/reports/store/index.js13
-rw-r--r--app/assets/javascripts/reports/store/mutation_types.js5
-rw-r--r--app/assets/javascripts/reports/store/mutations.js26
-rw-r--r--app/assets/javascripts/reports/store/state.js28
-rw-r--r--app/assets/javascripts/vue_shared/components/clipboard_button.vue13
-rw-r--r--app/assets/javascripts/vue_shared/components/stacked_progress_bar.vue3
-rw-r--r--app/assets/stylesheets/framework/buttons.scss4
-rw-r--r--app/assets/stylesheets/framework/stacked_progress_bar.scss2
-rw-r--r--app/assets/stylesheets/pages/commits.scss6
-rw-r--r--app/controllers/concerns/lfs_request.rb19
-rw-r--r--app/finders/groups_finder.rb16
-rw-r--r--app/finders/personal_projects_finder.rb19
-rw-r--r--app/finders/projects_finder.rb17
-rw-r--r--app/helpers/button_helper.rb2
-rw-r--r--app/helpers/ci_status_helper.rb13
-rw-r--r--app/helpers/commits_helper.rb9
-rw-r--r--app/helpers/visibility_level_helper.rb5
-rw-r--r--app/models/deploy_token.rb6
-rw-r--r--app/models/project.rb2
-rw-r--r--app/models/project_services/hangouts_chat_service.rb67
-rw-r--r--app/models/service.rb1
-rw-r--r--app/serializers/analytics_build_entity.rb2
-rw-r--r--app/serializers/analytics_build_serializer.rb2
-rw-r--r--app/serializers/analytics_commit_entity.rb2
-rw-r--r--app/serializers/analytics_commit_serializer.rb2
-rw-r--r--app/serializers/analytics_generic_serializer.rb2
-rw-r--r--app/serializers/analytics_issue_entity.rb2
-rw-r--r--app/serializers/analytics_issue_serializer.rb2
-rw-r--r--app/serializers/analytics_merge_request_entity.rb2
-rw-r--r--app/serializers/analytics_merge_request_serializer.rb2
-rw-r--r--app/serializers/analytics_stage_entity.rb2
-rw-r--r--app/serializers/analytics_stage_serializer.rb2
-rw-r--r--app/serializers/analytics_summary_entity.rb2
-rw-r--r--app/serializers/analytics_summary_serializer.rb2
-rw-r--r--app/serializers/award_emoji_entity.rb2
-rw-r--r--app/serializers/base_serializer.rb2
-rw-r--r--app/serializers/blob_entity.rb2
-rw-r--r--app/serializers/build_action_entity.rb2
-rw-r--r--app/serializers/build_artifact_entity.rb2
-rw-r--r--app/serializers/build_details_entity.rb2
-rw-r--r--app/serializers/build_metadata_entity.rb2
-rw-r--r--app/serializers/build_serializer.rb2
-rw-r--r--app/serializers/cluster_application_entity.rb2
-rw-r--r--app/serializers/cluster_entity.rb2
-rw-r--r--app/serializers/cluster_serializer.rb2
-rw-r--r--app/serializers/cohort_activity_month_entity.rb2
-rw-r--r--app/serializers/cohort_entity.rb2
-rw-r--r--app/serializers/cohorts_entity.rb2
-rw-r--r--app/serializers/cohorts_serializer.rb2
-rw-r--r--app/serializers/commit_entity.rb2
-rw-r--r--app/serializers/concerns/with_pagination.rb2
-rw-r--r--app/serializers/container_repositories_serializer.rb2
-rw-r--r--app/serializers/container_repository_entity.rb2
-rw-r--r--app/serializers/container_tag_entity.rb2
-rw-r--r--app/serializers/container_tags_serializer.rb2
-rw-r--r--app/serializers/deploy_key_entity.rb2
-rw-r--r--app/serializers/deploy_key_serializer.rb2
-rw-r--r--app/serializers/deploy_keys_project_entity.rb2
-rw-r--r--app/serializers/deployment_entity.rb2
-rw-r--r--app/serializers/deployment_serializer.rb2
-rw-r--r--app/serializers/diff_file_entity.rb2
-rw-r--r--app/serializers/diffs_entity.rb2
-rw-r--r--app/serializers/diffs_serializer.rb2
-rw-r--r--app/serializers/discussion_entity.rb2
-rw-r--r--app/serializers/discussion_serializer.rb2
-rw-r--r--app/serializers/entity_date_helper.rb23
-rw-r--r--app/serializers/entity_request.rb2
-rw-r--r--app/serializers/environment_entity.rb2
-rw-r--r--app/serializers/environment_serializer.rb2
-rw-r--r--app/serializers/group_child_entity.rb2
-rw-r--r--app/serializers/group_child_serializer.rb2
-rw-r--r--app/serializers/group_entity.rb2
-rw-r--r--app/serializers/group_serializer.rb2
-rw-r--r--app/serializers/group_variable_entity.rb2
-rw-r--r--app/serializers/group_variable_serializer.rb2
-rw-r--r--app/serializers/issuable_entity.rb2
-rw-r--r--app/serializers/issuable_sidebar_entity.rb2
-rw-r--r--app/serializers/issue_entity.rb2
-rw-r--r--app/serializers/issue_serializer.rb2
-rw-r--r--app/serializers/issue_sidebar_entity.rb2
-rw-r--r--app/serializers/job_entity.rb2
-rw-r--r--app/serializers/job_group_entity.rb2
-rw-r--r--app/serializers/label_entity.rb2
-rw-r--r--app/serializers/label_serializer.rb2
-rw-r--r--app/serializers/lfs_file_lock_entity.rb2
-rw-r--r--app/serializers/lfs_file_lock_serializer.rb2
-rw-r--r--app/serializers/merge_request_basic_entity.rb2
-rw-r--r--app/serializers/merge_request_basic_serializer.rb2
-rw-r--r--app/serializers/merge_request_create_entity.rb2
-rw-r--r--app/serializers/merge_request_create_serializer.rb2
-rw-r--r--app/serializers/merge_request_diff_entity.rb2
-rw-r--r--app/serializers/merge_request_metrics_entity.rb2
-rw-r--r--app/serializers/merge_request_serializer.rb2
-rw-r--r--app/serializers/merge_request_user_entity.rb2
-rw-r--r--app/serializers/merge_request_widget_entity.rb2
-rw-r--r--app/serializers/note_attachment_entity.rb2
-rw-r--r--app/serializers/note_entity.rb2
-rw-r--r--app/serializers/note_user_entity.rb2
-rw-r--r--app/serializers/pipeline_details_entity.rb2
-rw-r--r--app/serializers/pipeline_entity.rb2
-rw-r--r--app/serializers/pipeline_serializer.rb2
-rw-r--r--app/serializers/project_entity.rb2
-rw-r--r--app/serializers/project_mirror_entity.rb2
-rw-r--r--app/serializers/project_note_entity.rb2
-rw-r--r--app/serializers/project_note_serializer.rb2
-rw-r--r--app/serializers/project_serializer.rb2
-rw-r--r--app/serializers/request_aware_entity.rb2
-rw-r--r--app/serializers/runner_entity.rb2
-rw-r--r--app/serializers/stage_entity.rb2
-rw-r--r--app/serializers/stage_serializer.rb2
-rw-r--r--app/serializers/status_entity.rb2
-rw-r--r--app/serializers/submodule_entity.rb2
-rw-r--r--app/serializers/time_trackable_entity.rb2
-rw-r--r--app/serializers/tree_entity.rb2
-rw-r--r--app/serializers/tree_root_entity.rb2
-rw-r--r--app/serializers/tree_serializer.rb2
-rw-r--r--app/serializers/user_entity.rb2
-rw-r--r--app/serializers/user_serializer.rb2
-rw-r--r--app/serializers/variable_entity.rb2
-rw-r--r--app/serializers/variable_serializer.rb2
-rw-r--r--app/services/projects/import_service.rb2
-rw-r--r--app/uploaders/import_export_uploader.rb2
-rw-r--r--app/views/projects/blame/show.html.haml2
-rw-r--r--app/views/projects/blob/show.html.haml2
-rw-r--r--app/views/projects/commit/_commit_box.html.haml2
-rw-r--r--app/views/projects/commits/_commit.html.haml2
-rw-r--r--app/views/projects/pipelines/_info.html.haml2
-rw-r--r--app/views/projects/pipelines/_with_tabs.html.haml3
-rw-r--r--app/views/projects/pipelines/new.html.haml2
-rw-r--r--app/views/projects/services/prometheus/_show.html.haml2
-rw-r--r--app/views/projects/show.html.haml4
-rw-r--r--app/views/projects/tree/show.html.haml3
-rw-r--r--app/views/sherlock/queries/_general.html.haml4
-rw-r--r--app/workers/concerns/each_shard_worker.rb2
-rw-r--r--app/workers/delete_diff_files_worker.rb2
-rw-r--r--app/workers/repository_check/dispatch_worker.rb2
-rw-r--r--changelogs/unreleased/29278-commits-page-tooltips.yml5
-rw-r--r--changelogs/unreleased/45318-vuex-store.yml5
-rw-r--r--changelogs/unreleased/46869-deploy-tokens-failed-to-clone-lfs-repository.yml5
-rw-r--r--changelogs/unreleased/48745-project-exports-fail-when-uploads-have-been-migrated-to-object-storage.yml5
-rw-r--r--changelogs/unreleased/49272-sanitize-git-url-in-import-errors.yml5
-rw-r--r--changelogs/unreleased/api-minimal-access-level.yml5
-rw-r--r--changelogs/unreleased/blackst0ne-replace-sidekiq-inline-with-perform-enqueued-jobs.yml5
-rw-r--r--changelogs/unreleased/fix-filename-for-direct-uploads.yml5
-rw-r--r--changelogs/unreleased/frozen-string-enable-app-serializers.yml5
-rw-r--r--changelogs/unreleased/frozen-string-vestigial.yml5
-rw-r--r--changelogs/unreleased/gitaly-ff-branch-nil.yml5
-rw-r--r--changelogs/unreleased/hangouts_chat_integration.yml5
-rw-r--r--changelogs/unreleased/kp-stacked-progress-bar-decimal-places.yml5
-rw-r--r--changelogs/unreleased/project-visibility-tooltip.yml5
-rw-r--r--changelogs/unreleased/sh-bump-sanitize-4-6-6.yml5
-rw-r--r--changelogs/unreleased/sh-enable-frozen-literals-banzi-filters.yml5
-rw-r--r--changelogs/unreleased/sh-freeze-banzai-filter-strings.yml5
-rw-r--r--changelogs/unreleased/sh-remove-banzai-instrumentation.yml5
-rw-r--r--changelogs/unreleased/tz-mr-refactor-memory-reduction.yml5
-rw-r--r--changelogs/unreleased/winh-tree-view-gpg.yml5
-rw-r--r--config/initializers/8_metrics.rb16
-rw-r--r--doc/administration/monitoring/prometheus/gitlab_metrics.md2
-rw-r--r--doc/administration/pages/index.md45
-rw-r--r--doc/api/groups.md10
-rw-r--r--doc/api/issues.md6
-rw-r--r--doc/api/projects.md9
-rw-r--r--doc/api/services.md48
-rw-r--r--doc/ci/variables/README.md4
-rw-r--r--doc/ci/yaml/README.md38
-rw-r--r--doc/topics/autodevops/index.md2
-rw-r--r--doc/update/mysql_to_postgresql.md6
-rw-r--r--doc/user/profile/personal_access_tokens.md1
-rw-r--r--doc/user/project/integrations/hangouts_chat.md27
-rw-r--r--doc/user/project/integrations/img/hangouts_chat_configuration.pngbin0 -> 101788 bytes
-rw-r--r--doc/user/project/integrations/project_services.md1
-rw-r--r--lib/api/groups.rb3
-rw-r--r--lib/api/helpers.rb1
-rw-r--r--lib/api/projects.rb1
-rw-r--r--lib/api/services.rb9
-rw-r--r--lib/api/users.rb1
-rw-r--r--lib/banzai/filter/absolute_link_filter.rb2
-rw-r--r--lib/banzai/filter/abstract_reference_filter.rb2
-rw-r--r--lib/banzai/filter/ascii_doc_post_processing_filter.rb2
-rw-r--r--lib/banzai/filter/autolink_filter.rb2
-rw-r--r--lib/banzai/filter/blockquote_fence_filter.rb2
-rw-r--r--lib/banzai/filter/color_filter.rb2
-rw-r--r--lib/banzai/filter/commit_range_reference_filter.rb2
-rw-r--r--lib/banzai/filter/commit_reference_filter.rb2
-rw-r--r--lib/banzai/filter/commit_trailers_filter.rb2
-rw-r--r--lib/banzai/filter/emoji_filter.rb2
-rw-r--r--lib/banzai/filter/epic_reference_filter.rb2
-rw-r--r--lib/banzai/filter/external_issue_reference_filter.rb2
-rw-r--r--lib/banzai/filter/external_link_filter.rb2
-rw-r--r--lib/banzai/filter/gollum_tags_filter.rb2
-rw-r--r--lib/banzai/filter/html_entity_filter.rb2
-rw-r--r--lib/banzai/filter/image_lazy_load_filter.rb4
-rw-r--r--lib/banzai/filter/image_link_filter.rb2
-rw-r--r--lib/banzai/filter/inline_diff_filter.rb2
-rw-r--r--lib/banzai/filter/issuable_reference_filter.rb2
-rw-r--r--lib/banzai/filter/issuable_state_filter.rb2
-rw-r--r--lib/banzai/filter/issue_reference_filter.rb2
-rw-r--r--lib/banzai/filter/label_reference_filter.rb2
-rw-r--r--lib/banzai/filter/markdown_filter.rb2
-rw-r--r--lib/banzai/filter/math_filter.rb2
-rw-r--r--lib/banzai/filter/merge_request_reference_filter.rb2
-rw-r--r--lib/banzai/filter/mermaid_filter.rb2
-rw-r--r--lib/banzai/filter/milestone_reference_filter.rb2
-rw-r--r--lib/banzai/filter/plantuml_filter.rb2
-rw-r--r--lib/banzai/filter/redactor_filter.rb2
-rw-r--r--lib/banzai/filter/reference_filter.rb2
-rw-r--r--lib/banzai/filter/relative_link_filter.rb2
-rw-r--r--lib/banzai/filter/sanitization_filter.rb2
-rw-r--r--lib/banzai/filter/set_direction_filter.rb2
-rw-r--r--lib/banzai/filter/snippet_reference_filter.rb2
-rw-r--r--lib/banzai/filter/syntax_highlight_filter.rb4
-rw-r--r--lib/banzai/filter/table_of_contents_filter.rb4
-rw-r--r--lib/banzai/filter/task_list_filter.rb2
-rw-r--r--lib/banzai/filter/user_reference_filter.rb2
-rw-r--r--lib/banzai/filter/video_link_filter.rb2
-rw-r--r--lib/banzai/filter/wiki_link_filter.rb2
-rw-r--r--lib/banzai/filter/yaml_front_matter_filter.rb2
-rw-r--r--lib/feature.rb2
-rw-r--r--lib/gitlab/git/repository.rb12
-rw-r--r--lib/gitlab/import_export/merge_request_parser.rb1
-rw-r--r--locale/gitlab.pot2
-rw-r--r--spec/features/projects/commits/user_browses_commits_spec.rb1
-rw-r--r--spec/features/signed_commits_spec.rb10
-rw-r--r--spec/helpers/button_helper_spec.rb6
-rw-r--r--spec/helpers/visibility_level_helper_spec.rb23
-rw-r--r--spec/javascripts/diffs/components/diff_line_gutter_content_spec.js6
-rw-r--r--spec/javascripts/gpg_badges_spec.js76
-rw-r--r--spec/javascripts/lib/utils/common_utils_spec.js19
-rw-r--r--spec/javascripts/reports/store/actions_spec.js130
-rw-r--r--spec/javascripts/reports/store/mutations_spec.js101
-rw-r--r--spec/javascripts/vue_shared/components/clipboard_button_spec.js2
-rw-r--r--spec/javascripts/vue_shared/components/stacked_progress_bar_spec.js14
-rw-r--r--spec/lib/banzai/filter/image_lazy_load_filter_spec.rb14
-rw-r--r--spec/lib/feature_spec.rb32
-rw-r--r--spec/lib/gitlab/hashed_storage/migrator_spec.rb2
-rw-r--r--spec/lib/gitlab/import_export/all_models.yml1
-rw-r--r--spec/migrations/active_record/schedule_set_confidential_note_events_on_services_spec.rb2
-rw-r--r--spec/migrations/migrate_stage_id_reference_in_background_spec.rb2
-rw-r--r--spec/migrations/migrate_stages_statuses_spec.rb2
-rw-r--r--spec/migrations/normalize_ldap_extern_uids_spec.rb4
-rw-r--r--spec/migrations/schedule_create_gpg_key_subkeys_from_gpg_keys_spec.rb2
-rw-r--r--spec/migrations/schedule_merge_request_diff_migrations_spec.rb2
-rw-r--r--spec/migrations/schedule_merge_request_diff_migrations_take_two_spec.rb2
-rw-r--r--spec/migrations/schedule_merge_request_latest_merge_request_diff_id_migrations_spec.rb2
-rw-r--r--spec/migrations/schedule_set_confidential_note_events_on_webhooks_spec.rb2
-rw-r--r--spec/models/ci/build_trace_chunk_spec.rb4
-rw-r--r--spec/models/deploy_token_spec.rb9
-rw-r--r--spec/models/project_services/hangouts_chat_service_spec.rb246
-rw-r--r--spec/models/project_spec.rb1
-rw-r--r--spec/models/spam_log_spec.rb2
-rw-r--r--spec/requests/api/groups_spec.rb19
-rw-r--r--spec/requests/api/project_import_spec.rb2
-rw-r--r--spec/requests/api/projects_spec.rb30
-rw-r--r--spec/requests/api/users_spec.rb14
-rw-r--r--spec/requests/lfs_http_spec.rb38
-rw-r--r--spec/services/groups/destroy_service_spec.rb2
-rw-r--r--spec/services/projects/create_from_template_service_spec.rb2
-rw-r--r--spec/services/projects/destroy_service_spec.rb14
-rw-r--r--spec/services/projects/housekeeping_service_spec.rb2
-rw-r--r--spec/services/projects/import_service_spec.rb4
-rw-r--r--spec/services/users/destroy_service_spec.rb2
-rw-r--r--spec/workers/storage_migrator_worker_spec.rb2
286 files changed, 1950 insertions, 466 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 76a016b233c..7cc047c40ed 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,18 @@
documentation](doc/development/changelog.md) for instructions on adding your own
entry.
+## 11.1.1 (2018-07-23)
+
+### Fixed (2 changes)
+
+- Add missing Gitaly branch_update nil checks. !20711
+- Fix filename for accelerated uploads.
+
+### Added (1 change)
+
+- Add uploader support to Import/Export uploads. !20484
+
+
## 11.1.0 (2018-07-22)
### Security (6 changes)
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 631f80c6bd9..ad8022e972f 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -376,8 +376,14 @@ on those issues. Please select someone with relevant experience from the
[GitLab team][team]. If there is nobody mentioned with that expertise look in
the commit history for the affected files to find someone.
+We also use [GitLab Triage] to automate some triaging policies. This is
+currently setup as a [scheduled pipeline] running on the [`gl-triage`] branch.
+
[described in our handbook]: https://about.gitlab.com/handbook/engineering/issue-triage/
[issue bash events]: https://gitlab.com/gitlab-org/gitlab-ce/issues/17815
+[GitLab Triage]: https://gitlab.com/gitlab-org/gitlab-triage
+[scheduled pipeline]: https://gitlab.com/gitlab-org/gitlab-ce/pipeline_schedules/3732/edit
+[`gl-triage`]: https://gitlab.com/gitlab-org/gitlab-ce/tree/gl-triage
### Feature proposals
diff --git a/Gemfile b/Gemfile
index 0ad5df90c3c..47815f230d3 100644
--- a/Gemfile
+++ b/Gemfile
@@ -220,6 +220,9 @@ gem 'gemnasium-gitlab-service', '~> 0.2'
# Slack integration
gem 'slack-notifier', '~> 1.5.1'
+# Hangouts Chat integration
+gem 'hangouts-chat', '~> 0.0.5'
+
# Asana integration
gem 'asana', '~> 0.6.0'
@@ -230,7 +233,7 @@ gem 'ruby-fogbugz', '~> 0.2.1'
gem 'kubeclient', '~> 3.1.0'
# Sanitize user input
-gem 'sanitize', '~> 4.6.5'
+gem 'sanitize', '~> 4.6'
gem 'babosa', '~> 1.0.2'
# Sanitizes SVG input
diff --git a/Gemfile.lock b/Gemfile.lock
index 1d9c75f154c..22626c0071b 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -387,6 +387,7 @@ GEM
temple (>= 0.8.0)
thor
tilt
+ hangouts-chat (0.0.5)
hashdiff (0.3.4)
hashie (3.5.7)
hashie-forbidden_attributes (0.1.1)
@@ -513,7 +514,7 @@ GEM
net-ldap (0.16.0)
net-ssh (5.0.1)
netrc (0.11.0)
- nokogiri (1.8.3)
+ nokogiri (1.8.4)
mini_portile2 (~> 2.3.0)
nokogumbo (1.5.0)
nokogiri
@@ -807,7 +808,7 @@ GEM
et-orbi (~> 1.0)
rugged (0.27.2)
safe_yaml (1.0.4)
- sanitize (4.6.5)
+ sanitize (4.6.6)
crass (~> 1.0.2)
nokogiri (>= 1.4.4)
nokogumbo (~> 1.4)
@@ -1062,6 +1063,7 @@ DEPENDENCIES
grpc (~> 1.11.0)
haml_lint (~> 0.26.0)
hamlit (~> 2.8.8)
+ hangouts-chat (~> 0.0.5)
hashie-forbidden_attributes
health_check (~> 2.6.0)
hipchat (~> 1.5.0)
@@ -1155,7 +1157,7 @@ DEPENDENCIES
ruby_parser (~> 3.8)
rufus-scheduler (~> 3.4)
rugged (~> 0.27)
- sanitize (~> 4.6.5)
+ sanitize (~> 4.6)
sass-rails (~> 5.0.6)
scss_lint (~> 0.56.0)
seed-fu (~> 2.3.7)
diff --git a/Gemfile.rails5.lock b/Gemfile.rails5.lock
index 2d12faa28d9..5a0aaf05608 100644
--- a/Gemfile.rails5.lock
+++ b/Gemfile.rails5.lock
@@ -390,6 +390,7 @@ GEM
temple (>= 0.8.0)
thor
tilt
+ hangouts-chat (0.0.5)
hashdiff (0.3.4)
hashie (3.5.7)
hashie-forbidden_attributes (0.1.1)
@@ -1072,6 +1073,7 @@ DEPENDENCIES
grpc (~> 1.11.0)
haml_lint (~> 0.26.0)
hamlit (~> 2.8.8)
+ hangouts-chat (~> 0.0.5)
hashie-forbidden_attributes
health_check (~> 2.6.0)
hipchat (~> 1.5.0)
@@ -1166,7 +1168,7 @@ DEPENDENCIES
ruby_parser (~> 3.8)
rufus-scheduler (~> 3.4)
rugged (~> 0.27)
- sanitize (~> 4.6.5)
+ sanitize (~> 4.6)
sass-rails (~> 5.0.6)
scss_lint (~> 0.56.0)
seed-fu (~> 2.3.7)
diff --git a/app/assets/javascripts/diffs/components/diff_line_gutter_content.vue b/app/assets/javascripts/diffs/components/diff_line_gutter_content.vue
index 0fe0007057b..d184a76f038 100644
--- a/app/assets/javascripts/diffs/components/diff_line_gutter_content.vue
+++ b/app/assets/javascripts/diffs/components/diff_line_gutter_content.vue
@@ -71,6 +71,11 @@ export default {
required: false,
default: false,
},
+ discussions: {
+ type: Array,
+ required: false,
+ default: () => [],
+ },
},
computed: {
...mapState({
@@ -78,7 +83,6 @@ export default {
diffFiles: state => state.diffs.diffFiles,
}),
...mapGetters(['isLoggedIn']),
- ...mapGetters('diffs', ['discussionsByLineCode']),
lineHref() {
return this.lineCode ? `#${this.lineCode}` : '#';
},
@@ -88,24 +92,19 @@ export default {
this.showCommentButton &&
!this.isMatchLine &&
!this.isContextLine &&
- !this.hasDiscussions &&
- !this.isMetaLine
+ !this.isMetaLine &&
+ !this.hasDiscussions
);
},
- discussions() {
- return this.discussionsByLineCode[this.lineCode] || [];
- },
hasDiscussions() {
return this.discussions.length > 0;
},
shouldShowAvatarsOnGutter() {
- let render = this.hasDiscussions && this.showCommentButton;
-
if (!this.lineType && this.linePosition === LINE_POSITION_RIGHT) {
- render = false;
+ return false;
}
- return render;
+ return this.hasDiscussions && this.showCommentButton;
},
},
methods: {
diff --git a/app/assets/javascripts/diffs/components/diff_table_cell.vue b/app/assets/javascripts/diffs/components/diff_table_cell.vue
index 5962f30d9bb..e8e8ddc6c5e 100644
--- a/app/assets/javascripts/diffs/components/diff_table_cell.vue
+++ b/app/assets/javascripts/diffs/components/diff_table_cell.vue
@@ -67,6 +67,11 @@ export default {
required: false,
default: false,
},
+ discussions: {
+ type: Array,
+ required: false,
+ default: () => [],
+ },
},
computed: {
...mapGetters(['isLoggedIn']),
@@ -136,6 +141,7 @@ export default {
:is-match-line="isMatchLine"
:is-context-line="isContentLine"
:is-meta-line="isMetaLine"
+ :discussions="discussions"
/>
</td>
</template>
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 a6f011ff31e..1b5ae5e9f75 100644
--- a/app/assets/javascripts/diffs/components/inline_diff_comment_row.vue
+++ b/app/assets/javascripts/diffs/components/inline_diff_comment_row.vue
@@ -1,5 +1,5 @@
<script>
-import { mapState, mapGetters } from 'vuex';
+import { mapState } from 'vuex';
import diffDiscussions from './diff_discussions.vue';
import diffLineNoteForm from './diff_line_note_form.vue';
@@ -21,15 +21,16 @@ export default {
type: Number,
required: true,
},
+ discussions: {
+ type: Array,
+ required: false,
+ default: () => [],
+ },
},
computed: {
...mapState({
diffLineCommentForms: state => state.diffs.diffLineCommentForms,
}),
- ...mapGetters('diffs', ['discussionsByLineCode']),
- discussions() {
- return this.discussionsByLineCode[this.line.lineCode] || [];
- },
className() {
return this.discussions.length ? '' : 'js-temp-notes-holder';
},
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 0e306f39a9f..32d65ff994f 100644
--- a/app/assets/javascripts/diffs/components/inline_diff_table_row.vue
+++ b/app/assets/javascripts/diffs/components/inline_diff_table_row.vue
@@ -33,6 +33,11 @@ export default {
required: false,
default: false,
},
+ discussions: {
+ type: Array,
+ required: false,
+ default: () => [],
+ },
},
data() {
return {
@@ -89,6 +94,7 @@ export default {
:is-bottom="isBottom"
:is-hover="isHover"
:show-comment-button="true"
+ :discussions="discussions"
class="diff-line-num old_line"
/>
<diff-table-cell
@@ -98,6 +104,7 @@ export default {
:line-type="newLineType"
:is-bottom="isBottom"
:is-hover="isHover"
+ :discussions="discussions"
class="diff-line-num new_line"
/>
<td
diff --git a/app/assets/javascripts/diffs/components/inline_diff_view.vue b/app/assets/javascripts/diffs/components/inline_diff_view.vue
index 8e491d293e5..5f30cc57a59 100644
--- a/app/assets/javascripts/diffs/components/inline_diff_view.vue
+++ b/app/assets/javascripts/diffs/components/inline_diff_view.vue
@@ -20,7 +20,11 @@ export default {
},
},
computed: {
- ...mapGetters('diffs', ['commitId', 'discussionsByLineCode']),
+ ...mapGetters('diffs', [
+ 'commitId',
+ 'shouldRenderInlineCommentRow',
+ 'singleDiscussionByLineCode',
+ ]),
...mapState({
diffLineCommentForms: state => state.diffs.diffLineCommentForms,
}),
@@ -34,18 +38,7 @@ export default {
return window.gon.user_color_scheme;
},
},
- methods: {
- shouldRenderCommentRow(line) {
- if (this.diffLineCommentForms[line.lineCode]) return true;
-
- const lineDiscussions = this.discussionsByLineCode[line.lineCode];
- if (lineDiscussions === undefined) {
- return false;
- }
-
- return lineDiscussions.every(discussion => discussion.expanded);
- },
- },
+ methods: {},
};
</script>
@@ -64,13 +57,15 @@ export default {
:line="line"
:is-bottom="index + 1 === diffLinesLength"
:key="line.lineCode"
+ :discussions="singleDiscussionByLineCode(line.lineCode)"
/>
<inline-diff-comment-row
- v-if="shouldRenderCommentRow(line)"
+ v-if="shouldRenderInlineCommentRow(line)"
:diff-file-hash="diffFile.fileHash"
:line="line"
:line-index="index"
:key="index"
+ :discussions="singleDiscussionByLineCode(line.lineCode)"
/>
</template>
</tbody>
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 05e5cafc717..bb9a65c83fa 100644
--- a/app/assets/javascripts/diffs/components/parallel_diff_comment_row.vue
+++ b/app/assets/javascripts/diffs/components/parallel_diff_comment_row.vue
@@ -1,5 +1,5 @@
<script>
-import { mapState, mapGetters } from 'vuex';
+import { mapState } from 'vuex';
import diffDiscussions from './diff_discussions.vue';
import diffLineNoteForm from './diff_line_note_form.vue';
@@ -21,48 +21,51 @@ export default {
type: Number,
required: true,
},
+ leftDiscussions: {
+ type: Array,
+ required: false,
+ default: () => [],
+ },
+ rightDiscussions: {
+ type: Array,
+ required: false,
+ default: () => [],
+ },
},
computed: {
...mapState({
diffLineCommentForms: state => state.diffs.diffLineCommentForms,
}),
- ...mapGetters('diffs', ['discussionsByLineCode']),
leftLineCode() {
return this.line.left.lineCode;
},
rightLineCode() {
return this.line.right.lineCode;
},
- hasDiscussion() {
- const discussions = this.discussionsByLineCode;
-
- return discussions[this.leftLineCode] || discussions[this.rightLineCode];
- },
hasExpandedDiscussionOnLeft() {
- const discussions = this.discussionsByLineCode[this.leftLineCode];
-
+ const discussions = this.leftDiscussions;
return discussions ? discussions.every(discussion => discussion.expanded) : false;
},
hasExpandedDiscussionOnRight() {
- const discussions = this.discussionsByLineCode[this.rightLineCode];
-
+ const discussions = this.rightDiscussions;
return discussions ? discussions.every(discussion => discussion.expanded) : false;
},
hasAnyExpandedDiscussion() {
return this.hasExpandedDiscussionOnLeft || this.hasExpandedDiscussionOnRight;
},
shouldRenderDiscussionsOnLeft() {
- return this.discussionsByLineCode[this.leftLineCode] && this.hasExpandedDiscussionOnLeft;
+ return this.leftDiscussions && this.hasExpandedDiscussionOnLeft;
},
shouldRenderDiscussionsOnRight() {
- return (
- this.discussionsByLineCode[this.rightLineCode] &&
- this.hasExpandedDiscussionOnRight &&
- this.line.right.type
- );
+ return this.rightDiscussions && this.hasExpandedDiscussionOnRight && this.line.right.type;
+ },
+ showRightSideCommentForm() {
+ return this.line.right.type && this.diffLineCommentForms[this.rightLineCode];
},
className() {
- return this.hasDiscussion ? '' : 'js-temp-notes-holder';
+ return this.leftDiscussions.length > 0 || this.rightDiscussions.length > 0
+ ? ''
+ : 'js-temp-notes-holder';
},
},
};
@@ -80,13 +83,12 @@ export default {
class="content"
>
<diff-discussions
- v-if="discussionsByLineCode[leftLineCode].length"
- :discussions="discussionsByLineCode[leftLineCode]"
+ v-if="leftDiscussions.length"
+ :discussions="leftDiscussions"
/>
</div>
<diff-line-note-form
- v-if="diffLineCommentForms[leftLineCode] &&
- diffLineCommentForms[leftLineCode]"
+ v-if="diffLineCommentForms[leftLineCode]"
:diff-file-hash="diffFileHash"
:line="line.left"
:note-target-line="line.left"
@@ -100,13 +102,12 @@ export default {
class="content"
>
<diff-discussions
- v-if="discussionsByLineCode[rightLineCode].length"
- :discussions="discussionsByLineCode[rightLineCode]"
+ v-if="rightDiscussions.length"
+ :discussions="rightDiscussions"
/>
</div>
<diff-line-note-form
- v-if="diffLineCommentForms[rightLineCode] &&
- diffLineCommentForms[rightLineCode] && line.right.type"
+ v-if="showRightSideCommentForm"
:diff-file-hash="diffFileHash"
:line="line.right"
:note-target-line="line.right"
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 0031cedc68f..d4e54c2bd00 100644
--- a/app/assets/javascripts/diffs/components/parallel_diff_table_row.vue
+++ b/app/assets/javascripts/diffs/components/parallel_diff_table_row.vue
@@ -36,6 +36,16 @@ export default {
required: false,
default: false,
},
+ leftDiscussions: {
+ type: Array,
+ required: false,
+ default: () => [],
+ },
+ rightDiscussions: {
+ type: Array,
+ required: false,
+ default: () => [],
+ },
},
data() {
return {
@@ -116,6 +126,7 @@ export default {
:is-hover="isLeftHover"
:show-comment-button="true"
:diff-view-type="parallelDiffViewType"
+ :discussions="leftDiscussions"
class="diff-line-num old_line"
/>
<td
@@ -136,6 +147,7 @@ export default {
:is-hover="isRightHover"
:show-comment-button="true"
:diff-view-type="parallelDiffViewType"
+ :discussions="rightDiscussions"
class="diff-line-num new_line"
/>
<td
diff --git a/app/assets/javascripts/diffs/components/parallel_diff_view.vue b/app/assets/javascripts/diffs/components/parallel_diff_view.vue
index 8f8d6bbc818..4d97cb6d15d 100644
--- a/app/assets/javascripts/diffs/components/parallel_diff_view.vue
+++ b/app/assets/javascripts/diffs/components/parallel_diff_view.vue
@@ -21,7 +21,11 @@ export default {
},
},
computed: {
- ...mapGetters('diffs', ['commitId', 'discussionsByLineCode']),
+ ...mapGetters('diffs', [
+ 'commitId',
+ 'singleDiscussionByLineCode',
+ 'shouldRenderParallelCommentRow',
+ ]),
...mapState({
diffLineCommentForms: state => state.diffs.diffLineCommentForms,
}),
@@ -51,32 +55,6 @@ export default {
return window.gon.user_color_scheme;
},
},
- methods: {
- shouldRenderCommentRow(line) {
- const leftLineCode = line.left.lineCode;
- const rightLineCode = line.right.lineCode;
- const discussions = this.discussionsByLineCode;
- const leftDiscussions = discussions[leftLineCode];
- const rightDiscussions = discussions[rightLineCode];
- const hasDiscussion = leftDiscussions || rightDiscussions;
-
- const hasExpandedDiscussionOnLeft = leftDiscussions
- ? leftDiscussions.every(discussion => discussion.expanded)
- : false;
- const hasExpandedDiscussionOnRight = rightDiscussions
- ? rightDiscussions.every(discussion => discussion.expanded)
- : false;
-
- if (hasDiscussion && (hasExpandedDiscussionOnLeft || hasExpandedDiscussionOnRight)) {
- return true;
- }
-
- const hasCommentFormOnLeft = this.diffLineCommentForms[leftLineCode];
- const hasCommentFormOnRight = this.diffLineCommentForms[rightLineCode];
-
- return hasCommentFormOnLeft || hasCommentFormOnRight;
- },
- },
};
</script>
@@ -97,13 +75,17 @@ export default {
:line="line"
:is-bottom="index + 1 === diffLinesLength"
:key="index"
+ :left-discussions="singleDiscussionByLineCode(line.left.lineCode)"
+ :right-discussions="singleDiscussionByLineCode(line.right.lineCode)"
/>
<parallel-diff-comment-row
- v-if="shouldRenderCommentRow(line)"
+ v-if="shouldRenderParallelCommentRow(line)"
:key="`dcr-${index}`"
:line="line"
:diff-file-hash="diffFile.fileHash"
:line-index="index"
+ :left-discussions="singleDiscussionByLineCode(line.left.lineCode)"
+ :right-discussions="singleDiscussionByLineCode(line.right.lineCode)"
/>
</template>
</tbody>
diff --git a/app/assets/javascripts/diffs/store/getters.js b/app/assets/javascripts/diffs/store/getters.js
index d3881fa1a0a..c7b9b1a16e6 100644
--- a/app/assets/javascripts/diffs/store/getters.js
+++ b/app/assets/javascripts/diffs/store/getters.js
@@ -75,19 +75,21 @@ export const discussionsByLineCode = (state, getters, rootState, rootGetters) =>
const isDiffDiscussion = note.diff_discussion;
const hasLineCode = note.line_code;
const isResolvable = note.resolvable;
- const diffRefs = diffRefsByLineCode[note.line_code];
- if (isDiffDiscussion && hasLineCode && isResolvable && diffRefs) {
- const refs = convertObjectPropsToCamelCase(note.position.formatter);
- const originalRefs = convertObjectPropsToCamelCase(note.original_position.formatter);
+ if (isDiffDiscussion && hasLineCode && isResolvable) {
+ const diffRefs = diffRefsByLineCode[note.line_code];
+ if (diffRefs) {
+ const refs = convertObjectPropsToCamelCase(note.position.formatter);
+ const originalRefs = convertObjectPropsToCamelCase(note.original_position.formatter);
- if (_.isEqual(refs, diffRefs) || _.isEqual(originalRefs, diffRefs)) {
- const lineCode = note.line_code;
+ if (_.isEqual(refs, diffRefs) || _.isEqual(originalRefs, diffRefs)) {
+ const lineCode = note.line_code;
- if (acc[lineCode]) {
- acc[lineCode].push(note);
- } else {
- acc[lineCode] = [note];
+ if (acc[lineCode]) {
+ acc[lineCode].push(note);
+ } else {
+ acc[lineCode] = [note];
+ }
}
}
}
@@ -96,6 +98,47 @@ export const discussionsByLineCode = (state, getters, rootState, rootGetters) =>
}, {});
};
+export const singleDiscussionByLineCode = (state, getters) => lineCode => {
+ if (!lineCode) return [];
+ const discussions = getters.discussionsByLineCode;
+ return discussions[lineCode] || [];
+};
+
+export const shouldRenderParallelCommentRow = (state, getters) => line => {
+ const leftLineCode = line.left.lineCode;
+ const rightLineCode = line.right.lineCode;
+ const leftDiscussions = getters.singleDiscussionByLineCode(leftLineCode);
+ const rightDiscussions = getters.singleDiscussionByLineCode(rightLineCode);
+ const hasDiscussion = leftDiscussions.length || rightDiscussions.length;
+
+ const hasExpandedDiscussionOnLeft = leftDiscussions.length
+ ? leftDiscussions.every(discussion => discussion.expanded)
+ : false;
+ const hasExpandedDiscussionOnRight = rightDiscussions.length
+ ? rightDiscussions.every(discussion => discussion.expanded)
+ : false;
+
+ if (hasDiscussion && (hasExpandedDiscussionOnLeft || hasExpandedDiscussionOnRight)) {
+ return true;
+ }
+
+ const hasCommentFormOnLeft = state.diffLineCommentForms[leftLineCode];
+ const hasCommentFormOnRight = state.diffLineCommentForms[rightLineCode];
+
+ return hasCommentFormOnLeft || hasCommentFormOnRight;
+};
+
+export const shouldRenderInlineCommentRow = (state, getters) => line => {
+ if (state.diffLineCommentForms[line.lineCode]) return true;
+
+ const lineDiscussions = getters.singleDiscussionByLineCode(line.lineCode);
+ if (lineDiscussions.length === 0) {
+ return false;
+ }
+
+ return lineDiscussions.every(discussion => discussion.expanded);
+};
+
// prevent babel-plugin-rewire from generating an invalid default during karma∂ tests
export const getDiffFileByHash = state => fileHash =>
state.diffFiles.find(file => file.fileHash === fileHash);
diff --git a/app/assets/javascripts/gpg_badges.js b/app/assets/javascripts/gpg_badges.js
index 029fd6a67d4..efba6fc1aff 100644
--- a/app/assets/javascripts/gpg_badges.js
+++ b/app/assets/javascripts/gpg_badges.js
@@ -1,23 +1,36 @@
import $ from 'jquery';
import { parseQueryStringIntoObject } from '~/lib/utils/common_utils';
import axios from '~/lib/utils/axios_utils';
-import flash from '~/flash';
+import createFlash from '~/flash';
import { __ } from '~/locale';
export default class GpgBadges {
static fetch() {
- const badges = $('.js-loading-gpg-badge');
const tag = $('.js-signature-container');
+ if (tag.length === 0) {
+ return Promise.resolve();
+ }
+
+ const badges = $('.js-loading-gpg-badge');
badges.html('<i class="fa fa-spinner fa-spin"></i>');
+ const displayError = () => createFlash(__('An error occurred while loading commit signatures'));
+
+ const endpoint = tag.data('signaturesPath');
+ if (!endpoint) {
+ displayError();
+ return Promise.reject(new Error('Missing commit signatures endpoint!'));
+ }
+
const params = parseQueryStringIntoObject(tag.serialize());
- return axios.get(tag.data('signaturesPath'), { params })
- .then(({ data }) => {
- data.signatures.forEach((signature) => {
- badges.filter(`[data-commit-sha="${signature.commit_sha}"]`).replaceWith(signature.html);
- });
- })
- .catch(() => flash(__('An error occurred while loading commits')));
+ return axios
+ .get(endpoint, { params })
+ .then(({ data }) => {
+ data.signatures.forEach(signature => {
+ badges.filter(`[data-commit-sha="${signature.commit_sha}"]`).replaceWith(signature.html);
+ });
+ })
+ .catch(displayError);
}
}
diff --git a/app/assets/javascripts/lib/utils/common_utils.js b/app/assets/javascripts/lib/utils/common_utils.js
index 6b7550efff8..2f3dd6f6cbc 100644
--- a/app/assets/javascripts/lib/utils/common_utils.js
+++ b/app/assets/javascripts/lib/utils/common_utils.js
@@ -541,6 +541,26 @@ export const addSelectOnFocusBehaviour = (selector = '.js-select-on-focus') => {
});
};
+/**
+ * Method to round of values with decimal places
+ * with provided precision.
+ *
+ * Taken from https://stackoverflow.com/a/7343013/414749
+ *
+ * Eg; roundOffFloat(3.141592, 3) = 3.142
+ *
+ * Refer to spec/javascripts/lib/utils/common_utils_spec.js for
+ * more supported examples.
+ *
+ * @param {Float} number
+ * @param {Number} precision
+ */
+export const roundOffFloat = (number, precision = 0) => {
+ // eslint-disable-next-line no-restricted-properties
+ const multiplier = Math.pow(10, precision);
+ return Math.round(number * multiplier) / multiplier;
+};
+
window.gl = window.gl || {};
window.gl.utils = {
...(window.gl.utils || {}),
diff --git a/app/assets/javascripts/lib/utils/poll.js b/app/assets/javascripts/lib/utils/poll.js
index 7fca80c2fdb..91d8c30744f 100644
--- a/app/assets/javascripts/lib/utils/poll.js
+++ b/app/assets/javascripts/lib/utils/poll.js
@@ -38,7 +38,7 @@ import { normalizeHeaders } from './common_utils';
* } else {
* poll.stop();
* }
-* });
+ * });
*
* 1. Checks for response and headers before start polling
* 2. Interval is provided by `Poll-Interval` header.
@@ -51,8 +51,8 @@ export default class Poll {
constructor(options = {}) {
this.options = options;
this.options.data = options.data || {};
- this.options.notificationCallback = options.notificationCallback ||
- function notificationCallback() {};
+ this.options.notificationCallback =
+ options.notificationCallback || function notificationCallback() {};
this.intervalHeader = 'POLL-INTERVAL';
this.timeoutID = null;
@@ -63,6 +63,7 @@ export default class Poll {
const headers = normalizeHeaders(response.headers);
const pollInterval = parseInt(headers[this.intervalHeader], 10);
if (pollInterval > 0 && response.status === httpStatusCodes.OK && this.canPoll) {
+ clearTimeout(this.timeoutID);
this.timeoutID = setTimeout(() => {
this.makeRequest();
}, pollInterval);
@@ -77,11 +78,11 @@ export default class Poll {
notificationCallback(true);
return resource[method](data)
- .then((response) => {
+ .then(response => {
this.checkConditions(response);
notificationCallback(false);
})
- .catch((error) => {
+ .catch(error => {
notificationCallback(false);
if (error.status === httpStatusCodes.ABORTED) {
return;
diff --git a/app/assets/javascripts/notes/stores/mutations.js b/app/assets/javascripts/notes/stores/mutations.js
index ab6a95e2601..e1b159142c9 100644
--- a/app/assets/javascripts/notes/stores/mutations.js
+++ b/app/assets/javascripts/notes/stores/mutations.js
@@ -174,27 +174,19 @@ export default {
[types.UPDATE_NOTE](state, note) {
const noteObj = utils.findNoteObjectById(state.discussions, note.discussion_id);
-
if (noteObj.individual_note) {
noteObj.notes.splice(0, 1, note);
} else {
const comment = utils.findNoteObjectById(noteObj.notes, note.id);
- noteObj.notes.splice(noteObj.notes.indexOf(comment), 1, note);
+ Object.assign(comment, note);
}
},
[types.UPDATE_DISCUSSION](state, noteData) {
const note = noteData;
- let index = 0;
-
- state.discussions.forEach((n, i) => {
- if (n.id === note.id) {
- index = i;
- }
- });
-
+ const selectedDiscussion = state.discussions.find(n => n.id === note.id);
note.expanded = true; // override expand flag to prevent collapse
- state.discussions.splice(index, 1, note);
+ Object.assign(selectedDiscussion, note);
},
[types.CLOSE_ISSUE](state) {
@@ -215,12 +207,9 @@ export default {
[types.SET_DISCUSSION_DIFF_LINES](state, { discussionId, diffLines }) {
const discussion = utils.findNoteObjectById(state.discussions, discussionId);
- const index = state.discussions.indexOf(discussion);
- const discussionWithDiffLines = Object.assign({}, discussion, {
+ Object.assign(discussion, {
truncated_diff_lines: diffLines,
});
-
- state.discussions.splice(index, 1, discussionWithDiffLines);
},
};
diff --git a/app/assets/javascripts/notes/stores/utils.js b/app/assets/javascripts/notes/stores/utils.js
index a0e096ebfaf..c4a812c5af4 100644
--- a/app/assets/javascripts/notes/stores/utils.js
+++ b/app/assets/javascripts/notes/stores/utils.js
@@ -2,13 +2,11 @@ import AjaxCache from '~/lib/utils/ajax_cache';
const REGEX_QUICK_ACTIONS = /^\/\w+.*$/gm;
-export const findNoteObjectById = (notes, id) =>
- notes.filter(n => n.id === id)[0];
+export const findNoteObjectById = (notes, id) => notes.find(n => n.id === id);
export const getQuickActionText = note => {
let text = 'Applying command';
- const quickActions =
- AjaxCache.get(gl.GfmAutoComplete.dataSources.commands) || [];
+ const quickActions = AjaxCache.get(gl.GfmAutoComplete.dataSources.commands) || [];
const executedCommands = quickActions.filter(command => {
const commandRegex = new RegExp(`/${command.name}`);
@@ -29,5 +27,4 @@ export const getQuickActionText = note => {
export const hasQuickActions = note => REGEX_QUICK_ACTIONS.test(note);
-export const stripQuickActions = note =>
- note.replace(REGEX_QUICK_ACTIONS, '').trim();
+export const stripQuickActions = note => note.replace(REGEX_QUICK_ACTIONS, '').trim();
diff --git a/app/assets/javascripts/pages/projects/blob/show/index.js b/app/assets/javascripts/pages/projects/blob/show/index.js
index 85c6862d629..84e5bb3c46e 100644
--- a/app/assets/javascripts/pages/projects/blob/show/index.js
+++ b/app/assets/javascripts/pages/projects/blob/show/index.js
@@ -2,6 +2,7 @@ import Vue from 'vue';
import commitPipelineStatus from '~/projects/tree/components/commit_pipeline_status_component.vue';
import BlobViewer from '~/blob/viewer/index';
import initBlob from '~/pages/projects/init_blob';
+import GpgBadges from '~/gpg_badges';
document.addEventListener('DOMContentLoaded', () => {
new BlobViewer(); // eslint-disable-line no-new
@@ -26,4 +27,6 @@ document.addEventListener('DOMContentLoaded', () => {
},
});
}
+
+ GpgBadges.fetch();
});
diff --git a/app/assets/javascripts/pages/projects/show/index.js b/app/assets/javascripts/pages/projects/show/index.js
index 3b0f0f960b8..d2dc0c4570e 100644
--- a/app/assets/javascripts/pages/projects/show/index.js
+++ b/app/assets/javascripts/pages/projects/show/index.js
@@ -7,6 +7,7 @@ import TreeView from '~/tree';
import BlobViewer from '~/blob/viewer/index';
import Activities from '~/activities';
import { ajaxGet } from '~/lib/utils/common_utils';
+import GpgBadges from '~/gpg_badges';
import Star from '../../../star';
import notificationsDropdown from '../../../notifications_dropdown';
@@ -38,4 +39,6 @@ document.addEventListener('DOMContentLoaded', () => {
$(treeSlider).waitForImages(() => {
ajaxGet(document.querySelector('.js-tree-content').dataset.logsPath);
});
+
+ GpgBadges.fetch();
});
diff --git a/app/assets/javascripts/pages/projects/tree/show/index.js b/app/assets/javascripts/pages/projects/tree/show/index.js
index 7ad082a5e61..33d69d891d8 100644
--- a/app/assets/javascripts/pages/projects/tree/show/index.js
+++ b/app/assets/javascripts/pages/projects/tree/show/index.js
@@ -2,6 +2,7 @@ import $ from 'jquery';
import Vue from 'vue';
import initBlob from '~/blob_edit/blob_bundle';
import commitPipelineStatus from '~/projects/tree/components/commit_pipeline_status_component.vue';
+import GpgBadges from '~/gpg_badges';
import TreeView from '../../../../tree';
import ShortcutsNavigation from '../../../../shortcuts_navigation';
import BlobViewer from '../../../../blob/viewer';
@@ -14,7 +15,8 @@ document.addEventListener('DOMContentLoaded', () => {
new BlobViewer(); // eslint-disable-line no-new
new NewCommitForm($('.js-create-dir-form')); // eslint-disable-line no-new
$('#tree-slider').waitForImages(() =>
- ajaxGet(document.querySelector('.js-tree-content').dataset.logsPath));
+ ajaxGet(document.querySelector('.js-tree-content').dataset.logsPath),
+ );
initBlob();
const commitPipelineStatusEl = document.querySelector('.js-commit-pipeline-status');
@@ -36,4 +38,6 @@ document.addEventListener('DOMContentLoaded', () => {
},
});
}
+
+ GpgBadges.fetch();
});
diff --git a/app/assets/javascripts/projects/tree/components/commit_pipeline_status_component.vue b/app/assets/javascripts/projects/tree/components/commit_pipeline_status_component.vue
index a4c7c143e56..1c1e17563a1 100644
--- a/app/assets/javascripts/projects/tree/components/commit_pipeline_status_component.vue
+++ b/app/assets/javascripts/projects/tree/components/commit_pipeline_status_component.vue
@@ -1,27 +1,27 @@
<script>
- import Visibility from 'visibilityjs';
- import ciIcon from '~/vue_shared/components/ci_icon.vue';
- import loadingIcon from '~/vue_shared/components/loading_icon.vue';
- import Poll from '~/lib/utils/poll';
- import Flash from '~/flash';
- import { s__, sprintf } from '~/locale';
- import tooltip from '~/vue_shared/directives/tooltip';
- import CommitPipelineService from '../services/commit_pipeline_service';
+import Visibility from 'visibilityjs';
+import ciIcon from '~/vue_shared/components/ci_icon.vue';
+import loadingIcon from '~/vue_shared/components/loading_icon.vue';
+import Poll from '~/lib/utils/poll';
+import Flash from '~/flash';
+import { s__, sprintf } from '~/locale';
+import tooltip from '~/vue_shared/directives/tooltip';
+import CommitPipelineService from '../services/commit_pipeline_service';
- export default {
- directives: {
- tooltip,
+export default {
+ directives: {
+ tooltip,
+ },
+ components: {
+ ciIcon,
+ loadingIcon,
+ },
+ props: {
+ endpoint: {
+ type: String,
+ required: true,
},
- components: {
- ciIcon,
- loadingIcon,
- },
- props: {
- endpoint: {
- type: String,
- required: true,
- },
- /* This prop can be used to replace some of the `render_commit_status`
+ /* This prop can be used to replace some of the `render_commit_status`
used across GitLab, this way we could use this vue component and add a
realtime status where it makes sense
realtime: {
@@ -29,76 +29,77 @@
required: false,
default: true,
}, */
+ },
+ data() {
+ return {
+ ciStatus: {},
+ isLoading: true,
+ };
+ },
+ computed: {
+ statusTitle() {
+ return sprintf(s__('Commits|Commit: %{commitText}'), { commitText: this.ciStatus.text });
},
- data() {
- return {
- ciStatus: {},
- isLoading: true,
- };
- },
- computed: {
- statusTitle() {
- return sprintf(s__('Commits|Commit: %{commitText}'), { commitText: this.ciStatus.text });
- },
+ },
+ mounted() {
+ this.service = new CommitPipelineService(this.endpoint);
+ this.initPolling();
+ },
+ methods: {
+ successCallback(res) {
+ const { pipelines } = res.data;
+ if (pipelines.length > 0) {
+ // The pipeline entity always keeps the latest pipeline info on the `details.status`
+ this.ciStatus = pipelines[0].details.status;
+ }
+ this.isLoading = false;
},
- mounted() {
- this.service = new CommitPipelineService(this.endpoint);
- this.initPolling();
+ errorCallback() {
+ this.ciStatus = {
+ text: 'not found',
+ icon: 'status_notfound',
+ group: 'notfound',
+ };
+ this.isLoading = false;
+ Flash(s__('Something went wrong on our end'));
},
- methods: {
- successCallback(res) {
- const { pipelines } = res.data;
- if (pipelines.length > 0) {
- // The pipeline entity always keeps the latest pipeline info on the `details.status`
- this.ciStatus = pipelines[0].details.status;
- }
- this.isLoading = false;
- },
- errorCallback() {
- this.ciStatus = {
- text: 'not found',
- icon: 'status_notfound',
- group: 'notfound',
- };
- this.isLoading = false;
- Flash(s__('Something went wrong on our end'));
- },
- initPolling() {
- this.poll = new Poll({
- resource: this.service,
- method: 'fetchData',
- successCallback: response => this.successCallback(response),
- errorCallback: this.errorCallback,
- });
+ initPolling() {
+ this.poll = new Poll({
+ resource: this.service,
+ method: 'fetchData',
+ successCallback: response => this.successCallback(response),
+ errorCallback: this.errorCallback,
+ });
+
+ if (!Visibility.hidden()) {
+ this.isLoading = true;
+ this.poll.makeRequest();
+ } else {
+ this.fetchPipelineCommitData();
+ }
+ Visibility.change(() => {
if (!Visibility.hidden()) {
- this.isLoading = true;
- this.poll.makeRequest();
+ this.poll.restart();
} else {
- this.fetchPipelineCommitData();
+ this.poll.stop();
}
-
- Visibility.change(() => {
- if (!Visibility.hidden()) {
- this.poll.restart();
- } else {
- this.poll.stop();
- }
- });
- },
- fetchPipelineCommitData() {
- this.service.fetchData()
- .then(this.successCallback)
- .catch(this.errorCallback);
- },
+ });
},
- destroy() {
- this.poll.stop();
+ fetchPipelineCommitData() {
+ this.service
+ .fetchData()
+ .then(this.successCallback)
+ .catch(this.errorCallback);
},
- };
+ },
+ destroy() {
+ this.poll.stop();
+ },
+};
</script>
<template>
- <div>
+ <div class="ci-status-link">
<loading-icon
v-if="isLoading"
label="Loading pipeline status"
@@ -113,6 +114,7 @@
:title="statusTitle"
:aria-label="statusTitle"
:status="ciStatus"
+ :size="24"
data-container="body"
/>
</a>
diff --git a/app/assets/javascripts/reports/store/actions.js b/app/assets/javascripts/reports/store/actions.js
new file mode 100644
index 00000000000..15c077b0fd8
--- /dev/null
+++ b/app/assets/javascripts/reports/store/actions.js
@@ -0,0 +1,67 @@
+import Visibility from 'visibilityjs';
+import axios from '../../lib/utils/axios_utils';
+import Poll from '../../lib/utils/poll';
+import * as types from './mutation_types';
+
+export const setEndpoint = ({ commit }, endpoint) => commit(types.SET_ENDPOINT, endpoint);
+
+export const requestReports = ({ commit }) => commit(types.REQUEST_REPORTS);
+
+let eTagPoll;
+
+export const clearEtagPoll = () => {
+ eTagPoll = null;
+};
+
+export const stopPolling = () => {
+ if (eTagPoll) eTagPoll.stop();
+};
+
+export const restartPolling = () => {
+ if (eTagPoll) eTagPoll.restart();
+};
+
+/**
+ * We need to poll the reports endpoint while they are being parsed in the Backend.
+ * This can take up to one minute.
+ *
+ * Poll.js will handle etag response.
+ * While http status code is 204, it means it's parsing, and we'll keep polling
+ * When http status code is 200, it means parsing is done, we can show the results & stop polling
+ * When http status code is 500, it means parsing went wrong and we stop polling
+ */
+export const fetchReports = ({ state, dispatch }) => {
+ dispatch('requestReports');
+
+ eTagPoll = new Poll({
+ resource: {
+ getReports(endpoint) {
+ return axios.get(endpoint);
+ },
+ },
+ data: state.endpoint,
+ method: 'getReports',
+ successCallback: ({ data }) => dispatch('receiveReportsSuccess', data),
+ errorCallback: () => dispatch('receiveReportsError'),
+ });
+
+ if (!Visibility.hidden()) {
+ eTagPoll.makeRequest();
+ }
+
+ Visibility.change(() => {
+ if (!Visibility.hidden()) {
+ dispatch('restartPolling');
+ } else {
+ dispatch('stopPolling');
+ }
+ });
+};
+
+export const receiveReportsSuccess = ({ commit }, response) =>
+ commit(types.RECEIVE_REPORTS_SUCCESS, response);
+
+export const receiveReportsError = ({ commit }) => commit(types.RECEIVE_REPORTS_ERROR);
+
+// prevent babel-plugin-rewire from generating an invalid default during karma tests
+export default () => {};
diff --git a/app/assets/javascripts/reports/store/index.js b/app/assets/javascripts/reports/store/index.js
new file mode 100644
index 00000000000..af4f9688fb4
--- /dev/null
+++ b/app/assets/javascripts/reports/store/index.js
@@ -0,0 +1,13 @@
+import Vue from 'vue';
+import Vuex from 'vuex';
+import * as actions from './actions';
+import mutations from './mutations';
+import state from './state';
+
+Vue.use(Vuex);
+
+export default () => new Vuex.Store({
+ actions,
+ mutations,
+ state: state(),
+});
diff --git a/app/assets/javascripts/reports/store/mutation_types.js b/app/assets/javascripts/reports/store/mutation_types.js
new file mode 100644
index 00000000000..77722974c45
--- /dev/null
+++ b/app/assets/javascripts/reports/store/mutation_types.js
@@ -0,0 +1,5 @@
+export const SET_ENDPOINT = 'SET_ENDPOINT';
+
+export const REQUEST_REPORTS = 'REQUEST_REPORTS';
+export const RECEIVE_REPORTS_SUCCESS = 'RECEIVE_REPORTS_SUCCESS';
+export const RECEIVE_REPORTS_ERROR = 'RECEIVE_REPORTS_ERROR';
diff --git a/app/assets/javascripts/reports/store/mutations.js b/app/assets/javascripts/reports/store/mutations.js
new file mode 100644
index 00000000000..d9d301826cf
--- /dev/null
+++ b/app/assets/javascripts/reports/store/mutations.js
@@ -0,0 +1,26 @@
+/* eslint-disable no-param-reassign */
+import * as types from './mutation_types';
+
+export default {
+ [types.SET_ENDPOINT](state, endpoint) {
+ state.endpoint = endpoint;
+ },
+ [types.REQUEST_REPORTS](state) {
+ state.isLoading = true;
+ },
+ [types.RECEIVE_REPORTS_SUCCESS](state, response) {
+
+ state.isLoading = false;
+
+ state.summary.total = response.summary.total;
+ state.summary.resolved = response.summary.resolved;
+ state.summary.failed = response.summary.failed;
+
+ state.reports = response.suites;
+
+ },
+ [types.RECEIVE_REPORTS_ERROR](state) {
+ state.isLoading = false;
+ state.hasError = true;
+ },
+};
diff --git a/app/assets/javascripts/reports/store/state.js b/app/assets/javascripts/reports/store/state.js
new file mode 100644
index 00000000000..97f9d0a6859
--- /dev/null
+++ b/app/assets/javascripts/reports/store/state.js
@@ -0,0 +1,28 @@
+export default () => ({
+ endpoint: null,
+
+ isLoading: false,
+ hasError: false,
+
+ summary: {
+ total: 0,
+ resolved: 0,
+ failed: 0,
+ },
+
+ /**
+ * Each report will have the following format:
+ * {
+ * name: {String},
+ * summary: {
+ * total: {Number},
+ * resolved: {Number},
+ * failed: {Number},
+ * },
+ * new_failures: {Array.<Object>},
+ * resolved_failures: {Array.<Object>},
+ * existing_failures: {Array.<Object>},
+ * }
+ */
+ reports: [],
+});
diff --git a/app/assets/javascripts/vue_shared/components/clipboard_button.vue b/app/assets/javascripts/vue_shared/components/clipboard_button.vue
index dc5760bce28..d272bf3f55f 100644
--- a/app/assets/javascripts/vue_shared/components/clipboard_button.vue
+++ b/app/assets/javascripts/vue_shared/components/clipboard_button.vue
@@ -13,12 +13,19 @@
* />
*/
import tooltip from '../directives/tooltip';
+import Icon from '../components/icon.vue';
export default {
name: 'ClipboardButton',
+
directives: {
tooltip,
},
+
+ components: {
+ Icon,
+ },
+
props: {
text: {
type: String,
@@ -58,10 +65,6 @@ export default {
type="button"
class="btn"
>
- <i
- aria-hidden="true"
- class="fa fa-clipboard"
- >
- </i>
+ <icon name="duplicate" />
</button>
</template>
diff --git a/app/assets/javascripts/vue_shared/components/stacked_progress_bar.vue b/app/assets/javascripts/vue_shared/components/stacked_progress_bar.vue
index b1c2df54ef6..f44d361c47e 100644
--- a/app/assets/javascripts/vue_shared/components/stacked_progress_bar.vue
+++ b/app/assets/javascripts/vue_shared/components/stacked_progress_bar.vue
@@ -1,4 +1,5 @@
<script>
+import { roundOffFloat } from '~/lib/utils/common_utils';
import tooltip from '~/vue_shared/directives/tooltip';
export default {
@@ -70,7 +71,7 @@ export default {
},
methods: {
getPercent(count) {
- return Math.ceil((count / this.totalCount) * 100);
+ return roundOffFloat((count / this.totalCount) * 100, 1);
},
barStyle(percent) {
return `width: ${percent}%;`;
diff --git a/app/assets/stylesheets/framework/buttons.scss b/app/assets/stylesheets/framework/buttons.scss
index 523fcb05a87..646cedd79ed 100644
--- a/app/assets/stylesheets/framework/buttons.scss
+++ b/app/assets/stylesheets/framework/buttons.scss
@@ -294,6 +294,10 @@
.btn-clipboard {
border: 0;
padding: 0 5px;
+
+ svg {
+ top: auto;
+ }
}
.input-group-prepend,
diff --git a/app/assets/stylesheets/framework/stacked_progress_bar.scss b/app/assets/stylesheets/framework/stacked_progress_bar.scss
index 528ba53a48b..29a2d5881f7 100644
--- a/app/assets/stylesheets/framework/stacked_progress_bar.scss
+++ b/app/assets/stylesheets/framework/stacked_progress_bar.scss
@@ -10,7 +10,7 @@
.status-neutral,
.status-red, {
height: 100%;
- min-width: 30px;
+ min-width: 40px;
padding: 0 5px;
font-size: $tooltip-font-size;
font-weight: normal;
diff --git a/app/assets/stylesheets/pages/commits.scss b/app/assets/stylesheets/pages/commits.scss
index f75be4e01cd..63585e26022 100644
--- a/app/assets/stylesheets/pages/commits.scss
+++ b/app/assets/stylesheets/pages/commits.scss
@@ -205,7 +205,7 @@
> .ci-status-link,
> .btn,
> .commit-sha-group {
- margin-left: $gl-padding-8;
+ margin-left: $gl-padding;
}
}
@@ -235,10 +235,6 @@
fill: $gl-text-color-secondary;
}
- .fa-clipboard {
- color: $gl-text-color-secondary;
- }
-
:first-child {
border-bottom-left-radius: $border-radius-default;
border-top-left-radius: $border-radius-default;
diff --git a/app/controllers/concerns/lfs_request.rb b/app/controllers/concerns/lfs_request.rb
index 79ee5b2f91e..4584ff782a3 100644
--- a/app/controllers/concerns/lfs_request.rb
+++ b/app/controllers/concerns/lfs_request.rb
@@ -71,7 +71,22 @@ module LfsRequest
def lfs_download_access?
return false unless project.lfs_enabled?
- ci? || lfs_deploy_token? || user_can_download_code? || build_can_download_code?
+ ci? || lfs_deploy_token? || user_can_download_code? || build_can_download_code? || deploy_token_can_download_code?
+ end
+
+ def deploy_token_can_download_code?
+ deploy_token_present? &&
+ deploy_token.project == project &&
+ deploy_token.active? &&
+ deploy_token.read_repository?
+ end
+
+ def deploy_token_present?
+ user && user.is_a?(DeployToken)
+ end
+
+ def deploy_token
+ user
end
def lfs_upload_access?
@@ -86,7 +101,7 @@ module LfsRequest
end
def user_can_download_code?
- has_authentication_ability?(:download_code) && can?(user, :download_code, project)
+ has_authentication_ability?(:download_code) && can?(user, :download_code, project) && !deploy_token_present?
end
def build_can_download_code?
diff --git a/app/finders/groups_finder.rb b/app/finders/groups_finder.rb
index 0754123a3cf..0eeba1d2428 100644
--- a/app/finders/groups_finder.rb
+++ b/app/finders/groups_finder.rb
@@ -8,6 +8,7 @@
# owned: boolean
# parent: Group
# all_available: boolean (defaults to true)
+# min_access_level: integer
#
# Users with full private access can see all groups. The `owned` and `parent`
# params can be used to restrict the groups that are returned.
@@ -39,6 +40,7 @@ class GroupsFinder < UnionFinder
def all_groups
return [owned_groups] if params[:owned]
+ return [groups_with_min_access_level] if min_access_level?
return [Group.all] if current_user&.full_private_access? && all_available?
groups = []
@@ -56,6 +58,16 @@ class GroupsFinder < UnionFinder
current_user.groups
end
+ def groups_with_min_access_level
+ groups = current_user
+ .groups
+ .where('members.access_level >= ?', params[:min_access_level])
+
+ Gitlab::GroupHierarchy
+ .new(groups)
+ .base_and_descendants
+ end
+
def by_parent(groups)
return groups unless params[:parent]
@@ -73,4 +85,8 @@ class GroupsFinder < UnionFinder
def all_available?
params.fetch(:all_available, true)
end
+
+ def min_access_level?
+ current_user && params[:min_access_level].present?
+ end
end
diff --git a/app/finders/personal_projects_finder.rb b/app/finders/personal_projects_finder.rb
index 5aea0cb8192..18adfea747f 100644
--- a/app/finders/personal_projects_finder.rb
+++ b/app/finders/personal_projects_finder.rb
@@ -1,6 +1,7 @@
class PersonalProjectsFinder < UnionFinder
- def initialize(user)
+ def initialize(user, params = {})
@user = user
+ @params = params
end
# Finds the projects belonging to the user in "@user", limited to either
@@ -8,6 +9,8 @@ class PersonalProjectsFinder < UnionFinder
#
# current_user - When given the list of projects is limited to those only
# visible by this user.
+ # params - Optional query parameters
+ # min_access_level: integer
#
# Returns an ActiveRecord::Relation.
def execute(current_user = nil)
@@ -19,11 +22,21 @@ class PersonalProjectsFinder < UnionFinder
private
def all_projects(current_user)
- projects = []
+ return [projects_with_min_access_level(current_user)] if current_user && min_access_level?
+ projects = []
projects << @user.personal_projects.visible_to_user(current_user) if current_user
projects << @user.personal_projects.public_to_user(current_user)
-
projects
end
+
+ def projects_with_min_access_level(current_user)
+ @user
+ .personal_projects
+ .visible_to_user_and_access_level(current_user, @params[:min_access_level])
+ end
+
+ def min_access_level?
+ @params[:min_access_level].present?
+ end
end
diff --git a/app/finders/projects_finder.rb b/app/finders/projects_finder.rb
index b06595081e7..cac6643eff3 100644
--- a/app/finders/projects_finder.rb
+++ b/app/finders/projects_finder.rb
@@ -17,6 +17,7 @@
# search: string
# non_archived: boolean
# archived: 'only' or boolean
+# min_access_level: integer
#
class ProjectsFinder < UnionFinder
include CustomAttributesFilter
@@ -34,7 +35,7 @@ class ProjectsFinder < UnionFinder
user = params.delete(:user)
collection =
if user
- PersonalProjectsFinder.new(user).execute(current_user)
+ PersonalProjectsFinder.new(user, finder_params).execute(current_user)
else
init_collection
end
@@ -65,6 +66,8 @@ class ProjectsFinder < UnionFinder
def collection_with_user
if owned_projects?
current_user.owned_projects
+ elsif min_access_level?
+ current_user.authorized_projects.where('project_authorizations.access_level >= ?', params[:min_access_level])
else
if private_only?
current_user.authorized_projects
@@ -76,7 +79,7 @@ class ProjectsFinder < UnionFinder
# Builds a collection for an anonymous user.
def collection_without_user
- if private_only? || owned_projects?
+ if private_only? || owned_projects? || min_access_level?
Project.none
else
Project.public_to_user
@@ -91,6 +94,10 @@ class ProjectsFinder < UnionFinder
params[:non_public].present?
end
+ def min_access_level?
+ params[:min_access_level].present?
+ end
+
def by_ids(items)
project_ids_relation ? items.where(id: project_ids_relation) : items
end
@@ -143,4 +150,10 @@ class ProjectsFinder < UnionFinder
projects
end
end
+
+ def finder_params
+ return {} unless min_access_level?
+
+ { min_access_level: params[:min_access_level] }
+ end
end
diff --git a/app/helpers/button_helper.rb b/app/helpers/button_helper.rb
index 3605d6a3c95..0171a880164 100644
--- a/app/helpers/button_helper.rb
+++ b/app/helpers/button_helper.rb
@@ -51,7 +51,7 @@ module ButtonHelper
}
content_tag :button, button_attributes do
- concat(icon('clipboard', 'aria-hidden': 'true')) unless hide_button_icon
+ concat(sprite_icon('duplicate')) unless hide_button_icon
concat(button_text)
end
end
diff --git a/app/helpers/ci_status_helper.rb b/app/helpers/ci_status_helper.rb
index f49b5c7b51a..330959e536d 100644
--- a/app/helpers/ci_status_helper.rb
+++ b/app/helpers/ci_status_helper.rb
@@ -56,7 +56,7 @@ module CiStatusHelper
status.humanize
end
- def ci_icon_for_status(status)
+ def ci_icon_for_status(status, size: 16)
if detailed_status?(status)
return sprite_icon(status.icon)
end
@@ -85,7 +85,7 @@ module CiStatusHelper
'status_canceled'
end
- sprite_icon(icon_name, size: 16)
+ sprite_icon(icon_name, size: size)
end
def pipeline_status_cache_key(pipeline_status)
@@ -111,7 +111,8 @@ module CiStatusHelper
'commit',
commit.status(ref),
path,
- tooltip_placement: tooltip_placement)
+ tooltip_placement: tooltip_placement,
+ icon_size: 24)
end
def render_pipeline_status(pipeline, tooltip_placement: 'left')
@@ -125,16 +126,16 @@ module CiStatusHelper
Ci::Runner.instance_type.blank?
end
- def render_status_with_link(type, status, path = nil, tooltip_placement: 'left', cssclass: '', container: 'body')
+ def render_status_with_link(type, status, path = nil, tooltip_placement: 'left', cssclass: '', container: 'body', icon_size: 16)
klass = "ci-status-link ci-status-icon-#{status.dasherize} #{cssclass}"
title = "#{type.titleize}: #{ci_label_for_status(status)}"
data = { toggle: 'tooltip', placement: tooltip_placement, container: container }
if path
- link_to ci_icon_for_status(status), path,
+ link_to ci_icon_for_status(status, size: icon_size), path,
class: klass, title: title, data: data
else
- content_tag :span, ci_icon_for_status(status),
+ content_tag :span, ci_icon_for_status(status, size: icon_size),
class: klass, title: title, data: data
end
end
diff --git a/app/helpers/commits_helper.rb b/app/helpers/commits_helper.rb
index e5c3be47801..89fe90fd801 100644
--- a/app/helpers/commits_helper.rb
+++ b/app/helpers/commits_helper.rb
@@ -145,15 +145,14 @@ module CommitsHelper
person_name
end
- options = {
- class: "commit-#{options[:source]}-link has-tooltip",
- title: source_email
+ link_options = {
+ class: "commit-#{options[:source]}-link"
}
if user.nil?
- mail_to(source_email, text, options)
+ mail_to(source_email, text, link_options)
else
- link_to(text, user_path(user), options)
+ link_to(text, user_path(user), link_options)
end
end
diff --git a/app/helpers/visibility_level_helper.rb b/app/helpers/visibility_level_helper.rb
index e395cda03d3..cf2fe5a2019 100644
--- a/app/helpers/visibility_level_helper.rb
+++ b/app/helpers/visibility_level_helper.rb
@@ -126,10 +126,9 @@ module VisibilityLevelHelper
end
def visibility_icon_description(form_model)
- case form_model
- when Project
+ if form_model.respond_to?(:visibility_level_allowed_as_fork?)
project_visibility_icon_description(form_model.visibility_level)
- when Group
+ elsif form_model.respond_to?(:visibility_level_allowed_by_sub_groups?)
group_visibility_icon_description(form_model.visibility_level)
end
end
diff --git a/app/models/deploy_token.rb b/app/models/deploy_token.rb
index 5082dc45368..7ab647abe93 100644
--- a/app/models/deploy_token.rb
+++ b/app/models/deploy_token.rb
@@ -27,7 +27,7 @@ class DeployToken < ActiveRecord::Base
end
def active?
- !revoked
+ !revoked && expires_at > Date.today
end
def scopes
@@ -58,6 +58,10 @@ class DeployToken < ActiveRecord::Base
write_attribute(:expires_at, value.presence || Forever.date)
end
+ def admin?
+ false
+ end
+
private
def ensure_at_least_one_scope
diff --git a/app/models/project.rb b/app/models/project.rb
index ae0a13b17dc..f880d728839 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -154,6 +154,7 @@ class Project < ActiveRecord::Base
has_one :mock_monitoring_service
has_one :microsoft_teams_service
has_one :packagist_service
+ has_one :hangouts_chat_service
# TODO: replace these relations with the fork network versions
has_one :forked_project_link, foreign_key: "forked_to_project_id"
@@ -326,6 +327,7 @@ class Project < ActiveRecord::Base
scope :joined, ->(user) { where('namespace_id != ?', user.namespace_id) }
scope :starred_by, ->(user) { joins(:users_star_projects).where('users_star_projects.user_id': user.id) }
scope :visible_to_user, ->(user) { where(id: user.authorized_projects.select(:id).reorder(nil)) }
+ scope :visible_to_user_and_access_level, ->(user, access_level) { where(id: user.authorized_projects.where('project_authorizations.access_level >= ?', access_level).select(:id).reorder(nil)) }
scope :archived, -> { where(archived: true) }
scope :non_archived, -> { where(archived: false) }
scope :for_milestones, ->(ids) { joins(:milestones).where('milestones.id' => ids).distinct }
diff --git a/app/models/project_services/hangouts_chat_service.rb b/app/models/project_services/hangouts_chat_service.rb
new file mode 100644
index 00000000000..a8512c5f57c
--- /dev/null
+++ b/app/models/project_services/hangouts_chat_service.rb
@@ -0,0 +1,67 @@
+require 'hangouts_chat'
+
+class HangoutsChatService < ChatNotificationService
+ def title
+ 'Hangouts Chat'
+ end
+
+ def description
+ 'Receive event notifications in Google Hangouts Chat'
+ end
+
+ def self.to_param
+ 'hangouts_chat'
+ end
+
+ def help
+ 'This service sends notifications about projects events to Google Hangouts Chat room.<br />
+ To set up this service:
+ <ol>
+ <li><a href="https://developers.google.com/hangouts/chat/how-tos/webhooks">Set up an incoming webhook for your room</a>. All notifications will come to this room.</li>
+ <li>Paste the <strong>Webhook URL</strong> into the field below.</li>
+ <li>Select events below to enable notifications.</li>
+ </ol>'
+ end
+
+ def event_field(event)
+ end
+
+ def default_channel_placeholder
+ end
+
+ def webhook_placeholder
+ 'https://chat.googleapis.com/v1/spaces…'
+ end
+
+ def default_fields
+ [
+ { type: 'text', name: 'webhook', placeholder: "e.g. #{webhook_placeholder}" },
+ { type: 'checkbox', name: 'notify_only_broken_pipelines' },
+ { type: 'checkbox', name: 'notify_only_default_branch' }
+ ]
+ end
+
+ private
+
+ def notify(message, opts)
+ simple_text = parse_simple_text_message(message)
+ HangoutsChat::Sender.new(webhook).simple(simple_text)
+ end
+
+ def parse_simple_text_message(message)
+ header = message.pretext
+ return header if message.attachments.empty?
+
+ attachment = message.attachments.first
+ title = format_attachment_title(attachment)
+ body = attachment[:text]
+
+ [header, title, body].compact.join("\n")
+ end
+
+ def format_attachment_title(attachment)
+ return attachment[:title] unless attachment[:title_link]
+
+ "<#{attachment[:title_link]}|#{attachment[:title]}>"
+ end
+end
diff --git a/app/models/service.rb b/app/models/service.rb
index ad835293b46..cbfe0c6eedd 100644
--- a/app/models/service.rb
+++ b/app/models/service.rb
@@ -254,6 +254,7 @@ class Service < ActiveRecord::Base
emails_on_push
external_wiki
flowdock
+ hangouts_chat
hipchat
irker
jira
diff --git a/app/serializers/analytics_build_entity.rb b/app/serializers/analytics_build_entity.rb
index bdc22d71202..99663c8d5eb 100644
--- a/app/serializers/analytics_build_entity.rb
+++ b/app/serializers/analytics_build_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class AnalyticsBuildEntity < Grape::Entity
include RequestAwareEntity
include EntityDateHelper
diff --git a/app/serializers/analytics_build_serializer.rb b/app/serializers/analytics_build_serializer.rb
index f172d67d356..9c9f76a1c28 100644
--- a/app/serializers/analytics_build_serializer.rb
+++ b/app/serializers/analytics_build_serializer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class AnalyticsBuildSerializer < BaseSerializer
entity AnalyticsBuildEntity
end
diff --git a/app/serializers/analytics_commit_entity.rb b/app/serializers/analytics_commit_entity.rb
index 402cecbfd08..b25c603c9f0 100644
--- a/app/serializers/analytics_commit_entity.rb
+++ b/app/serializers/analytics_commit_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class AnalyticsCommitEntity < CommitEntity
include EntityDateHelper
diff --git a/app/serializers/analytics_commit_serializer.rb b/app/serializers/analytics_commit_serializer.rb
index cdbfecf2b70..0f65687a3c0 100644
--- a/app/serializers/analytics_commit_serializer.rb
+++ b/app/serializers/analytics_commit_serializer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class AnalyticsCommitSerializer < BaseSerializer
entity AnalyticsCommitEntity
end
diff --git a/app/serializers/analytics_generic_serializer.rb b/app/serializers/analytics_generic_serializer.rb
index 9f4859e8410..10391c13034 100644
--- a/app/serializers/analytics_generic_serializer.rb
+++ b/app/serializers/analytics_generic_serializer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class AnalyticsGenericSerializer < BaseSerializer
def represent(resource, opts = {})
resource.symbolize_keys!
diff --git a/app/serializers/analytics_issue_entity.rb b/app/serializers/analytics_issue_entity.rb
index b7d95ea020f..ab15bd0ac7a 100644
--- a/app/serializers/analytics_issue_entity.rb
+++ b/app/serializers/analytics_issue_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class AnalyticsIssueEntity < Grape::Entity
include RequestAwareEntity
include EntityDateHelper
diff --git a/app/serializers/analytics_issue_serializer.rb b/app/serializers/analytics_issue_serializer.rb
index 4fb3e8f1bb4..4a1777276a4 100644
--- a/app/serializers/analytics_issue_serializer.rb
+++ b/app/serializers/analytics_issue_serializer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class AnalyticsIssueSerializer < AnalyticsGenericSerializer
entity AnalyticsIssueEntity
end
diff --git a/app/serializers/analytics_merge_request_entity.rb b/app/serializers/analytics_merge_request_entity.rb
index 888265eaa38..b7134da9461 100644
--- a/app/serializers/analytics_merge_request_entity.rb
+++ b/app/serializers/analytics_merge_request_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class AnalyticsMergeRequestEntity < AnalyticsIssueEntity
expose :state
diff --git a/app/serializers/analytics_merge_request_serializer.rb b/app/serializers/analytics_merge_request_serializer.rb
index 4622a1dd855..f0b9115d02c 100644
--- a/app/serializers/analytics_merge_request_serializer.rb
+++ b/app/serializers/analytics_merge_request_serializer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class AnalyticsMergeRequestSerializer < AnalyticsGenericSerializer
entity AnalyticsMergeRequestEntity
end
diff --git a/app/serializers/analytics_stage_entity.rb b/app/serializers/analytics_stage_entity.rb
index 3e355a13e06..ae7c20c3bba 100644
--- a/app/serializers/analytics_stage_entity.rb
+++ b/app/serializers/analytics_stage_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class AnalyticsStageEntity < Grape::Entity
include EntityDateHelper
diff --git a/app/serializers/analytics_stage_serializer.rb b/app/serializers/analytics_stage_serializer.rb
index 613cf6874d8..86786273240 100644
--- a/app/serializers/analytics_stage_serializer.rb
+++ b/app/serializers/analytics_stage_serializer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class AnalyticsStageSerializer < BaseSerializer
entity AnalyticsStageEntity
end
diff --git a/app/serializers/analytics_summary_entity.rb b/app/serializers/analytics_summary_entity.rb
index 9c37afd53e1..39c6b4b06b2 100644
--- a/app/serializers/analytics_summary_entity.rb
+++ b/app/serializers/analytics_summary_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class AnalyticsSummaryEntity < Grape::Entity
expose :value, safe: true
expose :title
diff --git a/app/serializers/analytics_summary_serializer.rb b/app/serializers/analytics_summary_serializer.rb
index c87a24aa47c..b22bd737f03 100644
--- a/app/serializers/analytics_summary_serializer.rb
+++ b/app/serializers/analytics_summary_serializer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class AnalyticsSummarySerializer < BaseSerializer
entity AnalyticsSummaryEntity
end
diff --git a/app/serializers/award_emoji_entity.rb b/app/serializers/award_emoji_entity.rb
index 6e03cd02392..212931a2fa9 100644
--- a/app/serializers/award_emoji_entity.rb
+++ b/app/serializers/award_emoji_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class AwardEmojiEntity < Grape::Entity
expose :name
expose :user, using: API::Entities::UserSafe
diff --git a/app/serializers/base_serializer.rb b/app/serializers/base_serializer.rb
index 8cade280b0c..7b65bd22f54 100644
--- a/app/serializers/base_serializer.rb
+++ b/app/serializers/base_serializer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class BaseSerializer
attr_reader :params
diff --git a/app/serializers/blob_entity.rb b/app/serializers/blob_entity.rb
index b501fd5e964..3ac61481dea 100644
--- a/app/serializers/blob_entity.rb
+++ b/app/serializers/blob_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class BlobEntity < Grape::Entity
include RequestAwareEntity
diff --git a/app/serializers/build_action_entity.rb b/app/serializers/build_action_entity.rb
index f2d76a8ad81..f9da3f63911 100644
--- a/app/serializers/build_action_entity.rb
+++ b/app/serializers/build_action_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class BuildActionEntity < Grape::Entity
include RequestAwareEntity
diff --git a/app/serializers/build_artifact_entity.rb b/app/serializers/build_artifact_entity.rb
index 6e0e33bc09b..414f436e76e 100644
--- a/app/serializers/build_artifact_entity.rb
+++ b/app/serializers/build_artifact_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class BuildArtifactEntity < Grape::Entity
include RequestAwareEntity
diff --git a/app/serializers/build_details_entity.rb b/app/serializers/build_details_entity.rb
index 2de9624aed4..b887b99d31c 100644
--- a/app/serializers/build_details_entity.rb
+++ b/app/serializers/build_details_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class BuildDetailsEntity < JobEntity
expose :coverage, :erased_at, :duration
expose :tag_list, as: :tags
diff --git a/app/serializers/build_metadata_entity.rb b/app/serializers/build_metadata_entity.rb
index f16f3badffa..6242ee8957d 100644
--- a/app/serializers/build_metadata_entity.rb
+++ b/app/serializers/build_metadata_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class BuildMetadataEntity < Grape::Entity
expose :timeout_human_readable
expose :timeout_source do |metadata|
diff --git a/app/serializers/build_serializer.rb b/app/serializers/build_serializer.rb
index bae9932847f..0649fdad6a8 100644
--- a/app/serializers/build_serializer.rb
+++ b/app/serializers/build_serializer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class BuildSerializer < BaseSerializer
entity JobEntity
diff --git a/app/serializers/cluster_application_entity.rb b/app/serializers/cluster_application_entity.rb
index 77fc3336521..2bd17e58086 100644
--- a/app/serializers/cluster_application_entity.rb
+++ b/app/serializers/cluster_application_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ClusterApplicationEntity < Grape::Entity
expose :name
expose :status_name, as: :status
diff --git a/app/serializers/cluster_entity.rb b/app/serializers/cluster_entity.rb
index 7e5b0997878..c59f68bbc49 100644
--- a/app/serializers/cluster_entity.rb
+++ b/app/serializers/cluster_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ClusterEntity < Grape::Entity
include RequestAwareEntity
diff --git a/app/serializers/cluster_serializer.rb b/app/serializers/cluster_serializer.rb
index 2e13c1501e7..4bb4d4880d4 100644
--- a/app/serializers/cluster_serializer.rb
+++ b/app/serializers/cluster_serializer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ClusterSerializer < BaseSerializer
entity ClusterEntity
diff --git a/app/serializers/cohort_activity_month_entity.rb b/app/serializers/cohort_activity_month_entity.rb
index e6788a8b596..cdbc89a2dcd 100644
--- a/app/serializers/cohort_activity_month_entity.rb
+++ b/app/serializers/cohort_activity_month_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class CohortActivityMonthEntity < Grape::Entity
include ActionView::Helpers::NumberHelper
diff --git a/app/serializers/cohort_entity.rb b/app/serializers/cohort_entity.rb
index 7cdba5b0484..3d0213e1038 100644
--- a/app/serializers/cohort_entity.rb
+++ b/app/serializers/cohort_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class CohortEntity < Grape::Entity
include ActionView::Helpers::NumberHelper
diff --git a/app/serializers/cohorts_entity.rb b/app/serializers/cohorts_entity.rb
index 98f5995ba6f..a84d568bf30 100644
--- a/app/serializers/cohorts_entity.rb
+++ b/app/serializers/cohorts_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class CohortsEntity < Grape::Entity
expose :months_included
expose :cohorts, using: CohortEntity
diff --git a/app/serializers/cohorts_serializer.rb b/app/serializers/cohorts_serializer.rb
index fe9367b13d8..ceca5e1e5a8 100644
--- a/app/serializers/cohorts_serializer.rb
+++ b/app/serializers/cohorts_serializer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class CohortsSerializer < AnalyticsGenericSerializer
entity CohortsEntity
end
diff --git a/app/serializers/commit_entity.rb b/app/serializers/commit_entity.rb
index c8dd98cc04d..b3287c66554 100644
--- a/app/serializers/commit_entity.rb
+++ b/app/serializers/commit_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class CommitEntity < API::Entities::Commit
include RequestAwareEntity
diff --git a/app/serializers/concerns/with_pagination.rb b/app/serializers/concerns/with_pagination.rb
index 89631b73fcf..c8ffae355e8 100644
--- a/app/serializers/concerns/with_pagination.rb
+++ b/app/serializers/concerns/with_pagination.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module WithPagination
attr_accessor :paginator
diff --git a/app/serializers/container_repositories_serializer.rb b/app/serializers/container_repositories_serializer.rb
index 56dc70b5687..e1ce3c7b3ae 100644
--- a/app/serializers/container_repositories_serializer.rb
+++ b/app/serializers/container_repositories_serializer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ContainerRepositoriesSerializer < BaseSerializer
entity ContainerRepositoryEntity
end
diff --git a/app/serializers/container_repository_entity.rb b/app/serializers/container_repository_entity.rb
index 1103cf30a07..59bf35f5aff 100644
--- a/app/serializers/container_repository_entity.rb
+++ b/app/serializers/container_repository_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ContainerRepositoryEntity < Grape::Entity
include RequestAwareEntity
diff --git a/app/serializers/container_tag_entity.rb b/app/serializers/container_tag_entity.rb
index 8f1488e6cbb..637294877f8 100644
--- a/app/serializers/container_tag_entity.rb
+++ b/app/serializers/container_tag_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ContainerTagEntity < Grape::Entity
include RequestAwareEntity
diff --git a/app/serializers/container_tags_serializer.rb b/app/serializers/container_tags_serializer.rb
index 6ff3adff135..982ce33f6e3 100644
--- a/app/serializers/container_tags_serializer.rb
+++ b/app/serializers/container_tags_serializer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ContainerTagsSerializer < BaseSerializer
entity ContainerTagEntity
diff --git a/app/serializers/deploy_key_entity.rb b/app/serializers/deploy_key_entity.rb
index 2678f99510c..54bf030aba1 100644
--- a/app/serializers/deploy_key_entity.rb
+++ b/app/serializers/deploy_key_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class DeployKeyEntity < Grape::Entity
expose :id
expose :user_id
diff --git a/app/serializers/deploy_key_serializer.rb b/app/serializers/deploy_key_serializer.rb
index 8f849eb88b7..a1cd98b631b 100644
--- a/app/serializers/deploy_key_serializer.rb
+++ b/app/serializers/deploy_key_serializer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class DeployKeySerializer < BaseSerializer
entity DeployKeyEntity
end
diff --git a/app/serializers/deploy_keys_project_entity.rb b/app/serializers/deploy_keys_project_entity.rb
index 568ef5ab75e..5775ad72d0d 100644
--- a/app/serializers/deploy_keys_project_entity.rb
+++ b/app/serializers/deploy_keys_project_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class DeployKeysProjectEntity < Grape::Entity
expose :can_push
expose :project, using: ProjectEntity
diff --git a/app/serializers/deployment_entity.rb b/app/serializers/deployment_entity.rb
index 241c689bccd..344148a1fb7 100644
--- a/app/serializers/deployment_entity.rb
+++ b/app/serializers/deployment_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class DeploymentEntity < Grape::Entity
include RequestAwareEntity
diff --git a/app/serializers/deployment_serializer.rb b/app/serializers/deployment_serializer.rb
index cba5c3f311f..04db6b88489 100644
--- a/app/serializers/deployment_serializer.rb
+++ b/app/serializers/deployment_serializer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class DeploymentSerializer < BaseSerializer
entity DeploymentEntity
diff --git a/app/serializers/diff_file_entity.rb b/app/serializers/diff_file_entity.rb
index 61135fba97b..79844c9210a 100644
--- a/app/serializers/diff_file_entity.rb
+++ b/app/serializers/diff_file_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class DiffFileEntity < Grape::Entity
include RequestAwareEntity
include BlobHelper
diff --git a/app/serializers/diffs_entity.rb b/app/serializers/diffs_entity.rb
index bb804e5347a..f75ace14d9c 100644
--- a/app/serializers/diffs_entity.rb
+++ b/app/serializers/diffs_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class DiffsEntity < Grape::Entity
include DiffHelper
include RequestAwareEntity
diff --git a/app/serializers/diffs_serializer.rb b/app/serializers/diffs_serializer.rb
index 6771e10c5ac..9e5eb1699d4 100644
--- a/app/serializers/diffs_serializer.rb
+++ b/app/serializers/diffs_serializer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class DiffsSerializer < BaseSerializer
entity DiffsEntity
end
diff --git a/app/serializers/discussion_entity.rb b/app/serializers/discussion_entity.rb
index 7505bbdeb3d..6f95e6f9ca1 100644
--- a/app/serializers/discussion_entity.rb
+++ b/app/serializers/discussion_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class DiscussionEntity < Grape::Entity
include RequestAwareEntity
include NotesHelper
diff --git a/app/serializers/discussion_serializer.rb b/app/serializers/discussion_serializer.rb
index ed5e1224bb2..5be40e74175 100644
--- a/app/serializers/discussion_serializer.rb
+++ b/app/serializers/discussion_serializer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class DiscussionSerializer < BaseSerializer
entity DiscussionEntity
end
diff --git a/app/serializers/entity_date_helper.rb b/app/serializers/entity_date_helper.rb
index 464217123b4..cc0c2abf863 100644
--- a/app/serializers/entity_date_helper.rb
+++ b/app/serializers/entity_date_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module EntityDateHelper
include ActionView::Helpers::DateHelper
include ActionView::Helpers::TagHelper
@@ -50,15 +52,20 @@ module EntityDateHelper
elsif entity.due_date
is_upcoming = (entity.due_date - Date.today).to_i > 0
time_ago = time_ago_in_words(entity.due_date)
- content = time_ago.gsub(/\d+/) { |match| "<strong>#{match}</strong>" }
- content.slice!("about ")
- content << " " + (is_upcoming ? _("remaining") : _("ago"))
- content.html_safe
+
+ # https://gitlab.com/gitlab-org/gitlab-ce/issues/49440
+ #
+ # Need to improve the i18n here and do a full translation
+ # of the string instead of piecewise translations.
+ content = time_ago
+ .gsub(/\d+/) { |match| "<strong>#{match}</strong>" }
+ .remove("about ")
+ remaining_or_ago = is_upcoming ? _("remaining") : _("ago")
+
+ "#{content} #{remaining_or_ago}".html_safe
elsif entity.start_date && entity.start_date.past?
- days = entity.elapsed_days
- content = content_tag(:strong, days)
- content << " #{'day'.pluralize(days)} elapsed"
- content.html_safe
+ days = entity.elapsed_days
+ "#{content_tag(:strong, days)} #{'day'.pluralize(days)} elapsed".html_safe
end
end
end
diff --git a/app/serializers/entity_request.rb b/app/serializers/entity_request.rb
index 456ba1174c0..49e026e8c2a 100644
--- a/app/serializers/entity_request.rb
+++ b/app/serializers/entity_request.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class EntityRequest
# We use EntityRequest object to collect parameters and variables
# from the controller. Because options that are being passed to the entity
diff --git a/app/serializers/environment_entity.rb b/app/serializers/environment_entity.rb
index 83558fc6659..b18e9706db6 100644
--- a/app/serializers/environment_entity.rb
+++ b/app/serializers/environment_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class EnvironmentEntity < Grape::Entity
include RequestAwareEntity
diff --git a/app/serializers/environment_serializer.rb b/app/serializers/environment_serializer.rb
index 84722f33f59..dc1686c30c4 100644
--- a/app/serializers/environment_serializer.rb
+++ b/app/serializers/environment_serializer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class EnvironmentSerializer < BaseSerializer
include WithPagination
diff --git a/app/serializers/group_child_entity.rb b/app/serializers/group_child_entity.rb
index ee150eefd9e..f6804fe7f6a 100644
--- a/app/serializers/group_child_entity.rb
+++ b/app/serializers/group_child_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class GroupChildEntity < Grape::Entity
include ActionView::Helpers::NumberHelper
include RequestAwareEntity
diff --git a/app/serializers/group_child_serializer.rb b/app/serializers/group_child_serializer.rb
index 2baef0a5703..789707c2c9b 100644
--- a/app/serializers/group_child_serializer.rb
+++ b/app/serializers/group_child_serializer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class GroupChildSerializer < BaseSerializer
include WithPagination
diff --git a/app/serializers/group_entity.rb b/app/serializers/group_entity.rb
index 6d8466da902..c46c342ee5d 100644
--- a/app/serializers/group_entity.rb
+++ b/app/serializers/group_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class GroupEntity < Grape::Entity
include ActionView::Helpers::NumberHelper
include RequestAwareEntity
diff --git a/app/serializers/group_serializer.rb b/app/serializers/group_serializer.rb
index 8cf7eb63bcf..38c5757a6c1 100644
--- a/app/serializers/group_serializer.rb
+++ b/app/serializers/group_serializer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class GroupSerializer < BaseSerializer
include WithPagination
diff --git a/app/serializers/group_variable_entity.rb b/app/serializers/group_variable_entity.rb
index 62cf0b21e1e..0edab4a3092 100644
--- a/app/serializers/group_variable_entity.rb
+++ b/app/serializers/group_variable_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class GroupVariableEntity < Grape::Entity
expose :id
expose :key
diff --git a/app/serializers/group_variable_serializer.rb b/app/serializers/group_variable_serializer.rb
index 8f8205924aa..ed20b240cce 100644
--- a/app/serializers/group_variable_serializer.rb
+++ b/app/serializers/group_variable_serializer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class GroupVariableSerializer < BaseSerializer
entity GroupVariableEntity
end
diff --git a/app/serializers/issuable_entity.rb b/app/serializers/issuable_entity.rb
index 6f31fbd6b7c..e71bd3313fb 100644
--- a/app/serializers/issuable_entity.rb
+++ b/app/serializers/issuable_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class IssuableEntity < Grape::Entity
include RequestAwareEntity
diff --git a/app/serializers/issuable_sidebar_entity.rb b/app/serializers/issuable_sidebar_entity.rb
index 29138c803df..773d78d324c 100644
--- a/app/serializers/issuable_sidebar_entity.rb
+++ b/app/serializers/issuable_sidebar_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class IssuableSidebarEntity < Grape::Entity
include TimeTrackableEntity
include RequestAwareEntity
diff --git a/app/serializers/issue_entity.rb b/app/serializers/issue_entity.rb
index 840fdbcbf14..16a477c92fa 100644
--- a/app/serializers/issue_entity.rb
+++ b/app/serializers/issue_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class IssueEntity < IssuableEntity
include TimeTrackableEntity
diff --git a/app/serializers/issue_serializer.rb b/app/serializers/issue_serializer.rb
index 2555595379b..37cf5e28396 100644
--- a/app/serializers/issue_serializer.rb
+++ b/app/serializers/issue_serializer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class IssueSerializer < BaseSerializer
# This overrided method takes care of which entity should be used
# to serialize the `issue` based on `basic` key in `opts` param.
diff --git a/app/serializers/issue_sidebar_entity.rb b/app/serializers/issue_sidebar_entity.rb
index 6c823dbfe95..349ad9d1fef 100644
--- a/app/serializers/issue_sidebar_entity.rb
+++ b/app/serializers/issue_sidebar_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class IssueSidebarEntity < IssuableSidebarEntity
expose :assignees, using: API::Entities::UserBasic
end
diff --git a/app/serializers/job_entity.rb b/app/serializers/job_entity.rb
index 960e7291ae6..7bc1d87dea5 100644
--- a/app/serializers/job_entity.rb
+++ b/app/serializers/job_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class JobEntity < Grape::Entity
include RequestAwareEntity
diff --git a/app/serializers/job_group_entity.rb b/app/serializers/job_group_entity.rb
index 8554de55517..0941a9d36be 100644
--- a/app/serializers/job_group_entity.rb
+++ b/app/serializers/job_group_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class JobGroupEntity < Grape::Entity
include RequestAwareEntity
diff --git a/app/serializers/label_entity.rb b/app/serializers/label_entity.rb
index 4452161051e..98743d62b50 100644
--- a/app/serializers/label_entity.rb
+++ b/app/serializers/label_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class LabelEntity < Grape::Entity
expose :id, if: ->(label, _) { !label.is_a?(GlobalLabel) }
diff --git a/app/serializers/label_serializer.rb b/app/serializers/label_serializer.rb
index ad6ba8c46c9..25b9f7de243 100644
--- a/app/serializers/label_serializer.rb
+++ b/app/serializers/label_serializer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class LabelSerializer < BaseSerializer
entity LabelEntity
diff --git a/app/serializers/lfs_file_lock_entity.rb b/app/serializers/lfs_file_lock_entity.rb
index 264a77adc3f..7961c4e666b 100644
--- a/app/serializers/lfs_file_lock_entity.rb
+++ b/app/serializers/lfs_file_lock_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class LfsFileLockEntity < Grape::Entity
root 'locks', 'lock'
diff --git a/app/serializers/lfs_file_lock_serializer.rb b/app/serializers/lfs_file_lock_serializer.rb
index ba8fb1a461d..0367097e376 100644
--- a/app/serializers/lfs_file_lock_serializer.rb
+++ b/app/serializers/lfs_file_lock_serializer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class LfsFileLockSerializer < BaseSerializer
entity LfsFileLockEntity
end
diff --git a/app/serializers/merge_request_basic_entity.rb b/app/serializers/merge_request_basic_entity.rb
index 1c06691026d..f7eb74cf392 100644
--- a/app/serializers/merge_request_basic_entity.rb
+++ b/app/serializers/merge_request_basic_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class MergeRequestBasicEntity < IssuableSidebarEntity
expose :assignee_id
expose :merge_status
diff --git a/app/serializers/merge_request_basic_serializer.rb b/app/serializers/merge_request_basic_serializer.rb
index cc5c664c8fa..a68b48b00db 100644
--- a/app/serializers/merge_request_basic_serializer.rb
+++ b/app/serializers/merge_request_basic_serializer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class MergeRequestBasicSerializer < BaseSerializer
entity MergeRequestBasicEntity
end
diff --git a/app/serializers/merge_request_create_entity.rb b/app/serializers/merge_request_create_entity.rb
index 11234313293..e7a93004dda 100644
--- a/app/serializers/merge_request_create_entity.rb
+++ b/app/serializers/merge_request_create_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class MergeRequestCreateEntity < Grape::Entity
expose :iid
diff --git a/app/serializers/merge_request_create_serializer.rb b/app/serializers/merge_request_create_serializer.rb
index 08daf473319..b6df9ee13fc 100644
--- a/app/serializers/merge_request_create_serializer.rb
+++ b/app/serializers/merge_request_create_serializer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class MergeRequestCreateSerializer < BaseSerializer
entity MergeRequestCreateEntity
end
diff --git a/app/serializers/merge_request_diff_entity.rb b/app/serializers/merge_request_diff_entity.rb
index 32c761b45ac..433bfe60474 100644
--- a/app/serializers/merge_request_diff_entity.rb
+++ b/app/serializers/merge_request_diff_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class MergeRequestDiffEntity < Grape::Entity
include Gitlab::Routing
include GitHelper
diff --git a/app/serializers/merge_request_metrics_entity.rb b/app/serializers/merge_request_metrics_entity.rb
index 3548107ac16..1c9db08d103 100644
--- a/app/serializers/merge_request_metrics_entity.rb
+++ b/app/serializers/merge_request_metrics_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class MergeRequestMetricsEntity < Grape::Entity
expose :latest_closed_at, as: :closed_at
expose :merged_at
diff --git a/app/serializers/merge_request_serializer.rb b/app/serializers/merge_request_serializer.rb
index caf193bdae3..1f8c830e1aa 100644
--- a/app/serializers/merge_request_serializer.rb
+++ b/app/serializers/merge_request_serializer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class MergeRequestSerializer < BaseSerializer
# This overrided method takes care of which entity should be used
# to serialize the `merge_request` based on `serializer` key in `opts` param.
diff --git a/app/serializers/merge_request_user_entity.rb b/app/serializers/merge_request_user_entity.rb
index 33fc7b724d5..fd2d2897113 100644
--- a/app/serializers/merge_request_user_entity.rb
+++ b/app/serializers/merge_request_user_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class MergeRequestUserEntity < UserEntity
include RequestAwareEntity
include BlobHelper
diff --git a/app/serializers/merge_request_widget_entity.rb b/app/serializers/merge_request_widget_entity.rb
index a78bd77cf7c..4fe04e4b206 100644
--- a/app/serializers/merge_request_widget_entity.rb
+++ b/app/serializers/merge_request_widget_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class MergeRequestWidgetEntity < IssuableEntity
expose :state
expose :in_progress_merge_commit_sha
diff --git a/app/serializers/note_attachment_entity.rb b/app/serializers/note_attachment_entity.rb
index 1ad50568ab9..dc801e2bf4e 100644
--- a/app/serializers/note_attachment_entity.rb
+++ b/app/serializers/note_attachment_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class NoteAttachmentEntity < Grape::Entity
expose :url
expose :filename
diff --git a/app/serializers/note_entity.rb b/app/serializers/note_entity.rb
index 0e1f94a9f61..daa5c24d0f5 100644
--- a/app/serializers/note_entity.rb
+++ b/app/serializers/note_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class NoteEntity < API::Entities::Note
include RequestAwareEntity
include NotesHelper
diff --git a/app/serializers/note_user_entity.rb b/app/serializers/note_user_entity.rb
index 7289f3a0222..b00dfa7d353 100644
--- a/app/serializers/note_user_entity.rb
+++ b/app/serializers/note_user_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class NoteUserEntity < UserEntity
unexpose :web_url
end
diff --git a/app/serializers/pipeline_details_entity.rb b/app/serializers/pipeline_details_entity.rb
index 8ba9cac53c4..3b56767f774 100644
--- a/app/serializers/pipeline_details_entity.rb
+++ b/app/serializers/pipeline_details_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class PipelineDetailsEntity < PipelineEntity
expose :details do
expose :ordered_stages, as: :stages, using: StageEntity
diff --git a/app/serializers/pipeline_entity.rb b/app/serializers/pipeline_entity.rb
index f782b411b84..6cf1925adda 100644
--- a/app/serializers/pipeline_entity.rb
+++ b/app/serializers/pipeline_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class PipelineEntity < Grape::Entity
include RequestAwareEntity
diff --git a/app/serializers/pipeline_serializer.rb b/app/serializers/pipeline_serializer.rb
index 17a022539bb..4a33160afa1 100644
--- a/app/serializers/pipeline_serializer.rb
+++ b/app/serializers/pipeline_serializer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class PipelineSerializer < BaseSerializer
include WithPagination
entity PipelineDetailsEntity
diff --git a/app/serializers/project_entity.rb b/app/serializers/project_entity.rb
index b3e5fd21e97..60c4ba135d6 100644
--- a/app/serializers/project_entity.rb
+++ b/app/serializers/project_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ProjectEntity < Grape::Entity
include RequestAwareEntity
diff --git a/app/serializers/project_mirror_entity.rb b/app/serializers/project_mirror_entity.rb
index a9c08ac021a..8aba244cd11 100644
--- a/app/serializers/project_mirror_entity.rb
+++ b/app/serializers/project_mirror_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ProjectMirrorEntity < Grape::Entity
expose :id
diff --git a/app/serializers/project_note_entity.rb b/app/serializers/project_note_entity.rb
index e541bfbee8d..d7c4d0aacc6 100644
--- a/app/serializers/project_note_entity.rb
+++ b/app/serializers/project_note_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ProjectNoteEntity < NoteEntity
expose :human_access do |note|
note.project.team.human_max_access(note.author_id)
diff --git a/app/serializers/project_note_serializer.rb b/app/serializers/project_note_serializer.rb
index 763ad0bdb3f..2182904e815 100644
--- a/app/serializers/project_note_serializer.rb
+++ b/app/serializers/project_note_serializer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ProjectNoteSerializer < BaseSerializer
entity ProjectNoteEntity
end
diff --git a/app/serializers/project_serializer.rb b/app/serializers/project_serializer.rb
index 74de1e79a8f..23b96c2fc9e 100644
--- a/app/serializers/project_serializer.rb
+++ b/app/serializers/project_serializer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ProjectSerializer < BaseSerializer
entity ProjectEntity
end
diff --git a/app/serializers/request_aware_entity.rb b/app/serializers/request_aware_entity.rb
index d53fcfb8c1b..1524c1291d8 100644
--- a/app/serializers/request_aware_entity.rb
+++ b/app/serializers/request_aware_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module RequestAwareEntity
extend ActiveSupport::Concern
diff --git a/app/serializers/runner_entity.rb b/app/serializers/runner_entity.rb
index db26eadab2d..04ec80e0efa 100644
--- a/app/serializers/runner_entity.rb
+++ b/app/serializers/runner_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class RunnerEntity < Grape::Entity
include RequestAwareEntity
diff --git a/app/serializers/stage_entity.rb b/app/serializers/stage_entity.rb
index 2516df70ad9..00e6d32ee3a 100644
--- a/app/serializers/stage_entity.rb
+++ b/app/serializers/stage_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class StageEntity < Grape::Entity
include RequestAwareEntity
diff --git a/app/serializers/stage_serializer.rb b/app/serializers/stage_serializer.rb
index 091d8e91e43..11fb0d3f852 100644
--- a/app/serializers/stage_serializer.rb
+++ b/app/serializers/stage_serializer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class StageSerializer < BaseSerializer
include WithPagination
diff --git a/app/serializers/status_entity.rb b/app/serializers/status_entity.rb
index 47df7f9dcf9..306c30f0323 100644
--- a/app/serializers/status_entity.rb
+++ b/app/serializers/status_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class StatusEntity < Grape::Entity
include RequestAwareEntity
diff --git a/app/serializers/submodule_entity.rb b/app/serializers/submodule_entity.rb
index ed1f1ae0ef0..e475a4f301f 100644
--- a/app/serializers/submodule_entity.rb
+++ b/app/serializers/submodule_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class SubmoduleEntity < Grape::Entity
include RequestAwareEntity
diff --git a/app/serializers/time_trackable_entity.rb b/app/serializers/time_trackable_entity.rb
index e81cd7bec72..613d19703a4 100644
--- a/app/serializers/time_trackable_entity.rb
+++ b/app/serializers/time_trackable_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module TimeTrackableEntity
extend ActiveSupport::Concern
extend Grape
diff --git a/app/serializers/tree_entity.rb b/app/serializers/tree_entity.rb
index 9f1b485347f..9b7dc80e1d9 100644
--- a/app/serializers/tree_entity.rb
+++ b/app/serializers/tree_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class TreeEntity < Grape::Entity
include RequestAwareEntity
diff --git a/app/serializers/tree_root_entity.rb b/app/serializers/tree_root_entity.rb
index 496f070ddbd..f1cfcd943d8 100644
--- a/app/serializers/tree_root_entity.rb
+++ b/app/serializers/tree_root_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# TODO: Inherit from TreeEntity, when `Tree` implements `id` and `name` like `Gitlab::Git::Tree`.
class TreeRootEntity < Grape::Entity
include RequestAwareEntity
diff --git a/app/serializers/tree_serializer.rb b/app/serializers/tree_serializer.rb
index 713ade23bc9..536b8ab1ae2 100644
--- a/app/serializers/tree_serializer.rb
+++ b/app/serializers/tree_serializer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class TreeSerializer < BaseSerializer
entity TreeRootEntity
end
diff --git a/app/serializers/user_entity.rb b/app/serializers/user_entity.rb
index 876512b12dc..6236d66ff4a 100644
--- a/app/serializers/user_entity.rb
+++ b/app/serializers/user_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class UserEntity < API::Entities::UserBasic
include RequestAwareEntity
diff --git a/app/serializers/user_serializer.rb b/app/serializers/user_serializer.rb
index 49a71ebac61..2111e1b5667 100644
--- a/app/serializers/user_serializer.rb
+++ b/app/serializers/user_serializer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class UserSerializer < BaseSerializer
entity UserEntity
end
diff --git a/app/serializers/variable_entity.rb b/app/serializers/variable_entity.rb
index d576745c073..85cf367fe51 100644
--- a/app/serializers/variable_entity.rb
+++ b/app/serializers/variable_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class VariableEntity < Grape::Entity
expose :id
expose :key
diff --git a/app/serializers/variable_serializer.rb b/app/serializers/variable_serializer.rb
index 32ae82ab51c..586666cad8e 100644
--- a/app/serializers/variable_serializer.rb
+++ b/app/serializers/variable_serializer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class VariableSerializer < BaseSerializer
entity VariableEntity
end
diff --git a/app/services/projects/import_service.rb b/app/services/projects/import_service.rb
index e560d40371e..0c426faa22d 100644
--- a/app/services/projects/import_service.rb
+++ b/app/services/projects/import_service.rb
@@ -25,7 +25,7 @@ module Projects
success
rescue => e
- error("Error importing repository #{project.import_url} into #{project.full_path} - #{e.message}")
+ error("Error importing repository #{project.safe_import_url} into #{project.full_path} - #{e.message}")
end
private
diff --git a/app/uploaders/import_export_uploader.rb b/app/uploaders/import_export_uploader.rb
index 213ac5c8011..7c45ba5ca95 100644
--- a/app/uploaders/import_export_uploader.rb
+++ b/app/uploaders/import_export_uploader.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ImportExportUploader < AttachmentUploader
EXTENSION_WHITELIST = %w[tar.gz].freeze
diff --git a/app/views/projects/blame/show.html.haml b/app/views/projects/blame/show.html.haml
index e90916e340d..ef6f5c76de6 100644
--- a/app/views/projects/blame/show.html.haml
+++ b/app/views/projects/blame/show.html.haml
@@ -18,7 +18,7 @@
- commit = blame_group[:commit]
%td.blame-commit{ class: age_map_class(commit.committed_date, project_duration) }
.commit
- = author_avatar(commit, size: 36)
+ = author_avatar(commit, size: 36, has_tooltip: false)
.commit-row-title
%span.item-title.str-truncated-100
= link_to_markdown commit.title, project_commit_path(@project, commit.id), class: "cdark", title: commit.title
diff --git a/app/views/projects/blob/show.html.haml b/app/views/projects/blob/show.html.haml
index efb8175398b..5edab38bd64 100644
--- a/app/views/projects/blob/show.html.haml
+++ b/app/views/projects/blob/show.html.haml
@@ -3,6 +3,8 @@
- page_title @blob.path, @ref
+.js-signature-container{ data: { 'signatures-path': namespace_project_signatures_path } }
+
%div{ class: container_class }
= render 'projects/last_push'
diff --git a/app/views/projects/commit/_commit_box.html.haml b/app/views/projects/commit/_commit_box.html.haml
index 886dd73c33b..78522393d4b 100644
--- a/app/views/projects/commit/_commit_box.html.haml
+++ b/app/views/projects/commit/_commit_box.html.haml
@@ -10,7 +10,7 @@
%span.d-none.d-sm-inline authored
#{time_ago_with_tooltip(@commit.authored_date)}
%span= s_('ByAuthor|by')
- = author_avatar(@commit, size: 24)
+ = author_avatar(@commit, size: 24, has_tooltip: false)
%strong
= commit_author_link(@commit, avatar: true, size: 24)
- if @commit.different_committer?
diff --git a/app/views/projects/commits/_commit.html.haml b/app/views/projects/commits/_commit.html.haml
index 90e55fd0fb0..feaf44e8c0a 100644
--- a/app/views/projects/commits/_commit.html.haml
+++ b/app/views/projects/commits/_commit.html.haml
@@ -19,7 +19,7 @@
%li.commit.flex-row.js-toggle-container{ id: "commit-#{commit.short_id}" }
.avatar-cell.d-none.d-sm-block
- = author_avatar(commit, size: 36)
+ = author_avatar(commit, size: 36, has_tooltip: false)
.commit-detail.flex-list
.commit-content.qa-commit-content
diff --git a/app/views/projects/pipelines/_info.html.haml b/app/views/projects/pipelines/_info.html.haml
index 04131a90a57..bc247460d28 100644
--- a/app/views/projects/pipelines/_info.html.haml
+++ b/app/views/projects/pipelines/_info.html.haml
@@ -33,3 +33,5 @@
%span.js-details-content.hide
= link_to @pipeline.sha, project_commit_path(@project, @pipeline.sha), class: "commit-sha commit-hash-full"
= clipboard_button(text: @pipeline.sha, title: "Copy commit SHA to clipboard")
+
+ = render_if_exists "projects/pipelines/info_extension", pipeline: @pipeline
diff --git a/app/views/projects/pipelines/_with_tabs.html.haml b/app/views/projects/pipelines/_with_tabs.html.haml
index 951f80b378d..c63ff070f70 100644
--- a/app/views/projects/pipelines/_with_tabs.html.haml
+++ b/app/views/projects/pipelines/_with_tabs.html.haml
@@ -12,6 +12,7 @@
= link_to failures_project_pipeline_path(@project, @pipeline), data: { target: '#js-tab-failures', action: 'failures', toggle: 'tab' }, class: 'failures-tab' do
= _("Failed Jobs")
%span.badge.badge-pill.js-failures-counter= @pipeline.failed_builds.count
+ = render_if_exists "projects/pipelines/tabs_holder", pipeline: @pipeline, project: @project
.tab-content
#js-tab-pipeline.tab-pane
@@ -75,4 +76,4 @@
%pre.build-trace.build-trace-rounded
%code.bash.js-build-output
= build_summary(build)
-
+ = render_if_exists "projects/pipelines/tabs_content", pipeline: @pipeline, project: @project
diff --git a/app/views/projects/pipelines/new.html.haml b/app/views/projects/pipelines/new.html.haml
index 956f8fef6b8..c13e3194340 100644
--- a/app/views/projects/pipelines/new.html.haml
+++ b/app/views/projects/pipelines/new.html.haml
@@ -10,7 +10,7 @@
= form_errors(@pipeline)
.form-group.row
.col-sm-12
- = f.label :ref, s_('Pipeline|Create for')
+ = f.label :ref, s_('Pipeline|Create for'), class: 'col-form-label'
= hidden_field_tag 'pipeline[ref]', params[:ref] || @project.default_branch
= dropdown_tag(params[:ref] || @project.default_branch,
options: { toggle_class: 'js-branch-select wide git-revision-dropdown-toggle',
diff --git a/app/views/projects/services/prometheus/_show.html.haml b/app/views/projects/services/prometheus/_show.html.haml
index 9741b783db3..1d0b0265bb7 100644
--- a/app/views/projects/services/prometheus/_show.html.haml
+++ b/app/views/projects/services/prometheus/_show.html.haml
@@ -7,4 +7,4 @@
= link_to s_('PrometheusService|More information'), help_page_path('user/project/integrations/prometheus_library/metrics'), target: '_blank', rel: "noopener noreferrer"
.col-lg-9
- = render_if_exists 'projects/services/prometheus/metrics', project: @project
+ = render 'projects/services/prometheus/metrics', project: @project
diff --git a/app/views/projects/show.html.haml b/app/views/projects/show.html.haml
index e28accd5b43..803ecca48f7 100644
--- a/app/views/projects/show.html.haml
+++ b/app/views/projects/show.html.haml
@@ -8,6 +8,10 @@
= render partial: 'flash_messages', locals: { project: @project }
+- if @project.repository_exists? && !@project.empty_repo?
+ - signatures_path = namespace_project_signatures_path(project_id: @project.path, id: @project.default_branch)
+ .js-signature-container{ data: { 'signatures-path': signatures_path } }
+
%div{ class: [container_class, ("limit-container-width" unless fluid_layout)] }
= render "projects/last_push"
diff --git a/app/views/projects/tree/show.html.haml b/app/views/projects/tree/show.html.haml
index 3b4057e56d0..ace8120eeff 100644
--- a/app/views/projects/tree/show.html.haml
+++ b/app/views/projects/tree/show.html.haml
@@ -1,11 +1,14 @@
- @no_container = true
- breadcrumb_title _("Repository")
- @content_class = "limit-container-width" unless fluid_layout
+- signatures_path = namespace_project_signatures_path(namespace_id: @project.namespace.path, project_id: @project.path, id: @ref)
- page_title @path.presence || _("Files"), @ref
= content_for :meta_tags do
= auto_discovery_link_tag(:atom, project_commits_url(@project, @ref, rss_url_options), title: "#{@project.name}:#{@ref} commits")
+.js-signature-container{ data: { 'signatures-path': signatures_path } }
+
%div{ class: [(container_class), ("limit-container-width" unless fluid_layout)] }
= render 'projects/last_push'
= render 'projects/files', commit: @last_commit, project: @project, ref: @ref, content_url: project_tree_path(@project, @id)
diff --git a/app/views/sherlock/queries/_general.html.haml b/app/views/sherlock/queries/_general.html.haml
index 37747faed62..ddc089b0bd7 100644
--- a/app/views/sherlock/queries/_general.html.haml
+++ b/app/views/sherlock/queries/_general.html.haml
@@ -27,7 +27,7 @@
.card-header
.float-right
%button.js-clipboard-trigger.btn.btn-sm{ title: t('sherlock.copy_to_clipboard'), type: :button }
- %i.fa.fa-clipboard
+ = sprite_icon('duplicate')
%pre.hidden
= @query.formatted_query
%strong
@@ -42,7 +42,7 @@
.card-header
.float-right
%button.js-clipboard-trigger.btn.btn-sm{ title: t('sherlock.copy_to_clipboard'), type: :button }
- %i.fa.fa-clipboard
+ = sprite_icon('duplicate')
%pre.hidden
= @query.explain
%strong
diff --git a/app/workers/concerns/each_shard_worker.rb b/app/workers/concerns/each_shard_worker.rb
index d0a728fb495..00f589f957e 100644
--- a/app/workers/concerns/each_shard_worker.rb
+++ b/app/workers/concerns/each_shard_worker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module EachShardWorker
extend ActiveSupport::Concern
include ::Gitlab::Utils::StrongMemoize
diff --git a/app/workers/delete_diff_files_worker.rb b/app/workers/delete_diff_files_worker.rb
index bb8fbb9c373..0874a0b75e8 100644
--- a/app/workers/delete_diff_files_worker.rb
+++ b/app/workers/delete_diff_files_worker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class DeleteDiffFilesWorker
include ApplicationWorker
diff --git a/app/workers/repository_check/dispatch_worker.rb b/app/workers/repository_check/dispatch_worker.rb
index 96634f09a15..0a7d9a14c6a 100644
--- a/app/workers/repository_check/dispatch_worker.rb
+++ b/app/workers/repository_check/dispatch_worker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module RepositoryCheck
class DispatchWorker
include ApplicationWorker
diff --git a/changelogs/unreleased/29278-commits-page-tooltips.yml b/changelogs/unreleased/29278-commits-page-tooltips.yml
new file mode 100644
index 00000000000..d54301a1cf0
--- /dev/null
+++ b/changelogs/unreleased/29278-commits-page-tooltips.yml
@@ -0,0 +1,5 @@
+---
+title: Remove tooltips from commit author avatar and name in commit lists
+merge_request: 20674
+author:
+type: other
diff --git a/changelogs/unreleased/45318-vuex-store.yml b/changelogs/unreleased/45318-vuex-store.yml
new file mode 100644
index 00000000000..5ea89034bce
--- /dev/null
+++ b/changelogs/unreleased/45318-vuex-store.yml
@@ -0,0 +1,5 @@
+---
+title: Adds Vuex store for reports section in MR widget
+merge_request: 20709
+author:
+type: added
diff --git a/changelogs/unreleased/46869-deploy-tokens-failed-to-clone-lfs-repository.yml b/changelogs/unreleased/46869-deploy-tokens-failed-to-clone-lfs-repository.yml
new file mode 100644
index 00000000000..d490df58144
--- /dev/null
+++ b/changelogs/unreleased/46869-deploy-tokens-failed-to-clone-lfs-repository.yml
@@ -0,0 +1,5 @@
+---
+title: Allow cloning LFS repositories through DeployTokens
+merge_request: 20729
+author:
+type: other
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
deleted file mode 100644
index 7552e0d3878..00000000000
--- a/changelogs/unreleased/48745-project-exports-fail-when-uploads-have-been-migrated-to-object-storage.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add uploader support to Import/Export uploads
-merge_request: 20484
-author:
-type: added
diff --git a/changelogs/unreleased/49272-sanitize-git-url-in-import-errors.yml b/changelogs/unreleased/49272-sanitize-git-url-in-import-errors.yml
new file mode 100644
index 00000000000..c757e55f1cd
--- /dev/null
+++ b/changelogs/unreleased/49272-sanitize-git-url-in-import-errors.yml
@@ -0,0 +1,5 @@
+---
+title: Sanitize git URL in import errors
+merge_request:
+author: Jamie Schembri
+type: fixed
diff --git a/changelogs/unreleased/api-minimal-access-level.yml b/changelogs/unreleased/api-minimal-access-level.yml
new file mode 100644
index 00000000000..43cab246d69
--- /dev/null
+++ b/changelogs/unreleased/api-minimal-access-level.yml
@@ -0,0 +1,5 @@
+---
+title: Add filter for minimal access level in groups and projects API
+merge_request: 20478
+author: Marko, Peter
+type: added
diff --git a/changelogs/unreleased/blackst0ne-replace-sidekiq-inline-with-perform-enqueued-jobs.yml b/changelogs/unreleased/blackst0ne-replace-sidekiq-inline-with-perform-enqueued-jobs.yml
new file mode 100644
index 00000000000..69e6b7d815a
--- /dev/null
+++ b/changelogs/unreleased/blackst0ne-replace-sidekiq-inline-with-perform-enqueued-jobs.yml
@@ -0,0 +1,5 @@
+---
+title: Replace 'Sidekiq::Testing.inline!' with 'perform_enqueued_jobs'
+merge_request: 20768
+author: "@blackst0ne"
+type: other
diff --git a/changelogs/unreleased/fix-filename-for-direct-uploads.yml b/changelogs/unreleased/fix-filename-for-direct-uploads.yml
deleted file mode 100644
index a1bbf3704c0..00000000000
--- a/changelogs/unreleased/fix-filename-for-direct-uploads.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix filename for accelerated uploads
-merge_request:
-author:
-type: fixed
diff --git a/changelogs/unreleased/frozen-string-enable-app-serializers.yml b/changelogs/unreleased/frozen-string-enable-app-serializers.yml
new file mode 100644
index 00000000000..40c7b695d39
--- /dev/null
+++ b/changelogs/unreleased/frozen-string-enable-app-serializers.yml
@@ -0,0 +1,5 @@
+---
+title: Enable frozen string in app/serializers/**/*.rb
+merge_request: 20726
+author: gfyoung
+type: performance
diff --git a/changelogs/unreleased/frozen-string-vestigial.yml b/changelogs/unreleased/frozen-string-vestigial.yml
new file mode 100644
index 00000000000..79e92a19a7a
--- /dev/null
+++ b/changelogs/unreleased/frozen-string-vestigial.yml
@@ -0,0 +1,5 @@
+---
+title: Enable frozen string in newly added files to previously processed directories
+merge_request: 20763
+author: gfyoung
+type: performance
diff --git a/changelogs/unreleased/gitaly-ff-branch-nil.yml b/changelogs/unreleased/gitaly-ff-branch-nil.yml
deleted file mode 100644
index e7e689e6053..00000000000
--- a/changelogs/unreleased/gitaly-ff-branch-nil.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add missing Gitaly branch_update nil checks
-merge_request: 20711
-author:
-type: fixed
diff --git a/changelogs/unreleased/hangouts_chat_integration.yml b/changelogs/unreleased/hangouts_chat_integration.yml
new file mode 100644
index 00000000000..bf3484a6d02
--- /dev/null
+++ b/changelogs/unreleased/hangouts_chat_integration.yml
@@ -0,0 +1,5 @@
+---
+title: Add Hangouts Chat integration
+merge_request: 20290
+author: Kukovskii Vladimir
+type: added
diff --git a/changelogs/unreleased/kp-stacked-progress-bar-decimal-places.yml b/changelogs/unreleased/kp-stacked-progress-bar-decimal-places.yml
new file mode 100644
index 00000000000..a2fca4c5b91
--- /dev/null
+++ b/changelogs/unreleased/kp-stacked-progress-bar-decimal-places.yml
@@ -0,0 +1,5 @@
+---
+title: Show decimal place up to single digit in Stacked Progress Bar
+merge_request: 20776
+author:
+type: changed
diff --git a/changelogs/unreleased/project-visibility-tooltip.yml b/changelogs/unreleased/project-visibility-tooltip.yml
new file mode 100644
index 00000000000..806c93e493a
--- /dev/null
+++ b/changelogs/unreleased/project-visibility-tooltip.yml
@@ -0,0 +1,5 @@
+---
+title: Fix project visibility tooltip
+merge_request: 20535
+author: Jamie Schembri
+type: fixed
diff --git a/changelogs/unreleased/sh-bump-sanitize-4-6-6.yml b/changelogs/unreleased/sh-bump-sanitize-4-6-6.yml
new file mode 100644
index 00000000000..b9444440cb9
--- /dev/null
+++ b/changelogs/unreleased/sh-bump-sanitize-4-6-6.yml
@@ -0,0 +1,5 @@
+---
+title: Bump nokogiri to 1.8.4 and sanitize to 4.6.6 for performance
+merge_request: 20795
+author:
+type: performance
diff --git a/changelogs/unreleased/sh-enable-frozen-literals-banzi-filters.yml b/changelogs/unreleased/sh-enable-frozen-literals-banzi-filters.yml
new file mode 100644
index 00000000000..897d673e97d
--- /dev/null
+++ b/changelogs/unreleased/sh-enable-frozen-literals-banzi-filters.yml
@@ -0,0 +1,5 @@
+---
+title: Enable frozen strings in remaining lib/banzai/filter/*.rb files
+merge_request: 20777
+author:
+type: performance
diff --git a/changelogs/unreleased/sh-freeze-banzai-filter-strings.yml b/changelogs/unreleased/sh-freeze-banzai-filter-strings.yml
new file mode 100644
index 00000000000..37b397ea49f
--- /dev/null
+++ b/changelogs/unreleased/sh-freeze-banzai-filter-strings.yml
@@ -0,0 +1,5 @@
+---
+title: Enable frozen strings in lib/banzai/filter/*.rb
+merge_request: 20775
+author:
+type: performance
diff --git a/changelogs/unreleased/sh-remove-banzai-instrumentation.yml b/changelogs/unreleased/sh-remove-banzai-instrumentation.yml
new file mode 100644
index 00000000000..8bb3cd5942b
--- /dev/null
+++ b/changelogs/unreleased/sh-remove-banzai-instrumentation.yml
@@ -0,0 +1,5 @@
+---
+title: Remove method instrumentation for Banzai filters and reference parsers
+merge_request: 20770
+author:
+type: performance
diff --git a/changelogs/unreleased/tz-mr-refactor-memory-reduction.yml b/changelogs/unreleased/tz-mr-refactor-memory-reduction.yml
new file mode 100644
index 00000000000..16003fa9cad
--- /dev/null
+++ b/changelogs/unreleased/tz-mr-refactor-memory-reduction.yml
@@ -0,0 +1,5 @@
+---
+title: Reduces the client side memory footprint on merge requests
+merge_request: 20744
+author:
+type: performance
diff --git a/changelogs/unreleased/winh-tree-view-gpg.yml b/changelogs/unreleased/winh-tree-view-gpg.yml
new file mode 100644
index 00000000000..84d63814a47
--- /dev/null
+++ b/changelogs/unreleased/winh-tree-view-gpg.yml
@@ -0,0 +1,5 @@
+---
+title: Display GPG status on repository and blob pages
+merge_request: 20524
+author:
+type: changed
diff --git a/config/initializers/8_metrics.rb b/config/initializers/8_metrics.rb
index 8a851b89c56..4d8d35bf6cf 100644
--- a/config/initializers/8_metrics.rb
+++ b/config/initializers/8_metrics.rb
@@ -58,20 +58,6 @@ def instrument_classes(instrumentation)
instrumentation.instrument_instance_methods(const)
end
- # Instruments all Banzai filters and reference parsers
- {
- Filter: Rails.root.join('lib', 'banzai', 'filter', '*.rb'),
- ReferenceParser: Rails.root.join('lib', 'banzai', 'reference_parser', '*.rb')
- }.each do |const_name, path|
- Dir[path].each do |file|
- klass = File.basename(file, File.extname(file)).camelize
- const = Banzai.const_get(const_name).const_get(klass)
-
- instrumentation.instrument_methods(const)
- instrumentation.instrument_instance_methods(const)
- end
- end
-
instrumentation.instrument_methods(Banzai::Renderer)
instrumentation.instrument_methods(Banzai::Querying)
@@ -94,8 +80,6 @@ def instrument_classes(instrumentation)
instrumentation.instrument_instance_methods(RepositoryCheck::SingleRepositoryWorker)
- instrumentation.instrument_instance_methods(Rouge::Plugins::CommonMark)
- instrumentation.instrument_instance_methods(Rouge::Plugins::Redcarpet)
instrumentation.instrument_instance_methods(Rouge::Formatters::HTMLGitlab)
[:XML, :HTML].each do |namespace|
diff --git a/doc/administration/monitoring/prometheus/gitlab_metrics.md b/doc/administration/monitoring/prometheus/gitlab_metrics.md
index c0e98099e42..7bc92ae77ee 100644
--- a/doc/administration/monitoring/prometheus/gitlab_metrics.md
+++ b/doc/administration/monitoring/prometheus/gitlab_metrics.md
@@ -22,7 +22,7 @@ collect metrics from this endpoint. We recommend setting up another Prometheus
server, because the embedded server configuration is overwritten once every
[reconfigure of GitLab][reconfigure]. In the future this will not be required.
-## Metrics available
+## Unicorn Metrics available
The following metrics are available:
diff --git a/doc/administration/pages/index.md b/doc/administration/pages/index.md
index b3602bc35ab..eefa86f8e42 100644
--- a/doc/administration/pages/index.md
+++ b/doc/administration/pages/index.md
@@ -124,11 +124,6 @@ The Pages daemon doesn't listen to the outside world.
```
1. [Reconfigure GitLab][reconfigure]
-1. Restart gitlab-pages by running the following command:
-
- ```shell
- sudo gitlab-ctl restart gitlab-pages
- ```
Watch the [video tutorial][video-admin] for this configuration.
@@ -161,11 +156,6 @@ outside world.
respectively.
1. [Reconfigure GitLab][reconfigure]
-1. Restart gitlab-pages by running the following command:
-
- ```shell
- sudo gitlab-ctl restart gitlab-pages
- ```
## Advanced configuration
@@ -203,11 +193,6 @@ world. Custom domains are supported, but no TLS.
listens on. If you don't have IPv6, you can omit the IPv6 address.
1. [Reconfigure GitLab][reconfigure]
-1. Restart gitlab-pages by running the following command:
-
- ```shell
- sudo gitlab-ctl restart gitlab-pages
- ```
### Custom domains with TLS support
@@ -241,11 +226,6 @@ world. Custom domains and TLS are supported.
listens on. If you don't have IPv6, you can omit the IPv6 address.
1. [Reconfigure GitLab][reconfigure]
-1. Restart gitlab-pages by running the following command:
-
- ```shell
- sudo gitlab-ctl restart gitlab-pages
- ```
### Custom domain verification
@@ -267,7 +247,6 @@ Omnibus GitLab 11.1.
Follow the steps below to configure verbose logging of GitLab Pages daemon.
1. By default the daemon only logs with `INFO` level.
-
If you wish to make it log events with level `DEBUG` you must configure this in
`/etc/gitlab/gitlab.rb`:
@@ -291,11 +270,29 @@ are stored.
```
1. [Reconfigure GitLab][reconfigure]
-1. Restart gitlab-pages by running the following command:
+
+## Configure listener for reverse proxy requests
+
+Follow the steps below to configure the proxy listener of GitLab Pages. [Introduced](https://gitlab.com/gitlab-org/omnibus-gitlab/merge_requests/2533) in
+Omnibus GitLab 11.1.
+
+1. By default the listener is configured to listen for requests on `localhost:8090`.
+
+ If you wish to disable it you must configure this in
+ `/etc/gitlab/gitlab.rb`:
```shell
- sudo gitlab-ctl restart gitlab-pages
- ```
+ gitlab_pages['listen_proxy'] = nil
+ ```
+
+ If you wish to make it listen on a different port you must configure this also in
+ `/etc/gitlab/gitlab.rb`:
+
+ ```shell
+ gitlab_pages['listen_proxy'] = "localhost:10080"
+ ```
+
+1. [Reconfigure GitLab][reconfigure]
## Set maximum pages size
diff --git a/doc/api/groups.md b/doc/api/groups.md
index 11de75039ee..87be36cc815 100644
--- a/doc/api/groups.md
+++ b/doc/api/groups.md
@@ -10,13 +10,14 @@ Parameters:
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `skip_groups` | array of integers | no | Skip the group IDs passed |
-| `all_available` | boolean | no | Show all the groups you have access to (defaults to `false` for authenticated users, `true` for admin) |
+| `all_available` | boolean | no | Show all the groups you have access to (defaults to `false` for authenticated users, `true` for admin); Attributes `owned` and `min_access_level` have precedence |
| `search` | string | no | Return the list of authorized groups matching the search criteria |
| `order_by` | string | no | Order groups by `name`, `path` or `id`. Default is `name` |
| `sort` | string | no | Order groups in `asc` or `desc` order. Default is `asc` |
| `statistics` | boolean | no | Include group statistics (admins only) |
| `with_custom_attributes` | boolean | no | Include [custom attributes](custom_attributes.md) in response (admins only) |
-| `owned` | boolean | no | Limit to groups owned by the current user |
+| `owned` | boolean | no | Limit to groups explicitly owned by the current user |
+| `min_access_level` | integer | no | Limit to groups where current user has at least this [access level](members.md) |
```
GET /groups
@@ -94,13 +95,14 @@ Parameters:
| --------- | ---- | -------- | ----------- |
| `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) of the parent group |
| `skip_groups` | array of integers | no | Skip the group IDs passed |
-| `all_available` | boolean | no | Show all the groups you have access to (defaults to `false` for authenticated users, `true` for admin) |
+| `all_available` | boolean | no | Show all the groups you have access to (defaults to `false` for authenticated users, `true` for admin); Attributes `owned` and `min_access_level` have precedence |
| `search` | string | no | Return the list of authorized groups matching the search criteria |
| `order_by` | string | no | Order groups by `name`, `path` or `id`. Default is `name` |
| `sort` | string | no | Order groups in `asc` or `desc` order. Default is `asc` |
| `statistics` | boolean | no | Include group statistics (admins only) |
| `with_custom_attributes` | boolean | no | Include [custom attributes](custom_attributes.md) in response (admins only) |
-| `owned` | boolean | no | Limit to groups owned by the current user |
+| `owned` | boolean | no | Limit to groups explicitly owned by the current user |
+| `min_access_level` | integer | no | Limit to groups where current user has at least this [access level](members.md) |
```
GET /groups/:id/subgroups
diff --git a/doc/api/issues.md b/doc/api/issues.md
index 5613cb6d915..92fb3e9c307 100644
--- a/doc/api/issues.md
+++ b/doc/api/issues.md
@@ -37,7 +37,7 @@ GET /issues?my_reaction_emoji=star
| ------------------- | ---------------- | ---------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
| `state` | string | no | Return all issues or just those that are `opened` or `closed` |
| `labels` | string | no | Comma-separated list of label names, issues must have all labels to be returned. `No+Label` lists all issues with no labels |
-| `milestone` | string | no | The milestone title |
+| `milestone` | string | no | The milestone title. `No+Milestone` lists all issues with no milestone |
| `scope` | string | no | Return issues for the given scope: `created_by_me`, `assigned_to_me` or `all`. Defaults to `created_by_me`<br> For versions before 11.0, use the now deprecated `created-by-me` or `assigned-to-me` scopes instead.<br> _([Introduced][ce-13004] in GitLab 9.5. [Changed to snake_case][ce-18935] in GitLab 11.0)_ |
| `author_id` | integer | no | Return issues created by the given user `id`. Combine with `scope=all` or `scope=assigned_to_me`. _([Introduced][ce-13004] in GitLab 9.5)_ |
| `assignee_id` | integer | no | Return issues assigned to the given user `id` _([Introduced][ce-13004] in GitLab 9.5)_ |
@@ -151,7 +151,7 @@ GET /groups/:id/issues?my_reaction_emoji=star
| `state` | string | no | Return all issues or just those that are `opened` or `closed` |
| `labels` | string | no | Comma-separated list of label names, issues must have all labels to be returned. `No+Label` lists all issues with no labels |
| `iids[]` | Array[integer] | no | Return only the issues having the given `iid` |
-| `milestone` | string | no | The milestone title |
+| `milestone` | string | no | The milestone title. `No+Milestone` lists all issues with no milestone |
| `scope` | string | no | Return issues for the given scope: `created_by_me`, `assigned_to_me` or `all`.<br> For versions before 11.0, use the now deprecated `created-by-me` or `assigned-to-me` scopes instead.<br> _([Introduced][ce-13004] in GitLab 9.5. [Changed to snake_case][ce-18935] in GitLab 11.0)_ |
| `author_id` | integer | no | Return issues created by the given user `id` _([Introduced][ce-13004] in GitLab 9.5)_ |
| `assignee_id` | integer | no | Return issues assigned to the given user `id` _([Introduced][ce-13004] in GitLab 9.5)_ |
@@ -265,7 +265,7 @@ GET /projects/:id/issues?my_reaction_emoji=star
| `iids[]` | Array[integer] | no | Return only the milestone having the given `iid` |
| `state` | string | no | Return all issues or just those that are `opened` or `closed` |
| `labels` | string | no | Comma-separated list of label names, issues must have all labels to be returned. `No+Label` lists all issues with no labels |
-| `milestone` | string | no | The milestone title |
+| `milestone` | string | no | The milestone title. `No+Milestone` lists all issues with no milestone |
| `scope` | string | no | Return issues for the given scope: `created_by_me`, `assigned_to_me` or `all`.<br> For versions before 11.0, use the now deprecated `created-by-me` or `assigned-to-me` scopes instead.<br> _([Introduced][ce-13004] in GitLab 9.5. [Changed to snake_case][ce-18935] in GitLab 11.0)_ |
| `author_id` | integer | no | Return issues created by the given user `id` _([Introduced][ce-13004] in GitLab 9.5)_ |
| `assignee_id` | integer | no | Return issues assigned to the given user `id` _([Introduced][ce-13004] in GitLab 9.5)_ |
diff --git a/doc/api/projects.md b/doc/api/projects.md
index 9409afc88a8..f360b49c293 100644
--- a/doc/api/projects.md
+++ b/doc/api/projects.md
@@ -48,7 +48,7 @@ GET /projects
| `sort` | string | no | Return projects sorted in `asc` or `desc` order. Default is `desc` |
| `search` | string | no | Return list of projects matching the search criteria |
| `simple` | boolean | no | Return only limited fields for each project. This is a no-op without authentication as then _only_ simple fields are returned. |
-| `owned` | boolean | no | Limit by projects owned by the current user |
+| `owned` | boolean | no | Limit by projects explicitly owned by the current user |
| `membership` | boolean | no | Limit by projects that the current user is a member of |
| `starred` | boolean | no | Limit by projects starred by the current user |
| `statistics` | boolean | no | Include project statistics |
@@ -57,6 +57,7 @@ GET /projects
| `with_merge_requests_enabled` | boolean | no | Limit by enabled merge requests feature |
| `wiki_checksum_failed` | boolean | no | Limit projects where the wiki checksum calculation has failed _([Introduced][ee-6137] in [GitLab Premium][eep] 11.2)_ |
| `repository_checksum_failed` | boolean | no | Limit projects where the repository checksum calculation has failed _([Introduced][ee-6137] in [GitLab Premium][eep] 11.2)_ |
+| `min_access_level` | integer | no | Limit by current user minimal [access level](members.md) |
When `simple=true` or the user is unauthenticated this returns something like:
@@ -273,13 +274,14 @@ GET /users/:user_id/projects
| `sort` | string | no | Return projects sorted in `asc` or `desc` order. Default is `desc` |
| `search` | string | no | Return list of projects matching the search criteria |
| `simple` | boolean | no | Return only limited fields for each project. This is a no-op without authentication as then _only_ simple fields are returned. |
-| `owned` | boolean | no | Limit by projects owned by the current user |
+| `owned` | boolean | no | Limit by projects explicitly owned by the current user |
| `membership` | boolean | no | Limit by projects that the current user is a member of |
| `starred` | boolean | no | Limit by projects starred by the current user |
| `statistics` | boolean | no | Include project statistics |
| `with_custom_attributes` | boolean | no | Include [custom attributes](custom_attributes.md) in response (admins only) |
| `with_issues_enabled` | boolean | no | Limit by enabled issues feature |
| `with_merge_requests_enabled` | boolean | no | Limit by enabled merge requests feature |
+| `min_access_level` | integer | no | Limit by current user minimal [access level](members.md) |
```json
[
@@ -769,13 +771,14 @@ GET /projects/:id/forks
| `sort` | string | no | Return projects sorted in `asc` or `desc` order. Default is `desc` |
| `search` | string | no | Return list of projects matching the search criteria |
| `simple` | boolean | no | Return only limited fields for each project. This is a no-op without authentication as then _only_ simple fields are returned. |
-| `owned` | boolean | no | Limit by projects owned by the current user |
+| `owned` | boolean | no | Limit by projects explicitly owned by the current user |
| `membership` | boolean | no | Limit by projects that the current user is a member of |
| `starred` | boolean | no | Limit by projects starred by the current user |
| `statistics` | boolean | no | Include project statistics |
| `with_custom_attributes` | boolean | no | Include [custom attributes](custom_attributes.md) in response (admins only) |
| `with_issues_enabled` | boolean | no | Limit by enabled issues feature |
| `with_merge_requests_enabled` | boolean | no | Limit by enabled merge requests feature |
+| `min_access_level` | integer | no | Limit by current user minimal [access level](members.md) |
```bash
curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/5/forks"
diff --git a/doc/api/services.md b/doc/api/services.md
index aeb48ccc36c..efa173180bb 100644
--- a/doc/api/services.md
+++ b/doc/api/services.md
@@ -443,6 +443,54 @@ Get Gemnasium service settings for a project.
GET /projects/:id/services/gemnasium
```
+## Hangouts Chat
+
+Google GSuite team collaboration tool.
+
+>**Note:** This service was [introduced in v11.2](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/20290)
+
+### Create/Edit Hangouts Chat service
+
+Set Hangouts Chat service for a project.
+
+```
+PUT /projects/:id/services/hangouts_chat
+```
+
+>**Note:** Specific event parameters (e.g. `push_events` flag) were [introduced in v10.4][11435]
+
+Parameters:
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `webhook` | string | true | The Hangouts Chat webhook. e.g. https://chat.googleapis.com/v1/spaces... |
+| `notify_only_broken_pipelines` | boolean | false | Send notifications for broken pipelines |
+| `notify_only_default_branch` | boolean | false | Send notifications only for the default branch |
+| `push_events` | boolean | false | Enable notifications for push events |
+| `issues_events` | boolean | false | Enable notifications for issue events |
+| `confidential_issues_events` | boolean | false | Enable notifications for confidential issue events |
+| `merge_requests_events` | boolean | false | Enable notifications for merge request events |
+| `tag_push_events` | boolean | false | Enable notifications for tag push events |
+| `note_events` | boolean | false | Enable notifications for note events |
+| `pipeline_events` | boolean | false | Enable notifications for pipeline events |
+| `wiki_page_events` | boolean | false | Enable notifications for wiki page events |
+
+### Delete Hangouts Chat service
+
+Delete Hangouts Chat service for a project.
+
+```
+DELETE /projects/:id/services/hangouts_chat
+```
+
+### Get Hangouts Chat service settings
+
+Get Hangouts Chat service settings for a project.
+
+```
+GET /projects/:id/services/hangouts_chat
+```
+
## HipChat
Private group chat and IM
diff --git a/doc/ci/variables/README.md b/doc/ci/variables/README.md
index 84bd64d50cd..115e6e390c9 100644
--- a/doc/ci/variables/README.md
+++ b/doc/ci/variables/README.md
@@ -64,7 +64,7 @@ future GitLab releases.**
| **CI_JOB_MANUAL** | 8.12 | all | The flag to indicate that job was manually started |
| **CI_JOB_NAME** | 9.0 | 0.5 | The name of the job as defined in `.gitlab-ci.yml` |
| **CI_JOB_STAGE** | 9.0 | 0.5 | The name of the stage as defined in `.gitlab-ci.yml` |
-| **CI_JOB_TOKEN** | 9.0 | 1.2 | Token used for authenticating with the GitLab Container Registry |
+| **CI_JOB_TOKEN** | 9.0 | 1.2 | Token used for authenticating with the [GitLab Container Registry][registry] and downloading [dependent repositories][dependent-repositories] |
| **CI_JOB_URL** | 11.1 | 0.5 | Job details URL |
| **CI_REPOSITORY_URL** | 9.0 | all | The URL to clone the Git repository |
| **CI_RUNNER_DESCRIPTION** | 8.10 | 0.5 | The description of the runner as saved in GitLab |
@@ -566,3 +566,5 @@ Below you can find supported syntax reference:
[subgroups]: ../../user/group/subgroups/index.md
[builds-policies]: ../yaml/README.md#only-and-except-complex
[gitlab-deploy-token]: ../../user/project/deploy_tokens/index.md#gitlab-deploy-token
+[registry]: ../../user/project/container_registry.md
+[dependent-repositories]: ../../user/project/new_ci_build_permissions_model.md#dependent-repositories \ No newline at end of file
diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md
index 096b64eb881..d95f8c7c8cc 100644
--- a/doc/ci/yaml/README.md
+++ b/doc/ci/yaml/README.md
@@ -1411,43 +1411,6 @@ variables:
You can set it globally or per-job in the [`variables`](#variables) section.
-### Custom build directories
-
-> [Introduced][gitlab-runner-876] in Gitlab Runner 11.1
-
-NOTE: **Note:**
-This can only be used when `custom_build_dir` is set to true in the [Runner's
-configuration](https://docs.gitlab.com/runner/configuration/advanced-configuration.html).
-
-By default, GitLab Runner clones the repository in the `/builds` directory,
-but sometimes your project might require to have the code in a specific
-directory, like the GO projects for example. In that case, you can specify
-the `CI_PROJECT_DIR` variable to tell the Runner in which directory to clone
-the repository:
-
-```yml
-image: golang:1.10-alpine3.7
-
-variables:
- CI_PROJECT_DIR: /go/src/gitlab.com/namespace/project-name
-
-stages:
- - test
-
-dir:
- stage: test
- script:
- - pwd # /go/src/gitlab.com/namespace/project-name
-```
-
-The following executors may use this feature only when
-[concurrent](https://docs.gitlab.com/runner/configuration/advanced-configuration.html#the-global-section)
-is set to `1`:
-
-- `shell`
-- `ssh`
-- `docker`, `docker+machine` when the job's working directory is mounted as a host volume.
-
## Special YAML features
It's possible to use special YAML features like anchors (`&`), aliases (`*`)
@@ -1641,6 +1604,5 @@ CI with various languages.
[ce-7983]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/7983
[ce-7447]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/7447
[ce-12909]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/12909
-[gitlab-runner-876]: https://gitlab.com/gitlab-org/gitlab-runner/merge_requests/876
[schedules]: ../../user/project/pipelines/schedules.md
[variables-expressions]: ../variables/README.md#variables-expressions
diff --git a/doc/topics/autodevops/index.md b/doc/topics/autodevops/index.md
index de1d366adc3..f5574506595 100644
--- a/doc/topics/autodevops/index.md
+++ b/doc/topics/autodevops/index.md
@@ -591,7 +591,7 @@ also be customized, and you can easily use a [custom buildpack](#custom-buildpac
| `CANARY_ENABLED` | From GitLab 11.0, this variable can be used to define a [deploy policy for canary environments](#deploy-policy-for-canary-environments). |
| `INCREMENTAL_ROLLOUT_ENABLED`| From GitLab 10.8, this variable can be used to enable an [incremental rollout](#incremental-rollout-to-production) of your application for the production environment. |
| `TEST_DISABLED` | From GitLab 11.0, this variable can be used to disable the `test` job. If the variable is present, the job will not be created. |
-| `CODEQUALITY_DISABLED` | From GitLab 11.0, this variable can be used to disable the `codequality` job. If the variable is present, the job will not be created. |
+| `CODE_QUALITY_DISABLED` | From GitLab 11.0, this variable can be used to disable the `codequality` job. If the variable is present, the job will not be created. |
| `SAST_DISABLED` | From GitLab 11.0, this variable can be used to disable the `sast` job. If the variable is present, the job will not be created. |
| `DEPENDENCY_SCANNING_DISABLED` | From GitLab 11.0, this variable can be used to disable the `dependency_scanning` job. If the variable is present, the job will not be created. |
| `CONTAINER_SCANNING_DISABLED` | From GitLab 11.0, this variable can be used to disable the `sast:container` job. If the variable is present, the job will not be created. |
diff --git a/doc/update/mysql_to_postgresql.md b/doc/update/mysql_to_postgresql.md
index 0b38cde811d..7c6a14cb104 100644
--- a/doc/update/mysql_to_postgresql.md
+++ b/doc/update/mysql_to_postgresql.md
@@ -200,6 +200,12 @@ sudo apt-get install pgloader
sudo -u git cp config/database.yml.postgresql config/database.yml
sudo -u git -H chmod o-rwx config/database.yml
```
+1. Install Gems related to Postgresql
+
+ ``` bash
+ sudo -u git -H rm .bundle/config
+ sudo -u git -H bundle install --deployment --without development test mysql aws kerberos
+ ```
1. Run the following commands to prepare the schema:
diff --git a/doc/user/profile/personal_access_tokens.md b/doc/user/profile/personal_access_tokens.md
index 9b4fdd65e2f..601db5f424d 100644
--- a/doc/user/profile/personal_access_tokens.md
+++ b/doc/user/profile/personal_access_tokens.md
@@ -48,6 +48,7 @@ the following table.
| `api` | Grants complete access to the API (read/write) ([introduced][ce-5951] in GitLab 8.15). Required for accessing Git repositories over HTTP when 2FA is enabled. |
| `read_registry` | Allows to read [container registry] images if a project is private and authorization is required ([introduced][ce-11845] in GitLab 9.3). |
| `sudo` | Allows performing API actions as any user in the system (if the authenticated user is an admin) ([introduced][ce-14838] in GitLab 10.2). |
+| `read_repository` | Allows read-access to the repository through git clone). |
[2fa]: ../account/two_factor_authentication.md
[api]: ../../api/README.md
diff --git a/doc/user/project/integrations/hangouts_chat.md b/doc/user/project/integrations/hangouts_chat.md
new file mode 100644
index 00000000000..6ab44420a10
--- /dev/null
+++ b/doc/user/project/integrations/hangouts_chat.md
@@ -0,0 +1,27 @@
+# Hangouts Chat service
+
+The Hangouts Chat service sends notifications from GitLab to the room for which the webhook was created.
+
+## On Hangouts Chat
+
+1. Open the chat room in which you want to see the notifications.
+1. From the chat room menu, select **Configure Webhooks**.
+1. Click on **ADD WEBHOOK** and fill in the name of the bot that will post the messages. Optionally define avatar.
+1. Click **SAVE** and copy the **Webhook URL** of your webhook.
+
+See also [the Hangouts Chat documentation for configuring incoming webhooks](https://developers.google.com/hangouts/chat/how-tos/webhooks)
+
+## On GitLab
+
+When you have the **Webhook URL** for your Hangouts Chat room webhook, you can setup the GitLab service.
+
+1. Navigate to the [Integrations page](project_services.md#accessing-the-project-services) in your project's settings, i.e. **Project > Settings > Integrations**.
+1. Select the **Hangouts Chat** project service to configure it.
+1. Check the **Active** checkbox to turn on the service.
+1. Check the checkboxes corresponding to the GitLab events you want to receive.
+1. Paste the **Webhook URL** that you copied from the Hangouts Chat configuration step.
+1. Configure the remaining options and click `Save changes`.
+
+Your Hangouts Chat room will now start receiving GitLab event notifications as configured.
+
+![Hangouts Chat configuration](img/hangouts_chat_configuration.png)
diff --git a/doc/user/project/integrations/img/hangouts_chat_configuration.png b/doc/user/project/integrations/img/hangouts_chat_configuration.png
new file mode 100644
index 00000000000..33fadbe6547
--- /dev/null
+++ b/doc/user/project/integrations/img/hangouts_chat_configuration.png
Binary files differ
diff --git a/doc/user/project/integrations/project_services.md b/doc/user/project/integrations/project_services.md
index 8c51eb9915e..05ee1b4e6d7 100644
--- a/doc/user/project/integrations/project_services.md
+++ b/doc/user/project/integrations/project_services.md
@@ -35,6 +35,7 @@ Click on the service links to see further configuration instructions and details
| External Wiki | Replaces the link to the internal wiki with a link to an external wiki |
| Flowdock | Flowdock is a collaboration web app for technical teams |
| Gemnasium _(Has been deprecated in GitLab 11.0)_ | Gemnasium monitors your project dependencies and alerts you about updates and security vulnerabilities |
+| [Hangouts Chat](hangouts_chat.md) | Receive events notifications in Google Hangouts Chat |
| [HipChat](hipchat.md) | Private group chat and IM |
| [Irker (IRC gateway)](irker.md) | Send IRC messages, on update, to a list of recipients through an Irker gateway |
| [JIRA](jira.md) | JIRA issue tracker |
diff --git a/lib/api/groups.rb b/lib/api/groups.rb
index 797b04df059..b4f441f6a4f 100644
--- a/lib/api/groups.rb
+++ b/lib/api/groups.rb
@@ -34,11 +34,12 @@ module API
optional :owned, type: Boolean, default: false, desc: 'Limit by owned by authenticated user'
optional :order_by, type: String, values: %w[name path id], default: 'name', desc: 'Order by name, path or id'
optional :sort, type: String, values: %w[asc desc], default: 'asc', desc: 'Sort by asc (ascending) or desc (descending)'
+ optional :min_access_level, type: Integer, values: Gitlab::Access.all_values, desc: 'Minimum access level of authenticated user'
use :pagination
end
def find_groups(params, parent_id = nil)
- find_params = params.slice(:all_available, :custom_attributes, :owned)
+ find_params = params.slice(:all_available, :custom_attributes, :owned, :min_access_level)
find_params[:parent] = find_group!(parent_id) if parent_id
find_params[:all_available] =
find_params.fetch(:all_available, current_user&.full_private_access?)
diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb
index f7737468148..be17653dbb2 100644
--- a/lib/api/helpers.rb
+++ b/lib/api/helpers.rb
@@ -389,6 +389,7 @@ module API
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]
+ finder_params[:min_access_level] = params[:min_access_level] if params[:min_access_level]
finder_params
end
diff --git a/lib/api/projects.rb b/lib/api/projects.rb
index 889e3d4f819..eadde7b17bb 100644
--- a/lib/api/projects.rb
+++ b/lib/api/projects.rb
@@ -54,6 +54,7 @@ module API
optional :membership, type: Boolean, default: false, desc: 'Limit by projects that the current user is a member of'
optional :with_issues_enabled, type: Boolean, default: false, desc: 'Limit by enabled issues feature'
optional :with_merge_requests_enabled, type: Boolean, default: false, desc: 'Limit by enabled merge requests feature'
+ optional :min_access_level, type: Integer, values: Gitlab::Access.all_values, desc: 'Limit by minimum access level of authenticated user'
use :optional_filter_params_ee
end
diff --git a/lib/api/services.rb b/lib/api/services.rb
index 553e8dff4b9..1f2bf546cd7 100644
--- a/lib/api/services.rb
+++ b/lib/api/services.rb
@@ -368,6 +368,14 @@ module API
desc: "The project's slug on gemnasium.com"
}
],
+ 'hangouts-chat' => [
+ {
+ required: true,
+ name: :webhook,
+ type: String,
+ desc: 'The Hangouts Chat webhook. e.g. https://chat.googleapis.com/v1/spaces…'
+ }
+ ],
'hipchat' => [
{
required: true,
@@ -688,6 +696,7 @@ module API
ExternalWikiService,
FlowdockService,
GemnasiumService,
+ HangoutsChatService,
HipchatService,
IrkerService,
JiraService,
diff --git a/lib/api/users.rb b/lib/api/users.rb
index 5aaaf104dff..6da6c2b43de 100644
--- a/lib/api/users.rb
+++ b/lib/api/users.rb
@@ -42,6 +42,7 @@ module API
optional :can_create_group, type: Boolean, desc: 'Flag indicating the user can create groups'
optional :external, type: Boolean, desc: 'Flag indicating the user is an external user'
optional :avatar, type: File, desc: 'Avatar image for user'
+ optional :min_access_level, type: Integer, values: Gitlab::Access.all_values, desc: 'Limit by minimum access level of authenticated user'
all_or_none_of :extern_uid, :provider
end
diff --git a/lib/banzai/filter/absolute_link_filter.rb b/lib/banzai/filter/absolute_link_filter.rb
index 1ec6201523f..04ec568eee3 100644
--- a/lib/banzai/filter/absolute_link_filter.rb
+++ b/lib/banzai/filter/absolute_link_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'uri'
module Banzai
diff --git a/lib/banzai/filter/abstract_reference_filter.rb b/lib/banzai/filter/abstract_reference_filter.rb
index b39b11009b3..ad0806df8e6 100644
--- a/lib/banzai/filter/abstract_reference_filter.rb
+++ b/lib/banzai/filter/abstract_reference_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Filter
# Issues, Merge Requests, Snippets, Commits and Commit Ranges share
diff --git a/lib/banzai/filter/ascii_doc_post_processing_filter.rb b/lib/banzai/filter/ascii_doc_post_processing_filter.rb
index c9fcf057c5f..88439f06b5f 100644
--- a/lib/banzai/filter/ascii_doc_post_processing_filter.rb
+++ b/lib/banzai/filter/ascii_doc_post_processing_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Filter
class AsciiDocPostProcessingFilter < HTML::Pipeline::Filter
diff --git a/lib/banzai/filter/autolink_filter.rb b/lib/banzai/filter/autolink_filter.rb
index 4a143baeef6..deda4b1872e 100644
--- a/lib/banzai/filter/autolink_filter.rb
+++ b/lib/banzai/filter/autolink_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'uri'
module Banzai
diff --git a/lib/banzai/filter/blockquote_fence_filter.rb b/lib/banzai/filter/blockquote_fence_filter.rb
index 7108e828c6d..ad367cc5efe 100644
--- a/lib/banzai/filter/blockquote_fence_filter.rb
+++ b/lib/banzai/filter/blockquote_fence_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Filter
class BlockquoteFenceFilter < HTML::Pipeline::TextFilter
diff --git a/lib/banzai/filter/color_filter.rb b/lib/banzai/filter/color_filter.rb
index 6ab29ac281f..6d9bdb9cbd3 100644
--- a/lib/banzai/filter/color_filter.rb
+++ b/lib/banzai/filter/color_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Filter
# HTML filter that renders `color` followed by a color "chip".
diff --git a/lib/banzai/filter/commit_range_reference_filter.rb b/lib/banzai/filter/commit_range_reference_filter.rb
index 01b3b0dafb9..d6b46236a49 100644
--- a/lib/banzai/filter/commit_range_reference_filter.rb
+++ b/lib/banzai/filter/commit_range_reference_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Filter
# HTML filter that replaces commit range references with links.
diff --git a/lib/banzai/filter/commit_reference_filter.rb b/lib/banzai/filter/commit_reference_filter.rb
index 8cd92a1adba..c3e5ac41cb8 100644
--- a/lib/banzai/filter/commit_reference_filter.rb
+++ b/lib/banzai/filter/commit_reference_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Filter
# HTML filter that replaces commit references with links.
diff --git a/lib/banzai/filter/commit_trailers_filter.rb b/lib/banzai/filter/commit_trailers_filter.rb
index 7b55e8b36f6..f49c4b403db 100644
--- a/lib/banzai/filter/commit_trailers_filter.rb
+++ b/lib/banzai/filter/commit_trailers_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Filter
# HTML filter that replaces users' names and emails in commit trailers
diff --git a/lib/banzai/filter/emoji_filter.rb b/lib/banzai/filter/emoji_filter.rb
index 4eccd9d5ed5..c87948a30bf 100644
--- a/lib/banzai/filter/emoji_filter.rb
+++ b/lib/banzai/filter/emoji_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Filter
# HTML filter that replaces :emoji: and unicode with images.
diff --git a/lib/banzai/filter/epic_reference_filter.rb b/lib/banzai/filter/epic_reference_filter.rb
index 265924abe24..e06e2fb3870 100644
--- a/lib/banzai/filter/epic_reference_filter.rb
+++ b/lib/banzai/filter/epic_reference_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Filter
# The actual filter is implemented in the EE mixin
diff --git a/lib/banzai/filter/external_issue_reference_filter.rb b/lib/banzai/filter/external_issue_reference_filter.rb
index ed01a72ff9f..b4a7a44e109 100644
--- a/lib/banzai/filter/external_issue_reference_filter.rb
+++ b/lib/banzai/filter/external_issue_reference_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Filter
# HTML filter that replaces external issue tracker references with links.
diff --git a/lib/banzai/filter/external_link_filter.rb b/lib/banzai/filter/external_link_filter.rb
index d6327ef31cb..2e6d742de27 100644
--- a/lib/banzai/filter/external_link_filter.rb
+++ b/lib/banzai/filter/external_link_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Filter
# HTML Filter to modify the attributes of external links
diff --git a/lib/banzai/filter/gollum_tags_filter.rb b/lib/banzai/filter/gollum_tags_filter.rb
index bb9f488cd87..0c1bbd2d250 100644
--- a/lib/banzai/filter/gollum_tags_filter.rb
+++ b/lib/banzai/filter/gollum_tags_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Filter
# HTML Filter for parsing Gollum's tags in HTML. It's only parses the
diff --git a/lib/banzai/filter/html_entity_filter.rb b/lib/banzai/filter/html_entity_filter.rb
index e008fd428b0..406c2d3c96b 100644
--- a/lib/banzai/filter/html_entity_filter.rb
+++ b/lib/banzai/filter/html_entity_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'erb'
module Banzai
diff --git a/lib/banzai/filter/image_lazy_load_filter.rb b/lib/banzai/filter/image_lazy_load_filter.rb
index 4cd9b02b76c..afaee70f351 100644
--- a/lib/banzai/filter/image_lazy_load_filter.rb
+++ b/lib/banzai/filter/image_lazy_load_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Filter
# HTML filter that moves the value of image `src` attributes to `data-src`
@@ -5,7 +7,7 @@ module Banzai
class ImageLazyLoadFilter < HTML::Pipeline::Filter
def call
doc.xpath('descendant-or-self::img').each do |img|
- img['class'] ||= '' << 'lazy'
+ img.add_class('lazy')
img['data-src'] = img['src']
img['src'] = LazyImageTagHelper.placeholder_image
end
diff --git a/lib/banzai/filter/image_link_filter.rb b/lib/banzai/filter/image_link_filter.rb
index f318c425962..884a94fb761 100644
--- a/lib/banzai/filter/image_link_filter.rb
+++ b/lib/banzai/filter/image_link_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Filter
# HTML filter that wraps links around inline images.
diff --git a/lib/banzai/filter/inline_diff_filter.rb b/lib/banzai/filter/inline_diff_filter.rb
index 73e82a4d7e3..e9ddc6e0e3d 100644
--- a/lib/banzai/filter/inline_diff_filter.rb
+++ b/lib/banzai/filter/inline_diff_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Filter
class InlineDiffFilter < HTML::Pipeline::Filter
diff --git a/lib/banzai/filter/issuable_reference_filter.rb b/lib/banzai/filter/issuable_reference_filter.rb
index 7addf09be73..2963cba91e8 100644
--- a/lib/banzai/filter/issuable_reference_filter.rb
+++ b/lib/banzai/filter/issuable_reference_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Filter
class IssuableReferenceFilter < AbstractReferenceFilter
diff --git a/lib/banzai/filter/issuable_state_filter.rb b/lib/banzai/filter/issuable_state_filter.rb
index 1a415232545..d7fe012883d 100644
--- a/lib/banzai/filter/issuable_state_filter.rb
+++ b/lib/banzai/filter/issuable_state_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Filter
# HTML filter that appends state information to issuable links.
diff --git a/lib/banzai/filter/issue_reference_filter.rb b/lib/banzai/filter/issue_reference_filter.rb
index 6877cae8c55..f85be042999 100644
--- a/lib/banzai/filter/issue_reference_filter.rb
+++ b/lib/banzai/filter/issue_reference_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Filter
# HTML filter that replaces issue references with links. References to
diff --git a/lib/banzai/filter/label_reference_filter.rb b/lib/banzai/filter/label_reference_filter.rb
index a5f38046a43..b92e9e55bb9 100644
--- a/lib/banzai/filter/label_reference_filter.rb
+++ b/lib/banzai/filter/label_reference_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Filter
# HTML filter that replaces label references with links.
diff --git a/lib/banzai/filter/markdown_filter.rb b/lib/banzai/filter/markdown_filter.rb
index 944363f17d3..cdf758472c1 100644
--- a/lib/banzai/filter/markdown_filter.rb
+++ b/lib/banzai/filter/markdown_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Filter
class MarkdownFilter < HTML::Pipeline::TextFilter
diff --git a/lib/banzai/filter/math_filter.rb b/lib/banzai/filter/math_filter.rb
index b6e784c886b..9d1bc3cf60c 100644
--- a/lib/banzai/filter/math_filter.rb
+++ b/lib/banzai/filter/math_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'uri'
module Banzai
diff --git a/lib/banzai/filter/merge_request_reference_filter.rb b/lib/banzai/filter/merge_request_reference_filter.rb
index 10c40568006..7098767b583 100644
--- a/lib/banzai/filter/merge_request_reference_filter.rb
+++ b/lib/banzai/filter/merge_request_reference_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Filter
# HTML filter that replaces merge request references with links. References
diff --git a/lib/banzai/filter/mermaid_filter.rb b/lib/banzai/filter/mermaid_filter.rb
index 65c131e08d9..7c8b165a330 100644
--- a/lib/banzai/filter/mermaid_filter.rb
+++ b/lib/banzai/filter/mermaid_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Filter
class MermaidFilter < HTML::Pipeline::Filter
diff --git a/lib/banzai/filter/milestone_reference_filter.rb b/lib/banzai/filter/milestone_reference_filter.rb
index af8448937b3..328c8c1803b 100644
--- a/lib/banzai/filter/milestone_reference_filter.rb
+++ b/lib/banzai/filter/milestone_reference_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Filter
# HTML filter that replaces milestone references with links.
diff --git a/lib/banzai/filter/plantuml_filter.rb b/lib/banzai/filter/plantuml_filter.rb
index 28933c78966..caba8955bac 100644
--- a/lib/banzai/filter/plantuml_filter.rb
+++ b/lib/banzai/filter/plantuml_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "nokogiri"
require "asciidoctor-plantuml/plantuml"
diff --git a/lib/banzai/filter/redactor_filter.rb b/lib/banzai/filter/redactor_filter.rb
index caf11fe94c4..1f091f594f8 100644
--- a/lib/banzai/filter/redactor_filter.rb
+++ b/lib/banzai/filter/redactor_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Filter
# HTML filter that removes references to records that the current user does
diff --git a/lib/banzai/filter/reference_filter.rb b/lib/banzai/filter/reference_filter.rb
index 2411dd2cdfc..e5164e7f72a 100644
--- a/lib/banzai/filter/reference_filter.rb
+++ b/lib/banzai/filter/reference_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Filter
# Base class for GitLab Flavored Markdown reference filters.
diff --git a/lib/banzai/filter/relative_link_filter.rb b/lib/banzai/filter/relative_link_filter.rb
index 262458a872a..8e838d04bad 100644
--- a/lib/banzai/filter/relative_link_filter.rb
+++ b/lib/banzai/filter/relative_link_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'uri'
module Banzai
diff --git a/lib/banzai/filter/sanitization_filter.rb b/lib/banzai/filter/sanitization_filter.rb
index 8275bb9e149..80b9d3d045f 100644
--- a/lib/banzai/filter/sanitization_filter.rb
+++ b/lib/banzai/filter/sanitization_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Filter
# Sanitize HTML
diff --git a/lib/banzai/filter/set_direction_filter.rb b/lib/banzai/filter/set_direction_filter.rb
index c2976aeb7c6..45b259a2faf 100644
--- a/lib/banzai/filter/set_direction_filter.rb
+++ b/lib/banzai/filter/set_direction_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Filter
# HTML filter that sets dir="auto" for RTL languages support
diff --git a/lib/banzai/filter/snippet_reference_filter.rb b/lib/banzai/filter/snippet_reference_filter.rb
index 881e10afb9f..f4b6edb6174 100644
--- a/lib/banzai/filter/snippet_reference_filter.rb
+++ b/lib/banzai/filter/snippet_reference_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Filter
# HTML filter that replaces snippet references with links. References to
diff --git a/lib/banzai/filter/syntax_highlight_filter.rb b/lib/banzai/filter/syntax_highlight_filter.rb
index 6dbf0d68fe8..8a7f9045c24 100644
--- a/lib/banzai/filter/syntax_highlight_filter.rb
+++ b/lib/banzai/filter/syntax_highlight_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'rouge/plugins/common_mark'
require 'rouge/plugins/redcarpet'
@@ -15,7 +17,7 @@ module Banzai
end
def highlight_node(node)
- css_classes = 'code highlight js-syntax-highlight'
+ css_classes = +'code highlight js-syntax-highlight'
lang = node.attr('lang')
retried = false
diff --git a/lib/banzai/filter/table_of_contents_filter.rb b/lib/banzai/filter/table_of_contents_filter.rb
index b32660a8341..c6d1e028eaa 100644
--- a/lib/banzai/filter/table_of_contents_filter.rb
+++ b/lib/banzai/filter/table_of_contents_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Filter
# HTML filter that adds an anchor child element to all Headers in a
@@ -19,7 +21,7 @@ module Banzai
def call
return doc if context[:no_header_anchors]
- result[:toc] = ""
+ result[:toc] = +""
headers = Hash.new(0)
header_root = current_header = HeaderNode.new
diff --git a/lib/banzai/filter/task_list_filter.rb b/lib/banzai/filter/task_list_filter.rb
index 9fa5f589f3e..ef35a49edcb 100644
--- a/lib/banzai/filter/task_list_filter.rb
+++ b/lib/banzai/filter/task_list_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'task_list/filter'
module Banzai
diff --git a/lib/banzai/filter/user_reference_filter.rb b/lib/banzai/filter/user_reference_filter.rb
index c7fa8a8119f..11960047e5b 100644
--- a/lib/banzai/filter/user_reference_filter.rb
+++ b/lib/banzai/filter/user_reference_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Filter
# HTML filter that replaces user or group references with links.
diff --git a/lib/banzai/filter/video_link_filter.rb b/lib/banzai/filter/video_link_filter.rb
index 35cb10eae5d..0fb59c914c3 100644
--- a/lib/banzai/filter/video_link_filter.rb
+++ b/lib/banzai/filter/video_link_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Filter
# Find every image that isn't already wrapped in an `a` tag, and that has
diff --git a/lib/banzai/filter/wiki_link_filter.rb b/lib/banzai/filter/wiki_link_filter.rb
index 269d5bf74fa..870721f895d 100644
--- a/lib/banzai/filter/wiki_link_filter.rb
+++ b/lib/banzai/filter/wiki_link_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'uri'
module Banzai
diff --git a/lib/banzai/filter/yaml_front_matter_filter.rb b/lib/banzai/filter/yaml_front_matter_filter.rb
index 58e3e81209e..295964dd75d 100644
--- a/lib/banzai/filter/yaml_front_matter_filter.rb
+++ b/lib/banzai/filter/yaml_front_matter_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Banzai
module Filter
class YamlFrontMatterFilter < HTML::Pipeline::Filter
diff --git a/lib/feature.rb b/lib/feature.rb
index 314ae224d90..d27b2b0e72f 100644
--- a/lib/feature.rb
+++ b/lib/feature.rb
@@ -39,7 +39,7 @@ class Feature
# Flipper creates on-memory features when asked for a not-yet-created one.
# If we want to check if a feature has been actually set, we look for it
# on the persisted features list.
- persisted_names.include?(feature.name)
+ persisted_names.include?(feature.name.to_s)
end
def enabled?(key, thing = nil)
diff --git a/lib/gitlab/git/repository.rb b/lib/gitlab/git/repository.rb
index 5b955753a92..7bd5927d15e 100644
--- a/lib/gitlab/git/repository.rb
+++ b/lib/gitlab/git/repository.rb
@@ -826,6 +826,10 @@ module Gitlab
end
end
+ # This method, fetch_ref, is used from within
+ # Gitlab::Git::OperationService. OperationService will eventually only
+ # exist in gitaly-ruby. When we delete OperationService from gitlab-ce
+ # we can also remove fetch_ref.
def fetch_ref(source_repository, source_ref:, target_ref:)
Gitlab::Git.check_namespace!(source_repository)
source_repository = RemoteRepository.new(source_repository) unless source_repository.is_a?(RemoteRepository)
@@ -1253,14 +1257,6 @@ module Gitlab
run_git(args, env: source_repository.fetch_env)
end
- def rugged_add_remote(remote_name, url, mirror_refmap)
- rugged.remotes.create(remote_name, url)
-
- set_remote_as_mirror(remote_name, refmap: mirror_refmap) if mirror_refmap
- rescue Rugged::ConfigError
- remote_update(remote_name, url: url)
- end
-
def gitaly_delete_refs(*ref_names)
gitaly_ref_client.delete_refs(refs: ref_names) if ref_names.any?
end
diff --git a/lib/gitlab/import_export/merge_request_parser.rb b/lib/gitlab/import_export/merge_request_parser.rb
index f3d7407383c..d0527f014a7 100644
--- a/lib/gitlab/import_export/merge_request_parser.rb
+++ b/lib/gitlab/import_export/merge_request_parser.rb
@@ -25,6 +25,7 @@ module Gitlab
@project.repository.create_branch(@merge_request.target_branch, @merge_request.target_branch_sha)
end
+ # Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/1295
def fetch_ref
@project.repository.fetch_ref(@project.repository, source_ref: @diff_head_sha, target_ref: @merge_request.source_branch)
end
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 75b88a2cb2f..09a35b5da07 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -465,7 +465,7 @@ msgstr ""
msgid "An error occurred while importing project: ${details}"
msgstr ""
-msgid "An error occurred while loading commits"
+msgid "An error occurred while loading commit signatures"
msgstr ""
msgid "An error occurred while loading diff"
diff --git a/spec/features/projects/commits/user_browses_commits_spec.rb b/spec/features/projects/commits/user_browses_commits_spec.rb
index 23d8d606790..534cfe1eb12 100644
--- a/spec/features/projects/commits/user_browses_commits_spec.rb
+++ b/spec/features/projects/commits/user_browses_commits_spec.rb
@@ -238,6 +238,5 @@ def check_author_link(email, author)
author_link = find('.commit-author-link')
expect(author_link['href']).to eq(user_path(author))
- expect(author_link['title']).to eq(email)
expect(find('.commit-author-name').text).to eq(author.name)
end
diff --git a/spec/features/signed_commits_spec.rb b/spec/features/signed_commits_spec.rb
index 3d05474dca2..5003eb508c2 100644
--- a/spec/features/signed_commits_spec.rb
+++ b/spec/features/signed_commits_spec.rb
@@ -7,7 +7,7 @@ describe 'GPG signed commits', :js do
user = create :user, email: 'unrelated.user@example.org'
project.add_maintainer(user)
- Sidekiq::Testing.inline! do
+ perform_enqueued_jobs do
create :gpg_key, key: GpgHelpers::User1.public_key, user: user
end
@@ -21,7 +21,7 @@ describe 'GPG signed commits', :js do
end
# user changes his email which makes the gpg key verified
- Sidekiq::Testing.inline! do
+ perform_enqueued_jobs do
user.skip_reconfirmation!
user.update!(email: GpgHelpers::User1.emails.first)
end
@@ -48,7 +48,7 @@ describe 'GPG signed commits', :js do
end
# user adds the gpg key which makes the signature valid
- Sidekiq::Testing.inline! do
+ perform_enqueued_jobs do
create :gpg_key, key: GpgHelpers::User1.public_key, user: user
end
@@ -66,7 +66,7 @@ describe 'GPG signed commits', :js do
end
let(:user_1_key) do
- Sidekiq::Testing.inline! do
+ perform_enqueued_jobs do
create :gpg_key, key: GpgHelpers::User1.public_key, user: user_1
end
end
@@ -79,7 +79,7 @@ describe 'GPG signed commits', :js do
end
let(:user_2_key) do
- Sidekiq::Testing.inline! do
+ perform_enqueued_jobs do
create :gpg_key, key: GpgHelpers::User2.public_key, user: user_2
end
end
diff --git a/spec/helpers/button_helper_spec.rb b/spec/helpers/button_helper_spec.rb
index fee8df10129..630f3eff258 100644
--- a/spec/helpers/button_helper_spec.rb
+++ b/spec/helpers/button_helper_spec.rb
@@ -121,6 +121,8 @@ describe ButtonHelper do
end
describe 'clipboard_button' do
+ include IconsHelper
+
let(:user) { create(:user) }
let(:project) { build_stubbed(:project) }
@@ -145,7 +147,7 @@ describe ButtonHelper do
expect(element.attr('data-clipboard-text')).to eq(nil)
expect(element.inner_text).to eq("")
- expect(element).to have_selector('.fa.fa-clipboard')
+ expect(element.to_html).to include sprite_icon('duplicate')
end
end
@@ -178,7 +180,7 @@ describe ButtonHelper do
context 'with `hide_button_icon` attribute provided' do
it 'shows copy to clipboard button without tooltip support' do
- expect(element(hide_button_icon: true)).not_to have_selector('.fa.fa-clipboard')
+ expect(element(hide_button_icon: true).to_html).not_to include sprite_icon('duplicate')
end
end
end
diff --git a/spec/helpers/visibility_level_helper_spec.rb b/spec/helpers/visibility_level_helper_spec.rb
index 5077c89d7b4..a3be222b7bd 100644
--- a/spec/helpers/visibility_level_helper_spec.rb
+++ b/spec/helpers/visibility_level_helper_spec.rb
@@ -6,6 +6,29 @@ describe VisibilityLevelHelper do
let(:personal_snippet) { build(:personal_snippet) }
let(:project_snippet) { build(:project_snippet) }
+ describe 'visibility_icon_description' do
+ context 'used with a Project' do
+ it 'delegates projects to #project_visibility_icon_description' do
+ expect(visibility_icon_description(project))
+ .to match /project/i
+ end
+
+ context 'used with a ProjectPresenter' do
+ it 'delegates projects to #project_visibility_icon_description' do
+ expect(visibility_icon_description(project.present))
+ .to match /project/i
+ end
+ end
+
+ context 'used with a Group' do
+ it 'delegates groups to #group_visibility_icon_description' do
+ expect(visibility_icon_description(group))
+ .to match /group/i
+ end
+ end
+ end
+ end
+
describe 'visibility_level_description' do
context 'used with a Project' do
it 'delegates projects to #project_visibility_level_description' do
diff --git a/spec/javascripts/diffs/components/diff_line_gutter_content_spec.js b/spec/javascripts/diffs/components/diff_line_gutter_content_spec.js
index cb85d12daf2..bdc94131fc2 100644
--- a/spec/javascripts/diffs/components/diff_line_gutter_content_spec.js
+++ b/spec/javascripts/diffs/components/diff_line_gutter_content_spec.js
@@ -50,7 +50,11 @@ describe('DiffLineGutterContent', () => {
it('should return discussions for the given lineCode', () => {
const { lineCode } = getDiffFileMock().highlightedDiffLines[1];
- const component = createComponent({ lineCode, showCommentButton: true });
+ const component = createComponent({
+ lineCode,
+ showCommentButton: true,
+ discussions: getDiscussionsMockData(),
+ });
setDiscussions(component);
diff --git a/spec/javascripts/gpg_badges_spec.js b/spec/javascripts/gpg_badges_spec.js
index 97c771dcfd3..78330dd9633 100644
--- a/spec/javascripts/gpg_badges_spec.js
+++ b/spec/javascripts/gpg_badges_spec.js
@@ -1,23 +1,27 @@
import MockAdapter from 'axios-mock-adapter';
import axios from '~/lib/utils/axios_utils';
import GpgBadges from '~/gpg_badges';
+import { TEST_HOST } from 'spec/test_constants';
describe('GpgBadges', () => {
let mock;
const dummyCommitSha = 'n0m0rec0ffee';
const dummyBadgeHtml = 'dummy html';
const dummyResponse = {
- signatures: [{
- commit_sha: dummyCommitSha,
- html: dummyBadgeHtml,
- }],
+ signatures: [
+ {
+ commit_sha: dummyCommitSha,
+ html: dummyBadgeHtml,
+ },
+ ],
};
+ const dummyUrl = `${TEST_HOST}/dummy/signatures`;
beforeEach(() => {
mock = new MockAdapter(axios);
setFixtures(`
<form
- class="commits-search-form js-signature-container" data-signatures-path="/hello" action="/hello"
+ class="commits-search-form js-signature-container" data-signatures-path="${dummyUrl}" action="${dummyUrl}"
method="get">
<input name="utf8" type="hidden" value="✓">
<input type="search" name="search" id="commits-search"class="form-control search-text-input input-short">
@@ -32,25 +36,55 @@ describe('GpgBadges', () => {
mock.restore();
});
- it('displays a loading spinner', (done) => {
- mock.onGet('/hello').reply(200);
+ it('does not make a request if there is no container element', done => {
+ setFixtures('');
+ spyOn(axios, 'get');
- GpgBadges.fetch().then(() => {
- expect(document.querySelector('.js-loading-gpg-badge:empty')).toBe(null);
- const spinners = document.querySelectorAll('.js-loading-gpg-badge i.fa.fa-spinner.fa-spin');
- expect(spinners.length).toBe(1);
- done();
- }).catch(done.fail);
+ GpgBadges.fetch()
+ .then(() => {
+ expect(axios.get).not.toHaveBeenCalled();
+ })
+ .then(done)
+ .catch(done.fail);
});
- it('replaces the loading spinner', (done) => {
- mock.onGet('/hello').reply(200, dummyResponse);
+ it('throws an error if the endpoint is missing', done => {
+ setFixtures('<div class="js-signature-container"></div>');
+ spyOn(axios, 'get');
- GpgBadges.fetch().then(() => {
- expect(document.querySelector('.js-loading-gpg-badge')).toBe(null);
- const parentContainer = document.querySelector('.parent-container');
- expect(parentContainer.innerHTML.trim()).toEqual(dummyBadgeHtml);
- done();
- }).catch(done.fail);
+ GpgBadges.fetch()
+ .then(() => done.fail('Expected error to be thrown'))
+ .catch(error => {
+ expect(error.message).toBe('Missing commit signatures endpoint!');
+ expect(axios.get).not.toHaveBeenCalled();
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+
+ it('displays a loading spinner', done => {
+ mock.onGet(dummyUrl).replyOnce(200);
+
+ GpgBadges.fetch()
+ .then(() => {
+ expect(document.querySelector('.js-loading-gpg-badge:empty')).toBe(null);
+ const spinners = document.querySelectorAll('.js-loading-gpg-badge i.fa.fa-spinner.fa-spin');
+ expect(spinners.length).toBe(1);
+ done();
+ })
+ .catch(done.fail);
+ });
+
+ it('replaces the loading spinner', done => {
+ mock.onGet(dummyUrl).replyOnce(200, dummyResponse);
+
+ GpgBadges.fetch()
+ .then(() => {
+ expect(document.querySelector('.js-loading-gpg-badge')).toBe(null);
+ const parentContainer = document.querySelector('.parent-container');
+ expect(parentContainer.innerHTML.trim()).toEqual(dummyBadgeHtml);
+ done();
+ })
+ .catch(done.fail);
});
});
diff --git a/spec/javascripts/lib/utils/common_utils_spec.js b/spec/javascripts/lib/utils/common_utils_spec.js
index 41ff59949e5..71b26a315af 100644
--- a/spec/javascripts/lib/utils/common_utils_spec.js
+++ b/spec/javascripts/lib/utils/common_utils_spec.js
@@ -627,4 +627,23 @@ describe('common_utils', () => {
});
});
});
+
+ describe('roundOffFloat', () => {
+ it('Rounds off decimal places of a float number with provided precision', () => {
+ expect(commonUtils.roundOffFloat(3.141592, 3)).toBe(3.142);
+ });
+
+ it('Rounds off a float number to a whole number when provided precision is zero', () => {
+ expect(commonUtils.roundOffFloat(3.141592, 0)).toBe(3);
+ expect(commonUtils.roundOffFloat(3.5, 0)).toBe(4);
+ });
+
+ it('Rounds off float number to nearest 0, 10, 100, 1000 and so on when provided precision is below 0', () => {
+ expect(commonUtils.roundOffFloat(34567.14159, -1)).toBe(34570);
+ expect(commonUtils.roundOffFloat(34567.14159, -2)).toBe(34600);
+ expect(commonUtils.roundOffFloat(34567.14159, -3)).toBe(35000);
+ expect(commonUtils.roundOffFloat(34567.14159, -4)).toBe(30000);
+ expect(commonUtils.roundOffFloat(34567.14159, -5)).toBe(0);
+ });
+ });
});
diff --git a/spec/javascripts/reports/store/actions_spec.js b/spec/javascripts/reports/store/actions_spec.js
new file mode 100644
index 00000000000..c714c5af156
--- /dev/null
+++ b/spec/javascripts/reports/store/actions_spec.js
@@ -0,0 +1,130 @@
+import MockAdapter from 'axios-mock-adapter';
+import axios from '~/lib/utils/axios_utils';
+import {
+ setEndpoint,
+ requestReports,
+ fetchReports,
+ stopPolling,
+ clearEtagPoll,
+ receiveReportsSuccess,
+ receiveReportsError,
+} from '~/reports/store/actions';
+import state from '~/reports/store/state';
+import * as types from '~/reports/store/mutation_types';
+import testAction from 'spec/helpers/vuex_action_helper';
+import { TEST_HOST } from 'spec/test_constants';
+
+describe('Reports Store Actions', () => {
+ let mockedState;
+
+ beforeEach(() => {
+ mockedState = state();
+ });
+
+ describe('setEndpoint', () => {
+ it('should commit SET_ENDPOINT mutation', done => {
+ testAction(
+ setEndpoint,
+ 'endpoint.json',
+ mockedState,
+ [{ type: types.SET_ENDPOINT, payload: 'endpoint.json' }],
+ [],
+ done,
+ );
+ });
+ });
+
+ describe('requestReports', () => {
+ it('should commit REQUEST_REPORTS mutation', done => {
+ testAction(requestReports, null, mockedState, [{ type: types.REQUEST_REPORTS }], [], done);
+ });
+ });
+
+ describe('fetchReports', () => {
+ let mock;
+
+ beforeEach(() => {
+ mockedState.endpoint = `${TEST_HOST}/endpoint.json`;
+ mock = new MockAdapter(axios);
+ });
+
+ afterEach(() => {
+ mock.restore();
+ stopPolling();
+ clearEtagPoll();
+ });
+
+ describe('success', () => {
+ it('dispatches requestReports and receiveReportsSuccess ', done => {
+ mock.onGet(`${TEST_HOST}/endpoint.json`).replyOnce(200, { summary: {}, suites: [{ name: 'rspec' }] });
+
+ testAction(
+ fetchReports,
+ null,
+ mockedState,
+ [],
+ [
+ {
+ type: 'requestReports',
+ },
+ {
+ payload: { summary: {}, suites: [{ name: 'rspec' }] },
+ type: 'receiveReportsSuccess',
+ },
+ ],
+ done,
+ );
+ });
+ });
+
+ describe('error', () => {
+ beforeEach(() => {
+ mock.onGet(`${TEST_HOST}/endpoint.json`).reply(500);
+ });
+
+ it('dispatches requestReports and receiveReportsError ', done => {
+ testAction(
+ fetchReports,
+ null,
+ mockedState,
+ [],
+ [
+ {
+ type: 'requestReports',
+ },
+ {
+ type: 'receiveReportsError',
+ },
+ ],
+ done,
+ );
+ });
+ });
+ });
+
+ describe('receiveReportsSuccess', () => {
+ it('should commit RECEIVE_REPORTS_SUCCESS mutation', done => {
+ testAction(
+ receiveReportsSuccess,
+ { summary: {} },
+ mockedState,
+ [{ type: types.RECEIVE_REPORTS_SUCCESS, payload: { summary: {} } }],
+ [],
+ done,
+ );
+ });
+ });
+
+ describe('receiveReportsError', () => {
+ it('should commit RECEIVE_REPORTS_ERROR mutation', done => {
+ testAction(
+ receiveReportsError,
+ null,
+ mockedState,
+ [{ type: types.RECEIVE_REPORTS_ERROR }],
+ [],
+ done,
+ );
+ });
+ });
+});
diff --git a/spec/javascripts/reports/store/mutations_spec.js b/spec/javascripts/reports/store/mutations_spec.js
new file mode 100644
index 00000000000..3e0b15438c3
--- /dev/null
+++ b/spec/javascripts/reports/store/mutations_spec.js
@@ -0,0 +1,101 @@
+import state from '~/reports/store/state';
+import mutations from '~/reports/store/mutations';
+import * as types from '~/reports/store/mutation_types';
+
+describe('Reports Store Mutations', () => {
+ let stateCopy;
+
+ beforeEach(() => {
+ stateCopy = state();
+ });
+
+ describe('SET_ENDPOINT', () => {
+ it('should set endpoint', () => {
+ mutations[types.SET_ENDPOINT](stateCopy, 'endpoint.json');
+ expect(stateCopy.endpoint).toEqual('endpoint.json');
+ });
+ });
+
+ describe('REQUEST_REPORTS', () => {
+ it('should set isLoading to true', () => {
+ mutations[types.REQUEST_REPORTS](stateCopy);
+ expect(stateCopy.isLoading).toEqual(true);
+ });
+ });
+
+ describe('RECEIVE_REPORTS_SUCCESS', () => {
+ const mockedResponse = {
+ summary: {
+ total: 14,
+ resolved: 0,
+ failed: 7,
+ },
+ suites: [
+ {
+ name: 'build:linux',
+ summary: {
+ total: 2,
+ resolved: 0,
+ failed: 1,
+ },
+ new_failures: [
+ {
+ name: 'StringHelper#concatenate when a is git and b is lab returns summary',
+ execution_time: 0.0092435,
+ system_output:
+ 'Failure/Error: is_expected.to eq(\'gitlab\')',
+ },
+ ],
+ resolved_failures: [
+ {
+ name: 'StringHelper#concatenate when a is git and b is lab returns summary',
+ execution_time: 0.009235,
+ system_output:
+ 'Failure/Error: is_expected.to eq(\'gitlab\')',
+ },
+ ],
+ existing_failures: [
+ {
+ name: 'StringHelper#concatenate when a is git and b is lab returns summary',
+ execution_time: 1232.08,
+ system_output:
+ 'Failure/Error: is_expected.to eq(\'gitlab\')',
+ },
+ ],
+ },
+ ],
+ };
+
+ beforeEach(() => {
+ mutations[types.RECEIVE_REPORTS_SUCCESS](stateCopy, mockedResponse);
+ });
+
+ it('should reset isLoading', () => {
+ expect(stateCopy.isLoading).toEqual(false);
+ });
+
+ it('should set summary counts', () => {
+ expect(stateCopy.summary.total).toEqual(mockedResponse.summary.total);
+ expect(stateCopy.summary.resolved).toEqual(mockedResponse.summary.resolved);
+ expect(stateCopy.summary.failed).toEqual(mockedResponse.summary.failed);
+ });
+
+ it('should set reports', () => {
+ expect(stateCopy.reports).toEqual(mockedResponse.suites);
+ });
+ });
+
+ describe('RECEIVE_REPORTS_ERROR', () => {
+ beforeEach(() => {
+ mutations[types.RECEIVE_REPORTS_ERROR](stateCopy);
+ });
+ it('should reset isLoading', () => {
+ expect(stateCopy.isLoading).toEqual(false);
+ });
+
+ it('should set hasError to true', () => {
+ expect(stateCopy.hasError).toEqual(true);
+ });
+
+ });
+});
diff --git a/spec/javascripts/vue_shared/components/clipboard_button_spec.js b/spec/javascripts/vue_shared/components/clipboard_button_spec.js
index 97f0fbb04db..e135690349e 100644
--- a/spec/javascripts/vue_shared/components/clipboard_button_spec.js
+++ b/spec/javascripts/vue_shared/components/clipboard_button_spec.js
@@ -21,7 +21,7 @@ describe('clipboard button', () => {
it('renders a button for clipboard', () => {
expect(vm.$el.tagName).toEqual('BUTTON');
expect(vm.$el.getAttribute('data-clipboard-text')).toEqual('copy me');
- expect(vm.$el.querySelector('i').className).toEqual('fa fa-clipboard');
+ expect(vm.$el).toHaveSpriteIcon('duplicate');
});
it('should have a tooltip with default values', () => {
diff --git a/spec/javascripts/vue_shared/components/stacked_progress_bar_spec.js b/spec/javascripts/vue_shared/components/stacked_progress_bar_spec.js
index de3bf667fb3..076d940961d 100644
--- a/spec/javascripts/vue_shared/components/stacked_progress_bar_spec.js
+++ b/spec/javascripts/vue_shared/components/stacked_progress_bar_spec.js
@@ -10,9 +10,9 @@ const createComponent = (config) => {
successLabel: 'Synced',
failureLabel: 'Failed',
neutralLabel: 'Out of sync',
- successCount: 10,
- failureCount: 5,
- totalCount: 20,
+ successCount: 25,
+ failureCount: 10,
+ totalCount: 5000,
}, config);
return mountComponent(Component, defaultConfig);
@@ -32,7 +32,7 @@ describe('StackedProgressBarComponent', () => {
describe('computed', () => {
describe('neutralCount', () => {
it('returns neutralCount based on totalCount, successCount and failureCount', () => {
- expect(vm.neutralCount).toBe(5); // 20 - 10 - 5
+ expect(vm.neutralCount).toBe(4965); // 5000 - 25 - 10
});
});
});
@@ -40,7 +40,11 @@ describe('StackedProgressBarComponent', () => {
describe('methods', () => {
describe('getPercent', () => {
it('returns percentage from provided count based on `totalCount`', () => {
- expect(vm.getPercent(10)).toBe(50);
+ expect(vm.getPercent(500)).toBe(10);
+ });
+
+ it('returns percentage with decimal place from provided count based on `totalCount`', () => {
+ expect(vm.getPercent(10)).toBe(0.2);
});
});
diff --git a/spec/lib/banzai/filter/image_lazy_load_filter_spec.rb b/spec/lib/banzai/filter/image_lazy_load_filter_spec.rb
index 41f957c4e00..d06c5535309 100644
--- a/spec/lib/banzai/filter/image_lazy_load_filter_spec.rb
+++ b/spec/lib/banzai/filter/image_lazy_load_filter_spec.rb
@@ -7,6 +7,20 @@ describe Banzai::Filter::ImageLazyLoadFilter do
%(<img src="#{path}" />)
end
+ def image_with_class(path, class_attr = nil)
+ %(<img src="#{path}" class="#{class_attr}"/>)
+ end
+
+ it 'adds a class attribute' do
+ doc = filter(image('/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg'))
+ expect(doc.at_css('img')['class']).to eq 'lazy'
+ end
+
+ it 'appends to the current class attribute' do
+ doc = filter(image_with_class('/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg', 'test'))
+ expect(doc.at_css('img')['class']).to eq 'test lazy'
+ end
+
it 'transforms the image src to a data-src' do
doc = filter(image('/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg'))
expect(doc.at_css('img')['data-src']).to eq '/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg'
diff --git a/spec/lib/feature_spec.rb b/spec/lib/feature_spec.rb
index 6eb10497428..f313e675654 100644
--- a/spec/lib/feature_spec.rb
+++ b/spec/lib/feature_spec.rb
@@ -39,18 +39,36 @@ describe Feature do
end
describe '.persisted?' do
- it 'returns true for a persisted feature' do
- Feature::FlipperFeature.create!(key: 'foo')
+ context 'when the feature is persisted' do
+ it 'returns true when feature name is a string' do
+ Feature::FlipperFeature.create!(key: 'foo')
+
+ feature = double(:feature, name: 'foo')
+
+ expect(described_class.persisted?(feature)).to eq(true)
+ end
+
+ it 'returns true when feature name is a symbol' do
+ Feature::FlipperFeature.create!(key: 'foo')
- feature = double(:feature, name: 'foo')
+ feature = double(:feature, name: :foo)
- expect(described_class.persisted?(feature)).to eq(true)
+ expect(described_class.persisted?(feature)).to eq(true)
+ end
end
- it 'returns false for a feature that is not persisted' do
- feature = double(:feature, name: 'foo')
+ context 'when the feature is not persisted' do
+ it 'returns false when feature name is a string' do
+ feature = double(:feature, name: 'foo')
+
+ expect(described_class.persisted?(feature)).to eq(false)
+ end
- expect(described_class.persisted?(feature)).to eq(false)
+ it 'returns false when feature name is a symbol' do
+ feature = double(:feature, name: :bar)
+
+ expect(described_class.persisted?(feature)).to eq(false)
+ end
end
end
diff --git a/spec/lib/gitlab/hashed_storage/migrator_spec.rb b/spec/lib/gitlab/hashed_storage/migrator_spec.rb
index 813ae43b4d3..7eac2cacb90 100644
--- a/spec/lib/gitlab/hashed_storage/migrator_spec.rb
+++ b/spec/lib/gitlab/hashed_storage/migrator_spec.rb
@@ -65,7 +65,7 @@ describe Gitlab::HashedStorage::Migrator do
end
it 'migrate project' do
- Sidekiq::Testing.inline! do
+ perform_enqueued_jobs do
subject.migrate(project)
end
diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml
index 084ce3066d6..db5aab0cd76 100644
--- a/spec/lib/gitlab/import_export/all_models.yml
+++ b/spec/lib/gitlab/import_export/all_models.yml
@@ -211,6 +211,7 @@ project:
- slack_service
- microsoft_teams_service
- mattermost_service
+- hangouts_chat_service
- buildkite_service
- bamboo_service
- teamcity_service
diff --git a/spec/migrations/active_record/schedule_set_confidential_note_events_on_services_spec.rb b/spec/migrations/active_record/schedule_set_confidential_note_events_on_services_spec.rb
index 4395e2f8264..5c6f213e15b 100644
--- a/spec/migrations/active_record/schedule_set_confidential_note_events_on_services_spec.rb
+++ b/spec/migrations/active_record/schedule_set_confidential_note_events_on_services_spec.rb
@@ -31,7 +31,7 @@ describe ScheduleSetConfidentialNoteEventsOnServices, :migration, :sidekiq do
end
it 'correctly processes services' do
- Sidekiq::Testing.inline! do
+ perform_enqueued_jobs do
expect(services_table.where(confidential_note_events: nil).count).to eq 4
expect(services_table.where(confidential_note_events: true).count).to eq 1
diff --git a/spec/migrations/migrate_stage_id_reference_in_background_spec.rb b/spec/migrations/migrate_stage_id_reference_in_background_spec.rb
index a837498e1b1..dd6f5325750 100644
--- a/spec/migrations/migrate_stage_id_reference_in_background_spec.rb
+++ b/spec/migrations/migrate_stage_id_reference_in_background_spec.rb
@@ -44,7 +44,7 @@ describe MigrateStageIdReferenceInBackground, :migration, :sidekiq do
end
it 'schedules background migrations' do
- Sidekiq::Testing.inline! do
+ perform_enqueued_jobs do
expect(jobs.where(stage_id: nil).count).to eq 5
migrate!
diff --git a/spec/migrations/migrate_stages_statuses_spec.rb b/spec/migrations/migrate_stages_statuses_spec.rb
index ce35276cbf5..5483e24fce7 100644
--- a/spec/migrations/migrate_stages_statuses_spec.rb
+++ b/spec/migrations/migrate_stages_statuses_spec.rb
@@ -34,7 +34,7 @@ describe MigrateStagesStatuses, :sidekiq, :migration do
end
it 'correctly migrates stages statuses' do
- Sidekiq::Testing.inline! do
+ perform_enqueued_jobs do
expect(stages.where(status: nil).count).to eq 3
migrate!
diff --git a/spec/migrations/normalize_ldap_extern_uids_spec.rb b/spec/migrations/normalize_ldap_extern_uids_spec.rb
index 56a78f52802..c6ea1e3e49e 100644
--- a/spec/migrations/normalize_ldap_extern_uids_spec.rb
+++ b/spec/migrations/normalize_ldap_extern_uids_spec.rb
@@ -38,7 +38,7 @@ describe NormalizeLdapExternUids, :migration, :sidekiq do
end
it 'migrates the LDAP identities' do
- Sidekiq::Testing.inline! do
+ perform_enqueued_jobs do
migrate!
identities.where(id: 1..4).each do |identity|
expect(identity.extern_uid).to eq("uid=foo #{identity.id},ou=people,dc=example,dc=com")
@@ -47,7 +47,7 @@ describe NormalizeLdapExternUids, :migration, :sidekiq do
end
it 'does not modify non-LDAP identities' do
- Sidekiq::Testing.inline! do
+ perform_enqueued_jobs do
migrate!
identity = identities.last
expect(identity.extern_uid).to eq(" uid = foo 5, ou = People, dc = example, dc = com ")
diff --git a/spec/migrations/schedule_create_gpg_key_subkeys_from_gpg_keys_spec.rb b/spec/migrations/schedule_create_gpg_key_subkeys_from_gpg_keys_spec.rb
index ed306fb3d62..96bef107599 100644
--- a/spec/migrations/schedule_create_gpg_key_subkeys_from_gpg_keys_spec.rb
+++ b/spec/migrations/schedule_create_gpg_key_subkeys_from_gpg_keys_spec.rb
@@ -20,7 +20,7 @@ describe ScheduleCreateGpgKeySubkeysFromGpgKeys, :migration, :sidekiq do
end
it 'schedules background migrations' do
- Sidekiq::Testing.inline! do
+ perform_enqueued_jobs do
expect(GpgKeySubkey.count).to eq(0)
migrate!
diff --git a/spec/migrations/schedule_merge_request_diff_migrations_spec.rb b/spec/migrations/schedule_merge_request_diff_migrations_spec.rb
index d230f064444..9f7e47bae0d 100644
--- a/spec/migrations/schedule_merge_request_diff_migrations_spec.rb
+++ b/spec/migrations/schedule_merge_request_diff_migrations_spec.rb
@@ -33,7 +33,7 @@ describe ScheduleMergeRequestDiffMigrations, :migration, :sidekiq do
end
it 'schedules background migrations' do
- Sidekiq::Testing.inline! do
+ perform_enqueued_jobs do
non_empty = 'st_commits IS NOT NULL OR st_diffs IS NOT NULL'
expect(merge_request_diffs.where(non_empty).count).to eq 3
diff --git a/spec/migrations/schedule_merge_request_diff_migrations_take_two_spec.rb b/spec/migrations/schedule_merge_request_diff_migrations_take_two_spec.rb
index 1aab4ae1650..5bcb923af7b 100644
--- a/spec/migrations/schedule_merge_request_diff_migrations_take_two_spec.rb
+++ b/spec/migrations/schedule_merge_request_diff_migrations_take_two_spec.rb
@@ -33,7 +33,7 @@ describe ScheduleMergeRequestDiffMigrationsTakeTwo, :migration, :sidekiq do
end
it 'migrates the data' do
- Sidekiq::Testing.inline! do
+ perform_enqueued_jobs do
non_empty = 'st_commits IS NOT NULL OR st_diffs IS NOT NULL'
expect(merge_request_diffs.where(non_empty).count).to eq 3
diff --git a/spec/migrations/schedule_merge_request_latest_merge_request_diff_id_migrations_spec.rb b/spec/migrations/schedule_merge_request_latest_merge_request_diff_id_migrations_spec.rb
index c9fdbe95d13..76fe16581ac 100644
--- a/spec/migrations/schedule_merge_request_latest_merge_request_diff_id_migrations_spec.rb
+++ b/spec/migrations/schedule_merge_request_latest_merge_request_diff_id_migrations_spec.rb
@@ -53,7 +53,7 @@ describe ScheduleMergeRequestLatestMergeRequestDiffIdMigrations, :migration, :si
end
it 'schedules background migrations' do
- Sidekiq::Testing.inline! do
+ perform_enqueued_jobs do
expect(merge_requests_table.where(latest_merge_request_diff_id: nil).count).to eq 3
migrate!
diff --git a/spec/migrations/schedule_set_confidential_note_events_on_webhooks_spec.rb b/spec/migrations/schedule_set_confidential_note_events_on_webhooks_spec.rb
index 027f4a91c90..fa4ddd5fbc7 100644
--- a/spec/migrations/schedule_set_confidential_note_events_on_webhooks_spec.rb
+++ b/spec/migrations/schedule_set_confidential_note_events_on_webhooks_spec.rb
@@ -31,7 +31,7 @@ describe ScheduleSetConfidentialNoteEventsOnWebhooks, :migration, :sidekiq do
end
it 'correctly processes web hooks' do
- Sidekiq::Testing.inline! do
+ perform_enqueued_jobs do
expect(web_hooks_table.where(confidential_note_events: nil).count).to eq 4
expect(web_hooks_table.where(confidential_note_events: true).count).to eq 1
diff --git a/spec/models/ci/build_trace_chunk_spec.rb b/spec/models/ci/build_trace_chunk_spec.rb
index 774a638b430..915bf134d57 100644
--- a/spec/models/ci/build_trace_chunk_spec.rb
+++ b/spec/models/ci/build_trace_chunk_spec.rb
@@ -179,7 +179,7 @@ describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do
end
it 'migrates data to object storage' do
- Sidekiq::Testing.inline! do
+ perform_enqueued_jobs do
subject
build_trace_chunk.reload
@@ -201,7 +201,7 @@ describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do
end
it 'does not migrate data to object storage' do
- Sidekiq::Testing.inline! do
+ perform_enqueued_jobs do
data_store = build_trace_chunk.data_store
subject
diff --git a/spec/models/deploy_token_spec.rb b/spec/models/deploy_token_spec.rb
index f8d51a95833..cd84a684fec 100644
--- a/spec/models/deploy_token_spec.rb
+++ b/spec/models/deploy_token_spec.rb
@@ -62,11 +62,18 @@ describe DeployToken do
end
end
- context "when it hasn't been revoked" do
+ context "when it hasn't been revoked and is not expired" do
it 'should return true' do
expect(deploy_token.active?).to be_truthy
end
end
+
+ context "when it hasn't been revoked and is expired" do
+ it 'should return true' do
+ deploy_token.update_attribute(:expires_at, Date.today - 5.days)
+ expect(deploy_token.active?).to be_falsy
+ end
+ end
end
describe '#username' do
diff --git a/spec/models/project_services/hangouts_chat_service_spec.rb b/spec/models/project_services/hangouts_chat_service_spec.rb
new file mode 100644
index 00000000000..cfa55188a64
--- /dev/null
+++ b/spec/models/project_services/hangouts_chat_service_spec.rb
@@ -0,0 +1,246 @@
+require 'spec_helper'
+
+describe HangoutsChatService do
+ describe 'Associations' do
+ it { is_expected.to belong_to :project }
+ it { is_expected.to have_one :service_hook }
+ end
+
+ describe 'Validations' do
+ context 'when service is active' do
+ before do
+ subject.active = true
+ end
+
+ it { is_expected.to validate_presence_of(:webhook) }
+ it_behaves_like 'issue tracker service URL attribute', :webhook
+ end
+
+ context 'when service is inactive' do
+ before do
+ subject.active = false
+ end
+
+ it { is_expected.not_to validate_presence_of(:webhook) }
+ end
+ end
+
+ describe '#execute' do
+ let(:user) { create(:user) }
+ let(:project) { create(:project, :repository) }
+ let(:webhook_url) { 'https://example.gitlab.com/' }
+
+ before do
+ allow(subject).to receive_messages(
+ project: project,
+ project_id: project.id,
+ service_hook: true,
+ webhook: webhook_url
+ )
+
+ WebMock.stub_request(:post, webhook_url)
+ end
+
+ shared_examples 'Hangouts Chat service' do
+ it 'calls Hangouts Chat API' do
+ subject.execute(sample_data)
+
+ expect(WebMock)
+ .to have_requested(:post, webhook_url)
+ .with { |req| req.body =~ /\A{"text":.+}\Z/ }
+ .once
+ end
+ end
+
+ context 'with push events' do
+ let(:sample_data) do
+ Gitlab::DataBuilder::Push.build_sample(project, user)
+ end
+
+ it_behaves_like 'Hangouts Chat service'
+
+ it 'specifies the webhook when it is configured' do
+ expect(HangoutsChat::Sender).to receive(:new).with(webhook_url).and_return(double(:hangouts_chat_service).as_null_object)
+
+ subject.execute(sample_data)
+ end
+
+ context 'with not default branch' do
+ let(:sample_data) do
+ Gitlab::DataBuilder::Push.build(project, user, nil, nil, 'not-the-default-branch')
+ end
+
+ context 'when notify_only_default_branch enabled' do
+ before do
+ subject.notify_only_default_branch = true
+ end
+
+ it 'does not call the Hangouts Chat API' do
+ result = subject.execute(sample_data)
+
+ expect(result).to be_falsy
+ end
+ end
+
+ context 'when notify_only_default_branch disabled' do
+ before do
+ subject.notify_only_default_branch = false
+ end
+
+ it_behaves_like 'Hangouts Chat service'
+ end
+ end
+ end
+
+ context 'with issue events' do
+ let(:opts) { { title: 'Awesome issue', description: 'please fix' } }
+ let(:sample_data) do
+ service = Issues::CreateService.new(project, user, opts)
+ issue = service.execute
+ service.hook_data(issue, 'open')
+ end
+
+ it_behaves_like 'Hangouts Chat service'
+ end
+
+ context 'with merge events' do
+ let(:opts) do
+ {
+ title: 'Awesome merge_request',
+ description: 'please fix',
+ source_branch: 'feature',
+ target_branch: 'master'
+ }
+ end
+
+ let(:sample_data) do
+ service = MergeRequests::CreateService.new(project, user, opts)
+ merge_request = service.execute
+ service.hook_data(merge_request, 'open')
+ end
+
+ before do
+ project.add_developer(user)
+ end
+
+ it_behaves_like 'Hangouts Chat service'
+ end
+
+ context 'with wiki page events' do
+ let(:opts) do
+ {
+ title: 'Awesome wiki_page',
+ content: 'Some text describing some thing or another',
+ format: 'md',
+ message: 'user created page: Awesome wiki_page'
+ }
+ end
+ let(:wiki_page) { create(:wiki_page, wiki: project.wiki, attrs: opts) }
+ let(:sample_data) { Gitlab::DataBuilder::WikiPage.build(wiki_page, user, 'create') }
+
+ it_behaves_like 'Hangouts Chat service'
+ end
+
+ context 'with note events' do
+ let(:sample_data) { Gitlab::DataBuilder::Note.build(note, user) }
+
+ context 'with commit comment' do
+ let(:note) do
+ create(:note_on_commit, author: user,
+ project: project,
+ commit_id: project.repository.commit.id,
+ note: 'a comment on a commit')
+ end
+
+ it_behaves_like 'Hangouts Chat service'
+ end
+
+ context 'with merge request comment' do
+ let(:note) do
+ create(:note_on_merge_request, project: project,
+ note: 'merge request note')
+ end
+
+ it_behaves_like 'Hangouts Chat service'
+ end
+
+ context 'with issue comment' do
+ let(:note) do
+ create(:note_on_issue, project: project, note: 'issue note')
+ end
+
+ it_behaves_like 'Hangouts Chat service'
+ end
+
+ context 'with snippet comment' do
+ let(:note) do
+ create(:note_on_project_snippet, project: project,
+ note: 'snippet note')
+ end
+
+ it_behaves_like 'Hangouts Chat service'
+ end
+ end
+
+ context 'with pipeline events' do
+ let(:pipeline) do
+ create(:ci_pipeline,
+ project: project, status: status,
+ sha: project.commit.sha, ref: project.default_branch)
+ end
+ let(:sample_data) { Gitlab::DataBuilder::Pipeline.build(pipeline) }
+
+ context 'with failed pipeline' do
+ let(:status) { 'failed' }
+
+ it_behaves_like 'Hangouts Chat service'
+ end
+
+ context 'with succeeded pipeline' do
+ let(:status) { 'success' }
+
+ context 'with default notify_only_broken_pipelines' do
+ it 'does not call Hangouts Chat API' do
+ result = subject.execute(sample_data)
+
+ expect(result).to be_falsy
+ end
+ end
+
+ context 'when notify_only_broken_pipelines is false' do
+ before do
+ subject.notify_only_broken_pipelines = false
+ end
+
+ it_behaves_like 'Hangouts Chat service'
+ end
+ end
+
+ context 'with not default branch' do
+ let(:pipeline) do
+ create(:ci_pipeline, project: project, status: 'failed', ref: 'not-the-default-branch')
+ end
+
+ context 'when notify_only_default_branch enabled' do
+ before do
+ subject.notify_only_default_branch = true
+ end
+
+ it 'does not call the Hangouts Chat API' do
+ result = subject.execute(sample_data)
+
+ expect(result).to be_falsy
+ end
+ end
+
+ context 'when notify_only_default_branch disabled' do
+ before do
+ subject.notify_only_default_branch = false
+ end
+
+ it_behaves_like 'Hangouts Chat service'
+ end
+ end
+ end
+ end
+end
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index d200e5f2e42..b0ec725bf70 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -26,6 +26,7 @@ describe Project do
it { is_expected.to have_one(:slack_service) }
it { is_expected.to have_one(:microsoft_teams_service) }
it { is_expected.to have_one(:mattermost_service) }
+ it { is_expected.to have_one(:hangouts_chat_service) }
it { is_expected.to have_one(:packagist_service) }
it { is_expected.to have_one(:pushover_service) }
it { is_expected.to have_one(:asana_service) }
diff --git a/spec/models/spam_log_spec.rb b/spec/models/spam_log_spec.rb
index 0d6b4384ada..90a2caaeb88 100644
--- a/spec/models/spam_log_spec.rb
+++ b/spec/models/spam_log_spec.rb
@@ -22,7 +22,7 @@ describe SpamLog do
spam_log = build(:spam_log)
user = spam_log.user
- Sidekiq::Testing.inline! do
+ perform_enqueued_jobs do
spam_log.remove_user(deleted_by: admin)
end
diff --git a/spec/requests/api/groups_spec.rb b/spec/requests/api/groups_spec.rb
index 65b387a2170..3a8948f8477 100644
--- a/spec/requests/api/groups_spec.rb
+++ b/spec/requests/api/groups_spec.rb
@@ -226,6 +226,25 @@ describe API::Groups do
expect(json_response.first['name']).to eq(group2.name)
end
end
+
+ context 'when using min_access_level in the request' do
+ let!(:group3) { create(:group, :private) }
+ let(:response_groups) { json_response.map { |group| group['id'] } }
+
+ before do
+ group1.add_developer(user2)
+ group3.add_master(user2)
+ end
+
+ it 'returns an array of groups the user has at least master access' do
+ get api('/groups', user2), min_access_level: 40
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response).to include_pagination_headers
+ expect(json_response).to be_an Array
+ expect(response_groups).to eq([group2.id, group3.id])
+ end
+ end
end
describe "GET /groups/:id" do
diff --git a/spec/requests/api/project_import_spec.rb b/spec/requests/api/project_import_spec.rb
index 41243854ebc..55332f56508 100644
--- a/spec/requests/api/project_import_spec.rb
+++ b/spec/requests/api/project_import_spec.rb
@@ -102,7 +102,7 @@ describe API::ProjectImport do
it 'correctly overrides params during the import' do
override_params = { 'description' => 'Hello world' }
- Sidekiq::Testing.inline! do
+ perform_enqueued_jobs do
post api('/projects/import', user),
path: 'test-import',
file: fixture_file_upload(file),
diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb
index 5ac008c7e40..71e3436fa76 100644
--- a/spec/requests/api/projects_spec.rb
+++ b/spec/requests/api/projects_spec.rb
@@ -400,6 +400,22 @@ describe API::Projects do
end
end
end
+
+ context 'and with min_access_level' do
+ before do
+ project2.add_master(user2)
+ project3.add_developer(user2)
+ project4.add_reporter(user2)
+ end
+
+ it 'returns an array of groups the user has at least developer access' do
+ get api('/projects', user2), { min_access_level: 30 }
+ 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(project2.id, project3.id)
+ end
+ end
end
context 'when authenticated as a different user' do
@@ -681,6 +697,20 @@ describe API::Projects do
expect(json_response).to be_an Array
expect(json_response.map { |project| project['id'] }).to contain_exactly(public_project.id)
end
+
+ it 'returns projects filetered by minimal access level' do
+ private_project1 = create(:project, :private, name: 'private_project1', creator_id: user4.id, namespace: user4.namespace)
+ private_project2 = create(:project, :private, name: 'private_project2', creator_id: user4.id, namespace: user4.namespace)
+ private_project1.add_developer(user2)
+ private_project2.add_reporter(user2)
+
+ get api("/users/#{user4.id}/projects/", user2), { min_access_level: 30 }
+
+ 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(private_project1.id)
+ end
end
describe 'POST /projects/user/:id' do
diff --git a/spec/requests/api/users_spec.rb b/spec/requests/api/users_spec.rb
index a97c3f3461a..b3079c0a77b 100644
--- a/spec/requests/api/users_spec.rb
+++ b/spec/requests/api/users_spec.rb
@@ -1067,7 +1067,7 @@ describe API::Users do
end
it "deletes user" do
- Sidekiq::Testing.inline! { delete api("/users/#{user.id}", admin) }
+ perform_enqueued_jobs { delete api("/users/#{user.id}", admin) }
expect(response).to have_gitlab_http_status(204)
expect { User.find(user.id) }.to raise_error ActiveRecord::RecordNotFound
@@ -1079,30 +1079,30 @@ describe API::Users do
end
it "does not delete for unauthenticated user" do
- Sidekiq::Testing.inline! { delete api("/users/#{user.id}") }
+ perform_enqueued_jobs { delete api("/users/#{user.id}") }
expect(response).to have_gitlab_http_status(401)
end
it "is not available for non admin users" do
- Sidekiq::Testing.inline! { delete api("/users/#{user.id}", user) }
+ perform_enqueued_jobs { delete api("/users/#{user.id}", user) }
expect(response).to have_gitlab_http_status(403)
end
it "returns 404 for non-existing user" do
- Sidekiq::Testing.inline! { delete api("/users/999999", admin) }
+ perform_enqueued_jobs { delete api("/users/999999", admin) }
expect(response).to have_gitlab_http_status(404)
expect(json_response['message']).to eq('404 User Not Found')
end
it "returns a 404 for invalid ID" do
- Sidekiq::Testing.inline! { delete api("/users/ASDF", admin) }
+ perform_enqueued_jobs { delete api("/users/ASDF", admin) }
expect(response).to have_gitlab_http_status(404)
end
context "hard delete disabled" do
it "moves contributions to the ghost user" do
- Sidekiq::Testing.inline! { delete api("/users/#{user.id}", admin) }
+ perform_enqueued_jobs { delete api("/users/#{user.id}", admin) }
expect(response).to have_gitlab_http_status(204)
expect(issue.reload).to be_persisted
@@ -1112,7 +1112,7 @@ describe API::Users do
context "hard delete enabled" do
it "removes contributions" do
- Sidekiq::Testing.inline! { delete api("/users/#{user.id}?hard_delete=true", admin) }
+ perform_enqueued_jobs { delete api("/users/#{user.id}?hard_delete=true", admin) }
expect(response).to have_gitlab_http_status(204)
expect(Issue.exists?(issue.id)).to be_falsy
diff --git a/spec/requests/lfs_http_spec.rb b/spec/requests/lfs_http_spec.rb
index de39abdb746..c2378646f89 100644
--- a/spec/requests/lfs_http_spec.rb
+++ b/spec/requests/lfs_http_spec.rb
@@ -575,6 +575,40 @@ describe 'Git LFS API and storage' do
end
end
+ context 'when using Deploy Tokens' do
+ let(:project) { create(:project, :repository) }
+ let(:authorization) { authorize_deploy_token }
+ let(:update_user_permissions) { nil }
+ let(:role) { nil }
+ let(:update_lfs_permissions) do
+ project.lfs_objects << lfs_object
+ end
+
+ context 'when Deploy Token is valid' do
+ let(:deploy_token) { create(:deploy_token, projects: [project]) }
+
+ it_behaves_like 'an authorized requests'
+ end
+
+ context 'when Deploy Token is not valid' do
+ let(:deploy_token) { create(:deploy_token, projects: [project], read_repository: false) }
+
+ it 'responds with access denied' do
+ expect(response).to have_gitlab_http_status(401)
+ end
+ end
+
+ context 'when Deploy Token is not related to the project' do
+ let(:another_project) { create(:project, :repository) }
+ let(:deploy_token) { create(:deploy_token, projects: [another_project]) }
+
+ it 'responds with access forbidden' do
+ # We render 404, to prevent data leakage about existence of the project
+ expect(response).to have_gitlab_http_status(404)
+ end
+ end
+ end
+
context 'when build is authorized as' do
let(:authorization) { authorize_ci_project }
@@ -1381,6 +1415,10 @@ describe 'Git LFS API and storage' do
ActionController::HttpAuthentication::Basic.encode_credentials(user.username, Gitlab::LfsToken.new(user).token)
end
+ def authorize_deploy_token
+ ActionController::HttpAuthentication::Basic.encode_credentials(deploy_token.username, deploy_token.token)
+ end
+
def post_lfs_json(url, body = nil, headers = nil)
post(url, body.try(:to_json), (headers || {}).merge('Content-Type' => LfsRequest::CONTENT_TYPE))
end
diff --git a/spec/services/groups/destroy_service_spec.rb b/spec/services/groups/destroy_service_spec.rb
index a9baccd061a..b54491cf5f9 100644
--- a/spec/services/groups/destroy_service_spec.rb
+++ b/spec/services/groups/destroy_service_spec.rb
@@ -49,7 +49,7 @@ describe Groups::DestroyService do
context 'Sidekiq inline' do
before do
# Run sidekiq immediately to check that renamed dir will be removed
- Sidekiq::Testing.inline! { destroy_group(group, user, async) }
+ perform_enqueued_jobs { destroy_group(group, user, async) }
end
it 'verifies that paths have been deleted' do
diff --git a/spec/services/projects/create_from_template_service_spec.rb b/spec/services/projects/create_from_template_service_spec.rb
index 9aa9237d875..a43da01f37e 100644
--- a/spec/services/projects/create_from_template_service_spec.rb
+++ b/spec/services/projects/create_from_template_service_spec.rb
@@ -28,7 +28,7 @@ describe Projects::CreateFromTemplateService do
context 'the result project' do
before do
- Sidekiq::Testing.inline! do
+ perform_enqueued_jobs do
@project = subject.execute
end
diff --git a/spec/services/projects/destroy_service_spec.rb b/spec/services/projects/destroy_service_spec.rb
index 38660ad7a01..e428808ab68 100644
--- a/spec/services/projects/destroy_service_spec.rb
+++ b/spec/services/projects/destroy_service_spec.rb
@@ -45,18 +45,18 @@ describe Projects::DestroyService do
shared_examples 'handles errors thrown during async destroy' do |error_message|
it 'does not allow the error to bubble up' do
expect do
- Sidekiq::Testing.inline! { destroy_project(project, user, {}) }
+ perform_enqueued_jobs { destroy_project(project, user, {}) }
end.not_to raise_error
end
it 'unmarks the project as "pending deletion"' do
- Sidekiq::Testing.inline! { destroy_project(project, user, {}) }
+ perform_enqueued_jobs { destroy_project(project, user, {}) }
expect(project.reload.pending_delete).to be(false)
end
it 'stores an error message in `projects.delete_error`' do
- Sidekiq::Testing.inline! { destroy_project(project, user, {}) }
+ perform_enqueued_jobs { destroy_project(project, user, {}) }
expect(project.reload.delete_error).to be_present
expect(project.delete_error).to include(error_message)
@@ -66,7 +66,7 @@ describe Projects::DestroyService do
context 'Sidekiq inline' do
before do
# Run sidekiq immediatly to check that renamed repository will be removed
- Sidekiq::Testing.inline! { destroy_project(project, user, {}) }
+ perform_enqueued_jobs { destroy_project(project, user, {}) }
end
context 'when has remote mirrors' do
@@ -110,7 +110,7 @@ describe Projects::DestroyService do
end
it 'keeps project team intact upon an error' do
- Sidekiq::Testing.inline! do
+ perform_enqueued_jobs do
begin
destroy_project(project, user, {})
rescue ::Redis::CannotConnectError
@@ -128,7 +128,7 @@ describe Projects::DestroyService do
before do
project.project_feature.update_attribute("issues_access_level", ProjectFeature::PRIVATE)
# Run sidekiq immediately to check that renamed repository will be removed
- Sidekiq::Testing.inline! { destroy_project(project, user, {}) }
+ perform_enqueued_jobs { destroy_project(project, user, {}) }
end
it_behaves_like 'deleting the project'
@@ -172,7 +172,7 @@ describe Projects::DestroyService do
it 'allows error to bubble up and rolls back project deletion' do
expect do
- Sidekiq::Testing.inline! { destroy_project(project, user, {}) }
+ perform_enqueued_jobs { destroy_project(project, user, {}) }
end.to raise_error(Exception, 'Other error message')
expect(project.reload.pending_delete).to be(false)
diff --git a/spec/services/projects/housekeeping_service_spec.rb b/spec/services/projects/housekeeping_service_spec.rb
index 1cf373d1d72..18ecef1c0a1 100644
--- a/spec/services/projects/housekeeping_service_spec.rb
+++ b/spec/services/projects/housekeeping_service_spec.rb
@@ -35,7 +35,7 @@ describe Projects::HousekeepingService do
allow(subject).to receive(:gc_period).and_return(1)
project.increment_pushes_since_gc
- Sidekiq::Testing.inline! do
+ perform_enqueued_jobs do
expect { subject.execute }.to change { project.pushes_since_gc }.to(0)
end
end
diff --git a/spec/services/projects/import_service_spec.rb b/spec/services/projects/import_service_spec.rb
index b3815045792..e2a600d12d1 100644
--- a/spec/services/projects/import_service_spec.rb
+++ b/spec/services/projects/import_service_spec.rb
@@ -69,7 +69,7 @@ describe Projects::ImportService do
result = subject.execute
expect(result[:status]).to eq :error
- expect(result[:message]).to eq "Error importing repository #{project.import_url} into #{project.full_path} - The repository could not be created."
+ expect(result[:message]).to eq "Error importing repository #{project.safe_import_url} into #{project.full_path} - The repository could not be created."
end
context 'when repository creation succeeds' do
@@ -141,7 +141,7 @@ describe Projects::ImportService do
result = subject.execute
expect(result[:status]).to eq :error
- expect(result[:message]).to eq "Error importing repository #{project.import_url} into #{project.full_path} - Failed to import the repository"
+ expect(result[:message]).to eq "Error importing repository #{project.safe_import_url} into #{project.full_path} - Failed to import the repository"
end
context 'when repository import scheduled' do
diff --git a/spec/services/users/destroy_service_spec.rb b/spec/services/users/destroy_service_spec.rb
index f82d4b483e7..3bae8bfbd42 100644
--- a/spec/services/users/destroy_service_spec.rb
+++ b/spec/services/users/destroy_service_spec.rb
@@ -173,7 +173,7 @@ describe Users::DestroyService do
describe "user personal's repository removal" do
before do
- Sidekiq::Testing.inline! { service.execute(user) }
+ perform_enqueued_jobs { service.execute(user) }
end
context 'legacy storage' do
diff --git a/spec/workers/storage_migrator_worker_spec.rb b/spec/workers/storage_migrator_worker_spec.rb
index 815432aacce..808084c8f7c 100644
--- a/spec/workers/storage_migrator_worker_spec.rb
+++ b/spec/workers/storage_migrator_worker_spec.rb
@@ -13,7 +13,7 @@ describe StorageMigratorWorker do
end
it 'migrates projects in the specified range' do
- Sidekiq::Testing.inline! do
+ perform_enqueued_jobs do
worker.perform(ids.min, ids.max)
end