mrsk 0.13.2 → 0.14.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 +41 -0
- data/lib/mrsk/cli/accessory.rb +11 -11
- data/lib/mrsk/cli/app.rb +8 -8
- data/lib/mrsk/cli/base.rb +16 -16
- data/lib/mrsk/cli/build.rb +5 -5
- data/lib/mrsk/cli/main.rb +5 -5
- data/lib/mrsk/cli/prune.rb +3 -3
- data/lib/mrsk/cli/templates/sample_hooks/pre-deploy.sample +49 -22
- data/lib/mrsk/cli/traefik.rb +8 -8
- data/lib/mrsk/commands/base.rb +5 -1
- data/lib/mrsk/commands/builder/base.rb +12 -16
- data/lib/mrsk/commands/builder/multiarch/remote.rb +6 -14
- data/lib/mrsk/commands/builder/native/cached.rb +16 -0
- data/lib/mrsk/commands/builder/native/remote.rb +3 -11
- data/lib/mrsk/commands/builder/native.rb +2 -2
- data/lib/mrsk/commands/builder.rb +9 -3
- data/lib/mrsk/commands/traefik.rb +5 -3
- data/lib/mrsk/configuration/builder.rb +114 -0
- data/lib/mrsk/configuration.rb +24 -7
- data/lib/mrsk/version.rb +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 39d645ed2ca51ebf7edeaca7502b9b9d83456ad4fd11d5b905903de2ee76f189
|
4
|
+
data.tar.gz: c8fe80b16c14e89fd333224b887968620a88741c32a8b7cb4ee1cf9c37c1dcd5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fa4264e442bc470b02ebd946b4667535b9e5e8795cf9cb22c311693f381a0a9671c67d16dd6f1ae6391e48b8e7b2be51fc49671bccd2610e3ec98afd75002a12
|
7
|
+
data.tar.gz: 408f3500ff87847217b08f21c86a189b77e73f54d3b1a407a7e16a9f73fea20a883233cf8970f9f5fb6b6ebdcd69d3a47ec5006e42b7190cffd41c1c5b9bfb60
|
data/README.md
CHANGED
@@ -380,6 +380,16 @@ servers:
|
|
380
380
|
|
381
381
|
That'll start the job containers with `docker run ... --cap-add --cpu-count 4 ...`.
|
382
382
|
|
383
|
+
### Setting a minimum version
|
384
|
+
|
385
|
+
You can set the minimum MRSK version with:
|
386
|
+
|
387
|
+
```yaml
|
388
|
+
minimum_version: 0.13.3
|
389
|
+
```
|
390
|
+
|
391
|
+
Note: versions <= 0.13.2 will ignore this setting.
|
392
|
+
|
383
393
|
### Configuring logging
|
384
394
|
|
385
395
|
You can configure the logging driver and options passed to Docker using `logging`:
|
@@ -463,6 +473,37 @@ builder:
|
|
463
473
|
context: ".."
|
464
474
|
```
|
465
475
|
|
476
|
+
### Using multistage builder cache
|
477
|
+
|
478
|
+
Docker multistage build cache can singlehandedly speed up your builds by a lot. Currently MRSK only supports using the GHA cache or the Registry cache:
|
479
|
+
|
480
|
+
```yaml
|
481
|
+
# Using GHA cache
|
482
|
+
builder:
|
483
|
+
cache:
|
484
|
+
type: gha
|
485
|
+
|
486
|
+
# Using Registry cache
|
487
|
+
builder:
|
488
|
+
cache:
|
489
|
+
type: registry
|
490
|
+
|
491
|
+
# Using Registry cache with different cache image
|
492
|
+
builder:
|
493
|
+
cache:
|
494
|
+
type: registry
|
495
|
+
# default image name is <image>-build-cache
|
496
|
+
image: application-cache-image
|
497
|
+
|
498
|
+
# Using Registry cache with additinonal cache-to options
|
499
|
+
builder:
|
500
|
+
cache:
|
501
|
+
type: registry
|
502
|
+
options: mode=max,image-manifest=true,oci-mediatypes=true
|
503
|
+
```
|
504
|
+
|
505
|
+
For further insights into build cache optimization, check out documentation on Docker's official website: https://docs.docker.com/build/cache/.
|
506
|
+
|
466
507
|
### Using build secrets for new images
|
467
508
|
|
468
509
|
Some images need a secret passed in during build time, like a GITHUB_TOKEN, to give access to private gem repositories. This can be done by having the secret in ENV, then referencing it in the builder configuration:
|
data/lib/mrsk/cli/accessory.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
class Mrsk::Cli::Accessory < Mrsk::Cli::Base
|
2
2
|
desc "boot [NAME]", "Boot new accessory service on host (use NAME=all to boot all accessories)"
|
3
3
|
def boot(name)
|
4
|
-
|
4
|
+
mutating do
|
5
5
|
if name == "all"
|
6
6
|
MRSK.accessory_names.each { |accessory_name| boot(accessory_name) }
|
7
7
|
else
|
@@ -21,7 +21,7 @@ class Mrsk::Cli::Accessory < Mrsk::Cli::Base
|
|
21
21
|
|
22
22
|
desc "upload [NAME]", "Upload accessory files to host", hide: true
|
23
23
|
def upload(name)
|
24
|
-
|
24
|
+
mutating do
|
25
25
|
with_accessory(name) do |accessory|
|
26
26
|
on(accessory.hosts) do
|
27
27
|
accessory.files.each do |(local, remote)|
|
@@ -38,7 +38,7 @@ class Mrsk::Cli::Accessory < Mrsk::Cli::Base
|
|
38
38
|
|
39
39
|
desc "directories [NAME]", "Create accessory directories on host", hide: true
|
40
40
|
def directories(name)
|
41
|
-
|
41
|
+
mutating do
|
42
42
|
with_accessory(name) do |accessory|
|
43
43
|
on(accessory.hosts) do
|
44
44
|
accessory.directories.keys.each do |host_path|
|
@@ -51,7 +51,7 @@ class Mrsk::Cli::Accessory < Mrsk::Cli::Base
|
|
51
51
|
|
52
52
|
desc "reboot [NAME]", "Reboot existing accessory on host (stop container, remove container, start new container)"
|
53
53
|
def reboot(name)
|
54
|
-
|
54
|
+
mutating do
|
55
55
|
with_accessory(name) do |accessory|
|
56
56
|
stop(name)
|
57
57
|
remove_container(name)
|
@@ -62,7 +62,7 @@ class Mrsk::Cli::Accessory < Mrsk::Cli::Base
|
|
62
62
|
|
63
63
|
desc "start [NAME]", "Start existing accessory container on host"
|
64
64
|
def start(name)
|
65
|
-
|
65
|
+
mutating do
|
66
66
|
with_accessory(name) do |accessory|
|
67
67
|
on(accessory.hosts) do
|
68
68
|
execute *MRSK.auditor.record("Started #{name} accessory"), verbosity: :debug
|
@@ -74,7 +74,7 @@ class Mrsk::Cli::Accessory < Mrsk::Cli::Base
|
|
74
74
|
|
75
75
|
desc "stop [NAME]", "Stop existing accessory container on host"
|
76
76
|
def stop(name)
|
77
|
-
|
77
|
+
mutating do
|
78
78
|
with_accessory(name) do |accessory|
|
79
79
|
on(accessory.hosts) do
|
80
80
|
execute *MRSK.auditor.record("Stopped #{name} accessory"), verbosity: :debug
|
@@ -86,7 +86,7 @@ class Mrsk::Cli::Accessory < Mrsk::Cli::Base
|
|
86
86
|
|
87
87
|
desc "restart [NAME]", "Restart existing accessory container on host"
|
88
88
|
def restart(name)
|
89
|
-
|
89
|
+
mutating do
|
90
90
|
with_accessory(name) do
|
91
91
|
stop(name)
|
92
92
|
start(name)
|
@@ -165,7 +165,7 @@ class Mrsk::Cli::Accessory < Mrsk::Cli::Base
|
|
165
165
|
desc "remove [NAME]", "Remove accessory container, image and data directory from host (use NAME=all to remove all accessories)"
|
166
166
|
option :confirmed, aliases: "-y", type: :boolean, default: false, desc: "Proceed without confirmation question"
|
167
167
|
def remove(name)
|
168
|
-
|
168
|
+
mutating do
|
169
169
|
if name == "all"
|
170
170
|
MRSK.accessory_names.each { |accessory_name| remove(accessory_name) }
|
171
171
|
else
|
@@ -183,7 +183,7 @@ class Mrsk::Cli::Accessory < Mrsk::Cli::Base
|
|
183
183
|
|
184
184
|
desc "remove_container [NAME]", "Remove accessory container from host", hide: true
|
185
185
|
def remove_container(name)
|
186
|
-
|
186
|
+
mutating do
|
187
187
|
with_accessory(name) do |accessory|
|
188
188
|
on(accessory.hosts) do
|
189
189
|
execute *MRSK.auditor.record("Remove #{name} accessory container"), verbosity: :debug
|
@@ -195,7 +195,7 @@ class Mrsk::Cli::Accessory < Mrsk::Cli::Base
|
|
195
195
|
|
196
196
|
desc "remove_image [NAME]", "Remove accessory image from host", hide: true
|
197
197
|
def remove_image(name)
|
198
|
-
|
198
|
+
mutating do
|
199
199
|
with_accessory(name) do |accessory|
|
200
200
|
on(accessory.hosts) do
|
201
201
|
execute *MRSK.auditor.record("Removed #{name} accessory image"), verbosity: :debug
|
@@ -207,7 +207,7 @@ class Mrsk::Cli::Accessory < Mrsk::Cli::Base
|
|
207
207
|
|
208
208
|
desc "remove_service_directory [NAME]", "Remove accessory directory used for uploaded files and data directories from host", hide: true
|
209
209
|
def remove_service_directory(name)
|
210
|
-
|
210
|
+
mutating do
|
211
211
|
with_accessory(name) do |accessory|
|
212
212
|
on(accessory.hosts) do
|
213
213
|
execute *accessory.remove_service_directory
|
data/lib/mrsk/cli/app.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
class Mrsk::Cli::App < Mrsk::Cli::Base
|
2
2
|
desc "boot", "Boot app on servers (or reboot app if already running)"
|
3
3
|
def boot
|
4
|
-
|
4
|
+
mutating do
|
5
5
|
hold_lock_on_error do
|
6
6
|
say "Get most recent version available as an image...", :magenta unless options[:version]
|
7
7
|
using_version(version_or_latest) do |version|
|
@@ -43,7 +43,7 @@ class Mrsk::Cli::App < Mrsk::Cli::Base
|
|
43
43
|
|
44
44
|
desc "start", "Start existing app container on servers"
|
45
45
|
def start
|
46
|
-
|
46
|
+
mutating do
|
47
47
|
on(MRSK.hosts) do |host|
|
48
48
|
roles = MRSK.roles_on(host)
|
49
49
|
|
@@ -57,7 +57,7 @@ class Mrsk::Cli::App < Mrsk::Cli::Base
|
|
57
57
|
|
58
58
|
desc "stop", "Stop app container on servers"
|
59
59
|
def stop
|
60
|
-
|
60
|
+
mutating do
|
61
61
|
on(MRSK.hosts) do |host|
|
62
62
|
roles = MRSK.roles_on(host)
|
63
63
|
|
@@ -135,7 +135,7 @@ class Mrsk::Cli::App < Mrsk::Cli::Base
|
|
135
135
|
desc "stale_containers", "Detect app stale containers"
|
136
136
|
option :stop, aliases: "-s", type: :boolean, default: false, desc: "Stop the stale containers found"
|
137
137
|
def stale_containers
|
138
|
-
|
138
|
+
mutating do
|
139
139
|
stop = options[:stop]
|
140
140
|
|
141
141
|
cli = self
|
@@ -202,7 +202,7 @@ class Mrsk::Cli::App < Mrsk::Cli::Base
|
|
202
202
|
|
203
203
|
desc "remove", "Remove app containers and images from servers"
|
204
204
|
def remove
|
205
|
-
|
205
|
+
mutating do
|
206
206
|
stop
|
207
207
|
remove_containers
|
208
208
|
remove_images
|
@@ -211,7 +211,7 @@ class Mrsk::Cli::App < Mrsk::Cli::Base
|
|
211
211
|
|
212
212
|
desc "remove_container [VERSION]", "Remove app container with given version from servers", hide: true
|
213
213
|
def remove_container(version)
|
214
|
-
|
214
|
+
mutating do
|
215
215
|
on(MRSK.hosts) do |host|
|
216
216
|
roles = MRSK.roles_on(host)
|
217
217
|
|
@@ -225,7 +225,7 @@ class Mrsk::Cli::App < Mrsk::Cli::Base
|
|
225
225
|
|
226
226
|
desc "remove_containers", "Remove all app containers from servers", hide: true
|
227
227
|
def remove_containers
|
228
|
-
|
228
|
+
mutating do
|
229
229
|
on(MRSK.hosts) do |host|
|
230
230
|
roles = MRSK.roles_on(host)
|
231
231
|
|
@@ -239,7 +239,7 @@ class Mrsk::Cli::App < Mrsk::Cli::Base
|
|
239
239
|
|
240
240
|
desc "remove_images", "Remove all app images from servers", hide: true
|
241
241
|
def remove_images
|
242
|
-
|
242
|
+
mutating do
|
243
243
|
on(MRSK.hosts) do
|
244
244
|
execute *MRSK.auditor.record("Removed all app images"), verbosity: :debug
|
245
245
|
execute *MRSK.app.remove_images
|
data/lib/mrsk/cli/base.rb
CHANGED
@@ -72,28 +72,28 @@ module Mrsk::Cli
|
|
72
72
|
puts " Finished all in #{sprintf("%.1f seconds", runtime)}"
|
73
73
|
end
|
74
74
|
|
75
|
-
def
|
76
|
-
if MRSK.holding_lock?
|
77
|
-
|
78
|
-
|
79
|
-
run_hook "pre-connect"
|
75
|
+
def mutating
|
76
|
+
return yield if MRSK.holding_lock?
|
77
|
+
|
78
|
+
MRSK.config.ensure_env_available
|
80
79
|
|
81
|
-
|
80
|
+
run_hook "pre-connect"
|
82
81
|
|
83
|
-
|
84
|
-
yield
|
85
|
-
rescue
|
86
|
-
if MRSK.hold_lock_on_error?
|
87
|
-
error " \e[31mDeploy lock was not released\e[0m"
|
88
|
-
else
|
89
|
-
release_lock
|
90
|
-
end
|
82
|
+
acquire_lock
|
91
83
|
|
92
|
-
|
84
|
+
begin
|
85
|
+
yield
|
86
|
+
rescue
|
87
|
+
if MRSK.hold_lock_on_error?
|
88
|
+
error " \e[31mDeploy lock was not released\e[0m"
|
89
|
+
else
|
90
|
+
release_lock
|
93
91
|
end
|
94
92
|
|
95
|
-
|
93
|
+
raise
|
96
94
|
end
|
95
|
+
|
96
|
+
release_lock
|
97
97
|
end
|
98
98
|
|
99
99
|
def acquire_lock
|
data/lib/mrsk/cli/build.rb
CHANGED
@@ -3,7 +3,7 @@ class Mrsk::Cli::Build < Mrsk::Cli::Base
|
|
3
3
|
|
4
4
|
desc "deliver", "Build app and push app image to registry then pull image on servers"
|
5
5
|
def deliver
|
6
|
-
|
6
|
+
mutating do
|
7
7
|
push
|
8
8
|
pull
|
9
9
|
end
|
@@ -11,7 +11,7 @@ class Mrsk::Cli::Build < Mrsk::Cli::Base
|
|
11
11
|
|
12
12
|
desc "push", "Build and push app image to registry"
|
13
13
|
def push
|
14
|
-
|
14
|
+
mutating do
|
15
15
|
cli = self
|
16
16
|
|
17
17
|
verify_local_dependencies
|
@@ -37,7 +37,7 @@ class Mrsk::Cli::Build < Mrsk::Cli::Base
|
|
37
37
|
|
38
38
|
desc "pull", "Pull app image from registry onto servers"
|
39
39
|
def pull
|
40
|
-
|
40
|
+
mutating do
|
41
41
|
on(MRSK.hosts) do
|
42
42
|
execute *MRSK.auditor.record("Pulled image with version #{MRSK.config.version}"), verbosity: :debug
|
43
43
|
execute *MRSK.builder.clean, raise_on_non_zero_exit: false
|
@@ -48,7 +48,7 @@ class Mrsk::Cli::Build < Mrsk::Cli::Base
|
|
48
48
|
|
49
49
|
desc "create", "Create a build setup"
|
50
50
|
def create
|
51
|
-
|
51
|
+
mutating do
|
52
52
|
run_locally do
|
53
53
|
begin
|
54
54
|
debug "Using builder: #{MRSK.builder.name}"
|
@@ -67,7 +67,7 @@ class Mrsk::Cli::Build < Mrsk::Cli::Base
|
|
67
67
|
|
68
68
|
desc "remove", "Remove build setup"
|
69
69
|
def remove
|
70
|
-
|
70
|
+
mutating do
|
71
71
|
run_locally do
|
72
72
|
debug "Using builder: #{MRSK.builder.name}"
|
73
73
|
execute *MRSK.builder.remove
|
data/lib/mrsk/cli/main.rb
CHANGED
@@ -2,7 +2,7 @@ class Mrsk::Cli::Main < Mrsk::Cli::Base
|
|
2
2
|
desc "setup", "Setup all accessories and deploy app to servers"
|
3
3
|
def setup
|
4
4
|
print_runtime do
|
5
|
-
|
5
|
+
mutating do
|
6
6
|
invoke "mrsk:cli:server:bootstrap"
|
7
7
|
invoke "mrsk:cli:accessory:boot", [ "all" ]
|
8
8
|
deploy
|
@@ -14,7 +14,7 @@ class Mrsk::Cli::Main < Mrsk::Cli::Base
|
|
14
14
|
option :skip_push, aliases: "-P", type: :boolean, default: false, desc: "Skip image build and push"
|
15
15
|
def deploy
|
16
16
|
runtime = print_runtime do
|
17
|
-
|
17
|
+
mutating do
|
18
18
|
invoke_options = deploy_options
|
19
19
|
|
20
20
|
say "Log into image registry...", :magenta
|
@@ -53,7 +53,7 @@ class Mrsk::Cli::Main < Mrsk::Cli::Base
|
|
53
53
|
option :skip_push, aliases: "-P", type: :boolean, default: false, desc: "Skip image build and push"
|
54
54
|
def redeploy
|
55
55
|
runtime = print_runtime do
|
56
|
-
|
56
|
+
mutating do
|
57
57
|
invoke_options = deploy_options
|
58
58
|
|
59
59
|
if options[:skip_push]
|
@@ -83,7 +83,7 @@ class Mrsk::Cli::Main < Mrsk::Cli::Base
|
|
83
83
|
def rollback(version)
|
84
84
|
rolled_back = false
|
85
85
|
runtime = print_runtime do
|
86
|
-
|
86
|
+
mutating do
|
87
87
|
invoke_options = deploy_options
|
88
88
|
|
89
89
|
MRSK.config.version = version
|
@@ -180,7 +180,7 @@ class Mrsk::Cli::Main < Mrsk::Cli::Base
|
|
180
180
|
desc "remove", "Remove Traefik, app, accessories, and registry session from servers"
|
181
181
|
option :confirmed, aliases: "-y", type: :boolean, default: false, desc: "Proceed without confirmation question"
|
182
182
|
def remove
|
183
|
-
|
183
|
+
mutating do
|
184
184
|
if options[:confirmed] || ask("This will remove all containers and images. Are you sure?", limited_to: %w( y N ), default: "N") == "y"
|
185
185
|
invoke "mrsk:cli:traefik:remove", [], options.without(:confirmed)
|
186
186
|
invoke "mrsk:cli:app:remove", [], options.without(:confirmed)
|
data/lib/mrsk/cli/prune.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
class Mrsk::Cli::Prune < Mrsk::Cli::Base
|
2
2
|
desc "all", "Prune unused images and stopped containers"
|
3
3
|
def all
|
4
|
-
|
4
|
+
mutating do
|
5
5
|
containers
|
6
6
|
images
|
7
7
|
end
|
@@ -9,7 +9,7 @@ class Mrsk::Cli::Prune < Mrsk::Cli::Base
|
|
9
9
|
|
10
10
|
desc "images", "Prune dangling images"
|
11
11
|
def images
|
12
|
-
|
12
|
+
mutating do
|
13
13
|
on(MRSK.hosts) do
|
14
14
|
execute *MRSK.auditor.record("Pruned images"), verbosity: :debug
|
15
15
|
execute *MRSK.prune.dangling_images
|
@@ -20,7 +20,7 @@ class Mrsk::Cli::Prune < Mrsk::Cli::Base
|
|
20
20
|
|
21
21
|
desc "containers", "Prune all stopped containers, except the last 5"
|
22
22
|
def containers
|
23
|
-
|
23
|
+
mutating do
|
24
24
|
on(MRSK.hosts) do
|
25
25
|
execute *MRSK.auditor.record("Pruned containers"), verbosity: :debug
|
26
26
|
execute *MRSK.prune.containers
|
@@ -1,4 +1,4 @@
|
|
1
|
-
#!/bin/
|
1
|
+
#!/usr/bin/env ruby
|
2
2
|
|
3
3
|
# A sample pre-deploy hook
|
4
4
|
#
|
@@ -16,8 +16,6 @@
|
|
16
16
|
# MRSK_ROLE (if set)
|
17
17
|
# MRSK_DESTINATION (if set)
|
18
18
|
|
19
|
-
#!/usr/bin/env ruby
|
20
|
-
|
21
19
|
# Only check the build status for production deployments
|
22
20
|
if ENV["MRSK_COMMAND"] == "rollback" || ENV["MRSK_DESTINATION"] != "production"
|
23
21
|
exit 0
|
@@ -41,41 +39,70 @@ def exit_with_error(message)
|
|
41
39
|
exit 1
|
42
40
|
end
|
43
41
|
|
44
|
-
|
45
|
-
|
46
|
-
|
42
|
+
class GithubStatusChecks
|
43
|
+
attr_reader :remote_url, :git_sha, :github_client, :combined_status
|
44
|
+
|
45
|
+
def initialize
|
46
|
+
@remote_url = `git config --get remote.origin.url`.strip.delete_prefix("https://github.com/")
|
47
|
+
@git_sha = `git rev-parse HEAD`.strip
|
48
|
+
@github_client = Octokit::Client.new(access_token: ENV["GITHUB_TOKEN"])
|
49
|
+
refresh!
|
50
|
+
end
|
51
|
+
|
52
|
+
def refresh!
|
53
|
+
@combined_status = github_client.combined_status(remote_url, git_sha)
|
54
|
+
end
|
55
|
+
|
56
|
+
def state
|
57
|
+
combined_status[:state]
|
58
|
+
end
|
59
|
+
|
60
|
+
def first_status_url
|
61
|
+
first_status = combined_status[:statuses].find { |status| status[:state] == state }
|
62
|
+
first_status && first_status[:target_url]
|
63
|
+
end
|
64
|
+
|
65
|
+
def complete_count
|
66
|
+
combined_status[:statuses].count { |status| status[:state] != "pending"}
|
67
|
+
end
|
68
|
+
|
69
|
+
def total_count
|
70
|
+
combined_status[:statuses].count
|
71
|
+
end
|
72
|
+
|
73
|
+
def current_status
|
74
|
+
if total_count > 0
|
75
|
+
"Completed #{complete_count}/#{total_count} checks, see #{first_status_url} ..."
|
76
|
+
else
|
77
|
+
"Build not started..."
|
78
|
+
end
|
79
|
+
end
|
47
80
|
end
|
48
81
|
|
49
|
-
remote_url = `git config --get remote.origin.url`.strip.delete_prefix("https://github.com/")
|
50
|
-
git_sha = `git rev-parse HEAD`.strip
|
51
82
|
|
52
|
-
|
53
|
-
|
83
|
+
$stdout.sync = true
|
84
|
+
|
85
|
+
puts "Checking build status..."
|
54
86
|
attempts = 0
|
87
|
+
checks = GithubStatusChecks.new
|
55
88
|
|
56
89
|
begin
|
57
90
|
loop do
|
58
|
-
|
59
|
-
state = combined_status[:state]
|
60
|
-
first_status_url = first_status_url(combined_status, state)
|
61
|
-
|
62
|
-
case state
|
91
|
+
case checks.state
|
63
92
|
when "success"
|
64
|
-
puts "
|
93
|
+
puts "Checks passed, see #{checks.first_status_url}"
|
65
94
|
exit 0
|
66
95
|
when "failure"
|
67
|
-
exit_with_error "
|
96
|
+
exit_with_error "Checks failed, see #{checks.first_status_url}"
|
68
97
|
when "pending"
|
69
98
|
attempts += 1
|
70
99
|
end
|
71
100
|
|
72
|
-
|
73
|
-
|
74
|
-
if attempts == MAX_ATTEMPTS
|
75
|
-
exit_with_error "Build status is still pending, gave up after #{MAX_ATTEMPTS * ATTEMPTS_GAP} seconds"
|
76
|
-
end
|
101
|
+
exit_with_error "Checks are still pending, gave up after #{MAX_ATTEMPTS * ATTEMPTS_GAP} seconds" if attempts == MAX_ATTEMPTS
|
77
102
|
|
103
|
+
puts checks.current_status
|
78
104
|
sleep(ATTEMPTS_GAP)
|
105
|
+
checks.refresh!
|
79
106
|
end
|
80
107
|
rescue Octokit::NotFound
|
81
108
|
exit_with_error "Build status could not be found"
|
data/lib/mrsk/cli/traefik.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
class Mrsk::Cli::Traefik < Mrsk::Cli::Base
|
2
2
|
desc "boot", "Boot Traefik on servers"
|
3
3
|
def boot
|
4
|
-
|
4
|
+
mutating do
|
5
5
|
on(MRSK.traefik_hosts) do
|
6
6
|
execute *MRSK.registry.login
|
7
7
|
execute *MRSK.traefik.run, raise_on_non_zero_exit: false
|
@@ -11,7 +11,7 @@ class Mrsk::Cli::Traefik < Mrsk::Cli::Base
|
|
11
11
|
|
12
12
|
desc "reboot", "Reboot Traefik on servers (stop container, remove container, start new container)"
|
13
13
|
def reboot
|
14
|
-
|
14
|
+
mutating do
|
15
15
|
stop
|
16
16
|
remove_container
|
17
17
|
boot
|
@@ -20,7 +20,7 @@ class Mrsk::Cli::Traefik < Mrsk::Cli::Base
|
|
20
20
|
|
21
21
|
desc "start", "Start existing Traefik container on servers"
|
22
22
|
def start
|
23
|
-
|
23
|
+
mutating do
|
24
24
|
on(MRSK.traefik_hosts) do
|
25
25
|
execute *MRSK.auditor.record("Started traefik"), verbosity: :debug
|
26
26
|
execute *MRSK.traefik.start, raise_on_non_zero_exit: false
|
@@ -30,7 +30,7 @@ class Mrsk::Cli::Traefik < Mrsk::Cli::Base
|
|
30
30
|
|
31
31
|
desc "stop", "Stop existing Traefik container on servers"
|
32
32
|
def stop
|
33
|
-
|
33
|
+
mutating do
|
34
34
|
on(MRSK.traefik_hosts) do
|
35
35
|
execute *MRSK.auditor.record("Stopped traefik"), verbosity: :debug
|
36
36
|
execute *MRSK.traefik.stop, raise_on_non_zero_exit: false
|
@@ -40,7 +40,7 @@ class Mrsk::Cli::Traefik < Mrsk::Cli::Base
|
|
40
40
|
|
41
41
|
desc "restart", "Restart existing Traefik container on servers"
|
42
42
|
def restart
|
43
|
-
|
43
|
+
mutating do
|
44
44
|
stop
|
45
45
|
start
|
46
46
|
end
|
@@ -77,7 +77,7 @@ class Mrsk::Cli::Traefik < Mrsk::Cli::Base
|
|
77
77
|
|
78
78
|
desc "remove", "Remove Traefik container and image from servers"
|
79
79
|
def remove
|
80
|
-
|
80
|
+
mutating do
|
81
81
|
stop
|
82
82
|
remove_container
|
83
83
|
remove_image
|
@@ -86,7 +86,7 @@ class Mrsk::Cli::Traefik < Mrsk::Cli::Base
|
|
86
86
|
|
87
87
|
desc "remove_container", "Remove Traefik container from servers", hide: true
|
88
88
|
def remove_container
|
89
|
-
|
89
|
+
mutating do
|
90
90
|
on(MRSK.traefik_hosts) do
|
91
91
|
execute *MRSK.auditor.record("Removed traefik container"), verbosity: :debug
|
92
92
|
execute *MRSK.traefik.remove_container
|
@@ -96,7 +96,7 @@ class Mrsk::Cli::Traefik < Mrsk::Cli::Base
|
|
96
96
|
|
97
97
|
desc "remove_image", "Remove Traefik image from servers", hide: true
|
98
98
|
def remove_image
|
99
|
-
|
99
|
+
mutating do
|
100
100
|
on(MRSK.traefik_hosts) do
|
101
101
|
execute *MRSK.auditor.record("Removed traefik image"), verbosity: :debug
|
102
102
|
execute *MRSK.traefik.remove_image
|
data/lib/mrsk/commands/base.rb
CHANGED
@@ -13,7 +13,11 @@ module Mrsk::Commands
|
|
13
13
|
|
14
14
|
def run_over_ssh(*command, host:)
|
15
15
|
"ssh".tap do |cmd|
|
16
|
-
|
16
|
+
if config.ssh_proxy && config.ssh_proxy.is_a?(Net::SSH::Proxy::Jump)
|
17
|
+
cmd << " -J #{config.ssh_proxy.jump_proxies}"
|
18
|
+
elsif config.ssh_proxy && config.ssh_proxy.is_a?(Net::SSH::Proxy::Command)
|
19
|
+
cmd << " -o ProxyCommand='#{config.ssh_proxy.command_line_template}'"
|
20
|
+
end
|
17
21
|
cmd << " -t #{config.ssh_user}@#{host} '#{command.join(" ")}'"
|
18
22
|
end
|
19
23
|
end
|
@@ -3,6 +3,7 @@ class Mrsk::Commands::Builder::Base < Mrsk::Commands::Base
|
|
3
3
|
class BuilderError < StandardError; end
|
4
4
|
|
5
5
|
delegate :argumentize, to: Mrsk::Utils
|
6
|
+
delegate :args, :secrets, :dockerfile, :local_arch, :local_host, :remote_arch, :remote_host, :cache_from, :cache_to, to: :builder_config
|
6
7
|
|
7
8
|
def clean
|
8
9
|
docker :image, :rm, "--force", config.absolute_image
|
@@ -13,11 +14,11 @@ class Mrsk::Commands::Builder::Base < Mrsk::Commands::Base
|
|
13
14
|
end
|
14
15
|
|
15
16
|
def build_options
|
16
|
-
[ *build_tags, *build_labels, *build_args, *build_secrets, *build_dockerfile ]
|
17
|
+
[ *build_tags, *build_cache, *build_labels, *build_args, *build_secrets, *build_dockerfile ]
|
17
18
|
end
|
18
19
|
|
19
20
|
def build_context
|
20
|
-
context
|
21
|
+
config.builder.context
|
21
22
|
end
|
22
23
|
|
23
24
|
|
@@ -26,6 +27,13 @@ class Mrsk::Commands::Builder::Base < Mrsk::Commands::Base
|
|
26
27
|
[ "-t", config.absolute_image, "-t", config.latest_image ]
|
27
28
|
end
|
28
29
|
|
30
|
+
def build_cache
|
31
|
+
if cache_to && cache_from
|
32
|
+
["--cache-to", cache_to,
|
33
|
+
"--cache-from", cache_from]
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
29
37
|
def build_labels
|
30
38
|
argumentize "--label", { service: config.service }
|
31
39
|
end
|
@@ -46,19 +54,7 @@ class Mrsk::Commands::Builder::Base < Mrsk::Commands::Base
|
|
46
54
|
end
|
47
55
|
end
|
48
56
|
|
49
|
-
def
|
50
|
-
|
51
|
-
end
|
52
|
-
|
53
|
-
def secrets
|
54
|
-
(config.builder && config.builder["secrets"]) || []
|
55
|
-
end
|
56
|
-
|
57
|
-
def dockerfile
|
58
|
-
(config.builder && config.builder["dockerfile"]) || "Dockerfile"
|
59
|
-
end
|
60
|
-
|
61
|
-
def context
|
62
|
-
(config.builder && config.builder["context"]) || "."
|
57
|
+
def builder_config
|
58
|
+
config.builder
|
63
59
|
end
|
64
60
|
end
|
@@ -22,17 +22,17 @@ class Mrsk::Commands::Builder::Multiarch::Remote < Mrsk::Commands::Builder::Mult
|
|
22
22
|
end
|
23
23
|
|
24
24
|
def create_local_buildx
|
25
|
-
docker :buildx, :create, "--name", builder_name, builder_name_with_arch(
|
25
|
+
docker :buildx, :create, "--name", builder_name, builder_name_with_arch(local_arch), "--platform", "linux/#{local_arch}"
|
26
26
|
end
|
27
27
|
|
28
28
|
def append_remote_buildx
|
29
|
-
docker :buildx, :create, "--append", "--name", builder_name, builder_name_with_arch(
|
29
|
+
docker :buildx, :create, "--append", "--name", builder_name, builder_name_with_arch(remote_arch), "--platform", "linux/#{remote_arch}"
|
30
30
|
end
|
31
31
|
|
32
32
|
def create_contexts
|
33
33
|
combine \
|
34
|
-
create_context(
|
35
|
-
create_context(
|
34
|
+
create_context(local_arch, local_host),
|
35
|
+
create_context(remote_arch, remote_host)
|
36
36
|
end
|
37
37
|
|
38
38
|
def create_context(arch, host)
|
@@ -41,19 +41,11 @@ class Mrsk::Commands::Builder::Multiarch::Remote < Mrsk::Commands::Builder::Mult
|
|
41
41
|
|
42
42
|
def remove_contexts
|
43
43
|
combine \
|
44
|
-
remove_context(
|
45
|
-
remove_context(
|
44
|
+
remove_context(local_arch),
|
45
|
+
remove_context(remote_arch)
|
46
46
|
end
|
47
47
|
|
48
48
|
def remove_context(arch)
|
49
49
|
docker :context, :rm, builder_name_with_arch(arch)
|
50
50
|
end
|
51
|
-
|
52
|
-
def local
|
53
|
-
config.builder["local"]
|
54
|
-
end
|
55
|
-
|
56
|
-
def remote
|
57
|
-
config.builder["remote"]
|
58
|
-
end
|
59
51
|
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
class Mrsk::Commands::Builder::Native::Cached < Mrsk::Commands::Builder::Native
|
2
|
+
def create
|
3
|
+
docker :buildx, :create, "--use", "--driver=docker-container"
|
4
|
+
end
|
5
|
+
|
6
|
+
def remove
|
7
|
+
docker :buildx, :rm, builder_name
|
8
|
+
end
|
9
|
+
|
10
|
+
def push
|
11
|
+
docker :buildx, :build,
|
12
|
+
"--push",
|
13
|
+
*build_options,
|
14
|
+
build_context
|
15
|
+
end
|
16
|
+
end
|
@@ -28,29 +28,21 @@ class Mrsk::Commands::Builder::Native::Remote < Mrsk::Commands::Builder::Native
|
|
28
28
|
|
29
29
|
|
30
30
|
private
|
31
|
-
def arch
|
32
|
-
config.builder["remote"]["arch"]
|
33
|
-
end
|
34
|
-
|
35
|
-
def host
|
36
|
-
config.builder["remote"]["host"]
|
37
|
-
end
|
38
|
-
|
39
31
|
def builder_name
|
40
32
|
"mrsk-#{config.service}-native-remote"
|
41
33
|
end
|
42
34
|
|
43
35
|
def builder_name_with_arch
|
44
|
-
"#{builder_name}-#{
|
36
|
+
"#{builder_name}-#{remote_arch}"
|
45
37
|
end
|
46
38
|
|
47
39
|
def platform
|
48
|
-
"linux/#{
|
40
|
+
"linux/#{remote_arch}"
|
49
41
|
end
|
50
42
|
|
51
43
|
def create_context
|
52
44
|
docker :context, :create,
|
53
|
-
builder_name_with_arch, "--description", "'#{builder_name} #{
|
45
|
+
builder_name_with_arch, "--description", "'#{builder_name} #{remote_arch} native host'", "--docker", "'host=#{remote_host}'"
|
54
46
|
end
|
55
47
|
|
56
48
|
def remove_context
|
@@ -7,11 +7,13 @@ class Mrsk::Commands::Builder < Mrsk::Commands::Base
|
|
7
7
|
|
8
8
|
def target
|
9
9
|
case
|
10
|
-
when config.builder && config.builder
|
10
|
+
when !config.builder.multiarch? && !config.builder.cached?
|
11
11
|
native
|
12
|
-
when config.builder && config.builder
|
12
|
+
when !config.builder.multiarch? && config.builder.cached?
|
13
|
+
native_cached
|
14
|
+
when config.builder.local? && config.builder.remote?
|
13
15
|
multiarch_remote
|
14
|
-
when config.builder
|
16
|
+
when config.builder.remote?
|
15
17
|
native_remote
|
16
18
|
else
|
17
19
|
multiarch
|
@@ -22,6 +24,10 @@ class Mrsk::Commands::Builder < Mrsk::Commands::Base
|
|
22
24
|
@native ||= Mrsk::Commands::Builder::Native.new(config)
|
23
25
|
end
|
24
26
|
|
27
|
+
def native_cached
|
28
|
+
@native ||= Mrsk::Commands::Builder::Native::Cached.new(config)
|
29
|
+
end
|
30
|
+
|
25
31
|
def native_remote
|
26
32
|
@native ||= Mrsk::Commands::Builder::Native::Remote.new(config)
|
27
33
|
end
|
@@ -3,6 +3,9 @@ class Mrsk::Commands::Traefik < Mrsk::Commands::Base
|
|
3
3
|
|
4
4
|
DEFAULT_IMAGE = "traefik:v2.9"
|
5
5
|
CONTAINER_PORT = 80
|
6
|
+
DEFAULT_ARGS = {
|
7
|
+
'log.level' => 'DEBUG'
|
8
|
+
}
|
6
9
|
|
7
10
|
def run
|
8
11
|
docker :run, "--name traefik",
|
@@ -16,7 +19,6 @@ class Mrsk::Commands::Traefik < Mrsk::Commands::Base
|
|
16
19
|
*docker_options_args,
|
17
20
|
image,
|
18
21
|
"--providers.docker",
|
19
|
-
"--log.level=DEBUG",
|
20
22
|
*cmd_option_args
|
21
23
|
end
|
22
24
|
|
@@ -86,9 +88,9 @@ class Mrsk::Commands::Traefik < Mrsk::Commands::Base
|
|
86
88
|
|
87
89
|
def cmd_option_args
|
88
90
|
if args = config.traefik["args"]
|
89
|
-
optionize args, with: "="
|
91
|
+
optionize DEFAULT_ARGS.merge(args), with: "="
|
90
92
|
else
|
91
|
-
|
93
|
+
optionize DEFAULT_ARGS, with: "="
|
92
94
|
end
|
93
95
|
end
|
94
96
|
|
@@ -0,0 +1,114 @@
|
|
1
|
+
class Mrsk::Configuration::Builder
|
2
|
+
def initialize(config:)
|
3
|
+
@options = config.raw_config.builder || {}
|
4
|
+
@image = config.image
|
5
|
+
@server = config.registry["server"]
|
6
|
+
|
7
|
+
valid?
|
8
|
+
end
|
9
|
+
|
10
|
+
def to_h
|
11
|
+
@options
|
12
|
+
end
|
13
|
+
|
14
|
+
def multiarch?
|
15
|
+
@options["multiarch"] != false
|
16
|
+
end
|
17
|
+
|
18
|
+
def local?
|
19
|
+
!!@options["local"]
|
20
|
+
end
|
21
|
+
|
22
|
+
def remote?
|
23
|
+
!!@options["remote"]
|
24
|
+
end
|
25
|
+
|
26
|
+
def cached?
|
27
|
+
!!@options["cache"]
|
28
|
+
end
|
29
|
+
|
30
|
+
def args
|
31
|
+
@options["args"] || {}
|
32
|
+
end
|
33
|
+
|
34
|
+
def secrets
|
35
|
+
@options["secrets"] || []
|
36
|
+
end
|
37
|
+
|
38
|
+
def dockerfile
|
39
|
+
@options["dockerfile"] || "Dockerfile"
|
40
|
+
end
|
41
|
+
|
42
|
+
def context
|
43
|
+
@options["context"] || "."
|
44
|
+
end
|
45
|
+
|
46
|
+
def local_arch
|
47
|
+
@options["local"]["arch"] if local?
|
48
|
+
end
|
49
|
+
|
50
|
+
def local_host
|
51
|
+
@options["local"]["host"] if local?
|
52
|
+
end
|
53
|
+
|
54
|
+
def remote_arch
|
55
|
+
@options["remote"]["arch"] if remote?
|
56
|
+
end
|
57
|
+
|
58
|
+
def remote_host
|
59
|
+
@options["remote"]["host"] if remote?
|
60
|
+
end
|
61
|
+
|
62
|
+
def cache_from
|
63
|
+
if cached?
|
64
|
+
case @options["cache"]["type"]
|
65
|
+
when "gha"
|
66
|
+
cache_from_config_for_gha
|
67
|
+
when "registry"
|
68
|
+
cache_from_config_for_registry
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def cache_to
|
74
|
+
if cached?
|
75
|
+
case @options["cache"]["type"]
|
76
|
+
when "gha"
|
77
|
+
cache_to_config_for_gha
|
78
|
+
when "registry"
|
79
|
+
cache_to_config_for_registry
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
private
|
85
|
+
def valid?
|
86
|
+
if @options["local"] && !@options["remote"]
|
87
|
+
raise ArgumentError, "You must specify both local and remote builder config for remote multiarch builds"
|
88
|
+
end
|
89
|
+
|
90
|
+
if @options["cache"] && @options["cache"]["type"]
|
91
|
+
raise ArgumentError, "Invalid cache type: #{@options["cache"]["type"]}" unless ["gha", "registry"].include?(@options["cache"]["type"])
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def cache_image
|
96
|
+
@options["cache"]&.fetch("image", nil) || "#{@image}-build-cache"
|
97
|
+
end
|
98
|
+
|
99
|
+
def cache_from_config_for_gha
|
100
|
+
"type=gha"
|
101
|
+
end
|
102
|
+
|
103
|
+
def cache_from_config_for_registry
|
104
|
+
[ "type=registry", "ref=#{@server}/#{cache_image}" ].compact.join(",")
|
105
|
+
end
|
106
|
+
|
107
|
+
def cache_to_config_for_gha
|
108
|
+
[ "type=gha", @options["cache"]&.fetch("options", nil)].compact.join(",")
|
109
|
+
end
|
110
|
+
|
111
|
+
def cache_to_config_for_registry
|
112
|
+
[ "type=registry", @options["cache"]&.fetch("options", nil), "ref=#{@server}/#{cache_image}" ].compact.join(",")
|
113
|
+
end
|
114
|
+
end
|
data/lib/mrsk/configuration.rb
CHANGED
@@ -6,7 +6,7 @@ require "erb"
|
|
6
6
|
require "net/ssh/proxy/jump"
|
7
7
|
|
8
8
|
class Mrsk::Configuration
|
9
|
-
delegate :service, :image, :servers, :env, :labels, :registry, :
|
9
|
+
delegate :service, :image, :servers, :env, :labels, :registry, :stop_wait_time, :hooks_path, to: :raw_config, allow_nil: true
|
10
10
|
delegate :argumentize, :argumentize_env_with_secrets, :optionize, to: Mrsk::Utils
|
11
11
|
|
12
12
|
attr_accessor :destination
|
@@ -165,8 +165,12 @@ class Mrsk::Configuration
|
|
165
165
|
raw_config.readiness_delay || 7
|
166
166
|
end
|
167
167
|
|
168
|
+
def minimum_version
|
169
|
+
raw_config.minimum_version
|
170
|
+
end
|
171
|
+
|
168
172
|
def valid?
|
169
|
-
ensure_required_keys_present &&
|
173
|
+
ensure_required_keys_present && ensure_valid_mrsk_version
|
170
174
|
end
|
171
175
|
|
172
176
|
|
@@ -182,7 +186,7 @@ class Mrsk::Configuration
|
|
182
186
|
env_args: env_args,
|
183
187
|
volume_args: volume_args,
|
184
188
|
ssh_options: ssh_options,
|
185
|
-
builder:
|
189
|
+
builder: builder.to_h,
|
186
190
|
accessories: raw_config.accessories,
|
187
191
|
logging: logging_args,
|
188
192
|
healthcheck: healthcheck
|
@@ -197,6 +201,18 @@ class Mrsk::Configuration
|
|
197
201
|
raw_config.hooks_path || ".mrsk/hooks"
|
198
202
|
end
|
199
203
|
|
204
|
+
def builder
|
205
|
+
Mrsk::Configuration::Builder.new(config: self)
|
206
|
+
end
|
207
|
+
|
208
|
+
# Will raise KeyError if any secret ENVs are missing
|
209
|
+
def ensure_env_available
|
210
|
+
env_args
|
211
|
+
roles.each(&:env_args)
|
212
|
+
|
213
|
+
true
|
214
|
+
end
|
215
|
+
|
200
216
|
private
|
201
217
|
# Will raise ArgumentError if any required config keys are missing
|
202
218
|
def ensure_required_keys_present
|
@@ -221,14 +237,15 @@ class Mrsk::Configuration
|
|
221
237
|
true
|
222
238
|
end
|
223
239
|
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
240
|
+
def ensure_valid_mrsk_version
|
241
|
+
if minimum_version && Gem::Version.new(minimum_version) > Gem::Version.new(Mrsk::VERSION)
|
242
|
+
raise ArgumentError, "Current version is #{Mrsk::VERSION}, minimum required is #{minimum_version}"
|
243
|
+
end
|
228
244
|
|
229
245
|
true
|
230
246
|
end
|
231
247
|
|
248
|
+
|
232
249
|
def role_names
|
233
250
|
raw_config.servers.is_a?(Array) ? [ "web" ] : raw_config.servers.keys.sort
|
234
251
|
end
|
data/lib/mrsk/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mrsk
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.14.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- David Heinemeier Hansson
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-06-
|
11
|
+
date: 2023-06-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -204,6 +204,7 @@ files:
|
|
204
204
|
- lib/mrsk/commands/builder/multiarch.rb
|
205
205
|
- lib/mrsk/commands/builder/multiarch/remote.rb
|
206
206
|
- lib/mrsk/commands/builder/native.rb
|
207
|
+
- lib/mrsk/commands/builder/native/cached.rb
|
207
208
|
- lib/mrsk/commands/builder/native/remote.rb
|
208
209
|
- lib/mrsk/commands/docker.rb
|
209
210
|
- lib/mrsk/commands/healthcheck.rb
|
@@ -215,6 +216,7 @@ files:
|
|
215
216
|
- lib/mrsk/configuration.rb
|
216
217
|
- lib/mrsk/configuration/accessory.rb
|
217
218
|
- lib/mrsk/configuration/boot.rb
|
219
|
+
- lib/mrsk/configuration/builder.rb
|
218
220
|
- lib/mrsk/configuration/role.rb
|
219
221
|
- lib/mrsk/sshkit_with_ext.rb
|
220
222
|
- lib/mrsk/tags.rb
|