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