Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.rubocop_manual_todo.yml5
-rw-r--r--Gemfile6
-rw-r--r--Gemfile.lock21
-rw-r--r--app/assets/images/learn_gitlab/issue_created.svg65
-rw-r--r--app/assets/javascripts/diffs/components/app.vue14
-rw-r--r--app/assets/javascripts/diffs/components/diff_file.vue11
-rw-r--r--app/assets/javascripts/diffs/components/diff_row.vue17
-rw-r--r--app/assets/javascripts/diffs/components/inline_diff_table_row.vue5
-rw-r--r--app/assets/javascripts/diffs/components/parallel_diff_table_row.vue12
-rw-r--r--app/assets/javascripts/diffs/store/actions.js47
-rw-r--r--app/assets/javascripts/diffs/store/getters.js10
-rw-r--r--app/assets/javascripts/diffs/store/modules/diff_state.js6
-rw-r--r--app/assets/javascripts/diffs/store/modules/index.js6
-rw-r--r--app/assets/javascripts/diffs/store/mutation_types.js1
-rw-r--r--app/assets/javascripts/diffs/store/mutations.js6
-rw-r--r--app/assets/javascripts/diffs/utils/interoperability.js49
-rw-r--r--app/assets/javascripts/ide/components/ide_status_mr.vue2
-rw-r--r--app/assets/javascripts/ide/components/nav_dropdown_button.vue2
-rw-r--r--app/assets/javascripts/import_entities/import_projects/components/provider_repo_table_row.vue2
-rw-r--r--app/assets/javascripts/monitoring/components/links_section.vue2
-rw-r--r--app/assets/javascripts/pages/projects/learn_gitlab/components/learn_gitlab_b.vue3
-rw-r--r--app/assets/javascripts/pages/projects/learn_gitlab/components/learn_gitlab_info_card.vue2
-rw-r--r--app/assets/javascripts/pages/projects/learn_gitlab/constants/index.js9
-rw-r--r--app/assets/javascripts/pipelines/components/graph/constants.js1
-rw-r--r--app/assets/javascripts/pipelines/components/graph/graph_component_wrapper.vue23
-rw-r--r--app/assets/javascripts/pipelines/components/graph/graph_view_selector.vue2
-rw-r--r--app/controllers/profiles/notifications_controller.rb23
-rw-r--r--app/graphql/queries/pipelines/get_pipeline_details.query.graphql1
-rw-r--r--app/graphql/resolvers/concerns/board_issue_filterable.rb11
-rw-r--r--app/graphql/types/boards/assignee_wildcard_id_enum.rb13
-rw-r--r--app/graphql/types/boards/board_issue_input_type.rb4
-rw-r--r--app/helpers/appearances_helper.rb1
-rw-r--r--app/helpers/application_helper.rb9
-rw-r--r--app/helpers/learn_gitlab_helper.rb1
-rw-r--r--app/models/concerns/bulk_member_access_load.rb26
-rw-r--r--app/models/concerns/cascading_namespace_setting_attribute.rb2
-rw-r--r--app/models/concerns/has_repository.rb9
-rw-r--r--app/models/notification_setting.rb2
-rw-r--r--app/models/preloaders/user_max_access_level_in_projects_preloader.rb25
-rw-r--r--app/models/project_team.rb4
-rw-r--r--app/services/boards/lists/base_update_service.rb58
-rw-r--r--app/services/boards/lists/update_service.rb45
-rw-r--r--app/services/projects/update_pages_service.rb2
-rw-r--r--app/views/devise/passwords/new.html.haml4
-rw-r--r--app/views/import/shared/_new_project_form.html.haml2
-rw-r--r--app/views/projects/_new_project_fields.html.haml2
-rw-r--r--app/views/shared/projects/_search_bar.html.haml2
-rw-r--r--changelogs/unreleased/21043-fix-more-n-plus-one-queries.yml6
-rw-r--r--changelogs/unreleased/280781-support-assignee-wildcard-filters-board-issues-graphql.yml5
-rw-r--r--changelogs/unreleased/293953_inherit_default_branch_name_for_subgroups.yml5
-rw-r--r--changelogs/unreleased/Externalize-strings-in-passwords-new-html-haml.yml5
-rw-r--r--changelogs/unreleased/dblessing_cascading_settings_default_enabled.yml5
-rw-r--r--changelogs/unreleased/id-bump-gon-version.yml5
-rw-r--r--changelogs/unreleased/id-bump-rspec-rails-to-5-1.yml5
-rw-r--r--changelogs/unreleased/issue-220040-fix-rails-savebang-gitaly-client-module.yml5
-rw-r--r--changelogs/unreleased/mc-backstage-reschedule-artifac-expiry-date-again.yml5
-rw-r--r--config/application.rb28
-rw-r--r--config/feature_flags/development/cascading_namespace_settings.yml4
-rw-r--r--config/initializers/0_inject_enterprise_edition_module.rb6
-rw-r--r--config/initializers/0_license.rb14
-rw-r--r--config/initializers_before_autoloader/000_inflections.rb1
-rw-r--r--db/post_migrate/20210413132500_reschedule_artifact_expiry_backfill_again.rb44
-rw-r--r--db/schema_migrations/202104131325001
-rw-r--r--doc/administration/geo/disaster_recovery/index.md128
-rw-r--r--doc/administration/monitoring/prometheus/gitlab_metrics.md12
-rw-r--r--doc/api/graphql/reference/index.md9
-rw-r--r--doc/development/documentation/site_architecture/release_process.md181
-rw-r--r--doc/development/ee_features.md2
-rw-r--r--doc/development/feature_flags/index.md3
-rw-r--r--doc/development/licensed_feature_availability.md13
-rw-r--r--doc/integration/oauth_provider.md2
-rw-r--r--doc/user/admin_area/geo_nodes.md6
-rw-r--r--doc/user/project/description_templates.md13
-rw-r--r--doc/user/project/settings/index.md65
-rw-r--r--lib/gitlab.rb11
-rw-r--r--lib/gitlab/subscription_portal.rb7
-rw-r--r--locale/gitlab.pot35
-rw-r--r--package.json4
-rw-r--r--scripts/rspec_helpers.sh4
-rw-r--r--spec/controllers/profiles/notifications_controller_spec.rb24
-rw-r--r--spec/deprecation_toolkit_env.rb1
-rw-r--r--spec/features/admin/admin_settings_spec.rb2
-rw-r--r--spec/frontend/diffs/components/compare_versions_spec.js34
-rw-r--r--spec/frontend/diffs/components/diff_row_spec.js17
-rw-r--r--spec/frontend/diffs/components/inline_diff_table_row_spec.js13
-rw-r--r--spec/frontend/diffs/components/parallel_diff_table_row_spec.js23
-rw-r--r--spec/frontend/diffs/find_interop_attributes.js20
-rw-r--r--spec/frontend/diffs/store/actions_spec.js48
-rw-r--r--spec/frontend/diffs/store/getters_spec.js20
-rw-r--r--spec/frontend/diffs/store/mutations_spec.js13
-rw-r--r--spec/frontend/diffs/utils/interoperability_spec.js67
-rw-r--r--spec/frontend/fixtures/merge_requests_diffs.rb20
-rw-r--r--spec/frontend/pages/projects/learn_gitlab/components/__snapshots__/learn_gitlab_a_spec.js.snap22
-rw-r--r--spec/frontend/pages/projects/learn_gitlab/components/__snapshots__/learn_gitlab_b_spec.js.snap66
-rw-r--r--spec/frontend/pages/projects/learn_gitlab/components/__snapshots__/learn_gitlab_section_card_spec.js.snap4
-rw-r--r--spec/frontend/pages/projects/learn_gitlab/components/learn_gitlab_a_spec.js6
-rw-r--r--spec/frontend/pages/projects/learn_gitlab/components/learn_gitlab_b_spec.js6
-rw-r--r--spec/frontend/pages/projects/learn_gitlab/components/mock_data.js5
-rw-r--r--spec/frontend/pipelines/graph/graph_component_wrapper_spec.js68
-rw-r--r--spec/frontend/pipelines/graph/mock_data.js2
-rw-r--r--spec/frontend_integration/diffs/diffs_interopability_api.js25
-rw-r--r--spec/frontend_integration/diffs/diffs_interopability_spec.js161
-rw-r--r--spec/frontend_integration/test_helpers/fixtures.js6
-rw-r--r--spec/frontend_integration/test_helpers/mock_server/routes/diffs.js22
-rw-r--r--spec/frontend_integration/test_helpers/mock_server/routes/index.js1
-rw-r--r--spec/graphql/resolvers/board_list_issues_resolver_spec.rb18
-rw-r--r--spec/graphql/types/boards/board_issue_input_type_spec.rb4
-rw-r--r--spec/helpers/application_helper_spec.rb14
-rw-r--r--spec/helpers/learn_gitlab_helper_spec.rb1
-rw-r--r--spec/lib/gitlab/gitaly_client/object_pool_service_spec.rb4
-rw-r--r--spec/lib/gitlab/gitaly_client/repository_service_spec.rb2
-rw-r--r--spec/lib/gitlab/subscription_portal_spec.rb56
-rw-r--r--spec/lib/gitlab_spec.rb118
-rw-r--r--spec/migrations/20210413132500_reschedule_artifact_expiry_backfill_again_spec.rb38
-rw-r--r--spec/models/notification_setting_spec.rb14
-rw-r--r--spec/models/preloaders/user_max_access_level_in_projects_preloader_spec.rb37
-rw-r--r--spec/models/project_spec.rb60
-rw-r--r--spec/services/boards/lists/update_service_spec.rb41
-rw-r--r--spec/spec_helper.rb1
-rw-r--r--spec/support/shared_examples/boards/lists/update_service_shared_examples.rb43
-rw-r--r--yarn.lock16
121 files changed, 1847 insertions, 530 deletions
diff --git a/.rubocop_manual_todo.yml b/.rubocop_manual_todo.yml
index c2a529cf033..1073f6a04d1 100644
--- a/.rubocop_manual_todo.yml
+++ b/.rubocop_manual_todo.yml
@@ -46,8 +46,6 @@ Rails/SaveBang:
Exclude:
- 'ee/spec/controllers/projects/merge_requests_controller_spec.rb'
- 'ee/spec/controllers/subscriptions_controller_spec.rb'
- - 'ee/spec/frontend/fixtures/analytics.rb'
- - 'ee/spec/graphql/resolvers/vulnerabilities_resolver_spec.rb'
- 'ee/spec/initializers/fog_google_https_private_urls_spec.rb'
- 'ee/spec/lib/analytics/merge_request_metrics_calculator_spec.rb'
- 'ee/spec/lib/ee/gitlab/auth/ldap/sync/group_spec.rb'
@@ -182,8 +180,6 @@ Rails/SaveBang:
- 'spec/lib/gitlab/email/handler/unsubscribe_handler_spec.rb'
- 'spec/lib/gitlab/gfm/reference_rewriter_spec.rb'
- 'spec/lib/gitlab/git_access_spec.rb'
- - 'spec/lib/gitlab/gitaly_client/object_pool_service_spec.rb'
- - 'spec/lib/gitlab/gitaly_client/repository_service_spec.rb'
- 'spec/lib/gitlab/import_export/avatar_saver_spec.rb'
- 'spec/lib/gitlab/import_export/base/relation_factory_spec.rb'
- 'spec/lib/gitlab/import_export/design_repo_restorer_spec.rb'
@@ -424,7 +420,6 @@ RSpec/EmptyLineAfterFinalLetItBe:
Exclude:
- ee/spec/controllers/admin/geo/projects_controller_spec.rb
- ee/spec/controllers/admin/projects_controller_spec.rb
- - ee/spec/controllers/ee/projects/jobs_controller_spec.rb
- ee/spec/controllers/groups/analytics/cycle_analytics/stages_controller_spec.rb
- ee/spec/controllers/groups/analytics/cycle_analytics/summary_controller_spec.rb
- ee/spec/controllers/groups/analytics/cycle_analytics/value_streams_controller_spec.rb
diff --git a/Gemfile b/Gemfile
index cfa09f4c3cc..0cac20f42de 100644
--- a/Gemfile
+++ b/Gemfile
@@ -108,7 +108,7 @@ gem 'hashie-forbidden_attributes'
gem 'kaminari', '~> 1.0'
# HAML
-gem 'hamlit', '~> 2.14.4'
+gem 'hamlit', '~> 2.15.0'
# Files attachments
gem 'carrierwave', '~> 1.3'
@@ -294,7 +294,7 @@ gem 'terser', '1.0.2'
gem 'addressable', '~> 2.7'
gem 'gemojione', '~> 3.3'
-gem 'gon', '~> 6.2'
+gem 'gon', '~> 6.4.0'
gem 'request_store', '~> 1.5'
gem 'base32', '~> 0.3.0'
@@ -366,7 +366,7 @@ group :development, :test do
gem 'database_cleaner', '~> 1.7.0'
gem 'factory_bot_rails', '~> 6.1.0'
- gem 'rspec-rails', '~> 4.1.2'
+ gem 'rspec-rails', '~> 5.0.1'
# Prevent occasions where minitest is not bundled in packaged versions of ruby (see #3826)
gem 'minitest', '~> 5.11.0'
diff --git a/Gemfile.lock b/Gemfile.lock
index 246e52650ce..98daf9aa8fb 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -499,8 +499,9 @@ GEM
rubyntlm (~> 0.5)
globalid (0.4.2)
activesupport (>= 4.2.0)
- gon (6.2.0)
- actionpack (>= 3.0)
+ gon (6.4.0)
+ actionpack (>= 3.0.20)
+ i18n (>= 0.7)
multi_json
request_store (>= 1.0)
google-api-client (0.50.0)
@@ -591,7 +592,7 @@ GEM
rainbow
rubocop (>= 0.50.0)
sysexits (~> 1.1)
- hamlit (2.14.4)
+ hamlit (2.15.0)
temple (>= 0.8.2)
thor
tilt
@@ -1075,10 +1076,10 @@ GEM
proc_to_ast
rspec (>= 2.13, < 4)
unparser
- rspec-rails (4.1.2)
- actionpack (>= 4.2)
- activesupport (>= 4.2)
- railties (>= 4.2)
+ rspec-rails (5.0.1)
+ actionpack (>= 5.2)
+ activesupport (>= 5.2)
+ railties (>= 5.2)
rspec-core (~> 3.10)
rspec-expectations (~> 3.10)
rspec-mocks (~> 3.10)
@@ -1444,7 +1445,7 @@ DEPENDENCIES
gitlab-styles (~> 6.2.0)
gitlab_chronic_duration (~> 0.10.6.2)
gitlab_omniauth-ldap (~> 2.1.1)
- gon (~> 6.2)
+ gon (~> 6.4.0)
google-api-client (~> 0.33)
google-protobuf (~> 3.14.0)
gpgme (~> 2.0.19)
@@ -1460,7 +1461,7 @@ DEPENDENCIES
gssapi
guard-rspec
haml_lint (~> 0.36.0)
- hamlit (~> 2.14.4)
+ hamlit (~> 2.15.0)
hangouts-chat (~> 0.0.5)
hashie
hashie-forbidden_attributes
@@ -1563,7 +1564,7 @@ DEPENDENCIES
rouge (~> 3.26.0)
rqrcode-rails3 (~> 0.1.7)
rspec-parameterized
- rspec-rails (~> 4.1.2)
+ rspec-rails (~> 5.0.1)
rspec-retry (~> 0.6.1)
rspec_junit_formatter
rspec_profiling (~> 0.0.6)
diff --git a/app/assets/images/learn_gitlab/issue_created.svg b/app/assets/images/learn_gitlab/issue_created.svg
new file mode 100644
index 00000000000..01652b97fc0
--- /dev/null
+++ b/app/assets/images/learn_gitlab/issue_created.svg
@@ -0,0 +1,65 @@
+<svg width="81" height="48" viewBox="0 0 81 48" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g clip-path="url(#clip0)">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M42.9799 11.6386C42.9688 11.7501 42.955 11.8786 42.9384 12.0222C42.8865 12.4687 42.8257 12.9142 42.756 13.3582C42.7493 13.3954 42.7501 13.4335 42.7584 13.4704C42.7667 13.5072 42.7822 13.542 42.8041 13.5728C42.826 13.6035 42.8538 13.6296 42.8859 13.6495C42.918 13.6693 42.9538 13.6826 42.9911 13.6884C43.0284 13.6943 43.0664 13.6926 43.1031 13.6835C43.1397 13.6745 43.1742 13.6582 43.2045 13.6356C43.2347 13.613 43.2602 13.5847 43.2793 13.5521C43.2985 13.5196 43.3109 13.4835 43.316 13.4461C43.3915 12.9637 43.4531 12.506 43.5015 12.0874C43.5221 11.9092 43.5408 11.7308 43.5577 11.5522C43.5609 11.5151 43.5568 11.4777 43.5456 11.4422C43.5343 11.4067 43.5162 11.3738 43.4923 11.3453C43.4684 11.3168 43.439 11.2933 43.406 11.2761C43.373 11.2589 43.3369 11.2484 43.2998 11.2452C43.2627 11.242 43.2253 11.2461 43.1898 11.2573C43.1543 11.2685 43.1214 11.2866 43.0929 11.3106C43.0644 11.3345 43.0409 11.3638 43.0237 11.3969C43.0065 11.4299 42.996 11.466 42.9928 11.5031C42.9909 11.5263 42.9866 11.572 42.9799 11.6386ZM41.9287 16.9968C41.9171 17.0322 41.9127 17.0695 41.9156 17.1066C41.9185 17.1438 41.9287 17.1799 41.9456 17.2131C41.9624 17.2463 41.9857 17.2758 42.014 17.3C42.0423 17.3242 42.0751 17.3426 42.1105 17.3542C42.1459 17.3657 42.1832 17.3701 42.2203 17.3672C42.2574 17.3643 42.2936 17.3541 42.3268 17.3373C42.36 17.3204 42.3895 17.2971 42.4137 17.2688C42.4379 17.2405 42.4563 17.2078 42.4678 17.1724C42.6521 16.606 42.8172 15.985 42.9645 15.321C42.9734 15.2843 42.975 15.2462 42.9691 15.2089C42.9631 15.1716 42.9498 15.1359 42.9299 15.1039C42.91 15.0718 42.8839 15.044 42.8531 15.0222C42.8223 15.0004 42.7874 14.9849 42.7506 14.9767C42.7137 14.9686 42.6756 14.9678 42.6385 14.9746C42.6013 14.9813 42.5659 14.9954 42.5343 15.016C42.5027 15.0367 42.4755 15.0634 42.4543 15.0947C42.4332 15.1259 42.4185 15.1611 42.4111 15.1981C42.2675 15.8456 42.1069 16.4491 41.9287 16.9968ZM40.0489 19.874C40.0136 19.8862 39.981 19.9053 39.9531 19.9302C39.9252 19.955 39.9025 19.9852 39.8863 20.0189C39.8701 20.0526 39.8607 20.0891 39.8587 20.1265C39.8566 20.1638 39.862 20.2012 39.8745 20.2364C39.887 20.2716 39.9063 20.3041 39.9313 20.3318C39.9564 20.3596 39.9867 20.3821 40.0205 20.3981C40.0543 20.4141 40.0909 20.4232 40.1283 20.425C40.1656 20.4267 40.2029 20.4211 40.2381 20.4084C40.8012 20.209 41.2854 19.713 41.7083 18.9671C41.7454 18.9017 41.755 18.8242 41.735 18.7517C41.715 18.6792 41.667 18.6177 41.6016 18.5806C41.5362 18.5435 41.4587 18.5339 41.3862 18.554C41.3137 18.574 41.2522 18.622 41.2151 18.6874C40.8532 19.3257 40.4601 19.7283 40.0489 19.874ZM36.3662 20.7087C36.3319 20.7231 36.3007 20.7442 36.2746 20.7706C36.2484 20.7971 36.2277 20.8285 36.2136 20.863C36.1996 20.8974 36.1925 20.9343 36.1927 20.9716C36.1929 21.0088 36.2004 21.0456 36.2149 21.0799C36.2293 21.1142 36.2504 21.1454 36.2769 21.1715C36.3033 21.1977 36.3347 21.2184 36.3692 21.2324C36.4037 21.2465 36.4406 21.2536 36.4778 21.2534C36.515 21.2532 36.5518 21.2456 36.5861 21.2312C37.1757 20.9829 37.7714 20.792 38.3357 20.6679C38.4091 20.6517 38.4731 20.607 38.5136 20.5437C38.5541 20.4803 38.5677 20.4035 38.5516 20.3301C38.5354 20.2566 38.4907 20.1926 38.4274 20.1521C38.364 20.1117 38.2872 20.098 38.2138 20.1142C37.6153 20.2459 36.9871 20.4473 36.3662 20.7087ZM33.1143 22.7955C33.0607 22.8475 33.0298 22.9186 33.0283 22.9932C33.0267 23.0678 33.0547 23.1401 33.1061 23.1942C33.1576 23.2483 33.2282 23.28 33.3029 23.2823C33.3775 23.2846 33.45 23.2574 33.5047 23.2066C33.9359 22.7967 34.4242 22.42 34.9553 22.0829C35.0187 22.0426 35.0636 21.9788 35.08 21.9054C35.0964 21.832 35.083 21.7551 35.0427 21.6916C35.0024 21.6282 34.9385 21.5833 34.8651 21.5669C34.7918 21.5505 34.7149 21.5639 34.6514 21.6042C34.0901 21.9605 33.5729 22.3598 33.1143 22.7955ZM31.0777 26.1259C31.0686 26.162 31.0667 26.1995 31.0721 26.2364C31.0776 26.2732 31.0902 26.3086 31.1093 26.3406C31.1284 26.3725 31.1536 26.4004 31.1835 26.4226C31.2134 26.4448 31.2474 26.4609 31.2835 26.47C31.3196 26.4791 31.3571 26.481 31.3939 26.4755C31.4308 26.4701 31.4662 26.4575 31.4981 26.4384C31.5301 26.4193 31.558 26.394 31.5802 26.3642C31.6024 26.3343 31.6185 26.3003 31.6276 26.2642C31.7727 25.6871 32.0133 25.1347 32.3419 24.611C32.3799 24.5474 32.3915 24.4715 32.3742 24.3994C32.3569 24.3274 32.3121 24.265 32.2493 24.2256C32.1866 24.1862 32.1109 24.1729 32.0385 24.1886C31.9661 24.2043 31.9027 24.2478 31.8619 24.3096C31.5023 24.8826 31.2379 25.4896 31.0777 26.1259ZM31.0276 29.9893C31.0322 30.0262 31.0441 30.0619 31.0626 30.0943C31.081 30.1266 31.1056 30.155 31.135 30.1778C31.1644 30.2007 31.1981 30.2175 31.234 30.2273C31.2699 30.2371 31.3074 30.2398 31.3443 30.2352C31.3813 30.2305 31.4169 30.2186 31.4493 30.2002C31.4816 30.1818 31.51 30.1571 31.5328 30.1277C31.5557 30.0983 31.5725 30.0647 31.5823 30.0288C31.5922 29.9929 31.5948 29.9554 31.5902 29.9184C31.5208 29.3685 31.4806 28.7587 31.4683 28.0633C31.467 27.9881 31.4358 27.9165 31.3817 27.8643C31.3276 27.8121 31.255 27.7835 31.1798 27.7848C31.1046 27.7861 31.0331 27.8173 30.9808 27.8714C30.9286 27.9255 30.9 27.9981 30.9014 28.0733C30.914 28.7882 30.9556 29.418 31.0276 29.9893ZM32.2028 33.6691C32.238 33.7355 32.2981 33.7853 32.37 33.8074C32.4418 33.8295 32.5195 33.8222 32.586 33.7871C32.6524 33.7519 32.7022 33.6918 32.7243 33.6199C32.7465 33.5481 32.7392 33.4704 32.704 33.4039C32.3876 32.8064 32.1554 32.271 31.9768 31.7139C31.9539 31.6423 31.9034 31.5828 31.8365 31.5484C31.7697 31.514 31.6919 31.5075 31.6203 31.5305C31.5487 31.5535 31.4892 31.6039 31.4548 31.6708C31.4204 31.7376 31.414 31.8154 31.4369 31.887C31.6265 32.4779 31.8719 33.0435 32.2028 33.6691ZM33.6326 36.0823C33.8058 36.3634 33.9778 36.6453 34.1485 36.9279C34.1874 36.9923 34.2502 37.0386 34.3233 37.0567C34.3963 37.0747 34.4734 37.063 34.5378 37.0241C34.6022 36.9853 34.6485 36.9224 34.6665 36.8494C34.6846 36.7764 34.6729 36.6992 34.634 36.6348C34.4623 36.351 34.2895 36.0678 34.1155 35.7854C34.1306 35.81 33.7462 35.1858 33.6457 35.0219C33.6264 34.9897 33.601 34.9617 33.5709 34.9394C33.5407 34.9171 33.5065 34.901 33.4701 34.892C33.4337 34.8831 33.3958 34.8814 33.3588 34.8872C33.3217 34.8929 33.2862 34.906 33.2542 34.9256C33.2222 34.9451 33.1945 34.9709 33.1725 35.0013C33.1506 35.0317 33.1349 35.0661 33.1263 35.1026C33.1177 35.1391 33.1165 35.177 33.1227 35.2139C33.1289 35.2509 33.1423 35.2863 33.1623 35.3181C33.2632 35.4825 33.6481 36.1076 33.6326 36.0825V36.0823ZM35.3062 40.3242C35.3067 40.3615 35.3145 40.3982 35.3292 40.4324C35.3439 40.4666 35.3652 40.4976 35.3918 40.5236C35.4185 40.5496 35.45 40.57 35.4846 40.5838C35.5192 40.5976 35.5561 40.6045 35.5933 40.604C35.6306 40.6035 35.6673 40.5957 35.7015 40.581C35.7357 40.5663 35.7667 40.545 35.7927 40.5184C35.8187 40.4917 35.8392 40.4602 35.853 40.4256C35.8668 40.391 35.8736 40.3541 35.8731 40.3169C35.8648 39.6518 35.7475 38.9926 35.526 38.3655C35.4998 38.2962 35.4474 38.2399 35.3802 38.2087C35.313 38.1775 35.2363 38.1738 35.1664 38.1985C35.0966 38.2232 35.0392 38.2743 35.0065 38.3407C34.9738 38.4072 34.9684 38.4839 34.9915 38.5543C35.1924 39.1231 35.2987 39.721 35.3062 40.3242ZM34.27 43.7311C34.2299 43.7937 34.216 43.8695 34.2313 43.9422C34.2466 44.015 34.2898 44.0788 34.3517 44.12C34.4135 44.1612 34.4891 44.1764 34.5621 44.1624C34.6351 44.1484 34.6997 44.1063 34.7419 44.0452C35.109 43.4961 35.3947 42.8967 35.59 42.2658C35.6121 42.1939 35.6048 42.1162 35.5696 42.0497C35.5344 41.9833 35.4743 41.9335 35.4024 41.9114C35.3305 41.8893 35.2528 41.8967 35.1864 41.9319C35.1199 41.9671 35.0702 42.0272 35.0481 42.0991C34.8689 42.6778 34.6068 43.2275 34.27 43.7311Z" fill="#FDE5D8"/>
+<path d="M60.8446 31.6803L53.1713 38.2926M49.7697 32.4009L59.3328 29.0618L49.7697 32.4009Z" stroke="#FDE5D8" stroke-linecap="round"/>
+<path d="M60.2631 30.4887C60.3987 30.4104 60.4451 30.237 60.3668 30.1014C60.2886 29.9659 60.1152 29.9194 59.9796 29.9977L54.1288 33.3756C53.9932 33.4539 53.9468 33.6273 54.0251 33.7629C54.1033 33.8984 54.2767 33.9449 54.4123 33.8666L60.2631 30.4887Z" fill="#FDE5D8"/>
+<path d="M63.2421 30.9507L61.2578 27.5138C61.1535 27.3331 60.9223 27.2711 60.7415 27.3755L59.9327 27.8425C59.752 27.9468 59.69 28.178 59.7944 28.3588L61.7786 31.7956C61.883 31.9764 62.1142 32.0383 62.2949 31.9339L63.1037 31.467C63.2845 31.3626 63.3464 31.1314 63.2421 30.9507Z" fill="white" stroke="#FDE5D8"/>
+<path d="M69.8124 20.1746L73.8754 27.2119L63.7936 33.0326L59.7306 25.9953L69.8124 20.1746Z" stroke="#FDE5D8"/>
+<path d="M68.6454 29.795L68.3599 32.8374C68.35 32.9411 68.2766 33.0767 68.1935 33.1419L64.4623 36.0762C64.2994 36.2045 64.2327 36.1536 64.3133 35.9633L65.268 33.7089L65.5559 31.5788" stroke="#FDE5D8"/>
+<path d="M64.9604 23.4123L62.1829 22.1385C62.088 22.0951 61.934 22.0911 61.836 22.1302L57.4292 23.8944C57.2367 23.9715 57.2473 24.0546 57.4525 24.08L59.8823 24.3805L61.8709 25.1962" stroke="#FDE5D8"/>
+<path d="M69.8126 20.1746L76.321 20.7344C76.6326 20.7612 76.778 21.0118 76.645 21.2955L73.8758 27.2118" stroke="#FDE5D8"/>
+<path d="M62.0503 27.3839C62.1285 27.5195 62.3012 27.5663 62.4359 27.4886C62.5705 27.4108 62.6163 27.2379 62.538 27.1023C62.4597 26.9667 62.2871 26.9198 62.1524 26.9976C62.0177 27.0754 61.972 27.2483 62.0503 27.3839Z" fill="#FC8A51"/>
+<path d="M62.6173 28.3658C62.6955 28.5014 62.8682 28.5483 63.0028 28.4705C63.1375 28.3928 63.1832 28.2198 63.105 28.0842C63.0267 27.9487 62.8541 27.9018 62.7194 27.9795C62.5847 28.0573 62.539 28.2302 62.6173 28.3658Z" fill="#FC8A51"/>
+<path d="M63.1841 29.3476C63.2624 29.4832 63.435 29.5301 63.5697 29.4523C63.7044 29.3746 63.7501 29.2016 63.6718 29.066C63.5935 28.9305 63.4209 28.8836 63.2862 28.9613C63.1516 29.0391 63.1058 29.212 63.1841 29.3476Z" fill="#FC8A51"/>
+<path d="M63.7511 30.3297C63.8294 30.4653 64.002 30.5121 64.1367 30.4344C64.2714 30.3566 64.3171 30.1837 64.2388 30.0481C64.1605 29.9125 63.9879 29.8656 63.8532 29.9434C63.7186 30.0212 63.6728 30.1941 63.7511 30.3297Z" fill="#FC8A51"/>
+<path d="M65.9899 27.0731C66.5378 28.0221 67.7464 28.3501 68.6894 27.8057C69.6324 27.2612 69.9527 26.0505 69.4048 25.1015C68.8568 24.1524 67.6482 23.8244 66.7052 24.3689C65.7622 24.9133 65.4419 26.124 65.9899 27.0731Z" stroke="#FDE5D8"/>
+<path d="M66.8032 26.6036C67.0902 27.1008 67.7233 27.2726 68.2173 26.9874C68.7113 26.7022 68.879 26.068 68.592 25.5709C68.305 25.0738 67.6719 24.902 67.1779 25.1872C66.684 25.4723 66.5162 26.1065 66.8032 26.6036Z" stroke="#FDE5D8"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M18.5279 7.40988C18.4991 7.72887 18.5432 8.13101 18.7163 8.57152C18.7364 8.61656 18.7731 8.65211 18.8188 8.6708C18.8645 8.68948 18.9156 8.68987 18.9615 8.67188C19.0075 8.65389 19.0447 8.61891 19.0656 8.57418C19.0864 8.52945 19.0892 8.47842 19.0734 8.43167C18.924 8.05108 18.8863 7.70752 18.91 7.44408C18.9119 7.42254 18.9137 7.40893 18.9145 7.40421C18.9193 7.37911 18.9189 7.35331 18.9136 7.32834C18.9082 7.30336 18.8979 7.2797 18.8833 7.25876C18.8687 7.23781 18.85 7.22 18.8284 7.20637C18.8068 7.19273 18.7827 7.18355 18.7575 7.17936C18.7323 7.17517 18.7065 7.17605 18.6817 7.18196C18.6568 7.18787 18.6334 7.19868 18.6128 7.21376C18.5922 7.22885 18.5748 7.2479 18.5616 7.26979C18.5485 7.29169 18.5398 7.31599 18.5362 7.34128C18.5326 7.36404 18.5298 7.38692 18.5279 7.40988ZM20.6986 10.3736C20.7914 10.4189 20.9061 10.3855 20.9547 10.2991C21.0033 10.213 20.9675 10.1064 20.8746 10.061C20.5219 9.88963 20.2211 9.69933 19.9686 9.49278C19.9299 9.46178 19.8811 9.44626 19.8316 9.44923C19.7822 9.4522 19.7355 9.47344 19.7008 9.50884C19.6845 9.52574 19.6719 9.5459 19.6638 9.568C19.6557 9.59009 19.6524 9.61364 19.654 9.63711C19.6556 9.66058 19.6622 9.68344 19.6732 9.70422C19.6842 9.725 19.6995 9.74323 19.718 9.75772C19.9936 9.98298 20.3195 10.1892 20.6988 10.3738L20.6986 10.3736ZM23.1801 11.5188C23.2048 11.5264 23.2308 11.5289 23.2565 11.5263C23.2822 11.5238 23.3072 11.5161 23.3299 11.5038C23.3527 11.4915 23.3727 11.4748 23.389 11.4546C23.4052 11.4345 23.4172 11.4113 23.4244 11.3865C23.4399 11.3362 23.435 11.2819 23.4107 11.2352C23.3865 11.1886 23.3448 11.1533 23.2948 11.1371C22.9212 11.0216 22.5486 10.903 22.177 10.7814C22.1524 10.7735 22.1264 10.7705 22.1007 10.7727C22.0749 10.7749 22.0498 10.7822 22.0269 10.7941C22.004 10.8061 21.9837 10.8225 21.9672 10.8425C21.9507 10.8624 21.9383 10.8854 21.9308 10.9101C21.9146 10.9602 21.9187 11.0146 21.9423 11.0616C21.966 11.1087 22.0072 11.1445 22.057 11.1614C22.3837 11.269 22.606 11.3391 23.1801 11.5188ZM25.4353 12.2611C25.5204 12.3076 25.6285 12.2796 25.6772 12.1987C25.6887 12.1795 25.6962 12.158 25.6991 12.1358C25.7021 12.1135 25.7004 12.0909 25.6943 12.0692C25.6882 12.0476 25.6777 12.0275 25.6635 12.0101C25.6493 11.9927 25.6318 11.9784 25.6118 11.968C25.3295 11.8132 25.0086 11.676 24.6165 11.5381C24.5246 11.5059 24.4228 11.5507 24.389 11.6382C24.3549 11.7259 24.4022 11.823 24.4938 11.8554C24.8688 11.9871 25.1723 12.1169 25.4353 12.2611ZM27.2083 14.2457C27.247 14.3391 27.3614 14.3859 27.4632 14.3504C27.5653 14.3149 27.6163 14.2106 27.5776 14.1172C27.4169 13.7302 27.2274 13.3999 27.0036 13.1147C26.94 13.0336 26.8166 13.0151 26.7279 13.0735C26.6393 13.1317 26.6193 13.2447 26.6829 13.3258C26.8859 13.5843 27.0594 13.887 27.2083 14.2457ZM27.7057 16.8237C27.7091 16.9282 27.813 17.0108 27.9378 17.008C28.0625 17.005 28.1607 16.9178 28.1575 16.8133C28.1437 16.3968 28.1065 16.0185 28.04 15.6568C28.0209 15.5534 27.9054 15.4824 27.782 15.4984C27.6588 15.5143 27.5742 15.6113 27.5932 15.7146C27.6567 16.0595 27.6925 16.4224 27.7057 16.8237Z" fill="#EEEEEE"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M27.6172 18.4687C27.6047 18.7996 27.5957 19.1305 27.5903 19.4616C27.589 19.5661 27.6652 19.6521 27.7608 19.6536C27.8564 19.6551 27.9352 19.5718 27.9365 19.4675C27.9403 19.1902 27.9471 18.9608 27.9634 18.4827L27.9683 18.3378C27.9717 18.2334 27.897 18.1458 27.8016 18.142C27.706 18.1382 27.6257 18.2195 27.6221 18.324L27.6172 18.4687ZM27.7678 22.1483C27.7905 22.2513 27.895 22.3168 28.0014 22.2951C28.1076 22.2732 28.1754 22.1721 28.1529 22.0691C28.0741 21.71 28.0187 21.3434 27.9834 20.9609C27.9736 20.8562 27.878 20.7788 27.7697 20.7882C27.6614 20.7977 27.5815 20.8903 27.5911 20.995C27.6279 21.3922 27.6856 21.7737 27.7678 22.1483ZM28.7232 24.4772C28.7792 24.563 28.9033 24.5925 29.0005 24.543C29.0976 24.4937 29.1309 24.3841 29.0751 24.2983C28.8775 23.9966 28.7013 23.6814 28.5479 23.3551C28.505 23.2634 28.3863 23.22 28.2827 23.2578C28.1792 23.2956 28.1297 23.4004 28.1728 23.4919C28.333 23.8328 28.5169 24.1621 28.7232 24.4772ZM29.633 25.9325C29.8392 26.1713 30.063 26.3944 30.3025 26.5998C30.3398 26.6315 30.3877 26.6478 30.4366 26.6455C30.4855 26.6432 30.5317 26.6224 30.5658 26.5873C30.5821 26.5704 30.5948 26.5503 30.603 26.5282C30.6113 26.5062 30.6149 26.4827 30.6136 26.4592C30.6124 26.4357 30.6063 26.4127 30.5958 26.3916C30.5853 26.3706 30.5706 26.3519 30.5525 26.3368C30.3268 26.1431 30.1158 25.9329 29.9214 25.7078C29.8848 25.6652 29.8485 25.6222 29.8125 25.579C29.7807 25.5418 29.7359 25.5182 29.6872 25.5129C29.6386 25.5075 29.5897 25.5209 29.5506 25.5502C29.5315 25.5641 29.5156 25.5818 29.5036 25.6021C29.4917 25.6224 29.4841 25.645 29.4812 25.6684C29.4784 25.6918 29.4804 25.7155 29.4871 25.7381C29.4939 25.7607 29.5052 25.7816 29.5204 25.7997C29.5574 25.8439 29.5948 25.8883 29.633 25.9325ZM32.6235 28.1392C32.7142 28.1849 32.8223 28.1424 32.8647 28.0445C32.907 27.9463 32.8677 27.8299 32.777 27.7841C32.4527 27.6215 32.1381 27.4403 31.8347 27.2414C31.7491 27.1851 31.6375 27.2142 31.5853 27.3066C31.5331 27.3988 31.5602 27.5194 31.6458 27.5757C31.9605 27.7823 32.287 27.9704 32.6235 28.1392Z" fill="#E5E5E5"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M35.2883 29.0967C35.3928 29.1234 35.4977 29.054 35.5225 28.9418C35.5472 28.8293 35.4828 28.7165 35.3783 28.6898C35.0044 28.5945 34.6348 28.4834 34.2703 28.3567C34.1683 28.3212 34.0587 28.3812 34.0254 28.4912C33.9923 28.601 34.0483 28.7189 34.1505 28.7545C34.5248 28.8846 34.9044 28.9987 35.2883 29.0965V29.0967ZM37.7828 29.48C37.8875 29.4875 37.9775 29.3959 37.9841 29.2753C37.9907 29.1545 37.9111 29.0508 37.8066 29.0432C37.4321 29.0161 37.0589 28.9735 36.6879 28.9155C36.584 28.8992 36.4885 28.9831 36.4744 29.1028C36.4604 29.2226 36.5329 29.3327 36.6369 29.349C37.0169 29.4085 37.3993 29.4522 37.783 29.48H37.7828ZM40.464 29.2169C40.568 29.2065 40.6417 29.1305 40.6286 29.0468C40.6158 28.9635 40.5211 28.9041 40.4174 28.9145C40.0551 28.9508 39.6817 28.975 39.3005 28.9867C39.1958 28.9899 39.1144 29.0608 39.1185 29.1449C39.1225 29.229 39.2105 29.2946 39.3151 29.2912C39.6988 29.2795 40.082 29.2548 40.464 29.2169ZM43.156 28.7148C43.2562 28.6732 43.3026 28.5606 43.2599 28.4631C43.2385 28.4159 43.1995 28.379 43.1513 28.3601C43.1031 28.3412 43.0494 28.3419 43.0016 28.362C42.6464 28.5079 42.2817 28.6294 41.9099 28.7254C41.8852 28.7315 41.862 28.7424 41.8416 28.7575C41.8212 28.7727 41.804 28.7918 41.7911 28.8136C41.7781 28.8355 41.7696 28.8597 41.7662 28.8849C41.7627 28.9101 41.7643 28.9357 41.7708 28.9603C41.7991 29.0627 41.9071 29.1234 42.0121 29.0958C42.4018 28.9951 42.784 28.8678 43.1562 28.7148H43.156ZM45.326 26.9135C45.3812 26.8313 45.354 26.7234 45.2656 26.6721C45.1767 26.6211 45.0601 26.6461 45.0049 26.7283C44.8241 26.9974 44.5845 27.2449 44.2899 27.4691C44.2709 27.4827 44.255 27.5003 44.2433 27.5205C44.2316 27.5407 44.2242 27.5632 44.2218 27.5864C44.2193 27.6097 44.2218 27.6332 44.229 27.6554C44.2362 27.6777 44.2481 27.6981 44.2638 27.7155C44.3299 27.7903 44.449 27.8011 44.5299 27.7397C44.8555 27.4921 45.1225 27.216 45.326 26.9135ZM46.8046 25.0747C46.8407 25.0429 46.8628 24.998 46.866 24.9499C46.8692 24.9018 46.8532 24.8544 46.8216 24.8181C46.8059 24.8001 46.7869 24.7854 46.7655 24.7748C46.7441 24.7642 46.7209 24.758 46.6971 24.7564C46.6733 24.7548 46.6494 24.7579 46.6268 24.7656C46.6043 24.7733 46.5834 24.7853 46.5655 24.8011C46.2859 25.0458 46.0227 25.3086 45.7775 25.5878C45.7458 25.6241 45.7297 25.6716 45.7329 25.7197C45.736 25.7678 45.7581 25.8127 45.7943 25.8446C45.8122 25.8604 45.833 25.8724 45.8556 25.8801C45.8781 25.8878 45.902 25.891 45.9257 25.8894C45.9495 25.8878 45.9728 25.8816 45.9941 25.871C46.0155 25.8604 46.0345 25.8458 46.0502 25.8278C46.2849 25.5606 46.5368 25.309 46.8044 25.0747H46.8046ZM49.036 23.5975C49.126 23.5489 49.1602 23.4354 49.1125 23.3439C49.1015 23.3222 49.0862 23.303 49.0676 23.2873C49.049 23.2716 49.0275 23.2598 49.0043 23.2526C48.9811 23.2453 48.9566 23.2428 48.9324 23.2451C48.9082 23.2474 48.8847 23.2545 48.8633 23.266C48.5322 23.4443 48.2091 23.6372 47.8952 23.8441C47.8099 23.9004 47.7856 24.0167 47.8409 24.1036C47.8965 24.1905 48.0106 24.2151 48.096 24.1588C48.4009 23.9579 48.7145 23.7706 49.036 23.5975ZM51.454 22.4735C51.5579 22.4415 51.6148 22.3367 51.581 22.239C51.5471 22.1411 51.4354 22.0878 51.3315 22.1195C50.9508 22.236 50.5742 22.3654 50.2024 22.5077C50.1011 22.5464 50.0523 22.6551 50.0935 22.7501C50.1347 22.8454 50.2504 22.8911 50.3517 22.8524C50.7147 22.7135 51.0823 22.5872 51.454 22.4735Z" fill="#EEEEEE"/>
+<path d="M27.7796 18.3307C28.3014 18.3307 28.7244 17.9076 28.7244 17.3858C28.7244 16.864 28.3014 16.4409 27.7796 16.4409C27.2577 16.4409 26.8347 16.864 26.8347 17.3858C26.8347 17.9076 27.2577 18.3307 27.7796 18.3307Z" fill="white" stroke="#EEEEEE"/>
+<path d="M45.3543 27.4016C45.8761 27.4016 46.2992 26.9786 46.2992 26.4567C46.2992 25.9349 45.8761 25.5118 45.3543 25.5118C44.8325 25.5118 44.4094 25.9349 44.4094 26.4567C44.4094 26.9786 44.8325 27.4016 45.3543 27.4016Z" fill="white" stroke="#EEEEEE"/>
+<path d="M4.16876 10.9607C4.16876 10.9607 0.867338 17.1969 1.90104 20.9764C2.93474 24.756 5.11364 27.0237 9.08214 29.2914C13.0506 31.5591 15.2125 28.3465 20.5984 30.4253C25.9842 32.504 26.0787 38.5513 26.0787 38.5513" stroke="#B5A7DD" stroke-width="0.4" stroke-linecap="round" stroke-dasharray="8 10"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M49.8898 47.6221C49.8898 39.5857 43.375 33.0709 35.3386 33.0709C27.3022 33.0709 20.7874 39.5857 20.7874 47.6221" fill="white"/>
+<path d="M49.8898 47.6221C49.8898 39.5857 43.375 33.0709 35.3386 33.0709C27.3022 33.0709 20.7874 39.5857 20.7874 47.6221" stroke="#EEEEEE" stroke-linecap="round"/>
+<path d="M41.1969 43.8425C42.8668 43.8425 44.2205 42.4888 44.2205 40.8189C44.2205 39.149 42.8668 37.7953 41.1969 37.7953C39.527 37.7953 38.1732 39.149 38.1732 40.8189C38.1732 42.4888 39.527 43.8425 41.1969 43.8425Z" stroke="#EEEEEE"/>
+<path d="M28.8189 40.441C29.7061 40.441 30.4252 39.7218 30.4252 38.8347C30.4252 37.9476 29.7061 37.2284 28.8189 37.2284C27.9318 37.2284 27.2126 37.9476 27.2126 38.8347C27.2126 39.7218 27.9318 40.441 28.8189 40.441Z" stroke="#EEEEEE"/>
+<path d="M24.9449 44.9764C25.4667 44.9764 25.8898 44.5534 25.8898 44.0316C25.8898 43.5097 25.4667 43.0867 24.9449 43.0867C24.423 43.0867 24 43.5097 24 44.0316C24 44.5534 24.423 44.9764 24.9449 44.9764Z" stroke="#EEEEEE"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M45.703 3.43286L46.1389 3.54966C46.346 2.7768 47.1318 2.3159 47.8939 2.5201L48.0123 2.078C47.0096 1.80933 45.9757 2.416 45.7032 3.43291L45.703 3.43286Z" fill="#FC8A51"/>
+<path d="M47.9471 2.61347C48.1478 2.66723 48.3546 2.54588 48.4091 2.34244C48.4636 2.139 48.3452 1.93051 48.1445 1.87675C47.9439 1.823 47.7371 1.94434 47.6826 2.14778C47.6281 2.35122 47.7465 2.55972 47.9471 2.61347Z" fill="#FC8A51"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M45.412 3.35511L44.9761 3.23831C45.1832 2.46545 44.7332 1.67342 43.9711 1.46921L44.0896 1.02711C45.0922 1.29577 45.6843 2.33814 45.4119 3.35506L45.412 3.35511Z" fill="#FC8A51"/>
+<path d="M43.8787 1.52354C43.6781 1.46979 43.5597 1.26129 43.6142 1.05785C43.6687 0.854411 43.8755 0.733068 44.0761 0.786823C44.2768 0.840578 44.3952 1.04908 44.3407 1.25252C44.2862 1.45596 44.0794 1.5773 43.8787 1.52354Z" fill="#FC8A51"/>
+<path d="M47.8422 7.79545L46.7544 7.50399C46.6536 7.47698 46.55 7.5368 46.523 7.63762C46.496 7.73843 46.5558 7.84205 46.6566 7.86907L47.7444 8.16052C47.8452 8.18754 47.9488 8.12771 47.9758 8.0269C48.0028 7.92608 47.943 7.82246 47.8422 7.79545Z" fill="#FC8A51"/>
+<path d="M47.4759 9.27595L46.5007 8.7129C46.4103 8.66071 46.2947 8.69168 46.2425 8.78207C46.1904 8.87245 46.2213 8.98803 46.3117 9.04021L47.287 9.60327C47.3773 9.65545 47.4929 9.62448 47.5451 9.5341C47.5973 9.44371 47.5663 9.32813 47.4759 9.27595Z" fill="#FC8A51"/>
+<path d="M48.1795 6.35876L47.0534 6.35876C46.949 6.35876 46.8644 6.44337 46.8644 6.54774C46.8644 6.65211 46.949 6.73671 47.0534 6.73671L48.1795 6.73671C48.2839 6.73671 48.3685 6.6521 48.3685 6.54774C48.3685 6.44337 48.2839 6.35876 48.1795 6.35876Z" fill="#FC8A51"/>
+<path d="M41.3783 6.06356L42.4661 6.35502C42.5669 6.38203 42.6267 6.48565 42.5997 6.58647C42.5727 6.68728 42.469 6.74711 42.3682 6.72009L41.2805 6.42863C41.1797 6.40162 41.1198 6.298 41.1469 6.19719C41.1739 6.09637 41.2775 6.03655 41.3783 6.06356Z" fill="#FC8A51"/>
+<path d="M40.9549 7.52856L42.081 7.52856C42.1854 7.52856 42.27 7.61317 42.27 7.71754C42.27 7.82191 42.1854 7.90651 42.081 7.90651L40.9549 7.90651C40.8506 7.90651 40.7659 7.8219 40.7659 7.71754C40.7659 7.61317 40.8505 7.52856 40.9549 7.52856Z" fill="#FC8A51"/>
+<path d="M41.8041 4.65032L42.7794 5.21337C42.8698 5.26556 42.9007 5.38113 42.8485 5.47152C42.7964 5.5619 42.6808 5.59287 42.5904 5.54069L41.6151 4.97763C41.5248 4.92545 41.4938 4.80987 41.546 4.71949C41.5982 4.6291 41.7137 4.59813 41.8041 4.65032Z" fill="#FC8A51"/>
+<path d="M47.3515 6.28652C47.7063 4.96254 46.9206 3.60168 45.5967 3.24693C44.2728 2.89219 42.912 3.6779 42.5572 5.00187L42.1051 6.68907C41.7504 8.01304 42.536 9.3739 43.86 9.72865C45.1839 10.0834 46.5447 9.29768 46.8995 7.97371L47.3515 6.28652Z" stroke="#FC8A51"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M42.7135 5.00037L47.0721 6.16826L46.9254 6.71587L42.5668 5.54798L42.7135 5.00037Z" fill="#FC8A51"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M17.4662 2.50359L17.1869 2.57842C17.1557 2.46241 17.1019 2.35369 17.0286 2.25847C16.9554 2.16325 16.8641 2.0834 16.7599 2.02348C16.6558 1.96355 16.5409 1.92474 16.4217 1.90924C16.3026 1.89374 16.1816 1.90187 16.0656 1.93316L15.9909 1.6546C16.634 1.48229 17.2944 1.86252 17.4662 2.50359Z" fill="#EEEEEE"/>
+<path d="M16.0312 1.99224C15.9025 2.02671 15.7704 1.95069 15.736 1.82246C15.7017 1.69423 15.7781 1.56233 15.9067 1.52786C16.0354 1.4934 16.1675 1.56941 16.2019 1.69764C16.2362 1.82587 16.1598 1.95777 16.0312 1.99224Z" fill="#EEEEEE"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M17.6526 2.45366L17.9318 2.37883C17.9009 2.26274 17.8931 2.14169 17.909 2.02259C17.9248 1.9035 17.964 1.78869 18.0242 1.68473C18.0844 1.58076 18.1645 1.48969 18.2599 1.4167C18.3554 1.3437 18.4642 1.29023 18.5804 1.25932L18.5057 0.980773C17.8626 1.15308 17.4808 1.81259 17.6526 2.45366Z" fill="#EEEEEE"/>
+<path d="M18.6393 1.29344C18.7679 1.25898 18.8444 1.12708 18.81 0.998847C18.7756 0.870613 18.6435 0.794601 18.5149 0.82907C18.3862 0.863538 18.3098 0.995433 18.3442 1.12367C18.3785 1.2519 18.5107 1.32791 18.6393 1.29344Z" fill="#EEEEEE"/>
+<path d="M16.1505 5.24482L16.7167 5.0931C16.8175 5.06609 16.9211 5.12591 16.9481 5.22673C16.9752 5.32754 16.9153 5.43116 16.8145 5.45817L16.2483 5.60989C16.1475 5.63691 16.0439 5.57708 16.0168 5.47627C15.9898 5.37546 16.0497 5.27183 16.1505 5.24482Z" fill="#EEEEEE"/>
+<path d="M16.3578 6.1703L16.8655 5.8772C16.9559 5.82502 17.0714 5.85599 17.1236 5.94637C17.1758 6.03676 17.1448 6.15233 17.0545 6.20452L16.5468 6.49762C16.4564 6.5498 16.3408 6.51883 16.2886 6.42845C16.2365 6.33806 16.2674 6.22249 16.3578 6.1703Z" fill="#EEEEEE"/>
+<path d="M15.9573 4.35278L16.5435 4.35278C16.6479 4.35278 16.7325 4.43739 16.7325 4.54176C16.7325 4.64613 16.6479 4.73074 16.5435 4.73074L15.9573 4.73074C15.853 4.73074 15.7684 4.64613 15.7684 4.54176C15.7684 4.43739 15.853 4.35278 15.9573 4.35278Z" fill="#EEEEEE"/>
+<path d="M20.1626 4.16985L19.5964 4.32157C19.4956 4.34859 19.4357 4.45221 19.4628 4.55302C19.4898 4.65383 19.5934 4.71366 19.6942 4.68665L20.2604 4.53493C20.3612 4.50791 20.4211 4.40429 20.3941 4.30348C20.367 4.20267 20.2634 4.14284 20.1626 4.16985Z" fill="#EEEEEE"/>
+<path d="M20.4451 5.07507L19.8589 5.07507C19.7545 5.07507 19.6699 5.15968 19.6699 5.26405C19.6699 5.36842 19.7545 5.45303 19.8589 5.45303L20.4451 5.45303C20.5494 5.45303 20.634 5.36842 20.634 5.26405C20.634 5.15968 20.5494 5.07507 20.4451 5.07507Z" fill="#EEEEEE"/>
+<path d="M19.8834 3.30076L19.3757 3.59387C19.2853 3.64605 19.2543 3.76163 19.3065 3.85201C19.3587 3.9424 19.4743 3.97337 19.5647 3.92118L20.0723 3.62808C20.1627 3.5759 20.1937 3.46032 20.1415 3.36993C20.0893 3.27955 19.9737 3.24858 19.8834 3.30076Z" fill="#EEEEEE"/>
+<path d="M17.5341 2.38566L17.5343 2.38561C18.3829 2.15821 19.2552 2.66184 19.4826 3.51048L19.7541 4.52356C19.9815 5.3722 19.4778 6.24449 18.6292 6.47189L18.629 6.47193C17.7804 6.69933 16.9081 6.19571 16.6807 5.34707L16.4092 4.33398C16.1819 3.48534 16.6855 2.61305 17.5341 2.38566Z" stroke="#EEEEEE"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M19.3759 3.48474L16.582 4.23337L16.6798 4.59844L19.4737 3.84982L19.3759 3.48474Z" fill="#EEEEEE"/>
+<path d="M1.98426 23.0551C2.87139 23.0551 3.59056 22.336 3.59056 21.4488C3.59056 20.5617 2.87139 19.8425 1.98426 19.8425C1.09712 19.8425 0.37796 20.5617 0.37796 21.4488C0.37796 22.336 1.09712 23.0551 1.98426 23.0551Z" fill="white" stroke="#B5A7DD" stroke-width="0.4"/>
+<path d="M32.7874 24.9449C33.4658 24.9449 34.0158 24.3949 34.0158 23.7165C34.0158 23.0381 33.4658 22.4882 32.7874 22.4882C32.109 22.4882 31.5591 23.0381 31.5591 23.7165C31.5591 24.3949 32.109 24.9449 32.7874 24.9449Z" fill="white"/>
+<path d="M10.1102 31.9371C11.4148 31.9371 12.4724 30.8795 12.4724 29.5749C12.4724 28.2702 11.4148 27.2126 10.1102 27.2126C8.80561 27.2126 7.74802 28.2702 7.74802 29.5749C7.74802 30.8795 8.80561 31.9371 10.1102 31.9371Z" fill="white" stroke="#6B4FBB"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M9.77635 29.759L9.44262 29.4251C9.38944 29.3719 9.31732 29.3421 9.24211 29.3421C9.16691 29.3421 9.09479 29.3719 9.04161 29.4251C8.98843 29.4783 8.95856 29.5504 8.95856 29.6256C8.95856 29.7008 8.98843 29.773 9.04161 29.8261L9.57566 30.3602H9.57585V30.3604C9.68734 30.4719 9.86592 30.4713 9.97629 30.3609L11.1801 29.1572C11.2328 29.1038 11.2623 29.0319 11.2622 28.9569C11.2621 28.8819 11.2324 28.8101 11.1795 28.7569C11.1533 28.7305 11.1222 28.7095 11.0879 28.6952C11.0535 28.6808 11.0167 28.6734 10.9795 28.6733C10.9423 28.6733 10.9055 28.6805 10.8711 28.6948C10.8368 28.709 10.8055 28.7298 10.7792 28.7561L9.77635 29.759Z" fill="#FC8A51"/>
+<path d="M32.7874 24.7559C33.4658 24.7559 34.0158 24.2059 34.0158 23.5275C34.0158 22.8491 33.4658 22.2992 32.7874 22.2992C32.109 22.2992 31.5591 22.8491 31.5591 23.5275C31.5591 24.2059 32.109 24.7559 32.7874 24.7559Z" fill="white" stroke="#FC8A51"/>
+<path d="M4.53541 11.3386C5.16162 11.3386 5.66926 10.831 5.66926 10.2048C5.66926 9.57857 5.16162 9.07092 4.53541 9.07092C3.9092 9.07092 3.40155 9.57857 3.40155 10.2048C3.40155 10.831 3.9092 11.3386 4.53541 11.3386Z" fill="white" stroke="#B5A7DD" stroke-width="0.4"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M35.9339 27.1363C37.5787 25.6725 38.0151 23.182 36.8527 21.1688C35.5261 18.8708 32.6187 18.0654 30.3591 19.3701C28.0993 20.6748 27.343 23.5953 28.6698 25.8932C29.8483 27.9342 32.2732 28.7978 34.3841 28.0513L36.4437 31.619C36.563 31.8259 36.7596 31.9769 36.9903 32.0389C37.2209 32.1008 37.4667 32.0686 37.6736 31.9493C37.8802 31.8297 38.0309 31.6329 38.0926 31.4023C38.1542 31.1716 38.1218 30.9259 38.0024 30.7191L35.9339 27.1363ZM34.34 26.2653C35.8248 25.4081 36.3218 23.4889 35.4499 21.9788C34.5782 20.4686 32.6676 19.9395 31.1828 20.7967C29.6978 21.6541 29.2008 23.5733 30.0726 25.0834C30.9445 26.5934 32.8551 27.1227 34.34 26.2653Z" fill="white" stroke="#B5A7DD" stroke-width="0.5"/>
+</g>
+<defs>
+<clipPath id="clip0">
+<rect width="80.315" height="48" fill="white"/>
+</clipPath>
+</defs>
+</svg>
diff --git a/app/assets/javascripts/diffs/components/app.vue b/app/assets/javascripts/diffs/components/app.vue
index 8f41b848b0b..02fb5df07e8 100644
--- a/app/assets/javascripts/diffs/components/app.vue
+++ b/app/assets/javascripts/diffs/components/app.vue
@@ -184,7 +184,12 @@ export default {
'viewDiffsFileByFile',
'mrReviews',
]),
- ...mapGetters('diffs', ['whichCollapsedTypes', 'isParallelView', 'currentDiffIndex']),
+ ...mapGetters('diffs', [
+ 'whichCollapsedTypes',
+ 'isParallelView',
+ 'currentDiffIndex',
+ 'fileCodequalityDiff',
+ ]),
...mapGetters(['isNotesFetched', 'getNoteableData']),
diffs() {
if (!this.viewDiffsFileByFile) {
@@ -282,6 +287,7 @@ export default {
endpointMetadata: this.endpointMetadata,
endpointBatch: this.endpointBatch,
endpointCoverage: this.endpointCoverage,
+ endpointCodequality: this.endpointCodequality,
endpointUpdateUser: this.endpointUpdateUser,
projectPath: this.projectPath,
dismissEndpoint: this.dismissEndpoint,
@@ -291,10 +297,6 @@ export default {
mrReviews: this.rehydratedMrReviews,
});
- if (this.endpointCodequality) {
- this.setCodequalityEndpoint(this.endpointCodequality);
- }
-
if (this.shouldShow) {
this.fetchData();
}
@@ -339,7 +341,6 @@ export default {
...mapActions('diffs', [
'moveToNeighboringCommit',
'setBaseConfig',
- 'setCodequalityEndpoint',
'fetchDiffFilesMeta',
'fetchDiffFilesBatch',
'fetchCoverageFiles',
@@ -531,6 +532,7 @@ export default {
:help-page-path="helpPagePath"
:can-current-user-fork="canCurrentUserFork"
:view-diffs-file-by-file="viewDiffsFileByFile"
+ :codequality-diff="fileCodequalityDiff(file.file_path)"
/>
<div
v-if="showFileByFileNavigation"
diff --git a/app/assets/javascripts/diffs/components/diff_file.vue b/app/assets/javascripts/diffs/components/diff_file.vue
index bdbc13a38c4..93855db52b6 100644
--- a/app/assets/javascripts/diffs/components/diff_file.vue
+++ b/app/assets/javascripts/diffs/components/diff_file.vue
@@ -67,6 +67,11 @@ export default {
type: Boolean,
required: true,
},
+ codequalityDiff: {
+ type: Array,
+ required: false,
+ default: () => [],
+ },
},
data() {
return {
@@ -80,7 +85,7 @@ export default {
genericError: GENERIC_ERROR,
},
computed: {
- ...mapState('diffs', ['currentDiffFileId', 'codequalityDiff']),
+ ...mapState('diffs', ['currentDiffFileId']),
...mapGetters(['isNotesFetched']),
...mapGetters('diffs', ['getDiffFileDiscussions']),
viewBlobHref() {
@@ -149,9 +154,7 @@ export default {
return loggedIn && featureOn;
},
hasCodequalityChanges() {
- return (
- this.codequalityDiff?.files && this.codequalityDiff?.files[this.file.file_path]?.length > 0
- );
+ return this.codequalityDiff.length > 0;
},
},
watch: {
diff --git a/app/assets/javascripts/diffs/components/diff_row.vue b/app/assets/javascripts/diffs/components/diff_row.vue
index 9f0f7d46804..8d398a2ded4 100644
--- a/app/assets/javascripts/diffs/components/diff_row.vue
+++ b/app/assets/javascripts/diffs/components/diff_row.vue
@@ -13,6 +13,11 @@ import {
CONFLICT_THEIR,
CONFLICT_MARKER,
} from '../constants';
+import {
+ getInteropInlineAttributes,
+ getInteropOldSideAttributes,
+ getInteropNewSideAttributes,
+} from '../utils/interoperability';
import DiffGutterAvatars from './diff_gutter_avatars.vue';
import * as utils from './diff_row_utils';
@@ -116,6 +121,16 @@ export default {
isLeftConflictMarker() {
return [CONFLICT_MARKER_OUR, CONFLICT_MARKER_THEIR].includes(this.line.left?.type);
},
+ interopLeftAttributes() {
+ if (this.inline) {
+ return getInteropInlineAttributes(this.line.left);
+ }
+
+ return getInteropOldSideAttributes(this.line.left);
+ },
+ interopRightAttributes() {
+ return getInteropNewSideAttributes(this.line.right);
+ },
},
mounted() {
this.scrollToLineIfNeededParallel(this.line);
@@ -181,6 +196,7 @@ export default {
<div
data-testid="left-side"
class="diff-grid-left left-side"
+ v-bind="interopLeftAttributes"
@dragover.prevent
@dragenter="onDragEnter(line.left, index)"
@dragend="onDragEnd"
@@ -286,6 +302,7 @@ export default {
v-if="!inline"
data-testid="right-side"
class="diff-grid-right right-side"
+ v-bind="interopRightAttributes"
@dragover.prevent
@dragenter="onDragEnter(line.right, index)"
@dragend="onDragEnd"
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 b213e391a94..fd6c69e22c6 100644
--- a/app/assets/javascripts/diffs/components/inline_diff_table_row.vue
+++ b/app/assets/javascripts/diffs/components/inline_diff_table_row.vue
@@ -2,6 +2,7 @@
import { GlTooltipDirective, GlIcon, GlSafeHtmlDirective as SafeHtml } from '@gitlab/ui';
import { mapActions, mapGetters, mapState } from 'vuex';
import { CONTEXT_LINE_CLASS_NAME } from '../constants';
+import { getInteropInlineAttributes } from '../utils/interoperability';
import DiffGutterAvatars from './diff_gutter_avatars.vue';
import {
isHighlighted,
@@ -96,6 +97,9 @@ export default {
shouldShowAvatarsOnGutter() {
return this.line.hasDiscussions;
},
+ interopAttrs() {
+ return getInteropInlineAttributes(this.line);
+ },
},
mounted() {
this.scrollToLineIfNeededInline(this.line);
@@ -124,6 +128,7 @@ export default {
:id="inlineRowId"
:class="classNameMap"
class="line_holder"
+ v-bind="interopAttrs"
@mouseover="handleMouseMove"
@mouseout="handleMouseMove"
>
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 3d20dfd0c9b..147741fa2bb 100644
--- a/app/assets/javascripts/diffs/components/parallel_diff_table_row.vue
+++ b/app/assets/javascripts/diffs/components/parallel_diff_table_row.vue
@@ -3,6 +3,10 @@ import { GlTooltipDirective, GlIcon, GlSafeHtmlDirective as SafeHtml } from '@gi
import $ from 'jquery';
import { mapActions, mapGetters, mapState } from 'vuex';
import { CONTEXT_LINE_CLASS_NAME, PARALLEL_DIFF_VIEW_TYPE } from '../constants';
+import {
+ getInteropOldSideAttributes,
+ getInteropNewSideAttributes,
+} from '../utils/interoperability';
import DiffGutterAvatars from './diff_gutter_avatars.vue';
import * as utils from './diff_row_utils';
@@ -108,6 +112,12 @@ export default {
this.line.hasDiscussionsRight,
);
},
+ interopLeftAttributes() {
+ return getInteropOldSideAttributes(this.line.left);
+ },
+ interopRightAttributes() {
+ return getInteropNewSideAttributes(this.line.right);
+ },
},
mounted() {
this.scrollToLineIfNeededParallel(this.line);
@@ -217,6 +227,7 @@ export default {
:key="line.left.line_code"
v-safe-html="line.left.rich_text"
:class="parallelViewLeftLineType"
+ v-bind="interopLeftAttributes"
class="line_content with-coverage parallel left-side"
@mousedown="handleParallelLineMouseDown"
></td>
@@ -283,6 +294,7 @@ export default {
hll: isHighlighted,
},
]"
+ v-bind="interopRightAttributes"
class="line_content with-coverage parallel right-side"
@mousedown="handleParallelLineMouseDown"
></td>
diff --git a/app/assets/javascripts/diffs/store/actions.js b/app/assets/javascripts/diffs/store/actions.js
index 1c66ad1a18c..81416984dbf 100644
--- a/app/assets/javascripts/diffs/store/actions.js
+++ b/app/assets/javascripts/diffs/store/actions.js
@@ -1,4 +1,5 @@
import Cookies from 'js-cookie';
+import Visibility from 'visibilityjs';
import Vue from 'vue';
import { deprecatedCreateFlash as createFlash } from '~/flash';
import { diffViewerModes } from '~/ide/constants';
@@ -52,12 +53,15 @@ import {
prepareLineForRenamedFile,
} from './utils';
+let eTagPoll;
+
export const setBaseConfig = ({ commit }, options) => {
const {
endpoint,
endpointMetadata,
endpointBatch,
endpointCoverage,
+ endpointCodequality,
endpointUpdateUser,
projectPath,
dismissEndpoint,
@@ -71,6 +75,7 @@ export const setBaseConfig = ({ commit }, options) => {
endpointMetadata,
endpointBatch,
endpointCoverage,
+ endpointCodequality,
endpointUpdateUser,
projectPath,
dismissEndpoint,
@@ -233,6 +238,48 @@ export const fetchCoverageFiles = ({ commit, state }) => {
coveragePoll.makeRequest();
};
+export const clearEtagPoll = () => {
+ eTagPoll = null;
+};
+
+export const stopCodequalityPolling = () => {
+ if (eTagPoll) eTagPoll.stop();
+};
+
+export const restartCodequalityPolling = () => {
+ if (eTagPoll) eTagPoll.restart();
+};
+
+export const fetchCodequality = ({ commit, state, dispatch }) => {
+ eTagPoll = new Poll({
+ resource: {
+ getCodequalityDiffReports: (endpoint) => axios.get(endpoint),
+ },
+ data: state.endpointCodequality,
+ method: 'getCodequalityDiffReports',
+ successCallback: ({ status, data }) => {
+ if (status === httpStatusCodes.OK) {
+ commit(types.SET_CODEQUALITY_DATA, data);
+
+ eTagPoll.stop();
+ }
+ },
+ errorCallback: () => createFlash(__('Something went wrong on our end. Please try again!')),
+ });
+
+ if (!Visibility.hidden()) {
+ eTagPoll.makeRequest();
+ }
+
+ Visibility.change(() => {
+ if (!Visibility.hidden()) {
+ dispatch('restartCodequalityPolling');
+ } else {
+ dispatch('stopCodequalityPolling');
+ }
+ });
+};
+
export const setHighlightedRow = ({ commit }, lineCode) => {
const fileHash = lineCode.split('_')[0];
commit(types.SET_HIGHLIGHTED_ROW, lineCode);
diff --git a/app/assets/javascripts/diffs/store/getters.js b/app/assets/javascripts/diffs/store/getters.js
index dec3f87b03e..b06faa2284b 100644
--- a/app/assets/javascripts/diffs/store/getters.js
+++ b/app/assets/javascripts/diffs/store/getters.js
@@ -136,6 +136,16 @@ export const fileLineCoverage = (state) => (file, line) => {
};
/**
+ * Returns the codequality diff data for a given file
+ * @param {string} filePath
+ * @returns {Array}
+ */
+export const fileCodequalityDiff = (state) => (filePath) => {
+ if (!state.codequalityDiff.files || !state.codequalityDiff.files[filePath]) return [];
+ return state.codequalityDiff.files[filePath];
+};
+
+/**
* Returns index of a currently selected diff in diffFiles
* @returns {number}
*/
diff --git a/app/assets/javascripts/diffs/store/modules/diff_state.js b/app/assets/javascripts/diffs/store/modules/diff_state.js
index 38366663cfd..a99ef00d61e 100644
--- a/app/assets/javascripts/diffs/store/modules/diff_state.js
+++ b/app/assets/javascripts/diffs/store/modules/diff_state.js
@@ -9,7 +9,8 @@ import {
import { fileByFile } from '../../utils/preferences';
import { getDefaultWhitespace } from '../utils';
-const viewTypeFromQueryString = getParameterValues('view')[0];
+const getViewTypeFromQueryString = () => getParameterValues('view')[0];
+
const viewTypeFromCookie = Cookies.get(DIFF_VIEW_COOKIE_NAME);
const defaultViewType = INLINE_DIFF_VIEW_TYPE;
const whiteSpaceFromQueryString = getParameterValues('w')[0];
@@ -29,9 +30,10 @@ export default () => ({
startVersion: null, // Null unless a target diff is selected for comparison that is not the "base" diff
diffFiles: [],
coverageFiles: {},
+ codequalityDiff: {},
mergeRequestDiffs: [],
mergeRequestDiff: null,
- diffViewType: viewTypeFromQueryString || viewTypeFromCookie || defaultViewType,
+ diffViewType: getViewTypeFromQueryString() || viewTypeFromCookie || defaultViewType,
tree: [],
treeEntries: {},
showTreeList: true,
diff --git a/app/assets/javascripts/diffs/store/modules/index.js b/app/assets/javascripts/diffs/store/modules/index.js
index 03d11e60745..6860e24db6b 100644
--- a/app/assets/javascripts/diffs/store/modules/index.js
+++ b/app/assets/javascripts/diffs/store/modules/index.js
@@ -1,7 +1,7 @@
-import * as actions from 'ee_else_ce/diffs/store/actions';
-import createState from 'ee_else_ce/diffs/store/modules/diff_state';
-import mutations from 'ee_else_ce/diffs/store/mutations';
+import * as actions from '../actions';
import * as getters from '../getters';
+import mutations from '../mutations';
+import createState from './diff_state';
export default () => ({
namespaced: true,
diff --git a/app/assets/javascripts/diffs/store/mutation_types.js b/app/assets/javascripts/diffs/store/mutation_types.js
index 4641731c4b6..b0f396f905a 100644
--- a/app/assets/javascripts/diffs/store/mutation_types.js
+++ b/app/assets/javascripts/diffs/store/mutation_types.js
@@ -11,6 +11,7 @@ export const SET_MR_FILE_REVIEWS = 'SET_MR_FILE_REVIEWS';
export const SET_DIFF_VIEW_TYPE = 'SET_DIFF_VIEW_TYPE';
export const SET_COVERAGE_DATA = 'SET_COVERAGE_DATA';
+export const SET_CODEQUALITY_DATA = 'SET_CODEQUALITY_DATA';
export const SET_MERGE_REQUEST_DIFFS = 'SET_MERGE_REQUEST_DIFFS';
export const TOGGLE_LINE_HAS_FORM = 'TOGGLE_LINE_HAS_FORM';
export const ADD_CONTEXT_LINES = 'ADD_CONTEXT_LINES';
diff --git a/app/assets/javascripts/diffs/store/mutations.js b/app/assets/javascripts/diffs/store/mutations.js
index 9ff9a02d444..eacf76234fc 100644
--- a/app/assets/javascripts/diffs/store/mutations.js
+++ b/app/assets/javascripts/diffs/store/mutations.js
@@ -33,6 +33,7 @@ export default {
endpointMetadata,
endpointBatch,
endpointCoverage,
+ endpointCodequality,
endpointUpdateUser,
projectPath,
dismissEndpoint,
@@ -46,6 +47,7 @@ export default {
endpointMetadata,
endpointBatch,
endpointCoverage,
+ endpointCodequality,
endpointUpdateUser,
projectPath,
dismissEndpoint,
@@ -89,6 +91,10 @@ export default {
Object.assign(state, { coverageFiles });
},
+ [types.SET_CODEQUALITY_DATA](state, codequalityDiffData) {
+ Object.assign(state, { codequalityDiff: codequalityDiffData });
+ },
+
[types.RENDER_FILE](state, file) {
renderFile(file);
},
diff --git a/app/assets/javascripts/diffs/utils/interoperability.js b/app/assets/javascripts/diffs/utils/interoperability.js
new file mode 100644
index 00000000000..a52e8fd25f5
--- /dev/null
+++ b/app/assets/javascripts/diffs/utils/interoperability.js
@@ -0,0 +1,49 @@
+const OLD = 'old';
+const NEW = 'new';
+const ATTR_PREFIX = 'data-interop-';
+
+export const ATTR_TYPE = `${ATTR_PREFIX}type`;
+export const ATTR_LINE = `${ATTR_PREFIX}line`;
+export const ATTR_NEW_LINE = `${ATTR_PREFIX}new-line`;
+export const ATTR_OLD_LINE = `${ATTR_PREFIX}old-line`;
+
+export const getInteropInlineAttributes = (line) => {
+ if (!line) {
+ return null;
+ }
+
+ const interopType = line.type?.startsWith(OLD) ? OLD : NEW;
+
+ const interopLine = interopType === OLD ? line.old_line : line.new_line;
+
+ return {
+ [ATTR_TYPE]: interopType,
+ [ATTR_LINE]: interopLine,
+ [ATTR_NEW_LINE]: line.new_line,
+ [ATTR_OLD_LINE]: line.old_line,
+ };
+};
+
+export const getInteropOldSideAttributes = (line) => {
+ if (!line) {
+ return null;
+ }
+
+ return {
+ [ATTR_TYPE]: OLD,
+ [ATTR_LINE]: line.old_line,
+ [ATTR_OLD_LINE]: line.old_line,
+ };
+};
+
+export const getInteropNewSideAttributes = (line) => {
+ if (!line) {
+ return null;
+ }
+
+ return {
+ [ATTR_TYPE]: NEW,
+ [ATTR_LINE]: line.new_line,
+ [ATTR_NEW_LINE]: line.new_line,
+ };
+};
diff --git a/app/assets/javascripts/ide/components/ide_status_mr.vue b/app/assets/javascripts/ide/components/ide_status_mr.vue
index a3b26d23a17..d05ca4141c8 100644
--- a/app/assets/javascripts/ide/components/ide_status_mr.vue
+++ b/app/assets/javascripts/ide/components/ide_status_mr.vue
@@ -20,7 +20,7 @@ export default {
</script>
<template>
- <div class="d-flex-center flex-nowrap text-nowrap js-ide-status-mr">
+ <div class="d-flex-center gl-flex-nowrap text-nowrap js-ide-status-mr">
<gl-icon name="merge-request" />
<span class="ml-1 d-none d-sm-block">{{ s__('WebIDE|Merge request') }}</span>
<gl-link class="ml-1" :href="url">{{ text }}</gl-link>
diff --git a/app/assets/javascripts/ide/components/nav_dropdown_button.vue b/app/assets/javascripts/ide/components/nav_dropdown_button.vue
index 5bc740f2f43..3699073adb8 100644
--- a/app/assets/javascripts/ide/components/nav_dropdown_button.vue
+++ b/app/assets/javascripts/ide/components/nav_dropdown_button.vue
@@ -31,7 +31,7 @@ export default {
<template>
<dropdown-button>
- <span class="row flex-nowrap">
+ <span class="row gl-flex-nowrap">
<span class="col-auto flex-fill text-truncate">
<gl-icon :size="16" :aria-label="__('Current Branch')" name="branch" /> {{ branchLabel }}
</span>
diff --git a/app/assets/javascripts/import_entities/import_projects/components/provider_repo_table_row.vue b/app/assets/javascripts/import_entities/import_projects/components/provider_repo_table_row.vue
index ebb09947663..a803afeb901 100644
--- a/app/assets/javascripts/import_entities/import_projects/components/provider_repo_table_row.vue
+++ b/app/assets/javascripts/import_entities/import_projects/components/provider_repo_table_row.vue
@@ -111,7 +111,7 @@ export default {
</gl-link>
</td>
<td
- class="gl-display-flex gl-flex-sm-wrap gl-p-4 gl-pt-5 gl-vertical-align-top"
+ class="gl-display-flex gl-sm-flex-wrap gl-p-4 gl-pt-5 gl-vertical-align-top"
data-testid="fullPath"
data-qa-selector="project_path_content"
>
diff --git a/app/assets/javascripts/monitoring/components/links_section.vue b/app/assets/javascripts/monitoring/components/links_section.vue
index 3f9f57d4ac1..fb5ab12916e 100644
--- a/app/assets/javascripts/monitoring/components/links_section.vue
+++ b/app/assets/javascripts/monitoring/components/links_section.vue
@@ -15,7 +15,7 @@ export default {
<template>
<div
ref="linksSection"
- class="gl-sm-display-flex gl-flex-sm-wrap gl-mt-5 gl-p-3 gl-bg-gray-10 border gl-rounded-base links-section"
+ class="gl-sm-display-flex gl-sm-flex-wrap gl-mt-5 gl-p-3 gl-bg-gray-10 border gl-rounded-base links-section"
>
<div
v-for="(link, key) in links"
diff --git a/app/assets/javascripts/pages/projects/learn_gitlab/components/learn_gitlab_b.vue b/app/assets/javascripts/pages/projects/learn_gitlab/components/learn_gitlab_b.vue
index 33248082bcd..8f92ce95dbf 100644
--- a/app/assets/javascripts/pages/projects/learn_gitlab/components/learn_gitlab_b.vue
+++ b/app/assets/javascripts/pages/projects/learn_gitlab/components/learn_gitlab_b.vue
@@ -97,6 +97,9 @@ export default {
<div class="row row-cols-2 row-cols-md-3 row-cols-lg-4">
<div class="col gl-mb-6">
+ <learn-gitlab-info-card v-bind="infoProps('issueCreated')" />
+ </div>
+ <div class="col gl-mb-6">
<learn-gitlab-info-card v-bind="infoProps('mergeRequestCreated')" />
</div>
</div>
diff --git a/app/assets/javascripts/pages/projects/learn_gitlab/components/learn_gitlab_info_card.vue b/app/assets/javascripts/pages/projects/learn_gitlab/components/learn_gitlab_info_card.vue
index 3d2a8eed9d4..6cd3bbc359b 100644
--- a/app/assets/javascripts/pages/projects/learn_gitlab/components/learn_gitlab_info_card.vue
+++ b/app/assets/javascripts/pages/projects/learn_gitlab/components/learn_gitlab_info_card.vue
@@ -61,7 +61,7 @@ export default {
<div
class="gl-text-center gl-display-flex gl-justify-content-center gl-align-items-center gl-flex-direction-column learn-gitlab-info-card-content"
>
- <img :src="svg" />
+ <img :src="svg" :alt="actionLabel" />
<h6>{{ title }}</h6>
<p class="gl-font-sm gl-text-gray-700">{{ description }}</p>
<gl-link :href="url" target="_blank">{{ actionLabel }}</gl-link>
diff --git a/app/assets/javascripts/pages/projects/learn_gitlab/constants/index.js b/app/assets/javascripts/pages/projects/learn_gitlab/constants/index.js
index ba456131327..9e204aa6746 100644
--- a/app/assets/javascripts/pages/projects/learn_gitlab/constants/index.js
+++ b/app/assets/javascripts/pages/projects/learn_gitlab/constants/index.js
@@ -63,6 +63,15 @@ export const ACTION_LABELS = {
section: 'deploy',
position: 1,
},
+ issueCreated: {
+ title: s__('LearnGitLab|Create an issue'),
+ actionLabel: s__('LearnGitLab|Create an issue'),
+ description: s__(
+ 'LearnGitLab|Create/import issues (tickets) to collaborate on ideas and plan work.',
+ ),
+ section: 'plan',
+ position: 0,
+ },
};
export const ACTION_SECTIONS = {
diff --git a/app/assets/javascripts/pipelines/components/graph/constants.js b/app/assets/javascripts/pipelines/components/graph/constants.js
index c7d973b5985..216b6c6737c 100644
--- a/app/assets/javascripts/pipelines/components/graph/constants.js
+++ b/app/assets/javascripts/pipelines/components/graph/constants.js
@@ -13,5 +13,6 @@ export const GRAPHQL = 'graphql';
export const STAGE_VIEW = 'stage';
export const LAYER_VIEW = 'layer';
+export const VIEW_TYPE_KEY = 'pipeline_graph_view_type';
export const IID_FAILURE = 'missing_iid';
diff --git a/app/assets/javascripts/pipelines/components/graph/graph_component_wrapper.vue b/app/assets/javascripts/pipelines/components/graph/graph_component_wrapper.vue
index 71e6c26ff22..0bc6d883245 100644
--- a/app/assets/javascripts/pipelines/components/graph/graph_component_wrapper.vue
+++ b/app/assets/javascripts/pipelines/components/graph/graph_component_wrapper.vue
@@ -2,11 +2,12 @@
import { GlAlert, GlLoadingIcon } from '@gitlab/ui';
import getPipelineDetails from 'shared_queries/pipelines/get_pipeline_details.query.graphql';
import { __ } from '~/locale';
+import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue';
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { DEFAULT, DRAW_FAILURE, LOAD_FAILURE } from '../../constants';
import { reportToSentry } from '../../utils';
import { listByLayers } from '../parsing_utils';
-import { IID_FAILURE, LAYER_VIEW, STAGE_VIEW } from './constants';
+import { IID_FAILURE, LAYER_VIEW, STAGE_VIEW, VIEW_TYPE_KEY } from './constants';
import PipelineGraph from './graph_component.vue';
import GraphViewSelector from './graph_view_selector.vue';
import {
@@ -22,6 +23,7 @@ export default {
GlAlert,
GlLoadingIcon,
GraphViewSelector,
+ LocalStorageSync,
PipelineGraph,
},
mixins: [glFeatureFlagMixin()],
@@ -143,7 +145,7 @@ export default {
return this.$apollo.queries.pipeline.loading && !this.pipeline;
},
showGraphViewSelector() {
- return Boolean(this.glFeatures.pipelineGraphLayersView && this.pipeline);
+ return Boolean(this.glFeatures.pipelineGraphLayersView && this.pipeline?.usesNeeds);
},
},
mounted() {
@@ -184,6 +186,7 @@ export default {
this.currentViewType = type;
},
},
+ viewTypeKey: VIEW_TYPE_KEY,
};
</script>
<template>
@@ -191,11 +194,17 @@ export default {
<gl-alert v-if="showAlert" :variant="alert.variant" @dismiss="hideAlert">
{{ alert.text }}
</gl-alert>
- <graph-view-selector
- v-if="showGraphViewSelector"
- :type="currentViewType"
- @updateViewType="updateViewType"
- />
+ <local-storage-sync
+ :storage-key="$options.viewTypeKey"
+ :value="currentViewType"
+ @input="updateViewType"
+ >
+ <graph-view-selector
+ v-if="showGraphViewSelector"
+ :type="currentViewType"
+ @updateViewType="updateViewType"
+ />
+ </local-storage-sync>
<gl-loading-icon v-if="showLoadingIcon" class="gl-mx-auto gl-my-4" size="lg" />
<pipeline-graph
v-if="pipeline"
diff --git a/app/assets/javascripts/pipelines/components/graph/graph_view_selector.vue b/app/assets/javascripts/pipelines/components/graph/graph_view_selector.vue
index b8b41e826bd..f33e6290e37 100644
--- a/app/assets/javascripts/pipelines/components/graph/graph_view_selector.vue
+++ b/app/assets/javascripts/pipelines/components/graph/graph_view_selector.vue
@@ -57,7 +57,7 @@ export default {
<template>
<div class="gl-display-flex gl-align-items-center gl-my-4">
<span>{{ $options.i18n.labelText }}</span>
- <gl-dropdown class="gl-ml-4">
+ <gl-dropdown data-testid="pipeline-view-selector" class="gl-ml-4">
<template #button-content>
<gl-sprintf :message="currentDropdownText">
<template #code="{ content }">
diff --git a/app/controllers/profiles/notifications_controller.rb b/app/controllers/profiles/notifications_controller.rb
index 0a73239709a..6ef0ed6d365 100644
--- a/app/controllers/profiles/notifications_controller.rb
+++ b/app/controllers/profiles/notifications_controller.rb
@@ -3,18 +3,13 @@
class Profiles::NotificationsController < Profiles::ApplicationController
feature_category :users
- # rubocop: disable CodeReuse/ActiveRecord
def show
@user = current_user
@user_groups = user_groups
@group_notifications = UserGroupNotificationSettingsFinder.new(current_user, user_groups).execute
-
- @project_notifications = current_user.notification_settings.for_projects.order(:id)
- .preload_source_route
- .select { |notification| current_user.can?(:read_project, notification.source) }
+ @project_notifications = project_notifications_with_preloaded_associations
@global_notification_setting = current_user.global_notification_setting
end
- # rubocop: enable CodeReuse/ActiveRecord
def update
result = Users::UpdateService.new(current_user, user_params.merge(user: current_user)).execute
@@ -37,4 +32,20 @@ class Profiles::NotificationsController < Profiles::ApplicationController
def user_groups
GroupsFinder.new(current_user, all_available: false).execute.order_name_asc.page(params[:page])
end
+
+ # rubocop: disable CodeReuse/ActiveRecord
+ def project_notifications_with_preloaded_associations
+ project_notifications = current_user
+ .notification_settings
+ .for_projects
+ .order_by_id_asc
+ .preload_source_route
+
+ projects = project_notifications.map(&:source)
+ ActiveRecord::Associations::Preloader.new.preload(projects, { namespace: [:route, :owner], group: [] })
+ Preloaders::UserMaxAccessLevelInProjectsPreloader.new(projects, current_user).execute
+
+ project_notifications.select { |notification| current_user.can?(:read_project, notification.source) }
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
end
diff --git a/app/graphql/queries/pipelines/get_pipeline_details.query.graphql b/app/graphql/queries/pipelines/get_pipeline_details.query.graphql
index 92323923266..959bf7dc91d 100644
--- a/app/graphql/queries/pipelines/get_pipeline_details.query.graphql
+++ b/app/graphql/queries/pipelines/get_pipeline_details.query.graphql
@@ -27,6 +27,7 @@ query getPipelineDetails($projectPath: ID!, $iid: ID!) {
__typename
id
iid
+ usesNeeds
downstream {
__typename
nodes {
diff --git a/app/graphql/resolvers/concerns/board_issue_filterable.rb b/app/graphql/resolvers/concerns/board_issue_filterable.rb
index 1541738f46c..1943f9eb070 100644
--- a/app/graphql/resolvers/concerns/board_issue_filterable.rb
+++ b/app/graphql/resolvers/concerns/board_issue_filterable.rb
@@ -18,6 +18,17 @@ module BoardIssueFilterable
end
def set_filter_values(filters)
+ filter_by_assignee(filters)
+ end
+
+ def filter_by_assignee(filters)
+ if filters[:assignee_username] && filters[:assignee_wildcard_id]
+ raise ::Gitlab::Graphql::Errors::ArgumentError, 'Incompatible arguments: assigneeUsername, assigneeWildcardId.'
+ end
+
+ if filters[:assignee_wildcard_id]
+ filters[:assignee_id] = filters.delete(:assignee_wildcard_id)
+ end
end
end
diff --git a/app/graphql/types/boards/assignee_wildcard_id_enum.rb b/app/graphql/types/boards/assignee_wildcard_id_enum.rb
new file mode 100644
index 00000000000..ba9058a78d9
--- /dev/null
+++ b/app/graphql/types/boards/assignee_wildcard_id_enum.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module Types
+ module Boards
+ class AssigneeWildcardIdEnum < BaseEnum
+ graphql_name 'AssigneeWildcardId'
+ description 'Assignee ID wildcard values'
+
+ value 'NONE', 'No assignee is assigned.'
+ value 'ANY', 'An assignee is assigned.'
+ end
+ end
+end
diff --git a/app/graphql/types/boards/board_issue_input_type.rb b/app/graphql/types/boards/board_issue_input_type.rb
index 3404f24ea12..04f9341c44e 100644
--- a/app/graphql/types/boards/board_issue_input_type.rb
+++ b/app/graphql/types/boards/board_issue_input_type.rb
@@ -18,6 +18,10 @@ module Types
argument :search, GraphQL::STRING_TYPE,
required: false,
description: 'Search query for issue title or description.'
+
+ argument :assignee_wildcard_id, ::Types::Boards::AssigneeWildcardIdEnum,
+ required: false,
+ description: 'Filter by assignee wildcard. Incompatible with assigneeUsername.'
end
end
end
diff --git a/app/helpers/appearances_helper.rb b/app/helpers/appearances_helper.rb
index 3ae9f93a27a..65feea4f6e0 100644
--- a/app/helpers/appearances_helper.rb
+++ b/app/helpers/appearances_helper.rb
@@ -84,3 +84,4 @@ module AppearancesHelper
end
AppearancesHelper.prepend_if_ee('EE::AppearancesHelper')
+AppearancesHelper.prepend_if_jh('JH::AppearancesHelper')
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index 1115bee0843..6309d5a2d82 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -194,10 +194,16 @@ module ApplicationHelper
end
end
- def promo_host
+ # This needs to be used outside of Rails
+ def self.promo_host
'about.gitlab.com'
end
+ # Convenient method for Rails helper
+ def promo_host
+ ApplicationHelper.promo_host
+ end
+
def promo_url
'https://' + promo_host
end
@@ -406,3 +412,4 @@ module ApplicationHelper
end
ApplicationHelper.prepend_if_ee('EE::ApplicationHelper')
+ApplicationHelper.prepend_if_jh('JH::ApplicationHelper')
diff --git a/app/helpers/learn_gitlab_helper.rb b/app/helpers/learn_gitlab_helper.rb
index 0cb84fe5227..81896fb9fa4 100644
--- a/app/helpers/learn_gitlab_helper.rb
+++ b/app/helpers/learn_gitlab_helper.rb
@@ -24,6 +24,7 @@ module LearnGitlabHelper
private
ACTION_ISSUE_IDS = {
+ issue_created: 4,
git_write: 6,
pipeline_created: 7,
merge_request_created: 9,
diff --git a/app/models/concerns/bulk_member_access_load.rb b/app/models/concerns/bulk_member_access_load.rb
index f44ad474cd5..e252ca36629 100644
--- a/app/models/concerns/bulk_member_access_load.rb
+++ b/app/models/concerns/bulk_member_access_load.rb
@@ -13,13 +13,7 @@ module BulkMemberAccessLoad
raise 'Block is mandatory' unless block_given?
resource_ids = resource_ids.uniq
- key = max_member_access_for_resource_key(resource_klass, memoization_index)
- access = {}
-
- if Gitlab::SafeRequestStore.active?
- Gitlab::SafeRequestStore[key] ||= {}
- access = Gitlab::SafeRequestStore[key]
- end
+ access = load_access_hash(resource_klass, memoization_index)
# Look up only the IDs we need
resource_ids -= access.keys
@@ -39,10 +33,28 @@ module BulkMemberAccessLoad
access
end
+ def merge_value_to_request_store(resource_klass, resource_id, memoization_index, value)
+ max_member_access_for_resource_ids(resource_klass, [resource_id], memoization_index) do
+ { resource_id => value }
+ end
+ end
+
private
def max_member_access_for_resource_key(klass, memoization_index)
"max_member_access_for_#{klass.name.underscore.pluralize}:#{memoization_index}"
end
+
+ def load_access_hash(resource_klass, memoization_index)
+ key = max_member_access_for_resource_key(resource_klass, memoization_index)
+
+ access = {}
+ if Gitlab::SafeRequestStore.active?
+ Gitlab::SafeRequestStore[key] ||= {}
+ access = Gitlab::SafeRequestStore[key]
+ end
+
+ access
+ end
end
end
diff --git a/app/models/concerns/cascading_namespace_setting_attribute.rb b/app/models/concerns/cascading_namespace_setting_attribute.rb
index cd489e3a7f3..2b4a108a9a0 100644
--- a/app/models/concerns/cascading_namespace_setting_attribute.rb
+++ b/app/models/concerns/cascading_namespace_setting_attribute.rb
@@ -25,7 +25,7 @@ module CascadingNamespaceSettingAttribute
class_methods do
def cascading_settings_feature_enabled?
- ::Feature.enabled?(:cascading_namespace_settings, default_enabled: false)
+ ::Feature.enabled?(:cascading_namespace_settings, default_enabled: true)
end
private
diff --git a/app/models/concerns/has_repository.rb b/app/models/concerns/has_repository.rb
index b9ad78c14fd..774cda2c3e8 100644
--- a/app/models/concerns/has_repository.rb
+++ b/app/models/concerns/has_repository.rb
@@ -77,9 +77,14 @@ module HasRepository
def default_branch_from_preferences
return unless empty_repo?
- group_branch_default_name = group&.default_branch_name if respond_to?(:group)
+ (default_branch_from_group_preferences || Gitlab::CurrentSettings.default_branch_name).presence
+ end
+
+ def default_branch_from_group_preferences
+ return unless respond_to?(:group)
+ return unless group
- (group_branch_default_name || Gitlab::CurrentSettings.default_branch_name).presence
+ group.default_branch_name || group.root_ancestor.default_branch_name
end
def reload_default_branch
diff --git a/app/models/notification_setting.rb b/app/models/notification_setting.rb
index 72813b17501..9c3058b4706 100644
--- a/app/models/notification_setting.rb
+++ b/app/models/notification_setting.rb
@@ -30,6 +30,8 @@ class NotificationSetting < ApplicationRecord
scope :preload_source_route, -> { preload(source: [:route]) }
+ scope :order_by_id_asc, -> { order(id: :asc) }
+
# NOTE: Applicable unfound_translations.rb also needs to be updated when below events are changed.
EMAIL_EVENTS = [
:new_release,
diff --git a/app/models/preloaders/user_max_access_level_in_projects_preloader.rb b/app/models/preloaders/user_max_access_level_in_projects_preloader.rb
new file mode 100644
index 00000000000..671091480ee
--- /dev/null
+++ b/app/models/preloaders/user_max_access_level_in_projects_preloader.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+module Preloaders
+ # This class preloads the max access level for the user within the given projects and
+ # stores the values in requests store via the ProjectTeam class.
+ class UserMaxAccessLevelInProjectsPreloader
+ def initialize(projects, user)
+ @projects = projects
+ @user = user
+ end
+
+ def execute
+ access_levels = @user
+ .project_authorizations
+ .where(project_id: @projects)
+ .group(:project_id)
+ .maximum(:access_level)
+
+ @projects.each do |project|
+ access_level = access_levels[project.id] || Gitlab::Access::NO_ACCESS
+ ProjectTeam.new(project).write_member_access_for_user_id(@user.id, access_level)
+ end
+ end
+ end
+end
diff --git a/app/models/project_team.rb b/app/models/project_team.rb
index 5b7eded00cd..1a3f362e6a1 100644
--- a/app/models/project_team.rb
+++ b/app/models/project_team.rb
@@ -174,6 +174,10 @@ class ProjectTeam
end
end
+ def write_member_access_for_user_id(user_id, project_access_level)
+ merge_value_to_request_store(User, user_id, project.id, project_access_level)
+ end
+
def max_member_access(user_id)
max_member_access_for_user_ids([user_id])[user_id]
end
diff --git a/app/services/boards/lists/base_update_service.rb b/app/services/boards/lists/base_update_service.rb
new file mode 100644
index 00000000000..faf58e405fc
--- /dev/null
+++ b/app/services/boards/lists/base_update_service.rb
@@ -0,0 +1,58 @@
+# frozen_string_literal: true
+
+module Boards
+ module Lists
+ class BaseUpdateService < Boards::BaseService
+ def execute(list)
+ if execute_by_params(list)
+ success(list: list)
+ else
+ error(list.errors.messages, 422)
+ end
+ end
+
+ private
+
+ def execute_by_params(list)
+ update_preferences_result = update_preferences(list) if can_read?(list)
+ update_position_result = update_position(list) if can_admin?(list)
+
+ update_preferences_result || update_position_result
+ end
+
+ def update_preferences(list)
+ return unless preferences?
+
+ list.update_preferences_for(current_user, preferences)
+ end
+
+ def update_position(list)
+ return unless position?
+
+ move_service = Boards::Lists::MoveService.new(parent, current_user, params)
+
+ move_service.execute(list)
+ end
+
+ def preferences
+ { collapsed: Gitlab::Utils.to_boolean(params[:collapsed]) }
+ end
+
+ def preferences?
+ params.has_key?(:collapsed)
+ end
+
+ def position?
+ params.has_key?(:position)
+ end
+
+ def can_read?(list)
+ raise NotImplementedError
+ end
+
+ def can_admin?(list)
+ raise NotImplementedError
+ end
+ end
+ end
+end
diff --git a/app/services/boards/lists/update_service.rb b/app/services/boards/lists/update_service.rb
index e2d9c371ca2..2e1a6592cd9 100644
--- a/app/services/boards/lists/update_service.rb
+++ b/app/services/boards/lists/update_service.rb
@@ -2,50 +2,7 @@
module Boards
module Lists
- class UpdateService < Boards::BaseService
- def execute(list)
- if execute_by_params(list)
- success(list: list)
- else
- error(list.errors.messages, 422)
- end
- end
-
- private
-
- def execute_by_params(list)
- update_preferences_result = update_preferences(list) if can_read?(list)
- update_position_result = update_position(list) if can_admin?(list)
-
- update_preferences_result || update_position_result
- end
-
- def update_preferences(list)
- return unless preferences?
-
- list.update_preferences_for(current_user, preferences)
- end
-
- def update_position(list)
- return unless position?
-
- move_service = Boards::Lists::MoveService.new(parent, current_user, params)
-
- move_service.execute(list)
- end
-
- def preferences
- { collapsed: Gitlab::Utils.to_boolean(params[:collapsed]) }
- end
-
- def preferences?
- params.has_key?(:collapsed)
- end
-
- def position?
- params.has_key?(:position)
- end
-
+ class UpdateService < Boards::Lists::BaseUpdateService
def can_read?(list)
Ability.allowed?(current_user, :read_issue_board_list, parent)
end
diff --git a/app/services/projects/update_pages_service.rb b/app/services/projects/update_pages_service.rb
index d7e2e678dac..1ce6e8ae2dc 100644
--- a/app/services/projects/update_pages_service.rb
+++ b/app/services/projects/update_pages_service.rb
@@ -31,9 +31,9 @@ module Projects
# Create status notifying the deployment of pages
@status = create_status
+ @status.update_older_statuses_retried! if Feature.enabled?(:ci_fix_commit_status_retried, project, default_enabled: :yaml)
@status.enqueue!
@status.run!
- @status.update_older_statuses_retried! if Feature.enabled?(:ci_fix_commit_status_retried, project, default_enabled: :yaml)
raise InvalidStateError, 'missing pages artifacts' unless build.artifacts?
raise InvalidStateError, 'build SHA is outdated for this ref' unless latest?
diff --git a/app/views/devise/passwords/new.html.haml b/app/views/devise/passwords/new.html.haml
index c4672a5b25e..ef876779ad6 100644
--- a/app/views/devise/passwords/new.html.haml
+++ b/app/views/devise/passwords/new.html.haml
@@ -5,9 +5,9 @@
= render "devise/shared/error_messages", resource: resource
.form-group
= f.label :email
- = f.email_field :email, class: "form-control gl-form-input", required: true, value: params[:user_email], autofocus: true, title: 'Please provide a valid email address.'
+ = f.email_field :email, class: "form-control gl-form-input", required: true, value: params[:user_email], autofocus: true, title: _('Please provide a valid email address.')
.clearfix
- = f.submit "Reset password", class: "gl-button btn-confirm btn"
+ = f.submit _("Reset password"), class: "gl-button btn-confirm btn"
.clearfix.prepend-top-20
= render 'devise/shared/sign_in_link'
diff --git a/app/views/import/shared/_new_project_form.html.haml b/app/views/import/shared/_new_project_form.html.haml
index bfc4e65e23d..561c14dc68a 100644
--- a/app/views/import/shared/_new_project_form.html.haml
+++ b/app/views/import/shared/_new_project_form.html.haml
@@ -5,7 +5,7 @@
.form-group.col-12.col-sm-6
= label_tag :namespace_id, _('Project URL'), class: 'label-bold'
.form-group
- .input-group.flex-nowrap
+ .input-group.gl-flex-nowrap
- if current_user.can_select_namespace?
.input-group-prepend.flex-shrink-0.has-tooltip{ title: root_url }
.input-group-text
diff --git a/app/views/projects/_new_project_fields.html.haml b/app/views/projects/_new_project_fields.html.haml
index f6adb213916..4695cd59f32 100644
--- a/app/views/projects/_new_project_fields.html.haml
+++ b/app/views/projects/_new_project_fields.html.haml
@@ -12,7 +12,7 @@
.form-group.project-path.col-sm-6
= f.label :namespace_id, class: 'label-bold' do
%span= s_("Project URL")
- .input-group.flex-nowrap
+ .input-group.gl-flex-nowrap
- if current_user.can_select_namespace?
.input-group-prepend.flex-shrink-0.has-tooltip{ title: root_url }
.input-group-text
diff --git a/app/views/shared/projects/_search_bar.html.haml b/app/views/shared/projects/_search_bar.html.haml
index 6c3a6ce809f..8ec11d9cfbb 100644
--- a/app/views/shared/projects/_search_bar.html.haml
+++ b/app/views/shared/projects/_search_bar.html.haml
@@ -3,7 +3,7 @@
- flex_grow_and_shrink_xs = 'd-flex flex-xs-grow-1 flex-xs-shrink-1 flex-grow-0 flex-shrink-0'
.filtered-search-block.row-content-block.bt-0
- .filtered-search-wrapper.d-flex.flex-nowrap.flex-column.flex-sm-wrap.flex-sm-row.flex-xl-nowrap
+ .filtered-search-wrapper.d-flex.gl-flex-nowrap.flex-column.flex-sm-wrap.flex-sm-row.flex-xl-nowrap
- unless project_tab_filter == :starred
.filtered-search-nav.mb-2.mb-lg-0{ class: flex_grow_and_shrink_xs }
= render 'dashboard/projects/nav', project_tab_filter: project_tab_filter
diff --git a/changelogs/unreleased/21043-fix-more-n-plus-one-queries.yml b/changelogs/unreleased/21043-fix-more-n-plus-one-queries.yml
new file mode 100644
index 00000000000..15c867e107a
--- /dev/null
+++ b/changelogs/unreleased/21043-fix-more-n-plus-one-queries.yml
@@ -0,0 +1,6 @@
+---
+title: Eliminate N+1 database queries on the user notifications page within the project
+ notifications section
+merge_request: 59029
+author:
+type: performance
diff --git a/changelogs/unreleased/280781-support-assignee-wildcard-filters-board-issues-graphql.yml b/changelogs/unreleased/280781-support-assignee-wildcard-filters-board-issues-graphql.yml
new file mode 100644
index 00000000000..29fcceb5d96
--- /dev/null
+++ b/changelogs/unreleased/280781-support-assignee-wildcard-filters-board-issues-graphql.yml
@@ -0,0 +1,5 @@
+---
+title: Support filtering by assignee wildcard in GraphQL board list issues query
+merge_request: 58996
+author:
+type: added
diff --git a/changelogs/unreleased/293953_inherit_default_branch_name_for_subgroups.yml b/changelogs/unreleased/293953_inherit_default_branch_name_for_subgroups.yml
new file mode 100644
index 00000000000..cbb9c3a2744
--- /dev/null
+++ b/changelogs/unreleased/293953_inherit_default_branch_name_for_subgroups.yml
@@ -0,0 +1,5 @@
+---
+title: Inherit default branch name for subgroups
+merge_request: 57101
+author:
+type: fixed
diff --git a/changelogs/unreleased/Externalize-strings-in-passwords-new-html-haml.yml b/changelogs/unreleased/Externalize-strings-in-passwords-new-html-haml.yml
new file mode 100644
index 00000000000..34ff21e55c8
--- /dev/null
+++ b/changelogs/unreleased/Externalize-strings-in-passwords-new-html-haml.yml
@@ -0,0 +1,5 @@
+---
+title: Externalize strings in passwords/new.html.haml
+merge_request: 58236
+author: nuwe1
+type: other
diff --git a/changelogs/unreleased/dblessing_cascading_settings_default_enabled.yml b/changelogs/unreleased/dblessing_cascading_settings_default_enabled.yml
new file mode 100644
index 00000000000..1ec0672a962
--- /dev/null
+++ b/changelogs/unreleased/dblessing_cascading_settings_default_enabled.yml
@@ -0,0 +1,5 @@
+---
+title: Default enable cascading settings feature flag
+merge_request: 59026
+author:
+type: changed
diff --git a/changelogs/unreleased/id-bump-gon-version.yml b/changelogs/unreleased/id-bump-gon-version.yml
new file mode 100644
index 00000000000..c932da6e3b1
--- /dev/null
+++ b/changelogs/unreleased/id-bump-gon-version.yml
@@ -0,0 +1,5 @@
+---
+title: Update gon gem to 6.4.0
+merge_request: 51210
+author:
+type: other
diff --git a/changelogs/unreleased/id-bump-rspec-rails-to-5-1.yml b/changelogs/unreleased/id-bump-rspec-rails-to-5-1.yml
new file mode 100644
index 00000000000..c2c924636a1
--- /dev/null
+++ b/changelogs/unreleased/id-bump-rspec-rails-to-5-1.yml
@@ -0,0 +1,5 @@
+---
+title: Bump rspec-rails to 5.0.1
+merge_request: 59194
+author:
+type: other
diff --git a/changelogs/unreleased/issue-220040-fix-rails-savebang-gitaly-client-module.yml b/changelogs/unreleased/issue-220040-fix-rails-savebang-gitaly-client-module.yml
new file mode 100644
index 00000000000..54bbf67439d
--- /dev/null
+++ b/changelogs/unreleased/issue-220040-fix-rails-savebang-gitaly-client-module.yml
@@ -0,0 +1,5 @@
+---
+title: Fix Rails/SaveBang Rubocop offenses for gitaly client models
+merge_request: 58089
+author: Huzaifa Iftikhar @huzaifaiftikhar
+type: fixed
diff --git a/changelogs/unreleased/mc-backstage-reschedule-artifac-expiry-date-again.yml b/changelogs/unreleased/mc-backstage-reschedule-artifac-expiry-date-again.yml
new file mode 100644
index 00000000000..ffdd268c086
--- /dev/null
+++ b/changelogs/unreleased/mc-backstage-reschedule-artifac-expiry-date-again.yml
@@ -0,0 +1,5 @@
+---
+title: Schedule artifact expiry backfill again.
+merge_request: 59270
+author:
+type: changed
diff --git a/config/application.rb b/config/application.rb
index 03d96fdf85e..b9792cba793 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -57,21 +57,29 @@ module Gitlab
config.generators.templates.push("#{config.root}/generator_templates")
- if Gitlab.ee?
- ee_paths = config.eager_load_paths.each_with_object([]) do |path, memo|
- ee_path = config.root.join('ee', Pathname.new(path).relative_path_from(config.root))
- memo << ee_path.to_s
+ load_paths = lambda do |dir:|
+ ext_paths = config.eager_load_paths.each_with_object([]) do |path, memo|
+ ext_path = config.root.join(dir, Pathname.new(path).relative_path_from(config.root))
+ memo << ext_path.to_s
end
- ee_paths << "#{config.root}/ee/app/replicators"
+ ext_paths << "#{config.root}/#{dir}/app/replicators"
# Eager load should load CE first
- config.eager_load_paths.push(*ee_paths)
- config.helpers_paths.push "#{config.root}/ee/app/helpers"
+ config.eager_load_paths.push(*ext_paths)
+ config.helpers_paths.push "#{config.root}/#{dir}/app/helpers"
- # Other than Ruby modules we load EE first
- config.paths['lib/tasks'].unshift "#{config.root}/ee/lib/tasks"
- config.paths['app/views'].unshift "#{config.root}/ee/app/views"
+ # Other than Ruby modules we load extensions first
+ config.paths['lib/tasks'].unshift "#{config.root}/#{dir}/lib/tasks"
+ config.paths['app/views'].unshift "#{config.root}/#{dir}/app/views"
+ end
+
+ Gitlab.ee do
+ load_paths.call(dir: 'ee')
+ end
+
+ Gitlab.jh do
+ load_paths.call(dir: 'jh')
end
# Rake tasks ignore the eager loading settings, so we need to set the
diff --git a/config/feature_flags/development/cascading_namespace_settings.yml b/config/feature_flags/development/cascading_namespace_settings.yml
index ca4ad32d7c3..d638f457515 100644
--- a/config/feature_flags/development/cascading_namespace_settings.yml
+++ b/config/feature_flags/development/cascading_namespace_settings.yml
@@ -1,8 +1,8 @@
---
name: cascading_namespace_settings
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/55678
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/321724
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/327230
milestone: '13.11'
type: development
group: group::access
-default_enabled: false
+default_enabled: true
diff --git a/config/initializers/0_inject_enterprise_edition_module.rb b/config/initializers/0_inject_enterprise_edition_module.rb
index 7478727f869..f9c82f45040 100644
--- a/config/initializers/0_inject_enterprise_edition_module.rb
+++ b/config/initializers/0_inject_enterprise_edition_module.rb
@@ -31,6 +31,12 @@ module InjectEnterpriseEditionModule
include(ee_module) if Gitlab.ee?
end
+ def prepend_if_jh(constant, with_descendants: false)
+ return unless Gitlab.jh?
+
+ prepend_module(constant.constantize, with_descendants)
+ end
+
private
def prepend_module(mod, with_descendants)
diff --git a/config/initializers/0_license.rb b/config/initializers/0_license.rb
index ce3103be2e4..3db5ec0a91a 100644
--- a/config/initializers/0_license.rb
+++ b/config/initializers/0_license.rb
@@ -1,10 +1,18 @@
# frozen_string_literal: true
-Gitlab.ee do
+load_license = lambda do |dir:, license_name:|
prefix = ENV['GITLAB_LICENSE_MODE'] == 'test' ? 'test_' : ''
- public_key_file = File.read(Rails.root.join(".#{prefix}license_encryption_key.pub"))
+ public_key_file = File.read(Rails.root.join(dir, ".#{prefix}license_encryption_key.pub"))
public_key = OpenSSL::PKey::RSA.new(public_key_file)
Gitlab::License.encryption_key = public_key
rescue
- warn "WARNING: No valid license encryption key provided."
+ warn "WARNING: No valid #{license_name} encryption key provided."
+end
+
+Gitlab.ee do
+ load_license.call(dir: '.', license_name: 'license')
+end
+
+Gitlab.jh do
+ load_license.call(dir: 'jh', license_name: 'JH license')
end
diff --git a/config/initializers_before_autoloader/000_inflections.rb b/config/initializers_before_autoloader/000_inflections.rb
index 93cf9e01b96..de8f79b9a29 100644
--- a/config/initializers_before_autoloader/000_inflections.rb
+++ b/config/initializers_before_autoloader/000_inflections.rb
@@ -35,5 +35,6 @@ ActiveSupport::Inflector.inflections do |inflect|
vulnerability_feedback
)
inflect.acronym 'EE'
+ inflect.acronym 'JH'
inflect.acronym 'CSP'
end
diff --git a/db/post_migrate/20210413132500_reschedule_artifact_expiry_backfill_again.rb b/db/post_migrate/20210413132500_reschedule_artifact_expiry_backfill_again.rb
new file mode 100644
index 00000000000..b4570c8398b
--- /dev/null
+++ b/db/post_migrate/20210413132500_reschedule_artifact_expiry_backfill_again.rb
@@ -0,0 +1,44 @@
+# frozen_string_literal: true
+
+class RescheduleArtifactExpiryBackfillAgain < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+ MIGRATION = 'BackfillArtifactExpiryDate'
+ SWITCH_DATE = Date.new(2020, 06, 22).freeze
+
+ disable_ddl_transaction!
+
+ class JobArtifact < ActiveRecord::Base
+ include EachBatch
+
+ self.inheritance_column = :_type_disabled
+ self.table_name = 'ci_job_artifacts'
+
+ scope :without_expiry_date, -> { where(expire_at: nil) }
+ scope :before_switch, -> { where("date(created_at AT TIME ZONE 'UTC') < ?::date", SWITCH_DATE) }
+ end
+
+ def up
+ Gitlab::BackgroundMigration.steal(MIGRATION) do |job|
+ job.delete
+
+ false
+ end
+
+ queue_background_migration_jobs_by_range_at_intervals(
+ JobArtifact.without_expiry_date.before_switch,
+ MIGRATION,
+ 2.minutes,
+ batch_size: 200_000
+ )
+ end
+
+ def down
+ Gitlab::BackgroundMigration.steal(MIGRATION) do |job|
+ job.delete
+
+ false
+ end
+ end
+end
diff --git a/db/schema_migrations/20210413132500 b/db/schema_migrations/20210413132500
new file mode 100644
index 00000000000..662c7e33ef0
--- /dev/null
+++ b/db/schema_migrations/20210413132500
@@ -0,0 +1 @@
+407806cc168ef9859c9a4f1bd4db7a56aee01367e784ea0767889863b9ace35d \ No newline at end of file
diff --git a/doc/administration/geo/disaster_recovery/index.md b/doc/administration/geo/disaster_recovery/index.md
index fbecd2571ca..d1ea2978202 100644
--- a/doc/administration/geo/disaster_recovery/index.md
+++ b/doc/administration/geo/disaster_recovery/index.md
@@ -446,6 +446,134 @@ Now we need to make each **secondary** node listen to changes on the new **prima
to [initiate the replication process](../setup/database.md#step-3-initiate-the-replication-process) again but this time
for another **primary** node. All the old replication settings will be overwritten.
+## Promoting a secondary Geo cluster in GitLab Cloud Native Helm Charts
+
+When updating a Cloud Native Geo deployment, the process for updating any node that is external to the secondary Kubernetes cluster does not differ from the non Cloud Native approach. As such, you can always defer to [Promoting a secondary Geo node in single-secondary configurations](#promoting-a-secondary-geo-node-in-single-secondary-configurations) for more information.
+
+The following sections assume you are using the `gitlab` namespace. If you used a different namespace when setting up your cluster, you should also replace `--namespace gitlab` with your namespace.
+
+WARNING:
+In GitLab 13.2 and 13.3, promoting a secondary site to a primary while the
+secondary is paused fails. Do not pause replication before promoting a
+secondary. If the site is paused, be sure to resume before promoting. This
+issue has been fixed in GitLab 13.4 and later.
+
+### Step 1. Permanently disable the **primary** cluster
+
+WARNING:
+If the **primary** site goes offline, there may be data saved on the **primary** site
+that has not been replicated to the **secondary** site. This data should be treated
+as lost if you proceed.
+
+If an outage on the **primary** site happens, you should do everything possible to
+avoid a split-brain situation where writes can occur in two different GitLab
+instances, complicating recovery efforts. So to prepare for the failover, you
+must disable the **primary** site:
+
+- If you have access to the **primary** Kubernetes cluster, connect to it and disable the GitLab webservice and Sidekiq pods:
+
+ ```shell
+ kubectl --namespace gitlab scale deploy gitlab-geo-webservice-default --replicas=0
+ kubectl --namespace gitlab scale deploy gitlab-geo-sidekiq-all-in-1-v1 --replicas=0
+ ```
+
+- If you do not have access to the **primary** Kubernetes cluster, take the cluster offline and
+ prevent it from coming back online by any means at your disposal.
+ Since there are many ways you may prefer to accomplish this, we will avoid a
+ single recommendation. You may need to:
+
+ - Reconfigure the load balancers.
+ - Change DNS records (for example, point the primary DNS record to the
+ **secondary** site to stop usage of the **primary** site).
+ - Stop the virtual servers.
+ - Block traffic through a firewall.
+ - Revoke object storage permissions from the **primary** site.
+ - Physically disconnect a machine.
+
+### Step 2. Promote all **secondary** nodes external to the cluster
+
+WARNING:
+If the secondary site [has been paused](../../geo/index.md#pausing-and-resuming-replication), this performs
+a point-in-time recovery to the last known state.
+Data that was created on the primary while the secondary was paused will be lost.
+
+1. SSH in to the database node in the **secondary** and trigger PostgreSQL to
+ promote to read-write:
+
+ ```shell
+ sudo gitlab-ctl promote-db
+ ```
+
+ In GitLab 12.8 and earlier, see [Message: `sudo: gitlab-pg-ctl: command not found`](../replication/troubleshooting.md#message-sudo-gitlab-pg-ctl-command-not-found).
+
+1. Edit `/etc/gitlab/gitlab.rb` on the database node in the **secondary** site to
+ reflect its new status as **primary** by removing any lines that enabled the
+ `geo_secondary_role`:
+
+ NOTE:
+ Depending on your architecture these steps will need to be run on any GitLab node that is external to the **secondary** Kubernetes cluster.
+
+ ```ruby
+ ## In pre-11.5 documentation, the role was enabled as follows. Remove this line.
+ geo_secondary_role['enable'] = true
+
+ ## In 11.5+ documentation, the role was enabled as follows. Remove this line.
+ roles ['geo_secondary_role']
+ ```
+
+ After making these changes, [reconfigure GitLab](../../restart_gitlab.md#omnibus-gitlab-reconfigure) on the database node.
+
+### Step 3. Promote the **secondary** cluster
+
+1. Find the task runner pod:
+
+ ```shell
+ kubectl --namespace gitlab get pods -lapp=task-runner
+ ```
+
+1. Promote the secondary:
+
+ ```shell
+ kubectl --namespace gitlab exec -ti gitlab-geo-task-runner-XXX -- gitlab-rake geo:set_secondary_as_primary
+ ```
+
+1. Update the existing cluster configuration.
+
+ You can retrieve the existing config with Helm:
+
+ ```shell
+ helm --namespace gitlab get values gitlab-geo > gitlab.yaml
+ ```
+
+ The existing config will contain a section for Geo that should resemble:
+
+ ```yaml
+ geo:
+ enabled: true
+ role: secondary
+ nodeName: secondary.example.com
+ psql:
+ host: geo-2.db.example.com
+ port: 5431
+ password:
+ secret: geo
+ key: geo-postgresql-password
+ ```
+
+ To promote the **secondary** cluster to a **primary** cluster, update `role: secondary` to `role: primary`.
+
+ You can remove the entire `psql` section if the cluster will remain as a primary site, this refers to the tracking database and will be ignored whilst the cluster is acting as a primary site.
+
+ Update the cluster with the new config:
+
+ ```shell
+ helm upgrade --install --version <current Chart version> gitlab-geo gitlab/gitlab --namespace gitlab -f gitlab.yaml
+ ```
+
+1. Verify you can connect to the newly promoted primary using the URL used previously for the secondary.
+
+1. Success! The secondary has now been promoted to primary.
+
## Troubleshooting
This section was moved to [another location](../replication/troubleshooting.md#fixing-errors-during-a-failover-or-when-promoting-a-secondary-to-a-primary-node).
diff --git a/doc/administration/monitoring/prometheus/gitlab_metrics.md b/doc/administration/monitoring/prometheus/gitlab_metrics.md
index f56349fc9e6..4ba1a85babc 100644
--- a/doc/administration/monitoring/prometheus/gitlab_metrics.md
+++ b/doc/administration/monitoring/prometheus/gitlab_metrics.md
@@ -139,6 +139,18 @@ The following metrics can be controlled by feature flags:
| `gitlab_method_call_duration_seconds` | `prometheus_metrics_method_instrumentation` |
| `gitlab_view_rendering_duration_seconds` | `prometheus_metrics_view_instrumentation` |
+## Praefect metrics
+
+You can [configure Praefect to report metrics](../../gitaly/praefect.md#praefect).
+These are some of the Praefect metrics served from the `/metrics` path on the [configured port](index.md#changing-the-port-and-address-prometheus-listens-on)
+(9652 by default).
+
+| Metric | Type | Since | Description | Labels |
+| :----- | :--- | ----: | :---------- | :----- |
+| `gitaly_praefect_replication_latency_bucket` | Histogram | 12.10 | The amount of time it takes for replication to complete once the replication job starts. | |
+| `gitaly_praefect_replication_delay_bucket` | Histogram | 12.10 | A measure of how much time passes between when the replication job is created and when it starts. | |
+| `gitaly_praefect_node_latency_bucket` | Histogram | 12.10 | The latency in Gitaly returning health check information to Praefect. This indicates Praefect connection saturation. | |
+
## Sidekiq metrics
Sidekiq jobs may also gather metrics, and these metrics can be accessed if the
diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md
index 9fd509d61a8..8abeacd0d0e 100644
--- a/doc/api/graphql/reference/index.md
+++ b/doc/api/graphql/reference/index.md
@@ -7525,6 +7525,15 @@ The kind of an approval rule.
| `REGULAR` | A `regular` approval rule. |
| `REPORT_APPROVER` | A `report_approver` approval rule. |
+### `AssigneeWildcardId`
+
+Assignee ID wildcard values.
+
+| Value | Description |
+| ----- | ----------- |
+| `ANY` | An assignee is assigned. |
+| `NONE` | No assignee is assigned. |
+
### `AvailabilityEnum`
User availability status.
diff --git a/doc/development/documentation/site_architecture/release_process.md b/doc/development/documentation/site_architecture/release_process.md
index 4776a41aadb..9329b93bb26 100644
--- a/doc/development/documentation/site_architecture/release_process.md
+++ b/doc/development/documentation/site_architecture/release_process.md
@@ -1,181 +1,8 @@
---
-stage: none
-group: unassigned
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
+redirect_to: 'https://about.gitlab.com/handbook/engineering/ux/technical-writing/workflow/#monthly-documentation-releases'
---
-# Monthly release process
+This file was moved to [another location](https://about.gitlab.com/handbook/engineering/ux/technical-writing/workflow/#monthly-documentation-releases).
-When a new GitLab version is released on the 22nd, we release version-specific published
-documentation for the new version.
-
-We complete the process as soon as possible after the GitLab version is announced. The result is:
-
-- The [online published documentation](https://docs.gitlab.com) includes:
- - The three most recent minor releases of the current major version. For example 13.9, 13.8, and
- 13.7.
- - The most recent minor releases of the last two major versions. For example 12.10, and 11.11.
-- Documentation updates after the 22nd are for the next release. The versions drop down
- should have the current milestone with `-pre` appended to it, for example `13.10-pre`.
-
-Each documentation release:
-
-- Has a dedicated branch, named in the format `XX.yy`.
-- Has a Docker image that contains a build of that branch.
-
-For example:
-
-- For [GitLab 13.9](https://docs.gitlab.com/13.9/index.html), the
- [stable branch](https://gitlab.com/gitlab-org/gitlab-docs/-/tree/13.9) and Docker image:
- [`registry.gitlab.com/gitlab-org/gitlab-docs:13.9`](https://gitlab.com/gitlab-org/gitlab-docs/container_registry/631635).
-- For [GitLab 13.8](https://docs.gitlab.com/13.8/index.html), the
- [stable branch](https://gitlab.com/gitlab-org/gitlab-docs/-/tree/13.8) and Docker image:
- [`registry.gitlab.com/gitlab-org/gitlab-docs:13.8`](https://gitlab.com/gitlab-org/gitlab-docs/container_registry/631635).
-
-## Recommended timeline
-
-To minimize problems during the documentation release process, use the following timeline:
-
-- Any time before the 17th of the month:
-
- [Add the charts version](#add-chart-version), so that the documentation is built using the
- [version of the charts project that maps to](https://docs.gitlab.com/charts/installation/version_mappings.html)
- the GitLab release. This step may have been completed already.
-
-- Between the 17th and the 20th of the month:
-
- 1. [Create a stable branch and Docker image](#create-stable-branch-and-docker-image-for-release) for
- the new version.
- 1. [Create a release merge request](#create-release-merge-request) for the new version, which
- updates the version dropdown menu for the current documentation and adds the release to the
- Docker configuration. For example, the
- [release merge request for 13.9](https://gitlab.com/gitlab-org/gitlab-docs/-/merge_requests/1555).
- 1. [Update the three online versions](#update-dropdown-for-online-versions), so that they display the new release on their
- version dropdown menus.
-
-- On the 22nd of the month:
-
- [Merge the release merge requests and run the necessary Docker image builds](#merge-merge-requests-and-run-docker-image-builds).
-
-## Add chart version
-
-To add a new charts version for the release:
-
-1. Make sure you're in the root path of the `gitlab-docs` repository.
-1. Open `content/_data/chart_versions.yaml` and add the new stable branch version using the
- [version mapping](https://docs.gitlab.com/charts/installation/version_mappings.html). Only the
- `major.minor` version is needed.
-1. Create a new merge request and merge it.
-
-NOTE:
-If you have time, add anticipated future mappings to `content/_data/chart_versions.yaml`. This saves
-a step for the next GitLab release.
-
-## Create stable branch and Docker image for release
-
-To create a stable branch and Docker image for the release:
-
-1. Make sure you're in the root path of the `gitlab-docs` repository.
-1. Run the Rake task to create the single version. For example, to create the 13.9 release branch
- and perform others tasks:
-
- ```shell
- ./bin/rake "release:single[13.9]"
- ```
-
- A branch for the release is created, a new `Dockerfile.13.9` is created, and `.gitlab-ci.yml`
- has branches variables updated into a new branch. These files are automatically committed.
-
-1. Push the newly created branch, but **don't create a merge request**. After you push, the
- `image:docs-single` job creates a new Docker image tagged with the name of the branch you created
- earlier. You can see the Docker image in the `registry` environment at
- <https://gitlab.com/gitlab-org/gitlab-docs/-/environments/folders/registry>.
-
-For example, see [the 13.9 release pipeline](https://gitlab.com/gitlab-org/gitlab-docs/-/pipelines/260288747).
-
-Optionally, you can test locally by:
-
-1. Building the image and running it. For example, for GitLab 13.9 documentation:
-
- ```shell
- docker build -t docs:13.9 -f Dockerfile.13.9 .
- docker run -it --rm -p 4000:4000 docs:13.9
- ```
-
-1. Visiting <http://localhost:4000/13.9/> to see if everything works correctly.
-
-## Create release merge request
-
-NOTE:
-An [epic is open](https://gitlab.com/groups/gitlab-org/-/epics/4361) to automate this step.
-
-To create the release merge request for the release:
-
-1. Make sure you're in the root path of the `gitlab-docs` repository.
-1. Create a branch `release-X-Y`. For example:
-
- ```shell
- git checkout master
- git checkout -b release-13-9
- ```
-
-1. Edit `content/_data/versions.yaml` and update the lists of versions to reflect the new release:
-
- - Add the latest version to the `online:` section.
- - Move the oldest version in `online:` to the `offline:` section. There should now be three
- versions in `online:`.
-
-1. Update these Dockerfiles:
-
- - `dockerfiles/Dockerfile.archives`: Add the latest version to the top of the list.
- - `Dockerfile.master`: Remove the oldest version, and add the newest version to the
- top of the list.
-
-1. Commit and push to create the merge request. For example:
-
- ```shell
- git add content/ Dockerfile.master dockerfiles/Dockerfile.archives
- git commit -m "Release 13.9"
- git push origin release-13-9
- ```
-
-Do not merge the release merge request yet.
-
-## Update dropdown for online versions
-
-To update `content/_data/versions.yaml` for all online versions (stable branches `X.Y` of the
-`gitlab-docs` project). For example:
-
-- The merge request to [update the 13.9 version dropdown menu for the 13.9 release](https://gitlab.com/gitlab-org/gitlab-docs/-/merge_requests/1556).
-- The merge request to [update the 13.8 version dropdown menu for the 13.9 release](https://gitlab.com/gitlab-org/gitlab-docs/-/merge_requests/1557).
-- The merge request to [update the 13.7 version dropdown menu for the 13.9 release](https://gitlab.com/gitlab-org/gitlab-docs/-/merge_requests/1558).
-
-1. Run the Rake task that creates all of the necessary merge requests to update the dropdowns. For
- example, for the 13.9 release:
-
- ```shell
- git checkout release-13-9
- ./bin/rake release:dropdowns
- ```
-
-1. [Visit the merge requests page](https://gitlab.com/gitlab-org/gitlab-docs/-/merge_requests?label_name%5B%5D=release)
- to check that their pipelines pass.
-
-Do not merge these merge requests yet.
-
-## Merge merge requests and run Docker image builds
-
-The merge requests for the dropdowns should now all be merged into their respective stable branches.
-Each merge triggers a new pipeline for each stable branch. Wait for the stable branch pipelines to
-complete, then:
-
-1. Check the [pipelines page](https://gitlab.com/gitlab-org/gitlab-docs/pipelines)
- and make sure all stable branches have green pipelines.
-1. After all the pipelines succeed:
- 1. Merge all of the [dropdown merge requests](#update-dropdown-for-online-versions).
- 1. Merge the [release merge request](#create-release-merge-request).
-1. Finally, run the
- [`Build docker images weekly` pipeline](https://gitlab.com/gitlab-org/gitlab-docs/pipeline_schedules)
- that builds the `:latest` and `:archives` Docker images.
-
-As the last step in the scheduled pipeline, the documentation site deploys with all new versions.
+<!-- This redirect file can be deleted after <2021-07-12>. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/development/ee_features.md b/doc/development/ee_features.md
index 81014b7624c..35e7824721a 100644
--- a/doc/development/ee_features.md
+++ b/doc/development/ee_features.md
@@ -23,6 +23,8 @@ when no license is active. So EE features always should be guarded by
`project.feature_available?` or `group.feature_available?` (or
`License.feature_available?` if it is a system-wide feature).
+Frontend features should be guarded by pushing a flag from the backend by [using `push_licensed_feature`](licensed_feature_availability.md#restricting-frontend-features), and checked using `this.glFeatures.someFeature` in the frontend.
+
CE specs should remain untouched as much as possible and extra specs
should be added for EE. Licensed features can be stubbed using the
spec helper `stub_licensed_features` in `EE::LicenseHelpers`.
diff --git a/doc/development/feature_flags/index.md b/doc/development/feature_flags/index.md
index f6e398f1074..560e4f8cb90 100644
--- a/doc/development/feature_flags/index.md
+++ b/doc/development/feature_flags/index.md
@@ -292,8 +292,7 @@ end
### Frontend
-Use the `push_frontend_feature_flag` method for frontend code, which is
-available to all controllers that inherit from `ApplicationController`. You can use
+Use the `push_frontend_feature_flag` method which is available to all controllers that inherit from `ApplicationController`. You can use
this method to expose the state of a feature flag, for example:
```ruby
diff --git a/doc/development/licensed_feature_availability.md b/doc/development/licensed_feature_availability.md
index a9fc0414297..10e6d717a18 100644
--- a/doc/development/licensed_feature_availability.md
+++ b/doc/development/licensed_feature_availability.md
@@ -41,3 +41,16 @@ the instance license.
```ruby
License.feature_available?(:feature_symbol)
```
+
+## Restricting frontend features
+
+To restrict frontend features based on the license, use `push_licensed_feature`.
+The frontend can then access this via `this.glFeatures`:
+
+```ruby
+before_action do
+ push_licensed_feature(:feature_symbol)
+ # or by project/namespace
+ push_licensed_feature(:feature_symbol, project)
+end
+```
diff --git a/doc/integration/oauth_provider.md b/doc/integration/oauth_provider.md
index faf6f9e6f48..490397cdf1b 100644
--- a/doc/integration/oauth_provider.md
+++ b/doc/integration/oauth_provider.md
@@ -58,6 +58,8 @@ To add a new application for your user:
## Group owned applications
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/16227) in GitLab 13.11.
+
To add a new application for a group:
1. Navigate to the desired group.
diff --git a/doc/user/admin_area/geo_nodes.md b/doc/user/admin_area/geo_nodes.md
index f41170da975..e5132ef4e96 100644
--- a/doc/user/admin_area/geo_nodes.md
+++ b/doc/user/admin_area/geo_nodes.md
@@ -70,6 +70,12 @@ breaking communication between **primary** and **secondary** nodes when using
HTTPS, customize your Internal URL to point to a load balancer with TLS
terminated at the load balancer.
+WARNING:
+Starting with GitLab 13.3 and [until 13.11](https://gitlab.com/gitlab-org/gitlab/-/issues/325522),
+using an internal URL that is not accessible to the users will result in the
+OAuth authorization flow not working properly, as the users will get redirected
+to the internal URL instead of the external one.
+
## Multiple secondary nodes behind a load balancer
In GitLab 11.11, **secondary** nodes can use identical external URLs as long as
diff --git a/doc/user/project/description_templates.md b/doc/user/project/description_templates.md
index c45ce7a02d9..3acef242cef 100644
--- a/doc/user/project/description_templates.md
+++ b/doc/user/project/description_templates.md
@@ -51,8 +51,10 @@ directory in your repository. Commit and push to your default branch.
To create a Markdown file:
-1. Click the `+` button next to `master` and click **New file**.
-1. Add the name of your issue template to the **File name** text field next to `master`.
+1. In a project, go to **Repository**.
+1. Next to the default branch, select the **{plus}** button.
+1. Select **New file**.
+1. Next to the default branch, in the **File name** field, add the name of your issue template.
Make sure that your file has the `.md` extension, for
example `feature_request.md` or `Feature Request.md`.
1. Commit and push to your default branch.
@@ -61,9 +63,12 @@ If you don't have a `.gitlab/issue_templates` directory in your repository, you
To create the `.gitlab/issue_templates` directory:
-1. Click the `+` button next to `master` and select **New directory**.
+1. In a project, go to **Repository**.
+1. Next to the default branch, select the **{plus}** button.
+1. Select **New directory**.
1. Name this new directory `.gitlab` and commit to your default branch.
-1. Click the `+` button next to `master` again and select **New directory**.
+1. Next to the default branch, select the **{plus}** button.
+1. Select **New directory**.
1. Name your directory `issue_templates` and commit to your default branch.
To check if this has worked correctly, [create a new issue](issues/managing_issues.md#create-a-new-issue)
diff --git a/doc/user/project/settings/index.md b/doc/user/project/settings/index.md
index 62f61c01e19..bcfd7f5e5d9 100644
--- a/doc/user/project/settings/index.md
+++ b/doc/user/project/settings/index.md
@@ -65,6 +65,71 @@ can now create their own.
New compliance framework labels can be created and updated using GraphQL.
+#### Compliance pipeline configuration **(ULTIMATE)**
+
+> - [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/3156) in GitLab 13.9.
+> - [Deployed behind a feature flag](../../feature_flags.md).
+> - [Enabled by default](https://gitlab.com/gitlab-org/gitlab/-/issues/300324) in GitLab 13.11.
+> - Enabled on GitLab.com.
+> - Recommended for production use.
+
+WARNING:
+This feature might not be available to you. Check the **version history** note above for details.
+
+Group owners can use the compliance pipeline configuration to define compliance requirements
+such as scans or tests, and enforce them in individual projects.
+
+The [custom compliance framework](#custom-compliance-frameworks) feature allows group owners to specify the location
+of a compliance pipeline configuration stored and managed in a dedicated project, distinct from a developer's project.
+
+When you set up the compliance pipeline configuration field, use the
+`file@group/project` format. For example, you can configure
+`.compliance-gitlab-ci.yml@compliance-group/compliance-project`.
+This field is inherited by projects where the compliance framework label is applied. The result
+forces the project to run the compliance configurations.
+
+When a project with a custom label executes a pipeline, it begins by evaluating the compliance pipeline configuration.
+The custom pipeline configuration can then execute any included individual project configuration.
+
+The user running the pipeline in the project should at least have Reporter access to the compliance project.
+
+Example `.compliance-gitlab-ci.yml`
+
+```yaml
+stages: # Allows compliance team to control the ordering and interweaving of stages/jobs
+- pre-compliance
+- build
+- test
+- pre-deploy-compliance
+- deploy
+- post-compliance
+
+variables: # can be overriden by a developer's local .gitlab-ci.yml
+ FOO: sast
+
+sast: # none of these attributes can be overriden by a developer's local .gitlab-ci.yml
+ variables:
+ FOO: sast
+ stage: pre-compliance
+ script:
+ - echo "running $FOO"
+
+sanity check:
+ stage: pre-deploy-compliance
+ script:
+ - echo "running $FOO"
+
+
+audit trail:
+ stage: post-compliance
+ script:
+ - echo "running $FOO"
+
+include: # Execute individual project's configuration
+ project: '$CI_PROJECT_PATH'
+ file: '$CI_PROJECT_CONFIG_PATH'
+```
+
### Sharing and permissions
For your repository, you can set up features such as public access, repository features,
diff --git a/lib/gitlab.rb b/lib/gitlab.rb
index 11ecfb951aa..ddf08c8dc20 100644
--- a/lib/gitlab.rb
+++ b/lib/gitlab.rb
@@ -108,10 +108,21 @@ module Gitlab
!%w[true 1].include?(ENV['FOSS_ONLY'].to_s)
end
+ def self.jh?
+ @is_jh ||=
+ ee? &&
+ root.join('jh').exist? &&
+ !%w[true 1].include?(ENV['EE_ONLY'].to_s)
+ end
+
def self.ee
yield if ee?
end
+ def self.jh
+ yield if jh?
+ end
+
def self.http_proxy_env?
HTTP_PROXY_ENV_VARS.any? { |name| ENV[name] }
end
diff --git a/lib/gitlab/subscription_portal.rb b/lib/gitlab/subscription_portal.rb
index a918e7bec80..3072210d7c8 100644
--- a/lib/gitlab/subscription_portal.rb
+++ b/lib/gitlab/subscription_portal.rb
@@ -6,6 +6,11 @@ module Gitlab
::Gitlab.dev_or_test_env? ? 'https://customers.stg.gitlab.com' : 'https://customers.gitlab.com'
end
- SUBSCRIPTIONS_URL = ENV.fetch('CUSTOMER_PORTAL_URL', default_subscriptions_url).freeze
+ def self.subscriptions_url
+ ENV.fetch('CUSTOMER_PORTAL_URL', default_subscriptions_url)
+ end
end
end
+
+Gitlab::SubscriptionPortal.prepend_if_jh('JH::Gitlab::SubscriptionPortal')
+Gitlab::SubscriptionPortal::SUBSCRIPTIONS_URL = Gitlab::SubscriptionPortal.subscriptions_url.freeze
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index c7df470f207..8371a6f2812 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -8804,9 +8804,24 @@ msgstr ""
msgid "Corpus Management|Are you sure you want to delete the corpus?"
msgstr ""
+msgid "CorpusManagement|Actions"
+msgstr ""
+
+msgid "CorpusManagement|Corpus are used in fuzz testing as mutation source to Improve future testing."
+msgstr ""
+
+msgid "CorpusManagement|Corpus name"
+msgstr ""
+
msgid "CorpusManagement|Fuzz testing corpus management"
msgstr ""
+msgid "CorpusManagement|Last updated"
+msgstr ""
+
+msgid "CorpusManagement|Last used"
+msgstr ""
+
msgid "CorpusManagement|Latest Job:"
msgstr ""
@@ -8816,6 +8831,9 @@ msgstr ""
msgid "CorpusManagement|Not Set"
msgstr ""
+msgid "CorpusManagement|Target"
+msgstr ""
+
msgid "CorpusManagement|Total Size: %{totalSize}"
msgstr ""
@@ -18397,6 +18415,9 @@ msgstr ""
msgid "Learn GitLab|Trial only"
msgstr ""
+msgid "Learn More"
+msgstr ""
+
msgid "Learn how to %{link_start}contribute to the built-in templates%{link_end}"
msgstr ""
@@ -18475,12 +18496,18 @@ msgstr ""
msgid "LearnGitLab|Create a workflow for your new workspace, and learn how GitLab features work together:"
msgstr ""
+msgid "LearnGitLab|Create an issue"
+msgstr ""
+
msgid "LearnGitLab|Create or import a repository"
msgstr ""
msgid "LearnGitLab|Create or import your first repository into your new project."
msgstr ""
+msgid "LearnGitLab|Create/import issues (tickets) to collaborate on ideas and plan work."
+msgstr ""
+
msgid "LearnGitLab|Deploy"
msgstr ""
@@ -26722,6 +26749,9 @@ msgstr ""
msgid "Reset key"
msgstr ""
+msgid "Reset password"
+msgstr ""
+
msgid "Reset registration token"
msgstr ""
@@ -29023,9 +29053,6 @@ msgstr ""
msgid "Something went wrong on our end"
msgstr ""
-msgid "Something went wrong on our end while loading the code quality diff."
-msgstr ""
-
msgid "Something went wrong on our end."
msgstr ""
@@ -30997,7 +31024,7 @@ msgstr ""
msgid "The merge request can now be merged."
msgstr ""
-msgid "The merge request has made changes to this file that affect the number of code quality violations in it."
+msgid "The merge request has been updated, and the number of code quality violations in this file has changed."
msgstr ""
msgid "The metric must be one of %{metrics}."
diff --git a/package.json b/package.json
index c5b6d7da605..7524f79693f 100644
--- a/package.json
+++ b/package.json
@@ -51,7 +51,7 @@
"@gitlab/favicon-overlay": "2.0.0",
"@gitlab/svgs": "1.188.0",
"@gitlab/tributejs": "1.0.0",
- "@gitlab/ui": "29.3.0",
+ "@gitlab/ui": "29.4.0",
"@gitlab/visual-review-tools": "1.6.1",
"@rails/actioncable": "^6.0.3-4",
"@rails/ujs": "^6.0.3-4",
@@ -155,7 +155,7 @@
"vuex": "^3.6.0",
"web-vitals": "^0.2.4",
"webpack": "^4.46.0",
- "webpack-bundle-analyzer": "^4.4.0",
+ "webpack-bundle-analyzer": "^4.4.1",
"webpack-cli": "^3.3.12",
"webpack-stats-plugin": "^0.3.1",
"worker-loader": "^2.0.0",
diff --git a/scripts/rspec_helpers.sh b/scripts/rspec_helpers.sh
index 6ec19327942..e23231e429b 100644
--- a/scripts/rspec_helpers.sh
+++ b/scripts/rspec_helpers.sh
@@ -97,6 +97,10 @@ function rspec_paralellized_job() {
spec_folder_prefix="ee/"
fi
+ if [[ "${test_tool}" =~ "-jh" ]]; then
+ spec_folder_prefix="jh/"
+ fi
+
export KNAPSACK_LOG_LEVEL="debug"
export KNAPSACK_REPORT_PATH="knapsack/${report_name}_report.json"
diff --git a/spec/controllers/profiles/notifications_controller_spec.rb b/spec/controllers/profiles/notifications_controller_spec.rb
index 0ce2854571d..1ebf4363ba6 100644
--- a/spec/controllers/profiles/notifications_controller_spec.rb
+++ b/spec/controllers/profiles/notifications_controller_spec.rb
@@ -21,6 +21,30 @@ RSpec.describe Profiles::NotificationsController do
expect(response).to render_template :show
end
+ context 'when personal projects are present', :request_store do
+ let!(:personal_project_1) { create(:project, namespace: user.namespace) }
+
+ context 'N+1 query check' do
+ render_views
+
+ it 'does not have an N+1' do
+ sign_in(user)
+
+ get :show
+
+ control = ActiveRecord::QueryRecorder.new do
+ get :show
+ end
+
+ create_list(:project, 2, namespace: user.namespace)
+
+ expect do
+ get :show
+ end.not_to exceed_query_limit(control)
+ end
+ end
+ end
+
context 'with groups that do not have notification preferences' do
let_it_be(:group) { create(:group) }
let_it_be(:subgroup) { create(:group, parent: group) }
diff --git a/spec/deprecation_toolkit_env.rb b/spec/deprecation_toolkit_env.rb
index f4ead6d5f01..e49b3074b06 100644
--- a/spec/deprecation_toolkit_env.rb
+++ b/spec/deprecation_toolkit_env.rb
@@ -56,7 +56,6 @@ module DeprecationToolkitEnv
def self.allowed_kwarg_warning_paths
%w[
activerecord-6.0.3.4/lib/active_record/migration.rb
- devise-4.7.3/lib/devise/test/controller_helpers.rb
activesupport-6.0.3.4/lib/active_support/cache.rb
batch-loader-1.4.0/lib/batch_loader/graphql.rb
carrierwave-1.3.1/lib/carrierwave/sanitized_file.rb
diff --git a/spec/features/admin/admin_settings_spec.rb b/spec/features/admin/admin_settings_spec.rb
index aa848c25349..6732ef575d4 100644
--- a/spec/features/admin/admin_settings_spec.rb
+++ b/spec/features/admin/admin_settings_spec.rb
@@ -596,7 +596,7 @@ RSpec.describe 'Admin updates settings' do
context 'Nav bar' do
it 'shows default help links in nav' do
- default_support_url = 'https://about.gitlab.com/getting-help/'
+ default_support_url = "https://#{ApplicationHelper.promo_host}/getting-help/"
visit root_dashboard_path
diff --git a/spec/frontend/diffs/components/compare_versions_spec.js b/spec/frontend/diffs/components/compare_versions_spec.js
index 4feddb939c6..a01ec1db35c 100644
--- a/spec/frontend/diffs/components/compare_versions_spec.js
+++ b/spec/frontend/diffs/components/compare_versions_spec.js
@@ -211,19 +211,20 @@ describe('CompareVersions', () => {
});
describe('prev commit', () => {
- const { location } = window;
-
beforeAll(() => {
- delete window.location;
- window.location = { href: `${TEST_HOST}?commit_id=${mrCommit.id}` };
+ global.jsdom.reconfigure({
+ url: `${TEST_HOST}?commit_id=${mrCommit.id}`,
+ });
});
- beforeEach(() => {
- jest.spyOn(wrapper.vm, 'moveToNeighboringCommit').mockImplementation(() => {});
+ afterAll(() => {
+ global.jsdom.reconfigure({
+ url: TEST_HOST,
+ });
});
- afterAll(() => {
- window.location = location;
+ beforeEach(() => {
+ jest.spyOn(wrapper.vm, 'moveToNeighboringCommit').mockImplementation(() => {});
});
it('uses the correct href', () => {
@@ -253,19 +254,20 @@ describe('CompareVersions', () => {
});
describe('next commit', () => {
- const { location } = window;
-
beforeAll(() => {
- delete window.location;
- window.location = { href: `${TEST_HOST}?commit_id=${mrCommit.id}` };
+ global.jsdom.reconfigure({
+ url: `${TEST_HOST}?commit_id=${mrCommit.id}`,
+ });
});
- beforeEach(() => {
- jest.spyOn(wrapper.vm, 'moveToNeighboringCommit').mockImplementation(() => {});
+ afterAll(() => {
+ global.jsdom.reconfigure({
+ url: TEST_HOST,
+ });
});
- afterAll(() => {
- window.location = location;
+ beforeEach(() => {
+ jest.spyOn(wrapper.vm, 'moveToNeighboringCommit').mockImplementation(() => {});
});
it('uses the correct href', () => {
diff --git a/spec/frontend/diffs/components/diff_row_spec.js b/spec/frontend/diffs/components/diff_row_spec.js
index 5682b29d697..0bc1bd40f06 100644
--- a/spec/frontend/diffs/components/diff_row_spec.js
+++ b/spec/frontend/diffs/components/diff_row_spec.js
@@ -4,6 +4,7 @@ import Vuex from 'vuex';
import DiffRow from '~/diffs/components/diff_row.vue';
import { mapParallel } from '~/diffs/components/diff_row_utils';
import diffsModule from '~/diffs/store/modules';
+import { findInteropAttributes } from '../find_interop_attributes';
import diffFileMockData from '../mock_data/diff_file';
describe('DiffRow', () => {
@@ -211,4 +212,20 @@ describe('DiffRow', () => {
expect(coverage.classes('no-coverage')).toBeFalsy();
});
});
+
+ describe('interoperability', () => {
+ it.each`
+ desc | line | inline | leftSide | rightSide
+ ${'with inline and new_line'} | ${{ left: { old_line: 3, new_line: 5, type: 'new' } }} | ${true} | ${{ type: 'new', line: '5', oldLine: '3', newLine: '5' }} | ${null}
+ ${'with inline and no new_line'} | ${{ left: { old_line: 3, type: 'old' } }} | ${true} | ${{ type: 'old', line: '3', oldLine: '3' }} | ${null}
+ ${'with parallel and no right side'} | ${{ left: { old_line: 3, new_line: 5 } }} | ${false} | ${{ type: 'old', line: '3', oldLine: '3' }} | ${null}
+ ${'with parallel and no left side'} | ${{ right: { old_line: 3, new_line: 5 } }} | ${false} | ${null} | ${{ type: 'new', line: '5', newLine: '5' }}
+ ${'with parallel and right side'} | ${{ left: { old_line: 3 }, right: { new_line: 5 } }} | ${false} | ${{ type: 'old', line: '3', oldLine: '3' }} | ${{ type: 'new', line: '5', newLine: '5' }}
+ `('$desc, sets interop data attributes', ({ line, inline, leftSide, rightSide }) => {
+ const wrapper = createWrapper({ props: { line, inline } });
+
+ expect(findInteropAttributes(wrapper, '[data-testid="left-side"]')).toEqual(leftSide);
+ expect(findInteropAttributes(wrapper, '[data-testid="right-side"]')).toEqual(rightSide);
+ });
+ });
});
diff --git a/spec/frontend/diffs/components/inline_diff_table_row_spec.js b/spec/frontend/diffs/components/inline_diff_table_row_spec.js
index 28b3055b58c..66b63a7a1d0 100644
--- a/spec/frontend/diffs/components/inline_diff_table_row_spec.js
+++ b/spec/frontend/diffs/components/inline_diff_table_row_spec.js
@@ -3,6 +3,7 @@ import DiffGutterAvatars from '~/diffs/components/diff_gutter_avatars.vue';
import { mapInline } from '~/diffs/components/diff_row_utils';
import InlineDiffTableRow from '~/diffs/components/inline_diff_table_row.vue';
import { createStore } from '~/mr_notes/stores';
+import { findInteropAttributes } from '../find_interop_attributes';
import discussionsMockData from '../mock_data/diff_discussions';
import diffFileMockData from '../mock_data/diff_file';
@@ -310,4 +311,16 @@ describe('InlineDiffTableRow', () => {
});
});
});
+
+ describe('interoperability', () => {
+ it.each`
+ desc | line | expectation
+ ${'with type old'} | ${{ ...thisLine, type: 'old', old_line: 3, new_line: 5 }} | ${{ type: 'old', line: '3', oldLine: '3', newLine: '5' }}
+ ${'with type new'} | ${{ ...thisLine, type: 'new', old_line: 3, new_line: 5 }} | ${{ type: 'new', line: '5', oldLine: '3', newLine: '5' }}
+ `('$desc, sets interop data attributes', ({ line, expectation }) => {
+ createComponent({ line });
+
+ expect(findInteropAttributes(wrapper)).toEqual(expectation);
+ });
+ });
});
diff --git a/spec/frontend/diffs/components/parallel_diff_table_row_spec.js b/spec/frontend/diffs/components/parallel_diff_table_row_spec.js
index dbe8303077d..ed191d849fd 100644
--- a/spec/frontend/diffs/components/parallel_diff_table_row_spec.js
+++ b/spec/frontend/diffs/components/parallel_diff_table_row_spec.js
@@ -5,6 +5,7 @@ import DiffGutterAvatars from '~/diffs/components/diff_gutter_avatars.vue';
import { mapParallel } from '~/diffs/components/diff_row_utils';
import ParallelDiffTableRow from '~/diffs/components/parallel_diff_table_row.vue';
import { createStore } from '~/mr_notes/stores';
+import { findInteropAttributes } from '../find_interop_attributes';
import discussionsMockData from '../mock_data/diff_discussions';
import diffFileMockData from '../mock_data/diff_file';
@@ -418,5 +419,27 @@ describe('ParallelDiffTableRow', () => {
});
});
});
+
+ describe('interoperability', () => {
+ beforeEach(() => {
+ createComponent();
+ });
+
+ it('adds old side interoperability data attributes', () => {
+ expect(findInteropAttributes(wrapper, '.line_content.left-side')).toEqual({
+ type: 'old',
+ line: thisLine.left.old_line.toString(),
+ oldLine: thisLine.left.old_line.toString(),
+ });
+ });
+
+ it('adds new side interoperability data attributes', () => {
+ expect(findInteropAttributes(wrapper, '.line_content.right-side')).toEqual({
+ type: 'new',
+ line: thisLine.right.new_line.toString(),
+ newLine: thisLine.right.new_line.toString(),
+ });
+ });
+ });
});
});
diff --git a/spec/frontend/diffs/find_interop_attributes.js b/spec/frontend/diffs/find_interop_attributes.js
new file mode 100644
index 00000000000..d2266b20e16
--- /dev/null
+++ b/spec/frontend/diffs/find_interop_attributes.js
@@ -0,0 +1,20 @@
+export const findInteropAttributes = (parent, sel) => {
+ const target = sel ? parent.find(sel) : parent;
+
+ if (!target.exists()) {
+ return null;
+ }
+
+ const type = target.attributes('data-interop-type');
+
+ if (!type) {
+ return null;
+ }
+
+ return {
+ type,
+ line: target.attributes('data-interop-line'),
+ oldLine: target.attributes('data-interop-old-line'),
+ newLine: target.attributes('data-interop-new-line'),
+ };
+};
diff --git a/spec/frontend/diffs/store/actions_spec.js b/spec/frontend/diffs/store/actions_spec.js
index f46a42fae7a..1861de85ca9 100644
--- a/spec/frontend/diffs/store/actions_spec.js
+++ b/spec/frontend/diffs/store/actions_spec.js
@@ -17,6 +17,9 @@ import {
fetchDiffFilesBatch,
fetchDiffFilesMeta,
fetchCoverageFiles,
+ clearEtagPoll,
+ stopCodequalityPolling,
+ fetchCodequality,
assignDiscussionsToDiff,
removeDiscussionsFromDiff,
startRenderDiffsQueue,
@@ -98,6 +101,7 @@ describe('DiffsStoreActions', () => {
const endpointMetadata = '/diffs/set/endpoint/metadata';
const endpointBatch = '/diffs/set/endpoint/batch';
const endpointCoverage = '/diffs/set/coverage_reports';
+ const endpointCodequality = '/diffs/set/codequality_diff';
const projectPath = '/root/project';
const dismissEndpoint = '/-/user_callouts';
const showSuggestPopover = false;
@@ -109,6 +113,7 @@ describe('DiffsStoreActions', () => {
endpointBatch,
endpointMetadata,
endpointCoverage,
+ endpointCodequality,
projectPath,
dismissEndpoint,
showSuggestPopover,
@@ -118,6 +123,7 @@ describe('DiffsStoreActions', () => {
endpointBatch: '',
endpointMetadata: '',
endpointCoverage: '',
+ endpointCodequality: '',
projectPath: '',
dismissEndpoint: '',
showSuggestPopover: true,
@@ -130,6 +136,7 @@ describe('DiffsStoreActions', () => {
endpointMetadata,
endpointBatch,
endpointCoverage,
+ endpointCodequality,
projectPath,
dismissEndpoint,
showSuggestPopover,
@@ -299,6 +306,47 @@ describe('DiffsStoreActions', () => {
});
});
+ describe('fetchCodequality', () => {
+ let mock;
+ const endpointCodequality = '/fetch';
+
+ beforeEach(() => {
+ mock = new MockAdapter(axios);
+ });
+
+ afterEach(() => {
+ stopCodequalityPolling();
+ clearEtagPoll();
+ });
+
+ it('should commit SET_CODEQUALITY_DATA with received response', (done) => {
+ const data = {
+ files: { 'app.js': [{ line: 1, description: 'Unexpected alert.', severity: 'minor' }] },
+ };
+
+ mock.onGet(endpointCodequality).reply(200, { data });
+
+ testAction(
+ fetchCodequality,
+ {},
+ { endpointCodequality },
+ [{ type: types.SET_CODEQUALITY_DATA, payload: { data } }],
+ [],
+ done,
+ );
+ });
+
+ it('should show flash on API error', (done) => {
+ mock.onGet(endpointCodequality).reply(400);
+
+ testAction(fetchCodequality, {}, { endpointCodequality }, [], [], () => {
+ expect(createFlash).toHaveBeenCalledTimes(1);
+ expect(createFlash).toHaveBeenCalledWith(expect.stringMatching('Something went wrong'));
+ done();
+ });
+ });
+ });
+
describe('setHighlightedRow', () => {
it('should mark currently selected diff and set lineHash and fileHash of highlightedRow', () => {
testAction(setHighlightedRow, 'ABC_123', {}, [
diff --git a/spec/frontend/diffs/store/getters_spec.js b/spec/frontend/diffs/store/getters_spec.js
index 2e3a66d5b01..aac4909b151 100644
--- a/spec/frontend/diffs/store/getters_spec.js
+++ b/spec/frontend/diffs/store/getters_spec.js
@@ -376,6 +376,26 @@ describe('Diffs Module Getters', () => {
});
});
+ describe('fileCodequalityDiff', () => {
+ beforeEach(() => {
+ Object.assign(localState.codequalityDiff, {
+ files: { 'app.js': [{ line: 1, description: 'Unexpected alert.', severity: 'minor' }] },
+ });
+ });
+
+ it('returns empty array when no codequality data is available', () => {
+ Object.assign(localState.codequalityDiff, {});
+
+ expect(getters.fileCodequalityDiff(localState)('test.js')).toEqual([]);
+ });
+
+ it('returns array when codequality data is available for given file', () => {
+ expect(getters.fileCodequalityDiff(localState)('app.js')).toEqual([
+ { line: 1, description: 'Unexpected alert.', severity: 'minor' },
+ ]);
+ });
+ });
+
describe('suggestionCommitMessage', () => {
let rootState;
diff --git a/spec/frontend/diffs/store/mutations_spec.js b/spec/frontend/diffs/store/mutations_spec.js
index b549ca42634..eb31724ee45 100644
--- a/spec/frontend/diffs/store/mutations_spec.js
+++ b/spec/frontend/diffs/store/mutations_spec.js
@@ -115,6 +115,19 @@ describe('DiffsStoreMutations', () => {
});
});
+ describe('SET_CODEQUALITY_DATA', () => {
+ it('should set codequality data', () => {
+ const state = { codequalityDiff: {} };
+ const codequality = {
+ files: { 'app.js': [{ line: 1, description: 'Unexpected alert.', severity: 'minor' }] },
+ };
+
+ mutations[types.SET_CODEQUALITY_DATA](state, codequality);
+
+ expect(state.codequalityDiff).toEqual(codequality);
+ });
+ });
+
describe('SET_DIFF_VIEW_TYPE', () => {
it('should set diff view type properly', () => {
const state = {};
diff --git a/spec/frontend/diffs/utils/interoperability_spec.js b/spec/frontend/diffs/utils/interoperability_spec.js
new file mode 100644
index 00000000000..2557e83cb4c
--- /dev/null
+++ b/spec/frontend/diffs/utils/interoperability_spec.js
@@ -0,0 +1,67 @@
+import {
+ getInteropInlineAttributes,
+ getInteropNewSideAttributes,
+ getInteropOldSideAttributes,
+ ATTR_TYPE,
+ ATTR_LINE,
+ ATTR_NEW_LINE,
+ ATTR_OLD_LINE,
+} from '~/diffs/utils/interoperability';
+
+describe('~/diffs/utils/interoperability', () => {
+ describe('getInteropInlineAttributes', () => {
+ it.each([
+ ['with null input', { input: null, output: null }],
+ [
+ 'with type=old input',
+ {
+ input: { type: 'old', old_line: 3, new_line: 5 },
+ output: { [ATTR_TYPE]: 'old', [ATTR_LINE]: 3, [ATTR_OLD_LINE]: 3, [ATTR_NEW_LINE]: 5 },
+ },
+ ],
+ [
+ 'with type=old-nonewline input',
+ {
+ input: { type: 'old-nonewline', old_line: 3, new_line: 5 },
+ output: { [ATTR_TYPE]: 'old', [ATTR_LINE]: 3, [ATTR_OLD_LINE]: 3, [ATTR_NEW_LINE]: 5 },
+ },
+ ],
+ [
+ 'with type=new input',
+ {
+ input: { type: 'new', old_line: 3, new_line: 5 },
+ output: { [ATTR_TYPE]: 'new', [ATTR_LINE]: 5, [ATTR_OLD_LINE]: 3, [ATTR_NEW_LINE]: 5 },
+ },
+ ],
+ [
+ 'with type=bogus input',
+ {
+ input: { type: 'bogus', old_line: 3, new_line: 5 },
+ output: { [ATTR_TYPE]: 'new', [ATTR_LINE]: 5, [ATTR_OLD_LINE]: 3, [ATTR_NEW_LINE]: 5 },
+ },
+ ],
+ ])('%s', (desc, { input, output }) => {
+ expect(getInteropInlineAttributes(input)).toEqual(output);
+ });
+ });
+
+ describe('getInteropOldSideAttributes', () => {
+ it.each`
+ input | output
+ ${null} | ${null}
+ ${{ old_line: 2 }} | ${{ [ATTR_TYPE]: 'old', [ATTR_LINE]: 2, [ATTR_OLD_LINE]: 2 }}
+ `('with input=$input', ({ input, output }) => {
+ expect(getInteropOldSideAttributes(input)).toEqual(output);
+ });
+ });
+
+ describe('getInteropNewSideAttributes', () => {
+ it.each`
+ input | output
+ ${null} | ${null}
+ ${{ new_line: 2 }} | ${{ [ATTR_TYPE]: 'new', [ATTR_LINE]: 2, [ATTR_NEW_LINE]: 2 }}
+ `('with input=$input', ({ input, output }) => {
+ expect(getInteropNewSideAttributes(input)).toEqual(output);
+ });
+ });
+});
diff --git a/spec/frontend/fixtures/merge_requests_diffs.rb b/spec/frontend/fixtures/merge_requests_diffs.rb
index 5ad4176f7b8..edf1fcf3c0a 100644
--- a/spec/frontend/fixtures/merge_requests_diffs.rb
+++ b/spec/frontend/fixtures/merge_requests_diffs.rb
@@ -25,6 +25,10 @@ RSpec.describe Projects::MergeRequests::DiffsController, '(JavaScript fixtures)'
end
before do
+ # Create a user that matches the project.commit author
+ # This is so that the "author" information will be populated
+ create(:user, email: project.commit.author_email, name: project.commit.author_name)
+
sign_in(user)
end
@@ -33,17 +37,21 @@ RSpec.describe Projects::MergeRequests::DiffsController, '(JavaScript fixtures)'
end
it 'merge_request_diffs/with_commit.json' do
- # Create a user that matches the project.commit author
- # This is so that the "author" information will be populated
- create(:user, email: project.commit.author_email, name: project.commit.author_name)
-
render_merge_request(merge_request, commit_id: project.commit.sha)
end
+ it 'merge_request_diffs/diffs_metadata.json' do
+ render_merge_request(merge_request, action: :diffs_metadata)
+ end
+
+ it 'merge_request_diffs/diffs_batch.json' do
+ render_merge_request(merge_request, action: :diffs_batch, page: 1, per_page: 30)
+ end
+
private
- def render_merge_request(merge_request, view: 'inline', **extra_params)
- get :show, params: {
+ def render_merge_request(merge_request, action: :show, view: 'inline', **extra_params)
+ get action, params: {
namespace_id: project.namespace.to_param,
project_id: project,
id: merge_request.to_param,
diff --git a/spec/frontend/pages/projects/learn_gitlab/components/__snapshots__/learn_gitlab_a_spec.js.snap b/spec/frontend/pages/projects/learn_gitlab/components/__snapshots__/learn_gitlab_a_spec.js.snap
index cecaa3fc365..8b54a06ac7c 100644
--- a/spec/frontend/pages/projects/learn_gitlab/components/__snapshots__/learn_gitlab_a_spec.js.snap
+++ b/spec/frontend/pages/projects/learn_gitlab/components/__snapshots__/learn_gitlab_a_spec.js.snap
@@ -29,21 +29,21 @@ exports[`Learn GitLab Design A renders correctly 1`] = `
class="gl-text-gray-500 gl-mb-2"
data-testid="completion-percentage"
>
- 25% completed
+ 22% completed
</p>
<div
class="progress"
- max="8"
+ max="9"
value="2"
>
<div
- aria-valuemax="8"
+ aria-valuemax="9"
aria-valuemin="0"
aria-valuenow="2"
class="progress-bar"
role="progressbar"
- style="width: 25%;"
+ style="width: 22.22222222222222%;"
>
<!---->
</div>
@@ -242,6 +242,20 @@ exports[`Learn GitLab Design A renders correctly 1`] = `
class="gl-link"
href="http://example.com/"
>
+ Create an issue
+ </a>
+ </span>
+
+ <!---->
+ </div>
+ <div
+ class="gl-mb-4"
+ >
+ <span>
+ <a
+ class="gl-link"
+ href="http://example.com/"
+ >
Submit a merge request
</a>
</span>
diff --git a/spec/frontend/pages/projects/learn_gitlab/components/__snapshots__/learn_gitlab_b_spec.js.snap b/spec/frontend/pages/projects/learn_gitlab/components/__snapshots__/learn_gitlab_b_spec.js.snap
index a78544fb14c..07c7f2df09e 100644
--- a/spec/frontend/pages/projects/learn_gitlab/components/__snapshots__/learn_gitlab_b_spec.js.snap
+++ b/spec/frontend/pages/projects/learn_gitlab/components/__snapshots__/learn_gitlab_b_spec.js.snap
@@ -29,21 +29,21 @@ exports[`Learn GitLab Design B renders correctly 1`] = `
class="gl-text-gray-500 gl-mb-2"
data-testid="completion-percentage"
>
- 25% completed
+ 22% completed
</p>
<div
class="progress"
- max="8"
+ max="9"
value="2"
>
<div
- aria-valuemax="8"
+ aria-valuemax="9"
aria-valuemin="0"
aria-valuenow="2"
class="progress-bar"
role="progressbar"
- style="width: 25%;"
+ style="width: 22.22222222222222%;"
>
<!---->
</div>
@@ -94,6 +94,7 @@ exports[`Learn GitLab Design B renders correctly 1`] = `
class="gl-text-center gl-display-flex gl-justify-content-center gl-align-items-center gl-flex-direction-column learn-gitlab-info-card-content"
>
<img
+ alt="Invite your colleagues"
src="http://example.com/images/illustration.svg"
/>
@@ -151,6 +152,7 @@ exports[`Learn GitLab Design B renders correctly 1`] = `
class="gl-text-center gl-display-flex gl-justify-content-center gl-align-items-center gl-flex-direction-column learn-gitlab-info-card-content"
>
<img
+ alt="Create or import a repository"
src="http://example.com/images/illustration.svg"
/>
@@ -200,6 +202,7 @@ exports[`Learn GitLab Design B renders correctly 1`] = `
class="gl-text-center gl-display-flex gl-justify-content-center gl-align-items-center gl-flex-direction-column learn-gitlab-info-card-content"
>
<img
+ alt="Set-up CI/CD"
src="http://example.com/images/illustration.svg"
/>
@@ -249,6 +252,7 @@ exports[`Learn GitLab Design B renders correctly 1`] = `
class="gl-text-center gl-display-flex gl-justify-content-center gl-align-items-center gl-flex-direction-column learn-gitlab-info-card-content"
>
<img
+ alt="Try GitLab Ultimate for free"
src="http://example.com/images/illustration.svg"
/>
@@ -303,6 +307,7 @@ exports[`Learn GitLab Design B renders correctly 1`] = `
class="gl-text-center gl-display-flex gl-justify-content-center gl-align-items-center gl-flex-direction-column learn-gitlab-info-card-content"
>
<img
+ alt="Add code owners"
src="http://example.com/images/illustration.svg"
/>
@@ -357,6 +362,7 @@ exports[`Learn GitLab Design B renders correctly 1`] = `
class="gl-text-center gl-display-flex gl-justify-content-center gl-align-items-center gl-flex-direction-column learn-gitlab-info-card-content"
>
<img
+ alt="Enable require merge approvals"
src="http://example.com/images/illustration.svg"
/>
@@ -422,6 +428,57 @@ exports[`Learn GitLab Design B renders correctly 1`] = `
class="gl-text-center gl-display-flex gl-justify-content-center gl-align-items-center gl-flex-direction-column learn-gitlab-info-card-content"
>
<img
+ alt="Create an issue"
+ src="http://example.com/images/illustration.svg"
+ />
+
+ <h6>
+ Create an issue
+ </h6>
+
+ <p
+ class="gl-font-sm gl-text-gray-700"
+ >
+ Create/import issues (tickets) to collaborate on ideas and plan work.
+ </p>
+
+ <a
+ class="gl-link"
+ href="http://example.com/"
+ rel="noopener noreferrer"
+ target="_blank"
+ >
+ Create an issue
+ </a>
+ </div>
+ </div>
+
+ <!---->
+ </div>
+ </div>
+
+ <div
+ class="col gl-mb-6"
+ >
+ <div
+ class="gl-card gl-pt-0"
+ >
+ <!---->
+
+ <div
+ class="gl-card-body"
+ >
+ <div
+ class="gl-text-right gl-h-5"
+ >
+ <!---->
+ </div>
+
+ <div
+ class="gl-text-center gl-display-flex gl-justify-content-center gl-align-items-center gl-flex-direction-column learn-gitlab-info-card-content"
+ >
+ <img
+ alt="Submit a merge request (MR)"
src="http://example.com/images/illustration.svg"
/>
@@ -487,6 +544,7 @@ exports[`Learn GitLab Design B renders correctly 1`] = `
class="gl-text-center gl-display-flex gl-justify-content-center gl-align-items-center gl-flex-direction-column learn-gitlab-info-card-content"
>
<img
+ alt="Run a Security scan using CI/CD"
src="http://example.com/images/illustration.svg"
/>
diff --git a/spec/frontend/pages/projects/learn_gitlab/components/__snapshots__/learn_gitlab_section_card_spec.js.snap b/spec/frontend/pages/projects/learn_gitlab/components/__snapshots__/learn_gitlab_section_card_spec.js.snap
index 831c027cd66..ad8db0822cc 100644
--- a/spec/frontend/pages/projects/learn_gitlab/components/__snapshots__/learn_gitlab_section_card_spec.js.snap
+++ b/spec/frontend/pages/projects/learn_gitlab/components/__snapshots__/learn_gitlab_section_card_spec.js.snap
@@ -32,6 +32,10 @@ exports[`Learn GitLab Section Card renders correctly 1`] = `
value="[object Object]"
/>
<learn-gitlab-section-link-stub
+ action="issueCreated"
+ value="[object Object]"
+ />
+ <learn-gitlab-section-link-stub
action="gitWrite"
value="[object Object]"
/>
diff --git a/spec/frontend/pages/projects/learn_gitlab/components/learn_gitlab_a_spec.js b/spec/frontend/pages/projects/learn_gitlab/components/learn_gitlab_a_spec.js
index b226c046627..64ace341038 100644
--- a/spec/frontend/pages/projects/learn_gitlab/components/learn_gitlab_a_spec.js
+++ b/spec/frontend/pages/projects/learn_gitlab/components/learn_gitlab_a_spec.js
@@ -26,13 +26,13 @@ describe('Learn GitLab Design A', () => {
it('renders the progress percentage', () => {
const text = wrapper.find('[data-testid="completion-percentage"]').text();
- expect(text).toEqual('25% completed');
+ expect(text).toBe('22% completed');
});
it('renders the progress bar with correct values', () => {
- const progressBar = wrapper.find(GlProgressBar);
+ const progressBar = wrapper.findComponent(GlProgressBar);
expect(progressBar.attributes('value')).toBe('2');
- expect(progressBar.attributes('max')).toBe('8');
+ expect(progressBar.attributes('max')).toBe('9');
});
});
diff --git a/spec/frontend/pages/projects/learn_gitlab/components/learn_gitlab_b_spec.js b/spec/frontend/pages/projects/learn_gitlab/components/learn_gitlab_b_spec.js
index fbb989fae32..207944bfa1f 100644
--- a/spec/frontend/pages/projects/learn_gitlab/components/learn_gitlab_b_spec.js
+++ b/spec/frontend/pages/projects/learn_gitlab/components/learn_gitlab_b_spec.js
@@ -26,13 +26,13 @@ describe('Learn GitLab Design B', () => {
it('renders the progress percentage', () => {
const text = wrapper.find('[data-testid="completion-percentage"]').text();
- expect(text).toEqual('25% completed');
+ expect(text).toBe('22% completed');
});
it('renders the progress bar with correct values', () => {
- const progressBar = wrapper.find(GlProgressBar);
+ const progressBar = wrapper.findComponent(GlProgressBar);
expect(progressBar.attributes('value')).toBe('2');
- expect(progressBar.attributes('max')).toBe('8');
+ expect(progressBar.attributes('max')).toBe('9');
});
});
diff --git a/spec/frontend/pages/projects/learn_gitlab/components/mock_data.js b/spec/frontend/pages/projects/learn_gitlab/components/mock_data.js
index caac667e2b1..d6ee2b00c8e 100644
--- a/spec/frontend/pages/projects/learn_gitlab/components/mock_data.js
+++ b/spec/frontend/pages/projects/learn_gitlab/components/mock_data.js
@@ -39,4 +39,9 @@ export const testActions = {
completed: false,
svg: 'http://example.com/images/illustration.svg',
},
+ issueCreated: {
+ url: 'http://example.com/',
+ completed: false,
+ svg: 'http://example.com/images/illustration.svg',
+ },
};
diff --git a/spec/frontend/pipelines/graph/graph_component_wrapper_spec.js b/spec/frontend/pipelines/graph/graph_component_wrapper_spec.js
index 37fb617ef1a..8c469966be4 100644
--- a/spec/frontend/pipelines/graph/graph_component_wrapper_spec.js
+++ b/spec/frontend/pipelines/graph/graph_component_wrapper_spec.js
@@ -2,9 +2,15 @@ import { GlAlert, GlLoadingIcon } from '@gitlab/ui';
import { mount, shallowMount } from '@vue/test-utils';
import Vue from 'vue';
import VueApollo from 'vue-apollo';
+import { useLocalStorageSpy } from 'helpers/local_storage_helper';
import createMockApollo from 'helpers/mock_apollo_helper';
import getPipelineDetails from 'shared_queries/pipelines/get_pipeline_details.query.graphql';
-import { IID_FAILURE, LAYER_VIEW, STAGE_VIEW } from '~/pipelines/components/graph/constants';
+import {
+ IID_FAILURE,
+ LAYER_VIEW,
+ STAGE_VIEW,
+ VIEW_TYPE_KEY,
+} from '~/pipelines/components/graph/constants';
import PipelineGraph from '~/pipelines/components/graph/graph_component.vue';
import PipelineGraphWrapper from '~/pipelines/components/graph/graph_component_wrapper.vue';
import GraphViewSelector from '~/pipelines/components/graph/graph_view_selector.vue';
@@ -21,6 +27,7 @@ const defaultProvide = {
describe('Pipeline graph wrapper', () => {
Vue.use(VueApollo);
+ useLocalStorageSpy();
let wrapper;
const getAlert = () => wrapper.find(GlAlert);
@@ -216,7 +223,7 @@ describe('Pipeline graph wrapper', () => {
});
describe('view dropdown', () => {
- describe('when feature flag is off', () => {
+ describe('when pipelineGraphLayersView feature flag is off', () => {
beforeEach(async () => {
createComponentWithApollo();
jest.runOnlyPendingTimers();
@@ -228,7 +235,7 @@ describe('Pipeline graph wrapper', () => {
});
});
- describe('when feature flag is on', () => {
+ describe('when pipelineGraphLayersView feature flag is on', () => {
let layersFn;
beforeEach(async () => {
layersFn = jest.spyOn(parsingUtils, 'listByLayers');
@@ -245,7 +252,7 @@ describe('Pipeline graph wrapper', () => {
await wrapper.vm.$nextTick();
});
- it('appears', () => {
+ it('appears when pipeline uses needs', () => {
expect(getViewSelector().exists()).toBe(true);
});
@@ -259,6 +266,11 @@ describe('Pipeline graph wrapper', () => {
expect(getStageColumnTitle().text()).toBe('');
});
+ it('saves the view type to local storage', async () => {
+ await getViewSelector().vm.$emit('updateViewType', LAYER_VIEW);
+ expect(localStorage.setItem.mock.calls).toEqual([[VIEW_TYPE_KEY, LAYER_VIEW]]);
+ });
+
it('calls listByLayers only once no matter how many times view is switched', async () => {
expect(layersFn).not.toHaveBeenCalled();
await getViewSelector().vm.$emit('updateViewType', LAYER_VIEW);
@@ -269,5 +281,53 @@ describe('Pipeline graph wrapper', () => {
expect(layersFn).toHaveBeenCalledTimes(1);
});
});
+
+ describe('when feature flag is on and local storage is set', () => {
+ beforeEach(async () => {
+ localStorage.setItem(VIEW_TYPE_KEY, LAYER_VIEW);
+
+ createComponentWithApollo({
+ provide: {
+ glFeatures: {
+ pipelineGraphLayersView: true,
+ },
+ },
+ mountFn: mount,
+ });
+
+ jest.runOnlyPendingTimers();
+ await wrapper.vm.$nextTick();
+ });
+
+ it('reads the view type from localStorage when available', () => {
+ expect(wrapper.find('[data-testid="pipeline-view-selector"] code').text()).toContain(
+ 'needs:',
+ );
+ });
+ });
+
+ describe('when feature flag is on but pipeline does not use needs', () => {
+ beforeEach(async () => {
+ const nonNeedsResponse = { ...mockPipelineResponse };
+ nonNeedsResponse.data.project.pipeline.usesNeeds = false;
+
+ createComponentWithApollo({
+ provide: {
+ glFeatures: {
+ pipelineGraphLayersView: true,
+ },
+ },
+ mountFn: mount,
+ getPipelineDetailsHandler: jest.fn().mockResolvedValue(nonNeedsResponse),
+ });
+
+ jest.runOnlyPendingTimers();
+ await wrapper.vm.$nextTick();
+ });
+
+ it('does not appear when pipeline does not use needs', () => {
+ expect(getViewSelector().exists()).toBe(false);
+ });
+ });
});
});
diff --git a/spec/frontend/pipelines/graph/mock_data.js b/spec/frontend/pipelines/graph/mock_data.js
index e2d94055205..cf420f68f37 100644
--- a/spec/frontend/pipelines/graph/mock_data.js
+++ b/spec/frontend/pipelines/graph/mock_data.js
@@ -8,6 +8,7 @@ export const mockPipelineResponse = {
__typename: 'Pipeline',
id: 163,
iid: '22',
+ usesNeeds: true,
downstream: null,
upstream: null,
stages: {
@@ -569,6 +570,7 @@ export const wrappedPipelineReturn = {
__typename: 'Pipeline',
id: 'gid://gitlab/Ci::Pipeline/175',
iid: '38',
+ usesNeeds: true,
downstream: {
__typename: 'PipelineConnection',
nodes: [],
diff --git a/spec/frontend_integration/diffs/diffs_interopability_api.js b/spec/frontend_integration/diffs/diffs_interopability_api.js
new file mode 100644
index 00000000000..adfb93f27a2
--- /dev/null
+++ b/spec/frontend_integration/diffs/diffs_interopability_api.js
@@ -0,0 +1,25 @@
+/**
+ * This helper module contains the API expectation of the diff output HTML.
+ *
+ * This helps simulate what third-party HTML scrapers, such as Sourcegraph,
+ * should be looking for.
+ */
+export const getDiffCodePart = (codeElement) => {
+ const el = codeElement.closest('[data-interop-type]');
+
+ return el.dataset.interopType === 'old' ? 'base' : 'head';
+};
+
+export const getCodeElementFromLineNumber = (codeView, line, part) => {
+ const type = part === 'base' ? 'old' : 'new';
+
+ const el = codeView.querySelector(`[data-interop-${type}-line="${line}"]`);
+
+ return el ? el.querySelector('span.line') : null;
+};
+
+export const getLineNumberFromCodeElement = (codeElement) => {
+ const el = codeElement.closest('[data-interop-line]');
+
+ return parseInt(el.dataset.interopLine || '', 10);
+};
diff --git a/spec/frontend_integration/diffs/diffs_interopability_spec.js b/spec/frontend_integration/diffs/diffs_interopability_spec.js
new file mode 100644
index 00000000000..cb7659e16d3
--- /dev/null
+++ b/spec/frontend_integration/diffs/diffs_interopability_spec.js
@@ -0,0 +1,161 @@
+import { waitFor } from '@testing-library/dom';
+import { TEST_HOST } from 'helpers/test_constants';
+import initDiffsApp from '~/diffs';
+import { createStore } from '~/mr_notes/stores';
+import {
+ getDiffCodePart,
+ getLineNumberFromCodeElement,
+ getCodeElementFromLineNumber,
+} from './diffs_interopability_api';
+
+jest.mock('~/vue_shared/mixins/gl_feature_flags_mixin', () => () => ({
+ inject: {
+ glFeatures: {
+ from: 'window.gon.features',
+ default: () => global.window.gon?.features,
+ },
+ },
+}));
+
+const TEST_PROJECT_PATH = 'gitlab-org/gitlab-test';
+const TEST_BASE_URL = `/${TEST_PROJECT_PATH}/-/merge_requests/1/`;
+const TEST_DIFF_FILE = 'files/js/commit.coffee';
+const EXPECT_INLINE = [
+ ['head', 1],
+ ['head', 2],
+ ['head', 3],
+ ['base', 4],
+ ['head', 4],
+ null,
+ ['base', 6],
+ ['head', 6],
+ null,
+];
+const EXPECT_PARALLEL_LEFT_SIDE = [
+ ['base', 1],
+ ['base', 2],
+ ['base', 3],
+ ['base', 4],
+ null,
+ ['base', 6],
+ null,
+];
+const EXPECT_PARALLEL_RIGHT_SIDE = [
+ ['head', 1],
+ ['head', 2],
+ ['head', 3],
+ ['head', 4],
+ null,
+ ['head', 6],
+ null,
+];
+
+const startDiffsApp = () => {
+ const el = document.createElement('div');
+ el.id = 'js-diffs-app';
+ document.body.appendChild(el);
+ Object.assign(el.dataset, {
+ endpoint: TEST_BASE_URL,
+ endpointMetadata: `${TEST_BASE_URL}diffs_metadata.json`,
+ endpointBatch: `${TEST_BASE_URL}diffs_batch.json`,
+ projectPath: TEST_PROJECT_PATH,
+ helpPagePath: '/help',
+ currentUserData: 'null',
+ changesEmptyStateIllustration: '',
+ isFluidLayout: 'false',
+ dismissEndpoint: '',
+ showSuggestPopover: 'false',
+ showWhitespaceDefault: 'true',
+ viewDiffsFileByFile: 'false',
+ defaultSuggestionCommitMessage: 'Lorem ipsum',
+ });
+
+ const store = createStore();
+
+ const vm = initDiffsApp(store);
+
+ store.dispatch('setActiveTab', 'diffs');
+
+ return vm;
+};
+
+describe('diffs third party interoperability', () => {
+ let vm;
+
+ afterEach(() => {
+ vm.$destroy();
+ document.body.innerHTML = '';
+ });
+
+ const tryOrErrorMessage = (fn) => (...args) => {
+ try {
+ return fn(...args);
+ } catch (e) {
+ return e.message;
+ }
+ };
+
+ const findDiffFile = () => document.querySelector(`.diff-file[data-path="${TEST_DIFF_FILE}"]`);
+ const hasLines = (sel = 'tr.line_holder') => findDiffFile().querySelectorAll(sel).length > 0;
+ const findLineElements = (sel = 'tr.line_holder') =>
+ Array.from(findDiffFile().querySelectorAll(sel));
+
+ const findCodeElements = (lines, sel = 'td.line_content') => {
+ return lines.map((x) => x.querySelector(`${sel} span.line`));
+ };
+
+ const getCodeElementsInteropModel = (codeElements) =>
+ codeElements.map(
+ (x) =>
+ x && [
+ tryOrErrorMessage(getDiffCodePart)(x),
+ tryOrErrorMessage(getLineNumberFromCodeElement)(x),
+ ],
+ );
+
+ describe.each`
+ desc | unifiedDiffComponents | view | rowSelector | codeSelector | expectation
+ ${'inline view'} | ${false} | ${'inline'} | ${'tr.line_holder'} | ${'td.line_content'} | ${EXPECT_INLINE}
+ ${'parallel view left side'} | ${false} | ${'parallel'} | ${'tr.line_holder'} | ${'td.line_content.left-side'} | ${EXPECT_PARALLEL_LEFT_SIDE}
+ ${'parallel view right side'} | ${false} | ${'parallel'} | ${'tr.line_holder'} | ${'td.line_content.right-side'} | ${EXPECT_PARALLEL_RIGHT_SIDE}
+ ${'inline view'} | ${true} | ${'inline'} | ${'.diff-tr.line_holder'} | ${'.diff-td.line_content'} | ${EXPECT_INLINE}
+ ${'parallel view left side'} | ${true} | ${'parallel'} | ${'.diff-tr.line_holder'} | ${'.diff-td.line_content.left-side'} | ${EXPECT_PARALLEL_LEFT_SIDE}
+ ${'parallel view right side'} | ${true} | ${'parallel'} | ${'.diff-tr.line_holder'} | ${'.diff-td.line_content.right-side'} | ${EXPECT_PARALLEL_RIGHT_SIDE}
+ `(
+ '$desc (unifiedDiffComponents=$unifiedDiffComponents)',
+ ({ unifiedDiffComponents, view, rowSelector, codeSelector, expectation }) => {
+ beforeEach(async () => {
+ global.jsdom.reconfigure({
+ url: `${TEST_HOST}/${TEST_BASE_URL}/diffs?view=${view}`,
+ });
+ window.gon.features = { unifiedDiffComponents };
+
+ vm = startDiffsApp();
+
+ await waitFor(() => expect(hasLines(rowSelector)).toBe(true));
+ });
+
+ it('should match diff model', () => {
+ const lines = findLineElements(rowSelector);
+ const codes = findCodeElements(lines, codeSelector);
+
+ expect(getCodeElementsInteropModel(codes)).toEqual(expectation);
+ });
+
+ it.each`
+ lineNumber | part | expectedText
+ ${4} | ${'base'} | ${'new CommitFile(this)'}
+ ${4} | ${'head'} | ${'new CommitFile(@)'}
+ ${2} | ${'base'} | ${'constructor: ->'}
+ ${2} | ${'head'} | ${'constructor: ->'}
+ `(
+ 'should find code element lineNumber=$lineNumber part=$part',
+ ({ lineNumber, part, expectedText }) => {
+ const codeElement = getCodeElementFromLineNumber(findDiffFile(), lineNumber, part);
+
+ expect(codeElement.textContent.trim()).toBe(expectedText);
+ },
+ );
+ },
+ );
+});
diff --git a/spec/frontend_integration/test_helpers/fixtures.js b/spec/frontend_integration/test_helpers/fixtures.js
index b2768440607..5673e36197f 100644
--- a/spec/frontend_integration/test_helpers/fixtures.js
+++ b/spec/frontend_integration/test_helpers/fixtures.js
@@ -40,6 +40,12 @@ export const getMergeRequestVersions = factory.json(() =>
export const getRepositoryFiles = factory.json(() =>
require('test_fixtures/projects_json/files.json'),
);
+export const getDiffsMetadata = factory.json(() =>
+ require('test_fixtures/merge_request_diffs/diffs_metadata.json'),
+);
+export const getDiffsBatch = factory.json(() =>
+ require('test_fixtures/merge_request_diffs/diffs_batch.json'),
+);
export const getPipelinesEmptyResponse = factory.json(() =>
require('test_fixtures/projects_json/pipelines_empty.json'),
);
diff --git a/spec/frontend_integration/test_helpers/mock_server/routes/diffs.js b/spec/frontend_integration/test_helpers/mock_server/routes/diffs.js
new file mode 100644
index 00000000000..8301627e842
--- /dev/null
+++ b/spec/frontend_integration/test_helpers/mock_server/routes/diffs.js
@@ -0,0 +1,22 @@
+import { getDiffsMetadata, getDiffsBatch } from 'test_helpers/fixtures';
+import { withValues } from 'test_helpers/utils/obj';
+
+export default (server) => {
+ server.get('/:namespace/:project/-/merge_requests/:mrid/diffs_metadata.json', () => {
+ return getDiffsMetadata();
+ });
+
+ server.get('/:namespace/:project/-/merge_requests/:mrid/diffs_batch.json', () => {
+ const { pagination, ...result } = getDiffsBatch();
+
+ return {
+ ...result,
+ pagination: withValues(pagination, {
+ current_page: null,
+ next_page: null,
+ total_pages: 1,
+ next_page_href: null,
+ }),
+ };
+ });
+};
diff --git a/spec/frontend_integration/test_helpers/mock_server/routes/index.js b/spec/frontend_integration/test_helpers/mock_server/routes/index.js
index e30fecf2f06..48eff2702dd 100644
--- a/spec/frontend_integration/test_helpers/mock_server/routes/index.js
+++ b/spec/frontend_integration/test_helpers/mock_server/routes/index.js
@@ -5,6 +5,7 @@ export default (server) => {
require('./projects'),
require('./repository'),
require('./ci'),
+ require('./diffs'),
require('./404'),
].forEach(({ default: setup }) => {
setup(server);
diff --git a/spec/graphql/resolvers/board_list_issues_resolver_spec.rb b/spec/graphql/resolvers/board_list_issues_resolver_spec.rb
index 5eda840854a..6ffc8b045e9 100644
--- a/spec/graphql/resolvers/board_list_issues_resolver_spec.rb
+++ b/spec/graphql/resolvers/board_list_issues_resolver_spec.rb
@@ -39,6 +39,24 @@ RSpec.describe Resolvers::BoardListIssuesResolver do
expect(result).to match_array([issue1])
end
+
+ it 'raises an exception if both assignee_username and assignee_wildcard_id are present' do
+ expect do
+ resolve_board_list_issues(args: { filters: { assignee_username: ['username'], assignee_wildcard_id: 'NONE' } })
+ end.to raise_error(Gitlab::Graphql::Errors::ArgumentError)
+ end
+
+ it 'accepts assignee wildcard id NONE' do
+ result = resolve_board_list_issues(args: { filters: { assignee_wildcard_id: 'NONE' } })
+
+ expect(result).to match_array([issue1, issue2, issue3])
+ end
+
+ it 'accepts assignee wildcard id ANY' do
+ result = resolve_board_list_issues(args: { filters: { assignee_wildcard_id: 'ANY' } })
+
+ expect(result).to match_array([])
+ end
end
end
diff --git a/spec/graphql/types/boards/board_issue_input_type_spec.rb b/spec/graphql/types/boards/board_issue_input_type_spec.rb
index 6319ff9a88e..5d3efb9b40d 100644
--- a/spec/graphql/types/boards/board_issue_input_type_spec.rb
+++ b/spec/graphql/types/boards/board_issue_input_type_spec.rb
@@ -5,9 +5,9 @@ require 'spec_helper'
RSpec.describe GitlabSchema.types['BoardIssueInput'] do
it { expect(described_class.graphql_name).to eq('BoardIssueInput') }
- it 'exposes negated issue arguments' do
+ it 'has specific fields' do
allowed_args = %w(labelName milestoneTitle assigneeUsername authorUsername
- releaseTag myReactionEmoji not search)
+ releaseTag myReactionEmoji not search assigneeWildcardId)
expect(described_class.arguments.keys).to include(*allowed_args)
expect(described_class.arguments['not'].type).to eq(Types::Boards::NegatedBoardIssueInputType)
diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb
index 3ccf5ded9f5..ae039c1a8b1 100644
--- a/spec/helpers/application_helper_spec.rb
+++ b/spec/helpers/application_helper_spec.rb
@@ -168,11 +168,13 @@ RSpec.describe ApplicationHelper do
it { expect(helper.active_when(false)).to eq(nil) }
end
- describe '#promo_host' do
- subject { helper.promo_host }
+ unless Gitlab.jh?
+ describe '#promo_host' do
+ subject { helper.promo_host }
- it 'returns the url' do
- is_expected.to eq('about.gitlab.com')
+ it 'returns the url' do
+ is_expected.to eq('about.gitlab.com')
+ end
end
end
@@ -180,7 +182,7 @@ RSpec.describe ApplicationHelper do
subject { helper.promo_url }
it 'returns the url' do
- is_expected.to eq('https://about.gitlab.com')
+ is_expected.to eq("https://#{helper.promo_host}")
end
it 'changes if promo_host changes' do
@@ -194,7 +196,7 @@ RSpec.describe ApplicationHelper do
subject { helper.contact_sales_url }
it 'returns the url' do
- is_expected.to eq('https://about.gitlab.com/sales')
+ is_expected.to eq("https://#{helper.promo_host}/sales")
end
it 'changes if promo_url changes' do
diff --git a/spec/helpers/learn_gitlab_helper_spec.rb b/spec/helpers/learn_gitlab_helper_spec.rb
index 6cee8a9191c..82c8e4ba596 100644
--- a/spec/helpers/learn_gitlab_helper_spec.rb
+++ b/spec/helpers/learn_gitlab_helper_spec.rb
@@ -27,6 +27,7 @@ RSpec.describe LearnGitlabHelper do
it 'has all actions' do
expect(onboarding_actions_data.keys).to contain_exactly(
+ :issue_created,
:git_write,
:pipeline_created,
:merge_request_created,
diff --git a/spec/lib/gitlab/gitaly_client/object_pool_service_spec.rb b/spec/lib/gitlab/gitaly_client/object_pool_service_spec.rb
index 15eebf62a39..9c3bc935acc 100644
--- a/spec/lib/gitlab/gitaly_client/object_pool_service_spec.rb
+++ b/spec/lib/gitlab/gitaly_client/object_pool_service_spec.rb
@@ -11,7 +11,7 @@ RSpec.describe Gitlab::GitalyClient::ObjectPoolService do
subject { described_class.new(object_pool) }
before do
- subject.create(raw_repository)
+ subject.create(raw_repository) # rubocop:disable Rails/SaveBang
end
describe '#create' do
@@ -22,7 +22,7 @@ RSpec.describe Gitlab::GitalyClient::ObjectPoolService do
context 'when the pool already exists' do
it 'returns an error' do
expect do
- subject.create(raw_repository)
+ subject.create(raw_repository) # rubocop:disable Rails/SaveBang
end.to raise_error(GRPC::FailedPrecondition)
end
end
diff --git a/spec/lib/gitlab/gitaly_client/repository_service_spec.rb b/spec/lib/gitlab/gitaly_client/repository_service_spec.rb
index 5954e199f3c..26ec194a2e7 100644
--- a/spec/lib/gitlab/gitaly_client/repository_service_spec.rb
+++ b/spec/lib/gitlab/gitaly_client/repository_service_spec.rb
@@ -270,7 +270,7 @@ RSpec.describe Gitlab::GitalyClient::RepositoryService do
let(:object_pool_service) { Gitlab::GitalyClient::ObjectPoolService.new(object_pool) }
before do
- object_pool_service.create(repository)
+ object_pool_service.create(repository) # rubocop:disable Rails/SaveBang
object_pool_service.link_repository(repository)
end
diff --git a/spec/lib/gitlab/subscription_portal_spec.rb b/spec/lib/gitlab/subscription_portal_spec.rb
index 351af3c07d2..ad1affdac0b 100644
--- a/spec/lib/gitlab/subscription_portal_spec.rb
+++ b/spec/lib/gitlab/subscription_portal_spec.rb
@@ -3,39 +3,41 @@
require 'spec_helper'
RSpec.describe ::Gitlab::SubscriptionPortal do
- describe '.default_subscriptions_url' do
- subject { described_class.default_subscriptions_url }
-
- context 'on non test and non dev environments' do
- before do
- allow(Rails).to receive_message_chain(:env, :test?).and_return(false)
- allow(Rails).to receive_message_chain(:env, :development?).and_return(false)
+ unless Gitlab.jh?
+ describe '.default_subscriptions_url' do
+ subject { described_class.default_subscriptions_url }
+
+ context 'on non test and non dev environments' do
+ before do
+ allow(Rails).to receive_message_chain(:env, :test?).and_return(false)
+ allow(Rails).to receive_message_chain(:env, :development?).and_return(false)
+ end
+
+ it 'returns production subscriptions app URL' do
+ is_expected.to eq('https://customers.gitlab.com')
+ end
end
- it 'returns production subscriptions app URL' do
- is_expected.to eq('https://customers.gitlab.com')
- end
- end
+ context 'on dev environment' do
+ before do
+ allow(Rails).to receive_message_chain(:env, :test?).and_return(false)
+ allow(Rails).to receive_message_chain(:env, :development?).and_return(true)
+ end
- context 'on dev environment' do
- before do
- allow(Rails).to receive_message_chain(:env, :test?).and_return(false)
- allow(Rails).to receive_message_chain(:env, :development?).and_return(true)
+ it 'returns staging subscriptions app url' do
+ is_expected.to eq('https://customers.stg.gitlab.com')
+ end
end
- it 'returns staging subscriptions app url' do
- is_expected.to eq('https://customers.stg.gitlab.com')
- end
- end
-
- context 'on test environment' do
- before do
- allow(Rails).to receive_message_chain(:env, :test?).and_return(true)
- allow(Rails).to receive_message_chain(:env, :development?).and_return(false)
- end
+ context 'on test environment' do
+ before do
+ allow(Rails).to receive_message_chain(:env, :test?).and_return(true)
+ allow(Rails).to receive_message_chain(:env, :development?).and_return(false)
+ end
- it 'returns staging subscriptions app url' do
- is_expected.to eq('https://customers.stg.gitlab.com')
+ it 'returns staging subscriptions app url' do
+ is_expected.to eq('https://customers.stg.gitlab.com')
+ end
end
end
end
diff --git a/spec/lib/gitlab_spec.rb b/spec/lib/gitlab_spec.rb
index c5738ae730f..4df00eaa439 100644
--- a/spec/lib/gitlab_spec.rb
+++ b/spec/lib/gitlab_spec.rb
@@ -247,75 +247,117 @@ RSpec.describe Gitlab do
end
end
- describe '.ee?' do
+ describe 'ee? and jh?' do
before do
- stub_env('FOSS_ONLY', nil) # Make sure the ENV is clean
+ # Make sure the ENV is clean
+ stub_env('FOSS_ONLY', nil)
+ stub_env('EE_ONLY', nil)
+
described_class.instance_variable_set(:@is_ee, nil)
+ described_class.instance_variable_set(:@is_jh, nil)
end
after do
described_class.instance_variable_set(:@is_ee, nil)
+ described_class.instance_variable_set(:@is_jh, nil)
end
- context 'for EE' do
- before do
- root = Pathname.new('dummy')
- license_path = double(:path, exist?: true)
+ def stub_path(*paths, **arguments)
+ root = Pathname.new('dummy')
+ pathname = double(:path, **arguments)
- allow(described_class)
- .to receive(:root)
- .and_return(root)
+ allow(described_class)
+ .to receive(:root)
+ .and_return(root)
+ allow(root).to receive(:join)
+
+ paths.each do |path|
allow(root)
.to receive(:join)
- .with('ee/app/models/license.rb')
- .and_return(license_path)
+ .with(path)
+ .and_return(pathname)
end
+ end
- context 'when using FOSS_ONLY=1' do
+ describe '.ee?' do
+ context 'for EE' do
before do
- stub_env('FOSS_ONLY', '1')
+ stub_path('ee/app/models/license.rb', exist?: true)
end
- it 'returns not to be EE' do
- expect(described_class).not_to be_ee
+ context 'when using FOSS_ONLY=1' do
+ before do
+ stub_env('FOSS_ONLY', '1')
+ end
+
+ it 'returns not to be EE' do
+ expect(described_class).not_to be_ee
+ end
end
- end
- context 'when using FOSS_ONLY=0' do
- before do
- stub_env('FOSS_ONLY', '0')
+ context 'when using FOSS_ONLY=0' do
+ before do
+ stub_env('FOSS_ONLY', '0')
+ end
+
+ it 'returns to be EE' do
+ expect(described_class).to be_ee
+ end
end
- it 'returns to be EE' do
- expect(described_class).to be_ee
+ context 'when using default FOSS_ONLY' do
+ it 'returns to be EE' do
+ expect(described_class).to be_ee
+ end
end
end
- context 'when using default FOSS_ONLY' do
- it 'returns to be EE' do
- expect(described_class).to be_ee
+ context 'for CE' do
+ before do
+ stub_path('ee/app/models/license.rb', exist?: false)
+ end
+
+ it 'returns not to be EE' do
+ expect(described_class).not_to be_ee
end
end
end
- context 'for CE' do
- before do
- root = double(:path)
- license_path = double(:path, exists?: false)
+ describe '.jh?' do
+ context 'for JH' do
+ before do
+ stub_path(
+ 'ee/app/models/license.rb',
+ 'jh',
+ exist?: true)
+ end
- allow(described_class)
- .to receive(:root)
- .and_return(Pathname.new('dummy'))
+ context 'when using default FOSS_ONLY and EE_ONLY' do
+ it 'returns to be JH' do
+ expect(described_class).to be_jh
+ end
+ end
- allow(root)
- .to receive(:join)
- .with('ee/app/models/license.rb')
- .and_return(license_path)
- end
+ context 'when using FOSS_ONLY=1' do
+ before do
+ stub_env('FOSS_ONLY', '1')
+ end
+
+ it 'returns not to be JH' do
+ expect(described_class).not_to be_jh
+ end
+ end
+
+ context 'when using EE_ONLY=1' do
+ before do
+ stub_env('EE_ONLY', '1')
+ end
- it 'returns not to be EE' do
- expect(described_class).not_to be_ee
+ it 'returns not to be JH' do
+ expect(described_class).not_to be_jh
+ end
+ end
end
end
end
diff --git a/spec/migrations/20210413132500_reschedule_artifact_expiry_backfill_again_spec.rb b/spec/migrations/20210413132500_reschedule_artifact_expiry_backfill_again_spec.rb
new file mode 100644
index 00000000000..4f36a95f9cf
--- /dev/null
+++ b/spec/migrations/20210413132500_reschedule_artifact_expiry_backfill_again_spec.rb
@@ -0,0 +1,38 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+require Rails.root.join('db', 'post_migrate', '20210413132500_reschedule_artifact_expiry_backfill_again.rb')
+
+RSpec.describe RescheduleArtifactExpiryBackfillAgain, :migration do
+ let(:migration_class) { Gitlab::BackgroundMigration::BackfillArtifactExpiryDate }
+ let(:migration_name) { migration_class.to_s.demodulize }
+
+ before do
+ table(:namespaces).create!(id: 123, name: 'test_namespace', path: 'test_namespace')
+ table(:projects).create!(id: 123, name: 'sample_project', path: 'sample_project', namespace_id: 123)
+ end
+
+ it 'correctly schedules background migrations' do
+ first_artifact = create_artifact(job_id: 0, expire_at: nil, created_at: Date.new(2020, 06, 21))
+ second_artifact = create_artifact(job_id: 1, expire_at: nil, created_at: Date.new(2020, 06, 21))
+ create_artifact(job_id: 2, expire_at: Date.yesterday, created_at: Date.new(2020, 06, 21))
+ create_artifact(job_id: 3, expire_at: nil, created_at: Date.new(2020, 06, 23))
+
+ Sidekiq::Testing.fake! do
+ freeze_time do
+ migrate!
+
+ expect(BackgroundMigrationWorker.jobs.size).to eq(1)
+ expect(migration_name).to be_scheduled_migration_with_multiple_args(first_artifact.id, second_artifact.id)
+ end
+ end
+ end
+
+ private
+
+ def create_artifact(params)
+ table(:ci_builds).create!(id: params[:job_id], project_id: 123)
+ table(:ci_job_artifacts).create!(project_id: 123, file_type: 1, **params)
+ end
+end
diff --git a/spec/models/notification_setting_spec.rb b/spec/models/notification_setting_spec.rb
index 4ef5ab7af48..010b7455f85 100644
--- a/spec/models/notification_setting_spec.rb
+++ b/spec/models/notification_setting_spec.rb
@@ -200,4 +200,18 @@ RSpec.describe NotificationSetting do
subject.email_events
end
end
+
+ describe '#order_by_id_asc' do
+ let_it_be(:project) { create(:project) }
+ let_it_be(:other_project) { create(:project) }
+ let_it_be(:notification_setting_1) { create(:notification_setting, project: project) }
+ let_it_be(:notification_setting_2) { create(:notification_setting, project: other_project) }
+ let_it_be(:notification_setting_3) { create(:notification_setting, project: project) }
+
+ let(:ids) { [notification_setting_1, notification_setting_2, notification_setting_3].map(&:id) }
+
+ subject(:ordered_records) { described_class.where(id: ids, source: project).order_by_id_asc }
+
+ it { is_expected.to eq([notification_setting_1, notification_setting_3]) }
+ end
end
diff --git a/spec/models/preloaders/user_max_access_level_in_projects_preloader_spec.rb b/spec/models/preloaders/user_max_access_level_in_projects_preloader_spec.rb
new file mode 100644
index 00000000000..16e699b7e0e
--- /dev/null
+++ b/spec/models/preloaders/user_max_access_level_in_projects_preloader_spec.rb
@@ -0,0 +1,37 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Preloaders::UserMaxAccessLevelInProjectsPreloader do
+ let_it_be(:user) { create(:user) }
+ let_it_be(:project_1) { create(:project) }
+ let_it_be(:project_2) { create(:project) }
+ let_it_be(:project_3) { create(:project) }
+
+ let(:projects) { [project_1, project_2, project_3] }
+
+ before do
+ project_1.add_developer(user)
+ project_2.add_developer(user)
+ end
+
+ context 'preload maximum access level to avoid querying project_authorizations', :request_store do
+ it 'avoids N+1 queries', :request_store do
+ Preloaders::UserMaxAccessLevelInProjectsPreloader.new(projects, user).execute
+
+ query_count = ActiveRecord::QueryRecorder.new do
+ projects.each { |project| user.can?(:read_project, project) }
+ end.count
+
+ expect(query_count).to eq(0)
+ end
+
+ it 'runs N queries without preloading' do
+ query_count = ActiveRecord::QueryRecorder.new do
+ projects.each { |project| user.can?(:read_project, project) }
+ end.count
+
+ expect(query_count).to eq(projects.size)
+ end
+ end
+end
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index 098c88147c3..55d049b9d47 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -5225,57 +5225,27 @@ RSpec.describe Project, factory_default: :keep do
end
describe '#default_branch' do
- context 'with an empty repository' do
- let_it_be(:project) { create(:project_empty_repo) }
+ context 'with default_branch_name' do
+ let_it_be_with_refind(:root_group) { create(:group) }
+ let_it_be_with_refind(:project_group) { create(:group, parent: root_group) }
+ let_it_be_with_refind(:project) { create(:project, path: 'avatar', namespace: project_group) }
- context 'group.default_branch_name is available' do
- let(:project_group) { create(:group) }
- let(:project) { create(:project, path: 'avatar', namespace: project_group) }
-
- before do
- expect(Gitlab::CurrentSettings)
- .not_to receive(:default_branch_name)
-
- expect(project.group)
- .to receive(:default_branch_name)
- .and_return('example_branch')
- end
-
- it 'returns the group default value' do
- expect(project.default_branch).to eq('example_branch')
- end
+ where(:instance_branch, :root_group_branch, :project_group_branch, :project_branch) do
+ '' | nil | nil | nil
+ nil | nil | nil | nil
+ 'main' | nil | nil | 'main'
+ 'main' | 'root_branch' | nil | 'root_branch'
+ 'main' | 'root_branch' | 'group_branch' | 'group_branch'
end
- context 'Gitlab::CurrentSettings.default_branch_name is available' do
+ with_them do
before do
- expect(Gitlab::CurrentSettings)
- .to receive(:default_branch_name)
- .and_return(example_branch_name)
- end
-
- context 'is missing or nil' do
- let(:example_branch_name) { nil }
-
- it "returns nil" do
- expect(project.default_branch).to be_nil
- end
+ allow(Gitlab::CurrentSettings).to receive(:default_branch_name).and_return(instance_branch)
+ root_group.namespace_settings.update!(default_branch_name: root_group_branch)
+ project_group.namespace_settings.update!(default_branch_name: project_group_branch)
end
- context 'is blank' do
- let(:example_branch_name) { '' }
-
- it 'returns nil' do
- expect(project.default_branch).to be_nil
- end
- end
-
- context 'is present' do
- let(:example_branch_name) { 'example_branch_name' }
-
- it 'returns the expected branch name' do
- expect(project.default_branch).to eq(example_branch_name)
- end
- end
+ it { expect(project.default_branch).to eq(project_branch) }
end
end
end
diff --git a/spec/services/boards/lists/update_service_spec.rb b/spec/services/boards/lists/update_service_spec.rb
index cdc7784469a..10fed9b7aac 100644
--- a/spec/services/boards/lists/update_service_spec.rb
+++ b/spec/services/boards/lists/update_service_spec.rb
@@ -6,47 +6,6 @@ RSpec.describe Boards::Lists::UpdateService do
let(:user) { create(:user) }
let!(:list) { create(:list, board: board, position: 0) }
- shared_examples 'moving list' do
- context 'when user can admin list' do
- it 'calls Lists::MoveService to update list position' do
- board.resource_parent.add_developer(user)
-
- expect(Boards::Lists::MoveService).to receive(:new).with(board.resource_parent, user, params).and_call_original
- expect_any_instance_of(Boards::Lists::MoveService).to receive(:execute).with(list)
-
- service.execute(list)
- end
- end
-
- context 'when user cannot admin list' do
- it 'does not call Lists::MoveService to update list position' do
- expect(Boards::Lists::MoveService).not_to receive(:new)
-
- service.execute(list)
- end
- end
- end
-
- shared_examples 'updating list preferences' do
- context 'when user can read list' do
- it 'updates list preference for user' do
- board.resource_parent.add_guest(user)
-
- service.execute(list)
-
- expect(list.preferences_for(user).collapsed).to eq(true)
- end
- end
-
- context 'when user cannot read list' do
- it 'does not update list preference for user' do
- service.execute(list)
-
- expect(list.preferences_for(user).collapsed).to be_nil
- end
- end
- end
-
describe '#execute' do
let(:service) { described_class.new(board.resource_parent, user, params) }
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 487c7bb10e2..2607f2e8e21 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -55,6 +55,7 @@ require 'rainbow/ext/string'
Rainbow.enabled = false
require_relative('../ee/spec/spec_helper') if Gitlab.ee?
+require_relative('../jh/spec/spec_helper') if Gitlab.jh?
# Requires supporting ruby files with custom matchers and macros, etc,
# in spec/support/ and its subdirectories.
diff --git a/spec/support/shared_examples/boards/lists/update_service_shared_examples.rb b/spec/support/shared_examples/boards/lists/update_service_shared_examples.rb
new file mode 100644
index 00000000000..d8a74f2582d
--- /dev/null
+++ b/spec/support/shared_examples/boards/lists/update_service_shared_examples.rb
@@ -0,0 +1,43 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'moving list' do
+ context 'when user can admin list' do
+ it 'calls Lists::MoveService to update list position' do
+ board.resource_parent.add_developer(user)
+
+ expect_next_instance_of(Boards::Lists::MoveService, board.resource_parent, user, params) do |move_service|
+ expect(move_service).to receive(:execute).with(list).and_call_original
+ end
+
+ service.execute(list)
+ end
+ end
+
+ context 'when user cannot admin list' do
+ it 'does not call Lists::MoveService to update list position' do
+ expect(Boards::Lists::MoveService).not_to receive(:new)
+
+ service.execute(list)
+ end
+ end
+end
+
+RSpec.shared_examples 'updating list preferences' do
+ context 'when user can read list' do
+ it 'updates list preference for user' do
+ board.resource_parent.add_guest(user)
+
+ service.execute(list)
+
+ expect(list.preferences_for(user).collapsed).to eq(true)
+ end
+ end
+
+ context 'when user cannot read list' do
+ it 'does not update list preference for user' do
+ service.execute(list)
+
+ expect(list.preferences_for(user).collapsed).to be_falsy
+ end
+ end
+end
diff --git a/yarn.lock b/yarn.lock
index d704d66184c..9a9fc0f692e 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -907,10 +907,10 @@
resolved "https://registry.yarnpkg.com/@gitlab/tributejs/-/tributejs-1.0.0.tgz#672befa222aeffc83e7d799b0500a7a4418e59b8"
integrity sha512-nmKw1+hB6MHvlmPz63yPwVs1qQkycHwsKgxpEbzmky16Y6mL4EJMk3w1b8QlOAF/AIAzjCERPhe/R4MJiohbZw==
-"@gitlab/ui@29.3.0":
- version "29.3.0"
- resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-29.3.0.tgz#e549b73341246bb9cd1005b2f4c10a599c680cb1"
- integrity sha512-TWc3O3w7L+aCLC7Vp2JbYTFgCwseLExxjhwDfJuc2Iwkr+1k8k1ygctbid9XRX2Jcy3JPbL+o+m0K/ZXMJTWdg==
+"@gitlab/ui@29.4.0":
+ version "29.4.0"
+ resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-29.4.0.tgz#601b13700c5af4eeb6ca60fe72e59a2ee1b3d19a"
+ integrity sha512-GdV1DIP0Oq/abzlh93FPpJX/kb1Swk81znXEYpzhoL7vrg4Lotbkhlz+oaNMZF/F/YOZ8QUCKTcs2Q7HGgkKmQ==
dependencies:
"@babel/standalone" "^7.0.0"
"@gitlab/vue-toasted" "^1.3.0"
@@ -12429,10 +12429,10 @@ webidl-conversions@^6.1.0:
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-6.1.0.tgz#9111b4d7ea80acd40f5270d666621afa78b69514"
integrity sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==
-webpack-bundle-analyzer@^4.4.0:
- version "4.4.0"
- resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.4.0.tgz#74013106e7e2b07cbd64f3a5ae847f7e814802c7"
- integrity sha512-9DhNa+aXpqdHk8LkLPTBU/dMfl84Y+WE2+KnfI6rSpNRNVKa0VGLjPd2pjFubDeqnWmulFggxmWBxhfJXZnR0g==
+webpack-bundle-analyzer@^4.4.1:
+ version "4.4.1"
+ resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.4.1.tgz#c71fb2eaffc10a4754d7303b224adb2342069da1"
+ integrity sha512-j5m7WgytCkiVBoOGavzNokBOqxe6Mma13X1asfVYtKWM3wxBiRRu1u1iG0Iol5+qp9WgyhkMmBAcvjEfJ2bdDw==
dependencies:
acorn "^8.0.4"
acorn-walk "^8.0.0"