kamal-backup 0.4.1 → 0.5.0
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 +4 -4
- data/README.md +2 -0
- data/lib/kamal_backup/app.rb +13 -0
- data/lib/kamal_backup/cli/helpers.rb +7 -0
- data/lib/kamal_backup/cli.rb +9 -8
- data/lib/kamal_backup/config.rb +8 -1
- data/lib/kamal_backup/config_file.rb +1 -0
- data/lib/kamal_backup/rails_app.rb +0 -7
- data/lib/kamal_backup/scheduler.rb +15 -0
- data/lib/kamal_backup/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: aa8276219db8c5d8aa820e85e0f289f87715889cb5ea18ae2c99e3e12bbfe6b9
|
|
4
|
+
data.tar.gz: 626824ed56029bf3b713676d3376ab38d571942ee3809e7cf18abbfde9c24a45
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 75d362d35409c0447c29994a7bf4d23ac47d287fc88621eb46f32a56296537217cee76a28799983f45154361ecd61e4f553b609144969bed2b10016bf0ecc09c
|
|
7
|
+
data.tar.gz: e5f664a15123066441b940d945caed2335d8b50356acaa64d9ae6f49d87a7705894234e55db42ec6508aa188f727b01d4e4d4c71688ecd93b4dbf4c12d53ef5d
|
data/README.md
CHANGED
|
@@ -99,6 +99,8 @@ backup:
|
|
|
99
99
|
schedule: 1d
|
|
100
100
|
```
|
|
101
101
|
|
|
102
|
+
Only paths explicitly listed under `paths` are included in file snapshots. Omit `paths` for a database-only backup; `kamal-backup` never infers `storage` from Rails.
|
|
103
|
+
|
|
102
104
|
Boot it. The container runs `kamal-backup schedule` by default:
|
|
103
105
|
|
|
104
106
|
```sh
|
data/lib/kamal_backup/app.rb
CHANGED
|
@@ -32,6 +32,7 @@ module KamalBackup
|
|
|
32
32
|
def backup(force: false)
|
|
33
33
|
started_at = Time.now.utc
|
|
34
34
|
config.validate_backup
|
|
35
|
+
return disabled_backup_result(started_at) unless config.backup_enabled? || force
|
|
35
36
|
return skipped_backup_result(started_at) unless force || backup_due?(started_at)
|
|
36
37
|
|
|
37
38
|
restic.ensure_repository
|
|
@@ -136,6 +137,18 @@ module KamalBackup
|
|
|
136
137
|
due_at.nil? || now >= due_at
|
|
137
138
|
end
|
|
138
139
|
|
|
140
|
+
def disabled_backup_result(now)
|
|
141
|
+
Schema.record(
|
|
142
|
+
kind: 'backup_result',
|
|
143
|
+
status: 'skipped',
|
|
144
|
+
reason: 'disabled',
|
|
145
|
+
last_backup_at: last_backup_finished_at&.iso8601,
|
|
146
|
+
next_backup_at: nil,
|
|
147
|
+
force_command: 'kamal-backup backup --force',
|
|
148
|
+
finished_at: now.iso8601
|
|
149
|
+
)
|
|
150
|
+
end
|
|
151
|
+
|
|
139
152
|
def skipped_backup_result(now)
|
|
140
153
|
Schema.record(
|
|
141
154
|
kind: 'backup_result',
|
|
@@ -140,6 +140,13 @@ module KamalBackup
|
|
|
140
140
|
end
|
|
141
141
|
|
|
142
142
|
def print_backup_result(result)
|
|
143
|
+
if result[:status] == 'skipped' && result[:reason] == 'disabled'
|
|
144
|
+
puts('Backups are disabled for this app (backup.enabled is false).')
|
|
145
|
+
puts('Set backup.enabled to true and redeploy the accessory to resume scheduled backups.')
|
|
146
|
+
puts("Run `#{result.fetch(:force_command)}` to take one anyway.")
|
|
147
|
+
return
|
|
148
|
+
end
|
|
149
|
+
|
|
143
150
|
if result[:status] == 'skipped'
|
|
144
151
|
puts("No backup due. Last backup finished at #{result.fetch(:last_backup_at)}.")
|
|
145
152
|
puts("Next backup is due at #{result.fetch(:next_backup_at)}.")
|
data/lib/kamal_backup/cli.rb
CHANGED
|
@@ -26,15 +26,15 @@ module KamalBackup
|
|
|
26
26
|
CLI.basename
|
|
27
27
|
end
|
|
28
28
|
|
|
29
|
-
desc 'local [SNAPSHOT]', 'Restore the backup into the local database and
|
|
29
|
+
desc 'local [SNAPSHOT]', 'Restore the backup into the local database and configured file paths'
|
|
30
30
|
def local(snapshot = 'latest')
|
|
31
|
-
confirm!("Restore #{snapshot} into the local database and
|
|
31
|
+
confirm!("Restore #{snapshot} into the local database and configured file paths? This will overwrite local data.")
|
|
32
32
|
puts(JSON.pretty_generate(local_restore_app.restore_to_local_machine(snapshot)))
|
|
33
33
|
end
|
|
34
34
|
|
|
35
35
|
method_option :"confirm-production-restore", type: :boolean, default: false,
|
|
36
36
|
desc: 'Confirm production restore without interactive prompts'
|
|
37
|
-
desc 'production [SNAPSHOT]', 'Restore the backup into the production database and
|
|
37
|
+
desc 'production [SNAPSHOT]', 'Restore the backup into the production database and configured file paths'
|
|
38
38
|
def production(snapshot = 'latest')
|
|
39
39
|
confirm_production_restore!(snapshot)
|
|
40
40
|
|
|
@@ -133,7 +133,7 @@ module KamalBackup
|
|
|
133
133
|
class_option :config_file, aliases: '-c', type: :string, desc: 'Path to Kamal deploy config file'
|
|
134
134
|
class_option :destination, aliases: '-d', type: :string, desc: 'Kamal destination to use'
|
|
135
135
|
remove_command :tree
|
|
136
|
-
desc 'restore SUBCOMMAND ...ARGS', 'Restore
|
|
136
|
+
desc 'restore SUBCOMMAND ...ARGS', 'Restore database and configured file backups locally or into production'
|
|
137
137
|
subcommand 'restore', RestoreCLI
|
|
138
138
|
desc 'drill SUBCOMMAND ...ARGS', 'Run a restore drill on the local machine or on production infrastructure'
|
|
139
139
|
subcommand 'drill', DrillCLI
|
|
@@ -162,7 +162,7 @@ module KamalBackup
|
|
|
162
162
|
|
|
163
163
|
method_option :force, type: :boolean, default: false,
|
|
164
164
|
desc: 'Run a backup even if the configured schedule is not due'
|
|
165
|
-
desc 'backup', 'Run a due database and
|
|
165
|
+
desc 'backup', 'Run a due database and configured file backup'
|
|
166
166
|
def backup
|
|
167
167
|
if remote_command_mode?
|
|
168
168
|
argv = %w[kamal-backup backup]
|
|
@@ -238,10 +238,11 @@ module KamalBackup
|
|
|
238
238
|
puts
|
|
239
239
|
puts deploy_snippet
|
|
240
240
|
puts
|
|
241
|
-
puts 'The accessory runs scheduled database and file backups with backup.schedule.'
|
|
242
|
-
puts '
|
|
241
|
+
puts 'The accessory runs scheduled database and configured file backups with backup.schedule.'
|
|
242
|
+
puts 'File backups run only for paths explicitly configured in config/kamal-backup.yml.'
|
|
243
|
+
puts 'Rails local restores infer the development database and tmp state directory, but never file paths.'
|
|
243
244
|
puts 'Local restore and drill also require the restic binary on your machine.'
|
|
244
|
-
puts '
|
|
245
|
+
puts 'When production paths are configured, set their local targets in config/kamal-backup.local.yml.'
|
|
245
246
|
end
|
|
246
247
|
|
|
247
248
|
desc 'schedule', 'Run the foreground scheduler loop'
|
data/lib/kamal_backup/config.rb
CHANGED
|
@@ -168,6 +168,10 @@ module KamalBackup
|
|
|
168
168
|
truthy?('KAMAL_BACKUP_ALLOW_SUSPICIOUS_PATHS')
|
|
169
169
|
end
|
|
170
170
|
|
|
171
|
+
def backup_enabled?
|
|
172
|
+
!falsey?('BACKUP_ENABLED')
|
|
173
|
+
end
|
|
174
|
+
|
|
171
175
|
def backup_schedule_seconds
|
|
172
176
|
integer('BACKUP_SCHEDULE_SECONDS', 86_400, minimum: 1)
|
|
173
177
|
end
|
|
@@ -214,7 +218,10 @@ module KamalBackup
|
|
|
214
218
|
source_paths = local_restore_source_paths
|
|
215
219
|
target_paths = backup_paths
|
|
216
220
|
|
|
217
|
-
|
|
221
|
+
unless source_paths.size == target_paths.size
|
|
222
|
+
raise ConfigurationError,
|
|
223
|
+
'local file paths must be explicitly configured and match the number of production restore source paths'
|
|
224
|
+
end
|
|
218
225
|
|
|
219
226
|
source_paths.zip(target_paths)
|
|
220
227
|
end
|
|
@@ -271,6 +271,7 @@ module KamalBackup
|
|
|
271
271
|
hash = require_mapping(raw_value, "#{@path} backup")
|
|
272
272
|
env = {}
|
|
273
273
|
env['BACKUP_SCHEDULE_SECONDS'] = normalize_duration(hash['schedule'], "#{@path} backup.schedule") if hash.key?('schedule')
|
|
274
|
+
env['BACKUP_ENABLED'] = normalize_value(hash['enabled']) if hash.key?('enabled')
|
|
274
275
|
env.compact
|
|
275
276
|
end
|
|
276
277
|
|
|
@@ -22,9 +22,6 @@ module KamalBackup
|
|
|
22
22
|
{}.tap do |defaults|
|
|
23
23
|
defaults.merge!(deploy_defaults)
|
|
24
24
|
defaults.merge!(database_defaults)
|
|
25
|
-
|
|
26
|
-
defaults['BACKUP_PATHS'] = local_storage_path if local_storage_path
|
|
27
|
-
|
|
28
25
|
defaults['KAMAL_BACKUP_STATE_DIR'] = File.join(@cwd, 'tmp', 'kamal-backup')
|
|
29
26
|
end
|
|
30
27
|
end
|
|
@@ -90,10 +87,6 @@ module KamalBackup
|
|
|
90
87
|
end
|
|
91
88
|
end
|
|
92
89
|
|
|
93
|
-
def local_storage_path
|
|
94
|
-
File.join(@cwd, 'storage')
|
|
95
|
-
end
|
|
96
|
-
|
|
97
90
|
def local_database_config
|
|
98
91
|
environment = fetch(parsed_yaml(database_config_path), DEVELOPMENT_ENV)
|
|
99
92
|
return nil unless environment.is_a?(Hash)
|
|
@@ -15,6 +15,17 @@ module KamalBackup
|
|
|
15
15
|
$stderr.sync = true
|
|
16
16
|
|
|
17
17
|
install_signal_handlers
|
|
18
|
+
|
|
19
|
+
unless @config.backup_enabled?
|
|
20
|
+
# Stay up rather than exit: the accessory is still how restore, list, check and
|
|
21
|
+
# evidence reach the repository, and exiting would restart-loop under Kamal's
|
|
22
|
+
# `--restart unless-stopped`. Set backup.enabled to true and redeploy to resume.
|
|
23
|
+
log('scheduled backups are disabled (backup.enabled is false); no backups will be taken')
|
|
24
|
+
idle_until_stopped
|
|
25
|
+
log("scheduler stopped at #{Time.now.utc.iso8601}")
|
|
26
|
+
return
|
|
27
|
+
end
|
|
28
|
+
|
|
18
29
|
sleep_interruptibly(@config.backup_start_delay_seconds)
|
|
19
30
|
|
|
20
31
|
until @stop
|
|
@@ -43,6 +54,10 @@ module KamalBackup
|
|
|
43
54
|
end
|
|
44
55
|
end
|
|
45
56
|
|
|
57
|
+
def idle_until_stopped
|
|
58
|
+
sleep 1 until @stop
|
|
59
|
+
end
|
|
60
|
+
|
|
46
61
|
def sleep_interruptibly(seconds)
|
|
47
62
|
deadline = Time.now + seconds
|
|
48
63
|
sleep([deadline - Time.now, 1].min) while !@stop && Time.now < deadline
|
data/lib/kamal_backup/version.rb
CHANGED
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.
|
|
4
|
+
version: 0.5.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- crmne
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-08-10 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: thor
|