From 72559aa8d6adbd51c45cea3d1972d81a1222d9f7 Mon Sep 17 00:00:00 2001 From: "Bradley M. Kuhn" Date: Sun, 30 Aug 2020 20:14:54 -0700 Subject: [PATCH] Support Git repository and update it from upstream. I don't remember what the --repositoryURL option was supposed to do, but instead I now assume that the it's a Git repository if you give --branchName and make a copy, and pull from upstream and reload when there are updates. --- bean-query-goofy-daemon.plx | 119 +++++++++++++++++++++++++++++------- 1 file changed, 96 insertions(+), 23 deletions(-) diff --git a/bean-query-goofy-daemon.plx b/bean-query-goofy-daemon.plx index 775fec4..f2fa73f 100755 --- a/bean-query-goofy-daemon.plx +++ b/bean-query-goofy-daemon.plx @@ -7,27 +7,34 @@ use strict; use warnings; use autodie qw(:all); +use sigtrap 'handler' => \&CleanupEvertything, 'normal-signals', 'stack-trace' => 'error-signals'; + +use Carp; + use Getopt::Long; use File::Spec::Functions; -use File::Temp qw/:mktemp/; +use File::Temp qw/:mktemp tempdir/; +use Git::Repository 'Log'; use POSIX qw(mkfifo); use IPC::Shareable; use Expect; +my $RSYNC_CMD = '/usr/bin/rsync'; + # We have to set the PAGER to a passthrough text program to assure that # output does not get paused $ENV{PAGER} = "/usr/bin/cat"; my $BEANCOUNT_QUERY_CMD = "/usr/bin/bean-query"; -my($VERBOSE, $BEANCOUNT_DIR, $LOAD_FILE, $REPOSITORY_URL, $FIFO_DIR) = (0, undef, undef, undef, undef); +my($VERBOSE, $BEANCOUNT_DIR, $LOAD_FILE, $BRANCH_NAME, $FIFO_DIR) = (0, undef, undef, undef, undef); GetOptions("verbose=i" => \$VERBOSE, "beancountDir=s" => \$BEANCOUNT_DIR, - "loadFile=s" => \$LOAD_FILE, "repositoryURL=s" => \$REPOSITORY_URL, 'fifoDir=s' => \$FIFO_DIR); + "loadFile=s" => \$LOAD_FILE, "branchName=s" => \$BRANCH_NAME, 'fifoDir=s' => \$FIFO_DIR); sub UsageAndExit($) { - print STDERR "usage: $0 --loadFile=/path/to/file.beancount --beancountDir=/path/to/beancountdir --fifoDir=/path/to/directory/for/fifos [ --repositoryURL=URL_OF_REPOSITORY --verbose=N ]\n"; + print STDERR "usage: $0 --loadFile=/path/to/file.beancount --beancountDir=/path/to/beancountdir --fifoDir=/path/to/directory/for/fifos [ --branchName=BRANCH_NAME --verbose=N ]\n"; print STDERR "\n $_[0]\n"; exit 2; } @@ -41,21 +48,60 @@ chdir $BEANCOUNT_DIR; UsageAndExit("/path/to/file.beancount must a relative path to a file that exists in $BEANCOUNT_DIR") unless (defined $LOAD_FILE and -r $LOAD_FILE); -if (defined $REPOSITORY_URL) { - UsageAndExit("if --repositoryURL is provided, $BEANCOUNT_DIR must be a checkout of a Git repository, but there is no $BEANCOUNT_DIR/.git/config.") - unless (-r '.git/config'); +my($tempRepository, $tempRepositoryDirectory); - open(my $gitFH, "<", ".git/config"); - my $repoString; - while (my $line = <$gitFH>) { - chomp $line; - $repoString = $1 - if $line =~ /^\s*url\s*=\s*(\S+)\s*$/; +sub CleanupEvertything { + $tempRepository = undef if (defined $tempRepository); + $tempRepositoryDirectory = undef if (defined $tempRepositoryDirectory); + StopRunningBeanQuery(); + croak @_; +} +if (defined $BRANCH_NAME) { + my $absGitRepositoryDirectory = File::Spec->rel2abs( $BEANCOUNT_DIR ); + $tempRepositoryDirectory = tempdir('beancountquerygoofydaemongit_' . $$ . '_XXXXXXXXXXX', + TMPDIR => 1, CLEANUP => 1); + print STDERR "Copy Git repository to $tempRepositoryDirectory...." if $VERBOSE > 2; + system($RSYNC_CMD, '-Ha', "$absGitRepositoryDirectory/", "$tempRepositoryDirectory/"); + print STDERR "copy completed.\n" if $VERBOSE > 2; + $tempRepository = Git::Repository->new( work_tree => $tempRepositoryDirectory ); + + chdir $tempRepositoryDirectory; + $tempRepository->run(clean => '-fx', { quiet => 1 }); + $tempRepository->run(reset => '--hard', { quiet => 1 }); + $tempRepository->run(clean => '-fx', { quiet => 1 }); + $tempRepository->run(checkout => '$BRANCH_NAME', { quiet => 1 }); +} + +sub CheckUpstreamAndPull { + # Returns true iff. a pull was required and the files have changed. + return 0 unless (defined $tempRepository); + my $options = { quiet => 1 }; + if ($VERBOSE > 5) { + print STDERR "...clean & git pull..."; + $options = {}; + } + print STDERR "...check if upstream Git changed..." if $VERBOSE > 5; + my $updateOutput = $tempRepository->run(remote => 'update', $options); + print "$updateOutput" if defined $updateOutput and $updateOutput !~ /^\s*$/ and $VERBOSE > 5; + my $curRev = $tempRepository->run('rev-parse' => '@'); + my $remoteRev = $tempRepository->run('rev-parse' => '@{u}'); + my $baseRev = $tempRepository->run('merge-base' => '@', '@{u}'); + print STDERR "...$curRev is current, $remoteRev is remote Rev $baseRev is base...\n" if $VERBOSE > 6; + if ($curRev eq $remoteRev) { + print STDERR "no change..." if $VERBOSE > 5; + return 0; + } elsif ($curRev eq $baseRev) { + $tempRepository->run(clean => '-fx', $options); + $tempRepository->run(reset => '--hard', $options); + $tempRepository->run(clean => '-fx', $options); + my $pullOutput = $tempRepository->run('pull'); + print STDERR "\nPerformed pull since remote updated:\n $pullOutput\n" if ($VERBOSE > 0); + return 1; + } else { + CleanupEvertything(); + die("our local Git has $curRev, upstream is at $remoteRev, and the base is $baseRev " . + "so give up entirely on trying to make this work."); } - close $gitFH; - UsageAndExit("if --repositoryURL is provided, the checkout found in $BEANCOUNT_DIR must be of that repository, but .git/config does not list a URL.") unless defined $REPOSITORY_URL; - UsageAndExit("if --repositoryURL is provided, the checkout found in $BEANCOUNT_DIR must be of that repository, but it instead appears to be a checkout of $repoString.") - unless ($repoString eq $REPOSITORY_URL); } my $glue = 'BeAn'; my %options = ( @@ -81,34 +127,61 @@ sub StartRunningBeanQuery { $format = $currentFormat unless defined $format; $currentFormat = $format; + if (defined $tempRepository) { + print STDERR "Clearing temp files from repository..." if $VERBOSE > 4; + $tempRepository->run(clean => '-fx', { quiet => 1 }); + $tempRepository->run(reset => '--hard', { quiet => 1 }); + $tempRepository->run(clean => '-fx', { quiet => 1 }); + } else { + my(@findCmd) = ("/usr/bin/find", '.', '-name', '*.picklecache'); + if ($VERBOSE > 4) { + print STDERR "Cleared the following picklecache files (none listed means none existed)...\n"; + push(@findCmd, '-ls'); + } + push(@findCmd, '-exec', '/usr/bin/rm', '-f', '{}', ';'); + system(@findCmd); + print STDERR "...done clearing picklecache files.\n" if ($VERBOSE > 4); + } my @cmd = ($BEANCOUNT_QUERY_CMD); push(@cmd, '-f', $format) if defined $format; push(@cmd, $LOAD_FILE); + print STDERR "Starting beancount..." if $VERBOSE > 4; $runningBeanQuery = Expect->spawn(@cmd); + print STDERR "...spawned & loading data..." if $VERBOSE > 4; $runningBeanQuery->log_stdout(0); $runningBeanQuery->expect(undef, -re => '^\s*beancount\s*\>\s*') or die("Unable to find beancount prompt, output was instead: ". $runningBeanQuery->before() . $runningBeanQuery->after()); + print STDERR "now ready." if $VERBOSE > 4; print STDERR "Beancount started with output of:\n", $runningBeanQuery->before(), $runningBeanQuery->match(), $runningBeanQuery->after(), "\n" if ($VERBOSE > 3); } +sub StopRunningBeanQuery { + return if not defined $runningBeanQuery; + $runningBeanQuery->send("exit\n"); + $runningBeanQuery->soft_close(); +} +CheckUpstreamAndPull(); StartRunningBeanQuery('text'); - print STDERR "Beancount started. Main loop begins." if $VERBOSE > 0; while (1) { if (not defined $query{question}) { - print STDERR "No question posed..." if $VERBOSE > 2; + print STDERR "No question posed..." if $VERBOSE > 5; + if (CheckUpstreamAndPull()) { + StopRunningBeanQuery(); + StartRunningBeanQuery(); + } if (defined $query{fifoName}) { - print STDERR "fifo still active; locking to clear it..." if $VERBOSE > 2; + print STDERR "fifo still active; locking to clear it..." if $VERBOSE > 5; (tied %query)->shlock; - print STDERR "clearing fifo, $query{fifoName}..." if $VERBOSE > 2; + print STDERR "clearing fifo, $query{fifoName}..." if $VERBOSE > 5; no autodie 'unlink'; unlink($query{fifoName}); %query = (); (tied %query)->shunlock; - print STDERR "fifo cleared & lock released." if $VERBOSE > 2; + print STDERR "fifo cleared & lock released." if $VERBOSE > 5; } - print STDERR "sleep for 2 seconds\n" if $VERBOSE > 2; + print STDERR "sleep for 2 seconds\n" if $VERBOSE > 5; sleep 2; next; } elsif ($query{question} !~ /^[\,\=\~\-\@\w.\s\"\'\_\(\)\<\>\*\.\!]+$/) {