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