about summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--Makefile26
-rw-r--r--README137
-rw-r--r--leo.157
-rwxr-xr-xleo.pl122
-rw-r--r--share/leo.conf17
5 files changed, 151 insertions, 208 deletions
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..e646a32
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,26 @@
+# Install to /usr/local unless otherwise specified, such as `make
+# PREFIX=/app`.
+PREFIX?=/usr/local
+
+INSTALL?=install
+INSTALL_PROGRAM=$(INSTALL) -Dm 755
+INSTALL_DATA=$(INSTALL) -Dm 644
+
+bindir=$(DESTDIR)$(PREFIX)/bin
+sharedir=$(DESTDIR)$(PREFIX)/share
+
+help:
+	@echo "targets:"
+	@awk -F '#' '/^[a-zA-Z0-9_-]+:.*?#/ { print $0 }' $(MAKEFILE_LIST) \
+	| sed -n 's/^\(.*\): \(.*\)#\(.*\)/  \1|-\3/p' \
+	| column -t  -s '|'
+
+install: leo.pl leo.1 # system install
+	$(INSTALL_PROGRAM) leo.pl $(bindir)/leo
+	$(INSTALL_DATA) leo.1 $(sharedir)/man/man1/leo.1
+
+uninstall: # system uninstall
+	rm -f $(bindir)/leo
+	rm -f $(sharedir)/man/man1/leo.1
+
+.PHONY: install uninstall help
diff --git a/README b/README
index 0c04e4c..4fa56de 100644
--- a/README
+++ b/README
@@ -9,16 +9,8 @@ Table of Contents
 ─────────────────
 
 1. Installation
-2. Documentation
-.. 1. Profile
-.. 2. Options
-..... 1. encrypt/sign
-..... 2. signify
-..... 3. gzip
-..... 4. help
-3. Example
-4. Demo
-5. History
+.. 1. OpenBSD
+2. Demo
 
 
 Leo is a simple backup program. It creates tar(1) files from a
@@ -34,12 +26,9 @@ with signify(1).
 ══════════════
 
   ┌────
-  │ # Install all the dependencies. (OpenBSD)
-  │ doas pkg_add p5-IPC-Run3 p5-Config-Tiny p5-Path-Tiny
-  │
   │ # Clone the project.
-  │ git clone https://git.tilde.institute/andinus/leo && \
-  │     cd leo
+  │ git clone https://git.tilde.institute/andinus/leo
+  │ cd leo

   │ # Install dependencies with cpanm.
   │ cpanm --installdeps .
@@ -48,103 +37,32 @@ with signify(1).
   │ cp share/leo.conf $HOME/.config/leo.conf

   │ # Copy the script & make it executable.
-  │ cp leo.pl $HOME/bin/leo && \
-  │     chmod +x $HOME/bin/leo
+  │ sudo make install
   └────
 
 
-2 Documentation
-═══════════════
-
-  I use this to quickly backup some of my files. It works on profiles,
-  profiles are simple lists of files which get backed up.
-
-  *Note*: Backup file's mode will be changed to `0600'.
-
-
-2.1 Profile
+1.1 OpenBSD
 ───────────
 
-  Profile is a simple hash table (`%profile') which contains the list of
-  profiles. The profiles are mapped to a list of file paths which are to
-  be backed up.
-
-  You can run `help' to see all the profiles along with the paths.
-
-
-2.2 Options
-───────────
-
-  Some options can also be passed through environment variables. That
-  allows for configuration in shell rc file & the user can run leo
-  directly without looking at options.
-
-  ━━━━━━━━━━━━━━━━━━━━━━━━
-   encrypt  `LEO_ENCRYPT'
-   sign     `LEO_SIGN'
-   signify  `LEO_SIGNIFY'
-   gzip     `LEO_GZIP'
-  ━━━━━━━━━━━━━━━━━━━━━━━━
-
-
-2.2.1 encrypt/sign
-╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌
-
-  `encrypt_sign' handles `gpg2' related functions. It passes `--yes' by
-  default.
-
-  • `encrypt' / `sign' can also be set in configuration file, one can
-    also configure it for specific profile.
-
-  *Note*: `gpg2' might compress the backups depending on your config.
-   Default is to enable compression, if you don't want this then add `-z
-   0' to `@options'. `-z' specifies the compression level & 0 means no
-   compression.
-
-
-2.2.2 signify
-╌╌╌╌╌╌╌╌╌╌╌╌╌
-
-  `signify' will invoke `signify(1)' & the file will be signed.
-
-
-2.2.3 gzip
-╌╌╌╌╌╌╌╌╌╌
-
-  `gzip' will compress the tar archive with `gzip(1)'.
-
-
-2.2.4 help
-╌╌╌╌╌╌╌╌╌╌
-
-  Running just `leo' will print help.
-
-
-3 Example
-═════════
-
+  Clone & copy the config as shown above, then run these to install
+  dependencies & leo.
   ┌────
-  │ # This will encrypt, sign the tar file for documents, journal, pass &
-  │ # ssh profile.
-  │ leo --encrypt --sign documents journal pass ssh
-  │
-  │ # This will do the same. You can add these environment variables to
-  │ # your shell rc & then just run ``leo documents journal ssh pass'' to
-  │ # do the same.
-  │ LEO_ENCRYPT=1 LEO_SIGN=1 leo documents journal ssh pass
+  │ # Install all the dependencies. (OpenBSD)
+  │ doas pkg_add p5-IPC-Run3 p5-Config-Tiny p5-Path-Tiny

-  │ # Sign the file with signify(1).
-  │ leo --signify journal
+  │ # The man-path `/usr/local/share/man` is not indexed by default on
+  │ # openbsd. Using the `/usr` prefix works around this issue.
+  │ doas make PREFIX=/usr install
   └────
 
 
-4 Demo
+2 Demo
 ══════
 
   It's very easy to setup `leo', I made a demo video to show this. I
   already have Perl environment setup for this.
 
-  *Note*: Leo has changed since this was published.
+  *Note*: Leo has changed *a lot* since this was published.
 
   • Leo 2020-08-31: <https://asciinema.org/a/F97hVfgXDcd9g5IlST1t27ps3>
 
@@ -154,28 +72,3 @@ with signify(1).
 
 [cast file]
 <https://andinus.nand.sh/static/leo/2020-08-31_leo-demo.cast>
-
-
-5 History
-═════════
-
-  This was Leo's initial description:
-
-        Leo is a program to run my personal scripts. You might not
-        find them useful, these were previously shell scripts that
-        I rewrote in Perl.
-
-  I had created a sync function initially & was going to expand it. Then
-  I decided to remove those sync functions because it was too complex, I
-  replaced then with simple `sh' scripts.
-
-  I added a simple `archive' function later & decided to turn Leo into
-  that function. So, it's not a meta-program anymore. I was thinking of
-  creating something that does all the things for me but that'll be too
-  complex.
-
-  Later on the same day I removed dispatch table & switched to using
-  simple hash of lists to store backup paths mapped to profiles. And
-  also changed the word "archive" to "backup" everywhere.
-
-  I added a simple INI based config file before 0.1.0 release.
diff --git a/leo.1 b/leo.1
new file mode 100644
index 0000000..74637f9
--- /dev/null
+++ b/leo.1
@@ -0,0 +1,57 @@
+.TH LEO 1 "14 November 2020" "v0.4.0"
+
+.SH NAME
+leo \- a simple backup program
+
+.SH SYNOPSIS
+.B leo
+[--help] [--version] [--verbose] profiles
+.P
+
+.SH DESRIPTION
+.B leo
+is a simple backup program. It creates tar(1) files from a pre-defined
+list. It can encrypt/sign files with gpg2(1) & sign files with
+signify(1).
+
+I use this to quickly backup some of my files. It works on profiles,
+profiles are simple lists of files which get backed up.
+.SH PROFILE
+Profile is a simple hash table which contains the list of profiles.
+The profiles are mapped to a list of file paths which are to be backed
+up.
+.SH OPTIONS
+The options are as follows:
+.TP
+.B --verbose
+Verbose output. This flag will be passed to commands that leo
+executes.
+.TP
+.BI --version
+Print version.
+.TP
+.B --help
+Print help. This will also print list of profiles with their
+configuration.
+.SH ENVIRONMENT VARIABLES
+As shown in examples below, environment variables can be used to
+configure leo.
+
+Available options are:
+.B L_ENCRYPT
+.B L_SIGN
+.B L_SIGNIFY
+.B L_GZIP
+.SH EXAMPLES
+.TP
+.B leo journal emacs
+Backup `journal' & `emacs' profile.
+.TP
+.B L_ENCRYPT=1 leo journal
+Backup `journal' & encrypt it. This encrypt option can be over-ridden
+by the config file.
+.SH BUGS
+It'll set the tar & gpg output file's mode to 0600 & there is no way
+to configure that.
+.SH AUTHOR
+Andinus <andinus@nand.sh>
diff --git a/leo.pl b/leo.pl
index 3aece04..17b6482 100755
--- a/leo.pl
+++ b/leo.pl
@@ -7,23 +7,17 @@ use feature 'say';
 use IPC::Run3;
 use Path::Tiny;
 use Config::Tiny;
+use POSIX qw(strftime);
 use Getopt::Long qw/ GetOptions /;
 
-my $version = "leo v0.3.4";
+my $version = "leo v0.4.0";
 
 # Options.
-
-my %options = (
-    encrypt => $ENV{LEO_ENCRYPT},
-    sign => $ENV{LEO_SIGN},
-    signify => $ENV{LEO_SIGNIFY},
-    gzip => $ENV{LEO_GZIP},
-);
-
+my %options;
 GetOptions(
     \%options,
-    qw{ verbose encrypt sign signify gzip help version }
-) or die "Error in command line arguments\n";
+    qw{ verbose help version }
+) or die "leo: error in command line arguments\n";
 
 # Print version.
 print $version, "\n" and exit 0 if $options{version};
@@ -43,38 +37,41 @@ foreach my $key (sort keys $config->{_}->%*) {
     $options{$key} = $config->{_}->{$key};
 }
 
+# Die if user is using older config format.
+die "leo: old config format detected\n"
+    if exists $options{encrypt} or exists $options{sign};
+
 my %profile;
 
-foreach my $section (sort keys $config->%*) {
-    next if $section eq "_";
+foreach my $prof (sort keys $config->%*) {
+    next if $prof eq "_";
 
     # Set global values to local profiles.
-    foreach (qw( encrypt sign signify gzip )) {
-        $profile{$section}{$_} = $options{$_};
+    foreach (qw(L_ENCRYPT L_SIGN L_SIGNIFY L_GZIP)) {
+        $profile{$prof}{$_} = $options{$_};
     }
 
-    foreach my $key (sort keys $config->{$section}->%*) {
-        # Override encrypt & sign options with local values.
-        if ($key eq "encrypt"
-            or $key eq "sign"
-            or $key eq "signify"
-            or $key eq "gzip") {
-            $profile{$section}{$key} = $config->{$section}->{$key};
+    foreach my $key (sort keys $config->{$prof}->%*) {
+        # $profile{$prof} contains config values ($), {exclude}
+        # (@), {backup} (@).
+
+        # Set config values.
+        if ( length($key) >= 2
+             and substr($key, 0, 2) eq "L_") {
+            $profile{$prof}{$key} = $config->{$prof}->{$key};
             next;
         }
 
-        push @{ $profile{$section}{exclude} }, $key and next
-            if $config->{$section}->{$key} eq "exclude";
+        push @{ $profile{$prof}{exclude} }, $key and next
+            if $config->{$prof}->{$key} eq "exclude";
 
-        push @{ $profile{$section}{backup} }, $key;
+        push @{ $profile{$prof}{backup} }, $key;
     }
 }
 
 my $date = date();
 my $backup_dir = $options{backup_dir} || "/tmp/backups";
 
-path($backup_dir)->mkpath; # Create backup directory.
-
 my $gpg_fingerprint = $options{gpg_fingerprint} || "`nil'";
 
 my @gpg_recipients;
@@ -92,18 +89,18 @@ foreach my $prof ( @ARGV ) {
         say "++++++++********++++++++";
 
         my $file = "$backup_dir/${prof}/${date}.tar";
-        $file .= ".gz" if $profile{$prof}{gzip};
+        $file .= ".gz" if $profile{$prof}{L_GZIP};
 
         path("$backup_dir/${prof}")->mkpath; # Create backup directory.
         backup($prof, $file);
 
-        encrypt_sign($prof, $file) if $profile{$prof}{sign} or $profile{$prof}{encrypt};
+        encrypt_sign($prof, $file) if $profile{$prof}{L_SIGN} or $profile{$prof}{L_ENCRYPT};
 
         # gpg would've removed the `.tar' file so we sign the
         # encrypted file instead.
         my $encrypted_file = "${file}.gpg";
-        $file = $encrypted_file if $profile{$prof}{sign} or $profile{$prof}{encrypt};
-        signify($prof, $file) if $profile{$prof}{signify};
+        $file = $encrypted_file if $profile{$prof}{L_SIGN} or $profile{$prof}{L_ENCRYPT};
+        signify($prof, $file) if $profile{$prof}{L_SIGNIFY};
     } else {
         warn "[WARN] leo: no such profile :: `$prof' \n";
     }
@@ -114,7 +111,7 @@ sub backup {
     my $tar_file = shift @_;
 
     my @options;
-    push @options, "-z" if $profile{$prof}{gzip};
+    push @options, "-z" if $profile{$prof}{L_GZIP};
 
     # Make @backup_paths relative to '/'.
     my @backup_paths;
@@ -153,7 +150,7 @@ sub backup {
 
     path($tar_file)->chmod(0600)
         and print "Changed `$tar_file' mode to 0600.\n";
-    print "File was compressed with gzip(1)\n" if $profile{$prof}{gzip};
+    print "File was compressed with gzip(1)\n" if $profile{$prof}{L_GZIP};
 
     print "\n" and tar_list($tar_file) if $options{verbose};
 }
@@ -166,14 +163,14 @@ sub encrypt_sign {
     my @options = ();
     push @options, "--default-key", $gpg_fingerprint;
 
-    if ( $profile{$prof}{encrypt} ) {
+    if ( $profile{$prof}{L_ENCRYPT} ) {
         push @options, "--encrypt";
         push @options, "--recipient", $gpg_fingerprint;
         push @options, "--recipient", $_
             foreach @gpg_recipients;
     }
 
-    push @options, "--sign" if $profile{$prof}{sign};
+    push @options, "--sign" if $profile{$prof}{L_SIGN};
     push @options, "--verbose" if $options{verbose};
 
     say "\nEncrypt/Sign: $file";
@@ -184,8 +181,8 @@ sub encrypt_sign {
     $? # We assume non-zero is an error.
         ? die "Encrypt/Sign failed :: $?\n"
         : print "\nOutput: $file.gpg";
-    print " [Encrypted]" if $profile{$prof}{encrypt};
-    print " [Signed]" if $profile{$prof}{sign};
+    print " [Encrypted]" if $profile{$prof}{L_ENCRYPT};
+    print " [Signed]" if $profile{$prof}{L_SIGN};
     print "\n";
 
     unlink $file and say "$file deleted."
@@ -222,35 +219,22 @@ sub signify {
 sub HelpMessage {
     say qq{Backup files to $backup_dir.
 
++ means included
+- means excluded
+
 Profile:};
     foreach my $prof (sort keys %profile) {
         print "    $prof";
-        print " [Encrypt]" if $profile{$prof}{encrypt};
-        print " [Sign]" if $profile{$prof}{sign};
-        print " [Signify]" if $profile{$prof}{signify};
-        print " [gzip]" if $profile{$prof}{gzip};
+        print " [Encrypt]" if $profile{$prof}{L_ENCRYPT};
+        print " [Sign]" if $profile{$prof}{L_SIGN};
+        print " [Signify]" if $profile{$prof}{L_SIGNIFY};
+        print " [gzip]" if $profile{$prof}{L_GZIP};
         print "\n";
-        print "        $_\n" foreach $profile{$prof}{backup}->@*;
+        print "        + $_\n" foreach $profile{$prof}{backup}->@*;
+        print "        - $_\n" foreach $profile{$prof}{exclude}->@*;
         print "\n";
     }
     print qq{Options:
-    --encrypt };
-    print "[Enabled]" if $options{encrypt};
-    print qq{
-        Encrypt files with $gpg_fingerprint\n
-    --sign };
-    print "[Enabled]" if $options{sign};
-    print qq{
-        Sign files with $gpg_fingerprint\n
-    --signify };
-    print "[Enabled]" if $options{signify};
-    print qq{
-        Sign with signify(1)\n
-    --gzip };
-        print "[Enabled]" if $options{gzip};
-    print qq{
-        Compress with gzip(1)
-
     --version [$version]
     --verbose
     --help
@@ -260,22 +244,4 @@ Profile:};
 sub tar_create { run3 ["/bin/tar", "cf", @_]; }
 sub tar_list { run3 ["/bin/tar", "tvf", @_]; }
 
-sub date {
-    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) =
-        gmtime(time);
-
-    $year += 1900; # $year contains the number of years since 1900.
-
-    # $mon the month in the range 0..11 , with 0 indicating January
-    # and 11 indicating December.
-    my @months = qw( 01 02 03 04 05 06 07 08 09 10 11 12 );
-    my $month = $months[$mon];
-
-    # Pad by 2 zeros.
-    $mday = sprintf "%02d", $mday;
-    $hour = sprintf "%02d", $hour;
-    $min = sprintf "%02d", $min;
-    $sec = sprintf "%02d", $sec;
-
-    return "$year-$month-${mday}T${hour}:${min}:${sec}Z";
-}
+sub date { return strftime '%FT%T%z', gmtime() }
diff --git a/share/leo.conf b/share/leo.conf
index 75e9d0a..e46a61e 100644
--- a/share/leo.conf
+++ b/share/leo.conf
@@ -9,15 +9,14 @@ gpg_bin = gpg2
 signify_seckey = /home/andinus/.totally_not_my_key.sec
 
 # Options.
-encrypt = 1
-sign = 1
+L_ENCRYPT = 1
+L_SIGN = 1
 
 # Profile names in [].
 [journal]
 # Local options.
-encrypt = 0
-sign = 0
-signify = 1
+L_ENCRYPT = 0
+L_SIGNIFY = 1
 
 # Paths to backup. '=' after path is important.
 /home/andinus/documents/not_important.org.gpg =
@@ -25,11 +24,13 @@ signify = 1
 
 [emacs]
 # compress this archive
-gzip = 1
+L_GZIP = 1
 
 /home/andinus/.emacs.d =
-/home/andinus/.elfeed =
-/home/andinus/.org-timestamps =
+/home/andinus/.emacs.d/elpa = exclude
+
+[emacs/elpa]
+/home/andinus/.emacs.d/elpa =
 
 [projects]
 /home/andinus/projects =