diff --git a/scripts/publish-artifact.sh b/scripts/publish-artifact.sh index 1ca6915..8b5035a 100755 --- a/scripts/publish-artifact.sh +++ b/scripts/publish-artifact.sh @@ -1,10 +1,38 @@ #!/usr/bin/env bash set -e -goat account login -u "${TANGLED_REPO_DID}" -p "${APP_PASSWORD}" + +# Serialize across parallel invocations from goreleaser. goat stores its session +# in a single file (~/.local/state/goat/auth-session.json), so concurrent logins +# race on it, and the PDS gets slammed by simultaneous uploads. +exec 9>/tmp/atcr-publish-artifact.lock +flock 9 + +retry() { + local max=5 attempt=1 + while true; do + if "$@"; then return 0; fi + [ $attempt -ge $max ] && { echo "giving up after $max attempts: $*" >&2; return 1; } + local sleep_s=$((attempt * 3)) + echo "attempt $attempt failed; retrying in ${sleep_s}s..." >&2 + sleep $sleep_s + attempt=$((attempt + 1)) + done +} + +retry goat account login -u "${TANGLED_REPO_DID}" -p "${APP_PASSWORD}" + TAG_HASH=$(git rev-parse "$TANGLED_REF_NAME") TAG_BYTES=$(printf "$(printf '%s' "$TAG_HASH" | sed 's/../\\x&/g')" | base64 | tr -d '=') -BLOB_OUTPUT=$(goat blob upload "$ARTIFACT_PATH") + +BLOB_OUTPUT=$(retry goat blob upload "$ARTIFACT_PATH") echo "$BLOB_OUTPUT" + +# Deterministic rkey from (tag, artifact name) makes record create idempotent. +# Needed because tangled's PDS returns HTTP 500 (not 409) on duplicate rkey, +# so a retry after a mid-write 500 would otherwise create a duplicate record. +RKEY=$(printf '%s|%s' "$TANGLED_REF_NAME" "$ARTIFACT_NAME" | sha256sum | cut -c1-24) +RECORD_URI="at://${TANGLED_REPO_DID}/sh.tangled.repo.artifact/${RKEY}" + CREATED_AT=$(date -Iseconds) cat > temp_artifact.json < temp_artifact.json </dev/null 2>&1; then + echo "record already exists at $RECORD_URI — treating as success" + break + fi + [ $attempt -eq 5 ] && { echo "record create failed and record does not exist" >&2; exit 1; } + sleep $((attempt * 3)) +done + rm temp_artifact.json sleep 2