Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2025-03-17 12:07:29 +00:00
parent 64879e0d25
commit c83db9983a
51 changed files with 639 additions and 35 deletions

View File

@ -0,0 +1,24 @@
## Request
<!--
Please briefly describe what you are looking to add or change and select the approrpiate check-box.
-->
- [] This is an update to an existing rule
- [] This is a new rule
## Vendor
<!--
Please add a link to the vendor, documentation and any contact information you may have.
-->
## Examples
<!--
Please provide a list of examples of what this secret type could look like.
-->
/label ~"section::sec" ~"devops::application security testing" ~"group::secret detection" ~"GitLab Ultimate" ~"Category:Secret Detection" ~"Secret Detection:Pattern Change" ~"type::maintenance"
/assign @abellucci @amarpatel

View File

@ -28,7 +28,6 @@ Gitlab/ServiceResponse:
- 'app/services/packages/mark_package_for_destruction_service.rb'
- 'app/services/packages/rubygems/dependency_resolver_service.rb'
- 'app/services/snippets/base_service.rb'
- 'app/services/snippets/destroy_service.rb'
- 'app/services/timelogs/base_service.rb'
- 'app/services/work_items/create_and_link_service.rb'
- 'app/services/work_items/create_from_task_service.rb'

View File

@ -1 +1 @@
7d35879381a67a26332ecdd66392a2ad30bf64ca
fe81274a218800d7e46002719c712915c8e491bf

View File

@ -1,5 +1,5 @@
<script>
import { GlBadge, GlPopover, GlSkeletonLoader } from '@gitlab/ui';
import { GlIcon, GlBadge, GlPopover, GlSkeletonLoader } from '@gitlab/ui';
import { STATUS_CLOSED, STATUS_MERGED } from '~/issues/constants';
import { __ } from '~/locale';
import CiIcon from '~/vue_shared/components/ci_icon/ci_icon.vue';
@ -8,6 +8,7 @@ import query from '../queries/merge_request.query.graphql';
export default {
components: {
GlIcon,
GlBadge,
GlPopover,
GlSkeletonLoader,
@ -108,7 +109,8 @@ export default {
<h5 v-if="!$apollo.queries.mergeRequest.loading" class="my-2">{{ title }}</h5>
<!-- eslint-disable @gitlab/vue-require-i18n-strings -->
<div class="gl-text-subtle">
{{ `${namespacePath}!${iid}` }}
<gl-icon name="merge-request" />
<span class="gl-text-subtle">{{ `${namespacePath}!${iid}` }}</span>
</div>
<!-- eslint-enable @gitlab/vue-require-i18n-strings -->
</div>

View File

@ -15,6 +15,7 @@ export default {
variables() {
return {
...this.variables,
includeMergeabilityChecks: this.showMergeChecksSuccess,
perPage: PER_PAGE,
};
},
@ -43,6 +44,7 @@ export default {
},
},
},
inject: { showMergeChecksSuccess: { default: false } },
props: {
query: {
type: String,

View File

@ -1,5 +1,6 @@
<script>
import { GlBadge, GlTooltipDirective as GlTooltip } from '@gitlab/ui';
import { __ } from '~/locale';
import { TYPENAME_USER } from '~/graphql_shared/constants';
import { convertToGraphQLId } from '~/graphql_shared/utils';
import { BADGE_METHODS } from '../utils/status_badge';
@ -29,6 +30,21 @@ export default {
return this.mergeRequest.reviewers.nodes.find((r) => r.id === this.currentUserId);
},
badgeData() {
if (this.mergeRequest.mergeabilityChecks) {
const failedChecks = this.mergeRequest.mergeabilityChecks.filter(
({ status }) => status === 'FAILED',
);
if (failedChecks.length === 0) {
return {
icon: 'status-success',
variant: 'success',
text: __('Ready to merge'),
iconOpticallyAligned: true,
};
}
}
return BADGE_METHODS[this.listId]?.(this);
},
},

View File

@ -97,6 +97,7 @@ export function initMergeRequestDashboard(el) {
apolloProvider,
provide: {
mergeRequestsSearchDashboardPath: el.dataset.mergeRequestsSearchDashboardPath,
showMergeChecksSuccess: el.dataset.showMergeChecksSuccess === 'true',
},
render(createElement) {
return createElement(App, {

View File

@ -11,6 +11,7 @@ query requestingReview(
$perPage: Int!
$afterCursor: String
$sort: MergeRequestSort = UPDATED_DESC
$includeMergeabilityChecks: Boolean = false
) {
currentUser {
id

View File

@ -10,6 +10,7 @@ query assigneeOrReviewer(
$perPage: Int!
$afterCursor: String
$sort: MergeRequestSort = UPDATED_DESC
$includeMergeabilityChecks: Boolean = false
) {
currentUser {
id

View File

@ -56,5 +56,8 @@ fragment MergeRequestDashboardFragment on MergeRequest {
mergedAt
createdAt
updatedAt
mergeabilityChecks @include(if: $includeMergeabilityChecks) {
status
}
...MergeRequestApprovalFragment
}

View File

@ -10,6 +10,7 @@ query reviewRequests(
$perPage: Int!
$afterCursor: String
$sort: MergeRequestSort = UPDATED_DESC
$includeMergeabilityChecks: Boolean = false
) {
currentUser {
id

View File

@ -573,10 +573,11 @@
$anchor-offset: 20px;
a.anchor {
float: left;
margin-left: calc(-1 * $anchor-offset);
text-decoration: none;
outline: none;
position: absolute;
width: $anchor-offset;
&::after {
@include gl-dark-invert-keep-hue;
@ -587,15 +588,11 @@
&:hover > a.anchor::after {
visibility: visible;
position: absolute;
width: $anchor-offset;
}
> a.anchor:focus::after {
visibility: visible;
outline: auto;
position: absolute;
width: $anchor-offset;
}
}

View File

@ -48,8 +48,8 @@ module Snippets
can?(current_user, :admin_snippet, snippet)
end
def service_response_error(message, http_status)
ServiceResponse.error(message: message, http_status: http_status)
def service_response_error(message, reason)
ServiceResponse.error(message: message, reason: reason)
end
end
end

View File

@ -28,7 +28,7 @@
#js-merge-request-dashboard-config
- if merge_request_dashboard_enabled?(current_user) && !current_page?(merge_requests_search_dashboard_path)
#js-merge-request-dashboard{ data: { base_path: merge_requests_dashboard_path, merge_requests_search_dashboard_path: merge_requests_search_dashboard_path(assignee_username: current_user.username), initial_data: merge_request_dashboard_data.to_json } }
#js-merge-request-dashboard{ data: { base_path: merge_requests_dashboard_path, merge_requests_search_dashboard_path: merge_requests_search_dashboard_path(assignee_username: current_user.username), initial_data: merge_request_dashboard_data.to_json, show_merge_checks_success: Feature.enabled?(:merge_request_dashboard_merge_checks, current_user, type: :gitlab_com_derisk).to_s } }
= gl_loading_icon(size: 'lg')
- if !merge_request_dashboard_enabled?(current_user) || current_page?(merge_requests_search_dashboard_path)

View File

@ -0,0 +1,9 @@
---
name: merge_request_dashboard_merge_checks
feature_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/518710
introduced_by_url:
rollout_issue_url:
milestone: '17.10'
group: group::code review
type: gitlab_com_derisk
default_enabled: false

View File

@ -0,0 +1,8 @@
---
migration_job_name: BackfillMergeRequestContextCommitDiffFilesProjectId
description: Backfills sharding key `merge_request_context_commit_diff_files.project_id` from `merge_request_context_commits`.
feature_category: code_review_workflow
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/183935
milestone: '17.11'
queued_migration_version: 20250308115805
finalized_by: # version of the migration that finalized this BBM

View File

@ -5,4 +5,4 @@ feature_category: subscription_management
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/174626
milestone: '17.7'
queued_migration_version: 20241203172717
finalized_by: 20241203172717
finalized_by: 20250312151614

View File

@ -18,5 +18,4 @@ desired_sharding_key:
table: incident_management_oncall_rotations
sharding_key: project_id
belongs_to: rotation
awaiting_backfill_on_parent: true
table_size: small

View File

@ -9,14 +9,6 @@ description: Persists information about on-call rotation
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/49058
milestone: '13.7'
gitlab_schema: gitlab_main_cell
desired_sharding_key:
project_id:
references: projects
backfill_via:
parent:
foreign_key: oncall_schedule_id
table: incident_management_oncall_schedules
sharding_key: project_id
belongs_to: schedule
desired_sharding_key_migration_job_name: BackfillIncidentManagementOncallRotationsProjectId
sharding_key:
project_id: projects
table_size: small

View File

@ -18,5 +18,4 @@ desired_sharding_key:
table: incident_management_oncall_rotations
sharding_key: project_id
belongs_to: rotation
awaiting_backfill_on_parent: true
table_size: small

View File

@ -18,3 +18,4 @@ desired_sharding_key:
sharding_key: project_id
belongs_to: merge_request_context_commit
table_size: small
desired_sharding_key_migration_job_name: BackfillMergeRequestContextCommitDiffFilesProjectId

View File

@ -0,0 +1,9 @@
# frozen_string_literal: true
class AddProjectIdToMergeRequestContextCommitDiffFiles < Gitlab::Database::Migration[2.2]
milestone '17.11'
def change
add_column :merge_request_context_commit_diff_files, :project_id, :bigint
end
end

View File

@ -0,0 +1,16 @@
# frozen_string_literal: true
class IndexMergeRequestContextCommitDiffFilesOnProjectId < Gitlab::Database::Migration[2.2]
milestone '17.11'
disable_ddl_transaction!
INDEX_NAME = 'index_merge_request_context_commit_diff_files_on_project_id'
def up
add_concurrent_index :merge_request_context_commit_diff_files, :project_id, name: INDEX_NAME
end
def down
remove_concurrent_index_by_name :merge_request_context_commit_diff_files, INDEX_NAME
end
end

View File

@ -0,0 +1,17 @@
# frozen_string_literal: true
class AddMergeRequestContextCommitDiffFilesProjectIdFk < Gitlab::Database::Migration[2.2]
milestone '17.11'
disable_ddl_transaction!
def up
add_concurrent_foreign_key :merge_request_context_commit_diff_files, :projects, column: :project_id,
on_delete: :cascade
end
def down
with_lock_retries do
remove_foreign_key :merge_request_context_commit_diff_files, column: :project_id
end
end
end

View File

@ -0,0 +1,25 @@
# frozen_string_literal: true
class AddMergeRequestContextCommitDiffFilesProjectIdTrigger < Gitlab::Database::Migration[2.2]
milestone '17.11'
def up
install_sharding_key_assignment_trigger(
table: :merge_request_context_commit_diff_files,
sharding_key: :project_id,
parent_table: :merge_request_context_commits,
parent_sharding_key: :project_id,
foreign_key: :merge_request_context_commit_id
)
end
def down
remove_sharding_key_assignment_trigger(
table: :merge_request_context_commit_diff_files,
sharding_key: :project_id,
parent_table: :merge_request_context_commits,
parent_sharding_key: :project_id,
foreign_key: :merge_request_context_commit_id
)
end
end

View File

@ -0,0 +1,56 @@
# frozen_string_literal: true
class QueueBackfillMergeRequestContextCommitDiffFilesProjectId < Gitlab::Database::Migration[2.2]
milestone '17.11'
restrict_gitlab_migration gitlab_schema: :gitlab_main_cell
MIGRATION = "BackfillMergeRequestContextCommitDiffFilesProjectId"
STRATEGY = 'PrimaryKeyBatchingStrategy'
DELAY_INTERVAL = 2.minutes
BATCH_SIZE = 1000
SUB_BATCH_SIZE = 100
def up
model = define_batchable_model('merge_request_context_commit_diff_files')
max_merge_request_context_commit_id = model.maximum(:merge_request_context_commit_id)
max_relative_order = model.maximum(:relative_order)
max_merge_request_context_commit_id ||= 0
max_relative_order ||= 0
Gitlab::Database::BackgroundMigration::BatchedMigration.create!(
gitlab_schema: :gitlab_main_cell,
job_class_name: MIGRATION,
job_arguments: [
:project_id,
:merge_request_context_commits,
:project_id,
:merge_request_context_commit_id
],
table_name: :merge_request_context_commit_diff_files,
column_name: :merge_request_context_commit_id,
min_cursor: [0, 0],
max_cursor: [max_merge_request_context_commit_id, max_relative_order],
interval: DELAY_INTERVAL,
pause_ms: 100,
batch_class_name: STRATEGY,
batch_size: BATCH_SIZE,
sub_batch_size: SUB_BATCH_SIZE,
status_event: :execute
)
end
def down
delete_batched_background_migration(
MIGRATION,
:merge_request_context_commit_diff_files,
:merge_request_context_commit_id,
[
:project_id,
:merge_request_context_commits,
:project_id,
:merge_request_context_commit_id
]
)
end
end

View File

@ -0,0 +1,23 @@
# frozen_string_literal: true
class FinalizeBackfillSubscriptionAddOnPurchasesStartedAt < Gitlab::Database::Migration[2.2]
milestone '17.10'
disable_ddl_transaction!
restrict_gitlab_migration gitlab_schema: :gitlab_main
TABLE_NAME = :subscription_add_on_purchases
COLUMN_NAME = :id
def up
ensure_batched_background_migration_is_finished(
job_class_name: 'BackfillSubscriptionAddOnPurchasesStartedAt',
table_name: TABLE_NAME,
column_name: COLUMN_NAME,
job_arguments: []
)
end
def down
# no-op
end
end

View File

@ -0,0 +1,14 @@
# frozen_string_literal: true
class AddIncidentManagementOncallRotationProjectIdNotNull < Gitlab::Database::Migration[2.2]
milestone '17.11'
disable_ddl_transaction!
def up
add_not_null_constraint :incident_management_oncall_rotations, :project_id
end
def down
remove_not_null_constraint :incident_management_oncall_rotations, :project_id
end
end

View File

@ -0,0 +1 @@
291851377bba797384039b76be45cfdc55c3ed29fff681266bda61c350d245a7

View File

@ -0,0 +1 @@
80bbe5353965d1e04165311820c906037e4778043ff42ffcf08f47adb1815846

View File

@ -0,0 +1 @@
41e55d4ccec654a4947ca9d9044cd03866b86247f8a64b9902121698d292fe2c

View File

@ -0,0 +1 @@
01121faa2ac6fb6c452c65e0aeacb388d989c65699addb21a797c1942605a277

View File

@ -0,0 +1 @@
3938a36e27902e5d629029cf1991224c7563213c3fa9b5c602f93c1722e9716c

View File

@ -0,0 +1 @@
f499be508b6263c95072ac9f1da9f404c84d0ccb5471bee263c17a63a2adb904

View File

@ -0,0 +1 @@
2ca858d5a77dc2560ea3865ae2ec5ae7aade28c07865bd0a92855daf61d9fd3c

View File

@ -2941,6 +2941,22 @@ RETURN NEW;
END
$$;
CREATE FUNCTION trigger_91e1012b9851() RETURNS trigger
LANGUAGE plpgsql
AS $$
BEGIN
IF NEW."project_id" IS NULL THEN
SELECT "project_id"
INTO NEW."project_id"
FROM "merge_request_context_commits"
WHERE "merge_request_context_commits"."id" = NEW."merge_request_context_commit_id";
END IF;
RETURN NEW;
END
$$;
CREATE FUNCTION trigger_9259aae92378() RETURNS trigger
LANGUAGE plpgsql
AS $$
@ -15084,6 +15100,7 @@ CREATE TABLE incident_management_oncall_rotations (
active_period_start time without time zone,
active_period_end time without time zone,
project_id bigint,
CONSTRAINT check_28c39f8a0c CHECK ((project_id IS NOT NULL)),
CONSTRAINT check_5209fb5d02 CHECK ((char_length(name) <= 200))
);
@ -16357,7 +16374,8 @@ CREATE TABLE merge_request_context_commit_diff_files (
"binary" boolean,
merge_request_context_commit_id bigint NOT NULL,
generated boolean,
encoded_file_path boolean DEFAULT false NOT NULL
encoded_file_path boolean DEFAULT false NOT NULL,
project_id bigint
);
CREATE TABLE merge_request_context_commits (
@ -34285,6 +34303,8 @@ CREATE UNIQUE INDEX index_merge_request_cleanup_schedules_on_merge_request_id ON
CREATE INDEX index_merge_request_cleanup_schedules_on_status ON merge_request_cleanup_schedules USING btree (status);
CREATE INDEX index_merge_request_context_commit_diff_files_on_project_id ON merge_request_context_commit_diff_files USING btree (project_id);
CREATE INDEX index_merge_request_context_commits_on_project_id ON merge_request_context_commits USING btree (project_id);
CREATE UNIQUE INDEX index_merge_request_diff_commit_users_on_name_and_email ON merge_request_diff_commit_users USING btree (name, email);
@ -39273,6 +39293,8 @@ CREATE TRIGGER trigger_8fbb044c64ad BEFORE INSERT OR UPDATE ON design_management
CREATE TRIGGER trigger_90fa5c6951f1 BEFORE INSERT OR UPDATE ON dast_profiles_tags FOR EACH ROW EXECUTE FUNCTION trigger_90fa5c6951f1();
CREATE TRIGGER trigger_91e1012b9851 BEFORE INSERT OR UPDATE ON merge_request_context_commit_diff_files FOR EACH ROW EXECUTE FUNCTION trigger_91e1012b9851();
CREATE TRIGGER trigger_9259aae92378 BEFORE INSERT OR UPDATE ON packages_build_infos FOR EACH ROW EXECUTE FUNCTION trigger_9259aae92378();
CREATE TRIGGER trigger_93a5b044f4e8 BEFORE INSERT OR UPDATE ON snippet_user_mentions FOR EACH ROW EXECUTE FUNCTION trigger_93a5b044f4e8();
@ -41277,6 +41299,9 @@ ALTER TABLE ONLY project_topics
ALTER TABLE ONLY web_hooks
ADD CONSTRAINT fk_db1ea5699b FOREIGN KEY (integration_id) REFERENCES integrations(id) ON DELETE CASCADE;
ALTER TABLE ONLY merge_request_context_commit_diff_files
ADD CONSTRAINT fk_db51fb6abe FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
ALTER TABLE ONLY work_item_dates_sources
ADD CONSTRAINT fk_dbbe8917ee FOREIGN KEY (due_date_sourcing_work_item_id) REFERENCES issues(id) ON DELETE SET NULL;

View File

@ -8,7 +8,7 @@ title: GitLab for Slack app administration
{{< details >}}
- Tier: Free, Premium, Ultimate
- Offering: GitLab Self-Managed
- Offering: GitLab Self-Managed, GitLab Dedicated
{{< /details >}}

View File

@ -7,7 +7,10 @@ title: AI gateway
The [AI gateway](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/ai_gateway/) is a standalone service that gives access to AI-powered GitLab Duo features.
GitLab operates an instance of *AI gateway* that is used by all GitLab instances, including self-managed, GitLab Dedicated, and GitLab.com via [Cloud Connector](../../development/cloud_connector/_index.md).
GitLab operates an instance of AI gateway that is used by GitLab Self-Managed, GitLab Dedicated, and GitLab.com through the [Cloud Connector](../../development/cloud_connector/_index.md).
On GitLab Self-Managed, this GitLab instance of AI gateway applies regardless of whether you are using the
cloud-based AI gateway hosted by GitLab, or using [GitLab Duo Self-Hosted](../../administration/gitlab_duo_self_hosted/_index.md) to self-host the AI gateway.
This page describes where the AI gateway is deployed, and answers questions about region selection, data routing, and data sovereignty.

View File

@ -174,7 +174,7 @@ module API
response = service.execute
if response.error?
render_api_error!({ error: response.message }, response.http_status)
render_api_error!({ error: response.message }, response.reason)
end
end
end

View File

@ -225,7 +225,7 @@ module API
response = service.execute
if response.error?
render_api_error!({ error: response.message }, response.http_status)
render_api_error!({ error: response.message }, response.reason)
end
end
end

View File

@ -0,0 +1,27 @@
# frozen_string_literal: true
module Gitlab
module BackgroundMigration
class BackfillMergeRequestContextCommitDiffFilesProjectId < BatchedMigrationJob
operation_name :backfill_merge_request_context_commit_diff_files_project_id
feature_category :code_review_workflow
cursor :merge_request_context_commit_id, :relative_order
def perform
each_sub_batch do |relation|
connection.execute(<<~SQL)
WITH batched_relation AS (
#{relation.where(project_id: nil).select(:merge_request_context_commit_id, :relative_order).to_sql}
)
UPDATE merge_request_context_commit_diff_files
SET project_id = merge_request_context_commits.project_id
FROM batched_relation
INNER JOIN merge_request_context_commits ON batched_relation.merge_request_context_commit_id = merge_request_context_commits.id
WHERE merge_request_context_commit_diff_files.merge_request_context_commit_id = batched_relation.merge_request_context_commit_id
AND merge_request_context_commit_diff_files.relative_order = batched_relation.relative_order;
SQL
end
end
end
end
end

View File

@ -1225,6 +1225,9 @@ msgstr ""
msgid "%{project_path} is a project that you can use to add a README to your GitLab profile. Create a public project and initialize the repository with a README to get started. %{help_link_start}Learn more%{help_link_end}."
msgstr ""
msgid "%{projectsCount}, %{subGroupsCount}"
msgstr ""
msgid "%{project} has %{number} fork"
msgid_plural "%{project} has %{number} forks"
msgstr[0] ""
@ -48140,6 +48143,9 @@ msgstr ""
msgid "Ready to get started with GitLab? Follow these steps to get familiar with us:"
msgstr ""
msgid "Ready to merge"
msgstr ""
msgid "Ready to merge by members who can write to the target branch."
msgstr ""
@ -52664,6 +52670,9 @@ msgstr ""
msgid "SecurityExclusions|ex: spec/**/*.rb"
msgstr ""
msgid "SecurityInventory|Add project to this group to start tracking their security posture."
msgstr ""
msgid "SecurityInventory|CS"
msgstr ""
@ -52676,6 +52685,12 @@ msgstr ""
msgid "SecurityInventory|IaC"
msgstr ""
msgid "SecurityInventory|Manage security configuration"
msgstr ""
msgid "SecurityInventory|No projects found."
msgstr ""
msgid "SecurityInventory|SAST"
msgstr ""
@ -52688,6 +52703,9 @@ msgstr ""
msgid "SecurityInventory|View security coverage and vulnerabilities for all the projects in this group. Data is refreshed and may be upto 24 hours behind."
msgstr ""
msgid "SecurityInventory||An error occurred while fetching subgroups and projects. Please try again."
msgstr ""
msgid "SecurityOrchestration| and "
msgstr ""
@ -61506,6 +61524,9 @@ msgstr ""
msgid "Tool"
msgstr ""
msgid "Tool Coverage"
msgstr ""
msgid "Topic %{source_topic} was successfully merged into topic %{target_topic}."
msgstr ""

View File

@ -215,8 +215,6 @@ spec/frontend/todos/components/filtered_search_tokens/project_token_spec.js
spec/frontend/tooltips/components/tooltips_spec.js
spec/frontend/tooltips/index_spec.js
spec/frontend/vue_alerts_spec.js
spec/frontend/vue_merge_request_widget/components/mr_widget_pipeline_spec.js
spec/frontend/vue_merge_request_widget/components/states/merge_failed_pipeline_confirmation_dialog_spec.js
spec/frontend/vue_merge_request_widget/components/states/mr_widget_ready_to_merge_spec.js
spec/frontend/vue_popovers_spec.js
spec/frontend/vue_shared/components/confirm_modal_spec.js

View File

@ -96,6 +96,7 @@ describe('Merge requests query component', () => {
perPage: 20,
state: 'opened',
sort: 'UPDATED_DESC',
includeMergeabilityChecks: false,
});
});
@ -108,6 +109,7 @@ describe('Merge requests query component', () => {
perPage: 20,
state: 'opened',
sort: 'UPDATED_DESC',
includeMergeabilityChecks: false,
});
});

View File

@ -46,4 +46,29 @@ describe('Merge request status badge component', () => {
expect(findStatusBadge().element).toMatchSnapshot();
});
describe('ready to merge', () => {
it('renders ready to merge badge when there is no failed merge checks', () => {
createComponent({
mergeRequest: {
mergeabilityChecks: [{ status: 'SUCCESS' }],
},
});
expect(findStatusBadge().text()).toBe('Ready to merge');
expect(findStatusBadge().attributes('icon')).toBe('status-success');
});
it('renders normal status badge when there is failed merge checks', () => {
createComponent({
mergeRequest: {
mergeabilityChecks: [{ status: 'FAILED' }],
reviewers: { nodes: [] },
},
listId: 'assigned_to_you',
});
expect(findStatusBadge().text()).toBe('Reviewers needed');
});
});
});

View File

@ -122,7 +122,9 @@ if (global.document) {
MountingPortal: {
template: '<h1>MOUNTING-PORTAL</h1>',
},
Wormhole: {},
Wormhole: {
hasTarget: jest.fn(),
},
}));
VTU.config.global.renderStubDefaultSlot = true;

View File

@ -0,0 +1,197 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Gitlab::BackgroundMigration::BackfillMergeRequestContextCommitDiffFilesProjectId, feature_category: :code_review_workflow do
let(:connection) { ApplicationRecord.connection }
let(:organization) { organizations.create!(name: 'organization', path: 'organization') }
let(:start_cursor) { [0, 0] }
let(:end_cursor) { [merge_request_context_commits.maximum(:id), 1] }
let(:migration) do
described_class.new(
start_cursor: start_cursor,
end_cursor: end_cursor,
batch_table: :merge_request_context_commit_diff_files,
batch_column: :merge_request_context_commit_id,
sub_batch_size: 2,
pause_ms: 0,
connection: connection
)
end
shared_context 'for database tables' do
let(:namespaces) { table(:namespaces) }
let(:organizations) { table(:organizations) }
let(:merge_request_context_commits) { table(:merge_request_context_commits) { |t| t.primary_key = :id } }
let(:merge_requests) { table(:merge_requests) { |t| t.primary_key = :id } }
let(:projects) { table(:projects) }
let(:merge_request_context_commit_diff_files) do
table(:merge_request_context_commit_diff_files) { |t| t.primary_key = :merge_request_context_commit_id }
end
end
shared_context 'for namespaces' do
let(:namespace1) { namespaces.create!(name: 'namespace 1', path: 'namespace1', organization_id: organization.id) }
let(:namespace2) { namespaces.create!(name: 'namespace 2', path: 'namespace2', organization_id: organization.id) }
let(:namespace3) { namespaces.create!(name: 'namespace 3', path: 'namespace3', organization_id: organization.id) }
let(:namespace4) { namespaces.create!(name: 'namespace 4', path: 'namespace4', organization_id: organization.id) }
end
shared_context 'for projects' do
let(:project1) do
projects.create!(
namespace_id: namespace1.id,
project_namespace_id: namespace1.id,
organization_id: organization.id
)
end
let(:project2) do
projects.create!(
namespace_id: namespace2.id,
project_namespace_id: namespace2.id,
organization_id: organization.id
)
end
let(:project3) do
projects.create!(
namespace_id: namespace3.id,
project_namespace_id: namespace3.id,
organization_id: organization.id
)
end
let(:project4) do
projects.create!(
namespace_id: namespace4.id,
project_namespace_id: namespace4.id,
organization_id: organization.id
)
end
let(:commit) { OpenSSL::Digest::SHA256.hexdigest(SecureRandom.hex) }
end
shared_context 'for merge requests' do
let!(:merge_request_1) do
merge_requests.create!(
target_project_id: project1.id,
target_branch: 'master',
source_branch: 'feature',
source_project_id: project1.id
)
end
let!(:merge_request_2) do
merge_requests.create!(
target_project_id: project2.id,
target_branch: 'master',
source_branch: 'feature',
source_project_id: project2.id
)
end
let!(:merge_request_3) do
merge_requests.create!(
target_project_id: project3.id,
target_branch: 'master',
source_branch: 'feature',
source_project_id: project3.id
)
end
let!(:merge_request_4) do
merge_requests.create!(
target_project_id: project4.id,
target_branch: 'master',
source_branch: 'feature',
source_project_id: project4.id
)
end
end
shared_context 'for merge requests context diff and commits' do
let!(:merge_request_context_commit1) do
merge_request_context_commits.create!(
relative_order: 0,
sha: commit,
merge_request_id: merge_request_1.id,
project_id: project1.id
)
end
let!(:merge_request_context_commit2) do
merge_request_context_commits.create!(
relative_order: 0,
sha: commit,
merge_request_id: merge_request_2.id,
project_id: project2.id
)
end
let!(:merge_request_context_commit3) do
merge_request_context_commits.create!(
relative_order: 0,
sha: commit,
merge_request_id: merge_request_3.id,
project_id: project3.id
)
end
let!(:merge_request_context_commit4) do
merge_request_context_commits.create!(
relative_order: 0,
sha: commit,
merge_request_id: merge_request_4.id,
project_id: project4.id
)
end
let!(:merge_request_context_commit_diff_file_1) do
merge_request_context_commit_diff_files.create!(merge_request_context_commit_id: merge_request_context_commit1.id,
relative_order: 0, sha: commit, new_file: true, renamed_file: false, deleted_file: true,
too_large: false, a_mode: 100500, b_mode: 100755, new_path: 'new_path', old_path: 'old_path', project_id: nil)
end
let!(:merge_request_context_commit_diff_file_2) do
merge_request_context_commit_diff_files.create!(merge_request_context_commit_id: merge_request_context_commit2.id,
relative_order: 0, sha: commit, new_file: true, renamed_file: false, deleted_file: true,
too_large: false, a_mode: 100500, b_mode: 100755, new_path: 'new_path', old_path: 'old_path', project_id: nil)
end
let!(:merge_request_context_commit_diff_file_3) do
merge_request_context_commit_diff_files.create!(merge_request_context_commit_id: merge_request_context_commit3.id,
relative_order: 0, sha: commit, new_file: true, renamed_file: false, deleted_file: true,
too_large: false, a_mode: 100500, b_mode: 100755, new_path: 'new_path', old_path: 'old_path', project_id: nil)
end
let!(:merge_request_context_commit_diff_file_4) do
merge_request_context_commit_diff_files.create!(merge_request_context_commit_id: merge_request_context_commit4.id,
relative_order: 0, sha: commit, new_file: true, renamed_file: false, deleted_file: true, too_large: false,
a_mode: 100500, b_mode: 100755, new_path: 'new_path', old_path: 'old_path', project_id: project4.id)
end
end
include_context 'for database tables'
include_context 'for namespaces'
include_context 'for projects'
include_context 'for merge requests'
include_context 'for merge requests context diff and commits'
describe '#perform' do
it 'backfills merge_request_context_commit_diff_files.project_id correctly for relevant records' do
migration.perform
expect(merge_request_context_commit_diff_file_1.reload.project_id).to eq(merge_request_context_commit1.project_id)
expect(merge_request_context_commit_diff_file_2.reload.project_id).to eq(merge_request_context_commit2.project_id)
expect(merge_request_context_commit_diff_file_3.reload.project_id).to eq(merge_request_context_commit3.project_id)
end
it 'does not update merge_request_context_commit_diff_files with pre-existing project_id' do
expect { migration.perform }
.not_to change { merge_request_context_commit_diff_file_4.reload.project_id }
end
end
end

View File

@ -0,0 +1,33 @@
# frozen_string_literal: true
require 'spec_helper'
require_migration!
RSpec.describe QueueBackfillMergeRequestContextCommitDiffFilesProjectId, feature_category: :code_review_workflow do
let!(:batched_migration) { described_class::MIGRATION }
it 'schedules a new batched migration' do
reversible_migration do |migration|
migration.before -> {
expect(batched_migration).not_to have_scheduled_batched_migration
}
migration.after -> {
expect(batched_migration).to have_scheduled_batched_migration(
table_name: :merge_request_context_commit_diff_files,
column_name: :merge_request_context_commit_id,
interval: described_class::DELAY_INTERVAL,
batch_size: described_class::BATCH_SIZE,
sub_batch_size: described_class::SUB_BATCH_SIZE,
gitlab_schema: :gitlab_main_cell,
job_arguments: [
:project_id,
:merge_request_context_commits,
:project_id,
:merge_request_context_commit_id
]
)
}
end
end
end

View File

@ -396,6 +396,24 @@ RSpec.describe API::ProjectSnippets, :aggregate_failures, feature_category: :sou
expect(json_response['message']).to eq('404 Snippet Not Found')
end
context "when destruction fails" do
let(:error_message) { "some service error message" }
let(:error_response) { ServiceResponse.error(message: error_message, reason: :bad_request) }
before do
allow_next_instance_of(::Snippets::DestroyService) do |service|
allow(service).to receive(:execute).and_return(error_response)
end
end
it 'returns an error when DestroyService fails' do
delete api(path, admin, admin_mode: true)
expect(response).to have_gitlab_http_status(:bad_request)
expect(json_response['message']).to eq({ "error" => error_message })
end
end
it_behaves_like '412 response' do
subject(:request) { api(path, admin, admin_mode: true) }
end

View File

@ -546,6 +546,24 @@ RSpec.describe API::Snippets, :aggregate_failures, factory_default: :keep, featu
expect(json_response['message']).to eq('404 Snippet Not Found')
end
context "when destruction fails" do
let(:error_message) { "some service error message" }
let(:error_response) { ServiceResponse.error(message: error_message, reason: :bad_request) }
before do
allow_next_instance_of(::Snippets::DestroyService) do |service|
allow(service).to receive(:execute).and_return(error_response)
end
end
it 'returns an error when DestroyService fails' do
delete api("/snippets/#{public_snippet.id}", personal_access_token: user_token)
expect(response).to have_gitlab_http_status(:bad_request)
expect(json_response['message']).to eq({ "error" => error_message })
end
end
it_behaves_like '412 response' do
let(:request) { api("/snippets/#{public_snippet.id}", personal_access_token: user_token) }
end

View File

@ -15,6 +15,7 @@ RSpec.describe Snippets::DestroyService, feature_category: :source_code_manageme
it 'returns a ServiceResponse error' do
expect(subject).to be_error
expect(subject.reason).to eq(404)
end
end
@ -28,6 +29,17 @@ RSpec.describe Snippets::DestroyService, feature_category: :source_code_manageme
end
end
shared_examples 'an unauthorized destroy' do
it 'does not delete the snippet' do
expect { subject }.not_to change { Snippet.count }
end
it 'returns ServiceResponse error' do
expect(subject).to be_error
expect(subject.reason).to eq(403)
end
end
shared_examples 'an unsuccessful destroy' do
it 'does not delete the snippet' do
expect { subject }.not_to change { Snippet.count }
@ -35,6 +47,7 @@ RSpec.describe Snippets::DestroyService, feature_category: :source_code_manageme
it 'returns ServiceResponse error' do
expect(subject).to be_error
expect(subject.reason).to eq(400)
end
end
@ -116,7 +129,7 @@ RSpec.describe Snippets::DestroyService, feature_category: :source_code_manageme
context 'when user is not able to admin_project_snippet' do
let(:author) { other_user }
it_behaves_like 'an unsuccessful destroy'
it_behaves_like 'an unauthorized destroy'
end
end
@ -139,7 +152,7 @@ RSpec.describe Snippets::DestroyService, feature_category: :source_code_manageme
context 'when user is not able to admin_personal_snippet' do
let(:author) { other_user }
it_behaves_like 'an unsuccessful destroy'
it_behaves_like 'an unauthorized destroy'
end
end