|
| 1 | +#!/usr/bin/env python3 |
| 2 | +""" |
| 3 | +This script downloads an artifact from a GitHub Action workflow run, |
| 4 | +unzips and and stores the files in a directory of your choice. |
| 5 | +
|
| 6 | +Use cases: |
| 7 | +* You want to locally test or inspect the generated artifact. |
| 8 | +* You want to upload the artifact to a remote repository (test.pypi.org for example) |
| 9 | + before a release. |
| 10 | +""" |
| 11 | +import io |
| 12 | +import os |
| 13 | +import sys |
| 14 | +import json |
| 15 | +import time |
| 16 | +import zipfile |
| 17 | +import argparse |
| 18 | + |
| 19 | +import urllib.request |
| 20 | + |
| 21 | +parser = argparse.ArgumentParser( |
| 22 | + description="Download an artifact from a GitHub Action workflow run." |
| 23 | +) |
| 24 | +parser.add_argument("sha", help="Commit hash") |
| 25 | +parser.add_argument("token", help="GitHub Personal Access Token.") |
| 26 | +parser.add_argument( |
| 27 | + "-w", |
| 28 | + "--workflow", |
| 29 | + default="OpenTimelineIO", |
| 30 | + help="Name of the workflow to download artifact from.", |
| 31 | +) |
| 32 | +parser.add_argument( |
| 33 | + "-a", "--artifact", default="wheels", help="Artifact name to download." |
| 34 | +) |
| 35 | +parser.add_argument( |
| 36 | + "-d", "--directory", default="dist", help="Directory where to write the artifact." |
| 37 | +) |
| 38 | + |
| 39 | +args = parser.parse_args() |
| 40 | + |
| 41 | +headers = { |
| 42 | + "Accept": "application/vnd.github.v3+json", |
| 43 | + "Authorization": "token {args.token}".format(args=args), |
| 44 | +} |
| 45 | + |
| 46 | +if os.path.exists(args.directory) and os.listdir(args.directory): |
| 47 | + sys.stderr.write( |
| 48 | + "{0!r} directory contains files. It should be empty.".format(args.directory) |
| 49 | + ) |
| 50 | + sys.exit(1) |
| 51 | + |
| 52 | +if not os.path.exists(args.directory): |
| 53 | + os.makedirs(args.directory) |
| 54 | + |
| 55 | +request = urllib.request.Request( |
| 56 | + "https://api.github.com/repos/JeanChristopheMorinPerso/OpenTimelineIO/actions/runs?status=success", # noqa: E501 |
| 57 | + headers=headers, |
| 58 | +) |
| 59 | +response = urllib.request.urlopen(request).read() |
| 60 | +workflow_runs = json.loads(response)["workflow_runs"] |
| 61 | +for run in workflow_runs: |
| 62 | + if run["head_sha"] == args.sha and run["name"] == args.workflow: |
| 63 | + workflow_run = run |
| 64 | + break |
| 65 | +else: |
| 66 | + sys.stderr.write( |
| 67 | + "No run for a workflow named {0!r} found for commit {1!r}.".format( |
| 68 | + args.workflow, args.sha |
| 69 | + ) |
| 70 | + ) |
| 71 | + sys.exit(1) |
| 72 | + |
| 73 | + |
| 74 | +print("Found workflow:") |
| 75 | +print(" Name: {0}".format(workflow_run["name"])) |
| 76 | +print(" Branch: {0}".format(workflow_run["head_branch"])) |
| 77 | +print(" Commit: {0}".format(workflow_run["head_sha"])) |
| 78 | +print(" Committer: {0}".format(workflow_run["head_commit"]["committer"])) |
| 79 | +print(" Run Number: {0}".format(workflow_run["run_number"])) |
| 80 | +print(" Status: {0}".format(workflow_run["status"])) |
| 81 | +print(" Conclusion: {0}".format(workflow_run["conclusion"])) |
| 82 | +print(" URL: {0}".format(workflow_run["html_url"])) |
| 83 | + |
| 84 | + |
| 85 | +print("Getting list of artifacts") |
| 86 | +request = urllib.request.Request(workflow_run["artifacts_url"], headers=headers) |
| 87 | +response = urllib.request.urlopen(request).read() |
| 88 | +artifacts = json.loads(response)["artifacts"] |
| 89 | +for artifact in artifacts: |
| 90 | + if artifact["name"] == args.artifact: |
| 91 | + artifact_download_url = artifact["archive_download_url"] |
| 92 | + break |
| 93 | +else: |
| 94 | + sys.stderr.write("No artifact named {0!r} found.".format(args.artifact)) |
| 95 | + sys.exit(1) |
| 96 | + |
| 97 | +print( |
| 98 | + "Downloading {0!r} artifact and unzipping to {1!r}".format( |
| 99 | + args.artifact, args.directory |
| 100 | + ) |
| 101 | +) |
| 102 | + |
| 103 | +request = urllib.request.Request(artifact_download_url, headers=headers) |
| 104 | +file_content = urllib.request.urlopen(request).read() |
| 105 | + |
| 106 | +zip_file = zipfile.ZipFile(io.BytesIO(file_content)) |
| 107 | +for zip_info in zip_file.infolist(): |
| 108 | + output_path = os.path.join(args.directory, zip_info.filename) |
| 109 | + |
| 110 | + print("Writing {0!r} to {1!r}".format(zip_info.filename, output_path)) |
| 111 | + with open(output_path, "wb") as fd: |
| 112 | + fd.write(zip_file.open(zip_info).read()) |
| 113 | + |
| 114 | + # Keep the timestamp! |
| 115 | + date_time = time.mktime(zip_info.date_time + (0, 0, -1)) |
| 116 | + os.utime(output_path, (date_time, date_time)) |
0 commit comments