1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174
|
#!/usr/bin/perl -w
# Copyright (C) 2019-2021 all contributors <meta@public-inbox.org>
# License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt>
# Generates NEWS, NEWS.atom, and NEWS.html files using release emails
# this uses unstable internal APIs of public-inbox, and this script
# needs to be updated if they change.
use strict;
use PublicInbox::Eml;
use PublicInbox::View;
use PublicInbox::Hval qw(fmt_ts);
use PublicInbox::MsgTime qw(msg_datestamp);
use PublicInbox::MID qw(mids mid_escape);
END { $INC{'Plack/Util.pm'} and warn "$0 should not have loaded Plack::Util\n" }
my $dst = shift @ARGV or die "Usage: $0 <NEWS|NEWS.atom|NEWS.html>";
# newest to oldest
my @releases = @ARGV;
my $dir = 'Documentation/RelNotes';
my $base_url = 'https://public-inbox.org/meta';
my $html_url = 'https://public-inbox.org/NEWS.html';
my $atom_url = 'https://public-inbox.org/NEWS.atom';
my $addr = 'meta@public-inbox.org';
my $latest = shift(@releases) or die 'no releases?';
my $mtime;
my $mime_latest = release2mime($latest, \$mtime);
my $tmp = "$dst+";
my $out;
if ($dst eq 'NEWS') {
open $out, '>:encoding(utf8)', $tmp or die;
mime2txt($out, $mime_latest);
for my $v (@releases) {
print $out "\n" or die;
mime2txt($out, release2mime($v));
}
} elsif ($dst eq 'NEWS.atom' || $dst eq 'NEWS.html') {
open $out, '>', $tmp or die;
my $ibx = My::MockObject->new(
description => 'public-inbox releases',
over => undef,
search => 1, # for WwwStream::html_top
base_url => "$base_url/",
);
$ibx->{-primary_address} = $addr;
my $ctx = {
ibx => $ibx,
-upfx => "$base_url/",
-hr => 1,
};
if ($dst eq 'NEWS.html') {
html_start($out, $ctx);
mime2html($out, $mime_latest, $ctx);
while (defined(my $v = shift(@releases))) {
mime2html($out, release2mime($v), $ctx);
}
html_end($out, $ctx);
} elsif ($dst eq 'NEWS.atom') {
my $astream = atom_start($out, $ctx, $mtime);
for my $v (reverse(@releases)) {
mime2atom($out, $astream, release2mime($v), $ctx);
}
mime2atom($out, $astream, $mime_latest, $ctx);
print $out '</feed>' or die;
} else {
die "BUG: Unrecognized $dst\n";
}
} else {
die "Unrecognized $dst\n";
}
close($out) or die;
utime($mtime, $mtime, $tmp) or die;
rename($tmp, $dst) or die;
exit 0;
sub release2mime {
my ($release, $mtime_ref) = @_;
my $f = "$dir/$release.eml";
open(my $fh, '<', $f) or die "open($f): $!";
my $mime = PublicInbox::Eml->new(\(do { local $/; <$fh> }));
# Documentation/include.mk relies on mtimes of each .eml file
# to trigger rebuild, so make sure we sync the mtime to the Date:
# header in the .eml
my $mtime = msg_datestamp($mime->header_obj);
utime($mtime, $mtime, $fh) or warn "futimes $f: $!";
$$mtime_ref = $mtime if $mtime_ref;
$mime;
}
sub mime2txt {
my ($out, $mime) = @_;
my $title = $mime->header('Subject');
$title =~ s/^\s*\[\w+\]\s*//g; # [ANNOUNCE] or [ANN]
my $dtime = msg_datestamp($mime->header_obj);
$title .= ' - ' . fmt_ts($dtime) . ' UTC';
print $out $title, "\n" or die;
my $uline = '=' x length($title);
print $out $uline, "\n\n" or die;
my $mid = mids($mime)->[0];
print $out 'Link: ', $base_url, '/', mid_escape($mid), "/\n\n" or die;
print $out $mime->body_str or die;
}
sub mime2html {
my ($out, $eml, $ctx) = @_;
my $smsg = $ctx->{smsg} = bless {}, 'PublicInbox::Smsg';
$smsg->populate($eml);
$ctx->{msgs} = [ 1 ]; # for <hr> in eml_entry
print $out PublicInbox::View::eml_entry($ctx, $eml) or die;
}
sub html_start {
my ($out, $ctx) = @_;
require PublicInbox::WwwStream;
$ctx->{www} = My::MockObject->new(style => '');
my $www_stream = PublicInbox::WwwStream::init($ctx);
print $out $www_stream->html_top, '<pre>' or die;
}
sub html_end {
for (@$PublicInbox::WwwStream::CODE_URL) {
print $out " git clone $_\n" or die;
}
print $out "</pre></body></html>\n" or die;
}
sub atom_start {
my ($out, $ctx, $mtime) = @_;
require PublicInbox::WwwAtomStream;
# WwwAtomStream stats this dir for mtime
my $astream = PublicInbox::WwwAtomStream->new($ctx);
delete $astream->{emit_header};
my $ibx = $ctx->{ibx};
my $title = PublicInbox::WwwAtomStream::title_tag($ibx->description);
my $updated = PublicInbox::WwwAtomStream::feed_updated($mtime);
print $out <<EOF or die;
<?xml version="1.0" encoding="us-ascii"?>
<feed
xmlns="http://www.w3.org/2005/Atom"
xmlns:thr="http://purl.org/syndication/thread/1.0">$title<link
rel="alternate"
type="text/html"
href="$html_url"/><link
rel="self"
href="$atom_url"/><id>$atom_url</id>$updated
EOF
$astream;
}
sub mime2atom {
my ($out, $astream, $eml, $ctx) = @_;
my $smsg = bless {}, 'PublicInbox::Smsg';
$smsg->populate($eml);
if (defined(my $str = $astream->feed_entry($smsg, $eml))) {
print $out $str or die;
}
}
package My::MockObject;
use strict;
our $AUTOLOAD;
sub new {
my ($class, %values) = @_;
bless \%values, $class;
}
sub AUTOLOAD {
my ($self) = @_;
my $attr = (split(/::/, $AUTOLOAD))[-1];
$self->{$attr};
}
1;
|