|
| 1 | +#!/usr/bin/env python3 |
| 2 | +""" |
| 3 | +Upload CHANGES.rst to Tidelift as Markdown chunks |
| 4 | +""" |
| 5 | + |
| 6 | +import re |
| 7 | +import subprocess |
| 8 | + |
| 9 | +import requests |
| 10 | + |
| 11 | +class Buffer: |
| 12 | + def __init__(self): |
| 13 | + self.buffer = [] |
| 14 | + |
| 15 | + def append(self, text): |
| 16 | + self.buffer.append(text) |
| 17 | + |
| 18 | + def clear(self): |
| 19 | + self.buffer = [] |
| 20 | + |
| 21 | + def flush(self): |
| 22 | + buffered = "".join(self.buffer).strip() |
| 23 | + if buffered: |
| 24 | + yield ("text", buffered) |
| 25 | + self.clear() |
| 26 | + |
| 27 | + |
| 28 | +def parse_md(lines): |
| 29 | + buffer = Buffer() |
| 30 | + |
| 31 | + for line in lines: |
| 32 | + header_match = re.search(r"^(#+) (.+)$", line) |
| 33 | + is_header = bool(header_match) |
| 34 | + if is_header: |
| 35 | + yield from buffer.flush() |
| 36 | + hashes, text = header_match.groups() |
| 37 | + yield (f"h{len(hashes)}", text) |
| 38 | + else: |
| 39 | + buffer.append(line) |
| 40 | + yield from buffer.flush() |
| 41 | + |
| 42 | + |
| 43 | +def sections(parsed_data): |
| 44 | + """Convert a stream of parsed tokens into sections with text and notes. |
| 45 | +
|
| 46 | + Yields a stream of: |
| 47 | + ('h-level', 'header text', 'text') |
| 48 | +
|
| 49 | + """ |
| 50 | + header = None |
| 51 | + text = [] |
| 52 | + for ttype, ttext in parsed_data: |
| 53 | + if ttype.startswith('h'): |
| 54 | + if header: |
| 55 | + yield (*header, "\n".join(text)) |
| 56 | + text = [] |
| 57 | + notes = [] |
| 58 | + header = (ttype, ttext) |
| 59 | + elif ttype == "text": |
| 60 | + text.append(ttext) |
| 61 | + else: |
| 62 | + raise Exception(f"Don't know ttype {ttype!r}") |
| 63 | + yield (*header, "\n".join(text)) |
| 64 | + |
| 65 | + |
| 66 | +def relnotes(mdlines): |
| 67 | + for hlevel, htext, text in sections(parse_md(mdlines)): |
| 68 | + if hlevel == 'h2' and htext.startswith('Version '): |
| 69 | + version = htext.split()[1] |
| 70 | + yield version, text |
| 71 | + |
| 72 | +def convert_rst_file_to_markdown(rst_filename): |
| 73 | + markdown = subprocess.check_output(["pandoc", "-frst", "-tmarkdown_strict", "--atx-headers", rst_filename]) |
| 74 | + return markdown.decode("utf8") |
| 75 | + |
| 76 | +def update_release_note(package, version, text): |
| 77 | + url = f"https://api.tidelift.com/external-api/lifting/{package}/release-notes/{version}" |
| 78 | + with open("ci/tidelift.token") as ftoken: |
| 79 | + token = ftoken.read().strip() |
| 80 | + headers = { |
| 81 | + "Authorization": f"Bearer: {token}", |
| 82 | + } |
| 83 | + req_args = dict(url=url, data=text.encode('utf8'), headers=headers) |
| 84 | + result = requests.post(**req_args) |
| 85 | + if result.status_code == 409: |
| 86 | + result = requests.put(**req_args) |
| 87 | + print(f"{version}: {result.status_code}") |
| 88 | + |
| 89 | +def main(): |
| 90 | + markdown = convert_rst_file_to_markdown("CHANGES.rst") |
| 91 | + for version, text in relnotes(markdown.splitlines(True)): |
| 92 | + update_release_note("pypi/coverage", version, text) |
| 93 | + |
| 94 | +if __name__ == "__main__": |
| 95 | + main() |
0 commit comments