ci(gitlab-ci): use new versioning script to compute release version
This commit is contained in:
parent
45d1480181
commit
5ae515594d
@ -167,8 +167,7 @@ release:prepare:
|
||||
- if: $CI_COMMIT_TAG =~ /^v[0-9\.]+-test-.*$/
|
||||
script:
|
||||
- echo "Preparing release..."
|
||||
# TODO: get tag and pass to following release jobs as artifact
|
||||
- echo FOOBAR > .current-version
|
||||
- ./.gitlab-ci/version.pl > .current-version
|
||||
artifacts:
|
||||
paths:
|
||||
- .current-version
|
||||
|
||||
483
.gitlab-ci/version.pl
Executable file
483
.gitlab-ci/version.pl
Executable file
@ -0,0 +1,483 @@
|
||||
#!/usr/bin/env perl
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use Data::Dumper;
|
||||
|
||||
# Version changes:
|
||||
# v[x].[y].[z] -- Main version number
|
||||
# v[x].[y].[z]-test-[branchstring]-num -- test/branch/devel version number
|
||||
# on main/master: Biggest version so far, increment by occuring changes
|
||||
# on other branches: find version; be it branch string, old format or main version number;
|
||||
# increments from there. Increment version number, but on global conflict use new version number
|
||||
|
||||
# Actions and their results
|
||||
# chore -> +patch
|
||||
# feat -> +minor
|
||||
# fix -> +patch
|
||||
# [a-z]+! -> +major
|
||||
# perf -> +patch
|
||||
# refactor -> +patch
|
||||
# test -> +patch
|
||||
# style -> +patch
|
||||
# revert -> =
|
||||
# docs -> +patch
|
||||
# build -> =
|
||||
# ci -> =
|
||||
|
||||
# parameters with default values
|
||||
my %par = ();
|
||||
my %parKinds = (
|
||||
vcslog=>{
|
||||
arity=>1,
|
||||
def=>'git log --pretty=tformat:"%H :::: %d :::: %s"',
|
||||
help=>'set command which outputs the log information to be used; reads from STDIN if value is set to "-"',
|
||||
},
|
||||
vcstags=>{
|
||||
arity=>1,
|
||||
def=>'git tag',
|
||||
help=>'set command which outputs the used tags',
|
||||
},
|
||||
vcsbranch=>{
|
||||
arity=>1,
|
||||
def=>'git rev-parse --abbrev-ref HEAD',
|
||||
help=>'set command to find out the current branch name',
|
||||
},
|
||||
kind=>{
|
||||
arity=>1,
|
||||
def=>'v',
|
||||
help=>'set tag kind of version numbers; this option resets autokind to "". Implemented kinds: v: main version; t: test version',
|
||||
auto=>sub { $par{autokind}='' },
|
||||
},
|
||||
autokind=>{
|
||||
arity=>1,
|
||||
def=>'main=v,master=v,test=t,*=t',
|
||||
help=>'determine the tag kind from branch name instead of fixed value; use the first fitting glob',
|
||||
},
|
||||
change=>{
|
||||
arity=>1,
|
||||
def=>'chore=patch,feat=minor,feature=minor,fix=patch,BREAK=major,perf=patch,refactor=patch,test=patch,style=patch,revert=null,docs=patch,build=null,ci=null',
|
||||
help=>'how to react on which commit type; can be partially given. Actions are: "null", "major", "minor", "patch" or state "invalid" for removing this type',
|
||||
},
|
||||
v=>{def=>0,arity=>0,help=>'verbose'},
|
||||
h=>{def=>0,arity=>0,help=>'help'},
|
||||
);
|
||||
|
||||
for my $k(keys %parKinds) {
|
||||
$par{$k} = $parKinds{$k}{def}
|
||||
}
|
||||
|
||||
#for my $p(@ARGV) {
|
||||
#
|
||||
#}
|
||||
{
|
||||
my $i = 0;
|
||||
while($i<@ARGV) {
|
||||
if($ARGV[$i]=~m#^-(.*)#) {
|
||||
my $key = $1;
|
||||
if(not exists $parKinds{$key}) {
|
||||
die "$0: Unknown parameter: -$key\n";
|
||||
}
|
||||
my $pk = $parKinds{$key};
|
||||
die "$0: Too few parameters for '-$key'\n" if $i+$pk->{arity}>@ARGV;
|
||||
my @par = @ARGV[$i+1..$i+$pk->{arity}];
|
||||
#warn "<< @par >>";
|
||||
$i++;
|
||||
$i += $pk->{arity};
|
||||
if($pk->{arity}) {
|
||||
$par{$key} = $par[0]
|
||||
} else {
|
||||
$par{$key}=1
|
||||
}
|
||||
if(exists $pk->{auto}) {
|
||||
$pk->{auto}->()
|
||||
}
|
||||
} else {
|
||||
die "$0: Bad parameter: $ARGV[$i]\n"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if($par{'h'}) {
|
||||
print "Usage: $0 [flags and options]\n\nAvailable options:\n";
|
||||
for my $k(sort keys %parKinds) {
|
||||
print " -$k\n $parKinds{$k}{help}\n";
|
||||
if($parKinds{$k}{arity}) {
|
||||
print " Default value: $parKinds{$k}{def}\n";
|
||||
} else {
|
||||
print " This is a flag and not an option\n";
|
||||
}
|
||||
print "\n";
|
||||
}
|
||||
exit 0
|
||||
}
|
||||
|
||||
if($par{autokind}) {
|
||||
my $branch = `$par{vcsbranch}`;
|
||||
my @rules = split /,/, $par{autokind};
|
||||
RULES: {
|
||||
for my $r(@rules) {
|
||||
if($r!~m#(.*)=(.*)#) {
|
||||
die "$0: Bad rule in autokind: $r\n";
|
||||
}
|
||||
my ($glob, $kind) = ($1, $2);
|
||||
if(globString($glob, $branch)) {
|
||||
$par{'kind'} = $kind;
|
||||
last RULES
|
||||
}
|
||||
}
|
||||
warn "$0: No autokind rule matches; leaving the kind unchanged.\n"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if($par{'v'}) {
|
||||
print "VERBOSE: Parameters\n";
|
||||
for my $k(sort keys %par) {
|
||||
print " $k: $par{$k}\n"
|
||||
}
|
||||
}
|
||||
|
||||
my %typeReact = ();
|
||||
for my $as(split /,/, $par{change}) {
|
||||
if($as=~m#(.*)=(.*)#) {
|
||||
$typeReact{$1} = $2;
|
||||
} else {
|
||||
warn "$0: Unexpected change parameter: '$as'"
|
||||
}
|
||||
}
|
||||
|
||||
#my @have = split /\n/, `$par{vcstags}`;
|
||||
#
|
||||
#my @keep = grep { $_ } map { m#^($par{kind})([0-9].*)# ? [$1,$2] : undef } @have;
|
||||
#
|
||||
#my @oldVersions = ();
|
||||
|
||||
sub globString {
|
||||
my ($glob, $string) = @_;
|
||||
my @glob = map { m#\*# ? '*' : $_ } $glob=~m#(\?|\*+|[^\?\*]+)#g;
|
||||
my %matchCache = ();
|
||||
my $match = undef;
|
||||
my $matchCore = sub {
|
||||
my ($i, $j) = @_;
|
||||
return 1 if $i==@glob and $j==length $string;
|
||||
return 0 if $i>=@glob or $j>=length $string;
|
||||
return $match->($i+1,$j+1) if '?' eq $glob[$i];
|
||||
if('*' eq $glob[$i]) {
|
||||
for my $jj($j..length($string)) {
|
||||
return 1 if $match->($i+1, $jj);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
return $match->($i+1, $j+length($glob[$i])) if
|
||||
$glob[$i] eq substr($string, $j, length($glob[$i]));
|
||||
return 0
|
||||
};
|
||||
$match = sub {
|
||||
my ($i, $j) = @_;
|
||||
my $ij = "$i $j";
|
||||
my $res = $matchCache{$ij};
|
||||
if(not defined $res) {
|
||||
$res = $matchCore->($i, $j);
|
||||
$matchCache{$ij} = $res;
|
||||
}
|
||||
$res
|
||||
};
|
||||
$match->(0,0);
|
||||
}
|
||||
|
||||
sub parseVersion {
|
||||
my $v = shift;
|
||||
if(not defined $v) {
|
||||
my $c = join " ", caller;
|
||||
warn "$0: internal error (parseVersion called on undef at $c)\n";
|
||||
return undef
|
||||
}
|
||||
my ($pre,$ma,$mi,$p,$sp,$brn,$brv) = ();
|
||||
if($v=~m#^([a-z]*)([0-9]+)$#) {
|
||||
$pre = $1;
|
||||
$ma = $2;
|
||||
} elsif($v=~m#^([a-z]*)([0-9]+)\.([0-9]+)$#) {
|
||||
$pre = $1;
|
||||
$ma = $2;
|
||||
$mi = $3
|
||||
} elsif($v=~m#^([a-z]*)([0-9]+)\.([0-9]+)\.([0-9]+)$#) {
|
||||
$pre = $1;
|
||||
$ma = $2;
|
||||
$mi = $3;
|
||||
$p = $4;
|
||||
} elsif($v=~m#^([a-z]*)([0-9]+)\.([0-9]+)\.([0-9]+)-test-([a-z]+)-([0-9\.]+)$#) {
|
||||
$pre = $1;
|
||||
$ma = $2;
|
||||
$mi = $3;
|
||||
$p = $4;
|
||||
$sp = $5;
|
||||
$brn = $6;
|
||||
$brv = $7;
|
||||
} elsif($v=~m#^([a-z]*)([0-9]+)\.([0-9]+)\.([0-9]+)-(.*)$#) {
|
||||
$pre = $1;
|
||||
$ma = $2;
|
||||
$mi = $3;
|
||||
$p = $4;
|
||||
$sp = $5;
|
||||
} else {
|
||||
warn "$0: unexpected old version number: $v\n" if $par{v};
|
||||
return undef
|
||||
}
|
||||
$pre = 'v' if '' eq $pre;
|
||||
return {
|
||||
prefix=>$pre,
|
||||
major=>$ma,
|
||||
minor=>$mi,
|
||||
patch=>$p,
|
||||
subpatch=>$sp,
|
||||
branchname=>$brn,
|
||||
branchversion=>$brv,
|
||||
}
|
||||
}
|
||||
|
||||
#@oldVersions = sort {
|
||||
# ($a->{major} // 0) <=> ($b->{major} // 0) ||
|
||||
# ($a->{minor} // 0) <=> ($b->{minor} // 0) ||
|
||||
# ($a->{patch} // 0) <=> ($b->{patch} // 0) ||
|
||||
# ($a->{subpatch} // '') <=> ($b->{subpatch} // '')
|
||||
#} @oldVersions;
|
||||
|
||||
sub vsCompare {
|
||||
my ($vp, $wp) = @_;
|
||||
my ($v, $w) = ($vp, $wp);
|
||||
my ($verr, $werr) = (0,0);
|
||||
unless(ref $v) {
|
||||
eval { $v = parseVersion($v) };
|
||||
$verr = 1 if $@ or not defined $v;
|
||||
}
|
||||
unless(ref $w) {
|
||||
eval { $w = parseVersion($w) };
|
||||
$werr = 1 if $@ or not defined $w;
|
||||
}
|
||||
if($verr and $werr) {
|
||||
return $vp cmp $wp;
|
||||
}
|
||||
if($verr) {
|
||||
return -1
|
||||
}
|
||||
if($werr) {
|
||||
return 1
|
||||
}
|
||||
#for($v, $w) {
|
||||
# $_ = parseVersion($_) unless ref $_;
|
||||
#}
|
||||
if('v' eq $v->{prefix} and 'v' eq $w->{prefix}) {
|
||||
return(
|
||||
($v->{major} // 0) <=> ($w->{major} // 0) ||
|
||||
($v->{minor} // 0) <=> ($w->{minor} // 0) ||
|
||||
($v->{patch} // 0) <=> ($w->{patch} // 0) ||
|
||||
($v->{branchname} // '') cmp ($w->{branchname} // '') ||
|
||||
($v->{branchversion} // '') <=> ($w->{branchversion} // '') ||
|
||||
($v->{subpatch} // '') cmp ($w->{subpatch} // '')
|
||||
)
|
||||
} elsif('v' eq $v->{prefix} and 'v' ne $w->{prefix}) {
|
||||
return 1;
|
||||
} elsif('v' ne $v->{prefix} and 'v' eq $w->{prefix}) {
|
||||
return -1;
|
||||
} else {
|
||||
return vsStringDebug($v) cmp vsStringDebug($w)
|
||||
}
|
||||
}
|
||||
|
||||
sub vsStringDebug {
|
||||
my $v = shift;
|
||||
my $ret =
|
||||
("[" . ($v->{prefix} // 'undef') . "]") .
|
||||
($v->{major} // 'undef') . "." .
|
||||
($v->{minor} // 'undef') . "." .
|
||||
($v->{patch} // 'undef');
|
||||
$ret .= "-[$v->{subpatch}]" if defined $v->{subpatch};
|
||||
$ret .= "-test-" . ($v->{branchname} // 'undef') . "-" . ($v->{branchversion} // 'undef');
|
||||
return $ret
|
||||
}
|
||||
|
||||
sub vsString {
|
||||
my $v = shift;
|
||||
my $ret =
|
||||
($v->{major} // 0) . "." .
|
||||
($v->{minor} // 0) . "." .
|
||||
($v->{patch} // 0);
|
||||
$ret .= "-$v->{subpatch}" if defined $v->{subpatch};
|
||||
return $ret
|
||||
}
|
||||
|
||||
sub vsJustVersion {
|
||||
my $v = shift;
|
||||
my $ret =
|
||||
($v->{major} // 0) . "." .
|
||||
($v->{minor} // 0) . "." .
|
||||
($v->{patch} // 0);
|
||||
return $ret
|
||||
}
|
||||
|
||||
sub vsTestVersion {
|
||||
my $v = shift;
|
||||
my $ret =
|
||||
'v' .
|
||||
($v->{major} // 0) . "." .
|
||||
($v->{minor} // 0) . "." .
|
||||
($v->{patch} // 0) . "-test-" .
|
||||
($v->{branchname} // 'a') .
|
||||
($v->{branchversion} // '0.0.0');
|
||||
return $ret
|
||||
}
|
||||
|
||||
#print vsStringDebug($_), "\n" for @oldVersions;
|
||||
|
||||
#print " << $_->[1] >>\n" for @keep;
|
||||
|
||||
my @versionsOrig = ();
|
||||
if('-' eq $par{vcslog}) {
|
||||
@versionsOrig = <STDIN>;
|
||||
chomp for @versionsOrig
|
||||
} else {
|
||||
@versionsOrig = split /\n/, `$par{vcslog}`;
|
||||
}
|
||||
my @versions = ();
|
||||
for my $v(@versionsOrig) {
|
||||
if($v=~m#^(.*?\S)\s*::::\s*(.*?)\s*::::\s*(.*)#) {
|
||||
push @versions, {
|
||||
hash => $1,
|
||||
meta => $2,
|
||||
subject => $3
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#print Data::Dumper::Dumper(\@versions);
|
||||
|
||||
my @change = ();
|
||||
my $tag = undef;
|
||||
|
||||
my @versionPast = ();
|
||||
|
||||
VERSION: for my $v(@versions) {
|
||||
#if($v->{meta}=~m#tag\s*:\s*\Q$par{kind}\E(.*)\)#) {
|
||||
# $tag=$1;
|
||||
# last VERSION
|
||||
#}
|
||||
if($v->{meta}=~m#tag\s*:\s*([vtd]b?[0-9\.]+(?:-.*)?)\)#) {
|
||||
$v->{version} = $1;
|
||||
push @versionPast, $v->{version}
|
||||
}
|
||||
next if $v->{subject}=~m#^\s*(?:Merge (?:branch|remote)|Revert )#;
|
||||
if($v->{subject}=~m#^\s*([a-z]+)\s*(!?)\s*#) {
|
||||
my ($type, $break) = ($1, $2);
|
||||
if(exists $typeReact{$type}) {
|
||||
my $react = $typeReact{$type};
|
||||
next VERSION if 'null' eq $react;
|
||||
my %h = %$v;
|
||||
$h{react} = $react;
|
||||
push @change, \%h
|
||||
} else {
|
||||
warn "$0: cannot react on commit message '$v->{subject}', type '$type' unknown\n" if $par{$v};
|
||||
}
|
||||
} else {
|
||||
warn "$0: commit message not parseable: $v->{subject}\n" if $par{$v};
|
||||
}
|
||||
}
|
||||
|
||||
#$tag = parseVersion($tag);
|
||||
|
||||
for my $r(reverse @change) {
|
||||
if('major' eq $r->{react}) {
|
||||
$tag->{major}++;
|
||||
$tag->{minor}=0;
|
||||
$tag->{patch}=0;
|
||||
$tag->{subpatch}=undef;
|
||||
} elsif('minor' eq $r->{react}) {
|
||||
$tag->{minor}++;
|
||||
$tag->{patch}=0;
|
||||
$tag->{subpatch}=undef;
|
||||
} elsif('patch' eq $r->{react}) {
|
||||
$tag->{patch}++;
|
||||
$tag->{subpatch}=undef;
|
||||
} else {
|
||||
die "$0: Cannot perform modification '$r->{react}' (probably internal error)"
|
||||
}
|
||||
}
|
||||
|
||||
#print Data::Dumper::Dumper(\@change, $tag);
|
||||
#for my $c(@change) {
|
||||
# print "==\n";
|
||||
# for my $k(sort keys %$c) {
|
||||
# print " $k: $c->{$k}\n"
|
||||
# }
|
||||
# print "\n"
|
||||
#}
|
||||
#
|
||||
#print "\n";
|
||||
#for my $v(@versionPast) {
|
||||
# my $vv = vsStringDebug(parseVersion($v));
|
||||
# print "VERSION $v --> $vv\n"
|
||||
#}
|
||||
|
||||
my @allVersions = split /\n/, `$par{vcstags}`;
|
||||
|
||||
my @sortAll = sort {vsCompare($b, $a)} @allVersions;
|
||||
my @sortSee = sort {vsCompare($b, $a)} @versionPast;
|
||||
#print "all: $sortAll[0] -- see: $sortSee[0]\n";
|
||||
#
|
||||
#print vsString($tag), "\n";
|
||||
|
||||
my $mainVersion = 'v' eq $par{kind};
|
||||
|
||||
my $highStart = $mainVersion ? $sortAll[0] : $sortSee[0];
|
||||
my $highSee = $sortSee[0];
|
||||
my %reactCollect = ();
|
||||
SEARCHVERSION: for my $v(@versions) {
|
||||
next unless $v->{version};
|
||||
next unless $v->{react};
|
||||
$reactCollect{$v->{react}} = 1;
|
||||
if($highSee eq $v->{version}) {
|
||||
last SEARCHVERSION;
|
||||
}
|
||||
}
|
||||
|
||||
sub justVersionInc {
|
||||
my ($v, $react) = @_;
|
||||
my $vv = parseVersion($v);
|
||||
$vv->{patch}++ if $react->{patch};
|
||||
do {$vv->{minor}++; $vv->{patch}=0} if $react->{minor};
|
||||
do {$vv->{major}++; $vv->{minor}=0; $vv->{patch}=0} if $react->{major};
|
||||
return vsJustVersion($vv);
|
||||
}
|
||||
|
||||
my $newVersion = undef;
|
||||
|
||||
if($mainVersion) {
|
||||
$newVersion = "v" . justVersionInc($highStart, \%reactCollect);
|
||||
} else {
|
||||
my $v = parseVersion($highStart);
|
||||
if(exists $v->{branchname}) {
|
||||
$v->{branchversion} = justVersionInc($v->{branchversion} // '0.0.0', \%reactCollect);
|
||||
} else {
|
||||
$v->{branchname} = 'a';
|
||||
$v->{branchversion} = '0.0.0';
|
||||
}
|
||||
$newVersion = vsTestVersion($v);
|
||||
}
|
||||
|
||||
my %allVersions = ();
|
||||
for(@allVersions) {
|
||||
$allVersions{$_} = 1
|
||||
}
|
||||
while(exists $allVersions{$newVersion}) {
|
||||
if($mainVersion) {
|
||||
die "$0: probably internal error (collision in main version)\n"
|
||||
}
|
||||
my $v = parseVersion($newVersion);
|
||||
$v->{branchname} //= 'a';
|
||||
$v->{branchname}++;
|
||||
$newVersion = vsTestVersion($v);
|
||||
}
|
||||
|
||||
print "$newVersion\n";
|
||||
|
||||
Reference in New Issue
Block a user