fix directory calculations without --git
[p5sagit/Distar.git] / helpers / bump-version
CommitLineData
8ab5ea70 1#!/usr/bin/env perl
2
3use strict;
4use warnings FATAL => 'all';
5use File::Find;
25f4abaf 6use Getopt::Long qw(:config gnu_getopt);
ece2756d 7use File::Temp ();
8
9GetOptions(
10 "git" => \my $git,
40635bc9 11 "force" => \my $force,
6d17b785 12 'n|dry-run' => \my $dry_run,
edb3d044 13 'stable' => \my $stable,
14 'alpha' => \my $alpha,
ece2756d 15) or die("Error in command line arguments\n");
16
eb3aeb36 17my $old_version = shift
18 or die "no old version provided!\n";
19my $bump = shift;
ece2756d 20my ($new_decimal, $new_vstring) = bump_version($old_version, $bump);
edb3d044 21die "--stable and --alpha are incompatible!\n"
22 if $stable and $alpha;
ece2756d 23
6c6e7dc3 24warn "Bumping $old_version -> $new_decimal" . ($new_decimal ne $new_vstring ? " ($new_vstring)" : '') . "\n";
ece2756d 25
f39746fb 26my $file_match = qr{
343f5d00 27 Makefile\.PL
28 |lib[/\\].*\.(?:pod|pm)
29 |bin[/\\].*
30 |script[/\\].*
f39746fb 31}x;
32
33my $dir_match = qr{
34 (?:
35 .
36 |lib
37 |bin
38 |script
39 )
40 (?:[/\\]|$)
41}x;
343f5d00 42
ece2756d 43my %files;
44if ($git) {
176acdb4 45 if (system "git diff --quiet --cached HEAD") {
ece2756d 46 die "Staged changes!\n";
47 }
48 for (`git ls-files`) {
49 chomp;
50 next
f39746fb 51 unless /^$file_match$/;
ece2756d 52 $files{$_} = `git show HEAD:"$_"`;
53 }
54}
55else {
56 find({
57 no_chdir => 1,
58 wanted => sub {
f39746fb 59 my $fn = File::Spec->abs2rel($_, '.');
25bd077d 60 if (-d && $fn !~ /^$dir_match/) {
f39746fb 61 $File::Find::prune = 1;
62 return;
63 }
64 return
ece2756d 65 unless -f;
f39746fb 66 return
67 unless $fn =~ /^$file_match$/;
68 open my $fh, '<', $fn
69 or die "can't open $fn: $!";
70 $files{$fn} = do { local $/; <$fh> };
ece2756d 71 close $fh;
72 },
343f5d00 73 }, '.');
ece2756d 74}
75
76my $FILE_RE = qr{
77 (^.* \$VERSION \s* = \s* )
78 (['"]?) v?([0-9]+(?:[._][0-9]+)*) \2
79 ( \s*; )
80 (?:
81 (\s*\#\s*)
82 v?[.0-9]+
83 )?
84 (.*)$
85}x;
86my $MAKE_RE = qr{
22de1c6a 87 (^.* ['"]?version['"] \s* => \s* )
ece2756d 88 (['"]?) v?([0-9]+(?:[._][0-9]+)*) \2
89 ( \s*, )
90 (?:
91 (\s*\#\s*)
92 v?[.0-9]+
93 )?
94 (.*)$
95}x;
96
97my $patch = '';
98for my $file (sort keys %files) {
6d17b785 99 eval {
100 my $content = $files{$file};
101 my $file_diff = '';
102 my $re = $file eq 'Makefile.PL' ? $MAKE_RE : $FILE_RE;
103 my @lines = split /\r?\n/, $content;
104 for my $ln (0 .. $#lines) {
105 my $line = $lines[$ln];
106 if ($lines[$ln] =~ $re) {
107 die "unable to bump version number in $file from $old_version, found $3\n"
108 if !$force && $3 ne $old_version;
109 my $comment = ($5 ? $5 . $new_vstring : '');
110 my $new_line = "$1'$new_decimal'$4$comment$6";
111 $file_diff .= <<"END_DIFF";
ece2756d 112@@ -@{[ $ln ]},3 +@{[ $ln ]},3 @@
113 $lines[$ln-1]
114-$lines[$ln]
115+$new_line
116 $lines[$ln+1]
117END_DIFF
6d17b785 118 }
ece2756d 119 }
6d17b785 120 if ($file_diff) {
121 $patch .= <<"END_HEADER" . $file_diff;
ece2756d 122--- a/$file
123+++ b/$file
124END_HEADER
6d17b785 125 }
126 1;
127 } or $dry_run ? warn($@) : die($@);
ece2756d 128}
129
6d17b785 130if ($dry_run) {
131 print $patch;
132 exit;
133}
ece2756d 134my ($fh, $file) = File::Temp::tempfile( "bump-version-XXXXXX", TMPDIR => 1 );
135print { $fh } $patch;
136close $fh;
413a726c 137system qw(git --no-pager apply --apply --stat), $file
ece2756d 138 and exit 1;
139
140if ($git) {
141 system qw(git apply --cached), $file
142 and exit 1;
143
144 my $message = "Bumping version to $new_decimal";
145 system qw(git commit -m), $message
146 and exit 1;
147}
8ab5ea70 148
149sub version_parts {
576656c4 150 my $version = shift;
151 my $dotted = $version =~ s/^v//;
152 my @parts = split /\./, $version;
f7d6fb43 153 if (!$dotted && @parts <= 2) {
154 tr/_//d for @parts;
155 if (@parts == 2) {
156 my $dec = pop @parts;
157 $dec .= "0" x ((- length $dec) % 3);
158 push @parts, $dec =~ /(\d{1,3})/g;
159 }
160 }
161 elsif ($version =~ tr/_//) {
162 die "don't know how to handle underscores in dotted-decimal versions!\n";
8ab5ea70 163 }
164 $_ += 0 for @parts;
8ab5ea70 165 return @parts;
166}
167
ece2756d 168sub bump_version {
150fcad0 169 my ($version, $new) = @_;
8ab5ea70 170
150fcad0 171 my %bump_part = (major => 0, minor => 1, bugfix => 2, last => -1);
172 my $bump_this = $bump_part{$new||'last'};
576656c4 173
ece2756d 174 my $new_vstring;
175 my $new_decimal;
8ab5ea70 176
ece2756d 177 if (defined $bump_this) {
150fcad0 178 if ($version =~ /^v/ || ($version =~ tr/.//) > 1) {
6c6e7dc3 179 my $v = $version =~ /^(v)/ ? $1 : '';
edb3d044 180 if ($version =~ tr/_//d && !$stable || $alpha) {
181 die "can't bump dotted decimal versions with alpha components!\n";
182 }
150fcad0 183 my @parts = version_parts($version);
6c6e7dc3 184 $bump_this += @parts
185 if $bump_this < 0;
186 $parts[$_] = 0 for $bump_this+1 .. $#parts;
187 $parts[$_] = 0 for $#parts+1 .. $bump_this;
150fcad0 188 $parts[$bump_this]++;
150fcad0 189 $_ += 0
190 for @parts;
e05a4bcc 191 if (grep $_ > 999, @parts[1 .. $#parts]) {
192 warn "$new_decimal has a version component greater than 999. It will be incompatible with some uses in perl.\n";
193 }
6c6e7dc3 194 $new_decimal = $new_vstring = $v . join '.', @parts;
150fcad0 195 }
196 else {
edb3d044 197 my $alpha_pos;
198 if (!$stable) {
199 $alpha_pos = index($version, '_');
200 if ($alpha_pos == -1) {
201 undef $alpha_pos;
202 }
203 else {
204 my $dot_pos = index($version, '.');
205 $alpha_pos = $dot_pos == -1 ? -$alpha_pos : $alpha_pos - $dot_pos;
206 }
f7d6fb43 207 }
208 $new_decimal = $version;
209 $new_decimal =~ tr/_//d;
210 my $dec_len = $new_decimal =~ /(\.\d+)/ ? length($1) - 1 : 0;
211 if ($bump_this != -1) {
212 my $cut_len = $bump_this * 3;
213 $dec_len = $cut_len
214 if $dec_len < $cut_len;
215 $new_decimal =~ s/(\..{1,$cut_len}).*/$1/;
216 }
217 $new_decimal += 10 ** -($bump_this == -1 ? $dec_len : ($bump_this * 3));
218 $new_decimal = sprintf "%.${dec_len}f", $new_decimal;
edb3d044 219 if ($alpha) {
220 $alpha_pos ||= $dec_len >= 2 ? int($dec_len / 2) + 1 :
221 die "don't know how to make $new_decimal into an alpha version";
222 }
f7d6fb43 223 if (defined $alpha_pos) {
224 my $dot_pos = index($new_decimal, '.');
225 $dot_pos = length $new_decimal
226 if $dot_pos == -1;
227 substr $new_decimal, $dot_pos + $alpha_pos, 0, '_';
228 }
229 $new_vstring = 'v' . join '.', version_parts($new_decimal);
150fcad0 230 }
ece2756d 231 }
232 elsif ($new =~ /^v?[0-9]+(?:[._][0-9]+)*$/) {
233 $new_decimal = $new;
234 $new_vstring = join('.', version_parts($new_decimal));
235 }
236 else {
237 die "no idea which part to bump - $new means nothing to me"
238 }
239 return ($new_decimal, $new_vstring);
a4c19845 240}
241