Change shebang to Python3
[rnaseq-cwl-training.git] / bin / repo_check.py
1 #!/usr/bin/env python3
2
3 """
4 Check repository settings.
5 """
6
7
8 import sys
9 import os
10 from subprocess import Popen, PIPE
11 import re
12 from argparse import ArgumentParser
13
14 from util import Reporter, require
15
16 # Import this way to produce a more useful error message.
17 try:
18     import requests
19 except ImportError:
20     print('Unable to import requests module: please install requests', file=sys.stderr)
21     sys.exit(1)
22
23
24 # Pattern to match Git command-line output for remotes => (user name, project name).
25 P_GIT_REMOTE = re.compile(r'upstream\s+[^:]+:([^/]+)/([^.]+)\.git\s+\(fetch\)')
26
27 # Repository URL format string.
28 F_REPO_URL = 'https://github.com/{0}/{1}/'
29
30 # Pattern to match repository URLs => (user name, project name)
31 P_REPO_URL = re.compile(r'https?://github\.com/([^.]+)/([^/]+)/?')
32
33 # API URL format string.
34 F_API_URL = 'https://api.github.com/repos/{0}/{1}/labels'
35
36 # Expected labels and colors.
37 EXPECTED = {
38     'help wanted': 'dcecc7',
39     'status:in progress': '9bcc65',
40     'status:changes requested': '679f38',
41     'status:wait': 'fff2df',
42     'status:refer to cac': 'ffdfb2',
43     'status:need more info': 'ee6c00',
44     'status:blocked': 'e55100',
45     'status:out of scope': 'eeeeee',
46     'status:duplicate': 'bdbdbd',
47     'type:typo text': 'f8bad0',
48     'type:bug': 'eb3f79',
49     'type:formatting': 'ac1357',
50     'type:template and tools': '7985cb',
51     'type:instructor guide': '00887a',
52     'type:discussion': 'b2e5fc',
53     'type:enhancement': '7fdeea',
54     'type:clarification': '00acc0',
55     'type:teaching example': 'ced8dc',
56     'good first issue': 'ffeb3a',
57     'high priority': 'd22e2e'
58 }
59
60
61 def main():
62     """
63     Main driver.
64     """
65
66     args = parse_args()
67     reporter = Reporter()
68     repo_url = get_repo_url(args.repo_url)
69     check_labels(reporter, repo_url)
70     reporter.report()
71
72
73 def parse_args():
74     """
75     Parse command-line arguments.
76     """
77
78     parser = ArgumentParser(description="""Check repository settings.""")
79     parser.add_argument('-r', '--repo',
80                         default=None,
81                         dest='repo_url',
82                         help='repository URL')
83     parser.add_argument('-s', '--source',
84                         default=os.curdir,
85                         dest='source_dir',
86                         help='source directory')
87
88     args, extras = parser.parse_known_args()
89     require(not extras,
90             'Unexpected trailing command-line arguments "{0}"'.format(extras))
91
92     return args
93
94
95 def get_repo_url(repo_url):
96     """
97     Figure out which repository to query.
98     """
99
100     # Explicitly specified.
101     if repo_url is not None:
102         return repo_url
103
104     # Guess.
105     cmd = 'git remote -v'
106     p = Popen(cmd, shell=True, stdin=PIPE, stdout=PIPE,
107               close_fds=True, universal_newlines=True)
108     stdout_data, stderr_data = p.communicate()
109     stdout_data = stdout_data.split('\n')
110     matches = [P_GIT_REMOTE.match(line) for line in stdout_data]
111     matches = [m for m in matches if m is not None]
112     require(len(matches) == 1,
113             'Unexpected output from git remote command: "{0}"'.format(matches))
114
115     username = matches[0].group(1)
116     require(
117         username, 'empty username in git remote output {0}'.format(matches[0]))
118
119     project_name = matches[0].group(2)
120     require(
121         username, 'empty project name in git remote output {0}'.format(matches[0]))
122
123     url = F_REPO_URL.format(username, project_name)
124     return url
125
126
127 def check_labels(reporter, repo_url):
128     """
129     Check labels in repository.
130     """
131
132     actual = get_labels(repo_url)
133     extra = set(actual.keys()) - set(EXPECTED.keys())
134
135     reporter.check(not extra,
136                    None,
137                    'Extra label(s) in repository {0}: {1}',
138                    repo_url, ', '.join(sorted(extra)))
139
140     missing = set(EXPECTED.keys()) - set(actual.keys())
141     reporter.check(not missing,
142                    None,
143                    'Missing label(s) in repository {0}: {1}',
144                    repo_url, ', '.join(sorted(missing)))
145
146     overlap = set(EXPECTED.keys()).intersection(set(actual.keys()))
147     for name in sorted(overlap):
148         reporter.check(EXPECTED[name].lower() == actual[name].lower(),
149                        None,
150                        'Color mis-match for label {0} in {1}: expected {2}, found {3}',
151                        name, repo_url, EXPECTED[name], actual[name])
152
153
154 def get_labels(repo_url):
155     """
156     Get actual labels from repository.
157     """
158
159     m = P_REPO_URL.match(repo_url)
160     require(
161         m, 'repository URL {0} does not match expected pattern'.format(repo_url))
162
163     username = m.group(1)
164     require(username, 'empty username in repository URL {0}'.format(repo_url))
165
166     project_name = m.group(2)
167     require(
168         username, 'empty project name in repository URL {0}'.format(repo_url))
169
170     url = F_API_URL.format(username, project_name)
171     r = requests.get(url)
172     require(r.status_code == 200,
173             'Request for {0} failed with {1}'.format(url, r.status_code))
174
175     result = {}
176     for entry in r.json():
177         result[entry['name']] = entry['color']
178     return result
179
180
181 if __name__ == '__main__':
182     main()