-
Notifications
You must be signed in to change notification settings - Fork 5
Add macOS (hvf) and aarch64 support #308
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
e3a725d
6791f66
081e985
10e130e
2ae81be
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| # Hypervisor.framework requires the com.apple.security.hypervisor entitlement, | ||
| # so run macOS host binaries through a wrapper that ad-hoc signs them first. | ||
| # Cargo resolves this path relative to this file, not the working directory, | ||
| # so it also applies to the recipes that cd into src/hyperlight-js. | ||
| [target.'cfg(target_os = "macos")'] | ||
| runner = "dev/macos-sign-and-run.sh" |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -19,7 +19,7 @@ jobs: | |
| strategy: | ||
| fail-fast: true | ||
| matrix: | ||
| build: [windows-2025-debug, linux-kvm-debug, linux-hyperv3-debug, windows-2025-release, linux-kvm-release, linux-hyperv3-release] | ||
| build: [windows-2025-debug, linux-kvm-debug, linux-kvm-arm64-debug, linux-hyperv3-debug, macos-hvf-debug, windows-2025-release, linux-kvm-release, linux-kvm-arm64-release, linux-hyperv3-release, macos-hvf-release] | ||
| include: | ||
| - build: windows-2025-debug | ||
| os: [self-hosted, Windows, X64, "1ES.Pool=hld-win2025-amd"] | ||
|
|
@@ -28,25 +28,48 @@ jobs: | |
| - build: linux-kvm-debug | ||
| os: [self-hosted, Linux, X64, "1ES.Pool=hld-kvm-amd"] | ||
| hypervisor: kvm | ||
| arch: X64 | ||
| config: debug | ||
| - build: linux-kvm-arm64-debug | ||
| os: [self-hosted, Linux, arm64, kvm] | ||
| hypervisor: kvm | ||
| arch: arm64 | ||
| config: debug | ||
| - build: linux-hyperv3-debug | ||
| os: [self-hosted, Linux, X64, "1ES.Pool=hld-azlinux3-mshv-amd"] | ||
| hypervisor: hyperv3 | ||
| config: debug | ||
| - build: macos-hvf-debug | ||
| os: [self-hosted, macos, arm64, hvf] | ||
| hypervisor: hvf | ||
| config: debug | ||
| - build: windows-2025-release | ||
| os: [self-hosted, Windows, X64, "1ES.Pool=hld-win2025-amd"] | ||
| hypervisor: whp | ||
| config: release | ||
| - build: linux-kvm-release | ||
| os: [self-hosted, Linux, X64, "1ES.Pool=hld-kvm-amd"] | ||
| hypervisor: kvm | ||
| arch: X64 | ||
| config: release | ||
| - build: linux-kvm-arm64-release | ||
| os: [self-hosted, Linux, arm64, kvm] | ||
| hypervisor: kvm | ||
| arch: arm64 | ||
| config: release | ||
| - build: linux-hyperv3-release | ||
| os: [self-hosted, Linux, X64, "1ES.Pool=hld-azlinux3-mshv-amd"] | ||
| hypervisor: hyperv3 | ||
| config: release | ||
| runs-on: ${{ fromJson( | ||
| format('["self-hosted", "{0}", "X64", "1ES.Pool=hld-{1}-amd", "JobId=build-{2}-{3}-{4}-{5}"]', | ||
| - build: macos-hvf-release | ||
| os: [self-hosted, macos, arm64, hvf] | ||
| hypervisor: hvf | ||
| config: release | ||
| runs-on: ${{ fromJson(matrix.hypervisor == 'hvf' | ||
| && '["self-hosted", "macos", "arm64", "hvf"]' | ||
| || matrix.arch == 'arm64' | ||
| && '["self-hosted", "Linux", "arm64", "kvm"]' | ||
| || format('["self-hosted", "{0}", "X64", "1ES.Pool=hld-{1}-amd", "JobId=build-{2}-{3}-{4}-{5}"]', | ||
| matrix.hypervisor == 'whp' && 'Windows' || 'Linux', | ||
| matrix.hypervisor == 'whp' && 'win2025' || matrix.hypervisor == 'hyperv3' && 'azlinux3-mshv' || 'kvm', | ||
| matrix.build, | ||
|
|
@@ -64,13 +87,62 @@ jobs: | |
| rust-toolchain: "1.90" | ||
| just-version: "1.51" | ||
|
|
||
| # Unlike upstream hyperlight, which builds its guests once per architecture | ||
| # and ships them to the test jobs as artifacts, hyperlight-js builds the JS | ||
| # guest runtime from build.rs during every `cargo build`. That makes a | ||
| # bare-metal ELF toolchain a hard requirement on macOS too. | ||
| # | ||
| # macOS's system archiver cannot produce ELF archives. cargo-hyperlight | ||
| # prefers llvm-ar on macOS for exactly that reason, but silently falls back | ||
| # to /usr/bin/ar when llvm-ar is missing from PATH, which yields a guest | ||
| # link with every QuickJS and libc symbol undefined. The runners have no | ||
| # package manager, so take llvm-ar from rustup's llvm-tools component, | ||
| # which is version-matched to the pinned toolchain. | ||
| - name: Set up LLVM guest toolchain (macOS) | ||
| if: runner.os == 'macOS' | ||
| shell: bash | ||
|
Comment on lines
+101
to
+103
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we install this while provising runners or similar instead of here? And what's the retried for? |
||
| run: | | ||
| set -euo pipefail | ||
| llvm_bin="$(rustc --print sysroot)/lib/rustlib/$(rustc -vV | sed -n 's/^host: //p')/bin" | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. should we allow an existing llvm-ar not related to the rust-toolchain? |
||
| for attempt in 1 2 3; do | ||
| if [ -x "$llvm_bin/llvm-ar" ]; then | ||
| break | ||
| fi | ||
| echo "Installing llvm-tools (attempt $attempt)" | ||
| rustup component add llvm-tools || true | ||
| [ -x "$llvm_bin/llvm-ar" ] || sleep $((attempt * 10)) | ||
| done | ||
| if [ ! -x "$llvm_bin/llvm-ar" ]; then | ||
| echo "llvm-ar not found in $llvm_bin" >&2 | ||
| exit 1 | ||
| fi | ||
| echo "$llvm_bin" >> "$GITHUB_PATH" | ||
| "$llvm_bin/llvm-ar" --version | ||
|
|
||
| - name: install nodejs | ||
| uses: actions/setup-node@v7 | ||
| with: | ||
| node-version: '22' | ||
| cache: 'npm' | ||
| cache-dependency-path: 'src/js-host-api/package-lock.json' | ||
|
|
||
| # The cargo runner in .cargo/config.toml only signs binaries cargo itself | ||
| # launches, so the js-host-api tests -- which run under node via vitest -- | ||
| # still hit HV_DENIED. Sign a private copy rather than the shared tool | ||
| # cache: these runners are not ephemeral. | ||
| - name: Sign node for Hypervisor.framework (macOS) | ||
| if: runner.os == 'macOS' | ||
| shell: bash | ||
| run: | | ||
| set -euo pipefail | ||
| signed_bin="$RUNNER_TEMP/node-signed" | ||
| mkdir -p "$signed_bin" | ||
| cp "$(command -v node)" "$signed_bin/node" | ||
| codesign -f -s - --entitlements dev/macos-entitlements.plist "$signed_bin/node" | ||
| codesign -d --entitlements - "$signed_bin/node" | ||
| echo "$signed_bin" >> "$GITHUB_PATH" | ||
| "$signed_bin/node" --version | ||
|
|
||
| - name: fmt | ||
| run: just fmt-check | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -41,6 +41,10 @@ concurrency: | |
|
|
||
| env: | ||
| WORKING_DIR: src/js-host-api | ||
| # Comma-delimited npm package names that have not yet been published and | ||
| # therefore cannot have a trusted publisher configured. Remove each package | ||
| # in a follow-up PR after its first release and trusted-publisher setup. | ||
| FIRST_TIME_PACKAGES: ",@hyperlight-dev/js-host-api-darwin-arm64," | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This makes releases require NPM_TOKEN, but the caller in CreateRelease.yml does not forward secrets. Token validation will fail even if the repository secret exists. Could we also update the caller to pass it through? |
||
|
|
||
| jobs: | ||
| build: | ||
|
|
@@ -57,6 +61,9 @@ jobs: | |
| - target: x86_64-pc-windows-msvc | ||
| os: [self-hosted, Windows, X64, "1ES.Pool=hld-win2025-amd"] | ||
| build_name: win32-x64-msvc | ||
| - target: aarch64-apple-darwin | ||
| os: macos-15 | ||
| build_name: darwin-arm64 | ||
| runs-on: ${{ matrix.os }} | ||
| steps: | ||
| - uses: actions/checkout@v7 | ||
|
|
@@ -76,6 +83,33 @@ jobs: | |
| cache: 'npm' | ||
| cache-dependency-path: 'src/js-host-api/package-lock.json' | ||
|
|
||
| # Building js-host-api runs hyperlight-js's build.rs, which builds the JS | ||
| # guest runtime for a bare-metal ELF target. macOS's system archiver | ||
| # cannot produce ELF archives, and cargo-hyperlight silently falls back to | ||
| # it when llvm-ar is absent from PATH, leaving every QuickJS and libc | ||
| # symbol undefined at the guest link. Take llvm-ar from rustup's | ||
| # llvm-tools component so this does not depend on a package manager. | ||
| - name: Set up LLVM guest toolchain (macOS) | ||
| if: runner.os == 'macOS' | ||
| shell: bash | ||
| run: | | ||
| set -euo pipefail | ||
| llvm_bin="$(rustc --print sysroot)/lib/rustlib/$(rustc -vV | sed -n 's/^host: //p')/bin" | ||
| for attempt in 1 2 3; do | ||
| if [ -x "$llvm_bin/llvm-ar" ]; then | ||
| break | ||
| fi | ||
| echo "Installing llvm-tools (attempt $attempt)" | ||
| rustup component add llvm-tools || true | ||
| [ -x "$llvm_bin/llvm-ar" ] || sleep $((attempt * 10)) | ||
| done | ||
| if [ ! -x "$llvm_bin/llvm-ar" ]; then | ||
| echo "llvm-ar not found in $llvm_bin" >&2 | ||
| exit 1 | ||
| fi | ||
| echo "$llvm_bin" >> "$GITHUB_PATH" | ||
| "$llvm_bin/llvm-ar" --version | ||
|
|
||
| - name: Install dependencies | ||
| working-directory: ${{ env.WORKING_DIR }} | ||
| run: npm ci --ignore-scripts --omit=optional | ||
|
|
@@ -173,6 +207,12 @@ jobs: | |
| name: bindings-win32-x64-msvc | ||
| path: ${{ env.WORKING_DIR }}/artifacts/win32-x64-msvc | ||
|
|
||
| - name: Download macOS arm64 artifact | ||
| uses: actions/download-artifact@v8 | ||
| with: | ||
| name: bindings-darwin-arm64 | ||
| path: ${{ env.WORKING_DIR }}/artifacts/darwin-arm64 | ||
|
|
||
| - name: Download JS bindings | ||
| uses: actions/download-artifact@v8 | ||
| with: | ||
|
|
@@ -189,9 +229,11 @@ jobs: | |
| mv artifacts/linux-x64-gnu/*.node npm/linux-x64-gnu/js-host-api.linux-x64-gnu.node | ||
| mv artifacts/linux-x64-musl/*.node npm/linux-x64-musl/js-host-api.linux-x64-musl.node | ||
| mv artifacts/win32-x64-msvc/*.node npm/win32-x64-msvc/js-host-api.win32-x64-msvc.node | ||
| mv artifacts/darwin-arm64/*.node npm/darwin-arm64/js-host-api.darwin-arm64.node | ||
| ls -la npm/linux-x64-gnu/ | ||
| ls -la npm/linux-x64-musl/ | ||
| ls -la npm/win32-x64-msvc/ | ||
| ls -la npm/darwin-arm64/ | ||
|
|
||
| - name: Set package versions | ||
| working-directory: ${{ env.WORKING_DIR }} | ||
|
|
@@ -203,6 +245,7 @@ jobs: | |
| cd npm/linux-x64-gnu && npm version "$VERSION" --no-git-tag-version --allow-same-version | ||
| cd ../linux-x64-musl && npm version "$VERSION" --no-git-tag-version --allow-same-version | ||
| cd ../win32-x64-msvc && npm version "$VERSION" --no-git-tag-version --allow-same-version | ||
| cd ../darwin-arm64 && npm version "$VERSION" --no-git-tag-version --allow-same-version | ||
| env: | ||
| VERSION: ${{ inputs.version }} | ||
|
|
||
|
|
@@ -239,66 +282,55 @@ jobs: | |
| run: ./test-pack.sh | ||
|
|
||
| # ── Authentication strategy ──────────────────────────────────── | ||
| # Production releases via CreateRelease.yml use trusted publishing | ||
| # (OIDC) — npm auto-detects the id-token and exchanges it for a | ||
| # short-lived publish credential. Provenance attestations are | ||
| # generated automatically. The --provenance flag is added explicitly | ||
| # so the build fails loudly if OIDC is misconfigured. | ||
| # | ||
| # Manual workflow_dispatch requires an NPM_TOKEN repo secret because | ||
| # trusted publishing is configured for CreateRelease.yml only. | ||
| # Provenance is NOT available for token-based publishing. | ||
| # You should almost never need to publish manually — if you do, | ||
| # see docs/release.md for the full (deliberately painful) steps. | ||
| - name: Validate NPM_TOKEN for manual dispatch | ||
| if: ${{ github.event_name == 'workflow_dispatch' && !inputs.dry_run }} | ||
| # Established packages use trusted publishing (OIDC). Packages listed | ||
| # in FIRST_TIME_PACKAGES use NPM_TOKEN for this release only because | ||
| # npm has no trusted-publisher configuration until the package exists. | ||
| # Token-published packages cannot receive npm provenance attestations. | ||
| - name: Validate NPM_TOKEN for token-published packages | ||
| if: ${{ !inputs.dry_run && (github.event_name == 'workflow_dispatch' || env.FIRST_TIME_PACKAGES != '') }} | ||
| run: | | ||
| if [ -z "$NPM_TOKEN" ]; then | ||
| echo "::error::NPM_TOKEN repo secret is required for manual workflow_dispatch publishing." | ||
| echo "::error::See docs/release.md 'Manual npm publishing (emergency only)' for instructions." | ||
| echo "::error::NPM_TOKEN repo secret is required for manual publishing or first-time packages." | ||
| echo "::error::See docs/release.md for the first-time package bootstrap procedure." | ||
| exit 1 | ||
| fi | ||
| env: | ||
| NPM_TOKEN: ${{ secrets.NPM_TOKEN }} | ||
|
|
||
| # Set provenance flag once — OIDC path gets --provenance (fails loud | ||
| # if misconfigured), token path skips it (not supported). | ||
| - name: Set publish flags | ||
| id: publish-flags | ||
| run: | | ||
| if [ "${{ github.event_name }}" != "workflow_dispatch" ]; then | ||
| echo "provenance=--provenance" >> "$GITHUB_OUTPUT" | ||
| else | ||
| echo "provenance=" >> "$GITHUB_OUTPUT" | ||
| fi | ||
|
|
||
| - name: Publish Linux GNU package | ||
| if: ${{ !inputs.dry_run }} | ||
| working-directory: ${{ env.WORKING_DIR }}/npm/linux-x64-gnu | ||
| run: npm publish --access public --ignore-scripts ${{ steps.publish-flags.outputs.provenance }} | ||
| run: npm publish --access public --ignore-scripts ${{ (github.event_name == 'workflow_dispatch' || contains(env.FIRST_TIME_PACKAGES, ',@hyperlight-dev/js-host-api-linux-x64-gnu,')) && '' || '--provenance' }} | ||
| env: | ||
| NODE_AUTH_TOKEN: ${{ github.event_name == 'workflow_dispatch' && secrets.NPM_TOKEN || '' }} | ||
| NODE_AUTH_TOKEN: ${{ (github.event_name == 'workflow_dispatch' || contains(env.FIRST_TIME_PACKAGES, ',@hyperlight-dev/js-host-api-linux-x64-gnu,')) && secrets.NPM_TOKEN || '' }} | ||
|
|
||
| - name: Publish Linux musl package | ||
| if: ${{ !inputs.dry_run }} | ||
| working-directory: ${{ env.WORKING_DIR }}/npm/linux-x64-musl | ||
| run: npm publish --access public --ignore-scripts ${{ steps.publish-flags.outputs.provenance }} | ||
| run: npm publish --access public --ignore-scripts ${{ (github.event_name == 'workflow_dispatch' || contains(env.FIRST_TIME_PACKAGES, ',@hyperlight-dev/js-host-api-linux-x64-musl,')) && '' || '--provenance' }} | ||
| env: | ||
| NODE_AUTH_TOKEN: ${{ github.event_name == 'workflow_dispatch' && secrets.NPM_TOKEN || '' }} | ||
| NODE_AUTH_TOKEN: ${{ (github.event_name == 'workflow_dispatch' || contains(env.FIRST_TIME_PACKAGES, ',@hyperlight-dev/js-host-api-linux-x64-musl,')) && secrets.NPM_TOKEN || '' }} | ||
|
|
||
| - name: Publish Windows package | ||
| if: ${{ !inputs.dry_run }} | ||
| working-directory: ${{ env.WORKING_DIR }}/npm/win32-x64-msvc | ||
| run: npm publish --access public --ignore-scripts ${{ steps.publish-flags.outputs.provenance }} | ||
| run: npm publish --access public --ignore-scripts ${{ (github.event_name == 'workflow_dispatch' || contains(env.FIRST_TIME_PACKAGES, ',@hyperlight-dev/js-host-api-win32-x64-msvc,')) && '' || '--provenance' }} | ||
| env: | ||
| NODE_AUTH_TOKEN: ${{ github.event_name == 'workflow_dispatch' && secrets.NPM_TOKEN || '' }} | ||
| NODE_AUTH_TOKEN: ${{ (github.event_name == 'workflow_dispatch' || contains(env.FIRST_TIME_PACKAGES, ',@hyperlight-dev/js-host-api-win32-x64-msvc,')) && secrets.NPM_TOKEN || '' }} | ||
|
|
||
| - name: Publish macOS arm64 package | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could we validate the macOS package tarball before publishing? test-pack.sh only packs and installs the Linux GNU package. The macOS CI tests exercise the build, but not installation from the release tarball. |
||
| if: ${{ !inputs.dry_run }} | ||
| working-directory: ${{ env.WORKING_DIR }}/npm/darwin-arm64 | ||
| run: npm publish --access public --ignore-scripts ${{ (github.event_name == 'workflow_dispatch' || contains(env.FIRST_TIME_PACKAGES, ',@hyperlight-dev/js-host-api-darwin-arm64,')) && '' || '--provenance' }} | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. AI is telling me the |
||
| env: | ||
| NODE_AUTH_TOKEN: ${{ (github.event_name == 'workflow_dispatch' || contains(env.FIRST_TIME_PACKAGES, ',@hyperlight-dev/js-host-api-darwin-arm64,')) && secrets.NPM_TOKEN || '' }} | ||
|
|
||
| - name: Publish main package | ||
| if: ${{ !inputs.dry_run }} | ||
| working-directory: ${{ env.WORKING_DIR }} | ||
| run: npm publish --access public --ignore-scripts ${{ steps.publish-flags.outputs.provenance }} | ||
| run: npm publish --access public --ignore-scripts ${{ (github.event_name == 'workflow_dispatch' || contains(env.FIRST_TIME_PACKAGES, ',@hyperlight-dev/js-host-api,')) && '' || '--provenance' }} | ||
| env: | ||
| NODE_AUTH_TOKEN: ${{ github.event_name == 'workflow_dispatch' && secrets.NPM_TOKEN || '' }} | ||
| NODE_AUTH_TOKEN: ${{ (github.event_name == 'workflow_dispatch' || contains(env.FIRST_TIME_PACKAGES, ',@hyperlight-dev/js-host-api,')) && secrets.NPM_TOKEN || '' }} | ||
|
|
||
| - name: Verify all packages published | ||
| if: ${{ !inputs.dry_run }} | ||
|
|
@@ -309,7 +341,8 @@ jobs: | |
| for pkg in "@hyperlight-dev/js-host-api" \ | ||
| "@hyperlight-dev/js-host-api-linux-x64-gnu" \ | ||
| "@hyperlight-dev/js-host-api-linux-x64-musl" \ | ||
| "@hyperlight-dev/js-host-api-win32-x64-msvc"; do | ||
| "@hyperlight-dev/js-host-api-win32-x64-msvc" \ | ||
| "@hyperlight-dev/js-host-api-darwin-arm64"; do | ||
| if npm view "$pkg@$VERSION" version > /dev/null 2>&1; then | ||
| echo "✅ $pkg@$VERSION published" | ||
| else | ||
|
|
@@ -339,5 +372,8 @@ jobs: | |
| echo "--- @hyperlight-dev/js-host-api-win32-x64-msvc ---" | ||
| npm pack ./npm/win32-x64-msvc --dry-run | ||
| echo "" | ||
| echo "--- @hyperlight-dev/js-host-api-darwin-arm64 ---" | ||
| npm pack ./npm/darwin-arm64 --dry-run | ||
| echo "" | ||
| echo "--- @hyperlight-dev/js-host-api ---" | ||
| npm pack --dry-run | ||
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it appears not every entry has a
archfield. Is this intended? I think adding it to every entry would make sense