#!/usr/bin/perl

# Written by Adam H. Lewenberg <adamhl@stanford.edu>
# Copyright 2018, 2020, 2021 Board of Trustees, Leland Stanford Jr. University

## no critic (CodeLayout::ProhibitParensWithBuiltins);
## no critic (InputOutput::RequireBracedFileHandleWithPrint);
## no critic (Documentation::RequirePodSections);

use strict ;
use warnings ;
use autodie ;

use Carp ;
use Pod::Usage ;
use Getopt::Long::Descriptive ;

use Stanford::Puppet::RepoInfo ;

my $GIT = 'git' ;

## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ##
sub exit_with_error {
    my ($msg, $err_no) = @_ ;
    print STDERR "$msg \n" ;
    exit $err_no ;
}
## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ##

#<<<  perltiudy ignore this section as I like the options formatted this way.
my ($opt, $usage) = describe_options(
    'git-info %o (list|help|manual) <some-arg>',
    [ 'long|l',       'show full repository directory name' ],
    [ 'extra-info|x', 'show more commit information' ],
    [ 'sync-seconds', 'show number of seconds behind sync rather than human-readable time' ],
    [ 'max-seconds-behind',
                      'show the maximum number of seconds behind the live directory is' ],
    [ 'nagios',
                      'generate a Nagios-compatible message and exit code (implies --max-seconds-behind)' ],
    [ 'warning=s',    'warning limit (in seconds)', { default => 120 } ],
    [ 'critical=s',   'critical limit (in seconds)', { default => 240 } ],
    [ 'verbose|v',    'print extra stuff'            ],
    [ 'help|h',       'print usage message and exit', {'shortcircuit' => 1}],
    );
#>>>

if ($opt->help()) {
    print $usage->text ;
    exit 0 ;
}

my $VERBOSE = $opt->verbose() ;
my $LONG    = $opt->long() ;
my $OUTPUT_FORMAT ;
my $SYNC_SECONDS ;

if ($opt->extra_info) {
    $OUTPUT_FORMAT = 'standard' ;
} else {
    $OUTPUT_FORMAT = 'date-only' ;
}

if ($opt->sync_seconds) {
    $SYNC_SECONDS = 1 ;
} else {
    $SYNC_SECONDS = 0 ;
}

## ACTION
my $ACTION ;
my %valid_actions = (
    'help'   => 1,
    'manual' => 1,
    'list'   => 1,
    );

if ($ARGV[0]) {
    $ACTION = $ARGV[0] ;
} else {
    exit_with_error('missing action', 1) ;
}

if (!exists($valid_actions{$ACTION})) {
    print $usage->text ;
    exit_with_error("action '$ACTION' unrecognized", 1) ;
}

## Handle help and manual actions.
if ($ACTION eq 'help') {
    print $usage->text ;
    exit 0 ;
} elsif ($ACTION eq 'manual') {
    pod2usage(-verbose => 2) ;
    exit 0 ;
}

## REGEX
my $REGEX ;
if ($ARGV[1]) {
    $REGEX = $ARGV[1] ;
} else {
    $REGEX = q{.} ;
}

my $ri = Stanford::Puppet::RepoInfo->new(
    'regex'           => $REGEX,
    'output-format'   => $OUTPUT_FORMAT,
    'sync-seconds'    => $SYNC_SECONDS,
    'long'            => $LONG,
    'verbose'         => $VERBOSE,
    'nagios-warning'  => $opt->warning(),
    'nagios-critical' => $opt->critical(),
) ;

# Can only have SYNC_SECONDS with CodeManager
if ($SYNC_SECONDS && (! $ri->code_manager())) {
    my $msg = "error: the --sync-seconds option can only be used with Puppet Enterprise" ;
    exit_wit_error($msg, 1) ;
}

if ($opt->nagios()) {
    $ri->nagios() ;
} elsif ($opt->max_seconds_behind()) {
    my ($seconds_behind, $repo_most_behind) = $ri->max_seconds_behind() ;
    print $seconds_behind ;
    print "\n" ;
} else {
    my @messages = $ri->last_commits() ;

    if (@messages) {
        print join("\n", @messages) ;
        print "\n" ;
    }
}

exit ;

__END__

=head1 NAME

git-info - Show Puppet Git repository commit information

=head1 SYNOPSIS

git-info I<action> [options] [regular-expression]

=head1 DESCRIPTION

B<git-info> displays the last commit information for Puppet Git
repositories on a Puppet server. The B<git-info> script supports both
Puppet Enterprise and Puppet Open Source. That is, for each Git repository
on the Puppet server, it finds the latest commit date among all
that repository's branches and displays that date. Note that the only
commit information provided by this utility is commit hash, author name,
and commit date.

The repositories returned will be those that match the Perl-compatible
B<regular-expression>.  If B<regular-expression> is omitted all
repositories will be returned.

For Puppet Enterprise the output will show the number of seconds behind
the live repository is from the repository source.

You must supply an I<action> as the first argument. This should be
one of the following:

    list
    help
    manual

See the EXAMPLES section below for more information.

=head1 OPTIONS

=over 4

=item B<-x|--extra-info>

Rather than showing just the commit date show the commit hash, date, and author.

=item B<-l|--long>

Show the full repository name (includes the remote Git repository host).

=item B<--sync-seconds>

This option will show each repo followed by the number of seconds it is
out of sync. If the repo is in sync the number shown will be "0". This
option cannot be used when running against Puppet Open Source.

=item B<--max-seconds-behind>

Look at all repositories and for each find out how many seconds behind the
repository's live directory from the repository's last commit. Return the
maximum of all these numbers. If all repositories are in sync will
output "0".

=item B<--nagios>

Calculate the maximum number of seconds behind the live directories are as
per the C<--max-seconds-behind> option. Output a Nagios compatible exit
code and system message based on the C<--warning> and C<--critical>
values.

=item B<--critical> warning_seconds

This option is only used with the C<--nagios> option and sets the
"critical" threshold in seconds. Thus, passing in C<--critical 420> means
to generate a Nagios critical exit code if the max-seconds-behind exceeds
420.
If C<--critical> is not greater than C<--warning> B<git-info> will abort.
Default value: 240

=item B<--warning> warning_seconds

This option is only used with the C<--nagios> option and sets the
"warning" threshold in seconds. Thus, passing in C<--warning 300> means to
generate a Nagios warning exit code if the max-seconds-behind exceeds 300
but does not exceed the value of C<--critical>.
If C<--critical> is not greater than C<--warning> B<git-info> will abort.
Default value: 120

=item B<-v|--verbose>

Show debugging information.

=item B<-h|--help>

Show short help screen and exit.

=back

=head1 EXIT STATUS

The script will exit with 0 if the script completes and there were no
failures, 1 for any other reason.

=head1 NOTES

Later.

=head1 EXAMPLES

Display the last commit information for all Git repositories on the Puppet
Enterprise server:

    git-info list

Same as previous but only show more commit information:

    git-info list --extra-info

Show the last commit information for all Git repositories matching a
supplied PCRE:

    git-info list 'idg.*'

Generate a Nagios-compatible exit code and message if there is any
repository which is more than 5 minutes out-of-sync (critical) or 3
minutes out-of-sync (warning):

    git-info list --nagios --critical=300 --warning=180

Show a short help screen::

    git-info help

Display the man page:

    git-info manual

=head1 SEE ALSO

git(1)

=head1 AUTHOR

Adam Lewenberg <adamhl@stanford.edu>

=head1 COPYRIGHT AND LICENSE

Copyright 2018, 2020, 2021 The Board of Trustees of the Leland Stanford Junior
University.  All rights reserved.

Permission to use, copy, modify, and distribute this software and its
documentation for any purpose and without fee is hereby granted, provided
that the above copyright notice appear in all copies and that both that
copyright notice and this permission notice appear in supporting
documentation, and that the name of Stanford University not be used in
advertising or publicity pertaining to distribution of the software
without specific, written prior permission.  Stanford University makes no
representations about the suitability of this software for any purpose.
It is provided "as is" without express or implied warranty.

THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.

=cut

#############################################################################
#############################################################################




1;
