From 24fe4e2d4d74fdbcb4991dcd3b6297dca03039c1 Mon Sep 17 00:00:00 2001 From: Tobias Waldekranz Date: Tue, 3 Sep 2024 09:51:08 +0200 Subject: [PATCH] utils/gh-dl-artifact.sh: Add support script to download artifacts This will make it easier to locally test images when reviewing PRs, and will also be a useful tool when running tests on physical test rigs. [skip ci] --- doc/developers-guide.md | 27 +++++++ utils/gh-dl-artifact.sh | 161 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 188 insertions(+) create mode 100755 utils/gh-dl-artifact.sh diff --git a/doc/developers-guide.md b/doc/developers-guide.md index 8644e518..bcfd3572 100644 --- a/doc/developers-guide.md +++ b/doc/developers-guide.md @@ -166,6 +166,33 @@ The Infix automated test suite is built around Qemu and [Qeneth][2], see: * [Docker Image](../test/docker/README.md) +Reviewing +--------- + +While reviewing a pull request, you might find yourself wanting to +play around with a VM running that _exact_ version. For such +occations, [gh-dl-artifact.sh](../utils/gh-dl-artifact.sh) is your +friend in need! It will use the [GitHub CLI +(gh)](https://cli.github.com) to locate a prebuilt image from our CI +workflow, download it, and prepare a local output directory from which +you can launch both `make run` instances, and run regression tests +with `make test` and friends. + +For example, if you are curious about how PR 666 behaves in some +particular situation, you can use `gh` to switch to that branch, from +which `gh-dl-artifact.sh` can then download and prepare the +corresponding image for execution with our normal tooling: + + gh pr checkout 666 + ./utils/gh-dl-artifact.sh + cd x-artifact-a1b2c3d4-x86_64 + make run + +> **Note:** CI artifacts are built from a merge commit of the source +> and target branches. Therefore, the version in the Infix banner +> will not match the SHA of the commit you have checked out. + + Contributing ------------ diff --git a/utils/gh-dl-artifact.sh b/utils/gh-dl-artifact.sh new file mode 100755 index 00000000..d80888bf --- /dev/null +++ b/utils/gh-dl-artifact.sh @@ -0,0 +1,161 @@ +#!/bin/sh + +set -e + +usage() +{ + local me="$(basename $0)" + + cat <] [] + +Download the artifact of the specified revision, or HEAD if not +specified, that was built by GitHub's CI action; prepare a new output +tree (by applying the corresponding defconfig); and extract the +artifact. + + + Locate an artifact built from this revision. By default, look for + an artifact built from the current HEAD. + + Options: + + -a + Locate an artifact built for . By default, look for an + artifact built for the x86_64 architecture. + + -b
+ Create a new Git branch from named
, and switch + to it. By default, stay on the current branch if is + "HEAD", otherwise switch to one named "gh-dl-". + + -h + Show this help message and exit. + + -o Prepare the artifact in . By default, + the artifact is extracted in an output directory called + "x-gh-dl--". + + + Examples: + + Test the latest image for PR #666: + gh pr checkout 666 + $0 + cd x-artifact-a1b2c3d4-x86_64 + make run + +EOF +} + +ixdir=$(readlink -f $(dirname "$(readlink -f "$0")")/..) +. $ixdir/board/common/lib.sh + +workdir=$(mktemp --tmpdir -d infix-gh-dl-XXXXXXXX) +cleanup() +{ + rm -rf $workdir +} +trap cleanup EXIT + +topdir=$(git rev-parse --show-toplevel) + +apibase="repos/{owner}/{repo}" +arch=x86_64 +rev=$(git rev-parse HEAD) +br= +outdir= + +# getopt +while getopts "a:b:ho:" opt; do + case ${opt} in + a) + arch="${OPTARG}" + ;; + b) + br="${OPTARG}" + ;; + h) + usage && exit + ;; + o) + outdir="${OPTARG}" + ;; + esac +done +shift $((OPTIND - 1)) + +echo $br + +case $# in + 0) + ;; + 1) + rev=$(git rev-parse $1 2>/dev/null || true) + [ "$1" = "HEAD" ] || br=__DEFAULT + ;; + *) + usage && exit 1 + ;; +esac + + +ixmsg "Locating $arch build of $rev" + +sha=$(gh api "$apibase/commits/$rev" -q .sha || die "ERROR: Unknown revision \"$rev\"") +echo "SHA: $sha" + +run=$(gh api "$apibase/actions/runs?head_sha=$sha" \ + -q '[.workflow_runs[] | select(.name == "Bob the Builder")][0].id' \ + || die "ERROR: Found no workflow runs associated with $rev") +echo "Run: $run" + +gh api "$apibase/actions/runs/$run/artifacts" \ + -q ".artifacts[] | select(.name == \"artifact-$arch\")" >$workdir/artifact.json \ + || die "ERROR: Found no $arch artifact associated with run $run" +artifact="$(jq .id $workdir/artifact.json)" +echo "Artifact: $artifact" + +url="$(jq -r .archive_download_url $workdir/artifact.json)" +slug=$(echo $sha | head -c 8) + +outdir=${outdir:-$topdir/x-gh-dl-$slug-$arch} +imgdir=$outdir/images + + +if [ "$br" ]; then + if [ "$br" = "__DEFAULT" ]; then + br=gh-dl-$slug + fi + + ixmsg "Checking out $slug to branch $br" + + if ! git cat-file -e $sha 2>/dev/null; then + echo Artifact is built from $slug, which is not available locally. Updating all remotes... + git remote update + + if ! git cat-file -e $sha 2>/dev/null; then + die "ERROR: Unable to locate $slug" + fi + fi + + git checkout --recurse-submodules -B $br $sha +fi + +ixmsg "Setting up output directory $outdir" +make -C $topdir O=$outdir ${arch}_defconfig + + +ixmsg "Downloading artifact" +zip=$workdir/zip +gh api $url >$zip + + +ixmsg "Extracting artifact" +mkdir -p $imgdir +unzip -p $zip infix-$arch.tar.gz | gunzip | tar -C $imgdir -x --strip-components=1 + + +ixmsg "Done" +echo "From $outdir, you can now execute \`make run\`, " \ + "\`make test\` etc., using artifact $artifact" | fmt