simple_deploy 0.6.0 → 0.6.1
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +6 -0
- data/README.md +28 -16
- data/lib/simple_deploy/cli/clone.rb +9 -0
- data/lib/simple_deploy/config.rb +21 -7
- data/lib/simple_deploy/env.rb +9 -0
- data/lib/simple_deploy/version.rb +1 -1
- data/lib/simple_deploy.rb +1 -0
- data/spec/cli/clone_spec.rb +48 -6
- data/spec/config_spec.rb +41 -23
- metadata +22 -51
data/CHANGELOG
CHANGED
@@ -1,3 +1,9 @@
|
|
1
|
+
## v0.6.1
|
2
|
+
|
3
|
+
* Corrected the clone command so it accepts new attributes
|
4
|
+
* Added ability to pass config data into Config class which avoids having to have a .simple_deploy.yml file.
|
5
|
+
* Config file reads SIMPLE_DEPLOY_CONFIG_FILE for alternate config file location.
|
6
|
+
|
1
7
|
## v0.6.0
|
2
8
|
|
3
9
|
* Updated so it no longer caches simpledb attributes so it works better with stackster changes
|
data/README.md
CHANGED
@@ -1,28 +1,30 @@
|
|
1
1
|
[![Build Status](https://secure.travis-ci.org/intuit/simple_deploy.png)](http://travis-ci.org/intuit/simple_deploy)
|
2
2
|
|
3
|
-
|
3
|
+
Simple Deploy is an opinionated gem that helps manage and perform directed deployments to AWS Cloud Formation Stacks.
|
4
4
|
|
5
|
-
|
6
|
-
|
5
|
+
Prerequisites
|
6
|
+
-------------
|
7
|
+
|
8
|
+
* Ruby version 1.9.2 or higher installed.
|
9
|
+
* AWS account access key and secret key.
|
10
|
+
|
11
|
+
Installation
|
12
|
+
------------
|
7
13
|
|
8
14
|
Install the gem
|
9
15
|
|
10
16
|
```
|
11
|
-
gem install simple_deploy
|
17
|
+
gem install simple_deploy --no-ri --no-rdoc
|
12
18
|
```
|
13
19
|
|
14
20
|
Create a file **~/.simple_deploy.yml** and include within it:
|
15
21
|
|
16
22
|
```
|
17
23
|
environments:
|
18
|
-
|
24
|
+
preprod:
|
19
25
|
access_key: XXX
|
20
26
|
secret_key: yyy
|
21
27
|
region: us-west-1
|
22
|
-
|
23
|
-
notifications:
|
24
|
-
campfire:
|
25
|
-
token: XXX
|
26
28
|
```
|
27
29
|
|
28
30
|
Notifications
|
@@ -30,21 +32,31 @@ Notifications
|
|
30
32
|
|
31
33
|
Currently Simple Deploy only supports Campfire for notifications. To enable them, add your token, which can be obtained on the 'My Info' screen of Campfire in the notifications yaml above. If you don't want notificaitons, omit this section.
|
32
34
|
|
33
|
-
|
34
|
-
----------------------
|
35
|
-
|
36
|
-
The configuration file supports additional optional deployment parameters.
|
35
|
+
To enable notifications on deployment to a Campfire room. Append the below to the **~/.simple_deploy.yml**.
|
37
36
|
|
38
|
-
|
37
|
+
```
|
38
|
+
notifications:
|
39
|
+
campfire:
|
40
|
+
token: XXX
|
41
|
+
```
|
39
42
|
|
40
43
|
Commands
|
41
44
|
--------
|
42
45
|
|
43
|
-
For a list of commands, run simple_deploy -h
|
46
|
+
For a list of commands, run **simple_deploy -h**. To get more information about each subcommand, append a -h after the subcomand. For example: **simple_deploy deploy -h**.
|
44
47
|
|
45
48
|
Deploying
|
46
49
|
---------
|
47
50
|
|
48
51
|
By default simple deploy will use your user name and id_rsa key for deployments. To override either these, set the **SIMPLE_DEPLOY_SSH_USER** & **SIMPLE_DEPLOY_SSH_KEY** respectively.
|
49
52
|
|
50
|
-
|
53
|
+
```
|
54
|
+
export SIMPLE_DEPLOY_SSH_USER=user
|
55
|
+
export SIMPLE_DEPLOY_SSH_KEY=path_to_ssh_key
|
56
|
+
```
|
57
|
+
|
58
|
+
An alternate config file can be supplied by setting the **SIMPLE_DEPLOY_CONFIG_FILE** variable.
|
59
|
+
|
60
|
+
```
|
61
|
+
export SIMPLE_DEPLOY_CONFIG_FILE=/secret/my-config.yml
|
62
|
+
```
|
@@ -31,6 +31,7 @@ EOS
|
|
31
31
|
|
32
32
|
cloned_attributes = filter_attributes source_stack.attributes
|
33
33
|
new_attributes = merge_attributes cloned_attributes, override_attributes
|
34
|
+
new_attributes += add_attributes cloned_attributes, override_attributes
|
34
35
|
|
35
36
|
if @opts[:template]
|
36
37
|
template_file = @opts[:template]
|
@@ -58,6 +59,14 @@ EOS
|
|
58
59
|
end
|
59
60
|
end
|
60
61
|
|
62
|
+
def add_attributes(cloned_attributes, override_attributes)
|
63
|
+
override_attributes.map do |override|
|
64
|
+
key = override.keys.first
|
65
|
+
clone = cloned_attributes.find { |c| c.has_key? key }
|
66
|
+
clone ? nil : override
|
67
|
+
end.compact
|
68
|
+
end
|
69
|
+
|
61
70
|
def config
|
62
71
|
@config ||= Config.new.environment @opts[:environment]
|
63
72
|
end
|
data/lib/simple_deploy/config.rb
CHANGED
@@ -4,7 +4,7 @@ module SimpleDeploy
|
|
4
4
|
attr_accessor :config, :logger
|
5
5
|
|
6
6
|
def initialize(args = {})
|
7
|
-
load_config_file
|
7
|
+
self.config = args.fetch(:config) { load_config_file }
|
8
8
|
self.logger = args[:logger] ||= SimpleDeployLogger.new
|
9
9
|
end
|
10
10
|
|
@@ -24,7 +24,7 @@ module SimpleDeploy
|
|
24
24
|
'app' => 'AppArtifactURL',
|
25
25
|
'cookbooks' => 'CookbooksURL' }
|
26
26
|
name_to_url_map[artifact]
|
27
|
-
end
|
27
|
+
end
|
28
28
|
|
29
29
|
def deploy_script
|
30
30
|
'/opt/intu/admin/bin/configure.sh'
|
@@ -50,10 +50,8 @@ module SimpleDeploy
|
|
50
50
|
private
|
51
51
|
|
52
52
|
def load_config_file
|
53
|
-
config_file = "#{ENV['HOME']}/.simple_deploy.yml"
|
54
|
-
|
55
53
|
begin
|
56
|
-
|
54
|
+
YAML::load( File.open( config_file ) )
|
57
55
|
rescue Errno::ENOENT
|
58
56
|
raise "#{config_file} not found"
|
59
57
|
rescue Psych::SyntaxError => e
|
@@ -62,11 +60,27 @@ module SimpleDeploy
|
|
62
60
|
end
|
63
61
|
|
64
62
|
def env_home
|
65
|
-
|
63
|
+
env.load 'HOME'
|
66
64
|
end
|
67
65
|
|
68
66
|
def env_user
|
69
|
-
|
67
|
+
env.load 'USER'
|
68
|
+
end
|
69
|
+
|
70
|
+
def config_file
|
71
|
+
env_config_file || default_config_file
|
72
|
+
end
|
73
|
+
|
74
|
+
def env_config_file
|
75
|
+
env.load 'SIMPLE_DEPLOY_CONFIG_FILE'
|
76
|
+
end
|
77
|
+
|
78
|
+
def default_config_file
|
79
|
+
"#{env.load 'HOME'}/.simple_deploy.yml"
|
80
|
+
end
|
81
|
+
|
82
|
+
def env
|
83
|
+
@env ||= SimpleDeploy::Env.new
|
70
84
|
end
|
71
85
|
|
72
86
|
end
|
data/lib/simple_deploy.rb
CHANGED
data/spec/cli/clone_spec.rb
CHANGED
@@ -19,7 +19,6 @@ describe SimpleDeploy::CLI::Clone do
|
|
19
19
|
|
20
20
|
it 'should filter out deployment attributes' do
|
21
21
|
new_attributes = subject.send(:filter_attributes, @source_attributes)
|
22
|
-
|
23
22
|
new_attributes.size.should == 6
|
24
23
|
|
25
24
|
new_attributes[0].has_key?('AmiId').should be_true
|
@@ -57,7 +56,6 @@ describe SimpleDeploy::CLI::Clone do
|
|
57
56
|
|
58
57
|
it 'should merge the override attributes' do
|
59
58
|
merged_attributes = subject.send(:merge_attributes, @cloned_attributes, @override_attributes)
|
60
|
-
|
61
59
|
merged_attributes.size.should == 7
|
62
60
|
|
63
61
|
merged_attributes[0].has_key?('AmiId').should be_true
|
@@ -77,6 +75,46 @@ describe SimpleDeploy::CLI::Clone do
|
|
77
75
|
end
|
78
76
|
end
|
79
77
|
|
78
|
+
context 'add_attributes' do
|
79
|
+
before do
|
80
|
+
@cloned_attributes = [
|
81
|
+
{ 'AmiId' => 'ami-7b6a4e3e' },
|
82
|
+
{ 'AppEnv' => 'pod-2-cd-1' },
|
83
|
+
{ 'MaximumAppInstances' => 1 },
|
84
|
+
{ 'MinimumAppInstances' => 1 },
|
85
|
+
{ 'chef_repo_bucket_prefix' => 'intu-lc' },
|
86
|
+
{ 'chef_repo_domain' => 'live_community_chef_repo' },
|
87
|
+
{ 'deployment_user' => 'rmendes' }
|
88
|
+
]
|
89
|
+
|
90
|
+
@new_attributes = [
|
91
|
+
{ 'chef_repo_bucket_prefix' => 'updated-intu-lc' },
|
92
|
+
{ 'SolrClientTrafficContainer' => 'solr-client-traffic-container' },
|
93
|
+
{ 'SolrReplicationTrafficContainer' => 'solr-replication-traffic-container' }
|
94
|
+
]
|
95
|
+
end
|
96
|
+
|
97
|
+
it 'should add new override attributes' do
|
98
|
+
add_attributes = subject.send(:add_attributes, @cloned_attributes, @new_attributes)
|
99
|
+
add_attributes.size.should == 2
|
100
|
+
|
101
|
+
add_attributes[0].has_key?('SolrClientTrafficContainer').should be_true
|
102
|
+
add_attributes[0]['SolrClientTrafficContainer'].should == 'solr-client-traffic-container'
|
103
|
+
add_attributes[1].has_key?('SolrReplicationTrafficContainer').should be_true
|
104
|
+
add_attributes[1]['SolrReplicationTrafficContainer'].should == 'solr-replication-traffic-container'
|
105
|
+
end
|
106
|
+
|
107
|
+
it 'should return an empty array if there are no new attributes' do
|
108
|
+
new_attributes = [
|
109
|
+
{ 'chef_repo_bucket_prefix' => 'updated-intu-lc' },
|
110
|
+
{ 'chef_repo_domain' => 'updated_community_chef_repo' }
|
111
|
+
]
|
112
|
+
|
113
|
+
add_attributes = subject.send(:add_attributes, @cloned_attributes, new_attributes)
|
114
|
+
add_attributes.should be_empty
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
80
118
|
context 'stack creation' do
|
81
119
|
before do
|
82
120
|
@config = mock 'config'
|
@@ -85,7 +123,9 @@ describe SimpleDeploy::CLI::Clone do
|
|
85
123
|
:log_level => 'debug',
|
86
124
|
:source_name => 'source_stack',
|
87
125
|
:new_name => 'new_stack',
|
88
|
-
:attributes => ['chef_repo_bucket_prefix=updated-intu-lc',
|
126
|
+
:attributes => ['chef_repo_bucket_prefix=updated-intu-lc',
|
127
|
+
'chef_repo_domain=updated_community_chef_repo',
|
128
|
+
'SolrClientTrafficContainer=solr-client-traffic-container'] }
|
89
129
|
|
90
130
|
@source_stack = stub :attributes => {
|
91
131
|
'AmiId' => 'ami-7b6a4e3e',
|
@@ -118,7 +158,7 @@ describe SimpleDeploy::CLI::Clone do
|
|
118
158
|
and_return(@new_stack)
|
119
159
|
end
|
120
160
|
|
121
|
-
it 'should create the new stack using the filtered and
|
161
|
+
it 'should create the new stack using the filtered, merged and added attributes' do
|
122
162
|
SimpleDeploy::CLI::Shared.should_receive(:valid_options?).
|
123
163
|
with(:provided => @options,
|
124
164
|
:required => [:environment, :source_name, :new_name])
|
@@ -130,7 +170,8 @@ describe SimpleDeploy::CLI::Clone do
|
|
130
170
|
{ 'MaximumAppInstances' => 1 },
|
131
171
|
{ 'MinimumAppInstances' => 1 },
|
132
172
|
{ 'chef_repo_bucket_prefix' => 'updated-intu-lc' },
|
133
|
-
{ 'chef_repo_domain' => 'updated_community_chef_repo' }
|
173
|
+
{ 'chef_repo_domain' => 'updated_community_chef_repo' },
|
174
|
+
{ 'SolrClientTrafficContainer' => 'solr-client-traffic-container' }]
|
134
175
|
options[:template].should match /new_stack_template.json/
|
135
176
|
end
|
136
177
|
|
@@ -151,7 +192,8 @@ describe SimpleDeploy::CLI::Clone do
|
|
151
192
|
{ 'MaximumAppInstances' => 1 },
|
152
193
|
{ 'MinimumAppInstances' => 1 },
|
153
194
|
{ 'chef_repo_bucket_prefix' => 'updated-intu-lc' },
|
154
|
-
{ 'chef_repo_domain' => 'updated_community_chef_repo' }
|
195
|
+
{ 'chef_repo_domain' => 'updated_community_chef_repo' },
|
196
|
+
{ 'SolrClientTrafficContainer' => 'solr-client-traffic-container' }]
|
155
197
|
options[:template].should match /brand_new_template.json/
|
156
198
|
end
|
157
199
|
|
data/spec/config_spec.rb
CHANGED
@@ -1,33 +1,51 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe SimpleDeploy do
|
4
|
+
let(:config_data) do
|
5
|
+
{ 'environments' => {
|
6
|
+
'test_env' => {
|
7
|
+
'secret_key' => 'secret',
|
8
|
+
'access_key' => 'access',
|
9
|
+
'region' => 'us-west-1'
|
10
|
+
} },
|
11
|
+
'notifications' => {
|
12
|
+
'campfire' => {
|
13
|
+
'token' => 'my_token'
|
14
|
+
} } }
|
15
|
+
end
|
4
16
|
|
5
|
-
describe
|
6
|
-
|
7
|
-
|
8
|
-
'test_repo' => {
|
9
|
-
'bucket_prefix' => 'test_prefix',
|
10
|
-
'domain' => 'test_domain'
|
11
|
-
},
|
12
|
-
'test_repo2' => { },
|
13
|
-
},
|
14
|
-
'environments' => {
|
15
|
-
'test_env' => {
|
16
|
-
'secret_key' => 'secret',
|
17
|
-
'access_key' => 'access',
|
18
|
-
'region' => 'us-west-1'
|
19
|
-
}
|
20
|
-
},
|
21
|
-
'notifications' => {
|
22
|
-
'campfire' => {
|
23
|
-
'token' => 'my_token'
|
24
|
-
}
|
25
|
-
}
|
26
|
-
}
|
17
|
+
describe 'new' do
|
18
|
+
it 'should accept config data as an argument' do
|
19
|
+
YAML.should_not_receive(:load)
|
27
20
|
|
21
|
+
@config = SimpleDeploy::Config.new :config => config_data
|
22
|
+
@config.config.should == config_data
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'should load the config from ~/.simple_deploy.yml by default' do
|
28
26
|
File.should_receive(:open).with("#{ENV['HOME']}/.simple_deploy.yml").
|
29
|
-
and_return(
|
27
|
+
and_return(config_data.to_yaml)
|
28
|
+
@config = SimpleDeploy::Config.new
|
29
|
+
@config.config.should == config_data
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'should load the config from SIMPLE_DEPLOY_CONFIG_FILE if supplied' do
|
33
|
+
File.should_receive(:open).with("/my/config/file").
|
34
|
+
and_return(config_data.to_yaml)
|
35
|
+
env_mock = mock 'env'
|
36
|
+
SimpleDeploy::Env.stub :new => env_mock
|
37
|
+
env_mock.should_receive(:load).
|
38
|
+
with('SIMPLE_DEPLOY_CONFIG_FILE').
|
39
|
+
and_return "/my/config/file"
|
30
40
|
@config = SimpleDeploy::Config.new
|
41
|
+
@config.config.should == config_data
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
describe "after creating a configuration" do
|
47
|
+
before do
|
48
|
+
@config = SimpleDeploy::Config.new :config => config_data
|
31
49
|
end
|
32
50
|
|
33
51
|
it "should return the default artifacts to deploy" do
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: simple_deploy
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.6.
|
4
|
+
version: 0.6.1
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-11-
|
12
|
+
date: 2012-11-15 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rake
|
16
|
-
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirement: &70363065994720 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,15 +21,10 @@ dependencies:
|
|
21
21
|
version: '0'
|
22
22
|
type: :development
|
23
23
|
prerelease: false
|
24
|
-
version_requirements:
|
25
|
-
none: false
|
26
|
-
requirements:
|
27
|
-
- - ! '>='
|
28
|
-
- !ruby/object:Gem::Version
|
29
|
-
version: '0'
|
24
|
+
version_requirements: *70363065994720
|
30
25
|
- !ruby/object:Gem::Dependency
|
31
26
|
name: rspec
|
32
|
-
requirement: !ruby/object:Gem::Requirement
|
27
|
+
requirement: &70363066009920 !ruby/object:Gem::Requirement
|
33
28
|
none: false
|
34
29
|
requirements:
|
35
30
|
- - ~>
|
@@ -37,76 +32,51 @@ dependencies:
|
|
37
32
|
version: 2.11.0
|
38
33
|
type: :development
|
39
34
|
prerelease: false
|
40
|
-
version_requirements:
|
41
|
-
none: false
|
42
|
-
requirements:
|
43
|
-
- - ~>
|
44
|
-
- !ruby/object:Gem::Version
|
45
|
-
version: 2.11.0
|
35
|
+
version_requirements: *70363066009920
|
46
36
|
- !ruby/object:Gem::Dependency
|
47
37
|
name: capistrano
|
48
|
-
requirement: !ruby/object:Gem::Requirement
|
38
|
+
requirement: &70363066009000 !ruby/object:Gem::Requirement
|
49
39
|
none: false
|
50
40
|
requirements:
|
51
|
-
- -
|
41
|
+
- - =
|
52
42
|
- !ruby/object:Gem::Version
|
53
43
|
version: 2.13.5
|
54
44
|
type: :runtime
|
55
45
|
prerelease: false
|
56
|
-
version_requirements:
|
57
|
-
none: false
|
58
|
-
requirements:
|
59
|
-
- - '='
|
60
|
-
- !ruby/object:Gem::Version
|
61
|
-
version: 2.13.5
|
46
|
+
version_requirements: *70363066009000
|
62
47
|
- !ruby/object:Gem::Dependency
|
63
48
|
name: stackster
|
64
|
-
requirement: !ruby/object:Gem::Requirement
|
49
|
+
requirement: &70363066008120 !ruby/object:Gem::Requirement
|
65
50
|
none: false
|
66
51
|
requirements:
|
67
|
-
- -
|
52
|
+
- - =
|
68
53
|
- !ruby/object:Gem::Version
|
69
54
|
version: 0.3.2
|
70
55
|
type: :runtime
|
71
56
|
prerelease: false
|
72
|
-
version_requirements:
|
73
|
-
none: false
|
74
|
-
requirements:
|
75
|
-
- - '='
|
76
|
-
- !ruby/object:Gem::Version
|
77
|
-
version: 0.3.2
|
57
|
+
version_requirements: *70363066008120
|
78
58
|
- !ruby/object:Gem::Dependency
|
79
59
|
name: tinder
|
80
|
-
requirement: !ruby/object:Gem::Requirement
|
60
|
+
requirement: &70363066006760 !ruby/object:Gem::Requirement
|
81
61
|
none: false
|
82
62
|
requirements:
|
83
|
-
- -
|
63
|
+
- - =
|
84
64
|
- !ruby/object:Gem::Version
|
85
65
|
version: 1.9.1
|
86
66
|
type: :runtime
|
87
67
|
prerelease: false
|
88
|
-
version_requirements:
|
89
|
-
none: false
|
90
|
-
requirements:
|
91
|
-
- - '='
|
92
|
-
- !ruby/object:Gem::Version
|
93
|
-
version: 1.9.1
|
68
|
+
version_requirements: *70363066006760
|
94
69
|
- !ruby/object:Gem::Dependency
|
95
70
|
name: trollop
|
96
|
-
requirement: !ruby/object:Gem::Requirement
|
71
|
+
requirement: &70363066005780 !ruby/object:Gem::Requirement
|
97
72
|
none: false
|
98
73
|
requirements:
|
99
|
-
- -
|
74
|
+
- - =
|
100
75
|
- !ruby/object:Gem::Version
|
101
76
|
version: '2.0'
|
102
77
|
type: :runtime
|
103
78
|
prerelease: false
|
104
|
-
version_requirements:
|
105
|
-
none: false
|
106
|
-
requirements:
|
107
|
-
- - '='
|
108
|
-
- !ruby/object:Gem::Version
|
109
|
-
version: '2.0'
|
79
|
+
version_requirements: *70363066005780
|
110
80
|
description: I am designed to deploy artifacts uploaded by Heirloom
|
111
81
|
email:
|
112
82
|
- brett@weav.net
|
@@ -147,6 +117,7 @@ files:
|
|
147
117
|
- lib/simple_deploy/cli/template.rb
|
148
118
|
- lib/simple_deploy/cli/update.rb
|
149
119
|
- lib/simple_deploy/config.rb
|
120
|
+
- lib/simple_deploy/env.rb
|
150
121
|
- lib/simple_deploy/logger.rb
|
151
122
|
- lib/simple_deploy/notifier.rb
|
152
123
|
- lib/simple_deploy/notifier/campfire.rb
|
@@ -192,7 +163,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
192
163
|
version: '0'
|
193
164
|
segments:
|
194
165
|
- 0
|
195
|
-
hash:
|
166
|
+
hash: -1601352363388167763
|
196
167
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
197
168
|
none: false
|
198
169
|
requirements:
|
@@ -201,10 +172,10 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
201
172
|
version: '0'
|
202
173
|
segments:
|
203
174
|
- 0
|
204
|
-
hash:
|
175
|
+
hash: -1601352363388167763
|
205
176
|
requirements: []
|
206
177
|
rubyforge_project: simple_deploy
|
207
|
-
rubygems_version: 1.8.
|
178
|
+
rubygems_version: 1.8.16
|
208
179
|
signing_key:
|
209
180
|
specification_version: 3
|
210
181
|
summary: I help with deployments
|