chore(release): bump workspace version to 0.8.3 #12
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: release | |
| # Triggers: | |
| # - push of a `v*` tag (`git tag v0.1.0 && git push --tags`) — the | |
| # normal release path. | |
| # - manual `workflow_dispatch` with a tag input — useful for | |
| # re-running a build after fixing a release-time issue without | |
| # bumping the version. | |
| on: | |
| push: | |
| tags: ['v*'] | |
| workflow_dispatch: | |
| inputs: | |
| tag: | |
| description: 'Version tag (e.g. v0.1.0). Must already exist as a git tag.' | |
| required: true | |
| env: | |
| CARGO_TERM_COLOR: always | |
| # Strip debuginfo aggressively — the release profile already does | |
| # `strip = "symbols"` but cargo-zigbuild can leave more behind. | |
| RUSTFLAGS: '-C strip=symbols' | |
| permissions: | |
| contents: read | |
| jobs: | |
| build: | |
| name: build ${{ matrix.target }} | |
| # Self-hosted runner gate: only run on the canonical repo so a | |
| # malicious fork can't dispatch this workflow against the | |
| # XOXNO Hetzner box and execute arbitrary code on it. Tag-push | |
| # and manual-dispatch already require write access in the | |
| # canonical repo; this is belt-and-braces. | |
| if: github.repository_owner == 'XOXNO' | |
| # Single runner for every target. The self-hosted XOXNO Hetzner | |
| # box (Ubuntu x86_64) acts as the universal build host: | |
| # | |
| # - Linux x86_64 musl — native, via zig as a hermetic linker | |
| # - macOS arm64 — cross-compile via zig (Mach-O 64) | |
| # - macOS x86_64 — cross-compile via zig (Mach-O 64) | |
| # | |
| # `cargo-zigbuild` invokes a single `zig` binary as the C linker | |
| # for every target, which means we don't need the macOS SDK, | |
| # osxcross, Docker, musl-tools, or any per-target toolchain. | |
| # zig itself ships every libc shim it needs. | |
| runs-on: [self-hosted, Linux, X64] | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - target: aarch64-apple-darwin | |
| - target: x86_64-apple-darwin | |
| - target: x86_64-unknown-linux-musl | |
| # aarch64 Linux: deliberately dropped from the matrix. | |
| # Add it back if/when an operator deploys to AWS Graviton | |
| # / ARM Hetzner / Pi — until then it'd be a maintenance | |
| # burden with no consumer. | |
| steps: | |
| # Resolve the release tag once, into the per-job env. Every | |
| # downstream shell step reads `$RELEASE_TAG` — never substitutes | |
| # `${{ github.event.inputs.tag }}` directly into a shell line, | |
| # which would let a maliciously-crafted dispatch input inject | |
| # commands. See: | |
| # https://github.blog/security/vulnerability-research/how-to-catch-github-actions-workflow-injections-before-attackers-do/ | |
| - name: Resolve release tag (build job) | |
| id: tag | |
| env: | |
| INPUT_TAG: ${{ github.event.inputs.tag }} | |
| REF_NAME: ${{ github.ref_name }} | |
| shell: bash | |
| run: | | |
| # Prefer the workflow-dispatch input; fall back to the tag | |
| # that was pushed. | |
| TAG="${INPUT_TAG:-$REF_NAME}" | |
| if [ -z "$TAG" ]; then | |
| echo "no tag resolved — push a v* tag or pass `tag` input" >&2 | |
| exit 1 | |
| fi | |
| # Allowlist the tag shape — must look like a semver tag. | |
| # Anything else is rejected up front so we never expand a | |
| # hostile string into an archive name or shell command. | |
| case "$TAG" in | |
| v[0-9]*) ;; | |
| *) | |
| echo "rejecting tag '$TAG' — must start with v + digit" >&2 | |
| exit 1 | |
| ;; | |
| esac | |
| echo "tag=$TAG" >> "$GITHUB_OUTPUT" | |
| echo "RELEASE_TAG=$TAG" >> "$GITHUB_ENV" | |
| - uses: actions/checkout@v4 | |
| - uses: dtolnay/rust-toolchain@stable | |
| with: | |
| # Targets are also declared in rust-toolchain.toml; this | |
| # mirror exists so the `stable` toolchain dtolnay installs | |
| # carries them too. The actual build runs under 1.94.1 | |
| # (per rust-toolchain.toml) which auto-installs targets | |
| # the first time rustup materialises that channel. | |
| targets: ${{ matrix.target }} | |
| - name: Materialise pinned toolchain + targets | |
| # Triggers rustup to install 1.94.1 (per rust-toolchain.toml) | |
| # WITH the targets declared there. Without this, the first | |
| # cargo invocation would silently install 1.94.1 minus the | |
| # cross targets and the build would explode with | |
| # "can't find crate for `core`". | |
| run: rustup show active-toolchain || rustup show | |
| - uses: Swatinem/rust-cache@v2 | |
| with: | |
| # Per-target cache — Linux and macOS targets warm up | |
| # different artifact sets. | |
| key: ${{ matrix.target }} | |
| - name: Install zig (universal cross-toolchain) | |
| # Pinned to a long-published, fully-mirrored version. 0.16.0 | |
| # was unreachable on every mirror at v0.1.0 release time | |
| # (404 / 503 across machengine, hryx, nekos, linus, | |
| # liujiacai, ziglang.org). 0.13.0 has been mirror-stable | |
| # since 2024-08 and is what cargo-zigbuild's CI also uses. | |
| uses: mlugg/setup-zig@v1 | |
| with: | |
| version: 0.13.0 | |
| - name: Install cargo-zigbuild | |
| # `--locked` so we don't silently grab a fresh transitive | |
| # dep mid-release. Cached by Swatinem/rust-cache above. | |
| run: cargo install --locked cargo-zigbuild | |
| - name: Build (cargo-zigbuild) | |
| env: | |
| TARGET: ${{ matrix.target }} | |
| run: cargo zigbuild --release --target "$TARGET" --bin mxnode | |
| - name: Sanity check (binary exists + is the right ELF/Mach-O) | |
| env: | |
| TARGET: ${{ matrix.target }} | |
| shell: bash | |
| run: | | |
| BIN="target/${TARGET}/release/mxnode" | |
| ls -lh "$BIN" | |
| file "$BIN" | |
| [ -x "$BIN" ] | |
| - name: Package | |
| id: package | |
| env: | |
| TARGET: ${{ matrix.target }} | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| # $RELEASE_TAG and $TARGET are both controlled — the tag is | |
| # allowlisted to ^v[0-9].* above, the target comes from the | |
| # matrix (workflow author). | |
| BIN="target/${TARGET}/release/mxnode" | |
| DIST=dist | |
| ARCHIVE="mxnode-${RELEASE_TAG}-${TARGET}.tar.gz" | |
| mkdir -p "$DIST" | |
| STAGE=$(mktemp -d) | |
| cp "$BIN" "$STAGE/mxnode" | |
| [ -f README.md ] && cp README.md "$STAGE/" | |
| [ -f LICENSE ] && cp LICENSE "$STAGE/" | |
| tar -czf "$DIST/$ARCHIVE" -C "$STAGE" . | |
| rm -rf "$STAGE" | |
| # Per-archive sha256, joined into SHA256SUMS in the publish job. | |
| (cd "$DIST" && shasum -a 256 "$ARCHIVE" > "$ARCHIVE.sha256" 2>/dev/null \ | |
| || sha256sum "$ARCHIVE" > "$ARCHIVE.sha256") | |
| ls -lh "$DIST" | |
| echo "archive=$ARCHIVE" >> "$GITHUB_OUTPUT" | |
| - uses: actions/upload-artifact@v4 | |
| with: | |
| name: mxnode-${{ matrix.target }} | |
| path: dist/* | |
| retention-days: 14 | |
| if-no-files-found: error | |
| release: | |
| name: publish release | |
| needs: build | |
| # Stay on the same runner pool the build jobs ran on — single | |
| # source of CI hardware, no GitHub-hosted minutes consumed. | |
| if: github.repository_owner == 'XOXNO' | |
| runs-on: [self-hosted, Linux, X64] | |
| permissions: | |
| # Required by softprops/action-gh-release to attach assets + | |
| # write release notes. | |
| contents: write | |
| steps: | |
| - name: Resolve release tag (publish job) | |
| id: tag | |
| env: | |
| INPUT_TAG: ${{ github.event.inputs.tag }} | |
| REF_NAME: ${{ github.ref_name }} | |
| shell: bash | |
| run: | | |
| TAG="${INPUT_TAG:-$REF_NAME}" | |
| case "$TAG" in | |
| v[0-9]*) ;; | |
| *) | |
| echo "rejecting tag '$TAG' — must start with v + digit" >&2 | |
| exit 1 | |
| ;; | |
| esac | |
| echo "tag=$TAG" >> "$GITHUB_OUTPUT" | |
| - uses: actions/checkout@v4 | |
| - name: Download every per-target artifact | |
| uses: actions/download-artifact@v4 | |
| with: | |
| path: artifacts | |
| - name: Stage release files | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| mkdir -p dist | |
| # Flatten — each `mxnode-<target>` artifact dir contains | |
| # one .tar.gz + one .sha256 stub. | |
| find artifacts -type f \( -name '*.tar.gz' -o -name '*.sha256' \) \ | |
| -exec cp -v {} dist/ \; | |
| # Combine the per-archive .sha256 stubs into one | |
| # SHA256SUMS file the install script can curl directly. | |
| (cd dist && cat *.sha256 > SHA256SUMS && rm *.sha256) | |
| ls -lh dist | |
| - name: Create / update GitHub Release | |
| uses: softprops/action-gh-release@v2 | |
| with: | |
| tag_name: ${{ steps.tag.outputs.tag }} | |
| name: ${{ steps.tag.outputs.tag }} | |
| generate_release_notes: true | |
| fail_on_unmatched_files: true | |
| files: | | |
| dist/*.tar.gz | |
| dist/SHA256SUMS |