bootstrap-repo.sh (5423B)
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 # shellcheck disable=SC1091 13 . "${HERE}/channels.inc" 14 15 CHANNEL="${UNOS_CHANNEL:-lts}" 16 case "${1:-}" in 17 --channel) CHANNEL="${2:?--channel needs a value}"; shift 2 ;; 18 --channel=*) CHANNEL="${1#--channel=}"; shift ;; 19 esac 20 21 ARCH=$(chan_arch "${ARCH:-x86_64}") 22 PREFIX=$(chan_prefix "${CHANNEL}" "${ARCH}") 23 24 for v in BUCKET_ACCESS_KEY BUCKET_SECRET_KEY BUCKET_NAME BUCKET_ENDPOINT BUCKET_REGION BUCKET_PUBLIC_URL; do 25 eval "val=\${${v}:-}" 26 [ -n "${val}" ] || { echo "bootstrap-repo.sh: ${v} is not set" >&2; exit 1; } 27 done 28 29 url="${BUCKET_ENDPOINT%/}/${BUCKET_NAME}/${PREFIX}/APKINDEX.tar.gz" 30 public_url=$(chan_url "${BUCKET_PUBLIC_URL}" "${CHANNEL}" "${ARCH}" APKINDEX.tar.gz) 31 tmp_body=$(mktemp) 32 code=$(curl -sS -o "${tmp_body}" -w '%{http_code}' \ 33 --aws-sigv4 "aws:amz:${BUCKET_REGION}:s3" \ 34 --user "${BUCKET_ACCESS_KEY}:${BUCKET_SECRET_KEY}" \ 35 -H "x-amz-content-sha256: UNSIGNED-PAYLOAD" \ 36 "${url}") || code=000 37 38 case "${code}" in 39 2*) rm -f "${tmp_body}" 40 anon_code=$(curl -sS -o /dev/null -w '%{http_code}' "${public_url}") || anon_code=000 41 case "${anon_code}" in 2*) echo "==> repo already has ${PREFIX}/APKINDEX.tar.gz (${code}, anon ${anon_code}), nothing to do"; exit 0 ;; *) echo "==> repo has ${PREFIX}/APKINDEX.tar.gz but public fetch is ${anon_code}, re-publishing" ;; esac 42 ;; 43 404) rm -f "${tmp_body}"; echo "==> no ${PREFIX}/APKINDEX.tar.gz (404), bootstrapping empty index" ;; 44 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 ;; 45 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 ;; 46 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 ;; 47 *) echo "bootstrap-repo.sh: unexpected HTTP ${code} for ${url}" >&2; cat "${tmp_body}" >&2 2>/dev/null || true; rm -f "${tmp_body}"; exit 1 ;; 48 esac 49 50 REPODIR=$(chan_repodir "${CHANNEL}" "${ARCH}") 51 mkdir -p "${REPODIR}" 52 53 export PATH="${ROOT}/build/host/bin:${PATH}" 54 command -v apk >/dev/null 2>&1 || { echo "bootstrap-repo.sh: no apk on PATH; run mk/bootstrap-host.sh first" >&2; exit 1; } 55 56 . "${HERE}/sign-key.inc" 57 KEY=$(resolve_sign_key) || { echo "bootstrap-repo.sh: no signing key" >&2; exit 1; } 58 KEYNAME=$(basename "${KEY}" .rsa) 59 60 if [ ! -x "${HERE}/pax-tar" ] || [ "${HERE}/pax-tar.c" -nt "${HERE}/pax-tar" ]; then 61 cc -std=c99 -O2 -Wall -Wextra -o "${HERE}/pax-tar" "${HERE}/pax-tar.c" 62 fi 63 64 cd "${REPODIR}" 65 66 # apk index with no inputs still needs to produce a valid archive; 67 # `echo` avoids the shell expanding *.apk to a literal when empty. 68 echo -n "" | tar -czf APKINDEX.tar.gz -T /dev/null 2>/dev/null || true 69 # Try the real tool first; fall back to the empty tar if it produced nothing. 70 if [ ! -s APKINDEX.tar.gz ] || ! tar -tzf APKINDEX.tar.gz >/dev/null 2>&1; then 71 : > APKINDEX 72 tar -czf APKINDEX.tar.gz APKINDEX 73 rm -f APKINDEX 74 fi 75 76 # Rebuild properly if apk can do it (covers the signed-index header fixup below) 77 # and ensures the index is well-formed even when empty. 78 if ls ./*.apk >/dev/null 2>&1; then 79 apk index --allow-untrusted -o APKINDEX.tar.gz ./*.apk 80 else 81 # empty repo: create minimal index that `apk add` accepts 82 rm -f APKINDEX.tar.gz 83 mkdir -p .idx-empty 84 : > .idx-empty/APKINDEX 85 tar -czf APKINDEX.tar.gz -C .idx-empty APKINDEX 86 rm -rf .idx-empty 87 fi 88 89 echo "==> signing APKINDEX.tar.gz with ${KEYNAME}" 90 openssl dgst -sha1 -sign "${KEY}" -out ".SIGN.RSA.${KEYNAME}.rsa.pub" APKINDEX.tar.gz 91 mkdir -p "${ROOT}/build/work/.idxsig" 92 cp ".SIGN.RSA.${KEYNAME}.rsa.pub" "${ROOT}/build/work/.idxsig/" 93 "${HERE}/pax-tar" --no-checksum "${ROOT}/build/work/.idxsig" idxsig.tar 94 sigsiz=$(stat -c%s idxsig.tar) 95 head -c $((sigsiz - 1024)) idxsig.tar > idxsignotr.tar 96 gzip -n -9 -f idxsignotr.tar 97 cat idxsignotr.tar.gz APKINDEX.tar.gz > APKINDEX.signed.tar.gz 98 mv APKINDEX.signed.tar.gz APKINDEX.tar.gz 99 rm -f ".SIGN.RSA.${KEYNAME}.rsa.pub" idxsig.tar idxsignotr.tar.gz 100 rm -rf "${ROOT}/build/work/.idxsig" 101 102 echo "==> publishing ${PREFIX}/APKINDEX.tar.gz" 103 sha=$(sha256sum APKINDEX.tar.gz | cut -d' ' -f1) 104 tmp_put=$(mktemp) 105 code=$(curl -sS -o "${tmp_put}" -w '%{http_code}' \ 106 --aws-sigv4 "aws:amz:${BUCKET_REGION}:s3" \ 107 --user "${BUCKET_ACCESS_KEY}:${BUCKET_SECRET_KEY}" \ 108 -H "x-amz-content-sha256: ${sha}" \ 109 -X PUT --upload-file APKINDEX.tar.gz \ 110 "${url}") || { echo "bootstrap-repo.sh: upload failed" >&2; cat "${tmp_put}" >&2 2>/dev/null || true; rm -f "${tmp_put}"; exit 1; } 111 case "${code}" in 2*) rm -f "${tmp_put}"; echo "==> bootstrapped ${PREFIX}/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