# Cloudflare R2 CI Setup Upload a build artifact (e.g. a status JSON) to an R2 bucket from CI, using `awscli` against R2's S3-compatible API. ## Cloudflare side Create a bucket and a write-capable API token/key pair; note the account ID. ## CI config Variables: `R2_BUCKET_NAME`, `R2_ACCOUNT_ID`. Secrets: `R2_ACCESS_KEY_ID`, `R2_SECRET_ACCESS_KEY`. The keys must be passed as `AWS_ACCESS_KEY_ID` / `AWS_SECRET_ACCESS_KEY` — `awscli` reads those names. Also required: `AWS_DEFAULT_REGION=auto` and endpoint `https://${R2_ACCOUNT_ID}.r2.cloudflarestorage.com`. ## Job ```yaml publish_status: runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - uses: actions/setup-python@v6 with: { python-version: "3.12" } - run: pip install awscli - name: Publish to R2 env: R2_BUCKET_NAME: ${{ vars.R2_BUCKET_NAME }} R2_ACCOUNT_ID: ${{ vars.R2_ACCOUNT_ID }} AWS_ACCESS_KEY_ID: ${{ secrets.R2_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.R2_SECRET_ACCESS_KEY }} run: | set -eu # If publish is optional, skip on missing config; use exit 1 if mandatory. [ -n "${R2_BUCKET_NAME:-}" ] && [ -n "${R2_ACCOUNT_ID:-}" ] \ && [ -n "${AWS_ACCESS_KEY_ID:-}" ] && [ -n "${AWS_SECRET_ACCESS_KEY:-}" ] \ || { echo "Skipping: R2 config not set."; exit 0; } f="$(mktemp -d)/project-name.json" cat > "$f" <