Merge ~jslarraz/review-tools:add-pre-commit-hook into review-tools:master

Proposed by Jorge Sancho Larraz
Status: Merged
Merged at revision: b3c09ad0c19b8fabb9c7002d53392a8c0d412788
Proposed branch: ~jslarraz/review-tools:add-pre-commit-hook
Merge into: review-tools:master
Diff against target: 507 lines (+144/-104)
8 files modified
Makefile (+62/-26)
bin/find-unmatched-bin-versions (+10/-8)
bin/rock-check-notices (+9/-6)
bin/snap-check-notices (+30/-24)
bin/unpack-package (+1/-1)
dev/null (+0/-38)
git-hooks/pre-commit (+31/-0)
setup.py (+1/-1)
Reviewer Review Type Date Requested Status
Alex Murray Approve
Review via email: mp+465496@code.launchpad.net

Commit message

Add pre-commit hook to run black, flake8, pylint and unittests

To post a comment you must log in.
Revision history for this message
Alex Murray (alexmurray) wrote :

Thanks for this @jslarraz - I wonder if perhaps we should adapt the existing run-black etc scripts to be able to operate on a list of files, then we can make sure we run it with the same arguments etc as what is done in lpci?

Or if instead the hooks should just call `run-black` etc and run against all the files, and not worry about limiting it to the list of changed files...

Thoughts?

Revision history for this message
Jorge Sancho Larraz (jslarraz) wrote :

From my point of view, ideally we would like the git-hook to run black and linters only on modified files (as a `quick` check), and lpci to run them on the whole project anyway.

Independently on how those tools are run (i.e. git-hooks or lpci), they should be called with the same arguments. I tried to use the same arguments in the git-hook as used in current scripts (with the exception of black, as we agreed to start enforcing the format instead of just complain) but I agree that them should be handled in just one place for maintainability. Thus, scripts should be updated to be able to operate on a list of files.

I'm now wondering whether we should define those black/flake8/pylint runners directly in the Makefile defining the relevant targets and get rid of `run-black`, `run-flake8`, ` run-pylint` and `run-helper` to clean up the repository top level directory. Does it make sense?

Revision history for this message
Alex Murray (alexmurray) wrote :

Sure, I think putting them in the Makefile sounds like a good idea.

Revision history for this message
Emilia Torino (emitorino) wrote :

I only wonder if we should fix the older issues, and have the project clean? this way the check does not need to be run only on changed files.... thoughts?

Revision history for this message
Jorge Sancho Larraz (jslarraz) wrote :

@emitorino running checks only on changed files is mainly done for efficiency as you can only introduce format issues on files you are effectively modifying (hopefully). If by "older issues" you mean the code that didn't follow the black convention, it was reformatted on https://code.launchpad.net/~jslarraz/review-tools/+git/review-tools/+merge/465415, if it is anything else, please let me know to take a look :)

Revision history for this message
Jorge Sancho Larraz (jslarraz) wrote (last edit ):

Added a couple of commits on top

https://git.launchpad.net/~jslarraz/review-tools/commit/?id=dd72f359ce84c75e6502e829da1a31f65ac1c9af run black on some files that were not included or explicitly excluded in run-helper. After inspecting the output of black I don't see a good reason to don't format them properly. It will be great if you can take a look to double check I'm not missing anything there.

https://git.launchpad.net/~jslarraz/review-tools/commit/?id=52a3f0fbb8e2db4cc4926a416e18a2850dcf4942 moves test runners to makefile and update pre-commit hook accordingly. **Some special considerations as inline comments**

Revision history for this message
Emilia Torino (emitorino) wrote :

> @emitorino running checks only on changed files is mainly done for efficiency
> as you can only introduce format issues on files you are effectively modifying
> (hopefully).

ACK!

If by "older issues" you mean the code that didn't follow the
> black convention, it was reformatted on
> https://code.launchpad.net/~jslarraz/review-tools/+git/review-
> tools/+merge/465415, if it is anything else, please let me know to take a look
> :)

Ah nice! I havent run this recently, sorry!

Revision history for this message
Alex Murray (alexmurray) wrote :

LGTM! Thanks @jslarraz

review: Approve
Revision history for this message
Alex Murray (alexmurray) wrote :

It would seem that the snap build is now failing - can you take a look @jslarraz? https://launchpad.net/~myapps-reviewers/+snap/review-tools

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
diff --git a/Makefile b/Makefile
index df2bc11..7acb156 100644
--- a/Makefile
+++ b/Makefile
@@ -25,6 +25,22 @@ DEB_DEPENDENCIES := \
25SNAP_DEPENDENCIES := \25SNAP_DEPENDENCIES := \
26 black26 black
2727
28ifndef FILES
29 FILES := \
30 ./reviewtools/*py \
31 ./reviewtools/tests/*py \
32 ./bin/snap-review \
33 ./bin/create-snap-declaration \
34 ./bin/dump-tool \
35 ./bin/get-base-declaration \
36 ./bin/store-query \
37 ./bin/rock-check-notices \
38 ./bin/rock-updates-available \
39 ./bin/snap-updates-available \
40 ./bin/snap-check-* \
41 ./bin/snap-verify-*
42endif
43
28check-deb-deps:44check-deb-deps:
29 @for dep in $(DEB_DEPENDENCIES); do if ! dpkg -s $$dep 1>/dev/null 2>&1; then echo "Please apt install $$dep"; exit 1; fi; done45 @for dep in $(DEB_DEPENDENCIES); do if ! dpkg -s $$dep 1>/dev/null 2>&1; then echo "Please apt install $$dep"; exit 1; fi; done
3046
@@ -33,8 +49,40 @@ check-snap-deps:
3349
34check-deps: check-deb-deps check-snap-deps50check-deps: check-deb-deps check-snap-deps
3551
36test:52black: clean
37 ./run-tests53 black --check $(FILES)
54
55check-style: black
56
57pylint: clean
58 PYTHONPATH=. pylint -E --disable=no-member $(FILES)
59
60flake8: clean
61 flake8 $(FILES)
62
63check-syntax: pylint flake8
64
65.PHONY: check-names.list
66check-names.list:
67 (python3 collect-check-names-from-tests 2>&1 \
68 | grep 'CHECK|' | cut -d '|' -f 2- | egrep -v '(skeleton|some-check)' | LC_ALL=C sort \
69 | awk -F '|' '(l && $$1 != l1) {print l} {l1=$$1; l=$$0} END {print l}') > check-names.list
70
71check-names: check-names.list
72 # make sure check-names.list is up to date
73 cp -f check-names.list check-names.list.orig
74 $(MAKE) check-names.list
75 diff -Naur check-names.list.orig check-names.list || exit 1
76 rm -f check-names.list.orig
77
78unittest:
79 python3 -m unittest discover -v -s ./reviewtools/tests -p 'test_*'
80
81coverage:
82 python3 -m coverage run -m unittest discover -s ./reviewtools/tests -p 'test_*'
83
84coverage-report:
85 python3 -m coverage report --show-missing --omit="*skeleton*,*/dist-packages/*"
3886
39functest:87functest:
40 ./tests/test.sh88 ./tests/test.sh
@@ -51,27 +99,7 @@ functest-updates:
51functest-dump-tool:99functest-dump-tool:
52 ./tests/test-dump-tool.sh100 ./tests/test-dump-tool.sh
53101
54coverage:102check: check-deps check-style check-syntax check-names unittest functest-updates functest-dump-tool functest
55 python3 -m coverage run ./run-tests
56
57coverage-report:
58 python3 -m coverage report --show-missing --omit="*skeleton*,*/dist-packages/*"
59
60syntax-check: clean
61 ./run-flake8
62 ./run-pylint
63
64style-check: clean
65 ./run-black
66
67check-names:
68 # make sure check-names.list is up to date
69 cp -f check-names.list check-names.list.orig
70 ./collect-check-names
71 diff -Naur check-names.list.orig check-names.list || exit 1
72 rm -f check-names.list.orig
73
74check: check-deps test functest-updates functest-dump-tool functest syntax-check style-check check-names
75103
76clean:104clean:
77 rm -rf ./reviewtools/__pycache__ ./reviewtools/tests/__pycache__105 rm -rf ./reviewtools/__pycache__ ./reviewtools/tests/__pycache__
@@ -80,6 +108,14 @@ clean:
80 rm -rf ./review-tools-*108 rm -rf ./review-tools-*
81 rm -rf ./build ./review_tools.egg-info109 rm -rf ./build ./review_tools.egg-info
82110
83.PHONY: check-names.list111install-hooks:
84check-names.list:112 @if ! snap list | grep black -c >>/dev/null; then \
85 ./collect-check-names113 echo '*** black snap is not installed. Please install it and run `make install-hook` again ***'; \
114 elif ! dpkg -l | grep pylint -c >>/dev/null; then \
115 echo '*** pylint package is not installed. Please install it and run `make install-hook` again ***'; \
116 elif ! dpkg -l | grep flake8 -c >>/dev/null; then \
117 echo '*** flake8 package is not installed. Please install it and run `make install-hook` again ***'; \
118 else \
119 echo install -m 755 -b -S .backup git-hooks/pre-commit .git/hooks/pre-commit ; \
120 install -m 755 -b -S .backup git-hooks/pre-commit .git/hooks/pre-commit ; \
121 fi
diff --git a/bin/find-unmatched-bin-versions b/bin/find-unmatched-bin-versions
index 1b85a20..4316f35 100755
--- a/bin/find-unmatched-bin-versions
+++ b/bin/find-unmatched-bin-versions
@@ -22,14 +22,16 @@ for line in lines:
22 pkg = line[9:]22 pkg = line[9:]
23 db[rel][pkg] = {}23 db[rel][pkg] = {}
24 elif pkg is not None and line.startswith("Version: "):24 elif pkg is not None and line.startswith("Version: "):
25 db[rel][pkg]['version'] = line[9:]25 db[rel][pkg]["version"] = line[9:]
26 elif pkg is not None and line.startswith("Source: ") and ' (' in line:26 elif pkg is not None and line.startswith("Source: ") and " (" in line:
27 db[rel][pkg]['source'] = line[8:].split(' (')[0]27 db[rel][pkg]["source"] = line[8:].split(" (")[0]
28 db[rel][pkg]['source_version'] = line.split('(')[1].split(')')[0]28 db[rel][pkg]["source_version"] = line.split("(")[1].split(")")[0]
29 elif line == '' and pkg in db[rel]:29 elif line == "" and pkg in db[rel]:
30 if 'version' not in db[rel][pkg] or \30 if (
31 'source_version' not in db[rel][pkg] or \31 "version" not in db[rel][pkg]
32 db[rel][pkg]['version'] == db[rel][pkg]['source_version']:32 or "source_version" not in db[rel][pkg]
33 or db[rel][pkg]["version"] == db[rel][pkg]["source_version"]
34 ):
33 del db[rel][pkg]35 del db[rel][pkg]
3436
35print(json.dumps(db, sort_keys=True, indent=2))37print(json.dumps(db, sort_keys=True, indent=2))
diff --git a/bin/rock-check-notices b/bin/rock-check-notices
index 73406f7..e2e0271 100755
--- a/bin/rock-check-notices
+++ b/bin/rock-check-notices
@@ -18,7 +18,6 @@ import argparse
18import json18import json
19import os19import os
20import sys20import sys
21import tarfile
2221
23import reviewtools.common as common22import reviewtools.common as common
24from reviewtools.common import (23from reviewtools.common import (
@@ -49,8 +48,10 @@ def main():
49 "--no-fetch", help="use existing security db", action="store_true"48 "--no-fetch", help="use existing security db", action="store_true"
50 )49 )
51 parser.add_argument("--with-cves", help="show referenced cves", action="store_true")50 parser.add_argument("--with-cves", help="show referenced cves", action="store_true")
52 parser.add_argument('--ignore-pockets',51 parser.add_argument(
53 help='ignore updates published to the comma-separated list of pockets')52 "--ignore-pockets",
53 help="ignore updates published to the comma-separated list of pockets",
54 )
54 (args, argv) = parser.parse_known_args()55 (args, argv) = parser.parse_known_args()
5556
56 common.REPORT_OUTPUT = "console"57 common.REPORT_OUTPUT = "console"
@@ -95,9 +96,11 @@ def main():
95 if "USNDB" in os.environ:96 if "USNDB" in os.environ:
96 usndb_fn = os.environ["USNDB"]97 usndb_fn = os.environ["USNDB"]
9798
98 cmd_args = ["--usn-db=%s" % usndb_fn,99 cmd_args = [
99 "--rock=%s" % os.path.abspath(pkg),100 "--usn-db=%s" % usndb_fn,
100 "--ignore-pockets=%s" % args.ignore_pockets]101 "--rock=%s" % os.path.abspath(pkg),
102 "--ignore-pockets=%s" % args.ignore_pockets,
103 ]
101 if args.with_cves:104 if args.with_cves:
102 cmd_args.append("--with-cves")105 cmd_args.append("--with-cves")
103106
diff --git a/bin/snap-check-notices b/bin/snap-check-notices
index 76ef609..e1b16c2 100755
--- a/bin/snap-check-notices
+++ b/bin/snap-check-notices
@@ -31,22 +31,26 @@ from reviewtools.common import (
3131
3232
33def help():33def help():
34 print('''Usage:34 print(
35 """Usage:
35$ %s SNAP1 SNAP2 ...36$ %s SNAP1 SNAP2 ...
36''' % os.path.basename(sys.argv[0]))37"""
38 % os.path.basename(sys.argv[0])
39 )
3740
3841
39def main():42def main():
40 parser = argparse.ArgumentParser(43 parser = argparse.ArgumentParser(
41 prog='check-notices',44 prog="check-notices", description="Check a snap for needed security notices"
42 description='Check a snap for needed security notices'45 )
46 parser.add_argument(
47 "--no-fetch", help="use existing security db", action="store_true"
48 )
49 parser.add_argument("--with-cves", help="show referenced cves", action="store_true")
50 parser.add_argument(
51 "--ignore-pockets",
52 help="ignore updates published to the comma-separated list of pockets",
43 )53 )
44 parser.add_argument('--no-fetch', help='use existing security db',
45 action='store_true')
46 parser.add_argument('--with-cves', help='show referenced cves',
47 action='store_true')
48 parser.add_argument('--ignore-pockets',
49 help='ignore updates published to the comma-separated list of pockets')
50 (args, argv) = parser.parse_known_args()54 (args, argv) = parser.parse_known_args()
5155
52 common.REPORT_OUTPUT = "console"56 common.REPORT_OUTPUT = "console"
@@ -75,34 +79,36 @@ def main():
75 warn("Skipping '%s', not a regular file" % pkg)79 warn("Skipping '%s', not a regular file" % pkg)
76 continue80 continue
7781
78 snap = os.path.basename(pkg).split('_')[0]82 snap = os.path.basename(pkg).split("_")[0]
79 rev = "unknown"83 rev = "unknown"
80 if '_' in pkg:84 if "_" in pkg:
81 rev = os.path.splitext(os.path.basename(pkg))[0].split('_')[1]85 rev = os.path.splitext(os.path.basename(pkg))[0].split("_")[1]
8286
83 if snap in reports and rev in reports[snap]:87 if snap in reports and rev in reports[snap]:
84 debug("Skipping %s with revision %s" % (snap, rev))88 debug("Skipping %s with revision %s" % (snap, rev))
85 continue89 continue
8690
87 usndb_fn = os.path.join(os.environ['SNAP_USER_COMMON'], usndb)91 usndb_fn = os.path.join(os.environ["SNAP_USER_COMMON"], usndb)
88 if 'USNDB' in os.environ:92 if "USNDB" in os.environ:
89 usndb_fn = os.environ['USNDB']93 usndb_fn = os.environ["USNDB"]
9094
91 cmd_args = ['--usn-db=%s' % usndb_fn,95 cmd_args = [
92 '--snap=%s' % os.path.abspath(pkg),96 "--usn-db=%s" % usndb_fn,
93 '--ignore-pockets=%s' % args.ignore_pockets]97 "--snap=%s" % os.path.abspath(pkg),
98 "--ignore-pockets=%s" % args.ignore_pockets,
99 ]
94 if args.with_cves:100 if args.with_cves:
95 cmd_args.append('--with-cves')101 cmd_args.append("--with-cves")
96102
97 # this carries through to available103 # this carries through to available
98 if had_debug is not None:104 if had_debug is not None:
99 os.unsetenv('SNAP_DEBUG')105 os.unsetenv("SNAP_DEBUG")
100 # don't include stderr in the output since we try and parse this106 # don't include stderr in the output since we try and parse this
101 # output as JSON below and hence including errors from stderr in107 # output as JSON below and hence including errors from stderr in
102 # the output will make the JSON malformed108 # the output will make the JSON malformed
103 rc, out = cmd([available] + cmd_args, stderr=None)109 rc, out = cmd([available] + cmd_args, stderr=None)
104 if had_debug is not None:110 if had_debug is not None:
105 os.environ['SNAP_DEBUG'] = had_debug111 os.environ["SNAP_DEBUG"] = had_debug
106112
107 if rc != 0:113 if rc != 0:
108 warn(out)114 warn(out)
@@ -110,7 +116,7 @@ def main():
110116
111 if snap not in reports:117 if snap not in reports:
112 reports[snap] = dict()118 reports[snap] = dict()
113 if out == '':119 if out == "":
114 reports[snap][rev] = dict()120 reports[snap][rev] = dict()
115 else:121 else:
116 reports[snap][rev] = json.loads(out)122 reports[snap][rev] = json.loads(out)
@@ -118,7 +124,7 @@ def main():
118 print(json.dumps(reports, indent=2, sort_keys=True))124 print(json.dumps(reports, indent=2, sort_keys=True))
119125
120126
121if __name__ == '__main__':127if __name__ == "__main__":
122 try:128 try:
123 main()129 main()
124 except KeyboardInterrupt:130 except KeyboardInterrupt:
diff --git a/bin/unpack-package b/bin/unpack-package
index e85469c..4b08dbf 100755
--- a/bin/unpack-package
+++ b/bin/unpack-package
@@ -5,7 +5,7 @@ import sys
55
6from reviewtools import common6from reviewtools import common
77
8if __name__ == '__main__':8if __name__ == "__main__":
9 if len(sys.argv) != 3:9 if len(sys.argv) != 3:
10 common.error("%s <pkg> <dir>" % os.path.basename(sys.argv[0]))10 common.error("%s <pkg> <dir>" % os.path.basename(sys.argv[0]))
1111
diff --git a/collect-check-names b/collect-check-names
12deleted file mode 10075512deleted file mode 100755
index a562f8e..0000000
--- a/collect-check-names
+++ /dev/null
@@ -1,5 +0,0 @@
1#!/bin/sh
2
3(./collect-check-names-from-tests 2>&1 | grep 'CHECK|' | cut -d '|' -f 2- \
4 | egrep -v '(skeleton|some-check)' | LC_ALL=C sort \
5 | awk -F '|' '(l && $1 != l1) {print l} {l1=$1; l=$0} END {print l}') > check-names.list
diff --git a/git-hooks/pre-commit b/git-hooks/pre-commit
6new file mode 1006440new file mode 100644
index 0000000..986b90d
--- /dev/null
+++ b/git-hooks/pre-commit
@@ -0,0 +1,31 @@
1#!/usr/bin/env bash
2
3# If any command fails, exit immediately with that command's exit status
4set -eo pipefail
5
6# Find all changed files for this commit
7# Compute the diff only once to save a small amount of time.
8CHANGED_FILES=$(git diff --name-only --cached --diff-filter=ACMR)
9# Get only changed files that match our file suffix pattern
10get_pattern_files() {
11 pattern=$(echo "$*" | sed "s/ /\$\\\|/g")
12 echo "$CHANGED_FILES" | { grep "$pattern$" || true; }
13}
14# Get all changed python files
15PY_FILES=$(get_pattern_files .py)
16
17if [[ -n "$PY_FILES" ]]
18then
19 # Run black against changed python files for this commit
20 make black FILES=$PY_FILES
21 echo "black passes all altered python sources."
22 # Run flake8 against changed python files for this commit
23 make flake8 FILES=$PY_FILES
24 echo "flake8 passed!"
25 # Run pylint against changed python files for this commit
26 make pylint FILES=$PY_FILES
27 echo "pylint passed!"
28fi
29
30make unittest
31echo "unit tests passed!"
diff --git a/run-black b/run-black
0deleted file mode 10075532deleted file mode 100755
index 975b35e..0000000
--- a/run-black
+++ /dev/null
@@ -1,8 +0,0 @@
1#!/bin/sh
2set -e
3
4if command -v black > /dev/null ; then
5 ./run-helper black
6else
7 echo "Could not find black"
8fi
diff --git a/run-flake8 b/run-flake8
9deleted file mode 1007550deleted file mode 100755
index 4233dd1..0000000
--- a/run-flake8
+++ /dev/null
@@ -1,5 +0,0 @@
1#!/bin/sh
2set -e
3
4flake8 --version
5./run-helper flake8
diff --git a/run-helper b/run-helper
6deleted file mode 1007550deleted file mode 100755
index e19dadd..0000000
--- a/run-helper
+++ /dev/null
@@ -1,49 +0,0 @@
1#!/bin/sh
2set -e
3
4exe=
5args=
6env=
7case "$1" in
8 flake8)
9 exe="$1"
10 ;;
11 black)
12 exe="$1"
13 # for now, only check
14 args="--diff --quiet"
15 ;;
16 pylint3|pylint)
17 exe="$1"
18 env="PYTHONPATH=."
19 # for now, only errors
20 args="-E --disable=no-member"
21 ;;
22 *)
23 echo "run-helper black|flake8|pylint3|pylint"
24 exit 1
25 ;;
26esac
27
28echo "= $exe ="
29if [ -n "$env" ]; then
30 export "$env"
31fi
32for i in ./reviewtools/*py \
33 ./reviewtools/tests/*py \
34 ./bin/snap-review \
35 ./bin/create-snap-declaration \
36 ./bin/dump-tool \
37 ./bin/get-base-declaration \
38 ./bin/store-query \
39 ./bin/rock-check-notices \
40 ./bin/rock-updates-available \
41 ./bin/snap-updates-available \
42 ./bin/snap-check-* \
43 ./bin/snap-verify-* ; do
44 if echo "$i" | grep -q 'snap-check-notices\|rock-check-notices' ; then
45 continue # shell
46 fi
47 echo "Checking $i"
48 "$exe" $args "$i"
49done
diff --git a/run-pylint b/run-pylint
50deleted file mode 1007550deleted file mode 100755
index d00614b..0000000
--- a/run-pylint
+++ /dev/null
@@ -1,10 +0,0 @@
1#!/bin/sh
2set -e
3
4if command -v pylint3 > /dev/null ; then
5 ./run-helper pylint3
6elif command -v pylint > /dev/null ; then
7 ./run-helper pylint
8else
9 echo "Could not find pylint"
10fi
diff --git a/run-tests b/run-tests
11deleted file mode 1007550deleted file mode 100755
index 4c7d5a0..0000000
--- a/run-tests
+++ /dev/null
@@ -1,38 +0,0 @@
1#!/usr/bin/python3
2'''run-tests: run the test suite'''
3#
4# Copyright (C) 2013 Canonical Ltd.
5#
6# This program is free software: you can redistribute it and/or modify
7# it under the terms of the GNU General Public License as published by
8# the Free Software Foundation; version 3 of the License.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13# GNU General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18import os
19import sys
20import unittest
21
22# NOTE: changes to this file may also need to be made to
23# 'collect-check-names-from-tests'
24
25test_directory = 'reviewtools/tests/'
26if 'RT_EXTRAS_PATH' in os.environ and \
27 os.path.isdir(os.environ['RT_EXTRAS_PATH'] + '/tests'):
28 test_directory = os.environ['RT_EXTRAS_PATH'] + '/tests'
29
30test_filename = 'test_*'
31if len(sys.argv) > 1:
32 test_filename = sys.argv[1]
33
34suite = unittest.TestLoader().discover(test_directory, pattern=test_filename)
35test_result = unittest.TextTestRunner(verbosity=2).run(suite)
36if len(test_result.errors) > 0 or len(test_result.failures) > 0 or \
37 len(test_result.unexpectedSuccesses) > 0:
38 sys.exit(1)
diff --git a/setup.py b/setup.py
index 5d16646..eb4aeb8 100755
--- a/setup.py
+++ b/setup.py
@@ -10,7 +10,7 @@ import re
10changelog = "debian/changelog"10changelog = "debian/changelog"
11if os.path.exists(changelog):11if os.path.exists(changelog):
12 head = codecs.open(changelog, encoding="utf-8").readline()12 head = codecs.open(changelog, encoding="utf-8").readline()
13 match = re.compile(".*\((.*)\).*").match(head)13 match = re.compile(".*\((.*)\).*").match(head) # noqa: W605
14 if match:14 if match:
15 version = match.group(1)15 version = match.group(1)
1616

Subscribers

People subscribed via source and target branches