Compare commits

..

No commits in common. "master" and "v0.2.0" have entirely different histories.

2 changed files with 77 additions and 108 deletions

1
.envrc Normal file
View file

@ -0,0 +1 @@
use nix

184
mpbackup
View file

@ -6,27 +6,17 @@ require 'yaml'
DEFAULT_CONFIG_FILE = Pathname.new('/etc/mpbackup/config.yaml')
NAME = 'mpbackup'
VERSION = '0.6.0'
VERSION = '0.2.0'
def set_restic_vars(config)
if config['repository-file']
puts "Reading repository from file #{config['repository-file']} …"
repo = File.open(config['repository-file'], &:gets).chomp
elsif config['repository']
repo = config['repository']
else
error(1, 'No repository has been specified.')
end
repo = config['repository']
puts "Repository: #{repo}"
if config['password-file']
puts "Reading password from file #{config['password-file']} …"
password = File.open(config['password-file'], &:gets).chomp
else
password = STDIN.getpass('Please put in your restic password: ')
end
if config['cache-dir']
puts "Setting RESTIC_CACHE_DIR to #{config['cache-dir']} …"
ENV['RESTIC_CACHE_DIR'] = config['cache-dir']
end
ENV['RESTIC_REPOSITORY'] = repo
ENV['RESTIC_PASSWORD'] = password
@ -35,13 +25,6 @@ end
def unset_restic_vars
ENV.delete('RESTIC_REPOSITORY')
ENV.delete('RESTIC_PASSWORD')
ENV.delete('RESTIC_CACHE_DIR')
end
def set_restic_path(config)
if !config.key? 'restic-path'
config['restic-path'] = 'restic'
end
end
def error(exit_status, message)
@ -49,98 +32,22 @@ def error(exit_status, message)
exit exit_status
end
def check(config)
puts 'Checking restic repo …'
check_command = [config['restic-path'], 'check']
puts("Command: #{check_command.join(' ')}")
system(*check_command)
if $?.exitstatus > 0
error(1, "Checking restic repository failed.")
end
end
def backup(config)
puts "Backing up with restic …"
# https://restic.readthedocs.io/en/latest/040_backup.html#including-and-excluding-files
flags = config['backup'].select{|k,v|
k != 'exclude' && k != 'paths' && k != 'tags'}
flags = flags.map{|k,v| "--#{k}=#{v.to_s}"}
exclude = config.dig('backup', 'exclude')&.flat_map{|e| ['--exclude', e]} || []
tags = config.dig('backup', 'tags')&.flat_map{|t| ['--tag', t]} || []
paths = config.dig('backup', 'paths') || []
backup_command = [config['restic-path'], 'backup', *exclude, *paths, *flags]
puts("Command: #{backup_command.join(' ')}")
system(*backup_command)
if $?.exitstatus > 0
error(1, 'Failed to do backup.')
end
check(config)
if config.dig('forget', 'enable')
puts 'Forgetting unnecessary snapshots …'
flags = config['forget'].select{|k,v| k != 'enable'}
flags = flags.flat_map{|k,v| ['--' + k, v.to_s]}
forget_command = [config['restic-path'], 'forget'] + flags
puts("Command: #{forget_command.join(' ')}")
system(*forget_command)
# Data will only be deleted when `restic prune` is executed or when
# `restic forget` is called with `--prune`.
if $?.exitstatus > 0
error(1, "Forgetting snapshots failed.")
end
end
end
def prune(config)
puts 'Pruning restic repo …'
prune_command = [config['restic-path'], 'prune']
puts("Command: #{prune_command.join(' ')}")
system(*prune_command)
if $?.exitstatus > 0
error(1, 'Failed to prune.')
end
check
end
def run_restic(config)
puts 'Executing restic with the following arguments …'
puts "ARGV: #{ARGV}"
exec(config['restic-path'], *ARGV)
end
def act(config, options)
puts "Applying configuration #{config['name']} …"
set_restic_vars(config)
set_restic_path(config)
if options[:prune]
prune config
elsif options[:run_restic]
run_restic config
else
backup config
end
unset_restic_vars
end
unset_restic_vars
options = {}
options[:config_names] = []
options[:prune] = false
options[:run_restic] = false
OptionParser.new do |parser|
parser.on("-c NAME", "--configuration-name NAME", "Name of the configuration to use") do |v|
options[:config_names] << v
end
parser.on("-f CONFIG_FILE", "--config-file CONFIG_FILE", "Path to the configuration file.") do |v|
parser.on("-f CONFIG_FILE", "--config-file CONFIG_FILE", "Path to the configuration file") do |v|
options[:config_file] = v
end
parser.on("-h", "--help", "Print this help.") do
parser.on("-h", "--help", "Print this help") do
puts parser
exit
end
@ -149,14 +56,7 @@ OptionParser.new do |parser|
options[:prune] = v
end
parser.on('-r', '--run-restic', 'Set environment variables '\
'RESTIC_REPOSITORY and RESTIC_PASSWORD, execute restic (while passing '\
'all following command line arguments to restic).') do |v|
options[:run_restic] = v
parser.terminate
end
parser.on('--version', "Print version number.") do
parser.on('--version', "Print version number") do
puts "#{NAME}, #{VERSION}"
exit
end
@ -172,13 +72,76 @@ if !Pathname.new(config_file).exist? then
error(1, "Config file #{config_file} has not been found!")
end
def check
puts 'Checking restic repo …'
check_command = ['restic', 'check']
puts("Command: #{check_command.join(' ')}")
system(*check_command)
if $?.exitstatus > 0
error(1, "Checking restic repository #{ENV['RESTIC_REPOSITORY']} failed.")
end
end
def backup(config)
puts "Applying configuration #{config['name']} …"
set_restic_vars(config)
puts "Backing up with restic …"
# https://restic.readthedocs.io/en/latest/040_backup.html#including-and-excluding-files
exclude = config.dig('backup', 'exclude')&.flat_map{|e| ['--exclude', e]} || []
paths = config.dig('backup', 'paths') || []
backup_command = ['restic', 'backup', *exclude, *paths]
puts("Command: #{backup_command.join(' ')}")
system(*backup_command)
if $?.exitstatus > 0
error(1, 'Failed to do backup.')
end
check
if config.dig('forget', 'enable')
puts 'Forgetting unnecessary snapshots …'
flags = config['forget'].select{|k,v| k != 'enable'}
flags = flags.flat_map{|k,v| ['--' + k, v.to_s]}
forget_command = ['restic', 'forget'] + flags
puts("Command: #{forget_command.join(' ')}")
system(*forget_command)
# Data will only be deleted when `restic prune` is executed or when
# `restic forget` is called with `--prune`.
if $?.exitstatus > 0
error(1, "Forgetting snapshots failed.")
end
end
unset_restic_vars
end
def prune(config)
puts "Applying configuration #{config['name']} …"
set_restic_vars(config)
puts 'Pruning restic repo …'
prune_command = ['restic', 'prune']
puts("Command: #{prune_command.join(' ')}")
system(*prune_command)
if $?.exitstatus > 0
error(1, 'Failed to prune.')
end
check
end
puts "Using config file #{config_file} …"
configs = YAML.load_stream(File.open(config_file))
config_names = options[:config_names]
if config_names.empty?
puts "No configuration name has been given. Will use the first "\
"configuration from the file (#{configs.dig(0, 'name')})."
act(configs[0], options)
if options[:prune]
prune configs[0]
else
backup configs[0]
end
else
config_hash = configs.map {|c| [c['name'], c]}.to_h
config_names.each do |name|
@ -187,6 +150,11 @@ else
end
end
config_names.each do |config_name|
act(config_hash[config_name], options)
config = config_hash[config_name]
if options[:prune]
prune config
else
backup config
end
end
end