kamal-backup 0.2.6 → 0.2.7

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: 8322ff9eeead7c145127274766d96c54677333fafd7a94b57c6eb7b395cfeae6
4
+ data.tar.gz: b9b1d442c93b808b4d0e04c9fdd3eb757ff97df2470610769bd7e430836107e9
5
5
  SHA512:
6
- metadata.gz: 556c8066fd8fd4ac18d89bd75280e88ae1fe65e8cb2c882219575553e5f0d584bed21bf9fb8c686212630fd475d37e649016399951b1f125ee84d8ee503d3322
7
- data.tar.gz: a7e278ee0679a6511919681a8106977c79ffc63a719ba4889048921c605fb7d888ad786e9329b76ca94019a650a66831f89e347ff94e7e5d1964fe0f2e0e75d4
6
+ metadata.gz: 4d4031c8ffaa652db3f151c3b21cbf2e170571278a57d815880fa4bbe5a06d6dfce9690d9f59fbe5e68de77c97c6e1ac6a1da90c4c400ccb8af2acaf7d18cec7
7
+ data.tar.gz: f037233779d710baadf2c6c5e4100b8caccda9069d95b781411fbe35f5c8e4b93899c41d0652f6baa69f38f7e8107c83b65ef19d3ee988397067e5c40e0ae0b7
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
 
@@ -1,5 +1,6 @@
1
1
  require "fileutils"
2
2
  require "tempfile"
3
+ require "uri"
3
4
  require_relative "base"
4
5
 
5
6
  module KamalBackup
@@ -17,10 +18,7 @@ module KamalBackup
17
18
  source = sqlite_source
18
19
  Tempfile.create(["kamal-backup-", ".sqlite3"]) do |tempfile|
19
20
  tempfile.close
20
- Command.capture(
21
- CommandSpec.new(argv: ["sqlite3", source, ".backup #{sqlite_literal(tempfile.path)}"]),
22
- redactor: redactor
23
- )
21
+ backup_to_file(source, tempfile.path)
24
22
  restic.backup_file(
25
23
  tempfile.path,
26
24
  filename: database_filename(timestamp),
@@ -55,6 +53,40 @@ module KamalBackup
55
53
  config.required_value("SQLITE_DATABASE_PATH")
56
54
  end
57
55
 
56
+ def backup_to_file(source, target)
57
+ run_backup(source, target)
58
+ rescue CommandError => e
59
+ raise unless immutable_retry_safe?(source, e)
60
+
61
+ # Immutable mode skips WAL change detection, so only use it when no WAL sidecar exists.
62
+ run_backup(sqlite_immutable_uri(source), target)
63
+ end
64
+
65
+ def run_backup(source, target)
66
+ Command.capture(
67
+ CommandSpec.new(argv: ["sqlite3", source, ".backup #{sqlite_literal(target)}"]),
68
+ redactor: redactor
69
+ )
70
+ end
71
+
72
+ def immutable_retry_safe?(source, error)
73
+ readonly_database_error?(error) &&
74
+ !File.exist?("#{source}-wal") &&
75
+ !File.exist?("#{source}-shm")
76
+ end
77
+
78
+ def readonly_database_error?(error)
79
+ error.stderr.include?("readonly database") || error.message.include?("readonly database")
80
+ end
81
+
82
+ def sqlite_immutable_uri(source)
83
+ path = File.expand_path(source).split("/").map do |part|
84
+ URI.encode_www_form_component(part).gsub("+", "%20")
85
+ end.join("/")
86
+
87
+ "file:#{path}?immutable=1"
88
+ end
89
+
58
90
  def validate_scratch_restore_target(target)
59
91
  if File.expand_path(sqlite_source) == File.expand_path(target)
60
92
  raise ConfigurationError, "scratch SQLite path must differ from SQLITE_DATABASE_PATH"
@@ -1,3 +1,3 @@
1
1
  module KamalBackup
2
- VERSION = "0.2.6"
2
+ VERSION = "0.2.7"
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.7
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-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: thor