diff --git a/.gitlab/ci/rules.gitlab-ci.yml b/.gitlab/ci/rules.gitlab-ci.yml
index 93913e204be..d16dd28f597 100644
--- a/.gitlab/ci/rules.gitlab-ci.yml
+++ b/.gitlab/ci/rules.gitlab-ci.yml
@@ -602,6 +602,8 @@
when: never
- <<: *if-merge-request-targeting-stable-branch
when: never
+ - <<: *if-merge-request-labels-pipeline-expedite
+ when: never
.rails:rules:predictive-default-rules:
rules:
diff --git a/.rubocop_todo/gitlab/doc_url.yml b/.rubocop_todo/gitlab/doc_url.yml
index 9c781ca64e9..9935af05555 100644
--- a/.rubocop_todo/gitlab/doc_url.yml
+++ b/.rubocop_todo/gitlab/doc_url.yml
@@ -7,7 +7,7 @@ Gitlab/DocUrl:
- 'app/graphql/types/merge_request_type.rb'
- 'app/graphql/types/notes/diff_position_input_type.rb'
- 'app/graphql/types/query_complexity_type.rb'
- - 'app/helpers/learn_gitlab_helper.rb'
+ - 'ee/app/helpers/projects/learn_gitlab_helper.rb'
- 'app/models/integrations/apple_app_store.rb'
- 'app/models/integrations/microsoft_teams.rb'
- 'app/presenters/dev_ops_report/metric_presenter.rb'
diff --git a/.rubocop_todo/gitlab/strong_memoize_attr.yml b/.rubocop_todo/gitlab/strong_memoize_attr.yml
index 066f17aabe0..e0a243aec8e 100644
--- a/.rubocop_todo/gitlab/strong_memoize_attr.yml
+++ b/.rubocop_todo/gitlab/strong_memoize_attr.yml
@@ -726,6 +726,5 @@ Gitlab/StrongMemoizeAttr:
- 'lib/sidebars/groups/menus/merge_requests_menu.rb'
- 'lib/sidebars/projects/menus/analytics_menu.rb'
- 'lib/sidebars/projects/menus/issues_menu.rb'
- - 'lib/sidebars/projects/menus/learn_gitlab_menu.rb'
- 'lib/unnested_in_filters/rewriter.rb'
- 'tooling/graphql/docs/helper.rb'
diff --git a/.rubocop_todo/layout/first_hash_element_indentation.yml b/.rubocop_todo/layout/first_hash_element_indentation.yml
index facc7a58f1a..ef0e5fbe040 100644
--- a/.rubocop_todo/layout/first_hash_element_indentation.yml
+++ b/.rubocop_todo/layout/first_hash_element_indentation.yml
@@ -194,7 +194,6 @@ Layout/FirstHashElementIndentation:
- 'spec/frontend/fixtures/autocomplete_sources.rb'
- 'spec/graphql/types/ci/detailed_status_type_spec.rb'
- 'spec/helpers/groups/observability_helper_spec.rb'
- - 'spec/helpers/learn_gitlab_helper_spec.rb'
- 'spec/helpers/projects/pages_helper_spec.rb'
- 'spec/helpers/routing/pseudonymization_helper_spec.rb'
- 'spec/initializers/rack_multipart_patch_spec.rb'
diff --git a/.rubocop_todo/layout/line_length.yml b/.rubocop_todo/layout/line_length.yml
index 5e3b05e064d..b8ef2820dad 100644
--- a/.rubocop_todo/layout/line_length.yml
+++ b/.rubocop_todo/layout/line_length.yml
@@ -4041,7 +4041,6 @@ Layout/LineLength:
- 'spec/helpers/groups/group_members_helper_spec.rb'
- 'spec/helpers/groups_helper_spec.rb'
- 'spec/helpers/icons_helper_spec.rb'
- - 'spec/helpers/invite_members_helper_spec.rb'
- 'spec/helpers/issuables_helper_spec.rb'
- 'spec/helpers/issues_helper_spec.rb'
- 'spec/helpers/labels_helper_spec.rb'
diff --git a/.rubocop_todo/rspec/context_wording.yml b/.rubocop_todo/rspec/context_wording.yml
index b55e290f95a..e710e590a15 100644
--- a/.rubocop_todo/rspec/context_wording.yml
+++ b/.rubocop_todo/rspec/context_wording.yml
@@ -1525,7 +1525,6 @@ RSpec/ContextWording:
- 'spec/helpers/groups_helper_spec.rb'
- 'spec/helpers/ide_helper_spec.rb'
- 'spec/helpers/integrations_helper_spec.rb'
- - 'spec/helpers/invite_members_helper_spec.rb'
- 'spec/helpers/jira_connect_helper_spec.rb'
- 'spec/helpers/labels_helper_spec.rb'
- 'spec/helpers/listbox_helper_spec.rb'
diff --git a/.rubocop_todo/rspec/expect_in_hook.yml b/.rubocop_todo/rspec/expect_in_hook.yml
index d25c4137c1a..ae886e6e85e 100644
--- a/.rubocop_todo/rspec/expect_in_hook.yml
+++ b/.rubocop_todo/rspec/expect_in_hook.yml
@@ -163,7 +163,6 @@ RSpec/ExpectInHook:
- 'spec/graphql/mutations/design_management/move_spec.rb'
- 'spec/helpers/commits_helper_spec.rb'
- 'spec/helpers/groups_helper_spec.rb'
- - 'spec/helpers/invite_members_helper_spec.rb'
- 'spec/helpers/projects_helper_spec.rb'
- 'spec/helpers/search_helper_spec.rb'
- 'spec/helpers/users_helper_spec.rb'
diff --git a/.rubocop_todo/rspec/factory_bot/avoid_create.yml b/.rubocop_todo/rspec/factory_bot/avoid_create.yml
index 8fb2327f049..a857841f17a 100644
--- a/.rubocop_todo/rspec/factory_bot/avoid_create.yml
+++ b/.rubocop_todo/rspec/factory_bot/avoid_create.yml
@@ -36,7 +36,7 @@ RSpec/FactoryBot/AvoidCreate:
- 'ee/spec/helpers/ee/issuables_helper_spec.rb'
- 'ee/spec/helpers/ee/issues_helper_spec.rb'
- 'ee/spec/helpers/ee/labels_helper_spec.rb'
- - 'ee/spec/helpers/ee/learn_gitlab_helper_spec.rb'
+ - 'ee/spec/helpers/projects/learn_gitlab_helper_spec.rb'
- 'ee/spec/helpers/ee/lock_helper_spec.rb'
- 'ee/spec/helpers/ee/namespace_user_cap_reached_alert_helper_spec.rb'
- 'ee/spec/helpers/ee/namespaces_helper_spec.rb'
@@ -305,7 +305,6 @@ RSpec/FactoryBot/AvoidCreate:
- 'spec/helpers/keyset_helper_spec.rb'
- 'spec/helpers/labels_helper_spec.rb'
- 'spec/helpers/lazy_image_tag_helper_spec.rb'
- - 'spec/helpers/learn_gitlab_helper_spec.rb'
- 'spec/helpers/markup_helper_spec.rb'
- 'spec/helpers/members_helper_spec.rb'
- 'spec/helpers/merge_requests_helper_spec.rb'
diff --git a/.rubocop_todo/rspec/missing_feature_category.yml b/.rubocop_todo/rspec/missing_feature_category.yml
index 018e98d123d..8a9367ec2e7 100644
--- a/.rubocop_todo/rspec/missing_feature_category.yml
+++ b/.rubocop_todo/rspec/missing_feature_category.yml
@@ -584,7 +584,6 @@ RSpec/MissingFeatureCategory:
- 'ee/spec/helpers/ee/invite_members_helper_spec.rb'
- 'ee/spec/helpers/ee/issues_helper_spec.rb'
- 'ee/spec/helpers/ee/labels_helper_spec.rb'
- - 'ee/spec/helpers/ee/learn_gitlab_helper_spec.rb'
- 'ee/spec/helpers/ee/lock_helper_spec.rb'
- 'ee/spec/helpers/ee/namespace_user_cap_reached_alert_helper_spec.rb'
- 'ee/spec/helpers/ee/namespaces_helper_spec.rb'
@@ -2583,7 +2582,6 @@ RSpec/MissingFeatureCategory:
- 'ee/spec/workers/merge_trains/refresh_worker_spec.rb'
- 'ee/spec/workers/namespaces/sync_namespace_name_worker_spec.rb'
- 'ee/spec/workers/new_epic_worker_spec.rb'
- - 'ee/spec/workers/onboarding/create_learn_gitlab_worker_spec.rb'
- 'ee/spec/workers/personal_access_tokens/groups/policy_worker_spec.rb'
- 'ee/spec/workers/personal_access_tokens/instance/policy_worker_spec.rb'
- 'ee/spec/workers/post_receive_spec.rb'
@@ -6051,7 +6049,6 @@ RSpec/MissingFeatureCategory:
- 'spec/lib/sidebars/projects/menus/infrastructure_menu_spec.rb'
- 'spec/lib/sidebars/projects/menus/invite_team_members_menu_spec.rb'
- 'spec/lib/sidebars/projects/menus/issues_menu_spec.rb'
- - 'spec/lib/sidebars/projects/menus/learn_gitlab_menu_spec.rb'
- 'spec/lib/sidebars/projects/menus/merge_requests_menu_spec.rb'
- 'spec/lib/sidebars/projects/menus/monitor_menu_spec.rb'
- 'spec/lib/sidebars/projects/menus/packages_registries_menu_spec.rb'
@@ -6555,7 +6552,6 @@ RSpec/MissingFeatureCategory:
- 'spec/models/oauth_access_grant_spec.rb'
- 'spec/models/oauth_access_token_spec.rb'
- 'spec/models/onboarding/completion_spec.rb'
- - 'spec/models/onboarding/learn_gitlab_spec.rb'
- 'spec/models/onboarding/progress_spec.rb'
- 'spec/models/operations/feature_flag_spec.rb'
- 'spec/models/operations/feature_flags/strategy_spec.rb'
diff --git a/.rubocop_todo/style/if_unless_modifier.yml b/.rubocop_todo/style/if_unless_modifier.yml
index 39f4094d40f..3794232db31 100644
--- a/.rubocop_todo/style/if_unless_modifier.yml
+++ b/.rubocop_todo/style/if_unless_modifier.yml
@@ -1065,7 +1065,6 @@ Style/IfUnlessModifier:
- 'spec/features/projects/tree/create_file_spec.rb'
- 'spec/graphql/mutations/releases/update_spec.rb'
- 'spec/helpers/application_settings_helper_spec.rb'
- - 'spec/helpers/invite_members_helper_spec.rb'
- 'spec/lib/container_registry/gitlab_api_client_spec.rb'
- 'spec/lib/gitlab/background_migration/disable_expiration_policies_linked_to_no_container_images_spec.rb'
- 'spec/lib/gitlab/config/entry/validators/nested_array_helpers_spec.rb'
diff --git a/Gemfile b/Gemfile
index 96cf23256e0..4bbee264aa4 100644
--- a/Gemfile
+++ b/Gemfile
@@ -504,7 +504,7 @@ gem 'ssh_data', '~> 1.3'
gem 'spamcheck', '~> 1.0.0'
# Gitaly GRPC protocol definitions
-gem 'gitaly', '~> 15.5.2'
+gem 'gitaly', '~> 15.8.0-rc1'
# KAS GRPC protocol definitions
gem 'kas-grpc', '~> 0.0.2'
diff --git a/Gemfile.checksum b/Gemfile.checksum
index d8d62b87cae..cfc3af02693 100644
--- a/Gemfile.checksum
+++ b/Gemfile.checksum
@@ -199,7 +199,7 @@
{"name":"gettext_i18n_rails","version":"1.8.0","platform":"ruby","checksum":"95e5cf8440b1e08705b27f2bccb56143272c5a7a0dabcf54ea1bd701140a496f"},
{"name":"gettext_i18n_rails_js","version":"1.3.0","platform":"ruby","checksum":"5d10afe4be3639bff78c50a56768c20f39aecdabc580c08aa45573911c2bd687"},
{"name":"git","version":"1.11.0","platform":"ruby","checksum":"7e95ba4da8298a0373ef1a6862aa22007d761f3c8274b675aa787966fecea0f1"},
-{"name":"gitaly","version":"15.5.2","platform":"ruby","checksum":"62babe0596a4505bf95051ea50f17160055e6cf6cacf209273691542120d7881"},
+{"name":"gitaly","version":"15.8.0.pre.rc1","platform":"ruby","checksum":"9244245b602c6c903eb0e3b3629b51e888af179cbbe339269095a1ab9113dbb5"},
{"name":"gitlab","version":"4.19.0","platform":"ruby","checksum":"3f645e3e195dbc24f0834fbf83e8ccfb2056d8e9712b01a640aad418a6949679"},
{"name":"gitlab-chronic","version":"0.10.5","platform":"ruby","checksum":"f80f18dc699b708870a80685243331290bc10cfeedb6b99c92219722f729c875"},
{"name":"gitlab-dangerfiles","version":"3.6.6","platform":"ruby","checksum":"cabfe23490120188a653c827a32121bdd4abf4e9e91d1754bf170dd7e93781f1"},
diff --git a/Gemfile.lock b/Gemfile.lock
index 93851d2287c..a7bdfb7c00d 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -566,7 +566,7 @@ GEM
rails (>= 3.2.0)
git (1.11.0)
rchardet (~> 1.8)
- gitaly (15.5.2)
+ gitaly (15.8.0.pre.rc1)
grpc (~> 1.0)
gitlab (4.19.0)
httparty (~> 0.20)
@@ -1667,7 +1667,7 @@ DEPENDENCIES
gettext (~> 3.3)
gettext_i18n_rails (~> 1.8.0)
gettext_i18n_rails_js (~> 1.3)
- gitaly (~> 15.5.2)
+ gitaly (~> 15.8.0.pre.rc1)
gitlab-chronic (~> 0.10.5)
gitlab-dangerfiles (~> 3.6.6)
gitlab-experiment (~> 0.7.1)
diff --git a/app/assets/javascripts/pages/projects/learn_gitlab/components/included_in_trial_indicator.vue b/app/assets/javascripts/pages/projects/learn_gitlab/components/included_in_trial_indicator.vue
deleted file mode 100644
index 693dc6a15ad..00000000000
--- a/app/assets/javascripts/pages/projects/learn_gitlab/components/included_in_trial_indicator.vue
+++ /dev/null
@@ -1,15 +0,0 @@
-
-
-
- {{ $options.i18n.trialOnly }}
-
-
diff --git a/app/assets/javascripts/pages/projects/learn_gitlab/components/learn_gitlab.vue b/app/assets/javascripts/pages/projects/learn_gitlab/components/learn_gitlab.vue
deleted file mode 100644
index 54e15b6552c..00000000000
--- a/app/assets/javascripts/pages/projects/learn_gitlab/components/learn_gitlab.vue
+++ /dev/null
@@ -1,146 +0,0 @@
-
-
-
-
-
-
- {{ project.name }}
-
-
-
-
-
-
{{ $options.i18n.title }}
-
{{ $options.i18n.description }}
-
-
-
-
-
- {{ progressPercentage }}
- %
-
-
-
-
-
-
-
diff --git a/app/assets/javascripts/pages/projects/learn_gitlab/components/learn_gitlab_section_card.vue b/app/assets/javascripts/pages/projects/learn_gitlab/components/learn_gitlab_section_card.vue
deleted file mode 100644
index e8f0e6c47ee..00000000000
--- a/app/assets/javascripts/pages/projects/learn_gitlab/components/learn_gitlab_section_card.vue
+++ /dev/null
@@ -1,56 +0,0 @@
-
-
-
-
-
- {{ $options.i18n[section].title }}
- {{ $options.i18n[section].description }}
-
-
-
-
-
-
diff --git a/app/assets/javascripts/pages/projects/learn_gitlab/components/learn_gitlab_section_link.vue b/app/assets/javascripts/pages/projects/learn_gitlab/components/learn_gitlab_section_link.vue
deleted file mode 100644
index d9b0dbbb9b0..00000000000
--- a/app/assets/javascripts/pages/projects/learn_gitlab/components/learn_gitlab_section_link.vue
+++ /dev/null
@@ -1,151 +0,0 @@
-
-
-
-
-
-
- {{ actionLabelValue('title') }}
-
-
-
- {{ actionLabelValue('title') }}
-
-
-
-
- {{ actionLabelValue('title') }}
-
-
-
-
- {{ actionLabelValue('title') }}
-
-
- {{ popoverText }}
-
- {{ $options.i18n.viewAdminList }}
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/assets/javascripts/pages/projects/learn_gitlab/constants/index.js b/app/assets/javascripts/pages/projects/learn_gitlab/constants/index.js
deleted file mode 100644
index cb1a0302d91..00000000000
--- a/app/assets/javascripts/pages/projects/learn_gitlab/constants/index.js
+++ /dev/null
@@ -1,133 +0,0 @@
-import { s__ } from '~/locale';
-
-export const ACTION_LABELS = {
- gitWrite: {
- title: s__('LearnGitLab|Create a repository'),
- actionLabel: s__('LearnGitLab|Create a repository'),
- description: s__('LearnGitLab|Create or import your first repository into your new project.'),
- trackLabel: 'create_a_repository',
- section: 'workspace',
- position: 1,
- },
- userAdded: {
- title: s__('LearnGitLab|Invite your colleagues'),
- actionLabel: s__('LearnGitLab|Invite your colleagues'),
- description: s__(
- 'LearnGitLab|GitLab works best as a team. Invite your colleague to enjoy all features.',
- ),
- trackLabel: 'invite_your_colleagues',
- section: 'workspace',
- position: 0,
- },
- pipelineCreated: {
- title: s__("LearnGitLab|Set up your first project's CI/CD"),
- actionLabel: s__('LearnGitLab|Set up CI/CD'),
- description: s__('LearnGitLab|Save time by automating your integration and deployment tasks.'),
- trackLabel: 'set_up_your_first_project_s_ci_cd',
- section: 'workspace',
- position: 2,
- },
- trialStarted: {
- title: s__('LearnGitLab|Start a free trial of GitLab Ultimate'),
- actionLabel: s__('LearnGitLab|Try GitLab Ultimate for free'),
- description: s__('LearnGitLab|Try all GitLab features for 30 days, no credit card required.'),
- trackLabel: 'start_a_free_trial_of_gitlab_ultimate',
- section: 'workspace',
- position: 3,
- openInNewTab: true,
- },
- codeOwnersEnabled: {
- title: s__('LearnGitLab|Add code owners'),
- actionLabel: s__('LearnGitLab|Add code owners'),
- description: s__(
- 'LearnGitLab|Prevent unexpected changes to important assets by assigning ownership of files and paths.',
- ),
- trackLabel: 'add_code_owners',
- trialRequired: true,
- section: 'workspace',
- position: 4,
- openInNewTab: true,
- videoTutorial: 'https://vimeo.com/670896787',
- },
- requiredMrApprovalsEnabled: {
- title: s__('LearnGitLab|Enable require merge approvals'),
- actionLabel: s__('LearnGitLab|Enable require merge approvals'),
- description: s__('LearnGitLab|Route code reviews to the right reviewers, every time.'),
- trackLabel: 'enable_require_merge_approvals',
- trialRequired: true,
- section: 'workspace',
- position: 5,
- openInNewTab: true,
- videoTutorial: 'https://vimeo.com/670904904',
- },
- mergeRequestCreated: {
- title: s__('LearnGitLab|Submit a merge request (MR)'),
- actionLabel: s__('LearnGitLab|Submit a merge request (MR)'),
- description: s__('LearnGitLab|Review and edit proposed changes to source code.'),
- trackLabel: 'submit_a_merge_request_mr',
- section: 'plan',
- 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.',
- ),
- trackLabel: 'create_an_issue',
- section: 'plan',
- position: 0,
- },
- securityScanEnabled: {
- title: s__('LearnGitLab|Run a Security scan using CI/CD'),
- actionLabel: s__('LearnGitLab|Run a Security scan using CI/CD'),
- description: s__('LearnGitLab|Scan your code to uncover vulnerabilities before deploying.'),
- trackLabel: 'run_a_security_scan_using_ci_cd',
- section: 'deploy',
- position: 1,
- },
- licenseScanningRun: {
- title: s__('LearnGitLab|Scan dependencies for licenses'),
- trackLabel: 'scan_dependencies_for_licenses',
- trialRequired: true,
- section: 'deploy',
- position: 2,
- },
- secureDependencyScanningRun: {
- title: s__('LearnGitLab|Scan dependencies for vulnerabilities'),
- trackLabel: 'scan_dependencies_for_vulnerabilities',
- trialRequired: true,
- section: 'deploy',
- position: 3,
- },
- secureDastRun: {
- title: s__('LearnGitLab|Analyze your application for vulnerabilities with DAST'),
- trackLabel: 'analyze_your_application_for_vulnerabilities_with_dast',
- trialRequired: true,
- section: 'deploy',
- position: 4,
- },
-};
-
-export const ACTION_SECTIONS = {
- workspace: {
- title: s__('LearnGitLab|Set up your workspace'),
- description: s__(
- "LearnGitLab|Complete these tasks first so you can enjoy GitLab's features to their fullest:",
- ),
- },
- plan: {
- title: s__('LearnGitLab|Plan and execute'),
- description: s__(
- 'LearnGitLab|Create a workflow for your new workspace, and learn how GitLab features work together:',
- ),
- },
- deploy: {
- title: s__('LearnGitLab|Deploy'),
- description: s__(
- 'LearnGitLab|Use your new GitLab workflow to deploy your application, monitor its health, and keep it secure:',
- ),
- },
-};
-
-export const INVITE_MODAL_OPEN_COOKIE = 'confetti_post_signup';
diff --git a/app/assets/javascripts/pages/projects/learn_gitlab/index/index.js b/app/assets/javascripts/pages/projects/learn_gitlab/index/index.js
deleted file mode 100644
index af4a6f8a0c9..00000000000
--- a/app/assets/javascripts/pages/projects/learn_gitlab/index/index.js
+++ /dev/null
@@ -1,31 +0,0 @@
-import Vue from 'vue';
-import initInviteMembersModal from '~/invite_members/init_invite_members_modal';
-import initInviteMembersTrigger from '~/invite_members/init_invite_members_trigger';
-import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
-import LearnGitlab from '../components/learn_gitlab.vue';
-
-function initLearnGitlab() {
- const el = document.getElementById('js-learn-gitlab-app');
-
- if (!el) {
- return false;
- }
-
- const actions = convertObjectPropsToCamelCase(JSON.parse(el.dataset.actions));
- const sections = convertObjectPropsToCamelCase(JSON.parse(el.dataset.sections));
- const project = convertObjectPropsToCamelCase(JSON.parse(el.dataset.project));
-
- return new Vue({
- el,
- render(createElement) {
- return createElement(LearnGitlab, {
- props: { actions, sections, project },
- });
- },
- });
-}
-
-initInviteMembersModal();
-initInviteMembersTrigger();
-
-initLearnGitlab();
diff --git a/app/assets/javascripts/repository/components/table/row.vue b/app/assets/javascripts/repository/components/table/row.vue
index 27ac11f3c58..6dd059a349f 100644
--- a/app/assets/javascripts/repository/components/table/row.vue
+++ b/app/assets/javascripts/repository/components/table/row.vue
@@ -203,7 +203,7 @@ export default {
:is="linkComponent"
ref="link"
v-gl-hover-load="handlePreload"
- v-gl-tooltip:tooltip-container
+ v-gl-tooltip="{ placement: 'left', boundary: 'viewport' }"
:title="fullPath"
:to="routerLinkTo"
:href="url"
diff --git a/app/controllers/projects/learn_gitlab_controller.rb b/app/controllers/projects/learn_gitlab_controller.rb
deleted file mode 100644
index 0193fcc7be6..00000000000
--- a/app/controllers/projects/learn_gitlab_controller.rb
+++ /dev/null
@@ -1,33 +0,0 @@
-# frozen_string_literal: true
-
-module Projects
- class LearnGitlabController < Projects::ApplicationController
- before_action :authenticate_user!
- before_action :verify_learn_gitlab_available!
- before_action :enable_invite_for_help_continuous_onboarding_experiment
- before_action :enable_video_tutorials_continuous_onboarding_experiment
-
- feature_category :user_profile
- urgency :low, [:index]
-
- def index; end
-
- private
-
- def verify_learn_gitlab_available!
- access_denied! unless helpers.learn_gitlab_enabled?(project)
- end
-
- def enable_invite_for_help_continuous_onboarding_experiment
- return unless current_user.can?(:admin_group_member, project.namespace)
-
- experiment(:invite_for_help_continuous_onboarding, namespace: project.namespace) do |e|
- e.candidate {}
- end
- end
-
- def enable_video_tutorials_continuous_onboarding_experiment
- experiment(:video_tutorials_continuous_onboarding, namespace: project&.namespace).publish
- end
- end
-end
diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb
index cdccd883c0d..eb323a9fa39 100644
--- a/app/controllers/projects_controller.rb
+++ b/app/controllers/projects_controller.rb
@@ -222,7 +222,13 @@ class ProjectsController < Projects::ApplicationController
end
def housekeeping
- ::Repositories::HousekeepingService.new(@project, :gc).execute
+ task = if ::Feature.enabled?(:eager_housekeeping_on_manual_jobs, @project)
+ :eager
+ else
+ :gc
+ end
+
+ ::Repositories::HousekeepingService.new(@project, task).execute
redirect_to(
project_path(@project),
diff --git a/app/experiments/video_tutorials_continuous_onboarding_experiment.rb b/app/experiments/video_tutorials_continuous_onboarding_experiment.rb
deleted file mode 100644
index 2c5790f83d1..00000000000
--- a/app/experiments/video_tutorials_continuous_onboarding_experiment.rb
+++ /dev/null
@@ -1,6 +0,0 @@
-# frozen_string_literal: true
-
-class VideoTutorialsContinuousOnboardingExperiment < ApplicationExperiment
- control {}
- candidate {}
-end
diff --git a/app/helpers/invite_members_helper.rb b/app/helpers/invite_members_helper.rb
index 6fad1346426..e986b56fde4 100644
--- a/app/helpers/invite_members_helper.rb
+++ b/app/helpers/invite_members_helper.rb
@@ -45,7 +45,7 @@ module InviteMembersHelper
full_path: source.full_path
}
- if show_invite_members_for_task?(source)
+ if current_user && show_invite_members_for_task?(source)
dataset.merge!(
tasks_to_be_done_options: tasks_to_be_done_options.to_json,
projects: projects_for_source(source).to_json,
@@ -71,11 +71,9 @@ module InviteMembersHelper
{}
end
- def show_invite_members_for_task?(source)
- return unless current_user
-
- invite_for_help_continuous_onboarding = source.is_a?(Project) && experiment(:invite_for_help_continuous_onboarding, namespace: source.namespace).assigned.name == 'candidate'
- params[:open_modal] == 'invite_members_for_task' || invite_for_help_continuous_onboarding
+ # Overridden in EE
+ def show_invite_members_for_task?(_source)
+ params[:open_modal] == 'invite_members_for_task'
end
def tasks_to_be_done_options
diff --git a/app/helpers/learn_gitlab_helper.rb b/app/helpers/learn_gitlab_helper.rb
deleted file mode 100644
index e1241f8b572..00000000000
--- a/app/helpers/learn_gitlab_helper.rb
+++ /dev/null
@@ -1,106 +0,0 @@
-# frozen_string_literal: true
-
-module LearnGitlabHelper
- IMAGE_PATH_PLAN = "learn_gitlab/section_plan.svg"
- IMAGE_PATH_DEPLOY = "learn_gitlab/section_deploy.svg"
- IMAGE_PATH_WORKSPACE = "learn_gitlab/section_workspace.svg"
- LICENSE_SCANNING_RUN_URL = 'https://docs.gitlab.com/ee/user/compliance/license_compliance/index.html'
-
- def learn_gitlab_enabled?(project)
- return false unless current_user
-
- learn_gitlab_onboarding_available?(project)
- end
-
- def learn_gitlab_data(project)
- {
- actions: onboarding_actions_data(project).to_json,
- sections: onboarding_sections_data.to_json,
- project: onboarding_project_data(project).to_json
- }
- end
-
- def learn_gitlab_onboarding_available?(project)
- Onboarding::Progress.onboarding?(project.namespace) &&
- Onboarding::LearnGitlab.new(current_user).available?
- end
-
- private
-
- def onboarding_actions_data(project)
- attributes = onboarding_progress(project).attributes.symbolize_keys
-
- action_urls(project).to_h do |action, url|
- [
- action,
- {
- url: url,
- completed: attributes[Onboarding::Progress.column_name(action)].present?,
- enabled: true
- }
- ]
- end
- end
-
- def onboarding_sections_data
- {
- workspace: {
- svg: image_path(IMAGE_PATH_WORKSPACE)
- },
- plan: {
- svg: image_path(IMAGE_PATH_PLAN)
- },
- deploy: {
- svg: image_path(IMAGE_PATH_DEPLOY)
- }
- }
- end
-
- def onboarding_project_data(project)
- { name: project.name }
- end
-
- def action_urls(project)
- action_issue_urls.merge(
- issue_created: project_issues_path(project),
- git_write: project_path(project),
- merge_request_created: project_merge_requests_path(project),
- user_added: project_members_url(project),
- **deploy_section_action_urls(project)
- )
- end
-
- def action_issue_urls
- Onboarding::Completion::ACTION_ISSUE_IDS.transform_values do |id|
- project_issue_url(learn_gitlab_project, id)
- end
- end
-
- def deploy_section_action_urls(project)
- experiment(
- :security_actions_continuous_onboarding,
- namespace: project.namespace,
- user: current_user,
- sticky_to: current_user
- ) do |e|
- e.control { { security_scan_enabled: project_security_configuration_path(project) } }
- e.candidate do
- {
- license_scanning_run: LICENSE_SCANNING_RUN_URL,
- secure_dependency_scanning_run: project_security_configuration_path(project, anchor: 'dependency-scanning'),
- secure_dast_run: project_security_configuration_path(project, anchor: 'dast')
- }
- end
- end.run
- end
-
- def learn_gitlab_project
- @learn_gitlab_project ||= Onboarding::LearnGitlab.new(current_user).project
- end
-
- def onboarding_progress(project)
- Onboarding::Progress.find_by(namespace: project.namespace) # rubocop: disable CodeReuse/ActiveRecord
- end
-end
-
-LearnGitlabHelper.prepend_mod_with('LearnGitlabHelper')
diff --git a/app/helpers/sidebars_helper.rb b/app/helpers/sidebars_helper.rb
index 9ce68df26a1..5a19ce6149a 100644
--- a/app/helpers/sidebars_helper.rb
+++ b/app/helpers/sidebars_helper.rb
@@ -117,7 +117,6 @@ module SidebarsHelper
{
current_user: user,
container: project,
- learn_gitlab_enabled: learn_gitlab_enabled?(project),
current_ref: current_ref,
ref_type: ref_type,
jira_issues_integration: project_jira_issues_integration?,
diff --git a/app/models/onboarding/learn_gitlab.rb b/app/models/onboarding/learn_gitlab.rb
deleted file mode 100644
index d7a189ed6e2..00000000000
--- a/app/models/onboarding/learn_gitlab.rb
+++ /dev/null
@@ -1,38 +0,0 @@
-# frozen_string_literal: true
-
-module Onboarding
- class LearnGitlab
- PROJECT_NAME = 'Learn GitLab'
- PROJECT_NAME_ULTIMATE_TRIAL = 'Learn GitLab - Ultimate trial'
- BOARD_NAME = 'GitLab onboarding'
- LABEL_NAME = 'Novice'
-
- def initialize(current_user)
- @current_user = current_user
- end
-
- def available?
- project && board && label
- end
-
- def project
- @project ||= current_user.projects.find_by_name([PROJECT_NAME, PROJECT_NAME_ULTIMATE_TRIAL])
- end
-
- def board
- return unless project
-
- @board ||= project.boards.find_by_name(BOARD_NAME)
- end
-
- def label
- return unless project
-
- @label ||= project.labels.find_by_name(LABEL_NAME)
- end
-
- private
-
- attr_reader :current_user
- end
-end
diff --git a/app/views/projects/learn_gitlab/index.html.haml b/app/views/projects/learn_gitlab/index.html.haml
deleted file mode 100644
index 0e950c26d34..00000000000
--- a/app/views/projects/learn_gitlab/index.html.haml
+++ /dev/null
@@ -1,8 +0,0 @@
-- breadcrumb_title _("Learn GitLab")
-- page_title _("Learn GitLab")
-- add_page_specific_style 'page_bundles/learn_gitlab'
-- data = learn_gitlab_data(@project)
-
-= render 'projects/invite_members_modal', project: @project
-
-#js-learn-gitlab-app{ data: data }
diff --git a/app/workers/concerns/git_garbage_collect_methods.rb b/app/workers/concerns/git_garbage_collect_methods.rb
index c5f8c9c8464..de3796f7e43 100644
--- a/app/workers/concerns/git_garbage_collect_methods.rb
+++ b/app/workers/concerns/git_garbage_collect_methods.rb
@@ -57,7 +57,7 @@ module GitGarbageCollectMethods
end
def gc?(task)
- task == :gc || task == :prune
+ %i[gc eager prune].include?(task)
end
def try_obtain_lease(key)
@@ -84,8 +84,11 @@ module GitGarbageCollectMethods
repository = resource.repository.raw_repository
client = repository.gitaly_repository_client
- if task == :prune
+ case task
+ when :prune
client.prune_unreachable_objects
+ when :eager
+ client.optimize_repository(eager: true)
else
client.optimize_repository
end
diff --git a/config/feature_flags/development/eager_housekeeping_on_manual_jobs.yml b/config/feature_flags/development/eager_housekeeping_on_manual_jobs.yml
new file mode 100644
index 00000000000..640a4d5019f
--- /dev/null
+++ b/config/feature_flags/development/eager_housekeeping_on_manual_jobs.yml
@@ -0,0 +1,8 @@
+---
+name: eager_housekeeping_on_manual_jobs
+introduced_by_url: 'https://gitlab.com/gitlab-org/gitlab/-/merge_requests/108970'
+rollout_issue_url: 'https://gitlab.com/gitlab-org/gitlab/-/issues/388097'
+milestone: '15.9'
+type: development
+group: group::gitaly
+default_enabled: false
diff --git a/config/feature_flags/experiment/invite_for_help_continuous_onboarding.yml b/config/feature_flags/experiment/invite_for_help_continuous_onboarding.yml
deleted file mode 100644
index 398f3bddf8a..00000000000
--- a/config/feature_flags/experiment/invite_for_help_continuous_onboarding.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: invite_for_help_continuous_onboarding
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/73846
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/345708
-milestone: '14.5'
-type: experiment
-group: group::activation
-default_enabled: false
diff --git a/config/feature_flags/experiment/video_tutorials_continuous_onboarding.yml b/config/feature_flags/experiment/video_tutorials_continuous_onboarding.yml
deleted file mode 100644
index e047a4e9682..00000000000
--- a/config/feature_flags/experiment/video_tutorials_continuous_onboarding.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: video_tutorials_continuous_onboarding
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/82274
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/351916
-milestone: '14.9'
-type: experiment
-group: group::acquisition
-default_enabled: false
diff --git a/config/routes/project.rb b/config/routes/project.rb
index 4a2f8375249..ceb0671c034 100644
--- a/config/routes/project.rb
+++ b/config/routes/project.rb
@@ -92,8 +92,6 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
end
end
- get :learn_gitlab, action: :index, controller: 'learn_gitlab'
-
namespace :ci do
resource :lint, only: [:show, :create]
resource :pipeline_editor, only: [:show], controller: :pipeline_editor, path: 'editor'
diff --git a/doc/development/bulk_import.md b/doc/development/bulk_import.md
index 68709329c27..e2a10d4b7a7 100644
--- a/doc/development/bulk_import.md
+++ b/doc/development/bulk_import.md
@@ -24,7 +24,7 @@ works with a set of [ETL](#etl) Pipelines leveraging from the current [GitLab AP

-### [ETL](https://www.ibm.com/cloud/learn/etl)
+### [ETL](https://www.ibm.com/topics/etl)
diff --git a/doc/development/contributing/index.md b/doc/development/contributing/index.md
index 1a5b801a95a..e991db1204f 100644
--- a/doc/development/contributing/index.md
+++ b/doc/development/contributing/index.md
@@ -100,7 +100,7 @@ If you have any questions or need help, visit [Getting Help](https://about.gitla
communicate with the GitLab community. GitLab prefers [asynchronous communication](https://about.gitlab.com/handbook/communication/#internal-communication) over real-time communication.
We do encourage you to connect and hang out with us. GitLab has a Gitter room dedicated for [contributors](https://gitter.im/gitlab/contributors), which is bridged with our
-internal Slack. We actively monitor this channel. There is also a community-run [Discord server](https://discord.gg/gitlab) where you can
+internal Slack. We actively monitor this channel. There is also a community-run [Discord server](https://discord.com/invite/gitlab) where you can
find other contributors in the `#contributors` channel.
Thanks for your contribution!
diff --git a/doc/development/fe_guide/graphql.md b/doc/development/fe_guide/graphql.md
index 0fec38b1200..7ebaa5b5611 100644
--- a/doc/development/fe_guide/graphql.md
+++ b/doc/development/fe_guide/graphql.md
@@ -14,7 +14,7 @@ info: "See the Technical Writers assigned to Development Guidelines: https://abo
**General resources**:
- [📚 Official Introduction to GraphQL](https://graphql.org/learn/)
-- [📚 Official Introduction to Apollo](https://www.apollographql.com/tutorials/fullstack-quickstart/introduction)
+- [📚 Official Introduction to Apollo](https://www.apollographql.com/tutorials/fullstack-quickstart/01-introduction)
**GraphQL at GitLab**:
diff --git a/lib/api/projects.rb b/lib/api/projects.rb
index 7271615b623..04037a1842d 100644
--- a/lib/api/projects.rb
+++ b/lib/api/projects.rb
@@ -872,7 +872,13 @@ module API
authorize_admin_project
begin
- ::Repositories::HousekeepingService.new(user_project, :gc).execute
+ task = if ::Feature.enabled?(:eager_housekeeping_on_manual_jobs, user_project)
+ :eager
+ else
+ :gc
+ end
+
+ ::Repositories::HousekeepingService.new(user_project, task).execute
rescue ::Repositories::HousekeepingService::LeaseTaken => error
conflict!(error.message)
end
diff --git a/lib/gitlab/gitaly_client/repository_service.rb b/lib/gitlab/gitaly_client/repository_service.rb
index 203854264ce..bcc03ca08c9 100644
--- a/lib/gitlab/gitaly_client/repository_service.rb
+++ b/lib/gitlab/gitaly_client/repository_service.rb
@@ -24,8 +24,20 @@ module Gitlab
response.exists
end
- def optimize_repository
- request = Gitaly::OptimizeRepositoryRequest.new(repository: @gitaly_repo)
+ # Optimize the repository. By default, this will perform heuristical housekeeping in the repository, which
+ # is the recommended approach and will only optimize what needs to be optimized. If `eager = true`, then
+ # Gitaly will instead be asked to perform eager housekeeping. As a consequence the housekeeping run will take a
+ # _lot_ longer. It is not recommended to use eager housekeeping in general, but only in situations where it is
+ # explicitly required.
+ def optimize_repository(eager: false)
+ strategy = if eager
+ Gitaly::OptimizeRepositoryRequest::Strategy::STRATEGY_EAGER
+ else
+ Gitaly::OptimizeRepositoryRequest::Strategy::STRATEGY_HEURISTICAL
+ end
+
+ request = Gitaly::OptimizeRepositoryRequest.new(repository: @gitaly_repo,
+ strategy: strategy)
gitaly_client_call(@storage, :repository_service, :optimize_repository, request, timeout: GitalyClient.long_timeout)
end
diff --git a/lib/sidebars/projects/menus/learn_gitlab_menu.rb b/lib/sidebars/projects/menus/learn_gitlab_menu.rb
deleted file mode 100644
index b6fae2af93d..00000000000
--- a/lib/sidebars/projects/menus/learn_gitlab_menu.rb
+++ /dev/null
@@ -1,63 +0,0 @@
-# frozen_string_literal: true
-
-module Sidebars
- module Projects
- module Menus
- class LearnGitlabMenu < ::Sidebars::Menu
- include Gitlab::Utils::StrongMemoize
-
- override :link
- def link
- project_learn_gitlab_path(context.project)
- end
-
- override :active_routes
- def active_routes
- { controller: :learn_gitlab }
- end
-
- override :title
- def title
- _('Learn GitLab')
- end
-
- override :has_pill?
- def has_pill?
- context.learn_gitlab_enabled
- end
-
- override :pill_count
- def pill_count
- strong_memoize(:pill_count) do
- percentage = Onboarding::Completion.new(
- context.project.namespace,
- context.current_user
- ).percentage
-
- "#{percentage}%"
- end
- end
-
- override :extra_nav_link_html_options
- def extra_nav_link_html_options
- {
- class: 'home',
- data: {
- track_label: 'learn_gitlab'
- }
- }
- end
-
- override :sprite_icon
- def sprite_icon
- 'bulb'
- end
-
- override :render?
- def render?
- context.learn_gitlab_enabled
- end
- end
- end
- end
-end
diff --git a/lib/sidebars/projects/panel.rb b/lib/sidebars/projects/panel.rb
index 8ae8f931aab..9d0f5eb87bd 100644
--- a/lib/sidebars/projects/panel.rb
+++ b/lib/sidebars/projects/panel.rb
@@ -19,7 +19,6 @@ module Sidebars
def add_menus
add_menu(Sidebars::Projects::Menus::ProjectInformationMenu.new(context))
- add_menu(Sidebars::Projects::Menus::LearnGitlabMenu.new(context))
add_menu(Sidebars::Projects::Menus::RepositoryMenu.new(context))
add_menu(Sidebars::Projects::Menus::IssuesMenu.new(context))
add_menu(Sidebars::Projects::Menus::ExternalIssueTrackerMenu.new(context))
diff --git a/spec/controllers/projects/learn_gitlab_controller_spec.rb b/spec/controllers/projects/learn_gitlab_controller_spec.rb
deleted file mode 100644
index bcc8fdc0265..00000000000
--- a/spec/controllers/projects/learn_gitlab_controller_spec.rb
+++ /dev/null
@@ -1,49 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe Projects::LearnGitlabController, feature_category: :onboarding do
- describe 'GET #index' do
- let_it_be(:user) { create(:user) }
- let_it_be(:project) { create(:project, namespace: create(:group)) }
-
- let(:learn_gitlab_enabled) { true }
- let(:params) { { namespace_id: project.namespace.to_param, project_id: project } }
-
- subject(:action) { get :index, params: params }
-
- before do
- project.namespace.add_owner(user)
- allow(controller.helpers).to receive(:learn_gitlab_enabled?).and_return(learn_gitlab_enabled)
- end
-
- context 'for unauthenticated user' do
- it { is_expected.to have_gitlab_http_status(:redirect) }
- end
-
- context 'for authenticated user' do
- before do
- sign_in(user)
- end
-
- it { is_expected.to render_template(:index) }
-
- context 'when learn_gitlab is not available' do
- let(:learn_gitlab_enabled) { false }
-
- it { is_expected.to have_gitlab_http_status(:not_found) }
- end
-
- context 'with invite_for_help_continuous_onboarding experiment' do
- it 'tracks the assignment', :experiment do
- stub_experiments(invite_for_help_continuous_onboarding: true)
-
- expect(experiment(:invite_for_help_continuous_onboarding))
- .to track(:assignment).with_context(namespace: project.namespace).on_next_instance
-
- action
- end
- end
- end
- end
-end
diff --git a/spec/controllers/projects_controller_spec.rb b/spec/controllers/projects_controller_spec.rb
index bc58eaa1d6f..f850398b8f5 100644
--- a/spec/controllers/projects_controller_spec.rb
+++ b/spec/controllers/projects_controller_spec.rb
@@ -633,45 +633,67 @@ RSpec.describe ProjectsController do
let(:housekeeping) { Repositories::HousekeepingService.new(project) }
- context 'when authenticated as owner' do
- before do
- group.add_owner(user)
- sign_in(user)
+ shared_examples 'a housekeeping job' do
+ context 'when authenticated as owner' do
+ before do
+ group.add_owner(user)
+ sign_in(user)
- allow(Repositories::HousekeepingService).to receive(:new).with(project, :gc).and_return(housekeeping)
+ allow(Repositories::HousekeepingService).to receive(:new).with(project, expected_task).and_return(housekeeping)
+ end
+
+ it 'forces a full garbage collection' do
+ expect(housekeeping).to receive(:execute).once
+
+ post :housekeeping,
+ params: {
+ namespace_id: project.namespace.path,
+ id: project.path
+ }
+
+ expect(response).to have_gitlab_http_status(:found)
+ end
end
- it 'forces a full garbage collection' do
- expect(housekeeping).to receive(:execute).once
+ context 'when authenticated as developer' do
+ let(:developer) { create(:user) }
- post :housekeeping,
- params: {
- namespace_id: project.namespace.path,
- id: project.path
- }
+ before do
+ group.add_developer(developer)
+ end
- expect(response).to have_gitlab_http_status(:found)
+ it 'does not execute housekeeping' do
+ expect(housekeeping).not_to receive(:execute)
+
+ post :housekeeping,
+ params: {
+ namespace_id: project.namespace.path,
+ id: project.path
+ }
+
+ expect(response).to have_gitlab_http_status(:found)
+ end
end
end
- context 'when authenticated as developer' do
- let(:developer) { create(:user) }
+ context 'with :eager_housekeeping_on_manual_jobs disabled' do
+ let(:expected_task) { :gc }
before do
- group.add_developer(developer)
+ stub_feature_flags(eager_housekeeping_on_manual_jobs: false)
end
- it 'does not execute housekeeping' do
- expect(housekeeping).not_to receive(:execute)
+ it_behaves_like 'a housekeeping job'
+ end
- post :housekeeping,
- params: {
- namespace_id: project.namespace.path,
- id: project.path
- }
+ context 'with :eager_housekeeping_on_manual_jobs enabled' do
+ let(:expected_task) { :eager }
- expect(response).to have_gitlab_http_status(:found)
+ before do
+ stub_feature_flags(eager_housekeeping_on_manual_jobs: true)
end
+
+ it_behaves_like 'a housekeeping job'
end
end
diff --git a/spec/experiments/video_tutorials_continuous_onboarding_experiment_spec.rb b/spec/experiments/video_tutorials_continuous_onboarding_experiment_spec.rb
deleted file mode 100644
index 596791308a4..00000000000
--- a/spec/experiments/video_tutorials_continuous_onboarding_experiment_spec.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe VideoTutorialsContinuousOnboardingExperiment do
- it "defines a control and candidate" do
- expect(subject.behaviors.keys).to match_array(%w[control candidate])
- end
-end
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
deleted file mode 100644
index 83feb621478..00000000000
--- a/spec/frontend/pages/projects/learn_gitlab/components/__snapshots__/learn_gitlab_section_card_spec.js.snap
+++ /dev/null
@@ -1,62 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`Learn GitLab Section Card renders correctly 1`] = `
-
-
-
-
- Set up your workspace
-
-
-
- Complete these tasks first so you can enjoy GitLab's features to their fullest:
-
-
-
-
-
-
-
-
-
-
-
-`;
diff --git a/spec/frontend/pages/projects/learn_gitlab/components/__snapshots__/learn_gitlab_spec.js.snap b/spec/frontend/pages/projects/learn_gitlab/components/__snapshots__/learn_gitlab_spec.js.snap
deleted file mode 100644
index 6b6833b00c3..00000000000
--- a/spec/frontend/pages/projects/learn_gitlab/components/__snapshots__/learn_gitlab_spec.js.snap
+++ /dev/null
@@ -1,409 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`Learn GitLab renders correctly 1`] = `
-
-
-
-
-
-
- Learn GitLab
-
-
-
- Ready to get started with GitLab? Follow these steps to set up your workspace, plan and commit changes, and deploy your project.
-
-
-
-
-
-
- 22% completed
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Invite your colleagues
-
-
-
-
-
-
-
-
-
-
-
-
- Create a repository
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-`;
diff --git a/spec/frontend/pages/projects/learn_gitlab/components/learn_gitlab_section_card_spec.js b/spec/frontend/pages/projects/learn_gitlab/components/learn_gitlab_section_card_spec.js
deleted file mode 100644
index 3a511a009a9..00000000000
--- a/spec/frontend/pages/projects/learn_gitlab/components/learn_gitlab_section_card_spec.js
+++ /dev/null
@@ -1,27 +0,0 @@
-import { shallowMount } from '@vue/test-utils';
-import LearnGitlabSectionCard from '~/pages/projects/learn_gitlab/components/learn_gitlab_section_card.vue';
-import { testActions } from './mock_data';
-
-const defaultSection = 'workspace';
-const testImage = 'workspace.svg';
-
-describe('Learn GitLab Section Card', () => {
- let wrapper;
-
- afterEach(() => {
- wrapper.destroy();
- wrapper = null;
- });
-
- const createWrapper = () => {
- wrapper = shallowMount(LearnGitlabSectionCard, {
- propsData: { section: defaultSection, actions: testActions, svg: testImage },
- });
- };
-
- it('renders correctly', () => {
- createWrapper({ completed: false });
-
- expect(wrapper.element).toMatchSnapshot();
- });
-});
diff --git a/spec/frontend/pages/projects/learn_gitlab/components/learn_gitlab_section_link_spec.js b/spec/frontend/pages/projects/learn_gitlab/components/learn_gitlab_section_link_spec.js
deleted file mode 100644
index 29335308370..00000000000
--- a/spec/frontend/pages/projects/learn_gitlab/components/learn_gitlab_section_link_spec.js
+++ /dev/null
@@ -1,233 +0,0 @@
-import { GlPopover, GlLink } from '@gitlab/ui';
-import { mount } from '@vue/test-utils';
-import { extendedWrapper } from 'helpers/vue_test_utils_helper';
-import { stubExperiments } from 'helpers/experimentation_helper';
-import { mockTracking, triggerEvent, unmockTracking } from 'helpers/tracking_helper';
-import eventHub from '~/invite_members/event_hub';
-import LearnGitlabSectionLink from '~/pages/projects/learn_gitlab/components/learn_gitlab_section_link.vue';
-import { ACTION_LABELS } from '~/pages/projects/learn_gitlab/constants';
-
-const defaultAction = 'gitWrite';
-const defaultProps = {
- title: 'Create Repository',
- description: 'Some description',
- url: 'https://example.com',
- completed: false,
- enabled: true,
-};
-
-const openInNewTabProps = {
- url: 'https://docs.gitlab.com/ee/user/application_security/security_dashboard/',
- openInNewTab: true,
-};
-
-describe('Learn GitLab Section Link', () => {
- let wrapper;
-
- afterEach(() => {
- wrapper.destroy();
- wrapper = null;
- });
-
- const createWrapper = (action = defaultAction, props = {}) => {
- wrapper = extendedWrapper(
- mount(LearnGitlabSectionLink, {
- propsData: { action, value: { ...defaultProps, ...props } },
- }),
- );
- };
-
- const openInviteMembesrModalLink = () =>
- wrapper.find('[data-testid="invite-for-help-continuous-onboarding-experiment-link"]');
-
- const findUncompletedLink = () => wrapper.find('[data-testid="uncompleted-learn-gitlab-link"]');
- const findDisabledLink = () => wrapper.findByTestId('disabled-learn-gitlab-link');
- const findPopoverTrigger = () => wrapper.findByTestId('contact-admin-popover-trigger');
- const findPopover = () => wrapper.findComponent(GlPopover);
- const findPopoverLink = () => findPopover().findComponent(GlLink);
- const videoTutorialLink = () => wrapper.find('[data-testid="video-tutorial-link"]');
-
- it('renders no icon when not completed', () => {
- createWrapper(undefined, { completed: false });
-
- expect(wrapper.find('[data-testid="completed-icon"]').exists()).toBe(false);
- });
-
- it('renders the completion icon when completed', () => {
- createWrapper(undefined, { completed: true });
-
- expect(wrapper.find('[data-testid="completed-icon"]').exists()).toBe(true);
- });
-
- it('renders no trial only when it is not required', () => {
- createWrapper();
-
- expect(wrapper.find('[data-testid="trial-only"]').exists()).toBe(false);
- });
-
- it('renders trial only when trial is required', () => {
- createWrapper('codeOwnersEnabled');
-
- expect(wrapper.find('[data-testid="trial-only"]').exists()).toBe(true);
- });
-
- describe('disabled links', () => {
- beforeEach(() => {
- createWrapper('trialStarted', { enabled: false });
- });
-
- it('renders text without a link', () => {
- expect(findDisabledLink().exists()).toBe(true);
- expect(findDisabledLink().text()).toBe(ACTION_LABELS.trialStarted.title);
- expect(findDisabledLink().attributes('href')).toBeUndefined();
- });
-
- it('renders a popover trigger with question icon', () => {
- expect(findPopoverTrigger().exists()).toBe(true);
- expect(findPopoverTrigger().props('icon')).toBe('question-o');
- expect(findPopoverTrigger().attributes('aria-label')).toBe(
- LearnGitlabSectionLink.i18n.contactAdmin,
- );
- });
-
- it('renders a popover', () => {
- expect(findPopoverTrigger().attributes('id')).toBe(findPopover().props('target'));
- expect(findPopover().props()).toMatchObject({
- placement: 'top',
- triggers: 'hover focus',
- });
- });
-
- it('renders default disabled message', () => {
- expect(findPopover().text()).toContain(LearnGitlabSectionLink.i18n.contactAdmin);
- });
-
- it('renders custom disabled message if provided', () => {
- createWrapper('trialStarted', { enabled: false, message: 'Custom message' });
- expect(findPopover().text()).toContain('Custom message');
- });
-
- it('renders a link inside the popover', () => {
- expect(findPopoverLink().exists()).toBe(true);
- expect(findPopoverLink().attributes('href')).toBe(defaultProps.url);
- });
- });
-
- describe('links marked with openInNewTab', () => {
- beforeEach(() => {
- createWrapper('securityScanEnabled', openInNewTabProps);
- });
-
- it('renders links with blank target', () => {
- const linkElement = findUncompletedLink();
-
- expect(linkElement.exists()).toBe(true);
- expect(linkElement.attributes('target')).toEqual('_blank');
- });
-
- it('tracks the click', () => {
- const trackingSpy = mockTracking('_category_', wrapper.element, jest.spyOn);
-
- findUncompletedLink().trigger('click');
-
- expect(trackingSpy).toHaveBeenCalledWith('_category_', 'click_link', {
- label: 'run_a_security_scan_using_ci_cd',
- });
-
- unmockTracking();
- });
- });
-
- describe('rendering a link to open the invite_members modal instead of a regular link', () => {
- it.each`
- action | experimentVariant | showModal
- ${'userAdded'} | ${'candidate'} | ${true}
- ${'userAdded'} | ${'control'} | ${false}
- ${defaultAction} | ${'candidate'} | ${false}
- ${defaultAction} | ${'control'} | ${false}
- `(
- 'when the invite_for_help_continuous_onboarding experiment has variant: $experimentVariant and action is $action, the modal link is shown: $showModal',
- ({ action, experimentVariant, showModal }) => {
- stubExperiments({ invite_for_help_continuous_onboarding: experimentVariant });
- createWrapper(action);
-
- expect(openInviteMembesrModalLink().exists()).toBe(showModal);
- },
- );
- });
-
- describe('clicking the link to open the invite_members modal', () => {
- beforeEach(() => {
- jest.spyOn(eventHub, '$emit').mockImplementation();
-
- stubExperiments({ invite_for_help_continuous_onboarding: 'candidate' });
- createWrapper('userAdded');
- });
-
- it('calls the eventHub', () => {
- openInviteMembesrModalLink().vm.$emit('click');
-
- expect(eventHub.$emit).toHaveBeenCalledWith('openModal', { source: 'learn_gitlab' });
- });
-
- it('tracks the click', async () => {
- const trackingSpy = mockTracking('_category_', wrapper.element, jest.spyOn);
-
- triggerEvent(openInviteMembesrModalLink().element);
-
- expect(trackingSpy).toHaveBeenCalledWith('_category_', 'click_link', {
- label: 'invite_your_colleagues',
- property: 'Growth::Activation::Experiment::InviteForHelpContinuousOnboarding',
- });
-
- unmockTracking();
- });
- });
-
- describe('video_tutorials_continuous_onboarding experiment', () => {
- describe('when control', () => {
- beforeEach(() => {
- stubExperiments({ video_tutorials_continuous_onboarding: 'control' });
- createWrapper('codeOwnersEnabled');
- });
-
- it('renders no video link', () => {
- expect(videoTutorialLink().exists()).toBe(false);
- });
- });
-
- describe('when candidate', () => {
- beforeEach(() => {
- stubExperiments({ video_tutorials_continuous_onboarding: 'candidate' });
- createWrapper('codeOwnersEnabled');
- });
-
- it('renders video link with blank target', () => {
- const videoLinkElement = videoTutorialLink();
-
- expect(videoLinkElement.exists()).toBe(true);
- expect(videoLinkElement.attributes('target')).toEqual('_blank');
- });
-
- it('tracks the click', () => {
- const trackingSpy = mockTracking('_category_', wrapper.element, jest.spyOn);
-
- videoTutorialLink().trigger('click');
-
- expect(trackingSpy).toHaveBeenCalledWith('_category_', 'click_video_link', {
- label: 'add_code_owners',
- property: 'Growth::Conversion::Experiment::LearnGitLab',
- context: {
- data: {
- experiment: 'video_tutorials_continuous_onboarding',
- variant: 'candidate',
- },
- schema: 'iglu:com.gitlab/gitlab_experiment/jsonschema/1-0-0',
- },
- });
-
- unmockTracking();
- });
- });
- });
-});
diff --git a/spec/frontend/pages/projects/learn_gitlab/components/learn_gitlab_spec.js b/spec/frontend/pages/projects/learn_gitlab/components/learn_gitlab_spec.js
deleted file mode 100644
index 0f63c243342..00000000000
--- a/spec/frontend/pages/projects/learn_gitlab/components/learn_gitlab_spec.js
+++ /dev/null
@@ -1,113 +0,0 @@
-import { GlProgressBar, GlAlert } from '@gitlab/ui';
-import { mount } from '@vue/test-utils';
-import Cookies from '~/lib/utils/cookies';
-import LearnGitlab from '~/pages/projects/learn_gitlab/components/learn_gitlab.vue';
-import eventHub from '~/invite_members/event_hub';
-import { INVITE_MODAL_OPEN_COOKIE } from '~/pages/projects/learn_gitlab/constants';
-import { testActions, testSections, testProject } from './mock_data';
-
-describe('Learn GitLab', () => {
- let wrapper;
- let sidebar;
-
- const createWrapper = () => {
- wrapper = mount(LearnGitlab, {
- propsData: {
- actions: testActions,
- sections: testSections,
- project: testProject,
- },
- });
- };
-
- beforeEach(() => {
- sidebar = document.createElement('div');
- sidebar.innerHTML = `
-
- `;
- document.body.appendChild(sidebar);
- createWrapper();
- });
-
- afterEach(() => {
- wrapper.destroy();
- wrapper = null;
- sidebar.remove();
- });
-
- it('renders correctly', () => {
- expect(wrapper.element).toMatchSnapshot();
- });
-
- it('renders the progress percentage', () => {
- const text = wrapper.find('[data-testid="completion-percentage"]').text();
-
- expect(text).toBe('22% completed');
- });
-
- it('renders the progress bar with correct values', () => {
- const progressBar = wrapper.findComponent(GlProgressBar);
-
- expect(progressBar.attributes('value')).toBe('2');
- expect(progressBar.attributes('max')).toBe('9');
- });
-
- describe('Invite Members Modal', () => {
- let spy;
- let cookieSpy;
-
- beforeEach(() => {
- spy = jest.spyOn(eventHub, '$emit');
- cookieSpy = jest.spyOn(Cookies, 'remove');
- });
-
- afterEach(() => {
- Cookies.remove(INVITE_MODAL_OPEN_COOKIE);
- });
-
- it('emits openModal', () => {
- Cookies.set(INVITE_MODAL_OPEN_COOKIE, true);
-
- createWrapper();
-
- expect(spy).toHaveBeenCalledWith('openModal', {
- mode: 'celebrate',
- source: 'learn-gitlab',
- });
- expect(cookieSpy).toHaveBeenCalledWith(INVITE_MODAL_OPEN_COOKIE);
- });
-
- it('does not emit openModal when cookie is not set', () => {
- createWrapper();
-
- expect(spy).not.toHaveBeenCalled();
- expect(cookieSpy).toHaveBeenCalledWith(INVITE_MODAL_OPEN_COOKIE);
- });
- });
-
- describe('when the showSuccessfulInvitationsAlert event is fired', () => {
- const findAlert = () => wrapper.findComponent(GlAlert);
-
- beforeEach(() => {
- eventHub.$emit('showSuccessfulInvitationsAlert');
- });
-
- it('displays the successful invitations alert', () => {
- expect(findAlert().exists()).toBe(true);
- });
-
- it('displays a message with the project name', () => {
- expect(findAlert().text()).toBe(
- "Your team is growing! You've successfully invited new team members to the test-project project.",
- );
- });
-
- it('modifies the sidebar percentage', () => {
- expect(sidebar.textContent.trim()).toBe('22%');
- });
- });
-});
diff --git a/spec/frontend/pages/projects/learn_gitlab/components/learn_gitlab_trial_card_spec.js b/spec/frontend/pages/projects/learn_gitlab/components/learn_gitlab_trial_card_spec.js
deleted file mode 100644
index 6ab57e31fed..00000000000
--- a/spec/frontend/pages/projects/learn_gitlab/components/learn_gitlab_trial_card_spec.js
+++ /dev/null
@@ -1,12 +0,0 @@
-import { shallowMount } from '@vue/test-utils';
-import IncludedInTrialIndicator from '~/pages/projects/learn_gitlab/components/included_in_trial_indicator.vue';
-
-describe('Learn GitLab Trial Card', () => {
- it('renders correctly', () => {
- const wrapper = shallowMount(IncludedInTrialIndicator);
-
- expect(wrapper.text()).toEqual('- Included in trial');
-
- wrapper.destroy();
- });
-});
diff --git a/spec/frontend/pages/projects/learn_gitlab/components/mock_data.js b/spec/frontend/pages/projects/learn_gitlab/components/mock_data.js
deleted file mode 100644
index 1c29c68d2a9..00000000000
--- a/spec/frontend/pages/projects/learn_gitlab/components/mock_data.js
+++ /dev/null
@@ -1,73 +0,0 @@
-export const testActions = {
- gitWrite: {
- url: 'http://example.com/',
- completed: true,
- svg: 'http://example.com/images/illustration.svg',
- enabled: true,
- },
- userAdded: {
- url: 'http://example.com/',
- completed: true,
- svg: 'http://example.com/images/illustration.svg',
- enabled: true,
- },
- pipelineCreated: {
- url: 'http://example.com/',
- completed: false,
- svg: 'http://example.com/images/illustration.svg',
- enabled: true,
- },
- trialStarted: {
- url: 'http://example.com/',
- completed: false,
- svg: 'http://example.com/images/illustration.svg',
- enabled: true,
- },
- codeOwnersEnabled: {
- url: 'http://example.com/',
- completed: false,
- svg: 'http://example.com/images/illustration.svg',
- enabled: true,
- },
- requiredMrApprovalsEnabled: {
- url: 'http://example.com/',
- completed: false,
- svg: 'http://example.com/images/illustration.svg',
- enabled: true,
- },
- mergeRequestCreated: {
- url: 'http://example.com/',
- completed: false,
- svg: 'http://example.com/images/illustration.svg',
- enabled: true,
- },
- securityScanEnabled: {
- url: 'https://docs.gitlab.com/ee/foobar/',
- completed: false,
- svg: 'http://example.com/images/illustration.svg',
- enabled: true,
- openInNewTab: true,
- },
- issueCreated: {
- url: 'http://example.com/',
- completed: false,
- svg: 'http://example.com/images/illustration.svg',
- enabled: true,
- },
-};
-
-export const testSections = {
- workspace: {
- svg: 'workspace.svg',
- },
- deploy: {
- svg: 'deploy.svg',
- },
- plan: {
- svg: 'plan.svg',
- },
-};
-
-export const testProject = {
- name: 'test-project',
-};
diff --git a/spec/helpers/invite_members_helper_spec.rb b/spec/helpers/invite_members_helper_spec.rb
index 48e94ec7e98..abf8b65dc1e 100644
--- a/spec/helpers/invite_members_helper_spec.rb
+++ b/spec/helpers/invite_members_helper_spec.rb
@@ -36,7 +36,8 @@ RSpec.describe InviteMembersHelper do
end
it 'provides the correct attributes' do
- expect(helper.common_invite_group_modal_data(group, GroupMember, 'false')).to include({ groups_filter: 'descendant_groups', parent_id: group.id })
+ expect(helper.common_invite_group_modal_data(group, GroupMember, 'false'))
+ .to include({ groups_filter: 'descendant_groups', parent_id: group.id })
end
end
@@ -46,7 +47,8 @@ RSpec.describe InviteMembersHelper do
end
it 'does not return filter attributes' do
- expect(helper.common_invite_group_modal_data(project.group, ProjectMember, 'true').keys).not_to include(:groups_filter, :parent_id)
+ expect(helper.common_invite_group_modal_data(project.group, ProjectMember, 'true').keys)
+ .not_to include(:groups_filter, :parent_id)
end
end
end
@@ -64,7 +66,7 @@ RSpec.describe InviteMembersHelper do
expect(helper.common_invite_modal_dataset(project)).to include(attributes)
end
- context 'tasks_to_be_done' do
+ context 'with tasks_to_be_done' do
using RSpec::Parameterized::TableSyntax
subject(:output) { helper.common_invite_modal_dataset(source) }
@@ -79,9 +81,7 @@ RSpec.describe InviteMembersHelper do
{ value: :issues, text: 'Create/import issues (tickets) to collaborate on ideas and plan work' }
].to_json
)
- expect(output[:projects]).to eq(
- [{ id: project.id, title: project.title }].to_json
- )
+ expect(output[:projects]).to eq([{ id: project.id, title: project.title }].to_json)
expect(output[:new_project_path]).to eq(
source.is_a?(Project) ? '' : new_project_path(namespace_id: group.id)
)
@@ -93,8 +93,8 @@ RSpec.describe InviteMembersHelper do
end
end
- context 'inviting members for tasks' do
- where(:open_modal_param_present?, :logged_in?, :expected?) do
+ context 'when inviting members for tasks' do
+ where(:open_modal_param?, :logged_in?, :expected?) do
true | true | true
true | false | false
false | true | false
@@ -104,7 +104,7 @@ RSpec.describe InviteMembersHelper do
with_them do
before do
allow(helper).to receive(:current_user).and_return(developer) if logged_in?
- allow(helper).to receive(:params).and_return({ open_modal: 'invite_members_for_task' }) if open_modal_param_present?
+ allow(helper).to receive(:params).and_return({ open_modal: 'invite_members_for_task' }) if open_modal_param?
end
context 'when the source is a project' do
@@ -120,36 +120,6 @@ RSpec.describe InviteMembersHelper do
end
end
end
-
- context 'the invite_for_help_continuous_onboarding experiment' do
- where(:invite_for_help_continuous_onboarding?, :logged_in?, :expected?) do
- true | true | true
- true | false | false
- false | true | false
- false | false | false
- end
-
- with_them do
- before do
- allow(helper).to receive(:current_user).and_return(developer) if logged_in?
- stub_experiments(invite_for_help_continuous_onboarding: :candidate) if invite_for_help_continuous_onboarding?
- end
-
- context 'when the source is a project' do
- let_it_be(:source) { project }
-
- it_behaves_like 'including the tasks to be done attributes'
- end
-
- context 'when the source is a group' do
- let_it_be(:source) { group }
-
- let(:expected?) { false }
-
- it_behaves_like 'including the tasks to be done attributes'
- end
- end
- end
end
end
@@ -172,11 +142,9 @@ RSpec.describe InviteMembersHelper do
end
context 'when the user can not manage project members' do
- before do
- expect(helper).to receive(:can?).with(owner, :admin_project_member, project).and_return(false)
- end
-
it 'returns false' do
+ expect(helper).to receive(:can?).with(owner, :admin_project_member, project).and_return(false)
+
expect(helper.can_invite_members_for_project?(project)).to eq false
end
end
diff --git a/spec/helpers/learn_gitlab_helper_spec.rb b/spec/helpers/learn_gitlab_helper_spec.rb
deleted file mode 100644
index 0ec1434e496..00000000000
--- a/spec/helpers/learn_gitlab_helper_spec.rb
+++ /dev/null
@@ -1,162 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe LearnGitlabHelper, feature_category: :onboarding do
- include AfterNextHelpers
- include Devise::Test::ControllerHelpers
-
- let_it_be(:user) { create(:user) }
- let_it_be(:project) { create(:project, name: Onboarding::LearnGitlab::PROJECT_NAME, namespace: user.namespace) }
- let_it_be(:namespace) { project.namespace }
-
- before do
- allow_next_instance_of(Onboarding::LearnGitlab) do |learn_gitlab|
- allow(learn_gitlab).to receive(:project).and_return(project)
- end
-
- Onboarding::Progress.onboard(namespace)
- Onboarding::Progress.register(namespace, :git_write)
- end
-
- describe '#learn_gitlab_enabled?' do
- using RSpec::Parameterized::TableSyntax
-
- let_it_be(:user) { create(:user) }
- let_it_be(:project) { create(:project, namespace: user.namespace) }
-
- let(:params) { { namespace_id: project.namespace.to_param, project_id: project } }
-
- subject { helper.learn_gitlab_enabled?(project) }
-
- where(:onboarding, :learn_gitlab_available, :result) do
- true | true | true
- true | false | false
- false | true | false
- end
-
- with_them do
- before do
- allow(Onboarding::Progress).to receive(:onboarding?).with(project.namespace).and_return(onboarding)
- allow_next(Onboarding::LearnGitlab, user).to receive(:available?).and_return(learn_gitlab_available)
- end
-
- context 'when signed in' do
- before do
- sign_in(user)
- end
-
- it { is_expected.to eq(result) }
- end
- end
-
- context 'when not signed in' do
- it { is_expected.to eq(false) }
- end
- end
-
- describe '#learn_gitlab_data' do
- subject(:learn_gitlab_data) { helper.learn_gitlab_data(project) }
-
- let(:onboarding_actions_data) { Gitlab::Json.parse(learn_gitlab_data[:actions]).deep_symbolize_keys }
- let(:onboarding_sections_data) { Gitlab::Json.parse(learn_gitlab_data[:sections]).deep_symbolize_keys }
- let(:onboarding_project_data) { Gitlab::Json.parse(learn_gitlab_data[:project]).deep_symbolize_keys }
-
- shared_examples 'has all data' do
- it 'has all actions' do
- expected_keys = [
- :issue_created,
- :git_write,
- :pipeline_created,
- :merge_request_created,
- :user_added,
- :trial_started,
- :required_mr_approvals_enabled,
- :code_owners_enabled,
- :security_scan_enabled
- ]
-
- expect(onboarding_actions_data.keys).to contain_exactly(*expected_keys)
- end
-
- it 'has all section data', :aggregate_failures do
- expect(onboarding_sections_data.keys).to contain_exactly(:deploy, :plan, :workspace)
- expect(onboarding_sections_data.values.map(&:keys)).to match_array([[:svg]] * 3)
- end
-
- it 'has all project data', :aggregate_failures do
- expect(onboarding_project_data.keys).to contain_exactly(:name)
- expect(onboarding_project_data.values).to match_array([project.name])
- end
- end
-
- it_behaves_like 'has all data'
-
- it 'sets correct completion statuses' do
- expect(onboarding_actions_data).to match({
- issue_created: a_hash_including(completed: false),
- git_write: a_hash_including(completed: true),
- pipeline_created: a_hash_including(completed: false),
- merge_request_created: a_hash_including(completed: false),
- user_added: a_hash_including(completed: false),
- trial_started: a_hash_including(completed: false),
- required_mr_approvals_enabled: a_hash_including(completed: false),
- code_owners_enabled: a_hash_including(completed: false),
- security_scan_enabled: a_hash_including(completed: false)
- })
- end
-
- describe 'security_actions_continuous_onboarding experiment' do
- let(:base_paths) do
- {
- trial_started: a_hash_including(url: %r{/learn_gitlab/-/issues/2\z}),
- pipeline_created: a_hash_including(url: %r{/learn_gitlab/-/issues/7\z}),
- code_owners_enabled: a_hash_including(url: %r{/learn_gitlab/-/issues/10\z}),
- required_mr_approvals_enabled: a_hash_including(url: %r{/learn_gitlab/-/issues/11\z}),
- issue_created: a_hash_including(url: %r{/learn_gitlab/-/issues\z}),
- git_write: a_hash_including(url: %r{/learn_gitlab\z}),
- user_added: a_hash_including(url: %r{/learn_gitlab/-/project_members\z}),
- merge_request_created: a_hash_including(url: %r{/learn_gitlab/-/merge_requests\z})
- }
- end
-
- context 'when control' do
- before do
- stub_experiments(security_actions_continuous_onboarding: :control)
- end
-
- it 'sets correct paths' do
- expect(onboarding_actions_data).to match(
- base_paths.merge(
- security_scan_enabled: a_hash_including(
- url: %r{/learn_gitlab/-/security/configuration\z}
- )
- )
- )
- end
- end
-
- context 'when candidate' do
- before do
- stub_experiments(security_actions_continuous_onboarding: :candidate)
- end
-
- it 'sets correct paths' do
- expect(onboarding_actions_data).to match(
- base_paths.merge(
- license_scanning_run: a_hash_including(
- url: described_class::LICENSE_SCANNING_RUN_URL
- ),
- secure_dependency_scanning_run: a_hash_including(
- url: project_security_configuration_path(project, anchor: 'dependency-scanning')
- ),
- secure_dast_run: a_hash_including(
- url: project_security_configuration_path(project, anchor: 'dast')
- )
- )
- )
- end
- end
- end
- 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 5eb60d2caa5..434550186c1 100644
--- a/spec/lib/gitlab/gitaly_client/repository_service_spec.rb
+++ b/spec/lib/gitlab/gitaly_client/repository_service_spec.rb
@@ -5,7 +5,7 @@ require 'spec_helper'
RSpec.describe Gitlab::GitalyClient::RepositoryService do
using RSpec::Parameterized::TableSyntax
- let(:project) { create(:project) }
+ let_it_be(:project) { create(:project, :repository) }
let(:storage_name) { project.repository_storage }
let(:relative_path) { project.disk_path + '.git' }
let(:client) { described_class.new(project.repository) }
@@ -22,13 +22,38 @@ RSpec.describe Gitlab::GitalyClient::RepositoryService do
end
describe '#optimize_repository' do
- it 'sends a optimize_repository message' do
- expect_any_instance_of(Gitaly::RepositoryService::Stub)
- .to receive(:optimize_repository)
- .with(gitaly_request_with_path(storage_name, relative_path), kind_of(Hash))
- .and_return(double(:optimize_repository))
+ shared_examples 'a repository optimization' do
+ it 'sends a optimize_repository message' do
+ expect_any_instance_of(Gitaly::RepositoryService::Stub)
+ .to receive(:optimize_repository)
+ .with(gitaly_request_with_params(
+ strategy: expected_strategy
+ ), kind_of(Hash))
+ .and_call_original
- client.optimize_repository
+ client.optimize_repository(**params)
+ end
+ end
+
+ context 'with default parameter' do
+ let(:params) { {} }
+ let(:expected_strategy) { :STRATEGY_HEURISTICAL }
+
+ it_behaves_like 'a repository optimization'
+ end
+
+ context 'with heuristical housekeeping strategy' do
+ let(:params) { { eager: false } }
+ let(:expected_strategy) { :STRATEGY_HEURISTICAL }
+
+ it_behaves_like 'a repository optimization'
+ end
+
+ context 'with eager housekeeping strategy' do
+ let(:params) { { eager: true } }
+ let(:expected_strategy) { :STRATEGY_EAGER }
+
+ it_behaves_like 'a repository optimization'
end
end
diff --git a/spec/lib/sidebars/projects/menus/learn_gitlab_menu_spec.rb b/spec/lib/sidebars/projects/menus/learn_gitlab_menu_spec.rb
deleted file mode 100644
index 4ae29f28f3a..00000000000
--- a/spec/lib/sidebars/projects/menus/learn_gitlab_menu_spec.rb
+++ /dev/null
@@ -1,79 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe Sidebars::Projects::Menus::LearnGitlabMenu do
- let_it_be(:project) { build(:project) }
- let_it_be(:learn_gitlab_enabled) { true }
-
- let(:context) do
- Sidebars::Projects::Context.new(
- current_user: nil,
- container: project,
- learn_gitlab_enabled: learn_gitlab_enabled
- )
- end
-
- subject { described_class.new(context) }
-
- it 'does not contain any sub menu' do
- expect(subject.has_items?).to be false
- end
-
- describe '#nav_link_html_options' do
- let_it_be(:data_tracking) do
- {
- class: 'home',
- data: {
- track_label: 'learn_gitlab'
- }
- }
- end
-
- specify do
- expect(subject.nav_link_html_options).to eq(data_tracking)
- end
- end
-
- describe '#render?' do
- context 'when learn gitlab experiment is enabled' do
- it 'returns true' do
- expect(subject.render?).to eq true
- end
- end
-
- context 'when learn gitlab experiment is disabled' do
- let(:learn_gitlab_enabled) { false }
-
- it 'returns false' do
- expect(subject.render?).to eq false
- end
- end
- end
-
- describe '#has_pill?' do
- context 'when learn gitlab experiment is enabled' do
- it 'returns true' do
- expect(subject.has_pill?).to eq true
- end
- end
-
- context 'when learn gitlab experiment is disabled' do
- let(:learn_gitlab_enabled) { false }
-
- it 'returns false' do
- expect(subject.has_pill?).to eq false
- end
- end
- end
-
- describe '#pill_count' do
- it 'returns pill count' do
- expect_next_instance_of(Onboarding::Completion) do |onboarding|
- expect(onboarding).to receive(:percentage).and_return(20)
- end
-
- expect(subject.pill_count).to eq '20%'
- end
- end
-end
diff --git a/spec/models/onboarding/learn_gitlab_spec.rb b/spec/models/onboarding/learn_gitlab_spec.rb
deleted file mode 100644
index 5e3e1f9c304..00000000000
--- a/spec/models/onboarding/learn_gitlab_spec.rb
+++ /dev/null
@@ -1,69 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe Onboarding::LearnGitlab do
- let_it_be(:current_user) { create(:user) }
- let_it_be(:learn_gitlab_project) { create(:project, name: described_class::PROJECT_NAME) }
- let_it_be(:learn_gitlab_board) { create(:board, project: learn_gitlab_project, name: described_class::BOARD_NAME) }
- let_it_be(:learn_gitlab_label) { create(:label, project: learn_gitlab_project, name: described_class::LABEL_NAME) }
-
- before do
- learn_gitlab_project.add_developer(current_user)
- end
-
- describe '#available?' do
- using RSpec::Parameterized::TableSyntax
-
- where(:project, :board, :label, :expected_result) do
- nil | nil | nil | nil
- nil | nil | true | nil
- nil | true | nil | nil
- nil | true | true | nil
- true | nil | nil | nil
- true | nil | true | nil
- true | true | nil | nil
- true | true | true | true
- end
-
- with_them do
- before do
- allow_next_instance_of(described_class) do |learn_gitlab|
- allow(learn_gitlab).to receive(:project).and_return(project)
- allow(learn_gitlab).to receive(:board).and_return(board)
- allow(learn_gitlab).to receive(:label).and_return(label)
- end
- end
-
- subject { described_class.new(current_user).available? }
-
- it { is_expected.to be expected_result }
- end
- end
-
- describe '#project' do
- subject { described_class.new(current_user).project }
-
- it { is_expected.to eq learn_gitlab_project }
-
- context 'when it is created during trial signup' do
- let_it_be(:learn_gitlab_project) do
- create(:project, name: described_class::PROJECT_NAME_ULTIMATE_TRIAL, path: 'learn-gitlab-ultimate-trial')
- end
-
- it { is_expected.to eq learn_gitlab_project }
- end
- end
-
- describe '#board' do
- subject { described_class.new(current_user).board }
-
- it { is_expected.to eq learn_gitlab_board }
- end
-
- describe '#label' do
- subject { described_class.new(current_user).label }
-
- it { is_expected.to eq learn_gitlab_label }
- end
-end
diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb
index 0026ef291e3..63b805d1001 100644
--- a/spec/requests/api/projects_spec.rb
+++ b/spec/requests/api/projects_spec.rb
@@ -4716,51 +4716,73 @@ RSpec.describe API::Projects, feature_category: :projects do
end
describe 'POST /projects/:id/housekeeping' do
- let(:housekeeping) { Repositories::HousekeepingService.new(project) }
+ shared_examples 'a housekeeping job' do
+ let(:housekeeping) { Repositories::HousekeepingService.new(project) }
- before do
- allow(Repositories::HousekeepingService).to receive(:new).with(project, :gc).and_return(housekeeping)
- end
-
- context 'when authenticated as owner' do
- it 'starts the housekeeping process' do
- expect(housekeeping).to receive(:execute).once
-
- post api("/projects/#{project.id}/housekeeping", user)
-
- expect(response).to have_gitlab_http_status(:created)
+ before do
+ allow(Repositories::HousekeepingService).to receive(:new).with(project, expected_task).and_return(housekeeping)
end
- context 'when housekeeping lease is taken' do
- it 'returns conflict' do
- expect(housekeeping).to receive(:execute).once.and_raise(Repositories::HousekeepingService::LeaseTaken)
+ context 'when authenticated as owner' do
+ it 'starts the housekeeping process' do
+ expect(housekeeping).to receive(:execute).once
post api("/projects/#{project.id}/housekeeping", user)
- expect(response).to have_gitlab_http_status(:conflict)
- expect(json_response['message']).to match(/Somebody already triggered housekeeping for this resource/)
+ expect(response).to have_gitlab_http_status(:created)
+ end
+
+ context 'when housekeeping lease is taken' do
+ it 'returns conflict' do
+ expect(housekeeping).to receive(:execute).once.and_raise(Repositories::HousekeepingService::LeaseTaken)
+
+ post api("/projects/#{project.id}/housekeeping", user)
+
+ expect(response).to have_gitlab_http_status(:conflict)
+ expect(json_response['message']).to match(/Somebody already triggered housekeeping for this resource/)
+ end
+ end
+ end
+
+ context 'when authenticated as developer' do
+ before do
+ project_member
+ end
+
+ it 'returns forbidden error' do
+ post api("/projects/#{project.id}/housekeeping", user3)
+
+ expect(response).to have_gitlab_http_status(:forbidden)
+ end
+ end
+
+ context 'when unauthenticated' do
+ it 'returns authentication error' do
+ post api("/projects/#{project.id}/housekeeping")
+
+ expect(response).to have_gitlab_http_status(:unauthorized)
end
end
end
- context 'when authenticated as developer' do
+ context 'with :eager_housekeeping_on_manual_jobs disabled' do
+ let(:expected_task) { :gc }
+
before do
- project_member
+ stub_feature_flags(eager_housekeeping_on_manual_jobs: false)
end
- it 'returns forbidden error' do
- post api("/projects/#{project.id}/housekeeping", user3)
-
- expect(response).to have_gitlab_http_status(:forbidden)
- end
+ it_behaves_like 'a housekeeping job'
end
- context 'when unauthenticated' do
- it 'returns authentication error' do
- post api("/projects/#{project.id}/housekeeping")
+ context 'with :eager_housekeeping_on_manual_jobs enabled' do
+ let(:expected_task) { :eager }
- expect(response).to have_gitlab_http_status(:unauthorized)
+ before do
+ stub_feature_flags(eager_housekeeping_on_manual_jobs: true)
end
+
+ it_behaves_like 'a housekeeping job'
end
end
diff --git a/spec/support/rspec_order_todo.yml b/spec/support/rspec_order_todo.yml
index e50b08d8fbe..420a704e20c 100644
--- a/spec/support/rspec_order_todo.yml
+++ b/spec/support/rspec_order_todo.yml
@@ -966,11 +966,9 @@
- './ee/spec/helpers/ee/groups/settings_helper_spec.rb'
- './ee/spec/helpers/ee/hooks_helper_spec.rb'
- './ee/spec/helpers/ee/integrations_helper_spec.rb'
-- './ee/spec/helpers/ee/invite_members_helper_spec.rb'
- './ee/spec/helpers/ee/issuables_helper_spec.rb'
- './ee/spec/helpers/ee/issues_helper_spec.rb'
- './ee/spec/helpers/ee/labels_helper_spec.rb'
-- './ee/spec/helpers/ee/learn_gitlab_helper_spec.rb'
- './ee/spec/helpers/ee/lock_helper_spec.rb'
- './ee/spec/helpers/ee/namespaces_helper_spec.rb'
- './ee/spec/helpers/ee/namespace_user_cap_reached_alert_helper_spec.rb'
@@ -3633,7 +3631,6 @@
- './spec/experiments/ios_specific_templates_experiment_spec.rb'
- './spec/experiments/require_verification_for_namespace_creation_experiment_spec.rb'
- './spec/experiments/security_reports_mr_widget_prompt_experiment_spec.rb'
-- './spec/experiments/video_tutorials_continuous_onboarding_experiment_spec.rb'
- './spec/features/abuse_report_spec.rb'
- './spec/features/action_cable_logging_spec.rb'
- './spec/features/admin/admin_abuse_reports_spec.rb'
@@ -5160,7 +5157,6 @@
- './spec/helpers/import_helper_spec.rb'
- './spec/helpers/instance_configuration_helper_spec.rb'
- './spec/helpers/integrations_helper_spec.rb'
-- './spec/helpers/invite_members_helper_spec.rb'
- './spec/helpers/issuables_description_templates_helper_spec.rb'
- './spec/helpers/issuables_helper_spec.rb'
- './spec/helpers/issues_helper_spec.rb'
diff --git a/spec/support/shared_examples/workers/concerns/git_garbage_collect_methods_shared_examples.rb b/spec/support/shared_examples/workers/concerns/git_garbage_collect_methods_shared_examples.rb
index ba1bdfa7aa8..abb00efdee8 100644
--- a/spec/support/shared_examples/workers/concerns/git_garbage_collect_methods_shared_examples.rb
+++ b/spec/support/shared_examples/workers/concerns/git_garbage_collect_methods_shared_examples.rb
@@ -147,5 +147,19 @@ RSpec.shared_examples 'can collect git garbage' do |update_statistics: true|
subject.perform(resource.id, 'prune', lease_key, lease_uuid)
end
end
+
+ context 'eager' do
+ before do
+ expect(subject).to receive(:get_lease_uuid).and_return(lease_uuid)
+ end
+
+ specify do
+ expect_next_instance_of(Gitlab::GitalyClient::RepositoryService, repository.raw_repository) do |instance|
+ expect(instance).to receive(:optimize_repository).with(eager: true).and_call_original
+ end
+
+ subject.perform(resource.id, 'eager', lease_key, lease_uuid)
+ end
+ end
end
end
diff --git a/spec/views/layouts/nav/sidebar/_project.html.haml_spec.rb b/spec/views/layouts/nav/sidebar/_project.html.haml_spec.rb
index 4de2c011b93..82584155de5 100644
--- a/spec/views/layouts/nav/sidebar/_project.html.haml_spec.rb
+++ b/spec/views/layouts/nav/sidebar/_project.html.haml_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'layouts/nav/sidebar/_project' do
+RSpec.describe 'layouts/nav/sidebar/_project', feature_category: :navigation do
let_it_be_with_reload(:project) { create(:project, :repository) }
let(:user) { project.first_owner }
@@ -67,19 +67,6 @@ RSpec.describe 'layouts/nav/sidebar/_project' do
end
end
- describe 'Learn GitLab' do
- it 'has a link to the learn GitLab' do
- allow(view).to receive(:learn_gitlab_enabled?).and_return(true)
- allow_next_instance_of(Onboarding::Completion) do |onboarding|
- expect(onboarding).to receive(:percentage).and_return(20)
- end
-
- render
-
- expect(rendered).to have_link('Learn GitLab', href: project_learn_gitlab_path(project))
- end
- end
-
describe 'Repository' do
it 'has a link to the project tree path' do
render