diff --git a/.tangled/workflows/release.yml b/.tangled/workflows/release.yml index 2da8560..c6c952b 100644 --- a/.tangled/workflows/release.yml +++ b/.tangled/workflows/release.yml @@ -12,6 +12,10 @@ # a device secret it runs com.atproto.server.createSession against your PDS, so # a handle + app password authenticates a push non-interactively. # +# The two pushes are independent: Docker Hub (the deploy mirror) gates the build, +# while atcr.io is best-effort so its auth flakiness can't block a deploy. Both +# are always attempted regardless of the other's outcome. +# # Docker Hub auto-creates the repo as PRIVATE; make docker.io/$DOCKERHUB_USER/ # deepsky public (repo Settings → Visibility) so Railway can pull it without # credentials. @@ -47,31 +51,49 @@ steps: # `result` is a docker-archive tarball skopeo can read. ls -l "$(readlink -f result)" - - name: "Push to atcr.io and Docker Hub" + - name: "Push image" command: | - set -euo pipefail - # Fail clearly if any secret isn't reaching the step (report secret - # lengths only, never their values). - : "${ATCR_HANDLE:?repo secret ATCR_HANDLE is not set}" - : "${ATCR_APP_PASSWORD:?repo secret ATCR_APP_PASSWORD is not set}" - : "${DOCKERHUB_USER:?repo secret DOCKERHUB_USER is not set}" - : "${DOCKERHUB_TOKEN:?repo secret DOCKERHUB_TOKEN is not set}" - # skopeo (containers/image) hardcodes /var/tmp for unpacking the archive - # and ignores $TMPDIR; the minimal Nix step image lacks it, so create it. - mkdir -p /var/tmp + # Note: NOT `set -e` — the two registries are pushed independently so a + # failure of one never prevents the other from being attempted. Docker Hub + # (the deploy mirror Railway pulls) gates the build; atcr.io is best-effort + # so its known auth flakiness can't block a deploy. Flip atcr to gating by + # adding its rc to the final exit. + set -uo pipefail + mkdir -p /var/tmp # skopeo hardcodes /var/tmp; the minimal image lacks it tag="${TANGLED_REF_NAME}" # the pushed tag, e.g. v1.2.3 archive="docker-archive:$(readlink -f result)" - # push REF SRC:CREDS — copy the archive to :$tag and :latest of a target. - # --insecure-policy selects skopeo's built-in accept-anything signature - # policy; the Nix environment ships no /etc/containers/policy.json. + # push REF CREDS — copy the archive to :$tag and :latest of one target. + # --insecure-policy selects skopeo's accept-anything signature policy; the + # Nix environment ships no /etc/containers/policy.json. push() { - skopeo --insecure-policy copy --dest-creds "$2" "$archive" "docker://$1:${tag}" + skopeo --insecure-policy copy --dest-creds "$2" "$archive" "docker://$1:${tag}" && skopeo --insecure-policy copy --dest-creds "$2" "$archive" "docker://$1:latest" } - echo "Pushing to atcr.io as ${ATCR_HANDLE} (app password: ${#ATCR_APP_PASSWORD} chars)" - push "atcr.io/${ATCR_HANDLE}/deepsky" "${ATCR_HANDLE}:${ATCR_APP_PASSWORD}" + rc=0 + + # Docker Hub — deploy mirror; required. + : "${DOCKERHUB_USER:?repo secret DOCKERHUB_USER is not set}" + : "${DOCKERHUB_TOKEN:?repo secret DOCKERHUB_TOKEN is not set}" + echo "== Docker Hub (${DOCKERHUB_USER}) ==" + if push "docker.io/${DOCKERHUB_USER}/deepsky" "${DOCKERHUB_USER}:${DOCKERHUB_TOKEN}"; then + echo "Docker Hub: ok" + else + echo "Docker Hub: FAILED" + rc=1 + fi + + # atcr.io — canonical registry; best-effort (does not fail the build). + if [ -n "${ATCR_HANDLE:-}" ] && [ -n "${ATCR_APP_PASSWORD:-}" ]; then + echo "== atcr.io (${ATCR_HANDLE}, app password: ${#ATCR_APP_PASSWORD} chars) ==" + if push "atcr.io/${ATCR_HANDLE}/deepsky" "${ATCR_HANDLE}:${ATCR_APP_PASSWORD}"; then + echo "atcr.io: ok" + else + echo "atcr.io: FAILED (best-effort, not blocking the build)" + fi + else + echo "atcr.io: skipped (ATCR_HANDLE/ATCR_APP_PASSWORD not set)" + fi - echo "Pushing to Docker Hub as ${DOCKERHUB_USER} (token: ${#DOCKERHUB_TOKEN} chars)" - push "docker.io/${DOCKERHUB_USER}/deepsky" "${DOCKERHUB_USER}:${DOCKERHUB_TOKEN}" + exit $rc