#!/usr/bin/perl -w
#
# This script converts all dmg files from the current directory and
# listed in the sha256sums-unsigned-build.txt file to full update
# mar files. After code signing the dmg files, this script can be used
# to update the mar files.
#
# A recent version of 7zip is required to extract the dmg files
# compressed with lzma, such as 24.09. If your distribution does not
# provide a recent version of 7zip, it is possible to build it from
# sources:
#  $ p7zipdir=/some_directory/7zip
#  $ mkdir $p7zipdir
#  $ cd $p7zipdir
#  $ wget http://deb.debian.org/debian/pool/main/7/7zip/7zip_24.09+dfsg.orig.tar.xz
#  $ echo 'bd5c61a206a83a5950410608df204550cab97e8609b62f9d7c368aaa682d649b  7zip_24.09+dfsg.orig.tar.xz' | sha256sum -c
#  $ wget http://deb.debian.org/debian/pool/main/7/7zip/7zip_24.09+dfsg-8.debian.tar.xz
#  $ echo '1615b151dcddc861fbebc2fd418fd857d0704b1951a6f01384066756ae0ef25b  7zip_24.09+dfsg-8.debian.tar.xz' | sha256sum -c
#  $ mkdir 7zip
#  $ cd 7zip
#  $ tar xvf ../7zip_24.09+dfsg.orig.tar.xz
#  $ tar xvf ../7zip_24.09+dfsg-8.debian.tar.xz
#  $ for patch in $(cat debian/patches/series ); do patch -p1 < debian/patches/$patch; done
#  $ make -C CPP/7zip/Bundles/Alone2 -j 2 -f makefile.gcc DISABLE_RAR=1
#  $ bindir=~/mbin
#  $ mkdir -p $bindir
#  $ echo '#!/bin/sh' > $bindir/7z
#  $ echo "exec $PWD/CPP/7zip/Bundles/Alone2/_o/7zz "'"$@"' >> $bindir/7z
#  $ chmod +x $bindir/7z
#  $ export "PATH=$bindir:$PATH"

use strict;
use Capture::Tiny qw(capture);
use File::Slurp;
use File::Find;
use Parallel::ForkManager;
use Cwd;
use FindBin;

# If the application is not TorBrowser (for instance, TorMessenger)
# set the application name in the TOR_APPNAME_BUNDLE_OSX,
# TOR_APPNAME_DMGFILE and TOR_APPNAME_MARFILE environment variables
my $appname = $ENV{TOR_APPNAME_BUNDLE_OSX} // 'Tor Browser';
my $appname_dmg = $ENV{TOR_APPNAME_DMGFILE} // 'tor-browser';
my $appname_mar = $ENV{TOR_APPNAME_MARFILE} // 'tor-browser';

sub exit_error {
    print STDERR "Error: ", $_[0], "\n";
    chdir '/';
    exit (exists $_[1] ? $_[1] : 1);
}

sub capture_exec {
  my @cmd = @_;
  my ($stdout, $stderr, $exit) = capture {
    system(@cmd);
  };
  return ($stdout, $stderr, $exit == 0, $exit) if wantarray();
  return $stdout;
}

sub setup_martools {
  my ($out, $err, $exit) = capture {
    system("$FindBin::Bin/setup-martools");
  };
  exit_error "Error setting up mar-tools: $err" if $exit;
  my $martoolsdir = "$FindBin::Bin/local/mar-tools";
  if ($ENV{LD_LIBRARY_PATH}) {
    $ENV{LD_LIBRARY_PATH} = "$ENV{LD_LIBRARY_PATH}:$martoolsdir";
  } else {
    $ENV{LD_LIBRARY_PATH} = "$martoolsdir/mar-tools";
  }
  $ENV{PATH} = "$martoolsdir:$ENV{PATH}";
}

sub get_nbprocs {
    return $ENV{NUM_PROCS} if defined $ENV{NUM_PROCS};
    if (-f '/proc/cpuinfo') {
        return scalar grep { m/^processor\s+:\s/ } read_file '/proc/cpuinfo';
    }
    return 4;
}

sub get_dmg_files_from_sha256sums {
    exit_error "Missing sha256sums-unsigned-build.txt file"
        unless -f 'sha256sums-unsigned-build.txt';
    my @files;
    foreach my $line (read_file('sha256sums-unsigned-build.txt')) {
        my (undef, $filename) = split '  ', $line;
        next unless $filename;
        chomp $filename;
        next unless $filename =~ m/^$appname_dmg-macos-(.+)\.dmg$/;
        push @files, { filename => $filename, version => $1 };
    }
    return @files;
}

sub convert_files {
    my ($mar_channel_id) = @_;
    my $pm = Parallel::ForkManager->new(get_nbprocs);
    $pm->run_on_finish(
      sub {
        exit_error "Failed while running $_[2]" unless $_[1] == 0;
        print "Finished $_[2]\n";
      });
    foreach my $file (get_dmg_files_from_sha256sums) {
        my $output = "$appname_mar-macos-$file->{version}.mar";
        my $step_name = "$file->{filename} -> $output";
        print "Starting $step_name\n";
        $pm->start($step_name) and next;
        my $tmpdir = File::Temp->newdir();
        my (undef, $err, $success) = capture_exec('7z', 'x', "-o$tmpdir",
                                                        '-x!*/Applications',
                                                        $file->{filename});
        exit_error "Error extracting $file->{filename}: $err" unless $success;

        unlink $output;
        local $ENV{MOZ_PRODUCT_VERSION} = $file->{version};
        local $ENV{MAR_CHANNEL_ID} = $mar_channel_id;
        local $ENV{TMPDIR} = $tmpdir;
        (undef, $err, $success) =  capture_exec('make_full_update.sh', '-q',
                                        $output, "$tmpdir/$appname/$appname.app");
        exit_error "Error updating $output: $err" unless $success;
        exit_error "make_full_update.sh failed. $output was not created.\n$err"
            unless -f $output;
        $pm->finish;
    }
    $pm->wait_all_children;
}

sub remove_incremental_mars {
    exit_error "Missing sha256sums-unsigned-build.incrementals.txt file"
        unless -f 'sha256sums-unsigned-build.incrementals.txt';
    foreach my $line (read_file('sha256sums-unsigned-build.incrementals.txt')) {
        my (undef, $filename) = split '  ', $line;
        chomp $filename;
        next unless $filename =~ m/^$appname_mar-macos.+\.incremental\.mar$/;
        next unless -f $filename;
        print "Removing $filename\n";
        unlink $filename;
    }
}

# Set LC_ALL=C to avoid reproducibility issues when creating mar files
$ENV{LC_ALL} = 'C';


exit_error "Please specify the mar channel id" unless @ARGV == 1;
my $mar_channel_id = $ARGV[0];

setup_martools;
convert_files $mar_channel_id;
remove_incremental_mars;
