diff --git a/.crow/build.yaml b/.crow/build.yaml index b3b6c0e..18f126f 100644 --- a/.crow/build.yaml +++ b/.crow/build.yaml @@ -158,28 +158,29 @@ steps: - name: Check which R versions already exist in s3 image: reg.devxy.io/docker.io/library/alpine:3.23 privileged: true - environment: - AWS_ACCESS_KEY_ID: - from_secret: B2_S3_ACCESS_KEY_ID - AWS_SECRET_ACCESS_KEY: - from_secret: B2_S3_SECRET_KEY commands: - ip link set dev eth0 mtu 1280 2>/dev/null || true - for i in 1 2 3 4 5; do apk add -q --no-cache curl && break; sleep 5; done - | - # Backblaze B2 requires authentication for the list-bucket API (anonymous - # GET works only for individual public-read objects), so the request must be - # SigV4-signed with the same credentials used for the upload step. Without - # this the listing returns AccessDenied, r-versions-existing.txt stays empty, - # and every version is rebuilt even though it already exists. - LISTING=$(curl -s --aws-sigv4 "aws:amz:eu-central-003:s3" \ - --user "${AWS_ACCESS_KEY_ID}:${AWS_SECRET_ACCESS_KEY}" \ - "https://s3.eu-central-003.backblazeb2.com/devxy-r-builds?prefix=${PLATFORM_ID}/" | \ - grep -oE '[^<]+' | sed 's/<[^>]*>//g') + # Backblaze B2's list-bucket API requires authentication, but the upload + # credentials are scoped to the plugin-s3 image and aren't available here. + # Individual objects are public-read, so probe each expected artifact URL + # with an anonymous HEAD instead of listing the bucket. The filename mirrors + # what nfpm produces in builder/package.${PLATFORM} (version/release pinned + # to 1): r-_1_.{apk,deb} for alpine/ubuntu, R--1-1..rpm + # for el*. Shell variables must use bare $name, not ${name}: crow performs + # its own ${VAR} substitution on these commands before the shell runs, so + # ${VAR} for a shell var would be blanked out (only matrix vars resolve there). + case "${PLATFORM}" in + alpine-*) FMT="r-%s_1_%s.apk" ;; + ubuntu-*) FMT="r-%s_1_%s.deb" ;; + *) FMT="R-%s-1-1.%s.rpm" ;; + esac + BASE="https://s3.eu-central-003.backblazeb2.com/devxy-r-builds/${PLATFORM_ID}" : > r-versions-existing.txt for VERSION in $(cat r-versions-to-build.txt); do - if printf '%s\n' "$LISTING" | \ - awk -v arch="${ARCH_ID}" -v ver="$VERSION" 'index($0, arch) && index($0, ver) { found=1 } END { exit !found }'; then + URL="$BASE/$(printf "$FMT" "$VERSION" "${ARCH_ID}")" + if [ "$(curl -s -o /dev/null -w '%{http_code}' -I "$URL")" = "200" ]; then echo "$VERSION" >> r-versions-existing.txt fi done diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..911139b --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,13 @@ +# CLAUDE.md + +CI pipelines (Crow, in `.crow/`) that build R binaries and upload them to Backblaze B2. + +## Conventions + +- **PRs:** the remote is Forgejo on `git.devxy.io`; use `fj -H git.devxy.io` (not `gh`). +- **Storage:** Backblaze B2 bucket `devxy-r-builds` (endpoint configured in `.crow/build.yaml`). + +## Gotchas + +- **B2 requires authentication for the list-bucket API.** Anonymous GET only works for individual public-read objects — an empty bucket listing usually means missing credentials, not an empty bucket. This silently broke rebuild dedup once. +- When a pipeline fails, fetch the Crow logs yourself instead of asking the user to paste them, and monitor reruns in the background until green.