diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
commit | 36d22d82aa202bb199967e9512281e9a53db42c9 (patch) | |
tree | 105e8c98ddea1c1e4784a60a5a6410fa416be2de /testing/web-platform/tests/tools/ci/manifest_build.py | |
parent | Initial commit. (diff) | |
download | firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip |
Adding upstream version 115.7.0esr.upstream/115.7.0esr
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'testing/web-platform/tests/tools/ci/manifest_build.py')
-rw-r--r-- | testing/web-platform/tests/tools/ci/manifest_build.py | 200 |
1 files changed, 200 insertions, 0 deletions
diff --git a/testing/web-platform/tests/tools/ci/manifest_build.py b/testing/web-platform/tests/tools/ci/manifest_build.py new file mode 100644 index 0000000000..6d93a35258 --- /dev/null +++ b/testing/web-platform/tests/tools/ci/manifest_build.py @@ -0,0 +1,200 @@ +# mypy: allow-untyped-defs + +import json +import logging +import os +import subprocess +import sys +import tempfile + +import requests + +here = os.path.abspath(os.path.dirname(__file__)) +wpt_root = os.path.abspath(os.path.join(here, os.pardir, os.pardir)) + +if wpt_root not in sys.path: + sys.path.append(wpt_root) + +from tools.wpt.testfiles import get_git_cmd + +logging.basicConfig(level=logging.INFO) +logger = logging.getLogger(__name__) + + +class Status: + SUCCESS = 0 + FAIL = 1 + + +def run(cmd, return_stdout=False, **kwargs): + logger.info(" ".join(cmd)) + if return_stdout: + f = subprocess.check_output + else: + f = subprocess.check_call + return f(cmd, **kwargs) + + +def create_manifest(path): + run(["./wpt", "manifest", "-p", path]) + + +def compress_manifest(path): + for args in [["gzip", "-k", "-f", "--best"], + ["bzip2", "-k", "-f", "--best"], + ["zstd", "-k", "-f", "--ultra", "-22", "-q"]]: + run(args + [path]) + + +def request(url, desc, method=None, data=None, json_data=None, params=None, headers=None): + github_token = os.environ.get("GITHUB_TOKEN") + default_headers = { + "Authorization": "token %s" % github_token, + "Accept": "application/vnd.github.machine-man-preview+json" + } + + _headers = default_headers + if headers is not None: + _headers.update(headers) + + kwargs = {"params": params, + "headers": _headers} + try: + logger.info("Requesting URL %s" % url) + if json_data is not None or data is not None: + if method is None: + method = requests.post + kwargs["json"] = json_data + kwargs["data"] = data + elif method is None: + method = requests.get + + resp = method(url, **kwargs) + + except Exception as e: + logger.error(f"{desc} failed:\n{e}") + return None + + try: + resp.raise_for_status() + except requests.HTTPError: + logger.error("%s failed: Got HTTP status %s. Response:" % + (desc, resp.status_code)) + logger.error(resp.text) + return None + + try: + return resp.json() + except ValueError: + logger.error("%s failed: Returned data was not JSON Response:" % desc) + logger.error(resp.text) + + +def get_pr(owner, repo, sha): + data = request("https://api.github.com/search/issues?q=type:pr+is:merged+repo:%s/%s+sha:%s" % + (owner, repo, sha), "Getting PR") + if data is None: + return None + + items = data["items"] + if len(items) == 0: + logger.error("No PR found for %s" % sha) + return None + if len(items) > 1: + logger.warning("Found multiple PRs for %s" % sha) + + pr = items[0] + + return pr["number"] + + +def create_release(manifest_path, owner, repo, sha, tag, body): + logger.info(f"Creating a release for tag='{tag}', target_commitish='{sha}'") + create_url = f"https://api.github.com/repos/{owner}/{repo}/releases" + create_data = {"tag_name": tag, + "target_commitish": sha, + "name": tag, + "body": body, + "draft": True} + create_resp = request(create_url, "Release creation", json_data=create_data) + if not create_resp: + return False + + # Upload URL contains '{?name,label}' at the end which we want to remove + upload_url = create_resp["upload_url"].split("{", 1)[0] + + upload_exts = [".gz", ".bz2", ".zst"] + for upload_ext in upload_exts: + upload_filename = f"MANIFEST-{sha}.json{upload_ext}" + params = {"name": upload_filename, + "label": "MANIFEST.json%s" % upload_ext} + + with open(f"{manifest_path}{upload_ext}", "rb") as f: + upload_data = f.read() + + logger.info("Uploading %s bytes" % len(upload_data)) + + upload_resp = request(upload_url, "Manifest upload", data=upload_data, params=params, + headers={'Content-Type': 'application/octet-stream'}) + if not upload_resp: + return False + + release_id = create_resp["id"] + edit_url = f"https://api.github.com/repos/{owner}/{repo}/releases/{release_id}" + edit_data = create_data.copy() + edit_data["draft"] = False + edit_resp = request(edit_url, "Release publishing", method=requests.patch, json_data=edit_data) + if not edit_resp: + return False + + logger.info("Released %s" % edit_resp["html_url"]) + return True + + +def should_dry_run(): + with open(os.environ["GITHUB_EVENT_PATH"]) as f: + event = json.load(f) + logger.info(json.dumps(event, indent=2)) + + if "pull_request" in event: + logger.info("Dry run for PR") + return True + if event.get("ref") != "refs/heads/master": + logger.info("Dry run for ref %s" % event.get("ref")) + return True + return False + + +def main(): + dry_run = should_dry_run() + + manifest_path = os.path.join(tempfile.mkdtemp(), "MANIFEST.json") + + create_manifest(manifest_path) + + compress_manifest(manifest_path) + + owner, repo = os.environ["GITHUB_REPOSITORY"].split("/", 1) + + git = get_git_cmd(wpt_root) + head_rev = git("rev-parse", "HEAD").strip() + body = git("show", "--no-patch", "--format=%B", "HEAD") + + if dry_run: + return Status.SUCCESS + + pr = get_pr(owner, repo, head_rev) + if pr is None: + return Status.FAIL + tag_name = "merge_pr_%s" % pr + + if not create_release(manifest_path, owner, repo, head_rev, tag_name, body): + return Status.FAIL + + return Status.SUCCESS + + +if __name__ == "__main__": + code = main() # type: ignore + assert isinstance(code, int) + sys.exit(code) |