summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2023-02-09 12:11:06 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2023-02-09 12:11:06 +0000
commit6180f62ab34662c64103872b8352b25817b73a8d (patch)
tree2d7f30b845726fb0bff9ee191c11996c04c22c2c
parent2e2db606cc7547b445a11c367d8db6f5feb42443 (diff)
downloadgitlab-ce-6180f62ab34662c64103872b8352b25817b73a8d.tar.gz
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--.gitlab/issue_templates/Release group - bug.md43
-rw-r--r--.gitlab/issue_templates/Release group - feature.md26
-rw-r--r--.gitlab/issue_templates/Release group - maintenance.md18
-rw-r--r--.rubocop_todo/layout/argument_alignment.yml32
-rw-r--r--.rubocop_todo/rspec/factory_bot/avoid_create.yml1
-rw-r--r--.rubocop_todo/rspec/missing_feature_category.yml1
-rw-r--r--app/assets/javascripts/boards/components/board_content.vue2
-rw-r--r--app/assets/javascripts/pages/projects/forks/new/components/fork_form.vue33
-rw-r--r--app/assets/javascripts/projects/project_name_rules.js29
-rw-r--r--app/assets/javascripts/projects/project_new.js9
-rw-r--r--app/assets/stylesheets/framework/images.scss3
-rw-r--r--app/controllers/admin/application_settings/appearances_controller.rb11
-rw-r--r--app/models/appearance.rb12
-rw-r--r--app/views/admin/application_settings/appearances/_form.html.haml45
-rw-r--r--app/views/projects/_new_project_fields.html.haml6
-rw-r--r--config/feature_flags/development/improved_spread_parallel_import.yml2
-rw-r--r--config/locales/en.yml5
-rw-r--r--config/routes.rb6
-rw-r--r--config/routes/admin.rb1
-rw-r--r--db/post_migrate/20230126091522_add_unique_index_to_ci_build_pending_state.rb22
-rw-r--r--db/schema_migrations/202301260915221
-rw-r--r--db/structure.sql4
-rw-r--r--doc/api/members.md1
-rw-r--r--doc/ci/environments/index.md10
-rw-r--r--doc/user/project/index.md12
-rw-r--r--lib/api/helpers/members_helpers.rb3
-rw-r--r--lib/api/members.rb1
-rw-r--r--lib/gitlab/cache/helpers.rb9
-rw-r--r--lib/gitlab/database/migrations/base_background_runner.rb12
-rw-r--r--lib/gitlab/database/migrations/observation.rb16
-rw-r--r--lib/gitlab/database/migrations/observers/batch_details.rb53
-rw-r--r--lib/gitlab/database/migrations/test_batched_background_runner.rb26
-rw-r--r--locale/gitlab.pot25
-rw-r--r--spec/factories/member_roles.rb3
-rw-r--r--spec/features/admin/admin_appearance_spec.rb23
-rw-r--r--spec/frontend/pages/projects/forks/new/components/fork_form_spec.js38
-rw-r--r--spec/frontend/projects/project_new_spec.js18
-rw-r--r--spec/lib/api/helpers/caching_spec.rb2
-rw-r--r--spec/lib/gitlab/cache/helpers_spec.rb27
-rw-r--r--spec/lib/gitlab/database/migrations/instrumentation_spec.rb11
-rw-r--r--spec/lib/gitlab/database/migrations/observers/batch_details_spec.rb42
-rw-r--r--spec/lib/gitlab/database/migrations/test_batched_background_runner_spec.rb42
-rw-r--r--spec/models/ci/build_pending_state_spec.rb9
-rw-r--r--spec/services/ci/update_build_state_service_spec.rb2
-rw-r--r--spec/support/rspec_order_todo.yml1
-rw-r--r--spec/support/shared_examples/lib/cache_helpers_shared_examples.rb12
46 files changed, 566 insertions, 144 deletions
diff --git a/.gitlab/issue_templates/Release group - bug.md b/.gitlab/issue_templates/Release group - bug.md
new file mode 100644
index 00000000000..f542041593f
--- /dev/null
+++ b/.gitlab/issue_templates/Release group - bug.md
@@ -0,0 +1,43 @@
+## Summary
+
+<!-- Summarize the bug encountered concisely. -->
+
+## Steps to reproduce
+
+<!-- Describe how one can reproduce the issue - this is very important. Please use an ordered list. -->
+
+## What is the current *bug* behavior?
+
+<!-- Describe what actually happens. -->
+
+## What is the expected *correct* behavior?
+
+<!-- Describe what you should see instead. -->
+
+## Relevant logs and/or screenshots
+
+<!-- Paste any relevant logs - please use code blocks (```) to format console output, logs, and code
+ as it's tough to read otherwise. -->
+
+## Possible fixes
+
+<!-- If you can, link to the line of code that might be responsible for the problem. -->
+
+<!-- Default labels -->
+/label ~"group::release"
+/label ~"type::bug"
+/label ~"workflow::planning breakdown"
+
+<!-- Optional labels -->
+/label ~backend ~frontend ~UX
+
+<!-- https://about.gitlab.com/handbook/engineering/quality/issue-triage/#severity -->
+/label ~"severity::3" ~"priority::3"
+
+<!-- If you already have an implementation plan, please fill in detail below https://about.gitlab.com/handbook/engineering/development/ops/release/planning/#weights -->
+/weight X
+/label ~"backend-weight::" ~"frontend-weight::" ~"workflow::ready for development"
+## Implementation guide
+
+<!-- If the issue clear enough, consider inviting community contributors -->
+/label ~"Seeking community contributions" ~"good for new contributors"
diff --git a/.gitlab/issue_templates/Release group - feature.md b/.gitlab/issue_templates/Release group - feature.md
new file mode 100644
index 00000000000..0a695dac2ad
--- /dev/null
+++ b/.gitlab/issue_templates/Release group - feature.md
@@ -0,0 +1,26 @@
+## Problem to solve
+
+<!-- What problem do we solve? Try to define the who/what/why of the opportunity as a user story. For example, "As a (who), I want (what), so I can (why/value)." -->
+
+## Proposal
+
+<!-- Use this section to explain the feature and how it will work. It can be helpful to add technical details, design proposals, and links to related epics or issues. -->
+
+<!-- Default labels -->
+/label ~"group::release"
+/label ~"type::feature"
+/label ~"workflow::problem validation"
+/label ~UX
+
+<!-- Optional labels -->
+/label ~backend ~frontend
+
+<!-- If you already have an implementation plan, please fill in detail below https://about.gitlab.com/handbook/engineering/development/ops/release/planning/#weights -->
+/weight X
+/label ~"backend-weight::" ~"frontend-weight::" ~"workflow::ready for development"
+## Implementation guide
+
+<!-- If the issue clear enough, consider inviting community contributors -->
+/label ~"Seeking community contributions" ~"good for new contributors"
+
+
diff --git a/.gitlab/issue_templates/Release group - maintenance.md b/.gitlab/issue_templates/Release group - maintenance.md
new file mode 100644
index 00000000000..aa87015a44a
--- /dev/null
+++ b/.gitlab/issue_templates/Release group - maintenance.md
@@ -0,0 +1,18 @@
+## Background
+
+## Proposal
+
+<!-- Use this section to explain the feature and how it will work. It can be helpful to add technical details, design proposals, and links to related epics or issues. -->
+
+<!-- Set labels and other metadata -->
+/label ~"group::release"
+/label ~"type::maintenance"
+/label ~"workflow::planning breakdown"
+
+<!-- If you already have an implementation plan, please fill in detail below https://about.gitlab.com/handbook/engineering/development/ops/release/planning/#weights -->
+/weight X
+/label ~"backend-weight::" ~"frontend-weight::" ~"workflow::ready for development"
+## Implementation guide
+
+<!-- If the issue clear enough, consider inviting community contributors -->
+/label ~"Seeking community contributions" ~"good for new contributors"
diff --git a/.rubocop_todo/layout/argument_alignment.yml b/.rubocop_todo/layout/argument_alignment.yml
index 5d46979d563..4e22c8a85a4 100644
--- a/.rubocop_todo/layout/argument_alignment.yml
+++ b/.rubocop_todo/layout/argument_alignment.yml
@@ -524,7 +524,6 @@ Layout/ArgumentAlignment:
- 'app/graphql/types/snippets/blob_connection_type.rb'
- 'app/graphql/types/snippets/blob_type.rb'
- 'app/graphql/types/sort_enum.rb'
- - 'app/graphql/types/subscription_type.rb'
- 'app/graphql/types/task_completion_status.rb'
- 'app/graphql/types/terraform/state_type.rb'
- 'app/graphql/types/terraform/state_version_type.rb'
@@ -791,10 +790,6 @@ Layout/ArgumentAlignment:
- 'config/routes/unmatched_project.rb'
- 'config/routes/uploads.rb'
- 'config/routes/user.rb'
- - 'db/migrate/20210604082145_create_external_status_checks_table.rb'
- - 'db/migrate/20210609090856_add_expiry_id_ssh_key_notification_index.rb'
- - 'db/migrate/20210709221051_create_work_item_types.rb'
- - 'db/migrate/20210719182944_add_request_response_to_supporing_message.rb'
- 'db/migrate/20210901065504_add_index_on_name_and_id_to_public_groups.rb'
- 'db/migrate/20210910014741_add_dependency_proxy_ttl_group_policy_worker_capacity_to_application_settings.rb'
- 'db/migrate/20211111112639_add_fk_compliance_violations_merge_request.rb'
@@ -864,17 +859,6 @@ Layout/ArgumentAlignment:
- 'db/migrate/20230124193917_add_index_for_protected_tag_create_access_levels.rb'
- 'db/migrate/20230127151529_add_project_pointer_for_analytics_dashboard.rb'
- 'db/migrate/20230127151531_change_dashboard_analytics_project_pointer_project_null.rb'
- - 'db/post_migrate/20210611080951_fix_missing_traversal_ids.rb'
- - 'db/post_migrate/20210622045705_finalize_events_bigint_conversion.rb'
- - 'db/post_migrate/20210701141346_finalize_ci_builds_stage_id_bigint_conversion.rb'
- - 'db/post_migrate/20210707210916_finalize_ci_stages_bigint_conversion.rb'
- - 'db/post_migrate/20210708011426_finalize_ci_builds_metadata_bigint_conversion.rb'
- - 'db/post_migrate/20210802043253_finalize_push_event_payloads_bigint_conversion_3.rb'
- - 'db/post_migrate/20210804151444_prepare_indexes_for_ci_job_artifact_bigint_conversion.rb'
- - 'db/post_migrate/20210804153307_prepare_indexes_for_tagging_bigint_conversion.rb'
- - 'db/post_migrate/20210804154407_prepare_indexes_for_ci_stage_bigint_conversion.rb'
- - 'db/post_migrate/20210817024335_prepare_indexes_for_events_bigint_conversion.rb'
- - 'db/post_migrate/20210824174615_prepare_ci_builds_metadata_and_ci_build_async_indexes.rb'
- 'db/post_migrate/20210921062820_add_image_location_index_to_vulnerability_occurrences.rb'
- 'db/post_migrate/20211007093340_remove_analytics_snapshots_segment_id_column.rb'
- 'db/post_migrate/20211021140426_remove_geo_upload_deprecated_fields.rb'
@@ -1207,7 +1191,6 @@ Layout/ArgumentAlignment:
- 'ee/app/graphql/types/pipeline_security_report_finding_type.rb'
- 'ee/app/graphql/types/product_analytics/dashboard_type.rb'
- 'ee/app/graphql/types/product_analytics/visualization_type.rb'
- - 'ee/app/graphql/types/product_analytics/widget_type.rb'
- 'ee/app/graphql/types/protected_environment_type.rb'
- 'ee/app/graphql/types/protected_environments/approval_rule_for_summary_type.rb'
- 'ee/app/graphql/types/protected_environments/approval_rule_type.rb'
@@ -1343,7 +1326,6 @@ Layout/ArgumentAlignment:
- 'ee/app/services/security/track_scan_service.rb'
- 'ee/app/services/vulnerabilities/confirm_service.rb'
- 'ee/app/services/vulnerabilities/findings/find_or_create_from_security_finding_service.rb'
- - 'ee/app/services/vulnerabilities/security_finding/create_issue_service.rb'
- 'ee/app/services/vulnerabilities/security_finding/create_merge_request_service.rb'
- 'ee/app/services/vulnerabilities/starboard_vulnerability_resolve_service.rb'
- 'ee/app/services/vulnerability_feedback/create_service.rb'
@@ -1515,7 +1497,6 @@ Layout/ArgumentAlignment:
- 'ee/spec/features/search/elastic/global_search_spec.rb'
- 'ee/spec/features/search/elastic/group_search_spec.rb'
- 'ee/spec/features/security/project/discover_spec.rb'
- - 'ee/spec/features/trials/select_namespace_spec.rb'
- 'ee/spec/features/uncompleted_learn_gitlab_link_spec.rb'
- 'ee/spec/features/users/identity_verification_spec.rb'
- 'ee/spec/finders/boards/milestones_finder_spec.rb'
@@ -1546,6 +1527,7 @@ Layout/ArgumentAlignment:
- 'ee/spec/graphql/types/dast/pre_scan_verification_type_spec.rb'
- 'ee/spec/graphql/types/dast/profile_type_spec.rb'
- 'ee/spec/graphql/types/dast_scanner_profile_type_spec.rb'
+ - 'ee/spec/graphql/types/pipeline_security_report_finding_type_spec.rb'
- 'ee/spec/graphql/types/project_type_spec.rb'
- 'ee/spec/graphql/types/vulnerability_type_spec.rb'
- 'ee/spec/helpers/billing_plans_helper_spec.rb'
@@ -1639,8 +1621,6 @@ Layout/ArgumentAlignment:
- 'ee/spec/lib/incident_management/oncall_shift_generator_spec.rb'
- 'ee/spec/lib/omni_auth/strategies/group_saml_spec.rb'
- 'ee/spec/mailers/notify_spec.rb'
- - 'ee/spec/migrations/add_non_null_constraint_for_escalation_rule_on_pending_alert_escalations_spec.rb'
- - 'ee/spec/migrations/remove_schedule_and_status_null_constraints_from_pending_escalations_alert_spec.rb'
- 'ee/spec/migrations/update_vulnerability_occurrences_location_spec.rb'
- 'ee/spec/models/approval_wrapped_code_owner_rule_spec.rb'
- 'ee/spec/models/ci/bridge_spec.rb'
@@ -1664,8 +1644,8 @@ Layout/ArgumentAlignment:
- 'ee/spec/models/ee/event_spec.rb'
- 'ee/spec/models/ee/group_spec.rb'
- 'ee/spec/models/ee/namespace_spec.rb'
- - 'ee/spec/models/ee/user_spec.rb'
- 'ee/spec/models/ee/project_spec.rb'
+ - 'ee/spec/models/ee/user_spec.rb'
- 'ee/spec/models/epic_spec.rb'
- 'ee/spec/models/geo/project_registry_spec.rb'
- 'ee/spec/models/ldap_group_link_spec.rb'
@@ -2051,7 +2031,6 @@ Layout/ArgumentAlignment:
- 'lib/gitlab/ci/config/entry/root.rb'
- 'lib/gitlab/ci/config/entry/rules/rule/changes.rb'
- 'lib/gitlab/ci/config/extendable/entry.rb'
- - 'lib/gitlab/ci/config/external/mapper/matcher.rb'
- 'lib/gitlab/ci/lint.rb'
- 'lib/gitlab/ci/status/stage/common.rb'
- 'lib/gitlab/ci/trace/chunked_io.rb'
@@ -2660,7 +2639,6 @@ Layout/ArgumentAlignment:
- 'spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_base_spec.rb'
- 'spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_namespaces_spec.rb'
- 'spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_projects_spec.rb'
- - 'spec/lib/gitlab/database/tables_locker_spec.rb'
- 'spec/lib/gitlab/database/tables_truncate_spec.rb'
- 'spec/lib/gitlab/dependency_linker/parser/gemfile_spec.rb'
- 'spec/lib/gitlab/diff/file_collection/compare_spec.rb'
@@ -2768,7 +2746,6 @@ Layout/ArgumentAlignment:
- 'spec/mailers/emails/merge_requests_spec.rb'
- 'spec/mailers/emails/pipelines_spec.rb'
- 'spec/mailers/notify_spec.rb'
- - 'spec/migrations/20210708130419_reschedule_merge_request_diff_users_background_migration_spec.rb'
- 'spec/migrations/20210918201050_remove_old_pending_jobs_for_recalculate_vulnerabilities_occurrences_uuid_spec.rb'
- 'spec/migrations/20211110151350_schedule_drop_invalid_security_findings_spec.rb'
- 'spec/migrations/20211116111644_schedule_remove_occurrence_pipelines_and_duplicate_vulnerabilities_findings_spec.rb'
@@ -2795,9 +2772,6 @@ Layout/ArgumentAlignment:
- 'spec/migrations/cleanup_backfill_integrations_enable_ssl_verification_spec.rb'
- 'spec/migrations/cleanup_vulnerability_state_transitions_with_same_from_state_to_state_spec.rb'
- 'spec/migrations/delete_migrate_shared_vulnerability_scanners_spec.rb'
- - 'spec/migrations/migrate_protected_attribute_to_pending_builds_spec.rb'
- - 'spec/migrations/re_schedule_latest_pipeline_id_population_with_all_security_related_artifact_types_spec.rb'
- - 'spec/migrations/reschedule_delete_orphaned_deployments_spec.rb'
- 'spec/migrations/schedule_backfill_draft_status_on_merge_requests_corrected_regex_spec.rb'
- 'spec/migrations/schedule_fixing_security_scan_statuses_spec.rb'
- 'spec/migrations/schedule_purging_stale_security_scans_spec.rb'
@@ -3168,7 +3142,6 @@ Layout/ArgumentAlignment:
- 'spec/services/task_list_toggle_service_spec.rb'
- 'spec/services/todo_service_spec.rb'
- 'spec/services/users/activity_service_spec.rb'
- - 'spec/services/users/assigned_issues_count_service_spec.rb'
- 'spec/services/users/destroy_service_spec.rb'
- 'spec/services/users/migrate_records_to_ghost_user_in_batches_service_spec.rb'
- 'spec/services/users/migrate_records_to_ghost_user_service_spec.rb'
@@ -3181,7 +3154,6 @@ Layout/ArgumentAlignment:
- 'spec/support/helpers/api_internal_base_helpers.rb'
- 'spec/support/helpers/board_helpers.rb'
- 'spec/support/helpers/ci/source_pipeline_helpers.rb'
- - 'spec/support/helpers/ci/template_helpers.rb'
- 'spec/support/helpers/feature_flag_helpers.rb'
- 'spec/support/helpers/graphql_helpers.rb'
- 'spec/support/helpers/stub_object_storage.rb'
diff --git a/.rubocop_todo/rspec/factory_bot/avoid_create.yml b/.rubocop_todo/rspec/factory_bot/avoid_create.yml
index 0a4ea883cbe..6f4f9ed61b8 100644
--- a/.rubocop_todo/rspec/factory_bot/avoid_create.yml
+++ b/.rubocop_todo/rspec/factory_bot/avoid_create.yml
@@ -235,7 +235,6 @@ RSpec/FactoryBot/AvoidCreate:
- 'ee/spec/views/projects/security/sast_configuration/show.html.haml_spec.rb'
- 'ee/spec/views/projects/settings/subscriptions/_index.html.haml_spec.rb'
- 'ee/spec/views/registrations/groups_projects/new.html.haml_spec.rb'
- - 'ee/spec/views/registrations/welcome/continuous_onboarding_getting_started.html.haml_spec.rb'
- 'ee/spec/views/registrations/welcome/show.html.haml_spec.rb'
- 'ee/spec/views/search/_category.html.haml_spec.rb'
- 'ee/spec/views/shared/_clone_panel.html.haml_spec.rb'
diff --git a/.rubocop_todo/rspec/missing_feature_category.yml b/.rubocop_todo/rspec/missing_feature_category.yml
index 32a479a4ba5..91b2fb06070 100644
--- a/.rubocop_todo/rspec/missing_feature_category.yml
+++ b/.rubocop_todo/rspec/missing_feature_category.yml
@@ -2420,7 +2420,6 @@ RSpec/MissingFeatureCategory:
- 'ee/spec/views/projects/settings/subscriptions/_index.html.haml_spec.rb'
- 'ee/spec/views/registrations/company/new.html.haml_spec.rb'
- 'ee/spec/views/registrations/groups_projects/new.html.haml_spec.rb'
- - 'ee/spec/views/registrations/welcome/continuous_onboarding_getting_started.html.haml_spec.rb'
- 'ee/spec/views/registrations/welcome/show.html.haml_spec.rb'
- 'ee/spec/views/shared/_clone_panel.html.haml_spec.rb'
- 'ee/spec/views/shared/_kerberos_clone_button.html.haml_spec.rb'
diff --git a/app/assets/javascripts/boards/components/board_content.vue b/app/assets/javascripts/boards/components/board_content.vue
index 66fd2af5aa3..8a37719eae8 100644
--- a/app/assets/javascripts/boards/components/board_content.vue
+++ b/app/assets/javascripts/boards/components/board_content.vue
@@ -116,6 +116,8 @@ export default {
value: this.boardListsToUse,
delay: 100,
delayOnTouchOnly: true,
+ filter: 'input',
+ preventOnFilter: false,
};
return this.canDragColumns ? options : {};
diff --git a/app/assets/javascripts/pages/projects/forks/new/components/fork_form.vue b/app/assets/javascripts/pages/projects/forks/new/components/fork_form.vue
index 2028af8b8f0..85fe3477d7c 100644
--- a/app/assets/javascripts/pages/projects/forks/new/components/fork_form.vue
+++ b/app/assets/javascripts/pages/projects/forks/new/components/fork_form.vue
@@ -16,7 +16,7 @@ import { createAlert } from '~/flash';
import axios from '~/lib/utils/axios_utils';
import csrf from '~/lib/utils/csrf';
import { redirectTo } from '~/lib/utils/url_utility';
-import { s__ } from '~/locale';
+import { s__, __ } from '~/locale';
import validation from '~/vue_shared/directives/validation';
import {
VISIBILITY_LEVEL_PRIVATE_STRING,
@@ -25,8 +25,24 @@ import {
VISIBILITY_LEVELS_STRING_TO_INTEGER,
VISIBILITY_LEVELS_INTEGER_TO_STRING,
} from '~/visibility_level/constants';
+import { START_RULE, CONTAINS_RULE } from '~/projects/project_name_rules';
import ProjectNamespace from './project_namespace.vue';
+const feedbackMap = {
+ valueMissing: {
+ isInvalid: (el) => el.validity?.valueMissing,
+ message: __('Please fill out this field.'),
+ },
+ nameStartPattern: {
+ isInvalid: (el) => el.validity?.patternMismatch && !START_RULE.reg.test(el.value),
+ message: START_RULE.msg,
+ },
+ nameContainsPattern: {
+ isInvalid: (el) => el.validity?.patternMismatch && !CONTAINS_RULE.reg.test(el.value),
+ message: CONTAINS_RULE.msg,
+ },
+};
+
const initFormField = ({ value, required = true, skipValidation = false }) => ({
value,
required,
@@ -48,7 +64,7 @@ export default {
ProjectNamespace,
},
directives: {
- validation: validation(),
+ validation: validation(feedbackMap),
},
inject: {
newGroupPath: {
@@ -109,6 +125,15 @@ export default {
};
},
computed: {
+ projectNameDescription() {
+ if (this.form.fields.name.state === false) {
+ return null;
+ }
+
+ return s__(
+ 'ProjectsNew|Must start with a lowercase or uppercase letter, digit, emoji, or underscore. Can also contain dots, pluses, dashes, or spaces.',
+ );
+ },
projectVisibilityLevel() {
return VISIBILITY_LEVELS_STRING_TO_INTEGER[this.projectVisibility];
},
@@ -248,6 +273,7 @@ export default {
},
},
csrf,
+ projectNamePattern: `(${START_RULE.reg.source})|(${CONTAINS_RULE.reg.source})`,
};
</script>
@@ -257,8 +283,10 @@ export default {
<gl-form-group
:label="__('Project name')"
+ :description="projectNameDescription"
label-for="fork-name"
:invalid-feedback="form.fields.name.feedback"
+ data-testid="fork-name-form-group"
>
<gl-form-input
id="fork-name"
@@ -268,6 +296,7 @@ export default {
data-testid="fork-name-input"
:state="form.fields.name.state"
required
+ :pattern="$options.projectNamePattern"
/>
</gl-form-group>
diff --git a/app/assets/javascripts/projects/project_name_rules.js b/app/assets/javascripts/projects/project_name_rules.js
index eeef1fb5afc..4f62aa29ce4 100644
--- a/app/assets/javascripts/projects/project_name_rules.js
+++ b/app/assets/javascripts/projects/project_name_rules.js
@@ -1,28 +1,29 @@
import { __ } from '~/locale';
-const rulesReg = [
- {
- reg: /^[a-zA-Z0-9\u{00A9}-\u{1f9ff}_]/u,
- msg: __("Name must start with a letter, digit, emoji, or '_'"),
- },
- {
- reg: /^[a-zA-Z0-9\p{Pd}\u{002B}\u{00A9}-\u{1f9ff}_. ]+$/u,
- msg: __("Name can contain only letters, digits, emojis, '_', '.', '+', dashes, or spaces"),
- },
-];
+export const START_RULE = {
+ reg: /^[a-zA-Z0-9\u{00A9}-\u{1f9ff}_]/u,
+ msg: __('Name must start with a letter, digit, emoji, or underscore.'),
+};
+
+export const CONTAINS_RULE = {
+ reg: /^[a-zA-Z0-9\p{Pd}\u{002B}\u{00A9}-\u{1f9ff}_. ]+$/u,
+ msg: __(
+ 'Name can contain only lowercase or uppercase letters, digits, emojis, spaces, dots, underscores, dashes, or pluses.',
+ ),
+};
+
+const rulesReg = [START_RULE, CONTAINS_RULE];
/**
*
* @param {string} text
* @returns {string} msg
*/
-function checkRules(text) {
+export const checkRules = (text) => {
for (const item of rulesReg) {
if (!item.reg.test(text)) {
return item.msg;
}
}
return '';
-}
-
-export { checkRules };
+};
diff --git a/app/assets/javascripts/projects/project_new.js b/app/assets/javascripts/projects/project_new.js
index d71e80dffcf..99ea02aaa4f 100644
--- a/app/assets/javascripts/projects/project_new.js
+++ b/app/assets/javascripts/projects/project_new.js
@@ -90,13 +90,16 @@ const validateGroupNamespaceDropdown = (e) => {
const checkProjectName = (projectNameInput) => {
const msg = checkRules(projectNameInput.value);
- const projectNameError = document.querySelector('#project_name_error');
+ const projectNameError = document.querySelector('#js-project-name-error');
+ const projectNameDescription = document.getElementById('js-project-name-description');
if (!projectNameError) return;
if (msg) {
projectNameError.innerText = msg;
- projectNameError.classList.remove('hidden');
+ projectNameError.classList.remove('gl-display-none');
+ projectNameDescription.classList.add('gl-display-none');
} else {
- projectNameError.classList.add('hidden');
+ projectNameError.classList.add('gl-display-none');
+ projectNameDescription.classList.remove('gl-display-none');
}
};
diff --git a/app/assets/stylesheets/framework/images.scss b/app/assets/stylesheets/framework/images.scss
index 195a66bf9e5..3ac63b8f608 100644
--- a/app/assets/stylesheets/framework/images.scss
+++ b/app/assets/stylesheets/framework/images.scss
@@ -1,4 +1,5 @@
-.appearance-logo-preview {
+.appearance-logo-preview,
+.appearance-pwa-icon-preview {
max-width: 400px;
margin-bottom: 20px;
}
diff --git a/app/controllers/admin/application_settings/appearances_controller.rb b/app/controllers/admin/application_settings/appearances_controller.rb
index 7950c207f69..719e8e4a913 100644
--- a/app/controllers/admin/application_settings/appearances_controller.rb
+++ b/app/controllers/admin/application_settings/appearances_controller.rb
@@ -46,6 +46,15 @@ class Admin::ApplicationSettings::AppearancesController < Admin::ApplicationCont
redirect_to admin_application_settings_appearances_path, notice: _('Header logo was successfully removed.')
end
+ def pwa_icon
+ @appearance.remove_pwa_icon!
+
+ @appearance.save
+
+ redirect_to admin_application_settings_appearances_path,
+ notice: _('Progressive Web App (PWA) icon was successfully removed.')
+ end
+
def favicon
@appearance.remove_favicon!
@appearance.save
@@ -76,6 +85,8 @@ class Admin::ApplicationSettings::AppearancesController < Admin::ApplicationCont
logo_cache
header_logo
header_logo_cache
+ pwa_icon
+ pwa_icon_cache
favicon
favicon_cache
new_project_guidelines
diff --git a/app/models/appearance.rb b/app/models/appearance.rb
index 833f2335774..b926c6abedc 100644
--- a/app/models/appearance.rb
+++ b/app/models/appearance.rb
@@ -27,15 +27,21 @@ class Appearance < ApplicationRecord
cache_markdown_field :footer_message, pipeline: :broadcast_message
validates :pwa_name,
- length: { maximum: 255, message: N_("is too long (maximum is %{count} characters)") },
+ length: { maximum: 255, too_long: ->(object, data) {
+ N_("is too long (maximum is %{count} characters)")
+ } },
allow_blank: true
validates :pwa_short_name,
- length: { maximum: 255, message: N_("is too long (maximum is %{count} characters)") },
+ length: { maximum: 255, too_long: ->(object, data) {
+ N_("is too long (maximum is %{count} characters)")
+ } },
allow_blank: true
validates :pwa_description,
- length: { maximum: 2048, message: N_("is too long (maximum is %{count} characters)") },
+ length: { maximum: 2048, too_long: ->(object, data) {
+ N_("is too long (maximum is %{count} characters)")
+ } },
allow_blank: true
validates :logo, file_size: { maximum: 1.megabyte }
diff --git a/app/views/admin/application_settings/appearances/_form.html.haml b/app/views/admin/application_settings/appearances/_form.html.haml
index c20a86b9f9c..6c6334905ca 100644
--- a/app/views/admin/application_settings/appearances/_form.html.haml
+++ b/app/views/admin/application_settings/appearances/_form.html.haml
@@ -10,7 +10,7 @@
.col-lg-8
.form-group
- = f.label :header_logo, _('Header logo'), class: 'col-form-label label-bold pt-0'
+ = f.label :header_logo, _('Header logo'), class: 'col-form-label gl-pt-0'
%p
- if @appearance.header_logo?
= image_tag @appearance.header_logo_path, class: 'appearance-light-logo-preview'
@@ -29,7 +29,7 @@
.col-lg-8
.form-group
- = f.label :favicon, _('Favicon'), class: 'col-form-label label-bold pt-0'
+ = f.label :favicon, _('Favicon'), class: 'col-form-label gl-pt-0'
%p
- if @appearance.favicon?
= image_tag @appearance.favicon_path, class: 'appearance-light-logo-preview'
@@ -53,15 +53,15 @@
.col-lg-8
.form-group
- = f.label :title, class: 'col-form-label label-bold'
+ = f.label :title, class: 'col-form-label'
= f.text_field :title, class: "form-control gl-form-input"
.form-group
- = f.label :description, class: 'col-form-label label-bold'
+ = f.label :description, class: 'col-form-label'
= f.text_area :description, class: "form-control gl-form-input", rows: 10
.form-text.text-muted
= parsed_with_gfm
.form-group
- = f.label :logo, class: 'col-form-label label-bold pt-0'
+ = f.label :logo, class: 'col-form-label gl-pt-0'
%p
- if @appearance.logo?
= image_tag @appearance.logo_path, class: 'appearance-logo-preview'
@@ -77,11 +77,42 @@
%hr
.row
.col-lg-4.profile-settings-sidebar
+ %h4.gl-mt-0= _('Progressive Web App (PWA)')
+
+ .col-lg-8
+ .form-group
+ = f.label _("Name"), class: 'col-form-label'
+ = f.text_field :pwa_name, class: "form-control gl-form-input"
+ .form-group
+ = f.label _("Short name"), class: 'col-form-label'
+ = f.text_field :pwa_short_name, class: "form-control gl-form-input"
+ .form-group
+ = f.label _("Description"), class: 'col-form-label'
+ = f.text_area :pwa_description, class: "form-control gl-form-input", rows: 10
+ .form-text.text-muted
+ = parsed_with_gfm
+ .form-group
+ = f.label :pwa_icon, class: 'col-form-label gl-pt-0'
+ %p
+ - if @appearance.pwa_icon?
+ = image_tag @appearance.pwa_icon_path, class: 'appearance-pwa-icon-preview'
+ - if @appearance.persisted?
+ %br
+ = link_to _('Remove icon'), pwa_icon_admin_application_settings_appearances_path, data: { confirm: _("Icon will be removed. Are you sure?"), confirm_btn_variant: "danger" }, aria: { label: _('Remove icon') }, method: :delete, class: "btn gl-button btn-danger btn-danger-secondary btn-sm remove-logo"
+ %hr
+ = f.hidden_field :pwa_icon_cache
+ = f.file_field :pwa_icon, class: "", accept: 'image/*'
+ .form-text.text-muted
+ = _('Maximum file size is 1MB.')
+
+ %hr
+ .row
+ .col-lg-4.profile-settings-sidebar
%h4.gl-mt-0= _('New project pages')
.col-lg-8
.form-group
- = f.label :new_project_guidelines, class: 'col-form-label label-bold'
+ = f.label :new_project_guidelines, class: 'col-form-label'
%p
= f.text_area :new_project_guidelines, class: "form-control gl-form-input", rows: 10
.form-text.text-muted
@@ -94,7 +125,7 @@
.col-lg-8
.form-group
- = f.label :profile_image_guidelines, class: 'col-form-label label-bold'
+ = f.label :profile_image_guidelines, class: 'col-form-label'
%p
= f.text_area :profile_image_guidelines, class: "form-control gl-form-input", rows: 10
.form-text.text-muted
diff --git a/app/views/projects/_new_project_fields.html.haml b/app/views/projects/_new_project_fields.html.haml
index ec83782985b..53a1abdff33 100644
--- a/app/views/projects/_new_project_fields.html.haml
+++ b/app/views/projects/_new_project_fields.html.haml
@@ -6,11 +6,13 @@
.row{ id: project_name_id }
= f.hidden_field :ci_cd_only, value: ci_cd_only
- .form-group.project-name.col-sm-12
+ .form-group.gl-form-group.project-name.col-sm-12
= f.label :name, class: 'label-bold' do
%span= _("Project name")
= f.text_field :name, placeholder: "My awesome project", class: "form-control gl-form-input input-lg", data: { qa_selector: 'project_name', track_label: "#{track_label}", track_action: "activate_form_input", track_property: "project_name", track_value: "" }, required: true, aria: { required: true }
- #project_name_error.gl-field-error.hidden
+ %small#js-project-name-description.form-text.text-gl-muted
+ = s_("ProjectsNew|Must start with a lowercase or uppercase letter, digit, emoji, or underscore. Can also contain dots, pluses, dashes, or spaces.")
+ #js-project-name-error.gl-field-error.gl-mt-2.gl-display-none
.form-group.project-path.col-sm-6.gl-pr-0
= f.label :namespace_id, class: 'label-bold' do
%span= _('Project URL')
diff --git a/config/feature_flags/development/improved_spread_parallel_import.yml b/config/feature_flags/development/improved_spread_parallel_import.yml
index 803af69344f..a1d7caf12b0 100644
--- a/config/feature_flags/development/improved_spread_parallel_import.yml
+++ b/config/feature_flags/development/improved_spread_parallel_import.yml
@@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/388665
milestone: '15.9'
type: development
group: group::import
-default_enabled: false
+default_enabled: true
diff --git a/config/locales/en.yml b/config/locales/en.yml
index d5c7eecb068..796ca56c604 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -5,6 +5,11 @@ en:
hello: "Hello world"
activerecord:
attributes:
+ appearance:
+ pwa_name: "PWA name"
+ pwa_short_name: "PWA short name"
+ pwa_description: "PWA description"
+ pwa_icon: "Icon"
incident_management/timeline_event:
note: 'Timeline text'
issue_link:
diff --git a/config/routes.rb b/config/routes.rb
index eec3bb944fb..af1ecc687e0 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -61,11 +61,7 @@ InitializerConnections.with_disabled_database_connections do
# Sign up
scope path: '/users/sign_up', module: :registrations, as: :users_sign_up do
- resource :welcome, only: [:show, :update], controller: 'welcome' do
- Gitlab.ee do
- get :continuous_onboarding_getting_started, on: :collection
- end
- end
+ resource :welcome, only: [:show, :update], controller: 'welcome'
Gitlab.ee do
resource :company, only: [:new, :create], controller: 'company'
diff --git a/config/routes/admin.rb b/config/routes/admin.rb
index 6d1c3c661c0..9181c1c94cf 100644
--- a/config/routes/admin.rb
+++ b/config/routes/admin.rb
@@ -155,6 +155,7 @@ namespace :admin do
member do
get :preview_sign_in
delete :logo
+ delete :pwa_icon
delete :header_logos
delete :favicon
end
diff --git a/db/post_migrate/20230126091522_add_unique_index_to_ci_build_pending_state.rb b/db/post_migrate/20230126091522_add_unique_index_to_ci_build_pending_state.rb
new file mode 100644
index 00000000000..484b64feb97
--- /dev/null
+++ b/db/post_migrate/20230126091522_add_unique_index_to_ci_build_pending_state.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+class AddUniqueIndexToCiBuildPendingState < Gitlab::Database::Migration[2.1]
+ disable_ddl_transaction!
+
+ OLD_INDEX_NAME = :index_ci_build_pending_states_on_partition_id_build_id
+ NEW_INDEX_NAME = :unique_index_ci_build_pending_states_on_partition_id_build_id
+ TABLE_NAME = :ci_build_pending_states
+ COLUMNS = [:partition_id, :build_id]
+
+ def up
+ add_concurrent_index(TABLE_NAME, COLUMNS, unique: true, name: NEW_INDEX_NAME)
+
+ remove_concurrent_index_by_name(TABLE_NAME, OLD_INDEX_NAME)
+ end
+
+ def down
+ add_concurrent_index(TABLE_NAME, COLUMNS, name: OLD_INDEX_NAME)
+
+ remove_concurrent_index_by_name(TABLE_NAME, NEW_INDEX_NAME)
+ end
+end
diff --git a/db/schema_migrations/20230126091522 b/db/schema_migrations/20230126091522
new file mode 100644
index 00000000000..b179f736257
--- /dev/null
+++ b/db/schema_migrations/20230126091522
@@ -0,0 +1 @@
+48276b76dbedc046f7270e7204558ab045e48a6d7da9e1c0a58a76b06e51bfa5 \ No newline at end of file
diff --git a/db/structure.sql b/db/structure.sql
index ad2d102b5ee..69bc358b06e 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -29081,8 +29081,6 @@ CREATE INDEX index_ci_build_needs_on_partition_id_build_id ON ci_build_needs USI
CREATE UNIQUE INDEX index_ci_build_pending_states_on_build_id ON ci_build_pending_states USING btree (build_id);
-CREATE INDEX index_ci_build_pending_states_on_partition_id_build_id ON ci_build_pending_states USING btree (partition_id, build_id);
-
CREATE UNIQUE INDEX index_ci_build_report_results_on_partition_id_build_id ON ci_build_report_results USING btree (partition_id, build_id);
CREATE INDEX index_ci_build_report_results_on_project_id ON ci_build_report_results USING btree (project_id);
@@ -32207,6 +32205,8 @@ CREATE UNIQUE INDEX uniq_pkgs_debian_project_distributions_project_id_and_suite
CREATE UNIQUE INDEX unique_ci_builds_token_encrypted_and_partition_id ON ci_builds USING btree (token_encrypted, partition_id) WHERE (token_encrypted IS NOT NULL);
+CREATE UNIQUE INDEX unique_index_ci_build_pending_states_on_partition_id_build_id ON ci_build_pending_states USING btree (partition_id, build_id);
+
CREATE UNIQUE INDEX unique_merge_request_metrics_by_merge_request_id ON merge_request_metrics USING btree (merge_request_id);
CREATE UNIQUE INDEX unique_projects_on_name_namespace_id ON projects USING btree (name, namespace_id);
diff --git a/doc/api/members.md b/doc/api/members.md
index 4032ab1d651..950289effd2 100644
--- a/doc/api/members.md
+++ b/doc/api/members.md
@@ -574,6 +574,7 @@ PUT /projects/:id/members/:user_id
| `user_id` | integer | yes | The user ID of the member |
| `access_level` | integer | yes | A valid access level |
| `expires_at` | string | no | A date string in the format `YEAR-MONTH-DAY` |
+| `member_role_id` | integer | no | The ID of a member role **(ULTIMATE)** |
```shell
curl --request PUT --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/groups/:id/members/:user_id?access_level=40"
diff --git a/doc/ci/environments/index.md b/doc/ci/environments/index.md
index 887b51f2832..bee923c9648 100644
--- a/doc/ci/environments/index.md
+++ b/doc/ci/environments/index.md
@@ -618,6 +618,16 @@ Because `stop_review_app` is set to `auto_stop_in: 1 week`,
if a merge request is inactive for more than a week,
GitLab automatically triggers the `stop_review_app` job to stop the environment.
+#### Stop an environment without running the `on_stop` action
+
+There may be times when you want to stop an environment without running the defined
+[`on_stop`](../yaml/index.md#environmenton_stop) action. For example, you want to delete many
+environments without using CI/CD minutes.
+
+To stop an environment without running the defined `on_stop` action, execute the
+[Stop an environment API](../../api/environments.md#stop-an-environment) with the parameter
+`force=true`.
+
#### Stop an environment through the UI
NOTE:
diff --git a/doc/user/project/index.md b/doc/user/project/index.md
index d30136e57e4..decf3b071e7 100644
--- a/doc/user/project/index.md
+++ b/doc/user/project/index.md
@@ -16,8 +16,7 @@ To create a blank project:
1. On the right of the page, select **New project**.
1. Select **Create blank project**.
1. Enter the project details:
- - In the **Project name** field, enter the name of your project. You cannot use special characters at
- the start or end of a project name.
+ - In the **Project name** field, enter the name of your project. The name must start with a lowercase or uppercase letter (`a-zA-Z`), digit (`0-9`), emoji, or underscore (`_`). It can also contain dots (`.`), pluses (`+`), dashes (`-`), or spaces.
- In the **Project slug** field, enter the path to your project. The GitLab instance uses the
slug as the URL path to the project. To change the slug, first enter the project name,
then change the slug.
@@ -51,8 +50,7 @@ To create a project from a built-in template:
- To view a preview of the template, select **Preview**.
- To use a template for the project, select **Use template**.
1. Enter the project details:
- - In the **Project name** field, enter the name of your project. You cannot use special characters at
- the start or end of a project name.
+ - In the **Project name** field, enter the name of your project. The name must start with a lowercase or uppercase letter (`a-zA-Z`), digit (`0-9`), emoji, or underscore (`_`). It can also contain dots (`.`), pluses (`+`), dashes (`-`), or spaces.
- In the **Project slug** field, enter the path to your project. The GitLab instance uses the
slug as the URL path to the project. To change the slug, first enter the project name,
then change the slug.
@@ -83,8 +81,7 @@ Custom project templates are available at:
- To view a preview of the template, select **Preview**.
- To use a template for the project, select **Use template**.
1. Enter the project details:
- - In the **Project name** field, enter the name of your project. You cannot use special characters at
- the start or end of a project name.
+ - In the **Project name** field, enter the name of your project. The name must start with a lowercase or uppercase letter (`a-zA-Z`), digit (`0-9`), emoji, or underscore (`_`). It can also contain dots (`.`), pluses (`+`), dashes (`-`), or spaces.
- In the **Project slug** field, enter the path to your project. The GitLab instance uses the
slug as the URL path to the project. To change the slug, first enter the project name,
then change the slug.
@@ -110,8 +107,7 @@ To create a project from the HIPAA Audit Protocol template:
- To view a preview of the template, select **Preview**.
- To use the template for the project, select **Use template**.
1. Enter the project details:
- - In the **Project name** field, enter the name of your project. You cannot use special characters at
- the start or end of a project name.
+ - In the **Project name** field, enter the name of your project. The name must start with a lowercase or uppercase letter (`a-zA-Z`), digit (`0-9`), emoji, or underscore (`_`). It can also contain dots (`.`), pluses (`+`), dashes (`-`), or spaces.
- In the **Project slug** field, enter the path to your project. The GitLab instance uses the
slug as the URL path to the project. To change the slug, first enter the project name,
then change the slug.
diff --git a/lib/api/helpers/members_helpers.rb b/lib/api/helpers/members_helpers.rb
index a406a62344a..4b34a2bbe79 100644
--- a/lib/api/helpers/members_helpers.rb
+++ b/lib/api/helpers/members_helpers.rb
@@ -11,6 +11,9 @@ module API
params :optional_state_filter_ee do
end
+ params :optional_put_params_ee do
+ end
+
def find_source(source_type, id)
public_send("find_#{source_type}!", id) # rubocop:disable GitlabSecurity/PublicSend
end
diff --git a/lib/api/members.rb b/lib/api/members.rb
index 32c5227a939..1e640a6542a 100644
--- a/lib/api/members.rb
+++ b/lib/api/members.rb
@@ -139,6 +139,7 @@ module API
requires :user_id, type: Integer, desc: 'The user ID of the new member'
requires :access_level, type: Integer, desc: 'A valid access level'
optional :expires_at, type: DateTime, desc: 'Date string in the format YEAR-MONTH-DAY'
+ use :optional_put_params_ee
end
# rubocop: disable CodeReuse/ActiveRecord
put ":id/members/:user_id", feature_category: feature_category do
diff --git a/lib/gitlab/cache/helpers.rb b/lib/gitlab/cache/helpers.rb
index 024fa48c066..0fc0b1504af 100644
--- a/lib/gitlab/cache/helpers.rb
+++ b/lib/gitlab/cache/helpers.rb
@@ -45,7 +45,14 @@ module Gitlab
def contextual_cache_key(presenter, object, context)
return object.cache_key if context.nil?
- [presenter.class.name, object.cache_key, context.call(object)].flatten.join(":")
+ [presenter_class_name(presenter), object.cache_key, context.call(object)].flatten.join(":")
+ end
+
+ def presenter_class_name(presenter)
+ return presenter.class.name if presenter.is_a?(BaseSerializer)
+ return presenter.name if presenter.is_a?(Class) && presenter < Grape::Entity
+
+ raise ArgumentError, "presenter #{presenter} is not supported"
end
# Used for fetching or rendering a single object
diff --git a/lib/gitlab/database/migrations/base_background_runner.rb b/lib/gitlab/database/migrations/base_background_runner.rb
index 8975c04e33a..840add8783d 100644
--- a/lib/gitlab/database/migrations/base_background_runner.rb
+++ b/lib/gitlab/database/migrations/base_background_runner.rb
@@ -38,13 +38,15 @@ module Gitlab
def run_jobs_for_migration(migration_name:, jobs:, run_until:)
per_background_migration_result_dir = File.join(@result_dir, migration_name)
- instrumentation = Instrumentation.new(result_dir: per_background_migration_result_dir)
+ instrumentation = Instrumentation.new(result_dir: per_background_migration_result_dir,
+ observer_classes: observers)
+
batch_names = (1..).each.lazy.map { |i| "batch_#{i}" }
jobs.each do |j|
break if run_until <= Time.current
- meta = migration_meta(j)
+ meta = { job_meta: job_meta(j) }
instrumentation.observe(version: nil,
name: batch_names.next,
@@ -55,9 +57,13 @@ module Gitlab
end
end
- def migration_meta(_job)
+ def job_meta(_job)
{}
end
+
+ def observers
+ ::Gitlab::Database::Migrations::Observers.all_observers
+ end
end
end
end
diff --git a/lib/gitlab/database/migrations/observation.rb b/lib/gitlab/database/migrations/observation.rb
index 80388c4dbbb..cd048beac96 100644
--- a/lib/gitlab/database/migrations/observation.rb
+++ b/lib/gitlab/database/migrations/observation.rb
@@ -4,16 +4,12 @@
module Gitlab
module Database
module Migrations
- Observation = Struct.new(
- :version,
- :name,
- :walltime,
- :success,
- :total_database_size_change,
- :meta,
- :query_statistics,
- keyword_init: true
- )
+ Observation = Struct.new(:version, :name, :walltime, :success, :total_database_size_change,
+ :meta, :query_statistics, keyword_init: true) do
+ def to_json(...)
+ as_json.except('meta').to_json(...)
+ end
+ end
end
end
end
diff --git a/lib/gitlab/database/migrations/observers/batch_details.rb b/lib/gitlab/database/migrations/observers/batch_details.rb
new file mode 100644
index 00000000000..0f8cdcf3cd6
--- /dev/null
+++ b/lib/gitlab/database/migrations/observers/batch_details.rb
@@ -0,0 +1,53 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Database
+ module Migrations
+ module Observers
+ class BatchDetails < MigrationObserver
+ FILE_NAME = 'batch-details.json'
+
+ def before
+ @started_at = get_time
+ end
+
+ def after
+ @finished_at = get_time
+ end
+
+ def record
+ File.open(path, 'wb') { |file| file.write(file_contents.to_json) }
+ end
+
+ private
+
+ attr_reader :started_at, :finished_at
+
+ def file_contents
+ { time_spent: time_spent }.merge(job_meta)
+ end
+
+ def get_time
+ Process.clock_gettime(Process::CLOCK_MONOTONIC)
+ end
+
+ def time_spent
+ @time_spent ||= (finished_at - started_at).round(2)
+ end
+
+ def path
+ File.join(output_dir, FILE_NAME)
+ end
+
+ def job_meta
+ meta = observation.meta
+
+ return {} unless meta
+
+ meta[:job_meta].to_h
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/database/migrations/test_batched_background_runner.rb b/lib/gitlab/database/migrations/test_batched_background_runner.rb
index c123d01f327..3063430091d 100644
--- a/lib/gitlab/database/migrations/test_batched_background_runner.rb
+++ b/lib/gitlab/database/migrations/test_batched_background_runner.rb
@@ -6,6 +6,8 @@ module Gitlab
class TestBatchedBackgroundRunner < BaseBackgroundRunner
include Gitlab::Database::DynamicModelHelpers
+ MIGRATION_DETAILS_FILE_NAME = 'details.json'
+
def initialize(result_dir:, connection:, from_id:)
super(result_dir: result_dir, connection: connection)
@connection = connection
@@ -64,7 +66,11 @@ module Gitlab
end
end
- [migration.job_class_name, jobs_to_sample]
+ job_class_name = migration.job_class_name
+
+ export_migration_details(job_class_name, migration.slice(:interval, :total_tuple_count, :max_batch_size))
+
+ [job_class_name, jobs_to_sample]
end
end
end
@@ -112,11 +118,25 @@ module Gitlab
Gitlab::Database::SharedModel.using_connection(connection, &block)
end
- def migration_meta(job)
+ def job_meta(job)
set_shared_model_connection do
- job.batched_migration.slice(:max_batch_size, :total_tuple_count, :interval)
+ job.slice(:min_value, :max_value, :batch_size, :sub_batch_size, :pause_ms)
end
end
+
+ def export_migration_details(migration_name, attributes)
+ directory = result_dir.join(migration_name)
+
+ FileUtils.mkdir_p(directory) unless Dir.exist?(directory)
+
+ File.write(directory.join(MIGRATION_DETAILS_FILE_NAME), attributes.to_json)
+ end
+
+ def observers
+ ::Gitlab::Database::Migrations::Observers.all_observers + [
+ ::Gitlab::Database::Migrations::Observers::BatchDetails
+ ]
+ end
end
end
end
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 9b002c7bdcc..78d10e5b54d 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -20969,6 +20969,9 @@ msgstr ""
msgid "IP subnet restriction only allowed for top-level groups"
msgstr ""
+msgid "Icon will be removed. Are you sure?"
+msgstr ""
+
msgid "Id"
msgstr ""
@@ -25969,6 +25972,9 @@ msgstr ""
msgid "Maximum file size is 1 MB. Pages are optimized for a 128x128 px logo."
msgstr ""
+msgid "Maximum file size is 1MB."
+msgstr ""
+
msgid "Maximum file size is 1MB. Pages are optimized for a 24px tall header logo"
msgstr ""
@@ -27573,7 +27579,7 @@ msgstr ""
msgid "Name"
msgstr ""
-msgid "Name can contain only letters, digits, emojis, '_', '.', '+', dashes, or spaces"
+msgid "Name can contain only lowercase or uppercase letters, digits, emojis, spaces, dots, underscores, dashes, or pluses."
msgstr ""
msgid "Name can't be blank"
@@ -27585,7 +27591,7 @@ msgstr ""
msgid "Name is already taken."
msgstr ""
-msgid "Name must start with a letter, digit, emoji, or '_'"
+msgid "Name must start with a letter, digit, emoji, or underscore."
msgstr ""
msgid "Name new label"
@@ -32891,6 +32897,12 @@ msgstr ""
msgid "Progress tracking"
msgstr ""
+msgid "Progressive Web App (PWA)"
+msgstr ""
+
+msgid "Progressive Web App (PWA) icon was successfully removed."
+msgstr ""
+
msgid "Project"
msgstr ""
@@ -34022,6 +34034,9 @@ msgstr ""
msgid "ProjectsNew|Migrate your data from an external source like GitHub, Bitbucket, or another instance of GitLab."
msgstr ""
+msgid "ProjectsNew|Must start with a lowercase or uppercase letter, digit, emoji, or underscore. Can also contain dots, pluses, dashes, or spaces."
+msgstr ""
+
msgid "ProjectsNew|No import options available"
msgstr ""
@@ -35436,6 +35451,9 @@ msgstr ""
msgid "Remove header logo"
msgstr ""
+msgid "Remove icon"
+msgstr ""
+
msgid "Remove iteration"
msgstr ""
@@ -39514,6 +39532,9 @@ msgstr ""
msgid "Shimo|You've enabled the Shimo Workspace integration. You can view your wiki directly in Shimo."
msgstr ""
+msgid "Short name"
+msgstr ""
+
msgid "Should you ever lose your phone or access to your one time password secret, each of these recovery codes can be used one time each to regain access to your account. Please save them in a safe place, or you %{boldStart}will%{boldEnd} lose access to your account."
msgstr ""
diff --git a/spec/factories/member_roles.rb b/spec/factories/member_roles.rb
index 08df45a85f8..503438d2521 100644
--- a/spec/factories/member_roles.rb
+++ b/spec/factories/member_roles.rb
@@ -5,6 +5,7 @@ FactoryBot.define do
namespace { association(:group) }
base_access_level { Gitlab::Access::DEVELOPER }
- trait(:guest) { base_access_level { GroupMember::GUEST } }
+ trait(:developer) { base_access_level { Gitlab::Access::DEVELOPER } }
+ trait(:guest) { base_access_level { Gitlab::Access::GUEST } }
end
end
diff --git a/spec/features/admin/admin_appearance_spec.rb b/spec/features/admin/admin_appearance_spec.rb
index 5fbe7039c1d..252d9ac5bac 100644
--- a/spec/features/admin/admin_appearance_spec.rb
+++ b/spec/features/admin/admin_appearance_spec.rb
@@ -19,6 +19,9 @@ RSpec.describe 'Admin Appearance', feature_category: :not_owned do
fill_in 'appearance_title', with: 'MyCompany'
fill_in 'appearance_description', with: 'dev server'
+ fill_in 'appearance_pwa_name', with: 'GitLab PWA'
+ fill_in 'appearance_pwa_short_name', with: 'GitLab'
+ fill_in 'appearance_pwa_description', with: 'GitLab as PWA'
fill_in 'appearance_new_project_guidelines', with: 'Custom project guidelines'
fill_in 'appearance_profile_image_guidelines', with: 'Custom profile image guidelines'
click_button 'Update appearance settings'
@@ -28,6 +31,9 @@ RSpec.describe 'Admin Appearance', feature_category: :not_owned do
expect(page).to have_field('appearance_title', with: 'MyCompany')
expect(page).to have_field('appearance_description', with: 'dev server')
+ expect(page).to have_field('appearance_pwa_name', with: 'GitLab PWA')
+ expect(page).to have_field('appearance_pwa_short_name', with: 'GitLab')
+ expect(page).to have_field('appearance_pwa_description', with: 'GitLab as PWA')
expect(page).to have_field('appearance_new_project_guidelines', with: 'Custom project guidelines')
expect(page).to have_field('appearance_profile_image_guidelines', with: 'Custom profile image guidelines')
expect(page).to have_content 'Last edit'
@@ -135,6 +141,19 @@ RSpec.describe 'Admin Appearance', feature_category: :not_owned do
expect(page).not_to have_css(logo_selector)
end
+ it 'appearance pwa icon' do
+ sign_in(admin)
+ gitlab_enable_admin_mode_sign_in(admin)
+ visit admin_application_settings_appearances_path
+
+ attach_file(:appearance_pwa_icon, logo_fixture)
+ click_button 'Update appearance settings'
+ expect(page).to have_css(pwa_icon_selector)
+
+ click_link 'Remove icon'
+ expect(page).not_to have_css(pwa_icon_selector)
+ end
+
it 'header logos' do
sign_in(admin)
gitlab_enable_admin_mode_sign_in(admin)
@@ -183,6 +202,10 @@ RSpec.describe 'Admin Appearance', feature_category: :not_owned do
'//img[data-src^="/uploads/-/system/appearance/logo"]'
end
+ def pwa_icon_selector
+ '//img[data-src^="/uploads/-/system/appearance/pwa_icon"]'
+ end
+
def header_logo_selector
'//img[data-src^="/uploads/-/system/appearance/header_logo"]'
end
diff --git a/spec/frontend/pages/projects/forks/new/components/fork_form_spec.js b/spec/frontend/pages/projects/forks/new/components/fork_form_spec.js
index aee56247209..f0593a854b2 100644
--- a/spec/frontend/pages/projects/forks/new/components/fork_form_spec.js
+++ b/spec/frontend/pages/projects/forks/new/components/fork_form_spec.js
@@ -12,6 +12,7 @@ import ForkForm from '~/pages/projects/forks/new/components/fork_form.vue';
import createMockApollo from 'helpers/mock_apollo_helper';
import searchQuery from '~/pages/projects/forks/new/queries/search_forkable_namespaces.query.graphql';
import ProjectNamespace from '~/pages/projects/forks/new/components/project_namespace.vue';
+import { START_RULE, CONTAINS_RULE } from '~/projects/project_name_rules';
jest.mock('~/flash');
jest.mock('~/lib/utils/csrf', () => ({ token: 'mock-csrf-token' }));
@@ -475,6 +476,43 @@ describe('ForkForm component', () => {
expect(axios.post).not.toHaveBeenCalled();
});
+
+ describe('project name', () => {
+ it.each`
+ value | expectedErrorMessage
+ ${'?'} | ${START_RULE.msg}
+ ${'*'} | ${START_RULE.msg}
+ ${'a?'} | ${CONTAINS_RULE.msg}
+ ${'a*'} | ${CONTAINS_RULE.msg}
+ `(
+ 'shows "$expectedErrorMessage" error when value is $value',
+ async ({ value, expectedErrorMessage }) => {
+ createFullComponent();
+
+ findForkNameInput().vm.$emit('input', value);
+ await nextTick();
+ await submitForm();
+
+ const formGroup = wrapper.findComponent('[data-testid="fork-name-form-group"]');
+
+ expect(formGroup.vm.$attrs['invalid-feedback']).toBe(expectedErrorMessage);
+ expect(formGroup.vm.$attrs.description).toBe(null);
+ },
+ );
+
+ it.each(['a', '9', 'aa', '99'])('does not show error when value is %s', async (value) => {
+ createFullComponent();
+
+ findForkNameInput().vm.$emit('input', value);
+ await nextTick();
+ await submitForm();
+
+ const formGroup = wrapper.findComponent('[data-testid="fork-name-form-group"]');
+
+ expect(formGroup.vm.$attrs['invalid-feedback']).toBe('');
+ expect(formGroup.vm.$attrs.description).not.toBe(null);
+ });
+ });
});
describe('with valid form', () => {
diff --git a/spec/frontend/projects/project_new_spec.js b/spec/frontend/projects/project_new_spec.js
index d69bfc4ec92..8a1e9904a3f 100644
--- a/spec/frontend/projects/project_new_spec.js
+++ b/spec/frontend/projects/project_new_spec.js
@@ -9,6 +9,7 @@ describe('New Project', () => {
let $projectPath;
let $projectName;
let $projectNameError;
+ let $projectNameDescription;
const mockKeyup = (el) => el.dispatchEvent(new KeyboardEvent('keyup'));
const mockChange = (el) => el.dispatchEvent(new Event('change'));
@@ -31,7 +32,8 @@ describe('New Project', () => {
</div>
</div>
<input id="project_name" />
- <div class="gl-field-error hidden" id="project_name_error" />
+ <small id="js-project-name-description" />
+ <div class="gl-field-error gl-display-none" id="js-project-name-error" />
<input id="project_path" />
</div>
<div class="js-user-readme-repo"></div>
@@ -44,7 +46,8 @@ describe('New Project', () => {
$projectImportUrl = document.querySelector('#project_import_url');
$projectPath = document.querySelector('#project_path');
$projectName = document.querySelector('#project_name');
- $projectNameError = document.querySelector('#project_name_error');
+ $projectNameError = document.querySelector('#js-project-name-error');
+ $projectNameDescription = document.querySelector('#js-project-name-description');
});
afterEach(() => {
@@ -98,7 +101,7 @@ describe('New Project', () => {
});
it('no error message by default', () => {
- expect($projectNameError.classList.contains('hidden')).toBe(true);
+ expect($projectNameError.classList.contains('gl-display-none')).toBe(true);
});
it('show error message if name is validate', () => {
@@ -106,15 +109,16 @@ describe('New Project', () => {
triggerEvent($projectName, 'change');
expect($projectNameError.innerText).toBe(
- "Name must start with a letter, digit, emoji, or '_'",
+ 'Name must start with a letter, digit, emoji, or underscore.',
);
- expect($projectNameError.classList.contains('hidden')).toBe(false);
+ expect($projectNameError.classList.contains('gl-display-none')).toBe(false);
+ expect($projectNameDescription.classList.contains('gl-display-none')).toBe(true);
});
});
describe('project name rule', () => {
describe("Name must start with a letter, digit, emoji, or '_'", () => {
- const errormsg = "Name must start with a letter, digit, emoji, or '_'";
+ const errormsg = 'Name must start with a letter, digit, emoji, or underscore.';
it("'.foo' should error", () => {
const text = '.foo';
expect(checkRules(text)).toBe(errormsg);
@@ -127,7 +131,7 @@ describe('New Project', () => {
describe("Name can contain only letters, digits, emojis, '_', '.', '+', dashes, or spaces", () => {
const errormsg =
- "Name can contain only letters, digits, emojis, '_', '.', '+', dashes, or spaces";
+ 'Name can contain only lowercase or uppercase letters, digits, emojis, spaces, dots, underscores, dashes, or pluses.';
it("'foo(#^.^#)foo' should error", () => {
const text = 'foo(#^.^#)foo';
expect(checkRules(text)).toBe(errormsg);
diff --git a/spec/lib/api/helpers/caching_spec.rb b/spec/lib/api/helpers/caching_spec.rb
index 828af7b5f91..03025823357 100644
--- a/spec/lib/api/helpers/caching_spec.rb
+++ b/spec/lib/api/helpers/caching_spec.rb
@@ -47,12 +47,14 @@ RSpec.describe API::Helpers::Caching, :use_clean_rails_redis_caching do
context 'single object' do
let_it_be(:presentable) { create(:todo, project: project) }
+ let(:expected_cache_key_prefix) { 'API::Entities::Todo' }
it_behaves_like 'object cache helper'
end
context 'collection of objects' do
let_it_be(:presentable) { Array.new(5).map { create(:todo, project: project) } }
+ let(:expected_cache_key_prefix) { 'API::Entities::Todo' }
it_behaves_like 'collection cache helper'
end
diff --git a/spec/lib/gitlab/cache/helpers_spec.rb b/spec/lib/gitlab/cache/helpers_spec.rb
index 39d37e979b4..06131ee4546 100644
--- a/spec/lib/gitlab/cache/helpers_spec.rb
+++ b/spec/lib/gitlab/cache/helpers_spec.rb
@@ -33,10 +33,23 @@ RSpec.describe Gitlab::Cache::Helpers, :use_clean_rails_redis_caching do
context 'single object' do
let_it_be(:presentable) { create(:merge_request, source_project: project, source_branch: 'wip') }
- it_behaves_like 'object cache helper'
+ context 'when presenter is a serializer' do
+ let(:expected_cache_key_prefix) { 'MergeRequestSerializer' }
+
+ it_behaves_like 'object cache helper'
+ end
+
+ context 'when presenter is a Grape::Entity' do
+ let(:presenter) { API::Entities::MergeRequest }
+ let(:expected_cache_key_prefix) { 'API::Entities::MergeRequest' }
+
+ it_behaves_like 'object cache helper'
+ end
end
context 'collection of objects' do
+ let(:expected_cache_key_prefix) { 'MergeRequestSerializer' }
+
let_it_be(:presentable) do
[
create(:merge_request, source_project: project, source_branch: 'fix'),
@@ -46,5 +59,17 @@ RSpec.describe Gitlab::Cache::Helpers, :use_clean_rails_redis_caching do
it_behaves_like 'collection cache helper'
end
+
+ context 'when passed presenter is not a serializer or an entity' do
+ let(:presenter) { User }
+
+ let_it_be(:presentable) do
+ create(:merge_request, source_project: project, source_branch: 'master')
+ end
+
+ it 'throws an exception' do
+ expect { subject }.to raise_exception(ArgumentError, "presenter User is not supported")
+ end
+ end
end
end
diff --git a/spec/lib/gitlab/database/migrations/instrumentation_spec.rb b/spec/lib/gitlab/database/migrations/instrumentation_spec.rb
index b0bdbf5c371..4f347034c0b 100644
--- a/spec/lib/gitlab/database/migrations/instrumentation_spec.rb
+++ b/spec/lib/gitlab/database/migrations/instrumentation_spec.rb
@@ -18,6 +18,7 @@ RSpec.describe Gitlab::Database::Migrations::Instrumentation do
let(:migration_name) { 'test' }
let(:migration_version) { '12345' }
let(:migration_meta) { { 'max_batch_size' => 1, 'total_tuple_count' => 10, 'interval' => 60 } }
+ let(:expected_json_keys) { %w[version name walltime success total_database_size_change query_statistics] }
it 'executes the given block' do
expect do |b|
@@ -81,7 +82,10 @@ RSpec.describe Gitlab::Database::Migrations::Instrumentation do
expect(subject.success).to be_truthy
expect(subject.version).to eq(migration_version)
expect(subject.name).to eq(migration_name)
- expect(subject.meta).to eq(migration_meta)
+ end
+
+ it 'transforms observation to expected json' do
+ expect(Gitlab::Json.parse(subject.to_json).keys).to contain_exactly(*expected_json_keys)
end
end
@@ -114,7 +118,10 @@ RSpec.describe Gitlab::Database::Migrations::Instrumentation do
expect(subject['success']).to be_falsey
expect(subject['version']).to eq(migration_version)
expect(subject['name']).to eq(migration_name)
- expect(subject['meta']).to include(migration_meta)
+ end
+
+ it 'transforms observation to expected json' do
+ expect(Gitlab::Json.parse(subject.to_json).keys).to contain_exactly(*expected_json_keys)
end
end
end
diff --git a/spec/lib/gitlab/database/migrations/observers/batch_details_spec.rb b/spec/lib/gitlab/database/migrations/observers/batch_details_spec.rb
new file mode 100644
index 00000000000..5b3c23736ed
--- /dev/null
+++ b/spec/lib/gitlab/database/migrations/observers/batch_details_spec.rb
@@ -0,0 +1,42 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Database::Migrations::Observers::BatchDetails, feature_category: :database do
+ subject(:observe) { described_class.new(observation, path, connection) }
+
+ let(:connection) { ActiveRecord::Migration.connection }
+ let(:observation) { Gitlab::Database::Migrations::Observation.new(meta: meta) }
+ let(:path) { Dir.mktmpdir }
+ let(:file_name) { 'batch-details.json' }
+ let(:file_path) { Pathname.new(path).join(file_name) }
+ let(:json_file) { Gitlab::Json.parse(File.read(file_path)) }
+ let(:job_meta) do
+ { "min_value" => 1, "max_value" => 19, "batch_size" => 20, "sub_batch_size" => 5, "pause_ms" => 100 }
+ end
+
+ where(:meta, :expected_keys) do
+ [
+ [lazy { { job_meta: job_meta } }, %w[time_spent min_value max_value batch_size sub_batch_size pause_ms]],
+ [nil, %w[time_spent]],
+ [{ job_meta: nil }, %w[time_spent]]
+ ]
+ end
+
+ with_them do
+ before do
+ observe.before
+ observe.after
+ end
+
+ after do
+ FileUtils.remove_entry(path)
+ end
+
+ it 'records expected information to file' do
+ observe.record
+
+ expect(json_file.keys).to match_array(expected_keys)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/database/migrations/test_batched_background_runner_spec.rb b/spec/lib/gitlab/database/migrations/test_batched_background_runner_spec.rb
index 0b048617ce1..7b569f7ce56 100644
--- a/spec/lib/gitlab/database/migrations/test_batched_background_runner_spec.rb
+++ b/spec/lib/gitlab/database/migrations/test_batched_background_runner_spec.rb
@@ -45,18 +45,15 @@ RSpec.describe Gitlab::Database::Migrations::TestBatchedBackgroundRunner, :freez
end
with_them do
- let(:result_dir) { Dir.mktmpdir }
+ let(:result_dir) { Pathname.new(Dir.mktmpdir) }
+ let(:connection) { base_model.connection }
+ let(:table_name) { "_test_column_copying" }
+ let(:from_id) { 0 }
after do
FileUtils.rm_rf(result_dir)
end
- let(:connection) { base_model.connection }
-
- let(:table_name) { "_test_column_copying" }
-
- let(:from_id) { 0 }
-
before do
connection.execute(<<~SQL)
CREATE TABLE #{table_name} (
@@ -70,26 +67,15 @@ RSpec.describe Gitlab::Database::Migrations::TestBatchedBackgroundRunner, :freez
context 'running a real background migration' do
let(:interval) { 5.minutes }
- let(:meta) { { "max_batch_size" => nil, "total_tuple_count" => nil, "interval" => interval } }
-
- let(:params) do
- {
- version: nil,
- connection: connection,
- meta: {
- interval: 300,
- max_batch_size: nil,
- total_tuple_count: nil
- }
- }
- end
+ let(:params) { { version: nil, connection: connection } }
+ let(:migration_name) { 'CopyColumnUsingBackgroundMigrationJob' }
+ let(:migration_file_path) { result_dir.join('CopyColumnUsingBackgroundMigrationJob', 'details.json') }
+ let(:json_file) { Gitlab::Json.parse(File.read(migration_file_path)) }
+ let(:expected_file_keys) { %w[interval total_tuple_count max_batch_size] }
before do
- queue_migration('CopyColumnUsingBackgroundMigrationJob',
- table_name, :id,
- :id, :data,
- batch_size: 100,
- job_interval: interval) # job_interval is skipped when testing
+ # job_interval is skipped when testing
+ queue_migration(migration_name, table_name, :id, :id, :data, batch_size: 100, job_interval: interval)
end
subject(:sample_migration) do
@@ -113,6 +99,12 @@ RSpec.describe Gitlab::Database::Migrations::TestBatchedBackgroundRunner, :freez
subject
end
+
+ it 'exports migration details to a file' do
+ subject
+
+ expect(json_file.keys).to match_array(expected_file_keys)
+ end
end
context 'with jobs to run' do
diff --git a/spec/models/ci/build_pending_state_spec.rb b/spec/models/ci/build_pending_state_spec.rb
index 756180621ec..bff0b35f878 100644
--- a/spec/models/ci/build_pending_state_spec.rb
+++ b/spec/models/ci/build_pending_state_spec.rb
@@ -2,7 +2,14 @@
require 'spec_helper'
-RSpec.describe Ci::BuildPendingState do
+RSpec.describe Ci::BuildPendingState, feature_category: :continuous_integration do
+ describe 'validations' do
+ subject(:pending_state) { build(:ci_build_pending_state) }
+
+ it { is_expected.to belong_to(:build) }
+ it { is_expected.to validate_presence_of(:build) }
+ end
+
describe '#crc32' do
context 'when checksum does not exist' do
let(:pending_state) do
diff --git a/spec/services/ci/update_build_state_service_spec.rb b/spec/services/ci/update_build_state_service_spec.rb
index 90a86e7ae59..f8ecff20728 100644
--- a/spec/services/ci/update_build_state_service_spec.rb
+++ b/spec/services/ci/update_build_state_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ci::UpdateBuildStateService do
+RSpec.describe Ci::UpdateBuildStateService, feature_category: :continuous_integration do
let_it_be(:project) { create(:project) }
let_it_be(:pipeline) { create(:ci_pipeline, project: project) }
diff --git a/spec/support/rspec_order_todo.yml b/spec/support/rspec_order_todo.yml
index c43dd88e3fe..f9de9eadb3f 100644
--- a/spec/support/rspec_order_todo.yml
+++ b/spec/support/rspec_order_todo.yml
@@ -3147,7 +3147,6 @@
- './ee/spec/views/projects/security/sast_configuration/show.html.haml_spec.rb'
- './ee/spec/views/projects/settings/subscriptions/_index.html.haml_spec.rb'
- './ee/spec/views/registrations/groups_projects/new.html.haml_spec.rb'
-- './ee/spec/views/registrations/welcome/continuous_onboarding_getting_started.html.haml_spec.rb'
- './ee/spec/views/search/_category.html.haml_spec.rb'
- './ee/spec/views/shared/billings/_billing_plan_actions.html.haml_spec.rb'
- './ee/spec/views/shared/billings/_billing_plan.html.haml_spec.rb'
diff --git a/spec/support/shared_examples/lib/cache_helpers_shared_examples.rb b/spec/support/shared_examples/lib/cache_helpers_shared_examples.rb
index 6cdd7954b5f..42e8fa3d51f 100644
--- a/spec/support/shared_examples/lib/cache_helpers_shared_examples.rb
+++ b/spec/support/shared_examples/lib/cache_helpers_shared_examples.rb
@@ -17,7 +17,7 @@ RSpec.shared_examples_for 'object cache helper' do
end
it "fetches from the cache" do
- expect(instance.cache).to receive(:fetch).with("#{presenter.class.name}:#{presentable.cache_key}:#{user.cache_key}", expires_in: described_class::DEFAULT_EXPIRY).once
+ expect(instance.cache).to receive(:fetch).with("#{expected_cache_key_prefix}:#{presentable.cache_key}:#{user.cache_key}", expires_in: described_class::DEFAULT_EXPIRY).once
subject
end
@@ -28,7 +28,7 @@ RSpec.shared_examples_for 'object cache helper' do
end
it "uses the context to augment the cache key" do
- expect(instance.cache).to receive(:fetch).with("#{presenter.class.name}:#{presentable.cache_key}:#{project.cache_key}", expires_in: described_class::DEFAULT_EXPIRY).once
+ expect(instance.cache).to receive(:fetch).with("#{expected_cache_key_prefix}:#{presentable.cache_key}:#{project.cache_key}", expires_in: described_class::DEFAULT_EXPIRY).once
subject
end
@@ -38,7 +38,7 @@ RSpec.shared_examples_for 'object cache helper' do
it "sets the expiry when accessing the cache" do
kwargs[:expires_in] = 7.days
- expect(instance.cache).to receive(:fetch).with("#{presenter.class.name}:#{presentable.cache_key}:#{user.cache_key}", expires_in: 7.days).once
+ expect(instance.cache).to receive(:fetch).with("#{expected_cache_key_prefix}:#{presentable.cache_key}:#{user.cache_key}", expires_in: 7.days).once
subject
end
@@ -90,7 +90,7 @@ RSpec.shared_examples_for 'collection cache helper' do
end
it "fetches from the cache" do
- keys = presentable.map { |item| "#{presenter.class.name}:#{item.cache_key}:#{user.cache_key}" }
+ keys = presentable.map { |item| "#{expected_cache_key_prefix}:#{item.cache_key}:#{user.cache_key}" }
expect(instance.cache).to receive(:fetch_multi).with(*keys, expires_in: described_class::DEFAULT_EXPIRY).once.and_call_original
@@ -103,7 +103,7 @@ RSpec.shared_examples_for 'collection cache helper' do
end
it "uses the context to augment the cache key" do
- keys = presentable.map { |item| "#{presenter.class.name}:#{item.cache_key}:#{project.cache_key}" }
+ keys = presentable.map { |item| "#{expected_cache_key_prefix}:#{item.cache_key}:#{project.cache_key}" }
expect(instance.cache).to receive(:fetch_multi).with(*keys, expires_in: described_class::DEFAULT_EXPIRY).once.and_call_original
@@ -113,7 +113,7 @@ RSpec.shared_examples_for 'collection cache helper' do
context "expires_in is supplied" do
it "sets the expiry when accessing the cache" do
- keys = presentable.map { |item| "#{presenter.class.name}:#{item.cache_key}:#{user.cache_key}" }
+ keys = presentable.map { |item| "#{expected_cache_key_prefix}:#{item.cache_key}:#{user.cache_key}" }
kwargs[:expires_in] = 7.days
expect(instance.cache).to receive(:fetch_multi).with(*keys, expires_in: 7.days).once.and_call_original