Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 47 additions & 24 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Setup
- name: Setup debug build for tests
run: just setup

- name: Install musl tools and rebuild for musl target
Expand All @@ -74,16 +74,22 @@ jobs:
sudo apt-get update && sudo apt-get install -y musl-tools
rustup target add x86_64-unknown-linux-musl

# Rebuild hyperlight-js NAPI addon targeting musl
# Rebuild hyperlight-js NAPI addon targeting musl in release mode.
hl_dir=$(just resolve-hyperlight-dir)
cd "${hl_dir}/src/js-host-api"
npx napi build --platform --target x86_64-unknown-linux-musl
npx napi build --platform --release --strip --target x86_64-unknown-linux-musl

# Rebuild hyperlight-analysis NAPI addon targeting musl
# Rebuild hyperlight-analysis NAPI addon targeting musl in release mode.
cd "$GITHUB_WORKSPACE/src/code-validator/guest"
npx napi build --platform --target x86_64-unknown-linux-musl --manifest-path host/Cargo.toml
npx napi build --platform --release --strip --target x86_64-unknown-linux-musl --manifest-path host/Cargo.toml
node -e "require('fs').readdirSync('host').filter(f=>f.endsWith('.node')).forEach(f=>require('fs').copyFileSync('host/'+f,f))"

# This matrix leg must upload only musl artifacts; the setup step also
# creates host-platform glibc addons, which are supplied by linux-kvm.
rm -f "${hl_dir}/src/js-host-api/"*.linux-x64-gnu.node
rm -f "$GITHUB_WORKSPACE/src/code-validator/guest/host/"*.linux-x64-gnu.node
rm -f "$GITHUB_WORKSPACE/src/code-validator/guest/"*.linux-x64-gnu.node

# Verify musl .node files were actually produced
ls -la "${hl_dir}/src/js-host-api/"*.linux-x64-musl.node
ls -la "$GITHUB_WORKSPACE/src/code-validator/guest/"*linux-x64-musl* || ls -la "$GITHUB_WORKSPACE/src/code-validator/guest/host/"*linux-x64-musl*
Expand All @@ -98,6 +104,10 @@ jobs:
if: matrix.run_tests
run: just test

- name: Rebuild native addons for release packaging
if: matrix.run_tests
run: just build-release

# Upload the native .node addons so the publish job can combine them
- name: Upload native addons
uses: actions/upload-artifact@v7
Expand Down Expand Up @@ -159,6 +169,18 @@ jobs:
- name: Pack npm tarball
run: npm pack

- name: Check npm package size
run: |
TARBALL=$(ls *.tgz)
UNPACKED=$(npm pack --dry-run --json | node -e 'let data="";process.stdin.on("data",c=>data+=c);process.stdin.on("end",()=>console.log(JSON.parse(data)[0].unpackedSize))')
MAX=$((300 * 1024 * 1024))
echo "Packed tarball: $TARBALL"
echo "Unpacked size: $UNPACKED bytes (limit: $MAX)"
if [ "$UNPACKED" -gt "$MAX" ]; then
echo "❌ npm package is too large"
exit 1
fi

- name: Upload npm tarball
uses: actions/upload-artifact@v7
with:
Expand Down Expand Up @@ -260,33 +282,34 @@ jobs:
env:
VERSION: ${{ steps.ver.outputs.version }}
run: |
# Poll registry.npmjs.org for up to 5 minutes for the version to
# become resolvable. Cloudflare/CouchDB replication is usually fast
# but can lag for minutes after a fresh publish.
# Poll registry.npmjs.org for up to 5 minutes for the version metadata
# AND tarball blob to become fetchable. npm can expose dist metadata
# before the tarball URL has propagated through the CDN, so metadata
# visibility alone is not enough for installability.
for i in $(seq 1 30); do
ACTUAL=$(npm view "@hyperlight-dev/hyperagent@$VERSION" version 2>/dev/null || true)
if [ "$ACTUAL" = "$VERSION" ]; then
echo "✅ visible after $((i * 10))s"
exit 0
TARBALL=$(npm view "@hyperlight-dev/hyperagent@$VERSION" dist.tarball 2>/dev/null || true)
EXPECTED=$(npm view "@hyperlight-dev/hyperagent@$VERSION" dist.shasum 2>/dev/null || true)
TMP=$(mktemp)
if [ -n "$TARBALL" ] && [ -n "$EXPECTED" ] && curl -fsSL "$TARBALL" -o "$TMP"; then
FOUND=$(sha1sum "$TMP" | awk '{print $1}')
rm -f "$TMP"
if [ "$FOUND" = "$EXPECTED" ]; then
echo "✅ metadata and tarball visible after $((i * 10))s ($EXPECTED)"
exit 0
fi
echo "⏳ tarball shasum mismatch on attempt $i — expected $EXPECTED got $FOUND"
else
rm -f "$TMP"
echo "⏳ metadata visible, tarball not fetchable yet (attempt $i)"
fi
fi
sleep 10
done
echo "❌ @hyperlight-dev/hyperagent@$VERSION not visible after 300s"
echo "❌ @hyperlight-dev/hyperagent@$VERSION metadata/tarball not installable after 300s"
exit 1

- name: Verify tarball blob fetchable + shasum
env:
VERSION: ${{ steps.ver.outputs.version }}
run: |
TARBALL=$(npm view "@hyperlight-dev/hyperagent@$VERSION" dist.tarball)
EXPECTED=$(npm view "@hyperlight-dev/hyperagent@$VERSION" dist.shasum)
ACTUAL=$(curl -sSL --fail "$TARBALL" | sha1sum | awk '{print $1}')
if [ "$ACTUAL" != "$EXPECTED" ]; then
echo "❌ shasum mismatch — expected $EXPECTED got $ACTUAL"
exit 1
fi
echo "✅ tarball fetchable, shasum matches ($EXPECTED)"

- name: Install + run in ${{ matrix.image }}
env:
VERSION: ${{ steps.ver.outputs.version }}
Expand Down
5 changes: 4 additions & 1 deletion Justfile
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,9 @@ build-hyperlight target="debug": (build-runtime-release)
cd "${hl_dir}/src/hyperlight-js" && cargo clean -p hyperlight-js 2>/dev/null || true
# Build the NAPI addon (inherits HYPERLIGHT_JS_RUNTIME_PATH from env)
cd "${hl_dir}" && just build {{ if target == "debug" { "" } else { target } }}
if [ "{{target}}" = "release" ]; then
cd "${hl_dir}/src/js-host-api" && npx napi build --platform --release --strip
fi
# Symlink for npm file: dependency resolution
mkdir -p "{{justfile_dir()}}/deps"
ln -sfn "${hl_dir}/src/js-host-api" "{{hyperlight-link}}"
Expand All @@ -111,7 +114,7 @@ build-hyperlight target="debug": (build-runtime-release)
[private]
[windows]
build-hyperlight target="debug": (build-runtime-release)
$hl_dir = just resolve-hyperlight-dir; Push-Location (Join-Path $hl_dir "src" "hyperlight-js"); cargo clean -p hyperlight-js 2>$null; Pop-Location; Push-Location $hl_dir; just build {{ if target == "debug" { "" } else { target } }}; Pop-Location; $linkPath = [IO.Path]::GetFullPath("{{hyperlight-link}}"); $targetPath = Join-Path $hl_dir "src" "js-host-api"; New-Item -ItemType Directory -Path (Split-Path $linkPath) -Force | Out-Null; if (Test-Path $linkPath) { cmd /c rmdir /q $linkPath 2>$null }; cmd /c mklink /J $linkPath $targetPath; Write-Output "🔗 deps/js-host-api → $targetPath"
$hl_dir = just resolve-hyperlight-dir; Push-Location (Join-Path $hl_dir "src" "hyperlight-js"); cargo clean -p hyperlight-js 2>$null; Pop-Location; Push-Location $hl_dir; just build {{ if target == "debug" { "" } else { target } }}; Pop-Location; if ("{{target}}" -eq "release") { Push-Location (Join-Path $hl_dir "src" "js-host-api"); npx napi build --platform --release --strip; Pop-Location }; $linkPath = [IO.Path]::GetFullPath("{{hyperlight-link}}"); $targetPath = Join-Path $hl_dir "src" "js-host-api"; New-Item -ItemType Directory -Path (Split-Path $linkPath) -Force | Out-Null; if (Test-Path $linkPath) { cmd /c rmdir /q $linkPath 2>$null }; cmd /c mklink /J $linkPath $targetPath; Write-Output "🔗 deps/js-host-api → $targetPath"

# Build the hyperlight-analysis-guest NAPI addon (debug)
[private]
Expand Down
20 changes: 2 additions & 18 deletions scripts/build-binary.js
Original file line number Diff line number Diff line change
Expand Up @@ -217,25 +217,7 @@ if (!existsSync(analysisNode)) {
process.exit(1);
}

// Copy .node files for ALL available platforms so the package is cross-platform.
// The current platform's .node is guaranteed to exist (checked above).
// Additional platform .node files are copied if present (e.g. from CI matrix builds).
const ALL_TRIPLES = ["linux-x64-gnu", "linux-x64-musl", "win32-x64-msvc"];
for (const triple of ALL_TRIPLES) {
const hlNode = join(ROOT, `deps/js-host-api/js-host-api.${triple}.node`);
const anNode = join(
ROOT,
`src/code-validator/guest/host/hyperlight-analysis.${triple}.node`,
);
if (existsSync(hlNode)) {
copyFileSync(hlNode, join(LIB_DIR, `js-host-api.${triple}.node`));
console.log(` ✓ js-host-api.${triple}.node`);
}
if (existsSync(anNode)) {
copyFileSync(anNode, join(LIB_DIR, `hyperlight-analysis.${triple}.node`));
console.log(` ✓ hyperlight-analysis.${triple}.node`);
}
}

// Create a proper node_modules package structure for hyperlight-analysis
// so both require() and import() can resolve it in the bundled binary.
Expand All @@ -252,6 +234,7 @@ for (const triple of ALL_TRIPLES) {
anNode,
join(analysisPkgDir, `hyperlight-analysis.${triple}.node`),
);
console.log(` ✓ hyperlight-analysis.${triple}.node`);
}
}
// Copy the index.js and index.d.ts from the source package
Expand Down Expand Up @@ -284,6 +267,7 @@ for (const triple of ALL_TRIPLES) {
hlNode,
join(hyperlightHostApiDir, `js-host-api.${triple}.node`),
);
console.log(` ✓ js-host-api.${triple}.node`);
}
}
// Copy lib.js as lib.cjs, patching the require('./index.js') to './index.cjs'
Expand Down
2 changes: 1 addition & 1 deletion src/code-validator/guest/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
]
},
"scripts": {
"build": "napi build --platform --release --manifest-path host/Cargo.toml && node -e \"require('fs').readdirSync('host').filter(f=>f.endsWith('.node')).forEach(f=>require('fs').copyFileSync('host/'+f,f))\"",
"build": "napi build --platform --release --strip --manifest-path host/Cargo.toml && node -e \"require('fs').readdirSync('host').filter(f=>f.endsWith('.node')).forEach(f=>require('fs').copyFileSync('host/'+f,f))\"",
"build:debug": "napi build --platform --manifest-path host/Cargo.toml && node -e \"require('fs').readdirSync('host').filter(f=>f.endsWith('.node')).forEach(f=>require('fs').copyFileSync('host/'+f,f))\"",
"test": "vitest run",
"test:watch": "vitest",
Expand Down
Loading