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