checkbox-dev team mailing list archive
-
checkbox-dev team
-
Mailing list archive
-
Message #00052
[PATCH] support: add cut-release
This patch adds a support script 'cut-release'. The script can prepare
tagged branches of all the major python components of lp:checkbox
Signed-off-by: Zygmunt Krynicki <zygmunt.krynicki@xxxxxxxxxxxxx>
---
support/cut-release | 528 ++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 528 insertions(+)
create mode 100755 support/cut-release
diff --git a/support/cut-release b/support/cut-release
new file mode 100755
index 0000000..28250ca
--- /dev/null
+++ b/support/cut-release
@@ -0,0 +1,528 @@
+#!/bin/sh
+# This file is part of Checkbox.
+#
+# Copyright 2014 Canonical Ltd.
+# Written by:
+# Zygmunt Krynicki <zygmunt.krynicki@xxxxxxxxxxxxx>
+#
+# Checkbox is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 3,
+# as published by the Free Software Foundation.
+#
+# Checkbox is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Checkbox. If not, see <http://www.gnu.org/licenses/>.
+
+# Helper script to "cut" releases
+# ===============================
+#
+# This script is indented to run in a directory with lp:checkbox checked-out
+# (with bzr) as 'trunk'. Every change made by this script is local. The
+# resulting branches are intended to be proposed back into trunk and merged
+# with the CI system.
+
+
+assert() {
+ if ! "$@"; then
+ echo "E: assertion failed: $@" >&2
+ exit 1
+ fi
+}
+
+
+pep386_parse() {
+ # Parse a human friendly version string into PEP386 canonical version
+ #
+ # Arguments:
+ # 1 (version):
+ # The human-friendly version string
+ # Return:
+ # canonical representation of that version
+ if [ -z "$1" ]; then
+ echo "E: pep386_parse: missing argument: version">&2
+ exit 1
+ fi
+ echo "D: parsing version: $1" >&2
+ local version=$(echo "$1" | awk -- '{ gsub(/a/, "alpha"); gsub(/b/, "beta"); gsub(/c|rc/, "candidate"); gsub(/[a-z]+/, ".&."); print; }')
+ echo "D: after awk processing: $version" >&2
+ local major=
+ local minor=
+ local micro=
+ local releaselevel=final
+ local serial=
+ local rest=$version
+ local next=
+ while test -n "$rest"; do
+ if echo "$rest" | grep -F . -q; then
+ next=$(echo "$rest" | cut -d . -f 1)
+ rest=$(echo "$rest" | cut -d . -f 2-)
+ else
+ next="$rest"
+ rest=''
+ fi
+ case "$next" in
+ dev|alpha|beta|candidate|final)
+ releaselevel=$next
+ test -z "$major" && major=0
+ test -z "$minor" && minor=0
+ test -z "$micro" && micro=0
+ ;;
+ *)
+ if [ -z "$major" ]; then
+ major=$next
+ elif [ -z "$minor" ]; then
+ minor=$next
+ elif [ -z "$micro" ]; then
+ micro=$next
+ elif [ -z "$serial" ]; then
+ serial=$next
+ fi
+ ;;
+ esac
+ done
+ test -z "$major" && major=0
+ test -z "$minor" && minor=0
+ test -z "$micro" && micro=0
+ test -z "$serial" && serial=0
+ echo "D: after shell split/reassembly: $major.$minor.$micro.$releaselevel.$serial" >&2
+ echo "$major.$minor.$micro.$releaselevel.$serial"
+}
+
+
+pep386_unparse() {
+ # Reverse the pep386_parse() operation
+ #
+ # Arguments:
+ # 1 (canonical_version):
+ # The canonical PEP386 version string
+ # Return:
+ # human friendly version string without needless zeros, etc
+ if [ -z "$1" ]; then
+ echo "E: pep386_parse: missing argument: canonical_version">&2
+ exit 1
+ fi
+ echo "D: unparsing version: $1" >&2
+ local major=$(echo "$1" | cut -d . -f 1)
+ local minor=$(echo "$1" | cut -d . -f 2)
+ local micro=$(echo "$1" | cut -d . -f 3)
+ local releaselevel=$(echo "$1" | cut -d . -f 4)
+ local serial=$(echo "$1" | cut -d . -f 5)
+ local version="$major.$minor"
+ if [ "$micro" -ne 0 ]; then
+ version="$version.$micro"
+ fi
+ case "$releaselevel" in
+ dev)
+ if [ "$serial" -ne 0 ]; then
+ version="${version}.dev.${serial}"
+ else
+ version="${version}.dev"
+ fi
+ ;;
+ alpha)
+ version="${version}a${serial}"
+ ;;
+ beta)
+ version="${version}b${serial}"
+ ;;
+ candidate)
+ version="${version}c${serial}"
+ ;;
+ final)
+ ;;
+ esac
+ echo "D: unparsing: $version" >&2
+ echo "$version"
+}
+
+
+pep386_as_tuple() {
+ # Convert PEP386 version to python tuple sometimes found in __version__
+ #
+ # Arguments:
+ # 1 (canonical_version):
+ # The canonical PEP386 version string
+ # Return:
+ # Python tuple with the same data
+ if [ -z "$1" ]; then
+ echo "E: pep386_as_tuple: missing argument: canonical_version">&2
+ exit 1
+ fi
+ echo "D: canonical version: $1" >&2
+ local major=$(echo "$1" | cut -d . -f 1)
+ local minor=$(echo "$1" | cut -d . -f 2)
+ local micro=$(echo "$1" | cut -d . -f 3)
+ local releaselevel=$(echo "$1" | cut -d . -f 4)
+ local serial=$(echo "$1" | cut -d . -f 5)
+ local version="$major"
+ local tuple="($major, $minor, $micro, \"$releaselevel\", $serial)"
+ echo "D: python tuple: $tuple" >&2
+ echo "$tuple"
+}
+
+
+pep386_attr() {
+ # Access fields of pep386 canonical version
+ #
+ # Arguments:
+ # 1 (canonical_version):
+ # The canonical PEP386 version string
+ # 2+ (operation):
+ # Operations to perform. For getters they have a form of --field where
+ # field is one of the fields defined by the canonical version (major,
+ # minor, micro, releaselevel, serial). For setters the syntax is
+ # --field=value. Any number of setters can be used together. The first
+ # getter terminates the procedure.
+ #
+ # Return:
+ # If a getter was used, the field that was referred to. Otherwise the
+ # canonical version is returned (after being modified by any of the
+ # setters).
+ if [ -z "$1" ]; then
+ echo "E: pep386_attr: missing argument: canonical_version">&2
+ exit 1
+ fi
+ local major=$(echo "$1" | cut -d . -f 1)
+ local minor=$(echo "$1" | cut -d . -f 2)
+ local micro=$(echo "$1" | cut -d . -f 3)
+ local releaselevel=$(echo "$1" | cut -d . -f 4)
+ local serial=$(echo "$1" | cut -d . -f 5)
+ shift
+ while test -n "$1"; do
+ case "$1" in
+ --major)
+ echo "$major"
+ return
+ ;;
+ --major=*)
+ major=$(echo "$1" | cut -d = -f 2)
+ ;;
+ --minor)
+ echo "$minor"
+ return
+ ;;
+ --minor=*)
+ minor=$(echo "$1" | cut -d = -f 2)
+ ;;
+ --micro)
+ echo "$micro"
+ return
+ ;;
+ --micro=*)
+ micro=$(echo "$1" | cut -d = -f 2)
+ ;;
+ --releaselevel)
+ echo "$releaselevel"
+ return
+ ;;
+ --releaselevel=*)
+ releaselevel=$(echo "$1" | cut -d = -f 2)
+ ;;
+ --serial)
+ echo "$serial"
+ return
+ ;;
+ --serial=*)
+ serial=$(echo "$1" | cut -d = -f 2)
+ ;;
+ *)
+ echo "E: pep386_attr: bad argument: $1" >&2
+ exit 1
+ ;;
+ esac
+ shift
+ done
+ echo "$major.$minor.$micro.$releaselevel.$serial"
+}
+
+
+release_component() {
+ if [ -z "$1" ]; then
+ echo "E: release_component: missing argument: component_path" >&2
+ exit 1
+ fi
+ # path to the top-level directory of the component relative to tree root
+ local component_path=$1; shift
+
+ # Ensure that trunk exist
+ assert test -d trunk
+
+ # Interrogate name/version
+ local name=$(trunk/$component_path/setup.py --name)
+
+ # name of the component's top-level python package
+ local component_python_package=$name
+ # name of the component as it shows up in the commit message
+ local component_commit_name=$name
+ # name of the component as it shows up in tags
+ local component_tag_name=$name
+ # release policy (micro/minor/major/rc)
+ local bump_version=
+ local bump_level=
+
+ # Parse keyword arguments
+ while test -n "$1"; do
+ local value=$(echo $1 | cut -d = -f 2-)
+ case "$1" in
+ --python-pkg=*)
+ component_python_package=$value
+ ;;
+ --tag-name=*)
+ component_tag_name=$value
+ ;;
+ --commit-name=*)
+ component_commit_name=$value
+ ;;
+ --bump-version=*)
+ bump_version=$value
+ ;;
+ --bump-level=*)
+ bump_level=$value
+ ;;
+ *)
+ echo "E: release_component: bad argument: $1" >&2
+ exit 1
+ ;;
+ esac
+ shift
+ done
+
+ echo "I: preparing release candidate of $name"
+ echo "D: inspecting current version..."
+ local version=$(trunk/$component_path/setup.py --version)
+ echo "I: current version is $version"
+
+ # Parse version
+ local canonical_version=$(pep386_parse "$version")
+ echo "D: canonical version is $canonical_version"
+
+ # Compute next version (each release increments version)
+ echo "D: computing next version..."
+ echo "D: bump_level: $bump_level"
+ echo "D: bump_version: $bump_version"
+ local canonical_next_version=$canonical_version
+ case "$bump_version" in
+ major)
+ canonical_next_version=$(pep386_attr "$canonical_next_version" --major=$(expr $(pep386_attr "$canonical_next_version" --major) + 1) --minor=0 --micro=0)
+ ;;
+ minor)
+ canonical_next_version=$(pep386_attr "$canonical_next_version" --minor=$(expr $(pep386_attr "$canonical_next_version" --minor) + 1) --micro=0)
+ ;;
+ micro)
+ canonical_next_version=$(pep386_attr "$canonical_next_version" --micro=$(expr $(pep386_attr "$canonical_next_version" --micro) + 1))
+ ;;
+ esac
+ case "$bump_level" in
+ dev)
+ canonical_next_version=$(pep386_attr "$canonical_next_version" --releaselevel=dev --serial=0)
+ ;;
+ alpha)
+ canonical_next_version=$(pep386_attr "$canonical_next_version" --releaselevel=alpha --serial=1)
+ ;;
+ beta)
+ canonical_next_version=$(pep386_attr "$canonical_next_version" --releaselevel=beta --serial=1)
+ ;;
+ candidate)
+ canonical_next_version=$(pep386_attr "$canonical_next_version" --releaselevel=candidate --serial=1)
+ ;;
+ final)
+ canonical_next_version=$(pep386_attr "$canonical_next_version" --releaselevel=final --serial=0)
+ ;;
+ next-level)
+ case "$(pep386_attr \"$canonical_version\" --releaselevel)" in
+ dev)
+ canonical_next_version=$(pep386_attr "$canonical_next_version" --releaselevel=alpha --serial=1)
+ ;;
+ alpha)
+ canonical_next_version=$(pep386_attr "$canonical_next_version" --releaselevel=beta --serial=1)
+ ;;
+ beta)
+ canonical_next_version=$(pep386_attr "$canonical_next_version" --releaselevel=candidate --serial=1)
+ ;;
+ candidate)
+ canonical_next_version=$(pep386_attr "$canonical_next_version" --releaselevel=final --serial=0)
+ ;;
+ esac
+ ;;
+ next-serial)
+ canonical_next_version=$(pep386_attr "$canonical_next_version" --serial=$(expr $(pep386_attr "$canonical_next_version" --serial) + 1))
+ ;;
+ esac
+ echo "D: canonical next version is $canonical_next_version"
+ local next_version=$(pep386_unparse "$canonical_next_version")
+ echo "I: next version is $next_version"
+
+ if [ -z "$dry_run" ]; then
+ # Create a branch for the new release
+ echo "D: preparing release branch..."
+ local release_dir=$name-$next_version
+ assert test ! -e $release_dir
+ echo "I: branching trunk to ${release_dir}..."
+ bzr branch trunk "$release_dir" --quiet
+
+ # Apply the new version
+ echo "I: patching setup.py..."
+ sed -i -e "s!$version!$next_version!g" $release_dir/$component_path/setup.py
+ echo "I: patching $name/__init__.py..."
+ sed -i -e "s!__version__ = \(.*\)!__version__ = $(pep386_as_tuple $canonical_next_version)!g" $release_dir/$component_path/$component_python_package/__init__.py
+
+ # Commit and tag
+ echo "I: commiting version bump"
+ # NOTE: no way to bzr commit has no '-d'
+ (cd $release_dir && bzr commit -m "$component_commit_name: increment version to $next_version" --quiet)
+ bzr tag -d $release_dir "$component_tag_name-v$next_version" --quiet
+ echo "I: component $name is ready to be built in $release_dir"
+ else
+ echo "I: not doing anything in dry-run mode"
+ fi
+}
+
+
+help() {
+ echo "usage: cut-release [--debug] (checkbox|checkbox-ng|plainbox) [options]" >&2
+ echo
+ echo "This script is indented to run in a directory with lp:checkbox"
+ echo "branched (with bzr) as 'trunk'. Every change made is local."
+ echo
+ echo "component:"
+ echo " checkbox release checkbox-old"
+ echo " checkbox-ng release checkbox-ng"
+ echo " plainbox release plainbox"
+ echo
+ echo "general options"
+ echo " -n|--dry-run"
+ echo " don't prepare branch with version changes"
+ echo " --debug"
+ echo " display additional diagnostic messages"
+ echo
+ echo "component version options"
+ echo " --bump-version={major,minor,micro}"
+ echo " increment version component"
+ echo " --major|--minor|--micro"
+ echo " same as --bump-version=..."
+ echo
+ echo "component release level options"
+ echo " --bump-level={dev,alpha,beta,candidate,final,next-level,next-serial}"
+ echo " set/increment release level and serial"
+ echo " --dev|--alpha|--beta|--candidate|--final"
+ echo " same as --bump-level=..."
+ echo " --rc"
+ echo " same as --candidate"
+ echo
+ echo "NOTE: If any version change options are used without --bump-level"
+ echo " then the program behaves as if --bump-level=dev was passed"
+ echo "NOTE: Using: --bump-level=next-level resets serial to 1"
+ echo " (except for final, where it must be 0)"
+ echo "NOTE: Using: --bump-level=next-level never goes past final"
+ echo
+ echo "WARNING: Using: --bump-level=next-serial just increments the serial number"
+ echo " That is *incorrect* if current level is 'dev' or 'final'"
+}
+
+
+main() {
+ local component=
+ local bump_version=
+ local bump_level=
+ local dry_run=
+ # Parse keyword arguments
+ while test -n "$1"; do
+ if echo "$1" | grep -F -q =; then
+ local value=$(echo $1 | cut -d = -f 2-)
+ else
+ local value=
+ fi
+ case $1 in
+ checkbox)
+ component=checkbox
+ ;;
+ checkbox-ng)
+ component=checkbox-ng
+ ;;
+ plainbox)
+ component=plainbox
+ ;;
+ -n|--dry-run)
+ dry_run=1
+ ;;
+ --bump-version=*)
+ bump_version=$value
+ ;;
+ --major)
+ bump_version=major
+ ;;
+ --minor)
+ bump_version=minor
+ ;;
+ --micro)
+ bump_version=micro
+ ;;
+ --bump-level=*)
+ bump_level=$value
+ ;;
+ --dev)
+ bump_level=dev
+ ;;
+ --alpha)
+ bump_level=alpha
+ ;;
+ --beta)
+ bump_level=beta
+ ;;
+ --candidate|--rc)
+ bump_level=candidate
+ ;;
+ --final)
+ bump_level=final
+ ;;
+ --help)
+ help
+ exit 0
+ ;;
+ *)
+ echo "E: cut-release: bad argument: $1" >&2
+ exit 1
+ ;;
+ esac
+ shift
+ done
+ if [ -z "$component" ]; then
+ echo "E: cut-release: component name required"
+ echo
+ help
+ exit 1
+ fi
+ if [ -z "$bump_level" ] && [ -z "$bump_version" ]; then
+ echo "E: cut-release: expected at least one of --bump-level=... or --bump-version=..."
+ echo
+ help
+ exit 1
+ fi
+ if [ -n "$bump_version" ] && [ -z "$bump_level" ]; then
+ echo "I: assuming --bump-level=dev"
+ bump_level=dev
+ fi
+ case "$component" in
+ checkbox)
+ release_component checkbox-old --tag-name=checkbox --commit-name=checkbox-old --python-pkg=checkbox --bump-version=$bump_version --bump-level=$bump_level
+ ;;
+ checkbox-ng)
+ release_component checkbox-ng --python-pkg=checkbox_ng --bump-version=$bump_version --bump-level=$bump_level
+ ;;
+ plainbox)
+ release_component plainbox --bump-version=$bump_version --bump-level=$bump_level
+ ;;
+ esac
+}
+
+
+if [ "$1" = --debug ]; then
+ shift
+ main "$@" 2>&1
+else
+ main "$@" 2>&1 | grep -E -v "^D:"
+fi
--
1.9.rc1