fix --help output to be proper UTF-8
[clawsker.git] / ucl
diff --git a/ucl b/ucl
index cf1a4769e4b2ceda701f3d056f78bb1113d94e16..65ea49e0fb04b69355f0a23576744e84714d8b10 100755 (executable)
--- a/ucl
+++ b/ucl
@@ -1,8 +1,8 @@
 #!/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-2012 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 {
@@ -50,8 +176,7 @@ 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");
@@ -80,19 +205,7 @@ sub parse_options {
       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";
@@ -111,77 +224,75 @@ sub get_changelog_date {
   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);
@@ -204,11 +315,15 @@ sub main {
   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;