name: Release on: push: tags: ["v*"] jobs: macos: runs-on: macos-arm64 steps: - uses: https://code.forgejo.org/actions/checkout@v4 with: path: carrier - uses: https://code.forgejo.org/actions/checkout@v4 with: repository: randogoth/fumi ref: main path: fumi - uses: https://code.forgejo.org/actions/cache@v4 with: path: | ~/.cargo/registry carrier/target fumi/target key: cargo-macos-${{ hashFiles('carrier/Cargo.lock') }} restore-keys: cargo-macos- - run: devbox run build working-directory: carrier env: GITHUB_TOKEN: "" - run: mv target/release/carrier target/release/carrier-macos-arm64 working-directory: carrier # This runner is a laptop that's frequently asleep/offline, so this # job must not gate the release (and must not be gated by it either). # Instead of going through actions/forgejo-release - which only knows # how to blind-create a release, not append to one that may already # exist - this polls for the release the linux+flatpak job creates # and appends its own asset directly via the Forgejo API, landing # next to whatever binaries are already there. grep (not jq, whose # presence here isn't guaranteed) is safe for pulling the id out: per # Forgejo's structs.Release, ID is the first declared field and Go's # encoding/json marshals in declaration order, ahead of the nested # per-asset "id" fields in the "assets" array. - run: | REPO="${{ forge.repository }}" TAG="${{ forge.ref_name }}" FORGEJO="${{ env.FORGEJO_SERVER_URL }}" FORGEJO="${FORGEJO%%/}" TOKEN="${{ forge.token }}" FILE="target/release/carrier-macos-arm64" NAME="carrier-macos-arm64" release_id="" for i in $(seq 1 40); do body=$(curl --retry 5 -sS -H "Authorization: token $TOKEN" "$FORGEJO/api/v1/repos/$REPO/releases/tags/$TAG") release_id=$(echo "$body" | grep -o '"id":[0-9]*' | head -1 | grep -o '[0-9]*') if [ -n "$release_id" ]; then break fi echo "release for $TAG not ready yet, waiting 15s ($i/40)" sleep 15 done if [ -z "$release_id" ]; then echo "release for $TAG never appeared after 10 minutes" >&2 exit 1 fi curl --retry 5 --fail -sS -X POST \ -H "Authorization: token $TOKEN" \ -H "Content-Type: multipart/form-data" \ -F "attachment=@$FILE" \ "$FORGEJO/api/v1/repos/$REPO/releases/$release_id/assets?name=$NAME" working-directory: carrier # Same non-gating strategy as the macos job above: this runner is a # desktop machine that isn't guaranteed to be on when a tag is pushed, # so it must never gate the release (and must not be gated by it # either). It builds and bundles via windows/bundle.sh - the same # script windows/setup.sh runs for local one-command builds - then # appends the zip to the release the linux+flatpak jobs create, with # the same poll-for-the-release the macos job does. windows: runs-on: windows-x86_64 steps: - uses: https://code.forgejo.org/actions/checkout@v4 with: path: carrier - uses: https://code.forgejo.org/actions/checkout@v4 with: repository: randogoth/fumi ref: main path: fumi - uses: https://code.forgejo.org/actions/cache@v4 with: path: | ~/.cargo/registry carrier/target fumi/target key: cargo-windows-${{ hashFiles('carrier/Cargo.lock') }} restore-keys: cargo-windows- - run: bash windows/bundle.sh working-directory: carrier shell: bash - run: | REPO="${{ forge.repository }}" TAG="${{ forge.ref_name }}" FORGEJO="${{ env.FORGEJO_SERVER_URL }}" FORGEJO="${FORGEJO%%/}" TOKEN="${{ forge.token }}" VERSION=$(sed -n 's/^version = "\(.*\)"$/\1/p' Cargo.toml | head -1) FILE="carrier-windows-$VERSION.zip" NAME="carrier-windows-$VERSION.zip" release_id="" for i in $(seq 1 40); do body=$(curl --retry 5 -sS -H "Authorization: token $TOKEN" "$FORGEJO/api/v1/repos/$REPO/releases/tags/$TAG") release_id=$(echo "$body" | grep -o '"id":[0-9]*' | head -1 | grep -o '[0-9]*') if [ -n "$release_id" ]; then break fi echo "release for $TAG not ready yet, waiting 15s ($i/40)" sleep 15 done if [ -z "$release_id" ]; then echo "release for $TAG never appeared after 10 minutes" >&2 exit 1 fi curl --retry 5 --fail -sS -X POST \ -H "Authorization: token $TOKEN" \ -H "Content-Type: multipart/form-data" \ -F "attachment=@$FILE" \ "$FORGEJO/api/v1/repos/$REPO/releases/$release_id/assets?name=$NAME" working-directory: carrier shell: bash linux: runs-on: linux-x86_64 steps: - uses: https://code.forgejo.org/actions/checkout@v4 with: path: carrier - uses: https://code.forgejo.org/actions/checkout@v4 with: repository: randogoth/fumi ref: main path: fumi - uses: https://code.forgejo.org/actions/cache@v4 with: path: | ~/.cargo/registry carrier/target fumi/target key: cargo-linux-${{ hashFiles('carrier/Cargo.lock') }} restore-keys: cargo-linux- - run: devbox run build working-directory: carrier env: GITHUB_TOKEN: "" - run: mv target/release/carrier target/release/carrier-linux-x86_64 working-directory: carrier - uses: https://code.forgejo.org/forgejo/upload-artifact@v5 with: name: carrier-linux-x86_64 path: carrier/target/release/carrier-linux-x86_64 # Builds from source inside flatpak-builder's own sandbox, via the # rust-stable SDK extension (same as com.randogoth.carrier.json already # does for local `devbox run package`). Reusing the linux job's binary # was tried and reverted: that binary is dynamically linked against ~100 # libraries at absolute /nix/store paths, including its own ELF # interpreter - none of which exist inside the Flatpak sandbox, so it # can never execute there regardless of how it's packaged in. flatpak: runs-on: linux-x86_64 steps: - uses: https://code.forgejo.org/actions/checkout@v4 with: path: carrier - uses: https://code.forgejo.org/actions/checkout@v4 with: repository: randogoth/fumi ref: main path: fumi - run: devbox run bundle-flatpak working-directory: carrier env: GITHUB_TOKEN: "" - uses: https://code.forgejo.org/forgejo/upload-artifact@v5 with: name: carrier-x86_64.flatpak path: carrier/carrier-x86_64.flatpak # forgejo-release.sh (the engine behind actions/forgejo-release) always # does a blind create-then-upload of the release for the tag, with no # check for one already existing - running it from two parallel jobs # against the same tag either hard-fails the second create (override: # false) or races a delete-and-recreate that can wipe out the first job's # just-uploaded assets (override: true). So platform jobs only ever # produce artifacts; exactly one job, gated on both finishing, does the # single release create + upload. macos is deliberately left out of # needs (see that job): re-running this job after macos has appended its # asset will wipe that asset out via override - rerun macos again after # to restore it. release: needs: [linux, flatpak] runs-on: linux-x86_64 steps: - uses: https://code.forgejo.org/forgejo/download-artifact@v7 with: path: dist/release merge-multiple: true - uses: https://code.forgejo.org/actions/forgejo-release@v2.13.4 with: direction: upload release-dir: dist/release # Safe here specifically because this is the only job that ever # calls forgejo-release for a given tag: re-running this same job # for the same tag (e.g. a manual rerun) replaces its release # cleanly instead of failing on "already exists". override: true