stairs 0.4.0 → 0.4.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +4 -0
- data/Guardfile +1 -1
- data/README.md +8 -16
- data/Rakefile +4 -2
- data/lib/stairs/env_adapters/dotenv.rb +4 -0
- data/lib/stairs/env_adapters/rbenv.rb +4 -0
- data/lib/stairs/env_adapters/rvm.rb +4 -0
- data/lib/stairs/step.rb +21 -5
- data/lib/stairs/util/file_mutation.rb +12 -2
- data/lib/stairs/version.rb +1 -1
- data/spec/lib/stairs/env_adapters/dotenv_spec.rb +11 -1
- data/spec/lib/stairs/env_adapters/rbenv_spec.rb +11 -1
- data/spec/lib/stairs/env_adapters/rvm_spec.rb +11 -1
- data/spec/lib/stairs/step_spec.rb +72 -2
- data/spec/lib/stairs/util/file_mutation_spec.rb +50 -4
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cb094a690ad438125715b25683b2d11f176432ed
|
4
|
+
data.tar.gz: d3eb3604d83427287293ecac50daf8ed6a5ea841
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 54186ff0a046b2dcbe68fb116a6915ffb7cc350105c0e633e62ff05d37460f705a2c08334c542195ddc1a7b1cfdfb14613aaf60e18f2839f5a7d66d3b381f07c
|
7
|
+
data.tar.gz: 6c5e729f8a7e3b8df3c5f5b0fe9c20264c9b95e688ba43e435f747cafade1426084cb81029ea59f3b3f3ad570154c43c68b3ac990589bf9d00b4c4e3e343cac4
|
data/.rubocop.yml
CHANGED
@@ -32,6 +32,10 @@ SpecialGlobalVars:
|
|
32
32
|
CollectionMethods:
|
33
33
|
Enabled: false
|
34
34
|
|
35
|
+
# Cuz we don't use any fancy characters anyway
|
36
|
+
Encoding:
|
37
|
+
Enabled: false
|
38
|
+
|
35
39
|
# I just don't care so much about clean specs.. Like short methods and short lines
|
36
40
|
AllCops:
|
37
41
|
Excludes:
|
data/Guardfile
CHANGED
data/README.md
CHANGED
@@ -43,8 +43,7 @@ $ rake newb
|
|
43
43
|
|
44
44
|
## Defining scripts
|
45
45
|
|
46
|
-
A script composes many steps that setup a project.
|
47
|
-
not yet implemented.
|
46
|
+
A script composes many steps that setup a project.
|
48
47
|
|
49
48
|
```ruby
|
50
49
|
bundle
|
@@ -55,7 +54,7 @@ setup :s3
|
|
55
54
|
setup :zencoder, required: false
|
56
55
|
|
57
56
|
setup :misc do
|
58
|
-
env "CHECK_IT", provide
|
57
|
+
env "CHECK_IT", provide("Cool check it value")
|
59
58
|
end
|
60
59
|
|
61
60
|
rake "db:setup"
|
@@ -65,38 +64,30 @@ finish "Just run rails s and sidekiq to get rolling!"
|
|
65
64
|
|
66
65
|
### Example CLI
|
67
66
|
|
68
|
-
Given the above script, the CLI might look like this.
|
69
|
-
is desired future functionality (bundle/db tasks, spacing, last words).
|
67
|
+
Given the above script, the CLI might look like this.
|
70
68
|
|
71
69
|
```
|
72
70
|
$ rake newb
|
73
71
|
Looks like you're using rbenv to manage environment variables. Is this correct? (Y/N): Y
|
74
|
-
|
75
72
|
= Running script setup.rb
|
76
|
-
|
77
73
|
== Running bundle
|
78
74
|
...
|
79
75
|
== Completed bundle
|
80
|
-
|
81
76
|
== Running S3
|
82
77
|
AWS access key: 39u39d9u291
|
83
78
|
AWS secret: 19jd920i10is0i01i0s01ks0kfknkje
|
84
79
|
Do you have an existing bucket? (Y/N): Y
|
85
80
|
Bucket name (leave blank for app-dev): my-cool-bucket
|
86
81
|
== Completed S3
|
87
|
-
|
88
82
|
== Starting Zencoder
|
89
83
|
This step is optional, would you like to perform it? (Y/N): N
|
90
84
|
== Completed Zencoder
|
91
|
-
|
92
85
|
== Starting Misc
|
93
86
|
Cool check it value: w00t
|
94
87
|
== Completed Misc
|
95
|
-
|
96
88
|
== Running db:setup
|
97
89
|
...
|
98
90
|
== Completed db:setup
|
99
|
-
|
100
91
|
== All done!
|
101
92
|
Run rails s and sidekiq to get rolling!
|
102
93
|
```
|
@@ -106,7 +97,7 @@ Run rails s and sidekiq to get rolling!
|
|
106
97
|
### Collecting values
|
107
98
|
```ruby
|
108
99
|
value = provide "Something"
|
109
|
-
value = provide "Another", required: false
|
100
|
+
value = provide "Another", required: false
|
110
101
|
provide "More", default: "a-default"
|
111
102
|
```
|
112
103
|
|
@@ -158,6 +149,7 @@ end
|
|
158
149
|
#### Using predefined steps (aka plugins)
|
159
150
|
```ruby
|
160
151
|
setup :s3
|
152
|
+
setup :facebook, required: false
|
161
153
|
```
|
162
154
|
|
163
155
|
## Plugins for common setups
|
@@ -175,9 +167,9 @@ setup :s3
|
|
175
167
|
|
176
168
|
### Defining custom plugins
|
177
169
|
|
178
|
-
Steps inherit from `Stairs::Step`, have a title,
|
179
|
-
implement the `run` method. See those included and in the
|
180
|
-
extension gems for examples.
|
170
|
+
Steps inherit from `Stairs::Step` and live in `Stairs::Steps`, have a title,
|
171
|
+
description, and implement the `run` method. See those included and in the
|
172
|
+
various extension gems for examples.
|
181
173
|
|
182
174
|
## Contributing
|
183
175
|
|
data/Rakefile
CHANGED
data/lib/stairs/step.rb
CHANGED
@@ -1,8 +1,14 @@
|
|
1
1
|
module Stairs
|
2
2
|
class Step
|
3
|
+
attr_reader :options
|
4
|
+
|
5
|
+
def initialize(options={})
|
6
|
+
@options = options.reverse_merge required: true
|
7
|
+
end
|
8
|
+
|
3
9
|
def run!
|
4
10
|
stairs_info "== Running #{step_title}"
|
5
|
-
run
|
11
|
+
run if run_step?
|
6
12
|
stairs_info "== Completed #{step_title}"
|
7
13
|
end
|
8
14
|
|
@@ -58,7 +64,12 @@ module Stairs
|
|
58
64
|
# Set or update env var
|
59
65
|
def env(name, value)
|
60
66
|
ENV[name] = value
|
61
|
-
|
67
|
+
|
68
|
+
if value
|
69
|
+
Stairs.configuration.env_adapter.set name, value
|
70
|
+
else
|
71
|
+
Stairs.configuration.env_adapter.unset name
|
72
|
+
end
|
62
73
|
end
|
63
74
|
|
64
75
|
# Replace contents of file
|
@@ -74,15 +85,15 @@ module Stairs
|
|
74
85
|
# Embed a step where step_name is a symbol that can be resolved to a class
|
75
86
|
# in Stairs::Steps or a block is provided to be executed in an instance
|
76
87
|
# of Step
|
77
|
-
def setup(step_name, &block)
|
88
|
+
def setup(step_name, options={}, &block)
|
78
89
|
if block_given?
|
79
|
-
Step.new.tap do |step|
|
90
|
+
Step.new(options).tap do |step|
|
80
91
|
step.define_singleton_method :run, &block
|
81
92
|
step.step_title = step_name.to_s.titleize
|
82
93
|
end.run!
|
83
94
|
else
|
84
95
|
klass = "Stairs::Steps::#{step_name.to_s.camelize}".constantize
|
85
|
-
klass.new.run!
|
96
|
+
klass.new(options).run!
|
86
97
|
end
|
87
98
|
end
|
88
99
|
|
@@ -97,6 +108,11 @@ module Stairs
|
|
97
108
|
|
98
109
|
private
|
99
110
|
|
111
|
+
def run_step?
|
112
|
+
return true if options[:required]
|
113
|
+
choice "This step is optional, would you like to perform it?"
|
114
|
+
end
|
115
|
+
|
100
116
|
class Choice
|
101
117
|
# TODO: shouldn't care about case?
|
102
118
|
def initialize(question, choices=%w[Y N], &block)
|
@@ -5,7 +5,7 @@ module Stairs
|
|
5
5
|
def replace_or_append(pattern, string, filename)
|
6
6
|
if File.exists? filename
|
7
7
|
contents = File.read filename
|
8
|
-
if contents
|
8
|
+
if contents =~ pattern
|
9
9
|
contents.sub! pattern, string
|
10
10
|
write contents, filename
|
11
11
|
return
|
@@ -15,11 +15,21 @@ module Stairs
|
|
15
15
|
write_line string, filename
|
16
16
|
end
|
17
17
|
|
18
|
+
def remove(pattern, filename)
|
19
|
+
return unless File.exists? filename
|
20
|
+
|
21
|
+
contents = File.read filename
|
22
|
+
if contents =~ pattern
|
23
|
+
contents.slice!(pattern)
|
24
|
+
write contents, filename
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
18
28
|
def write_line(string, filename)
|
19
29
|
File.open filename, "a+" do |file|
|
20
30
|
# ensure file ends with newline before appending
|
21
31
|
last_line = file.each_line.reduce("") { |m, l| m = l }
|
22
|
-
file.puts "" unless last_line
|
32
|
+
file.puts "" unless last_line == "" || last_line =~ /(.*)\n/
|
23
33
|
|
24
34
|
file.puts string
|
25
35
|
end
|
data/lib/stairs/version.rb
CHANGED
@@ -35,4 +35,14 @@ describe Stairs::EnvAdapters::Dotenv do
|
|
35
35
|
subject.set(name, value)
|
36
36
|
end
|
37
37
|
end
|
38
|
-
|
38
|
+
|
39
|
+
describe "#unset" do
|
40
|
+
it "delegates to the well tested FileMutation util" do
|
41
|
+
Stairs::Util::FileMutation.should_receive(:remove).with(
|
42
|
+
Regexp.new("^SOMETHING=(.*)\n"),
|
43
|
+
".env",
|
44
|
+
)
|
45
|
+
subject.unset "SOMETHING"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -37,4 +37,14 @@ describe Stairs::EnvAdapters::Rbenv do
|
|
37
37
|
subject.set(name, value)
|
38
38
|
end
|
39
39
|
end
|
40
|
-
|
40
|
+
|
41
|
+
describe "#unset" do
|
42
|
+
it "delegates to the well tested FileMutation util" do
|
43
|
+
Stairs::Util::FileMutation.should_receive(:remove).with(
|
44
|
+
Regexp.new("^SOMETHING=(.*)\n"),
|
45
|
+
".rbenv-vars",
|
46
|
+
)
|
47
|
+
subject.unset "SOMETHING"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -37,4 +37,14 @@ describe Stairs::EnvAdapters::RVM do
|
|
37
37
|
subject.set(name, value)
|
38
38
|
end
|
39
39
|
end
|
40
|
-
|
40
|
+
|
41
|
+
describe "#unset" do
|
42
|
+
it "delegates to the well tested FileMutation util" do
|
43
|
+
Stairs::Util::FileMutation.should_receive(:remove).with(
|
44
|
+
Regexp.new("^export SOMETHING=(.*)\n"),
|
45
|
+
".rvmrc",
|
46
|
+
)
|
47
|
+
subject.unset "SOMETHING"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -42,6 +42,23 @@ describe Stairs::Step do
|
|
42
42
|
end
|
43
43
|
end
|
44
44
|
|
45
|
+
describe "options" do
|
46
|
+
describe "default options" do
|
47
|
+
it "is required" do
|
48
|
+
expect(subject.options[:required]).to be_true
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
context "with options" do
|
53
|
+
let(:options) { { something: "cool" } }
|
54
|
+
subject { anon_step.new(options) }
|
55
|
+
|
56
|
+
it "exposes options on the instance" do
|
57
|
+
expect(subject.options[:something]).to eq "cool"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
45
62
|
describe "#run!" do
|
46
63
|
before { subject.stub run: true }
|
47
64
|
before { anon_step.title "Step Name" }
|
@@ -60,6 +77,30 @@ describe Stairs::Step do
|
|
60
77
|
output = capture_stdout { subject.run! }
|
61
78
|
expect(output).to include "== Completed Step Name"
|
62
79
|
end
|
80
|
+
|
81
|
+
context "when the step is not required" do
|
82
|
+
subject { anon_step.new required: false }
|
83
|
+
before { subject.stub run: true }
|
84
|
+
|
85
|
+
it "prompts the user to decide if we should run the step" do
|
86
|
+
output = follow_prompts("Y") { subject.run! }
|
87
|
+
expect(output).to include "This step is optional, would you like to perform it? (Y/N): "
|
88
|
+
end
|
89
|
+
|
90
|
+
context "user says yes" do
|
91
|
+
it "runs the step" do
|
92
|
+
subject.should_receive :run
|
93
|
+
follow_prompts("Y") { subject.run! }
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
context "user says no" do
|
98
|
+
it "doesn't run the step" do
|
99
|
+
subject.should_not_receive :run
|
100
|
+
follow_prompts("N") { subject.run! }
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
63
104
|
end
|
64
105
|
|
65
106
|
describe "#provide" do
|
@@ -214,7 +255,7 @@ describe Stairs::Step do
|
|
214
255
|
let(:adapter) { double("adapter", set: true) }
|
215
256
|
before { Stairs.configuration.env_adapter = adapter }
|
216
257
|
|
217
|
-
it "delegates to the adapter" do
|
258
|
+
it "delegates to the adapter's set" do
|
218
259
|
adapter.should_receive(:set).with("NAME", "value")
|
219
260
|
subject.env "NAME", "value"
|
220
261
|
end
|
@@ -223,6 +264,13 @@ describe Stairs::Step do
|
|
223
264
|
ENV.should_receive(:[]=).with("NAME", "value")
|
224
265
|
subject.env "NAME", "value"
|
225
266
|
end
|
267
|
+
|
268
|
+
context "with no value" do
|
269
|
+
it "delegates to the adapter's unset" do
|
270
|
+
adapter.should_receive(:unset).with("NAME")
|
271
|
+
subject.env "NAME", nil
|
272
|
+
end
|
273
|
+
end
|
226
274
|
end
|
227
275
|
|
228
276
|
describe "#write" do
|
@@ -272,6 +320,16 @@ describe Stairs::Step do
|
|
272
320
|
mock_step_class.any_instance.should_receive(:run!)
|
273
321
|
subject.setup :mock_step
|
274
322
|
end
|
323
|
+
|
324
|
+
context "with options" do
|
325
|
+
let(:options) { { something: "cool" } }
|
326
|
+
before { mock_step_class.any_instance.stub run!: true }
|
327
|
+
|
328
|
+
it "passes options to the step" do
|
329
|
+
mock_step_class.should_receive(:new).with(options).and_call_original
|
330
|
+
subject.setup :mock_step, options
|
331
|
+
end
|
332
|
+
end
|
275
333
|
end
|
276
334
|
|
277
335
|
context "with a block" do
|
@@ -288,6 +346,18 @@ describe Stairs::Step do
|
|
288
346
|
output = capture_stdout { call_method }
|
289
347
|
expect(output).to include "I'm running in Stairs::Step"
|
290
348
|
end
|
349
|
+
|
350
|
+
context "with options" do
|
351
|
+
let(:options) { { something: "cool" } }
|
352
|
+
before { described_class.any_instance.stub run!: true }
|
353
|
+
|
354
|
+
it "passes options to the step" do
|
355
|
+
instance = subject # Initialize class before asserting against #new
|
356
|
+
|
357
|
+
described_class.should_receive(:new).with(options).and_call_original
|
358
|
+
subject.setup(:custom_step, options) { true }
|
359
|
+
end
|
360
|
+
end
|
291
361
|
end
|
292
362
|
end
|
293
|
-
end
|
363
|
+
end
|
@@ -35,6 +35,41 @@ describe Stairs::Util::FileMutation do
|
|
35
35
|
end
|
36
36
|
end
|
37
37
|
|
38
|
+
describe ".remove" do
|
39
|
+
before do
|
40
|
+
File.open(filename, "w") do |file|
|
41
|
+
file.write "VAR_1=hey\nVAR_2=hello\nVAR_3=hi\n"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
context "when the pattern exists in file" do
|
46
|
+
it "removes the match from the file" do
|
47
|
+
subject.remove /^VAR_2=(.*)\n/, filename
|
48
|
+
expect(File.read(filename)).to eq "VAR_1=hey\nVAR_3=hi\n"
|
49
|
+
end
|
50
|
+
|
51
|
+
context "and is the last line without a trailing newline" do
|
52
|
+
before do
|
53
|
+
File.open(filename, "w+") do |file|
|
54
|
+
file.write "VAR_1=hey\nVAR_2=hello\nVAR_3=hi"
|
55
|
+
end
|
56
|
+
|
57
|
+
it "still matches and removes it" do
|
58
|
+
subject.remove /^VAR_3=(.*)\n/, filename
|
59
|
+
expect(File.read(filename)).to eq "VAR_1=hey\nVAR_2=hello\n"
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
context "when the pattern does not exist in file" do
|
66
|
+
it "leaves the file untouched" do
|
67
|
+
subject.remove /^VAR_2=(.*)\n/, filename
|
68
|
+
expect(File.read(filename)).to eq "VAR_1=hey\nVAR_3=hi\n"
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
38
73
|
describe ".write_line" do
|
39
74
|
before do
|
40
75
|
File.open(filename, "w") do |file|
|
@@ -51,9 +86,7 @@ describe Stairs::Util::FileMutation do
|
|
51
86
|
|
52
87
|
context "when there is no newline at the bottom" do
|
53
88
|
before do
|
54
|
-
File.
|
55
|
-
|
56
|
-
File.open(filename, "w") do |file|
|
89
|
+
File.open(filename, "w+") do |file|
|
57
90
|
file.write("line1\nline2")
|
58
91
|
end
|
59
92
|
end
|
@@ -63,6 +96,19 @@ describe Stairs::Util::FileMutation do
|
|
63
96
|
expect(File.read(filename)).to eq "line1\nline2\nline3\n"
|
64
97
|
end
|
65
98
|
end
|
99
|
+
|
100
|
+
context "when the file is empty" do
|
101
|
+
before do
|
102
|
+
File.open filename, "w+" do |file|
|
103
|
+
file.write ""
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
it "writes to the first line of the line" do
|
108
|
+
described_class.write_line "line1", filename
|
109
|
+
expect(File.read(filename)).to eq "line1\n"
|
110
|
+
end
|
111
|
+
end
|
66
112
|
end
|
67
113
|
|
68
114
|
describe ".write" do
|
@@ -95,4 +141,4 @@ describe Stairs::Util::FileMutation do
|
|
95
141
|
end
|
96
142
|
end
|
97
143
|
end
|
98
|
-
end
|
144
|
+
end
|