1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220
|
#!/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_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. /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!
|