nocoffee-kamal 2.3.0.1 → 2.3.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: eb35407e368cbba3b4910024786b8ab7cdde387c95f7fc2c13f42b012e4a70aa
4
- data.tar.gz: ef27660c970f54c9ba62305fa42f308835c661551f117e7964984d3437eb650c
3
+ metadata.gz: 223e16fa86229aef57b0240731cc7f2941f224aa4552e605bd5d7182166084c2
4
+ data.tar.gz: bb2d69b0ffe59f22eaa1f7a22f0b368d5fbbef2a041bc17b96ac68aef28972fd
5
5
  SHA512:
6
- metadata.gz: d0b0ce4e4a9fe6943b2601f5ae9a50ff6cbe6a3a57d1e085a9f31d19e82aaa13434af30962c5392a794cdad3fb18598b0d9dad57d7cc53f84ad6ec82811f791f
7
- data.tar.gz: 5d6f6279a3c19bea79f62b523b27167f092488c8a75eb6f825f959ee3e6cc09ed2f8d249194d249f68abf513e0d417d5f00a2da9b70f12ecd8726c1003c1173a
6
+ metadata.gz: cbbd09944ed7dcdf4e4d0065e49800d04290b471ff6165bb9dacf3609e8ddbff6351309aebdccadf673690cc6b8fafcb8be75e2b88a29ea08823dd4cc7f0a86c
7
+ data.tar.gz: c3d96a182bb8e20becbfb9a8621a98d66f2415007278a568c3785b535b661f9aae549b7c5578130a23b086e54b989da2de7413860e8865c9e4cbd8050c3062a8
@@ -18,6 +18,11 @@ class Kamal::Cli::Accessory < Kamal::Cli::Base
18
18
  execute *accessory.ensure_env_directory
19
19
  upload! accessory.secrets_io, accessory.secrets_path, mode: "0600"
20
20
  execute *accessory.run
21
+
22
+ if accessory.running_proxy?
23
+ target = capture_with_info(*accessory.container_id_for(container_name: accessory.service_name, only_running: true)).strip
24
+ execute *accessory.deploy(target: target)
25
+ end
21
26
  end
22
27
  end
23
28
  end
@@ -75,6 +80,10 @@ class Kamal::Cli::Accessory < Kamal::Cli::Base
75
80
  on(hosts) do
76
81
  execute *KAMAL.auditor.record("Started #{name} accessory"), verbosity: :debug
77
82
  execute *accessory.start
83
+ if accessory.running_proxy?
84
+ target = capture_with_info(*accessory.container_id_for(container_name: accessory.service_name, only_running: true)).strip
85
+ execute *accessory.deploy(target: target)
86
+ end
78
87
  end
79
88
  end
80
89
  end
@@ -87,6 +96,11 @@ class Kamal::Cli::Accessory < Kamal::Cli::Base
87
96
  on(hosts) do
88
97
  execute *KAMAL.auditor.record("Stopped #{name} accessory"), verbosity: :debug
89
98
  execute *accessory.stop, raise_on_non_zero_exit: false
99
+
100
+ if accessory.running_proxy?
101
+ target = capture_with_info(*accessory.container_id_for(container_name: accessory.service_name, only_running: true)).strip
102
+ execute *accessory.remove if target
103
+ end
90
104
  end
91
105
  end
92
106
  end
@@ -112,14 +126,15 @@ class Kamal::Cli::Accessory < Kamal::Cli::Base
112
126
  end
113
127
  end
114
128
 
115
- desc "exec [NAME] [CMD]", "Execute a custom command on servers (use --help to show options)"
129
+ desc "exec [NAME] [CMD...]", "Execute a custom command on servers within the accessory container (use --help to show options)"
116
130
  option :interactive, aliases: "-i", type: :boolean, default: false, desc: "Execute command over ssh for an interactive shell (use for console/bash)"
117
131
  option :reuse, type: :boolean, default: false, desc: "Reuse currently running container instead of starting a new one"
118
- def exec(name, cmd)
132
+ def exec(name, *cmd)
133
+ cmd = Kamal::Utils.join_commands(cmd)
119
134
  with_accessory(name) do |accessory, hosts|
120
135
  case
121
136
  when options[:interactive] && options[:reuse]
122
- say "Launching interactive command with via SSH from existing container...", :magenta
137
+ say "Launching interactive command via SSH from existing container...", :magenta
123
138
  run_locally { exec accessory.execute_in_existing_container_over_ssh(cmd) }
124
139
 
125
140
  when options[:interactive]
@@ -128,16 +143,16 @@ class Kamal::Cli::Accessory < Kamal::Cli::Base
128
143
 
129
144
  when options[:reuse]
130
145
  say "Launching command from existing container...", :magenta
131
- on(hosts) do
146
+ on(hosts) do |host|
132
147
  execute *KAMAL.auditor.record("Executed cmd '#{cmd}' on #{name} accessory"), verbosity: :debug
133
- capture_with_info(*accessory.execute_in_existing_container(cmd))
148
+ puts_by_host host, capture_with_info(*accessory.execute_in_existing_container(cmd))
134
149
  end
135
150
 
136
151
  else
137
152
  say "Launching command from new container...", :magenta
138
- on(hosts) do
153
+ on(hosts) do |host|
139
154
  execute *KAMAL.auditor.record("Executed cmd '#{cmd}' on #{name} accessory"), verbosity: :debug
140
- capture_with_info(*accessory.execute_in_new_container(cmd))
155
+ puts_by_host host, capture_with_info(*accessory.execute_in_new_container(cmd))
141
156
  end
142
157
  end
143
158
  end
@@ -1,11 +1,17 @@
1
1
  class Kamal::Cli::Secrets < Kamal::Cli::Base
2
2
  desc "fetch [SECRETS...]", "Fetch secrets from a vault"
3
3
  option :adapter, type: :string, aliases: "-a", required: true, desc: "Which vault adapter to use"
4
- option :account, type: :string, required: true, desc: "The account identifier or username"
4
+ option :account, type: :string, required: false, desc: "The account identifier or username"
5
5
  option :from, type: :string, required: false, desc: "A vault or folder to fetch the secrets from"
6
6
  option :inline, type: :boolean, required: false, hidden: true
7
7
  def fetch(*secrets)
8
- results = adapter(options[:adapter]).fetch(secrets, **options.slice(:account, :from).symbolize_keys)
8
+ adapter = initialize_adapter(options[:adapter])
9
+
10
+ if adapter.requires_account? && options[:account].blank?
11
+ return puts "No value provided for required options '--account'"
12
+ end
13
+
14
+ results = adapter.fetch(secrets, **options.slice(:account, :from).symbolize_keys)
9
15
 
10
16
  return_or_puts JSON.dump(results).shellescape, inline: options[:inline]
11
17
  end
@@ -29,7 +35,7 @@ class Kamal::Cli::Secrets < Kamal::Cli::Base
29
35
  end
30
36
 
31
37
  private
32
- def adapter(adapter)
38
+ def initialize_adapter(adapter)
33
39
  Kamal::Secrets::Adapters.lookup(adapter)
34
40
  end
35
41
 
@@ -16,8 +16,8 @@ servers:
16
16
  # Enable SSL auto certification via Let's Encrypt and allow for multiple apps on a single web server.
17
17
  # Remove this section when using multiple web servers and ensure you terminate SSL at your load balancer.
18
18
  #
19
- # Note: If using Cloudflare, set encryption mode in SSL/TLS setting to "Full" to enable CF-to-app encryption.
20
- proxy:
19
+ # Note: If using Cloudflare, set encryption mode in SSL/TLS setting to "Full" to enable CF-to-app encryption.
20
+ proxy:
21
21
  ssl: true
22
22
  host: app.example.com
23
23
  # Proxy connects to your container on port 80 by default.
@@ -36,6 +36,9 @@ registry:
36
36
  # Configure builder setup.
37
37
  builder:
38
38
  arch: amd64
39
+ # Pass in additional build args needed for your Dockerfile.
40
+ # args:
41
+ # RUBY_VERSION: <%= File.read('.ruby-version').strip %>
39
42
 
40
43
  # Inject ENV variables into containers (secrets come from .kamal/secrets).
41
44
  #
@@ -0,0 +1,16 @@
1
+ module Kamal::Commands::Accessory::Proxy
2
+ delegate :proxy_container_name, to: :config
3
+
4
+ def deploy(target:)
5
+ proxy_exec :deploy, service_name, *proxy.deploy_command_args(target: target)
6
+ end
7
+
8
+ def remove
9
+ proxy_exec :remove, service_name
10
+ end
11
+
12
+ private
13
+ def proxy_exec(*command)
14
+ docker :exec, proxy_container_name, "kamal-proxy", *command
15
+ end
16
+ end
@@ -1,9 +1,13 @@
1
1
  class Kamal::Commands::Accessory < Kamal::Commands::Base
2
+ include Proxy
3
+
2
4
  attr_reader :accessory_config
3
5
  delegate :service_name, :image, :hosts, :port, :files, :directories, :cmd,
4
6
  :network_args, :publish_args, :env_args, :volume_args, :label_args, :option_args,
5
- :secrets_io, :secrets_path, :env_directory,
7
+ :secrets_io, :secrets_path, :env_directory, :proxy, :running_proxy?,
6
8
  to: :accessory_config
9
+ delegate :proxy_container_name, to: :config
10
+
7
11
 
8
12
  def initialize(config, name:)
9
13
  super(config)
@@ -2,7 +2,7 @@ module Kamal::Commands::App::Containers
2
2
  DOCKER_HEALTH_LOG_FORMAT = "'{{json .State.Health}}'"
3
3
 
4
4
  def list_containers
5
- docker :container, :ls, "--all", *filter_args
5
+ docker :container, :ls, "--all", *container_filter_args
6
6
  end
7
7
 
8
8
  def list_container_names
@@ -20,7 +20,7 @@ module Kamal::Commands::App::Containers
20
20
  end
21
21
 
22
22
  def remove_containers
23
- docker :container, :prune, "--force", *filter_args
23
+ docker :container, :prune, "--force", *container_filter_args
24
24
  end
25
25
 
26
26
  def container_health_log(version:)
@@ -4,7 +4,7 @@ module Kamal::Commands::App::Images
4
4
  end
5
5
 
6
6
  def remove_images
7
- docker :image, :prune, "--all", "--force", *filter_args
7
+ docker :image, :prune, "--all", "--force", *image_filter_args
8
8
  end
9
9
 
10
10
  def tag_latest_image
@@ -47,7 +47,7 @@ class Kamal::Commands::App < Kamal::Commands::Base
47
47
  end
48
48
 
49
49
  def info
50
- docker :ps, *filter_args
50
+ docker :ps, *container_filter_args
51
51
  end
52
52
 
53
53
 
@@ -67,7 +67,7 @@ class Kamal::Commands::App < Kamal::Commands::Base
67
67
 
68
68
  def list_versions(*docker_args, statuses: nil)
69
69
  pipe \
70
- docker(:ps, *filter_args(statuses: statuses), *docker_args, "--format", '"{{.Names}}"'),
70
+ docker(:ps, *container_filter_args(statuses: statuses), *docker_args, "--format", '"{{.Names}}"'),
71
71
  extract_version_from_name
72
72
  end
73
73
 
@@ -91,11 +91,15 @@ class Kamal::Commands::App < Kamal::Commands::Base
91
91
  end
92
92
 
93
93
  def latest_container(format:, filters: nil)
94
- docker :ps, "--latest", *format, *filter_args(statuses: ACTIVE_DOCKER_STATUSES), argumentize("--filter", filters)
94
+ docker :ps, "--latest", *format, *container_filter_args(statuses: ACTIVE_DOCKER_STATUSES), argumentize("--filter", filters)
95
95
  end
96
96
 
97
- def filter_args(statuses: nil)
98
- argumentize "--filter", filters(statuses: statuses)
97
+ def container_filter_args(statuses: nil)
98
+ argumentize "--filter", container_filters(statuses: statuses)
99
+ end
100
+
101
+ def image_filter_args
102
+ argumentize "--filter", image_filters
99
103
  end
100
104
 
101
105
  def extract_version_from_name
@@ -103,13 +107,17 @@ class Kamal::Commands::App < Kamal::Commands::Base
103
107
  %(while read line; do echo ${line##{role.container_prefix}-}; done)
104
108
  end
105
109
 
106
- def filters(statuses: nil)
110
+ def container_filters(statuses: nil)
107
111
  [ "label=service=#{config.service}" ].tap do |filters|
108
- filters << "label=destination=#{config.destination}" if config.destination
112
+ filters << "label=destination=#{config.destination}"
109
113
  filters << "label=role=#{role}" if role
110
114
  statuses&.each do |status|
111
115
  filters << "status=#{status}"
112
116
  end
113
117
  end
114
118
  end
119
+
120
+ def image_filters
121
+ [ "label=service=#{config.service}" ]
122
+ end
115
123
  end
@@ -6,7 +6,7 @@ class Kamal::Commands::Builder::Base < Kamal::Commands::Base
6
6
  delegate :argumentize, to: Kamal::Utils
7
7
  delegate \
8
8
  :args, :secrets, :dockerfile, :target, :arches, :local_arches, :remote_arches, :remote,
9
- :cache_from, :cache_to, :ssh, :provenance, :driver, :docker_driver?,
9
+ :cache_from, :cache_to, :ssh, :provenance, :sbom, :driver, :docker_driver?,
10
10
  to: :builder_config
11
11
 
12
12
  def clean
@@ -37,7 +37,7 @@ class Kamal::Commands::Builder::Base < Kamal::Commands::Base
37
37
  end
38
38
 
39
39
  def build_options
40
- [ *build_tags, *build_cache, *build_labels, *build_args, *build_secrets, *build_dockerfile, *build_target, *build_ssh, *builder_provenance ]
40
+ [ *build_tags, *build_cache, *build_labels, *build_args, *build_secrets, *build_dockerfile, *build_target, *build_ssh, *builder_provenance, *builder_sbom ]
41
41
  end
42
42
 
43
43
  def build_context
@@ -101,6 +101,10 @@ class Kamal::Commands::Builder::Base < Kamal::Commands::Base
101
101
  argumentize "--provenance", provenance unless provenance.nil?
102
102
  end
103
103
 
104
+ def builder_sbom
105
+ argumentize "--sbom", sbom unless sbom.nil?
106
+ end
107
+
104
108
  def builder_config
105
109
  config.builder
106
110
  end
@@ -5,7 +5,7 @@ class Kamal::Configuration::Accessory
5
5
 
6
6
  delegate :argumentize, :optionize, to: Kamal::Utils
7
7
 
8
- attr_reader :name, :accessory_config, :env
8
+ attr_reader :name, :accessory_config, :env, :proxy
9
9
 
10
10
  def initialize(name, config:)
11
11
  @name, @config, @accessory_config = name.inquiry, config, config.raw_config["accessories"][name]
@@ -20,6 +20,8 @@ class Kamal::Configuration::Accessory
20
20
  config: accessory_config.fetch("env", {}),
21
21
  secrets: config.secrets,
22
22
  context: "accessories/#{name}/env"
23
+
24
+ initialize_proxy if running_proxy?
23
25
  end
24
26
 
25
27
  def service_name
@@ -106,6 +108,17 @@ class Kamal::Configuration::Accessory
106
108
  accessory_config["cmd"]
107
109
  end
108
110
 
111
+ def running_proxy?
112
+ @accessory_config["proxy"].present?
113
+ end
114
+
115
+ def initialize_proxy
116
+ @proxy = Kamal::Configuration::Proxy.new \
117
+ config: config,
118
+ proxy_config: accessory_config["proxy"],
119
+ context: "accessories/#{name}/proxy"
120
+ end
121
+
109
122
  private
110
123
  attr_accessor :config
111
124
 
@@ -176,7 +189,9 @@ class Kamal::Configuration::Accessory
176
189
 
177
190
  def hosts_from_roles
178
191
  if accessory_config.key?("roles")
179
- accessory_config["roles"].flat_map { |role| config.role(role).hosts }
192
+ accessory_config["roles"].flat_map do |role|
193
+ config.role(role)&.hosts || raise(Kamal::ConfigurationError, "Unknown role in accessories config: '#{role}'")
194
+ end
180
195
  end
181
196
  end
182
197
 
@@ -115,6 +115,10 @@ class Kamal::Configuration::Builder
115
115
  builder_config["provenance"]
116
116
  end
117
117
 
118
+ def sbom
119
+ builder_config["sbom"]
120
+ end
121
+
118
122
  def git_clone?
119
123
  Kamal::Git.used? && builder_config["context"].nil?
120
124
  end
@@ -98,3 +98,7 @@ accessories:
98
98
  # Defaults to kamal:
99
99
  network: custom
100
100
 
101
+ # Proxy
102
+ #
103
+ proxy:
104
+ ...
@@ -108,3 +108,9 @@ builder:
108
108
  # It is used to configure provenance attestations for the build result.
109
109
  # The value can also be a boolean to enable or disable provenance attestations.
110
110
  provenance: mode=max
111
+
112
+ # SBOM (Software Bill of Materials)
113
+ #
114
+ # It is used to configure SBOM generation for the build result.
115
+ # The value can also be a boolean to enable or disable SBOM generation.
116
+ sbom: true
@@ -54,6 +54,11 @@ proxy:
54
54
  # Next big thing after...
55
55
  tls_on_demand_url: "http://example.com/check_host"
56
56
 
57
+ # TLSFlexibleMode
58
+ #
59
+ # Next big thing after...
60
+ tls_flexible_mode: false
61
+
57
62
  # Response timeout
58
63
  #
59
64
  # How long to wait for requests to complete before timing out, defaults to 30 seconds:
@@ -31,6 +31,7 @@ class Kamal::Configuration::Proxy
31
31
  host: hosts,
32
32
  tls: proxy_config["ssl"].presence,
33
33
  "tls-on-demand-url": proxy_config["tls_on_demand_url"],
34
+ "tls-flexible-mode": proxy_config["tls_flexible_mode"].presence,
34
35
  "deploy-timeout": seconds_duration(config.deploy_timeout),
35
36
  "drain-timeout": seconds_duration(config.drain_timeout),
36
37
  "health-check-interval": seconds_duration(proxy_config.dig("healthcheck", "interval")),
@@ -14,7 +14,7 @@ class Kamal::Configuration
14
14
 
15
15
  include Validation
16
16
 
17
- PROXY_MINIMUM_VERSION = "v0.8.2.6"
17
+ PROXY_MINIMUM_VERSION = "v0.8.2.8"
18
18
  PROXY_HTTP_PORT = 80
19
19
  PROXY_HTTPS_PORT = 443
20
20
  PROXY_LOG_MAX_SIZE = "10m"
@@ -0,0 +1,34 @@
1
+ class Kamal::Secrets::Adapters::AwsSecretsManager < Kamal::Secrets::Adapters::Base
2
+ private
3
+ def login(_account)
4
+ nil
5
+ end
6
+
7
+ def fetch_secrets(secrets, account:, session:)
8
+ {}.tap do |results|
9
+ JSON.parse(get_from_secrets_manager(secrets, account: account))["SecretValues"].each do |secret|
10
+ secret_name = secret["Name"]
11
+ secret_string = JSON.parse(secret["SecretString"])
12
+
13
+ secret_string.each do |key, value|
14
+ results["#{secret_name}/#{key}"] = value
15
+ end
16
+ end
17
+ end
18
+ end
19
+
20
+ def get_from_secrets_manager(secrets, account:)
21
+ `aws secretsmanager batch-get-secret-value --secret-id-list #{secrets.map(&:shellescape).join(" ")} --profile #{account.shellescape}`.tap do
22
+ raise RuntimeError, "Could not read #{secret} from AWS Secrets Manager" unless $?.success?
23
+ end
24
+ end
25
+
26
+ def check_dependencies!
27
+ raise RuntimeError, "AWS CLI is not installed" unless cli_installed?
28
+ end
29
+
30
+ def cli_installed?
31
+ `aws --version 2> /dev/null`
32
+ $?.success?
33
+ end
34
+ end
@@ -1,13 +1,20 @@
1
1
  class Kamal::Secrets::Adapters::Base
2
2
  delegate :optionize, to: Kamal::Utils
3
3
 
4
- def fetch(secrets, account:, from: nil)
4
+ def fetch(secrets, account: nil, from: nil)
5
+ raise RuntimeError, "Missing required option '--account'" if requires_account? && account.blank?
6
+
5
7
  check_dependencies!
8
+
6
9
  session = login(account)
7
10
  full_secrets = secrets.map { |secret| [ from, secret ].compact.join("/") }
8
11
  fetch_secrets(full_secrets, account: account, session: session)
9
12
  end
10
13
 
14
+ def requires_account?
15
+ true
16
+ end
17
+
11
18
  private
12
19
  def login(...)
13
20
  raise NotImplementedError
@@ -0,0 +1,53 @@
1
+ class Kamal::Secrets::Adapters::Doppler < Kamal::Secrets::Adapters::Base
2
+ def requires_account?
3
+ false
4
+ end
5
+
6
+ private
7
+ def login(*)
8
+ unless loggedin?
9
+ `doppler login -y`
10
+ raise RuntimeError, "Failed to login to Doppler" unless $?.success?
11
+ end
12
+ end
13
+
14
+ def loggedin?
15
+ `doppler me --json 2> /dev/null`
16
+ $?.success?
17
+ end
18
+
19
+ def fetch_secrets(secrets, **)
20
+ project_and_config_flags = ""
21
+ unless service_token_set?
22
+ project, config, _ = secrets.first.split("/")
23
+
24
+ unless project && config
25
+ raise RuntimeError, "Missing project or config from '--from=project/config' option"
26
+ end
27
+
28
+ project_and_config_flags = "-p #{project.shellescape} -c #{config.shellescape}"
29
+ end
30
+
31
+ secret_names = secrets.collect { |s| s.split("/").last }
32
+
33
+ items = `doppler secrets get #{secret_names.map(&:shellescape).join(" ")} --json #{project_and_config_flags}`
34
+ raise RuntimeError, "Could not read #{secrets} from Doppler" unless $?.success?
35
+
36
+ items = JSON.parse(items)
37
+
38
+ items.transform_values { |value| value["computed"] }
39
+ end
40
+
41
+ def service_token_set?
42
+ ENV["DOPPLER_TOKEN"] && ENV["DOPPLER_TOKEN"][0, 5] == "dp.st"
43
+ end
44
+
45
+ def check_dependencies!
46
+ raise RuntimeError, "Doppler CLI is not installed" unless cli_installed?
47
+ end
48
+
49
+ def cli_installed?
50
+ `doppler --version 2> /dev/null`
51
+ $?.success?
52
+ end
53
+ end
@@ -0,0 +1,5 @@
1
+ class Kamal::Secrets::Adapters::TestOptionalAccount < Kamal::Secrets::Adapters::Test
2
+ def requires_account?
3
+ false
4
+ end
5
+ end
data/lib/kamal/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Kamal
2
- VERSION = "2.3.0.1"
2
+ VERSION = "2.3.0.3"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: nocoffee-kamal
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.3.0.1
4
+ version: 2.3.0.3
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: 2024-11-04 00:00:00.000000000 Z
11
+ date: 2024-11-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -204,7 +204,7 @@ dependencies:
204
204
  - - ">="
205
205
  - !ruby/object:Gem::Version
206
206
  version: '0'
207
- description: Kamal with the TLS on demand feature
207
+ description:
208
208
  email: dhh@hey.com
209
209
  executables:
210
210
  - kamal
@@ -247,6 +247,7 @@ files:
247
247
  - lib/kamal/commander/specifics.rb
248
248
  - lib/kamal/commands.rb
249
249
  - lib/kamal/commands/accessory.rb
250
+ - lib/kamal/commands/accessory/proxy.rb
250
251
  - lib/kamal/commands/app.rb
251
252
  - lib/kamal/commands/app/assets.rb
252
253
  - lib/kamal/commands/app/containers.rb
@@ -312,11 +313,14 @@ files:
312
313
  - lib/kamal/git.rb
313
314
  - lib/kamal/secrets.rb
314
315
  - lib/kamal/secrets/adapters.rb
316
+ - lib/kamal/secrets/adapters/aws_secrets_manager.rb
315
317
  - lib/kamal/secrets/adapters/base.rb
316
318
  - lib/kamal/secrets/adapters/bitwarden.rb
319
+ - lib/kamal/secrets/adapters/doppler.rb
317
320
  - lib/kamal/secrets/adapters/last_pass.rb
318
321
  - lib/kamal/secrets/adapters/one_password.rb
319
322
  - lib/kamal/secrets/adapters/test.rb
323
+ - lib/kamal/secrets/adapters/test_optional_account.rb
320
324
  - lib/kamal/secrets/dotenv/inline_command_substitution.rb
321
325
  - lib/kamal/sshkit_with_ext.rb
322
326
  - lib/kamal/tags.rb
@@ -342,8 +346,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
342
346
  - !ruby/object:Gem::Version
343
347
  version: '0'
344
348
  requirements: []
345
- rubygems_version: 3.5.19
349
+ rubygems_version: 3.5.23
346
350
  signing_key:
347
351
  specification_version: 4
348
- summary: Deploy web apps in containers to servers running Docker with zero downtime
352
+ summary: Kamal with the TLS on demand feature.
349
353
  test_files: []