OXYGENHUB
◆ main oxygenvcs-web / deploy / pull.sh
2.8 KiB · Bourne Again Shell (bash) · executable · last changed The site: a static product page, built by OxygenFlow, pulled by its host (refs #1) 9 days ago source blame history raw
1
#!/bin/sh
2
# Pull the newest passing build of the site from OxygenHub and make it
3
# live (oxygenvcs-web#1). Run by a timer on the host that serves the site
4
# (Earth now, a VisionFive 2 Lite later); the host needs no inbound
5
# access and no credentials, since the repository is public.
6
#
7
# Each run: find the newest passing run of main, and if it is not the one
8
# live already, download its site.tar.gz, check it against the run's
9
# provenance statement (the artifact's SHA-256, this repository, main),
10
# unpack it into releases/RUN and point `current` at it in one rename.
11
# The last five releases stay for a rollback: `ln -sfn releases/N current`.
12
#
13
# Needs: sh, curl, python3 (reading JSON), sha256sum, tar.
14
set -eu
15
HUB=${HUB:-https://oxygenhub.io}
16
REPO=${REPO:-andymccall/oxygenvcs-web}
17
BOOKMARK=${BOOKMARK:-main}
18
ROOT=${ROOT:-/srv/oxygenvcs}
19
KEEP=${KEEP:-5}
20
# Run after a new release goes live: busybox httpd resolves its document
21
# root when it starts, so the service restarts it (see README.md).
22
RELOAD=${RELOAD:-}
23
24
mkdir -p "$ROOT/releases"
25
cd "$ROOT"
26
json() { python3 -I -c "import json,sys; $1" ; }
27
28
run=$(curl -fsS "$HUB/api/v1/repos/$REPO/flow/runs?limit=20" | json "
29
runs=[r for r in json.load(sys.stdin) if r['bookmark']=='$BOOKMARK' and r['status']=='passed']
30
print(runs[0]['number'] if runs else '')")
31
[ -n "$run" ] || { echo "no passing run of $BOOKMARK yet"; exit 0; }
32
live=$(readlink current 2>/dev/null | sed 's|releases/||' || true)
33
[ "$run" = "$live" ] && exit 0
34
35
art="$HUB/$REPO/flow/$run/artifacts/site/site.tar.gz"
36
tmp=$(mktemp -d "$ROOT/.pull.XXXXXX")
37
trap 'rm -rf "$tmp"' EXIT
38
curl -fsS "$art" -o "$tmp/site.tar.gz"
39
curl -fsS "$art/provenance" -o "$tmp/provenance.json"
40
got=$(sha256sum "$tmp/site.tar.gz" | cut -d' ' -f1)
41
# The statement must name this file's digest, this repository and the
42
# bookmark. (Its DSSE signature is the hub's; checking that here is for
43
# when oh can verify provenance offline.)
44
json "
45
import base64
46
env=json.load(open('$tmp/provenance.json'))
47
st=json.loads(base64.b64decode(env['payload']))
48
digests=[s['digest'].get('sha256') for s in st.get('subject',[])]
49
p=st['predicate']['buildDefinition']['externalParameters']
50
assert '$got' in digests, 'the artifact does not match its provenance'
51
assert p['repository']=='$HUB/$REPO', 'provenance names another repository: '+p['repository']
52
assert p['bookmark']=='$BOOKMARK', 'provenance names another bookmark: '+p['bookmark']
53
" <"$tmp/provenance.json"
54
55
mkdir "$tmp/site"
56
tar -C "$tmp/site" -xzf "$tmp/site.tar.gz"
57
[ -f "$tmp/site/index.html" ] || { echo "run $run: no index.html in the site"; exit 1; }
58
rm -rf "releases/$run"
59
mv "$tmp/site" "releases/$run"
60
ln -sfn "releases/$run" current.new
61
mv -T current.new current
62
echo "live: run $run ($got)"
63
[ -z "$RELOAD" ] || sh -c "$RELOAD"
64
# Keep the newest few.
65
ls -1 releases | sort -n | head -n -"$KEEP" | while read -r old; do rm -rf "releases/$old"; done