kamal-backup 0.1.0.pre.8 → 0.1.0.pre.9
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 +21 -8
- data/lib/kamal_backup/cli.rb +3 -18
- data/lib/kamal_backup/config.rb +6 -1
- data/lib/kamal_backup/rails_app.rb +152 -0
- data/lib/kamal_backup/version.rb +1 -1
- data/lib/kamal_backup.rb +1 -0
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: dffee9d885ca23342a84ac3fc494d09f9f44b323a10ae981df4665f31206b20a
|
|
4
|
+
data.tar.gz: 0c6352b4f00008b54b22442f0b192eb54adf7f52c0350ab8cb5c69dd1cff9c4f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 77f916eec58efe57ad7c1b65b8f93cab39d647b9051dc6d77cbebfa66f830cbc65bfdf9bdfc8aa0016109969a88b68bef8f2ea79a71a97ce4670b6c1028fa8ba
|
|
7
|
+
data.tar.gz: 631793de8449a07bf774f91a5bf7aea669b4773da960a27319cf8ecca00c6194a37214f36cc70c49857247406e665292318ad145f9c9e87d15f53751b92bf00a
|
data/README.md
CHANGED
|
@@ -40,7 +40,7 @@ group :development do
|
|
|
40
40
|
end
|
|
41
41
|
```
|
|
42
42
|
|
|
43
|
-
Install it and generate the
|
|
43
|
+
Install it and generate the shared config stub:
|
|
44
44
|
|
|
45
45
|
```sh
|
|
46
46
|
bundle install
|
|
@@ -50,7 +50,14 @@ bundle exec kamal-backup init
|
|
|
50
50
|
That creates:
|
|
51
51
|
|
|
52
52
|
- `config/kamal-backup.yml`
|
|
53
|
-
|
|
53
|
+
|
|
54
|
+
For most Rails apps, that is enough. `restore local` and `drill local` can infer:
|
|
55
|
+
|
|
56
|
+
- the development database target from `config/database.yml`
|
|
57
|
+
- the local files target from `storage`
|
|
58
|
+
- the local drill state directory from `tmp/kamal-backup`
|
|
59
|
+
|
|
60
|
+
Only create `config/kamal-backup.local.yml` if you need to override those local defaults.
|
|
54
61
|
|
|
55
62
|
Then add the backup accessory to `config/deploy.yml`:
|
|
56
63
|
|
|
@@ -174,11 +181,17 @@ That shared `run:<timestamp>` tag lets you match the database backup and file ba
|
|
|
174
181
|
- `RESTIC_REPOSITORY`
|
|
175
182
|
- `LOCAL_RESTORE_SOURCE_PATHS` from the accessory `BACKUP_PATHS`
|
|
176
183
|
|
|
177
|
-
|
|
184
|
+
For a normal Rails app, the local targets come from Rails conventions:
|
|
185
|
+
|
|
186
|
+
- the development database in `config/database.yml`
|
|
187
|
+
- `storage` as the local files target
|
|
188
|
+
- `tmp/kamal-backup` as the local drill state directory
|
|
189
|
+
|
|
190
|
+
You still provide the local secrets yourself in env:
|
|
178
191
|
|
|
179
|
-
- `
|
|
180
|
-
- `
|
|
181
|
-
-
|
|
192
|
+
- `RESTIC_PASSWORD`
|
|
193
|
+
- `POSTGRES_PASSWORD` or `MYSQL_PWD` when needed
|
|
194
|
+
- `RESTIC_REPOSITORY` when it is not visible through `kamal config`
|
|
182
195
|
|
|
183
196
|
Example:
|
|
184
197
|
|
|
@@ -271,12 +284,12 @@ DATABASE_ADAPTER=sqlite
|
|
|
271
284
|
SQLITE_DATABASE_PATH=/data/db/production.sqlite3
|
|
272
285
|
```
|
|
273
286
|
|
|
274
|
-
|
|
287
|
+
Optional local config files:
|
|
275
288
|
|
|
276
289
|
- `config/kamal-backup.yml`
|
|
277
290
|
- `config/kamal-backup.local.yml`
|
|
278
291
|
|
|
279
|
-
Keep secrets such as `RESTIC_PASSWORD`, cloud credentials, and local DB passwords in environment variables, not in YAML files.
|
|
292
|
+
`config/kamal-backup.local.yml` is only for nonstandard local targets. Keep secrets such as `RESTIC_PASSWORD`, cloud credentials, and local DB passwords in environment variables, not in YAML files.
|
|
280
293
|
|
|
281
294
|
## Docs
|
|
282
295
|
|
data/lib/kamal_backup/cli.rb
CHANGED
|
@@ -97,10 +97,6 @@ module KamalBackup
|
|
|
97
97
|
File.join(init_config_root, "kamal-backup.yml")
|
|
98
98
|
end
|
|
99
99
|
|
|
100
|
-
def local_config_path
|
|
101
|
-
File.join(init_config_root, "kamal-backup.local.yml")
|
|
102
|
-
end
|
|
103
|
-
|
|
104
100
|
def write_init_file(path, contents)
|
|
105
101
|
if File.exist?(path)
|
|
106
102
|
say "Exists: #{path}", :yellow
|
|
@@ -119,19 +115,6 @@ module KamalBackup
|
|
|
119
115
|
YAML
|
|
120
116
|
end
|
|
121
117
|
|
|
122
|
-
def local_config_template
|
|
123
|
-
<<~YAML
|
|
124
|
-
# Local machine targets for restore local and drill local.
|
|
125
|
-
# With -d/-c, APP_NAME, DATABASE_ADAPTER, RESTIC_REPOSITORY, and
|
|
126
|
-
# LOCAL_RESTORE_SOURCE_PATHS can come from your Kamal accessory config.
|
|
127
|
-
# Keep secrets such as RESTIC_PASSWORD and cloud credentials in env.
|
|
128
|
-
database_url: postgres://localhost/your_app_development
|
|
129
|
-
backup_paths:
|
|
130
|
-
- storage
|
|
131
|
-
state_dir: tmp/kamal-backup
|
|
132
|
-
YAML
|
|
133
|
-
end
|
|
134
|
-
|
|
135
118
|
def deploy_snippet
|
|
136
119
|
<<~YAML
|
|
137
120
|
accessories:
|
|
@@ -339,12 +322,14 @@ module KamalBackup
|
|
|
339
322
|
desc "init", "Create kamal-backup config stubs for local restore and drill commands"
|
|
340
323
|
def init
|
|
341
324
|
write_init_file(shared_config_path, shared_config_template)
|
|
342
|
-
write_init_file(local_config_path, local_config_template)
|
|
343
325
|
|
|
344
326
|
puts
|
|
345
327
|
puts "Add this accessory block to your Kamal deploy config:"
|
|
346
328
|
puts
|
|
347
329
|
puts deploy_snippet
|
|
330
|
+
puts
|
|
331
|
+
puts "For most Rails apps, restore local and drill local can infer the development database, storage path, and tmp state directory."
|
|
332
|
+
puts "Create config/kamal-backup.local.yml only if you need to override those local defaults."
|
|
348
333
|
end
|
|
349
334
|
|
|
350
335
|
desc "schedule", "Run the foreground scheduler loop"
|
data/lib/kamal_backup/config.rb
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
require "uri"
|
|
2
2
|
require "yaml"
|
|
3
3
|
require_relative "errors"
|
|
4
|
+
require_relative "rails_app"
|
|
4
5
|
|
|
5
6
|
module KamalBackup
|
|
6
7
|
class Config
|
|
@@ -46,7 +47,7 @@ module KamalBackup
|
|
|
46
47
|
|
|
47
48
|
def initialize(env: ENV, cwd: Dir.pwd, defaults: {})
|
|
48
49
|
raw_env = env.to_h
|
|
49
|
-
@env = defaults.to_h.merge(load_config_files(raw_env, cwd: cwd)).merge(raw_env)
|
|
50
|
+
@env = project_defaults(cwd: cwd).merge(defaults.to_h).merge(load_config_files(raw_env, cwd: cwd)).merge(raw_env)
|
|
50
51
|
end
|
|
51
52
|
|
|
52
53
|
def app_name
|
|
@@ -283,6 +284,10 @@ module KamalBackup
|
|
|
283
284
|
end
|
|
284
285
|
|
|
285
286
|
private
|
|
287
|
+
def project_defaults(cwd:)
|
|
288
|
+
RailsApp.new(cwd: cwd).defaults
|
|
289
|
+
end
|
|
290
|
+
|
|
286
291
|
def load_config_files(raw_env, cwd:)
|
|
287
292
|
config_paths(raw_env, cwd: cwd).each_with_object({}) do |path, merged|
|
|
288
293
|
next unless File.file?(path)
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
require "erb"
|
|
2
|
+
require "uri"
|
|
3
|
+
require "yaml"
|
|
4
|
+
|
|
5
|
+
module KamalBackup
|
|
6
|
+
class RailsApp
|
|
7
|
+
DEVELOPMENT_ENV = "development"
|
|
8
|
+
|
|
9
|
+
def initialize(cwd:)
|
|
10
|
+
@cwd = File.expand_path(cwd)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def defaults
|
|
14
|
+
return {} unless rails_app?
|
|
15
|
+
|
|
16
|
+
{}.tap do |defaults|
|
|
17
|
+
defaults.merge!(deploy_defaults)
|
|
18
|
+
defaults.merge!(database_defaults)
|
|
19
|
+
|
|
20
|
+
if local_storage_path
|
|
21
|
+
defaults["BACKUP_PATHS"] = local_storage_path
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
defaults["KAMAL_BACKUP_STATE_DIR"] = File.join(@cwd, "tmp", "kamal-backup")
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
def rails_app?
|
|
30
|
+
File.file?(database_config_path)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def deploy_defaults
|
|
34
|
+
service = fetch(parsed_yaml(deploy_config_path), :service)
|
|
35
|
+
|
|
36
|
+
if service
|
|
37
|
+
{ "APP_NAME" => service.to_s }
|
|
38
|
+
else
|
|
39
|
+
{}
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def database_defaults
|
|
44
|
+
config = local_database_config
|
|
45
|
+
return {} unless config
|
|
46
|
+
|
|
47
|
+
if url = fetch(config, :url)
|
|
48
|
+
adapter = adapter_from_url(url)
|
|
49
|
+
|
|
50
|
+
{
|
|
51
|
+
"DATABASE_ADAPTER" => adapter,
|
|
52
|
+
"DATABASE_URL" => url.to_s
|
|
53
|
+
}.compact
|
|
54
|
+
else
|
|
55
|
+
case normalize_adapter(fetch(config, :adapter))
|
|
56
|
+
when "postgres"
|
|
57
|
+
{
|
|
58
|
+
"DATABASE_ADAPTER" => "postgres",
|
|
59
|
+
"PGHOST" => fetch(config, :host),
|
|
60
|
+
"PGPORT" => fetch(config, :port)&.to_s,
|
|
61
|
+
"PGUSER" => fetch(config, :username),
|
|
62
|
+
"PGDATABASE" => fetch(config, :database)
|
|
63
|
+
}.compact
|
|
64
|
+
when "mysql"
|
|
65
|
+
{
|
|
66
|
+
"DATABASE_ADAPTER" => "mysql",
|
|
67
|
+
"MYSQL_HOST" => fetch(config, :host),
|
|
68
|
+
"MYSQL_PORT" => fetch(config, :port)&.to_s,
|
|
69
|
+
"MYSQL_USER" => fetch(config, :username),
|
|
70
|
+
"MYSQL_DATABASE" => fetch(config, :database)
|
|
71
|
+
}.compact
|
|
72
|
+
when "sqlite"
|
|
73
|
+
database = fetch(config, :database)
|
|
74
|
+
if database
|
|
75
|
+
{
|
|
76
|
+
"DATABASE_ADAPTER" => "sqlite",
|
|
77
|
+
"SQLITE_DATABASE_PATH" => File.expand_path(database.to_s, @cwd)
|
|
78
|
+
}
|
|
79
|
+
else
|
|
80
|
+
{}
|
|
81
|
+
end
|
|
82
|
+
else
|
|
83
|
+
{}
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def local_storage_path
|
|
89
|
+
File.join(@cwd, "storage")
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def local_database_config
|
|
93
|
+
environment = fetch(parsed_yaml(database_config_path), DEVELOPMENT_ENV)
|
|
94
|
+
return nil unless environment.is_a?(Hash)
|
|
95
|
+
|
|
96
|
+
if database_entry?(environment)
|
|
97
|
+
environment
|
|
98
|
+
else
|
|
99
|
+
primary = fetch(environment, :primary)
|
|
100
|
+
primary if primary.is_a?(Hash)
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def database_entry?(config)
|
|
105
|
+
fetch(config, :adapter) || fetch(config, :database) || fetch(config, :url)
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def adapter_from_url(url)
|
|
109
|
+
normalize_adapter(URI.parse(url.to_s).scheme)
|
|
110
|
+
rescue URI::InvalidURIError
|
|
111
|
+
nil
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def parsed_yaml(path)
|
|
115
|
+
return {} unless File.file?(path)
|
|
116
|
+
|
|
117
|
+
rendered = ERB.new(File.read(path), trim_mode: "-").result
|
|
118
|
+
data = YAML.safe_load(rendered, permitted_classes: [], aliases: true)
|
|
119
|
+
|
|
120
|
+
if data.is_a?(Hash)
|
|
121
|
+
data
|
|
122
|
+
else
|
|
123
|
+
{}
|
|
124
|
+
end
|
|
125
|
+
rescue Psych::SyntaxError => e
|
|
126
|
+
raise ConfigurationError, "invalid YAML in #{path}: #{e.message}"
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def fetch(hash, key)
|
|
130
|
+
hash[key] || hash[key.to_s] || hash[key.to_sym]
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def normalize_adapter(value)
|
|
134
|
+
case value.to_s.downcase
|
|
135
|
+
when "postgres", "postgresql"
|
|
136
|
+
"postgres"
|
|
137
|
+
when "mysql", "mysql2", "mariadb"
|
|
138
|
+
"mysql"
|
|
139
|
+
when "sqlite", "sqlite3"
|
|
140
|
+
"sqlite"
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def database_config_path
|
|
145
|
+
File.join(@cwd, "config", "database.yml")
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def deploy_config_path
|
|
149
|
+
File.join(@cwd, "config", "deploy.yml")
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
end
|
data/lib/kamal_backup/version.rb
CHANGED
data/lib/kamal_backup.rb
CHANGED
|
@@ -4,6 +4,7 @@ require_relative "kamal_backup/errors"
|
|
|
4
4
|
require_relative "kamal_backup/command"
|
|
5
5
|
require_relative "kamal_backup/redactor"
|
|
6
6
|
require_relative "kamal_backup/config"
|
|
7
|
+
require_relative "kamal_backup/rails_app"
|
|
7
8
|
require_relative "kamal_backup/kamal_bridge"
|
|
8
9
|
require_relative "kamal_backup/restic"
|
|
9
10
|
require_relative "kamal_backup/evidence"
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: kamal-backup
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.0.pre.
|
|
4
|
+
version: 0.1.0.pre.9
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- crmne
|
|
@@ -61,6 +61,7 @@ files:
|
|
|
61
61
|
- lib/kamal_backup/errors.rb
|
|
62
62
|
- lib/kamal_backup/evidence.rb
|
|
63
63
|
- lib/kamal_backup/kamal_bridge.rb
|
|
64
|
+
- lib/kamal_backup/rails_app.rb
|
|
64
65
|
- lib/kamal_backup/redactor.rb
|
|
65
66
|
- lib/kamal_backup/restic.rb
|
|
66
67
|
- lib/kamal_backup/scheduler.rb
|