Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2025-01-24 00:34:42 +00:00
parent 2ef2c32853
commit 47970fc5b7
30 changed files with 395 additions and 169 deletions

View File

@ -1 +1 @@
6dc57705f4d70c87ea803faa572e5bb844cbc569
dd5a7ec67062bdb145fa2f66385a9bea72ccce1f

View File

@ -47,7 +47,10 @@ module MergeRequests
delegate :project, :author_id, :changed_paths, to: :merge_request
def contains_locked_lfs_files?
project.lfs_file_locks.for_paths(changed_paths.map(&:path)).not_for_users(author_id).exists?
return false unless project.lfs_file_locks.exists?
paths = changed_paths.map(&:path).uniq
project.lfs_file_locks.for_paths(paths).not_for_users(author_id).exists?
end
def check_inactive?

View File

@ -53,7 +53,7 @@
.syntax-theme.row
- Gitlab::ColorSchemes.each do |scheme|
%label.col-6.col-sm-4.col-md-3.col-lg-auto.gl-mb-5
.preview= image_tag "#{scheme.css_class}-scheme-preview.png"
.preview= image_tag "#{scheme.css_class}-scheme-preview.png", alt: "#{scheme.css_class}-scheme-preview.png"
= f.gitlab_ui_radio_component :color_scheme_id, scheme.id,
scheme.name,
radio_options: { checked: user_color_schema_id == scheme.id }

View File

@ -39,6 +39,13 @@ ci_group_variables:
- table: namespaces
column: group_id
on_delete: async_delete
ci_instance_runner_monthly_usages:
- table: namespaces
column: root_namespace_id
on_delete: async_delete
- table: projects
column: project_id
on_delete: async_nullify
ci_job_token_authorizations:
- table: projects
column: origin_project_id

View File

@ -0,0 +1,13 @@
---
table_name: ci_gitlab_hosted_runner_monthly_usages
classes:
- Ci::Minutes::GitlabHostedRunnerMonthlyUsage
feature_categories:
- continuous_integration
description: Per month CI usage data at the runner and project level meant to store
GitLab hosted runners data on Dedicated installations only.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/171336
milestone: '17.9'
gitlab_schema: gitlab_ci
exempt_from_sharding: true
notes: Exempted from sharding because Dedicated only.

View File

@ -0,0 +1,16 @@
---
table_name: ci_instance_runner_monthly_usages
classes:
- Ci::Minutes::InstanceRunnerMonthlyUsage
feature_categories:
- continuous_integration
description: Per month CI usage data at the runner and project level meant to store
instance runner usage except hosted runners on dedicated
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/171336
milestone: '17.9'
gitlab_schema: gitlab_ci
sharding_key:
# The table entries belong to a namespace/group only
# We don't shard on projects because the usage is recorded as caused by a project but shouldn't move with that project
# the usage should stay with it's root namespace even if the project moves
root_namespace_id: namespaces

View File

@ -0,0 +1,47 @@
# frozen_string_literal: true
class CreateCiInstanceRunnerMonthlyUsages < Gitlab::Database::Migration[2.2]
milestone '17.9'
disable_ddl_transaction!
def up
# rubocop:disable Migration/EnsureFactoryForTable -- False Positive
create_table :ci_instance_runner_monthly_usages do |t|
# 8 bytes
t.references :runner,
type: :bigint, index: false, null: true,
foreign_key: { to_table: :ci_runners, on_delete: :nullify }
t.bigint :runner_duration_seconds, null: false, default: 0
t.bigint :project_id, null: true
t.bigint :root_namespace_id, null: false
t.timestamps_with_timezone null: false
# 4 bytes
t.date :billing_month, null: false
t.integer :notification_level, null: false, default: 100
# variables bytes
t.decimal :compute_minutes_used, precision: 18, scale: 4, null: false, default: 0.0
end
add_index :ci_instance_runner_monthly_usages, [:root_namespace_id, :billing_month],
name: 'index_ci_instance_runner_monthly_usages_on_namespace_and_month'
add_index :ci_instance_runner_monthly_usages, [:project_id, :billing_month],
name: 'index_ci_instance_runner_monthly_usages_on_project_and_month'
add_index :ci_instance_runner_monthly_usages,
[:runner_id, :billing_month, :root_namespace_id, :project_id],
name: 'idx_instance_runner_usage_unique',
unique: true
add_check_constraint(
:ci_instance_runner_monthly_usages,
"(billing_month = date_trunc('month', billing_month::timestamp with time zone))",
'ci_instance_runner_monthly_usages_year_month_constraint'
)
# rubocop:enable Migration/EnsureFactoryForTable -- False Positive
end
def down
drop_table :ci_instance_runner_monthly_usages if table_exists? :ci_instance_runner_monthly_usages
end
end

View File

@ -0,0 +1,47 @@
# frozen_string_literal: true
# Dedicated only
class CreateCiGitlabHostedRunnerMonthlyUsages < Gitlab::Database::Migration[2.2]
milestone '17.9'
disable_ddl_transaction!
def up
# rubocop:disable Migration/EnsureFactoryForTable -- False Positive
create_table :ci_gitlab_hosted_runner_monthly_usages do |t|
# 8 bytes
t.references :runner,
type: :bigint, index: false, null: false
t.bigint :runner_duration_seconds, null: false, default: 0
t.bigint :project_id, null: false
t.bigint :root_namespace_id, null: false
t.timestamps_with_timezone null: false
# 4 bytes
t.date :billing_month, null: false
t.integer :notification_level, null: false, default: 100
# variables bytes
t.decimal :compute_minutes_used, precision: 18, scale: 4, null: false, default: 0.0
end
add_index :ci_gitlab_hosted_runner_monthly_usages, [:root_namespace_id, :billing_month],
name: 'idx_hosted_runner_usage_on_namespace_billing_month'
add_index :ci_gitlab_hosted_runner_monthly_usages, [:project_id, :billing_month],
name: 'idx_hosted_runner_usage_on_project_billing_month'
add_index :ci_gitlab_hosted_runner_monthly_usages,
[:runner_id, :billing_month, :root_namespace_id, :project_id],
name: 'idx_hosted_runner_usage_unique',
unique: true
add_check_constraint(
:ci_gitlab_hosted_runner_monthly_usages,
"(billing_month = date_trunc('month', billing_month::timestamp with time zone))",
'ci_hosted_runner_monthly_usages_month_constraint'
)
# rubocop:enable Migration/EnsureFactoryForTable -- False Positive
end
def down
drop_table :ci_gitlab_hosted_runner_monthly_usages if table_exists? :ci_gitlab_hosted_runner_monthly_usages
end
end

View File

@ -0,0 +1 @@
23937513149f40d61d67db1e2735693dd667bf23197e967d067416135c29374a

View File

@ -0,0 +1 @@
27ff90b2bc2682e6f5a9a012302e271626a68fa027d2dad4db6f98dcad81f052

View File

@ -9905,6 +9905,29 @@ CREATE SEQUENCE ci_freeze_periods_id_seq
ALTER SEQUENCE ci_freeze_periods_id_seq OWNED BY ci_freeze_periods.id;
CREATE TABLE ci_gitlab_hosted_runner_monthly_usages (
id bigint NOT NULL,
runner_id bigint NOT NULL,
runner_duration_seconds bigint DEFAULT 0 NOT NULL,
project_id bigint NOT NULL,
root_namespace_id bigint NOT NULL,
created_at timestamp with time zone NOT NULL,
updated_at timestamp with time zone NOT NULL,
billing_month date NOT NULL,
notification_level integer DEFAULT 100 NOT NULL,
compute_minutes_used numeric(18,4) DEFAULT 0.0 NOT NULL,
CONSTRAINT ci_hosted_runner_monthly_usages_month_constraint CHECK ((billing_month = date_trunc('month'::text, (billing_month)::timestamp with time zone)))
);
CREATE SEQUENCE ci_gitlab_hosted_runner_monthly_usages_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE ci_gitlab_hosted_runner_monthly_usages_id_seq OWNED BY ci_gitlab_hosted_runner_monthly_usages.id;
CREATE TABLE ci_group_variables (
id bigint NOT NULL,
key character varying NOT NULL,
@ -9935,6 +9958,29 @@ CREATE SEQUENCE ci_group_variables_id_seq
ALTER SEQUENCE ci_group_variables_id_seq OWNED BY ci_group_variables.id;
CREATE TABLE ci_instance_runner_monthly_usages (
id bigint NOT NULL,
runner_id bigint,
runner_duration_seconds bigint DEFAULT 0 NOT NULL,
project_id bigint,
root_namespace_id bigint NOT NULL,
created_at timestamp with time zone NOT NULL,
updated_at timestamp with time zone NOT NULL,
billing_month date NOT NULL,
notification_level integer DEFAULT 100 NOT NULL,
compute_minutes_used numeric(18,4) DEFAULT 0.0 NOT NULL,
CONSTRAINT ci_instance_runner_monthly_usages_year_month_constraint CHECK ((billing_month = date_trunc('month'::text, (billing_month)::timestamp with time zone)))
);
CREATE SEQUENCE ci_instance_runner_monthly_usages_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE ci_instance_runner_monthly_usages_id_seq OWNED BY ci_instance_runner_monthly_usages.id;
CREATE TABLE ci_instance_variables (
id bigint NOT NULL,
variable_type smallint DEFAULT 1 NOT NULL,
@ -24346,8 +24392,12 @@ ALTER TABLE ONLY ci_deleted_objects ALTER COLUMN id SET DEFAULT nextval('ci_dele
ALTER TABLE ONLY ci_freeze_periods ALTER COLUMN id SET DEFAULT nextval('ci_freeze_periods_id_seq'::regclass);
ALTER TABLE ONLY ci_gitlab_hosted_runner_monthly_usages ALTER COLUMN id SET DEFAULT nextval('ci_gitlab_hosted_runner_monthly_usages_id_seq'::regclass);
ALTER TABLE ONLY ci_group_variables ALTER COLUMN id SET DEFAULT nextval('ci_group_variables_id_seq'::regclass);
ALTER TABLE ONLY ci_instance_runner_monthly_usages ALTER COLUMN id SET DEFAULT nextval('ci_instance_runner_monthly_usages_id_seq'::regclass);
ALTER TABLE ONLY ci_instance_variables ALTER COLUMN id SET DEFAULT nextval('ci_instance_variables_id_seq'::regclass);
ALTER TABLE ONLY ci_job_token_authorizations ALTER COLUMN id SET DEFAULT nextval('ci_job_token_authorizations_id_seq'::regclass);
@ -26434,9 +26484,15 @@ ALTER TABLE ONLY ci_deleted_objects
ALTER TABLE ONLY ci_freeze_periods
ADD CONSTRAINT ci_freeze_periods_pkey PRIMARY KEY (id);
ALTER TABLE ONLY ci_gitlab_hosted_runner_monthly_usages
ADD CONSTRAINT ci_gitlab_hosted_runner_monthly_usages_pkey PRIMARY KEY (id);
ALTER TABLE ONLY ci_group_variables
ADD CONSTRAINT ci_group_variables_pkey PRIMARY KEY (id);
ALTER TABLE ONLY ci_instance_runner_monthly_usages
ADD CONSTRAINT ci_instance_runner_monthly_usages_pkey PRIMARY KEY (id);
ALTER TABLE ONLY ci_instance_variables
ADD CONSTRAINT ci_instance_variables_pkey PRIMARY KEY (id);
@ -30033,6 +30089,12 @@ CREATE INDEX idx_group_audit_events_on_project_created_at_id ON ONLY group_audit
CREATE INDEX idx_headers_instance_external_audit_event_destination_id ON instance_audit_events_streaming_headers USING btree (instance_external_audit_event_destination_id);
CREATE INDEX idx_hosted_runner_usage_on_namespace_billing_month ON ci_gitlab_hosted_runner_monthly_usages USING btree (root_namespace_id, billing_month);
CREATE INDEX idx_hosted_runner_usage_on_project_billing_month ON ci_gitlab_hosted_runner_monthly_usages USING btree (project_id, billing_month);
CREATE UNIQUE INDEX idx_hosted_runner_usage_unique ON ci_gitlab_hosted_runner_monthly_usages USING btree (runner_id, billing_month, root_namespace_id, project_id);
CREATE UNIQUE INDEX idx_import_placeholder_memberships_on_source_user_group_id ON import_placeholder_memberships USING btree (source_user_id, group_id);
CREATE INDEX idx_import_placeholder_memberships_on_source_user_id_and_id ON import_placeholder_memberships USING btree (source_user_id, id);
@ -30055,6 +30117,8 @@ CREATE INDEX idx_instance_audit_events_on_author_id_created_at_id ON ONLY instan
CREATE UNIQUE INDEX idx_instance_external_audit_event_destination_id_key_uniq ON instance_audit_events_streaming_headers USING btree (instance_external_audit_event_destination_id, key);
CREATE UNIQUE INDEX idx_instance_runner_usage_unique ON ci_instance_runner_monthly_usages USING btree (runner_id, billing_month, root_namespace_id, project_id);
CREATE INDEX idx_issues_on_health_status_not_null ON issues USING btree (health_status) WHERE (health_status IS NOT NULL);
CREATE INDEX idx_issues_on_project_id_and_created_at_and_id_and_state_id ON issues USING btree (project_id, created_at, id, state_id);
@ -30961,6 +31025,10 @@ CREATE INDEX index_ci_freeze_periods_on_project_id ON ci_freeze_periods USING bt
CREATE UNIQUE INDEX index_ci_group_variables_on_group_id_and_key_and_environment ON ci_group_variables USING btree (group_id, key, environment_scope);
CREATE INDEX index_ci_instance_runner_monthly_usages_on_namespace_and_month ON ci_instance_runner_monthly_usages USING btree (root_namespace_id, billing_month);
CREATE INDEX index_ci_instance_runner_monthly_usages_on_project_and_month ON ci_instance_runner_monthly_usages USING btree (project_id, billing_month);
CREATE UNIQUE INDEX index_ci_instance_variables_on_key ON ci_instance_variables USING btree (key);
CREATE INDEX index_ci_job_artifact_states_on_job_artifact_id_partition_id ON ci_job_artifact_states USING btree (job_artifact_id, partition_id);
@ -39643,6 +39711,9 @@ ALTER TABLE ONLY issue_user_mentions
ALTER TABLE ONLY namespace_settings
ADD CONSTRAINT fk_rails_3896d4fae5 FOREIGN KEY (namespace_id) REFERENCES namespaces(id) ON DELETE CASCADE;
ALTER TABLE ONLY ci_instance_runner_monthly_usages
ADD CONSTRAINT fk_rails_38b9dcccc9 FOREIGN KEY (runner_id) REFERENCES ci_runners(id) ON DELETE SET NULL;
ALTER TABLE ONLY packages_cleanup_policies
ADD CONSTRAINT fk_rails_393ba98591 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;

View File

@ -43,11 +43,13 @@ that are coded across multiple repositories.
|---|---|---|
| [All feature flags in GitLab](../../../user/feature_flags.md) | [Generated during docs build](https://gitlab.com/gitlab-org/gitlab-docs/-/blob/main/doc/raketasks.md#generate-the-feature-flag-tables) | [Technical Writing](https://handbook.gitlab.com/handbook/product/ux/technical-writing/) |
| [GitLab Runner feature flags](https://docs.gitlab.com/runner/configuration/feature-flags.html) | [Page source](https://gitlab.com/gitlab-org/gitlab-runner/-/blob/ec6e1797d2173a95c8ac7f726bd62f6f110b7211/docs/configuration/feature-flags.md?plain=1#L39) | [Runner](https://handbook.gitlab.com/handbook/engineering/development/ops/verify/runner/) |
| [GitLab Runner Kubernetes API settings](https://docs.gitlab.com/runner/executors/kubernetes/) | Generated with [mage](https://gitlab.com/gitlab-org/gitlab-runner/-/blob/main/.gitlab/ci/qa.gitlab-ci.yml#L133) | [Runner](https://handbook.gitlab.com/handbook/engineering/development/ops/verify/runner/) |
| [Deprecations and removals by version](../../../update/deprecations.md) | [Update the deprecations and removals documentation](../../deprecation_guidelines/index.md#update-the-deprecations-and-removals-documentation) | |
| [Breaking change windows](../../../update/breaking_windows.md) | [Update the breaking change windows documentation](../../deprecation_guidelines/index.md#update-the-breaking-change-windows-documentation) | |
| [GraphQL API resources](../../../api/graphql/reference/index.md) | [GraphQL API style guide](../../api_graphql_styleguide.md#documentation-and-schema) | [Import and Integrate](https://handbook.gitlab.com/handbook/engineering/development/dev/foundations/import-and-integrate/) |
| [Audit event types](../../../user/compliance/audit_event_types.md) | [Audit event development guidelines](../../audit_event_guide/index.md) | [Compliance](https://handbook.gitlab.com/handbook/engineering/development/sec/govern/compliance/) |
| [Available custom role permissions](../../../user/custom_roles/abilities.md) | [Generated by Rake task](https://gitlab.com/gitlab-org/gitlab/-/blob/master/tooling/custom_roles/docs/templates/custom_abilities.md.erb) | [Authorization](https://handbook.gitlab.com/handbook/product/categories/#authorization-group)|
| [Application settings analysis](../../cells/application_settings_analysis.md) | [Generated by Ruby script](https://gitlab.com/gitlab-org/gitlab/-/blob/2bb2910c84fad965bde473aa2881d88358b6e96e/scripts/cells/application-settings-analysis.rb#L353) | |
| DAST vulnerability check documentation ([Example](../../../user/application_security/dast/browser/checks/798.19.md)) | [How to generate the Markdown](https://gitlab.com/gitlab-org/security-products/dast-cwe-checks/-/blob/main/doc/how-to-generate-the-markdown-documentation.md) | [Dynamic Analysis](https://handbook.gitlab.com/handbook/product/categories/#dynamic-analysis-group) |
| [The docs homepage](../../../index.md) | | [Technical Writing](https://handbook.gitlab.com/handbook/product/ux/technical-writing/) |

View File

@ -631,3 +631,15 @@ If you notice any inconsistencies in your merge request approval rules, you can
These actions help ensure that your merge request approval policies are correctly applied and consistent across all merge requests.
If you continue to experience issues with merge request approval policies after taking these steps, contact GitLab support for assistance.
### Merge requests that fix a detected vulnerability require approval
If your policy configuration includes the `detected` state, merge requests that
fix previously detected vulnerabilities still require approval. The merge request
approval policy evaluates based on vulnerabilities that existed before the changes
in the merge request, which adds an additional layer of review for any changes that affect
known vulnerabilities.
If you want to allow merge requests that fix vulnerabilities to proceed without
any additional approvals due to a detected vulnerability, consider removing the
`detected` state from your policy configuration.

View File

@ -238,6 +238,23 @@ To do this:
- Job
```
## Configure Trivy report artifact deletion
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/480845) in GitLab 17.9.
By default, the GitLab agent deletes the Trivy report artifact after a scan has completed.
You can configure the GitLab agent to preserve the report artifact, so you can view the report in its raw state.
To do this:
- Set `delete_report_artifact` to `false`:
```yaml
container_scanning:
delete_report_artifact: false
```
## View cluster vulnerabilities
To view vulnerability information in GitLab:

View File

@ -20,7 +20,6 @@ module QA
Runtime::Feature.disable(:ci_release_cli_catalog_publish_option)
Flow::Login.sign_in
Flow::Project.enable_catalog_resource_feature(project)
end
@ -34,41 +33,52 @@ module QA
project.create_repository_tag('1.0.0')
project.visit!
visit_job('create-release-with-existing-tag')
Flow::Pipeline.wait_for_pipeline_creation_via_api(project: project)
project.visit_job('create-release-with-existing-tag')
Page::Project::Job::Show.perform do |show|
expect(show.output).to have_content('release created successfully!')
expect(show.output).to have_content('Tag: 1.0.0')
expect(show.output).to have_content('Name: 1.0.0')
expect(show.output).to have_content('Description: A long description of the release')
Support::Waiter.wait_until { show.has_passed? }
aggregate_failures 'Job has expected contents' do
expect(show.output).to have_content('release created successfully!')
expect(show.output).to have_content('Tag: 1.0.0')
expect(show.output).to have_content('Name: 1.0.0')
expect(show.output).to have_content('Description: A long description of the release')
end
end
visit_catalog_resource_show_page
Page::Explore::CiCdCatalog::Show.perform do |show|
expect(show).to have_version_badge('1.0.0')
expect(show).to have_component_name('new_component')
expect(show).to have_input(
name: 'scanner-output',
required: 'false',
type: 'string',
description: '',
default: 'json'
)
aggregate_failures 'Catalog component has expected contents' do
expect(show).to have_version_badge('1.0.0')
expect(show).to have_component_name('new_component')
expect(show).to have_input(
name: 'scanner-output',
required: 'false',
type: 'string',
description: '',
default: 'json'
)
end
show.click_latest_version_badge
end
Page::Project::Tag::Show.perform do |show|
expect(show).to have_tag_name('1.0.0')
expect(show).to have_no_tag_message
aggregate_failures 'Project tag has expected contents' do
expect(show).to have_tag_name('1.0.0')
expect(show).to have_no_tag_message
end
show.click_release_link
end
Page::Project::Release::Show.perform do |show|
expect(show).to have_release_name('1.0.0')
expect(show).to have_release_description('A long description of the release')
aggregate_failures 'Project release has expected contents' do
expect(show).to have_release_name('1.0.0')
expect(show).to have_release_description('A long description of the release')
end
end
end
@ -78,49 +88,60 @@ module QA
project.create_repository_tag('1.0.0')
project.visit!
visit_job('create-release-with-new-tag-filled-with-information')
Flow::Pipeline.wait_for_pipeline_creation_via_api(project: project)
project.visit_job('create-release-with-new-tag-filled-with-information')
Page::Project::Job::Show.perform do |show|
expect(show.output).to have_content('release created successfully!')
expect(show.output).to have_content('Tag: v9.0.2')
expect(show.output).to have_content('Name: new release v9.0.2')
expect(show.output).to have_content('Description: A long description of the release')
expect(show.output).to have_content('Released At: 2026-01-01 00:00:00 +0000 UTC')
expect(show.output).to have_content('Asset::Link::Name: Download link')
expect(show.output).to have_content('Asset::Link::URL: https://gitlab-runner-downloads.s3.amazonaws.com/v16.9.0-rc2/binaries/gitlab-runner-linux-amd64')
expect(show.output).to have_content('Milestone: v1.0 -')
expect(show.output).to have_content('Milestone: v2.0 -')
Support::Waiter.wait_until { show.has_passed? }
aggregate_failures 'Job has expected contents' do
expect(show.output).to have_content('release created successfully!')
expect(show.output).to have_content('Tag: v9.0.2')
expect(show.output).to have_content('Name: new release v9.0.2')
expect(show.output).to have_content('Description: A long description of the release')
expect(show.output).to have_content('Released At: 2026-01-01 00:00:00 +0000 UTC')
expect(show.output).to have_content('Asset::Link::Name: Download link')
expect(show.output).to have_content('Asset::Link::URL: https://gitlab-runner-downloads.s3.amazonaws.com/v16.9.0-rc2/binaries/gitlab-runner-linux-amd64')
expect(show.output).to have_content('Milestone: v1.0 -')
expect(show.output).to have_content('Milestone: v2.0 -')
end
end
visit_catalog_resource_show_page
Page::Explore::CiCdCatalog::Show.perform do |show|
expect(show).to have_version_badge('9.0.2')
expect(show).to have_component_name('new_component')
expect(show).to have_input(
name: 'scanner-output',
required: 'false',
type: 'string',
description: '',
default: 'json'
)
aggregate_failures 'Catalog component has expected contents' do
expect(show).to have_version_badge('9.0.2')
expect(show).to have_component_name('new_component')
expect(show).to have_input(
name: 'scanner-output',
required: 'false',
type: 'string',
description: '',
default: 'json'
)
end
show.click_latest_version_badge
end
Page::Project::Tag::Show.perform do |show|
expect(show).to have_tag_name('v9.0.2')
expect(show).to have_tag_message('a new tag')
aggregate_failures 'Project tag has expected contents' do
expect(show).to have_tag_name('v9.0.2')
expect(show).to have_tag_message('a new tag')
end
show.click_release_link
end
Page::Project::Release::Show.perform do |show|
expect(show).to have_release_name('new release v9.0.2')
expect(show).to have_release_description('A long description of the release')
expect(show).to have_milestone_title('v1.0')
expect(show).to have_milestone_title('v2.0')
expect(show).to have_asset_link('Download link', '/binaries/gitlab-runner-linux-amd64')
aggregate_failures 'Project release has expected contents' do
expect(show).to have_release_name('new release v9.0.2')
expect(show).to have_release_description('A long description of the release')
expect(show).to have_milestone_title('v1.0')
expect(show).to have_milestone_title('v2.0')
expect(show).to have_asset_link('Download link', '/binaries/gitlab-runner-linux-amd64')
end
end
end
@ -198,17 +219,6 @@ module QA
YAML
end
def visit_job(job_name)
Flow::Pipeline.visit_latest_pipeline
Page::Project::Pipeline::Show.perform do |show|
Support::Waiter.wait_until { show.has_passed? }
expect(show).to have_job(job_name)
show.click_job(job_name)
end
end
def visit_catalog_resource_show_page
Page::Main::Menu.perform do |main|
main.go_to_explore

View File

@ -29,17 +29,16 @@ module QA
end
before do
Flow::Login.sign_in
project.change_pipeline_variables_minimum_override_role('developer')
Flow::Login.sign_in
project.visit!
Page::Project::Menu.perform(&:go_to_pipelines)
Page::Project::Pipeline::Index.perform(&:click_run_pipeline_button)
end
after do
runner&.remove_via_api!
runner.remove_via_api!
end
it 'manually creates a pipeline and uses the defined custom variable value',

View File

@ -8,15 +8,11 @@ module QA
before do
Flow::Login.sign_in
add_files_to_project
project.visit!
Flow::Pipeline.visit_latest_pipeline
project.visit_latest_pipeline
end
after do
project.remove_via_api!
end
it 'runs the pipeline with composed config', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/348002' do
it 'runs the pipeline with composed config',
testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/348002' do
Page::Project::Pipeline::Show.perform do |pipeline|
aggregate_failures 'pipeline has all expected jobs' do
expect(pipeline).to have_job('build')
@ -32,6 +28,7 @@ module QA
create(:commit, project: project, commit_message: 'Add CI and local files', actions: [
build_config_file, test_config_file, non_detectable_file, main_ci_file
])
Flow::Pipeline.wait_for_pipeline_creation_via_api(project: project)
end
def main_ci_file

View File

@ -15,8 +15,7 @@ module QA
Flow::Login.sign_in
add_included_files
add_main_ci_file
project.visit!
Flow::Pipeline.visit_latest_pipeline(status: 'Passed')
project.visit_latest_pipeline
end
after do
@ -47,6 +46,8 @@ module QA
def add_main_ci_file
create(:commit, project: project, commit_message: 'Add config file', actions: [main_ci_file])
Flow::Pipeline.wait_for_pipeline_creation_via_api(project: project)
Flow::Pipeline.wait_for_latest_pipeline_to_have_status(project: project, status: 'success')
end
def add_included_files

View File

@ -15,10 +15,9 @@ module QA
add_included_files_for(main_project)
add_included_files_for(project1)
add_included_files_for(project2)
add_main_ci_file(main_project)
add_ci_file_to_main_project
main_project.visit!
Flow::Pipeline.visit_latest_pipeline(status: 'Passed')
main_project.visit_latest_pipeline
end
after do
@ -43,7 +42,11 @@ module QA
private
def add_included_files_for(project)
files = [
create(:commit, project: project, commit_message: 'Add files', actions: included_files(project))
end
def included_files(project)
[
{
action: 'create',
file_path: 'file1.yml',
@ -63,12 +66,12 @@ module QA
YAML
}
]
create(:commit, project: project, commit_message: 'Add files', actions: files)
end
def add_main_ci_file(project)
create(:commit, project: project, commit_message: 'Add config file', actions: [main_ci_file])
def add_ci_file_to_main_project
create(:commit, project: main_project, commit_message: 'Add config file', actions: [main_ci_file])
Flow::Pipeline.wait_for_pipeline_creation_via_api(project: main_project)
Flow::Pipeline.wait_for_latest_pipeline_to_have_status(project: main_project, status: 'success')
end
def main_ci_file

View File

@ -19,7 +19,7 @@ module QA
testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/358059'
) do
add_ci_files(success_child_ci_file)
Flow::Pipeline.visit_latest_pipeline
project.visit_latest_pipeline
Page::Project::Pipeline::Show.perform do |parent_pipeline|
expect(parent_pipeline).to have_child_pipeline
@ -32,7 +32,7 @@ module QA
testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/358060'
) do
add_ci_files(fail_child_ci_file)
Flow::Pipeline.visit_latest_pipeline
project.visit_latest_pipeline
Page::Project::Pipeline::Show.perform do |parent_pipeline|
expect(parent_pipeline).to have_child_pipeline
@ -97,7 +97,9 @@ module QA
create(:commit,
project: project,
commit_message: 'Add parent and child pipelines CI files.',
actions: [child_ci_file, parent_ci_file]).project.visit!
actions: [child_ci_file, parent_ci_file]
)
Flow::Pipeline.wait_for_pipeline_creation_via_api(project: project)
end
end
end

View File

@ -11,19 +11,21 @@ module QA
let!(:runner) { create(:group_runner, group: group, name: executor, tags: [executor]) }
before do
Flow::Login.sign_in
upstream_project.change_pipeline_variables_minimum_override_role('developer')
downstream_project.change_pipeline_variables_minimum_override_role('developer')
add_ci_file(downstream_project, downstream_ci_file)
add_ci_file(upstream_project, upstream_ci_file)
upstream_project.change_pipeline_variables_minimum_override_role('developer')
downstream_project.change_pipeline_variables_minimum_override_role('developer')
upstream_project.visit!
Flow::Pipeline.visit_latest_pipeline(status: 'Passed')
Flow::Login.sign_in
Flow::Pipeline.wait_for_pipeline_creation_via_api(project: upstream_project)
Flow::Pipeline.wait_for_latest_pipeline_to_have_status(project: upstream_project, status: 'success')
upstream_project.visit_latest_pipeline
end
after do
runner.remove_via_api!
[upstream_project, downstream_project].each(&:remove_via_api!)
end
it 'runs the pipeline with composed config',

View File

@ -15,11 +15,11 @@ module QA
end
before do
Flow::Login.sign_in
update_runner_policy(allowed_policies)
add_ci_file
Flow::Login.sign_in
project.visit!
Flow::Pipeline.visit_latest_pipeline
project.visit_latest_pipeline
end
after do
@ -63,7 +63,7 @@ module QA
with_them do
it 'applies pull policy in job correctly', testcase: params[:testcase] do
visit_job
project.visit_job(job_name)
if pull_image
expect(job_log).to have_content(message),
@ -93,7 +93,7 @@ module QA
issue: "https://gitlab.com/gitlab-org/gitlab/-/issues/462232"
}
) do
visit_job
project.visit_job(job_name)
expect(job_log).to include(text1, text2),
"Expected to find contents #{text1} and #{text2} in #{job_log}, but didn't."
@ -140,14 +140,9 @@ module QA
YAML
}
])
end
def visit_job
Page::Project::Pipeline::Show.perform do |show|
Support::Waiter.wait_until(max_duration: 90) { show.completed? }
show.click_job(job_name)
end
Flow::Pipeline.wait_for_pipeline_creation_via_api(project: project)
Flow::Pipeline.wait_for_latest_pipeline_to_have_status(project: project, status: 'success')
end
def job_log

View File

@ -57,15 +57,14 @@ module QA
end
before do
make_sure_to_have_a_skipped_pipeline
Flow::Login.sign_in
project.visit!
Flow::Pipeline.visit_latest_pipeline(status: 'Skipped')
Flow::Pipeline.wait_for_pipeline_creation_via_api(project: project)
Flow::Pipeline.wait_for_latest_pipeline_to_have_status(project: project, status: 'skipped')
project.visit_latest_pipeline
end
after do
runner&.remove_via_api!
runner.remove_via_api!
end
it(
@ -74,10 +73,7 @@ module QA
) do
Page::Project::Pipeline::Show.perform do |show|
show.click_job_action('Prep') # Trigger pipeline manually
show.wait_until(max_duration: 300, sleep_interval: 2, reload: false) do
project.latest_pipeline[:status] == 'success'
end
Flow::Pipeline.wait_for_latest_pipeline_to_have_status(project: project, status: 'success', wait: 300)
aggregate_failures do
expect(show).to have_build('Test', status: :success)
@ -91,45 +87,6 @@ module QA
end
end
end
private
# Wait for first pipeline to finish and have "skipped" status
# If it takes too long, create new pipeline and retry (2 times)
def make_sure_to_have_a_skipped_pipeline
attempts ||= 1
Runtime::Logger.info('Waiting for pipeline to have status "skipped"...')
Support::Waiter.wait_until(max_duration: 120, sleep_interval: 3, retry_on_exception: true) do
project.latest_pipeline[:status] == 'skipped'
end
rescue Support::Repeater::WaitExceededError
raise 'Failed to create skipped pipeline after 3 attempts.' unless (attempts += 1) < 4
Runtime::Logger.debug(
"Previous pipeline took too long to finish. Potential jobs with problems:\n#{problematic_jobs}"
)
Runtime::Logger.info("Triggering a new pipeline...")
trigger_new_pipeline
retry
end
def trigger_new_pipeline
original_count = project.pipelines.length
create(:pipeline, project: project)
Support::Waiter.wait_until(sleep_interval: 1) { project.pipelines.length > original_count }
end
# We know that all the jobs in pipeline are purposely skipped
# The pipeline should have status "skipped" almost right away after being created
# If pipeline is held up, likely because there are some jobs that
# doesn't have either "skipped" or "manual" status
def problematic_jobs
pipeline = create(:pipeline, project: project, id: project.latest_pipeline[:id])
acceptable_statuses = %w[skipped manual]
pipeline.jobs.select { |job| !(acceptable_statuses.include? job[:status]) }
end
end
end
end

View File

@ -10,8 +10,7 @@ module QA
before do
Flow::Login.sign_in
add_ci_files
project.visit!
Flow::Pipeline.visit_latest_pipeline(status: 'Passed')
project.visit_latest_pipeline
end
after do
@ -29,9 +28,9 @@ module QA
expect(parent_pipeline).not_to have_child_pipeline
parent_pipeline.click_job_action('trigger')
Support::Waiter.wait_until { parent_pipeline.has_child_pipeline? }
parent_pipeline.expand_child_pipeline
Support::Waiter.wait_until(max_duration: 240) { parent_pipeline.has_child_pipeline? }
parent_pipeline.expand_child_pipeline
expect(parent_pipeline).to have_build('child_build', status: nil)
end
end
@ -42,6 +41,8 @@ module QA
create(:commit, project: project, commit_message: 'Add parent and child pipelines CI files.', actions: [
child_ci_file, parent_ci_file
])
Flow::Pipeline.wait_for_pipeline_creation_via_api(project: project)
Flow::Pipeline.wait_for_latest_pipeline_to_have_status(project: project, status: 'success')
end
def parent_ci_file

View File

@ -10,8 +10,7 @@ module QA
before do
Flow::Login.sign_in
add_ci_files
project.visit!
Flow::Pipeline.visit_latest_pipeline(status: 'Passed')
project.visit_latest_pipeline
end
after do
@ -47,6 +46,8 @@ module QA
def add_ci_files
create(:commit, project: project, commit_message: 'todo', actions: [child_ci_file, parent_ci_file])
Flow::Pipeline.wait_for_pipeline_creation_via_api(project: project)
Flow::Pipeline.wait_for_latest_pipeline_to_have_status(project: project, status: 'success')
end
def parent_ci_file

View File

@ -54,13 +54,8 @@ module QA
push.commit_message = 'Commit .gitlab-ci.yml'
end
# observe pipeline creation
project.visit!
Flow::Pipeline.visit_latest_pipeline
Page::Project::Pipeline::Show.perform do |show|
show.click_job('test')
end
Flow::Pipeline.wait_for_pipeline_creation_via_api(project: project)
project.visit_job('test')
Page::Project::Job::Show.perform do |show|
# user views job succeeding

View File

@ -101,6 +101,7 @@ RSpec.describe 'Database schema',
ci_builds_runner_session: %w[project_id],
ci_daily_build_group_report_results: %w[partition_id],
ci_deleted_objects: %w[project_id],
ci_gitlab_hosted_runner_monthly_usages: %w[root_namespace_id project_id runner_id],
ci_job_artifacts: %w[partition_id project_id job_id],
ci_namespace_monthly_usages: %w[namespace_id],
ci_pipeline_artifacts: %w[partition_id],

View File

@ -564,6 +564,8 @@ container_repositories:
- project
- name
project:
- instance_runner_monthly_usages
- hosted_runner_monthly_usages
- catalog_resource
- catalog_resource_sync_events
- catalog_resource_versions

View File

@ -25,7 +25,7 @@ RSpec.describe 'Puma' do
WebMock.allow_net_connect!
end
%w[SIGQUIT SIGTERM SIGKILL].each do |signal|
%w[SIGTERM SIGKILL].each do |signal|
it "has a worker that self-terminates on signal #{signal}" do
response = Excon.get('unix://', socket: @socket_path)
expect(response.status).to eq(200)

View File

@ -24,40 +24,63 @@ RSpec.describe MergeRequests::Mergeability::CheckLfsFileLocksService, feature_ca
context 'when lfs is enabled' do
let(:only_allow_merge_if_pipeline_succeeds) { true }
let(:changed_path) { instance_double('Gitlab::Git::ChangedPath', path: 'README.md') }
let(:changed_paths) do
[
instance_double('Gitlab::Git::ChangedPath', path: 'README.md'),
instance_double('Gitlab::Git::ChangedPath', path: 'conflict.rb'),
instance_double('Gitlab::Git::ChangedPath', path: 'README.md')
]
end
before do
allow(merge_request).to receive(:changed_paths).and_return([changed_path])
allow(merge_request).to receive(:changed_paths).and_return(changed_paths)
allow(project.lfs_file_locks).to receive(:exists?).and_call_original
allow(project.lfs_file_locks).to receive(:for_paths).and_call_original
end
context 'when there are no lfs files locks for this project' do
it 'returns a check result with status success' do
expect(execute.status).to eq Gitlab::MergeRequests::Mergeability::CheckResult::SUCCESS_STATUS
end
it 'returns early before querying for matching file locks' do
execute
expect(project.lfs_file_locks).to have_received(:exists?)
expect(project.lfs_file_locks).not_to have_received(:for_paths)
end
end
context 'when there are lfs files locked by the merge request author' do
let(:user) { create(:user) }
before do
allow(merge_request).to receive(:author_id).and_return(user.id)
create(:lfs_file_lock, project: project, path: changed_path.path, user: user)
create(:lfs_file_lock, project: project, path: changed_paths.first.path, user: merge_request.author)
end
it 'returns a check result with status success' do
expect(execute.status).to eq Gitlab::MergeRequests::Mergeability::CheckResult::SUCCESS_STATUS
end
it 'deduplicates the changed paths' do
execute
expect(project.lfs_file_locks).to have_received(:exists?)
expect(project.lfs_file_locks).to have_received(:for_paths).with(changed_paths.map(&:path).uniq)
end
end
context 'when there are lfs files locked by another user' do
before do
allow(merge_request).to receive(:author_id).and_return(0)
create(:lfs_file_lock, project: project, path: changed_path.path)
create(:lfs_file_lock, project: project, path: changed_paths.second.path)
end
it 'returns a check result with status failure' do
expect(execute.status).to eq Gitlab::MergeRequests::Mergeability::CheckResult::FAILED_STATUS
end
it 'deduplicates the changed paths' do
execute
expect(project.lfs_file_locks).to have_received(:exists?)
expect(project.lfs_file_locks).to have_received(:for_paths).with(changed_paths.map(&:path).uniq)
end
end
end