Always checking upstream
[rnaseq-cwl-training.git] / bin / repo_check.py
1 #!/usr/bin/env python
2
3 """
4 Check repository settings.
5 """
6
7 import sys
8 import os
9 from subprocess import Popen, PIPE
10 import re
11 from optparse import OptionParser
12
13 from util import Reporter, load_yaml, require
14
15 # Import this way to produce a more useful error message.
16 try:
17     import requests
18 except ImportError:
19     print('Unable to import requests module: please install requests', file=sys.stderr)
20     sys.exit(1)
21
22
23 # Pattern to match Git command-line output for remotes => (user name, project name).
24 P_GIT_REMOTE = re.compile(r'upstream\s+[^:]+:([^/]+)/([^.]+)\.git\s+\(fetch\)')
25
26 # Repository URL format string.
27 F_REPO_URL = 'https://github.com/{0}/{1}/'
28
29 # Pattern to match repository URLs => (user name, project name)
30 P_REPO_URL = re.compile(r'https?://github\.com/([^.]+)/([^/]+)/?')
31
32 # API URL format string.
33 F_API_URL = 'https://api.github.com/repos/{0}/{1}/labels'
34
35 # Expected labels and colors.
36 EXPECTED = {
37     'bug' : 'bd2c00',
38     'discussion' : 'fc8dc1',
39     'enhancement' : '9cd6dc',
40     'help-wanted' : 'f4fd9c',
41     'instructor-training' : '6e5494',
42     'newcomer-friendly' : 'eec275',
43     'question' : '808040',
44     'template-and-tools' : '2b3990',
45     'work-in-progress' : '7ae78e'
46 }
47
48
49 def main():
50     """
51     Main driver.
52     """
53
54     args = parse_args()
55     reporter = Reporter()
56     repo_url = get_repo_url(args.source_dir, args.repo_url)
57     check_labels(reporter, repo_url)
58     reporter.report()
59
60
61 def parse_args():
62     """
63     Parse command-line arguments.
64     """
65
66     parser = OptionParser()
67     parser.add_option('-r', '--repo',
68                       default=None,
69                       dest='repo_url',
70                       help='repository URL')
71     parser.add_option('-s', '--source',
72                       default=os.curdir,
73                       dest='source_dir',
74                       help='source directory')
75
76     args, extras = parser.parse_args()
77     require(not extras,
78             'Unexpected trailing command-line arguments "{0}"'.format(extras))
79
80     return args
81
82
83 def get_repo_url(source_dir, repo_url):
84     """
85     Figure out which repository to query.
86     """
87
88     # Explicitly specified.
89     if repo_url is not None:
90         return repo_url
91
92     # Guess.
93     cmd = 'git remote -v'
94     p = Popen(cmd, shell=True, stdin=PIPE, stdout=PIPE, close_fds=True, universal_newlines=True)
95     stdout_data, stderr_data = p.communicate()
96     stdout_data = stdout_data.split('\n')
97     matches = [P_GIT_REMOTE.match(line) for line in stdout_data]
98     matches = [m for m in matches if m is not None]
99     require(len(matches) == 1,
100             'Unexpected output from git remote command: "{0}"'.format(matches))
101
102     username = matches[0].group(1)
103     require(username, 'empty username in git remote output {0}'.format(matches[0]))
104
105     project_name = matches[0].group(2)
106     require(username, 'empty project name in git remote output {0}'.format(matches[0]))
107
108     url = F_REPO_URL.format(username, project_name)
109     return url
110
111
112 def check_labels(reporter, repo_url):
113     """
114     Check labels in repository.
115     """
116
117     actual = get_labels(repo_url)
118     extra = set(actual.keys()) - set(EXPECTED.keys())
119
120     reporter.check(not extra,
121                    None,
122                    'Extra label(s) in repository {0}: {1}',
123                    repo_url, ', '.join(sorted(extra)))
124
125     missing = set(EXPECTED.keys()) - set(actual.keys())
126     reporter.check(not missing,
127                    None,
128                    'Missing label(s) in repository {0}: {1}',
129                    repo_url, ', '.join(sorted(missing)))
130
131     overlap = set(EXPECTED.keys()).intersection(set(actual.keys()))
132     for name in sorted(overlap):
133         reporter.check(EXPECTED[name] == actual[name],
134                        None,
135                        'Color mis-match for label {0} in {1}: expected {2}, found {3}',
136                        name, repo_url, EXPECTED[name], actual[name])
137
138
139 def get_labels(repo_url):
140     """
141     Get actual labels from repository.
142     """
143
144     m = P_REPO_URL.match(repo_url)
145     require(m, 'repository URL {0} does not match expected pattern'.format(repo_url))
146
147     username = m.group(1)
148     require(username, 'empty username in repository URL {0}'.format(repo_url))
149
150     project_name = m.group(2)
151     require(username, 'empty project name in repository URL {0}'.format(repo_url))
152
153     url = F_API_URL.format(username, project_name)
154     r = requests.get(url)
155     require(r.status_code == 200,
156             'Request for {0} failed with {1}'.format(url, r.status_code))
157
158     result = {}
159     for entry in r.json():
160         result[entry['name']] = entry['color']
161     return result
162
163
164 if __name__ == '__main__':
165     main()