| #!/bin/bash | |
| set -eu | |
| cd "$( dirname "${BASH_SOURCE[0]}" )/.." | |
| version= | |
| version_next= | |
| main() { | |
| local opt_auto=0 | |
| while [[ $# -gt 0 ]] ; do | |
| case $1 in | |
| -auto) | |
| opt_auto=1 | |
| ;; | |
| *) | |
| echo "Usage: $0 [-auto]" >&2 | |
| exit 0 | |
| ;; | |
| esac | |
| shift | |
| done | |
| if [[ "$opt_auto" -eq 1 ]] ; then | |
| auto_prepare_release | |
| else | |
| interactive | |
| fi | |
| } | |
| auto_prepare_release() { | |
| echo 'script/release: auto mode for CI, will check or modify version based on tag' >&2 | |
| assert_tree_clean | |
| local is_tag=0 | |
| local version_tag= | |
| if version_tag=$(git describe --candidates=0 --tags HEAD 2>/dev/null) ; then | |
| is_tag=1 | |
| version_tag=${version_tag##v} | |
| version_check "$version_tag" | |
| fi | |
| local last_tag= | |
| local version_replace= | |
| if [[ "$is_tag" -eq 0 ]] ; then | |
| last_tag=$(git tag --sort=-version:refname |head -n1) | |
| last_tag=${last_tag##v} | |
| version_replace="${last_tag}.post$(date -u +%y%m%d%H%M)" | |
| update_version "setup.py" "s/VERSION =.+/VERSION = \"$version_replace\"/" | |
| update_version "python2/httplib2/__init__.py" "s/__version__ =.+/__version__ = \"$version_replace\"/" | |
| update_version "python3/httplib2/__init__.py" "s/__version__ =.+/__version__ = \"$version_replace\"/" | |
| version_check "$version_replace" | |
| fi | |
| } | |
| interactive() { | |
| echo 'script/release: interactive mode for creating new tagged releases with human assistance' >&2 | |
| local branch="${1-$(git symbolic-ref --short HEAD)}" | |
| version="$(PYTHONPATH=$PWD/python3 python3 -c 'import httplib2; print(httplib2.__version__)')" | |
| printf "\nbranch: %s httplib2.__version__: '%s'\n" $branch $version >&2 | |
| if [[ "$branch" != "master" ]] ; then | |
| echo "Must be on master" >&2 | |
| exit 1 | |
| fi | |
| assert_tree_clean | |
| last_commit_message=$(git show --format="%s" --no-patch HEAD) | |
| expect_commit_message="v$version release" | |
| if [[ "$last_commit_message" != "$expect_commit_message" ]] ; then | |
| printf "Last commit message: '%s' expected: '%s'\n" "$last_commit_message" "$expect_commit_message" >&2 | |
| if confirm "Create release commit? [yN] " ; then | |
| create_commit | |
| elif ! confirm "Continue without proper release commit? [yN] " ; then | |
| exit 1 | |
| fi | |
| fi | |
| confirm "Continue? [yN] " || exit 1 | |
| echo "Creating tag v$version" >&2 | |
| if ! git tag "v$version" ; then | |
| echo "git tag failed " >&2 | |
| confirm "Continue still? [yN] " || exit 1 | |
| fi | |
| echo "Building package" >&2 | |
| find . -name '*.pyc' -o -name '*.pyo' -o -name '*.orig' -delete | |
| rm -rf python{2,3}/.cache | |
| rm -rf build dist | |
| local venv=./venv-release | |
| if [[ ! -d "$venv" ]] ; then | |
| virtualenv $venv | |
| $venv/bin/pip install -U check-manifest pip 'setuptools>=43.0' wheel twine | |
| fi | |
| $venv/bin/python setup.py clean --all | |
| $venv/bin/python setup.py sdist bdist_wheel | |
| $venv/bin/check-manifest || echo "FIXME check-manifest" >&2 | |
| if confirm "Upload to PyPI? Use in special situation, normally CI (Travis) will upload to PyPI. [yN] " ; then | |
| $venv/bin/twine upload dist/* || exit 1 | |
| fi | |
| git push --tags | |
| } | |
| create_commit() { | |
| echo "" >&2 | |
| echo "Plan:" >&2 | |
| echo "1. bump version" >&2 | |
| echo "2. update CHANGELOG" >&2 | |
| echo "3. commit" >&2 | |
| echo "4. run bin/release again" >&2 | |
| echo "" >&2 | |
| bump_version | |
| edit_news | |
| git diff | |
| confirm "Ready to commit? [Yn] " || exit 1 | |
| git commit -a -m "v$version_next release" | |
| echo "Re-exec $0 to continue" >&2 | |
| exec $0 | |
| } | |
| bump_version() { | |
| local current=$version | |
| echo "Current version: '$current'" >&2 | |
| echo -n "Enter next version (empty to abort): " >&2 | |
| read version_next | |
| if [[ -z "$version_next" ]] ; then | |
| exit 1 | |
| fi | |
| echo "Next version: '$version_next'" >&2 | |
| update_version "python3/httplib2/__init__.py" "s/__version__ =.+/__version__ = \"$version_next\"/" | |
| update_version "python2/httplib2/__init__.py" "s/__version__ =.+/__version__ = \"$version_next\"/" | |
| update_version "setup.py" "s/VERSION =.+/VERSION = \"$version_next\"/" | |
| confirm "Confirm changes? [yN] " || exit 1 | |
| } | |
| update_version() { | |
| local path="$1" | |
| local sed_expr="$2" | |
| # sed -E --in-place='' -e "s/VERSION =.+/VERSION = \"$version_replace\"/" setup.py | |
| # sed -E --in-place='' -e "s/__version__ =.+/__version__ = \"$version_replace\"/" python2/httplib2/__init__.py python3/httplib2/__init__.py | |
| echo "Updating file '$path'" >&2 | |
| if ! sed -E --in-place='' -e "$sed_expr" "$path" ; then | |
| echo "sed error $?" >&2 | |
| exit 1 | |
| fi | |
| assert_modified "$path" | |
| echo "" >&2 | |
| } | |
| edit_news() { | |
| echo "Changes since last release:" >&2 | |
| git log --format='%h %an %s' "v$version"^.. -- || exit 1 | |
| echo "" >&2 | |
| patch -p1 <<EOT | |
| diff a/CHANGELOG b/CHANGELOG | |
| --- a/CHANGELOG | |
| +++ b/CHANGELOG | |
| @@ -0,0 +1,4 @@ | |
| +$version_next | |
| + | |
| + EDIT HERE. Describe important changes and link to more information. | |
| + | |
| EOT | |
| local editor=$(which edit 2>/dev/null) | |
| [[ -z "$editor" ]] && editor="$EDITOR" | |
| if [[ -n "$editor" ]] ; then | |
| if confirm "Open default editor for CHANGELOG? [Yn] " ; then | |
| $editor CHANGELOG | |
| else | |
| confirm "Edit CHANGELOG manually and press any key" | |
| fi | |
| else | |
| echo "Unable to determine default text editor." >&2 | |
| confirm "Edit CHANGELOG manually and press any key" | |
| fi | |
| echo "" >&2 | |
| assert_modified CHANGELOG | |
| echo "" >&2 | |
| confirm "Confirm changes? [yN] " || exit 1 | |
| } | |
| assert_modified() { | |
| local path="$1" | |
| if git diff --exit-code "$path" ; then | |
| echo "File '$path' is not modified" >&2 | |
| exit 1 | |
| fi | |
| } | |
| assert_tree_clean() { | |
| if [[ -n "$(git status --short -uall)" ]] ; then | |
| echo "Tree must be clean. git status:" >&2 | |
| echo "" >&2 | |
| git status --short -uall | |
| echo "" >&2 | |
| exit 1 | |
| fi | |
| } | |
| version_check() { | |
| local need=$1 | |
| local version_setup=$(fgrep 'VERSION =' setup.py |tr -d " '\"" |cut -d\= -f2) | |
| local version_py2=$(cd python2 ; python2 -Es -c 'import httplib2;print(httplib2.__version__)') | |
| local version_py3=$(cd python3 ; python3 -Es -c 'import httplib2;print(httplib2.__version__)') | |
| if [[ "$version_setup" != "$need" ]] ; then | |
| echo "error: setup.py VERSION=$version_setup expected=$need" >&1 | |
| exit 1 | |
| fi | |
| if [[ "$version_py2" != "$need" ]] ; then | |
| echo "error: python2/httplib2/__init__.py:__version__=$version_py2 expected=$need" >&1 | |
| exit 1 | |
| fi | |
| if [[ "$version_py3" != "$need" ]] ; then | |
| echo "error: python3/httplib2/__init__.py:__version__=$version_py3 expected=$need" >&1 | |
| exit 1 | |
| fi | |
| } | |
| confirm() { | |
| local reply | |
| local prompt="$1" | |
| read -n1 -p "$prompt" reply >&2 | |
| echo "" >&2 | |
| rc=0 | |
| local default_y=" \[Yn\] $" | |
| if [[ -z "$reply" ]] && [[ "$prompt" =~ $default_y ]] ; then | |
| reply="y" | |
| fi | |
| [[ "$reply" != "y" ]] && rc=1 | |
| return $rc | |
| } | |
| main "$@" |