Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2025-04-15 06:12:13 +00:00
parent d88bd7ffe3
commit d762b1add0
24 changed files with 277 additions and 79 deletions

View File

@ -144,6 +144,7 @@ db:check-schema:
function db_check_schema() {
bundle exec rake db:drop db:create db:migrate
scripts/validate_migration_timestamps
scripts/validate_migration_checksum
}
run_with_custom_exit_code db_check_schema

View File

@ -23,7 +23,7 @@ export default {
forceCancelJobButtonLabel: s__('Job|Force cancel'),
forceCancelJobButtonTooltip: s__('Job|Force cancel a job stuck in `canceling` state'),
forceCancelJobConfirmText: s__(
'Job|Are you sure you want to force cancel this job? This will immediately mark the job as canceled, even if the runner is unresponsive.',
'Job|Are you sure you want to force cancel this job? This will immediately mark the job as canceled, even if the job is still running.',
),
},
forwardDeploymentFailureModalId,

View File

@ -69,7 +69,7 @@ class Admin::RunnersController < Admin::ApplicationController
def assign_projects
@projects =
if params[:search].present?
::Project.search(params[:search])
::Project.search(params[:search], include_namespace: true)
else
Project.all
end

View File

@ -13,13 +13,15 @@ module AutoMerge # rubocop:disable Gitlab/BoundedContexts -- Existing module
default: 'this merge request cannot be added to the merge train.'
}.freeze
VALID_STATUSES = %i[available unavailable].freeze
def self.success
new(
status: :available
)
end
def self.error(unavailable_reason:, unsuccessful_check: nil)
def self.error(unavailable_reason: :default, unsuccessful_check: nil)
new(
status: :unavailable,
unavailable_reason: unavailable_reason,
@ -30,6 +32,8 @@ module AutoMerge # rubocop:disable Gitlab/BoundedContexts -- Existing module
attr_reader :status, :unavailable_reason, :unsuccessful_check
def initialize(status:, unavailable_reason: nil, unsuccessful_check: nil)
raise ArgumentError, "Invalid status" unless VALID_STATUSES.include?(status)
self.status = status
self.unavailable_reason = unavailable_reason
self.unsuccessful_check = unsuccessful_check

View File

@ -46,9 +46,9 @@ module AutoMerge
override :availability_details
def availability_details(merge_request)
super do
default_reason = AutoMerge::AvailabilityCheck.error(unavailable_reason: :default)
next default_reason if merge_request.project.merge_trains_enabled?
next default_reason if merge_request.mergeable? && !merge_request.diff_head_pipeline_considered_in_progress?
default_error = AutoMerge::AvailabilityCheck.error
next default_error if merge_request.project.merge_trains_enabled?
next default_error if merge_request.mergeable? && !merge_request.diff_head_pipeline_considered_in_progress?
AutoMerge::AvailabilityCheck.success
end

View File

@ -1,9 +1,10 @@
- project = local_assigns[:project].nil? || local_assigns[:project]
- assigned = false unless local_assigns[:assigned] == true
- is_owner = false unless local_assigns[:is_owner] == true
- is_assigned = false unless local_assigns[:is_assigned] == true
%li
.gl-flex.gl-gap-3
- if assigned
- if is_assigned
.gl-min-w-6.gl-w-6
= sprite_icon('status-success', variant: 'success', css_class: 'gl-mt-3')
- else
@ -13,6 +14,8 @@
.gl-grow.gl-self-center
%h3.gl-m-0.gl-text-base
= project.full_name
- if is_owner
= render Pajamas::BadgeComponent.new(s_('Runners|Owner'), variant: 'info')
- if project.description
%p.gl-mb-0.gl-text-sm.gl-text-subtle
= project.description

View File

@ -7,7 +7,7 @@
#js-admin-runner-edit{ data: {runner_id: @runner.id, runner_path: admin_runner_path(@runner) } }
- if @runner.project_type?
= render ::Layouts::SettingsSectionComponent.new(_('Restrict projects for this runner'), options: { class: 'gl-mt-7 gl-pt-6 gl-border-t' }) do |c|
= render ::Layouts::SettingsSectionComponent.new(s_('Runners|Assign this runner to projects'), options: { class: 'gl-mt-7 gl-pt-6 gl-border-t' }) do |c|
- c.with_body do
.gl-flex.gl-flex-col.gl-gap-5
= render ::Layouts::CrudComponent.new(_('Assigned projects'), icon: 'project', count: @runner.runner_projects.count) do |c|
@ -17,13 +17,15 @@
- @runner.runner_projects.each do |runner_project|
- project = runner_project.project
- if project
= render "project", project: project, assigned: true do
= render Pajamas::ButtonComponent.new(variant: :danger, category: :secondary, size: :small, href: admin_namespace_project_runner_project_path(project.namespace, project, runner_project), method: :delete, button_options: { class: 'gl-self-center' }) do
= _('Disable')
- is_owner = project == @runner.owner
= render "project", project: project, is_owner: is_owner, is_assigned: true do
- if !is_owner
= render Pajamas::ButtonComponent.new(variant: :danger, category: :secondary, size: :small, href: admin_namespace_project_runner_project_path(project.namespace, project, runner_project), method: :delete, button_options: { class: 'gl-self-center' }) do
= s_('Runners|Unassign')
= render ::Layouts::CrudComponent.new(s_('Runners|Select projects to assign to this runner')) do |c|
- c.with_body do
= form_tag edit_admin_runner_path(@runner), id: 'runner-projects-search', class: 'gl-w-full gl-p-5', method: :get do
= form_tag edit_admin_runner_path(@runner), class: 'gl-w-full gl-p-5', method: :get do
.input-group
= search_field_tag :search, params[:search], class: 'form-control gl-form-input', spellcheck: false
.input-group-append
@ -35,7 +37,7 @@
= gitlab_ui_form_for project.runner_projects.new, url: admin_namespace_project_runner_projects_path(project.namespace, project), method: :post, html: { class: 'gl-self-center' } do |f|
= f.hidden_field :runner_id, value: @runner.id
= render Pajamas::ButtonComponent.new(size: :small, type: :submit) do
= _('Enable')
= s_('Runners|Assign')
- c.with_pagination do
= paginate_without_count @projects

View File

@ -13,7 +13,6 @@
scope: project
resolution_role: Maintainer
manual_task: true
window: 1
body: | # (required) Don't change this line.
We introduced the OpenTofu CI/CD template in 16.8 as CI/CD components were not available for GitLab Self-Managed yet.
With the introduction of [GitLab CI/CD components for GitLab Self-Managed](https://docs.gitlab.com/ci/components/#use-a-gitlabcom-component-in-a-self-managed-instance)

View File

@ -2,7 +2,6 @@
removal_milestone: "Pending"
announcement_milestone: "17.9"
breaking_change: true
window: 2
reporter: nagyv-gitlab
stage: deploy
issue_url: https://gitlab.com/gitlab-org/cluster-integration/gitlab-agent/-/issues/656
@ -13,7 +12,7 @@
resolution_role: Maintainer
manual_task: true
body: | # (required) Don't change this line.
In GitLab 18.0, we'll remove support for the `kpt`-based installation of the agent for Kubernetes.
We'll remove support for the `kpt`-based installation of the agent for Kubernetes.
Instead, you should install the agent with one of the supported installation methods:
- Helm (recommended)

View File

@ -8081,7 +8081,7 @@ This change has been removed from its original milestone and is being reassessed
{{< /alert >}}
In GitLab 18.0, we'll remove support for the `kpt`-based installation of the agent for Kubernetes.
We'll remove support for the `kpt`-based installation of the agent for Kubernetes.
Instead, you should install the agent with one of the supported installation methods:
- Helm (recommended)

View File

@ -105,6 +105,11 @@ pre-push:
files: git diff --name-only --diff-filter=d $(git merge-base origin/master HEAD)..HEAD
glob: '{db/migrate/*.rb,db/post_migrate/*.rb}'
run: scripts/validate_migration_timestamps
db-migration-checksum:
tags: database
files: git diff --name-only --diff-filter=d $(git merge-base origin/master HEAD)..HEAD
glob: '{db/migrate/*.rb,db/post_migrate/*.rb}'
run: scripts/validate_migration_checksum
static-verification:
skip: true # This is disabled by default. You can enable this check by adding skip: false in lefthook-local.yml https://github.com/evilmartians/lefthook/blob/master/docs/configuration.md#skip
tags: backend

View File

@ -34670,7 +34670,7 @@ msgstr[1] ""
msgid "Job|Are you sure you want to erase this job log and artifacts?"
msgstr ""
msgid "Job|Are you sure you want to force cancel this job? This will immediately mark the job as canceled, even if the runner is unresponsive."
msgid "Job|Are you sure you want to force cancel this job? This will immediately mark the job as canceled, even if the job is still running."
msgstr ""
msgid "Job|Browse"
@ -51123,9 +51123,6 @@ msgstr ""
msgid "Restrict membership by email domain"
msgstr ""
msgid "Restrict projects for this runner"
msgstr ""
msgid "Restricted access"
msgstr ""
@ -51559,6 +51556,12 @@ msgstr ""
msgid "Runners|Are you sure you want to disable instance runners for %{groupName}?"
msgstr ""
msgid "Runners|Assign"
msgstr ""
msgid "Runners|Assign this runner to projects"
msgstr ""
msgid "Runners|Assigned Group"
msgstr ""
@ -52400,6 +52403,9 @@ msgstr ""
msgid "Runners|UTC Time"
msgstr ""
msgid "Runners|Unassign"
msgstr ""
msgid "Runners|Understand how long it takes for runners to pick up a job. %{linkStart}How is this calculated?%{linkEnd}"
msgstr ""

View File

@ -67,7 +67,9 @@ module Gitlab
"gitlab.webservice.workhorse.image" => "#{IMAGE_REPOSITORY}/gitlab-workhorse-ee",
"gitlab.webservice.workhorse.tag" => workhorse_version,
"gitlab.kas.image.repository" => "#{IMAGE_REPOSITORY}/gitlab-kas",
"gitlab.kas.image.tag" => with_semver_prefix(kas_version)
"gitlab.kas.image.tag" => with_semver_prefix(kas_version),
"gitlab.registry.image.repository" => "#{IMAGE_REPOSITORY}/gitlab-container-registry",
"gitlab.registry.image.tag" => registry_version
}
end

View File

@ -53,6 +53,10 @@ module Gitlab
File.join(ci_project_dir, "GITLAB_KAS_VERSION")
).strip
end
def registry_version
@registry_version ||= ENV["GITLAB_CONTAINER_REGISTRY_TAG"].presence || commit_sha
end
end
end
end

View File

@ -27,7 +27,8 @@ RSpec.describe Gitlab::Orchestrator::Deployment::DefaultValues do
:@webservice_version,
:@workhorse_version,
:@gitlab_shell_version,
:@sidekiq_version
:@sidekiq_version,
:@registry_version
]
end
@ -90,7 +91,9 @@ RSpec.describe Gitlab::Orchestrator::Deployment::DefaultValues do
"gitlab.webservice.workhorse.image" => "#{image_repository}/gitlab-workhorse-ee",
"gitlab.webservice.workhorse.tag" => ci_commit_sha,
"gitlab.kas.image.repository" => "#{image_repository}/gitlab-kas",
"gitlab.kas.image.tag" => kas_version
"gitlab.kas.image.tag" => kas_version,
"gitlab.registry.image.repository" => "#{image_repository}/gitlab-container-registry",
"gitlab.registry.image.tag" => ci_commit_sha
})
end
@ -113,7 +116,8 @@ RSpec.describe Gitlab::Orchestrator::Deployment::DefaultValues do
"GITLAB_SIDEKIQ_TAG" => "1088d209ac5dd8d245b00946de0760eb8fc9a181",
"GITLAB_WEBSERVICE_TAG" => "b0ccc088a766801c8db9e7c564ad28472f33916c",
"GITLAB_WORKHORSE_TAG" => "4a3990fb621ba6f6b7ddf36089868b24e22bb598",
"GITLAB_KAS_TAG" => "03faf0a4227405febb714c4eaa78e4f16f5d0a37"
"GITLAB_KAS_TAG" => "03faf0a4227405febb714c4eaa78e4f16f5d0a37",
"GITLAB_CONTAINER_REGISTRY_TAG" => "595f6534d8286bf1b9d3b1f527cb3af93a9a63c5"
}
end
@ -125,7 +129,8 @@ RSpec.describe Gitlab::Orchestrator::Deployment::DefaultValues do
"gitlab.sidekiq.image.tag" => image_tags["GITLAB_SIDEKIQ_TAG"],
"gitlab.webservice.image.tag" => image_tags["GITLAB_WEBSERVICE_TAG"],
"gitlab.webservice.workhorse.tag" => image_tags["GITLAB_WORKHORSE_TAG"],
"gitlab.kas.image.tag" => image_tags["GITLAB_KAS_TAG"]
"gitlab.kas.image.tag" => image_tags["GITLAB_KAS_TAG"],
"gitlab.registry.image.tag" => image_tags["GITLAB_CONTAINER_REGISTRY_TAG"]
})
end
end

View File

@ -0,0 +1,62 @@
# frozen_string_literal: true
# Checks for presence of migration checksum files when adding new migrations
class MigrationChecksumChecker
MIGRATION_DIRS = %w[db/migrate db/post_migrate].freeze
CHECKSUM_DIR = 'db/schema_migrations'
TIMESTAMP_REGEX = /\A(\d+)_/
CHECKSUM_LENGTH = 64
ERROR_CODE = 1
Result = Struct.new(:error_code, :error_message)
def check
missing_or_invalid_files = find_checksum_issues
return if missing_or_invalid_files.empty?
format_error_result(missing_or_invalid_files)
end
private
def find_checksum_issues
issues = {}
MIGRATION_DIRS.each do |migration_dir|
next unless Dir.exist?(migration_dir)
Dir[File.join(migration_dir, '*.rb')].each do |migration_file|
timestamp = extract_timestamp(migration_file)
next unless timestamp
checksum_file = File.join(CHECKSUM_DIR, timestamp)
if !File.exist?(checksum_file)
issues[migration_file] = "Missing checksum file"
elsif File.zero?(checksum_file)
issues[migration_file] = "Empty checksum file"
else
checksum_content = File.read(checksum_file).chomp
issues[migration_file] = "Invalid checksum length" if checksum_content.length != CHECKSUM_LENGTH
end
end
end
issues
end
def extract_timestamp(filename)
file_basename = File.basename(filename)
match = TIMESTAMP_REGEX.match(file_basename)
match ? match[1] : nil
end
def format_error_result(issues)
message = issues.map do |file, issue_type|
"#{issue_type} for migration: #{file}\n"
end.join('')
Result.new(ERROR_CODE, "\e[31mError: Issues found with migration checksum files\n\n#{message}\e[0m")
end
end

View File

@ -494,7 +494,8 @@ module Trigger
"GITLAB_SIDEKIQ_TAG" => container_versions["gitlab-sidekiq-#{edition}"],
"GITLAB_WEBSERVICE_TAG" => container_versions["gitlab-webservice-#{edition}"],
"GITLAB_WORKHORSE_TAG" => container_versions["gitlab-workhorse-#{edition}"],
"GITLAB_KAS_TAG" => container_versions["gitlab-kas"]
"GITLAB_KAS_TAG" => container_versions["gitlab-kas"],
"GITLAB_CONTAINER_REGISTRY_TAG" => container_versions["gitlab-container-registry"]
}
end

View File

@ -0,0 +1,12 @@
#!/usr/bin/env ruby
# frozen_string_literal: true
require_relative './database/migration_checksum_checker'
result = MigrationChecksumChecker.new.check
if result
puts result.error_message
exit result.error_code
end

View File

@ -79,8 +79,8 @@ RSpec.describe Admin::RunnersController, feature_category: :fleet_visibility do
describe '#edit' do
render_views
let_it_be(:project) { create(:project) }
let_it_be(:project_two) { create(:project) }
let_it_be(:project) { create(:project, name: 'My project 1') }
let_it_be(:project_two) { create(:project, name: 'My project 2') }
it 'shows a runner edit page' do
get :edit, params: { id: runner.id }
@ -88,6 +88,13 @@ RSpec.describe Admin::RunnersController, feature_category: :fleet_visibility do
expect(response).to have_gitlab_http_status(:ok)
end
it 'shows a list of projects in runner edit page' do
get :edit, params: { id: runner.id, search: 'My project' }
expect(response).to have_gitlab_http_status(:ok)
expect(assigns(:projects)).to match_array([project, project_two])
end
it 'shows 404 for unknown runner' do
get :edit, params: { id: 0 }

View File

@ -112,9 +112,8 @@ RSpec.describe "Admin manages runner in admin section", :js, feature_category: :
describe 'search' do
before do
search_form = find('#runner-projects-search')
search_form.fill_in 'search', with: project1.name
search_form.click_button 'Search'
fill_in 'search', with: project1.name
click_button 'Search'
end
it 'contains name of correct project' do
@ -123,12 +122,12 @@ RSpec.describe "Admin manages runner in admin section", :js, feature_category: :
end
end
describe 'enable/create' do
describe 'assign' do
shared_examples 'assignable runner' do
it 'enables a runner for a project' do
within_testid('unassigned-projects') do
within('li', text: project2.full_name) do
click_on 'Enable'
click_on 'Assign'
end
end
@ -160,47 +159,32 @@ RSpec.describe "Admin manages runner in admin section", :js, feature_category: :
end
end
describe 'disable/destroy' do
context 'when runner is being removed from owner project' do
it 'denies removing project runner from project' do
within_testid('assigned-projects') do
click_on 'Disable'
end
describe 'unassign' do
let_it_be(:runner) { create(:ci_runner, :project, projects: [project1, project2]) }
new_runner_project = find_by_testid('unassigned-projects')
expect(page).to have_content('Failed unassigning runner from project')
expect(new_runner_project).to have_content(project2.name)
end
let(:project_to_unassign) { project2 }
let(:runner_project_to_unassign) { runner.runner_projects.find_by_project_id(project_to_unassign.id) }
let(:delete_route_path) do
admin_namespace_project_runner_project_path(
id: runner_project_to_unassign,
project_id: project_to_unassign,
namespace_id: project_to_unassign.parent
)
end
context 'when project being disabled is runner owner project' do
let_it_be(:runner) { create(:ci_runner, :project, projects: [project1, project2]) }
before do
visit edit_admin_runner_path(runner)
end
let(:project_to_delete) { project2 }
let(:runner_project_to_delete) { runner.runner_projects.find_by_project_id(project_to_delete.id) }
let(:delete_route_path) do
admin_namespace_project_runner_project_path(
id: runner_project_to_delete,
project_id: project_to_delete,
namespace_id: project_to_delete.parent
)
it 'removes project runner from project' do
within_testid('assigned-projects') do
find("a[href='#{delete_route_path}']").click
end
before do
visit edit_admin_runner_path(runner)
end
new_runner_project = find_by_testid('unassigned-projects')
it 'removes project runner from project' do
within_testid('assigned-projects') do
find("a[href='#{delete_route_path}']").click
end
new_runner_project = find_by_testid('unassigned-projects')
expect(page).to have_content('Runner unassigned from project.')
expect(new_runner_project).to have_content(project_to_delete.name)
end
expect(page).to have_content('Runner unassigned from project.')
expect(new_runner_project).to have_content(project_to_unassign.name)
end
end
end

View File

@ -29,7 +29,7 @@ const mockRunnerSha = mockRunner.shortSha;
Vue.use(VueApollo);
Vue.use(VueRouter);
describe('AdminRunnerShowApp', () => {
describe('ProjectRunnerShowApp', () => {
let wrapper;
let mockRunnerQuery;

View File

@ -0,0 +1,103 @@
# frozen_string_literal: true
require 'fast_spec_helper'
require_relative '../../../scripts/database/migration_checksum_checker'
RSpec.describe MigrationChecksumChecker, "#check", feature_category: :database do
let(:db_migration_path) { 'spec/fixtures/migrations/db/migrate' }
let(:checksum_dir_path) { 'spec/fixtures/migrations/db/schema_migrations' }
let(:timestamp) { '20250320005730' }
let(:migration_file) { "#{db_migration_path}/#{timestamp}_test_migration.rb" }
let(:checksum_file) { "#{checksum_dir_path}/#{timestamp}" }
let(:valid_checksum) { "85210d36999484fafe57284bc5c68985d68f48061530ff79429c84875a6420f2" }
before do
stub_const('MigrationChecksumChecker::MIGRATION_DIRS', [db_migration_path])
stub_const('MigrationChecksumChecker::CHECKSUM_DIR', checksum_dir_path)
FileUtils.mkdir_p(db_migration_path)
FileUtils.mkdir_p(checksum_dir_path)
File.write(migration_file, "class TestMigration < ActiveRecord::Migration[7.0]; end")
end
subject(:check) { described_class.new.check }
context "when all migrations have matching checksum files" do
before do
File.write(checksum_file, valid_checksum)
end
after do
File.delete(migration_file)
File.delete(checksum_file)
end
it { expect(check).to be_nil }
end
context "when a migration is missing a checksum file" do
after do
File.delete(migration_file)
end
it 'returns an error result' do
expect(check.error_code).to eq(1)
expect(check.error_message).to include('Missing checksum file for migration')
expect(check.error_message).to include(migration_file)
end
end
context "when a migration has an empty checksum file" do
before do
File.write(checksum_file, "")
end
after do
File.delete(migration_file)
File.delete(checksum_file)
end
it 'returns an error result' do
expect(check.error_code).to eq(1)
expect(check.error_message).to include('Empty checksum file for migration')
expect(check.error_message).to include(migration_file)
end
end
context "when a migration has a checksum file with invalid length" do
before do
File.write(checksum_file, "invalid_checksum_that_is_not_64_characters")
end
after do
File.delete(migration_file)
File.delete(checksum_file)
end
it 'returns an error result' do
expect(check.error_code).to eq(1)
expect(check.error_message).to include('Invalid checksum length for migration')
expect(check.error_message).to include(migration_file)
end
end
context 'with multiple migrations' do
let(:timestamp2) { '20250325123456' }
let(:migration_file2) { "#{db_migration_path}/#{timestamp2}_another_migration.rb" }
before do
File.write(migration_file2, "class AnotherMigration < ActiveRecord::Migration[7.0]; end")
File.write(checksum_file, valid_checksum)
end
after do
File.delete(migration_file)
File.delete(migration_file2)
File.delete(checksum_file)
end
it 'reports only the missing checksum' do
expect(check.error_message).to include(migration_file2)
expect(check.error_message).not_to include(migration_file)
end
end
end

View File

@ -176,13 +176,6 @@ RSpec.describe SemgrepResultProcessor, feature_category: :tooling do
describe SemgrepResultProcessor do
describe '#perform_allowlist_check' do
let(:processor) { described_class.new }
let(:original_env) { ENV.to_hash }
after do
# Restore original environment after each test
ENV.clear
original_env.each { |k, v| stub_env(k, v) }
end
context 'when CI_PROJECT_DIR is not allowlisted' do
before do

View File

@ -3,6 +3,10 @@
require 'spec_helper'
RSpec.describe AutoMerge::AvailabilityCheck, feature_category: :shared do
context 'with invalid status' do
it { expect { described_class.new(status: :invalid) }.to raise_error(ArgumentError, 'Invalid status') }
end
describe '.success' do
it 'creates a success response without an unavailable_reason or unsuccessful_check' do
expect(described_class.success).to be_truthy
@ -26,8 +30,10 @@ RSpec.describe AutoMerge::AvailabilityCheck, feature_category: :shared do
end
context 'without an unavailable reason' do
it 'raises an argument error' do
expect { described_class.error }.to raise_error(ArgumentError)
it 'returns the default error' do
response = described_class.error
expect(response.unavailable_reason).to eq(:default)
end
end
end