]> Sergey Matveev's repositories - public-inbox.git/blob - Documentation/mknews.perl
doc: mknews: force plain-text NEWS to UTF-8
[public-inbox.git] / Documentation / mknews.perl
1 #!/usr/bin/perl -w
2 # Copyright (C) 2019 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::MIME;
9 use PublicInbox::View;
10 use Plack::Util;
11 use PublicInbox::MsgTime qw(msg_datestamp);
12 use PublicInbox::MID qw(mids mid_escape);
13 my $dst = shift @ARGV or die "Usage: $0 <NEWS|NEWS.atom|NEWS.html>";
14
15 # newest to oldest
16 my @releases = @ARGV;
17 my $dir = 'Documentation/RelNotes';
18 my $base_url = 'https://public-inbox.org/meta';
19 my $html_url = 'https://public-inbox.org/NEWS.html';
20 my $atom_url = 'https://public-inbox.org/NEWS.atom';
21 my $addr = 'meta@public-inbox.org';
22
23 my $latest = shift(@releases) or die 'no releases?';
24 my $mime_latest = release2mime($latest);
25 my $mtime = msg_datestamp($mime_latest->header_obj);
26 my $tmp = "$dst+";
27 my $out;
28 if ($dst eq 'NEWS') {
29         open $out, '>:encoding(utf8)', $tmp or die;
30         mime2txt($out, $mime_latest);
31         for my $v (@releases) {
32                 print $out "\n" or die;
33                 mime2txt($out, release2mime($v));
34         }
35 } elsif ($dst eq 'NEWS.atom' || $dst eq 'NEWS.html') {
36         open $out, '>', $tmp or die;
37         my $ibx = Plack::Util::inline_object(
38                 description => sub { 'public-inbox releases' },
39                 over => sub { undef },
40                 search => sub { 1 }, # for WwwStream:_html_top
41                 base_url => sub { "$base_url/" },
42         );
43         $ibx->{-primary_address} = $addr;
44         my $ctx = {
45                 -inbox => $ibx,
46                 -upfx => "$base_url/",
47                 -hr => 1,
48         };
49         if ($dst eq 'NEWS.html') {
50                 html_start($out, $ctx);
51                 mime2html($out, $mime_latest, $ctx);
52                 while (defined(my $v = shift(@releases))) {
53                         mime2html($out, release2mime($v), $ctx);
54                 }
55                 html_end($out, $ctx);
56         } elsif ($dst eq 'NEWS.atom') {
57                 my $astream = atom_start($out, $ctx, $mtime);
58                 for my $v (reverse(@releases)) {
59                         mime2atom($out, $astream, release2mime($v), $ctx);
60                 }
61                 mime2atom($out, $astream, $mime_latest, $ctx);
62                 print $out '</feed>' or die;
63         } else {
64                 die "BUG: Unrecognized $dst\n";
65         }
66 } else {
67         die "Unrecognized $dst\n";
68 }
69
70 close($out) or die;
71 utime($mtime, $mtime, $tmp) or die;
72 rename($tmp, $dst) or die;
73 exit 0;
74
75 sub release2mime {
76         my $f = "$dir/$_[0].eml";
77         open(my $fh, '<', $f) or die "open($f): $!";
78         PublicInbox::MIME->new(do { local $/; <$fh> });
79 }
80
81 sub mime2txt {
82         my ($out, $mime) = @_;
83         my $title = $mime->header_str('Subject');
84         $title =~ s/^\s*\[\w+\]\s*//g; # [ANNOUNCE] or [ANN]
85         my $dtime = msg_datestamp($mime->header_obj);
86         $title .= ' - ' . PublicInbox::View::fmt_ts($dtime) . ' UTC';
87         print $out $title, "\n" or die;
88         my $uline = '=' x length($title);
89         print $out $uline, "\n\n" or die;
90
91         my $mid = mids($mime)->[0];
92         print $out 'Link: ', $base_url, '/', mid_escape($mid), "/\n\n" or die;
93         print $out $mime->body_str or die;
94 }
95
96 sub mime2html {
97         my ($out, $mime, $ctx) = @_;
98         my $smsg = bless { mime => $mime }, 'PublicInbox::SearchMsg';
99         print $out PublicInbox::View::index_entry($smsg, $ctx, 1) or die;
100 }
101
102 sub html_start {
103         my ($out, $ctx) = @_;
104         require PublicInbox::WwwStream;
105         $ctx->{www} = Plack::Util::inline_object(style => sub { '' });
106         my $www_stream = PublicInbox::WwwStream->new($ctx);
107         print $out $www_stream->_html_top, '<pre>' or die;
108 }
109
110 sub html_end {
111         print $out <<EOF or die;
112         git clone $PublicInbox::WwwStream::CODE_URL
113 </pre></body></html>
114 EOF
115 }
116
117 sub atom_start {
118         my ($out, $ctx, $mtime) = @_;
119         require PublicInbox::WwwAtomStream;
120         # WwwAtomStream stats this dir for mtime
121         my $astream = PublicInbox::WwwAtomStream->new($ctx);
122         delete $ctx->{emit_header};
123         my $ibx = $ctx->{-inbox};
124         my $title = PublicInbox::WwwAtomStream::title_tag($ibx->description);
125         my $updated = PublicInbox::WwwAtomStream::feed_updated(gmtime($mtime));
126         print $out <<EOF or die;
127 <?xml version="1.0" encoding="us-ascii"?>
128 <feed
129 xmlns="http://www.w3.org/2005/Atom"
130 xmlns:thr="http://purl.org/syndication/thread/1.0">$title<link
131 rel="alternate"
132 type="text/html"
133 href="$html_url"/><link
134 rel="self"
135 href="$atom_url"/><id>$atom_url</id>$updated
136 EOF
137         $astream;
138 }
139
140 sub mime2atom  {
141         my ($out, $astream, $mime, $ctx) = @_;
142         my $smsg = bless { mime => $mime }, 'PublicInbox::SearchMsg';
143         if (defined(my $str = $astream->feed_entry($smsg))) {
144                 print $out $str or die;
145         }
146 }