-
Notifications
You must be signed in to change notification settings - Fork 0
/
build.py
258 lines (212 loc) · 8.26 KB
/
build.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
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
"""
Formic build - uses Dovetail (available on PyPi)
But Dovetail was only supported by Python2
"""
# from dovetail import *
import dovetail
import subprocess
import os
import sys
import re
################################################################################
# Build directory layout and language-independent app names
BUILD = os.path.abspath(os.path.join(os.path.dirname(__file__), "build"))
BUILD_DOC = os.path.join(BUILD, "html")
BUILD_PYLINT = os.path.join(BUILD, "pylint", "pylint.out")
BUILD_SLOCCOUNT = os.path.join(BUILD, "sloccount", "sloccount.sc")
BUILD_PYTEST = os.path.join(BUILD, "pytest", "junit.xml")
BUILD_COVER = os.path.join(BUILD, "coverage")
if os.name == "nt":
PYLINT_EXE = "pylint.bat"
PYTEST_EXE = "py.test.exe"
COVERAGE = "coverage.exe"
SPHINX = "sphinx-build.exe"
else:
PYLINT_EXE = "pylint"
PYTEST_EXE = "py.test"
COVERAGE = "coverage"
SPHINX = "sphinx-build"
################################################################################
# Metrics' subtasks
@dovetail.task
@dovetail.requires("pylint")
@dovetail.check_result
def pylint():
"""Runs pylint report and saves into build directory"""
return subprocess.call("{0} formic".format(PYLINT_EXE).split(" "))
@dovetail.task
@dovetail.do_if(dovetail.Env("WORKSPACE"))
@dovetail.mkdirs(os.path.dirname(BUILD_PYLINT))
@dovetail.requires("pylint")
def pylint_jenkins():
"""Run PyLint on the code and produce a report suitable for the
Jenkins plugin 'violations'.
Note that there is a bug in the Violations plugin which means that
absolute paths to source (produced by PyLint) are not read. The sed command
removes the workspace part of the path making everything good again. This
requires the environment variable WORKSPACE from Jenkins"""
cmd = '{0} formic -f parseable'.format(PYLINT_EXE).split(' ')
return dovetail.call(cmd, stdout=BUILD_PYLINT)
@dovetail.task
@dovetail.do_if(dovetail.Which("sloccount"))
@dovetail.mkdirs(os.path.dirname(BUILD_SLOCCOUNT))
@dovetail.fail_if(dovetail.StdErr())
@dovetail.check_result
@dovetail.adjust_env(
LC_ALL="C") # Internationalisation issues in Perl on the build platform
def sloccount():
"""If David A Wheeler's excellent SLOCcount is installed, run it capturing
results.
The format used is suitable for processing by the sloccount plugin in Jenkins."""
return dovetail.call(
"sloccount --duplicates --wide --details formic".split(" "),
stdout=BUILD_SLOCCOUNT)
################################################################################
# Main tasks
@dovetail.task
@dovetail.requires("pytest>2.2")
@dovetail.mkdirs(os.path.dirname(BUILD_PYTEST))
@dovetail.fail_if(dovetail.StdErr())
def unit_test():
subprocess.check_call('{0} --junitxml {1} formic'.format(
PYTEST_EXE, BUILD_PYTEST).split(" "))
@dovetail.task
@dovetail.fail_if(dovetail.StdErr())
def sanity_test():
import formic
version = formic.get_version()
line = subprocess.check_output("formic --version".split()).strip()
print(line)
assert line.startswith("formic")
assert line.endswith("http://www.aviser.asia/formic")
assert version in line
subprocess.check_call("formic --help".split())
subprocess.check_call("formic --usage".split())
@dovetail.task
@dovetail.depends(unit_test, sanity_test)
def test():
"""Runs py.test on the development files"""
@dovetail.task
@dovetail.depends(pylint_jenkins, test, sloccount)
def metrics():
"""Run various metrics-gatherers on formic, capturing results in a way
that Jenkins can interpret them"""
pass
@dovetail.task
def develop():
"""Run 'python setup.py develop' so formic is present in the current
Python environment"""
return subprocess.call("python setup.py develop".split())
@dovetail.task
@dovetail.requires("sphinx")
@dovetail.depends(develop)
def doc():
"""Runs Sphinx to produce the documentation (output to ./build/htmldoc)"""
return subprocess.call("{0} doc {1}".format(SPHINX, BUILD_DOC).split())
@dovetail.task
def configure_google_analytics():
"""An optional task; if run, this will switch on Google Analystics, reporting
documentation usage to Aviser.
This is meant to be run only by Aviser when producing HTML for the main
web site.
"""
f = open(os.path.join("doc", "_templates", "google-analytics.html"), "w")
f.write("""<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-31981784-1']);
_gaq.push(['_setDomainName', 'aviser.asia']);
_gaq.push(['_setAllowLinker', true]);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
</script>""")
f.close()
@dovetail.task
@dovetail.requires("coverage", "pytest>2.2", "pytest-cov")
@dovetail.mkdirs(BUILD_COVER)
@dovetail.cwd("formic")
def coverage():
"""Runs the whole test suite with Coverage
Reports are in ./build/coverage"""
subprocess.check_call(
'{0} --cov-config ../coveragerc --cov-report html --cov-report xml --cov formic'.
format(PYTEST_EXE).split(" "))
@dovetail.task
@dovetail.depends(test, doc)
@dovetail.requires('yolk')
def publish():
"""Publishes Formic to PyPi (don't run unless you are the maintainer)"""
print("To be ready for release, remember:")
print(" 1) Update the version number (and associated test)")
print(" 2) Update the ChangeLog.rst (and other documentation)")
print(
" ChangeLog should have an line (title) consisting of the version number"
)
print(" 3) Tag Mercurial with 'Release <version>'")
print(" 4) Push updates to BitBucket")
print(" 5) Set the RELEASE environment variable")
print(" $ export RELEASE=formic")
cmd = [
'bash', '-c',
'yolk -M formic | grep "^version: " | sed "s/^version: \\(.*\\)$/\\1/"'
]
published_version = subprocess.check_output(cmd).strip()
our_version = open(os.path.join("formic", "VERSION.txt"), "r").read()
# sanity test on version
if "\n" in published_version or\
len(published_version) < 3 or len(published_version) > 10:
raise Exception(
"Published version number seems weird: " + published_version)
print("Published version:", published_version)
print("Current version: ", our_version)
if our_version == published_version:
raise Exception(
"You are attempting to republish version " + our_version)
# sanity: check ChangeLog starts with our version
changelog = open("CHANGELOG.rst", "r")
found = False
for line in changelog.readlines():
if line.strip() == our_version:
print("ChangeLog has an entry")
found = True
break
changelog.close()
if not found:
raise Exception(
"The ChangeLog does not appear to include comments on this release"
)
# Sanity check: is there a release tag
tags = subprocess.check_output("hg tags".split())
found = False
looking_for = "Release " + our_version
for line in tags.split("\n"):
match = re.match(r"^(.*)\s+[0-9]+:[0-9a-f]+$", line)
if match:
tag = match.group(1).strip()
if tag == looking_for:
print("Found tag", tag)
found = True
break
if not found:
raise Exception(
"Mercurial does not have the release tag: " + looking_for)
status = subprocess.check_output(["hg", "status"])
for line in status.split("\n"):
if len(line) > 0:
raise Exception("Uncommitted changes present")
try:
v = os.environ["RELEASE"]
if v != "formic":
raise KeyError()
except KeyError:
print("$RELEASE environment variable is not set")
raise
subprocess.check_call("python setup.py bdist_egg upload".split())
subprocess.check_call("python setup.py sdist upload".split())
################################################################################
# Bootstrap for running the script directly
if __name__ == "__main__":
dovetail.run(sys.argv[1:])