lono 0.5.2 → 1.0.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/.ruby-version +1 -1
- data/CHANGELOG.md +5 -0
- data/README.md +152 -23
- data/lib/lono.rb +1 -0
- data/lib/lono/cli.rb +3 -2
- data/lib/lono/dsl.rb +59 -12
- data/lib/lono/new.rb +4 -2
- data/lib/lono/template.rb +22 -9
- data/lib/lono/version.rb +1 -1
- data/lib/{starter_project → starter_project_json}/Gemfile +2 -1
- data/lib/{starter_project → starter_project_json}/Guardfile +0 -0
- data/lib/starter_project_json/config/lono.rb +20 -0
- data/lib/{starter_project → starter_project_json}/config/lono/api.rb +9 -9
- data/lib/{starter_project → starter_project_json}/templates/db.json.erb +1 -1
- data/lib/{starter_project → starter_project_json}/templates/partial/host_record.json.erb +0 -0
- data/lib/{starter_project → starter_project_json}/templates/partial/server.json.erb +0 -0
- data/lib/{starter_project → starter_project_json}/templates/user_data/app.sh.erb +1 -4
- data/lib/{starter_project → starter_project_json}/templates/user_data/db.sh.erb +0 -0
- data/lib/{starter_project → starter_project_json}/templates/user_data/db2.sh.erb +0 -0
- data/lib/{starter_project → starter_project_json}/templates/user_data/ruby_script.rb.erb +0 -0
- data/lib/{starter_project/templates/app.json.erb → starter_project_json/templates/web.json.erb} +1 -1
- data/lib/starter_project_yaml/Gemfile +4 -0
- data/lib/starter_project_yaml/Guardfile +12 -0
- data/lib/{starter_project → starter_project_yaml}/config/lono.rb +5 -5
- data/lib/starter_project_yaml/config/lono/api.rb +58 -0
- data/lib/starter_project_yaml/templates/db.yml.erb +148 -0
- data/lib/starter_project_yaml/templates/partial/host_record.yml.erb +14 -0
- data/lib/starter_project_yaml/templates/partial/server.yml.erb +59 -0
- data/lib/starter_project_yaml/templates/partial/user_data/bootstrap.sh.erb +5 -0
- data/lib/starter_project_yaml/templates/web.yml.erb +205 -0
- data/lono.gemspec +1 -1
- data/spec/lib/lono/dsl_spec.rb +184 -0
- data/spec/lib/lono/new_spec.rb +59 -0
- data/spec/lib/lono_spec.rb +6 -116
- data/spec/spec_helper.rb +1 -0
- metadata +42 -15
@@ -0,0 +1,205 @@
|
|
1
|
+
<% @app,@role,@env = name.sub('.yml','').split('-') -%>
|
2
|
+
---
|
3
|
+
AWSTemplateFormatVersion: '2010-09-09'
|
4
|
+
Description: <%= @app.capitalize %> Stack
|
5
|
+
Outputs:
|
6
|
+
ELBHostname:
|
7
|
+
Description: The URL of the website
|
8
|
+
Value:
|
9
|
+
Fn::Join:
|
10
|
+
- ''
|
11
|
+
- - http://
|
12
|
+
- Fn::GetAtt:
|
13
|
+
- elb
|
14
|
+
- DNSName
|
15
|
+
Parameters:
|
16
|
+
Ami:
|
17
|
+
Default: <%= @ami %>
|
18
|
+
Description: Ami id
|
19
|
+
Type: String
|
20
|
+
Application:
|
21
|
+
Default: <%= @app %>
|
22
|
+
Description: Application name
|
23
|
+
Type: String
|
24
|
+
Environment:
|
25
|
+
Default: <%= @env %>
|
26
|
+
Description: stag, prod etc
|
27
|
+
Type: String
|
28
|
+
InstanceType:
|
29
|
+
AllowedValues:
|
30
|
+
- t1.micro
|
31
|
+
- m1.small
|
32
|
+
- m1.medium
|
33
|
+
- m1.large
|
34
|
+
- m1.xlarge
|
35
|
+
- m2.xlarge
|
36
|
+
- m2.2xlarge
|
37
|
+
- m2.4xlarge
|
38
|
+
- c1.medium
|
39
|
+
- c1.xlarge
|
40
|
+
- cc1.4xlarge
|
41
|
+
- cc2.8xlarge
|
42
|
+
- cg1.4xlarge
|
43
|
+
- t2.nano
|
44
|
+
- t2.micro
|
45
|
+
- t2.small
|
46
|
+
- t2.medium
|
47
|
+
- t2.large
|
48
|
+
- t2.xlarge
|
49
|
+
- t2.2xlarge
|
50
|
+
- m4.large
|
51
|
+
- m4.xlarge
|
52
|
+
- m4.2xlarge
|
53
|
+
- m4.4xlarge
|
54
|
+
- m4.10xlarge
|
55
|
+
- m4.16xlarge
|
56
|
+
ConstraintDescription: must be a valid EC2 instance type.
|
57
|
+
Default: <%= @instance_type %>
|
58
|
+
Description: WebServer EC2 instance type
|
59
|
+
Type: String
|
60
|
+
KeyName:
|
61
|
+
Default: default
|
62
|
+
Description: The EC2 Key Pair to allow SSH access to the instances
|
63
|
+
Type: String
|
64
|
+
Role:
|
65
|
+
Default: <%= @role %>
|
66
|
+
Description: redis, psql, app, etc
|
67
|
+
Type: String
|
68
|
+
WebServerPort:
|
69
|
+
Default: '<%= @port %>'
|
70
|
+
Description: The TCP port for the Web Server
|
71
|
+
Type: Number
|
72
|
+
Resources:
|
73
|
+
CPUAlarmHigh:
|
74
|
+
Properties:
|
75
|
+
AlarmActions:
|
76
|
+
- Ref: WebServerScaleUpPolicy
|
77
|
+
AlarmDescription: Scale-up if CPU > <%= @high_threshold %>% for <%= @high_mins %>
|
78
|
+
ComparisonOperator: GreaterThanThreshold
|
79
|
+
Dimensions:
|
80
|
+
- Name: AutoScalingGroupName
|
81
|
+
Value:
|
82
|
+
Ref: WebServerGroup
|
83
|
+
EvaluationPeriods: '<%= @high_periods %>'
|
84
|
+
MetricName: CPUUtilization
|
85
|
+
Namespace: AWS/EC2
|
86
|
+
Period: '60'
|
87
|
+
Statistic: Average
|
88
|
+
Threshold: '<%= @high_threshold %>'
|
89
|
+
Type: AWS::CloudWatch::Alarm
|
90
|
+
CPUAlarmLow:
|
91
|
+
Properties:
|
92
|
+
AlarmActions:
|
93
|
+
- Ref: WebServerScaleDownPolicy
|
94
|
+
AlarmDescription: Scale-down if CPU < <%= @low_threshold %>% for 10 minutes
|
95
|
+
ComparisonOperator: LessThanThreshold
|
96
|
+
Dimensions:
|
97
|
+
- Name: AutoScalingGroupName
|
98
|
+
Value:
|
99
|
+
Ref: WebServerGroup
|
100
|
+
EvaluationPeriods: '<%= @low_periods %>'
|
101
|
+
MetricName: CPUUtilization
|
102
|
+
Namespace: AWS/EC2
|
103
|
+
Period: '60'
|
104
|
+
Statistic: Average
|
105
|
+
Threshold: '<%= @low_threshold %>'
|
106
|
+
Type: AWS::CloudWatch::Alarm
|
107
|
+
<%= partial("host_record.yml.erb", domain: "mydomain.net") %>
|
108
|
+
LaunchConfig:
|
109
|
+
Properties:
|
110
|
+
BlockDeviceMappings:
|
111
|
+
- DeviceName: "/dev/sdb"
|
112
|
+
VirtualName: ephemeral0
|
113
|
+
ImageId: !Ref Ami
|
114
|
+
InstanceType: !Ref InstanceType
|
115
|
+
KeyName:
|
116
|
+
Ref: KeyName
|
117
|
+
SecurityGroups:
|
118
|
+
- global
|
119
|
+
- Fn::Join:
|
120
|
+
- "-"
|
121
|
+
- - Ref: Application
|
122
|
+
- Ref: Environment
|
123
|
+
- Ref: ServiceSecurityGroup
|
124
|
+
UserData:
|
125
|
+
Fn::Base64: !Sub | # No more Fn::Join needed
|
126
|
+
<%= partial("user_data/bootstrap.sh.erb", {}, indent: 10) %>
|
127
|
+
Type: AWS::AutoScaling::LaunchConfiguration
|
128
|
+
ServiceSecurityGroup:
|
129
|
+
Properties:
|
130
|
+
GroupDescription: Enable SSH access and HTTP from the load balancer only
|
131
|
+
SecurityGroupIngress:
|
132
|
+
- CidrIp: 0.0.0.0/0
|
133
|
+
FromPort: '22'
|
134
|
+
IpProtocol: tcp
|
135
|
+
ToPort: '22'
|
136
|
+
- FromPort:
|
137
|
+
Ref: WebServerPort
|
138
|
+
IpProtocol: tcp
|
139
|
+
SourceSecurityGroupName:
|
140
|
+
Fn::GetAtt:
|
141
|
+
- elb
|
142
|
+
- SourceSecurityGroup.GroupName
|
143
|
+
SourceSecurityGroupOwnerId:
|
144
|
+
Fn::GetAtt:
|
145
|
+
- elb
|
146
|
+
- SourceSecurityGroup.OwnerAlias
|
147
|
+
ToPort:
|
148
|
+
Ref: WebServerPort
|
149
|
+
Type: AWS::EC2::SecurityGroup
|
150
|
+
WebServerGroup:
|
151
|
+
Properties:
|
152
|
+
AvailabilityZones: !GetAZs ""
|
153
|
+
HealthCheckGracePeriod: '300'
|
154
|
+
HealthCheckType: ELB
|
155
|
+
LaunchConfigurationName:
|
156
|
+
Ref: LaunchConfig
|
157
|
+
LoadBalancerNames:
|
158
|
+
- Ref: elb
|
159
|
+
MaxSize: '<%= @max_size %>'
|
160
|
+
MinSize: '<%= @min_size %>'
|
161
|
+
Type: AWS::AutoScaling::AutoScalingGroup
|
162
|
+
WebServerScaleDownPolicy:
|
163
|
+
Properties:
|
164
|
+
AdjustmentType: ChangeInCapacity
|
165
|
+
AutoScalingGroupName:
|
166
|
+
Ref: WebServerGroup
|
167
|
+
Cooldown: '120'
|
168
|
+
ScalingAdjustment: "<%= @down_adjustment %>"
|
169
|
+
Type: AWS::AutoScaling::ScalingPolicy
|
170
|
+
WebServerScaleUpPolicy:
|
171
|
+
Properties:
|
172
|
+
AdjustmentType: ChangeInCapacity
|
173
|
+
AutoScalingGroupName:
|
174
|
+
Ref: WebServerGroup
|
175
|
+
Cooldown: '120'
|
176
|
+
ScalingAdjustment: '<%= @up_adjustment %>'
|
177
|
+
Type: AWS::AutoScaling::ScalingPolicy
|
178
|
+
elb:
|
179
|
+
Properties:
|
180
|
+
AvailabilityZones: !GetAZs ""
|
181
|
+
HealthCheck:
|
182
|
+
HealthyThreshold: '3'
|
183
|
+
Interval: '6'
|
184
|
+
Target:
|
185
|
+
Fn::Join:
|
186
|
+
- ''
|
187
|
+
- - 'HTTP:'
|
188
|
+
- Ref: WebServerPort
|
189
|
+
- "/up/elb"
|
190
|
+
Timeout: '5'
|
191
|
+
UnhealthyThreshold: '5'
|
192
|
+
Listeners:
|
193
|
+
- InstancePort:
|
194
|
+
Ref: WebServerPort
|
195
|
+
LoadBalancerPort: '80'
|
196
|
+
Protocol: HTTP
|
197
|
+
<% if @ssl_cert %>
|
198
|
+
- InstancePort:
|
199
|
+
Ref: WebServerPort
|
200
|
+
LoadBalancerPort: '443'
|
201
|
+
PolicyNames: []
|
202
|
+
Protocol: HTTPS
|
203
|
+
SSLCertificateId: '<%= @ssl_cert %>'
|
204
|
+
<% end %>
|
205
|
+
Type: AWS::ElasticLoadBalancing::LoadBalancer
|
data/lono.gemspec
CHANGED
@@ -0,0 +1,184 @@
|
|
1
|
+
require File.expand_path("../../../spec_helper", __FILE__)
|
2
|
+
|
3
|
+
describe Lono::DSL do
|
4
|
+
before(:each) do
|
5
|
+
@project_root = File.expand_path("../../../../tmp/lono_project", __FILE__)
|
6
|
+
end
|
7
|
+
after(:each) do
|
8
|
+
FileUtils.rm_rf(@project_root) unless ENV['KEEP_TMP_PROJECT']
|
9
|
+
end
|
10
|
+
|
11
|
+
context "json starter project" do
|
12
|
+
before(:each) do
|
13
|
+
new_project = Lono::New.new(
|
14
|
+
force: true,
|
15
|
+
quiet: true,
|
16
|
+
format: 'json',
|
17
|
+
project_root: @project_root
|
18
|
+
)
|
19
|
+
new_project.run
|
20
|
+
end
|
21
|
+
|
22
|
+
it "json" do
|
23
|
+
dsl = Lono::DSL.new(
|
24
|
+
project_root: @project_root,
|
25
|
+
quiet: true
|
26
|
+
)
|
27
|
+
dsl.evaluate # run the dependent instance_eval and load_subfoler so @templates is assigned
|
28
|
+
detected_format = dsl.detect_format
|
29
|
+
expect(detected_format).to eq 'json'
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
context "yaml starter project" do
|
34
|
+
before(:each) do
|
35
|
+
new_project = Lono::New.new(
|
36
|
+
force: true,
|
37
|
+
quiet: true,
|
38
|
+
format: 'yaml',
|
39
|
+
project_root: @project_root
|
40
|
+
)
|
41
|
+
new_project.run
|
42
|
+
end
|
43
|
+
|
44
|
+
it "yaml" do
|
45
|
+
dsl = Lono::DSL.new(
|
46
|
+
project_root: @project_root,
|
47
|
+
quiet: true
|
48
|
+
)
|
49
|
+
dsl.evaluate # run the dependent instance_eval and load_subfoler so @templates is assigned
|
50
|
+
detected_format = dsl.detect_format
|
51
|
+
expect(detected_format).to eq 'yaml'
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
context "multiple format starter project" do
|
56
|
+
# it "yaml" do
|
57
|
+
# dsl = Lono::DSL.new(
|
58
|
+
# project_root: @project,
|
59
|
+
# quiet: true
|
60
|
+
# )
|
61
|
+
# detected_format = dsl.detect_format
|
62
|
+
# expect(detected_format).to eq 'yaml'
|
63
|
+
# end
|
64
|
+
end
|
65
|
+
|
66
|
+
context "json starter project" do
|
67
|
+
before(:each) do
|
68
|
+
new_project = Lono::New.new(
|
69
|
+
force: true,
|
70
|
+
quiet: true,
|
71
|
+
format: 'json',
|
72
|
+
project_root: @project_root
|
73
|
+
)
|
74
|
+
new_project.run
|
75
|
+
|
76
|
+
dsl = Lono::DSL.new(
|
77
|
+
project_root: @project_root,
|
78
|
+
quiet: true
|
79
|
+
)
|
80
|
+
dsl.run
|
81
|
+
end
|
82
|
+
|
83
|
+
it "should generate cloudformation template" do
|
84
|
+
raw = IO.read("#{@project_root}/output/api-web-prod.json")
|
85
|
+
json = JSON.load(raw)
|
86
|
+
expect(json['Description']).to eq "Api Stack"
|
87
|
+
expect(json['Mappings']['AWSRegionArch2AMI']['us-east-1']['64']).to eq 'ami-123'
|
88
|
+
end
|
89
|
+
|
90
|
+
it "should make trailing options pass to the partial helper available as instance variables" do
|
91
|
+
raw = IO.read("#{@project_root}/output/api-web-prod.json")
|
92
|
+
json = JSON.load(raw)
|
93
|
+
expect(json['Resources']['HostRecord']['Properties']['Comment']).to eq 'DNS name for mydomain.com'
|
94
|
+
end
|
95
|
+
|
96
|
+
it "should generate user data with variables" do
|
97
|
+
raw = IO.read("#{@project_root}/output/api-redis-prod.json")
|
98
|
+
json = JSON.load(raw)
|
99
|
+
expect(json['Description']).to eq "Api redis"
|
100
|
+
user_data = json['Resources']['server']['Properties']['UserData']['Fn::Base64']['Fn::Join'][1]
|
101
|
+
expect(user_data).to include("VARTEST=foo\n")
|
102
|
+
end
|
103
|
+
|
104
|
+
it "should include multiple user_data scripts" do
|
105
|
+
raw = IO.read("#{@project_root}/output/api-redis-prod.json")
|
106
|
+
json = JSON.load(raw)
|
107
|
+
expect(json['Description']).to eq "Api redis"
|
108
|
+
user_data = json['Resources']['server']['Properties']['UserData']['Fn::Base64']['Fn::Join'][1]
|
109
|
+
expect(user_data).to include("DB2=test\n")
|
110
|
+
end
|
111
|
+
|
112
|
+
it "should generate db template" do
|
113
|
+
raw = IO.read("#{@project_root}/output/api-redis-prod.json")
|
114
|
+
json = JSON.load(raw)
|
115
|
+
expect(json['Description']).to eq "Api redis"
|
116
|
+
user_data = json['Resources']['server']['Properties']['UserData']['Fn::Base64']['Fn::Join'][1]
|
117
|
+
expect(user_data).to include({"Ref" => "AWS::StackName"})
|
118
|
+
expect(user_data).to include({"Ref" => "WaitHandle"})
|
119
|
+
expect(user_data).to include({
|
120
|
+
"Fn::FindInMap" => [
|
121
|
+
"EnvironmentMapping",
|
122
|
+
"HostnamePrefix",
|
123
|
+
{"Ref" => "Environment"}
|
124
|
+
]
|
125
|
+
})
|
126
|
+
expect(user_data).to include({
|
127
|
+
"Fn::FindInMap" => [
|
128
|
+
"MapName",
|
129
|
+
"TopLevelKey",
|
130
|
+
"SecondLevelKey"
|
131
|
+
]
|
132
|
+
})
|
133
|
+
expect(user_data).to include({"Ref" => "DRINK"})
|
134
|
+
|
135
|
+
expect(user_data).to include({"Fn::Base64" => "value to encode"})
|
136
|
+
expect(user_data).to include({"Fn::GetAtt" => ["server", "PublicDnsName"]})
|
137
|
+
expect(user_data).to include({"Fn::GetAZs" => "AWS::Region"})
|
138
|
+
expect(user_data).to include({"Fn::Join" => [ ':', ['a','b','c']]})
|
139
|
+
expect(user_data).to include({"Fn::Select" => [ '1', ['a','b','c']]})
|
140
|
+
end
|
141
|
+
|
142
|
+
it "should transform bash script to CF template user_data" do
|
143
|
+
block = Proc.new { }
|
144
|
+
template = Lono::Template.new("foo", block)
|
145
|
+
|
146
|
+
line = 'echo {"Ref"=>"AWS::StackName"} > /tmp/stack_name ; {"Ref"=>"Ami"}'
|
147
|
+
data = template.transform(line)
|
148
|
+
expect(data).to eq ["echo ", {"Ref"=>"AWS::StackName"}, " > /tmp/stack_name ; ", {"Ref"=>"Ami"}, "\n"]
|
149
|
+
|
150
|
+
line = 'echo {"Ref"=>"AWS::StackName"} > /tmp/stack_name'
|
151
|
+
data = template.transform(line)
|
152
|
+
expect(data).to eq ["echo ", {"Ref"=>"AWS::StackName"}, " > /tmp/stack_name\n"]
|
153
|
+
|
154
|
+
line = 'echo {"Fn::FindInMap" => [ "A", "B", {"Ref"=>"AWS::StackName"} ]}'
|
155
|
+
data = template.transform(line)
|
156
|
+
expect(data).to eq ["echo ", {"Fn::FindInMap" => ["A", "B", {"Ref"=>"AWS::StackName"}]}, "\n"]
|
157
|
+
|
158
|
+
line = 'echo {"Fn::FindInMap" => [ "A", "B", {"Ref"=>"AWS::StackName"} ]} > /tmp/stack_name ; {"Ref"=>"Ami"}'
|
159
|
+
data = template.transform(line)
|
160
|
+
expect(data).to eq ["echo ", {"Fn::FindInMap" => ["A", "B", {"Ref"=>"AWS::StackName"}]}, " > /tmp/stack_name ; ", {"Ref"=>"Ami"}, "\n"]
|
161
|
+
end
|
162
|
+
|
163
|
+
it "should not transform user_data ruby scripts" do
|
164
|
+
raw = IO.read("#{@project_root}/output/api-worker-prod.json")
|
165
|
+
json = JSON.load(raw)
|
166
|
+
user_data = json['Resources']['LaunchConfig']['Properties']['UserData']['Fn::Base64']['Fn::Join'][1]
|
167
|
+
expect(user_data).to include(%Q|ec2.tags.create(ec2.instances[my_instance_id], "Name", {value: Facter.hostname})\n|)
|
168
|
+
expect(user_data).to include(%Q{find_all{ |record_set| record_set[:name] == record_name }\n})
|
169
|
+
end
|
170
|
+
|
171
|
+
it "should create parent folders for parent/db-stack.json" do
|
172
|
+
directory_created = File.exist?("#{@project_root}/output/parent")
|
173
|
+
expect(directory_created).to be true
|
174
|
+
end
|
175
|
+
|
176
|
+
it "task should generate CloudFormation templates" do
|
177
|
+
raw = IO.read("#{@project_root}/output/api-web-prod.json")
|
178
|
+
json = JSON.load(raw)
|
179
|
+
expect(json['Description']).to eq "Api Stack"
|
180
|
+
expect(json['Mappings']['AWSRegionArch2AMI']['us-east-1']['64']).to eq 'ami-123'
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require File.expand_path("../../../spec_helper", __FILE__)
|
2
|
+
|
3
|
+
describe Lono::New do
|
4
|
+
before(:each) do
|
5
|
+
@project_root = File.expand_path("../../../../tmp/lono_project", __FILE__)
|
6
|
+
end
|
7
|
+
after(:each) do
|
8
|
+
FileUtils.rm_rf(@project_root) unless ENV['KEEP_TMP_PROJECT']
|
9
|
+
end
|
10
|
+
|
11
|
+
context "json starter project" do
|
12
|
+
before(:each) do
|
13
|
+
new_project = Lono::New.new(
|
14
|
+
force: true,
|
15
|
+
quiet: true,
|
16
|
+
format: 'json',
|
17
|
+
project_root: @project_root
|
18
|
+
)
|
19
|
+
new_project.run
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should be able to lono generate" do
|
23
|
+
dsl = Lono::DSL.new(
|
24
|
+
project_root: @project_root,
|
25
|
+
quiet: true
|
26
|
+
)
|
27
|
+
dsl.run
|
28
|
+
generated = File.exist?("#{@project_root}/output/blog-web-prod.json")
|
29
|
+
expect(generated).to be true
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
context "yaml starter project" do
|
34
|
+
before(:each) do
|
35
|
+
new_project = Lono::New.new(
|
36
|
+
force: true,
|
37
|
+
quiet: true,
|
38
|
+
format: 'yaml',
|
39
|
+
project_root: @project_root
|
40
|
+
)
|
41
|
+
new_project.run
|
42
|
+
end
|
43
|
+
|
44
|
+
it "should be able to lono generate" do
|
45
|
+
dsl = Lono::DSL.new(
|
46
|
+
project_root: @project_root,
|
47
|
+
quiet: true
|
48
|
+
)
|
49
|
+
dsl.run
|
50
|
+
generated = File.exist?("#{@project_root}/output/blog-web-prod.yml")
|
51
|
+
expect(generated).to be true
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
context "multiple format starter project" do
|
56
|
+
# TODO: this should not generate anything but puts out a message to the user that the
|
57
|
+
# project needs to be either all yaml or all json format
|
58
|
+
end
|
59
|
+
end
|