aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGertjan van den Burg <gertjanvandenburg@gmail.com>2021-01-15 16:14:38 +0000
committerGertjan van den Burg <gertjanvandenburg@gmail.com>2021-01-15 16:14:38 +0000
commit1fcf71c55757a238d63815632d42e8c625460b26 (patch)
treeedfc001e5920d795d9697fc658cb96a9ade4a3dc
parentAdd copy directive to makefile (diff)
downloadSyncRNG-1fcf71c55757a238d63815632d42e8c625460b26.tar.gz
SyncRNG-1fcf71c55757a238d63815632d42e8c625460b26.zip
Add first version of release script
-rw-r--r--python/make_release.py266
1 files changed, 266 insertions, 0 deletions
diff --git a/python/make_release.py b/python/make_release.py
new file mode 100644
index 0000000..67e179e
--- /dev/null
+++ b/python/make_release.py
@@ -0,0 +1,266 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+Do-nothing script for making a release
+
+This idea comes from here:
+https://blog.danslimmon.com/2019/07/15/do-nothing-scripting-the-key-to-gradual-automation/
+
+Author: Gertjan van den Burg
+Date: 2021-01-15
+
+"""
+
+import colorama
+import os
+import sys
+import tempfile
+import webbrowser
+
+URLS = {
+ "CI": "https://github.com/GjjvdBurg/SyncRNG",
+}
+
+
+def colored(msg, color=None, style=None):
+ colors = {
+ "red": colorama.Fore.RED,
+ "green": colorama.Fore.GREEN,
+ "cyan": colorama.Fore.CYAN,
+ "yellow": colorama.Fore.YELLOW,
+ "magenta": colorama.Fore.MAGENTA,
+ None: "",
+ }
+ styles = {
+ "bright": colorama.Style.BRIGHT,
+ "dim": colorama.Style.DIM,
+ None: "",
+ }
+ pre = colors[color] + styles[style]
+ post = colorama.Style.RESET_ALL
+ return f"{pre}{msg}{post}"
+
+
+def cprint(msg, color=None, style=None):
+ print(colored(msg, color=color, style=style))
+
+
+def wait_for_enter():
+ input(colored("\nPress Enter to continue", style="dim"))
+ print()
+
+
+def get_package_name():
+ with open("./setup.py", "r") as fp:
+ nameline = next(
+ (l.strip() for l in fp if l.startswith("NAME = ")), None
+ )
+ return nameline.split("=")[-1].strip().strip('"')
+
+
+def get_package_version(pkgname):
+ ctx = {}
+ with open(f"{pkgname}/__version__.py", "r") as fp:
+ exec(fp.read(), ctx)
+ return ctx["__version__"]
+
+
+class Step:
+ def pre(self, context):
+ pass
+
+ def post(self, context):
+ wait_for_enter()
+
+ def run(self, context):
+ try:
+ self.pre(context)
+ self.action(context)
+ self.post(context)
+ except KeyboardInterrupt:
+ cprint("\nInterrupted.", color="red")
+ raise SystemExit(1)
+
+ def instruct(self, msg):
+ cprint(msg, color="green")
+
+ def print_run(self, msg):
+ cprint("Run:", color="cyan", style="bright")
+ self.print_cmd(msg)
+
+ def print_cmd(self, msg):
+ cprint("\t" + msg, color="cyan", style="bright")
+
+ def do_cmd(self, cmd):
+ cprint(f"Going to run: {cmd}", color="magenta", style="bright")
+ wait_for_enter()
+ os.system(cmd)
+
+
+class GitToMaster(Step):
+ def action(self, context):
+ self.instruct("Make sure you're on master and changes are merged in")
+ self.print_run("git checkout master")
+
+
+class UpdateChangelog(Step):
+ def action(self, context):
+ self.instruct(f"Update change log for version {context['version']}")
+ self.print_run("vi CHANGELOG.md")
+
+
+class UpdateReadme(Step):
+ def action(self, context):
+ self.instruct("Update readme if necessary")
+ self.print_run("vi README.md")
+
+
+class CopyFile(Step):
+ def __init__(self, source, target):
+ self._source = source
+ self._target = target
+
+ def action(self, context):
+ self.do_cmd(f"cp -v {self.source} {self.target}")
+
+
+class RunTests(Step):
+ def action(self, context):
+ self.do_cmd("make test")
+
+
+class BumpVersionPackage(Step):
+ def action(self, context):
+ self.instruct("Update __version__.py with new version")
+ self.do_cmd(f"vi {context['pkgname']}/__version__.py")
+
+ def post(self, context):
+ wait_for_enter()
+ context["version"] = self._get_version(context)
+
+ def _get_version(self, context):
+ # Get the version from the version file
+ return get_package_version(context["pkgname"])
+
+
+class MakeClean(Step):
+ def action(self, context):
+ self.do_cmd("make clean")
+
+
+class InstallFromTestPyPI(Step):
+ def action(self, context):
+ tmpvenv = tempfile.mkdtemp(prefix="p2r_venv_")
+ self.do_cmd(
+ f"python -m venv {tmpvenv} && source {tmpvenv}/bin/activate && "
+ "pip install --no-cache-dir --index-url "
+ "https://test.pypi.org/simple/ "
+ "--extra-index-url https://pypi.org/simple "
+ f"{context['pkgname']}=={context['version']}"
+ )
+ context["tmpvenv"] = tmpvenv
+
+
+class TestPackage(Step):
+ def action(self, context):
+ self.instruct(
+ f"Ensure that the following command gives version {context['version']}"
+ )
+ self.do_cmd(
+ f"source {context['tmpvenv']}/bin/activate && pip list | grep {context['pkgname']}"
+ )
+
+
+class RemoveVenv(Step):
+ def action(self, context):
+ self.do_cmd(f"rm -rf {context['tmpvenv']}")
+
+
+class GitTagVersion(Step):
+ def action(self, context):
+ self.do_cmd(f"git tag v{context['version']}")
+
+
+class GitTagPreRelease(Step):
+ def action(self, context):
+ self.instruct("Tag version as a pre-release (increment as needed)")
+ self.print_run(f"git tag v{context['version']}-rc.1")
+
+
+class GitAdd(Step):
+ def action(self, context):
+ self.instruct("Add everything to git and commit")
+ self.print_run("git gui")
+
+
+class GitAddRelease(Step):
+ def action(self, context):
+ self.instruct("Add Changelog & Readme to git")
+ self.instruct(
+ f"Commit with title: {context['pkgname']} Release {context['version']}"
+ )
+ self.instruct("Embed changelog in body commit message")
+ self.print_run("git gui")
+
+
+class PushToPyPI(Step):
+ def action(self, context):
+ self.do_cmd("twine upload dist/*")
+
+
+class PushToGitHub(Step):
+ def action(self, context):
+ self.do_cmd("git push -u --tags origin master")
+
+
+class WaitForCI(Step):
+ def action(self, context):
+ webbrowser.open(URLS["CI"])
+ self.instruct("Wait for CI to complete and verify that its successful")
+
+
+def main(target=None):
+ colorama.init()
+ procedure = [
+ ("gittomaster", GitToMaster()),
+ ("copy_readme", CopyFile("../README.md", "./README.md")),
+ ("copy_license", CopyFile("../LICENSE", "./LICENSE")),
+ ("gitadd1", GitAdd()),
+ ("clean1", MakeClean()),
+ ("runtests", RunTests()),
+ # trigger CI to run tests on all platforms
+ ("push1", PushToGitHub()),
+ ("ci1", WaitForCI()),
+ ("bumpversion", BumpVersionPackage()),
+ ("gitadd2", GitAdd()),
+ ("gittagpre", GitTagPreRelease()),
+ # trigger CI to run tests using cibuildwheel
+ ("push2", PushToGitHub()),
+ ("ci2", WaitForCI()),
+ ("changelog", UpdateChangelog()),
+ ("readme", UpdateReadme()),
+ ("install", InstallFromTestPyPI()),
+ ("testpkg", TestPackage()),
+ ("remove_venv", RemoveVenv()),
+ ("addrelease", GitAddRelease()),
+ ("tagfinal", GitTagVersion()),
+ # triggers Travis to build with cibw and push to PyPI
+ ("push3", PushToGitHub()),
+ ("ci3", WaitForCI()),
+ ]
+ context = {}
+ context["pkgname"] = get_package_name()
+ context["version"] = get_package_version(context["pkgname"])
+ skip = True if target else False
+ for name, step in procedure:
+ if not name == target and skip:
+ continue
+ skip = False
+ step.run(context)
+ cprint("\nDone!", color="yellow", style="bright")
+
+
+if __name__ == "__main__":
+ target = sys.argv[1] if len(sys.argv) > 1 else None
+ main(target=target)