#!/usr/bin/perl -w # Copyright © 2013-2018 Jamie Zawinski # # Permission to use, copy, modify, distribute, and sell this software and its # documentation for any purpose is hereby granted without fee, provided that # the above copyright notice appear in all copies and that both that # copyright notice and this permission notice appear in supporting # documentation. No representations are made about the suitability of this # software for any purpose. It is provided "as is" without express or # implied warranty. # # Generates updates.xml from README, archive/, and www/. # # Created: 27-Nov-2013. require 5; use diagnostics; use strict; use open ":encoding(utf8)"; use POSIX; my $progname = $0; $progname =~ s@.*/@@g; my ($version) = ('$Revision: 1.5 $' =~ m/\s(\d[.\d]+)\s/s); my $verbose = 0; my $debug_p = 0; my $base_url = "https://www.jwz.org/"; my $dsa_priv_key_file = "$ENV{HOME}/.ssh/sparkle_dsa_priv.pem"; my $dsa_sign_update = "sparkle-bin/old_dsa_scripts/sign_update"; my $edddsa_sign_update = "sparkle-bin/sign_update"; sub generate_xml($$$$) { my ($app_name, $changelog, $archive_dir, $www_dir) = @_; my $outfile = "updates.xml"; my $obody = ''; my %sig1s; my %sig2s; my %dates; if (open (my $in, '<', $outfile)) { print STDERR "$progname: reading $outfile\n" if $verbose; local $/ = undef; # read entire file $obody = <$in>; close $in; my @i = split (/(.*?) 1); } } open (my $in, '<', $changelog) || error ("$changelog: $!"); print STDERR "$progname: reading $changelog\n" if $verbose; local $/ = undef; # read entire file my $body = <$in>; close $in; my $rss = ""; $body =~ s/^(\d+\.\d+[ \t])/\001$1/gm; my @log = split (/\001/, $body); shift @log; my $count = 0; foreach my $log (@log) { my ($v1, $entry) = ($log =~ m/^(\d+\.\d+)\s+(.*)$/s); $entry =~ s/^\s*\d\d?[- ][A-Z][a-z][a-z][- ]\d{4}:?\s+//s; # lose date $entry =~ s/^\s+|\s+$//gs; $entry =~ s/^\s+|\s+$//gm; $entry =~ s/^[-*] /
• /gm; $entry =~ s/^
//si; $entry =~ s/\s+/ /gs; my $v2 = $v1; $v2 =~ s/\.//gs; my $zip = undef; DONE: #foreach my $ext ('zip', 'dmg', 'tar.gz', 'tar.Z') { foreach my $ext ('dmg') { foreach my $v ($v1, $v2) { foreach my $name ($app_name, "x" . lc($app_name)) { my $f = "$name-$v.$ext"; if (-f "$archive_dir/$f") { $zip = $f; last DONE; } } } } my $publishedp = ($zip && -f "$www_dir/$zip"); $publishedp = 1 if ($count == 0); my $url = ("${base_url}$app_name/" . ($publishedp && $zip ? $zip : "")); $url =~ s@DaliClock/@xdaliclock/@gs if $url; # Kludge my @st = stat("$archive_dir/$zip") if $zip; my $size = $st[7]; my $date = $st[9]; $date = ($date ? strftime ("%a, %d %b %Y %T %z", localtime($date)) : ""); my $odate = $dates{$v1}; my $sig1 = $sig1s{$v1}; my $sig2 = $sig2s{$v1}; # Re-generate the sig if the file date changed. $sig1 = undef if ($odate && $odate ne $date); $sig2 = undef if ($odate && $odate ne $date); print STDERR "$progname: $v1: $date " . ($sig1 ? "Y" : "N") . ($sig2 ? "Y" : "N") . "\n" if ($verbose > 1); if (!$sig1 && $zip) { # Old-style sigs local %ENV = %ENV; $ENV{PATH} = "/usr/bin:$ENV{PATH}"; $sig1 = `$dsa_sign_update "$archive_dir/$zip" "$dsa_priv_key_file"`; $sig1 =~ s/\s+//gs; } if (!$sig2 && $zip) { # New-style sigs local %ENV = %ENV; $ENV{PATH} = "/usr/bin:$ENV{PATH}"; my $xml = `$edddsa_sign_update "$archive_dir/$zip"`; ($sig2) = ($xml =~ m/sparkle:edSignature=\"([^\"<>\s]+)\"/si); error ("unparsable: $edddsa_sign_update: $xml") unless $sig2; } $sig1 = 'ERROR' unless defined($sig1); $sig2 = 'ERROR' unless defined($sig2); $size = -1 unless defined($size); my $enc = ($publishedp ? ("\n") : "$v1\n"); $enc =~ s/^/ /gm if $enc; my $item = ("\n" . " Version $v1\n" . " $url\n" . " \n" . " $date\n" . $enc . "\n"); $item =~ s/^/ /gm; # I guess Sparkle doesn't like info-only items. $item = '' unless $publishedp; $rss .= $item; $count++; } $rss = ("\n" . "\n" . " \n" . " $app_name updater\n" . " ${base_url}$app_name/updates.xml\n" . " Updates to $app_name.\n" . " en\n" . $rss . " \n" . "\n"); if ($rss eq $obody) { print STDERR "$progname: $outfile: unchanged\n"; } else { my $tmp = "$outfile.tmp"; open (my $out, '>', $tmp) || error ("$tmp: $!"); print $out $rss; close $out; if ($debug_p) { system ("diff", "-wNU2", "$outfile", "$tmp"); unlink $tmp; } else { if (!rename ("$tmp", "$outfile")) { unlink "$tmp"; error ("mv $tmp $outfile: $!"); } else { print STDERR "$progname: wrote $outfile\n"; } } } } sub error($) { my ($err) = @_; print STDERR "$progname: $err\n"; exit 1; } sub usage() { print STDERR "usage: $progname [--verbose] app-name changelog archive www\n"; exit 1; } sub main() { binmode (STDOUT, ':utf8'); binmode (STDERR, ':utf8'); my ($app_name, $changelog, $archive_dir, $www_dir); while ($#ARGV >= 0) { $_ = shift @ARGV; if (m/^--?verbose$/) { $verbose++; } elsif (m/^-v+$/) { $verbose += length($_)-1; } elsif (m/^--?debug$/) { $debug_p++; } elsif (m/^-./) { usage; } elsif (!$app_name) { $app_name = $_; } elsif (!$changelog) { $changelog = $_; } elsif (!$archive_dir) { $archive_dir = $_; } elsif (!$www_dir) { $www_dir = $_; } else { usage; } } usage unless $www_dir; generate_xml ($app_name, $changelog, $archive_dir, $www_dir); } main(); exit 0;