Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot
2025-06-28 03:10:17 +00:00
parent 4da4d596a8
commit 580d82401a
4 changed files with 398 additions and 232 deletions

View File

@ -2,219 +2,14 @@
# frozen_string_literal: true
require 'active_support/core_ext/object/to_query'
require 'optparse'
require 'open3'
require 'rainbow/refinement'
using Rainbow
module Secpick
BRANCH_PREFIX = 'security'
STABLE_SUFFIX = 'stable'
NEW_SCRIPT = 'scripts/backport_fix_to_stable_branch'
DEFAULT_REMOTE = 'security'
SECURITY_MR_URL = 'https://gitlab.com/gitlab-org/security/gitlab/-/merge_requests/new'
class SecurityFix
def initialize
@options = self.class.options
end
def dry_run?
@options[:try] == true
end
def source_branch
branch = "#{@options[:branch]}-#{@options[:version]}"
branch = "#{BRANCH_PREFIX}-#{branch}" unless branch.start_with?("#{BRANCH_PREFIX}-")
branch
end
def stable_branch
"#{@options[:version]}-#{STABLE_SUFFIX}-ee"
end
def git_pick_commands
[
fetch_stable_branch,
create_backport_branch,
cherry_pick_commit
]
end
def git_push_commands
[
push_to_remote,
checkout_original_branch
]
end
def git_commands
git_pick_commands + git_push_commands
end
def gitlab_params
{
issuable_template: 'Security Release',
merge_request: {
source_branch: source_branch,
target_branch: stable_branch
}
}
end
def new_mr_url
SECURITY_MR_URL
end
def create!
if dry_run?
puts "\nGit commands:".blue
puts git_commands.join("\n")
if !@options[:merge_request]
puts "\nMerge request URL:".blue
puts new_mr_url
end
puts "\nMerge request params:".blue
pp gitlab_params
else
cmd = git_pick_commands.join(' && ')
stdin, stdout, stderr, wait_thr = Open3.popen3(cmd)
puts stdout.read&.green
puts stderr.read&.red
unless wait_thr.value.success?
puts <<~MSG
It looks like cherry pick failed!
Open a new terminal and fix the conflicts.
Once fixed run `git cherry-pick --continue`
After you are done, return here and continue. (Press n to cancel)
Ready to continue? (Y/n)
MSG
unless ['', 'Y', 'y'].include?(gets.chomp)
puts "\nRemaining git commands:".blue
puts 'git cherry-pick --continue'
puts git_push_commands.join("\n")
exit 1
end
end
stdin.close
stdout.close
stderr.close
cmd = git_push_commands.join(' && ')
stdin, stdout, stderr, wait_thr = Open3.popen3(cmd)
puts stdout.read&.green
puts stderr.read&.red
if wait_thr.value.success? && !@options[:merge_request]
puts "#{new_mr_url}?#{gitlab_params.to_query}".blue
end
stdin.close
stdout.close
stderr.close
end
end
def self.options
{ version: nil, branch: nil, sha: nil, merge_request: false }.tap do |options|
parser = OptionParser.new do |opts|
opts.banner = "Usage: #{$0} [options]"
opts.on('-v', '--version 10.0', 'Version') do |version|
options[:version] = version&.tr('.', '-')
end
opts.on('-b', '--branch security-fix-branch', 'Original branch name (optional, defaults to current)') do |branch|
options[:branch] = branch
end
opts.on('-s', '--sha abcd', 'SHA or SHA range to cherry pick (optional, defaults to current)') do |sha|
options[:sha] = sha
end
opts.on('-r', '--remote dev', "Git remote name of security repo (optional, defaults to `#{DEFAULT_REMOTE}`)") do |remote|
options[:remote] = remote
end
opts.on('--mr', '--merge-request', 'Create relevant security Merge Request targeting the stable branch') do
options[:merge_request] = true
end
opts.on('-d', '--dry-run', 'Only show Git commands, without calling them') do
options[:try] = true
end
opts.on('-h', '--help', 'Displays Help') do
puts opts
exit
end
end
parser.parse!
options[:sha] ||= `git rev-parse HEAD`.strip
options[:branch] ||= `git rev-parse --abbrev-ref HEAD`.strip
options[:remote] ||= DEFAULT_REMOTE
nil_options = options.select {|_, v| v.nil? }
unless nil_options.empty?
abort("Missing: #{nil_options.keys.join(', ')}. Use #{$0} --help to see the list of options available".red)
end
abort("Wrong version format #{options[:version].bold}".red) unless options[:version] =~ /\A\d*\-\d*\Z/
end
end
private
def checkout_original_branch
"git checkout #{@options[:branch]}"
end
def push_to_remote
[
"git push #{@options[:remote]} #{source_branch} --no-verify",
*merge_request_push_options
].join(' ')
end
def merge_request_push_options
return [] unless @options[:merge_request]
[
"-o mr.create",
"-o mr.target='#{stable_branch}'",
"-o mr.description='Please apply Security Release template.\\n/milestone %#{milestone}'"
]
end
def cherry_pick_commit
"git cherry-pick #{@options[:sha]}"
end
def create_backport_branch
"git checkout -B #{source_branch} #{@options[:remote]}/#{stable_branch} --no-track"
end
def fetch_stable_branch
"git fetch #{@options[:remote]} #{stable_branch}"
end
def milestone
@options[:version].gsub('-', '.')
end
end
end
Secpick::SecurityFix.new.create!
puts "\n⚠ This tool has moved! ⚠️\n".red
puts "bin/secpick has updated and moved to ".white + NEW_SCRIPT.red.bold
puts "\nThe new script can be used for #{'security fix backports'.bold}".white + " and #{'bug fix backports'.bold}.\n".white
puts "Please run again with:".white
puts "\n#{NEW_SCRIPT} #{ARGV.join(' ')}".blue
exit 1

View File

@ -278,7 +278,7 @@
"crypto": "^1.0.1",
"custom-jquery-matchers": "^2.1.0",
"dependency-cruiser": "^16.9.0",
"eslint": "9.29.0",
"eslint": "9.30.0",
"eslint-formatter-gitlab": "^6.0.1",
"eslint-import-resolver-jest": "3.0.2",
"eslint-import-resolver-webpack": "0.13.10",

View File

@ -0,0 +1,371 @@
#!/usr/bin/env ruby
# frozen_string_literal: true
require 'active_support/core_ext/object/to_query'
require 'optparse'
require 'open3'
require 'rainbow/refinement'
require 'tty-prompt'
using Rainbow
class Backport
SECURITY_REPO_URLS = {
ssh: 'git@gitlab.com:gitlab-org/security/gitlab.git',
http: 'https://gitlab.com/gitlab-org/security/gitlab.git'
}.freeze
DEFAULT_OPTIONS = {
base: {
version: nil, branch: nil, sha: nil, merge_request: false, stable_branch_suffix: 'stable'
},
security: {
branch_prefix: 'security',
new_merge_request_url: 'https://gitlab.com/gitlab-org/security/gitlab/-/merge_requests/new',
merge_request_template: 'Security Fix'
},
bugfix: {
branch_prefix: 'backport',
new_merge_request_url: 'https://gitlab.com/gitlab-org/gitlab/-/merge_requests/new',
merge_request_template: 'Stable Branch'
}
}.freeze
BACKPORT_TYPE_CHOICES = [
{ name: 'Security Fix', value: :security }.freeze,
{ name: 'Bug Fix', value: :bugfix }.freeze
].freeze
def initialize
@prompt = TTY::Prompt.new(help_color: :cyan)
@options = build_options
end
attr_reader :prompt
def create!
correct_repository_check!
return dry_run if dry_run?
pick_commits
push_commits
end
private
def correct_repository_check!
if security_backport? && !pushing_to_security_remote?
abort('⛔️ Can only push security backports to the security repository ⛔️'.red)
elsif !security_backport? && pushing_to_security_remote?
abort('⛔️ Bugfixes should be pushed to the canonical repository ⛔️'.red)
end
end
def pushing_to_security_remote?
@options[:remote] == security_remote_name
end
def build_options
options = DEFAULT_OPTIONS[:base].dup
parse_options(options)
options[:sha] ||= git_head_sha
options[:branch] ||= git_current_branch
options[:remote] ||= select_git_remote
options[:version] ||= select_version
options.merge(DEFAULT_OPTIONS[confirmed_backport_type!])
end
def confirmed_backport_type!
backport_type = prompt.select('⚠️ What type of fix are you backporting? ⚠️'.red, BACKPORT_TYPE_CHOICES)
@security_backport = backport_type == :security
backport_type
end
def security_backport?
@security_backport
end
def add_security_remote
puts "⚠️ You do not have the security remote configured ⚠️".red
return attempt_to_add_security_remote if prompt.yes?('Would you like me to add the remote for you now?')
print_security_remote_config_instructions_and_exit
end
def attempt_to_add_security_remote
security_repo_url = prompt.select('Which remote format do you normally use?', git_remote_style_choices)
stdin, stdout, stderr, wait_thr = Open3.popen3("git remote add security #{security_repo_url}")
if wait_thr.value.success? # rubocop:disable Cop/LineBreakAroundConditionalBlock -- https://gitlab.com/gitlab-org/ruby/gems/gitlab-styles/-/merge_requests/258
@security_remote_name = 'security'
puts '✅ Successfully created `security` remote'.white
else
puts "⛔️ Could not create `security` remote ⛔️".red
puts ('-' * 80).red
puts "\n#{stderr.read}".red
puts ('-' * 80).red
print_security_remote_config_instructions_and_exit
end
ensure
stdin.close
stdout.close
stderr.close
end
def print_security_remote_config_instructions_and_exit
puts "Please add the gitlab security remote to git with:".white
puts "git remote add security #{SECURITY_REPO_URLS[:ssh]}".cyan
puts "or".white
puts "git remote add security #{SECURITY_REPO_URLS[:http]}".cyan
puts "and then try again".white
exit 1
end
def parse_options(options)
parser = OptionParser.new do |opts|
opts.banner = <<~BANNER
Usage: #{$PROGRAM_NAME} [options]
This tool requires confirmation for the backport type and will prompt
for the remote and version unless specified.
BANNER
opts.on('-v', '--version 10.0', 'Version to target (opens prompt if not passed)') do |version|
options[:version] = version&.tr('.', '-')
end
opts.on('-r', '--remote dev', "Git remote name of repository (opens prompt if not passed)") do |remote|
options[:remote] = remote
end
opts.on('-b', '--branch branch-name', 'Original branch name (optional, defaults to current branch)') do |branch|
options[:branch] = branch
end
opts.on('-s', '--sha abcd', 'SHA or SHA range to cherry pick (optional, defaults to HEAD SHA)') do |sha|
options[:sha] = sha
end
opts.on('--mr', '--merge-request', 'Create a Merge Request targeting the stable branch') do
options[:merge_request] = true
end
opts.on('-d', '--dry-run', 'Display the Git commands this script will run without calling them') do
options[:try] = true
end
opts.on('-h', '--help', 'Displays Help') do
puts opts
exit
end
end
parser.parse!
end
def git_head_sha
`git rev-parse HEAD`.strip
end
def git_current_branch
`git rev-parse --abbrev-ref HEAD`.strip
end
def select_git_remote
prompt.select("Which remote do you want to push to?", remote_choices)
end
def git_remotes
@git_remotes ||= `git remote -v`.strip.split("\n").each_with_object({}) do |line, output|
name, url, _type = line.split(/\s+/)
output[name] = url
end
end
def security_remote_name
return @security_remote_name if defined?(@security_remote_name)
@security_remote_name = git_remotes.find { |_k, url| SECURITY_REPO_URLS.value?(url) }&.first
add_security_remote if @security_remote_name.nil?
@security_remote_name
end
# Sorts git remotes so the security remote is first in the list
def remote_choices
[security_remote_name] + (git_remotes.keys - [security_remote_name])
end
def git_remote_style_choices
SECURITY_REPO_URLS.map { |style, url| { name: "#{style}: #{url}", value: url } }
end
def select_version
prompt.select('Which version are you targeting?', version_choices)
end
def current_version
version_filepath = File.join(File.dirname($PROGRAM_NAME), '../VERSION')
full_version = File.read(version_filepath)
major, minor, _rest = full_version.split('.')
[major.to_i, minor.to_i]
end
def version_choices
major, minor = current_version
Array.new(3) do
minor -= 1
if minor < 0
major -= 1
minor = 11
end
{ name: "#{major}.#{minor}", value: "#{major}-#{minor}" }
end
end
def dry_run?
@options[:try] == true
end
def dry_run
puts "\nGit commands:".blue
puts git_commands.join("\n")
puts "\nMerge request URL:".blue
puts new_merge_request_url
end
def pick_commits
cmd = git_pick_commands.join(' && ')
stdin, stdout, stderr, wait_thr = Open3.popen3(cmd)
puts stdout.read.green
puts stderr.read.red
unless wait_thr.value.success? # rubocop:disable Cop/LineBreakAroundConditionalBlock -- https://gitlab.com/gitlab-org/ruby/gems/gitlab-styles/-/merge_requests/258
puts <<~MSG
It looks like cherry pick failed!
Open a new terminal and fix the conflicts.
Once fixed run `git cherry-pick --continue`
After you are done, return here and continue. (Press n to cancel)
Ready to continue? (Y/n)
MSG
unless ['', 'Y', 'y'].include?(gets.chomp)
puts "\nRemaining git commands:".blue
puts 'git cherry-pick --continue'
puts git_push_commands.join("\n")
exit 1
end
end
ensure
stdin.close
stdout.close
stderr.close
end
def push_commits
cmd = git_push_commands.join(' && ')
stdin, stdout, stderr, wait_thr = Open3.popen3(cmd)
puts stdout.read.green
puts stderr.read.red
puts new_merge_request_url.blue if wait_thr.value.success? && !@options[:merge_request]
ensure
stdin.close
stdout.close
stderr.close
end
def git_pick_commands
[
fetch_stable_branch,
create_backport_branch,
cherry_pick_commit
]
end
def git_push_commands
[
push_to_remote,
checkout_original_branch
]
end
def git_commands
git_pick_commands + git_push_commands
end
def fetch_stable_branch
"git fetch #{@options[:remote]} #{stable_branch}"
end
def create_backport_branch
"git checkout -B #{source_branch} #{@options[:remote]}/#{stable_branch} --no-track"
end
def cherry_pick_commit
"git cherry-pick #{@options[:sha]}"
end
def push_to_remote
[
"git push #{@options[:remote]} #{source_branch} --no-verify",
*merge_request_push_options
].join(' ')
end
def checkout_original_branch
"git checkout #{@options[:branch]}"
end
def gitlab_params
{
issuable_template: @options[:merge_request_template],
merge_request: {
source_branch: source_branch,
target_branch: stable_branch
}
}
end
def merge_request_push_options
return [] unless @options[:merge_request]
[
"-o mr.create",
"-o mr.target='#{stable_branch}'",
"-o mr.description='Please apply `#{@options[:merge_request_template]}` template.'",
"-o mr.milestone='#{@options[:version].tr('-', '.')}'"
]
end
def source_branch
branch = "#{@options[:branch]}-#{@options[:version]}"
branch = "#{@options[:branch_prefix]}-#{branch}" unless branch.start_with?("#{@options[:branch_prefix]}-")
branch
end
def stable_branch
"#{@options[:version]}-#{@options[:stable_branch_suffix]}-ee"
end
def new_merge_request_url
"#{@options[:new_merge_request_url]}?#{gitlab_params.to_query}"
end
end
Backport.new.create!

View File

@ -1272,19 +1272,19 @@
resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.12.1.tgz#cfc6cffe39df390a3841cde2abccf92eaa7ae0e0"
integrity sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==
"@eslint/config-array@^0.20.1":
version "0.20.1"
resolved "https://registry.yarnpkg.com/@eslint/config-array/-/config-array-0.20.1.tgz#454f89be82b0e5b1ae872c154c7e2f3dd42c3979"
integrity sha512-OL0RJzC/CBzli0DrrR31qzj6d6i6Mm3HByuhflhl4LOBiWxN+3i6/t/ZQQNii4tjksXi8r2CRW1wMpWA2ULUEw==
"@eslint/config-array@^0.21.0":
version "0.21.0"
resolved "https://registry.yarnpkg.com/@eslint/config-array/-/config-array-0.21.0.tgz#abdbcbd16b124c638081766392a4d6b509f72636"
integrity sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==
dependencies:
"@eslint/object-schema" "^2.1.6"
debug "^4.3.1"
minimatch "^3.1.2"
"@eslint/config-helpers@^0.2.1":
version "0.2.1"
resolved "https://registry.yarnpkg.com/@eslint/config-helpers/-/config-helpers-0.2.1.tgz#26042c028d1beee5ce2235a7929b91c52651646d"
integrity sha512-RI17tsD2frtDu/3dmI7QRrD4bedNKPM08ziRYaC5AhkGrzIAJelm9kJU1TznK+apx6V+cqRz8tfpEeG3oIyjxw==
"@eslint/config-helpers@^0.3.0":
version "0.3.0"
resolved "https://registry.yarnpkg.com/@eslint/config-helpers/-/config-helpers-0.3.0.tgz#3e09a90dfb87e0005c7694791e58e97077271286"
integrity sha512-ViuymvFmcJi04qdZeDc2whTHryouGcDlaxPqarTD0ZE10ISpxGUVZGZDx4w01upyIynL3iu6IXH2bS1NhclQMw==
"@eslint/core@^0.14.0":
version "0.14.0"
@ -1315,10 +1315,10 @@
minimatch "^3.1.2"
strip-json-comments "^3.1.1"
"@eslint/js@9.29.0", "@eslint/js@^9.15.0":
version "9.29.0"
resolved "https://registry.yarnpkg.com/@eslint/js/-/js-9.29.0.tgz#dc6fd117c19825f8430867a662531da36320fe56"
integrity sha512-3PIF4cBw/y+1u2EazflInpV+lYsSG0aByVIQzAgb1m1MhHFSbqTyNqtBKHgWf/9Ykud+DhILS9EGkmekVhbKoQ==
"@eslint/js@9.30.0", "@eslint/js@^9.15.0":
version "9.30.0"
resolved "https://registry.yarnpkg.com/@eslint/js/-/js-9.30.0.tgz#c396fa450d5505dd9b7b8846b33f0491aebd9a2d"
integrity sha512-Wzw3wQwPvc9sHM+NjakWTcPx11mbZyiYHuwWa/QfZ7cIRX7WK54PSk7bdyXDaoaopUcMatv1zaQvOAAO8hCdww==
"@eslint/object-schema@^2.1.6":
version "2.1.6"
@ -7511,18 +7511,18 @@ eslint-visitor-keys@^4.2.1:
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz#4cfea60fe7dd0ad8e816e1ed026c1d5251b512c1"
integrity sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==
eslint@9.29.0:
version "9.29.0"
resolved "https://registry.yarnpkg.com/eslint/-/eslint-9.29.0.tgz#65e3db3b7e5a5b04a8af541741a0f3648d0a81a6"
integrity sha512-GsGizj2Y1rCWDu6XoEekL3RLilp0voSePurjZIkxL3wlm5o5EC9VpgaP7lrCvjnkuLvzFBQWB3vWB3K5KQTveQ==
eslint@9.30.0:
version "9.30.0"
resolved "https://registry.yarnpkg.com/eslint/-/eslint-9.30.0.tgz#fb0c655f5e28fc1b2f4050c28efa1876d78034fc"
integrity sha512-iN/SiPxmQu6EVkf+m1qpBxzUhE12YqFLOSySuOyVLJLEF9nzTf+h/1AJYc1JWzCnktggeNrjvQGLngDzXirU6g==
dependencies:
"@eslint-community/eslint-utils" "^4.2.0"
"@eslint-community/regexpp" "^4.12.1"
"@eslint/config-array" "^0.20.1"
"@eslint/config-helpers" "^0.2.1"
"@eslint/config-array" "^0.21.0"
"@eslint/config-helpers" "^0.3.0"
"@eslint/core" "^0.14.0"
"@eslint/eslintrc" "^3.3.1"
"@eslint/js" "9.29.0"
"@eslint/js" "9.30.0"
"@eslint/plugin-kit" "^0.3.1"
"@humanfs/node" "^0.16.6"
"@humanwhocodes/module-importer" "^1.0.1"