released 0.0.1
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 +7 -0
- data/Gemfile +16 -0
- data/Gemfile.lock +130 -0
- data/README.md +163 -0
- data/Rakefile +13 -0
- data/lib/released/goal.rb +38 -0
- data/lib/released/goals/file_exists.rb +30 -0
- data/lib/released/goals/gem_built.rb +46 -0
- data/lib/released/goals/gem_pushed.rb +60 -0
- data/lib/released/goals/git_ref_pushed.rb +39 -0
- data/lib/released/goals/github_release_exists.rb +33 -0
- data/lib/released/goals/shell.rb +40 -0
- data/lib/released/goals.rb +11 -0
- data/lib/released/pipeline_reader.rb +73 -0
- data/lib/released/runner.rb +171 -0
- data/lib/released/version.rb +3 -0
- data/lib/released.rb +17 -0
- data/released.gemspec +38 -0
- data/spec/released/goals/gem_built_spec.rb +64 -0
- data/spec/released/goals/gem_pushed_spec.rb +149 -0
- data/spec/released/goals/git_ref_pushed_spec.rb +74 -0
- data/spec/released/pipeline_reader_spec.rb +49 -0
- data/spec/spec_helper.rb +33 -0
- metadata +185 -0
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
module Released
|
|
2
|
+
class PipelineReader
|
|
3
|
+
def initialize(filename)
|
|
4
|
+
@filename = filename
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def read
|
|
8
|
+
yaml = transform(YAML.load_file(@filename))
|
|
9
|
+
|
|
10
|
+
goals = []
|
|
11
|
+
|
|
12
|
+
yaml['goals'].each do |goal_yaml|
|
|
13
|
+
name = goal_yaml.keys.first
|
|
14
|
+
config = goal_yaml[name]
|
|
15
|
+
# TODO: what if there are more?
|
|
16
|
+
|
|
17
|
+
goals << Released::Goal.named(name.to_sym).new(config)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
goals
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
|
|
25
|
+
def transform(obj)
|
|
26
|
+
case obj
|
|
27
|
+
when Hash
|
|
28
|
+
transform_hash(obj)
|
|
29
|
+
when Array
|
|
30
|
+
transform_array(obj)
|
|
31
|
+
when String
|
|
32
|
+
transform_string(obj)
|
|
33
|
+
else
|
|
34
|
+
obj
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def transform_hash(hash)
|
|
39
|
+
hash.each_with_object({}) do |(key, value), memo|
|
|
40
|
+
memo[key] = transform(value)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def transform_array(array)
|
|
45
|
+
array.map do |elem|
|
|
46
|
+
transform(elem)
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def transform_string(string)
|
|
51
|
+
case string
|
|
52
|
+
when /\Aenv!(.*)/
|
|
53
|
+
ENV.fetch(Regexp.last_match(1))
|
|
54
|
+
when /\Ash!(.*)/
|
|
55
|
+
`#{Regexp.last_match(1)}`
|
|
56
|
+
when /\A-----BEGIN PGP MESSAGE-----/
|
|
57
|
+
decrypt(string)
|
|
58
|
+
else
|
|
59
|
+
string
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def decrypt(string)
|
|
64
|
+
stdout = ''
|
|
65
|
+
stderr = ''
|
|
66
|
+
|
|
67
|
+
piper = Nanoc::Extra::Piper.new(stdout: stdout, stderr: stderr)
|
|
68
|
+
piper.run(['gpg', '--decrypt', '--no-tty'], string)
|
|
69
|
+
|
|
70
|
+
stdout
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
module Released
|
|
2
|
+
class Runner
|
|
3
|
+
# FIXME: extract UI
|
|
4
|
+
|
|
5
|
+
class TUI
|
|
6
|
+
def initialize(io)
|
|
7
|
+
@io = io
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def move_up(num)
|
|
11
|
+
@io <<
|
|
12
|
+
if num == 1
|
|
13
|
+
"\e[A"
|
|
14
|
+
else
|
|
15
|
+
"\e[#{num}A"
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def move_down(num)
|
|
20
|
+
@io <<
|
|
21
|
+
if num == 1
|
|
22
|
+
"\e[B"
|
|
23
|
+
else
|
|
24
|
+
"\e[#{num}B"
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def move_to_left(col = 1)
|
|
29
|
+
@io <<
|
|
30
|
+
if col == 1
|
|
31
|
+
"\e[G"
|
|
32
|
+
else
|
|
33
|
+
"\e[#{col}G"
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def clear_to_end_of_line
|
|
38
|
+
@io << "\e[K"
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def initialize(goals, dry_run: false)
|
|
43
|
+
@goals = goals
|
|
44
|
+
@dry_run = dry_run
|
|
45
|
+
|
|
46
|
+
@tui = TUI.new($stdout)
|
|
47
|
+
@tui_mutex = Mutex.new
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def run
|
|
51
|
+
assess_all
|
|
52
|
+
try_achieve_all
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
private
|
|
56
|
+
|
|
57
|
+
def print_goals
|
|
58
|
+
@goals.each do |goal|
|
|
59
|
+
left.times { $stdout << '. ' }
|
|
60
|
+
|
|
61
|
+
@tui.move_to_left
|
|
62
|
+
$stdout << goal
|
|
63
|
+
$stdout << ' '
|
|
64
|
+
|
|
65
|
+
@tui.move_to_left(left - 1)
|
|
66
|
+
$stdout << ' '
|
|
67
|
+
|
|
68
|
+
@tui.move_to_left
|
|
69
|
+
@tui.move_down(1)
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def handle_error(e)
|
|
74
|
+
puts
|
|
75
|
+
puts 'FAILURE!'
|
|
76
|
+
puts '-----'
|
|
77
|
+
puts e.message
|
|
78
|
+
puts
|
|
79
|
+
puts e.backtrace.join("\n")
|
|
80
|
+
puts '-----'
|
|
81
|
+
puts 'Aborting!'
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def write_state(idx, left, state)
|
|
85
|
+
up = @goals.size - idx
|
|
86
|
+
@tui.move_up(up)
|
|
87
|
+
@tui.move_to_left(left)
|
|
88
|
+
$stdout << state
|
|
89
|
+
@tui.clear_to_end_of_line
|
|
90
|
+
@tui.move_to_left
|
|
91
|
+
@tui.move_down(up)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def left
|
|
95
|
+
@_left ||= @goals.map { |g| g.to_s.size }.max + 5
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def assess_all
|
|
99
|
+
puts 'Assessing goals…'
|
|
100
|
+
print_goals
|
|
101
|
+
|
|
102
|
+
@goals.each.with_index do |_, idx|
|
|
103
|
+
write_state(idx, left, 'waiting')
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
@goals.each.with_index do |goal, idx|
|
|
107
|
+
if goal.assessable?
|
|
108
|
+
write_state(idx, left, 'working…')
|
|
109
|
+
|
|
110
|
+
begin
|
|
111
|
+
goal.assess
|
|
112
|
+
write_state(idx, left, 'ok (succeeded)')
|
|
113
|
+
rescue => e
|
|
114
|
+
write_state(idx, left, 'failed')
|
|
115
|
+
handle_error(e)
|
|
116
|
+
exit 1 # FIXME: eww
|
|
117
|
+
end
|
|
118
|
+
else
|
|
119
|
+
write_state(idx, left, 'ok (skipped)')
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
puts
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def try_achieve_all
|
|
127
|
+
puts 'Achieving goals…'
|
|
128
|
+
print_goals
|
|
129
|
+
|
|
130
|
+
@goals.each.with_index do |_, idx|
|
|
131
|
+
write_state(idx, left, 'waiting')
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
@goals.each.with_index do |goal, idx|
|
|
135
|
+
if @dry_run
|
|
136
|
+
if goal.achieved?
|
|
137
|
+
write_state(idx, left, 'ok (already achieved)')
|
|
138
|
+
else
|
|
139
|
+
write_state(idx, left, 'pending')
|
|
140
|
+
end
|
|
141
|
+
next
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
if goal.achieved?
|
|
145
|
+
write_state(idx, left, 'ok (already achieved)')
|
|
146
|
+
next
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
begin
|
|
150
|
+
write_state(idx, left, 'working…')
|
|
151
|
+
goal.try_achieve
|
|
152
|
+
rescue => e
|
|
153
|
+
write_state(idx, left, 'errored')
|
|
154
|
+
handle_error(e)
|
|
155
|
+
exit 1 # FIXME: eww
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
if !goal.effectful?
|
|
159
|
+
write_state(idx, left, 'ok (passed)')
|
|
160
|
+
next
|
|
161
|
+
elsif goal.achieved?
|
|
162
|
+
write_state(idx, left, 'ok (newly achieved)')
|
|
163
|
+
next
|
|
164
|
+
else
|
|
165
|
+
write_state(idx, left, 'failed')
|
|
166
|
+
exit 1 # FIXME: eww
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
end
|
data/lib/released.rb
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
module Released
|
|
2
|
+
end
|
|
3
|
+
|
|
4
|
+
require 'English'
|
|
5
|
+
require 'json'
|
|
6
|
+
require 'net/http'
|
|
7
|
+
require 'shellwords'
|
|
8
|
+
require 'uri'
|
|
9
|
+
|
|
10
|
+
require 'ddplugin'
|
|
11
|
+
require 'nanoc'
|
|
12
|
+
|
|
13
|
+
require_relative 'released/version'
|
|
14
|
+
require_relative 'released/goal'
|
|
15
|
+
require_relative 'released/runner'
|
|
16
|
+
require_relative 'released/pipeline_reader'
|
|
17
|
+
require_relative 'released/goals'
|
data/released.gemspec
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
require_relative 'lib/released/version'
|
|
2
|
+
|
|
3
|
+
Gem::Specification.new do |s|
|
|
4
|
+
s.name = 'released'
|
|
5
|
+
s.version = Released::VERSION
|
|
6
|
+
s.homepage = 'https://github.com/ddfreyne/released'
|
|
7
|
+
s.summary = 'extensible release tool'
|
|
8
|
+
s.description = ''
|
|
9
|
+
|
|
10
|
+
s.author = 'Denis Defreyne'
|
|
11
|
+
s.email = 'denis.defreyne@stoneship.org'
|
|
12
|
+
s.license = 'MIT'
|
|
13
|
+
|
|
14
|
+
s.files =
|
|
15
|
+
Dir['[A-Z]*'] +
|
|
16
|
+
Dir['{lib,spec}/**/*'] +
|
|
17
|
+
Dir['*.gemspec']
|
|
18
|
+
s.require_paths = ['lib']
|
|
19
|
+
|
|
20
|
+
s.rdoc_options = ['--main', 'README.md']
|
|
21
|
+
|
|
22
|
+
s.required_ruby_version = '>= 2.3.0'
|
|
23
|
+
|
|
24
|
+
# Dependencies for goals
|
|
25
|
+
# TODO: Move into plugins
|
|
26
|
+
s.add_runtime_dependency('git')
|
|
27
|
+
s.add_runtime_dependency('twitter')
|
|
28
|
+
s.add_runtime_dependency('gems')
|
|
29
|
+
s.add_runtime_dependency('netrc')
|
|
30
|
+
s.add_runtime_dependency('octokit')
|
|
31
|
+
|
|
32
|
+
# TODO: Remove this (copy piper?)
|
|
33
|
+
s.add_runtime_dependency('nanoc', '~> 4.4')
|
|
34
|
+
|
|
35
|
+
s.add_runtime_dependency('ddplugin', '~> 1.0')
|
|
36
|
+
|
|
37
|
+
s.add_development_dependency('bundler', '>= 1.7.10', '< 2.0')
|
|
38
|
+
end
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
describe Released::Goals::GemBuilt do
|
|
2
|
+
subject(:goal) do
|
|
3
|
+
described_class.new(config)
|
|
4
|
+
end
|
|
5
|
+
|
|
6
|
+
let(:config) do
|
|
7
|
+
{
|
|
8
|
+
'name' => 'donkey',
|
|
9
|
+
'version' => '0.1',
|
|
10
|
+
}
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
let(:donkey_gemspec) do
|
|
14
|
+
<<~STRING
|
|
15
|
+
Gem::Specification.new do |s|
|
|
16
|
+
s.name = 'donkey'
|
|
17
|
+
s.version = '0.1'
|
|
18
|
+
|
|
19
|
+
s.summary = 'the cutest animal'
|
|
20
|
+
s.author = 'Denis Defreyne'
|
|
21
|
+
s.email = 'denis.defreyne@stoneship.org'
|
|
22
|
+
s.license = 'MIT'
|
|
23
|
+
|
|
24
|
+
s.files = []
|
|
25
|
+
end
|
|
26
|
+
STRING
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
describe '#achieved?' do
|
|
30
|
+
subject { goal.achieved? }
|
|
31
|
+
|
|
32
|
+
context 'file exists' do
|
|
33
|
+
before { File.write('donkey-0.1.gem', 'hello!') }
|
|
34
|
+
it { is_expected.to eql(true) }
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
context 'file does not exist' do
|
|
38
|
+
it { is_expected.to eql(false) }
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
describe '#try_achieve' do
|
|
43
|
+
subject { goal.try_achieve }
|
|
44
|
+
|
|
45
|
+
context 'no gemspec' do
|
|
46
|
+
it 'raises' do
|
|
47
|
+
expect { subject }.to raise_error(/Gemspec file not found/)
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
context 'valid gemspec' do
|
|
52
|
+
before { File.write('donkey.gemspec', donkey_gemspec) }
|
|
53
|
+
|
|
54
|
+
it 'builds the gem' do
|
|
55
|
+
expect { subject }.to change { File.file?('donkey-0.1.gem') }.from(false).to(true)
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
describe '#failure_reason' do
|
|
61
|
+
subject { goal.failure_reason }
|
|
62
|
+
it { is_expected.to eql('expected the file donkey-0.1.gem to exist') }
|
|
63
|
+
end
|
|
64
|
+
end
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
describe Released::Goals::GemPushed, stdio: true do
|
|
2
|
+
subject(:goal) do
|
|
3
|
+
described_class.new(config)
|
|
4
|
+
end
|
|
5
|
+
|
|
6
|
+
let(:config) do
|
|
7
|
+
{
|
|
8
|
+
'name' => gem_name,
|
|
9
|
+
'version' => gem_version,
|
|
10
|
+
'authorization' => authorization,
|
|
11
|
+
'rubygems_base_url' => rubygems_base_url,
|
|
12
|
+
}
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
let(:gem_name) { 'nanoc' }
|
|
16
|
+
let(:gem_version) { '4.4.2' }
|
|
17
|
+
let(:authorization) { raise 'override me' }
|
|
18
|
+
let(:rubygems_base_url) { 'https://rubygems.org' }
|
|
19
|
+
|
|
20
|
+
let(:correct_authorization) { '83f5b7b9516c4342068cc60063a75de09bdb44d3' }
|
|
21
|
+
let(:incorrect_authorization) { '66666666666666666666666666666666' }
|
|
22
|
+
|
|
23
|
+
before do
|
|
24
|
+
gemspec =
|
|
25
|
+
<<~STRING
|
|
26
|
+
Gem::Specification.new do |s|
|
|
27
|
+
s.name = 'nanoc'
|
|
28
|
+
s.version = '4.4.2'
|
|
29
|
+
|
|
30
|
+
s.summary = 'the best thing ever'
|
|
31
|
+
s.author = 'Denis Defreyne'
|
|
32
|
+
s.email = 'denis.defreyne@stoneship.org'
|
|
33
|
+
s.license = 'MIT'
|
|
34
|
+
|
|
35
|
+
s.files = []
|
|
36
|
+
end
|
|
37
|
+
STRING
|
|
38
|
+
|
|
39
|
+
File.write('donkey.gemspec', gemspec)
|
|
40
|
+
system('gem', 'build', '--silent', 'donkey.gemspec')
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
describe '#assess' do
|
|
44
|
+
subject { goal.assess }
|
|
45
|
+
|
|
46
|
+
context 'incorrect authorization' do
|
|
47
|
+
let(:authorization) { incorrect_authorization }
|
|
48
|
+
|
|
49
|
+
it 'raises' do
|
|
50
|
+
VCR.use_cassette('goals__gem_pushed_spec__assess__incorrect_auth') do
|
|
51
|
+
expect { subject }.to raise_error(
|
|
52
|
+
RuntimeError, 'Authorization failed'
|
|
53
|
+
)
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
context 'correct authorization' do
|
|
59
|
+
let(:authorization) { correct_authorization }
|
|
60
|
+
|
|
61
|
+
context 'response does not include requested gem' do
|
|
62
|
+
let(:gem_name) { 'definitely_not_nanoc' }
|
|
63
|
+
|
|
64
|
+
it 'raises' do
|
|
65
|
+
VCR.use_cassette('goals__gem_pushed_spec__assess__correct_auth_but_gem_not_present') do
|
|
66
|
+
expect { subject }.to raise_error(
|
|
67
|
+
RuntimeError, 'List of owned gems does not include request gem'
|
|
68
|
+
)
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
context 'response includes requested gem' do
|
|
74
|
+
it 'raises' do
|
|
75
|
+
VCR.use_cassette('goals__gem_pushed_spec__assess__correct_auth_and_gem_present') do
|
|
76
|
+
expect { subject }.not_to raise_error
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
describe '#achieved?' do
|
|
84
|
+
subject { goal.achieved? }
|
|
85
|
+
|
|
86
|
+
context 'incorrect authorization' do
|
|
87
|
+
let(:authorization) { incorrect_authorization }
|
|
88
|
+
|
|
89
|
+
it 'raises' do
|
|
90
|
+
VCR.use_cassette('goals__gem_pushed_spec___achieved_q__incorrect_auth') do
|
|
91
|
+
expect { subject }.to raise_error(
|
|
92
|
+
RuntimeError, 'Authorization failed'
|
|
93
|
+
)
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
context 'correct authorization' do
|
|
99
|
+
let(:authorization) { correct_authorization }
|
|
100
|
+
|
|
101
|
+
context 'achieved' do
|
|
102
|
+
it 'is achieved' do
|
|
103
|
+
VCR.use_cassette('goals__gem_pushed_spec___achieved_q__achieved') do
|
|
104
|
+
expect(subject).to be
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
context 'not achieved' do
|
|
110
|
+
it 'is not achieved' do
|
|
111
|
+
VCR.use_cassette('goals__gem_pushed_spec___achieved_q__not_achieved') do
|
|
112
|
+
expect(subject).not_to be
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
describe '#try_achieve' do
|
|
120
|
+
subject { goal.try_achieve }
|
|
121
|
+
|
|
122
|
+
let(:rubygems_repo) do
|
|
123
|
+
Gems::Client.new(
|
|
124
|
+
key: '83f5b7b9516c4342068cc60063a75de09bdb44d3',
|
|
125
|
+
host: 'https://rubygems.org',
|
|
126
|
+
)
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
let(:authorization) { correct_authorization }
|
|
130
|
+
|
|
131
|
+
example do
|
|
132
|
+
VCR.use_cassette('goals__gem_pushed_spec___try_achieve__step_a') do
|
|
133
|
+
expect(rubygems_repo.gems.any? { |g| g['name'] == 'nanoc' && g['version'] == '4.4.2' }).not_to be
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
VCR.use_cassette('goals__gem_pushed_spec___try_achieve__step_b') do
|
|
137
|
+
subject
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
VCR.use_cassette('goals__gem_pushed_spec___try_achieve__step_c') do
|
|
141
|
+
expect(rubygems_repo.gems.any? { |g| g['name'] == 'nanoc' && g['version'] == '4.4.2' }).to be
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
describe '#failure_reason' do
|
|
147
|
+
# TODO
|
|
148
|
+
end
|
|
149
|
+
end
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
describe Released::Goals::GitRefPushed do
|
|
2
|
+
subject(:goal) do
|
|
3
|
+
described_class.new(config)
|
|
4
|
+
end
|
|
5
|
+
|
|
6
|
+
let(:config) do
|
|
7
|
+
{
|
|
8
|
+
'working_dir' => 'local',
|
|
9
|
+
'remote' => 'gitlab',
|
|
10
|
+
'branch' => 'devel',
|
|
11
|
+
}
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
let!(:local) do
|
|
15
|
+
Git.init('local').tap do |g|
|
|
16
|
+
g.config('user.name', 'Testy McTestface')
|
|
17
|
+
g.config('user.email', 'testface@example.com')
|
|
18
|
+
|
|
19
|
+
g.chdir { File.write('hello.txt', 'hi there') }
|
|
20
|
+
g.add('hello.txt')
|
|
21
|
+
g.commit('Add greeting')
|
|
22
|
+
g.branch('devel').checkout
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
let!(:remote) do
|
|
27
|
+
Git.init('remote')
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
before do
|
|
31
|
+
local.add_remote('gitlab', './remote')
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
describe '#achieved?' do
|
|
35
|
+
subject { goal.achieved? }
|
|
36
|
+
|
|
37
|
+
context 'not pushed' do
|
|
38
|
+
it { is_expected.not_to be }
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
context 'pushed, but not right rev' do
|
|
42
|
+
before do
|
|
43
|
+
goal.try_achieve
|
|
44
|
+
|
|
45
|
+
local.chdir { File.write('bye.txt', 'bye now') }
|
|
46
|
+
local.add('bye.txt')
|
|
47
|
+
local.commit('Add farewell')
|
|
48
|
+
local.branch('devel').checkout
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
it { is_expected.not_to be }
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
context 'pushed' do
|
|
55
|
+
before { goal.try_achieve }
|
|
56
|
+
it { is_expected.to be }
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
describe '#try_achieve' do
|
|
61
|
+
subject { goal.try_achieve }
|
|
62
|
+
|
|
63
|
+
example do
|
|
64
|
+
expect(remote.branches['devel']).to be_nil
|
|
65
|
+
subject
|
|
66
|
+
expect(remote.branches['devel'].gcommit.sha).to eql(local.branches['devel'].gcommit.sha)
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
describe '#failure_reason' do
|
|
71
|
+
subject { goal.failure_reason }
|
|
72
|
+
it { is_expected.to eql('HEAD does not exist on gitlab/devel') }
|
|
73
|
+
end
|
|
74
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
describe Released::PipelineReader do
|
|
2
|
+
subject(:pipeline_reader) { described_class.new(filename) }
|
|
3
|
+
|
|
4
|
+
let(:filename) { 'pipeline.yaml' }
|
|
5
|
+
|
|
6
|
+
before do
|
|
7
|
+
ENV['FAVORITE_GEM_AUTHOR'] = 'denis'
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
describe '#transform' do
|
|
11
|
+
subject { pipeline_reader.send(:transform, obj) }
|
|
12
|
+
|
|
13
|
+
context 'with array' do
|
|
14
|
+
let(:obj) { %w(hello env!FAVORITE_GEM_AUTHOR) }
|
|
15
|
+
it { is_expected.to eql(%w(hello denis)) }
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
context 'with hash' do
|
|
19
|
+
let(:obj) { { people: ['env!FAVORITE_GEM_AUTHOR'] } }
|
|
20
|
+
it { is_expected.to eql(people: ['denis']) }
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
context 'with string' do
|
|
24
|
+
context 'normal string' do
|
|
25
|
+
let(:obj) { 'hello' }
|
|
26
|
+
it { is_expected.to eql(obj) }
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
context 'env! string' do
|
|
30
|
+
let(:obj) { 'env!FAVORITE_GEM_AUTHOR' }
|
|
31
|
+
it { is_expected.to eql('denis') }
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
context 'sh! string' do
|
|
35
|
+
let(:obj) { 'sh!echo -n hello' }
|
|
36
|
+
it { is_expected.to eql('hello') }
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
context 'encrypted string' do
|
|
40
|
+
# TODO
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
context 'with anything else' do
|
|
45
|
+
let(:obj) { :donkey }
|
|
46
|
+
it { is_expected.to eql(obj) }
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
data/spec/spec_helper.rb
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
require 'released'
|
|
2
|
+
|
|
3
|
+
require 'webmock/rspec'
|
|
4
|
+
require 'vcr'
|
|
5
|
+
require 'rspec/its'
|
|
6
|
+
|
|
7
|
+
VCR.configure do |config|
|
|
8
|
+
config.cassette_library_dir = 'fixtures/vcr_cassettes'
|
|
9
|
+
config.hook_into :webmock
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
RSpec.configure do |c|
|
|
13
|
+
c.around(:each, stdio: true) do |example|
|
|
14
|
+
orig_stdout = $stdout
|
|
15
|
+
orig_stderr = $stderr
|
|
16
|
+
|
|
17
|
+
$stdout = StringIO.new
|
|
18
|
+
$stderr = StringIO.new
|
|
19
|
+
|
|
20
|
+
example.run
|
|
21
|
+
|
|
22
|
+
$stdout = orig_stdout
|
|
23
|
+
$stderr = orig_stderr
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
c.around(:each) do |example|
|
|
27
|
+
Dir.mktmpdir('released-specs') do |dir|
|
|
28
|
+
FileUtils.cd(dir) do
|
|
29
|
+
example.run
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|