3 # ucl - update changelog for subversion or git projects (perl version)
5 # Copyright (c) 2005-2012 by Ricardo Mones <ricardo@mones.org>
7 # Permission is hereby granted, free of charge, to any person obtaining a
8 # copy of this software and associated documentation files (the "Software"),
9 # to deal in the Software without restriction, including without limitation
10 # the rights to use, copy, modify, merge, publish, distribute, sublicense,
11 # and/or sell copies of the Software, and to permit persons to whom the
12 # Software is furnished to do so, subject to the following conditions:
14 # The above copyright notice and this permission notice shall be included
15 # in all copies or substantial portions of the Software.
17 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20 # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22 # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
23 # DEALINGS IN THE SOFTWARE.
29 my $PROJECT = 'clawsker'; # project name
30 my $TYPE = 'git'; # either 'git' or 'svn'
31 my $REPO = 'git@github.com:mones/clawsker.git'; # repository base URL
34 my $CL = './ChangeLog'; # changelog file
35 my $VF = './VERSION'; # version file
36 my $VC = './VC'; # revision counter
38 my @NOTFORRELEASE = ( 'ucl', 'VC' );
41 my $SF = '.ucl.status'; # status file
42 my $PCL = '.ucl.prev'; # previous changelog
43 # global version numbers
47 my $help = <<'ENDOFHELP'
49 ucl Update changelog for Subversion/Git repository
53 -r x.y.z Update is for release version x.y.z
54 -t x.y.z Tags current trunk as release x.y.z on repository
55 -T x.y.z Makes x.y.z release tarball
57 Subversion repositories are assumed to have the trunk, branches and tags
58 subtrees just under repository root.
59 Repository tag names are always project-version to ease tarball generation.
72 my $sourceurl = "$REPO/trunk/$PROJECT";
73 my $targeturl = "$REPO/tags/$PROJECT/$tag";
74 qx/svn cp $sourceurl $targeturl -m $msg/;
78 sub repo_get_tag_to_dir {
79 my $tag = shift; # destination dir name is tag too
81 qx/git archive --format zip --output .tmp_$tag.zip --prefix=$tag\/ $tag/;
82 qx/unzip .tmp_$tag.zip -d ./;
83 unlink(".tmp_$tag.zip") unless ! -d "$tag";
86 my $targeturl = "$REPO/tags/$PROJECT/$tag";
87 qx/svn export $targeturl/;
93 qx/git checkout master/;
96 qx/LANGUAGE=C svn up/;
100 sub repo_get_revision {
102 if ($TYPE eq 'git') {
103 open (VCF, "<$VC") or die "opening $VC: $!\n";
109 if ($TYPE eq 'svn') {
110 $_ = qx/LANGUAGE=C svn info | grep "Revision"/;
118 sub repo_get_modifications {
120 if ($TYPE eq 'git') {
121 qx/git status --porcelain > $SF/;
122 open (STF, "<$SF") or die "opening $SF: $!\n";
125 if (/^\s?M\s+(.*)$/) {
126 push (@mods, "\t* $1");
128 if (/^AM?\s+(.*)$/) {
129 push (@mods, "\t* $1\t\t**NEW**");
132 push (@mods, "\t* $1\t\t**REMOVED**");
135 print "Info: untracked: $1\n";
139 unlink ($SF); # remove the status file
141 if ($TYPE eq 'svn') {
142 qx/svn status > $SF/;
143 open (STF, "<$SF") or die "opening $SF: $!\n";
146 if (/^MM?\s+(.*)$/) {
147 push (@mods, "\t* $1");
149 if (/^AM?\s+(.*)$/) {
150 push (@mods, "\t* $1\t\t**NEW**");
153 push (@mods, "\t* $1\t\t**REMOVED**");
156 print "Info: not versioned: $1\n";
160 unlink ($SF); # remove the status file
165 sub tag_new_release {
168 die "Invalid release version\n" unless (/^\d+\.\d+\.\d+$/);
169 my $namevers = "$PROJECT-$relnum";
170 my $commitmsg = "'tag release $relnum'";
171 &repo_tag ($namevers, $commitmsg);
174 sub tarball_from_release_tag {
177 die "Invalid release version\n" unless (/^\d+\.\d+\.\d+$/);
178 my $namevers = "$PROJECT-$relnum";
179 &repo_get_tag_to_dir ($namevers);
180 foreach my $file (@NOTFORRELEASE) {
181 if (-f "$namevers/$file") {
182 unlink("$namevers/$file");
185 qx/tar czf $namevers.tar.gz $namevers/;
189 if (defined($ARGV[0])) {
192 die "Undefined release version\n" unless defined ($ARGV[1]);
194 die "Invalid release version\n" unless (/^\d+\.\d+\.\d+$/);
198 die "Undefined release version\n" unless defined ($ARGV[1]);
199 &tag_new_release ($ARGV[1]);
203 die "Undefined release version\n" unless defined ($ARGV[1]);
204 &tarball_from_release_tag ($ARGV[1]);
211 die "Unknown option. Try -? for help\n";
216 sub get_changelog_date {
217 my ($sec, $min, $hour, $mday, $mon, $year, $wday) = localtime();
220 $hour = ($hour < 10)? "0$hour": "$hour";
221 $min = ($min < 10)? "0$min": "$min";
222 $mon = ($mon < 10)? "0$mon": "$mon";
223 $mday = ($mday < 10)? "0$mday": "$mday";
224 return "$year-$mon-$mday $hour:$min";
227 sub get_version_array {
229 if (not defined ($rel)) { # get number from version file
230 open (VTF, "<$VF") or die "opening $VF: $!\n";
239 if (/^(\d+)\.(\d+)\.(\d+)$/) {
240 @vers = ($1, $2, $3, "");
242 if (/^(\d+)\.(\d+)\.(\d+)svn(\d+)$/) {
243 @vers = ($1, $2, $3, $4);
245 if (/^(\d+)\.(\d+)\.(\d+)git(\d+)$/) {
246 @vers = ($1, $2, $3, $4);
251 sub get_newer_version {
252 my ($maj, $min, $mic, $ext, $lastr) = @_;
253 my $nvers = "$maj.$min.$mic";
254 if (not defined($release) or ($release eq "")) {
256 $nvers = ($nvers . $TYPE . (1 + $lastr));
262 sub get_newer_header {
263 my $newversion = shift;
264 my $cldate = &get_changelog_date;
265 my $cluser = $USER; # $ENV{USER};
266 return "$cldate $cluser $newversion";
270 # check we're in the right place
271 -e "$CL" or die "Oops, no ChangeLog here\n";
272 -e "$VF" or die "Oops, no VERSION here\n";
274 # parser VERSION file or $release argument
275 my $vs = &get_version_array ($release);
276 my ($major, $minor, $micro, $extra) = (@$vs[0], @$vs[1], @$vs[2], @$vs[3]);
277 # update directory to be sure we're at the last rev
279 # get last revision from repository
280 my $lastrev = &repo_get_revision ();
281 # calculate modifications
282 my $modifs = &repo_get_modifications ();
283 # save previous changelog
286 my $newver = &get_newer_version ($major, $minor, $micro, $extra, $lastrev);
287 # write new entry header
288 open (NCL, ">$CL") or die "opening $CL: $!\n";
289 # write new entry modifications
290 my $newhdr = &get_newer_header ($newver);
291 print NCL "$newhdr\n\n";
292 foreach my $modif (@$modifs) { print NCL "$modif\n"; }
294 # and previous entries
295 open (PCL, "<$PCL") or die "opening $PCL: $!\n";
296 while (<PCL>) { print NCL $_; }
299 # and keep a digest for checking
300 my $oldmd5 = qx/md5sum $CL | cut -f1 -d" "/;
302 my $editor = 'vim'; # (defined($ENV{DISPLAY}))? 'gvim -f': 'vim';
303 if (defined($ENV{EDITOR})) {
304 $editor = $ENV{EDITOR};
306 my @editor = ($editor, $CL);
309 my $newmd5 = qx/md5sum $CL | cut -f1 -d" "/;
310 if ($oldmd5 eq $newmd5) {
311 print "Unmodified ChangeLog, discarding changes.\n";
317 # update new version after changes
318 qx/echo $newver > $VF/;
319 $TYPE eq 'git' and do { # and counter for git
320 my $newrev = 1 + $lastrev;
321 qx/echo $newrev > $VC/;