stairs 0.4.0 → 0.4.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 +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
|