kamal-backup 0.2.6 → 0.2.8

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: 446615d131dce8d1bbece3ca08f663472dd3cf76bd66d576e9e9e9f256560f7a
4
- data.tar.gz: 073254be6dba231b897c613a991df30f558e0812cdb1a03640a47a6fb7721cef
3
+ metadata.gz: d3bd8ce5d3434c45246641943cdcd4eeeab4bda3caf5fec309f9b65efbd3a21a
4
+ data.tar.gz: 3bf191da068b6c64540d98d786c49772f451085b08dce8f5058c1923818a3a91
5
5
  SHA512:
6
- metadata.gz: 556c8066fd8fd4ac18d89bd75280e88ae1fe65e8cb2c882219575553e5f0d584bed21bf9fb8c686212630fd475d37e649016399951b1f125ee84d8ee503d3322
7
- data.tar.gz: a7e278ee0679a6511919681a8106977c79ffc63a719ba4889048921c605fb7d888ad786e9329b76ca94019a650a66831f89e347ff94e7e5d1964fe0f2e0e75d4
6
+ metadata.gz: a74bcf4abc214cbc22f4c9aed16c067b8e928c8d666ba3e73155c52ac8ef0b3ec3be0b5f68b01f258025b6cfada5629562de372e3a46f1f8321c395ef92d3aee
7
+ data.tar.gz: 0bc5fc2dd8e7e103cb93f0e4fa576ae9a7e643f4084edbec2b644ae9c8a47ba5917014f7b6ba0e1ae261a98c3b574b75f789985710eb27bcb912ea87d84d6d6f
data/README.md CHANGED
@@ -30,7 +30,7 @@ Most self-hosted Rails apps need the same things:
30
30
  - file-backed Active Storage backups from mounted volumes
31
31
  - a fast way to restore production data locally
32
32
  - restore drills that do not touch the live production database
33
- - evidence that says more than "the backup job is green"
33
+ - evidence that says more than "the backup ran"
34
34
 
35
35
  `kamal-backup` packages that workflow into a small Ruby gem, a production accessory image, and a restic repository.
36
36
 
@@ -71,6 +71,8 @@ accessories:
71
71
  - "chatwithwork_backup_state:/var/lib/kamal-backup"
72
72
  ```
73
73
 
74
+ For SQLite databases stored on the mounted storage volume, omit `:ro` from that volume.
75
+
74
76
  Put the backup settings in `config/kamal-backup.yml`:
75
77
 
76
78
  ```yaml
@@ -147,6 +147,43 @@ module KamalBackup
147
147
  end
148
148
  end
149
149
 
150
+ def confirm_production_restore!(snapshot)
151
+ return if options[:"confirm-production-restore"]
152
+
153
+ if options[:yes]
154
+ raise ConfigurationError, "--yes does not bypass restore production; use --confirm-production-restore only for deliberate automation"
155
+ end
156
+
157
+ unless $stdin.tty?
158
+ raise ConfigurationError, "production restore confirmation required; rerun interactively or pass --confirm-production-restore only for deliberate automation"
159
+ end
160
+
161
+ app_name = production_restore_confirmation_config.required_app_name
162
+ say "This will overwrite the production database and file paths for #{app_name} from backup #{snapshot}.", :red
163
+ require_typed_confirmation("Type the app name to continue", app_name)
164
+ require_typed_confirmation("Type RESTORE PRODUCTION to continue", "RESTORE PRODUCTION")
165
+ confirm!("Restore #{snapshot} into production now? This will overwrite production data.")
166
+ end
167
+
168
+ def require_typed_confirmation(prompt, expected)
169
+ answer = ask("#{prompt}:").to_s.strip
170
+ return if answer == expected
171
+
172
+ raise ConfigurationError, "aborted"
173
+ end
174
+
175
+ def production_restore_confirmation_config
176
+ if deployment_mode?
177
+ Config.new(
178
+ env: bridge.accessory_environment(accessory_name: accessory_name),
179
+ config_paths: [Config::SHARED_CONFIG_PATH],
180
+ load_project_defaults: false
181
+ )
182
+ else
183
+ direct_app.config
184
+ end
185
+ end
186
+
150
187
  def prompt_required(label)
151
188
  unless $stdin.tty?
152
189
  raise ConfigurationError, "#{label.downcase} is required; pass it on the command line"
@@ -234,12 +271,13 @@ module KamalBackup
234
271
  puts(JSON.pretty_generate(local_app.restore_to_local_machine(snapshot)))
235
272
  end
236
273
 
274
+ method_option :"confirm-production-restore", type: :boolean, default: false, desc: "Confirm production restore without interactive prompts"
237
275
  desc "production [SNAPSHOT]", "Restore the backup into the production database and Active Storage path"
238
276
  def production(snapshot = "latest")
239
- confirm!("Restore #{snapshot} into the production database and Active Storage path? This will overwrite production data.")
277
+ confirm_production_restore!(snapshot)
240
278
 
241
279
  if deployment_mode?
242
- exec_remote(["kamal-backup", "restore", "production", snapshot, "--yes"])
280
+ exec_remote(["kamal-backup", "restore", "production", snapshot, "--confirm-production-restore"])
243
281
  else
244
282
  puts(JSON.pretty_generate(direct_app.restore_to_production(snapshot)))
245
283
  end
@@ -42,7 +42,6 @@ module KamalBackup
42
42
  "backup_schedule_seconds" => "BACKUP_SCHEDULE_SECONDS",
43
43
  "backup_start_delay_seconds" => "BACKUP_START_DELAY_SECONDS",
44
44
  "state_dir" => "KAMAL_BACKUP_STATE_DIR",
45
- "allow_production_restore" => "KAMAL_BACKUP_ALLOW_PRODUCTION_RESTORE",
46
45
  "allow_suspicious_paths" => "KAMAL_BACKUP_ALLOW_SUSPICIOUS_PATHS",
47
46
  "pgpassword" => "PGPASSWORD",
48
47
  "mysql_pwd" => "MYSQL_PWD"
@@ -104,10 +103,6 @@ module KamalBackup
104
103
  value("RESTIC_CHECK_READ_DATA_SUBSET")
105
104
  end
106
105
 
107
- def allow_production_restore?
108
- truthy?("KAMAL_BACKUP_ALLOW_PRODUCTION_RESTORE")
109
- end
110
-
111
106
  def allow_in_place_file_restore?
112
107
  truthy?("KAMAL_BACKUP_ALLOW_IN_PLACE_FILE_RESTORE")
113
108
  end
@@ -245,8 +240,8 @@ module KamalBackup
245
240
  def validate_local_database_restore_target(target)
246
241
  raise ConfigurationError, "local restore database target is required" if target.to_s.strip.empty?
247
242
 
248
- if production_named_target?(target) && !allow_production_restore?
249
- raise ConfigurationError, "refusing production-looking local restore target #{target}; set KAMAL_BACKUP_ALLOW_PRODUCTION_RESTORE=true to override"
243
+ if production_named_target?(target)
244
+ raise ConfigurationError, "refusing production-looking local restore target #{target}; use restore production for production restores"
250
245
  end
251
246
  end
252
247
 
@@ -266,8 +261,8 @@ module KamalBackup
266
261
  def validate_database_restore_target(target)
267
262
  raise ConfigurationError, "restore database target is required" if target.to_s.strip.empty?
268
263
 
269
- if production_like_target?(target) && !allow_production_restore?
270
- raise ConfigurationError, "refusing production-looking restore target #{target}; set KAMAL_BACKUP_ALLOW_PRODUCTION_RESTORE=true to override"
264
+ if production_like_target?(target)
265
+ raise ConfigurationError, "refusing production-looking restore target #{target}; choose a scratch target that does not look like production"
271
266
  end
272
267
  end
273
268
 
@@ -384,8 +379,8 @@ module KamalBackup
384
379
  if environment = local_restore_environment
385
380
  key, value = environment
386
381
 
387
- if production_environment?(value) && !allow_production_restore?
388
- raise ConfigurationError, "restore local refuses to run with #{key}=#{value}; set KAMAL_BACKUP_ALLOW_PRODUCTION_RESTORE=true to override"
382
+ if production_environment?(value)
383
+ raise ConfigurationError, "restore local refuses to run with #{key}=#{value}; unset #{key} or use restore production"
389
384
  end
390
385
  end
391
386
  end
@@ -1,3 +1,3 @@
1
1
  module KamalBackup
2
- VERSION = "0.2.6"
2
+ VERSION = "0.2.8"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kamal-backup
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.6
4
+ version: 0.2.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - crmne
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-04-28 00:00:00.000000000 Z
11
+ date: 2026-05-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: thor