kamal-backup 0.3.0.beta21 → 0.3.1

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.
@@ -1,10 +1,16 @@
1
- require "erb"
2
- require "uri"
3
- require "yaml"
1
+ # frozen_string_literal: true
2
+
3
+ require 'erb'
4
+ require 'uri'
5
+ require 'yaml'
6
+ require_relative 'databases/base'
7
+ require_relative 'yaml_access'
4
8
 
5
9
  module KamalBackup
6
10
  class RailsApp
7
- DEVELOPMENT_ENV = "development"
11
+ include YamlAccess
12
+
13
+ DEVELOPMENT_ENV = 'development'
8
14
 
9
15
  def initialize(cwd:)
10
16
  @cwd = File.expand_path(cwd)
@@ -17,136 +23,120 @@ module KamalBackup
17
23
  defaults.merge!(deploy_defaults)
18
24
  defaults.merge!(database_defaults)
19
25
 
20
- if local_storage_path
21
- defaults["BACKUP_PATHS"] = local_storage_path
22
- end
26
+ defaults['BACKUP_PATHS'] = local_storage_path if local_storage_path
23
27
 
24
- defaults["KAMAL_BACKUP_STATE_DIR"] = File.join(@cwd, "tmp", "kamal-backup")
28
+ defaults['KAMAL_BACKUP_STATE_DIR'] = File.join(@cwd, 'tmp', 'kamal-backup')
25
29
  end
26
30
  end
27
31
 
28
32
  private
29
- def rails_app?
30
- File.file?(database_config_path)
31
- end
32
33
 
33
- def deploy_defaults
34
- service = fetch(parsed_yaml(deploy_config_path), :service)
34
+ def rails_app?
35
+ File.file?(database_config_path)
36
+ end
37
+
38
+ def deploy_defaults
39
+ service = fetch(parsed_yaml(deploy_config_path), :service)
35
40
 
36
- if service
37
- { "APP_NAME" => service.to_s }
38
- else
39
- {}
40
- end
41
+ if service
42
+ { 'APP_NAME' => service.to_s }
43
+ else
44
+ {}
41
45
  end
46
+ end
42
47
 
43
- def database_defaults
44
- config = local_database_config
45
- return {} unless config
48
+ def database_defaults
49
+ config = local_database_config
50
+ return {} unless config
46
51
 
47
- if url = fetch(config, :url)
48
- adapter = adapter_from_url(url)
52
+ if (url = fetch(config, :url))
53
+ adapter = adapter_from_url(url)
49
54
 
55
+ {
56
+ 'DATABASE_ADAPTER' => adapter,
57
+ 'DATABASE_URL' => url.to_s
58
+ }.compact
59
+ else
60
+ case Databases.normalize_adapter(fetch(config, :adapter))
61
+ when 'postgres'
50
62
  {
51
- "DATABASE_ADAPTER" => adapter,
52
- "DATABASE_URL" => url.to_s
63
+ 'DATABASE_ADAPTER' => 'postgres',
64
+ 'PGHOST' => fetch(config, :host),
65
+ 'PGPORT' => fetch(config, :port)&.to_s,
66
+ 'PGUSER' => fetch(config, :username),
67
+ 'PGDATABASE' => fetch(config, :database)
53
68
  }.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"
69
+ when 'mysql'
70
+ {
71
+ 'DATABASE_ADAPTER' => 'mysql',
72
+ 'MYSQL_HOST' => fetch(config, :host),
73
+ 'MYSQL_PORT' => fetch(config, :port)&.to_s,
74
+ 'MYSQL_USER' => fetch(config, :username),
75
+ 'MYSQL_DATABASE' => fetch(config, :database)
76
+ }.compact
77
+ when 'sqlite'
78
+ database = fetch(config, :database)
79
+ if database
65
80
  {
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
81
+ 'DATABASE_ADAPTER' => 'sqlite',
82
+ 'SQLITE_DATABASE_PATH' => File.expand_path(database.to_s, @cwd)
83
+ }
82
84
  else
83
85
  {}
84
86
  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
87
  else
99
- primary = fetch(environment, :primary)
100
- primary if primary.is_a?(Hash)
88
+ {}
101
89
  end
102
90
  end
91
+ end
103
92
 
104
- def database_entry?(config)
105
- fetch(config, :adapter) || fetch(config, :database) || fetch(config, :url)
106
- end
93
+ def local_storage_path
94
+ File.join(@cwd, 'storage')
95
+ end
96
+
97
+ def local_database_config
98
+ environment = fetch(parsed_yaml(database_config_path), DEVELOPMENT_ENV)
99
+ return nil unless environment.is_a?(Hash)
107
100
 
108
- def adapter_from_url(url)
109
- normalize_adapter(URI.parse(url.to_s).scheme)
110
- rescue URI::InvalidURIError
111
- nil
101
+ if database_entry?(environment)
102
+ environment
103
+ else
104
+ primary = fetch(environment, :primary)
105
+ primary if primary.is_a?(Hash)
112
106
  end
107
+ end
113
108
 
114
- def parsed_yaml(path)
115
- return {} unless File.file?(path)
109
+ def database_entry?(config)
110
+ fetch(config, :adapter) || fetch(config, :database) || fetch(config, :url)
111
+ end
116
112
 
117
- rendered = ERB.new(File.read(path), trim_mode: "-").result
118
- data = YAML.safe_load(rendered, permitted_classes: [], aliases: true)
113
+ def adapter_from_url(url)
114
+ Databases.normalize_adapter(URI.parse(url.to_s).scheme)
115
+ rescue URI::InvalidURIError
116
+ nil
117
+ end
119
118
 
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
119
+ def parsed_yaml(path)
120
+ return {} unless File.file?(path)
128
121
 
129
- def fetch(hash, key)
130
- hash[key] || hash[key.to_s] || hash[key.to_sym]
131
- end
122
+ rendered = ERB.new(File.read(path), trim_mode: '-').result
123
+ data = YAML.safe_load(rendered, permitted_classes: [], aliases: true)
132
124
 
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
125
+ if data.is_a?(Hash)
126
+ data
127
+ else
128
+ {}
142
129
  end
130
+ rescue Psych::SyntaxError => e
131
+ raise ConfigurationError, "invalid YAML in #{path}: #{e.message}"
132
+ end
143
133
 
144
- def database_config_path
145
- File.join(@cwd, "config", "database.yml")
146
- end
134
+ def database_config_path
135
+ File.join(@cwd, 'config', 'database.yml')
136
+ end
147
137
 
148
- def deploy_config_path
149
- File.join(@cwd, "config", "deploy.yml")
150
- end
138
+ def deploy_config_path
139
+ File.join(@cwd, 'config', 'deploy.yml')
140
+ end
151
141
  end
152
142
  end
@@ -1,8 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module KamalBackup
2
4
  class Redactor
3
5
  SECRET_KEY_PATTERN = /(pass|password|secret|token|key|credential|authorization)/i
4
6
  SENSITIVE_KEY_PATTERN = /(?:pass|password|secret|token|key|credential|authorization)|\A(?:user|username|pguser|.*_user|.*_username)\z/i
5
- REDACTED = "[REDACTED]"
7
+ REDACTED = '[REDACTED]'
6
8
 
7
9
  def initialize(secret_values: [], env: ENV)
8
10
  @secret_values = Array(secret_values).compact.map(&:to_s).reject { |value| value.empty? || value.length < 4 }
@@ -31,22 +33,25 @@ module KamalBackup
31
33
  end
32
34
 
33
35
  private
34
- def known_secret_values
35
- @known_secret_values ||= begin
36
- env_secrets = @env.each_with_object([]) do |(key, value), values|
37
- values << value.to_s if key.to_s.match?(SECRET_KEY_PATTERN)
38
- end
39
36
 
40
- (@secret_values + env_secrets).compact.uniq.reject { |value| value.empty? || value.length < 4 }
37
+ def known_secret_values
38
+ @known_secret_values ||= begin
39
+ env_secrets = @env.each_with_object([]) do |(key, value), values|
40
+ values << value.to_s if key.to_s.match?(SECRET_KEY_PATTERN)
41
41
  end
42
+
43
+ (@secret_values + env_secrets).compact.uniq.reject { |value| value.empty? || value.length < 4 }
42
44
  end
45
+ end
43
46
 
44
- def redact_url_credentials(value)
45
- value.gsub(%r{(://)([^/\s]+)@}) do
46
- "#{$1}#{REDACTED}@"
47
- end.gsub(/([?&](?:password|token|secret|key|access_key_id|secret_access_key)=)[^&\s]+/i) do
48
- "#{$1}#{REDACTED}"
49
- end
47
+ def redact_url_credentials(value)
48
+ redacted = value.gsub(%r{(://)([^/\s]+)@}) do
49
+ "#{::Regexp.last_match(1)}#{REDACTED}@"
50
50
  end
51
+
52
+ redacted.gsub(/([?&](?:password|token|secret|key|access_key_id|secret_access_key)=)[^&\s]+/i) do
53
+ "#{::Regexp.last_match(1)}#{REDACTED}"
54
+ end
55
+ end
51
56
  end
52
57
  end