7
7
## It is set as callback for `src/ci/docker/x86_64-gnu-tools/repo.sh` by the CI scripts
8
8
## when a new commit lands on `master` (i.e., after it passed all checks on `auto`).
9
9
10
+ from __future__ import print_function
11
+
10
12
import sys
11
13
import re
12
14
import os
20
22
import urllib .request as urllib2
21
23
22
24
# List of people to ping when the status of a tool or a book changed.
25
+ # These should be collaborators of the rust-lang/rust repository (with at least
26
+ # read privileges on it). CI will fail otherwise.
23
27
MAINTAINERS = {
24
- 'miri' : '@oli-obk @RalfJung @eddyb' ,
25
- 'clippy-driver' : '@Manishearth @llogiq @mcarton @oli-obk @phansch @flip1995 @yaahc' ,
26
- 'rls' : '@Xanewok' ,
27
- 'rustfmt' : '@topecongiro' ,
28
- 'book' : '@carols10cents @steveklabnik' ,
29
- 'nomicon' : '@frewsxcv @Gankro' ,
30
- 'reference' : '@steveklabnik @Havvy @matthewjasper @ehuss' ,
31
- 'rust-by-example' : '@steveklabnik @marioidival @projektir' ,
32
- 'embedded-book' : (
33
- '@adamgreig @andre-richter @jamesmunns @korken89 '
34
- '@ryankurte @thejpster @therealprof'
35
- ),
36
- 'edition-guide' : '@ehuss @Centril @steveklabnik' ,
37
- 'rustc-guide' : '@mark-i-m @spastorino @amanjeev'
28
+ 'miri' : {'oli-obk' , 'RalfJung' , 'eddyb' },
29
+ 'clippy-driver' : {
30
+ 'Manishearth' , 'llogiq' , 'mcarton' , 'oli-obk' , 'phansch' , 'flip1995' ,
31
+ 'yaahc' ,
32
+ },
33
+ 'rls' : {'Xanewok' },
34
+ 'rustfmt' : {'topecongiro' },
35
+ 'book' : {'carols10cents' , 'steveklabnik' },
36
+ 'nomicon' : {'frewsxcv' , 'Gankra' },
37
+ 'reference' : {'steveklabnik' , 'Havvy' , 'matthewjasper' , 'ehuss' },
38
+ 'rust-by-example' : {'steveklabnik' , 'marioidival' },
39
+ 'embedded-book' : {
40
+ 'adamgreig' , 'andre-richter' , 'jamesmunns' , 'korken89' ,
41
+ 'ryankurte' , 'thejpster' , 'therealprof' ,
42
+ },
43
+ 'edition-guide' : {'ehuss' , 'Centril' , 'steveklabnik' },
44
+ 'rustc-guide' : {'mark-i-m' , 'spastorino' , 'amanjeev' },
38
45
}
39
46
40
47
REPOS = {
52
59
}
53
60
54
61
62
+ def validate_maintainers (repo , github_token ):
63
+ '''Ensure all maintainers are assignable on a GitHub repo'''
64
+ next_link_re = re .compile (r'<([^>]+)>; rel="next"' )
65
+
66
+ # Load the list of assignable people in the GitHub repo
67
+ assignable = []
68
+ url = 'https://api.github.com/repos/%s/collaborators?per_page=100' % repo
69
+ while url is not None :
70
+ response = urllib2 .urlopen (urllib2 .Request (url , headers = {
71
+ 'Authorization' : 'token ' + github_token ,
72
+ # Properly load nested teams.
73
+ 'Accept' : 'application/vnd.github.hellcat-preview+json' ,
74
+ }))
75
+ assignable .extend (user ['login' ] for user in json .load (response ))
76
+ # Load the next page if available
77
+ url = None
78
+ link_header = response .headers .get ('Link' )
79
+ if link_header :
80
+ matches = next_link_re .match (link_header )
81
+ if matches is not None :
82
+ url = matches .group (1 )
83
+
84
+ errors = False
85
+ for tool , maintainers in MAINTAINERS .items ():
86
+ for maintainer in maintainers :
87
+ if maintainer not in assignable :
88
+ errors = True
89
+ print (
90
+ "error: %s maintainer @%s is not assignable in the %s repo"
91
+ % (tool , maintainer , repo ),
92
+ )
93
+
94
+ if errors :
95
+ print ()
96
+ print (" To be assignable, a person needs to be explicitly listed as a" )
97
+ print (" collaborator in the repository settings. The simple way to" )
98
+ print (" fix this is to ask someone with 'admin' privileges on the repo" )
99
+ print (" to add the person or whole team as a collaborator with 'read'" )
100
+ print (" privileges. Those privileges don't grant any extra permissions" )
101
+ print (" so it's safe to apply them." )
102
+ print ()
103
+ print ("The build will fail due to this." )
104
+ exit (1 )
105
+
55
106
def read_current_status (current_commit , path ):
56
107
'''Reads build status of `current_commit` from content of `history/*.tsv`
57
108
'''
@@ -73,13 +124,12 @@ def maybe_delink(message):
73
124
def issue (
74
125
tool ,
75
126
status ,
76
- maintainers ,
127
+ assignees ,
77
128
relevant_pr_number ,
78
129
relevant_pr_user ,
79
130
pr_reviewer ,
80
131
):
81
132
# Open an issue about the toolstate failure.
82
- assignees = [x .strip () for x in maintainers .split ('@' ) if x != '' ]
83
133
if status == 'test-fail' :
84
134
status_description = 'has failing tests'
85
135
else :
@@ -100,7 +150,7 @@ def issue(
100
150
REPOS .get (tool ), relevant_pr_user , pr_reviewer
101
151
)),
102
152
'title' : '`{}` no longer builds after {}' .format (tool , relevant_pr_number ),
103
- 'assignees' : assignees ,
153
+ 'assignees' : list ( assignees ) ,
104
154
'labels' : ['T-compiler' , 'I-nominated' ],
105
155
})
106
156
print ("Creating issue:\n {}" .format (request ))
@@ -150,18 +200,19 @@ def update_latest(
150
200
old = status [os ]
151
201
new = s .get (tool , old )
152
202
status [os ] = new
203
+ maintainers = ' ' .join ('@' + name for name in MAINTAINERS [tool ])
153
204
if new > old : # comparing the strings, but they are ordered appropriately!
154
205
# things got fixed or at least the status quo improved
155
206
changed = True
156
207
message += '🎉 {} on {}: {} → {} (cc {}, @rust-lang/infra).\n ' \
157
- .format (tool , os , old , new , MAINTAINERS . get ( tool ) )
208
+ .format (tool , os , old , new , maintainers )
158
209
elif new < old :
159
210
# tests or builds are failing and were not failing before
160
211
changed = True
161
212
title = '💔 {} on {}: {} → {}' \
162
213
.format (tool , os , old , new )
163
214
message += '{} (cc {}, @rust-lang/infra).\n ' \
164
- .format (title , MAINTAINERS . get ( tool ) )
215
+ .format (title , maintainers )
165
216
# Most tools only create issues for build failures.
166
217
# Other failures can be spurious.
167
218
if new == 'build-fail' or (tool == 'miri' and new == 'test-fail' ):
@@ -200,6 +251,16 @@ def update_latest(
200
251
201
252
202
253
if __name__ == '__main__' :
254
+ repo = os .environ .get ('TOOLSTATE_VALIDATE_MAINTAINERS_REPO' )
255
+ if repo :
256
+ github_token = os .environ .get ('TOOLSTATE_REPO_ACCESS_TOKEN' )
257
+ if github_token :
258
+ validate_maintainers (repo , github_token )
259
+ else :
260
+ print ('skipping toolstate maintainers validation since no GitHub token is present' )
261
+ # When validating maintainers don't run the full script.
262
+ exit (0 )
263
+
203
264
cur_commit = sys .argv [1 ]
204
265
cur_datetime = datetime .datetime .utcnow ().strftime ('%Y-%m-%dT%H:%M:%SZ' )
205
266
cur_commit_msg = sys .argv [2 ]
0 commit comments