No cell exercised the headline command as a published package, so the deep-import
breakage above shipped undetected. Each nx cell now runs migrate-helm-libraries
against the freshly scaffolded workspace before generating anything: with no spartan
libraries it no-ops ("No libraries to migrate") without prompting, but loading it
resolves the @nx/workspace/@nx/devkit deep imports in the installed CLI - so a
consumer's stricter exports map turns the cell red instead of reaching users.
cli-smoke
Smoke tests that verify the spartan CLI against every supported workspace setup, by scaffolding real workspaces, running the generators, consuming a generated component, and building the result. This catches the class of bug that the in-memory generator unit tests cannot: broken tsconfig path mapping, missing dependencies, generated code that does not compile or is not consumable through its public alias, secondary-entrypoint wiring, and theme styles that never reach the build output.
They are smoke tests, not full end-to-end tests: they assert the output compiles, is consumable, and builds with themed styles, but they do not run the app or assert runtime behaviour.
What it does
- global-setup starts a local verdaccio registry (the
local-registrytarget on the rootspartanproject), then builds and publishes@spartan-ng/cliand@spartan-ng/brainto it under a forced version (9999.0.0-smoke.<timestamp>- unique per run so pnpm's cache can't serve a stale build). They share a version because the CLI writes the@spartan-ng/braindependency at the installed CLI version. - For each cell in
src/matrix.tsit scaffolds a fresh workspace (in the OS temp dir, not the repo), pins Tailwind v4 via the documented.postcssrc.jsonsetup, installs the locally published CLI, writescomponents.json, runsinit+ui <component>, asserts the CLI's ownhealthcheckreports no issues on the generated output, drops in a probe component that uses the generated code through the@spartan-ng/helm/*alias, then builds and asserts the theme CSS variables reached the output. A generator error, a healthcheck failure, a non-zero build, or missing themed CSS fails the cell. - global-teardown stops the registry.
The matrix
nxmonorepo x {library,entrypoint} x {buildable,non-buildable}nx-standalone- single-app nx workspace with notsconfig.base.jsonacli- Angular CLI, no Nxall-components- generates every supported primitive into an Angular CLI app and builds them all in oneng build; the slowest cell, but fans out to its own runner. SetnightlyOnly: trueinsrc/matrix.tsto drop it from per-PR CI if it proves too slow.
Tailwind is fixed at v4. Add or remove combinations in src/matrix.ts.
Running
# full matrix (also: `pnpm smoke`)
pnpm nx run cli-smoke:smoke
# a single cell (jest matches the whole test name, so use a substring, not an anchored id)
pnpm nx run cli-smoke:smoke --testNamePattern="nx-lib-buildable scaffolds"
Each cell installs dependencies and runs a real Angular build, so it is slow; locally the cells run
single-threaded (one shared local registry). On failure the scaffolded workspace is left under
<os-tmp>/spartan-cli-smoke-workspaces/<cell-id> for inspection; on success it is removed.
CI
.github/workflows/cli-smoke.ymlruns the matrix, fanning each cell out to its own runner so they run in parallel from the start - wall-clock is roughly the slowest single cell, not the sum.- It runs on every push/PR (no path filter) so the
cli-smoke resultjob can be a required check that never sits pending. Cheapness on unrelated PRs comes in two layers: (1) agit diffgate beforepnpm installskips the job in seconds when nothing CLI-relevant changed; (2) when it does install, the in-runnerisSmokeAffected()check (src/support/affected.ts) makes the precise nx-affected call and still short-circuits to a pass if cli-smoke isn't actually affected.nrwl/nx-set-shasprovides the base/head both layers use. - A fast
drift-guardjob fails CI ifmatrix.tsgrows a cell the workflow's static matrix doesn't list (keep the two in sync). cli-smoke resultaggregates the matrix + drift-guard into one status to require in branch protection.
- It runs on every push/PR (no path filter) so the
- nightly-release.yml runs the full matrix unconditionally every night (single job; no base/head, so the affected check always runs it).