22"""Add the interface coverage of a release to its release notes.
33
44The script reads `summary/<project>/<version>.json` from the coverage site and
5- writes a Markdown table between two markers in the release notes. Running it
6- again replaces the table, so it is safe to repeat. It uses `gh` to read and edit
5+ writes an `## Interface Coverage` section between two markers. Running it again
6+ replaces the section in place, so it is safe to repeat. Without the markers, the
7+ section is added at the end of the release notes. It uses `gh` to read and edit
78the release, which needs a token with write access to the repository contents.
89"""
910
1213import subprocess
1314import time
1415import urllib .error
16+ import urllib .parse
1517import urllib .request
1618
1719START = '<!-- interface-coverage:start -->'
@@ -42,8 +44,14 @@ def section(summary, baselines, profile):
4244 rows = list (dict .fromkeys (item ['baseline' ] for item in results ))
4345 if baselines :
4446 rows = [baseline for baseline in baselines if baseline in rows ]
45- lines = [START , '### Interface coverage' , '' ,
46- f"{ summary ['note' ]} Coverage is not a measure of completed behavior." , '' ,
47+ # Summary links are absolute when the site was generated with a site URL.
48+ site = results [0 ]['url' ].split ('index.html?' , 1 )[0 ]
49+ if not site .startswith (('http://' , 'https://' )):
50+ raise SystemExit ('The summary has relative links. Generate the site with a site URL.' )
51+ query = urllib .parse .urlencode (dict (project = summary ['project' ], version = summary ['version' ]),
52+ quote_via = urllib .parse .quote )
53+ lines = [START , '## Interface Coverage' , '' ,
54+ f"Full report: [{ summary ['project' ]} { summary ['version' ]} ]({ site } index.html?{ query } )" , '' ,
4755 '| Baseline | ' + ' | ' .join (scopes ) + ' |' , '| --- | ' + ' | ' .join ('---:' for _ in scopes ) + ' |' ]
4856 for baseline in rows :
4957 cells = []
@@ -58,6 +66,14 @@ def section(summary, baselines, profile):
5866 return '\n ' .join (lines + [END ])
5967
6068
69+ def replacing_section (body , block ):
70+ """Replaces the section between the markers, or adds it at the end of the notes."""
71+ if START in body and END in body :
72+ before , rest = body .split (START , 1 )
73+ return before + block + rest .split (END , 1 )[1 ]
74+ return f'{ body } \n \n { block } ' if body else block
75+
76+
6177def main ():
6278 parser = argparse .ArgumentParser (description = __doc__ .split ('\n \n ' )[0 ])
6379 parser .add_argument ('--repository' , default = 'OpenSwiftUIProject/OpenSwiftUI' )
@@ -75,11 +91,7 @@ def main():
7591 block = section (summary , arguments .baseline , arguments .profile )
7692 body = subprocess .check_output (['gh' , 'release' , 'view' , arguments .version , '--repo' , arguments .repository ,
7793 '--json' , 'body' , '--jq' , '.body' ], text = True ).rstrip ('\n ' )
78- if START in body and END in body :
79- before , rest = body .split (START , 1 )
80- body = before + block + rest .split (END , 1 )[1 ]
81- else :
82- body = f'{ body } \n \n { block } ' if body else block
94+ body = replacing_section (body , block )
8395 if arguments .dry_run :
8496 print (body )
8597 return
0 commit comments