#!/bin/bash # Fetches a run's artifact for the publish job, and fast. # # nightly-fetch # # 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"