Repository navigation
fix(cli): resolve bundled handlers next to the entrypoint - #414
Conversation
jsteinich
left a comment
There was a problem hiding this comment.
Reviewed as part of the full tele/s1–tele/s6 stack. The e2e itself is the best part of this whole stack — asserting on a real envelope reaching a real sink from the real esbuild bundle is a much stronger oracle than the mocked unit tests, and the leak assertions are exactly the right set: stack names, resource ids, private-registry hosts, local provider paths, and the SENTRY_ENVIRONMENT / SENTRY_TRACE / SENTRY_BAGGAGE values a user or CI might export. Matching "sentry.environment":{"value":"production" as a key/value adjacency rather than a bare production is a nice touch.
Three things.
1. The title undersells a production code change — please rename
The PR is titled chore(gha): run the telemetry delivery e2e on every build, but it also changes how the shipped CLI resolves its handlers, in packages/cdktn-cli/src/bin/cmds/helper/utilities.ts:
- const filePath = path.join(__dirname, "..", "handlers.js");
+ const filePath = path.join(__dirname, "cmds", "handlers.js");In the bundle, utilities.ts is inlined into bin/cdktn.js, so __dirname is bundle/bin — meaning the old first branch resolved to bundle/handlers.js, never matched, and every invocation fell through to the projectRootPath() lookup. The new path does match. That's a real behaviour change on the hot path of every command, and it's the right change, but it isn't a chore(gha).
The commit itself is labelled honestly (feat(cli): resolve bundled handlers next to the entrypoint and honour CDKTN_BUNDLE_OUTDIR) — the problem is only the PR title, and this repo allows squash-merge with PR_TITLE as the commit subject. So as it stands this lands as a chore and never appears in the changelog.
Suggest just renaming the PR to cover both, e.g. feat(cli): resolve bundled handlers next to the entrypoint and run the telemetry e2e in CI.
2. Run the e2e as its own job rather than appending to build-and-package
build-and-package is the critical-path job — every other workflow gates on it. This step adds a second full esbuild, four CLI invocations (one of which is cdktn convert, which shells out to init), and a poll loop that waits up to 10 s on a local HTTP sink. A flake or a timeout in any of that now fails the build for everyone, including PRs that touch nothing near telemetry.
A separate job depending on build-and-package would isolate the blast radius and make the failure legible when it does fire. It also fits the existing ci/skip-* label convention, which this step currently sits outside of.
3. The script has an undeclared dependency on pnpm package
cdktn convert resolves dist via pkg.version === "0.0.0" ? dist : undefined, and in-repo the version is 0.0.0 — so the convert trigger needs dist/ to exist. That holds in CI only because the step is appended after pnpm run package in the same job. Run tools/validate-sentry-e2e.sh on a clean checkout locally and it fails somewhere deep inside convert instead of saying what's missing.
Worth an explicit guard near the top:
[ -d "$ROOT/dist" ] || { echo "FAIL: run 'pnpm package' first" >&2; exit 1; }This becomes more important, not less, if #2 moves it to a separate job where the ordering is no longer implicit.
b67e335 to
ee69f72
Compare
ee69f72 to
e76083f
Compare
e76083f to
1bced26
Compare
1bced26 to
ab44f74
Compare
ab44f74 to
e421558
Compare
|
@jsteinich all three points are addressed; the description is rewritten. 1. Title hid a production change. Agreed. The title is now 2. Own job. Done in dbf40ab. The step is out of 3. Undeclared dependency on Also changed in the e2e:
One finding to flag. The leak scan showed that opt-in crash reports carry the CLI's absolute install path in stack frames (which includes the username when installed under a home directory) and Sentry's default device, OS and locale contexts. This is not introduced by the stack: |
e421558 to
3907c74
Compare
3907c74 to
7e46028
Compare
Workflow hardening for the parts this PR addsLumen runs zizmor over changed workflows so a PR does not add to the existing findings. Two new ones come from this layer; both are small and worth taking. Scope is deliberately limited to what this PR adds or modifies — the pre-existing findings elsewhere in these files are backlog, not part of this change. 1.
|
|
Both applied in 6d381bb: zizmor 1.30.0 on |
6d381bb to
e64364e
Compare
… CDKTN_BUNDLE_OUTDIR
The e2e leaves build-and-package and becomes a reusable workflow that pr.yml calls after quality and build, skipped by ci/skip-unit like the unit tests and counted in PR - Result.
…nother owner The trace_metric, cli.stack.provider and event checks repeat patterns await_items already required, the error_type key check is subsumed by its value check, and the crash output shape is owned by the cdktn-cli error-handling tests.
The telemetry code also lives in bin/cmds/handlers.js, which the scan of bin/cdktn.js never read.
…lemetry e2e A second stack shares two providers with the first; the sink exposes each metric's value and attributes so the script can compare the delivered stack metrics with one item per distinct identity.
…'s in the telemetry e2e Runs against an empty CDKTF_HOME and requires every metric and the crash event to carry exactly the install id minted there, the fixed server name and the fixed environment. The leak scan gains the hostname, the working directories and the username; the username is tolerated only inside the bundle's own install path, which the crash event's stack frames carry.
…e telemetry e2e workflow
e64364e to
1b4518d
Compare
Upgrades `@sentry/node` from 7.120.4 to 10.73.0 so the usage metrics added later in the stack can use the v10 metrics API; the 7.x SDK's custom metrics are no longer accepted by Sentry (sunset 2024-10-07). Error and crash reporting behave as before, with one privacy improvement: crash reports no longer carry the machine hostname. Part of #48. ### What changes - `@sentry/node` is pinned to exactly `10.73.0` in `@cdktn/commons`, `@cdktn/cli-core` and `cdktn-cli`, so the bundle keeps one shared SDK instance. Only `@sentry/*` and its transitive dependencies move in `pnpm-lock.yaml`. - `Sentry.configureScope` becomes `Sentry.getCurrentScope()` in `cli-core/src/lib/error-reporting.ts` and in `Errors.setScope` (`commons/src/errors.ts`). - The `Sentry.init` options in `error-reporting.ts` are rebuilt for v10: the removed `autoSessionTracking` is dropped; `tracesSampleRate: 0`; `environment: "production"`, so `SENTRY_ENVIRONMENT` is never read; `serverName: "cdktn-cli"`, so the hostname is never attached; `enableMetrics: true`, stated explicitly instead of relying on the SDK default. - A fresh propagation context is set at init, so an inherited `SENTRY_TRACE` / `SENTRY_BAGGAGE` is discarded and never propagates. ### Not in this PR - No usage metrics are emitted yet; they start in #411. - Opt-in crash reports still carry what they carry on `main` apart from the hostname: the CLI's install path in stack frames and the SDK's default device, OS and locale contexts. Scrubbing those is a follow-up. - `@sentry/node` is a devDependency of `cdktn-cli` (it reaches users through the bundle and `@cdktn/commons`). Whether that is intended is a separate question. ### Testing - Unit: `error-reporting.test.ts` asserts the exact init options (release, `tracesSampleRate`, `environment`, `serverName`, `enableMetrics`) and that a fresh trace is seeded. - Unit: the existing crash-reporting tests (`shouldReportCrash`, `captureException`) pass unchanged. - Delivery of metrics through the real SDK is proven by the bundle e2e in #414, not here. Stack: 1 of 6 — **#409** → #410 → #411 → #412 → #413 → #414
Fixes the top-level CLI error handler printing every failure twice and aborting with `ERR_UNHANDLED_REJECTION`, and makes the process flush and exit explicitly. The handler registered `.fail()` as `async`, which yargs does not await, so the rejection escaped to Node. Fixes #360, fixes #361. ### What changes - `.fail()` is synchronous and only records the failure. `runCli` (`cdktn-cli/src/bin/error-handling.ts`) awaits `parseAsync()` with `.exitProcess(false)`, reports through one awaited `reportFailure`, and makes the single `process.exit()` call. It also catches synchronous handler throws, which yargs never routes to `.fail()`. - `terraform-cli.ts` throws an `Error` instead of a raw string, which is what printed `undefined` / `undefined`. - More crash events than before: `reportFailure` captures `External` and unexpected errors explicitly (never `Usage`). It stays consent-gated, since Sentry is only initialised after consent. - The success path (including `--help` and `--version`) awaits a bounded Sentry flush (4000 ms), drains stdout and stderr, then exits with `process.exitCode ?? 0`. An ingest endpoint that never answers cannot hold the process open, and `cdktn convert | less` is no longer truncated at 64 KiB. A reader that closes early (`| head -1`) exits 0. - A shell completion request returns early and lets the loop drain, because yargs does not await its asynchronous completion callback. - `watch()` in cli-core stays pending until its abort signal fires, so the explicit exit does not end a watch session after its first run. A deploy that fails for any reason other than the abort now rejects the session, so `cdktn watch` ends through the normal error path with exit 1. Before, that failure was an unhandled rejection: a raw stack without Sentry, a silently stuck watcher with it. Runs start in one place and one promise covers a run and the re-runs queued behind it. - `CdktfProject.execute()` observes every stack promise it starts. Two early exits (the wait at the parallelism limit, and the catch around picking the next stack) skipped the final await, so a failed stack in a multi-stack deploy or destroy also surfaced as an unhandled rejection next to the real error. The error still propagates the same way. ### Not in this PR - When a stack fails at the parallelism limit, `execute()` rejects without waiting for the other stacks that are still running, unlike its other exits. This predates the change and is left as it is. - `cmds/get.ts` calls `readConfigSync()` at import, so an invalid `cdktf.json` still crashes before the failure reporter runs. This predates the change. - Commands that call `process.exit()` directly bypass the flush; nothing emits metrics on those paths yet, and #411 flushes before each one that does. ### Testing - Unit: `runCli` is the only export and is driven directly, with console and `process.exit` spies: one failure matrix (async and sync throws, raw strings, error-shaped objects, Usage, External, invalid choice) asserts the exact output, what is captured, exit 1, and that a rejected argument never runs the handler. - Multi-stack `deploy` and `destroy` through real stack executors: first stack fails under `parallelism: 1`, a failing stack with a dependant that never starts, and one failing while another runs; each rejects with the original error and leaves no unhandled rejection. - `watch`: pending until aborted; a failed deploy rejects with the same error, closes the watcher and stops; a change during a deploy yields exactly one follow-up run; a failing queued re-run rejects; a change after the session ended starts nothing. - Child-process integration: a real Sentry client against a local sink delivers the crash envelope before exit, including for an `External` error; a sink that accepts and never answers still lets the success path exit 0 within the bound. - The #360 / #361 triggers print once, reach `Debug Information:`, and print a real message with no unhandled-rejection output. Stack: 2 of 6 — #409 → **#410** → #411 → #412 → #413 → #414
…etrics and consent (#411) The CLI stops sending anything to `checkpoint-api.hashicorp.com`. Usage analytics now go to the project's own Sentry as counted metrics, behind a separate `sendUsageTelemetry` consent flag in `cdktf.json`. Resolves #48. ### What changes - The HashiCorp transport (`commons/src/checkpoint.ts`) and its test are deleted, and `nock` leaves `cli-core`. The user and project ids move to `commons/src/identity.ts`. `commons/src/telemetry.ts` holds the whole contract: the consent gate, the metrics, and the single bounded `flushTelemetry`. - Consent: `initializErrorReporting` prompts once per unset flag (stdin and stdout are TTYs, not CI) and saves the answer to `cdktf.json`; `convert` asks before it chdirs into its temporary project. The first decision captured in a process wins: a nested command (`convert` → `init` → `get`, `init` → `get`, `provider add` → `get`) returns from reporting init without reading flags, prompting or building a second client. `parseConfig`, the usage reader and the crash reader share one tri-state reader: booleans and `"true"`/`"false"` count, anything else reads as unset. Usage telemetry follows `CHECKPOINT_DISABLE` > decision at command start > `sendUsageTelemetry` > on; with only usage consent no error events are sent. - Run model: a run counts once as `cli.command.invoked` at start, then once as `cli.command.completed` or `cli.command.error`, so `error / invoked` is a failure rate. A nested operation (a deploy's synth, init's get) adds only its own metrics; `init` completes after its nested get succeeds. - Failure paths that used to `process.exit(1)` (synth code-maker guard, `watch --auto-approve`, Terraform availability check) throw `Errors.Usage`, reaching the single failure path. The two self-exiting synth paths count and flush themselves. - `cli.error` counts every error built by the `Errors` factories by type and command (never message or context); `cli.command.error` counts failed runs. A handled Usage error counts only as `cli.error`. - Command scope, consent and run state live in a `Symbol.for` store (`process-state.ts`), because the esbuild bundles each carry their own copy of commons. | Metric | When emitted | Attributes (base: `command`, `ci`, `language`) | |---|---|---| | `cli.command.invoked` | once per run, at start | base only | | `cli.command.completed` | once per successful run | base only | | `cli.command.error` | once per failed run | base + `error_type` (`Usage`, `External`, `Internal`, `unexpected`) | | `cli.synth.duration` | each synth, ms distribution | base only | | `cli.error` | each error from the `Errors` factories | `type`, `command` only | Sentry itself adds the random install id (`~/.cdktf/config.json`), release `cdktn-cli-<version>`, environment `production`, and server `cdktn-cli`. Never sent: stack names, ids, paths, hostname or username, error messages and context, `SENTRY_*` values. ### Migration notes - cdktn sends no data to HashiCorp. The new `sendUsageTelemetry` flag is independent of `sendCrashReports`; the six templates carry it. - Interactive runs ask once about each unset flag. The crash-reporting prompt also appears now where `sendCrashReports` is missing; it never fired before because a missing key was read as "no". - `provider list` and `provider upgrade` now initialise reporting like the other commands, so their first interactive run in a project with unset flags shows the prompts before the output. - CI and piped runs never prompt: usage telemetry stays on by default (now to Sentry), crash reporting stays off. Builds without a Sentry DSN send nothing. ### Coverage - Counted: `convert`, `deploy`, `destroy`, `diff`, `get`, `init`, `list`, `output`, `synth`, `watch`, `provider add`, `provider list`, `provider upgrade`. Not counted: `login` (handles a credential), `debug` (must work when the environment is broken, without consent or reporting init), shell completion, `--help`, `--version` and unknown commands (no handler runs). - A package-manager attribute is a follow-up after #422. ### Testing - Unit: a real v10 client with a capturing transport asserts the metric names, attributes and the exact multiset of run metrics; raw-envelope scans assert no host identity; the consent matrix and the config normalisation tables. - Handler level: `convert` prompts and persists before its chdir, and never prompts on piped stdin; each former `process.exit` guard yields one `cli.command.error` and one flush. - Bundle-level delivery is covered by the e2e in #414. Stack: 3 of 6 — #409 → #410 → **#411** → #412 → #413 → #414 --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
… package manager in usage metrics (#412) Every usage metric now carries the environment it ran in: the installed Terraform or OpenTofu binary and version, the target versions the project declares, the OS and architecture, and the project's package manager. This answers which binaries, platforms and package managers people actually run. Part of #48. ### What changes - The base attribute set of every metric grows by the attributes below (`commons/src/telemetry.ts`). No new metrics are added. - `commons/src/terraform.ts` probes the binary with `<binary> version`, lazily, at most once per process, shared across bundle copies via process state. The probe is killed after 1500 ms. A failed or timed-out probe reports `binary: unknown`; a binary that cannot be spawned reports `missing`. This replaces the `terraform version -json` call that ran at module import in every commons copy regardless of consent; with usage telemetry off, metrics trigger no probe. - `cdktn debug` uses the same probe and prints `"terraform": null` when it fails. - `cli-core` captures the project's target attributes once at init from the project that is running, so later metrics in the run carry the same values. - Values are validated before they become attributes: versions are reduced to `MAJOR.MINOR.PATCH` and sent only for a recognised product; ranges are canonical semver or the literal `invalid`. No path, binary location or free text is forwarded. - The package manager is detected once at command start in the project the run started in, and is omitted when it cannot be determined. Only members of a closed set are sent. | Attribute | Value | Source | |---|---|---| | `os`, `arch` | `process.platform`, `process.arch` | Node | | `binary` | `terraform`, `opentofu`, `unknown`, `missing` | version probe | | `binary_version` | `MAJOR.MINOR.PATCH`, only for `terraform` or `opentofu` | version probe | | `target_terraform`, `target_opentofu` | canonical semver range, else `invalid` | `targetVersions` in `cdktf.json`, else the CLI defaults | | `targets_declared` | boolean | `cdktf.json` has a `targetVersions` block | | `validate_installed_binary` | boolean | `validateInstalledBinary === true` in `cdktf.json` | | `package_manager` | `npm`, `pnpm`, `yarn`, `yarn-berry` (TypeScript); `pip`, `pipenv` (Python); `maven`, `gradle` (Java); `nuget`; `go` | TypeScript: `detectNodePackageManager` from #422. Python: `pipenv` if the `app` command uses it, else `pip`. Java: `build.gradle` present, else `maven`. C# and Go: constant | All are added to every metric that carries the base attributes (`command`, `ci`, `language`, from #411), except `cli.error`, which keeps only `type` and `command`. ### Not in this PR - A shell wrapper named `terraform` that runs the real binary as a child is killed only at the wrapper, so the probe can wait for the child to close stdout. Shims that exec or symlink (tfenv, asdf, mise) are unaffected. Fixing it means changing `exec` in `commons/src/util.ts`. ### Testing - Unit: the sorted attribute key set of `cli.command.invoked` is pinned, so an extra attribute fails; target attributes captured at init appear on later metrics. - Unit with fixture binaries: the probe runs once, a hanging binary resolves `unknown` at the timeout, a missing binary resolves `missing`, an unrecognised product sends no version. - Table-driven validation of versions and ranges, including prerelease and build text. - Package manager: each detected value appears on `invoked` and on the terminal metric; it is absent outside a project, and a detector result outside the closed set is dropped. Stack: 4 of 6 — #409 → #410 → #411 → **#412** → #413 → #414 --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
#413) Restores the per-stack signal the HashiCorp transport used to carry (backend, providers, overrides) as counted Sentry metrics, and reports what `get` and `init` generate. Volume is bounded per run: metrics are aggregated into counters with caps, never one event per stack or provider. Part of #48. ### What changes - `sendStackTelemetry` reduces each stack's metadata block and `required_providers` to enumerated values and counts; stack names, resource ids and addresses never enter a metric. Providers are public-registry `namespace/type`; other hosts become `private-registry`, malformed input `other`. Modules reduce to `local`, `git`, `private-registry`, `other` or a public registry address. - Aggregation: one event per distinct identity, value = stacks sharing it, count attributes summed over the group. Caps: 8 stack groups, 32 provider tuples, 32 override types, 32 get/init sources. The most counted are kept; the rest fold into one event per metric with `truncated: true`, no identity attributes and the folded sum as value, so totals hold. A run sends at most 75 stack events. - One stack block per process: a synth nested in another command sends only `cli.synth.duration`; `deploy`/`destroy` report the stacks they selected, `diff` the one diffed stack. So `watch` reports its first deploy only, `output` and `list` send no stack block, counts are project-level (stacks using a provider), and stacks cannot be correlated. - `diff`, `deploy` and `destroy` send the stack metrics whether the Terraform operation resolves or rejects, then rethrow the original error; a failed send never replaces it. That includes a failed Terraform init. The failure itself is counted once as `cli.command.error`, and a run that failed is never counted as `cli.command.completed`, even with zero errored stacks. - Per-command scalars ride on `cli.command.completed` through a typed allow-list; nothing else is forwarded. `provider`/`binding` are classified against `terraformProviders` read at command start. | Metric | When emitted | Attributes (plus base) | |---|---|---| | `cli.stack` | per distinct group of synth/diff/deploy/destroy | `backend` (enumerated, `other`, `unknown`), `cloud`, `library_version`, `override_count`, `import_count`, `moved_count` (sums), `truncated` | | `cli.stack.provider` | per distinct provider tuple; value = stacks requiring it | `provider`, `version_constraint` (canonical or `invalid`), `binding` (`generated`/`prebuilt`), `truncated` | | `cli.stack.override` | per distinct resource type; value = stacks overriding it | `resource_type` (grammar, 64-char cap, `module.<kind>`, else `other`), `override_count` (total paths), `truncated` | | `cli.stack.failed` | when at least one stack errored; value = stacks whose Terraform operation errored. Stacks that never started are not counted, and a failed init counts none | base only | | `cli.get.provider`, `cli.get.module` | per distinct generated binding source; value = entries | `provider` / `module`, `truncated` | | `cli.init.provider` | per distinct provider passed to init | `provider`, `truncated` | | scalars on `cli.command.completed` | per run | synth `synth_origin` (`watch`); init `template` (built-in name or `remote`), `is_remote`, `provider_count`; convert `module_count`, `provider_count`, `converted_lines`; get `provider_count`, `module_count` | ### Testing - Unit: normaliser tables (provider and module sources, backends, constraints, resource types, identity length caps); aggregation values, tie order, residual `truncated` events with conserved totals, and tolerance of malformed entries. - Unit with a real v10 client: the delivered stack, get and init metrics carry only the documented attributes; raw-envelope `LEAK-*` markers prove no name, host or path is sent. - Failed runs are driven through real stack executors against a Terraform stub that rejects, not hand-set state: a rejected plan, apply or destroy yields the stack metrics and `cli.stack.failed`, then the original error and no `cli.command.completed`; a rejected init (diff, deploy, destroy) yields the stack metrics with no failed count. - One stack errors while another never starts (`parallelism: 1`, and a failing stack with a dependant; deploy and destroy): `cli.stack` 2, `cli.stack.failed` 1, no `cli.command.completed`, the original error propagates. - The bundle-level aggregation check is the e2e in #414. Stack: 5 of 6 — #409 → #410 → #411 → #412 → **#413** → #414
🤖 Release PR — merge to cut a new release. Kept open and rebased as commits land on `main`. --- <details><summary>0.25.0</summary> ## [0.25.0](v0.24.0...v0.25.0) (2026-10-27) ### ⚠ BREAKING CHANGES * **lib:** `IAssetPackaging` gains `acceptsDirectorySource` as a required member. Custom implementations of the interface must add it. * **lib:** ValidateBinaryVersion and ValidateTerraformVersion are removed (they were only reachable through deep imports, not the cdktn package root). Synth no longer executes the terraform/tofu binary for feature validation; set validateInstalledBinary: true in cdktf.json to verify the installed binary before commands that run it. * **lib:** resource moves (moved blocks) are now validated at synth against the project's declared targetVersions (default terraform >=1.5.7, opentofu >=1.6.0) instead of the locally installed binary. Projects that declare terraform targets below 1.5.0 now fail synth; projects whose installed binary is older than their declared targets should set validateInstalledBinary: true in cdktf.json. * **lib:** import blocks (importFrom / ImportableResource) are now validated at synth against the project's declared targetVersions (default terraform >=1.5.7, opentofu >=1.6.0) instead of the locally installed binary. Projects that declare terraform targets below 1.5.0 now fail synth; projects whose installed binary is older than their declared targets should set validateInstalledBinary: true in cdktf.json. * **lib:** terraform_data resources are now validated at synth against the project's declared targetVersions (default terraform >=1.5.7, opentofu >=1.6.0) instead of the locally installed binary. Projects that declare terraform targets below 1.4.0 now fail synth; projects whose installed binary is older than their declared targets should set * **lib:** cloud backends (the cloud block) are now validated at synth against the project's declared targetVersions (default terraform >=1.5.7, opentofu >=1.6.0) instead of the locally installed binary. Projects that declare terraform targets below 1.1.0 now fail synth; projects whose installed binary is older than their declared targets should set validateInstalledBinary: true in cdktf.json. * **lib:** IAssetPackaging gains a required member omitsDirectoryEntries; external implementations must add it. Custom TerraformAsset assetHash values are now validated against `/^[A-Za-z0-9_.-]+$/` and throw if they contain other characters (e.g. "feature/my-branch"). * **lib:** add asset pipeline foundation types and hash utilities ([#371](#371)) ### Features * **cli:** add a typescript-pnpm init template and deprecate Yarn ([#422](#422)) ([81da164](81da164)) * **cli:** move the TypeScript template to tsx and TypeScript 6 ([#396](#396)) ([b50349d](b50349d)) * **cli:** point AI coding agents at the CDKTN docs MCP server ([#518](#518)) ([f257cd9](f257cd9)) * **cli:** rename `--cdktf-version` to `--cdktn-version` in `init` ([#228](#228)) ([dd3818e](dd3818e)) * **cli:** replace HashiCorp checkpoint telemetry with Sentry usage metrics and consent ([#411](#411)) ([e17046a](e17046a)) * **cli:** report aggregated stack, provider and command usage metrics ([#413](#413)) ([ed9b1e5](ed9b1e5)) * **cli:** report the installed binary, target versions, platform and package manager in usage metrics ([#412](#412)) ([bfe26a8](bfe26a8)) * **cli:** resolve providers from the registry the project targets ([#445](#445)) ([78898ae](78898ae)) * **deps:** adopt jsii 6.0 and TypeScript 6 ([#395](#395)) ([26fa8fa](26fa8fa)) * **gha:** Revert "run the affected unit tests against OpenTofu ([#439](#439))" ([#446](#446)) ([ae015ef](ae015ef)) * **gha:** run the affected unit tests against OpenTofu ([#439](#439)) ([823682d](823682d)) * **gha:** run the affected unit tests against OpenTofu ([#447](#447)) ([c0c4c1c](c0c4c1c)) * **lib:** add asset bundling extension interface ([#442](#442)) ([7839625](7839625)) * **lib:** add asset pipeline foundation types and hash utilities ([#371](#371)) ([a008d12](a008d12)) * **lib:** add asset staging core with no bundling ([#433](#433)) ([44d8bd1](44d8bd1)) * **lib:** add support for single-file bundler outputs ([#464](#464)) ([3592db0](3592db0)) * **lib:** pass ignore strategy through terraform asset ([#472](#472)) ([705e5b9](705e5b9)) * **tests:** add per-test runtime pinning to the integration matrix ([#405](#405)) ([17c2278](17c2278)) ### Bug Fixes * **cli:** catch failures of the background environment collection ([#531](#531)) ([c5feed8](c5feed8)) * **cli:** correct the constructs range comment in the TypeScript template hook ([#408](#408)) ([9fc0e43](9fc0e43)) * **cli:** don't crash after a successful deploy when an output is missing ([#375](#375)) ([0bbd64a](0bbd64a)) * **cli:** fail cleanly when the --outputs-file write fails ([#376](#376)) ([8f9d03a](8f9d03a)) * **cli:** make `cdktn get` remove stale files for resources removed from a provider ([#421](#421)) ([d6e28dc](d6e28dc)) * **cli:** match prebuilt providers regardless of the project's registry ([#493](#493)) ([ea82124](ea82124)) * **cli:** probe with `version` instead of `-v` ([#471](#471)) ([b87ee22](b87ee22)) * **cli:** require patched Node 22.23.4 and bump undici for security fixes ([#516](#516)) ([76436f5](76436f5)) * **cli:** resolve bundled handlers next to the entrypoint ([#414](#414)) ([49600c1](49600c1)) * **cli:** resolve upgraded providers from the CLI's own registry ([#489](#489)) ([10672ee](10672ee)) * **cli:** stop the top-level error handler racing yargs ([#410](#410)) ([5d0c2ef](5d0c2ef)) * **deps:** move jsii-rosetta to 6.0.16 for stream-json 3 ([#463](#463)) ([f1ec16f](f1ec16f)) * **gha:** authenticate to public.ecr.aws in the docker image build ([#519](#519)) ([c65bf2f](c65bf2f)) * **gha:** don't fail the pnpm upgrade jobs on ignored dep builds ([#399](#399)) ([5b66342](5b66342)) * **gha:** move the jsii package cache out of the container HOME ([#514](#514)) ([df5b490](df5b490)) * **gha:** pass -R to gh pr edit in release_github relabel step ([#372](#372)) ([358edc7](358edc7)) * **gha:** push only the prerelease tag from prepare-next ([#476](#476)) ([848f2a6](848f2a6)) * **lib:** avoid terraform init plugin-cache race in test matchers ([#467](#467)) ([26847f9](26847f9)) * **lib:** retry the staging install without --prefer-offline outside CI ([#407](#407)) ([7bb26e7](7bb26e7)) * **provider-generator:** pass the default provider configuration a module requires on get ([#466](#466)) ([a5225ab](a5225ab)) * **provider-generator:** pick the docs registry from targetVersions ([#443](#443)) ([00f2884](00f2884)) * **provider-generator:** pick the module docs registry from targetVersions ([#484](#484)) ([8200330](8200330)) * **provider-generator:** regenerate bindings when the target registry changes ([#490](#490)) ([813e022](813e022)) * **provider-generator:** satisfy module provider configuration aliases on get ([#383](#383)) ([54a8217](54a8217)) * **provider-generator:** stop a provider_functions resource clobbering the functions submodule ([#403](#403)) ([cf19de9](cf19de9)) * **tests:** await the pip install in the python provider command tests ([#478](#478)) ([1c83a36](1c83a36)) * **tests:** bound and retry the dist publish to the local registry ([#425](#425)) ([cc9f9e2](cc9f9e2)) * **tests:** build the edge provider bindings in their own cached Nx target ([#482](#482)) ([f480cc9](f480cc9)) * **tests:** drop unused ts-jest transform from shared jest preset ([#382](#382)) ([03f6e16](03f6e16)) * **tests:** mark the terraform-cloud target Terraform-only in the matrix ([#430](#430)) ([b0deefa](b0deefa)) * **tests:** run example builds with the Nx cache skipped ([#477](#477)) ([3e25e13](3e25e13)) * **tools:** declare the cdktn-cli bundle as a cached Nx output with its real inputs ([#485](#485)) ([10dd118](10dd118)) * **tools:** make the workspace build, package, lint and test on Windows ([#364](#364)) ([8dcddbe](8dcddbe)) ### Code Refactoring * **lib:** Remove the ValidateBinaryVersion synth-time binary probe ([#457](#457)) ([e60142e](e60142e)) * **lib:** Use declared targetVersions for synth-time validation of import blocks ([#454](#454)) ([c0097f4](c0097f4)) * **lib:** Use declared targetVersions for synth-time validation of resource moves ([#453](#453)) ([43edc1e](43edc1e)) * **lib:** Use declared targetVersions for synth-time validation of terraform_data ([#455](#455)) ([a1c125b](a1c125b)) * **lib:** Use declared targetVersions for synth-time validation of the cloud backend ([#456](#456)) ([4a83507](4a83507)) ### Miscellaneous Chores * add OpenTofu and current Terraform to the jsii-terraform image ([#394](#394)) ([2f29d5a](2f29d5a)) * declare complete Nx build outputs so cache hits restore them ([#474](#474)) ([794cf77](794cf77)) * **deps:** move the remaining packages to TypeScript 6 ([#406](#406)) ([c55d360](c55d360)) * **deps:** move the root TypeScript to 6 ([#417](#417)) ([3cae1e2](3cae1e2)) * **deps:** Update xstate to v5.32.4 ([#333](#333)) ([5ebf74f](5ebf74f)) * **deps:** upgrade @sentry/node to 10.x ([#409](#409)) ([2714fff](2714fff)) * **deps:** use caret ranges for unbundled runtime dependencies ([#503](#503)) ([e722357](e722357)) * **docs:** add the test-audit skill for authoring and auditing tests ([#475](#475)) ([909c612](909c612)) * **examples:** move the TypeScript examples to tsx and TypeScript 6 ([#419](#419)) ([857e182](857e182)) * Flexible python version ([#420](#420)) ([955f62a](955f62a)) * **gha:** adopt the new jsii-terraform image and test Terraform 1.5.7 + 1.16.1 ([#398](#398)) ([3ad760b](3ad760b)) * **gha:** gate dependency upgrades on release age ([#418](#418)) ([b611a87](b611a87)) * **gha:** retire Docker Hub publishing of jsii-terraform ([#388](#388)) ([e53cddf](e53cddf)) * **gha:** select the CLI by binary name instead of concatenating a version ([#438](#438)) ([914da56](914da56)) * **gha:** skip the remote Nx cache in the release workflow ([#487](#487)) ([bce91da](bce91da)) * **lib:** stop publishing build state in the cdktn package ([#397](#397)) ([51e4a95](51e4a95)) * npm-check-updates && pnpm update ([#326](#326)) ([7095f6b](7095f6b)) * **readme:** point to the cdktn-io/action GitHub Action ([#504](#504)) ([e387852](e387852)), closes [#58](#58) * **tests:** add timeout handling to spawned commands ([#432](#432)) ([ddb4a8a](ddb4a8a)) * **tests:** run the function-binding scripts with tsx ([#431](#431)) ([fec67bf](fec67bf)) ### Tests * **lib:** ensure TerraformAsset stage within stack own directory ([#434](#434)) ([9a80366](9a80366)) * **lib:** move matcher Docker fixture to a provider version OpenTofu accepts ([#429](#429)) ([7b0da12](7b0da12)) * **tests:** move the python providers fixture to Docker provider 3.9.0 ([#440](#440)) ([a678473](a678473)) * **tests:** move the remaining kreuzwerker/docker pins off unsigned versions ([#459](#459)) ([d855cc1](d855cc1)) * **tests:** move the testing-matchers fixtures to Docker provider 3.x ([#436](#436)) ([8e2c60e](8e2c60e)) * **tests:** re-enable the Python edge integration test ([#404](#404)) ([0a80626](0a80626)) </details> --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). Co-authored-by: open-constructs-cdktn[bot] <291052431+open-constructs-cdktn[bot]@users.noreply.github.com>
A bundled CLI now loads the handlers that sit next to its own entrypoint instead of always falling back to the shipped
bundle/at the package root. The telemetry delivery e2e, which needs this, now runs in CI as its own job. Part of #48.What changes
requireHandlers(cdktn-cli/src/bin/cmds/helper/utilities.ts) probes__dirname/cmds/handlers.jsfirst. In a bundle__dirnameis<outdir>/bin, so this is the file the bundle lays down; the old probe,__dirname/../handlers.js, matched no bundle layout. Unbundled code still falls back to<package root>/bundle/bin/cmds/handlers.js.tools/validate-sentry-e2e.shbuilds a scratch bundle inpackages/cdktn-cli/bundle-e2ewith a local-sink DSN baked in (CDKTN_BUNDLE_OUTDIRinbuild.ts, gitignored), startstools/sentry-sink.mjson a free port, and runsconvert, a failing synth, two stacks and a crashing command. It polls the sink and fails naming the metric that never arrived.invoked, then onecompletedorerrorper run), the per-run stack aggregation, the crash path, and the identity on the wire (install id,cdktn-cli,production). The hostname, username, working directories, stack names, provider hosts andSENTRY_*values must appear nowhere in what the sink received, and the bundle must not referencecheckpoint-api.hashicorp.com..github/workflows/telemetry-e2e.ymlruns it as its own job,Telemetry E2E, called frompr.ymlafterqualityandbuild. It runsnx build cdktn-cli, needs noSENTRY_DSNsecret, and is skipped byci/skip-unit. The job is part of thePR - Resultgate, andbuild-and-packageis unchanged.Not in this PR
pnpm buildoutputs, notpnpm package:convert --language typescriptnever readsdist. The script checks for the build outputs first and fails with "run pnpm build first".mainor in the release workflow.telemetry.test.ts,sentry-sink.mjs,error-handling.integration.test.ts) remain separate; a shared test-support helper is a follow-up.Testing
bash tools/validate-sentry-e2e.sh): the real esbuild bundle, run as child processes against the local sink.TIERS.mdlists the e2e as its own test tier.Stack: 6 of 6 — #409 → #410 → #411 → #412 → #413 → #414