Skip to content

ci: add global-install smoke test for CLI preview#455

Merged
miguel-heygen merged 2 commits into
fix/harden-core-runtime-resolutionfrom
ci/global-install-smoke-test
Apr 23, 2026
Merged

ci: add global-install smoke test for CLI preview#455
miguel-heygen merged 2 commits into
fix/harden-core-runtime-resolutionfrom
ci/global-install-smoke-test

Conversation

@miguel-heygen
Copy link
Copy Markdown
Collaborator

@miguel-heygen miguel-heygen commented Apr 23, 2026

Summary

Adds a CI smoke test that reproduces the exact failure from #452hyperframes preview printing ✘ [ERROR] Could not resolve "…/runtime/entry.ts" when installed globally via npm.

The job simulates what a real user does:

  1. npm pack the CLI → install globally with --prefix
  2. hyperframes init test-project --example blank
  3. hyperframes preview --port 3099 in background
  4. curl http://localhost:3099/api/runtime.js — assert non-empty JS
  5. Assert stderr has no ✘ [ERROR] or Failed to load runtime

Also adds .github/workflows/** to the change detection filter so CI jobs run when workflow files change.

Validation: the test catches the broken state

Verified locally that the grep pattern fires on the pre-#452 code and passes on the fixed code:

Broken (0.4.15-alpha.1, globally installed via npm i -g hyperframes@alpha):

$ hyperframes preview --port 3098 2>/tmp/hf-stderr.log
$ grep -E '✘ \[ERROR\]|Failed to load runtime' /tmp/hf-stderr.log
✘ [ERROR] Could not resolve "/opt/homebrew/lib/node_modules/hyperframes/runtime/entry.ts"
[studio] Failed to load runtime source fallback: Error: Build failed with 1 error: …
→ CAUGHT: assertion fires, test fails ✓

Fixed (built from #452 fix branch):

$ node packages/cli/dist/cli.js preview --port 3098 2>/tmp/hf-stderr.log
$ grep -E '✘ \[ERROR\]|Failed to load runtime' /tmp/hf-stderr.log
(empty)
→ PASS: no errors ✓

If this smoke test had existed before #452, it would have caught the bug before it shipped.

Test plan

Copy link
Copy Markdown
Collaborator

@jrusso1020 jrusso1020 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verdict: approve

Exactly what we discussed — reproduces Teodora's user-flow in CI. npm pack → npm i -g --prefix /tmp/hf-smoke → init blank → hyperframes preview → probe /api/runtime.js for non-empty body, then grep stderr for ✘ \[ERROR\]|Failed to load runtime. The grep pattern was validated against the pre-#452 broken state (both substrings present in stderr) and passes on fixed code, so the alarm is load-bearing, not decorative. --prefix /tmp/hf-smoke + PATH=/tmp/hf-smoke/bin:$PATH sidesteps sudo cleanly.

The .github/workflows/** addition to changes.code is a quiet quality-of-life fix — without it, CI wouldn't rerun when the workflow itself changed. Good catch.

Plumbing is sound: needs: [changes, build] for gating, 10-minute timeout, 15-second server-readiness poll, background server kill + wait for clean teardown, grep -qE with raw unicode (which works because run shell is UTF-8 by default on ubuntu-latest). Smoke job itself clocks ~50s on this run — cheap. Smoke: global install is already green on the head commit.

Two non-blockers:

  1. Port 3099 is also used by regression tests. On dedicated ubuntu-latest it doesn't collide, but if someone ever runs this job on a self-hosted runner alongside regression, you'd want an env-var override or a random-port probe. Not worth complicating now.

  2. npx hyperframes path isn't covered. The reported bug hit both global and npx — same resolution, different path layout under ~/.npm/_npx/<hash>/node_modules/. A follow-up variant job (or a matrix) that does npm_config_cache=$(mktemp -d) npx hyperframes@file:./packages/cli/... init ... would close the other half of the repro. Non-blocking since #452 fixes both paths with the same guard, and the grep pattern would trigger equivalently for either.

Two housekeeping asks when you merge: (a) tick the last checkbox in the test plan once this lands (CI green on this branch is the evidence), and (b) flip Smoke: global install to a required status check in branch protection so future regressions actually block merge — the test only protects if it gates.

Ship it.


Review by hyperframes

Reproduces the exact failure from #452 where `hyperframes preview`
crashed with esbuild runtime errors after `npm i -g` installation.

The job packs the CLI tarball, installs it globally via --prefix,
scaffolds a blank project, starts the preview server, probes
/api/runtime.js for a non-empty response, and asserts stderr contains
no esbuild `[ERROR]` or `Failed to load runtime` messages.

Runs after the build job passes and only when packages/ changes are
detected.
Without this, CI jobs skip when only .github/workflows/ files change,
making it impossible to validate new CI jobs on their own PRs.
@miguel-heygen miguel-heygen force-pushed the ci/global-install-smoke-test branch from 498f35b to 1355a0a Compare April 23, 2026 19:27
@miguel-heygen miguel-heygen changed the base branch from main to fix/harden-core-runtime-resolution April 23, 2026 19:27
@miguel-heygen miguel-heygen merged commit 0d16a18 into fix/harden-core-runtime-resolution Apr 23, 2026
6 checks passed
@miguel-heygen miguel-heygen deleted the ci/global-install-smoke-test branch April 23, 2026 19:28
miguel-heygen added a commit that referenced this pull request Apr 23, 2026
* fix(core): guard buildHyperframesRuntimeScript against missing entry.ts

buildHyperframesRuntimeScript() now checks existsSync(entryPath) before
calling esbuild.buildSync(). When entry.ts doesn't exist (bundled or
published contexts where only dist/ ships), it returns null instead of
letting esbuild fail with stderr output.

This prevents any future consumer that bundles @hyperframes/core from
rediscovering the "esbuild can't find entry.ts" bug.

Updated loadHyperframeRuntimeSource() and all call-site test scripts
to handle the nullable return type.

* feat(core): add getHyperframeRuntimeScript() inlined constant

The build script now generates src/generated/runtime-inline.ts during
build:hyperframes-runtime, containing the IIFE as a string constant.
tsc then compiles it into dist/generated/runtime-inline.js.

getHyperframeRuntimeScript() is the production-safe path: no esbuild,
no file I/O, no import.meta.url arithmetic. It is exported from the
@hyperframes/core package index.

Build order changed from "tsc && build:hyperframes-runtime" to
"build:hyperframes-runtime && tsc" so the generated file exists
before tsc runs.

* fix(cli): use inlined runtime constant as production fallback

loadRuntimeSource() now has three resolution strategies:
1. esbuild from source (dev only)
2. Inlined constant via getHyperframeRuntimeScript() (production)
3. Pre-built IIFE artifact file (final fallback)

The inlined constant path avoids file I/O entirely, making runtime
resolution work reliably in bundled/published contexts.

* ci: add global-install smoke test for CLI preview (#455)

## Summary

Adds a CI smoke test that reproduces the exact failure from #452 — `hyperframes preview` printing `✘ [ERROR] Could not resolve "…/runtime/entry.ts"` when installed globally via npm.

The job simulates what a real user does:
1. `npm pack` the CLI → install globally with `--prefix`
2. `hyperframes init test-project --example blank`
3. `hyperframes preview --port 3099` in background
4. `curl http://localhost:3099/api/runtime.js` — assert non-empty JS
5. Assert stderr has no `✘ [ERROR]` or `Failed to load runtime`

Also adds `.github/workflows/**` to the change detection filter so CI jobs run when workflow files change.

## Validation: the test catches the broken state

Verified locally that the grep pattern fires on the pre-#452 code and passes on the fixed code:

**Broken (0.4.15-alpha.1, globally installed via `npm i -g hyperframes@alpha`):**
```
$ hyperframes preview --port 3098 2>/tmp/hf-stderr.log
$ grep -E '✘ \[ERROR\]|Failed to load runtime' /tmp/hf-stderr.log
✘ [ERROR] Could not resolve "/opt/homebrew/lib/node_modules/hyperframes/runtime/entry.ts"
[studio] Failed to load runtime source fallback: Error: Build failed with 1 error: …
→ CAUGHT: assertion fires, test fails ✓
```

**Fixed (built from #452 fix branch):**
```
$ node packages/cli/dist/cli.js preview --port 3098 2>/tmp/hf-stderr.log
$ grep -E '✘ \[ERROR\]|Failed to load runtime' /tmp/hf-stderr.log
(empty)
→ PASS: no errors ✓
```

If this smoke test had existed before #452, it would have caught the bug before it shipped.

## Test plan

- [x] Grep pattern catches broken state — verified locally against pre-#452 global install
- [x] Fixed state passes — verified locally against #452-fixed build
- [x] CI job runs green — https://github.com/heygen-com/hyperframes/actions/runs/24854025990/job/72762086753
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants