gitlab/bin/secpick
Alex Kalderimis 0f563756b2 Skip hooks when pushing security branches with secpick
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.
2021-08-03 13:07:02 +00:00

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!