From 99f4cf0220e588dc43b8f5f139bcf84b3b239805 Mon Sep 17 00:00:00 2001 From: Ultradesu Date: Fri, 2 Oct 2026 12:47:19 +0100 Subject: [PATCH] Add a GitHub release workflow alongside the Gitea one MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Gitea and GitHub need different release plumbing, so each gets its own file: the Gitea workflow stays in .gitea/workflows, and this GitHub one lives in .github/workflows. The build half is identical (static musl Linux via cargo-zigbuild, native macOS aarch64 and Windows x86_64); only publishing differs — GitHub uses the v4 artifact backend and `gh release create`/`upload`, which the Gitea file cannot use. Because Gitea reads both .gitea/workflows and .github/workflows, every job here is guarded with `if: github.server_url == 'https://github.com'`, so on Gitea this file's jobs are skipped (no v4/gh failures) and the .gitea file is the one that runs. GitHub never reads .gitea, so there it runs only this one. Publish is re-run safe: it creates the release on the first run and uploads with --clobber onto an existing release afterwards; a pre-release suffix on the tag marks it prerelease. Co-Authored-By: Claude Opus 4.8 --- .github/workflows/release.yml | 187 ++++++++++++++++++++++++++++++++++ 1 file changed, 187 insertions(+) create mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..479d52d --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,187 @@ +name: Release + +# GitHub version of the release pipeline. The build half mirrors the Gitea one +# in .gitea/workflows/release.yml (static musl Linux via cargo-zigbuild, native +# macOS and Windows); only the publish half differs: GitHub needs the v4 +# artifact backend and fills the release with `gh`. +# +# Gitea reads both .gitea/workflows and .github/workflows, so without a guard it +# would also try to run this file and fail on @v4 and `gh`. Every job is gated +# on running on github.com, so on Gitea they are simply skipped — the Gitea file +# is the one that runs there. +on: + push: + tags: ["v*"] + +permissions: + contents: write + +env: + CARGO_TERM_COLOR: always + CARGO_NET_RETRY: "10" + +jobs: + build: + name: ${{ matrix.asset }} + if: ${{ github.server_url == 'https://github.com' }} + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + include: + - os: ubuntu-latest + target: x86_64-unknown-linux-musl + asset: tsunagi-linux-x86_64 + archive: tar.gz + zig: true + - os: ubuntu-latest + target: aarch64-unknown-linux-musl + asset: tsunagi-linux-aarch64 + archive: tar.gz + zig: true + - os: macos-latest + target: aarch64-apple-darwin + asset: tsunagi-macos-aarch64 + archive: tar.gz + zig: false + - os: windows-latest + target: x86_64-pc-windows-msvc + asset: tsunagi-windows-x86_64 + archive: zip + zig: false + + steps: + - uses: actions/checkout@v4 + + - uses: dtolnay/rust-toolchain@stable + with: + targets: ${{ matrix.target }} + + - uses: Swatinem/rust-cache@v2 + with: + key: ${{ matrix.target }} + + # aws-lc-sys (iroh's rustls backend) and the bundled sqlite are C, and the + # musl targets have no prebuilt bindings, so bindgen needs libclang and the + # C build needs cmake. zig is the cross C compiler cargo-zigbuild drives. + - name: Install musl build tooling + if: matrix.zig + run: sudo apt-get update && sudo apt-get install -y --no-install-recommends cmake clang libclang-dev + - name: Install zig + if: matrix.zig + uses: mlugg/setup-zig@v2 + with: + # Pin a real, archived Zig release. Left unpinned the action resolves + # to the newest version it knows of (a yet-unreleased 0.16.0-dev), + # whose tarball 404s on ziglang.org while the community mirrors flake. + version: 0.14.1 + - name: Install cargo-zigbuild + if: matrix.zig + uses: taiki-e/install-action@v2 + with: + tool: cargo-zigbuild + + - name: Build + shell: bash + run: | + set -euo pipefail + if [[ "${{ matrix.zig }}" == "true" ]]; then + # zig's linker ignores the `-O1` GNU-ld optimization flag Rust passes + # and says so on stderr, which rustc now surfaces as a linker_messages + # warning. It is cosmetic — the binary is correct — so quiet just that + # one lint to keep the musl build log clean. A real link error still + # fails the build. + export RUSTFLAGS="${RUSTFLAGS:-} -A linker_messages" + cargo zigbuild --release --locked --target "${{ matrix.target }}" --bin tsunagi + else + cargo build --release --locked --target "${{ matrix.target }}" --bin tsunagi + fi + + - name: Package + shell: bash + run: | + set -euo pipefail + + version="${GITHUB_REF_NAME#v}" + stage="dist/pkg/${{ matrix.asset }}" + mkdir -p "$stage" + + bin="tsunagi" + [[ "${{ runner.os }}" == "Windows" ]] && bin="tsunagi.exe" + cp "target/${{ matrix.target }}/release/${bin}" "$stage/" + + # The top-level README, plus the per-OS install notes and integration + # files, so the archive carries what the platform needs to set up. + cp README.md "$stage/" + case "${{ runner.os }}" in + Linux) + cp dist/linux/tsunagi.service dist/linux/install.sh "$stage/" + cp dist/linux/README.md "$stage/INSTALL.md" + ;; + macOS) + cp dist/macos/cy.hexor.tsunagi.plist "$stage/" + cp dist/macos/README.md "$stage/INSTALL.md" + ;; + Windows) + cp dist/windows/README.md "$stage/INSTALL.md" + ;; + esac + + archive="${{ matrix.asset }}-${version}.${{ matrix.archive }}" + if [[ "${{ matrix.archive }}" == "zip" ]]; then + (cd dist/pkg && 7z a "../../${archive}" "${{ matrix.asset }}") + else + tar -C dist/pkg -czf "${archive}" "${{ matrix.asset }}" + fi + + - name: Upload the archive as a run artifact + uses: actions/upload-artifact@v4 + with: + name: ${{ matrix.asset }} + path: ${{ matrix.asset }}-*.* + if-no-files-found: error + + publish: + name: Publish release + needs: build + if: ${{ github.server_url == 'https://github.com' }} + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - name: Download every build artifact + uses: actions/download-artifact@v4 + with: + path: artifacts + + - name: Show what was collected + run: find artifacts -type f + + - name: Create the release and upload the archives + shell: bash + env: + GH_TOKEN: ${{ github.token }} + TAG: ${{ github.ref_name }} + run: | + set -euo pipefail + + # download-artifact@v4 nests each artifact under its own directory, so + # the archives are artifacts//. + mapfile -t files < <(find artifacts -type f \( -name '*.tar.gz' -o -name '*.zip' \)) + printf 'asset: %s\n' "${files[@]}" + + prerelease=() + case "$TAG" in *-*) prerelease=(--prerelease) ;; esac + + # Create the release on the first run; on a re-run, add/replace assets + # on the release that is already there. + if gh release view "$TAG" --repo "$GITHUB_REPOSITORY" >/dev/null 2>&1; then + gh release upload "$TAG" "${files[@]}" --repo "$GITHUB_REPOSITORY" --clobber + else + gh release create "$TAG" "${files[@]}" \ + --repo "$GITHUB_REPOSITORY" \ + --title "tsunagi $TAG" \ + --generate-notes \ + --verify-tag \ + "${prerelease[@]}" + fi