unos-repository

APK repository for unos
git clone git://git.finwo.net/misc/unos-repository
Log | Files | Refs | README

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"