feat(HAI-079): add cross-platform matrix builds for release workflows

- Convert release.yml and test-release.yml to matrix strategy with Linux, macOS (arm64/x64), and Windows runners
- Add platform-specific checksum generation (sha256sum, shasum, Get-FileHash)
- Split release into build and release jobs with artifact upload/download
- Add matrix build assertion tests for both workflow files
- Update README with supported platforms table and binary details
This commit is contained in:
Dustin Byrne
2026-03-26 00:50:00 -04:00
parent 5dea073d22
commit 7deca373f7
4 changed files with 212 additions and 51 deletions

View File

@@ -9,9 +9,25 @@ permissions:
contents: write contents: write
jobs: jobs:
release: build:
name: Build & Release name: Build (${{ matrix.binary }})
runs-on: ubuntu-latest runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-latest
target: bun-linux-x64
binary: hai-linux-x64
- os: macos-latest
target: bun-darwin-arm64
binary: hai-darwin-arm64
- os: macos-13
target: bun-darwin-x64
binary: hai-darwin-x64
- os: windows-latest
target: bun-windows-x64
binary: hai-windows-x64.exe
steps: steps:
- name: Checkout - name: Checkout
@@ -36,28 +52,57 @@ jobs:
run: pnpm build run: pnpm build
- name: Build standalone binary - name: Build standalone binary
run: pnpm build:exe run: pnpm --filter hai build:exe -- --target ${{ matrix.target }}
- name: Rename binary with platform and arch - name: Generate checksum (Linux)
run: | if: runner.os == 'Linux'
PLATFORM=$(uname -s | tr '[:upper:]' '[:lower:]')
ARCH=$(uname -m)
case "$ARCH" in
x86_64) ARCH="x64" ;;
aarch64) ARCH="arm64" ;;
esac
cp packages/cli/dist/hai packages/cli/dist/hai-${PLATFORM}-${ARCH}
- name: Generate checksums
run: | run: |
cd packages/cli/dist cd packages/cli/dist
for file in hai-*; do sha256sum ${{ matrix.binary }} > ${{ matrix.binary }}.sha256
sha256sum "$file" > "${file}.sha256"
done - name: Generate checksum (macOS)
if: runner.os == 'macOS'
run: |
cd packages/cli/dist
shasum -a 256 ${{ matrix.binary }} > ${{ matrix.binary }}.sha256
- name: Generate checksum (Windows)
if: runner.os == 'Windows'
shell: pwsh
run: |
cd packages/cli/dist
$hash = (Get-FileHash -Algorithm SHA256 "${{ matrix.binary }}").Hash.ToLower()
"$hash ${{ matrix.binary }}" | Out-File -Encoding ascii "${{ matrix.binary }}.sha256"
- name: Upload binary artifact
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.binary }}
path: |
packages/cli/dist/${{ matrix.binary }}
packages/cli/dist/${{ matrix.binary }}.sha256
release:
name: Create Release
needs: build
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Download all artifacts
uses: actions/download-artifact@v4
with:
path: artifacts
- name: Collect release files
run: |
mkdir -p release
find artifacts -type f -exec cp {} release/ \;
ls -la release/
- name: Create GitHub Release - name: Create GitHub Release
uses: softprops/action-gh-release@v2 uses: softprops/action-gh-release@v2
with: with:
generate_release_notes: true generate_release_notes: true
files: | files: release/*
packages/cli/dist/hai-*

View File

@@ -4,9 +4,25 @@ on:
workflow_dispatch: workflow_dispatch:
jobs: jobs:
test-release: build:
name: Test Build & Package name: Test Build (${{ matrix.binary }})
runs-on: ubuntu-latest runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-latest
target: bun-linux-x64
binary: hai-linux-x64
- os: macos-latest
target: bun-darwin-arm64
binary: hai-darwin-arm64
- os: macos-13
target: bun-darwin-x64
binary: hai-darwin-x64
- os: windows-latest
target: bun-windows-x64
binary: hai-windows-x64.exe
steps: steps:
- name: Checkout - name: Checkout
@@ -31,32 +47,59 @@ jobs:
run: pnpm build run: pnpm build
- name: Build standalone binary - name: Build standalone binary
run: pnpm build:exe run: pnpm --filter hai build:exe -- --target ${{ matrix.target }}
- name: Rename binary with platform and arch - name: Generate checksum (Linux)
run: | if: runner.os == 'Linux'
PLATFORM=$(uname -s | tr '[:upper:]' '[:lower:]')
ARCH=$(uname -m)
case "$ARCH" in
x86_64) ARCH="x64" ;;
aarch64) ARCH="arm64" ;;
esac
cp packages/cli/dist/hai packages/cli/dist/hai-${PLATFORM}-${ARCH}
- name: Generate checksums
run: | run: |
cd packages/cli/dist cd packages/cli/dist
for file in hai-*; do sha256sum ${{ matrix.binary }} > ${{ matrix.binary }}.sha256
sha256sum "$file" > "${file}.sha256"
done
- name: Smoke test - name: Generate checksum (macOS)
if: runner.os == 'macOS'
run: | run: |
chmod +x packages/cli/dist/hai cd packages/cli/dist
packages/cli/dist/hai --help shasum -a 256 ${{ matrix.binary }} > ${{ matrix.binary }}.sha256
- name: Generate checksum (Windows)
if: runner.os == 'Windows'
shell: pwsh
run: |
cd packages/cli/dist
$hash = (Get-FileHash -Algorithm SHA256 "${{ matrix.binary }}").Hash.ToLower()
"$hash ${{ matrix.binary }}" | Out-File -Encoding ascii "${{ matrix.binary }}.sha256"
- name: Smoke test (Unix)
if: runner.os != 'Windows'
run: |
chmod +x packages/cli/dist/${{ matrix.binary }}
packages/cli/dist/${{ matrix.binary }} --help
- name: Smoke test (Windows)
if: runner.os == 'Windows'
run: .\packages\cli\dist\${{ matrix.binary }} --help
- name: Upload binary artifact - name: Upload binary artifact
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: with:
name: hai-binary name: ${{ matrix.binary }}
path: packages/cli/dist/hai-* path: |
packages/cli/dist/${{ matrix.binary }}
packages/cli/dist/${{ matrix.binary }}.sha256
collect:
name: Collect Artifacts
needs: build
runs-on: ubuntu-latest
steps:
- name: Download all artifacts
uses: actions/download-artifact@v4
with:
path: artifacts
- name: Upload combined archive
uses: actions/upload-artifact@v4
with:
name: hai-all-platforms
path: artifacts/**/*

View File

@@ -203,10 +203,16 @@ Pre-built standalone binaries are published automatically via GitHub Actions.
### Downloading binaries ### Downloading binaries
Download the latest binary from the [GitHub Releases](../../releases) page. Each release includes: Download the latest binary from the [GitHub Releases](../../releases) page. Each release includes platform-specific binaries and SHA256 checksum files for verification.
- Platform-specific binaries (e.g., `hai-linux-x64`) #### Supported platforms
- SHA256 checksum files for verification
| Platform | Binary | Runner |
|----------|--------|--------|
| Linux x64 | `hai-linux-x64` | `ubuntu-latest` |
| macOS arm64 (Apple Silicon) | `hai-darwin-arm64` | `macos-latest` |
| macOS x64 (Intel) | `hai-darwin-x64` | `macos-13` |
| Windows x64 | `hai-windows-x64.exe` | `windows-latest` |
### Triggering a release ### Triggering a release
@@ -217,7 +223,7 @@ git tag v0.1.0
git push origin v0.1.0 git push origin v0.1.0
``` ```
The release workflow will automatically build the binary and create a GitHub Release with the artifact attached. The release workflow will automatically build native binaries for all supported platforms and create a GitHub Release with all artifacts attached.
### CI pipeline ### CI pipeline

View File

@@ -88,13 +88,50 @@ describe("Release workflow (.github/workflows/release.yml)", () => {
expect(content).toContain("pnpm build"); expect(content).toContain("pnpm build");
}); });
it("includes pnpm build:exe step", () => { it("includes build:exe step", () => {
expect(content).toContain("pnpm build:exe"); expect(content).toContain("build:exe");
}); });
it("generates SHA256 checksums", () => { it("generates SHA256 checksums", () => {
expect(content).toContain("sha256sum"); expect(content).toContain("sha256sum");
}); });
describe("matrix build strategy", () => {
it("has a build job with strategy.matrix including at least 4 entries", () => {
const buildJob = workflow.jobs.build;
expect(buildJob).toBeDefined();
expect(buildJob.strategy?.matrix?.include?.length).toBeGreaterThanOrEqual(4);
});
it("includes all required OS runners", () => {
const runners = workflow.jobs.build.strategy.matrix.include.map((e: any) => e.os);
expect(runners).toContain("ubuntu-latest");
expect(runners).toContain("macos-latest");
expect(runners).toContain("macos-13");
expect(runners).toContain("windows-latest");
});
it("includes all required Bun targets", () => {
const targets = workflow.jobs.build.strategy.matrix.include.map((e: any) => e.target);
expect(targets).toContain("bun-linux-x64");
expect(targets).toContain("bun-darwin-arm64");
expect(targets).toContain("bun-darwin-x64");
expect(targets).toContain("bun-windows-x64");
});
it("has a release job that needs the build job", () => {
const releaseJob = workflow.jobs.release;
expect(releaseJob).toBeDefined();
const needs = Array.isArray(releaseJob.needs) ? releaseJob.needs : [releaseJob.needs];
expect(needs).toContain("build");
});
it("generates checksums on all platforms", () => {
expect(content).toContain("sha256sum");
expect(content).toContain("shasum -a 256");
expect(content).toContain("Get-FileHash");
});
});
}); });
describe("Test Release workflow (.github/workflows/test-release.yml)", () => { describe("Test Release workflow (.github/workflows/test-release.yml)", () => {
@@ -124,8 +161,8 @@ describe("Test Release workflow (.github/workflows/test-release.yml)", () => {
expect(content).toContain("pnpm build"); expect(content).toContain("pnpm build");
}); });
it("includes pnpm build:exe step", () => { it("includes build:exe step", () => {
expect(content).toContain("pnpm build:exe"); expect(content).toContain("build:exe");
}); });
it("includes smoke test with --help", () => { it("includes smoke test with --help", () => {
@@ -135,4 +172,34 @@ describe("Test Release workflow (.github/workflows/test-release.yml)", () => {
it("uploads artifact", () => { it("uploads artifact", () => {
expect(content).toContain("actions/upload-artifact"); expect(content).toContain("actions/upload-artifact");
}); });
describe("matrix build strategy", () => {
it("has a build job with strategy.matrix including at least 4 entries", () => {
const buildJob = workflow.jobs.build;
expect(buildJob).toBeDefined();
expect(buildJob.strategy?.matrix?.include?.length).toBeGreaterThanOrEqual(4);
});
it("includes all required OS runners", () => {
const runners = workflow.jobs.build.strategy.matrix.include.map((e: any) => e.os);
expect(runners).toContain("ubuntu-latest");
expect(runners).toContain("macos-latest");
expect(runners).toContain("macos-13");
expect(runners).toContain("windows-latest");
});
it("includes all required Bun targets", () => {
const targets = workflow.jobs.build.strategy.matrix.include.map((e: any) => e.target);
expect(targets).toContain("bun-linux-x64");
expect(targets).toContain("bun-darwin-arm64");
expect(targets).toContain("bun-darwin-x64");
expect(targets).toContain("bun-windows-x64");
});
it("generates checksums on all platforms", () => {
expect(content).toContain("sha256sum");
expect(content).toContain("shasum -a 256");
expect(content).toContain("Get-FileHash");
});
});
}); });