Skip to content

Instantly share code, notes, and snippets.

@nZac
Last active December 15, 2016 16:55
Show Gist options
  • Save nZac/81005986c72d2bbfcc10f8764cedd2da to your computer and use it in GitHub Desktop.
Save nZac/81005986c72d2bbfcc10f8764cedd2da to your computer and use it in GitHub Desktop.
Release PyPI Project
#!/bin/bash -e
#
# Author: Nick Zaccardi <[email protected]>
#
# This script is designed to help release packages to PyPI faster. It tries its
# best to walk you through all the steps to release a source packages and a
# binary wheel package.
#
# The script does the following
#
# 1. Updates your version file ($SRC_DIR/version.py)
# 2. Adds any merge commits since the last tag to the changelog.rst file
# 3. Commits, tags and pushes those changes to the upstream repository
# 4. Creates, signs, and pushes the packages to PyPi
# HELPER FUNCTIONS
confirm () {
read -r -p "${1:-Are you sure?} [y/N]: " response
case $response in
[yY][eE][sS]|[yY])
true
;;
*)
false
;;
esac
}
echoerr() { printf "%s\n" "$*" >&2; }
# ENVIRONMENT CHECKS... Fail quick
test -z "$(git status --porcelain)" || { echoerr "ERROR: Unclean head."; exit 1; }
test -n "$1" || { echoerr "ERROR: Pass the directory of the package as the first param to this script"; exit 1; }
# Setup some vars for use later
HAS_GPG=$(command -v gpg 2>&1>/dev/null)
# Determine the source directory from the first argument passed in
SRC_DIR=$1
# The setup.py file should have everything we need
PROJECT_NAME=$(python setup.py --name)
REPO_URL=$(python setup.py --url 2>/dev/null)
CURRENT_VERSION=$(python setup.py -V)
# First things first, ask for the new version
echo -n "Enter new version number (current $CURRENT_VERSION): "; read -r NEW_VERSION
if confirm "Update version file?"; then
echo "VERSION = '$NEW_VERSION'" > "$SRC_DIR/version.py"
fi
# Figure out the changelog updates based on the merge commits in the history
# since the last tag. Then we link them to the web repo (Github normally) so
# that you get quick access to what changed.
if confirm "Update changelog?"; then
CURRENT_GIT_TAG=$(git describe --tags --abbrev=0)
GIT_CHANGELOG=$(git log --merges --pretty=format:"* %s (%h_)" "$CURRENT_GIT_TAG..HEAD")
GIT_CHANGELOG_LINKS=$(git log --merges --pretty=format:".. _%h: $REPO_URL/commit/%h" "$CURRENT_GIT_TAG..HEAD")
CHANGELOG_HEADER="$NEW_VERSION - $(date +%Y-%m-%d)"
{
echo -e "$CHANGELOG_HEADER"
echo -e "##################\n"
echo -e "$GIT_CHANGELOG\n"
echo -e "$GIT_CHANGELOG_LINKS\n\n"
cat changelog.rst
} > tmp
# Allow the release manager to make any tweaks to the changelog
$EDITOR changelog.rst
mv tmp changelog.rst
fi
# Show what has changed since we started, should be just a few files
git status
# Committing will add any files created by this process (version and changlog)
# and then tag (optionally signing) then push to the upstream repo of your
# choice. You might want a CI process to run
if confirm "Commit?"; then
git add .
git commit --quiet -m "Version Bump $NEW_VERSION"
if $HAS_GPG; then
GPG_TTY=$(tty) git tag --sign -m "Version Bump $NEW_VERSION" "$NEW_VERSION"
else
git tag -m "Version Bump $NEW_VERSION" "$NEW_VERSION"
fi
if confirm "Push to Git remote?"; then
echo -n "Which remote should I push to?"; read -r UPSTREAM
git push --tags "$UPSTREAM" master
fi
fi
# Construct the package, optionally signing and pushing that package to PyPI it
# will install twine and ensure wheel is installed
if confirm "Build Package?"; then
rm -rf dist build
pip --quiet install twine wheel
python setup.py --quiet sdist bdist_wheel
if $HAS_GPG && confirm "Sign sdist and wheel?"; then
GPG_TTY=$(tty) gpg --detach-sign -a "dist/$PROJECT_NAME-$(python setup.py -V).tar.gz"
GPG_TTY=$(tty) gpg --detach-sign -a "dist/$PROJECT_NAME-$(python setup.py -V)-py2.py3-none-any.whl"
fi
if confirm "Push to PyPI?"; then
twine upload dist/*
fi
fi
@nZac
Copy link
Author

nZac commented Dec 15, 2016

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment