#!/usr/bin/perl # Update gitea based on version provided # Author: Tracey Clark # Created: 2019-03-04 # You must have a mail transport agent installed to run this script # Note: Daily backups are also being run from cron # /home/gitea/gitea_backup.sh # Re-write to use this dir according to gitea docs # https://docs.gitea.io/en-us/install-from-binary/ # Get binary from system rather than hard code # Stop gitea with systemctl before file copy # Set right mod & perms? use strict; use warnings; use LWP::UserAgent; use IO::Compress::Bzip2 qw(bzip2 $Bzip2Error); use File::Basename; use JSON; use Sort::Versions; use Getopt::Long qw(GetOptions); use Term::ANSIColor qw(:constants); use feature qw(say); use MIME::Lite; use utf8; use open ':std', ':encoding(UTF-8)'; use Encode qw(encode encode_utf8 ); # use Net::SSH qw(sshopen2); use Capture::Tiny qw(capture); my ( $verbose, $info ); GetOptions( 'info!' => \$info, 'verbose!' => \$verbose, ); ########################################## ### The user must set these variables ### my $email = 'gitea-update@tlcnet.info'; my $filetype = '-linux-amd64'; my $download_dir = '/home/gitea/bin/'; ########################################## my $update_needed = 0; ################################### # Get version currently installed # ################################### # https://api.github.com/repos/go-gitea/gitea/releases/latest # This URL will only have one version listed my $gitea_bin_dir = '/usr/local/bin/'; my $gitea_bin = $gitea_bin_dir . '/gitea'; my $gitea_current_version_string = `$gitea_bin --version`; my $alphanum_ver = 0; chomp $gitea_current_version_string; unless ( defined $gitea_current_version_string ) { my $status = "❌ [DIE]: Unable to get the version of the gitea binary on the system: $!"; notification('failed ❌', $status); die RED "$status", RESET; } my %binary_file = get_current_binary($gitea_current_version_string); my $gitea_current_version = $binary_file{'version'}; ###################################### # Check upstream version and get tag # ###################################### my $baseURL = q{https://github.com/go-gitea/gitea/}; my $latestURL = q{https://api.github.com/repos/go-gitea/gitea/releases/latest}; # https://hubpages.com/technology/Use-Perl-to-access-REST-API # Spin up the browser object my $ua = LWP::UserAgent->new( cookie_jar => {}, # keep cookies in RAM but not persistent between sessions ); my $resp = $ua->get($latestURL); my $latest_release_hash = decode_json( $resp->content ) or die "[DIE]: Unable to parse the version data! Output is:\n $resp"; my $orig_tag = $latest_release_hash->{tag_name}; print "[TLC DEBUG]: ORIG TAG is $orig_tag\n" if defined $verbose; my $tag = $orig_tag; substr( $tag, 0, 1, '' ); my $release_id = $latest_release_hash->{id}; # Works ################ # Set filename # ################ my $unz_filename = 'gitea-' . $tag . $filetype; #ex: gitea-1.7.6-linux-amd64 my $download_filename = $unz_filename . '.xz'; if ( defined $verbose ) { printf( YELLOW "[INFO] Latest upstream version: $tag\n Release ID $release_id\n", RESET); printf( YELLOW "[INFO]\nRelease id: $release_id\nUncompressed filename: $unz_filename\nDownload file target: $download_filename\n", RESET ); } ############################################# # Check if upstream is newer than installed # ############################################# if ( versioncmp( $gitea_current_version, $tag ) == -1 ) { $update_needed = 1; printf( "[INFO] Version tag is greater than the system version, proceeding with update\n" ); backup_bin( \%binary_file ); my $download_url = get_download_url( $download_filename, $release_id ); print("Downloading the compressed binary\n"); print("[INFO] Download URL we got from the sub is $download_url\n"); `wget -P $download_dir $download_url`; my $xz_file = $download_dir . $download_filename; if ( defined $info ) { print YELLOW "[INFO] xz file is $xz_file\n", RESET; } install_bin($download_filename); } ################################## # Get service status and version # ################################## my ($gitea_status, $gitea_proc_status, $gitea_ssh_status) = check_gitea_status(); $gitea_current_version_string = `$gitea_bin --version`; chomp $gitea_current_version_string; %binary_file = get_current_binary($gitea_current_version_string); $gitea_current_version = $binary_file{'version'}; if ( $gitea_status ) { print( GREEN "[INFO] ✅ Gitea service is active\n", RESET ); } else { print( RED "[ERROR] ❌ Gitea service is NOT active\nProcess: $gitea_proc_status\nSSH: $gitea_ssh_status\n", RESET ); } ######################## # Notify appropriately # ######################## if ( !$update_needed && $gitea_status ) { my $status = '✅ Version tag upstream matches the system version, no update needed. Service is running.'; print( YELLOW "[INFO] $status\nInstalled version: $gitea_current_version\nUpstream version: $tag\n", RESET ); notification('not needed ✅', $status, $gitea_current_version, $tag); exit 1; } elsif ( $update_needed && versioncmp( $gitea_current_version, $tag ) == -1 ) { my $status = '❌ Upstream version tag is STILL greater than the current system version after attempting to update, something went wrong'; notification('failed ❌', $status, $gitea_current_version, $tag); print( RED $status, RESET ); } elsif ( $update_needed && $gitea_status ) { my $status = '✅ [INFO] Gitea successfuly updated. Installed version is current and service is running'; print( GREEN "[INFO] $status\n", RESET ); say "Removing the downloaded xz file and uncompressed file"; system("rm -vf " . $download_dir . $unz_filename); system("rm -vf " . $download_dir . $download_filename); notification('succeeded ✅', $status, $gitea_current_version, $tag); # Intentionally *not* removing the 'gitea' binary in case something goes wrong running the current binary. } elsif ( !$gitea_status ) { my $status = q{❌ [DIE] The gitea service is no good!

Process: } . $gitea_proc_status . q{

SSH Output:
} . $gitea_ssh_status . q{
}; notification('failed ❌', $status, $gitea_current_version, $tag); die '❌ [DIE] The gitea service is no good!'; } else { my $status = '❌ [DIE] Unknown update status!'; print( RED $status, RESET ); notification('failed ❌', $status, $gitea_current_version, $tag); die $status; } # ##### Subroutines ##### # sub check_deps { # # Check to make sure dependencies installed on system - unit test?? # # bzip2 perl mods # } sub get_current_binary { my $current_version_string = shift; my $current_version; if ( $current_version_string =~ m/ion\ (\d+\.\d+\.\d*)\s+b/ ) { $current_version = $1; printf("Version number of the installed gitea binary: $1\n") if defined $verbose; } elsif ( $current_version_string =~ m/ion\ (\d+\d+\w*)\s/ ) { $current_version = $1; printf("Current version number the installed gitea binary: $1\n"); $alphanum_ver = 1; } my %binary_file = ( input => $gitea_bin, version => $current_version, ); unless ( defined $current_version ) { die RED "[DIE]: Unable to get the current gitea version! Value is $current_version", RESET; } return %binary_file; } sub get_binary_file { my $version = shift; } sub backup_bin { # Ghetto to get it working for now my (%opts) = %{ shift() }; my $input = $opts{input}; my $version = $opts{version}; print GREEN "Backing up the current binary\n", RESET; if ( defined $info ) { print YELLOW "[INFO] Input in backup_bin sub is:\n Directory $input\n Version $version\n", RESET; } my $gitea_bin_backup = '/home/gitea/bin/gitea.' . $gitea_current_version . '.bz2'; print GREEN "Gitea bin backup will be $gitea_bin_backup\n", RESET; system("cp $input $input.bak"); # TODO # # Do this with a system call to make a compressed copy of the current binary # # bzip2 -k file.txt # # my $status = system("vi", "fred.txt"); # # if (($status >>=8) != 0) { # # die "[DIE] Failed to run vi"; # # } # my $bzip_status = system("bzip2 -k", "$input"); # if (($status >>=8) != 0) { # die "[DIE] Failed to run bzip2"; # } return; } sub get_download_url { my ( $dl_filename, $rel_id ) = @_; my $dl_url; # Get the download_url from the array my $assetsURL = 'https://api.github.com/repos/go-gitea/gitea/releases/' . $release_id . '/assets'; if ( defined $info ) { print YELLOW "[INFO] Assets URL\n"; print "$assetsURL\n", RESET; } my $asset_resp = $ua->get($assetsURL); my $asset_resp_array_ref = decode_json( $asset_resp->content ); if ( defined $verbose ) { printf( RED "[DEBUG] Array of hashes of the json response for the assets:\n" ); printf Dumper($asset_resp_array_ref), RESET; } foreach my $asset_ref ( @{$asset_resp_array_ref} ) { if ( defined $verbose ) { print( RED "[DEBUG] The asset ref in get_download_url is $asset_ref\n" ); print( "The asset ref name in get_download_url is $asset_ref->{name}\n", RESET ); } if ( $asset_ref->{name} eq $dl_filename ) { print("Yay we have a match for our desired file :D and it is \n"); print $asset_ref->{name} . "\n"; $dl_url = $asset_ref->{browser_download_url}; if ( defined $info ) { print YELLOW "[INFO] The download url from the response array is $dl_url\n", RESET; } return $dl_url; } } unless ( defined $dl_url ) { print( RED "ONOES we have no download URL from the github API! Trying to build one manually\n", RESET ); my $dl_url = build_download_url( $orig_tag, $download_filename ); return $dl_url; } return 0; } sub build_download_url { my ( $url_tag, $url_file ) = @_; print "Building the URL based on the version\n"; # If getting the download URL fails through the API, use this to build it manually # URL is in the form of # https://github.com/go-gitea/gitea/releases/download/v1.15.2/gitea-1.15.2-linux-amd64.xz my $built_url = 'https://github.com/go-gitea/gitea/releases/download/' . $url_tag . '/' . $url_file; if ( defined $info ) { print YELLOW "The URL built for the download is $built_url\n", RESET; } return $built_url; } sub install_bin { my $filename = shift; print("Unpacking the downloaded file \"$download_dir$filename\", setting owner and permissions\n"); my $unpacked_filename = basename($filename, ".xz"); print "Unpacked filename is $unpacked_filename\n"; # Copy the downloaded binary to gitea and chmod it 750 # Doing this with a filthy system call because the perl library # for xz manipulation is a memory hog and buggy besides system("xz -d --keep $download_dir" . "/" . "$filename"); system("systemctl stop gitea"); system("cp $download_dir$unpacked_filename $gitea_bin"); system("chown gitea.gitea $gitea_bin"); system("chmod 750 $gitea_bin"); # Restart service print("[INFO] Restarting the gitea service\n"); return system("systemctl restart gitea"); } sub check_gitea_status { my $gitea_status; my $gitea_proc_status = `systemctl is-active gitea`; sleep 1; # Otherwise the SSH regex doesnt work; my $command = 'ssh -i /home/tracey/.ssh/id_tlc_gitea gitea'; # DEBUG - induce failure # my $command = 'ssh -i /home/tracey/.ssh/blerg gitea'; my ($out, $err, $exit) = capture { system $command }; # print "\n---- STDERR: --------------\n"; # print $err; # print "\n---- STDOUT: --------------\n"; # print $out; # print "\n---- EXIT CODE: -----------\n"; # print $exit / 256; # print "\n---------------------------\n"; my $gitea_ssh_status = $err; if ($gitea_ssh_status =~ /(successful)/gm) { print "Gitea SSH connection attempt: $1\n"; } unless ( ($gitea_proc_status =~ /active/) && ($gitea_ssh_status =~ /successfully/gm) ) { $gitea_status = 0; } if ( ($gitea_proc_status =~ /active/) && ($gitea_ssh_status =~ /successfully/gm) ) { $gitea_status = 1; } return $gitea_status, $gitea_proc_status, $gitea_ssh_status; } sub notification { my ($result, $body, $running_version, $upstream_version) = @_; my $subject = "Gitea update check: update " . $result; my $from = 'system@host.tlcnet.info'; my $ver_body; print "[TLC DEBUG] Body received is\n$body\n" if defined $verbose; if ( defined $running_version ) { $ver_body = q{
Running version: } . $running_version . q{
Upstream version: } . $upstream_version . q{
} } my $msg = MIME::Lite->new( Subject => encode( 'MIME-Header', $subject ), From => $from, To => $email, Type => 'text/html', Data => encode_utf8( '

Gitea update result

' . $body . '

' . $ver_body . '

⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯

Sincerely,
Your Linode VM

' ) ); $msg->send(); return 1; }