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