Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Start versioning augur and add a release process #210

Merged
merged 12 commits into from
Sep 4, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,6 @@ script:
- if [ $TRAVIS_PYTHON_VERSION = "3.6" ]; then (cd tests/builds/zika/; snakemake); fi
- if [ $TRAVIS_PYTHON_VERSION = "3.6" ]; then (cd tests/builds/tb/; snakemake); fi
after_success:
- if [[ "$TRAVIS_PYTHON_VERSION" == "3.6" && "$TRAVIS_PULL_REQUEST" == "false" && $TRAVIS_BRANCH = "master" ]]; then
./scripts/rebuild-docker-image.sh;
- if [[ "$TRAVIS_PYTHON_VERSION" == "3.6" && "$TRAVIS_PULL_REQUEST" == "false" && $TRAVIS_BRANCH = "release" ]]; then
./devel/travis-rebuild-docker-image;
fi
9 changes: 9 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# __NEXT__


# 3.0.0.dev1 (unreleased)

## Development

* Start versioning augur beginning with 3.0.0. A new `augur version` command
reports the running version.
37 changes: 37 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,43 @@ to make module "editable" and install dev dependencies.

Each virus build consists of a `prepare.py` and `process.py` file. Currently supported builds are listed in the [builds directory](builds/).


## Development

Development of `augur` happens at <https://github.com/nextstrain/augur>.

We currently target compatibility with Python 3.4 and higher. This may be
increased to in the future.

Versions for this project from 3.0.0 onwards aim to follow the [Semantic
Versioning rules](https://semver.org).

### Releasing

New releases are tagged in git using a [_signed_ tag][]. The `release` branch
should always point to the latest release tag. Source and wheel (binary)
distributions are uploaded to [the nextstrain-augur project on
PyPi](https://pypi.org/project/nextstrain-augur).

There is a `./devel/release` script which will prepare a new release from your
local repository. It ends with instructions for you on how to push the release
commit/tag/branch and how to upload the built distributions to PyPi. You'll
need [a PyPi account][] and [twine][] installed to do the latter.

[_signed_ tag]: https://git-scm.com/book/en/v2/Git-Tools-Signing-Your-Work
[a PyPi account]: https://pypi.org/account/register/
[twine]: https://pypi.org/project/twine

### Travis CI

Branches and PRs are tested by Travis CI jobs configured in `.travis.yml`.

New releases, via pushes to the `release` branch, trigger a new [docker-base][]
build to keep the Docker image up-to-date.

[docker-base]: https://github.com/nextstrain/docker-base


## License and copyright

Copyright 2014-2018 Trevor Bedford and Richard Neher.
Expand Down
1 change: 1 addition & 0 deletions augur/__version__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
__version__ = '3.0.0.dev1'
9 changes: 9 additions & 0 deletions augur/version.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
"""
Prints the version of augur.
"""

from .__version__ import __version__

def run(args):
print("augur", __version__)
return 0
10 changes: 9 additions & 1 deletion bin/augur
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import sys
from augur import parse, filter, align, tree, refine, ancestral
from augur import traits, translate, mask, titers, export
from augur import validate, sequence_traits, clades
from augur import validate, sequence_traits, clades, version

if __name__=="__main__":
import argparse
Expand Down Expand Up @@ -193,6 +193,14 @@ if __name__=="__main__":
validate_parser.add_argument('--new-schema', action="store_true", help="use nexflu JSON schema")
validate_parser.set_defaults(func=validate.run)

## VERSION.PY
version_parser = subparsers.add_parser(
"version",
description = version.__doc__,
formatter_class = argparse.ArgumentDefaultsHelpFormatter)

version_parser.set_defaults(func=version.run)

args = parser.parse_args()
return_code = args.func(args)
sys.exit(return_code)
177 changes: 177 additions & 0 deletions devel/release
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
#!/bin/bash
#
# Prepares a new version release locally, leaving you ready to either:
#
# A. Push and upload it if you're satisfied with the state of git and the
# built distributions, or…
#
# B. Rewind the process if you're not (via devel/rewind-release or
# manually).
#
# You will need to have GPG and git's user.signingKey option configured in
# order for this script to complete.
#
# The script will prompt you for the new version if you do not provide it as
# the sole argument.
#
set -euo pipefail

devel="$(dirname $0)"
repo="$devel/.."
version_file="$repo/augur/__version__.py"
changes_file="$repo/CHANGES.md"

main() {
assert-clean-working-dir
assert-changelog-has-additions

if [[ $# -gt 0 ]]; then
version="$1"
else
version=$(next-version)
fi

assert-version-is-new $version
update-version $version
update-changelog $version
commit-and-tag $version
merge-to-release-branch $version
build-dist
remind-to-push $version
}

assert-clean-working-dir() {
local status="$(git status --porcelain --untracked-files=no | grep -vwF "$(basename "$changes_file")" || true)"

if [[ -n $status ]]; then
echo "Please commit all changes before releasing:" >&2
echo >&2
echo "$status" >&2
echo >&2
echo "Only $(basename "$changes_file") is allowed to have uncommitted changes." >&2
exit 1
fi
}

assert-changelog-has-additions() {
local current_version="$(read-version)"
local numstat="$(git diff --numstat "$current_version" -- "$changes_file")"
local insertions deletions rest

if [[ -z $numstat ]]; then
insertions=0
deletions=0
else
read -r insertions deletions rest <<<"$numstat"
fi

local net_changed=$(($insertions - $deletions))

if [[ $net_changed -lt 1 ]]; then
echo "It doesn't look like $(basename "$changes_file") was updated; only $insertions - $deletions = $net_changed line(s) were changed." >&2
exit 1
fi
}

assert-version-is-new() {
local current_version="$(read-version)"
local new_version="$1"

if [[ -z $new_version || $new_version == $current_version ]]; then
echo "You must provide a new version!" >&2
exit 1
elif ! version-is-gt $current_version $new_version; then
echo "You must provide a new version greater than the last!" >&2
exit 1
fi
}

version-is-gt() {
python3 /dev/stdin "$1" "$2" <<<"$(cut -c 9- <<<'
from sys import argv, exit
from pkg_resources import parse_version

version = list(map(parse_version, argv[1:3]))

gt = version[1] > version[0]

exit(int(not gt))
')"
}

next-version() {
local current_version="$(read-version)"

read -e -p "Current version is $current_version."$'\n'"New version? " -i "$current_version" new_version

echo "$new_version"
}

update-version() {
local new_version="$1"
local current_version="$(read-version)"

perl -pi -e "s/(?<=^__version__ = ')(.*)(?='$)/$new_version/" "$version_file"

if [[ $new_version != $(read-version) ]]; then
echo "Failed to update $version_file!" >&2
exit 1
fi
}

update-changelog() {
local new_version="$1"
local today="$(date +"%d %B %Y")"

# Remove leading zero from day if present
today="${today#0}"

# Add the new version heading immediately after the __NEXT__ heading,
# preserving the __NEXT__ heading itself.
perl -pi -e "s/(?<=^# __NEXT__$)/\n\n\n# $new_version ($today)/" "$changes_file"
}

commit-and-tag() {
local version="$1"

git commit -m "version $version" "$version_file" "$changes_file"
git tag -sm "version $version" "$version"
}

merge-to-release-branch() {
local version="$1"

git checkout release
git pull --ff-only
git merge --ff-only "$version"
git checkout -
}

build-dist() {
rm -rfv dist augur.egg-info
python3 setup.py clean
python3 setup.py sdist bdist_wheel
}

remind-to-push() {
local version="$1"

echo
echo
echo "Version updated, committed, and tagged!"
echo
echo "Please remember to push, including tags:"
echo
echo " git push origin master release tag $version"
echo
echo "You'll also want to upload the built releases to PyPi:"
echo
echo " twine upload dist/*"
echo
}

read-version() {
python3 -c "exec(open('''$version_file''').read()); print(__version__)"
}

main "$@"
52 changes: 52 additions & 0 deletions devel/rewind-release
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
#!/bin/bash
#
# Rewinds the changes made by an immediately prior run of devel/release. This
# is only useful if a) you've spotted a mistake after running devel/release but
# before pushing, or b) you're testing changes to the devel/release script and
# want to run it repeatedly with the same input.
#
set -euo pipefail

devel="$(dirname $0)"
repo="$devel/.."
version_file="$repo/augur/__version__.py"
changes_file="$repo/CHANGES.md"

main() {
local version="$(git describe --exact-match)"

if [[ -z $version ]]; then
echo >&2
echo "The current HEAD doesn't match any tag. This script can only be" >&2
echo "run immediately after devel/release." >&2
echo >&2
exit 1
fi

assert-clean-working-dir

git reset "$version~"
git tag -d "$version"
git checkout "$version_file"
remove-changelog-header "$version"
}

assert-clean-working-dir() {
local status="$(git status --porcelain --untracked-files=no)"

if [[ -n $status ]]; then
echo >&2
echo "Please commit all changes before releasing:" >&2
echo >&2
echo "$status" >&2
echo >&2
exit 1
fi
}

remove-changelog-header() {
local version="$1"
perl -0pi -e "s/(?<=^# __NEXT__\n\n)\n# \Q$version\E .+\n\n//m" "$changes_file"
}

main "$@"
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
#!/bin/bash
set -euo pipefail

echo "Pinging Travis CI to rebuild Docker image"

body='{
Expand Down
20 changes: 17 additions & 3 deletions setup.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
import os
from setuptools import setup
from pathlib import Path

base_dir = Path(__file__).parent.resolve()
version_file = base_dir / "augur/__version__.py"

# Eval the version file to get __version__; avoids importing our own package
with version_file.open() as f:
exec(f.read())

setup(
name = "augur",
version = "0.1.0",
name = "nextstrain-augur",
version = __version__,
author = "nextstrain developers",
author_email = "trevor@bedford.io, richard.neher@unibas.ch",
description = ("Pipelines for real-time phylogenetic analysis"),
Expand All @@ -12,6 +19,7 @@
url = "https://github.com/nextstrain/augur",
packages=['augur'],
package_data={'augur': ['data/*']},
python_requires = '>=3.4',
install_requires = [
"bcbio-gff >=0.6.4, ==0.6.*",
"biopython >=1.69, ==1.*",
Expand All @@ -35,6 +43,12 @@
"Development Status :: 3 - Alpha",
"Topic :: Science",
"License :: OSI Approved :: MIT License",

# Python 3 only; pathlib is >=3.4
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.4",
"Programming Language :: Python :: 3.5",
"Programming Language :: Python :: 3.6",
],
scripts=['bin/augur']
)