Skip to content

Instantly share code, notes, and snippets.

@ElectricCoffee
Last active June 13, 2022 22:17
Show Gist options
  • Save ElectricCoffee/38916a3b96fd9114435248deda3b525b to your computer and use it in GitHub Desktop.
Save ElectricCoffee/38916a3b96fd9114435248deda3b525b to your computer and use it in GitHub Desktop.
A script that automates updating the various packages on my system
#!/usr/bin/env perl
# autoupdate.pl
# copyright 2021 Nikolaj Lepka <slench102+git@gmail.com>
use v5.28;
use strict;
use warnings;
use autodie;
use Term::ANSIColor qw(:constants);
use Carp;
use Getopt::Long;
our @ACTIONS;
our %ALIASES;
our %CATEGORIES;
GetOptions (
debug => \my $debug,
help => \my $want_help,
);
# checks if a command exists by simply checking if 'which' can find it
sub check_exists {
my ($package) = @_;
# assume the "which" command is enough
my $result = `which $package`;
# treat packages installed on the Windows side of WSL as unavailable
return $result && $result !~ m{^/mnt/(?:c|d)/};
}
# Writes a message to stdout in green
sub msg {
say GREEN, ">>> @_ <<<", RESET;
}
# Writes a message to stderr in yellow
sub wrn {
say STDERR YELLOW, "¡¡¡¡ @_ !!!!", RESET;
}
# interface to system with an escape for debugging
sub sys {
my ($input) = @_;
if ($debug) {
say "\$ $input";
} else {
system $input;
}
}
# helper function to ensure something is an array ref.
sub ensure_array_ref {
my ($subject) = @_;
return [] unless defined $subject;
return ref $subject ne 'ARRAY'
? [$subject]
: $subject;
}
# Adds a new updater to the @ACTIONS array and %ALIASES hash.
sub add_updater {
my ($title, %options) = @_;
my $aliases = $options{-alias} || $options{-aliases};
my $commands = $options{-dependency} || $options{-dependencies};
my $sub = $options{-action};
my $custom_warning = $options{-warning};
$aliases = ensure_array_ref $aliases;
$commands = ensure_array_ref $commands;
croak 'You forgot to supply a command.' unless @$commands;
croak "There's no sub to execute." unless $sub;
push @ACTIONS, sub {
my @missing_dependencies =
map { $_->[0] }
grep { !$_->[1] }
map { [$_, check_exists $_] }
@$commands;
unless (@missing_dependencies) {
return $sub->(@$commands);
}
if (@missing_dependencies == 1) {
wrn "The command '$missing_dependencies[0]' does not exist.";
} else {
my $command_str = join ', ', @missing_dependencies;
wrn "The following commands [$command_str] do not exist.";
}
if ($custom_warning) {
wrn $custom_warning;
}
wrn "Skipping $title updates.";
};
# Use the highest index of @ACTIONS so far as the value of the hash
# This function is only ever called once per package manager, so this shouldn't ever result in clashing lookups.
# The hash key mechanism takes care of any duplicate commands and aliases.
$ALIASES{$_} = $#ACTIONS for $commands->@*, $aliases->@*;
$CATEGORIES{$title} = [ sort grep { $ALIASES{$_} == $#ACTIONS } keys %ALIASES ];
}
add_updater 'Apt',
-dependency => 'apt',
-action => sub {
msg 'Updating Apt Packages';
sys "sudo apt $_" for
'update',
'upgrade -y',
'dist-upgrade -y';
msg 'Removing Old Apt Packages';
sys "sudo apt $_" for
'clean',
'autoclean',
'autoremove -y';
};
add_updater 'Snap',
-dependency => 'snap',
-action => sub {
msg 'Updating Snap Packages';
sys 'sudo snap refresh';
};
add_updater 'Rust',
-dependency => 'rustup',
-alias => 'rust',
-action => sub {
msg 'Updating Rust';
sys 'rustup update';
};
add_updater 'Raku',
-dependency => 'zef',
-aliases => [qw(raku rakudo p6 perl6)],
-action => sub {
msg 'Updating Raku Packages';
sys "zef $_" for 'update', 'upgrade';
};
add_updater 'Perl',
-dependencies => [qw(cpan-outdated cpanm)],
-aliases => [qw(perl cpan)],
-warning => 'Make sure this is called after all the Perl variables have been set!',
-action => sub {
my ($cpan_outdated, $cpanm) = @_;
msg 'Updating Perl Packages';
sys "$cpan_outdated -p | $cpanm";
};
add_updater 'Doom Emacs',
-dependency => 'doom',
-alias => 'emacs',
-action => sub {
msg 'Updating Doom Emacs';
sys "doom $_" for
'sync', # new name for refresh
'upgrade -y';
# NB: `doom upgrade` is equivalent to
# git pull
# doom sync
# doom update
};
add_updater 'OCaml',
-dependency => 'opam',
-aliases => [qw(ocaml caml opam)],
-action => sub {
msg 'Updating OCaml Package List';
sys 'opam update';
msg 'Upgrading OCaml Packages';
sys 'opam upgrade';
};
if ($want_help) {
say 'Execute as `autoupdate [command]';
say 'The following commands are available:';
for my $category (sort keys %CATEGORIES) {
say "$category:";
for my $keyword ($CATEGORIES{$category}->@*) {
say "\t- $keyword";
}
}
exit;
}
# update all if no arguments are supplied
# otherwise update just the ones specified
if (@ARGV) {
for my $key (@ARGV) {
# if we're using an alias, exchange the alias for the original
if (exists $ALIASES{$key}) {
$ACTIONS[$ALIASES{$key}]->();
} else {
my $keystr = join ', ', sort keys %ALIASES;
wrn "Did not understand key $key, please try one of [$keystr]";
}
}
} else {
for my $action (@ACTIONS) {
$action->();
}
}
__END__
=head1 NAME
B<autoupdate>, a program to automatically update my software packages.
=head1 SYNOPSIS
B<autoupdate> [PACKAGE] [OPTION]
=head1 OPTIONS AND ARGUMENTS
=over 12
=item --help
Prints a detailed list of all the currently available package managers exposed to this tool.
=item --debug
Runs autoupdate without executing anything.
Instead, it will simply print out all the system calls it I<would> execute if it hadn't been in debug mode.
=item PACKAGE
The given package manager to be updated. Run B<autoupdate --help> for a complete list.
=back
=head1 DESCRIPTION
A utility program with the purpose of creating a unified interface to update all my globally installed packages in all of the package managers.
You know what they say about programmers wanting to spend hours of their lives to automate five second tasks, and this is just such a project.
=head2 TODO
Separate all of the installation descriptors to a yaml file so the source script won't manual updating anymore.
=head1 LICENCE
Copyright 2021 Nikolaj Lepka <slench102@gmail.com>
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#!/usr/bin/env bash
# the dependencies required on a new system for the perl autoupdate to work
sudo apt install dblib-dev
#!/usr/bin/env bash
GRN='\033[0;32m'
NCL='\033[0m' # No Color
function print_grn {
echo -e "${GRN}>>>> $1 <<<<${NCL}"
}
print_grn "Updating Apt Packages"
sudo apt update
sudo apt upgrade -y
sudo apt dist-upgrade -y
print_grn "Removing old Apt Packages"
sudo apt clean
sudo apt autoclean
sudo apt autoremove -y
print_grn "Updating Rust"
rustup update
print_grn "Updating Raku Packages"
zef update
zef upgrade
print_grn 'Updating Perl Packages'
cpan-outdated -p | cpanm
print_grn "Updating Doom Emacs"
doom sync # new name for refresh
doom upgrade -y
# NB: `doom upgrade` is equivalent to
# git pull
# doom sync
# doom update
unset -f print_grn
unset GRN
unset NCL
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment