diff --git a/.gitlab/ci/qa-common/variables.gitlab-ci.yml b/.gitlab/ci/qa-common/variables.gitlab-ci.yml index 3d567382f47..dcef0aa6774 100644 --- a/.gitlab/ci/qa-common/variables.gitlab-ci.yml +++ b/.gitlab/ci/qa-common/variables.gitlab-ci.yml @@ -18,4 +18,4 @@ variables: # Retry failed specs in separate process QA_RETRY_FAILED_SPECS: "true" # helm chart ref used by test-on-cng pipeline - GITLAB_HELM_CHART_REF: "b2e14fd2722528e664401e736e0e1edd0f4a6f69" + GITLAB_HELM_CHART_REF: "e05a64634ee53ca6868c47c6081e0f3a686e41f1" diff --git a/app/assets/javascripts/persistent_user_callouts.js b/app/assets/javascripts/persistent_user_callouts.js index fb708909269..29bbee23217 100644 --- a/app/assets/javascripts/persistent_user_callouts.js +++ b/app/assets/javascripts/persistent_user_callouts.js @@ -29,6 +29,7 @@ const PERSISTENT_USER_CALLOUTS = [ '.js-all-seats-used', '.js-period-in-terraform-state-name-alert', '.js-expired-duo-pro-trial-widget', + '.js-duo-free-access-ending-banner', ]; const initCallouts = () => { diff --git a/app/assets/stylesheets/page_bundles/notes/_timeline.scss b/app/assets/stylesheets/page_bundles/notes/_timeline.scss index 2b47346469c..4f3071ffd45 100644 --- a/app/assets/stylesheets/page_bundles/notes/_timeline.scss +++ b/app/assets/stylesheets/page_bundles/notes/_timeline.scss @@ -77,11 +77,6 @@ .timeline-entry:not(.draft-note):last-child::before { @apply gl-bg-default; - // stylelint-disable-next-line gitlab/no-gl-class - .gl-dark & { - @apply gl-bg-subtle; - } - // stylelint-disable-next-line gitlab/no-gl-class .gl-dark .modal-body & { @apply gl-bg-strong; diff --git a/app/graphql/resolvers/projects/user_contributed_projects_resolver.rb b/app/graphql/resolvers/projects/user_contributed_projects_resolver.rb index 73f8b72ed04..712c0141163 100644 --- a/app/graphql/resolvers/projects/user_contributed_projects_resolver.rb +++ b/app/graphql/resolvers/projects/user_contributed_projects_resolver.rb @@ -14,17 +14,26 @@ module Resolvers required: false, description: 'Return only projects where current user has at least the specified access level.' + argument :include_personal, GraphQL::Types::Boolean, + description: 'Include personal projects.', + required: false, + default_value: false + alias_method :user, :object def resolve(**args) - ContributedProjectsFinder.new( + contributed_projects = ContributedProjectsFinder.new( user: user, current_user: current_user, params: { order_by: args[:sort], min_access_level: args[:min_access_level] } - ).execute.joined(user) + ).execute + + return contributed_projects if args[:include_personal] + + contributed_projects.joined(user) end end end diff --git a/app/models/users/callout.rb b/app/models/users/callout.rb index 6c0a13a630e..75e0ba6e7a1 100644 --- a/app/models/users/callout.rb +++ b/app/models/users/callout.rb @@ -93,7 +93,8 @@ module Users period_in_terraform_state_name_alert: 91, work_item_epic_feedback: 92, # EE-only branch_rules_tip_callout: 93, - openssl_callout: 94 + openssl_callout: 94, + duo_free_access_ending_banner: 95 # EE-only } validates :feature_name, diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml index 8d01876cf2b..fbcf3046348 100644 --- a/app/views/groups/show.html.haml +++ b/app/views/groups/show.html.haml @@ -21,6 +21,8 @@ = render_if_exists 'gitlab_subscriptions/trials/alert', namespace: @group += render_if_exists 'shared/duo_free_access_ending_banner', resource: @group + = render 'groups/home_panel' = render_if_exists 'groups/group_activity_analytics', group: @group diff --git a/app/views/projects/empty.html.haml b/app/views/projects/empty.html.haml index 5245cf03f4b..e892c1d9ab1 100644 --- a/app/views/projects/empty.html.haml +++ b/app/views/projects/empty.html.haml @@ -3,6 +3,7 @@ - @skip_current_level_breadcrumb = true += render_if_exists 'projects/duo_free_access_ending_banner', project: @project = render partial: 'flash_messages', locals: { project: @project } = render 'clusters_deprecation_alert' diff --git a/app/views/projects/show.html.haml b/app/views/projects/show.html.haml index b75fa6d531b..1419c4cc05f 100644 --- a/app/views/projects/show.html.haml +++ b/app/views/projects/show.html.haml @@ -7,6 +7,7 @@ = auto_discovery_link_tag(:atom, project_path(@project, rss_url_options), title: "#{@project.name} activity") = render_if_exists 'shared/promotions/promote_mobile_devops', project: @project += render_if_exists 'projects/duo_free_access_ending_banner', project: @project = render partial: 'flash_messages', locals: { project: @project } = render 'clusters_deprecation_alert' diff --git a/db/docs/batched_background_migrations/backfill_agent_activity_events_agent_project_id.yml b/db/docs/batched_background_migrations/backfill_agent_activity_events_agent_project_id.yml index c39a9d0b817..9e703bdfd44 100644 --- a/db/docs/batched_background_migrations/backfill_agent_activity_events_agent_project_id.yml +++ b/db/docs/batched_background_migrations/backfill_agent_activity_events_agent_project_id.yml @@ -1,8 +1,9 @@ --- migration_job_name: BackfillAgentActivityEventsAgentProjectId -description: Backfills sharding key `agent_activity_events.agent_project_id` from `cluster_agents`. +description: Backfills sharding key `agent_activity_events.agent_project_id` from + `cluster_agents`. feature_category: deployment_management introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/154615 milestone: '17.1' queued_migration_version: 20240529184616 -finalized_by: # version of the migration that finalized this BBM +finalized_by: '20240926231943' diff --git a/db/migrate/20240924195606_add_migrate_memberships_to_bulk_import_configurations.rb b/db/migrate/20240924195606_add_migrate_memberships_to_bulk_import_configurations.rb new file mode 100644 index 00000000000..1508ec00111 --- /dev/null +++ b/db/migrate/20240924195606_add_migrate_memberships_to_bulk_import_configurations.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +class AddMigrateMembershipsToBulkImportConfigurations < Gitlab::Database::Migration[2.2] + milestone '17.5' + + def change + add_column :bulk_import_configurations, :migrate_memberships, :boolean, default: true, null: false + end +end diff --git a/db/post_migrate/20240924234448_remove_faulty_async_index_definitions.rb b/db/post_migrate/20240924234448_remove_faulty_async_index_definitions.rb new file mode 100644 index 00000000000..7d1acf37d7c --- /dev/null +++ b/db/post_migrate/20240924234448_remove_faulty_async_index_definitions.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +class RemoveFaultyAsyncIndexDefinitions < Gitlab::Database::Migration[2.2] + milestone '17.5' + + def up + unprepare_async_index_by_name :merge_request_diff_commits_b5377a7a34, + :index_merge_request_diff_commits_b5377a7a34_on_project_id + unprepare_async_index_by_name :merge_request_diff_files_99208b8fac, + :index_merge_request_diff_files_99208b8fac_on_project_id + end + + def down + # no-op + end +end diff --git a/db/post_migrate/20240926231943_finalize_backfill_agent_activity_events_agent_project_id.rb b/db/post_migrate/20240926231943_finalize_backfill_agent_activity_events_agent_project_id.rb new file mode 100644 index 00000000000..d53091f9f30 --- /dev/null +++ b/db/post_migrate/20240926231943_finalize_backfill_agent_activity_events_agent_project_id.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +class FinalizeBackfillAgentActivityEventsAgentProjectId < Gitlab::Database::Migration[2.2] + milestone '17.5' + + disable_ddl_transaction! + + restrict_gitlab_migration gitlab_schema: :gitlab_main_cell + + def up + ensure_batched_background_migration_is_finished( + job_class_name: 'BackfillAgentActivityEventsAgentProjectId', + table_name: :agent_activity_events, + column_name: :id, + job_arguments: [:agent_project_id, :cluster_agents, :project_id, :agent_id], + finalize: true + ) + end + + def down; end +end diff --git a/db/schema_migrations/20240924195606 b/db/schema_migrations/20240924195606 new file mode 100644 index 00000000000..5f3340c63f1 --- /dev/null +++ b/db/schema_migrations/20240924195606 @@ -0,0 +1 @@ +7388e13f70524cc140a13602d7dde566e213ac16773ee66b776c065c29489e33 \ No newline at end of file diff --git a/db/schema_migrations/20240924234448 b/db/schema_migrations/20240924234448 new file mode 100644 index 00000000000..d7bbe91b516 --- /dev/null +++ b/db/schema_migrations/20240924234448 @@ -0,0 +1 @@ +dca955fb2f25ca3e40cdf2a4fe195a27bc53a1134eb172ecbe6b436fdc888ca0 \ No newline at end of file diff --git a/db/schema_migrations/20240926231943 b/db/schema_migrations/20240926231943 new file mode 100644 index 00000000000..b59fab4aa74 --- /dev/null +++ b/db/schema_migrations/20240926231943 @@ -0,0 +1 @@ +0c7ed8e3faa3001929f69f884ca08614190e86040794b2320b79c0469fe412e7 \ No newline at end of file diff --git a/db/structure.sql b/db/structure.sql index 0f8a3c53be8..bfda44c4b9e 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -7572,7 +7572,8 @@ CREATE TABLE bulk_import_configurations ( encrypted_access_token text, encrypted_access_token_iv text, created_at timestamp with time zone NOT NULL, - updated_at timestamp with time zone NOT NULL + updated_at timestamp with time zone NOT NULL, + migrate_memberships boolean DEFAULT true NOT NULL ); CREATE SEQUENCE bulk_import_configurations_id_seq diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md index c592421acfa..e8ba4a043cd 100644 --- a/doc/api/graphql/reference/index.md +++ b/doc/api/graphql/reference/index.md @@ -17369,6 +17369,7 @@ four standard [pagination arguments](#pagination-arguments): | Name | Type | Description | | ---- | ---- | ----------- | +| `includePersonal` | [`Boolean`](#boolean) | Include personal projects. | | `minAccessLevel` | [`AccessLevelEnum`](#accesslevelenum) | Return only projects where current user has at least the specified access level. | | `sort` | [`ProjectSort`](#projectsort) | Sort contributed projects. | @@ -18257,6 +18258,7 @@ four standard [pagination arguments](#pagination-arguments): | Name | Type | Description | | ---- | ---- | ----------- | +| `includePersonal` | [`Boolean`](#boolean) | Include personal projects. | | `minAccessLevel` | [`AccessLevelEnum`](#accesslevelenum) | Return only projects where current user has at least the specified access level. | | `sort` | [`ProjectSort`](#projectsort) | Sort contributed projects. | @@ -20649,6 +20651,7 @@ four standard [pagination arguments](#pagination-arguments): | Name | Type | Description | | ---- | ---- | ----------- | +| `includePersonal` | [`Boolean`](#boolean) | Include personal projects. | | `minAccessLevel` | [`AccessLevelEnum`](#accesslevelenum) | Return only projects where current user has at least the specified access level. | | `sort` | [`ProjectSort`](#projectsort) | Sort contributed projects. | @@ -26221,6 +26224,7 @@ four standard [pagination arguments](#pagination-arguments): | Name | Type | Description | | ---- | ---- | ----------- | +| `includePersonal` | [`Boolean`](#boolean) | Include personal projects. | | `minAccessLevel` | [`AccessLevelEnum`](#accesslevelenum) | Return only projects where current user has at least the specified access level. | | `sort` | [`ProjectSort`](#projectsort) | Sort contributed projects. | @@ -26611,6 +26615,7 @@ four standard [pagination arguments](#pagination-arguments): | Name | Type | Description | | ---- | ---- | ----------- | +| `includePersonal` | [`Boolean`](#boolean) | Include personal projects. | | `minAccessLevel` | [`AccessLevelEnum`](#accesslevelenum) | Return only projects where current user has at least the specified access level. | | `sort` | [`ProjectSort`](#projectsort) | Sort contributed projects. | @@ -27047,6 +27052,7 @@ four standard [pagination arguments](#pagination-arguments): | Name | Type | Description | | ---- | ---- | ----------- | +| `includePersonal` | [`Boolean`](#boolean) | Include personal projects. | | `minAccessLevel` | [`AccessLevelEnum`](#accesslevelenum) | Return only projects where current user has at least the specified access level. | | `sort` | [`ProjectSort`](#projectsort) | Sort contributed projects. | @@ -27456,6 +27462,7 @@ four standard [pagination arguments](#pagination-arguments): | Name | Type | Description | | ---- | ---- | ----------- | +| `includePersonal` | [`Boolean`](#boolean) | Include personal projects. | | `minAccessLevel` | [`AccessLevelEnum`](#accesslevelenum) | Return only projects where current user has at least the specified access level. | | `sort` | [`ProjectSort`](#projectsort) | Sort contributed projects. | @@ -33950,6 +33957,7 @@ four standard [pagination arguments](#pagination-arguments): | Name | Type | Description | | ---- | ---- | ----------- | +| `includePersonal` | [`Boolean`](#boolean) | Include personal projects. | | `minAccessLevel` | [`AccessLevelEnum`](#accesslevelenum) | Return only projects where current user has at least the specified access level. | | `sort` | [`ProjectSort`](#projectsort) | Sort contributed projects. | @@ -38764,6 +38772,7 @@ Name of the feature that the callout is for. | `DEPLOYMENT_APPROVALS_EMPTY_STATE` | Callout feature name for deployment_approvals_empty_state. | | `DEPLOYMENT_DETAILS_FEEDBACK` | Callout feature name for deployment_details_feedback. | | `DUO_CHAT_CALLOUT` | Callout feature name for duo_chat_callout. | +| `DUO_FREE_ACCESS_ENDING_BANNER` | Callout feature name for duo_free_access_ending_banner. | | `FEATURE_FLAGS_NEW_VERSION` | Callout feature name for feature_flags_new_version. | | `GCP_SIGNUP_OFFER` | Callout feature name for gcp_signup_offer. | | `GEO_ENABLE_HASHED_STORAGE` | Callout feature name for geo_enable_hashed_storage. | @@ -41100,6 +41109,7 @@ four standard [pagination arguments](#pagination-arguments): | Name | Type | Description | | ---- | ---- | ----------- | +| `includePersonal` | [`Boolean`](#boolean) | Include personal projects. | | `minAccessLevel` | [`AccessLevelEnum`](#accesslevelenum) | Return only projects where current user has at least the specified access level. | | `sort` | [`ProjectSort`](#projectsort) | Sort contributed projects. | diff --git a/doc/ci/testing/test_coverage_visualization/jacoco.md b/doc/ci/testing/test_coverage_visualization/jacoco.md index 6f640335e5c..9b02266d887 100644 --- a/doc/ci/testing/test_coverage_visualization/jacoco.md +++ b/doc/ci/testing/test_coverage_visualization/jacoco.md @@ -8,7 +8,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w DETAILS: **Tier:** Free, Premium, Ultimate -**Offering:** GitLab.com, Self-managed, GitLab Dedicated +**Offering:** GitLab.com, Self-managed **Status:** Beta > - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/227345) in GitLab 17.3 [with a flag](../../../administration/feature_flags.md) named `jacoco_coverage_reports`. Disabled by default. diff --git a/doc/development/cells/index.md b/doc/development/cells/index.md index c5fc80879eb..77dac8f737f 100644 --- a/doc/development/cells/index.md +++ b/doc/development/cells/index.md @@ -12,11 +12,16 @@ For background of GitLab Cells, refer to the [design document](https://handbook. Depending on the use case, your feature may be [cell-local or clusterwide](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/cells/#how-do-i-decide-whether-to-move-my-feature-to-the-cluster-cell-or-organization-level) and hence the tables used for the feature should also use the appropriate schema. -When you choose the appropriate schema for tables, consider the following guidelines as part of the [Cells](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/cells/) architecture: +When you choose the appropriate [schema](../database/multiple_databases.md#gitlab-schema) for tables, consider the following guidelines as part of the [Cells](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/cells/) architecture: - Default to `gitlab_main_cell`: We expect most tables to be assigned to the `gitlab_main_cell` schema by default. Choose this schema if the data in the table is related to `projects` or `namespaces`. - Consult with the Tenant Scale group: If you believe that the `gitlab_main_clusterwide` schema is more suitable for a table, seek approval from the Tenant Scale group. This is crucial because it has scaling implications and may require reconsideration of the schema choice. +Tables with `gitlab_main_clusterwide` schema will need additional work to be replicated to other / all cells. +The replication strategy will likely be different for each case, but will involve internal APIs. +The application may also need to be modified to restrict writes to prevent conflicts. +We may also ask teams to update tables from `gitlab_main_clusterwide` to `gitlab_main_cell` as required, which also might require adding sharding keys to these tables. + To understand how existing tables are classified, you can use [this dashboard](https://manojmj.gitlab.io/tenant-scale-schema-progress/). After a schema has been assigned, the merge request pipeline might fail due to one or more of the following reasons, which can be rectified by following the linked guidelines: diff --git a/doc/development/code_suggestions/index.md b/doc/development/code_suggestions/index.md index 8598a3e9c3d..eda634eb5e3 100644 --- a/doc/development/code_suggestions/index.md +++ b/doc/development/code_suggestions/index.md @@ -32,7 +32,7 @@ This should enable everyone to see locally any change in an IDE being sent to th 1. Main Application (GDK): 1. Install the [GitLab Development Kit](https://gitlab.com/gitlab-org/gitlab-development-kit/-/blob/main/doc/index.md#one-line-installation). - 1. Enable Feature Flag ```ai_duo_code_suggestions_switch```: + 1. Enable Feature Flag `ai_duo_code_suggestions_switch`: 1. In your terminal, go to your `gitlab-development-kit` > `gitlab` directory. 1. Run `gdk rails console` or `bundle exec rails c` to start a Rails console. 1. [Enable the Feature Flag](../../administration/feature_flags.md#enable-or-disable-the-feature) for the Code Suggestions tokens API by calling `Feature.enable(:ai_duo_code_suggestions_switch)` from the console. @@ -63,7 +63,7 @@ with the deployed staging AI Gateway. To do this: 1. Add a **GitLab Ultimate Self-Managed** subscription with a [Duo Pro subscription add-on](../../subscriptions/subscription-add-ons.md) to your GDK instance. 1. Sign in to the [staging Customers Portal](https://customers.staging.gitlab.com) by selecting the **Continue with GitLab.com account** button. - If you do not have an existing account, you are prompted to create one. + If you do not have an existing account, you are prompted to create one. 1. If you do not have an existing cloud activation code, visit the **Ultimate Self-Managed Subscription** page using the [buy subscription flow link](https://gitlab.com/gitlab-org/customers-gitlab-com/-/blob/8aa922840091ad5c5d96ada43d0065a1b6198841/doc/flows/buy_subscription.md). 1. Purchase the subscription using [a test credit card](https://gitlab.com/gitlab-org/customers-gitlab-com/#testing-credit-card-information). 1. Once you have a subscription, on the subscription card, select the ellipse menu **...** > **Buy Duo Pro add-on**. @@ -81,13 +81,13 @@ with the deployed staging AI Gateway. To do this: export GITLAB_SIMULATE_SAAS=0 ``` - On a non-GDK instance, you can set the variables using `gitlab_rails['env']` in the `gitlab.rb` file: + On a non-GDK instance, you can set the variables using `gitlab_rails['env']` in the `gitlab.rb` file: ```shell gitlab_rails['env'] = { - 'GITLAB_LICENSE_MODE' => 'test', - 'CUSTOMER_PORTAL_URL' => 'https://customers.staging.gitlab.com', - 'AI_GATEWAY_URL' => 'https://cloud.staging.gitlab.com/ai' + 'GITLAB_LICENSE_MODE' => 'test', + 'CUSTOMER_PORTAL_URL' => 'https://customers.staging.gitlab.com', + 'AI_GATEWAY_URL' => 'https://cloud.staging.gitlab.com/ai' } ``` @@ -97,19 +97,19 @@ with the deployed staging AI Gateway. To do this: 1. Add the new activation code. 1. Inside your GDK, navigate to **Admin area** > **GitLab Duo Pro**, go to `/admin/code_suggestions` -1. Filter users to find `root` and click the toggle to assign a GitLab Duo Pro add-on seat to the root user +1. Filter users to find `root` and click the toggle to assign a GitLab Duo Pro add-on seat to the root user. ### Setup instructions to use the Duo Pro add-on with a **staging** GitLab.com account -1. Have your account ready at -1. [Create a new group](../../user/group/index.md#create-a-group) or use an existing one as the namespace which will receive the Duo Pro access -1. Navigate to `Settings > Billing` -1. Initiate the purchase flow for the Ultimate plan by clicking on `Upgrade to Ultimate` -1. After being redirected to , click on `Continue with your Gitlab.com account` -1. Purchase the SaaS Ultimate subscription using [a test credit card](https://gitlab.com/gitlab-org/customers-gitlab-com#testing-credit-card-information) -1. Find the newly purchased subscription card, and select from the three dots menu the option `Buy GitLab Duo Pro` -1. Purchase the GitLab Duo Pro add-on using the same test credit card from the above steps -1. Go back to and verify that your group has access to Duo Pro by navigating to `Settings > GitLab Duo` and managing seats +1. Have your account ready at . +1. [Create a new group](../../user/group/index.md#create-a-group) or use an existing one as the namespace which will receive the Duo Pro access. +1. Navigate to `Settings > Billing`. +1. Initiate the purchase flow for the Ultimate plan by clicking on `Upgrade to Ultimate`. +1. After being redirected to , click on `Continue with your Gitlab.com account`. +1. Purchase the SaaS Ultimate subscription using [a test credit card](https://gitlab.com/gitlab-org/customers-gitlab-com#testing-credit-card-information). +1. Find the newly purchased subscription card, and select from the three dots menu the option `Buy GitLab Duo Pro`. +1. Purchase the GitLab Duo Pro add-on using the same test credit card from the above steps. +1. Go back to and verify that your group has access to Duo Pro by navigating to `Settings > GitLab Duo` and managing seats. ### Video demonstrations of installing and using Code Suggestions in IDEs diff --git a/lib/gitlab/quick_actions/merge_request_actions.rb b/lib/gitlab/quick_actions/merge_request_actions.rb index addd7f098a3..5f9dbf90c05 100644 --- a/lib/gitlab/quick_actions/merge_request_actions.rb +++ b/lib/gitlab/quick_actions/merge_request_actions.rb @@ -351,8 +351,7 @@ module Gitlab end types MergeRequest condition do - quick_action_target.persisted? && - reviewers_to_remove?(@updates) && + reviewers_to_remove?(@updates) && current_user.can?(:"admin_#{quick_action_target.to_ability_name}", project) end parse_params do |unassign_reviewer_param| @@ -360,12 +359,24 @@ module Gitlab extract_users(unassign_reviewer_param) if quick_action_target.allows_multiple_reviewers? end command :unassign_reviewer, :remove_reviewer do |users = nil| + current_reviewers = quick_action_target.reviewers + # if preceding commands have been executed already, we need to use the updated reviewer_ids + current_reviewers = User.find(@updates[:reviewer_ids]) if @updates[:reviewer_ids].present? + if quick_action_target.allows_multiple_reviewers? && users&.any? @updates[:reviewer_ids] ||= quick_action_target.reviewers.map(&:id) @updates[:reviewer_ids] -= users.map(&:id) else @updates[:reviewer_ids] = [] end + + removed_reviewers = current_reviewers.select { |user| @updates[:reviewer_ids].exclude?(user.id) } + # only generate the message here if the change would not be traceable otherwise + # because all reviewers have been assigned and removed immediately + if removed_reviewers.present? && !reviewers_to_remove?(@updates) + @execution_message[:unassign_reviewer] = _("Removed %{reviewer_text} %{reviewer_references}.") % + { reviewer_text: 'reviewer'.pluralize(removed_reviewers.size), reviewer_references: removed_reviewers.map(&:to_reference).to_sentence } + end end end diff --git a/locale/gitlab.pot b/locale/gitlab.pot index ecc0a5aea15..9c7f1646953 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -20213,6 +20213,18 @@ msgstr "" msgid "DuoEnterpriseTrial|We just need some additional information to activate your trial." msgstr "" +msgid "DuoFreeAccessEndingBanner|Buy Duo Pro" +msgstr "" + +msgid "DuoFreeAccessEndingBanner|Dismiss Free Access Ending banner" +msgstr "" + +msgid "DuoFreeAccessEndingBanner|Free access to GitLab Duo is ending on %{date}" +msgstr "" + +msgid "DuoFreeAccessEndingBanner|Starting %{date}, all GitLab Duo features, including Duo Chat, %{link_start}require a paid add-on subscription.%{link_end} To ensure uninterrupted access to Code Suggestions and Chat, buy Duo Pro and assign seats to your users. Or, for Duo Enterprise options, contact Sales." +msgstr "" + msgid "DuoProDiscover|Accelerate your path to market" msgstr "" diff --git a/package.json b/package.json index 4b3995e822e..de86839e0e9 100644 --- a/package.json +++ b/package.json @@ -279,7 +279,7 @@ "eslint-plugin-import": "^2.30.0", "eslint-plugin-local-rules": "^3.0.2", "eslint-plugin-no-jquery": "2.7.0", - "eslint-plugin-no-unsanitized": "^4.1.0", + "eslint-plugin-no-unsanitized": "^4.1.1", "fake-indexeddb": "^4.0.1", "gettext-extractor": "^3.7.0", "gettext-extractor-vue": "^5.1.0", diff --git a/spec/lib/gitlab/database/no_cross_db_foreign_keys_spec.rb b/spec/lib/gitlab/database/no_cross_db_foreign_keys_spec.rb index 848ce551916..f66acb1226e 100644 --- a/spec/lib/gitlab/database/no_cross_db_foreign_keys_spec.rb +++ b/spec/lib/gitlab/database/no_cross_db_foreign_keys_spec.rb @@ -33,8 +33,7 @@ RSpec.describe 'cross-database foreign keys' do 'protected_branch_merge_access_levels.user_id', # https://gitlab.com/gitlab-org/gitlab/-/issues/431055 'user_group_callouts.user_id', # https://gitlab.com/gitlab-org/gitlab/-/issues/421287 'subscription_user_add_on_assignments.user_id', # https://gitlab.com/gitlab-org/gitlab/-/issues/444666 - 'subscription_add_on_purchases.subscription_add_on_id', # https://gitlab.com/gitlab-org/gitlab/-/issues/444666 - 'dast_pre_scan_verifications.dast_profile_id' # https://gitlab.com/gitlab-org/gitlab/-/issues/479141 + 'subscription_add_on_purchases.subscription_add_on_id' # https://gitlab.com/gitlab-org/gitlab/-/issues/444666 ] end diff --git a/spec/requests/api/graphql/user/contributed_projects_query_spec.rb b/spec/requests/api/graphql/user/contributed_projects_query_spec.rb index 1fe088e1403..64b7a24dea2 100644 --- a/spec/requests/api/graphql/user/contributed_projects_query_spec.rb +++ b/spec/requests/api/graphql/user/contributed_projects_query_spec.rb @@ -9,22 +9,25 @@ RSpec.describe 'Getting contributedProjects of the user', feature_category: :gro let(:user_params) { { username: user.username } } let(:user_fields) { 'contributedProjects { nodes { id } }' } - let_it_be(:user) { create(:user) } + let_it_be(:user) { create(:user, :with_namespace) } let_it_be(:current_user) { create(:user) } let_it_be(:public_project) { create(:project, :public) } let_it_be(:private_project) { create(:project, :private) } let_it_be(:internal_project) { create(:project, :internal) } + let_it_be(:personal_project) { create(:project, namespace: user.namespace) } let(:path) { %i[user contributed_projects nodes] } before_all do private_project.add_developer(user) private_project.add_developer(current_user) + personal_project.add_developer(current_user) travel_to(4.hours.from_now) { create(:push_event, project: private_project, author: user) } travel_to(3.hours.from_now) { create(:push_event, project: internal_project, author: user) } travel_to(2.hours.from_now) { create(:push_event, project: public_project, author: user) } + travel_to(2.hours.from_now) { create(:push_event, project: personal_project, author: user) } end it_behaves_like 'a working graphql query' do @@ -410,6 +413,37 @@ RSpec.describe 'Getting contributedProjects of the user', feature_category: :gro end end + context 'when include_personal argument is false' do + it 'does not include personal projects' do + post_graphql(query, current_user: current_user) + + expect(graphql_data_at(*path)) + .to contain_exactly( + a_graphql_entity_for(private_project), + a_graphql_entity_for(internal_project), + a_graphql_entity_for(public_project) + ) + end + end + + context 'when include_personal argument is true' do + let(:query_with_include_personal) do + graphql_query_for(:user, user_params, 'contributedProjects(includePersonal: true) { nodes { id } }') + end + + it 'includes personal projects' do + post_graphql(query_with_include_personal, current_user: current_user) + + expect(graphql_data_at(*path)) + .to contain_exactly( + a_graphql_entity_for(private_project), + a_graphql_entity_for(internal_project), + a_graphql_entity_for(public_project), + a_graphql_entity_for(personal_project) + ) + end + end + describe 'sorting and pagination' do let(:data_path) { [:user, :contributed_projects] } diff --git a/spec/services/quick_actions/interpret_service_spec.rb b/spec/services/quick_actions/interpret_service_spec.rb index e7503fbd19b..715ca663c88 100644 --- a/spec/services/quick_actions/interpret_service_spec.rb +++ b/spec/services/quick_actions/interpret_service_spec.rb @@ -1200,6 +1200,26 @@ RSpec.describe QuickActions::InterpretService, feature_category: :team_planning it_behaves_like 'failed command', 'Could not apply unassign_reviewer command.' end + context 'with a not-yet-persisted merge request and a preceding assign_reviewer command' do + let(:content) do + <<-QUICKACTION +/assign_reviewer #{developer.to_reference} +/unassign_reviewer #{developer.to_reference} + QUICKACTION + end + + let(:issuable) { build(:merge_request) } + + it 'adds and then removes a single reviewer in a single step' do + _, updates, message = service.execute(content, issuable) + translated_string = _("Assigned %{developer_to_reference} as reviewer. Removed reviewer %{developer_to_reference}.") + formatted_message = format(translated_string, developer_to_reference: developer.to_reference.to_s) + + expect(updates).to eq(reviewer_ids: []) + expect(message).to eq(formatted_message) + end + end + context 'with anything after the command' do let(:content) { '/unassign_reviewer supercalifragilisticexpialidocious' } diff --git a/yarn.lock b/yarn.lock index aa5f5ba85ad..ac8672cfba2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6800,10 +6800,10 @@ eslint-plugin-no-jquery@2.7.0: resolved "https://registry.yarnpkg.com/eslint-plugin-no-jquery/-/eslint-plugin-no-jquery-2.7.0.tgz#855f5631cf5b8e25b930cf6f06e02dd81f132e72" integrity sha512-Aeg7dA6GTH1AcWLlBtWNzOU9efK5KpNi7b0EhBO0o0M+awyzguUUo8gF6hXGjQ9n5h8/uRtYv9zOqQkeC5CG0w== -eslint-plugin-no-unsanitized@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-no-unsanitized/-/eslint-plugin-no-unsanitized-4.1.0.tgz#2c914e8ea8048c3afaac8f0c12384747aba6497a" - integrity sha512-9A8Yrbkkex8e56ivxJ2f5dXN2Js2BmKC8QgmeYZjadyiGUngo3KLXDlq6ZzalmCHyLwLF5MoQLPR6FWlNc+Qbw== +eslint-plugin-no-unsanitized@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-no-unsanitized/-/eslint-plugin-no-unsanitized-4.1.1.tgz#15e638416fcac50c4f1d4d13473cfa339ea163aa" + integrity sha512-N0yf7iYWQAO/qiglZlrASXRf6I/18q9d9NNR5Vw175zgrPduvLfnBwgWwM75D4g4lbrd9uPNXlieaFGWZ40h4A== eslint-plugin-promise@^7.0.0: version "7.0.0"