aboutsummaryrefslogtreecommitdiff
path: root/py_release_tools/commands.py
diff options
context:
space:
mode:
Diffstat (limited to 'py_release_tools/commands.py')
-rw-r--r--py_release_tools/commands.py158
1 files changed, 158 insertions, 0 deletions
diff --git a/py_release_tools/commands.py b/py_release_tools/commands.py
new file mode 100644
index 0000000..e57f99c
--- /dev/null
+++ b/py_release_tools/commands.py
@@ -0,0 +1,158 @@
1import os
2import re
3import sys
4from io import BytesIO
5from distutils import log
6from distutils.cmd import Command
7from subprocess import check_output
8from distutils.errors import DistutilsError
9
10
11def simple_call(cmd):
12 return check_output(cmd.split(" "))
13
14
15class SimpleCommand(Command):
16 """Default behavior for simple commands
17 """
18
19 user_options = []
20
21 def initialize_options(self):
22 pass
23
24 def finalize_options(self):
25 pass
26
27 def install_requires(self):
28 if self.distribution.install_requires:
29 self.distribution.fetch_build_eggs(
30 self.distribution.install_requires)
31
32 if self.distribution.tests_require:
33 self.distribution.fetch_build_eggs(
34 self.distribution.tests_require)
35
36 def run(self):
37 self.install_requires()
38 self._run()
39
40
41class IncrementSemanticVersion(SimpleCommand):
42 """Increment Semantic Version and Commmit to Git
43
44 Version incrementing uses semantic versioning. This command accepts -M or
45 --major, -m or --minor to increment a major or minor release. If no flags
46 are passed then a patch release is created.
47 """
48
49 user_options = [
50 ("major", "M", "increment version for major release"),
51 ("minor", "m", "increment version for minor release"),
52 ]
53
54 boolean_options = ("major", "minor")
55
56 def initialize_options(self):
57 self.major = False
58 self.minor = False
59
60 def _new_version(self, version):
61 major, minor, patch = [int(i) for i in version.split(".")]
62
63 if self.major:
64 return "{}.0.0".format(major + 1)
65 elif self.minor:
66 return "{}.{}.0".format(major, minor + 1)
67 else:
68 return "{}.{}.{}".format(major, minor, patch + 1)
69
70 def _update_version(self):
71 pattern = re.compile('^(\s+)version="([0-9\.]+)"')
72 output = BytesIO()
73
74 with open("setup.py", "r") as fp:
75 for line in fp:
76 result = pattern.match(line)
77
78 if not result:
79 output.write(line)
80 else:
81 spaces, version = result.groups()
82 new_version = self._new_version(version)
83 output.write(
84 '{}version="{}",\n'.format(spaces, new_version))
85
86 with open("setup.py", "w") as fp:
87 fp.write(output.getvalue())
88
89 return new_version
90
91 def _run(self):
92 if simple_call("git status --porcelain").strip():
93 raise DistutilsError("Uncommited changes, "
94 "commit all changes before release")
95
96 new_version = self._update_version()
97
98 check_output([
99 "git", "commit", "-m", "Release {}".format(new_version)])
100
101 simple_call("git tag release-{}".format(new_version))
102
103
104class GitPush(SimpleCommand):
105 """Push changes and tags to git origin
106 """
107
108 description = "push changes to git origin"
109
110 def _run(self):
111 simple_call("git push origin master")
112 simple_call("git push --tags")
113
114
115class TestsWithCoverage(SimpleCommand):
116 """Run Unit Tests with Coverage
117 """
118
119 description = "run unit tests with coverage"
120
121 def _run(self):
122 from coverage import coverage
123
124 cov = coverage(data_file=".coverage", branch=True,
125 source=self.distribution.packages)
126 cov.start()
127
128 # Unittest calls exit. How naughty.
129 try:
130 self.run_command("test")
131 except SystemExit:
132 pass
133
134 cov.stop()
135 cov.xml_report(outfile="coverage.xml")
136 cov.html_report()
137
138
139class PEP8CheckStyle(SimpleCommand):
140 """Run PEP8 Code Style Valiation
141 """
142
143 description = "run PEP8 style validations"
144
145 def _run(self):
146 from pep8 import StyleGuide
147
148 self.run_command("egg_info")
149 files = self.get_finalized_command("egg_info")
150
151 report = StyleGuide().check_files([
152 p for p in files.filelist.files if p.endswith(".py")])
153
154 if report.total_errors:
155 raise DistutilsError(
156 "Found {} PEP8 violations".format(report.total_errors))
157 else:
158 log.info("No PEP8 violations found")