gerrit-seed 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 92b8f30d835cb2010f4e98d6ed2834bf886b8066848ee1d153475e0035f11158
4
+ data.tar.gz: ed7fcc4048c58ba43f714c3f4361534c7860adfc5a9d3f9161f313b1f83c13b4
5
+ SHA512:
6
+ metadata.gz: 011da5e9cae315ab188e1f62b6604b843880084a2b0ac6e2c34d1027fa1a8f2847db73c7608ce3ac7c9afb7384b61c69fef37b7e3e061fc0e7b9111020f4fb17
7
+ data.tar.gz: 2b08d98c7ac478559a7d207484ace3abaf44ad665f520dfa110ea5fa07d4e0765a2ce46cf3521c39959188b3dd8937e6255bc33fc6a404d7ec29046d3e064fdb
data/bin/gerrit-seed ADDED
@@ -0,0 +1,28 @@
1
+ #!/usr/local/bin/ruby
2
+
3
+ blob = ARGF.read
4
+
5
+ require 'gerrit_seed'
6
+
7
+ logger = GerritSeed::Logger.new
8
+
9
+ begin
10
+ GerritSeed::Seeder.new(
11
+ git: GerritSeed::Git.new(
12
+ log: logger,
13
+ dir: ENV.fetch('OUTDIR', File.join(Dir.pwd, 'build')),
14
+ host: ENV.fetch("GERRIT_HOST", 'localhost'),
15
+ port: ENV.fetch("GERRIT_PORT", 29418).to_i,
16
+ user: ENV.fetch("GERRIT_USER", 'admin'),
17
+ ),
18
+ gerrit: GerritSeed::Gerrit.new(
19
+ log: logger,
20
+ host: ENV.fetch("GERRIT_HOST", 'localhost'),
21
+ port: ENV.fetch("GERRIT_PORT", 29418).to_i,
22
+ user: ENV.fetch("GERRIT_USER", 'admin')
23
+ )
24
+ ).apply(YAML.safe_load(blob))
25
+ rescue GerritSeed::Shell::CommandError => e
26
+ puts e.message
27
+ Kernel.exit 1
28
+ end
data/bin/gerrit-unseed ADDED
@@ -0,0 +1,34 @@
1
+ #!/usr/local/bin/ruby
2
+
3
+ blob = ARGF.read
4
+
5
+ require 'gerrit_seed'
6
+
7
+ logger = GerritSeed::Logger.new
8
+ gerrit = GerritSeed::Gerrit.new(
9
+ log: logger,
10
+ host: ENV.fetch("GERRIT_HOST", 'localhost'),
11
+ port: ENV.fetch("GERRIT_PORT", 29418).to_i,
12
+ user: ENV.fetch("GERRIT_USER", 'admin')
13
+ )
14
+
15
+ gerrit.install_plugin(
16
+ name: 'delete-project',
17
+ url: 'https://gerrit-ci.gerritforge.com/job/plugin-delete-project-bazel-stable-2.15/lastSuccessfulBuild/artifact/bazel-genfiles/plugins/delete-project/delete-project.jar'
18
+ )
19
+
20
+ begin
21
+ GerritSeed::Unseeder.new(
22
+ git: GerritSeed::Git.new(
23
+ log: logger,
24
+ dir: ENV.fetch('OUTDIR', File.join(Dir.pwd, 'build')),
25
+ host: ENV.fetch("GERRIT_HOST", 'localhost'),
26
+ port: ENV.fetch("GERRIT_PORT", 29418).to_i,
27
+ user: ENV.fetch("GERRIT_USER", 'admin'),
28
+ ),
29
+ gerrit: gerrit
30
+ ).apply(YAML.safe_load(blob))
31
+ rescue GerritSeed::Shell::CommandError => e
32
+ puts e.message
33
+ Kernel.exit 1
34
+ end
@@ -0,0 +1,142 @@
1
+ module GerritSeed
2
+ class Gerrit
3
+ attr_reader :host, :log, :port, :shell, :user
4
+
5
+ def initialize(host:, log:, port:, user:)
6
+ @host = host
7
+ @log = log
8
+ @port = port
9
+ @shell = Shell.new
10
+ @user = user
11
+ end
12
+
13
+ def create_project(name:, **)
14
+ unless exec('gerrit ls-projects').match(name)
15
+ exec('gerrit create-project',
16
+ name,
17
+ '--change-id', 'TRUE',
18
+ '--empty-commit',
19
+ '--require-change-id'
20
+ )
21
+
22
+ log.ok("project: #{name}")
23
+ end
24
+ end
25
+
26
+ def delete_project(name:, **)
27
+ if has_project?(name)
28
+ begin
29
+ exec('delete-project', 'delete', '--force', '--yes-really-delete', name)
30
+ rescue Shell::CommandError => e
31
+ raise if has_project?(name) || e.output.strip != 'fatal: internal server error'
32
+ end
33
+
34
+ log.ok("project: #{name}")
35
+ end
36
+ end
37
+
38
+ def has_project?(name)
39
+ exec('gerrit ls-projects').lines.map(&:strip).include?(name)
40
+ end
41
+
42
+ def install_plugin(name:, url:)
43
+ unless JSON.parse(exec('gerrit plugin ls --format JSON')).key?(name.to_s)
44
+ exec('gerrit', 'plugin', 'install', "'#{url}'")
45
+ log.ok("plugin: #{name}")
46
+ end
47
+ end
48
+
49
+ def create_user(email:, group:, full_name:, username:, ssh_key:, **)
50
+ begin
51
+ exec('gerrit create-account', '--group', sq(group), username)
52
+ log.ok("user: #{username}")
53
+ rescue Shell::CommandError => e
54
+ raise unless e.output.strip == "fatal: username '#{username}' already exists"
55
+ end
56
+
57
+ exec(
58
+ 'gerrit', 'set-account',
59
+ '--active',
60
+ '--full-name', sq(full_name),
61
+ '--add-email', sq(email),
62
+ '--add-ssh-key', '-',
63
+ '--preferred-email', sq(email),
64
+ username,
65
+ stdin: File.read(File.expand_path(ssh_key))
66
+ )
67
+ end
68
+
69
+ def delete_user(username:, **)
70
+ exec('gerrit set-account', '--inactive', username)
71
+ log.ok("user: #{username}")
72
+ rescue Shell::CommandError => e
73
+ raise unless e.output.strip == 'fatal: account not active'
74
+ end
75
+
76
+ def create_change(change, changes:, git:, users:, **)
77
+ branch_name = branch_name_of(change[:name])
78
+ parent_change = changes.detect do |other_change|
79
+ other_change[:project] == change[:project] &&
80
+ other_change[:name].start_with?(change[:parent])
81
+ end
82
+
83
+ parent_branch_name = if parent_change
84
+ branch_name_of(parent_change[:name])
85
+ else
86
+ change[:parent]
87
+ end
88
+
89
+ author = users.detect { |x| x[:username] == change[:author] }
90
+
91
+ unless author
92
+ fail "Author '#{change[:author]}' for change '#{change[:name]}' could not be found"
93
+ end
94
+
95
+ git.checkout(
96
+ branch: branch_name,
97
+ commit: parent_branch_name,
98
+ )
99
+
100
+ git.shell.("touch #{branch_name}.in")
101
+ git.shell.("git add #{branch_name}.in")
102
+ git.commit(
103
+ author: author[:full_name],
104
+ email: author[:email],
105
+ subject: change[:name],
106
+ )
107
+
108
+ unless change_submitted?(change)
109
+ git.push(
110
+ user: author[:username],
111
+ reviewers: %w[admin] + users.map { |x| x[:username] }
112
+ )
113
+
114
+ log.ok("change: #{change[:project]} - #{change[:name]}")
115
+ end
116
+ end
117
+
118
+ private
119
+
120
+ def branch_name_of(string)
121
+ string.gsub(/\W+/, '_').gsub(/\_+/, '_').gsub(/^_|_$/, '').downcase
122
+ end
123
+
124
+ def change_submitted?(change)
125
+ exec('gerrit query --format json -- status:open').lines.any? do |line|
126
+ JSON.parse(line)['subject'] == change[:name]
127
+ end
128
+ end
129
+
130
+ def exec(*cmd, **kwargs)
131
+ shell.(
132
+ 'ssh', '-p', port.to_s, "#{user}@#{host}",
133
+ *cmd,
134
+ **kwargs
135
+ )
136
+ end
137
+
138
+ def sq(word)
139
+ "'#{word}'"
140
+ end
141
+ end
142
+ end
@@ -0,0 +1,86 @@
1
+ module GerritSeed
2
+ class Git
3
+ attr_reader :dir, :host, :port, :log, :name, :shell, :user
4
+
5
+ def initialize(dir:, host:, log:, name: nil, port:, user:)
6
+ @dir = dir
7
+ @host = host
8
+ @log = log
9
+ @name = name
10
+ @port = port
11
+ @shell = Shell.new(chdir: dir)
12
+ @user = user
13
+ end
14
+
15
+ def clone(name:)
16
+ unless File.exist?(File.join(dir, name))
17
+ shell.("git", "clone", "ssh://#{user}@#{host}:#{port}/#{name}", name)
18
+ shell.(
19
+ 'scp',
20
+ '-p',
21
+ '-P', port.to_s,
22
+ "#{user}@#{host}:hooks/commit-msg",
23
+ "#{name}/.git/hooks/"
24
+ )
25
+ end
26
+
27
+ self.class.new(
28
+ dir: File.join(dir, name),
29
+ host: host,
30
+ log: log,
31
+ name: name,
32
+ port: port,
33
+ user: user
34
+ )
35
+ end
36
+
37
+ def rm_rf(name:)
38
+ if File.exist?(File.join(dir, name))
39
+ FileUtils.rm_rf(File.join(dir, name))
40
+ end
41
+ end
42
+
43
+ def checkout(branch:, commit:)
44
+ if available_branches.include?(branch)
45
+ shell.('git', 'checkout', branch)
46
+ else
47
+ shell.('git', 'checkout', '-b', branch, commit)
48
+ end
49
+ end
50
+
51
+ def commit(author:, email:, subject:)
52
+ return if shell.('git', 'status', '--short').empty?
53
+
54
+ shell.(
55
+ 'git',
56
+ "-c", "user.name='#{author}'",
57
+ "-c", "user.email='#{email}'",
58
+ 'commit',
59
+ "--author='#{author} <#{email}>'",
60
+ '-m', subject
61
+ )
62
+ end
63
+
64
+ def push(user:, reviewers: [])
65
+ fail "name is not set, did you forget to #clone?" if @name.nil?
66
+
67
+ reviewers_query = if reviewers.any?
68
+ '%' + reviewers.map { |x| "r=#{x}" }.join(',')
69
+ else
70
+ ''
71
+ end
72
+
73
+ shell.(
74
+ 'git', 'push',
75
+ "ssh://#{user}@#{host}:#{port}/#{name}",
76
+ 'HEAD:refs/for/master' + reviewers_query
77
+ )
78
+ end
79
+
80
+ private
81
+
82
+ def available_branches
83
+ shell.('git', 'branch', '-l', '--format', '%(refname:short)').lines.map(&:strip)
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,12 @@
1
+ require 'pastel'
2
+
3
+ module GerritSeed
4
+ class Logger
5
+ def initialize()
6
+ end
7
+
8
+ def ok(msg)
9
+ puts Pastel.new.green("✓ #{msg}")
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,38 @@
1
+ module GerritSeed
2
+ class Seeder
3
+ attr_reader :gerrit, :git
4
+
5
+ def initialize(gerrit:, git:)
6
+ @gerrit = gerrit
7
+ @git = git
8
+ end
9
+
10
+ def apply(directive_list)
11
+ directives = directive_list.reduce({}) do |acc, item|
12
+ item.keys.each do |key|
13
+ acc[key.to_sym] ||= []
14
+ acc[key.to_sym] << item[key].transform_keys(&:to_sym)
15
+ end
16
+
17
+ acc
18
+ end
19
+
20
+
21
+ directives[:user].each(&gerrit.method(:create_user))
22
+ directives[:project].each(&gerrit.method(:create_project))
23
+
24
+ repos = directives[:project].reduce({}) do |acc, name:, **|
25
+ acc[name.to_s] = git.clone(name: name)
26
+ acc
27
+ end
28
+
29
+ directives[:change].reduce([]) do |acc, change|
30
+ repo = repos.fetch(change[:project].to_s)
31
+
32
+ gerrit.create_change(change, git: repo, changes: acc, users: directives[:user])
33
+
34
+ acc.push(change)
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,65 @@
1
+ require 'open3'
2
+
3
+ module GerritSeed
4
+ class Shell
5
+ class CommandError < StandardError
6
+ attr_reader :command, :exit_status, :output
7
+
8
+ def initialize(message:, command:, exit_status:, output:)
9
+ @command = command
10
+ @exit_status = exit_status
11
+ @output = output
12
+
13
+ super(message)
14
+ end
15
+ end
16
+
17
+ def initialize(chdir: Dir.pwd)
18
+ @chdir = chdir
19
+ end
20
+
21
+ def call(*cmd, stdin: nil, **kwargs)
22
+ buffer, exit_status = Open3.capture2e(*cmd, stdin_data: stdin, chdir: @chdir)
23
+
24
+ # buffer = []
25
+ # exit_status = nil
26
+ #
27
+ # Open3.popen2e(*cmd, stdin_data: stdin, chdir: @chdir) do |_stdin, stdout_stderr, wait_thr|
28
+ # # _stdin.write(stdin) unless stdin.nil?
29
+
30
+ # lines = stdout_stderr.to_a
31
+ # buffer.concat(lines)
32
+
33
+ # # lines.each do |line|
34
+ # # puts line
35
+ # # end
36
+
37
+ # exit_status = wait_thr.value
38
+ # end
39
+
40
+ # buffer = buffer.join
41
+
42
+ unless exit_status.success?
43
+ raise CommandError.new(
44
+ message: (
45
+ <<~MESSAGE
46
+ Shell command failed: #{exit_status}:
47
+
48
+ #{cmd}
49
+
50
+ Output:
51
+
52
+ #{buffer}
53
+
54
+ MESSAGE
55
+ ),
56
+ command: cmd,
57
+ exit_status: exit_status,
58
+ output: buffer
59
+ )
60
+ end
61
+
62
+ buffer
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,30 @@
1
+ module GerritSeed
2
+ class Unseeder
3
+ attr_reader :gerrit, :git
4
+
5
+ def initialize(gerrit:, git:)
6
+ @gerrit = gerrit
7
+ @git = git
8
+ end
9
+
10
+ def apply(directive_list)
11
+ directives = directive_list.reduce({}) do |acc, item|
12
+ item.keys.each do |key|
13
+ acc[key.to_sym] ||= []
14
+ acc[key.to_sym] << item[key].transform_keys(&:to_sym)
15
+ end
16
+
17
+ acc
18
+ end
19
+
20
+ directives[:project].tap do |projects|
21
+ projects.each(&gerrit.method(:delete_project))
22
+ projects.each(&git.method(:rm_rf))
23
+ end
24
+
25
+ directives[:user].reject do |user|
26
+ user[:username] == gerrit.user
27
+ end.each(&gerrit.method(:delete_user))
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,10 @@
1
+ require 'fileutils'
2
+ require 'json'
3
+ require 'yaml'
4
+
5
+ require 'gerrit_seed/shell'
6
+ require 'gerrit_seed/logger'
7
+ require 'gerrit_seed/git'
8
+ require 'gerrit_seed/gerrit'
9
+ require 'gerrit_seed/seeder'
10
+ require 'gerrit_seed/unseeder'
@@ -0,0 +1,52 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe GerritSeed::Shell do
4
+ def capture_output
5
+ stdout, stderr = $stdout, $stderr
6
+ $stdout, $stderr = StringIO.new, StringIO.new
7
+
8
+ result = yield
9
+
10
+ OpenStruct.new(
11
+ stdout: $stdout.string,
12
+ stderr: $stderr.string,
13
+ result: result
14
+ )
15
+ ensure
16
+ $stdout, $stderr = stdout, stderr
17
+ end
18
+
19
+ it 'forwards stdout' do
20
+ output = capture_output do
21
+ subject.('echo hi')
22
+ end
23
+
24
+ expect(output.stdout).to eq("hi\n")
25
+ end
26
+
27
+ it 'forwards stderr' do
28
+ output = capture_output do
29
+ subject.('echo hi 1>&2')
30
+ end
31
+
32
+ expect(output.stdout).to eq("hi\n")
33
+ end
34
+
35
+ it 'returns output' do
36
+ capture_output do
37
+ expect(subject.('echo hi && echo there 1>&2')).to eq("hi\nthere\n")
38
+ end
39
+ end
40
+
41
+ it 'raises CommandError on exit code > 0' do
42
+ expect {
43
+ capture_output { subject.('which foo') }
44
+ }.to raise_error(GerritSeed::Shell::CommandError)
45
+ end
46
+
47
+ it 'accepts stdin' do
48
+ output = capture_output do
49
+ subject.('cat', stdin: 'gerrit-seed.gemspec')
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'simplecov'
4
+
5
+ SimpleCov.start do
6
+ add_filter(%r{/spec/})
7
+ coverage_dir("#{ENV.fetch('COVERAGE_DIR', 'coverage')}")
8
+ end
9
+
10
+ require 'gerrit_seed'
11
+
12
+ RSpec.configure do |config|
13
+ config.expect_with :rspec do |expectations|
14
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
15
+ end
16
+
17
+ config.mock_with :rspec do |mocks|
18
+ mocks.verify_partial_doubles = true
19
+ end
20
+
21
+ config.shared_context_metadata_behavior = :apply_to_host_groups
22
+
23
+ config.filter_run_when_matching :focus
24
+
25
+ config.disable_monkey_patching!
26
+
27
+ config.warnings = true
28
+
29
+ config.default_formatter = 'doc' if config.files_to_run.one?
30
+
31
+ config.order = :random
32
+
33
+ Kernel.srand config.seed
34
+ end
metadata ADDED
@@ -0,0 +1,114 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: gerrit-seed
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Ahmad Amireh
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2019-02-14 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: json
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.1'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.1'
27
+ - !ruby/object:Gem::Dependency
28
+ name: pastel
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '0.7'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '0.7'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.8'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.8'
55
+ - !ruby/object:Gem::Dependency
56
+ name: simplecov
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '0.16'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '0.16'
69
+ description: Declarative Gerrit seeding with YAML.
70
+ email: ahmad@amireh.net
71
+ executables:
72
+ - gerrit-seed
73
+ - gerrit-unseed
74
+ extensions: []
75
+ extra_rdoc_files: []
76
+ files:
77
+ - bin/gerrit-seed
78
+ - bin/gerrit-unseed
79
+ - lib/gerrit_seed.rb
80
+ - lib/gerrit_seed/gerrit.rb
81
+ - lib/gerrit_seed/git.rb
82
+ - lib/gerrit_seed/logger.rb
83
+ - lib/gerrit_seed/seeder.rb
84
+ - lib/gerrit_seed/shell.rb
85
+ - lib/gerrit_seed/unseeder.rb
86
+ - spec/shell_spec.rb
87
+ - spec/spec_helper.rb
88
+ homepage: https://github.com/amireh/gerrit-seed
89
+ licenses:
90
+ - MIT
91
+ metadata: {}
92
+ post_install_message:
93
+ rdoc_options: []
94
+ require_paths:
95
+ - lib
96
+ required_ruby_version: !ruby/object:Gem::Requirement
97
+ requirements:
98
+ - - "~>"
99
+ - !ruby/object:Gem::Version
100
+ version: 2.5.1
101
+ required_rubygems_version: !ruby/object:Gem::Requirement
102
+ requirements:
103
+ - - ">="
104
+ - !ruby/object:Gem::Version
105
+ version: '0'
106
+ requirements: []
107
+ rubyforge_project:
108
+ rubygems_version: 2.7.6
109
+ signing_key:
110
+ specification_version: 4
111
+ summary: Seed Gerrit with sample data from a YAML file
112
+ test_files:
113
+ - spec/shell_spec.rb
114
+ - spec/spec_helper.rb