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