#!/usr/bin/perl -w
use strict;
use utf8;

=encoding utf-8

=head1 NAME

updater - a command-line tool to mass-update multiple version-controlled projects

=head1 SYNOPSIS

    updater [-ds] init [DIR]
    updater [-ds] [-D DIR]
    updater [-ds] list [-Y my.yaml] [DIR]
    updater [-ds] update [-Y my.yaml] [DIR]
    updater [-ds] backup [-Y my.yaml] [DIR]
    updater [-ds] restore [-Y my.yaml] [DIR]

=head1 OPTIONS

=over 4

=item B<-d, --debug>

Print debug information on STDOUT

=item B<-D /path/to/root/dir, --dir=/path/to/root/dir>

Sets working data directory

Default: current directory (.)

=item B<-h, --help>

Show short help information and quit

=item B<-H, --longhelp>

Show long help information and quit

=item B<-l, --ls, --list>

Turns off update performing and show list of found folders

=item B<-s, --silent>

Turns on the silent mode

=item B<-V, --version>

Print the version number of the program and quit

=item B<-Y YAML_FILE, --yaml=YAML_FILE>

Specifies yaml file to manually migration directory structure

Default: C<.updater.yml>

=back

=head1 COMMANDS

=over 4

=item B<init>, B<initialize>, B<ini>

    updater init

Forced init the work makefile

=item B<restore>, B<re>

    updater restore

Restore file struct by YAML file

=item B<status>, B<info>, B<st>

    updater status

Show status information

=item B<update>, B<up>

    updater update

Performs update found folders

=back

=head1 DESCRIPTION

The updater tool provides a unified way to update directory structures
containing multiple version-controlled projects (e.g., Git, SVN, etc.).
With a single command, users can synchronize all subdirectories to their
latest state by automatically executing update commands such as git pull
or svn up in each repository. This simplifies and automates the process
of keeping multiple projects up to date.

=head2 CUSTOM UPDATING

If any subdirectory contains a file named C<.updater> with a list of commands
to execute, that directory will take priority during processing, even if it
contains system subdirectories such as C<.svn>, C<.git>, or others.

All commands listed in the C<.updater> file must be executed strictly in
the specified order.

If the C<.updater> file is empty or contains no commands, the directory will be
skipped. This allows you to explicitly disable processing of certain directories,
even if they contain system subdirectories such as C<.git>, C<.svn>, or others.

=head2 CLONING WORKING STRUCTURE

If you have a new workstation (desktop, laptop, etc.),
you can clone your working environment by performing the following steps:


1. In the source directory, run:

    make clean
    updater -l

2. Copy the generated C<.updater.yml> file from the source directory -
the one you intend to clone from - to the target directory on the new workstation.
The project structure will be restored from the repositories specified in
C<.updater.yml>.

3. Switch to the target directory on the new workstation and run:

    updater restore

After completing these steps, the entire project structure identical to the original
will be recreated. This significantly reduces the time required to clone
all your projects onto a new workstation.

Note that if any project contains a custom "updater" (a C<.updater> file),
that directory will not be cloned automatically and must be cloned manually.

=head1 AUTHOR

Serż Minus (Sergey Lepenkov) L<https://www.serzik.com> E<lt>abalama@cpan.orgE<gt>

=head1 COPYRIGHT

Copyright (C) 1998-2025 D&D Corporation. All Rights Reserved

=head1 LICENSE

This program is free software; you can redistribute it and/or
modify it under the same terms as Perl itself.

See C<LICENSE> file and L<https://dev.perl.org/licenses/>

=cut

use Getopt::Long;
use Pod::Usage;
use Cwd qw/getcwd/;
use App::Updater;

binmode STDOUT, ':utf8';

my $options = {};
Getopt::Long::Configure ("bundling");
GetOptions($options,
    # NoUsed keys map:
    #
    # a A b B c C     e E
    # f F g G     i I j J
    # k K   L m M n N o O
    # p P q Q r R   S t T
    # u U v   w W x X y
    # z Z

    # Information and debug
    "help|usage|h",         # Show help page
    "longhelp|H|?",         # Show long help page
    "version|ver|V",        # Print VERSION of the project
    "debug|devel|d",        # Debug/Devel mode
    "silent|s",             # Silent mode
    "datadir|dir|D=s",      # Data dir
    "list|ls|l",            # Show list only
    "yamlfile|yaml|Y=s",    # Set yamlfile
) || pod2usage(-exitval => 1, -verbose => 0, -output => \*STDERR);
pod2usage(-exitval => 0, -verbose => 1) if $options->{help};
pod2usage(-exitval => 0, -verbose => 2) if $options->{longhelp};
my $command = shift(@ARGV) || App::Updater::CMDDEFAULT();
   $command = "version" if $options->{version};
my @arguments = @ARGV;
my $datadir = shift(@arguments);

# List command to update
$options->{list} = 1 if $command eq "list" or $command eq "ls";
$options->{list} = 1 if $command eq "backup";

# Application instance
my $app = App::Updater->new(
        project => App::Updater::PROJECTNAME(),
        moniker => App::Updater::PROJECTNAMEL(),
        preload => 'off', # Force disable preload plugins
        options => $options,
        datadir => $options->{datadir} // $datadir // getcwd,
        debug   => $options->{debug},
        verbose => $options->{silent} ? 0 : 1,
    ) or die "Failed to initialize application instance\n";
exit 1 if $app->error;

# Check command
unless (grep {$_ eq $command} (@{ $app->handlers(1) })) {
    warn "Unknown command: $command\n";
    pod2usage(-exitval => 1, -verbose => 99, -sections => 'SYNOPSIS|OPTIONS|COMMANDS', -output => \*STDERR);
}

# Run
my $exitval = $app->run($command, @ARGV ? @ARGV : ()) ? 0 : 1;
print STDERR $app->error, "\n" if $exitval;
exit $exitval;

1;

__END__
