From c400af5ed4304744ac96519674ad6debbceb966f Mon Sep 17 00:00:00 2001 From: Martin Puppe Date: Wed, 25 Mar 2020 23:27:00 +0100 Subject: [PATCH 01/14] Add option -r for executing restic The new option -r allows executing any restic command with the repository and password settings specified in the configuration file. --- mpbackup | 52 ++++++++++++++++++++++++++++++++-------------------- 1 file changed, 32 insertions(+), 20 deletions(-) diff --git a/mpbackup b/mpbackup index 0060439..191864e 100644 --- a/mpbackup +++ b/mpbackup @@ -37,17 +37,18 @@ 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 @@ -56,7 +57,14 @@ OptionParser.new do |parser| options[:prune] = v end - parser.on('--version', "Print version number") do + 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 puts "#{NAME}, #{VERSION}" exit end @@ -83,9 +91,6 @@ def check 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]} || [] @@ -117,9 +122,6 @@ def backup(config) 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(' ')}") @@ -131,17 +133,32 @@ def prune(config) check end +def run_restic(config) + puts 'Executing restic with the following arguments …' + puts "ARGV: #{ARGV}" + exec('restic', *ARGV) +end + +def act(config, options) + puts "Applying configuration ‘#{config['name']}’ …" + set_restic_vars(config) + if options[:prune] + prune config + elsif options[:run_restic] + run_restic config + else + backup config + end + unset_restic_vars +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')}’)." - if options[:prune] - prune configs[0] - else - backup configs[0] - end + act(configs[0], options) else config_hash = configs.map {|c| [c['name'], c]}.to_h config_names.each do |name| @@ -150,11 +167,6 @@ else end end config_names.each do |config_name| - config = config_hash[config_name] - if options[:prune] - prune config - else - backup config - end + act(config_hash[config_name], options) end end From 9a7ac16d9fbe7486a43e6bc1e1e6c3012fbb2553 Mon Sep 17 00:00:00 2001 From: Martin Puppe Date: Wed, 25 Mar 2020 23:30:33 +0100 Subject: [PATCH 02/14] Version 0.2.0 -> 0.3.0 --- mpbackup | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mpbackup b/mpbackup index 191864e..0825454 100644 --- a/mpbackup +++ b/mpbackup @@ -6,7 +6,7 @@ require 'yaml' DEFAULT_CONFIG_FILE = Pathname.new('/etc/mpbackup/config.yaml') NAME = 'mpbackup' -VERSION = '0.2.0' +VERSION = '0.3.0' def set_restic_vars(config) repo = config['repository'] From 9e3a9f27ff3f018c93ed4a044b3b150935e2bef2 Mon Sep 17 00:00:00 2001 From: Martin Puppe Date: Wed, 25 Mar 2020 23:42:57 +0100 Subject: [PATCH 03/14] Move method definitions to the top --- mpbackup | 142 +++++++++++++++++++++++++++---------------------------- 1 file changed, 70 insertions(+), 72 deletions(-) diff --git a/mpbackup b/mpbackup index 0825454..748aab3 100644 --- a/mpbackup +++ b/mpbackup @@ -32,6 +32,76 @@ def error(exit_status, message) exit exit_status 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 "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 +end + +def prune(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 + +def run_restic(config) + puts 'Executing restic with the following arguments …' + puts "ARGV: #{ARGV}" + exec('restic', *ARGV) +end + +def act(config, options) + puts "Applying configuration ‘#{config['name']}’ …" + set_restic_vars(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 = {} @@ -80,78 +150,6 @@ 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 "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 '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 - -def run_restic(config) - puts 'Executing restic with the following arguments …' - puts "ARGV: #{ARGV}" - exec('restic', *ARGV) -end - -def act(config, options) - puts "Applying configuration ‘#{config['name']}’ …" - set_restic_vars(config) - if options[:prune] - prune config - elsif options[:run_restic] - run_restic config - else - backup config - end - unset_restic_vars -end - puts "Using config file #{config_file} …" configs = YAML.load_stream(File.open(config_file)) config_names = options[:config_names] From dc53973745045ad9f4892d8384298267dac1b92a Mon Sep 17 00:00:00 2001 From: Martin Puppe Date: Thu, 26 Mar 2020 12:08:52 +0100 Subject: [PATCH 04/14] Optionally read repository from file --- mpbackup | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/mpbackup b/mpbackup index 748aab3..7112ec7 100644 --- a/mpbackup +++ b/mpbackup @@ -9,8 +9,14 @@ NAME = 'mpbackup' VERSION = '0.3.0' def set_restic_vars(config) - repo = config['repository'] - puts "Repository: #{repo}" + 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 if config['password-file'] puts "Reading password from file #{config['password-file']} …" password = File.open(config['password-file'], &:gets).chomp From c1d760da2eab55757353394275e7ed44ef9ffe89 Mon Sep 17 00:00:00 2001 From: Martin Puppe Date: Thu, 26 Mar 2020 12:11:03 +0100 Subject: [PATCH 05/14] Version 0.3.0 -> 0.4.0 --- mpbackup | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mpbackup b/mpbackup index 7112ec7..0613d7d 100644 --- a/mpbackup +++ b/mpbackup @@ -6,7 +6,7 @@ require 'yaml' DEFAULT_CONFIG_FILE = Pathname.new('/etc/mpbackup/config.yaml') NAME = 'mpbackup' -VERSION = '0.3.0' +VERSION = '0.4.0' def set_restic_vars(config) if config['repository-file'] From 43fff22a2a71c3b0d472ff37cfcbd1de6289d28f Mon Sep 17 00:00:00 2001 From: Martin Puppe Date: Mon, 14 Sep 2020 20:49:46 +0200 Subject: [PATCH 06/14] Delete .envrc --- .envrc | 1 - 1 file changed, 1 deletion(-) delete mode 100644 .envrc diff --git a/.envrc b/.envrc deleted file mode 100644 index 1d953f4..0000000 --- a/.envrc +++ /dev/null @@ -1 +0,0 @@ -use nix From e4ddf366956b8d6fd3bc4aef629be2438fc44509 Mon Sep 17 00:00:00 2001 From: Martin Puppe Date: Mon, 14 Sep 2020 20:53:55 +0200 Subject: [PATCH 07/14] Add config option for cache home directory --- mpbackup | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/mpbackup b/mpbackup index 0613d7d..efded99 100644 --- a/mpbackup +++ b/mpbackup @@ -8,6 +8,8 @@ DEFAULT_CONFIG_FILE = Pathname.new('/etc/mpbackup/config.yaml') NAME = 'mpbackup' VERSION = '0.4.0' +$original_cache_home = ENV['XDG_CACHE_HOME'] + def set_restic_vars(config) if config['repository-file'] puts "Reading repository from file #{config['repository-file']} …" @@ -23,6 +25,10 @@ def set_restic_vars(config) else password = STDIN.getpass('Please put in your restic password: ') end + if config['cache-home'] + puts "Setting XDG_CACHE_HOME to #{config['cache-home']} …" + ENV['XDG_CACHE_HOME'] = config['cache-home'] + end ENV['RESTIC_REPOSITORY'] = repo ENV['RESTIC_PASSWORD'] = password @@ -31,6 +37,11 @@ end def unset_restic_vars ENV.delete('RESTIC_REPOSITORY') ENV.delete('RESTIC_PASSWORD') + if $original_cache_home + ENV['XDG_CACHE_HOME'] = $original_cache_home + else + ENV.delete('XDG_CACHE_HOME') + end end def error(exit_status, message) From 22e8c358c69a924cf0fdc12a762a09909f1a9b59 Mon Sep 17 00:00:00 2001 From: Martin Puppe Date: Mon, 14 Sep 2020 20:54:42 +0200 Subject: [PATCH 08/14] Don't print repository path The full path may contain a password. --- mpbackup | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mpbackup b/mpbackup index efded99..9ac879d 100644 --- a/mpbackup +++ b/mpbackup @@ -55,7 +55,7 @@ def check puts("Command: #{check_command.join(' ')}") system(*check_command) if $?.exitstatus > 0 - error(1, "Checking restic repository #{ENV['RESTIC_REPOSITORY']} failed.") + error(1, "Checking restic repository failed.") end end From 0f3797eb32eb5feafccfb517444289cd4617a969 Mon Sep 17 00:00:00 2001 From: Martin Puppe Date: Mon, 14 Sep 2020 20:55:35 +0200 Subject: [PATCH 09/14] Add support for more restic flags --- mpbackup | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/mpbackup b/mpbackup index 9ac879d..8f69f08 100644 --- a/mpbackup +++ b/mpbackup @@ -62,9 +62,11 @@ 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'} + flags = flags.map{|k,v| "--#{k}=#{v.to_s}"} exclude = config.dig('backup', 'exclude')&.flat_map{|e| ['--exclude', e]} || [] paths = config.dig('backup', 'paths') || [] - backup_command = ['restic', 'backup', *exclude, *paths] + backup_command = ['restic', 'backup', *exclude, *paths, *flags] puts("Command: #{backup_command.join(' ')}") system(*backup_command) if $?.exitstatus > 0 From 67058870e2d9e9b3341140394b0e894e32707030 Mon Sep 17 00:00:00 2001 From: Martin Puppe Date: Mon, 14 Sep 2020 20:56:53 +0200 Subject: [PATCH 10/14] Version 0.4.0 -> 0.5.0 --- mpbackup | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mpbackup b/mpbackup index 8f69f08..57a7449 100644 --- a/mpbackup +++ b/mpbackup @@ -6,7 +6,7 @@ require 'yaml' DEFAULT_CONFIG_FILE = Pathname.new('/etc/mpbackup/config.yaml') NAME = 'mpbackup' -VERSION = '0.4.0' +VERSION = '0.5.0' $original_cache_home = ENV['XDG_CACHE_HOME'] From 0d98802d8a43a92d0e41ce591360f67b94a4e008 Mon Sep 17 00:00:00 2001 From: Martin Puppe Date: Thu, 4 Mar 2021 09:31:29 +0100 Subject: [PATCH 11/14] Add support for specifying tags for backups --- mpbackup | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/mpbackup b/mpbackup index 57a7449..aba9d47 100644 --- a/mpbackup +++ b/mpbackup @@ -62,9 +62,11 @@ 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'} + 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 = ['restic', 'backup', *exclude, *paths, *flags] puts("Command: #{backup_command.join(' ')}") From 84824aae02d13862d908b6f55a547a5e9d6a8bb1 Mon Sep 17 00:00:00 2001 From: Martin Puppe Date: Fri, 5 Mar 2021 14:56:29 +0100 Subject: [PATCH 12/14] Replace config option cache-home with cache-dir --- mpbackup | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/mpbackup b/mpbackup index aba9d47..4e70eb0 100644 --- a/mpbackup +++ b/mpbackup @@ -8,8 +8,6 @@ DEFAULT_CONFIG_FILE = Pathname.new('/etc/mpbackup/config.yaml') NAME = 'mpbackup' VERSION = '0.5.0' -$original_cache_home = ENV['XDG_CACHE_HOME'] - def set_restic_vars(config) if config['repository-file'] puts "Reading repository from file #{config['repository-file']} …" @@ -25,9 +23,9 @@ def set_restic_vars(config) else password = STDIN.getpass('Please put in your restic password: ') end - if config['cache-home'] - puts "Setting XDG_CACHE_HOME to #{config['cache-home']} …" - ENV['XDG_CACHE_HOME'] = config['cache-home'] + 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 @@ -37,11 +35,7 @@ end def unset_restic_vars ENV.delete('RESTIC_REPOSITORY') ENV.delete('RESTIC_PASSWORD') - if $original_cache_home - ENV['XDG_CACHE_HOME'] = $original_cache_home - else - ENV.delete('XDG_CACHE_HOME') - end + ENV.delete('RESTIC_CACHE_DIR') end def error(exit_status, message) From b0973f25d74e90872800bf629c65fdb665f8e37d Mon Sep 17 00:00:00 2001 From: Martin Puppe Date: Sat, 6 Mar 2021 16:13:34 +0100 Subject: [PATCH 13/14] Make path to restic executable configurable --- mpbackup | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/mpbackup b/mpbackup index 4e70eb0..1935947 100644 --- a/mpbackup +++ b/mpbackup @@ -38,14 +38,20 @@ def unset_restic_vars 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) STDERR.puts("Error: #{message}") exit exit_status end -def check +def check(config) puts 'Checking restic repo …' - check_command = ['restic', 'check'] + check_command = [config['restic-path'], 'check'] puts("Command: #{check_command.join(' ')}") system(*check_command) if $?.exitstatus > 0 @@ -62,20 +68,20 @@ def backup(config) 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 = ['restic', 'backup', *exclude, *paths, *flags] + 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 + 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 = ['restic', 'forget'] + flags + 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 @@ -88,7 +94,7 @@ end def prune(config) puts 'Pruning restic repo …' - prune_command = ['restic', 'prune'] + prune_command = [config['restic-path'], 'prune'] puts("Command: #{prune_command.join(' ')}") system(*prune_command) if $?.exitstatus > 0 @@ -101,12 +107,13 @@ end def run_restic(config) puts 'Executing restic with the following arguments …' puts "ARGV: #{ARGV}" - exec('restic', *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] From cfba3f37c50dde899ffa0c1d6a81d0f15ed29911 Mon Sep 17 00:00:00 2001 From: Martin Puppe Date: Sat, 6 Mar 2021 16:14:40 +0100 Subject: [PATCH 14/14] Version 0.5.0 -> 0.6.0 --- mpbackup | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mpbackup b/mpbackup index 1935947..594526b 100644 --- a/mpbackup +++ b/mpbackup @@ -6,7 +6,7 @@ require 'yaml' DEFAULT_CONFIG_FILE = Pathname.new('/etc/mpbackup/config.yaml') NAME = 'mpbackup' -VERSION = '0.5.0' +VERSION = '0.6.0' def set_restic_vars(config) if config['repository-file']