mrsk 0.13.2 → 0.15.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3a752ca316aea9ce851cc9b4ac96cd7fde613f94dd1b63e238c3320b10f82e8c
4
- data.tar.gz: 689bba95ccc72b27b9cc85572b6e4d995f6bfed3d09be30c2076a81ed1eb07f6
3
+ metadata.gz: 0b7e74f66fc6480dd991ce88dc5a2527832c6c61ff85ede26625e1e86f1650c9
4
+ data.tar.gz: 9621b2ae90f53dc652fc41abfa28b000bac895bfcf4a7dc4c82db72787f78e08
5
5
  SHA512:
6
- metadata.gz: cdbb9e9b58364ba933778bb18bf4576ee3c611fcbb3be9b341c2e21d53d08d37bd524c694e058be4456f0d7da837b488d20c009973017fa39f6c57fb5835d0ce
7
- data.tar.gz: eb58c8ac236172f91719582f7b9606d2255c122be41c7f341a6a54e6b1680b4715f55ad99492ee47a57c38b74626b129d92bd77ed0d864042a7c457343aade13
6
+ metadata.gz: 6bd519ba6639ae9f8c565177a4cbfd2bcf6e725452b76b1b95032f7e8288227b2ae3e3550d1fd946eb85a37c36b2b2a03419c097f64f0777110b3c06f128a545
7
+ data.tar.gz: 119a7b8551b5f72a2ba54c0f5c059ef36a47a85852616ffd6df04cd3b168113b5892f571609a228c71330cd3cd69045f7fc03e3005a1b95aec41d3ee6a91c4d4
data/README.md CHANGED
@@ -63,6 +63,24 @@ This will:
63
63
 
64
64
  Voila! All the servers are now serving the app on port 80. If you're just running a single server, you're ready to go. If you're running multiple servers, you need to put a load balancer in front of them. For subsequent deploys, or if your servers already have Docker and curl installed, you can just run `mrsk deploy`.
65
65
 
66
+ ### Rails <7 usage
67
+
68
+ MRSK is not needed to be in your application Gemfile to be used. However, if you want to guarantee specific MRSK version in your CI/CD workflows, you can create a separate Gemfile for MRSK, for example, `gemfile/mrsk.gemfile`:
69
+
70
+ ```ruby
71
+ source 'https://rubygems.org'
72
+
73
+ gem 'mrsk', '~> 0.14'
74
+ ```
75
+
76
+ Bundle with `BUNDLE_GEMFILE=gemfiles/mrsk.gemfile bundle`.
77
+
78
+ After this MRSK can be used for deployment:
79
+
80
+ ```sh
81
+ BUNDLE_GEMFILE=gemfiles/mrsk.gemfile bundle exec mrsk deploy
82
+ ```
83
+
66
84
  ## Vision
67
85
 
68
86
  In the past decade+, there's been an explosion in commercial offerings that make deploying web apps easier. Heroku kicked it off with an incredible offering that stayed ahead of the competition seemingly forever. These days we have excellent alternatives like Fly.io and Render. And hosted Kubernetes is making things easier too on AWS, GCP, Digital Ocean, and elsewhere. But these are all offerings that have you renting computers in the cloud at a premium. If you want to run on your own hardware, or even just have a clear migration path to do so in the future, you need to carefully consider how locked in you get to these commercial platforms. Preferably before the bills swallow your business whole!
@@ -123,6 +141,8 @@ This template can safely be checked into git. Then everyone deploying the app ca
123
141
 
124
142
  If you need separate env variables for different destinations, you can set them with `.env.destination.erb` for the template, which will generate `.env.staging` when run with `mrsk envify -d staging`.
125
143
 
144
+ Note: If you utilize biometrics with 1Password you can remove the `session_token` related parts in the example and just call `op read op://Vault/Docker Hub/password -n`.
145
+
126
146
  #### Bitwarden as a secret store
127
147
 
128
148
  If you are using open source secret store like bitwarden, you can create `.env.erb` as a template which looks up the secrets.
@@ -206,13 +226,13 @@ ssh:
206
226
  user: app
207
227
  ```
208
228
 
209
- If you are using non-root user, you need to bootstrap your servers manually, before using them with MRSK. On Ubuntu, you'd do:
229
+ If you are using non-root user (`app` as above example), you need to bootstrap your servers manually, before using them with MRSK. On Ubuntu, you'd do:
210
230
 
211
231
  ```bash
212
232
  sudo apt update
213
233
  sudo apt upgrade -y
214
234
  sudo apt install -y docker.io curl git
215
- sudo usermod -a -G docker ubuntu
235
+ sudo usermod -a -G docker app
216
236
  ```
217
237
 
218
238
  ### Using a proxy SSH host
@@ -238,6 +258,15 @@ ssh:
238
258
  proxy_command: aws ssm start-session --target %h --document-name AWS-StartSSHSession --parameters 'portNumber=%p' --region=us-east-1 ## ssh via aws ssm
239
259
  ```
240
260
 
261
+ ### Configuring the SSH log level
262
+
263
+ ```yaml
264
+ ssh:
265
+ log_level: debug
266
+ ```
267
+
268
+ Valid levels are `debug`, `info`, `warn`, `error` and `fatal` (default).
269
+
241
270
  ### Using env variables
242
271
 
243
272
  You can inject env variables into the app containers using `env`:
@@ -380,6 +409,16 @@ servers:
380
409
 
381
410
  That'll start the job containers with `docker run ... --cap-add --cpu-count 4 ...`.
382
411
 
412
+ ### Setting a minimum version
413
+
414
+ You can set the minimum MRSK version with:
415
+
416
+ ```yaml
417
+ minimum_version: 0.13.3
418
+ ```
419
+
420
+ Note: versions <= 0.13.2 will ignore this setting.
421
+
383
422
  ### Configuring logging
384
423
 
385
424
  You can configure the logging driver and options passed to Docker using `logging`:
@@ -463,6 +502,37 @@ builder:
463
502
  context: ".."
464
503
  ```
465
504
 
505
+ ### Using multistage builder cache
506
+
507
+ 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:
508
+
509
+ ```yaml
510
+ # Using GHA cache
511
+ builder:
512
+ cache:
513
+ type: gha
514
+
515
+ # Using Registry cache
516
+ builder:
517
+ cache:
518
+ type: registry
519
+
520
+ # Using Registry cache with different cache image
521
+ builder:
522
+ cache:
523
+ type: registry
524
+ # default image name is <image>-build-cache
525
+ image: application-cache-image
526
+
527
+ # Using Registry cache with additinonal cache-to options
528
+ builder:
529
+ cache:
530
+ type: registry
531
+ options: mode=max,image-manifest=true,oci-mediatypes=true
532
+ ```
533
+
534
+ For further insights into build cache optimization, check out documentation on Docker's official website: https://docs.docker.com/build/cache/.
535
+
466
536
  ### Using build secrets for new images
467
537
 
468
538
  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:
@@ -581,6 +651,16 @@ traefik:
581
651
  entrypoints.otherentrypoint.address: ':9000'
582
652
  ```
583
653
 
654
+ ### Rebooting Traefik
655
+
656
+ If you make changes to Traefik args or labels, you'll need to reboot with:
657
+
658
+ `mrsk traefik reboot`
659
+
660
+ In production, reboot the Traefik containers one by one with a slower but safer approach, using a rolling reboot:
661
+
662
+ `mrsk traefik reboot --rolling`
663
+
584
664
  ### Configuring build args for new images
585
665
 
586
666
  Build arguments that aren't secret can also be configured:
@@ -825,7 +905,7 @@ If you wish to remove the entire application, including Traefik, containers, ima
825
905
 
826
906
  ## Locking
827
907
 
828
- Commands that are unsafe to run concurrently will take a deploy lock while they run. The lock is the `mrsk_lock` directory on the primary server.
908
+ Commands that are unsafe to run concurrently will take a deploy lock while they run. The lock is the `mrsk_lock-<service>` directory on the primary server.
829
909
 
830
910
  You can check the lock status with:
831
911
 
@@ -881,8 +961,8 @@ firing a JSON webhook. These variables include:
881
961
  - `MRSK_RECORDED_AT` - UTC timestamp in ISO 8601 format, e.g. `2023-04-14T17:07:31Z`
882
962
  - `MRSK_PERFORMER` - the local user performing the command (from `whoami`)
883
963
  - `MRSK_SERVICE_VERSION` - an abbreviated service and version for use in messages, e.g. app@150b24f
884
- - `MRSK_VERSION` - an full version being deployed
885
- - `MRSK_HOSTS` - a comma separated list of the hosts targeted by the command
964
+ - `MRSK_VERSION` - the full version being deployed
965
+ - `MRSK_HOSTS` - a comma-separated list of the hosts targeted by the command
886
966
  - `MRSK_COMMAND` - The command we are running
887
967
  - `MRSK_SUBCOMMAND` - optional: The subcommand we are running
888
968
  - `MRSK_DESTINATION` - optional: destination, e.g. "staging"
@@ -899,9 +979,8 @@ Used for pre-build checks - e.g. there are no uncommitted changes or that CI has
899
979
  3. pre-deploy
900
980
  For final checks before deploying, e.g. checking CI completed
901
981
 
902
- 3. post-deploy - run after a deploy, redeploy or rollback
903
-
904
- This hook is also passed a `MRSK_RUNTIME` env variable.
982
+ 3. post-deploy - run after a deploy, redeploy or rollback.
983
+ This hook is also passed a `MRSK_RUNTIME` env variable set to the total seconds the deploy took.
905
984
 
906
985
  This could be used to broadcast a deployment message, or register the new version with an APM.
907
986
 
@@ -912,7 +991,7 @@ The command could look something like:
912
991
  curl -q -d content="[My App] ${MRSK_PERFORMER} Rolled back to version ${MRSK_VERSION}" https://3.basecamp.com/XXXXX/integrations/XXXXX/buckets/XXXXX/chats/XXXXX/lines
913
992
  ```
914
993
 
915
- That'll post a line like follows to a preconfigured chatbot in Basecamp:
994
+ That'll post a line like the following to a preconfigured chatbot in Basecamp:
916
995
 
917
996
  ```
918
997
  [My App] [dhh] Rolled back to version d264c4e92470ad1bd18590f04466787262f605de
@@ -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
- def boot(name)
4
- with_lock do
3
+ def boot(name, login: true)
4
+ mutating do
5
5
  if name == "all"
6
6
  MRSK.accessory_names.each { |accessory_name| boot(accessory_name) }
7
7
  else
@@ -10,7 +10,7 @@ class Mrsk::Cli::Accessory < Mrsk::Cli::Base
10
10
  upload(name)
11
11
 
12
12
  on(accessory.hosts) do
13
- execute *MRSK.registry.login
13
+ execute *MRSK.registry.login if login
14
14
  execute *MRSK.auditor.record("Booted #{name} accessory"), verbosity: :debug
15
15
  execute *accessory.run
16
16
  end
@@ -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
- with_lock do
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
- with_lock do
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,18 +51,22 @@ 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
- with_lock do
54
+ mutating do
55
55
  with_accessory(name) do |accessory|
56
+ on(accessory.hosts) do
57
+ execute *MRSK.registry.login
58
+ end
59
+
56
60
  stop(name)
57
61
  remove_container(name)
58
- boot(name)
62
+ boot(name, login: false)
59
63
  end
60
64
  end
61
65
  end
62
66
 
63
67
  desc "start [NAME]", "Start existing accessory container on host"
64
68
  def start(name)
65
- with_lock do
69
+ mutating do
66
70
  with_accessory(name) do |accessory|
67
71
  on(accessory.hosts) do
68
72
  execute *MRSK.auditor.record("Started #{name} accessory"), verbosity: :debug
@@ -74,7 +78,7 @@ class Mrsk::Cli::Accessory < Mrsk::Cli::Base
74
78
 
75
79
  desc "stop [NAME]", "Stop existing accessory container on host"
76
80
  def stop(name)
77
- with_lock do
81
+ mutating do
78
82
  with_accessory(name) do |accessory|
79
83
  on(accessory.hosts) do
80
84
  execute *MRSK.auditor.record("Stopped #{name} accessory"), verbosity: :debug
@@ -86,7 +90,7 @@ class Mrsk::Cli::Accessory < Mrsk::Cli::Base
86
90
 
87
91
  desc "restart [NAME]", "Restart existing accessory container on host"
88
92
  def restart(name)
89
- with_lock do
93
+ mutating do
90
94
  with_accessory(name) do
91
95
  stop(name)
92
96
  start(name)
@@ -165,7 +169,7 @@ class Mrsk::Cli::Accessory < Mrsk::Cli::Base
165
169
  desc "remove [NAME]", "Remove accessory container, image and data directory from host (use NAME=all to remove all accessories)"
166
170
  option :confirmed, aliases: "-y", type: :boolean, default: false, desc: "Proceed without confirmation question"
167
171
  def remove(name)
168
- with_lock do
172
+ mutating do
169
173
  if name == "all"
170
174
  MRSK.accessory_names.each { |accessory_name| remove(accessory_name) }
171
175
  else
@@ -183,7 +187,7 @@ class Mrsk::Cli::Accessory < Mrsk::Cli::Base
183
187
 
184
188
  desc "remove_container [NAME]", "Remove accessory container from host", hide: true
185
189
  def remove_container(name)
186
- with_lock do
190
+ mutating do
187
191
  with_accessory(name) do |accessory|
188
192
  on(accessory.hosts) do
189
193
  execute *MRSK.auditor.record("Remove #{name} accessory container"), verbosity: :debug
@@ -195,7 +199,7 @@ class Mrsk::Cli::Accessory < Mrsk::Cli::Base
195
199
 
196
200
  desc "remove_image [NAME]", "Remove accessory image from host", hide: true
197
201
  def remove_image(name)
198
- with_lock do
202
+ mutating do
199
203
  with_accessory(name) do |accessory|
200
204
  on(accessory.hosts) do
201
205
  execute *MRSK.auditor.record("Removed #{name} accessory image"), verbosity: :debug
@@ -207,7 +211,7 @@ class Mrsk::Cli::Accessory < Mrsk::Cli::Base
207
211
 
208
212
  desc "remove_service_directory [NAME]", "Remove accessory directory used for uploaded files and data directories from host", hide: true
209
213
  def remove_service_directory(name)
210
- with_lock do
214
+ mutating do
211
215
  with_accessory(name) do |accessory|
212
216
  on(accessory.hosts) do
213
217
  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
- with_lock do
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
- with_lock do
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
- with_lock do
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
- with_lock do
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
- with_lock do
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
- with_lock do
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
- with_lock do
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
- with_lock do
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 with_lock
76
- if MRSK.holding_lock?
77
- yield
78
- else
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
- acquire_lock
80
+ run_hook "pre-connect"
82
81
 
83
- begin
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
- raise
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
- release_lock
93
+ raise
96
94
  end
95
+
96
+ release_lock
97
97
  end
98
98
 
99
99
  def acquire_lock
@@ -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
- with_lock do
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
- with_lock do
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
- with_lock do
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
- with_lock do
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
- with_lock do
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
- with_lock do
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
- with_lock do
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
- with_lock do
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
- with_lock do
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
- with_lock do
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)
@@ -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
- with_lock do
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
- with_lock do
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
- with_lock do
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/sh
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
- def first_status_url(combined_status, state)
45
- first_status = combined_status[:statuses].find { |status| status[:state] == state }
46
- first_status && first_status[:target_url]
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
- repository = Octokit::Repository.from_url(remote_url)
53
- github_client = Octokit::Client.new(access_token: ENV["GITHUB_TOKEN"])
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
- combined_status = github_client.combined_status(remote_url, git_sha)
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 "Build passed, see #{first_status_url}"
93
+ puts "Checks passed, see #{checks.first_status_url}"
65
94
  exit 0
66
95
  when "failure"
67
- exit_with_error "Build failed, see #{first_status_url}"
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
- puts "Waiting #{ATTEMPTS_GAP} more seconds for build to complete#{", see #{first_status_url}" if first_status_url}..."
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"
@@ -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
- with_lock do
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
@@ -10,17 +10,22 @@ class Mrsk::Cli::Traefik < Mrsk::Cli::Base
10
10
  end
11
11
 
12
12
  desc "reboot", "Reboot Traefik on servers (stop container, remove container, start new container)"
13
+ option :rolling, type: :boolean, default: false, desc: "Reboot traefik on hosts in sequence, rather than in parallel"
13
14
  def reboot
14
- with_lock do
15
- stop
16
- remove_container
17
- boot
15
+ mutating do
16
+ on(MRSK.traefik_hosts, in: options[:rolling] ? :sequence : :parallel) do
17
+ execute *MRSK.auditor.record("Rebooted traefik"), verbosity: :debug
18
+ execute *MRSK.registry.login
19
+ execute *MRSK.traefik.stop, raise_on_non_zero_exit: false
20
+ execute *MRSK.traefik.remove_container
21
+ execute *MRSK.traefik.run, raise_on_non_zero_exit: false
22
+ end
18
23
  end
19
24
  end
20
25
 
21
26
  desc "start", "Start existing Traefik container on servers"
22
27
  def start
23
- with_lock do
28
+ mutating do
24
29
  on(MRSK.traefik_hosts) do
25
30
  execute *MRSK.auditor.record("Started traefik"), verbosity: :debug
26
31
  execute *MRSK.traefik.start, raise_on_non_zero_exit: false
@@ -30,7 +35,7 @@ class Mrsk::Cli::Traefik < Mrsk::Cli::Base
30
35
 
31
36
  desc "stop", "Stop existing Traefik container on servers"
32
37
  def stop
33
- with_lock do
38
+ mutating do
34
39
  on(MRSK.traefik_hosts) do
35
40
  execute *MRSK.auditor.record("Stopped traefik"), verbosity: :debug
36
41
  execute *MRSK.traefik.stop, raise_on_non_zero_exit: false
@@ -40,7 +45,7 @@ class Mrsk::Cli::Traefik < Mrsk::Cli::Base
40
45
 
41
46
  desc "restart", "Restart existing Traefik container on servers"
42
47
  def restart
43
- with_lock do
48
+ mutating do
44
49
  stop
45
50
  start
46
51
  end
@@ -77,7 +82,7 @@ class Mrsk::Cli::Traefik < Mrsk::Cli::Base
77
82
 
78
83
  desc "remove", "Remove Traefik container and image from servers"
79
84
  def remove
80
- with_lock do
85
+ mutating do
81
86
  stop
82
87
  remove_container
83
88
  remove_image
@@ -86,7 +91,7 @@ class Mrsk::Cli::Traefik < Mrsk::Cli::Base
86
91
 
87
92
  desc "remove_container", "Remove Traefik container from servers", hide: true
88
93
  def remove_container
89
- with_lock do
94
+ mutating do
90
95
  on(MRSK.traefik_hosts) do
91
96
  execute *MRSK.auditor.record("Removed traefik container"), verbosity: :debug
92
97
  execute *MRSK.traefik.remove_container
@@ -96,7 +101,7 @@ class Mrsk::Cli::Traefik < Mrsk::Cli::Base
96
101
 
97
102
  desc "remove_image", "Remove Traefik image from servers", hide: true
98
103
  def remove_image
99
- with_lock do
104
+ mutating do
100
105
  on(MRSK.traefik_hosts) do
101
106
  execute *MRSK.auditor.record("Removed traefik image"), verbosity: :debug
102
107
  execute *MRSK.traefik.remove_image
@@ -13,7 +13,11 @@ module Mrsk::Commands
13
13
 
14
14
  def run_over_ssh(*command, host:)
15
15
  "ssh".tap do |cmd|
16
- cmd << " -J #{config.ssh_proxy.jump_proxies}" if config.ssh_proxy
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 args
50
- (config.builder && config.builder["args"]) || {}
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(local["arch"]), "--platform", "linux/#{local["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(remote["arch"]), "--platform", "linux/#{remote["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(local["arch"], local["host"]),
35
- create_context(remote["arch"], remote["host"])
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(local["arch"]),
45
- remove_context(remote["arch"])
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}-#{arch}"
36
+ "#{builder_name}-#{remote_arch}"
45
37
  end
46
38
 
47
39
  def platform
48
- "linux/#{arch}"
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} #{arch} native host'", "--docker", "'host=#{host}'"
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
@@ -1,10 +1,10 @@
1
1
  class Mrsk::Commands::Builder::Native < Mrsk::Commands::Builder::Base
2
2
  def create
3
- # No-op on native
3
+ # No-op on native without cache
4
4
  end
5
5
 
6
6
  def remove
7
- # No-op on native
7
+ # No-op on native without cache
8
8
  end
9
9
 
10
10
  def push
@@ -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["multiarch"] == false
10
+ when !config.builder.multiarch? && !config.builder.cached?
11
11
  native
12
- when config.builder && config.builder["local"] && config.builder["remote"]
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 && config.builder["remote"]
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
@@ -40,7 +40,7 @@ class Mrsk::Commands::Lock < Mrsk::Commands::Base
40
40
  end
41
41
 
42
42
  def lock_dir
43
- :mrsk_lock
43
+ "mrsk_lock-#{config.service}"
44
44
  end
45
45
 
46
46
  def lock_details_file
@@ -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
@@ -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, :builder, :stop_wait_time, :hooks_path, to: :raw_config, allow_nil: true
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
@@ -153,7 +153,15 @@ class Mrsk::Configuration
153
153
  end
154
154
 
155
155
  def ssh_options
156
- { user: ssh_user, proxy: ssh_proxy, auth_methods: [ "publickey" ] }.compact
156
+ { user: ssh_user, proxy: ssh_proxy, auth_methods: [ "publickey" ], logger: ssh_logger }.compact
157
+ end
158
+
159
+ def ssh_logger
160
+ @ssh_logger ||= ::Logger.new(STDERR).tap { |logger| logger.level = ssh_log_level }
161
+ end
162
+
163
+ def ssh_log_level
164
+ (raw_config.ssh && raw_config.ssh["log_level"]) || ::Logger::FATAL
157
165
  end
158
166
 
159
167
 
@@ -165,8 +173,12 @@ class Mrsk::Configuration
165
173
  raw_config.readiness_delay || 7
166
174
  end
167
175
 
176
+ def minimum_version
177
+ raw_config.minimum_version
178
+ end
179
+
168
180
  def valid?
169
- ensure_required_keys_present && ensure_env_available
181
+ ensure_required_keys_present && ensure_valid_mrsk_version
170
182
  end
171
183
 
172
184
 
@@ -181,8 +193,9 @@ class Mrsk::Configuration
181
193
  service_with_version: service_with_version,
182
194
  env_args: env_args,
183
195
  volume_args: volume_args,
184
- ssh_options: ssh_options,
185
- builder: raw_config.builder,
196
+ ssh_options: ssh_options.except(:logger),
197
+ ssh_log_level: ssh_log_level,
198
+ builder: builder.to_h,
186
199
  accessories: raw_config.accessories,
187
200
  logging: logging_args,
188
201
  healthcheck: healthcheck
@@ -197,6 +210,18 @@ class Mrsk::Configuration
197
210
  raw_config.hooks_path || ".mrsk/hooks"
198
211
  end
199
212
 
213
+ def builder
214
+ Mrsk::Configuration::Builder.new(config: self)
215
+ end
216
+
217
+ # Will raise KeyError if any secret ENVs are missing
218
+ def ensure_env_available
219
+ env_args
220
+ roles.each(&:env_args)
221
+
222
+ true
223
+ end
224
+
200
225
  private
201
226
  # Will raise ArgumentError if any required config keys are missing
202
227
  def ensure_required_keys_present
@@ -221,14 +246,15 @@ class Mrsk::Configuration
221
246
  true
222
247
  end
223
248
 
224
- # Will raise KeyError if any secret ENVs are missing
225
- def ensure_env_available
226
- env_args
227
- roles.each(&:env_args)
249
+ def ensure_valid_mrsk_version
250
+ if minimum_version && Gem::Version.new(minimum_version) > Gem::Version.new(Mrsk::VERSION)
251
+ raise ArgumentError, "Current version is #{Mrsk::VERSION}, minimum required is #{minimum_version}"
252
+ end
228
253
 
229
254
  true
230
255
  end
231
256
 
257
+
232
258
  def role_names
233
259
  raw_config.servers.is_a?(Array) ? [ "web" ] : raw_config.servers.keys.sort
234
260
  end
data/lib/mrsk/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Mrsk
2
- VERSION = "0.13.2"
2
+ VERSION = "0.15.0"
3
3
  end
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.13.2
4
+ version: 0.15.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-01 00:00:00.000000000 Z
11
+ date: 2023-07-24 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
@@ -241,7 +243,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
241
243
  - !ruby/object:Gem::Version
242
244
  version: '0'
243
245
  requirements: []
244
- rubygems_version: 3.4.10
246
+ rubygems_version: 3.4.16
245
247
  signing_key:
246
248
  specification_version: 4
247
249
  summary: Deploy web apps in containers to servers running Docker with zero downtime.