1# -*- coding: UTF-8 -*- 2""" 3Tasks for releasing this project. 4 5Normal steps:: 6 7 8 python setup.py sdist bdist_wheel 9 10 twine register dist/{project}-{version}.tar.gz 11 twine upload dist/* 12 13 twine upload --skip-existing dist/* 14 15 python setup.py upload 16 # -- DEPRECATED: No longer supported -> Use RTD instead 17 # -- DEPRECATED: python setup.py upload_docs 18 19pypi repositories: 20 21 * https://pypi.python.org/pypi 22 * https://testpypi.python.org/pypi (not working anymore) 23 * https://test.pypi.org/legacy/ (not working anymore) 24 25Configuration file for pypi repositories: 26 27.. code-block:: init 28 29 # -- FILE: $HOME/.pypirc 30 [distutils] 31 index-servers = 32 pypi 33 testpypi 34 35 [pypi] 36 # DEPRECATED: repository = https://pypi.python.org/pypi 37 username = __USERNAME_HERE__ 38 password: 39 40 [testpypi] 41 # DEPRECATED: repository = https://test.pypi.org/legacy 42 username = __USERNAME_HERE__ 43 password: 44 45.. seealso:: 46 47 * https://packaging.python.org/ 48 * https://packaging.python.org/guides/ 49 * https://packaging.python.org/tutorials/distributing-packages/ 50""" 51 52from __future__ import absolute_import, print_function 53from invoke import Collection, task 54from ._tasklet_cleanup import path_glob 55from ._dry_run import DryRunContext 56 57 58# ----------------------------------------------------------------------------- 59# TASKS: 60# ----------------------------------------------------------------------------- 61@task 62def checklist(ctx=None): # pylint: disable=unused-argument 63 """Checklist for releasing this project.""" 64 checklist_text = """PRE-RELEASE CHECKLIST: 65[ ] Everything is checked in 66[ ] All tests pass w/ tox 67 68RELEASE CHECKLIST: 69[{x1}] Bump version to new-version and tag repository (via bump_version) 70[{x2}] Build packages (sdist, bdist_wheel via prepare) 71[{x3}] Register and upload packages to testpypi repository (first) 72[{x4}] Verify release is OK and packages from testpypi are usable 73[{x5}] Register and upload packages to pypi repository 74[{x6}] Push last changes to Github repository 75 76POST-RELEASE CHECKLIST: 77[ ] Bump version to new-develop-version (via bump_version) 78[ ] Adapt CHANGES (if necessary) 79[ ] Commit latest changes to Github repository 80""" 81 steps = dict(x1=None, x2=None, x3=None, x4=None, x5=None, x6=None) 82 yesno_map = {True: "x", False: "_", None: " "} 83 answers = {name: yesno_map[value] 84 for name, value in steps.items()} 85 print(checklist_text.format(**answers)) 86 87 88@task(name="bump_version") 89def bump_version(ctx, new_version, version_part=None, dry_run=False): 90 """Bump version (to prepare a new release).""" 91 version_part = version_part or "minor" 92 if dry_run: 93 ctx = DryRunContext(ctx) 94 ctx.run("bumpversion --new-version={} {}".format(new_version, 95 version_part)) 96 97 98@task(name="build", aliases=["build_packages"]) 99def build_packages(ctx, hide=False): 100 """Build packages for this release.""" 101 print("build_packages:") 102 ctx.run("python setup.py sdist bdist_wheel", echo=True, hide=hide) 103 104 105@task 106def prepare(ctx, new_version=None, version_part=None, hide=True, 107 dry_run=False): 108 """Prepare the release: bump version, build packages, ...""" 109 if new_version is not None: 110 bump_version(ctx, new_version, version_part=version_part, 111 dry_run=dry_run) 112 build_packages(ctx, hide=hide) 113 packages = ensure_packages_exist(ctx, check_only=True) 114 print_packages(packages) 115 116# -- NOT-NEEDED: 117# @task(name="register") 118# def register_packages(ctx, repo=None, dry_run=False): 119# """Register release (packages) in artifact-store/repository.""" 120# original_ctx = ctx 121# if repo is None: 122# repo = ctx.project.repo or "pypi" 123# if dry_run: 124# ctx = DryRunContext(ctx) 125 126# packages = ensure_packages_exist(original_ctx) 127# print_packages(packages) 128# for artifact in packages: 129# ctx.run("twine register --repository={repo} {artifact}".format( 130# artifact=artifact, repo=repo)) 131 132 133@task 134def upload(ctx, repo=None, dry_run=False): 135 """Upload release packages to repository (artifact-store).""" 136 original_ctx = ctx 137 if repo is None: 138 repo = ctx.project.repo or "pypi" 139 if dry_run: 140 ctx = DryRunContext(ctx) 141 142 packages = ensure_packages_exist(original_ctx) 143 print_packages(packages) 144 ctx.run("twine upload --repository={repo} dist/*".format(repo=repo)) 145 146 147# -- DEPRECATED: Use RTD instead 148# @task(name="upload_docs") 149# def upload_docs(ctx, repo=None, dry_run=False): 150# """Upload and publish docs. 151# 152# NOTE: Docs are built first. 153# """ 154# if repo is None: 155# repo = ctx.project.repo or "pypi" 156# if dry_run: 157# ctx = DryRunContext(ctx) 158# 159# ctx.run("python setup.py upload_docs") 160# 161# ----------------------------------------------------------------------------- 162# TASK HELPERS: 163# ----------------------------------------------------------------------------- 164def print_packages(packages): 165 print("PACKAGES[%d]:" % len(packages)) 166 for package in packages: 167 package_size = package.stat().st_size 168 package_time = package.stat().st_mtime 169 print(" - %s (size=%s)" % (package, package_size)) 170 171def ensure_packages_exist(ctx, pattern=None, check_only=False): 172 if pattern is None: 173 project_name = ctx.project.name 174 project_prefix = project_name.replace("_", "-").split("-")[0] 175 pattern = "dist/%s*" % project_prefix 176 177 packages = list(path_glob(pattern, current_dir=".")) 178 if not packages: 179 if check_only: 180 message = "No artifacts found: pattern=%s" % pattern 181 raise RuntimeError(message) 182 else: 183 # -- RECURSIVE-SELF-CALL: Once 184 print("NO-PACKAGES-FOUND: Build packages first ...") 185 build_packages(ctx, hide=True) 186 packages = ensure_packages_exist(ctx, pattern, 187 check_only=True) 188 return packages 189 190 191# ----------------------------------------------------------------------------- 192# TASK CONFIGURATION: 193# ----------------------------------------------------------------------------- 194# DISABLED: register_packages 195namespace = Collection(bump_version, checklist, prepare, build_packages, upload) 196namespace.configure({ 197 "project": { 198 "repo": "pypi", 199 } 200}) 201