0f563756b2
This makes sense, because some hooks will fail if the security master and origin master are very different, but we know (from the generated command) that the use of push here is safe.
136 lines
3.6 KiB
Ruby
Executable file
136 lines
3.6 KiB
Ruby
Executable file
#!/usr/bin/env ruby
|
|
|
|
# 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'
|
|
|
|
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_commands
|
|
["git fetch #{@options[:remote]} #{stable_branch}",
|
|
"git checkout -B #{source_branch} #{@options[:remote]}/#{stable_branch} --no-track",
|
|
"git cherry-pick #{@options[:sha]}",
|
|
"git push #{@options[:remote]} #{source_branch} --no-verify",
|
|
"git checkout #{@options[:branch]}"]
|
|
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")
|
|
|
|
puts "\nMerge request URL:".blue
|
|
puts new_mr_url
|
|
|
|
puts "\nMerge request params:".blue
|
|
pp gitlab_params
|
|
else
|
|
cmd = git_commands.join(' && ')
|
|
stdin, stdout, stderr, wait_thr = Open3.popen3(cmd)
|
|
|
|
puts stdout.read&.green
|
|
puts stderr.read&.red
|
|
|
|
if wait_thr.value.success?
|
|
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 }.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('-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
|
|
end
|
|
end
|
|
|
|
Secpick::SecurityFix.new.create!
|