stairs 0.6.1 → 0.7.0
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 +0 -4
- data/README.md +60 -5
- data/bin/stairs +11 -1
- data/lib/stairs/runner.rb +9 -1
- data/lib/stairs/script.rb +4 -3
- data/lib/stairs/step.rb +11 -4
- data/lib/stairs/tasks.rb +1 -1
- data/lib/stairs/util/file_mutation.rb +1 -1
- data/lib/stairs/version.rb +1 -1
- data/spec/lib/stairs/runner_spec.rb +14 -2
- data/spec/lib/stairs/script_spec.rb +15 -2
- data/spec/lib/stairs/step_spec.rb +68 -15
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d00a852f658b8f3191f7ae6c5f7edf56823ecc38
|
4
|
+
data.tar.gz: 424bc1126fe1cb6757b735a3b72a95032e225518
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4b7faabcf3e3168c77a3e3afb2d9da5d1cdb020ab71dd4f8a3a90870e4605b39ef37f496e5243e05fb26f4b7cb53a0d6af87d6ea939474892efeb64faefbf59e
|
7
|
+
data.tar.gz: 53e273c0bf8ed61bc395cae6b366e9d9e32f12f54741f021dbf3dff5d6a645ae93e7d092156a079746d46abee344fea28b87622247281b330f5cd8359283b427
|
data/.rubocop.yml
CHANGED
data/README.md
CHANGED
@@ -20,6 +20,21 @@ interactive prompts for everything else.
|
|
20
20
|
[](https://travis-ci.org/patbenatar/stairs)
|
21
21
|
[](https://codeclimate.com/github/patbenatar/stairs)
|
22
22
|
|
23
|
+
## Table of Contents
|
24
|
+
|
25
|
+
* [Setup](#setup)
|
26
|
+
* [Running Scripts](#running-scripts)
|
27
|
+
* [Command Line Utility](#advanced)
|
28
|
+
* [Defining Scripts](#defining-scripts)
|
29
|
+
* [Collecting Input](#collecting-values)
|
30
|
+
* [Asking Questions](#asking-questions)
|
31
|
+
* [Setting ENV vars](#setting-env-vars)
|
32
|
+
* [Writing to Files](#writing-files)
|
33
|
+
* [Miscellaneous](#misc-helpers)
|
34
|
+
* [Steps](#steps)
|
35
|
+
* [Groups](#groups)
|
36
|
+
* [Plugins](#plugins)
|
37
|
+
|
23
38
|
## Setup
|
24
39
|
|
25
40
|
### Rails
|
@@ -42,7 +57,7 @@ Same as above, but you'll have to manually add the Stairs Rake tasks to your
|
|
42
57
|
require "stairs/tasks"
|
43
58
|
```
|
44
59
|
|
45
|
-
##
|
60
|
+
## Running Scripts
|
46
61
|
|
47
62
|
### Basic
|
48
63
|
|
@@ -60,6 +75,7 @@ If you want more control, use the `stairs` command line utility:
|
|
60
75
|
$ stairs --help
|
61
76
|
Usage: stairs [options]
|
62
77
|
--use-defaults Use defaults when available
|
78
|
+
-g, --groups GROUPS Specify groups to run. e.g. init,reset
|
63
79
|
```
|
64
80
|
|
65
81
|
## Defining scripts
|
@@ -99,8 +115,9 @@ end
|
|
99
115
|
dinner = choice "Meat or vegetables?", ["Meat", "Vegetables"]
|
100
116
|
```
|
101
117
|
|
102
|
-
### Setting
|
103
|
-
Stairs currently supports writing environment variables for rbenv-vars, RVM, and
|
118
|
+
### Setting ENV vars
|
119
|
+
Stairs currently supports writing environment variables for rbenv-vars, RVM, and
|
120
|
+
dotenv.
|
104
121
|
|
105
122
|
```ruby
|
106
123
|
env "NAME", value
|
@@ -124,7 +141,9 @@ Display a message when setup completes
|
|
124
141
|
finish "Now that you're done, go have a drink!"
|
125
142
|
```
|
126
143
|
|
127
|
-
###
|
144
|
+
### Steps
|
145
|
+
|
146
|
+
Group related setup procedures into named steps using `setup`:
|
128
147
|
|
129
148
|
```ruby
|
130
149
|
setup :a_cool_service do
|
@@ -133,12 +152,48 @@ end
|
|
133
152
|
```
|
134
153
|
|
135
154
|
#### Using predefined steps (aka plugins)
|
155
|
+
|
136
156
|
```ruby
|
137
157
|
setup :s3
|
138
158
|
setup :facebook, required: false
|
139
159
|
```
|
140
160
|
|
141
|
-
|
161
|
+
[Available Plugins](#plugins)
|
162
|
+
|
163
|
+
### Groups
|
164
|
+
|
165
|
+
Stairs supports organizing your script into groups in a way similar to what you
|
166
|
+
may be used to with Bundler. With groups you can target specific steps to run
|
167
|
+
for different use cases (see `-g` option in the [command line utility](#advanced)).
|
168
|
+
|
169
|
+
Anything outside of a group will always be executed. Anything within a group
|
170
|
+
will only be executed when its group is run. By default, Stairs runs the `newb`
|
171
|
+
group with `$ rake newb`.
|
172
|
+
|
173
|
+
For example, you may want to run different steps on a brand new setup than you
|
174
|
+
would when resetting an existing setup:
|
175
|
+
|
176
|
+
```ruby
|
177
|
+
group :newb do
|
178
|
+
setup :s3
|
179
|
+
end
|
180
|
+
|
181
|
+
group :newb, :reset do
|
182
|
+
setup :balanced
|
183
|
+
rake "db:setup"
|
184
|
+
end
|
185
|
+
```
|
186
|
+
|
187
|
+
And then run your reset like so:
|
188
|
+
|
189
|
+
```bash
|
190
|
+
$ stairs -g reset
|
191
|
+
```
|
192
|
+
|
193
|
+
## Plugins
|
194
|
+
|
195
|
+
Many projects share dependencies. Plugins are predefined setup steps for common
|
196
|
+
use cases.
|
142
197
|
|
143
198
|
Some steps support options. Options are specified as a hash like so:
|
144
199
|
|
data/bin/stairs
CHANGED
@@ -3,6 +3,8 @@
|
|
3
3
|
require "optparse"
|
4
4
|
require "stairs"
|
5
5
|
|
6
|
+
groups = nil
|
7
|
+
|
6
8
|
Stairs.configure do |config|
|
7
9
|
OptionParser.new do |options|
|
8
10
|
options.banner = "Usage: stairs [options]"
|
@@ -10,6 +12,14 @@ Stairs.configure do |config|
|
|
10
12
|
options.on("--use-defaults", "Use defaults when available") do |value|
|
11
13
|
config.use_defaults = value
|
12
14
|
end
|
15
|
+
|
16
|
+
options.on(
|
17
|
+
"-g",
|
18
|
+
"--groups GROUPS",
|
19
|
+
"Specify groups to run. e.g. init,reset"
|
20
|
+
) do |value|
|
21
|
+
groups = value.split(",").map { |g| g.to_sym } if value
|
22
|
+
end
|
13
23
|
end.parse!
|
14
24
|
end
|
15
25
|
|
@@ -17,4 +27,4 @@ end
|
|
17
27
|
rails_application = File.expand_path("./config/application.rb")
|
18
28
|
require rails_application if File.exists?(rails_application)
|
19
29
|
|
20
|
-
Stairs::Runner.new.run!
|
30
|
+
Stairs::Runner.new(groups).run!
|
data/lib/stairs/runner.rb
CHANGED
@@ -1,8 +1,16 @@
|
|
1
1
|
module Stairs
|
2
2
|
class Runner
|
3
|
+
def initialize(groups=nil)
|
4
|
+
@groups = groups
|
5
|
+
end
|
6
|
+
|
3
7
|
def run!
|
4
8
|
Stairs::InteractiveConfiguration.new.run!
|
5
|
-
Stairs::Script.new("setup.rb").run!
|
9
|
+
Stairs::Script.new("setup.rb", groups).run!
|
6
10
|
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
attr_reader :groups
|
7
15
|
end
|
8
16
|
end
|
data/lib/stairs/script.rb
CHANGED
@@ -1,8 +1,9 @@
|
|
1
1
|
module Stairs
|
2
2
|
class Script
|
3
|
-
def initialize(filename)
|
3
|
+
def initialize(filename, groups)
|
4
4
|
@filename = filename
|
5
5
|
@script = File.read(@filename)
|
6
|
+
@groups = groups
|
6
7
|
end
|
7
8
|
|
8
9
|
def run!
|
@@ -13,9 +14,9 @@ module Stairs
|
|
13
14
|
private
|
14
15
|
|
15
16
|
def run
|
16
|
-
Step.new.instance_eval(script)
|
17
|
+
Step.new(groups).instance_eval(script)
|
17
18
|
end
|
18
19
|
|
19
|
-
attr_reader :script, :filename
|
20
|
+
attr_reader :script, :filename, :groups
|
20
21
|
end
|
21
22
|
end
|
data/lib/stairs/step.rb
CHANGED
@@ -1,9 +1,12 @@
|
|
1
1
|
module Stairs
|
2
2
|
class Step
|
3
|
-
attr_reader :options
|
3
|
+
attr_reader :options, :groups
|
4
4
|
|
5
|
-
def initialize(
|
5
|
+
def initialize(*args)
|
6
|
+
options = args.extract_options!
|
6
7
|
@options = options.reverse_merge required: true
|
8
|
+
|
9
|
+
@groups = args.first
|
7
10
|
end
|
8
11
|
|
9
12
|
def run!
|
@@ -85,16 +88,20 @@ module Stairs
|
|
85
88
|
# of Step
|
86
89
|
def setup(step_name, options={}, &block)
|
87
90
|
if block_given?
|
88
|
-
Step.new(options).tap do |step|
|
91
|
+
Step.new(groups, options).tap do |step|
|
89
92
|
step.define_singleton_method :run, &block
|
90
93
|
step.step_title = step_name.to_s.titleize
|
91
94
|
end.run!
|
92
95
|
else
|
93
96
|
klass = "Stairs::Steps::#{step_name.to_s.camelize}".constantize
|
94
|
-
klass.new(options).run!
|
97
|
+
klass.new(groups, options).run!
|
95
98
|
end
|
96
99
|
end
|
97
100
|
|
101
|
+
def group(*names)
|
102
|
+
yield if !groups || (names & groups).any?
|
103
|
+
end
|
104
|
+
|
98
105
|
def finish(message)
|
99
106
|
puts "== All done!".green
|
100
107
|
puts message.green
|
data/lib/stairs/tasks.rb
CHANGED
@@ -28,7 +28,7 @@ module Stairs
|
|
28
28
|
def write_line(string, filename)
|
29
29
|
File.open filename, "a+" do |file|
|
30
30
|
# ensure file ends with newline before appending
|
31
|
-
last_line = file.each_line.reduce("") { |m, l|
|
31
|
+
last_line = file.each_line.reduce("") { |m, l| l }
|
32
32
|
file.puts "" unless last_line == "" || last_line =~ /(.*)\n/
|
33
33
|
|
34
34
|
file.puts string
|
data/lib/stairs/version.rb
CHANGED
@@ -1,6 +1,9 @@
|
|
1
1
|
require "spec_helper"
|
2
2
|
|
3
3
|
describe Stairs::Runner do
|
4
|
+
let(:groups) { nil }
|
5
|
+
let(:subject) { described_class.new(groups) }
|
6
|
+
|
4
7
|
describe "#run!" do
|
5
8
|
let(:script_double) { double("script", run!: true) }
|
6
9
|
|
@@ -16,11 +19,20 @@ describe Stairs::Runner do
|
|
16
19
|
subject.run!
|
17
20
|
end
|
18
21
|
|
19
|
-
it "runs the setup.rb script" do
|
20
|
-
Stairs::Script.should_receive(:new).with("setup.rb").and_return(script_double)
|
22
|
+
it "runs all groups in the setup.rb script" do
|
23
|
+
Stairs::Script.should_receive(:new).with("setup.rb", groups).and_return(script_double)
|
21
24
|
script_double.should_receive(:run!)
|
22
25
|
|
23
26
|
subject.run!
|
24
27
|
end
|
28
|
+
|
29
|
+
context "with groups provided" do
|
30
|
+
let(:groups) { [:reset] }
|
31
|
+
|
32
|
+
it "passes the specified groups to the script" do
|
33
|
+
Stairs::Script.should_receive(:new).with("setup.rb", groups)
|
34
|
+
subject.run!
|
35
|
+
end
|
36
|
+
end
|
25
37
|
end
|
26
38
|
end
|
@@ -2,7 +2,9 @@ require "spec_helper"
|
|
2
2
|
|
3
3
|
describe Stairs::Script do
|
4
4
|
let(:filename) { "setup.rb" }
|
5
|
-
|
5
|
+
let(:groups) { [:reset] }
|
6
|
+
subject { described_class.new(filename, groups) }
|
7
|
+
|
6
8
|
|
7
9
|
context "with a script present" do
|
8
10
|
before do
|
@@ -13,12 +15,23 @@ describe Stairs::Script do
|
|
13
15
|
|
14
16
|
after { File.delete(filename) }
|
15
17
|
|
18
|
+
describe "initialize" do
|
19
|
+
it "receives groups" do
|
20
|
+
expect { described_class.new(filename, groups) }.not_to raise_error
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
16
24
|
describe "#run!" do
|
17
25
|
it "outputs running message" do
|
18
26
|
output = capture_stdout { subject.run! }
|
19
27
|
expect(output).to include "= Running script setup.rb"
|
20
28
|
end
|
21
29
|
|
30
|
+
it "passes groups to the new instance of Step" do
|
31
|
+
Stairs::Step.should_receive(:new).with(groups)
|
32
|
+
subject.run!
|
33
|
+
end
|
34
|
+
|
22
35
|
it "evaluates the script in the context of an instance of Step" do
|
23
36
|
# because our test setup.rb only contains `self.class` we can check
|
24
37
|
# this way:
|
@@ -26,4 +39,4 @@ describe Stairs::Script do
|
|
26
39
|
end
|
27
40
|
end
|
28
41
|
end
|
29
|
-
end
|
42
|
+
end
|
@@ -2,7 +2,8 @@ require "spec_helper"
|
|
2
2
|
|
3
3
|
describe Stairs::Step do
|
4
4
|
let(:anon_step) { Class.new(described_class) }
|
5
|
-
|
5
|
+
let(:groups) { nil }
|
6
|
+
subject { anon_step.new(groups) }
|
6
7
|
|
7
8
|
describe "metadata" do
|
8
9
|
describe "step_title" do
|
@@ -42,21 +43,27 @@ describe Stairs::Step do
|
|
42
43
|
end
|
43
44
|
end
|
44
45
|
|
45
|
-
describe "
|
46
|
-
describe "
|
47
|
-
|
48
|
-
|
46
|
+
describe "#initialize" do
|
47
|
+
describe "options" do
|
48
|
+
describe "default options" do
|
49
|
+
it "is required" do
|
50
|
+
expect(subject.options[:required]).to be_true
|
51
|
+
end
|
49
52
|
end
|
50
|
-
end
|
51
53
|
|
52
|
-
|
53
|
-
|
54
|
-
|
54
|
+
context "with options" do
|
55
|
+
let(:options) { { something: "cool" } }
|
56
|
+
subject { anon_step.new(options) }
|
55
57
|
|
56
|
-
|
57
|
-
|
58
|
+
it "exposes options on the instance" do
|
59
|
+
expect(subject.options[:something]).to eq "cool"
|
60
|
+
end
|
58
61
|
end
|
59
62
|
end
|
63
|
+
|
64
|
+
it "exposes supplied groups on the instance" do
|
65
|
+
expect(subject.groups).to eq groups
|
66
|
+
end
|
60
67
|
end
|
61
68
|
|
62
69
|
describe "#run!" do
|
@@ -314,18 +321,23 @@ describe Stairs::Step do
|
|
314
321
|
|
315
322
|
context "with a valid step_name" do
|
316
323
|
let!(:mock_step_class) { Stairs::Steps::MockStep = Class.new(Stairs::Step) }
|
324
|
+
before { mock_step_class.any_instance.stub run!: true }
|
317
325
|
|
318
326
|
it "instantiates and runs the step" do
|
319
327
|
mock_step_class.any_instance.should_receive(:run!)
|
320
328
|
subject.setup :mock_step
|
321
329
|
end
|
322
330
|
|
331
|
+
it "passes groups to the step" do
|
332
|
+
mock_step_class.should_receive(:new).with(groups, {}).and_call_original
|
333
|
+
subject.setup :mock_step
|
334
|
+
end
|
335
|
+
|
323
336
|
context "with options" do
|
324
337
|
let(:options) { { something: "cool" } }
|
325
|
-
before { mock_step_class.any_instance.stub run!: true }
|
326
338
|
|
327
339
|
it "passes options to the step" do
|
328
|
-
mock_step_class.should_receive(:new).with(options).and_call_original
|
340
|
+
mock_step_class.should_receive(:new).with(groups, options).and_call_original
|
329
341
|
subject.setup :mock_step, options
|
330
342
|
end
|
331
343
|
end
|
@@ -336,6 +348,12 @@ describe Stairs::Step do
|
|
336
348
|
subject.setup(:custom_step) { puts "I'm running in #{self.class}" }
|
337
349
|
end
|
338
350
|
|
351
|
+
# Initialize primary subject before asserting against #new for the Step
|
352
|
+
# it initializes
|
353
|
+
def initialize_primary_subject
|
354
|
+
instance = subject
|
355
|
+
end
|
356
|
+
|
339
357
|
it "sets the new step's title to a titleized version of step_name" do
|
340
358
|
output = capture_stdout { call_method }
|
341
359
|
expect(output).to include "Custom Step"
|
@@ -346,17 +364,52 @@ describe Stairs::Step do
|
|
346
364
|
expect(output).to include "I'm running in Stairs::Step"
|
347
365
|
end
|
348
366
|
|
367
|
+
it "passes groups to the step" do
|
368
|
+
initialize_primary_subject
|
369
|
+
|
370
|
+
described_class.should_receive(:new).with(groups, {}).and_call_original
|
371
|
+
call_method
|
372
|
+
end
|
373
|
+
|
349
374
|
context "with options" do
|
350
375
|
let(:options) { { something: "cool" } }
|
351
376
|
before { described_class.any_instance.stub run!: true }
|
352
377
|
|
353
378
|
it "passes options to the step" do
|
354
|
-
|
379
|
+
initialize_primary_subject
|
355
380
|
|
356
|
-
described_class.should_receive(:new).with(options).and_call_original
|
381
|
+
described_class.should_receive(:new).with(groups, options).and_call_original
|
357
382
|
subject.setup(:custom_step, options) { true }
|
358
383
|
end
|
359
384
|
end
|
360
385
|
end
|
361
386
|
end
|
387
|
+
|
388
|
+
describe "#group" do
|
389
|
+
let(:name) { :reset }
|
390
|
+
|
391
|
+
context "when the name is in the groups to run" do
|
392
|
+
let(:groups) { [:reset, :init] }
|
393
|
+
|
394
|
+
it "calls the supplied block" do
|
395
|
+
expect { |b| subject.group(name, &b) }.to yield_control
|
396
|
+
end
|
397
|
+
end
|
398
|
+
|
399
|
+
context "when the name is not in the groups to run" do
|
400
|
+
let(:groups) { [:init] }
|
401
|
+
|
402
|
+
it "does not call the supplied block" do
|
403
|
+
expect { |b| subject.group(name, &b) }.not_to yield_control
|
404
|
+
end
|
405
|
+
|
406
|
+
context "but no groups to run are provided" do
|
407
|
+
let(:groups) { nil }
|
408
|
+
|
409
|
+
it "calls the supplied block" do
|
410
|
+
expect { |b| subject.group(name, &b) }.to yield_control
|
411
|
+
end
|
412
|
+
end
|
413
|
+
end
|
414
|
+
end
|
362
415
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: stairs
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- patbenatar
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-
|
11
|
+
date: 2013-12-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|