bootstrap-repo.sh (5135B)
1 #!/bin/sh 2 # mk/bootstrap-repo.sh - ensure an APKINDEX exists in the S3 repo. 3 # 4 # If the bucket already has x86_64/APKINDEX.tar.gz, do nothing. 5 # Otherwise publish an empty signed index so `apk add` against the 6 # repo stops 404ing before the first real package has been published. 7 set -eu 8 9 HERE=$(cd "$(dirname "$0")" && pwd) 10 ROOT=$(cd "${HERE}/.." && pwd) 11 12 ARCH="${ARCH:-x86_64}" 13 case "${ARCH}" in arm64) ARCH=aarch64 ;; esac 14 15 for v in BUCKET_ACCESS_KEY BUCKET_SECRET_KEY BUCKET_NAME BUCKET_ENDPOINT BUCKET_REGION BUCKET_PUBLIC_URL; do 16 eval "val=\${${v}:-}" 17 [ -n "${val}" ] || { echo "bootstrap-repo.sh: ${v} is not set" >&2; exit 1; } 18 done 19 20 url="${BUCKET_ENDPOINT%/}/${BUCKET_NAME}/${ARCH}/APKINDEX.tar.gz" 21 public_url="${BUCKET_PUBLIC_URL%/}/${ARCH}/APKINDEX.tar.gz" 22 tmp_body=$(mktemp) 23 code=$(curl -sS -o "${tmp_body}" -w '%{http_code}' \ 24 --aws-sigv4 "aws:amz:${BUCKET_REGION}:s3" \ 25 --user "${BUCKET_ACCESS_KEY}:${BUCKET_SECRET_KEY}" \ 26 -H "x-amz-content-sha256: UNSIGNED-PAYLOAD" \ 27 "${url}") || code=000 28 29 case "${code}" in 30 2*) rm -f "${tmp_body}" 31 anon_code=$(curl -sS -o /dev/null -w '%{http_code}' "${public_url}") || anon_code=000 32 case "${anon_code}" in 2*) echo "==> repo already has ${ARCH}/APKINDEX.tar.gz (${code}, anon ${anon_code}), nothing to do"; exit 0 ;; *) echo "==> repo has ${ARCH}/APKINDEX.tar.gz but public fetch is ${anon_code}, re-publishing" ;; esac 33 ;; 34 404) rm -f "${tmp_body}"; echo "==> no ${ARCH}/APKINDEX.tar.gz (404), bootstrapping empty index" ;; 35 000) echo "bootstrap-repo.sh: could not reach ${url} (curl failed)" >&2; cat "${tmp_body}" >&2 2>/dev/null || true; rm -f "${tmp_body}"; exit 1 ;; 36 400) echo "bootstrap-repo.sh: bad request (400) for ${url} -- check BUCKET_ENDPOINT/BUCKET_NAME/BUCKET_REGION" >&2; echo "--- S3 response ---" >&2; cat "${tmp_body}" >&2 2>/dev/null || true; echo "--- env ---" >&2; echo "ENDPOINT=${BUCKET_ENDPOINT} NAME=${BUCKET_NAME} REGION=${BUCKET_REGION}" >&2; rm -f "${tmp_body}"; exit 1 ;; 37 403) echo "bootstrap-repo.sh: permission denied (403) for ${url} -- check BUCKET credentials/region" >&2; echo "--- S3 response ---" >&2; cat "${tmp_body}" >&2 2>/dev/null || true; rm -f "${tmp_body}"; exit 1 ;; 38 *) echo "bootstrap-repo.sh: unexpected HTTP ${code} for ${url}" >&2; cat "${tmp_body}" >&2 2>/dev/null || true; rm -f "${tmp_body}"; exit 1 ;; 39 esac 40 41 mkdir -p "${ROOT}/build/repo/${ARCH}" 42 43 export PATH="${ROOT}/build/host/bin:${PATH}" 44 command -v apk >/dev/null 2>&1 || { echo "bootstrap-repo.sh: no apk on PATH; run mk/bootstrap-host.sh first" >&2; exit 1; } 45 46 . "${HERE}/sign-key.inc" 47 KEY=$(resolve_sign_key) || { echo "bootstrap-repo.sh: no signing key" >&2; exit 1; } 48 KEYNAME=$(basename "${KEY}" .rsa) 49 50 if [ ! -x "${HERE}/pax-tar" ] || [ "${HERE}/pax-tar.c" -nt "${HERE}/pax-tar" ]; then 51 cc -std=c99 -O2 -Wall -Wextra -o "${HERE}/pax-tar" "${HERE}/pax-tar.c" 52 fi 53 54 cd "${ROOT}/build/repo/${ARCH}" 55 56 # apk index with no inputs still needs to produce a valid archive; 57 # `echo` avoids the shell expanding *.apk to a literal when empty. 58 echo -n "" | tar -czf APKINDEX.tar.gz -T /dev/null 2>/dev/null || true 59 # Try the real tool first; fall back to the empty tar if it produced nothing. 60 if [ ! -s APKINDEX.tar.gz ] || ! tar -tzf APKINDEX.tar.gz >/dev/null 2>&1; then 61 : > APKINDEX 62 tar -czf APKINDEX.tar.gz APKINDEX 63 rm -f APKINDEX 64 fi 65 66 # Rebuild properly if apk can do it (covers the signed-index header fixup below) 67 # and ensures the index is well-formed even when empty. 68 if ls ./*.apk >/dev/null 2>&1; then 69 apk index --allow-untrusted -o APKINDEX.tar.gz ./*.apk 70 else 71 # empty repo: create minimal index that `apk add` accepts 72 rm -f APKINDEX.tar.gz 73 mkdir -p .idx-empty 74 : > .idx-empty/APKINDEX 75 tar -czf APKINDEX.tar.gz -C .idx-empty APKINDEX 76 rm -rf .idx-empty 77 fi 78 79 echo "==> signing APKINDEX.tar.gz with ${KEYNAME}" 80 openssl dgst -sha1 -sign "${KEY}" -out ".SIGN.RSA.${KEYNAME}.rsa.pub" APKINDEX.tar.gz 81 mkdir -p "${ROOT}/build/work/.idxsig" 82 cp ".SIGN.RSA.${KEYNAME}.rsa.pub" "${ROOT}/build/work/.idxsig/" 83 "${HERE}/pax-tar" --no-checksum "${ROOT}/build/work/.idxsig" idxsig.tar 84 sigsiz=$(stat -c%s idxsig.tar) 85 head -c $((sigsiz - 1024)) idxsig.tar > idxsignotr.tar 86 gzip -n -9 -f idxsignotr.tar 87 cat idxsignotr.tar.gz APKINDEX.tar.gz > APKINDEX.signed.tar.gz 88 mv APKINDEX.signed.tar.gz APKINDEX.tar.gz 89 rm -f ".SIGN.RSA.${KEYNAME}.rsa.pub" idxsig.tar idxsignotr.tar.gz 90 rm -rf "${ROOT}/build/work/.idxsig" 91 92 echo "==> publishing ${ARCH}/APKINDEX.tar.gz" 93 sha=$(sha256sum APKINDEX.tar.gz | cut -d' ' -f1) 94 tmp_put=$(mktemp) 95 code=$(curl -sS -o "${tmp_put}" -w '%{http_code}' \ 96 --aws-sigv4 "aws:amz:${BUCKET_REGION}:s3" \ 97 --user "${BUCKET_ACCESS_KEY}:${BUCKET_SECRET_KEY}" \ 98 -H "x-amz-content-sha256: ${sha}" \ 99 -X PUT --upload-file APKINDEX.tar.gz \ 100 "${url}") || { echo "bootstrap-repo.sh: upload failed" >&2; cat "${tmp_put}" >&2 2>/dev/null || true; rm -f "${tmp_put}"; exit 1; } 101 case "${code}" in 2*) rm -f "${tmp_put}"; echo "==> bootstrapped ${ARCH}/APKINDEX.tar.gz (${code})" ;; *) echo "bootstrap-repo.sh: upload returned HTTP ${code}" >&2; cat "${tmp_put}" >&2 2>/dev/null || true; rm -f "${tmp_put}"; exit 1 ;; esac