-
Notifications
You must be signed in to change notification settings - Fork 67
/
tasks.py
147 lines (122 loc) · 4.41 KB
/
tasks.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
import io
import os
import re
import sys
from glob import glob
from invoke import task
DOCS_PORT = os.environ.get("DOCS_PORT", 8000)
#: branch prefixes for which some checks are skipped
SPECIAL_BRANCHES = ("master", "develop", "release")
@task
def clean(c):
"""Remove artifacts and binary files."""
c.run("python setup.py clean --all")
patterns = ["build", "dist"]
patterns.extend(glob("*.egg*"))
patterns.append("docs/_build")
patterns.append("**/*.pyc")
for pattern in patterns:
c.run("rm -rf {}".format(pattern))
@task
def lint(c):
"""Run linting tox environments."""
c.run("tox -eruff,isort,black,pypi-description")
@task # NOQA
def format(c): # NOQA
"""Run code formatting tasks."""
c.run("tox -eblacken,isort_format")
@task
def towncrier_check(c): # NOQA
"""Check towncrier files."""
output = io.StringIO()
c.run("git branch -a --contains HEAD", out_stream=output)
skipped_branch_prefix = ["pull/", "release/", "develop", "master", "HEAD"]
# cleanup branch names by removing PR-only names in local, remote and disconnected branches to ensure the current
# (i.e. user defined) branch name is used
branches = list(
filter(
lambda x: x and all(not x.startswith(part) for part in skipped_branch_prefix),
(
branch.replace("origin/", "").replace("remotes/", "").strip("* (")
for branch in output.getvalue().split("\n")
),
)
)
print("Candidate branches", ", ".join(output.getvalue().split("\n")))
if not branches:
# if no branch name matches, we are in one of the excluded branches above, so we just exit
print("Skip check, branch excluded by configuration")
return
branch = branches[0]
towncrier_file = None
for branch in branches:
if any(branch.startswith(prefix) for prefix in SPECIAL_BRANCHES):
sys.exit(0)
try:
parts = re.search(r"(?P<type>\w+)/\D*(?P<number>\d+)\D*", branch).groups()
towncrier_file = os.path.join("changes", "{1}.{0}".format(*parts))
if not os.path.exists(towncrier_file) or os.path.getsize(towncrier_file) == 0:
print(
"=========================\n"
"Current tree does not contain the towncrier file {} or file is empty\n"
"please check CONTRIBUTING documentation.\n"
"========================="
"".format(towncrier_file)
)
sys.exit(2)
else:
break
except AttributeError:
pass
if not towncrier_file:
print(
"=========================\n"
"Branch {} does not respect the '<type>/(<optional-task-type>-)<number>-description' format\n"
"=========================\n"
"".format(branch)
)
sys.exit(1)
@task
def test(c):
"""Run test in local environment."""
c.run("python setup.py test")
@task
def test_all(c):
"""Run all tox environments."""
c.run("tox")
@task
def coverage(c):
"""Run test with coverage in local environment."""
c.run("coverage erase")
c.run("run setup.py test")
c.run("report -m")
@task
def tag_release(c, level, new_version=""):
"""Tag release version."""
if new_version:
new_version = f" --new-version {new_version}"
c.run(f"bump-my-version bump {level}{new_version}")
@task
def tag_dev(c, level, new_version=""):
"""Tag development version."""
if new_version:
new_version = f" --new-version {new_version}"
elif level == "release":
c.run("bump-my-version bump patch --no-commit")
level = "relver"
c.run(f"bump-my-version bump {level} --message='Bump develop version [ci skip]' {new_version} --allow-dirty")
@task(pre=[clean])
def docbuild(c):
"""Build documentation."""
os.chdir("docs")
build_dir = os.environ.get("BUILD_DIR", "_build/html")
c.run("python -msphinx -W -b html -d _build/doctrees . %s" % build_dir)
@task(docbuild)
def docserve(c):
"""Serve docs at http://localhost:$DOCS_PORT/ (default port is 8000)."""
from livereload import Server
server = Server()
server.watch("docs/conf.py", lambda: docbuild(c))
server.watch("CONTRIBUTING.rst", lambda: docbuild(c))
server.watch("docs/*.rst", lambda: docbuild(c))
server.serve(port=DOCS_PORT, root="_build/html")