The first publish spent 9 min 42 s in the download action for the 4.6 GB stable release; the 11.5 GB edge release would not have fitted in the job's 45 minutes. GitHub's artifact storage limits each connection to a few megabytes a second, and the action uses few. An artifact is one zip at a signed address. test/vm-image/nightly-fetch asks the API for that address and takes it with aria2c over 16 connections, checks the size and unpacks it. The same 4.6 GB artifact came down in 123 s that way. The publish job gets actions: read for it, and checks out test/vm-image for the script. test/unit/nightly-test.sh plays the network with stand-ins: the newest artifact of a name is the one fetched, a failed download is tried again with a new address, a wrong size is refused and not unpacked.
50 lines
2.4 KiB
Bash
Executable File
50 lines
2.4 KiB
Bash
Executable File
#!/bin/bash
|
|
# Fetches a run's artifact for the publish job, and fast.
|
|
#
|
|
# nightly-fetch <owner/repo> <run_id> <artifact_name> <out_dir>
|
|
#
|
|
# The download action takes a multi-gigabyte artifact over a few connections,
|
|
# and GitHub's storage limits each one to a few megabytes a second: an 11 GB
|
|
# release would outlast the job. An artifact is one zip at a signed address,
|
|
# so this asks the API for that address and takes it over 16 connections.
|
|
#
|
|
# Needs gh (with GH_TOKEN allowed to read the run's artifacts), curl, aria2c
|
|
# and unzip. A run started again keeps the artifacts of its earlier attempts
|
|
# under the same name: the newest is the one fetched.
|
|
set -euo pipefail
|
|
[[ $# -eq 4 ]] || { sed -n '2,13p' "$0" >&2; exit 2; }
|
|
repo=$1 run=$2 name=$3 out=$4
|
|
fail() { echo "nightly-fetch: $*" >&2; exit 1; }
|
|
|
|
id=$(gh api "repos/$repo/actions/runs/$run/artifacts" --paginate \
|
|
--jq ".artifacts[] | select(.name == \"$name\" and .expired == false) | .id" | sort -n | tail -n 1)
|
|
[[ -n $id ]] || fail "run $run has no artifact named $name"
|
|
size=$(gh api "repos/$repo/actions/artifacts/$id" --jq .size_in_bytes)
|
|
echo "nightly-fetch: artifact $id, $((size / 1048576)) MiB"
|
|
|
|
zip=$(mktemp -u "${TMPDIR:-/tmp}/nightly-fetch.XXXXXX.zip")
|
|
trap 'rm -f "$zip" "$zip.aria2"' EXIT
|
|
started=$(date +%s)
|
|
# The address is signed for a few minutes. A try that fails asks for a new one
|
|
# and carries on from what is already there.
|
|
for attempt in 1 2 3; do
|
|
url=$(curl -fsS -o /dev/null -w '%{redirect_url}' \
|
|
-H "Authorization: Bearer $(gh auth token)" -H "Accept: application/vnd.github+json" \
|
|
"https://api.github.com/repos/$repo/actions/artifacts/$id/zip")
|
|
[[ $url == https://* ]] || fail "the API gave no download address for artifact $id"
|
|
if aria2c --quiet --continue --max-connection-per-server=16 --split=16 --min-split-size=16M \
|
|
--file-allocation=none --dir="$(dirname "$zip")" --out="$(basename "$zip")" "$url"; then
|
|
break
|
|
fi
|
|
(( attempt < 3 )) || fail "the download failed three times"
|
|
echo "nightly-fetch: attempt $attempt failed, trying again" >&2
|
|
sleep 10
|
|
done
|
|
[[ $(stat -c %s "$zip") == "$size" ]] || fail "downloaded $(stat -c %s "$zip") bytes, the artifact is $size"
|
|
seconds=$(( $(date +%s) - started ))
|
|
echo "nightly-fetch: downloaded in $seconds s, $(( size / 1048576 / (seconds > 0 ? seconds : 1) )) MiB/s"
|
|
|
|
mkdir -p "$out"
|
|
unzip -q "$zip" -d "$out"
|
|
echo "nightly-fetch: $(find "$out" -type f | wc -l) files in $out"
|