From c0a2300b52c682975fbe365de21547db71d18a38 Mon Sep 17 00:00:00 2001 From: Andinus Date: Wed, 13 Jan 2021 11:29:00 +0530 Subject: Port leo to Raku This is a huge change. I had some more ideas in mind but I won't be able to work on them. leo's raku version can be improved. I'm personally using the Raku version because the rotate feature is nice. --- leo.raku | 249 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 249 insertions(+) create mode 100755 leo.raku (limited to 'leo.raku') diff --git a/leo.raku b/leo.raku new file mode 100755 index 0000000..365e3ad --- /dev/null +++ b/leo.raku @@ -0,0 +1,249 @@ +#!/usr/bin/env raku + +use v6.d; +use Config; + +enum Actions ( + backup => "Make backup", + rotate => "Rotate the backups", + list => "List profiles / profile options", +); + +sub USAGE { + say $*USAGE; + say "\nActions:"; + for Actions.enums.kv -> $action, $description { + say " " x 4, $action, + " " x (Actions.enums.keys.map(*.chars).max - $action.chars) + 4, + $description; + } +} + +sub read-configuration(--> Config) { + return Config.new.read( + %*ENV ?? + %*ENV ~ "/leo.toml" !! %*ENV ~ "/.config/leo.toml" + ); +} + +# If nothing is passed then named argument multi prevails so we just +# create one so that USAGE() gets printed if no argument is passed. +multi sub MAIN(Bool :$help-implied) is hidden-from-USAGE { USAGE() } +multi sub MAIN(Bool :$version) is hidden-from-USAGE { say "Leo v0.6.0" } + +multi sub MAIN(Actions $action, Bool :v(:$verbose)) is hidden-from-USAGE { + note 'Verbosity on' if $verbose; + note 'Dispatched Actions only sub' if $verbose; + note 'Reading configuration' if $verbose; + + my Config $config = read-configuration(); + + given $action.key { + when 'list' { + say "Profiles:"; + say " " x 4, $_ for $config.keys.sort; + exit 0; + } + default { + note "(!!) No profile passed"; + exit 1; + } + } +} + +multi sub MAIN( + Actions $action, #= action to perform + *@profiles, #= profiles to perform the action on + Bool :v(:$verbose), #= increase verbosity + Bool :$version, #= print version +) { + note 'Verbosity on' if $verbose; + note 'Reading configuration' if $verbose; + + my Config $config = read-configuration(); + + # Default number of backups to hold / keep. + my int ($default_hold, $default_keep) = (1, 2); + + # $backup_dir holds path to backup directory. + my Str $backup_dir; + my DateTime $date = DateTime.new(time); + + $backup_dir = $config ?? $config !! "/tmp/backups"; + + # Fix $backup_dir permission. + unless $backup_dir.IO.mode eq "0700" { + note "Setting mode to 700: '$backup_dir'" if $verbose; + $backup_dir.IO.chmod(0o700); + } + + for @profiles -> $profile { + say "[$profile]"; + without ($config{$profile}) { + note "(!!) No such profile"; + exit 1; + } + + my Str $profile_dir = $backup_dir ~ "/$profile"; + + # Create the profile backup directory if it doesn't exist. + unless $profile_dir.IO ~~ :d { + note "Creating profile backup directory: '$profile_dir'" if $verbose; + mkdir($profile_dir, 0o700) or die "$!"; + } + + given $action.key { + when 'backup' { + my IO $backup_file = "$profile_dir/$date.tgz".IO; + + say "Backup: ", $backup_file.Str; + + note "\nCalling backup-profile subroutine" if $verbose; + backup-profile($profile, $backup_file, $config, $verbose); + + if ( + ($config{$profile} // $config) or + ($config{$profile} // $config) + ) { + note "\nCalling gnupg subroutine" if $verbose; + gnupg($profile, $backup_file, $config, $verbose); + } + } + when 'list' { + say "Encryption ", ($config{$profile} // + $config) ?? + "ON" !! "OFF"; + + say "GnuPG sign ", ($config{$profile} // + $config) ?? + "ON" !! "OFF"; + + say "Holding ", ( + $config{$profile} // + $config // $default_hold + ), " backups"; + + say "Keeping ", ( + $config{$profile} // + $config // $default_keep + ), " backups"; + + say "Base directory: ", + ($config{$profile} // + $config // + "/"); + + say "\nPaths: "; + say " " x 4, $_ for @($config{$profile} // + "."); + + if $config{$profile}.defined { + say "\nExclude: "; + say " " x 4, $_ for @($config{$profile}); + } + } + when 'rotate' { + my DateTime %backups; + for dir $profile_dir -> $path { + next unless $path.f; + if $path.Str ~~ /$profile '/' (.*) + ['.tar'|'.tar.gpg'|'.tgz'|'.tgz.gpg']$/ -> $match { + %backups{$path.Str} = DateTime.new($match[0].Str); + } + } + + say "Total backups: ", %backups.elems; + exit 0 if %backups.elems == 0; # Exit if 0 backups. + + # Hold the backups that are to be held. Default is to hold 1 + # backup. + for %backups.sort(*.value).map(*.key).head( + $config{$profile} // + $config // $default_hold + ) { + note "(H) Holding: ", $_ if $verbose; + %backups{$_}:delete; + } + + # We'll keep `n' latest backups where `n' equals to + # the number of backups we want to keep. Default is to + # keep 2 backups. + for %backups.sort(*.value).map(*.key).tail( + $config{$profile} // + $config // $default_keep + ) { + note "(K) Keeping: ", $_ if $verbose; + %backups{$_}:delete; + } + + # Now we just remove all backups in %backups. + for %backups -> $backup { + note "(D) Deleting: ", $backup.key if $verbose; + unlink($backup.key); + } + } + default { + note "Invalid action"; + exit 1; + } + } + } +} + +sub backup-profile ( + Str $profile, IO $backup_file, Config $config, Bool $verbose +) { + my IO $base_dir = $_.IO with ( + $config{$profile} // + $config // + "/" + ); + chdir $base_dir or die "$!"; + + my Str @options = <-c -z>; + push @options, '-vv' if $verbose; + push @options, '-f', $backup_file.Str; + push @options, '-C', $base_dir.Str; + + my Str @backup_paths; + for ( + @($config{$profile} // ".") + ) -> $path { + if $path.IO.d { + push @backup_paths, $_.Str for dir $path; + } else { + push @backup_paths, $path; + } + } + + run , @options, ( + @backup_paths (-) @($config{$profile}) + ).keys; +} + +sub gnupg ( + Str $profile, IO $backup_file, Config $config, Bool $verbose +) { + my Str @options = '--yes'; + + if ( + $config{$profile} // + $config + ) { + push @options, '--encrypt'; + with $config { + push @options, "--recipient", $_ for @($_); + } + } + + push @options, $verbose ?? '--verbose' !! '--quiet'; + push @options, '--sign' if ( + $config{$profile} // + $config + ); + push @options, '--default-key', $_ with $config; + + run , @options, $backup_file; + + unlink($backup_file) or die "$!"; +} -- cgit 1.4.1-2-gfad0