diff options
28 files changed, 318 insertions, 117 deletions
diff --git a/app/assets/javascripts/breadcrumb.js b/app/assets/javascripts/breadcrumb.js index 0dacd5af5cc..b06a6f8b141 100644 --- a/app/assets/javascripts/breadcrumb.js +++ b/app/assets/javascripts/breadcrumb.js @@ -34,7 +34,7 @@ export default () => { $('li.expander').remove(); // set focus on first breadcrumb item - $('.breadcrumb-item-text').first().focus(); + $('.js-breadcrumb-item-text').first().focus(); }); } }; diff --git a/app/assets/stylesheets/framework/breadcrumbs.scss b/app/assets/stylesheets/framework/breadcrumbs.scss index 2ed611f7ba9..b71382f5570 100644 --- a/app/assets/stylesheets/framework/breadcrumbs.scss +++ b/app/assets/stylesheets/framework/breadcrumbs.scss @@ -11,11 +11,3 @@ vertical-align: sub; } } - -.breadcrumb-item-text { - text-decoration: inherit; - - @include media-breakpoint-down(xs) { - @include str-truncated(128px); - } -} diff --git a/app/helpers/button_helper.rb b/app/helpers/button_helper.rb index e6212ee7d8d..851de133a38 100644 --- a/app/helpers/button_helper.rb +++ b/app/helpers/button_helper.rb @@ -8,7 +8,9 @@ module ButtonHelper # :gfm - GitLab Flavored Markdown to copy, if different from `text` (optional) # :target - Selector for target element to copy from (optional) # :class - CSS classes to be applied to the button (optional) - # :title - Button's title attribute (used for the tooltip) (optional) + # :title - Button's title attribute (used for the tooltip) (optional, default: Copy) + # :aria_label - Button's aria-label attribute (optional) + # :aria_keyshortcuts - Button's aria-keyshortcuts attribute (optional) # :button_text - Button's displayed label (optional) # :hide_tooltip - Whether the tooltip should be hidden (optional, default: false) # :hide_button_icon - Whether the icon should be hidden (optional, default: false) @@ -31,6 +33,8 @@ module ButtonHelper def clipboard_button(data = {}) css_class = data.delete(:class) title = data.delete(:title) || _('Copy') + aria_keyshortcuts = data.delete(:aria_keyshortcuts) || nil + aria_label = data.delete(:aria_label) || title button_text = data[:button_text] || nil hide_tooltip = data[:hide_tooltip] || false hide_button_icon = data[:hide_button_icon] || false @@ -54,7 +58,7 @@ module ButtonHelper data[:clipboard_target] = target if target unless hide_tooltip - data = { toggle: 'tooltip', placement: 'bottom', container: 'body' }.merge(data) + data = { toggle: 'tooltip', placement: 'bottom', container: 'body', html: 'true' }.merge(data) end render ::Pajamas::ButtonComponent.new( @@ -62,7 +66,7 @@ module ButtonHelper variant: variant, category: category, size: size, - button_options: { class: css_class, title: title, aria: { label: title, live: 'polite' }, data: data, itemprop: item_prop }) do + button_options: { class: css_class, title: title, aria: { keyshortcuts: aria_keyshortcuts, label: aria_label, live: 'polite' }, data: data, itemprop: item_prop }) do button_text end end diff --git a/app/helpers/groups_helper.rb b/app/helpers/groups_helper.rb index 25a2cc8a5ae..8b5d05b6408 100644 --- a/app/helpers/groups_helper.rb +++ b/app/helpers/groups_helper.rb @@ -57,7 +57,7 @@ module GroupsHelper push_to_schema_breadcrumb(simple_sanitize(group.name), group_path(group)) if name - full_title << ' · '.html_safe + link_to(simple_sanitize(name), url, class: 'group-path breadcrumb-item-text js-breadcrumb-item-text') + full_title << ' · '.html_safe + link_to(simple_sanitize(name), url, class: 'group-path js-breadcrumb-item-text') push_to_schema_breadcrumb(simple_sanitize(name), url) end @@ -241,8 +241,8 @@ module GroupsHelper private - def group_title_link(group, hidable: false, show_avatar: false, for_dropdown: false) - link_to(group_path(group), class: "group-path #{'breadcrumb-item-text' unless for_dropdown} js-breadcrumb-item-text #{'hidable' if hidable}") do + def group_title_link(group, hidable: false, show_avatar: false) + link_to(group_path(group), class: "group-path js-breadcrumb-item-text #{'hidable' if hidable}") do icon = render Pajamas::AvatarComponent.new(group, alt: group.name, class: "avatar-tile", size: 16) if group.try(:avatar_url) || show_avatar [icon, simple_sanitize(group.name)].join.html_safe end diff --git a/app/helpers/merge_requests_helper.rb b/app/helpers/merge_requests_helper.rb index 100b9349df6..767838df076 100644 --- a/app/helpers/merge_requests_helper.rb +++ b/app/helpers/merge_requests_helper.rb @@ -275,7 +275,10 @@ module MergeRequestsHelper def merge_request_header(project, merge_request) link_to_author = link_to_member(project, merge_request.author, size: 24, extra_class: 'gl-font-weight-bold gl-mr-2', avatar: false) - copy_button = clipboard_button(text: merge_request.source_branch, title: _('Copy branch name'), class: 'gl-display-none! gl-md-display-inline-block! js-source-branch-copy') + copy_action_description = _('Copy branch name') + copy_action_shortcut = 'b' + copy_button_title = "#{copy_action_description} <kbd class='flat ml-1'>#{copy_action_shortcut}</kbd>" + copy_button = clipboard_button(text: merge_request.source_branch, title: copy_button_title, aria_keyshortcuts: copy_action_shortcut, aria_label: copy_action_description, class: 'gl-display-none! gl-md-display-inline-block! js-source-branch-copy') target_branch = link_to merge_request.target_branch, project_tree_path(merge_request.target_project, merge_request.target_branch), title: merge_request.target_branch, class: 'ref-container gl-display-inline-block gl-text-truncate gl-max-w-26 gl-mx-2' diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb index cbc2db9e79f..cb8c86a7d29 100644 --- a/app/helpers/projects_helper.rb +++ b/app/helpers/projects_helper.rb @@ -791,7 +791,7 @@ module ProjectsHelper link_to project_path(project) do icon = render Pajamas::AvatarComponent.new(project, alt: project.name, size: 16, class: 'avatar-tile') if project.avatar_url && !Rails.env.test? - [icon, content_tag("span", project_name, class: "breadcrumb-item-text js-breadcrumb-item-text")].join.html_safe + [icon, content_tag("span", project_name, class: "js-breadcrumb-item-text")].join.html_safe end end diff --git a/app/services/issuable/common_system_notes_service.rb b/app/services/issuable/common_system_notes_service.rb index db28be864a7..a0fa1616f7b 100644 --- a/app/services/issuable/common_system_notes_service.rb +++ b/app/services/issuable/common_system_notes_service.rb @@ -2,10 +2,11 @@ module Issuable class CommonSystemNotesService < ::BaseProjectService - attr_reader :issuable + attr_reader :issuable, :is_update def execute(issuable, old_labels: [], old_milestone: nil, is_update: true) @issuable = issuable + @is_update = is_update # We disable touch so that created system notes do not update # the noteable's updated_at field @@ -17,10 +18,10 @@ module Issuable handle_description_change_note - handle_time_tracking_note if issuable.is_a?(TimeTrackable) create_discussion_lock_note if issuable.previous_changes.include?('discussion_locked') end + handle_time_tracking_note if issuable.is_a?(TimeTrackable) handle_start_date_or_due_date_change_note create_milestone_change_event(old_milestone) if issuable.previous_changes.include?('milestone_id') create_labels_note(old_labels) if old_labels && issuable.labels != old_labels @@ -37,13 +38,11 @@ module Issuable end def handle_time_tracking_note - if issuable.previous_changes.include?('time_estimate') - create_time_estimate_note - end + estimate_updated = is_update && issuable.previous_changes.include?('time_estimate') + estimate_set = !is_update && issuable.time_estimate != 0 - if issuable.time_spent? - create_time_spent_note - end + create_time_estimate_note if estimate_updated || estimate_set + create_time_spent_note if issuable.time_spent? end def handle_description_change_note diff --git a/app/services/work_items/callbacks/current_user_todos.rb b/app/services/work_items/callbacks/current_user_todos.rb new file mode 100644 index 00000000000..c6c74a5ce3d --- /dev/null +++ b/app/services/work_items/callbacks/current_user_todos.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +module WorkItems + module Callbacks + class CurrentUserTodos < Base + def before_update + return unless params.present? && params.key?(:action) + + case params[:action] + when "add" + add_todo + when "mark_as_done" + mark_as_done(params[:todo_id]) + end + end + + private + + def add_todo + return unless has_permission?(:create_todo) + + TodoService.new.mark_todo(work_item, current_user)&.first + end + + def mark_as_done(todo_id) + todos = TodosFinder.new(current_user, state: :pending, target_id: work_item.id).execute + todos = todo_id ? todos.id_in(todo_id) : todos + + return if todos.empty? + + TodoService.new.resolve_todos(todos, current_user, resolved_by_action: :api_done) + end + end + end +end diff --git a/app/services/work_items/widgets/current_user_todos_service/update_service.rb b/app/services/work_items/widgets/current_user_todos_service/update_service.rb deleted file mode 100644 index 38e2ae4de32..00000000000 --- a/app/services/work_items/widgets/current_user_todos_service/update_service.rb +++ /dev/null @@ -1,37 +0,0 @@ -# frozen_string_literal: true - -module WorkItems - module Widgets - module CurrentUserTodosService - class UpdateService < WorkItems::Widgets::BaseService - def before_update_in_transaction(params:) - return unless params.present? && params.key?(:action) - - case params[:action] - when "add" - add_todo - when "mark_as_done" - mark_as_done(params[:todo_id]) - end - end - - private - - def add_todo - return unless has_permission?(:create_todo) - - TodoService.new.mark_todo(work_item, current_user)&.first - end - - def mark_as_done(todo_id) - todos = TodosFinder.new(current_user, state: :pending, target_id: work_item.id).execute - todos = todo_id ? todos.id_in(todo_id) : todos - - return if todos.empty? - - TodoService.new.resolve_todos(todos, current_user, resolved_by_action: :api_done) - end - end - end - end -end diff --git a/db/migrate/20240105000000_rename_workspace_url_domain_to_dns_zone.rb b/db/migrate/20240105000000_rename_workspace_url_domain_to_dns_zone.rb new file mode 100644 index 00000000000..72e7ea566a8 --- /dev/null +++ b/db/migrate/20240105000000_rename_workspace_url_domain_to_dns_zone.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +class RenameWorkspaceUrlDomainToDnsZone < Gitlab::Database::Migration[2.2] + milestone '16.8' + disable_ddl_transaction! + + def up + rename_column_concurrently :workspaces, :url_domain, :dns_zone + end + + def down + undo_rename_column_concurrently :workspaces, :url_domain, :dns_zone + end +end diff --git a/db/post_migrate/20231219132423_remove_epic_id_column_from_vulnerabilities.rb b/db/post_migrate/20231219132423_remove_epic_id_column_from_vulnerabilities.rb new file mode 100644 index 00000000000..fa6379a409a --- /dev/null +++ b/db/post_migrate/20231219132423_remove_epic_id_column_from_vulnerabilities.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +class RemoveEpicIdColumnFromVulnerabilities < Gitlab::Database::Migration[2.2] + disable_ddl_transaction! + + milestone '16.8' + + def up + with_lock_retries do + remove_column :vulnerabilities, :epic_id + end + end + + def down + add_column :vulnerabilities, :epic_id, :bigint unless column_exists?(:vulnerabilities, :epic_id) + + # Add back index and constraint that were dropped in `up` + add_concurrent_index(:vulnerabilities, :epic_id) + add_concurrent_foreign_key(:vulnerabilities, :epics, column: :epic_id, on_delete: :nullify) + end +end diff --git a/db/post_migrate/20240105000001_cleanup_workspaces_url_domain_to_dns_zone_rename.rb b/db/post_migrate/20240105000001_cleanup_workspaces_url_domain_to_dns_zone_rename.rb new file mode 100644 index 00000000000..d37d0c9a5af --- /dev/null +++ b/db/post_migrate/20240105000001_cleanup_workspaces_url_domain_to_dns_zone_rename.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +class CleanupWorkspacesUrlDomainToDnsZoneRename < Gitlab::Database::Migration[2.2] + milestone '16.8' + + disable_ddl_transaction! + + def up + cleanup_concurrent_column_rename :workspaces, :url_domain, :dns_zone + end + + def down + undo_cleanup_concurrent_column_rename :workspaces, :url_domain, :dns_zone + end +end diff --git a/db/post_migrate/20240108181808_remove_package_registry_duplicated_indexes.rb b/db/post_migrate/20240108181808_remove_package_registry_duplicated_indexes.rb new file mode 100644 index 00000000000..96380b94d50 --- /dev/null +++ b/db/post_migrate/20240108181808_remove_package_registry_duplicated_indexes.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +class RemovePackageRegistryDuplicatedIndexes < Gitlab::Database::Migration[2.2] + disable_ddl_transaction! + milestone '16.8' + + DUPLICATED_INDEXES = [ + { + name: :index_packages_debian_group_distributions_on_group_id, + table: :packages_debian_group_distributions, + column: :group_id + }, + { + name: :index_packages_debian_project_distributions_on_project_id, + table: :packages_debian_project_distributions, + column: :project_id + }, + { + name: :index_packages_tags_on_package_id, + table: :packages_tags, + column: :package_id + } + ] + + def up + DUPLICATED_INDEXES.each do |index| + remove_concurrent_index_by_name(index[:table], index[:name]) + end + end + + def down + DUPLICATED_INDEXES.each do |index| + add_concurrent_index(index[:table], index[:column], name: index[:name]) + end + end +end diff --git a/db/schema_migrations/20231219132423 b/db/schema_migrations/20231219132423 new file mode 100644 index 00000000000..82fb1923c91 --- /dev/null +++ b/db/schema_migrations/20231219132423 @@ -0,0 +1 @@ +ed2b44c085d02dfb5e361f3f33dd62b9b5fed0e3ae570ff79936feadad66561a
\ No newline at end of file diff --git a/db/schema_migrations/20240105000000 b/db/schema_migrations/20240105000000 new file mode 100644 index 00000000000..5d7b53970b7 --- /dev/null +++ b/db/schema_migrations/20240105000000 @@ -0,0 +1 @@ +5fd81c2408e7e4ae564b719702b69e7645fb84822e77d2aee9eb284a68daf1dd
\ No newline at end of file diff --git a/db/schema_migrations/20240105000001 b/db/schema_migrations/20240105000001 new file mode 100644 index 00000000000..45bc8d78c16 --- /dev/null +++ b/db/schema_migrations/20240105000001 @@ -0,0 +1 @@ +7a53aa32b606bb1ae2b2816fe50d2ef57eb899fefc7dfd1ad558da36898a3155
\ No newline at end of file diff --git a/db/schema_migrations/20240108181808 b/db/schema_migrations/20240108181808 new file mode 100644 index 00000000000..542a576fdf1 --- /dev/null +++ b/db/schema_migrations/20240108181808 @@ -0,0 +1 @@ +688603d4b31b17d1eeb133e293876aae77ab68b80f656fb9b902ca38d4c7beed
\ No newline at end of file diff --git a/db/structure.sql b/db/structure.sql index 82417b9b6be..31a1dd8ae30 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -25201,7 +25201,6 @@ ALTER SEQUENCE vs_code_settings_id_seq OWNED BY vs_code_settings.id; CREATE TABLE vulnerabilities ( id bigint NOT NULL, - epic_id bigint, project_id bigint NOT NULL, author_id bigint NOT NULL, created_at timestamp with time zone NOT NULL, @@ -26014,12 +26013,12 @@ CREATE TABLE workspaces ( config_version integer DEFAULT 1 NOT NULL, force_include_all_resources boolean DEFAULT true NOT NULL, url_prefix text, - url_domain text, url_query_string text, - CONSTRAINT check_03c5d442fd CHECK ((char_length(url_domain) <= 256)), + dns_zone text, CONSTRAINT check_15543fb0fa CHECK ((char_length(name) <= 64)), CONSTRAINT check_157d5f955c CHECK ((char_length(namespace) <= 64)), CONSTRAINT check_2b401b0034 CHECK ((char_length(deployment_resource_version) <= 64)), + CONSTRAINT check_67c4c93554 CHECK ((char_length(dns_zone) <= 256)), CONSTRAINT check_77d1a2ff50 CHECK ((char_length(processed_devfile) <= 65535)), CONSTRAINT check_8a0ab61b6b CHECK ((char_length(url_query_string) <= 256)), CONSTRAINT check_8e363ee3ad CHECK ((char_length(devfile_ref) <= 256)), @@ -34529,14 +34528,10 @@ CREATE INDEX index_packages_debian_group_component_files_on_component_id ON pack CREATE INDEX index_packages_debian_group_distributions_on_creator_id ON packages_debian_group_distributions USING btree (creator_id); -CREATE INDEX index_packages_debian_group_distributions_on_group_id ON packages_debian_group_distributions USING btree (group_id); - CREATE INDEX index_packages_debian_project_component_files_on_component_id ON packages_debian_project_component_files USING btree (component_id); CREATE INDEX index_packages_debian_project_distributions_on_creator_id ON packages_debian_project_distributions USING btree (creator_id); -CREATE INDEX index_packages_debian_project_distributions_on_project_id ON packages_debian_project_distributions USING btree (project_id); - CREATE INDEX index_packages_debian_publications_on_distribution_id ON packages_debian_publications USING btree (distribution_id); CREATE UNIQUE INDEX index_packages_debian_publications_on_package_id ON packages_debian_publications USING btree (package_id); @@ -34613,8 +34608,6 @@ CREATE INDEX index_packages_rpm_metadata_on_package_id ON packages_rpm_metadata CREATE INDEX index_packages_rpm_repository_files_on_project_id_and_file_name ON packages_rpm_repository_files USING btree (project_id, file_name); -CREATE INDEX index_packages_tags_on_package_id ON packages_tags USING btree (package_id); - CREATE INDEX index_packages_tags_on_package_id_and_updated_at ON packages_tags USING btree (package_id, updated_at DESC); CREATE INDEX index_packages_tags_on_project_id ON packages_tags USING btree (project_id); @@ -35685,8 +35678,6 @@ CREATE INDEX index_vulnerabilities_on_detected_at_and_id ON vulnerabilities USIN CREATE INDEX index_vulnerabilities_on_dismissed_by_id ON vulnerabilities USING btree (dismissed_by_id); -CREATE INDEX index_vulnerabilities_on_epic_id ON vulnerabilities USING btree (epic_id); - CREATE INDEX index_vulnerabilities_on_finding_id ON vulnerabilities USING btree (finding_id); CREATE INDEX index_vulnerabilities_on_project_id_and_id ON vulnerabilities USING btree (project_id, id); @@ -38098,9 +38089,6 @@ ALTER TABLE ONLY project_statistics ALTER TABLE ONLY agent_project_authorizations ADD CONSTRAINT fk_1d30bb4987 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE; -ALTER TABLE ONLY vulnerabilities - ADD CONSTRAINT fk_1d37cddf91 FOREIGN KEY (epic_id) REFERENCES epics(id) ON DELETE SET NULL; - ALTER TABLE ONLY boards ADD CONSTRAINT fk_1e9a074a35 FOREIGN KEY (group_id) REFERENCES namespaces(id) ON DELETE CASCADE; diff --git a/doc/api/merge_requests.md b/doc/api/merge_requests.md index a852c7c0b96..02407b5e650 100644 --- a/doc/api/merge_requests.md +++ b/doc/api/merge_requests.md @@ -2014,7 +2014,9 @@ This API returns specific HTTP status codes: | HTTP Status | Message | Reason | |-------------|--------------------------------------------|--------| | `202` | *(no message)* | Successfully enqueued. | -| `403` | <ul><li>`Source branch does not exist`</li><li>`Cannot push to source branch`</li><li>`Source branch is protected from force push`</li></ul> | You don't have permission to push to the merge request's source branch. | +| `403` | `Cannot push to source branch` | You don't have permission to push to the merge request's source branch. | +| `403` | `Source branch does not exist` | You don't have permission to push to the merge request's source branch. | +| `403` | `Source branch is protected from force push` | You don't have permission to push to the merge request's source branch. | | `409` | `Failed to enqueue the rebase operation` | A long-lived transaction might have blocked your request. | If the request is enqueued successfully, the response contains: diff --git a/doc/architecture/blueprints/gitlab_housekeeper/index.md b/doc/architecture/blueprints/gitlab_housekeeper/index.md new file mode 100644 index 00000000000..76ff032bbcf --- /dev/null +++ b/doc/architecture/blueprints/gitlab_housekeeper/index.md @@ -0,0 +1,132 @@ +--- +status: implemented +creation-date: "2023-10-18" +authors: [ "@DylanGriffith" ] +coach: +approvers: [ "@rymai", "@tigerwnz" ] +owning-stage: "~devops::tenant scale" +participating-stages: [] +--- + +<!-- vale gitlab.FutureTense = NO --> + +# GitLab Housekeeper - automating merge requests + +## Summary + +This blueprint documents the philosophy behind the "GitLab Housekeeper" gem +which was introduced in +<https://gitlab.com/gitlab-org/gitlab/-/merge_requests/139492> and has already +been used to create many merge requests. + +The tool should be used to save developers from mundane repetitive tasks that +can be automated. The tool is scoped to any task where a developer needs to +create a straightforward merge request and is known ahead of time. + +This tool should be useful for at least the following kinds of mundane MRs +we create: + +1. Remove a feature flag after X date +1. Remove an unused index where the unused index is identified by some + automation +1. Remove an `ignore_column` after X date (part of renaming/removing columns + multi-step procedure) +1. Populate sharding keys for organizations/cells on tables that are missing a + sharding key + +## Motivation + +We've observed there are many cases where developers are doing a lot of +manual work for tasks that are entirely predictable and automatable. Often +these manual tasks are done after waiting some known period of time. As such we +usually create an issue and set the future milestone. Then in the future the +developer remembers to followup on that issue and opens an MR to make the +manual change. + +The biggest examples we've seen lately are: + +1. Feature flag removal: <https://gitlab.com/groups/gitlab-org/-/epics/5325>. We + have many opportunities for automation with feature flags but this blueprint + focuses on removing the feature flag after it's fully rolled out. A step + that is often forgotten leading to growing technical debt. +1. Removing duplicated or unused indexes in Postgres: + <https://gitlab.com/gitlab-org/gitlab/-/issues/385701>. For now we're + developing automation that creates issues and assigns them to groups to + follow up and manually open MRs to remove them. This blueprint would take it + a step further and the automation would just create the MRs to remove them + once we have identified them. +1. Removing out of date `ignore_column` references: + <https://docs.gitlab.com/ee/development/database/avoiding_downtime_in_migrations.html#removing-the-ignore-rule-release-m2> + . For now we leave a note in our code telling us the date it needs to be + removed and often create an issue as a reminder. This blueprint proposes + that automation just reads this note and opens the MR to remove it after the + date. +1. Adding and backfilling sharding keys for organizations for Cells: + <https://gitlab.com/gitlab-org/gitlab/-/merge_requests/133796>. The cells + architecture depends on all tables having a sharding key that is attributed + to an organization. We will need to backfill this for ~300 tables. Much of + this will be repetitive and mundane work that we can automate provided that + groups just identify what the name of the sharding key should be and how we + will backfill it. As such we can automate the creation of MRs that guess the + sharding key and owning groups can check and correct those MRs. Then we can + automate the MR creation for adding the columns and backfilling the data. + Some kind of automation like this will be necessary to finish this work in a + reasonable timeframe. + +### Goals + +1. Identify the common tasks that take development time and automate them. +1. Focus on MR creation rather than issue creation as MRs are the results we + want and issues are a process for reminding us to get those results. +1. Improve developer job satisfication by knowing that automation is doing the + busy work while we get to do the challenging and creative work. +1. Developers should be encouraged to contribute to the automation framework + when they see a pattern rather than documenting the manual work for future + developers to do it again. +1. Automation MRs should be very easily identified and reviewed and merged much + more quickly than other MRs. If our automation MRs cause too much effort for + reviewers we maybe will outweigh the benefits. This might mean that some + automations get disabled when they are just noisy. + +## Solution + +The +[GitLab Housekeeper gem](https://gitlab.com/gitlab-org/gitlab/-/tree/master/gems/gitlab-housekeeper) +should be used to automate creation of mundane merge requests. + +Using this tool reflects our +[bias for action](https://handbook.gitlab.com/handbook/values/#bias-for-action) +subvalue. As such, developers should preference contributing a new +[keep](https://gitlab.com/gitlab-org/gitlab/-/tree/master/keeps) over the following: + +1. Documenting a process that involves creating several merge requests over a + period of time +1. Setting up periodic reminders for developers (in Slack or issues) to create + some merge request + +The keeps may sometimes take more work to implement than documentation or +reminders so judgement should be used to assess the likely time savings from +using automation. The `gitlab-housekeeper` gem will evolve over time with many +utilities that make it simpler to contribute new keeps and it is expected that +over time the cost to implementing a keep should be small enough that we will +mostly prefer this whenever developers need to do a repeatable task more than a +few times. + +## Design and implementation details + +The key details for this architecture is: + +1. The design of this tool is like a combination of `rubocop -a` and Renovate + bot. It extends on `rubocop -a` to understand when things need to be removed + after certain deadlines as well as creating a steady stream of manageable + merge requests for the reviewer rather than leaving those decisions to the + developer. Like the renovate bot it attempts to create MRs periodically and + assign them to the right people to review. +1. The keeps live in the GitLab repo which means that there are no + dependencies to update and the keeps can use code inside the + GitLab codebase. +1. The script can be run locally by a developer or can be run periodically + in some automated way. +1. The keeps are able to use any data sources (eg. local code, Prometheus, + Postgres database archive, logs) needed to determine whether and how to make + the change. diff --git a/doc/development/feature_flags/index.md b/doc/development/feature_flags/index.md index 59c796ca331..bea42d6340d 100644 --- a/doc/development/feature_flags/index.md +++ b/doc/development/feature_flags/index.md @@ -244,7 +244,7 @@ created using the [Experiment Tracking template](https://gitlab.com/gitlab-org/g the worker name itself, for example, `run_sidekiq_jobs_AuthorizedProjectsWorker`. Some examples for using `worker` type feature flags can be found in [deferring Sidekiq jobs](#deferring-sidekiq-jobs). -### [Deprecated]`development` type +### (Deprecated) `development` type The `development` type is deprecated in favor of the `gitlab_com_derisk`, `wip`, and `beta` feature flag types. diff --git a/doc/solutions/cloud/aws/gitlab_aws_integration.md b/doc/solutions/cloud/aws/gitlab_aws_integration.md index 31459fca747..fddb5ccfa38 100644 --- a/doc/solutions/cloud/aws/gitlab_aws_integration.md +++ b/doc/solutions/cloud/aws/gitlab_aws_integration.md @@ -37,16 +37,16 @@ These integrations have to do with using GitLab to build application workloads a [12/28/2023 AWS Release Announcement for Self-Managed / Dedicated](https://aws.amazon.com/about-aws/whats-new/2023/12/codepipeline-gitlab-self-managed/) -**AWS CodeStar Connections** - enables SCM connections to multiple AWS Services. [Configure GitLab](https://docs.aws.amazon.com/dtconsole/latest/userguide/connections-create-gitlab.html). [Supported Providers](https://docs.aws.amazon.com/dtconsole/latest/userguide/supported-versions-connections.html). [Supported AWS Services](https://docs.aws.amazon.com/dtconsole/latest/userguide/integrations-connections.html) - each one may have to make updates to support GitLab, so here is the subset that support GitLab. This works with GitLab.com SaaS, GitLab Self-Managed and GitLab Dedicated. AWS CodeStar connections are not available in all AWS regions - the exclusion list is [documented here](https://docs.aws.amazon.com/codepipeline/latest/userguide/action-reference-CodestarConnectionSource.html). [[12/28/2023](https://aws.amazon.com/about-aws/whats-new/2023/12/codepipeline-gitlab-self-managed/)] `[AWS Built]` +**AWS CodeStar Connections** - enables SCM connections to multiple AWS Services. [Configure GitLab](https://docs.aws.amazon.com/dtconsole/latest/userguide/connections-create-gitlab.html). [Supported Providers](https://docs.aws.amazon.com/dtconsole/latest/userguide/supported-versions-connections.html). [Supported AWS Services](https://docs.aws.amazon.com/dtconsole/latest/userguide/integrations-connections.html) - each one may have to make updates to support GitLab, so here is the subset that support GitLab. This works with GitLab.com SaaS, GitLab Self-Managed and GitLab Dedicated. AWS CodeStar connections are not available in all AWS regions - the exclusion list is [documented here](https://docs.aws.amazon.com/codepipeline/latest/userguide/action-reference-CodestarConnectionSource.html). ([12/28/2023](https://aws.amazon.com/about-aws/whats-new/2023/12/codepipeline-gitlab-self-managed/)) `[AWS Built]` [Video Explanation of AWS CodeStar Connection Integration for AWS (1 min)](https://youtu.be/f7qTSa_bNig) AWS Services that are supported directly by a CodeStar Connection in an AWS account: -- **Amazon CodeWhisperer Customization Capability** [[12/28/2023](https://aws.amazon.com/about-aws/whats-new/2023/12/codepipeline-gitlab-self-managed/)] [can connect to a GitLab repository](https://aws.amazon.com/blogs/aws/new-customization-capability-in-amazon-codewhisperer-generates-even-better-suggestions-preview/). `[AWS Built]` -- **AWS Service Catalog** directly inherits CodeStar Connections, there is not any specific documentation about GitLab because it just uses any GitLab CodeStar Connection that has been created in the account. [[12/28/2023](https://aws.amazon.com/about-aws/whats-new/2023/12/codepipeline-gitlab-self-managed/)] `[AWS Built]` -- **AWS Proton** directly inherits CodeStar Connections, there is not any specific documentation about GitLab since it just uses any GitLab CodeStar Connection that has been created in the account. [[12/28/2023](https://aws.amazon.com/about-aws/whats-new/2023/12/codepipeline-gitlab-self-managed/)] `[AWS Built]` -- **AWS Glue Notebook Jobs** directly inherit CodeStar Connections, there is not any specific documentation about GitLab because it just uses any GitLab CodeStar Connection that has been created in the account. [[12/28/2023](https://aws.amazon.com/about-aws/whats-new/2023/12/codepipeline-gitlab-self-managed/)] `[AWS Built]` +- **Amazon CodeWhisperer Customization Capability** ([12/28/2023](https://aws.amazon.com/about-aws/whats-new/2023/12/codepipeline-gitlab-self-managed/)) [can connect to a GitLab repository](https://aws.amazon.com/blogs/aws/new-customization-capability-in-amazon-codewhisperer-generates-even-better-suggestions-preview/). `[AWS Built]` +- **AWS Service Catalog** directly inherits CodeStar Connections, there is not any specific documentation about GitLab because it just uses any GitLab CodeStar Connection that has been created in the account. ([12/28/2023](https://aws.amazon.com/about-aws/whats-new/2023/12/codepipeline-gitlab-self-managed/)) `[AWS Built]` +- **AWS Proton** directly inherits CodeStar Connections, there is not any specific documentation about GitLab since it just uses any GitLab CodeStar Connection that has been created in the account. ([12/28/2023](https://aws.amazon.com/about-aws/whats-new/2023/12/codepipeline-gitlab-self-managed/)) `[AWS Built]` +- **AWS Glue Notebook Jobs** directly inherit CodeStar Connections, there is not any specific documentation about GitLab because it just uses any GitLab CodeStar Connection that has been created in the account. ([12/28/2023](https://aws.amazon.com/about-aws/whats-new/2023/12/codepipeline-gitlab-self-managed/)) `[AWS Built]` Documentation and References: @@ -59,8 +59,8 @@ Documentation and References: AWS Services that are supported by an AWS CodePipeline integration: -- **AWS CodeBuild Integration** - through CodePipeline support. [[12/28/2023](https://aws.amazon.com/about-aws/whats-new/2023/12/codepipeline-gitlab-self-managed/)] `[AWS Built]` -- **Amazon SageMaker MLOps Projects** are created via CodePipeline ([as noted here](https://docs.aws.amazon.com/sagemaker/latest/dg/sagemaker-projects-walkthrough-3rdgit.html#sagemaker-proejcts-walkthrough-connect-3rdgit)), there is not any specific documentation about GitLab since it just uses any GitLab CodeStar Connection that has been created in the account. [[12/28/2023](https://aws.amazon.com/about-aws/whats-new/2023/12/codepipeline-gitlab-self-managed/)] `[AWS Built]` +- **AWS CodeBuild Integration** - through CodePipeline support. ([12/28/2023](https://aws.amazon.com/about-aws/whats-new/2023/12/codepipeline-gitlab-self-managed/)) `[AWS Built]` +- **Amazon SageMaker MLOps Projects** are created via CodePipeline ([as noted here](https://docs.aws.amazon.com/sagemaker/latest/dg/sagemaker-projects-walkthrough-3rdgit.html#sagemaker-proejcts-walkthrough-connect-3rdgit)), there is not any specific documentation about GitLab since it just uses any GitLab CodeStar Connection that has been created in the account. ([12/28/2023](https://aws.amazon.com/about-aws/whats-new/2023/12/codepipeline-gitlab-self-managed/)) `[AWS Built]` Documentation and References: @@ -75,12 +75,12 @@ Documentation and References: #### Custom GitLab Integration in AWS Services -- **Amazon SageMaker Notebooks** [allow Git repositories to be specified by the Git clone URL](https://docs.aws.amazon.com/sagemaker/latest/dg/nbi-git-resource.html) and configuration of a secret - so GitLab is configurable. [[12/28/2023](https://aws.amazon.com/about-aws/whats-new/2023/12/codepipeline-gitlab-self-managed/)] `[AWS Configuration]` +- **Amazon SageMaker Notebooks** [allow Git repositories to be specified by the Git clone URL](https://docs.aws.amazon.com/sagemaker/latest/dg/nbi-git-resource.html) and configuration of a secret - so GitLab is configurable. ([12/28/2023](https://aws.amazon.com/about-aws/whats-new/2023/12/codepipeline-gitlab-self-managed/)) `[AWS Configuration]` - **AWS Amplify** - [uses a Git integration mechanism designed by the AWS Amplify team](https://docs.aws.amazon.com/amplify/latest/userguide/getting-started.html). `[AWS Built]` #### Other SCM Integration Options -- [GitLab Push Mirroring to CodeCommit](../../../user/project/repository/mirror/push.md#set-up-a-push-mirror-from-gitlab-to-aws-codecommit) Workaround enables GitLab repositories to leverage CodePipeline SCM Triggers. GitLab can already leverage S3 and Container Triggers for CodePipeline. This work around enabled CodePipeline capabilities since it was documented. [06/06/2020] `[GitLab Configuration]` +- [GitLab Push Mirroring to CodeCommit](../../../user/project/repository/mirror/push.md#set-up-a-push-mirror-from-gitlab-to-aws-codecommit) Workaround enables GitLab repositories to leverage CodePipeline SCM Triggers. GitLab can already leverage S3 and Container Triggers for CodePipeline. This work around enabled CodePipeline capabilities since it was documented. (06/06/2020) `[GitLab Configuration]` See [CD and Operations Integrations](#cd-and-operations-integrations) below for Continuous Deployment (CD) specific integrations that are also available. @@ -88,12 +88,12 @@ See [CD and Operations Integrations](#cd-and-operations-integrations) below for - **Direct CI Integrations That Use Keys, IAM or OIDC/JWT to Authenticate to AWS Services from GitLab Runners** - **Amazon CodeGuru Reviewer CI workflows using GitLab CI** - can be done, not yet documented.`[AWS Solution]` `[CI Solution]` -- [Amazon CodeGuru Secure Scanning using GitLab CI](https://docs.aws.amazon.com/codeguru/latest/security-ug/get-started-gitlab.html) [[06/13/2022](https://aws.amazon.com/about-aws/whats-new/2023/06/amazon-codeguru-security-available-preview/)] `[AWS Solution]` `[CI Solution]` +- [Amazon CodeGuru Secure Scanning using GitLab CI](https://docs.aws.amazon.com/codeguru/latest/security-ug/get-started-gitlab.html) ([06/13/2022](https://aws.amazon.com/about-aws/whats-new/2023/06/amazon-codeguru-security-available-preview/)) `[AWS Solution]` `[CI Solution]` ### CD and Operations Integrations -- **AWS CodeDeploy Integration** - through CodePipeline support dicussed above in SCM integrations. This capability allows GitLab to interface with [this list of advanced deployment subsystems in AWS](https://docs.aws.amazon.com/codepipeline/latest/userguide/integrations-action-type.html#integrations-deploy). [[12/28/2023](https://aws.amazon.com/about-aws/whats-new/2023/12/codepipeline-gitlab-self-managed/)] `[AWS Built]` -- **AWS SAM Pipelines** - [pipelines support for GitLab](https://aws.amazon.com/about-aws/whats-new/2021/07/simplify-ci-cd-configuration-serverless-applications-your-favorite-ci-cd-system-public-preview). [7/31/2021] +- **AWS CodeDeploy Integration** - through CodePipeline support discussed above in SCM integrations. This capability allows GitLab to interface with [this list of advanced deployment subsystems in AWS](https://docs.aws.amazon.com/codepipeline/latest/userguide/integrations-action-type.html#integrations-deploy). ([12/28/2023](https://aws.amazon.com/about-aws/whats-new/2023/12/codepipeline-gitlab-self-managed/)) `[AWS Built]` +- **AWS SAM Pipelines** - [pipelines support for GitLab](https://aws.amazon.com/about-aws/whats-new/2021/07/simplify-ci-cd-configuration-serverless-applications-your-favorite-ci-cd-system-public-preview). (7/31/2021) - [Integrate EKS clusters for application deployment](../../../user/infrastructure/clusters/connect/new_eks_cluster.md). `[GitLab Built]` - [GitLab pushing a build Artifact to a CodePipeline monitored S3 location](https://docs.aws.amazon.com/codepipeline/latest/userguide/pipelines-about-starting.html#change-detection-methods) `[AWS Built]` - [GitLab Pushing a container to a CodePipeline monitored AWS ECR](https://docs.aws.amazon.com/codepipeline/latest/userguide/pipelines-about-starting.html#change-detection-methods) `[AWS Built]` diff --git a/spec/features/projects/show/redirects_spec.rb b/spec/features/projects/show/redirects_spec.rb index ef326b92b98..a0424831973 100644 --- a/spec/features/projects/show/redirects_spec.rb +++ b/spec/features/projects/show/redirects_spec.rb @@ -14,7 +14,7 @@ RSpec.describe 'Projects > Show > Redirects', feature_category: :groups_and_proj it 'shows public project page' do visit project_path(public_project) - page.within '.breadcrumbs .breadcrumb-item-text' do + page.within '.breadcrumbs .js-breadcrumb-item-text' do expect(page).to have_content(public_project.name) end end diff --git a/spec/features/projects/show/user_sees_git_instructions_spec.rb b/spec/features/projects/show/user_sees_git_instructions_spec.rb index 4933b3f239c..40549beae9f 100644 --- a/spec/features/projects/show/user_sees_git_instructions_spec.rb +++ b/spec/features/projects/show/user_sees_git_instructions_spec.rb @@ -49,7 +49,7 @@ RSpec.describe 'Projects > Show > User sees Git instructions', feature_category: let(:user_has_ssh_key) { false } it 'shows details' do - page.within('.breadcrumbs .breadcrumb-item-text') do + page.within('.breadcrumbs .js-breadcrumb-item-text') do expect(page).to have_content(project.title) end diff --git a/spec/services/issuable/common_system_notes_service_spec.rb b/spec/services/issuable/common_system_notes_service_spec.rb index 3d83c9ec9c2..fb2292bfe64 100644 --- a/spec/services/issuable/common_system_notes_service_spec.rb +++ b/spec/services/issuable/common_system_notes_service_spec.rb @@ -142,5 +142,9 @@ RSpec.describe Issuable::CommonSystemNotesService, feature_category: :team_plann context 'when changing dates' do it_behaves_like 'system note for issuable date changes' end + + context 'when setting an estimae' do + it_behaves_like 'system note creation', { time_estimate: 5 }, 'changed time estimate', false + end end end diff --git a/spec/services/work_items/widgets/current_user_todos_service/update_service_spec.rb b/spec/services/work_items/callbacks/current_user_todos_spec.rb index aa7257e9e62..0f16687e620 100644 --- a/spec/services/work_items/widgets/current_user_todos_service/update_service_spec.rb +++ b/spec/services/work_items/callbacks/current_user_todos_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe WorkItems::Widgets::CurrentUserTodosService::UpdateService, feature_category: :team_planning do +RSpec.describe WorkItems::Callbacks::CurrentUserTodos, feature_category: :team_planning do let_it_be(:reporter) { create(:user) } let_it_be(:project) { create(:project, :private) } let_it_be(:current_user) { reporter } @@ -25,16 +25,16 @@ RSpec.describe WorkItems::Widgets::CurrentUserTodosService::UpdateService, featu create(:todo, state: :pending, target: work_item, target_type: work_item.class.name, user: create(:user)) end - let(:widget) { work_item.widgets.find { |widget| widget.is_a?(WorkItems::Widgets::CurrentUserTodos) } } + let(:widget) { work_item.widgets.find { |widget| widget.is_a?(WorkItems::Callbacks::CurrentUserTodos) } } before_all do project.add_reporter(reporter) end describe '#before_update_in_transaction' do - subject do - described_class.new(widget: widget, current_user: current_user) - .before_update_in_transaction(params: params) + subject(:service) do + described_class.new(issuable: work_item, current_user: current_user, params: params) + .before_update end context 'when adding a todo' do @@ -44,7 +44,7 @@ RSpec.describe WorkItems::Widgets::CurrentUserTodosService::UpdateService, featu let(:current_user) { create(:user) } it 'does add a todo' do - expect { subject }.not_to change { Todo.count } + expect { service }.not_to change { Todo.count } end end @@ -52,7 +52,7 @@ RSpec.describe WorkItems::Widgets::CurrentUserTodosService::UpdateService, featu let(:params) { { action: "add" } } it 'creates a new todo for the user and the work item' do - expect { subject }.to change { current_user.todos.count }.by(1) + expect { service }.to change { current_user.todos.count }.by(1) todo = current_user.todos.last @@ -69,7 +69,7 @@ RSpec.describe WorkItems::Widgets::CurrentUserTodosService::UpdateService, featu let(:current_user) { create(:user) } it 'does not change todo status' do - subject + service expect(pending_todo1.reload).to be_pending expect(pending_todo2.reload).to be_pending @@ -80,7 +80,7 @@ RSpec.describe WorkItems::Widgets::CurrentUserTodosService::UpdateService, featu context 'when resolving all todos of the work item', :aggregate_failures do it 'resolves todos of the user for the work item' do - subject + service expect(pending_todo1.reload).to be_done expect(pending_todo2.reload).to be_done @@ -93,7 +93,7 @@ RSpec.describe WorkItems::Widgets::CurrentUserTodosService::UpdateService, featu let(:params) { { action: "mark_as_done", todo_id: pending_todo1.id } } it 'resolves todos of the user for the work item' do - subject + service expect(pending_todo1.reload).to be_done expect(pending_todo2.reload).to be_pending diff --git a/spec/support/helpers/database/duplicate_indexes.yml b/spec/support/helpers/database/duplicate_indexes.yml index 87a1e0c2c50..acfda313020 100644 --- a/spec/support/helpers/database/duplicate_indexes.yml +++ b/spec/support/helpers/database/duplicate_indexes.yml @@ -105,19 +105,6 @@ ml_models: p_ci_runner_machine_builds: index_p_ci_runner_machine_builds_on_runner_machine_id: - index_ci_runner_machine_builds_on_runner_machine_id -packages_debian_group_distributions: - uniq_pkgs_debian_group_distributions_group_id_and_codename: - - index_packages_debian_group_distributions_on_group_id - uniq_pkgs_debian_group_distributions_group_id_and_suite: - - index_packages_debian_group_distributions_on_group_id -packages_debian_project_distributions: - uniq_pkgs_debian_project_distributions_project_id_and_codename: - - index_packages_debian_project_distributions_on_project_id - uniq_pkgs_debian_project_distributions_project_id_and_suite: - - index_packages_debian_project_distributions_on_project_id -packages_tags: - index_packages_tags_on_package_id_and_updated_at: - - index_packages_tags_on_package_id pages_domains: index_pages_domains_on_project_id_and_enabled_until: - index_pages_domains_on_project_id diff --git a/spec/support/shared_examples/services/common_system_notes_shared_examples.rb b/spec/support/shared_examples/services/common_system_notes_shared_examples.rb index 1887b38b50e..14b0aa1ab08 100644 --- a/spec/support/shared_examples/services/common_system_notes_shared_examples.rb +++ b/spec/support/shared_examples/services/common_system_notes_shared_examples.rb @@ -1,7 +1,9 @@ # frozen_string_literal: true -RSpec.shared_examples 'system note creation' do |update_params, note_text| - subject { described_class.new(project: project, current_user: user).execute(issuable, old_labels: []) } +RSpec.shared_examples 'system note creation' do |update_params, note_text, is_update = true| + subject do + described_class.new(project: project, current_user: user).execute(issuable, old_labels: [], is_update: is_update) + end before do issuable.assign_attributes(update_params) |