diff --git a/.gitea/workflows/macos-probe.yaml b/.gitea/workflows/macos-probe.yaml deleted file mode 100644 index 565625a..0000000 --- a/.gitea/workflows/macos-probe.yaml +++ /dev/null @@ -1,81 +0,0 @@ -# THROWAWAY build smoke-test for the macOS (Tart) runner. Verifies both -# *-apple-darwin targets actually compile and link (incl. arboard's AppKit) -# through the flake on the real Mac, before the full release-macos workflow is -# wired. Delete once that lands. -# -# Push-triggered (workflow_dispatch only works for workflows on the default -# branch; our CI is on `ci`). Runs when the flake/toolchain or this file change. -# Bring the Mac up before pushing so the run isn't left queued. -name: macos-build-test -on: - push: - paths: - - '.gitea/workflows/macos-probe.yaml' - - 'flake.nix' - - 'rust-toolchain.toml' - workflow_dispatch: - -jobs: - build: - # Label NAME only — `:host` in the runner registration is the execution - # backend (run on host), not part of the label. - runs-on: macos - env: - # Guarantee flakes regardless of the Mac's nix config. - NIX_CONFIG: "experimental-features = nix-command flakes" - steps: - - uses: actions/checkout@v4 - - name: test (macOS — the gate only covers Linux) - run: nix develop -c cargo test --no-fail-fast - - name: build, de-nix, sign, verify both darwin targets - run: | - set -e - for t in aarch64-apple-darwin x86_64-apple-darwin; do - echo "==================== $t ====================" - nix develop -c cargo build --release --target "$t" - f="target/$t/release/rdbms-playground" - - # The darwin stdenv bakes a /nix/store libiconv load path into the - # binary. Rewrite it to the system libiconv (every Mac has it, ABI- - # compatible), then re-sign ad-hoc — install_name_tool invalidates - # the signature and arm64 won't run an unsigned/broken-sig binary. - for l in $(otool -L "$f" | awk '/\/nix\/store.*libiconv.*dylib/ {print $1}'); do - echo "rewrite $l -> /usr/lib/libiconv.2.dylib" - install_name_tool -change "$l" /usr/lib/libiconv.2.dylib "$f" - done - codesign --force --sign - "$f" - - echo "--- linked libs ---"; otool -L "$f" - if otool -L "$f" | grep -q /nix/store; then - echo "ERROR: $t still links a /nix/store dylib"; exit 1 - fi - codesign --verify --verbose=2 "$f" && echo "signature OK" - - # Smoke-run the natively-runnable target (this VM is arm64). - if [ "$t" = "aarch64-apple-darwin" ]; then - echo "--- run --help ---"; "$f" --help | head -1 - else - echo "(skip run: $t needs Rosetta)" - fi - echo "OK: $t portable" - done - echo "=== both darwin targets built, de-nixed, signed, verified ===" - - - name: prune nix store — keep the last 2 toolchain generations - # The runner wipes the whole workspace before each run, so cargo target/ - # never accumulates (no sweep needed). The persistent caches are the nix - # store (/nix) and ~/.cargo (in $HOME). Bound the nix store by generation: - # record the current devShell closure as a generation of a persistent - # profile (lives in $HOME, survives the workspace wipe), keep the 2 newest - # (current + previous), reclaim what the older ones referenced. No time - # window — never more than two toolchains regardless of flake.lock churn. - if: always() - run: | - echo "--- disk before ---"; df -h / | tail -1 - P="$HOME/.cache/rdbms-ci/toolchain" - nix develop --profile "$P" -c true || true - nix-env -p "$P" --delete-generations +2 || true - nix-collect-garbage || true - echo "--- disk after ---"; df -h / | tail -1 - # ~/.cargo/registry also persists but grows only on Cargo.lock bumps; - # bound it later with `cargo-cache --autoclean` if it ever matters. diff --git a/.gitea/workflows/release-macos.yaml b/.gitea/workflows/release-macos.yaml new file mode 100644 index 0000000..8f75829 --- /dev/null +++ b/.gitea/workflows/release-macos.yaml @@ -0,0 +1,95 @@ +# macOS release leg — the two *-apple-darwin binaries, built natively on the +# Tart (Apple-Silicon) runner and attached to an existing Gitea release. +# +# Manual dispatch only: the Mac runner is intermittent, so this is triggered by +# hand (with the Mac up) for a given release tag. The 4-target Linux/Windows +# release (release.yaml) runs on the tag itself and never waits on the Mac, so a +# release always has those four; the macOS two are added by dispatching this. +# +# NOTE: Gitea exposes workflow_dispatch only for workflows on the DEFAULT branch, +# so this becomes triggerable once the CI work is merged to `main`. +name: release-macos +on: + workflow_dispatch: + inputs: + tag: + description: 'Release tag to build the macOS binaries for and attach to (e.g. v0.1.0)' + required: true + +jobs: + release-macos: + runs-on: macos + env: + NIX_CONFIG: "experimental-features = nix-command flakes" + TAG: ${{ inputs.tag }} + # Auto-provided by Gitea Actions; has repo write (release) scope. + TOKEN: ${{ secrets.GITEA_TOKEN }} + API: ${{ github.server_url }}/api/v1 + REPO: ${{ github.repository }} + steps: + - uses: actions/checkout@v4 + with: + ref: ${{ inputs.tag }} + + - name: test + run: nix develop -c cargo test --no-fail-fast + + - name: build, de-nix, sign, package + publish + run: | + set -e + mkdir -p dist + for t in aarch64-apple-darwin x86_64-apple-darwin; do + echo "==================== $t ====================" + nix develop -c cargo build --release --target "$t" + f="target/$t/release/rdbms-playground" + + # Rewrite the nix-store libiconv load path to the system one, then + # re-sign ad-hoc (install_name_tool invalidates the signature; arm64 + # requires a valid one). Guard against any remaining /nix/store dep. + for l in $(otool -L "$f" | awk '/\/nix\/store.*libiconv.*dylib/ {print $1}'); do + install_name_tool -change "$l" /usr/lib/libiconv.2.dylib "$f" + done + codesign --force --sign - "$f" + if otool -L "$f" | grep -q /nix/store; then + echo "ERROR: $t binary links a /nix/store dylib"; exit 1 + fi + + out="rdbms-playground-$TAG-$t" + cp "$f" "dist/$out" + ( cd dist && shasum -a 256 "$out" > "$out.sha256" ) # macOS: shasum, not sha256sum + done + ls -l dist + + # Idempotent create-or-get the release (release.yaml likely created it + # already from the tag), then upload the two macOS binaries + checksums. + created=$(curl -sS -X POST "$API/repos/$REPO/releases" \ + -H "Authorization: token $TOKEN" -H "Content-Type: application/json" \ + -d "{\"tag_name\":\"$TAG\",\"name\":\"$TAG\",\"body\":\"Automated release for $TAG.\"}") + id=$(printf '%s' "$created" | node -e 'let s="";process.stdin.on("data",d=>s+=d).on("end",()=>{try{const o=JSON.parse(s);process.stdout.write(String(o.id||""))}catch(e){}})') + if [ -z "$id" ]; then + id=$(curl -sS "$API/repos/$REPO/releases/tags/$TAG" \ + -H "Authorization: token $TOKEN" \ + | node -e 'let s="";process.stdin.on("data",d=>s+=d).on("end",()=>{process.stdout.write(String(JSON.parse(s).id))})') + fi + echo "release id: $id" + for fa in dist/*; do + name=$(basename "$fa") + echo "uploading $name" + curl -sS -X POST "$API/repos/$REPO/releases/$id/assets?name=$name" \ + -H "Authorization: token $TOKEN" -F "attachment=@$fa" > /dev/null + done + echo "published macOS assets for $TAG" + + - name: prune nix store — keep the last 2 toolchain generations + # The runner wipes the workspace each run, so cargo target/ never + # accumulates. Bound the persistent nix store by generation: record the + # current devShell as a generation of a persistent profile (in $HOME), + # keep the 2 newest, reclaim what older ones referenced. + if: always() + run: | + echo "--- disk before ---"; df -h / | tail -1 + P="$HOME/.cache/rdbms-ci/toolchain" + nix develop --profile "$P" -c true || true + nix-env -p "$P" --delete-generations +2 || true + nix-collect-garbage || true + echo "--- disk after ---"; df -h / | tail -1