2018-02-05 13:38:02 +01:00
#!/usr/bin/env perl
2014-02-20 02:05:13 +01:00
# creates a new release
# Copyright (C) 2014 Jürgen E. Fischer <jef@norbit.de>
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
use strict ;
use warnings ;
use Getopt::Long ;
use Pod::Usage ;
2018-06-29 10:48:52 +02:00
use POSIX ;
2014-02-20 02:05:13 +01:00
2015-02-22 11:48:18 +01:00
my $ dryrun ;
sub updateCMakeLists ($$$$) {
my ( $ major , $ minor , $ patch , $ release ) = @ _ ;
if ( $ dryrun ) {
print "DRYRUN: Update CMakeLists.txt to $major.$minor.$patch ($release)\n" ;
return ;
}
rename "CMakeLists.txt" , "CMakeLists.txt.orig" or die "cannot rename CMakeLists.txt: $!" ;
open I , "CMakeLists.txt.orig" ;
open O , ">CMakeLists.txt" or die "cannot create CMakeLists.txt: $!" ;
while ( <I> ) {
2020-11-11 23:02:08 +01:00
s/SET\(CPACK_PACKAGE_VERSION_MAJOR "(\d+)"\)/set(CPACK_PACKAGE_VERSION_MAJOR "$major")/i ;
s/SET\(CPACK_PACKAGE_VERSION_MINOR "(\d+)"\)/set(CPACK_PACKAGE_VERSION_MINOR "$minor")/i ;
s/SET\(CPACK_PACKAGE_VERSION_PATCH "(\d+)"\)/set(CPACK_PACKAGE_VERSION_PATCH "$patch")/i ;
s/SET\(RELEASE_NAME "(.+)"\)/set(RELEASE_NAME "$release")/i ;
2015-02-22 11:48:18 +01:00
print O ;
}
close O ;
close I ;
}
sub run ($$) {
my ( $ cmd , $ errmsg ) = @ _ ;
if ( $ dryrun ) {
print "DRYRUN: $cmd\n" ;
} elsif ( system ( $ cmd ) != 0 ) {
print STDERR "error: $errmsg [$?]" ;
exit 1 ;
}
}
my $ newreleasename ;
2014-02-20 02:05:13 +01:00
my $ help ;
2015-02-22 11:48:18 +01:00
my $ domajor ;
my $ dominor ;
my $ dopoint ;
2015-03-01 22:00:46 +01:00
my $ doltr = 0 ;
2016-07-02 18:01:10 +02:00
my $ dopremajor = 0 ;
my $ skipts = 0 ;
2014-02-20 02:05:13 +01:00
my $ result = GetOptions (
2015-02-22 11:48:18 +01:00
"major" = > \ $ domajor ,
"minor" = > \ $ dominor ,
2020-05-17 09:17:35 +02:00
"point:i" = > \ $ dopoint ,
2015-02-22 11:48:18 +01:00
"releasename=s" = > \ $ newreleasename ,
"help" = > \ $ help ,
2015-03-01 22:00:46 +01:00
"ltr" = > \ $ doltr ,
2015-02-22 11:48:18 +01:00
"dryrun" = > \ $ dryrun ,
2016-07-02 18:01:10 +02:00
"premajor" = > \ $ dopremajor ,
"skipts" = > \ $ skipts ,
2014-02-20 02:05:13 +01:00
) ;
2015-02-22 11:48:18 +01:00
pod2usage ( 1 ) if $ help ;
2014-02-20 02:05:13 +01:00
2015-02-22 11:48:18 +01:00
my $ i = 0 ;
$ i + + if defined $ domajor ;
$ i + + if defined $ dominor ;
$ i + + if defined $ dopoint ;
pod2usage ( "Exactly one of -major, -minor or -point expected" ) if $ i != 1 ;
2020-05-17 09:17:35 +02:00
pod2usage ( "Release name for major and minor releases expected" ) if ! defined $ dopoint && ! defined $ newreleasename ;
2016-07-02 18:01:10 +02:00
pod2usage ( "Pre-major releases can only be minor releases" ) if $ dopremajor && ! $ dominor ;
2015-02-22 11:48:18 +01:00
pod2usage ( "No CMakeLists.txt in current directory" ) unless - r "CMakeLists.txt" ;
2014-02-20 02:05:13 +01:00
my $ major ;
my $ minor ;
2015-02-22 11:48:18 +01:00
my $ patch ;
my $ releasename ;
2014-02-20 02:05:13 +01:00
open F , "CMakeLists.txt" ;
while ( <F> ) {
2020-11-11 23:02:08 +01:00
if ( /SET\(CPACK_PACKAGE_VERSION_MAJOR "(\d+)"\)/i ) {
2014-02-20 02:05:13 +01:00
$ major = $ 1 ;
2020-11-11 23:02:08 +01:00
} elsif ( /SET\(CPACK_PACKAGE_VERSION_MINOR "(\d+)"\)/i ) {
2014-02-20 02:05:13 +01:00
$ minor = $ 1 ;
2020-11-11 23:02:08 +01:00
} elsif ( /SET\(CPACK_PACKAGE_VERSION_PATCH "(\d+)"\)/i ) {
2015-02-22 11:48:18 +01:00
$ patch = $ 1 ;
2020-11-11 23:02:08 +01:00
} elsif ( /SET\(RELEASE_NAME \"(.*)\"\)/i ) {
2015-02-22 11:48:18 +01:00
$ releasename = $ 1 ;
2014-02-20 02:05:13 +01:00
}
}
close F ;
2015-02-22 11:48:18 +01:00
my $ branch = `git rev-parse --abbrev-ref HEAD 2>/dev/null` ;
$ branch =~ s/\s+$// ;
pod2usage ( "Not on a branch" ) unless $ branch ;
2016-07-02 18:01:10 +02:00
pod2usage ( "Current branch is $branch. master or a release branch expected" ) if $ branch !~ /^(master.*|release-(\d+)_(\d+))$/ ;
2016-10-20 18:06:32 +02:00
pod2usage ( "Version mismatch ($2.$3) in branch $branch vs. $major.$minor in CMakeLists.txt)" ) if $ branch !~ /master/ && ( $ major != $ 2 || $ minor != $ 3 ) ;
pod2usage ( "Release name Master expected on master branch" ) if $ branch =~ /^master/ && $ releasename ne "Master" ;
2015-02-22 11:48:18 +01:00
2016-07-02 18:01:10 +02:00
if ( $ branch =~ /^master.*/ ) {
2020-05-17 09:17:35 +02:00
pod2usage ( "No point releases on master branch" ) if defined $ dopoint ;
2015-02-22 11:48:18 +01:00
pod2usage ( "No new release name for major/minor release" ) unless $ newreleasename || $ newreleasename eq $ releasename ;
2014-02-20 02:05:13 +01:00
} else {
2020-05-17 09:17:35 +02:00
pod2usage ( "Only point releases on release branches" ) if ! defined $ dopoint ;
2015-02-22 11:48:18 +01:00
pod2usage ( "New release names only for new minor releases" ) if $ newreleasename ;
$ newreleasename = $ releasename ;
2014-02-20 02:05:13 +01:00
}
2015-02-22 11:48:18 +01:00
my $ newmajor ;
my $ newminor ;
my $ newpatch ;
if ( $ domajor ) {
$ newmajor = $ major + 1 ;
$ newminor = 0 ;
$ newpatch = 0 ;
} elsif ( $ dominor ) {
$ newmajor = $ major ;
2014-02-20 02:05:13 +01:00
$ newminor = $ minor + 1 ;
2015-02-22 11:48:18 +01:00
$ newpatch = 0 ;
2020-05-17 09:17:35 +02:00
} elsif ( defined $ dopoint ) {
2015-02-22 11:48:18 +01:00
$ newmajor = $ major ;
$ newminor = $ minor ;
2020-05-17 09:17:35 +02:00
pod2usage ( "Given point release number <= $patch" ) if $ dopoint && $ dopoint <= $ patch ;
$ newpatch = $ dopoint ? $ dopoint : $ patch + 1 ;
2015-02-22 11:48:18 +01:00
} else {
pod2usage ( "No version change" ) ;
2014-02-20 02:05:13 +01:00
}
2016-02-26 14:33:07 +01:00
my $ splashwidth ;
2020-05-17 09:17:35 +02:00
unless ( defined $ dopoint ) {
2023-03-31 13:14:17 +02:00
pod2usage ( "Splash images/splash/splash-${newmajor}.${newminor}rc.png not found" ) unless - r "images/splash/splash-${newmajor}.${newminor}rc.png" ;
} elsif ( $ newpatch == 1 ) {
pod2usage ( "Splash images/splash/splash-${newmajor}.${newminor}.png not found" ) unless - r "images/splash/splash-${newmajor}.${newminor}.png" ;
} elsif ( $ newpatch == 4 ) { # TODO handle EPRs
2024-02-23 13:00:29 +01:00
if ( system ( "git tag -l | grep -q '^ltr-${newmajor}_${newminor}'" ) == 0 ) {
2023-03-31 13:14:17 +02:00
pod2usage ( "Splash images/splash/splash-${newmajor}.${newminor}ltr.png not found" ) unless - r "images/splash/splash-${newmajor}.${newminor}ltr.png" ;
}
2015-03-09 20:11:35 +01:00
}
2014-02-20 02:05:13 +01:00
print "Last pull rebase...\n" ;
2015-02-22 11:48:18 +01:00
run ( "git pull --rebase" , "git pull rebase failed" ) ;
2014-02-20 02:05:13 +01:00
my $ release = "$newmajor.$newminor" ;
2015-02-22 11:48:18 +01:00
my $ version = "$release.$newpatch" ;
2014-02-20 02:05:13 +01:00
my $ relbranch = "release-${newmajor}_${newminor}" ;
2015-03-01 22:00:46 +01:00
my $ ltrtag = $ doltr ? "ltr-${newmajor}_${newminor}" : "" ;
2015-02-22 11:48:18 +01:00
my $ reltag = "final-${newmajor}_${newminor}_${newpatch}" ;
2014-02-20 02:05:13 +01:00
2019-06-25 20:42:25 +02:00
unless ( $ skipts ) {
print "Pulling transifex translations...\n" ;
2025-02-20 19:35:21 +01:00
run ( "CONSIDER_TS_DROP_FATAL=1 scripts/pull_ts.sh" , "pull_ts.sh failed" ) ;
2019-06-25 20:42:25 +02:00
run ( "git add i18n/*.ts" , "adding translations failed" ) ;
run ( "git commit -n -a -m \"translation update for $version from transifex\"" , "could not commit translation updates" ) ;
} else {
print "TRANSIFEX UPDATE SKIPPED!\n" ;
2015-02-22 11:48:18 +01:00
}
2015-02-20 08:42:46 +01:00
2015-02-20 12:41:24 +01:00
print "Updating changelog...\n" ;
2015-02-22 11:48:18 +01:00
run ( "scripts/create_changelog.sh" , "create_changelog.sh failed" ) ;
2018-09-25 01:26:30 +02:00
run ( "perl -i -pe 's#<releases>#<releases>\n <release version=\"$newmajor.$newminor.$newpatch\" date=\"" . strftime ( "%Y-%m-%d" , localtime ) . "\" />#' linux/org.qgis.qgis.appdata.xml.in" , "appdata update failed" ) ;
2014-02-20 02:05:13 +01:00
2020-05-17 09:17:35 +02:00
unless ( defined $ dopoint ) {
2023-03-03 13:33:50 +01:00
run ( "scripts/update_news.pl $newmajor.$newminor \"$newreleasename\"" , "could not update news" ) if $ major > 2 || ( $ major == 2 && $ minor > 14 ) ;
2016-02-24 21:15:34 +01:00
2017-02-20 17:55:03 +01:00
run ( "git commit -n -a -m \"changelog and news update for $release\"" , "could not commit changelog and news update" ) ;
2014-06-20 11:40:35 +02:00
2015-02-22 11:48:18 +01:00
print "Creating and checking out branch...\n" ;
run ( "git checkout -b $relbranch" , "git checkout release branch failed" ) ;
2014-06-20 11:40:35 +02:00
}
2015-06-26 14:12:54 +02:00
updateCMakeLists ( $ newmajor , $ newminor , $ newpatch , $ newreleasename ) ;
2014-06-20 11:40:35 +02:00
2015-02-22 11:48:18 +01:00
print "Updating version...\n" ;
run ( "dch -r ''" , "dch failed" ) ;
run ( "dch --newversion $version 'Release of $version'" , "dch failed" ) ;
run ( "cp debian/changelog /tmp" , "backup changelog failed" ) ;
2020-05-17 09:17:35 +02:00
unless ( defined $ dopoint ) {
2020-06-10 11:56:50 +02:00
run ( "perl -i -pe 's/qgis-dev-deps/qgis-ltr-deps/;' INSTALL.md" , "could not update osgeo4w deps package" ) if $ doltr ;
run ( "perl -i -pe 's/qgis-dev-deps/qgis-rel-deps/;' INSTALL.md" , "could not update osgeo4w deps package" ) unless $ doltr ;
2023-03-04 13:38:53 +01:00
run ( "cp -v images/splash/splash-${newmajor}.${newminor}rc.png images/splash/splash.png" , "splash png switch failed" ) ;
2023-06-23 18:27:55 +02:00
run ( "sed -i -e 's/r:qgis-application/r:$relbranch-qgis-application/' .tx/config" , "update of transifex update failed" ) ;
2023-03-03 13:33:50 +01:00
run ( "git commit -n -a -m \"Release of $release ($newreleasename)\"" , "release commit failed" ) ;
2015-03-01 22:00:46 +01:00
run ( "git tag $reltag -m 'Version $release'" , "release tag failed" ) ;
2023-06-23 18:27:55 +02:00
run ( "for i in \$(seq 20); do tx push -s && exit 0; echo \"Retry \$i/20...\"; done; exit 1" , "push translation for $relbranch branch" ) ;
2014-06-20 11:40:35 +02:00
} else {
2023-03-04 13:38:53 +01:00
if ( $ newpatch == 1 ) {
run ( "cp -v images/splash/splash-${newmajor}.${newminor}.png images/splash/splash.png" , "splash png switch failed" ) ;
} elsif ( $ newpatch == 4 ) { # TODO handle EPRs
2024-02-22 21:37:34 +01:00
if ( system ( "git tag -l | grep -q '^ltr-${newmajor}_${newminor}'" ) == 0 ) {
2023-03-04 13:38:53 +01:00
run ( "cp -v images/splash/splash-${newmajor}.${newminor}ltr.png images/splash/splash.png" , "splash png switch failed" ) ;
}
}
2017-02-20 17:55:03 +01:00
run ( "git commit -n -a -m 'Release of $version'" , "release commit failed" ) ;
2015-02-22 11:48:18 +01:00
run ( "git tag $reltag -m 'Version $version'" , "tag failed" ) ;
2014-06-20 11:40:35 +02:00
}
2017-02-20 17:55:03 +01:00
run ( "git tag $ltrtag -m 'Long term release $release'" , "ltr tag failed" ) if $ doltr ;
2014-02-20 02:05:13 +01:00
print "Producing archive...\n" ;
2016-02-26 13:46:13 +01:00
run ( "git archive --format tar --prefix=qgis-$version/ $reltag | bzip2 -c >qgis-$version.tar.bz2" , "git archive failed" ) ;
2019-07-26 20:46:23 +02:00
run ( "sha256sum qgis-$version.tar.bz2 >qgis-$version.tar.bz2.sha256" , "sha256sum failed" ) ;
2015-02-22 11:48:18 +01:00
2016-07-02 18:01:10 +02:00
my @ topush ;
2020-05-17 09:17:35 +02:00
unless ( defined $ dopoint ) {
2022-10-23 12:46:09 +02:00
my $ apiv = "$newmajor.$newminor" ;
2015-02-22 11:48:18 +01:00
$ newminor + + ;
print "Updating master...\n" ;
2016-10-21 14:08:06 +02:00
run ( "git checkout $branch" , "checkout master failed" ) ;
2016-07-02 18:01:10 +02:00
if ( $ dopremajor ) {
print " Creating master_$newmajor...\n" ;
run ( "git checkout -b master_$newmajor" , "checkout master_$newmajor failed" ) ;
updateCMakeLists ( $ newmajor , $ newminor , 0 , "Master" ) ;
run ( "cp /tmp/changelog debian" , "restore changelog failed" ) ;
run ( "dch -r ''" , "dch failed" ) ;
run ( "dch --newversion $newmajor.$newminor.0 'New development version $newmajor.$newminor after branch of $release'" , "dch failed" ) ;
2017-02-20 17:55:03 +01:00
run ( "git commit -n -a -m 'New development branch for interim $newmajor.x releases'" , "bump version failed" ) ;
2016-07-02 18:01:10 +02:00
push @ topush , "master_$newmajor" ;
run ( "git checkout master" , "checkout master failed" ) ;
2016-07-10 20:53:49 +02:00
$ newminor = 99 ;
2016-07-02 18:01:10 +02:00
}
2023-03-20 19:25:27 +01:00
run ( "perl -i -pe \"s#Earlier versions of the documentation are also available on the QGIS website:#\$&\\n<a href=\\\"https://qgis.org/api/$apiv\\\">$apiv" . ( $ doltr ? " (LTR)" : "" ) . "</a>,#\" doc/index.dox" , "index.dox update failed" ) ;
2022-10-23 12:46:09 +02:00
2015-02-22 11:48:18 +01:00
updateCMakeLists ( $ newmajor , $ newminor , 0 , "Master" ) ;
run ( "cp /tmp/changelog debian" , "restore changelog failed" ) ;
run ( "dch -r ''" , "dch failed" ) ;
run ( "dch --newversion $newmajor.$newminor.0 'New development version $newmajor.$newminor after branch of $release'" , "dch failed" ) ;
2017-02-20 17:55:03 +01:00
run ( "git commit -n -a -m 'Bump version to $newmajor.$newminor'" , "bump version failed" ) ;
2016-07-02 18:01:10 +02:00
2016-10-21 14:08:06 +02:00
push @ topush , $ branch ;
2015-02-22 11:48:18 +01:00
}
2014-02-20 02:05:13 +01:00
2016-07-02 18:01:10 +02:00
push @ topush , $ relbranch ;
my $ topush = join ( " " , @ topush ) ;
2014-02-20 02:05:13 +01:00
print "Push dry-run...\n" ;
2016-05-04 12:29:16 +02:00
run ( "git push -n --follow-tags origin $topush" , "push dry run failed" ) ;
2020-06-25 14:32:46 +02:00
print "Now manually push and upload the tar balls:\n\tgit push --follow-tags origin $topush\n\trsync qgis-$version.tar.bz2* ssh.qgis.org:/var/www/downloads/\n" ;
2022-03-05 19:42:42 +01:00
print "Update version-ltr.txt rewrite rule on website\n" if $ doltr ;
2022-10-23 12:46:09 +02:00
unless ( defined $ dopoint ) {
2020-06-25 14:32:46 +02:00
print "Update the versions and release name in release spreadsheet.\n" ;
print "Package and update the website afterwards.\n" ;
}
2016-07-02 18:01:10 +02:00
print "WARNING: TRANSIFEX UPDATE SKIPPED!\n" if $ skipts ;
2014-02-20 02:05:13 +01:00
= head1 NAME
release . pl - create a new release
= head1 SYNOPSIS
2020-05-17 09:17:35 +02:00
release . pl { { - major | - minor [ - premajor ] } [ - skipts ] - releasename = releasename | - point [ = version ] } [ - ltr ]
2014-02-20 02:05:13 +01:00
Options:
2015-02-22 11:48:18 +01:00
- major do a new major release
- minor do a new minor release
2020-05-17 09:17:35 +02:00
- point [ = number ] do a new point release with an optional number
2015-02-22 11:48:18 +01:00
- releasename = name new release name for master / minor release
2015-03-01 22:00:46 +01:00
- ltr new release is a long term release
2015-02-22 11:48:18 +01:00
- dryrun just echo but don ' t run any commands
2016-07-02 18:01:10 +02:00
- skipts skip transifex update
- premajor branch off a second "master" branch before
a major release
2015-03-09 20:11:35 +01:00
Major and minor releases also require a new splash screen
2023-03-04 13:38:53 +01:00
images /splash/s plash - M . Nrc . png , which should have a label
"release candidate" .
The first point release also requires a new splash screen
images /splash/s plash - M . N . png without that label .
The fourth point release of a ltr branch requires a new splash screen
images /splash/s plash - M . Nltr . png with the label " long - term
release " .
2016-07-02 18:01:10 +02:00
A pre - major minor release also produces a second branch
master_ $ currentmajor to allow more interim minor releases
while the new major version is being developed in master .
For that the minor version of the master branch leading
2020-05-17 09:17:35 +02:00
to the next major release is bumped to 99 .
2014-02-20 02:05:13 +01:00
= cut