stack_master 0.14.0 → 0.15.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +31 -0
- data/features/apply.feature +11 -11
- data/features/apply_with_s3.feature +1 -1
- data/features/delete.feature +1 -1
- data/features/events.feature +1 -1
- data/features/region_aliases.feature +1 -1
- data/lib/stack_master/config.rb +3 -0
- data/lib/stack_master/stack.rb +1 -1
- data/lib/stack_master/stack_definition.rb +8 -7
- data/lib/stack_master/template_compiler.rb +2 -2
- data/lib/stack_master/template_compilers/cfndsl.rb +2 -2
- data/lib/stack_master/template_compilers/json.rb +2 -2
- data/lib/stack_master/template_compilers/sparkle_formation.rb +8 -3
- data/lib/stack_master/template_compilers/yaml.rb +1 -1
- data/lib/stack_master/validator.rb +1 -1
- data/lib/stack_master/version.rb +1 -1
- data/spec/stack_master/stack_spec.rb +1 -1
- data/spec/stack_master/template_compiler_spec.rb +8 -2
- data/spec/stack_master/template_compilers/sparkle_formation_spec.rb +17 -2
- data/stack_master.gemspec +1 -1
- metadata +7 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 44ae5dfa519051496de9c2707df2441f8a0a9115
|
4
|
+
data.tar.gz: 8f2065b8c59a2a2399f0398a0dc6a8541d864287
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 50f3472de66c642581c80398f5430c9fcb596f5b973582834a65bfbca589265be7e2951a584e2459f42f2aa9fcdf1fd2c9fd9bfd07dca1397e8007d67641c6f4
|
7
|
+
data.tar.gz: 2a2e5221add3e4aa4c3910de36f73d26eb551d63b3272526e5329124d386feccc8566bcbe593a4e49833e8996c7835ab1d577b4e72d70326176fbc5242f45c1e
|
data/README.md
CHANGED
@@ -390,6 +390,37 @@ container_definitions array!(
|
|
390
390
|
)
|
391
391
|
```
|
392
392
|
|
393
|
+
## Compiler Options & Alternate Template Directories
|
394
|
+
|
395
|
+
StackMaster allows you to separate your stack definitions and parameters from your templates by way of a `template_dir` key in your stack_master.yml.
|
396
|
+
You can also pass compiler-specific options to the template compiler to further customize SparkleFormation or CfnDsl's behavior. Combining the 2 lets you move your SFN templates away from your stack definitions. For example, if your project is laid out as:
|
397
|
+
|
398
|
+
```
|
399
|
+
project-root
|
400
|
+
|-- envs
|
401
|
+
|-- env-1
|
402
|
+
|-- stack_master.yml
|
403
|
+
|-- env-2
|
404
|
+
|-- stack_master.yml
|
405
|
+
|-- sparkle
|
406
|
+
|-- templates
|
407
|
+
|-- my-stack.rb
|
408
|
+
```
|
409
|
+
|
410
|
+
Your env-1/stack_master.yml files can reference common templates by setting:
|
411
|
+
|
412
|
+
```
|
413
|
+
template_dir: ../../sparkle/templates
|
414
|
+
stack_defaults:
|
415
|
+
compiler_options:
|
416
|
+
sparkle_path: ../../sparkle
|
417
|
+
|
418
|
+
stacks:
|
419
|
+
us-east-1:
|
420
|
+
my-stack:
|
421
|
+
template: my-stack.rb
|
422
|
+
```
|
423
|
+
|
393
424
|
## Commands
|
394
425
|
|
395
426
|
```bash
|
data/features/apply.feature
CHANGED
@@ -78,7 +78,7 @@ Feature: Apply command
|
|
78
78
|
| + "Vpc": { |
|
79
79
|
| Parameters diff: |
|
80
80
|
| KeyName: my-key |
|
81
|
-
And the output should match /2020-10-29 00:00:00
|
81
|
+
And the output should match /2020-10-29 00:00:00 (\+|\-)[0-9]{4} myapp-vpc AWS::CloudFormation::Stack CREATE_COMPLETE/
|
82
82
|
Then the exit status should be 0
|
83
83
|
|
84
84
|
Scenario: Run apply and don't create the stack
|
@@ -90,7 +90,7 @@ Feature: Apply command
|
|
90
90
|
| Parameters diff: |
|
91
91
|
| KeyName: my-key |
|
92
92
|
| aborted |
|
93
|
-
And the output should not match /2020-10-29 00:00:00
|
93
|
+
And the output should not match /2020-10-29 00:00:00 (\+|\-)[0-9]{4} myapp-vpc AWS::CloudFormation::Stack CREATE_COMPLETE/
|
94
94
|
Then the exit status should be 0
|
95
95
|
|
96
96
|
Scenario: Run apply with region only and create 2 stacks
|
@@ -120,11 +120,11 @@ Feature: Apply command
|
|
120
120
|
| + "Vpc": { |
|
121
121
|
| Parameters diff: |
|
122
122
|
| KeyName: my-key |
|
123
|
-
And the output should match /2020-10-29 00:00:00
|
124
|
-
And the output should match /2020-10-29 00:00:00
|
123
|
+
And the output should match /2020-10-29 00:00:00 (\+|\-)[0-9]{4} myapp-vpc AWS::CloudFormation::Stack CREATE_COMPLETE/
|
124
|
+
And the output should match /2020-10-29 00:00:00 (\+|\-)[0-9]{4} myapp-web AWS::CloudFormation::Stack CREATE_COMPLETE/
|
125
125
|
Then the exit status should be 0
|
126
|
-
And the output should match /2020-10-29 00:00:00
|
127
|
-
And the output should match /2020-10-29 00:00:00
|
126
|
+
And the output should match /2020-10-29 00:00:00 (\+|\-)[0-9]{4} myapp-vpc AWS::CloudFormation::Stack CREATE_COMPLETE/
|
127
|
+
And the output should match /2020-10-29 00:00:00 (\+|\-)[0-9]{4} myapp-web AWS::CloudFormation::Stack CREATE_COMPLETE/
|
128
128
|
Then the exit status should be 0
|
129
129
|
|
130
130
|
Scenario: Create stack with --changed
|
@@ -154,11 +154,11 @@ Feature: Apply command
|
|
154
154
|
| + "Vpc": { |
|
155
155
|
| Parameters diff: |
|
156
156
|
| KeyName: my-key |
|
157
|
-
And the output should match /2020-10-29 00:00:00
|
158
|
-
And the output should match /2020-10-29 00:00:00
|
157
|
+
And the output should match /2020-10-29 00:00:00 (\+|\-)[0-9]{4} myapp-vpc AWS::CloudFormation::Stack CREATE_COMPLETE/
|
158
|
+
And the output should match /2020-10-29 00:00:00 (\+|\-)[0-9]{4} myapp-web AWS::CloudFormation::Stack CREATE_COMPLETE/
|
159
159
|
Then the exit status should be 0
|
160
|
-
And the output should match /2020-10-29 00:00:00
|
161
|
-
And the output should match /2020-10-29 00:00:00
|
160
|
+
And the output should match /2020-10-29 00:00:00 (\+|\-)[0-9]{4} myapp-vpc AWS::CloudFormation::Stack CREATE_COMPLETE/
|
161
|
+
And the output should match /2020-10-29 00:00:00 (\+|\-)[0-9]{4} myapp-web AWS::CloudFormation::Stack CREATE_COMPLETE/
|
162
162
|
Then the exit status should be 0
|
163
163
|
|
164
164
|
Scenario: Update a stack
|
@@ -324,7 +324,7 @@ Feature: Apply command
|
|
324
324
|
| + "TestSg": { |
|
325
325
|
| Parameters diff: |
|
326
326
|
| VpcId: vpc-xxxxxx |
|
327
|
-
And the output should match /2020-10-29 00:00:00
|
327
|
+
And the output should match /2020-10-29 00:00:00 (\+|\-)[0-9]{4} myapp-web AWS::CloudFormation::Stack CREATE_COMPLETE/
|
328
328
|
Then the exit status should be 0
|
329
329
|
|
330
330
|
Scenario: Create a stack with a notification ARN and a stack update policy
|
@@ -68,7 +68,7 @@ Feature: Apply command
|
|
68
68
|
| + "Vpc": { |
|
69
69
|
| Parameters diff: |
|
70
70
|
| KeyName: my-key |
|
71
|
-
And the output should match /2020-10-29 00:00:00
|
71
|
+
And the output should match /2020-10-29 00:00:00 (\+|\-)[0-9]{4} myapp-vpc AWS::CloudFormation::Stack CREATE_COMPLETE/
|
72
72
|
And an S3 file in bucket "my-bucket" with key "cfn_templates/my-app/myapp_vpc.json" exists with content:
|
73
73
|
"""
|
74
74
|
{
|
data/features/delete.feature
CHANGED
@@ -8,7 +8,7 @@ Feature: Delete command
|
|
8
8
|
| stack_id | event_id | stack_name | logical_resource_id | resource_status | resource_type | timestamp |
|
9
9
|
| 1 | 1 | myapp-vpc | myapp-vpc | DELETE_COMPLETE | AWS::CloudFormation::Stack | 2020-10-29 00:00:00 |
|
10
10
|
When I run `stack_master delete us-east-1 myapp-vpc --trace`
|
11
|
-
And the output should match /2020-10-29 00:00:00
|
11
|
+
And the output should match /2020-10-29 00:00:00 (\+|\-)[0-9]{4} myapp-vpc AWS::CloudFormation::Stack DELETE_COMPLETE/
|
12
12
|
Then the exit status should be 0
|
13
13
|
|
14
14
|
Scenario: Run a delete command on a stack that does not exists
|
data/features/events.feature
CHANGED
@@ -30,4 +30,4 @@ Feature: Events command
|
|
30
30
|
| 1 | 1 | myapp-vpc | TestSg | CREATE_COMPLETE | AWS::EC2::SecurityGroup | 2020-10-29 00:00:00 |
|
31
31
|
| 1 | 1 | myapp-vpc | myapp-vpc | CREATE_COMPLETE | AWS::CloudFormation::Stack | 2020-10-29 00:00:00 |
|
32
32
|
When I run `stack_master events us-east-1 myapp-vpc --trace`
|
33
|
-
And the output should match /2020-10-29 00:00:00
|
33
|
+
And the output should match /2020-10-29 00:00:00 (\+|\-)[0-9]{4} myapp-vpc AWS::CloudFormation::Stack CREATE_COMPLETE/
|
@@ -58,5 +58,5 @@ Feature: Region aliases
|
|
58
58
|
| + "Vpc": { |
|
59
59
|
| Parameters diff: |
|
60
60
|
| KeyName: my-key |
|
61
|
-
And the output should match /2020-10-29 00:00:00
|
61
|
+
And the output should match /2020-10-29 00:00:00 (\+|\-)[0-9]{4} myapp-vpc AWS::CloudFormation::Stack CREATE_COMPLETE/
|
62
62
|
Then the exit status should be 0
|
data/lib/stack_master/config.rb
CHANGED
@@ -12,6 +12,7 @@ module StackMaster
|
|
12
12
|
|
13
13
|
attr_accessor :stacks,
|
14
14
|
:base_dir,
|
15
|
+
:template_dir,
|
15
16
|
:stack_defaults,
|
16
17
|
:region_defaults,
|
17
18
|
:region_aliases,
|
@@ -33,6 +34,7 @@ module StackMaster
|
|
33
34
|
def initialize(config, base_dir)
|
34
35
|
@config = config
|
35
36
|
@base_dir = base_dir
|
37
|
+
@template_dir = config.fetch('template_dir', nil)
|
36
38
|
@stack_defaults = config.fetch('stack_defaults', {})
|
37
39
|
@region_aliases = Utils.underscore_keys_to_hyphen(config.fetch('region_aliases', {}))
|
38
40
|
@region_to_aliases = @region_aliases.inject({}) do |hash, (key, value)|
|
@@ -108,6 +110,7 @@ module StackMaster
|
|
108
110
|
'region' => region,
|
109
111
|
'stack_name' => stack_name,
|
110
112
|
'base_dir' => @base_dir,
|
113
|
+
'template_dir' => @template_dir,
|
111
114
|
'additional_parameter_lookup_dirs' => @region_to_aliases[region])
|
112
115
|
@stacks << StackDefinition.new(stack_attributes)
|
113
116
|
end
|
data/lib/stack_master/stack.rb
CHANGED
@@ -63,7 +63,7 @@ module StackMaster
|
|
63
63
|
|
64
64
|
def self.generate(stack_definition, config)
|
65
65
|
parameter_hash = ParameterLoader.load(stack_definition.parameter_files)
|
66
|
-
template_body = TemplateCompiler.compile(config, stack_definition.template_file_path)
|
66
|
+
template_body = TemplateCompiler.compile(config, stack_definition.template_file_path, stack_definition.compiler_options)
|
67
67
|
template_format = TemplateUtils.identify_template_format(template_body)
|
68
68
|
parameters = ParameterResolver.resolve(config, stack_definition, parameter_hash)
|
69
69
|
stack_policy_body = if stack_definition.stack_policy_file_path
|
@@ -7,20 +7,24 @@ module StackMaster
|
|
7
7
|
:role_arn,
|
8
8
|
:notification_arns,
|
9
9
|
:base_dir,
|
10
|
+
:template_dir,
|
10
11
|
:secret_file,
|
11
12
|
:stack_policy_file,
|
12
13
|
:additional_parameter_lookup_dirs,
|
13
14
|
:s3,
|
14
|
-
:files
|
15
|
+
:files,
|
16
|
+
:compiler_options
|
15
17
|
|
16
18
|
include Utils::Initializable
|
17
19
|
|
18
20
|
def initialize(attributes = {})
|
19
21
|
@additional_parameter_lookup_dirs = []
|
22
|
+
@compiler_options = {}
|
20
23
|
@notification_arns = []
|
21
24
|
@s3 = {}
|
22
25
|
@files = []
|
23
26
|
super
|
27
|
+
@template_dir ||= File.join(@base_dir, 'templates')
|
24
28
|
end
|
25
29
|
|
26
30
|
def ==(other)
|
@@ -35,15 +39,12 @@ module StackMaster
|
|
35
39
|
@secret_file == other.secret_file &&
|
36
40
|
@stack_policy_file == other.stack_policy_file &&
|
37
41
|
@additional_parameter_lookup_dirs == other.additional_parameter_lookup_dirs &&
|
38
|
-
@s3 == other.s3
|
39
|
-
|
40
|
-
|
41
|
-
def template_dir
|
42
|
-
File.join(base_dir, 'templates')
|
42
|
+
@s3 == other.s3 &&
|
43
|
+
@compiler_options == other.compiler_options
|
43
44
|
end
|
44
45
|
|
45
46
|
def template_file_path
|
46
|
-
File.join(template_dir, template)
|
47
|
+
File.expand_path(File.join(template_dir, template))
|
47
48
|
end
|
48
49
|
|
49
50
|
def files_dir
|
@@ -2,10 +2,10 @@ module StackMaster
|
|
2
2
|
class TemplateCompiler
|
3
3
|
TemplateCompilationFailed = Class.new(RuntimeError)
|
4
4
|
|
5
|
-
def self.compile(config, template_file_path)
|
5
|
+
def self.compile(config, template_file_path, compiler_options = {})
|
6
6
|
compiler = template_compiler_for_file(template_file_path, config)
|
7
7
|
compiler.require_dependencies
|
8
|
-
compiler.compile(template_file_path)
|
8
|
+
compiler.compile(template_file_path, compiler_options)
|
9
9
|
rescue
|
10
10
|
raise TemplateCompilationFailed.new("Failed to compile #{template_file_path}.")
|
11
11
|
end
|
@@ -4,10 +4,10 @@ module StackMaster::TemplateCompilers
|
|
4
4
|
require 'cfndsl'
|
5
5
|
end
|
6
6
|
|
7
|
-
def self.compile(template_file_path)
|
7
|
+
def self.compile(template_file_path, compiler_options = {})
|
8
8
|
::CfnDsl.eval_file_with_extras(template_file_path).to_json
|
9
9
|
end
|
10
10
|
|
11
11
|
StackMaster::TemplateCompiler.register(:cfndsl, self)
|
12
12
|
end
|
13
|
-
end
|
13
|
+
end
|
@@ -7,7 +7,7 @@ module StackMaster::TemplateCompilers
|
|
7
7
|
require 'json'
|
8
8
|
end
|
9
9
|
|
10
|
-
def self.compile(template_file_path)
|
10
|
+
def self.compile(template_file_path, compiler_options = {})
|
11
11
|
template_body = File.read(template_file_path)
|
12
12
|
if template_body.size > MAX_TEMPLATE_SIZE
|
13
13
|
# Parse the json and rewrite compressed
|
@@ -19,4 +19,4 @@ module StackMaster::TemplateCompilers
|
|
19
19
|
|
20
20
|
StackMaster::TemplateCompiler.register(:json, self)
|
21
21
|
end
|
22
|
-
end
|
22
|
+
end
|
@@ -5,11 +5,16 @@ module StackMaster::TemplateCompilers
|
|
5
5
|
require 'stack_master/sparkle_formation/template_file'
|
6
6
|
end
|
7
7
|
|
8
|
-
def self.compile(template_file_path)
|
9
|
-
|
8
|
+
def self.compile(template_file_path, compiler_options = {})
|
9
|
+
if compiler_options["sparkle_path"]
|
10
|
+
::SparkleFormation.sparkle_path = File.expand_path(compiler_options["sparkle_path"])
|
11
|
+
else
|
12
|
+
::SparkleFormation.sparkle_path = File.dirname(template_file_path)
|
13
|
+
end
|
14
|
+
|
10
15
|
JSON.pretty_generate(::SparkleFormation.compile(template_file_path))
|
11
16
|
end
|
12
17
|
|
13
18
|
StackMaster::TemplateCompiler.register(:sparkle_formation, self)
|
14
19
|
end
|
15
|
-
end
|
20
|
+
end
|
@@ -11,7 +11,7 @@ module StackMaster
|
|
11
11
|
|
12
12
|
def perform
|
13
13
|
StackMaster.stdout.print "#{@stack_definition.stack_name}: "
|
14
|
-
template_body = TemplateCompiler.compile(@config, @stack_definition.template_file_path)
|
14
|
+
template_body = TemplateCompiler.compile(@config, @stack_definition.template_file_path, @stack_definition.compiler_options)
|
15
15
|
cf.validate_template(template_body: TemplateUtils.maybe_compressed_template_body(template_body))
|
16
16
|
StackMaster.stdout.puts "valid"
|
17
17
|
true
|
data/lib/stack_master/version.rb
CHANGED
@@ -87,7 +87,7 @@ RSpec.describe StackMaster::Stack do
|
|
87
87
|
before do
|
88
88
|
allow(StackMaster::ParameterLoader).to receive(:load).and_return(parameter_hash)
|
89
89
|
allow(StackMaster::ParameterResolver).to receive(:resolve).and_return(resolved_parameters)
|
90
|
-
allow(StackMaster::TemplateCompiler).to receive(:compile).with(config, stack_definition.template_file_path).and_return(template_body)
|
90
|
+
allow(StackMaster::TemplateCompiler).to receive(:compile).with(config, stack_definition.template_file_path, stack_definition.compiler_options).and_return(template_body)
|
91
91
|
allow(File).to receive(:read).with(stack_definition.stack_policy_file_path).and_return(stack_policy_body)
|
92
92
|
end
|
93
93
|
|
@@ -5,7 +5,7 @@ RSpec.describe StackMaster::TemplateCompiler do
|
|
5
5
|
|
6
6
|
class TestTemplateCompiler
|
7
7
|
def self.require_dependencies; end
|
8
|
-
def self.compile(template_file_path); end
|
8
|
+
def self.compile(template_file_path, compile_options); end
|
9
9
|
end
|
10
10
|
|
11
11
|
context 'when a template compiler is registered for the given file type' do
|
@@ -14,10 +14,16 @@ RSpec.describe StackMaster::TemplateCompiler do
|
|
14
14
|
}
|
15
15
|
|
16
16
|
it 'compiles the template using the relevant template compiler' do
|
17
|
-
expect(TestTemplateCompiler).to receive(:compile).with(template_file_path)
|
17
|
+
expect(TestTemplateCompiler).to receive(:compile).with(template_file_path, anything)
|
18
18
|
StackMaster::TemplateCompiler.compile(config, template_file_path)
|
19
19
|
end
|
20
20
|
|
21
|
+
it 'passes compile_options to the template compiler' do
|
22
|
+
opts = {foo: 1, bar: true, baz: "meh"}
|
23
|
+
expect(TestTemplateCompiler).to receive(:compile).with(template_file_path, opts)
|
24
|
+
StackMaster::TemplateCompiler.compile(config, template_file_path, opts)
|
25
|
+
end
|
26
|
+
|
21
27
|
context 'when template compilation fails' do
|
22
28
|
before { allow(TestTemplateCompiler).to receive(:compile).and_raise(RuntimeError) }
|
23
29
|
|
@@ -1,7 +1,7 @@
|
|
1
1
|
RSpec.describe StackMaster::TemplateCompilers::SparkleFormation do
|
2
2
|
describe '.compile' do
|
3
3
|
def compile
|
4
|
-
described_class.compile(template_file_path)
|
4
|
+
described_class.compile(template_file_path, compiler_options)
|
5
5
|
end
|
6
6
|
|
7
7
|
before do
|
@@ -9,6 +9,7 @@ RSpec.describe StackMaster::TemplateCompilers::SparkleFormation do
|
|
9
9
|
end
|
10
10
|
|
11
11
|
let(:template_file_path) { '/base_dir/templates/template.rb' }
|
12
|
+
let(:compiler_options) { {} }
|
12
13
|
|
13
14
|
it 'compiles with sparkleformation' do
|
14
15
|
expect(compile).to eq("{\n}")
|
@@ -18,5 +19,19 @@ RSpec.describe StackMaster::TemplateCompilers::SparkleFormation do
|
|
18
19
|
compile
|
19
20
|
expect(SparkleFormation.sparkle_path).to eq File.dirname(template_file_path)
|
20
21
|
end
|
22
|
+
|
23
|
+
context 'with a custom sparkle_path' do
|
24
|
+
let(:compiler_options) { { "sparkle_path" => '../foo' } }
|
25
|
+
|
26
|
+
it 'does not use the default path' do
|
27
|
+
compile
|
28
|
+
expect(SparkleFormation.sparkle_path).to_not eq File.dirname(template_file_path)
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'expands the given path' do
|
32
|
+
compile
|
33
|
+
expect(SparkleFormation.sparkle_path).to match %r{^/.+/foo}
|
34
|
+
end
|
35
|
+
end
|
21
36
|
end
|
22
|
-
end
|
37
|
+
end
|
data/stack_master.gemspec
CHANGED
@@ -33,7 +33,7 @@ Gem::Specification.new do |spec|
|
|
33
33
|
spec.add_dependency "erubis"
|
34
34
|
spec.add_dependency "colorize"
|
35
35
|
spec.add_dependency "activesupport", "~> 4.2"
|
36
|
-
spec.add_dependency "sparkle_formation"
|
36
|
+
spec.add_dependency "sparkle_formation"
|
37
37
|
spec.add_dependency "table_print"
|
38
38
|
spec.add_dependency "dotgpg"
|
39
39
|
spec.add_dependency "deep_merge"
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: stack_master
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.15.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Steve Hodgkiss
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2017-
|
12
|
+
date: 2017-04-27 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: bundler
|
@@ -211,16 +211,16 @@ dependencies:
|
|
211
211
|
name: sparkle_formation
|
212
212
|
requirement: !ruby/object:Gem::Requirement
|
213
213
|
requirements:
|
214
|
-
- - "
|
214
|
+
- - ">="
|
215
215
|
- !ruby/object:Gem::Version
|
216
|
-
version: '
|
216
|
+
version: '0'
|
217
217
|
type: :runtime
|
218
218
|
prerelease: false
|
219
219
|
version_requirements: !ruby/object:Gem::Requirement
|
220
220
|
requirements:
|
221
|
-
- - "
|
221
|
+
- - ">="
|
222
222
|
- !ruby/object:Gem::Version
|
223
|
-
version: '
|
223
|
+
version: '0'
|
224
224
|
- !ruby/object:Gem::Dependency
|
225
225
|
name: table_print
|
226
226
|
requirement: !ruby/object:Gem::Requirement
|
@@ -450,7 +450,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
450
450
|
version: '0'
|
451
451
|
requirements: []
|
452
452
|
rubyforge_project:
|
453
|
-
rubygems_version: 2.6.
|
453
|
+
rubygems_version: 2.6.11
|
454
454
|
signing_key:
|
455
455
|
specification_version: 4
|
456
456
|
summary: StackMaster is a sure-footed way of creating, updating and keeping track
|