#!/usr/bin/perl -w
#
-# ucl - update changelog for subversion projects (perl version)
+# ucl - update changelog for subversion or git projects (perl version)
#
-# Copyright (c) 2005-2010 by Ricardo Mones <ricardo@mones.org>
+# Copyright (c) 2005-2011 by Ricardo Mones <ricardo@mones.org>
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the "Software"),
use strict;
-my $PROJECT = "clawsker"; # project name under trunk
-my $REPO = "https://svn.mones.org/svn"; # repository base URL
-my $CL = './ChangeLog'; # changelog file
-my $VF = './VERSION'; # version file
-my $SF = '.ucl.status'; # status file
-my $PCL = '.ucl.prev'; # previous changelog
-my @NOTFORRELEASE = ( "ucl" );
+# project data
+my $PROJECT = 'clawsker'; # project name
+my $TYPE = 'git'; # either 'git' or 'svn'
+my $REPO = 'git@github.com:mones/clawsker.git'; # repository base URL
+my $USER = "mones";
+# project files
+my $CL = './ChangeLog'; # changelog file
+my $VF = './VERSION'; # version file
+my $VC = './VC'; # revision counter
+# excluded files
+my @NOTFORRELEASE = ( 'ucl', 'VC' );
+
+# temporary files
+my $SF = '.ucl.status'; # status file
+my $PCL = '.ucl.prev'; # previous changelog
+# global version numbers
my $release = undef;
+sub print_help_info {
+ my $help = <<'ENDOFHELP'
+Description:
+ ucl Update changelog for Subversion/Git repository
+Syntax:
+ ucl [options]
+Where options are:
+ -r x.y.z Update is for release version x.y.z
+ -t x.y.z Tags current trunk as release x.y.z on repository
+ -T x.y.z Makes x.y.z release tarball
+Notes:
+ Subversion repositories are assumed to have the trunk, branches and tags
+ subtrees just under repository root.
+ Repository tag names are always project-version to ease tarball generation.
+ENDOFHELP
+ ;
+ print $help;
+ exit 0;
+}
+
+sub repo_tag {
+ my ($tag, $msg) = @_;
+ if ($TYPE eq 'git') {
+ qx/git branch $tag/;
+ }
+ if ($TYPE eq 'svn') {
+ my $sourceurl = "$REPO/trunk/$PROJECT";
+ my $targeturl = "$REPO/tags/$PROJECT/$tag";
+ qx/svn cp $sourceurl $targeturl -m $msg/;
+ }
+}
+
+sub repo_get_tag_to_dir {
+ my $tag = shift; # destination dir name is tag too
+ if ($TYPE eq 'git') {
+ qx/git archive --format zip --output .tmp_$tag.zip --prefix=$tag\/ $tag/;
+ qx/unzip .tmp_$tag.zip -d ./;
+ unlink(".tmp_$tag.zip") unless ! -d "$tag";
+ }
+ if ($TYPE eq 'svn') {
+ my $targeturl = "$REPO/tags/$PROJECT/$tag";
+ qx/svn export $targeturl/;
+ }
+}
+
+sub repo_update {
+ if ($TYPE eq 'git') {
+ qx/git checkout master/;
+ }
+ if ($TYPE eq 'svn') {
+ qx/LANGUAGE=C svn up/;
+ }
+}
+
+sub repo_get_revision {
+ my $rev = '';
+ if ($TYPE eq 'git') {
+ open (VCF, "<$VC") or die "opening $VC: $!\n";
+ $_ = <VCF>;
+ chomp;
+ $rev = $_;
+ close (VCF);
+ }
+ if ($TYPE eq 'svn') {
+ $_ = qx/LANGUAGE=C svn info | grep "Revision"/;
+ @_ = split (':');
+ $rev = $_[1];
+ $rev =~ s/\s+//;
+ }
+ return $rev;
+}
+
+sub repo_get_modifications {
+ my @mods = ();
+ if ($TYPE eq 'git') {
+ qx/git status --porcelain > $SF/;
+ open (STF, "<$SF") or die "opening $SF: $!\n";
+ while (<STF>) {
+ chomp;
+ if (/^\s?M\s+(.*)$/) {
+ push (@mods, "\t* $1");
+ }
+ if (/^AM?\s+(.*)$/) {
+ push (@mods, "\t* $1\t\t**NEW**");
+ }
+ if (/^D\s+(.*)$/) {
+ push (@mods, "\t* $1\t\t**REMOVED**");
+ }
+ if (/^\?\s+(.*)$/) {
+ print "Info: untracked: $1\n";
+ }
+ }
+ close (STF);
+ unlink ($SF); # remove the status file
+ }
+ if ($TYPE eq 'svn') {
+ qx/svn status > $SF/;
+ open (STF, "<$SF") or die "opening $SF: $!\n";
+ while (<STF>) {
+ chomp;
+ if (/^MM?\s+(.*)$/) {
+ push (@mods, "\t* $1");
+ }
+ if (/^AM?\s+(.*)$/) {
+ push (@mods, "\t* $1\t\t**NEW**");
+ }
+ if (/^D\s+(.*)$/) {
+ push (@mods, "\t* $1\t\t**REMOVED**");
+ }
+ if (/^\?\s+(.*)$/) {
+ print "Info: not versioned: $1\n";
+ }
+ }
+ close (STF);
+ unlink ($SF); # remove the status file
+ }
+ return \@mods;
+}
+
sub tag_new_release {
my $relnum = shift;
$_ = $relnum;
die "Invalid release version\n" unless (/^\d+\.\d+\.\d+$/);
my $namevers = "$PROJECT-$relnum";
- my $sourceurl = "$REPO/trunk/$PROJECT";
- my $targeturl = "$REPO/tags/$PROJECT/$namevers";
my $commitmsg = "'tag release $relnum'";
- qx/svn cp $sourceurl $targeturl -m $commitmsg/;
+ &repo_tag ($namevers, $commitmsg);
}
sub tarball_from_release_tag {
$_ = $relnum;
die "Invalid release version\n" unless (/^\d+\.\d+\.\d+$/);
my $namevers = "$PROJECT-$relnum";
- my $targeturl = "$REPO/tags/$PROJECT/$namevers";
- qx/svn export $targeturl/;
+ &repo_get_tag_to_dir ($namevers);
foreach my $file (@NOTFORRELEASE) {
if (-f "$namevers/$file") {
unlink("$namevers/$file");
exit 0;
}
elsif (/-\?/) {
- my $help = <<'ENDOFHELP'
-Description:
- ucl Update changelog for Subversion repository
-Syntax:
- ucl [options]
-Where options are:
- -r x.y.z Update is for release version x.y.z
- -t x.y.z Tags current trunk as release x.y.z on repository
- -T x.y.z Makes x.y.z release tarball
-ENDOFHELP
- ;
- print $help;
- exit 0;
+ &print_help_info ();
}
else {
die "Unknown option. Try -? for help\n";
return "$year-$mon-$mday $hour:$min";
}
-sub main {
- # check we're in the right place
- -e "$CL" or die "Oops, no ChangeLog here\n";
- -e "$VF" or die "Oops, no VERSION here\n";
- &parse_options;
- # parser VERSION file or $release argument
- my ($major, $minor, $micro, $extra) = (undef, undef, undef, undef);
- if (not defined ($release)) { # got number it from version file
- open (VTF, "<$VF");
+sub get_version_array {
+ my $rel = shift;
+ if (not defined ($rel)) { # get number from version file
+ open (VTF, "<$VF") or die "opening $VF: $!\n";
$_ = <VTF>;
chomp;
close (VTF);
}
else {
- $_ = $release;
+ $_ = $rel;
}
+ my @vers = ();
if (/^(\d+)\.(\d+)\.(\d+)$/) {
- ($major, $minor, $micro, $extra) = ($1, $2, $3, "");
+ @vers = ($1, $2, $3, "");
}
if (/^(\d+)\.(\d+)\.(\d+)svn(\d+)$/) {
- ($major, $minor, $micro, $extra) = ($1, $2, $3, $4);
+ @vers = ($1, $2, $3, $4);
+ }
+ if (/^(\d+)\.(\d+)\.(\d+)git(\d+)$/) {
+ @vers = ($1, $2, $3, $4);
}
+ return \@vers;
+}
+
+sub get_newer_version {
+ my ($maj, $min, $mic, $ext, $lastr) = @_;
+ my $nvers = "$maj.$min.$mic";
+ if (not defined($release) or ($release eq "")) {
+ if ($ext ne "") {
+ $nvers = ($nvers . $TYPE . (1 + $lastr));
+ }
+ }
+ return $nvers;
+}
+
+sub get_newer_header {
+ my $newversion = shift;
+ my $cldate = &get_changelog_date;
+ my $cluser = $USER; # $ENV{USER};
+ return "$cldate $cluser $newversion";
+}
+
+sub main {
+ # check we're in the right place
+ -e "$CL" or die "Oops, no ChangeLog here\n";
+ -e "$VF" or die "Oops, no VERSION here\n";
+ &parse_options;
+ # parser VERSION file or $release argument
+ my $vs = &get_version_array ($release);
+ my ($major, $minor, $micro, $extra) = (@$vs[0], @$vs[1], @$vs[2], @$vs[3]);
# update directory to be sure we're at the last rev
- $_ = qx/LANGUAGE=C svn up/;
- my @updateout = split /\\n/;
+ &repo_update ();
# get last revision from repository
- $_ = qx/LANGUAGE=C svn info | grep "Revision"/;
- @_ = split (':');
- my $lastrev = $_[1];
- $lastrev =~ s/\s+//;
+ my $lastrev = &repo_get_revision ();
# calculate modifications
- qx/svn status > $SF/;
- my @modifs = ();
- open (STF, "<$SF");
- while (<STF>) {
- chomp;
- if (/^MM?\s+(.*)$/) {
- push (@modifs, "\t* $1");
- }
- if (/^AM?\s+(.*)$/) {
- push (@modifs, "\t* $1\t\t**NEW**");
- }
- if (/^D\s+(.*)$/) {
- push (@modifs, "\t* $1\t\t**REMOVED**");
- }
- if (/^\?\s+(.*)$/) {
- print "Info: not versioned: $1\n";
- }
- }
- close (STF);
- unlink ($SF); # remove the status file
+ my $modifs = &repo_get_modifications ();
# save previous changelog
rename ($CL, $PCL);
+ # get new version
+ my $newver = &get_newer_version ($major, $minor, $micro, $extra, $lastrev);
# write new entry header
- open (NCL, ">$CL");
- my $cldate = &get_changelog_date;
- my $cluser = $ENV{USER};
- my $clvers = "$major.$minor.$micro";
- if ($release eq "") {
- if ($extra ne "") {
- $clvers = ($clvers . "svn" . (1 + $lastrev));
- }
- }
+ open (NCL, ">$CL") or die "opening $CL: $!\n";
# write new entry modifications
- print NCL "$cldate $cluser $clvers\n\n";
- foreach my $modif (@modifs) {
- print NCL "$modif\n";
- }
+ my $newhdr = &get_newer_header ($newver);
+ print NCL "$newhdr\n\n";
+ foreach my $modif (@$modifs) { print NCL "$modif\n"; }
print NCL "\n";
# and previous entries
- open (PCL, "<$PCL");
+ open (PCL, "<$PCL") or die "opening $PCL: $!\n";
while (<PCL>) { print NCL $_; }
close (PCL);
close (NCL);
else {
unlink ($PCL);
# update new version after changes
- qx/echo $clvers > $VF/;
+ qx/echo $newver > $VF/;
+ $TYPE eq 'git' and do { # and counter for git
+ my $newrev = 1 + $lastrev;
+ qx/echo $newrev > $VC/;
+ }
}
}
-main;
+&main;
exit 0;