Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
0c84203069
commit
f7f7703e1e
|
|
@ -282,7 +282,6 @@ export default {
|
|||
'ee/app/assets/javascripts/hand_raise_leads/hand_raise_lead/components/hand_raise_lead_modal.vue',
|
||||
'ee/app/assets/javascripts/insights/components/insights_chart.vue',
|
||||
'ee/app/assets/javascripts/invite_members/components/invite_modal_base.vue',
|
||||
'ee/app/assets/javascripts/merge_requests/components/reviewers/approval_summary.vue',
|
||||
'ee/app/assets/javascripts/oncall_schedules/components/add_edit_schedule_form.vue',
|
||||
'ee/app/assets/javascripts/oncall_schedules/components/oncall_schedule.vue',
|
||||
'ee/app/assets/javascripts/oncall_schedules/components/rotations/components/rotation_assignee.vue',
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ 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: "5613c3aaf1cacdc6df7e05b96de980be74742116"
|
||||
GITLAB_HELM_CHART_REF: "dd78d0e357b90c54a7a7073f9d6ac18e8cc06161"
|
||||
# Specific ref for cng-mirror project to trigger builds for
|
||||
GITLAB_CNG_MIRROR_REF: "df7aafcccafdbab732a7cf757efb3b7b74c851dd"
|
||||
# Makes sure some of the common scripts from pipeline-common use bundler to execute commands
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
41a9a2963eb4f08f2ba15667d005377f6127fa4d
|
||||
e7ca9463ccd7a3a3001d3d7d3ac21408b9632f6e
|
||||
|
|
|
|||
|
|
@ -138,6 +138,7 @@ export const initSharedAccessTokenApp = () => {
|
|||
const {
|
||||
accessTokenMaxDate,
|
||||
accessTokenMinDate,
|
||||
accessTokenAvailableScopes,
|
||||
accessTokenName,
|
||||
accessTokenDescription,
|
||||
accessTokenScopes,
|
||||
|
|
@ -152,6 +153,7 @@ export const initSharedAccessTokenApp = () => {
|
|||
name: 'AccessTokensRoot',
|
||||
pinia,
|
||||
provide: {
|
||||
accessTokenAvailableScopes: JSON.parse(accessTokenAvailableScopes),
|
||||
accessTokenMaxDate,
|
||||
accessTokenMinDate,
|
||||
accessTokenCreate,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
<script>
|
||||
import { GlBadge } from '@gitlab/ui';
|
||||
import { camelCase } from 'lodash';
|
||||
import { QUERIES } from '../constants';
|
||||
|
||||
export default {
|
||||
|
|
@ -30,7 +31,7 @@ export default {
|
|||
query: QUERIES[query].countQuery,
|
||||
variables,
|
||||
manual: true,
|
||||
context: { batchKey: `MergeRequestTabsCounts` },
|
||||
context: { batchKey: `MergeRequestTabsCounts_${camelCase(this.title)}` },
|
||||
result({ data }) {
|
||||
if (data?.currentUser) {
|
||||
this.count += data?.currentUser?.mergeRequests?.count ?? 0;
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ export default {
|
|||
MaxExpirationDateMessage: () =>
|
||||
import('ee_component/vue_shared/components/access_tokens/max_expiration_date_message.vue'),
|
||||
},
|
||||
inject: ['accessTokenMaxDate', 'accessTokenMinDate'],
|
||||
inject: ['accessTokenMaxDate', 'accessTokenMinDate', 'accessTokenAvailableScopes'],
|
||||
props: {
|
||||
name: {
|
||||
type: String,
|
||||
|
|
@ -85,72 +85,6 @@ export default {
|
|||
helpScopes: helpPagePath('user/profile/personal_access_tokens', {
|
||||
anchor: 'personal-access-token-scopes',
|
||||
}),
|
||||
scopes: [
|
||||
{
|
||||
value: 'read_service_ping',
|
||||
text: s__(
|
||||
'AccessTokens|Grant access to download Service Ping payload via API when authenticated as an admin user.',
|
||||
),
|
||||
},
|
||||
{
|
||||
value: 'read_user',
|
||||
text: s__(
|
||||
'AccessTokens|Grants read-only access to your profile through the /user API endpoint, which includes username, public email, and full name. Also grants access to read-only API endpoints under /users.',
|
||||
),
|
||||
},
|
||||
{
|
||||
value: 'read_repository',
|
||||
text: s__(
|
||||
'AccessTokens|Grants read-only access to repositories on private projects using Git-over-HTTP or the Repository Files API.',
|
||||
),
|
||||
},
|
||||
{
|
||||
value: 'read_api',
|
||||
text: s__(
|
||||
'AccessTokens|Grants read access to the API, including all groups and projects, the container registry, and the package registry.',
|
||||
),
|
||||
},
|
||||
{
|
||||
value: 'self_rotate',
|
||||
text: s__('AccessTokens|Grants permission for token to rotate itself.'),
|
||||
},
|
||||
{
|
||||
value: 'write_repository',
|
||||
text: s__(
|
||||
'AccessTokens|Grants read-write access to repositories on private projects using Git-over-HTTP (not using the API).',
|
||||
),
|
||||
},
|
||||
{
|
||||
value: 'api',
|
||||
text: s__(
|
||||
'AccessTokens|Grants complete read/write access to the API, including all groups and projects, the container registry, the dependency proxy, and the package registry.',
|
||||
),
|
||||
},
|
||||
{
|
||||
value: 'ai_features',
|
||||
text: s__('AccessTokens|Grants access to GitLab Duo related API endpoints.'),
|
||||
},
|
||||
{ value: 'create_runner', text: s__('AccessTokens|Grants create access to the runners.') },
|
||||
{ value: 'manage_runner', text: s__('AccessTokens|Grants access to manage the runners.') },
|
||||
{
|
||||
value: 'k8s_proxy',
|
||||
text: s__(
|
||||
'AccessTokens|Grants permission to perform Kubernetes API calls using the agent for Kubernetes.',
|
||||
),
|
||||
},
|
||||
{
|
||||
value: 'sudo',
|
||||
text: s__(
|
||||
'AccessTokens|Grants permission to perform API actions as any user in the system, when authenticated as an admin user.',
|
||||
),
|
||||
},
|
||||
{
|
||||
value: 'admin_mode',
|
||||
text: s__(
|
||||
'AccessTokens|Grants permission to perform API actions as an administrator, when Admin Mode is enabled.',
|
||||
),
|
||||
},
|
||||
],
|
||||
fields: {
|
||||
name: {
|
||||
label: s__('AccessTokens|Token name'),
|
||||
|
|
@ -232,7 +166,7 @@ export default {
|
|||
<template #input(scopes)="{ id, input, validation, value }">
|
||||
<gl-form-checkbox-group :id="id" :state="validation.state" :checked="value" @input="input">
|
||||
<gl-form-checkbox
|
||||
v-for="scope in $options.scopes"
|
||||
v-for="scope in accessTokenAvailableScopes"
|
||||
:key="scope.value"
|
||||
:value="scope.value"
|
||||
:state="validation.state"
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
@import '../mixins_and_variables_and_functions';
|
||||
|
||||
.geo-replicable-item-grid {
|
||||
grid-template-columns: 8ch 1fr auto;
|
||||
grid-gap: 1rem;
|
||||
grid-template-columns: max-content 1fr auto;
|
||||
grid-gap: 0.5rem;
|
||||
}
|
||||
|
||||
.geo-replicable-filter-grid {
|
||||
|
|
|
|||
|
|
@ -35,10 +35,24 @@ module AccessTokensHelper
|
|||
}.to_json
|
||||
end
|
||||
|
||||
def personal_access_token_data(token)
|
||||
def filter_sort_scopes(scopes, sources)
|
||||
scopes.select { |scope| ::Gitlab::Auth::UI_SCOPES_ORDERED_BY_PERMISSION.include?(scope) }
|
||||
.sort_by { |scope| ::Gitlab::Auth::UI_SCOPES_ORDERED_BY_PERMISSION.index(scope) }
|
||||
.map do |value|
|
||||
{
|
||||
value: value,
|
||||
text: t(value, scope: sources)
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
def personal_access_token_data(token, user = current_user)
|
||||
sources = scope_description(:personal_access_token)
|
||||
scopes = ::Gitlab::Auth.available_scopes_for(user)
|
||||
{
|
||||
access_token: {
|
||||
**expires_at_field_data,
|
||||
available_scopes: filter_sort_scopes(scopes, sources).to_json,
|
||||
name: token[:name],
|
||||
description: token[:description],
|
||||
scopes: token[:scopes].to_json,
|
||||
|
|
|
|||
|
|
@ -96,17 +96,18 @@ module CrossDatabaseModification
|
|||
end
|
||||
|
||||
def gitlab_schema
|
||||
case self.name
|
||||
when 'ActiveRecord::Base', 'ApplicationRecord'
|
||||
dbname, conn = Gitlab::Database.all_database_connections.find { |(_, conn)| conn.klass.name == self.name }
|
||||
|
||||
if conn
|
||||
:"gitlab_#{dbname}"
|
||||
# DB connections do not reference ApplicationRecord directly as they are used to apply load balancing,
|
||||
# so instead we use the module to determine if we are in the main database
|
||||
elsif self.name == 'ApplicationRecord'
|
||||
:gitlab_main
|
||||
when 'SecApplicationRecord'
|
||||
:gitlab_sec
|
||||
when 'Ci::ApplicationRecord'
|
||||
:gitlab_ci
|
||||
when 'PackageMetadata::ApplicationRecord'
|
||||
elsif self.name == 'PackageMetadata::ApplicationRecord'
|
||||
:gitlab_pm
|
||||
else
|
||||
Gitlab::Database::GitlabSchema.table_schema(table_name) if table_name
|
||||
elsif table_name
|
||||
Gitlab::Database::GitlabSchema.table_schema(table_name)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
files = git.added_files + git.modified_files
|
||||
|
||||
finalize_batched_background_migration_validator.validate_migrations(files)
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require_relative '../../tooling/danger/finalize_batched_background_migration_validator_helper'
|
||||
|
||||
module Danger
|
||||
class FinalizeBatchedBackgroundMigrationValidator < ::Danger::Plugin
|
||||
# Put the helper code somewhere it can be tested
|
||||
include Tooling::Danger::FinalizeBatchedBackgroundMigrationValidatorHelper
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
---
|
||||
table_name: workspace_tokens
|
||||
classes:
|
||||
- RemoteDevelopment::WorkspaceToken
|
||||
feature_categories:
|
||||
- workspaces
|
||||
description: Stores the token associated with a workspace
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/193071
|
||||
milestone: '18.1'
|
||||
gitlab_schema: gitlab_main_cell
|
||||
sharding_key:
|
||||
project_id: projects
|
||||
table_size: small
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class CreateWorkspaceTokens < Gitlab::Database::Migration[2.3]
|
||||
milestone '18.1'
|
||||
|
||||
# @return [void]
|
||||
def change
|
||||
create_table :workspace_tokens do |t|
|
||||
t.references :workspace, foreign_key: { on_delete: :cascade }, null: false, index: { unique: true }
|
||||
|
||||
t.timestamps_with_timezone null: false
|
||||
t.bigint :project_id, null: false, index: true
|
||||
t.text :token_encrypted, limit: 512, null: false
|
||||
|
||||
t.check_constraint 'char_length(token_encrypted) <= 512'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddProjectIdForeignKeyToWorkspaceTokens < Gitlab::Database::Migration[2.3]
|
||||
disable_ddl_transaction!
|
||||
milestone '18.1'
|
||||
|
||||
def up
|
||||
add_concurrent_foreign_key :workspace_tokens, :projects, column: :project_id, on_delete: :cascade
|
||||
end
|
||||
|
||||
def down
|
||||
with_lock_retries do
|
||||
remove_foreign_key :workspace_tokens, column: :project_id
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1 @@
|
|||
00efa3926632481160d4c4e2bf87bbddd19a32cd52515534e65e4a4dcfe3ab29
|
||||
|
|
@ -0,0 +1 @@
|
|||
d5096916ba707df9a6939c0703a2577fc8c244acd9a8c3304c0d83e697e149bb
|
||||
|
|
@ -26121,6 +26121,26 @@ CREATE SEQUENCE workspace_agentk_states_id_seq
|
|||
|
||||
ALTER SEQUENCE workspace_agentk_states_id_seq OWNED BY workspace_agentk_states.id;
|
||||
|
||||
CREATE TABLE workspace_tokens (
|
||||
id bigint NOT NULL,
|
||||
workspace_id bigint NOT NULL,
|
||||
created_at timestamp with time zone NOT NULL,
|
||||
updated_at timestamp with time zone NOT NULL,
|
||||
project_id bigint NOT NULL,
|
||||
token_encrypted text NOT NULL,
|
||||
CONSTRAINT check_35b32b26d3 CHECK ((char_length(token_encrypted) <= 512)),
|
||||
CONSTRAINT chk_rails_78f4622c4e CHECK ((char_length(token_encrypted) <= 512))
|
||||
);
|
||||
|
||||
CREATE SEQUENCE workspace_tokens_id_seq
|
||||
START WITH 1
|
||||
INCREMENT BY 1
|
||||
NO MINVALUE
|
||||
NO MAXVALUE
|
||||
CACHE 1;
|
||||
|
||||
ALTER SEQUENCE workspace_tokens_id_seq OWNED BY workspace_tokens.id;
|
||||
|
||||
CREATE TABLE workspace_variables (
|
||||
id bigint NOT NULL,
|
||||
workspace_id bigint NOT NULL,
|
||||
|
|
@ -28354,6 +28374,8 @@ ALTER TABLE ONLY work_item_widget_definitions ALTER COLUMN id SET DEFAULT nextva
|
|||
|
||||
ALTER TABLE ONLY workspace_agentk_states ALTER COLUMN id SET DEFAULT nextval('workspace_agentk_states_id_seq'::regclass);
|
||||
|
||||
ALTER TABLE ONLY workspace_tokens ALTER COLUMN id SET DEFAULT nextval('workspace_tokens_id_seq'::regclass);
|
||||
|
||||
ALTER TABLE ONLY workspace_variables ALTER COLUMN id SET DEFAULT nextval('workspace_variables_id_seq'::regclass);
|
||||
|
||||
ALTER TABLE ONLY workspaces ALTER COLUMN id SET DEFAULT nextval('workspaces_id_seq'::regclass);
|
||||
|
|
@ -31594,6 +31616,9 @@ ALTER TABLE ONLY work_item_widget_definitions
|
|||
ALTER TABLE ONLY workspace_agentk_states
|
||||
ADD CONSTRAINT workspace_agentk_states_pkey PRIMARY KEY (id);
|
||||
|
||||
ALTER TABLE ONLY workspace_tokens
|
||||
ADD CONSTRAINT workspace_tokens_pkey PRIMARY KEY (id);
|
||||
|
||||
ALTER TABLE ONLY workspace_variables
|
||||
ADD CONSTRAINT workspace_variables_pkey PRIMARY KEY (id);
|
||||
|
||||
|
|
@ -38226,6 +38251,10 @@ CREATE INDEX index_workspace_agentk_states_on_project_id ON workspace_agentk_sta
|
|||
|
||||
CREATE UNIQUE INDEX index_workspace_agentk_states_on_workspace_id ON workspace_agentk_states USING btree (workspace_id);
|
||||
|
||||
CREATE INDEX index_workspace_tokens_on_project_id ON workspace_tokens USING btree (project_id);
|
||||
|
||||
CREATE UNIQUE INDEX index_workspace_tokens_on_workspace_id ON workspace_tokens USING btree (workspace_id);
|
||||
|
||||
CREATE INDEX index_workspace_variables_on_project_id ON workspace_variables USING btree (project_id);
|
||||
|
||||
CREATE INDEX index_workspace_variables_on_workspace_id ON workspace_variables USING btree (workspace_id);
|
||||
|
|
@ -42673,6 +42702,9 @@ ALTER TABLE ONLY merge_request_diffs
|
|||
ALTER TABLE ONLY ml_candidates
|
||||
ADD CONSTRAINT fk_56d6ed4d3d FOREIGN KEY (experiment_id) REFERENCES ml_experiments(id) ON DELETE CASCADE;
|
||||
|
||||
ALTER TABLE ONLY workspace_tokens
|
||||
ADD CONSTRAINT fk_5724f2499d FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
|
||||
|
||||
ALTER TABLE ONLY projects_branch_rules_squash_options
|
||||
ADD CONSTRAINT fk_574b8d531f FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
|
||||
|
||||
|
|
@ -44947,6 +44979,9 @@ ALTER TABLE ONLY work_item_parent_links
|
|||
ALTER TABLE ONLY system_access_microsoft_graph_access_tokens
|
||||
ADD CONSTRAINT fk_rails_604908851f FOREIGN KEY (system_access_microsoft_application_id) REFERENCES system_access_microsoft_applications(id) ON DELETE CASCADE;
|
||||
|
||||
ALTER TABLE ONLY workspace_tokens
|
||||
ADD CONSTRAINT fk_rails_60666321a4 FOREIGN KEY (workspace_id) REFERENCES workspaces(id) ON DELETE CASCADE;
|
||||
|
||||
ALTER TABLE ONLY vulnerability_state_transitions
|
||||
ADD CONSTRAINT fk_rails_60e4899648 FOREIGN KEY (vulnerability_id) REFERENCES vulnerabilities(id) ON DELETE CASCADE;
|
||||
|
||||
|
|
|
|||
|
|
@ -1485,14 +1485,13 @@ background migration.
|
|||
|
||||
```ruby
|
||||
class FinalizeBackfillRouteNamespaceId < Gitlab::Database::Migration[2.1]
|
||||
MIGRATION = 'BackfillRouteNamespaceId'
|
||||
disable_ddl_transaction!
|
||||
|
||||
restrict_gitlab_migration gitlab_schema: :gitlab_main
|
||||
|
||||
def up
|
||||
ensure_batched_background_migration_is_finished(
|
||||
job_class_name: MIGRATION,
|
||||
job_class_name: 'BackfillRouteNamespaceId',
|
||||
table_name: :routes,
|
||||
column_name: :id,
|
||||
job_arguments: [],
|
||||
|
|
|
|||
|
|
@ -115,10 +115,17 @@ consume system resources. You can use the following settings and configuration d
|
|||
mattermost_external_url 'http://mattermost.example.com'
|
||||
|
||||
# Shut down GitLab services on the Mattermost server
|
||||
alertmanager['enable'] = false
|
||||
gitlab_exporter['enable'] = false
|
||||
gitlab_kas['enable'] = false
|
||||
gitlab_rails['enable'] = false
|
||||
redis['enable'] = false
|
||||
postgres_exporter['enable'] = false
|
||||
grafana['enable'] = false
|
||||
letsencrypt['enable'] = false
|
||||
node_exporter['enable'] = false
|
||||
postgres_exporter['enable'] = false
|
||||
prometheus['enable'] = false
|
||||
redis_exporter['enable'] = false
|
||||
redis['enable'] = false
|
||||
```
|
||||
|
||||
Then follow the appropriate steps in the [Authorize GitLab Mattermost section](#authorize-gitlab-mattermost). Last, to enable
|
||||
|
|
|
|||
|
|
@ -37,21 +37,22 @@ Group items that are migrated to the destination GitLab instance include:
|
|||
|
||||
<!-- vale gitlab_base.OutdatedVersions = NO -->
|
||||
|
||||
| Group item | Introduced in |
|
||||
|:---------------------|:----------------------------------------------------------------------------|
|
||||
| Badges | [GitLab 13.11](https://gitlab.com/gitlab-org/gitlab/-/issues/292431) |
|
||||
| Boards | [GitLab 13.7](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/18938) |
|
||||
| Board lists | [GitLab 13.7](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/24863) |
|
||||
| Epics <sup>1</sup> | [GitLab 13.7](https://gitlab.com/gitlab-org/gitlab/-/issues/250281) |
|
||||
| Group labels <sup>2</sup> | [GitLab 13.9](https://gitlab.com/gitlab-org/gitlab/-/issues/292429) |
|
||||
| Iterations | [GitLab 13.10](https://gitlab.com/gitlab-org/gitlab/-/issues/292428) |
|
||||
| Iteration cadences | [GitLab 15.4](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96570) |
|
||||
| Members <sup>3</sup> | [GitLab 13.9](https://gitlab.com/gitlab-org/gitlab/-/issues/299415) |
|
||||
| Group milestones | [GitLab 13.10](https://gitlab.com/gitlab-org/gitlab/-/issues/292427) |
|
||||
| Namespace settings | [GitLab 14.10](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/85128) |
|
||||
| Release milestones | [GitLab 15.0](https://gitlab.com/gitlab-org/gitlab/-/issues/339422) |
|
||||
| Subgroups | [GitLab 13.7](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/18938) |
|
||||
| Uploads | [GitLab 13.7](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/18938) |
|
||||
| Group item | Introduced in |
|
||||
| ------------------------- | ------------- |
|
||||
| Badges | [GitLab 13.11](https://gitlab.com/gitlab-org/gitlab/-/issues/292431) |
|
||||
| Board lists | [GitLab 13.7](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/24863) |
|
||||
| Boards | [GitLab 13.7](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/18938) |
|
||||
| Epics <sup>1</sup> | [GitLab 13.7](https://gitlab.com/gitlab-org/gitlab/-/issues/250281) |
|
||||
| Group labels <sup>2</sup> | [GitLab 13.9](https://gitlab.com/gitlab-org/gitlab/-/issues/292429) |
|
||||
| Group milestones | [GitLab 13.10](https://gitlab.com/gitlab-org/gitlab/-/issues/292427) |
|
||||
| Iteration cadences | [GitLab 15.4](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96570) |
|
||||
| Iterations | [GitLab 13.10](https://gitlab.com/gitlab-org/gitlab/-/issues/292428) |
|
||||
| Members <sup>3</sup> | [GitLab 13.9](https://gitlab.com/gitlab-org/gitlab/-/issues/299415) |
|
||||
| Namespace settings | [GitLab 14.10](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/85128) |
|
||||
| Release milestones | [GitLab 15.0](https://gitlab.com/gitlab-org/gitlab/-/issues/339422) |
|
||||
| Subgroups | [GitLab 13.7](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/18938) |
|
||||
| Uploads | [GitLab 13.7](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/18938) |
|
||||
| Wikis | [GitLab 14.6](https://gitlab.com/gitlab-org/gitlab/-/issues/345923) |
|
||||
|
||||
**Footnotes**:
|
||||
|
||||
|
|
@ -242,6 +243,7 @@ Some project items are excluded from migration because they:
|
|||
from GitLab Self-Managed to GitLab.com or GitLab Dedicated
|
||||
- Linked issues
|
||||
- Merge request approval rules
|
||||
- Wiki comments
|
||||
|
||||
{{< alert type="note" >}}
|
||||
|
||||
|
|
|
|||
|
|
@ -18,8 +18,8 @@ title: Test a new look for issues
|
|||
- [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/9584) in GitLab 17.5 [with a flag](../../../administration/feature_flags.md) named `work_items_view_preference`. Disabled by default. This feature is in [beta](../../../policy/development_stages_support.md#beta).
|
||||
- Feature flag named `work_items_view_preference` enabled on GitLab.com in GitLab 17.9 for a subset of users.
|
||||
- Feature flag named `work_items_view_preference` [enabled](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/184496) on GitLab.com, GitLab Self-Managed, and GitLab Dedicated in 17.10.
|
||||
- **New look** toggle [hidden](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/182330) with feature flag named `work_item_view_for_issues`. Flag enabled on GitLab.com, GitLab Self-Managed, and GitLab Dedicated in 17.11.
|
||||
- [Generally available](https://gitlab.com/gitlab-org/gitlab/-/issues/482931) in GitLab 18.2. Feature flag `work_items_view_preference` removed.
|
||||
- [Enabled on GitLab.com, GitLab Self-Managed, and GitLab Dedicated](https://gitlab.com/gitlab-org/gitlab/-/issues/482931) in GitLab 17.11.
|
||||
- [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/482931) to feature flag named `work_item_view_for_issues` in GitLab 18.1. Enabled on GitLab.com, GitLab Self-Managed, and GitLab Dedicated. Feature flag `work_items_view_preference` removed.
|
||||
|
||||
{{< /history >}}
|
||||
|
||||
|
|
@ -61,15 +61,6 @@ The new issues experience includes these improvements:
|
|||
only exist in groups.
|
||||
- **Development**: Merge requests, branches, and feature flags related to this item are shown in a single list.
|
||||
|
||||
## Toggle the new experience
|
||||
|
||||
When you view the Issues page or issue detail page, you can toggle the new experience.
|
||||
|
||||
To toggle the new issue look:
|
||||
|
||||
1. In the upper-right corner look for the **New look** badge.
|
||||
1. Select the badge to toggle the experience on or off.
|
||||
|
||||
## Related topics
|
||||
|
||||
- [Test a new look for epics](../../group/epics/epic_work_items.md)
|
||||
|
|
|
|||
|
|
@ -809,7 +809,7 @@ You can use the OR operator (**is one of: `||`**) when you [filter the list of i
|
|||
{{< history >}}
|
||||
|
||||
- [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/464063) in GitLab 17.4 [with a flag](../../../administration/feature_flags.md) named `issues_list_drawer`. Disabled by default.
|
||||
- In GitLab 17.11, if [the new look for issues](issue_work_items.md) is enabled, this feature is also enabled.
|
||||
- In GitLab 17.11 and later, if [the new look for issues](issue_work_items.md) is enabled, this feature is also enabled.
|
||||
|
||||
{{< /history >}}
|
||||
|
||||
|
|
|
|||
|
|
@ -76,6 +76,31 @@ module Gitlab
|
|||
# Default scopes for OAuth applications that don't define their own
|
||||
DEFAULT_SCOPES = [API_SCOPE].freeze
|
||||
|
||||
# Scopes ordered by permission level, from lowest to highest.
|
||||
# This is used to determine the order of scopes in the UI.
|
||||
# If the scope is not present in this list, it won't appear in the UI.
|
||||
UI_SCOPES_ORDERED_BY_PERMISSION = [
|
||||
READ_SERVICE_PING_SCOPE,
|
||||
READ_USER_SCOPE,
|
||||
READ_REPOSITORY_SCOPE,
|
||||
READ_OBSERVABILITY_SCOPE,
|
||||
READ_VIRTUAL_REGISTRY_SCOPE,
|
||||
READ_REGISTRY_SCOPE,
|
||||
READ_API_SCOPE,
|
||||
SELF_ROTATE_SCOPE,
|
||||
WRITE_REPOSITORY_SCOPE,
|
||||
WRITE_OBSERVABILITY_SCOPE,
|
||||
WRITE_VIRTUAL_REGISTRY_SCOPE,
|
||||
WRITE_REGISTRY_SCOPE,
|
||||
API_SCOPE,
|
||||
AI_FEATURES,
|
||||
CREATE_RUNNER_SCOPE,
|
||||
MANAGE_RUNNER_SCOPE,
|
||||
K8S_PROXY_SCOPE,
|
||||
ADMIN_MODE_SCOPE,
|
||||
SUDO_SCOPE
|
||||
].freeze
|
||||
|
||||
CI_JOB_USER = 'gitlab-ci-token'
|
||||
|
||||
class << self
|
||||
|
|
|
|||
|
|
@ -3139,45 +3139,6 @@ msgstr ""
|
|||
msgid "AccessTokens|Forces new personal, group, project and impersonation tokens to require an expiration date. Does not affect existing token expiration dates or personal access tokens for service accounts. This setting overrides the group-level %{link_start}service account token expiration%{link_end} setting."
|
||||
msgstr ""
|
||||
|
||||
msgid "AccessTokens|Grant access to download Service Ping payload via API when authenticated as an admin user."
|
||||
msgstr ""
|
||||
|
||||
msgid "AccessTokens|Grants access to GitLab Duo related API endpoints."
|
||||
msgstr ""
|
||||
|
||||
msgid "AccessTokens|Grants access to manage the runners."
|
||||
msgstr ""
|
||||
|
||||
msgid "AccessTokens|Grants complete read/write access to the API, including all groups and projects, the container registry, the dependency proxy, and the package registry."
|
||||
msgstr ""
|
||||
|
||||
msgid "AccessTokens|Grants create access to the runners."
|
||||
msgstr ""
|
||||
|
||||
msgid "AccessTokens|Grants permission for token to rotate itself."
|
||||
msgstr ""
|
||||
|
||||
msgid "AccessTokens|Grants permission to perform API actions as an administrator, when Admin Mode is enabled."
|
||||
msgstr ""
|
||||
|
||||
msgid "AccessTokens|Grants permission to perform API actions as any user in the system, when authenticated as an admin user."
|
||||
msgstr ""
|
||||
|
||||
msgid "AccessTokens|Grants permission to perform Kubernetes API calls using the agent for Kubernetes."
|
||||
msgstr ""
|
||||
|
||||
msgid "AccessTokens|Grants read access to the API, including all groups and projects, the container registry, and the package registry."
|
||||
msgstr ""
|
||||
|
||||
msgid "AccessTokens|Grants read-only access to repositories on private projects using Git-over-HTTP or the Repository Files API."
|
||||
msgstr ""
|
||||
|
||||
msgid "AccessTokens|Grants read-only access to your profile through the /user API endpoint, which includes username, public email, and full name. Also grants access to read-only API endpoints under /users."
|
||||
msgstr ""
|
||||
|
||||
msgid "AccessTokens|Grants read-write access to repositories on private projects using Git-over-HTTP (not using the API)."
|
||||
msgstr ""
|
||||
|
||||
msgid "AccessTokens|How do I use DPoP?"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -27851,6 +27812,9 @@ msgstr ""
|
|||
msgid "Geo|Replication summary"
|
||||
msgstr ""
|
||||
|
||||
msgid "Geo|Replication: %{status}"
|
||||
msgstr ""
|
||||
|
||||
msgid "Geo|Repository synchronization concurrency limit"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
|||
|
|
@ -248,7 +248,7 @@
|
|||
"devDependencies": {
|
||||
"@eslint/eslintrc": "^3.2.0",
|
||||
"@eslint/js": "^9.15.0",
|
||||
"@gitlab/eslint-plugin": "20.7.1",
|
||||
"@gitlab/eslint-plugin": "21.0.0",
|
||||
"@gitlab/noop": "^1.0.1",
|
||||
"@gitlab/stylelint-config": "6.2.2",
|
||||
"@graphql-eslint/eslint-plugin": "4.4.0",
|
||||
|
|
|
|||
|
|
@ -22,6 +22,11 @@ describe('AccessTokenForm', () => {
|
|||
|
||||
const accessTokenMaxDate = '2021-07-06';
|
||||
const accessTokenMinDate = '2020-07-06';
|
||||
const accessTokenAvailableScopes = [
|
||||
{ value: 'read_service_ping', text: 'scope 1' },
|
||||
{ value: 'read_user', text: 'scope 2' },
|
||||
{ value: 'other', text: 'scope 3' },
|
||||
];
|
||||
|
||||
const createComponent = (props = {}) => {
|
||||
wrapper = mountExtended(AccessTokenForm, {
|
||||
|
|
@ -29,6 +34,7 @@ describe('AccessTokenForm', () => {
|
|||
provide: {
|
||||
accessTokenMaxDate,
|
||||
accessTokenMinDate,
|
||||
accessTokenAvailableScopes,
|
||||
},
|
||||
propsData: {
|
||||
...props,
|
||||
|
|
@ -82,12 +88,10 @@ describe('AccessTokenForm', () => {
|
|||
createComponent();
|
||||
|
||||
const checkboxes = findCheckboxes();
|
||||
expect(checkboxes).toHaveLength(13);
|
||||
expect(checkboxes).toHaveLength(3);
|
||||
const checkbox = checkboxes.at(0);
|
||||
expect(checkbox.find('input').element.value).toBe('read_service_ping');
|
||||
expect(checkbox.find('label').text()).toContain(
|
||||
'Grant access to download Service Ping payload via API when authenticated as an admin user.',
|
||||
);
|
||||
expect(checkbox.find('label').text()).toContain('scope 1');
|
||||
});
|
||||
|
||||
describe('reset button', () => {
|
||||
|
|
|
|||
|
|
@ -66,6 +66,17 @@ RSpec.describe AccessTokensHelper, feature_category: :system_access do
|
|||
end
|
||||
end
|
||||
|
||||
describe '#filter_sort_scopes' do
|
||||
it 'excludes unknown scopes' do
|
||||
expect(helper.filter_sort_scopes([:dummy], [:doorkeeper])).to match([])
|
||||
end
|
||||
|
||||
it 'sort scopes' do
|
||||
expect(helper.filter_sort_scopes([:sudo, :admin_mode],
|
||||
[:doorkeeper])).to match([{ text: String, value: :admin_mode }, { text: String, value: :sudo }])
|
||||
end
|
||||
end
|
||||
|
||||
describe '#personal_access_token_data' do
|
||||
before do
|
||||
allow(helper).to receive_messages(
|
||||
|
|
@ -77,10 +88,11 @@ RSpec.describe AccessTokensHelper, feature_category: :system_access do
|
|||
it 'returns data for the PATs UI in the user settings' do
|
||||
expect(helper.personal_access_token_data({ name: 'My token',
|
||||
description: 'My description',
|
||||
scopes: [:api, :sudo] })).to match(a_hash_including({
|
||||
scopes: [:api, :sudo] }, 'dummy_user')).to match(a_hash_including({
|
||||
access_token: {
|
||||
max_date: '2022-03-02',
|
||||
min_date: '2022-03-02',
|
||||
available_scopes: '[]',
|
||||
name: 'My token',
|
||||
description: 'My description',
|
||||
scopes: '["api","sudo"]',
|
||||
|
|
|
|||
|
|
@ -46,14 +46,11 @@ RSpec.describe Gitlab::Auth, :use_clean_rails_memory_store_caching, feature_cate
|
|||
describe 'available_scopes' do
|
||||
before do
|
||||
stub_container_registry_config(enabled: true)
|
||||
stub_config(dependency_proxy: { enabled: true })
|
||||
end
|
||||
|
||||
it 'contains all non-default scopes' do
|
||||
expect(subject.all_available_scopes).to match_array %i[
|
||||
api read_user read_api read_repository read_service_ping write_repository read_registry write_registry
|
||||
sudo admin_mode read_observability write_observability create_runner manage_runner k8s_proxy ai_features
|
||||
self_rotate
|
||||
]
|
||||
expect(subject.all_available_scopes).to match_array(subject::UI_SCOPES_ORDERED_BY_PERMISSION)
|
||||
end
|
||||
|
||||
it 'contains for non-admin user all non-default scopes without ADMIN access and without observability scopes' do
|
||||
|
|
@ -61,7 +58,7 @@ RSpec.describe Gitlab::Auth, :use_clean_rails_memory_store_caching, feature_cate
|
|||
|
||||
expect(subject.available_scopes_for(user)).to match_array %i[
|
||||
api read_user read_api read_repository write_repository read_registry write_registry
|
||||
create_runner manage_runner k8s_proxy ai_features self_rotate
|
||||
create_runner manage_runner k8s_proxy ai_features self_rotate read_virtual_registry write_virtual_registry
|
||||
]
|
||||
end
|
||||
|
||||
|
|
@ -70,7 +67,7 @@ RSpec.describe Gitlab::Auth, :use_clean_rails_memory_store_caching, feature_cate
|
|||
|
||||
expect(subject.available_scopes_for(user)).to match_array %i[
|
||||
api read_user read_api read_repository read_service_ping write_repository read_registry write_registry
|
||||
sudo admin_mode create_runner manage_runner k8s_proxy ai_features self_rotate
|
||||
sudo admin_mode create_runner manage_runner k8s_proxy ai_features self_rotate read_virtual_registry write_virtual_registry
|
||||
]
|
||||
end
|
||||
|
||||
|
|
@ -88,7 +85,7 @@ RSpec.describe Gitlab::Auth, :use_clean_rails_memory_store_caching, feature_cate
|
|||
expect(subject.available_scopes_for(group)).to match_array %i[
|
||||
api read_api read_repository write_repository read_registry write_registry
|
||||
read_observability write_observability create_runner manage_runner k8s_proxy ai_features
|
||||
self_rotate
|
||||
self_rotate read_virtual_registry write_virtual_registry
|
||||
]
|
||||
end
|
||||
|
||||
|
|
@ -119,6 +116,8 @@ RSpec.describe Gitlab::Auth, :use_clean_rails_memory_store_caching, feature_cate
|
|||
write_observability
|
||||
write_registry
|
||||
write_repository
|
||||
read_virtual_registry
|
||||
write_virtual_registry
|
||||
]
|
||||
end
|
||||
|
||||
|
|
@ -135,7 +134,7 @@ RSpec.describe Gitlab::Auth, :use_clean_rails_memory_store_caching, feature_cate
|
|||
|
||||
expect(subject.available_scopes_for(group)).to match_array %i[
|
||||
api read_api read_repository write_repository read_registry write_registry create_runner manage_runner
|
||||
k8s_proxy ai_features self_rotate
|
||||
k8s_proxy ai_features self_rotate read_virtual_registry write_virtual_registry
|
||||
]
|
||||
end
|
||||
|
||||
|
|
@ -168,7 +167,7 @@ RSpec.describe Gitlab::Auth, :use_clean_rails_memory_store_caching, feature_cate
|
|||
expect(subject.available_scopes_for(group)).to match_array %i[
|
||||
api read_api read_repository write_repository read_registry write_registry
|
||||
read_observability write_observability create_runner manage_runner k8s_proxy ai_features
|
||||
self_rotate
|
||||
self_rotate read_virtual_registry write_virtual_registry
|
||||
]
|
||||
end
|
||||
|
||||
|
|
@ -177,7 +176,7 @@ RSpec.describe Gitlab::Auth, :use_clean_rails_memory_store_caching, feature_cate
|
|||
|
||||
expect(subject.available_scopes_for(user)).to match_array %i[
|
||||
api read_user read_api read_repository write_repository read_registry write_registry read_service_ping
|
||||
sudo admin_mode create_runner manage_runner k8s_proxy ai_features self_rotate
|
||||
sudo admin_mode create_runner manage_runner k8s_proxy ai_features self_rotate read_virtual_registry write_virtual_registry
|
||||
]
|
||||
end
|
||||
|
||||
|
|
@ -198,7 +197,7 @@ RSpec.describe Gitlab::Auth, :use_clean_rails_memory_store_caching, feature_cate
|
|||
expect(subject.available_scopes_for(other_group)).to match_array %i[
|
||||
api read_api read_repository write_repository read_registry write_registry
|
||||
create_runner manage_runner k8s_proxy ai_features
|
||||
self_rotate
|
||||
self_rotate read_virtual_registry write_virtual_registry
|
||||
]
|
||||
end
|
||||
|
||||
|
|
@ -253,10 +252,6 @@ RSpec.describe Gitlab::Auth, :use_clean_rails_memory_store_caching, feature_cate
|
|||
context 'when dependency proxy is enabled' do
|
||||
let(:virtual_registry_scopes) { %i[read_virtual_registry write_virtual_registry] }
|
||||
|
||||
before do
|
||||
stub_config(dependency_proxy: { enabled: true })
|
||||
end
|
||||
|
||||
it 'contains all virtual registry related scopes' do
|
||||
expect(subject.virtual_registry_scopes).to eq virtual_registry_scopes
|
||||
end
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe CrossDatabaseModification do
|
||||
RSpec.describe CrossDatabaseModification, feature_category: 'database' do
|
||||
describe '.transaction' do
|
||||
it 'adds the current gitlab schema to gitlab_transactions_stack', :aggregate_failures do
|
||||
ApplicationRecord.transaction do
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
- Security::VulnerabilityElasticSearchFinder # Reason: The finder deals with Elasticsearch records and not DB records
|
||||
- Security::VulnerabilityElasticIdentifierNamesFinder # Reason: The finder deals with Elasticsearch records and not DB records
|
||||
- Security::VulnerabilityElasticCountBySeverityFinder # Reason: The finder deals with Elasticsearch records and not DB records
|
||||
- Security::VulnerabilityElasticCountOverTimeFinder # Reason: The finder deals with Elasticsearch records and not DB records
|
||||
|
||||
# Temporary excludes (aka TODOs)
|
||||
# For example:
|
||||
|
|
|
|||
|
|
@ -0,0 +1,255 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'fast_spec_helper'
|
||||
require 'gitlab/dangerfiles/spec_helper'
|
||||
require_relative '../../../tooling/danger/finalize_batched_background_migration_validator_helper'
|
||||
require_relative '../../../tooling/danger/project_helper'
|
||||
|
||||
RSpec.describe Tooling::Danger::FinalizeBatchedBackgroundMigrationValidatorHelper, feature_category: :database do
|
||||
include_context "with dangerfile"
|
||||
let(:fake_project_helper) { instance_double(Tooling::Danger::ProjectHelper) }
|
||||
let(:fake_danger) { DangerSpecHelper.fake_danger.include(described_class) }
|
||||
let(:valid_file_path) { 'db/post_migrate/20230101000000_finalize_migration.rb' }
|
||||
let(:invalid_file_path) { 'app/models/user.rb' }
|
||||
let(:helper) { fake_danger.new(helper: fake_project_helper) }
|
||||
|
||||
describe '#validate_migrations' do
|
||||
context 'when no post migrate files are present' do
|
||||
it 'returns early' do
|
||||
expect(helper).not_to receive(:display_errors)
|
||||
|
||||
helper.validate_migrations([invalid_file_path])
|
||||
end
|
||||
end
|
||||
|
||||
context 'when post migrate files are present' do
|
||||
before do
|
||||
allow(File).to receive(:read).and_call_original
|
||||
allow(File).to receive(:read).with(valid_file_path).and_return(file_content)
|
||||
end
|
||||
|
||||
context 'with no ensure_batched_background_migration_is_finished call' do
|
||||
let(:file_content) do
|
||||
<<~RUBY
|
||||
class QueueMigration < Gitlab::Database::Migration[2.2]
|
||||
def up
|
||||
queue_batched_background_migration(
|
||||
)
|
||||
end
|
||||
end
|
||||
RUBY
|
||||
end
|
||||
|
||||
it 'does not report any errors' do
|
||||
expect(helper).to receive(:display_errors).with([])
|
||||
|
||||
helper.validate_migrations([valid_file_path])
|
||||
end
|
||||
end
|
||||
|
||||
context 'with valid content' do
|
||||
let(:file_content) do
|
||||
<<~RUBY
|
||||
class FinalizeMigration < Gitlab::Database::Migration[2.2]
|
||||
def up
|
||||
ensure_batched_background_migration_is_finished(
|
||||
job_class_name: 'MigrationClass',
|
||||
table_name: :users,
|
||||
column_name: :id,
|
||||
job_arguments: []
|
||||
)
|
||||
end
|
||||
end
|
||||
RUBY
|
||||
end
|
||||
|
||||
it 'does not report any errors' do
|
||||
expect(helper).to receive(:display_errors).with([])
|
||||
|
||||
helper.validate_migrations([valid_file_path])
|
||||
end
|
||||
end
|
||||
|
||||
context 'with a constant instead of string for job_class_name' do
|
||||
let(:file_content) do
|
||||
<<~RUBY
|
||||
class FinalizeMigration < Gitlab::Database::Migration[2.2]
|
||||
def up
|
||||
ensure_batched_background_migration_is_finished(
|
||||
job_class_name: MyMigrationClass,
|
||||
table_name: :users,
|
||||
column_name: :id,
|
||||
job_arguments: []
|
||||
)
|
||||
end
|
||||
end
|
||||
RUBY
|
||||
end
|
||||
|
||||
it 'reports an error about job_class_name' do
|
||||
expect(helper).to receive(:display_errors) do |errors|
|
||||
expect(errors.length).to eq(1)
|
||||
expect(errors.first[:message]).to include('The value of job_class_name should be a string in PascalCase')
|
||||
end
|
||||
|
||||
helper.validate_migrations([valid_file_path])
|
||||
end
|
||||
end
|
||||
|
||||
context 'with multiple ensure_batched_background_migration_is_finished calls' do
|
||||
let(:file_content) do
|
||||
<<~RUBY
|
||||
class FinalizeMigration < Gitlab::Database::Migration[2.2]
|
||||
|
||||
def up
|
||||
first_finalize_migration
|
||||
second_finalize_migration
|
||||
end
|
||||
|
||||
def first_finalize_migration
|
||||
ensure_batched_background_migration_is_finished(
|
||||
job_class_name: 'MigrationClass1',
|
||||
table_name: :users,
|
||||
column_name: :id,
|
||||
job_arguments: []
|
||||
)
|
||||
end
|
||||
def second_finalize_migration
|
||||
ensure_batched_background_migration_is_finished(
|
||||
job_class_name: 'MigrationClass2',
|
||||
table_name: :projects,
|
||||
column_name: :id,
|
||||
job_arguments: []
|
||||
)
|
||||
end
|
||||
end
|
||||
RUBY
|
||||
end
|
||||
|
||||
it 'reports an error about multiple migrations' do
|
||||
expect(helper).to receive(:display_errors) do |errors|
|
||||
expect(errors.length).to eq(1)
|
||||
expect(errors.first[:message]).to include('There should only be one finalize batched background migration')
|
||||
end
|
||||
|
||||
helper.validate_migrations([valid_file_path])
|
||||
end
|
||||
end
|
||||
|
||||
context 'with multiple errors' do
|
||||
let(:file_content) do
|
||||
<<~RUBY
|
||||
class FinalizeMigration < Gitlab::Database::Migration[2.2]
|
||||
|
||||
def up
|
||||
first_finalize_migration
|
||||
second_finalize_migration
|
||||
end
|
||||
|
||||
def first_finalize_migration
|
||||
ensure_batched_background_migration_is_finished(
|
||||
job_class_name: MigrationClass1,
|
||||
table_name: :users,
|
||||
column_name: :id,
|
||||
job_arguments: []
|
||||
)
|
||||
end
|
||||
def second_finalize_migration
|
||||
ensure_batched_background_migration_is_finished(
|
||||
job_class_name: MigrationClass2,
|
||||
table_name: :projects,
|
||||
column_name: :id,
|
||||
job_arguments: []
|
||||
)
|
||||
end
|
||||
end
|
||||
RUBY
|
||||
end
|
||||
|
||||
it 'reports both errors' do
|
||||
expect(helper).to receive(:display_errors) do |errors|
|
||||
expect(errors.length).to eq(2)
|
||||
expect(errors.pluck(:message)).to include(
|
||||
a_string_matching('There should only be one finalize batched background migration'),
|
||||
a_string_matching('The value of job_class_name should be a string in PascalCase')
|
||||
)
|
||||
end
|
||||
|
||||
helper.validate_migrations([valid_file_path])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#validate_job_class_name_format' do
|
||||
it 'returns nil when job_class_name is a string' do
|
||||
file_content = <<~RUBY
|
||||
ensure_batched_background_migration_is_finished(
|
||||
job_class_name: "MigrationClass",
|
||||
table_name: :users
|
||||
)
|
||||
RUBY
|
||||
|
||||
expect(helper.validate_job_class_name_format(valid_file_path, file_content)).to be_nil
|
||||
end
|
||||
|
||||
it 'returns an error when job_class_name value is a constant' do
|
||||
file_content = <<~RUBY
|
||||
ensure_batched_background_migration_is_finished(
|
||||
job_class_name: MigrationClass,
|
||||
table_name: :users
|
||||
)
|
||||
RUBY
|
||||
|
||||
result = helper.validate_job_class_name_format(valid_file_path, file_content)
|
||||
expect(result).to be_a(Hash)
|
||||
expect(result[:message]).to include('The value of job_class_name should be a string in PascalCase')
|
||||
end
|
||||
end
|
||||
|
||||
describe '#validate_migration_count' do
|
||||
it 'returns nil when exactly one migration is present' do
|
||||
file_content = <<~RUBY
|
||||
ensure_batched_background_migration_is_finished(
|
||||
job_class_name: 'MigrationClass',
|
||||
table_name: :users
|
||||
)
|
||||
RUBY
|
||||
|
||||
expect(helper.validate_migration_count(valid_file_path, file_content)).to be_nil
|
||||
end
|
||||
|
||||
it 'returns an error when multiple migrations are present' do
|
||||
file_content = <<~RUBY
|
||||
ensure_batched_background_migration_is_finished(job_class_name: 'First')
|
||||
ensure_batched_background_migration_is_finished(job_class_name: 'Second')
|
||||
RUBY
|
||||
|
||||
result = helper.validate_migration_count(valid_file_path, file_content)
|
||||
expect(result).to be_a(Hash)
|
||||
expect(result[:message]).to include('There should only be one finalize batched background migration')
|
||||
end
|
||||
end
|
||||
|
||||
describe '#display_errors' do
|
||||
it 'returns nil when no errors are present' do
|
||||
expect(helper.display_errors([])).to be_nil
|
||||
end
|
||||
|
||||
it 'calls fail with formatted error messages when errors are present' do
|
||||
errors = [
|
||||
{ file: 'file1.rb', message: 'Error 1' },
|
||||
{ file: 'file2.rb', message: 'Error 2' }
|
||||
]
|
||||
|
||||
expected_messages = [
|
||||
'**file1.rb**: Error 1',
|
||||
'**file2.rb**: Error 2'
|
||||
]
|
||||
|
||||
expect(helper).to receive(:fail).with(expected_messages)
|
||||
|
||||
helper.display_errors(errors)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Tooling
|
||||
module Danger
|
||||
module FinalizeBatchedBackgroundMigrationValidatorHelper
|
||||
POST_MIGRATE_PATH = [
|
||||
'db/post_migrate',
|
||||
'ee/db/geo/post_migrate',
|
||||
'ee/db/embedding/post_migrate'
|
||||
].freeze
|
||||
|
||||
def validate_migrations(changed_files)
|
||||
post_migrate_migrations = changed_files.select { |f| f.match?(%r{^(?:#{POST_MIGRATE_PATH.join('|')})/.+$}) }
|
||||
|
||||
return if post_migrate_migrations.empty?
|
||||
|
||||
errors = []
|
||||
|
||||
post_migrate_migrations.each do |file_path|
|
||||
file_content = File.read(file_path)
|
||||
|
||||
# Check only finalize batched background migration files
|
||||
next unless file_content.include?('ensure_batched_background_migration_is_finished')
|
||||
|
||||
errors << validate_job_class_name_format(file_path, file_content)
|
||||
|
||||
errors << validate_migration_count(file_path, file_content)
|
||||
end
|
||||
display_errors(errors.compact)
|
||||
end
|
||||
|
||||
def validate_job_class_name_format(file_path, file_content)
|
||||
return unless file_content.include?('job_class_name')
|
||||
|
||||
return if file_content.match?(/job_class_name:[ \t]*["'][A-Za-z0-9]+["']/)
|
||||
|
||||
{ file: file_path,
|
||||
message: "The value of job_class_name should be a string in PascalCase e.g 'FinalizeMigrationClass' " }
|
||||
end
|
||||
|
||||
def validate_migration_count(file_path, file_content)
|
||||
occurrences = file_content.scan(/ensure_batched_background_migration_is_finished/).count
|
||||
return unless occurrences != 1
|
||||
|
||||
{ file: file_path, message: "There should only be one finalize batched background migration per class" }
|
||||
end
|
||||
|
||||
def display_errors(errors)
|
||||
return if errors.empty?
|
||||
|
||||
failure_messages = errors.map do |error|
|
||||
"**#{error[:file]}**: #{error[:message]}"
|
||||
end
|
||||
|
||||
return unless failure_messages.any?
|
||||
|
||||
fail(failure_messages)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
41
yarn.lock
41
yarn.lock
|
|
@ -1404,13 +1404,13 @@
|
|||
vue-resizable "1.3.4"
|
||||
vue-runtime-helpers "^1.1.2"
|
||||
|
||||
"@gitlab/eslint-plugin@20.7.1":
|
||||
version "20.7.1"
|
||||
resolved "https://registry.yarnpkg.com/@gitlab/eslint-plugin/-/eslint-plugin-20.7.1.tgz#89dfb95bf6ffec50bd7d9bace42622634a529cf0"
|
||||
integrity sha512-BSB5SC86ITaauEab0HMWLTiaA1QgFlwz5keoO8YQ0JSjPaKusVOGDYDoxQHtZVuRBVfYQi0Cwf5dSWiHJWAeEw==
|
||||
"@gitlab/eslint-plugin@21.0.0":
|
||||
version "21.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@gitlab/eslint-plugin/-/eslint-plugin-21.0.0.tgz#293913ba721534f7074dd88f515cfa6e7f8b6682"
|
||||
integrity sha512-ag2q6NltsDBaTwIjpZgUvIYtp/WcHaJoq/wVGFJPywCT56N3qFgvF3tHNoHOOxfq/IliNipUTGpEXXD7Kwwslw==
|
||||
dependencies:
|
||||
"@typescript-eslint/eslint-plugin" "^7.14.1"
|
||||
eslint-config-airbnb-base "^15.0.0"
|
||||
confusing-browser-globals "^1.0.11"
|
||||
eslint-config-prettier "^9.1.0"
|
||||
eslint-plugin-import "^2.29.1"
|
||||
eslint-plugin-jest "^28.6.0"
|
||||
|
|
@ -5621,10 +5621,10 @@ config-chain@^1.1.13:
|
|||
ini "^1.3.4"
|
||||
proto-list "~1.2.1"
|
||||
|
||||
confusing-browser-globals@^1.0.10:
|
||||
version "1.0.10"
|
||||
resolved "https://registry.yarnpkg.com/confusing-browser-globals/-/confusing-browser-globals-1.0.10.tgz#30d1e7f3d1b882b25ec4933d1d1adac353d20a59"
|
||||
integrity sha512-gNld/3lySHwuhaVluJUKLePYirM3QNCKzVxqAdhJII9/WXKVX5PURzMVJspS1jTslSqjeuG4KMVTSouit5YPHA==
|
||||
confusing-browser-globals@^1.0.11:
|
||||
version "1.0.11"
|
||||
resolved "https://registry.yarnpkg.com/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz#ae40e9b57cdd3915408a2805ebd3a5585608dc81"
|
||||
integrity sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==
|
||||
|
||||
connect-history-api-fallback@^2.0.0:
|
||||
version "2.0.0"
|
||||
|
|
@ -7122,7 +7122,7 @@ error-ex@^1.3.1:
|
|||
dependencies:
|
||||
is-arrayish "^0.2.1"
|
||||
|
||||
es-abstract@^1.19.1, es-abstract@^1.22.1, es-abstract@^1.22.3, es-abstract@^1.23.0, es-abstract@^1.23.2:
|
||||
es-abstract@^1.22.1, es-abstract@^1.22.3, es-abstract@^1.23.0, es-abstract@^1.23.2:
|
||||
version "1.23.3"
|
||||
resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.23.3.tgz#8f0c5a35cd215312573c5a27c87dfd6c881a0aa0"
|
||||
integrity sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==
|
||||
|
|
@ -7418,16 +7418,6 @@ escodegen@^2.0.0:
|
|||
optionalDependencies:
|
||||
source-map "~0.6.1"
|
||||
|
||||
eslint-config-airbnb-base@^15.0.0:
|
||||
version "15.0.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint-config-airbnb-base/-/eslint-config-airbnb-base-15.0.0.tgz#6b09add90ac79c2f8d723a2580e07f3925afd236"
|
||||
integrity sha512-xaX3z4ZZIcFLvh2oUNvcX5oEofXda7giYmuplVxoOg5A7EXJMrUyqRgR+mhDhPK8LZ4PttFOBvCYDbX3sUoUig==
|
||||
dependencies:
|
||||
confusing-browser-globals "^1.0.10"
|
||||
object.assign "^4.1.2"
|
||||
object.entries "^1.1.5"
|
||||
semver "^6.3.0"
|
||||
|
||||
eslint-config-prettier@^9.1.0:
|
||||
version "9.1.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz#31af3d94578645966c082fcb71a5846d3c94867f"
|
||||
|
|
@ -11625,7 +11615,7 @@ object-visit@^1.0.0:
|
|||
dependencies:
|
||||
isobject "^3.0.0"
|
||||
|
||||
object.assign@^4.1.2, object.assign@^4.1.5:
|
||||
object.assign@^4.1.5:
|
||||
version "4.1.5"
|
||||
resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.5.tgz#3a833f9ab7fdb80fc9e8d2300c803d216d8fdbb0"
|
||||
integrity sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==
|
||||
|
|
@ -11635,15 +11625,6 @@ object.assign@^4.1.2, object.assign@^4.1.5:
|
|||
has-symbols "^1.0.3"
|
||||
object-keys "^1.1.1"
|
||||
|
||||
object.entries@^1.1.5:
|
||||
version "1.1.5"
|
||||
resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.5.tgz#e1acdd17c4de2cd96d5a08487cfb9db84d881861"
|
||||
integrity sha512-TyxmjUoZggd4OrrU1W66FMDG6CuqJxsFvymeyXI51+vQLN67zYfZseptRge703kKQdo4uccgAKebXFcRCzk4+g==
|
||||
dependencies:
|
||||
call-bind "^1.0.2"
|
||||
define-properties "^1.1.3"
|
||||
es-abstract "^1.19.1"
|
||||
|
||||
object.fromentries@^2.0.8:
|
||||
version "2.0.8"
|
||||
resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.8.tgz#f7195d8a9b97bd95cbc1999ea939ecd1a2b00c65"
|
||||
|
|
|
|||
Loading…
Reference in New Issue