Skip to content

Commit a192f20

Browse files
committed
fix(release): reduce npm package size and harden smoke test
1 parent df1a03c commit a192f20

4 files changed

Lines changed: 54 additions & 44 deletions

File tree

.github/workflows/publish.yml

Lines changed: 47 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ jobs:
6565
env:
6666
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
6767

68-
- name: Setup
68+
- name: Setup debug build for tests
6969
run: just setup
7070

7171
- name: Install musl tools and rebuild for musl target
@@ -74,16 +74,22 @@ jobs:
7474
sudo apt-get update && sudo apt-get install -y musl-tools
7575
rustup target add x86_64-unknown-linux-musl
7676
77-
# Rebuild hyperlight-js NAPI addon targeting musl
77+
# Rebuild hyperlight-js NAPI addon targeting musl in release mode.
7878
hl_dir=$(just resolve-hyperlight-dir)
7979
cd "${hl_dir}/src/js-host-api"
80-
npx napi build --platform --target x86_64-unknown-linux-musl
80+
npx napi build --platform --release --strip --target x86_64-unknown-linux-musl
8181
82-
# Rebuild hyperlight-analysis NAPI addon targeting musl
82+
# Rebuild hyperlight-analysis NAPI addon targeting musl in release mode.
8383
cd "$GITHUB_WORKSPACE/src/code-validator/guest"
84-
npx napi build --platform --target x86_64-unknown-linux-musl --manifest-path host/Cargo.toml
84+
npx napi build --platform --release --strip --target x86_64-unknown-linux-musl --manifest-path host/Cargo.toml
8585
node -e "require('fs').readdirSync('host').filter(f=>f.endsWith('.node')).forEach(f=>require('fs').copyFileSync('host/'+f,f))"
8686
87+
# This matrix leg must upload only musl artifacts; the setup step also
88+
# creates host-platform glibc addons, which are supplied by linux-kvm.
89+
rm -f "${hl_dir}/src/js-host-api/"*.linux-x64-gnu.node
90+
rm -f "$GITHUB_WORKSPACE/src/code-validator/guest/host/"*.linux-x64-gnu.node
91+
rm -f "$GITHUB_WORKSPACE/src/code-validator/guest/"*.linux-x64-gnu.node
92+
8793
# Verify musl .node files were actually produced
8894
ls -la "${hl_dir}/src/js-host-api/"*.linux-x64-musl.node
8995
ls -la "$GITHUB_WORKSPACE/src/code-validator/guest/"*linux-x64-musl* || ls -la "$GITHUB_WORKSPACE/src/code-validator/guest/host/"*linux-x64-musl*
@@ -98,6 +104,10 @@ jobs:
98104
if: matrix.run_tests
99105
run: just test
100106

107+
- name: Rebuild native addons for release packaging
108+
if: matrix.run_tests
109+
run: just build-release
110+
101111
# Upload the native .node addons so the publish job can combine them
102112
- name: Upload native addons
103113
uses: actions/upload-artifact@v7
@@ -159,6 +169,18 @@ jobs:
159169
- name: Pack npm tarball
160170
run: npm pack
161171

172+
- name: Check npm package size
173+
run: |
174+
TARBALL=$(ls *.tgz)
175+
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))')
176+
MAX=$((300 * 1024 * 1024))
177+
echo "Packed tarball: $TARBALL"
178+
echo "Unpacked size: $UNPACKED bytes (limit: $MAX)"
179+
if [ "$UNPACKED" -gt "$MAX" ]; then
180+
echo "❌ npm package is too large"
181+
exit 1
182+
fi
183+
162184
- name: Upload npm tarball
163185
uses: actions/upload-artifact@v7
164186
with:
@@ -260,33 +282,34 @@ jobs:
260282
env:
261283
VERSION: ${{ steps.ver.outputs.version }}
262284
run: |
263-
# Poll registry.npmjs.org for up to 5 minutes for the version to
264-
# become resolvable. Cloudflare/CouchDB replication is usually fast
265-
# but can lag for minutes after a fresh publish.
285+
# Poll registry.npmjs.org for up to 5 minutes for the version metadata
286+
# AND tarball blob to become fetchable. npm can expose dist metadata
287+
# before the tarball URL has propagated through the CDN, so metadata
288+
# visibility alone is not enough for installability.
266289
for i in $(seq 1 30); do
267290
ACTUAL=$(npm view "@hyperlight-dev/hyperagent@$VERSION" version 2>/dev/null || true)
268291
if [ "$ACTUAL" = "$VERSION" ]; then
269-
echo "✅ visible after $((i * 10))s"
270-
exit 0
292+
TARBALL=$(npm view "@hyperlight-dev/hyperagent@$VERSION" dist.tarball 2>/dev/null || true)
293+
EXPECTED=$(npm view "@hyperlight-dev/hyperagent@$VERSION" dist.shasum 2>/dev/null || true)
294+
TMP=$(mktemp)
295+
if [ -n "$TARBALL" ] && [ -n "$EXPECTED" ] && curl -fsSL "$TARBALL" -o "$TMP"; then
296+
FOUND=$(sha1sum "$TMP" | awk '{print $1}')
297+
rm -f "$TMP"
298+
if [ "$FOUND" = "$EXPECTED" ]; then
299+
echo "✅ metadata and tarball visible after $((i * 10))s ($EXPECTED)"
300+
exit 0
301+
fi
302+
echo "⏳ tarball shasum mismatch on attempt $i — expected $EXPECTED got $FOUND"
303+
else
304+
rm -f "$TMP"
305+
echo "⏳ metadata visible, tarball not fetchable yet (attempt $i)"
306+
fi
271307
fi
272308
sleep 10
273309
done
274-
echo "❌ @hyperlight-dev/hyperagent@$VERSION not visible after 300s"
310+
echo "❌ @hyperlight-dev/hyperagent@$VERSION metadata/tarball not installable after 300s"
275311
exit 1
276312
277-
- name: Verify tarball blob fetchable + shasum
278-
env:
279-
VERSION: ${{ steps.ver.outputs.version }}
280-
run: |
281-
TARBALL=$(npm view "@hyperlight-dev/hyperagent@$VERSION" dist.tarball)
282-
EXPECTED=$(npm view "@hyperlight-dev/hyperagent@$VERSION" dist.shasum)
283-
ACTUAL=$(curl -sSL --fail "$TARBALL" | sha1sum | awk '{print $1}')
284-
if [ "$ACTUAL" != "$EXPECTED" ]; then
285-
echo "❌ shasum mismatch — expected $EXPECTED got $ACTUAL"
286-
exit 1
287-
fi
288-
echo "✅ tarball fetchable, shasum matches ($EXPECTED)"
289-
290313
- name: Install + run in ${{ matrix.image }}
291314
env:
292315
VERSION: ${{ steps.ver.outputs.version }}

Justfile

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,9 @@ build-hyperlight target="debug": (build-runtime-release)
101101
cd "${hl_dir}/src/hyperlight-js" && cargo clean -p hyperlight-js 2>/dev/null || true
102102
# Build the NAPI addon (inherits HYPERLIGHT_JS_RUNTIME_PATH from env)
103103
cd "${hl_dir}" && just build {{ if target == "debug" { "" } else { target } }}
104+
if [ "{{target}}" = "release" ]; then
105+
cd "${hl_dir}/src/js-host-api" && npx napi build --platform --release --strip
106+
fi
104107
# Symlink for npm file: dependency resolution
105108
mkdir -p "{{justfile_dir()}}/deps"
106109
ln -sfn "${hl_dir}/src/js-host-api" "{{hyperlight-link}}"
@@ -111,7 +114,7 @@ build-hyperlight target="debug": (build-runtime-release)
111114
[private]
112115
[windows]
113116
build-hyperlight target="debug": (build-runtime-release)
114-
$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"
117+
$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"
115118
116119
# Build the hyperlight-analysis-guest NAPI addon (debug)
117120
[private]

scripts/build-binary.js

Lines changed: 2 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -217,25 +217,7 @@ if (!existsSync(analysisNode)) {
217217
process.exit(1);
218218
}
219219

220-
// Copy .node files for ALL available platforms so the package is cross-platform.
221-
// The current platform's .node is guaranteed to exist (checked above).
222-
// Additional platform .node files are copied if present (e.g. from CI matrix builds).
223220
const ALL_TRIPLES = ["linux-x64-gnu", "linux-x64-musl", "win32-x64-msvc"];
224-
for (const triple of ALL_TRIPLES) {
225-
const hlNode = join(ROOT, `deps/js-host-api/js-host-api.${triple}.node`);
226-
const anNode = join(
227-
ROOT,
228-
`src/code-validator/guest/host/hyperlight-analysis.${triple}.node`,
229-
);
230-
if (existsSync(hlNode)) {
231-
copyFileSync(hlNode, join(LIB_DIR, `js-host-api.${triple}.node`));
232-
console.log(` ✓ js-host-api.${triple}.node`);
233-
}
234-
if (existsSync(anNode)) {
235-
copyFileSync(anNode, join(LIB_DIR, `hyperlight-analysis.${triple}.node`));
236-
console.log(` ✓ hyperlight-analysis.${triple}.node`);
237-
}
238-
}
239221

240222
// Create a proper node_modules package structure for hyperlight-analysis
241223
// so both require() and import() can resolve it in the bundled binary.
@@ -252,6 +234,7 @@ for (const triple of ALL_TRIPLES) {
252234
anNode,
253235
join(analysisPkgDir, `hyperlight-analysis.${triple}.node`),
254236
);
237+
console.log(` ✓ hyperlight-analysis.${triple}.node`);
255238
}
256239
}
257240
// Copy the index.js and index.d.ts from the source package
@@ -284,6 +267,7 @@ for (const triple of ALL_TRIPLES) {
284267
hlNode,
285268
join(hyperlightHostApiDir, `js-host-api.${triple}.node`),
286269
);
270+
console.log(` ✓ js-host-api.${triple}.node`);
287271
}
288272
}
289273
// Copy lib.js as lib.cjs, patching the require('./index.js') to './index.cjs'

src/code-validator/guest/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
]
1919
},
2020
"scripts": {
21-
"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))\"",
21+
"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))\"",
2222
"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))\"",
2323
"test": "vitest run",
2424
"test:watch": "vitest",

0 commit comments

Comments
 (0)