Large file storage for AT Protocol: an IPFS server & gateway with XRPC upload & admin. atfs.dev
ipfs atproto xrpc
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163#!/usr/bin/env bash## Cross-compiles the atfs CLI (./cmd/atfs) for every platform named on the# command line, or for every platform atfs ships CLI binaries for when none# are named.## The target list is the SAME four GOOS/GOARCH pairs as flake.nix's# `systems` — that list is atfs's own commitment to which platforms the CLI# supports, so a release that shipped a different set of binaries would be# publishing artifacts for platforms the flake doesn't (or vice versa,# silently dropping one it does). Restated here rather than read out of# flake.nix because there's no nix on the machines that call this script# (see CLAUDE.md's "flake.nix's vendorHash and flake.lock are derived,# never authored" — the same reasoning: no nix on the dev machine, so# nothing here can shell out to it to ask).## This is the CLI-binary counterpart to hack/build-sbc-images.sh: same# structure, same --targets/--boards introspection flag, same VERSION# convention. hack/publish-release-images.sh is the caller — it uploads# whatever this script builds, exactly as it already does for# hack/build-sbc-images.sh's board images (see that script's header).## CGO_ENABLED=0 matches the CGO-free ground rule (CLAUDE.md) and the flake's# own `env.CGO_ENABLED = 0`; the ldflags match both the flake and# .tangled/workflows/publish-sbc-images.yml's existing single-platform# "build the atfs CLI" step, so `atfs --version` reports the same thing# whichever build produced the binary.## VERSION in the environment feeds -X main.version; it defaults to a git# describe, so a shallow or tagless checkout falls back to a short SHA,# which is fine and honest. A release passes its validated tag explicitly.## The two darwin-* binaries are additionally code-signed and notarized# (ATFS-e3fz) when APPLE_CERT_P12_BASE64, APPLE_CERT_PASSWORD and# APPLE_API_KEY_JSON_BASE64 are ALL set — see# .tangled/workflows/publish-sbc-images.yml's header for what each one is# and where to get it. Any other subset (one or two of the three) is# refused outright: a half-configured signing setup is far more likely a# mistake than a deliberate choice, and shipping an unsigned binary while# believing it's signed is worse than failing loudly. With none set at all,# darwin binaries build exactly as they always have — this is what keeps# local dev builds and any release cut before the Apple credentials exist# working unchanged.## Signing uses rcodesign (the `apple-codesign` Rust crate) rather than# Xcode's codesign/notarytool, since Tangled's CI has no macOS runner (see# CLAUDE.md) — rcodesign speaks Apple's signing and Notary API from Linux.# A standalone executable (not an .app/.dmg/.pkg) can be signed directly,# but Apple's notary service only accepts a zip/dmg/pkg submission and a# notarization ticket can only be STAPLED to one of those, never to a bare# Mach-O binary — so the signed binary itself is what ships, a throwaway# zip of it is what gets submitted for notarization, and Gatekeeper# performs its usual online check against Apple's servers the first time a# downloaded (quarantined) copy is run. That's the same mechanism the# downloads page's now-conditional quarantine notice describes.## Usage: hack/build-cli-binaries.sh [TARGET...]# hack/build-cli-binaries.sh --targets # print the target list, build nothingset -euo pipefail
# Every platform atfs ships CLI binaries for — kept identical to flake.nix's# `systems` list by hand (see above for why this can't just read that file).# Target id is $GOOS-$GOARCH, the same plain lowercase-hyphenated shape# hack/build-sbc-images.sh uses for board ids.ALL_TARGETS=(linux-amd64 linux-arm64 darwin-amd64 darwin-arm64)
if [ "${1:-}" = "--targets" ]; then echo "${ALL_TARGETS[*]}" exit 0fi
TARGETS=("$@")[ "${#TARGETS[@]}" -gt 0 ] || TARGETS=("${ALL_TARGETS[@]}")
VERSION="${VERSION:-$(git describe --tags --always --dirty 2>/dev/null || echo unknown)}"
# SIGN_APPLE decides once, up front, whether any darwin-* target this run# builds gets signed — never decided per-target, so both mac binaries in a# release always come out the same way. See the header comment above for# what "some but not all" refusal is guarding against.n_apple_vars=0for var in APPLE_CERT_P12_BASE64 APPLE_CERT_PASSWORD APPLE_API_KEY_JSON_BASE64; do [ -n "${!var:-}" ] && n_apple_vars=$((n_apple_vars + 1))donecase "$n_apple_vars" in 0) SIGN_APPLE=0 ;; 3) SIGN_APPLE=1 ;; *) echo "APPLE_CERT_P12_BASE64, APPLE_CERT_PASSWORD and APPLE_API_KEY_JSON_BASE64 must be set together or not at all (got $n_apple_vars of 3) — see .tangled/workflows/publish-sbc-images.yml's header" >&2 exit 1 ;;esac
# Decoded once, however many darwin-* targets this run builds, into a temp# dir removed on exit whatever happens — these are secrets, not build# output, and never belong on disk longer than the sign/notarize calls# that need them.APPLE_SECRETS_DIR=""if [ "$SIGN_APPLE" -eq 1 ]; then APPLE_SECRETS_DIR="$(mktemp -d)" trap 'rm -rf "$APPLE_SECRETS_DIR"' EXIT base64 -d <<<"$APPLE_CERT_P12_BASE64" >"$APPLE_SECRETS_DIR/cert.p12" printf '%s' "$APPLE_CERT_PASSWORD" >"$APPLE_SECRETS_DIR/cert.password" base64 -d <<<"$APPLE_API_KEY_JSON_BASE64" >"$APPLE_SECRETS_DIR/api-key.json" chmod 600 "$APPLE_SECRETS_DIR"/*fi
# sign_and_notarize code-signs $1 in place with the hardened runtime flag# notarization requires, zips it into a throwaway submission, and blocks# (--wait) on Apple's notary service accepting it. A rejection or any# transport failure exits non-zero, which — same as every other step in# this script — takes the whole build down: a release is not the place to# discover a lapsed certificate or a rejected binary by shipping it anyway.sign_and_notarize() { local bin="$1" zip="$1.zip" echo " signing + notarizing…" rcodesign sign \ --p12-file "$APPLE_SECRETS_DIR/cert.p12" \ --p12-password-file "$APPLE_SECRETS_DIR/cert.password" \ --code-signature-flags runtime \ "$bin" rm -f "$zip" zip -q "$zip" "$bin" rcodesign notary-submit \ --api-key-file "$APPLE_SECRETS_DIR/api-key.json" \ --wait \ "$zip" rm -f "$zip" # A marker, not a modification to $bin itself: hack/publish-release-images.sh # reads this to record `signed: true` in the release manifest per CLI # target (ATFS-e3fz), which is how the downloads page knows to stop # telling visitors to clear the quarantine flag by hand. touch "$bin.signed"}
for target in "${TARGETS[@]}"; do case "$target" in linux-amd64) goos=linux; goarch=amd64 ;; linux-arm64) goos=linux; goarch=arm64 ;; darwin-amd64) goos=darwin; goarch=amd64 ;; darwin-arm64) goos=darwin; goarch=arm64 ;; *) echo "unknown target '$target' — valid targets: ${ALL_TARGETS[*]}" >&2 exit 1 ;; esac
echo "=== go build: $target ($VERSION) ===" out="atfs-$target" # Removed first, so a build that dies partway can't leave a previous run's # binary (or a stale .signed marker claiming a fresh unsigned rebuild is # signed) sitting there for a later step to upload as this one's (same # reasoning as hack/build-sbc-images.sh's rm -f before each gosd build). rm -f "$out" "$out.signed" GOOS="$goos" GOARCH="$goarch" CGO_ENABLED=0 go build \ -ldflags "-X main.version=$VERSION" \ -o "$out" ./cmd/atfs
if [ "$SIGN_APPLE" -eq 1 ] && [[ "$target" == darwin-* ]]; then sign_and_notarize "$out" fidone