Module: KamalBackup::CLI::Helpers

Included in:
KamalBackup::CLI, CommandBase
Defined in:
lib/kamal_backup/cli.rb

Instance Method Summary collapse

Instance Method Details

#accessory_nameObject



84
85
86
# File 'lib/kamal_backup/cli.rb', line 84

def accessory_name
  @accessory_name ||= bridge.accessory_name(preferred: local_preferences.accessory_name)
end

#accessory_reboot_commandObject



113
114
115
116
117
118
# File 'lib/kamal_backup/cli.rb', line 113

def accessory_reboot_command
  argv = ["bin/kamal", "accessory", "reboot", accessory_name]
  argv.concat(["-c", options[:config_file]]) if options[:config_file]
  argv.concat(["-d", options[:destination]]) if options[:destination]
  Shellwords.join(argv)
end

#bridgeObject



63
64
65
66
67
68
69
70
# File 'lib/kamal_backup/cli.rb', line 63

def bridge
  @bridge ||= KamalBridge.new(
    redactor: redactor,
    config_file: options[:config_file],
    destination: options[:destination],
    env: command_env
  )
end

#command_envObject



14
15
16
# File 'lib/kamal_backup/cli.rb', line 14

def command_env
  CLI.command_env || ENV
end

#confirm!(message) ⇒ Object



138
139
140
141
142
143
144
145
146
147
148
# File 'lib/kamal_backup/cli.rb', line 138

def confirm!(message)
  return if options[:yes]

  unless $stdin.tty?
    raise ConfigurationError, "confirmation required; rerun with --yes"
  end

  unless yes?("#{message} [y/N]")
    raise ConfigurationError, "aborted"
  end
end

#default_deploy_config?Boolean

Returns:

  • (Boolean)


76
77
78
# File 'lib/kamal_backup/cli.rb', line 76

def default_deploy_config?
  File.file?(File.expand_path(KamalBridge::DEFAULT_CONFIG_FILE))
end

#deploy_snippetObject



196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
# File 'lib/kamal_backup/cli.rb', line 196

def deploy_snippet
  <<~YAML
    accessories:
      backup:
        image: ghcr.io/crmne/kamal-backup:#{VERSION}
        host: your-server.example.com
        files:
          - config/kamal-backup.yml:/app/config/kamal-backup.yml:ro
        env:
          secret:
            - PGPASSWORD
            - RESTIC_PASSWORD
            - AWS_ACCESS_KEY_ID
            - AWS_SECRET_ACCESS_KEY
        volumes:
          - "your_app_storage:/data/storage:ro"
          - "your_app_backup_state:/var/lib/kamal-backup"
  YAML
end

#deployment_mode?Boolean

Returns:

  • (Boolean)


72
73
74
# File 'lib/kamal_backup/cli.rb', line 72

def deployment_mode?
  !options[:destination].to_s.strip.empty? || !options[:config_file].to_s.strip.empty?
end

#direct_appObject



22
23
24
# File 'lib/kamal_backup/cli.rb', line 22

def direct_app
  @direct_app ||= App.new(config: Config.new(env: command_env), redactor: redactor)
end

#ensure_remote_version_match!Object

Raises:



103
104
105
106
107
108
109
110
111
# File 'lib/kamal_backup/cli.rb', line 103

def ensure_remote_version_match!
  return if remote_version == VERSION

  raise ConfigurationError, <<~MESSAGE.strip
    local gem version #{VERSION} does not match remote accessory version #{remote_version}.
    Reboot the backup accessory to pick up the latest image:
    #{accessory_reboot_command}
  MESSAGE
end

#exec_remote(argv, require_version_match: true) ⇒ Object



92
93
94
95
96
97
98
99
100
101
# File 'lib/kamal_backup/cli.rb', line 92

def exec_remote(argv, require_version_match: true)
  ensure_remote_version_match! if require_version_match

  result = bridge.execute_on_accessory(
    accessory_name: accessory_name,
    command: Shellwords.join(argv)
  )
  print(result.stdout)
  result
end

#init_config_rootObject



163
164
165
166
# File 'lib/kamal_backup/cli.rb', line 163

def init_config_root
  config_file = options[:config_file] || KamalBridge::DEFAULT_CONFIG_FILE
  File.dirname(File.expand_path(config_file))
end

#local_appObject



26
27
28
# File 'lib/kamal_backup/cli.rb', line 26

def local_app
  @local_app ||= App.new(config: local_command_config, redactor: redactor)
end

#local_command_configObject



34
35
36
37
38
39
40
41
42
43
44
45
46
# File 'lib/kamal_backup/cli.rb', line 34

def local_command_config
  @local_command_config ||= begin
    if deployment_mode?
      Config.new(
        env: command_env,
        defaults: production_source_defaults,
        config_paths: [Config::LOCAL_CONFIG_PATH]
      )
    else
      Config.new(env: command_env)
    end
  end
end

#local_preferencesObject



30
31
32
# File 'lib/kamal_backup/cli.rb', line 30

def local_preferences
  @local_preferences ||= Config.new(env: command_env)
end


120
121
122
123
124
125
126
127
# File 'lib/kamal_backup/cli.rb', line 120

def print_remote_version_status
  status = remote_version == VERSION ? "in sync" : "out of sync"

  puts("local: #{VERSION}")
  puts("remote: #{remote_version}")
  puts("status: #{status}")
  puts("fix: #{accessory_reboot_command}") if status == "out of sync"
end

#production_source_defaultsObject



48
49
50
# File 'lib/kamal_backup/cli.rb', line 48

def production_source_defaults
  shared_config_source_defaults.merge(bridge.local_restore_defaults(accessory_name: accessory_name))
end

#prompt_required(label) ⇒ Object



150
151
152
153
154
155
156
157
158
159
160
161
# File 'lib/kamal_backup/cli.rb', line 150

def prompt_required(label)
  unless $stdin.tty?
    raise ConfigurationError, "#{label.downcase} is required; pass it on the command line"
  end

  value = ask("#{label}:").to_s.strip
  if value.empty?
    raise ConfigurationError, "#{label.downcase} is required"
  else
    value
  end
end

#redactorObject



18
19
20
# File 'lib/kamal_backup/cli.rb', line 18

def redactor
  @redactor ||= Redactor.new(env: command_env)
end

#remote_command_mode?Boolean

Returns:

  • (Boolean)


80
81
82
# File 'lib/kamal_backup/cli.rb', line 80

def remote_command_mode?
  deployment_mode? || default_deploy_config?
end

#remote_versionObject



88
89
90
# File 'lib/kamal_backup/cli.rb', line 88

def remote_version
  @remote_version ||= bridge.remote_version(accessory_name: accessory_name)
end

#shared_config_pathObject



168
169
170
# File 'lib/kamal_backup/cli.rb', line 168

def shared_config_path
  File.join(init_config_root, "kamal-backup.yml")
end

#shared_config_source_defaultsObject



52
53
54
55
56
57
58
59
60
61
# File 'lib/kamal_backup/cli.rb', line 52

def shared_config_source_defaults
  config = Config.new(env: {}, config_paths: [Config::SHARED_CONFIG_PATH], load_project_defaults: false)

  {}.tap do |defaults|
    defaults["APP_NAME"] = config.app_name if config.app_name
    defaults["DATABASE_ADAPTER"] = config.database_adapter if config.database_adapter
    defaults["RESTIC_REPOSITORY"] = config.restic_repository if config.restic_repository
    defaults["LOCAL_RESTORE_SOURCE_PATHS"] = config.backup_paths.join("\n") if config.backup_paths.any?
  end
end

#shared_config_templateObject



182
183
184
185
186
187
188
189
190
191
192
193
194
# File 'lib/kamal_backup/cli.rb', line 182

def shared_config_template
  <<~YAML
    accessory: backup
    app_name: your-app
    database_adapter: postgres
    database_url: postgres://your-app@your-db:5432/your_app_production
    backup_paths:
      - /data/storage
    restic_repository: s3:https://s3.example.com/your-app-backups
    restic_init_if_missing: true
    backup_schedule_seconds: 86400
  YAML
end

#validate_deploy_configObject



129
130
131
132
133
134
135
136
# File 'lib/kamal_backup/cli.rb', line 129

def validate_deploy_config
  config = Config.new(
    env: bridge.accessory_environment(accessory_name: accessory_name),
    config_paths: [Config::SHARED_CONFIG_PATH],
    load_project_defaults: false
  )
  config.validate_backup(check_files: false)
end

#write_init_file(path, contents) ⇒ Object



172
173
174
175
176
177
178
179
180
# File 'lib/kamal_backup/cli.rb', line 172

def write_init_file(path, contents)
  if File.exist?(path)
    say "Exists: #{path}", :yellow
  else
    FileUtils.mkdir_p(File.dirname(path))
    File.write(path, contents)
    say "Created: #{path}", :green
  end
end