publish.sh (4715B)
1 #!/bin/sh 2 # mk/publish.sh - upload built packages and the index to the S3 repo. 3 # 4 # ./mk/publish.sh publish every .apk plus APKINDEX for $ARCH 5 # ./mk/publish.sh zlib popt publish only those packages (plus index) 6 # ./mk/publish.sh --index-only re-upload APKINDEX.tar.gz and nothing else 7 # ./mk/publish.sh --dry-run print what would be uploaded 8 # 9 # Environment (supplied by the CI runner; no mounts are provided): 10 # 11 # BUCKET_ACCESS_KEY S3 access key id 12 # BUCKET_SECRET_KEY S3 secret access key 13 # BUCKET_NAME bucket to write into 14 # BUCKET_ENDPOINT base URL of the S3 service, e.g. https://s3.finwo.net 15 # BUCKET_REGION region name, e.g. eu-west1 16 # 17 # Uses `curl --aws-sigv4` rather than awscli or rclone: curl is already needed 18 # to fetch sources, signs SigV4 natively since 7.75, and pulling in a Python or 19 # Go client for four PUTs is not worth the image size or the supply chain. 20 set -eu 21 22 HERE=$(cd "$(dirname "$0")" && pwd) 23 ROOT=$(cd "${HERE}/.." && pwd) 24 25 ARCH="${ARCH:-x86_64}" 26 DRY=0 27 INDEX_ONLY=0 28 PKGS="" 29 30 while [ $# -gt 0 ]; do 31 case "$1" in 32 --arch) ARCH="$2"; shift 2 ;; 33 --arch=*) ARCH="${1#--arch=}"; shift ;; 34 --dry-run) DRY=1; shift ;; 35 --index-only) INDEX_ONLY=1; shift ;; 36 -h|--help) sed -n '2,20p' "$0"; exit 0 ;; 37 -*) echo "publish.sh: unknown option: $1" >&2; exit 1 ;; 38 *) PKGS="${PKGS} $1"; shift ;; 39 esac 40 done 41 42 msg() { printf '==> %s\n' "$*"; } 43 die() { printf 'publish.sh: error: %s\n' "$*" >&2; exit 1; } 44 45 REPODIR="${ROOT}/build/repo/${ARCH}" 46 [ -d "${REPODIR}" ] || die "no such repo dir: ${REPODIR}" 47 48 if [ "${DRY}" != "1" ]; then 49 for v in BUCKET_ACCESS_KEY BUCKET_SECRET_KEY BUCKET_NAME BUCKET_ENDPOINT BUCKET_REGION; do 50 eval "val=\${${v}:-}" 51 [ -n "${val}" ] || die "${v} is not set" 52 done 53 54 # These two are easy to transpose, and transposing them produces a signature 55 # mismatch rather than anything that names the real problem. Check the shape 56 # instead of trusting the order they were exported in. 57 case "${BUCKET_ENDPOINT}" in 58 http://*|https://*) ;; 59 *) die "BUCKET_ENDPOINT must be a URL (e.g. https://s3.finwo.net), got '${BUCKET_ENDPOINT}'. 60 Note BUCKET_REGION is the region name (e.g. eu-west1); the two look swapped." ;; 61 esac 62 case "${BUCKET_REGION}" in 63 http://*|https://*) die "BUCKET_REGION must be a region name (e.g. eu-west1), not a URL. 64 Note BUCKET_ENDPOINT is the URL; the two look swapped." ;; 65 esac 66 67 command -v curl >/dev/null 2>&1 || die "curl is required" 68 fi 69 70 # put <localfile> <key> 71 put() { 72 src="$1" 73 key="$2" 74 # Built after the dry-run check on purpose: --dry-run must work with no 75 # credentials in the environment at all, and set -u would abort here. 76 if [ "${DRY}" = "1" ]; then 77 printf ' would PUT %s -> %s\n' "${src}" "${key}" 78 return 0 79 fi 80 url="${BUCKET_ENDPOINT%/}/${BUCKET_NAME}/${key}" 81 sha=$(sha256sum "${src}" | cut -d' ' -f1) 82 code=$(curl -sS -o /dev/null -w '%{http_code}' \ 83 --aws-sigv4 "aws:amz:${BUCKET_REGION}:s3" \ 84 --user "${BUCKET_ACCESS_KEY}:${BUCKET_SECRET_KEY}" \ 85 -H "x-amz-content-sha256: ${sha}" \ 86 -X PUT --upload-file "${src}" \ 87 "${url}") || die "upload failed: ${key}" 88 case "${code}" in 89 2*) printf ' %s (%s)\n' "${key}" "${code}" ;; 90 *) die "upload of ${key} returned HTTP ${code}" ;; 91 esac 92 } 93 94 # Which .apk files to publish. 95 if [ "${INDEX_ONLY}" = "1" ]; then 96 FILES="" 97 elif [ -n "${PKGS}" ]; then 98 FILES="" 99 for p in ${PKGS}; do 100 found="" 101 for f in "${REPODIR}/${p}"-*.apk; do 102 [ -e "${f}" ] || continue 103 # `zlib-1.3.1-r0.apk` matches zlib, but `zlib-doc-...` must not, so 104 # require the character after the name to start a version field. 105 base=$(basename "${f}") 106 rest=${base#"${p}"-} 107 case "${rest}" in 108 [0-9]*) found="${found} ${f}" ;; 109 esac 110 done 111 [ -n "${found}" ] || die "no built .apk for '${p}' in ${REPODIR}" 112 FILES="${FILES}${found}" 113 done 114 else 115 FILES=$(ls "${REPODIR}"/*.apk 2>/dev/null || true) 116 [ -n "${FILES}" ] || die "no .apk files in ${REPODIR}" 117 fi 118 119 msg "publishing to ${BUCKET_NAME:-<dry-run>}/${ARCH}" 120 for f in ${FILES}; do 121 put "${f}" "${ARCH}/$(basename "${f}")" 122 done 123 124 # The index goes last, always. It is the file clients read to discover what 125 # exists, so publishing it before its packages would advertise packages that 126 # are not there yet; publishing it after is merely briefly stale. 127 IDX="${REPODIR}/APKINDEX.tar.gz" 128 if [ -f "${IDX}" ]; then 129 put "${IDX}" "${ARCH}/APKINDEX.tar.gz" 130 else 131 [ "${INDEX_ONLY}" = "1" ] && die "no APKINDEX.tar.gz in ${REPODIR}; run ./mk/repo-index.sh" 132 msg "note: no APKINDEX.tar.gz to publish (run ./mk/repo-index.sh first)" 133 fi 134 135 msg "done"