secpick 3.58 KB
Newer Older
1
#!/usr/bin/env ruby
2

James Lopez's avatar
James Lopez committed
3 4 5
# frozen_string_literal: false

require 'active_support/core_ext/object/to_query'
6 7 8 9 10
require 'optparse'
require 'open3'
require 'rainbow/refinement'
using Rainbow

11 12
module Secpick
  BRANCH_PREFIX = 'security'.freeze
13
  STABLE_PREFIX = 'stable'.freeze
14 15
  DEFAULT_REMOTE = 'dev'.freeze
  NEW_MR_URL = 'https://dev.gitlab.org/gitlab/gitlabhq/merge_requests/new'.freeze
16

17 18 19 20
  class SecurityFix
    def initialize
      @options = self.class.options
    end
21

22 23 24
    def ee?
      File.exist?('./CHANGELOG-EE.md')
    end
25

26 27 28
    def dry_run?
      @options[:try] == true
    end
29

30 31 32
    def original_branch
      @options[:branch].strip
    end
33

34 35 36 37 38
    def source_branch
      branch = "#{original_branch}-#{@options[:version]}"
      branch.prepend("#{BRANCH_PREFIX}-") unless branch.start_with?("#{BRANCH_PREFIX}-")
      branch.freeze
    end
39

40 41
    def stable_branch
      "#{STABLE_PREFIX}-#{@options[:version]}".tap do |name|
42 43 44
        name << "-ee" if ee?
      end.freeze
    end
45

46
    def git_commands
47 48 49
      ["git fetch #{@options[:remote]} #{stable_branch}",
       "git checkout #{stable_branch}",
       "git pull #{@options[:remote]} #{stable_branch}",
50 51 52 53 54
       "git checkout -B #{source_branch}",
       "git cherry-pick #{@options[:sha]}",
       "git push #{@options[:remote]} #{source_branch}",
       "git checkout #{original_branch}"]
    end
55

56 57 58 59
    def gitlab_params
      {
        merge_request: {
          source_branch: source_branch,
60 61
          target_branch: stable_branch,
          description: '/label ~security'
62 63 64
        }
      }
    end
65

66 67 68 69 70 71 72
    def new_mr_url
      if ee?
        NEW_MR_URL.sub('gitlabhq', 'gitlab-ee')
      else
        NEW_MR_URL
      end
    end
73

74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94
    def create!
      if dry_run?
        puts git_commands.join("\n").green
        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
95

96 97 98 99 100 101 102
    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
103

104 105 106
          opts.on('-b', '--branch security-fix-branch', 'Original branch name (optional, defaults to current)') do |branch|
            options[:branch] = branch
          end
107

108 109 110
          opts.on('-s', '--sha abcd', 'SHA to cherry pick') do |sha|
            options[:sha] = sha
          end
111

112 113 114
          opts.on('-r', '--remote abcd', 'Git remote name of dev.gitlab.org (optional, defaults to `dev`)') do |remote|
            options[:remote] = remote
          end
115

116 117 118
          opts.on('-d', '--dry-run', 'Only show Git commands, without calling them') do |remote|
            options[:try] = true
          end
119

120 121
          opts.on('-h', '--help', 'Displays Help') do
            puts opts
James Lopez's avatar
James Lopez committed
122

123 124 125
            exit
          end
        end
126

127
        parser.parse!
128

129 130
        options[:branch] ||= `git rev-parse --abbrev-ref HEAD`
        options[:remote] ||= DEFAULT_REMOTE
131

132 133
        abort("Missing options. Use #{$0} --help to see the list of options available".red) if options.values.include?(nil)
        abort("Wrong version format #{options[:version].bold}".red) unless options[:version] =~ /\A\d*\-\d*\Z/
134 135 136
      end
    end
  end
James Lopez's avatar
James Lopez committed
137 138
end

139
Secpick::SecurityFix.new.create!