feat(cli): CI/CD flags, fix legacy TS runner, restore device params and tokens (cascades on #31)#32
Merged
vitorfdl merged 11 commits intofeat/cli-refactoring-testsfrom Apr 27, 2026
Conversation
Adds --all to deploy every analysis in tagoconfig.json without any prompt, and -t/--token to supply a profile token at invocation time (bypassing the lock file, which doesn't exist in CI runners). Also rejects the legacy "deploy all" positional with a helpful pointer to --all. The "all" positional has never worked as the help text suggested — it opened an interactive prompt — so dropping it is a bug fix, not a breaking change. Help examples updated: remove the misleading "deploy all" line and add pipeline-friendly usages covering --all, --all + stage-env, and the full CI combination (--all -e prod -t $TAGOIO_TOKEN --silent). Surgical scope: no changes to init, login, or any shared infrastructure. Only deploy.ts and analysis/index.ts are touched.
Documents the deploy flow for CI runners using --all + -e + -t + --silent, including the install step for @tago-io/cli and @tago-io/builder (deploy shells out to analysis-builder, so the builder needs to be on PATH). Notes that -t accepts either a profile token or an external-analysis token with the proper Access Management permissions, and that a pre-configured tagoconfig.json in the repository is required.
Adds coverage for the four new behaviors:
- legacy positional "all" is rejected with a pointer to --all
- --all deploys every analysis with no prompt
- -t/--token overrides the lock-file token for the run
- --all + -t end-to-end works with no lock file (CI flow)
Also updates the existing "env missing" test to reflect the new
error message ("No profile token found" instead of "Environment not
found") and introduces a defaultOptions() helper to DRY up the test
setup now that IDeployOptions has 5 required fields.
…gister `tagoio run <name>` was broken on every Node run: the command called `node -r <swc-register-path>` via `resolveCLIPath`, but that package was removed during Phase 1 when SWC was ripped out. Every invocation failed with `Cannot find module '.../node_modules/@swc-node/register/index'`. Switches the Node path in `_buildCMD` to `node --experimental-strip-types --watch`. Node 24+ strips TypeScript type annotations natively, so no external loader is required. The flag is stable on Node 24 and does not emit a runtime warning (unlike `--experimental-transform-types`, which does). Limitation: strip-types does not handle `enum` or `namespace` — not used in customer analyses today. Also: - Drops the now-unused `resolveCLIPath` import from run-analysis.ts. - Updates the three `_buildCMD` tests that asserted the old SWC path. - README: removes the `.swcrc` / `.swcrc.example` note from the Analysis Runner section (the debugger no longer needs source-map config — Node's strip-types preserves line numbers). - README (CI/CD): documents the Access Management rule required when using an external-analysis token in `-t, --token` (Access Analysis + Edit Analysis + Upload Analysis Script), so least-privilege pipelines don't hit Authorization Denied at deploy time. - `analysis/index.ts`: spells out `--env` instead of `-e` in the help examples for consistency with the option name shown above the list.
`tagoio run <name>` was broken for legacy analysis projects that use
`"module": "CommonJS"` + `"moduleResolution": "Node"` in their tsconfig
and import subpaths without a `.js` extension (e.g.
`import { Data } from "@tago-io/sdk/lib/types"`). Commit 60c5792 had
swapped the runner to `node --experimental-transform-types --watch`, but
Node's built-in TS execution routes those files through the ESM loader,
which rejects extensionless specifiers with `ERR_MODULE_NOT_FOUND`.
Every legacy analysis failed at import time.
Switches `_buildCMD` to invoke tsx's CLI via the cached binary shipped
inside the CLI's own node_modules:
`node <cli-path>/node_modules/tsx/dist/cli.mjs watch <script>`. tsx
pairs esbuild (TS transpile) with oxc-resolver and CJS-aware module
resolution, so the same classic imports resolve cleanly. Watch mode is
provided by `tsx watch` instead of `node --watch`, and `--inspect` /
`--clear` continue to work.
Why tsx over reviving `@swc-node/register`:
- Install footprint: ~12 MB per platform (esbuild single native binary)
vs ~25-36 MB for @swc/core + oxc-resolver
- One native binary to ship, not two
- Watch + REPL ship built-in
- Same resolver internally (oxc-resolver), so no correctness difference
- 10x the weekly npm downloads and stronger maintenance signal
Also:
- Adds `tsx@^4.21.0` to dependencies.
- Reintroduces the `resolveCLIPath` import removed in 60c5792.
- Updates the three `_buildCMD` tests that asserted the Node native
flags to assert the tsx CLI path and `watch` subcommand instead.
The `--tsnd` and `--deno` flags are unchanged.
Device restore was only recreating the device record itself. Two pieces of state that users expect to survive a backup/restore cycle were silently dropped: - Configuration parameters (`device.params`) — these carry per-device runtime settings and are required by most integrations. - Tokens tied to a physical device by `serie_number` — without these, the device cannot communicate after restore. Adds three helpers in `devices.ts`: - `stripDeviceFields` centralizes the list of fields the TagoIO API rejects on create/edit (ids, timestamps, profile, and the two restored-separately fields `params` and `tokens`). Both `processCreateTask` and `processEditTask` now delegate to it, removing the previously duplicated destructuring. - `restoreDeviceParams` replays the backup's params via the dedicated `paramSet` endpoint. - `restoreDeviceTokens` recreates only tokens with a `serie_number` (the identifying attribute for physical devices); ephemeral tokens without one are intentionally skipped. On the edit path it fetches the current token list first and skips serials already present, so re-runs are idempotent. Token values cannot be preserved because the backup stores them masked — callers must update integrations that relied on the old value. Tests cover: param restoration on create/edit, serial-token recreation, skipping tokens without a serial, idempotency when a serial already exists on the target device, and graceful handling of a failed `tokenCreate` (logged, restore continues).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR started with the
deploy --all/-t/--tokenflags for CI/CD and grew to cover two more items surfaced while smoke-testing the branch end-to-end:tagoio runwas broken for legacy CJS analyses, and device restore was silently dropping params and tokens.1.
tagoio deployCI/CD flags--allflagDeploys every analysis registered in
tagoconfig.jsonwithout opening any prompt. Previously,tagoio deploy allopened an interactive picker — despite the help text suggesting otherwise. The positional"all"is now rejected with a helpful pointer to--all, which is a bug fix, not a breaking change.-t, --token <profile-token>flagSupplies a profile token for the current invocation, bypassing the local
.tago-lock.*.lockfile. This is the missing piece for CI/CD — runners have no lock file, andtagoio init/tagoio loginare interactive and can't run headlessly.Together,
tagoio deploy --all --env prod -t $TOKEN --silentruns end-to-end without any prompt and without requiringinit/loginfirst.Code-quality cleanups in
deploy.tsNo behavior change:
if (options.all) { /* empty */ } else if (...)pattern was inverted toif (!options.all) { ... }, removing a branch whose only purpose was to block entry into the interactive selection.returnaftererrorHandler(...)was removed (TypeScript flagged it as unreachable —errorHandleris typednever), bringing the file in line with the 5+ other call sites that already omit the return.2.
tagoio run: tsx loader for CJS-compatible TS executionBug
tagoio run <name>was broken for every legacy analysis project that uses"module": "CommonJS"+"moduleResolution": "Node"in theirtsconfig.jsonand imports subpaths without a.jsextension (e.g.import { Data } from "@tago-io/sdk/lib/types"). Commit 60c5792 had swapped the runner tonode --experimental-transform-types --watch, but Node's built-in TS execution routes those files through the ESM loader, which rejects extensionless specifiers withERR_MODULE_NOT_FOUND:Every legacy customer analysis failed at import time. Investigation confirmed there's no Node 24 flag that fixes this —
--experimental-specifier-resolution=nodewas removed in Node 19 and has no replacement;--experimental-transform-typesonly strips types, it doesn't downlevelimporttorequire; and no flag forces CJS loader to accept ESM syntax.Fix
_buildCMDnow invokes tsx's CLI via the binary shipped inside the CLI's ownnode_modules:tsx pairs esbuild (TS transpile) with oxc-resolver and CJS-aware module resolution, so the same classic imports resolve cleanly. Watch mode is provided by
tsx watchinstead ofnode --watch, and--inspect/--clearcontinue to work.Why tsx over reviving
@swc-node/register@swc/core+oxc-resolveroxc-resolveralone does not solve the problem — it resolves paths but does not transpile TS syntax or plug into Node's loader hooks--tsnd(ts-node-dev) and--denoflags are unchanged.3.
tagoio backupdevice restore: params and serial tokensDevice restore was only recreating the device record itself. Two pieces of state that users expect to survive a backup/restore cycle were silently dropped:
device.params) — required by most integrations.serie_number— without these, the device cannot communicate after restore.Adds three helpers in
devices.ts:stripDeviceFieldscentralizes the list of fields the TagoIO API rejects on create/edit (ids, timestamps, profile, and the two restored-separately fieldsparamsandtokens). BothprocessCreateTaskandprocessEditTaskdelegate to it, removing previously duplicated destructuring.restoreDeviceParamsreplays the backup's params via the dedicatedparamSetendpoint.restoreDeviceTokensrecreates only tokens with aserie_number(the identifying attribute for physical devices); ephemeral tokens without one are intentionally skipped. On the edit path it fetches the current token list first and skips serials already present, so re-runs are idempotent. Token values cannot be preserved because the backup stores them masked — callers must update integrations that relied on the old value.Tests cover: param restoration on create/edit, serial-token recreation, skipping tokens without a serial, idempotency when a serial already exists on the target device, and graceful handling of a failed
tokenCreate(logged, restore continues).Acceptance criteria
Deploy CI/CD (original scope)
--allflag added-t, --token <profile-token>flag added (deploy-only, not global)tagoio deploy --alldeploys every analysis without any prompttagoio deploy --all --env <env> -t $TOKEN --silentworks end-to-end with no lock file--alland-texamples including CI/CD usagedeployonly (noinit/loginprerequisite)tagoio deploy <name>ortagoio deploy(no-arg picker)init,login, or other commandsRun fix
--inspect,--clearcontinue to work--tsndand--denounchangedBackup device restore
paramsare restored viaparamSetserie_numberare recreated on restoreserie_numberare skippedtokenCreateis logged and does not abort the restoreVerification
npx tsc --noEmit→ cleannpx vitest run→ 436/436 (up from 426 — 10 new tests for the backup changes)npm run linter→ 0/0npm run build→ cleanhandler.tsfrom a real CJS analysis project (analysis-prosentry,tsconfigwith"module": "CommonJS", imports@tago-io/sdk+@tago-io/sdk/lib/typeswithout.js) now resolves and runs successfully under the new tsx-based runner. Same file under the previous--experimental-transform-typescommand fails withERR_MODULE_NOT_FOUND.Commit layout
--all+-t/--tokenflag implementation + help text refresh--all,-toverride,--all + -tend-to-end, and the legacy"all"rejection_buildCMDtestsTest plan
tagoio deploy --all --env <env> -t $TOKEN --silentagainst a real project withtagoconfig.jsonconfigured.tagoio deploy allproduces the--allpointer error.tagoio deploy <name>andtagoio deploy(no args) still behave as before.tagoio run <name>on a legacy CJS analysis with extensionless subpath imports.tagoio run <name> -d(debug) and-c(clear).paramsand serial-number tokens, restore it, and confirm params and tokens are present.