@@ -281,12 +281,64 @@ def write_csv(output: pathlib.Path, rows: List[Tuple]):
281
281
for row in rows :
282
282
writer .writerow (row )
283
283
284
- def sync_csv (rows : List [Tuple ], from_github : List [PaperInfo ]) -> List [Tuple ]:
284
+ def create_github_issue (paper : PaperInfo , labels : List [str ]) -> None :
285
+ """
286
+ Create a new Github issue representing the given PaperInfo.
287
+ """
288
+ paper_name = paper .paper_name .replace ('``' , '`' ).replace ('\\ ' , '' )
289
+
290
+ create_cli = ['gh' , 'issue' , 'create' , '--repo' , 'llvm/llvm-project' ,
291
+ '--title' , f'{ paper .paper_number } : { paper_name } ' ,
292
+ '--body' , f'**Link:** https://wg21.link/{ paper .paper_number } ' ,
293
+ '--project' , 'libc++ Standards Conformance' ,
294
+ '--label' , 'libc++' ]
295
+
296
+ for label in labels :
297
+ create_cli += ['--label' , label ]
298
+
299
+ print ("Do you want to create the following issue?" )
300
+ print (create_cli )
301
+ answer = input ("y/n: " )
302
+ if answer == 'n' :
303
+ print ("Not creating issue" )
304
+ return
305
+ elif answer != 'y' :
306
+ print (f"Invalid answer { answer } , skipping" )
307
+ return
308
+
309
+ print ("Creating issue" )
310
+ issue_link = subprocess .check_output (create_cli ).decode ().strip ()
311
+ print (f"Created tracking issue for { paper .paper_number } : { issue_link } " )
312
+
313
+ # Retrieve the "Github project item ID" by re-adding the issue to the project again,
314
+ # even though we created it inside the project in the first place.
315
+ item_add_cli = ['gh' , 'project' , 'item-add' , LIBCXX_CONFORMANCE_PROJECT , '--owner' , 'llvm' , '--url' , issue_link , '--format' , 'json' ]
316
+ item = json .loads (subprocess .check_output (item_add_cli ).decode ().strip ())
317
+
318
+ # Then, adjust the 'Meeting Voted' field of that item.
319
+ meeting_voted_cli = ['gh' , 'project' , 'item-edit' ,
320
+ '--project-id' , 'PVT_kwDOAQWwKc4AlOgt' ,
321
+ '--field-id' , 'PVTF_lADOAQWwKc4AlOgtzgdUEXI' , '--text' , paper .meeting ,
322
+ '--id' , item ['id' ]]
323
+ subprocess .check_call (meeting_voted_cli )
324
+
325
+ # And also adjust the 'Status' field of the item to 'To Do'.
326
+ status_cli = ['gh' , 'project' , 'item-edit' ,
327
+ '--project-id' , 'PVT_kwDOAQWwKc4AlOgt' ,
328
+ '--field-id' , 'PVTSSF_lADOAQWwKc4AlOgtzgdUBak' , '--single-select-option-id' , 'f75ad846' ,
329
+ '--id' , item ['id' ]]
330
+ subprocess .check_call (status_cli )
331
+
332
+ def sync_csv (rows : List [Tuple ], from_github : List [PaperInfo ], create_new : bool , labels : List [str ] = None ) -> List [Tuple ]:
285
333
"""
286
334
Given a list of CSV rows representing an existing status file and a list of PaperInfos representing
287
335
up-to-date (but potentially incomplete) tracking information from Github, this function returns the
288
336
new CSV rows synchronized with the up-to-date information.
289
337
338
+ If `create_new` is True and a paper from the CSV file is not tracked on Github yet, this also prompts
339
+ to create a new issue on Github for tracking it. In that case the created issue is tagged with the
340
+ provided labels.
341
+
290
342
Note that this only tracks changes from 'not implemented' issues to 'implemented'. If an up-to-date
291
343
PaperInfo reports that a paper is not implemented but the existing CSV rows report it as implemented,
292
344
it is an error (i.e. the result is not a CSV row where the paper is *not* implemented).
@@ -305,49 +357,82 @@ def sync_csv(rows: List[Tuple], from_github: List[PaperInfo]) -> List[Tuple]:
305
357
# issue tracking it, which we validate below.
306
358
tracking = [gh for gh in from_github if paper .paper_number == gh .paper_number ]
307
359
308
- # If there is no tracking issue for that row in the CSV, this is an error since we're
309
- # missing a Github issue.
310
- if len (tracking ) == 0 :
311
- print (f"Can't find any Github issue for CSV row: { row } " )
360
+ # If there's more than one tracking issue, something is weird.
361
+ if len (tracking ) > 1 :
362
+ print (f"Found a row with more than one tracking issue: { row } \n tracked by: { tracking } " )
312
363
results .append (row )
313
364
continue
314
365
315
- # If there's more than one tracking issue, something is weird too.
316
- if len (tracking ) > 1 :
317
- print (f"Found a row with more than one tracking issue: { row } \n tracked by: { tracking } " )
366
+ # If there is no tracking issue for that row and we are creating new issues, do that.
367
+ # Otherwise just log that we're missing an issue.
368
+ if len (tracking ) == 0 :
369
+ if create_new :
370
+ assert labels is not None , "Missing labels when creating new Github issues"
371
+ create_github_issue (paper , labels = labels )
372
+ else :
373
+ print (f"Can't find any Github issue for CSV row: { row } " )
318
374
results .append (row )
319
375
continue
320
376
321
377
results .append (merge (paper , tracking [0 ]).for_printing ())
322
378
323
379
return results
324
380
325
- CSV_FILES_TO_SYNC = [
326
- 'Cxx17Issues.csv' ,
327
- 'Cxx17Papers.csv' ,
328
- 'Cxx20Issues.csv' ,
329
- 'Cxx20Papers.csv' ,
330
- 'Cxx23Issues.csv' ,
331
- 'Cxx23Papers.csv' ,
332
- 'Cxx2cIssues.csv' ,
333
- 'Cxx2cPapers.csv' ,
334
- ]
335
-
336
- def main ():
381
+ CSV_FILES_TO_SYNC = {
382
+ 'Cxx17Issues.csv' : ['c++17' , 'lwg-issue' ],
383
+ 'Cxx17Papers.csv' : ['c++17' , 'wg21 paper' ],
384
+ 'Cxx20Issues.csv' : ['c++20' , 'lwg-issue' ],
385
+ 'Cxx20Papers.csv' : ['c++20' , 'wg21 paper' ],
386
+ 'Cxx23Issues.csv' : ['c++23' , 'lwg-issue' ],
387
+ 'Cxx23Papers.csv' : ['c++23' , 'wg21 paper' ],
388
+ 'Cxx2cIssues.csv' : ['c++26' , 'lwg-issue' ],
389
+ 'Cxx2cPapers.csv' : ['c++26' , 'wg21 paper' ],
390
+ }
391
+
392
+ def main (argv ):
393
+ import argparse
394
+ parser = argparse .ArgumentParser (prog = 'synchronize-status-files' ,
395
+ description = 'Synchronize the libc++ conformance status files with Github issues' )
396
+ parser .add_argument ('--validate-only' , action = 'store_true' ,
397
+ help = "Only perform the data validation of CSV files." )
398
+ parser .add_argument ('--create-new' , action = 'store_true' ,
399
+ help = "Create new Github issues for CSV rows that do not correspond to any existing Github issue." )
400
+ parser .add_argument ('--load-github-from' , type = str ,
401
+ help = "A json file to load the Github project information from instead of querying the API. This is useful for testing to avoid rate limiting." )
402
+ args = parser .parse_args (argv )
403
+
337
404
libcxx_root = pathlib .Path (os .path .dirname (os .path .dirname (os .path .abspath (__file__ ))))
338
405
339
- # Extract the list of PaperInfos from issues we're tracking on Github.
340
- print ("Loading all issues from Github" )
341
- gh_command_line = ['gh' , 'project' , 'item-list' , LIBCXX_CONFORMANCE_PROJECT , '--owner' , 'llvm' , '--format' , 'json' , '--limit' , '9999999' ]
342
- project_info = json .loads (subprocess .check_output (gh_command_line ))
406
+ # Perform data validation for all the CSV files.
407
+ print ("Performing data validation of the CSV files" )
408
+ for filename in CSV_FILES_TO_SYNC :
409
+ csv = load_csv (libcxx_root / 'docs' / 'Status' / filename )
410
+ for row in csv [1 :]: # Skip the header
411
+ if row [0 ] != "" : # Skip separator rows
412
+ PaperInfo .from_csv_row (row )
413
+
414
+ if args .validate_only :
415
+ return
416
+
417
+ # Load all the Github issues tracking papers from Github.
418
+ if args .load_github_from :
419
+ print (f"Loading all issues from { args .load_github_from } " )
420
+ with open (args .load_github_from , 'r' ) as f :
421
+ project_info = json .load (f )
422
+ else :
423
+ print ("Loading all issues from Github" )
424
+ gh_command_line = ['gh' , 'project' , 'item-list' , LIBCXX_CONFORMANCE_PROJECT , '--owner' , 'llvm' , '--format' , 'json' , '--limit' , '9999999' ]
425
+ project_info = json .loads (subprocess .check_output (gh_command_line ))
343
426
from_github = [PaperInfo .from_github_issue (i ) for i in project_info ['items' ]]
344
427
345
- for filename in CSV_FILES_TO_SYNC :
428
+ # Synchronize CSV files with the Github issues.
429
+ for (filename , labels ) in CSV_FILES_TO_SYNC .items ():
346
430
print (f"Synchronizing { filename } with Github issues" )
347
431
file = libcxx_root / 'docs' / 'Status' / filename
348
432
csv = load_csv (file )
349
- synced = sync_csv (csv , from_github )
433
+ synced = sync_csv (csv , from_github , create_new = args . create_new , labels = labels )
350
434
write_csv (file , synced )
351
435
352
436
if __name__ == '__main__' :
353
- main ()
437
+ import sys
438
+ main (sys .argv [1 :])
0 commit comments