From dbdb682225c2d7f6813f3fbcfad9a7dd37e657e0 Mon Sep 17 00:00:00 2001 From: Silvano Cerza <3314350+silvanocerza@users.noreply.github.com> Date: Tue, 28 Mar 2023 09:56:42 +0200 Subject: [PATCH] Enhance release_docs.py (#4459) --- .github/utils/release_docs.py | 184 +++++++++----------- .github/workflows/minor_version_release.yml | 6 +- 2 files changed, 81 insertions(+), 109 deletions(-) diff --git a/.github/utils/release_docs.py b/.github/utils/release_docs.py index 27929a5e3..2513873e6 100644 --- a/.github/utils/release_docs.py +++ b/.github/utils/release_docs.py @@ -1,135 +1,107 @@ -""" -Use the Readme API to create fork a new version of the docs and also to rename the latest unstable. -For example, if Readme currently has v1.9 and v1.10-unstable, and this script is used to release v1.10, -Readme will then contain v1.9, v1.10 (forked from v1.10-unstable) and v1.11-unstable (renamed from v1.10-unstable). -""" - +import os +import re +import sys import base64 import argparse import requests -def assert_valid_version(new_version): - if not new_version.startswith("v"): - raise ValueError("Version must start with 'v'") - if not new_version[1:].replace(".", "").replace("-latest", "").isdigit(): - raise ValueError("Version must be a number") - return True +VERSION_VALIDATOR = re.compile(r"^[0-9]+\.[0-9]+$") + + +class ReadmeAuth(requests.auth.AuthBase): + def __call__(self, r): + r.headers["authorization"] = f"Basic {readme_token()}" + return r + + +def readme_token(): + api_key = os.getenv("RDME_API_KEY", None) + if not api_key: + raise Exception("RDME_API_KEY env var is not set") + + api_key = f"{api_key}:" + return base64.b64encode(api_key.encode("utf-8")).decode("utf-8") def get_versions(): + """ + Return all versions currently published in Readme.io. + """ url = "https://dash.readme.com/api/v1/version" - headers = {"Accept": "application/json", "Authorization": api_key_b64} - response = requests.get(url, headers=headers) - return [v["version"] for v in response.json()] + res = requests.get(url, auth=ReadmeAuth(), timeout=30) + res.raise_for_status() + return [v["version_clean"] for v in res.json()] -def create_version(new_version, fork_from_version, is_stable=False): - url = "https://dash.readme.com/api/v1/version" - payload = { - "is_beta": False, - "version": new_version, - "from": fork_from_version, - "is_hidden": False, - "is_stable": is_stable, - } - headers = {"Accept": "application/json", "Content-Type": "application/json", "Authorization": api_key_b64} - response = requests.post(url, json=payload, headers=headers) - print("create_version()") - print(response.text) +def create_new_unstable(current, new): + """ + Create new version by copying current. + + :param current: Existing current unstable version + :param new: Non existing new unstable version + """ + url = "https://dash.readme.com/api/v1/version/" + payload = {"is_beta": False, "version": new, "from": current, "is_hidden": False, "is_stable": False} + res = requests.post(url, json=payload, auth=ReadmeAuth(), timeout=30) + res.raise_for_status() -def update_version_name(old_unstable_name, new_unstable_name): - url = "https://dash.readme.com/api/v1/version/{}".format(old_unstable_name) - payload = {"is_beta": False, "version": new_unstable_name, "from": old_unstable_name, "is_hidden": False} +def promote_unstable_to_stable(unstable, stable): + """ + Rename the current unstable to stable and set it as stable. - headers = {"accept": "application/json", "content-type": "application/json", "authorization": api_key_b64} - - response = requests.put(url, json=payload, headers=headers) - print(response.text) + :param unstable: Existing unstable version + :param stable: Non existing new stable version + """ + url = f"https://dash.readme.com/api/v1/version/{unstable}" + payload = {"is_beta": False, "version": stable, "from": unstable, "is_hidden": False, "is_stable": True} + res = requests.put(url, json=payload, auth=ReadmeAuth(), timeout=30) + res.raise_for_status() -def generate_new_unstable_name(unstable_version_name): - version_digits_str = unstable_version_name[1:].replace("-unstable", "") - version_digits_split = version_digits_str.split(".") - version_digits_split[1] = str(int(version_digits_split[1]) + 1) - incremented_version_digits = ".".join(version_digits_split) - new_unstable = "v" + incremented_version_digits + "-unstable" - return new_unstable - - -def get_categories(version): - url = "https://dash.readme.com/api/v1/categories?perPage=10&page=1" - headers = {"accept": "application/json", "x-readme-version": version, "authorization": api_key_b64} - response = requests.get(url, headers=headers) - return response.text - - -def hide_version(depr_version): - url = "https://dash.readme.com/api/v1/version/{}".format(depr_version) - payload = {"is_beta": False, "version": depr_version, "from": "", "is_hidden": True} - - headers = {"accept": "application/json", "content-type": "application/json", "authorization": api_key_b64} - - response = requests.put(url, json=payload, headers=headers) - print(response.text) - - -def generate_new_depr_name(depr_name): - version_digits_str = depr_name[1:] - version_digits_split = version_digits_str.split(".") - version_digits_split[1] = str(int(version_digits_split[1]) + 1) - incremented_version_digits = ".".join(version_digits_split) - new_depr = "v" + incremented_version_digits + "-and-older" - return new_depr - - -def get_old_and_older_name(versions): - ret = [] - for v in versions: - if v.endswith("-and-older"): - ret.append(v) - if len(ret) == 1: - return ret[0] - return None - - -def generate_new_and_older_name(old): - digits_str = old[1:].replace("-and-older", "") - digits_split = digits_str.split(".") - digits_split[1] = str(int(digits_split[1]) + 1) - incremented_digits = ".".join(digits_split) - new = "v" + incremented_digits + "-and-older" - return new +def calculate_new_unstable(version): + # version must be formatted like so . + major, minor = version.split(".") + return f"{major}.{int(minor) + 1}.0-unstable" if __name__ == "__main__": - # Comments below are for a case where we are releasing new_version="v1.9". - # This requires for v1.9-unstable and v1.8 to exist in Readme. - parser = argparse.ArgumentParser() parser.add_argument( - "-v", "--version", help="The new minor version that is being released (e.g. v1.9.1).", required=True + "-v", "--new-version", help="The new minor version that is being released (e.g. 1.9).", required=True ) - parser.add_argument("-k", "--key", help="The Readme API key for Haystack documentation.", required=True) args = parser.parse_args() - api_key = args.key - api_key += ":" - api_key_b64 = "Basic " + base64.b64encode(api_key.encode("utf-8")).decode("utf-8") + if VERSION_VALIDATOR.match(args.new_version) is None: + sys.exit("Version must be formatted like so .") + + # This two are the version that we must have published in the end + new_stable = f"{args.new_version}.0" + new_unstable = calculate_new_unstable(args.new_version) - new_version = args.version - # Drop the patch version, e.g. v1.9.1 -> v1.9 - new_version = ".".join(new_version.split(".")[:2]) versions = get_versions() + new_stable_is_published = new_stable in versions + new_unstable_is_published = new_unstable in versions - curr_unstable = new_version + "-unstable" - assert new_version[1:] not in versions, "Version {} already exists in Readme.".format(new_version[1:]) - assert curr_unstable[1:] in versions, "Version {} does not exist in Readme.".format(curr_unstable[1:]) + if new_stable_is_published and new_unstable_is_published: + # If both versions are published there's nothing to do. + # We fail gracefully. + print(f"Both new version {new_stable} and {new_unstable} are already published.") + sys.exit(0) + elif new_stable_is_published or new_unstable_is_published: + # Either new stable or unstable is already published, it's to risky to + # proceed so we abort the publishing process. + sys.exit(f"Either version {new_stable} or {new_unstable} are already published. Too risky to proceed.") - # create v1.9 forked from v1.9-unstable - create_version(new_version=new_version, fork_from_version=curr_unstable, is_stable=False) + # This version must exist since it's the one we're trying to promote + # to stable. + current_unstable = f"{new_stable}-unstable" - # rename v1.9-unstable to v1.10-unstable - new_unstable = generate_new_unstable_name(curr_unstable) - update_version_name(curr_unstable, new_unstable) + if current_unstable not in versions: + sys.exit(f"Can't find version {current_unstable} to promote to {new_stable}") + + # First we create new unstable from the currently existing one + create_new_unstable(current_unstable, new_unstable) + # Then we promote the current unstable to stable since it's the one being published + promote_unstable_to_stable(current_unstable, new_stable) diff --git a/.github/workflows/minor_version_release.yml b/.github/workflows/minor_version_release.yml index cad80ba1e..20e25255b 100644 --- a/.github/workflows/minor_version_release.yml +++ b/.github/workflows/minor_version_release.yml @@ -49,9 +49,9 @@ jobs: - name: Install release_docs.py dependencies run: pip install requests - # Note that patch versions all sync to the one readme minor version - # e.g. Haystack 1.9.1 and 1.9.2 both map to Readme 1.9 - name: Release Readme version + env: + RDME_API_KEY: ${{ secrets.README_API_KEY }} run: | git checkout main - python ./.github/utils/release_docs.py --version v${{ steps.versions.outputs.current_release_minor }} --key ${{ secrets.README_API_KEY }} + python ./.github/utils/release_docs.py --new-version ${{ steps.versions.outputs.current_release_minor }}