gerrit-seed 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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