systemless 0.33.1

High-Level Emulation for classic Macintosh applications
Documentation
name: Release binaries

on:
  workflow_call:
    inputs:
      tag:
        description: Existing release tag to build and attach binaries to
        required: true
        type: string
  workflow_dispatch:
    inputs:
      tag:
        description: Existing release tag to build and attach binaries to
        required: true
        type: string

permissions:
  contents: read

jobs:
  build:
    name: Binary (${{ matrix.target }})
    runs-on: ${{ matrix.os }}
    strategy:
      fail-fast: false
      matrix:
        include:
          - target: x86_64-unknown-linux-gnu
            os: ubuntu-22.04
          - target: aarch64-unknown-linux-gnu
            os: ubuntu-22.04-arm
          - target: x86_64-apple-darwin
            os: macos-15-intel
          - target: aarch64-apple-darwin
            os: macos-14
          - target: x86_64-pc-windows-msvc
            os: windows-2022
          - target: aarch64-pc-windows-msvc
            os: windows-11-arm
    defaults:
      run:
        shell: bash
    steps:
      - uses: actions/checkout@v6
        with:
          ref: ${{ inputs.tag || github.sha }}
      - uses: actions/setup-python@v6
        with:
          python-version: '3.12'
      - name: Validate release tag against crate version
        if: inputs.tag != ''
        env:
          RELEASE_TAG: ${{ inputs.tag }}
        run: |
          python - <<'PYTHON'
          import os, tomllib
          with open("Cargo.toml", "rb") as manifest:
              version = tomllib.load(manifest)["package"]["version"]
          assert os.environ["RELEASE_TAG"] == f"v{version}", "Release tag must match Cargo.toml"
          PYTHON
      - name: Install Linux audio dependencies
        if: runner.os == 'Linux'
        run: |
          sudo apt-get update
          sudo apt-get install -y pkg-config libasound2-dev
      - uses: dtolnay/rust-toolchain@stable
        with:
          targets: ${{ matrix.target }}
      - uses: Swatinem/rust-cache@v2
        with:
          key: release-${{ matrix.target }}
      - name: Build all release binaries with default GUI and JIT features
        run: cargo build --locked --release --bins --target '${{ matrix.target }}'
      - name: Package and smoke-test archive
        run: |
          python - '${{ matrix.target }}' <<'PYTHON'
          import hashlib
          import json
          import pathlib
          import shutil
          import subprocess
          import sys
          import tarfile
          import tempfile
          import tomllib
          import zipfile


          target = sys.argv[1]
          with open("Cargo.toml", "rb") as manifest:
              version = tomllib.load(manifest)["package"]["version"]
          name = f"systemless-{target}-v{version}"
          windows = "windows" in target
          metadata = json.loads(subprocess.check_output(
              ["cargo", "metadata", "--locked", "--no-deps", "--format-version", "1"], text=True
          ))
          crate = next(p for p in metadata["packages"] if p["name"] == "systemless")
          binaries = [t["name"] + (".exe" if windows else "")
                      for t in crate["targets"] if "bin" in t["kind"]]
          assert binaries, "Cargo metadata contains no binaries"
          dist = pathlib.Path("dist")
          dist.mkdir(exist_ok=True)
          archive = dist / (name + (".zip" if windows else ".tgz"))

          with tempfile.TemporaryDirectory() as temporary:
              root = pathlib.Path(temporary)
              package = root / name
              package.mkdir()
              for binary in binaries:
                  shutil.copy2(pathlib.Path("target") / target / "release" / binary, package)
              for filename in ("LICENSE", "OFL.txt", "README.md"):
                  shutil.copy2(filename, package)
              if windows:
                  with zipfile.ZipFile(archive, "w", zipfile.ZIP_DEFLATED) as output:
                      for path in sorted(package.iterdir()):
                          output.write(path, f"{name}/{path.name}")
              else:
                  with tarfile.open(archive, "w:gz") as output:
                      output.add(package, arcname=name)
              # Run the executable extracted from the archive to check both the package
              # layout and native runtime dependencies without requiring a GUI display.
              extracted = root / "extracted"
              if windows:
                  with zipfile.ZipFile(archive) as source:
                      source.extractall(extracted)
              else:
                  with tarfile.open(archive) as source:
                      source.extractall(extracted, filter="data")
              for binary in binaries:
                  assert (extracted / name / binary).is_file(), f"Archive is missing {binary}"
              binary = "systemless.exe" if windows else "systemless"
              subprocess.run([str(extracted / name / binary), "--help"], check=True)

          checksum = hashlib.file_digest(archive.open("rb"), "sha256").hexdigest()
          archive.with_name(archive.name + ".sha256").write_text(
              f"{checksum}  {archive.name}\n", encoding="utf-8"
          )
          print(archive)
          PYTHON
      - uses: actions/upload-artifact@v4
        with:
          name: systemless-${{ matrix.target }}
          path: dist/*
          if-no-files-found: error

  upload:
    needs: build
    if: inputs.tag != '' && github.event_name != 'pull_request'
    runs-on: ubuntu-latest
    permissions:
      contents: write
    concurrency:
      group: release-binaries-${{ inputs.tag }}
      cancel-in-progress: false
    steps:
      - uses: actions/download-artifact@v4
        with:
          pattern: systemless-*
          merge-multiple: true
          path: dist
      - name: Attach archives and checksums to the existing release
        env:
          GH_TOKEN: ${{ github.token }}
          GH_REPO: ${{ github.repository }}
          RELEASE_TAG: ${{ inputs.tag }}
        run: |
          gh release view "$RELEASE_TAG"
          gh release upload "$RELEASE_TAG" dist/* --clobber