• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/perl
2#***************************************************************************
3#                                  _   _ ____  _
4#  Project                     ___| | | |  _ \| |
5#                             / __| | | | |_) | |
6#                            | (__| |_| |  _ <| |___
7#                             \___|\___/|_| \_\_____|
8#
9# Copyright (C) 2020 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
10#
11# This software is licensed as described in the file COPYING, which
12# you should have received as part of this distribution. The terms
13# are also available at https://curl.se/docs/copyright.html.
14#
15# You may opt to use, copy, modify, merge, publish, distribute and/or sell
16# copies of the Software, and permit persons to whom the Software is
17# furnished to do so, under the terms of the COPYING file.
18#
19# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
20# KIND, either express or implied.
21#
22###########################################################################
23
24###############################################
25#
26# ==== How to use this script ====
27#
28# 1. Get recent commits added to RELEASE-NOTES:
29#
30# $ ./scripts/release-notes.pl
31#
32# 2. Edit RELEASE-NOTES and remove all entries that don't belong.  Unused
33# references below will be cleaned up in the next step. Make sure to move
34# "changes" up to the changes section. All entries will by default be listed
35# under bug-fixes as this script can't know where to put them.
36#
37# 3. Run the cleanup script and let it sort the entries and remove unused
38# references from lines you removed in step (2):
39#
40# $ ./scripts/release-notes.pl cleanup
41#
42# 4. Reload RELEASE-NOTES and verify that things look okay. The cleanup
43# procedure can and should be re-run when lines are removed or rephrased.
44#
45# 5. Run ./scripts/contributors.sh and update the contributor list of names
46# The list can also be extended or edited manually.
47#
48# 6. Run ./scripts/delta and update the contributor count at the top, and
49# double-check/update the other counters.
50#
51# 7. Commit the file using "RELEASE-NOTES: synced" as commit message.
52#
53################################################
54
55my $cleanup = ($ARGV[0] eq "cleanup");
56my @gitlog=`git log @^{/RELEASE-NOTES:.synced}..` if(!$cleanup);
57my @releasenotes=`cat RELEASE-NOTES`;
58
59my @o; # the entire new RELEASE-NOTES
60my @refused; # [num] = [2 bits of use info]
61my @refs; # [number] = [URL]
62for my $l (@releasenotes) {
63    if($l =~ /^ o .*\[(\d+)\]/) {
64        # referenced, set bit 0
65        $refused[$1]=1;
66    }
67    elsif($l =~ /^ \[(\d+)\] = (.*)/) {
68        # listed in a reference, set bit 1
69        $refused[$1] |= 2;
70        $refs[$1] = $2;
71    }
72}
73
74# Return a new fresh reference number
75sub getref {
76    for my $r (1 .. $#refs) {
77        if(!$refused[$r] & 1) {
78            return $r;
79        }
80    }
81    # add at the end
82    return $#refs + 1;
83}
84
85my $short;
86my $first;
87for my $l (@gitlog) {
88    chomp $l;
89    if($l =~ /^commit/) {
90        if($first) {
91            onecommit($short);
92        }
93        # starts a new commit
94        undef @fixes;
95        undef @closes;
96        undef @bug;
97        $short = "";
98        $first = 0;
99    }
100    elsif(($l =~ /^    (.*)/) && !$first) {
101        # first line
102        $short = $1;
103        $first = 1;
104        push @line, $short;
105    }
106    elsif(($l =~ /^    (.*)/) && $first) {
107        # not the first
108        my $line = $1;
109
110        if($line =~ /^Fixes(:|) .*[^0-9](\d+)/i) {
111            push @fixes, $2;
112        }
113        elsif($line =~ /^Clo(s|)es(:|) .*[^0-9](\d+)/i) {
114            push @closes, $3;
115        }
116        elsif($line =~ /^Bug: (.*)/i) {
117            push @bug, $1;
118        }
119    }
120}
121if($first) {
122    onecommit($short);
123}
124
125# call at the end of a parsed commit
126sub onecommit {
127    my ($short)=@_;
128    my $ref;
129
130    if($bug[0]) {
131        $ref = $bug[0];
132    }
133    elsif($fixes[0]) {
134        $ref = $fixes[0];
135    }
136    elsif($closes[0]) {
137        $ref = $closes[0];
138    }
139
140    if($ref =~ /^#?(\d+)/) {
141        $ref = "https://curl.se/bug/?i=$1"
142    }
143    if($ref) {
144        my $r = getref();
145        $refs[$r] = $ref;
146        $moreinfo{$short}=$r;
147        $refused[$r] |= 1;
148    }
149}
150
151#### Output the new RELEASE-NOTES
152
153my @bullets;
154for my $l (@releasenotes) {
155    if(($l =~ /^This release includes the following bugfixes:/) && !$cleanup) {
156        push @o, $l;
157        push @o, "\n";
158        for my $f (@line) {
159            push @o, sprintf " o %s%s\n", $f,
160                $moreinfo{$f}? sprintf(" [%d]", $moreinfo{$f}): "";
161            $refused[$moreinfo{$f}]=3;
162        }
163        push @o, " --- new entries are listed above this ---";
164        next;
165    }
166    elsif($cleanup) {
167        if($l =~ /^ --- new entries are listed/) {
168            # ignore this if still around
169            next;
170        }
171        elsif($l =~ /^ o .*/) {
172            push @bullets, $l;
173            next;
174        }
175        elsif($bullets[0]) {
176            # output them case insensitively
177            for my $b (sort { "\L$a" cmp "\L$b" } @bullets) {
178                push @o, $b;
179            }
180            undef @bullets;
181        }
182    }
183    if($l =~ /^ \[(\d+)\] = /) {
184        # stop now
185        last;
186    }
187    else {
188        push @o, $l;
189    }
190}
191
192my @srefs;
193my $ln;
194for my $n (1 .. $#refs) {
195    my $r = $refs[$n];
196    if($r && ($refused[$n] & 1)) {
197        push @o, sprintf " [%d] = %s\n", $n, $r;
198    }
199}
200
201open(O, ">RELEASE-NOTES");
202for my $l (@o) {
203    print O $l;
204}
205close(O);
206
207exit;
208
209# Debug: show unused references
210for my $r (1 .. $#refs) {
211    if($refused[$r] != 3) {
212        printf "%s is %d!\n", $r, $refused[$r];
213    }
214}
215