diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..661f162 --- /dev/null +++ b/Makefile @@ -0,0 +1,72 @@ +# How to Make a Release +# --------------------- +# +# This file answers the question "how to make a release" hopefully +# better than a document does (only meejah and warner may currently do +# the "upload to PyPI" part anyway) +# + +default: + echo "see Makefile" + +release-clean: + @echo "Cleanup stale release: " `python newest-version.py` + -rm NEWS.md.asc + -rm dist/magic-wormhole-mailbox-server-`python newest-version.py`.tar.gz* + -rm dist/magic_wormhole_mailbox_server-`python newest-version.py`-py3-none-any.whl* + git tag -d `python newest-version.py` + +# create a branch, like: git checkout -b prepare-release-0.16.0 +# then run these, so CI can run on the release +release: + @echo "Is checkout clean?" + git diff-files --quiet + git diff-index --quiet --cached HEAD -- + + @echo "Install required build software" + python -m pip install --editable .[dev] + + @echo "Test README" + python setup.py check -s + + @echo "Is GPG Agent rubnning, and has key?" + gpg --pinentry=loopback -u meejah@meejah.ca --armor --clear-sign NEWS.md + + @echo "Bump version and create tag" + python update-version.py +# python update-version.py --patch # for bugfix release + + @echo "Build and sign wheel" + python setup.py bdist_wheel + gpg --pinentry=loopback -u meejah@meejah.ca --armor --detach-sign dist/magic_wormhole_mailbox_server-`git describe --abbrev=0`-py3-none-any.whl + ls dist/*`git describe --abbrev=0`* + + @echo "Build and sign source-dist" + python setup.py sdist + gpg --pinentry=loopback -u meejah@meejah.ca --armor --detach-sign dist/magic-wormhole-mailbox-server-`git describe --abbrev=0`.tar.gz + ls dist/*`git describe --abbrev=0`* + +release-test: + gpg --verify dist/magic-wormhole-mailbox-server-`git describe --abbrev=0`.tar.gz.asc + gpg --verify dist/magic_wormhole_mailbox_server-`git describe --abbrev=0`-py3-none-any.whl.asc + python -m venv testmf_venv + testmf_venv/bin/pip install --upgrade pip + testmf_venv/bin/pip install dist/magic_wormhole_mailbox_server-`git describe --abbrev=0`-py3-none-any.whl + testmf_venv/bin/twistd wormhole-mailbox --version + testmf_venv/bin/pip uninstall -y magic_wormhole_mailbox_server + testmf_venv/bin/pip install dist/magic-wormhole-mailbox-server-`git describe --abbrev=0`.tar.gz + testmf_venv/bin/twistd wormhole-mailbox --version + rm -rf testmf_venv + +release-upload: + twine upload --username __token__ --password `cat PRIVATE-release-token` dist/magic_wormhole_mailbox_server-`git describe --abbrev=0`-py3-none-any.whl dist/magic_wormhole_mailbox_server-`git describe --abbrev=0`-py3-none-any.whl.asc dist/magic-wormhole-mailbox-server-`git describe --abbrev=0`.tar.gz dist/magic-wormhole-mailbox-server-`git describe --abbrev=0`.tar.gz.asc + mv dist/*-`git describe --abbrev=0`.tar.gz.asc signatures/ + mv dist/*-`git describe --abbrev=0`-py3-none-any.whl.asc signatures/ + git add signatures/magic-wormhole-mailbox-server-`git describe --abbrev=0`.tar.gz.asc + git add signatures/magic_wormhole_mailbox_server-`git describe --abbrev=0`-py3-none-any.whl.asc + git commit -m "signatures for release" + git push origin-push `git describe --abbrev=0` + + +dilation.png: dilation.seqdiag + seqdiag --no-transparency -T png --size 1000x800 -o dilation.png diff --git a/newest-version.py b/newest-version.py new file mode 100644 index 0000000..e2449cb --- /dev/null +++ b/newest-version.py @@ -0,0 +1,23 @@ +# +# print out the most-recent version +# + +from dulwich.repo import Repo +from dulwich.porcelain import tag_list + + +def existing_tags(git): + versions = [ + tuple(map(int, v.decode("utf8").split("."))) + for v in tag_list(git) + ] + return versions + + +def main(): + git = Repo(".") + print("{}.{}.{}".format(*sorted(existing_tags(git))[-1])) + + +if __name__ == "__main__": + main() diff --git a/update-version.py b/update-version.py new file mode 100644 index 0000000..8c5f450 --- /dev/null +++ b/update-version.py @@ -0,0 +1,100 @@ +# +# this updates the (tagged) version of the software +# +# it will only update the "minor" version (e.g. 0.12.* -> 0.13.0) +# +# Any "options" are hard-coded in here (e.g. the GnuPG key to use) +# + +import sys +import time +from datetime import datetime + +from dulwich.repo import Repo +from dulwich.porcelain import ( + tag_list, + tag_create, + status, +) + +from twisted.internet.task import ( + react, +) +from twisted.internet.defer import ( + ensureDeferred, +) + +author = "meejah " + + +def existing_tags(git): + versions = [ + tuple(map(int, v.decode("utf8").split("."))) + for v in tag_list(git) + ] + return versions + + +def create_new_version(git, only_patch): + versions = existing_tags(git) + major, minor, patch = sorted(versions)[-1] + if only_patch: + next_version = "{}.{}.{}".format(major, minor, patch + 1) + else: + next_version = "{}.{}.{}".format(major, minor + 1, 0) + return next_version + + +async def main(reactor): + git = Repo(".") + + # including untracked files can be very slow (if there are lots, + # like in virtualenvs) and we don't care anyway + st = status(git, untracked_files="no") + if any(st.staged.values()) or st.unstaged: + print("unclean checkout; aborting") + raise SystemExit(1) + + for arg in sys.argv[1:]: + if arg not in ("--no-tag", "--patch"): + print("unknown arg: {}".format(arg)) + raise SystemExit(2) + + v = create_new_version(git, "--patch" in sys.argv) + if "--no-tag" in sys.argv: + print(v) + return + + print("Latest version: {}.{}.{}".format(*sorted(existing_tags(git))[-1])) + print("New tag will be {}".format(v)) + + # the "tag time" is seconds from the epoch .. we quantize these to + # the start of the day in question, in UTC. + now = datetime.now() + s = now.utctimetuple() + ts = int( + time.mktime( + time.struct_time(( + s.tm_year, s.tm_mon, s.tm_mday, 0, 0, 0, 0, s.tm_yday, 0 + )) + ) + ) + tag_create( + repo=git, + tag=v.encode("utf8"), + author=author.encode("utf8"), + message="release magic-wormhole-{}".format(v).encode("utf8"), + annotated=True, + objectish=b"HEAD", + sign=author.encode("utf8"), + tag_time=ts, + tag_timezone=0, + ) + + print("Tag created locally, it is not pushed") + print("To push it run something like:") + print(" git push origin {}".format(v)) + + +if __name__ == "__main__": + react(lambda r: ensureDeferred(main(r)))