Skip to content

fix(cli): fail cleanly when the --outputs-file write fails - #376

Merged
so0k merged 6 commits into
fix/deploy-outputs-undefined-crashfrom
fix/outputs-file-write-failures
Oct 27, 2026
Merged

so0k merged 6 commits into
fix/deploy-outputs-undefined-crashfrom
fix/outputs-file-write-failures

Conversation

@so0k

@so0k so0k commented Aug 7, 2026 •

Copy link
Copy Markdown
Contributor

Related issue

No GitHub issue. Found while reviewing #375; it is a separate crash on a different code path.

Stacked on #375. This PR targets fix/deploy-outputs-undefined-crash, so the diff shown here is only this change. Review #375 first.

Description

A failed --outputs-file write after an otherwise successful deploy crashed the CLI with a raw stack trace, after telling the user the file had been written.

Root cause

onOutputsRetrieved was typed (outputs) => void. A void return type accepts an async function, so TypeScript never flagged that handlers.ts wires it to the async saveOutputs while the caller did not await it:

onOutputsRetrieved(outputs); // returns a promise nobody holds

A write failure became a rejected promise nobody observed. The command carried on, printed The outputs have been written to <path>, and the process then died on the unhandled rejection. cdktn output --outputs-file had the same defect; watch never uses this callback.

This is not a success-to-failure change

A failing --outputs-file write has always been fatal, through a crash. Measured against published CLIs:

CLI Exit code Stack trace False "written to" line
cdktf-cli@0.21.0 7 yes yes
cdktn-cli@0.23.4 7 yes yes
cdktn-cli@0.24.0 1 yes yes

No version exits 0, so no flag or opt-in is introduced. Relative to 0.24.0 the exit code is unchanged; the message improves.

The fix

All of it lives in presentOutputs (cmds/helper/outputs.ts, introduced in #375), which both runDeploy and runOutput now await. The order is:

  1. print the outputs table (or No outputs found.);
  2. await the write;
  3. print the written to line.

So the values stay on screen when the write fails, and the written to line can never follow a failed write.

A rejected write is rethrown as a typed error, Failed to write outputs: <original message>:

  • ENOENT / ENOTDIR (the path does not exist), EISDIR (it names an existing directory) and ENAMETOOLONG are Usage errors: printed as one line, not captured by Sentry (bin/error-handling.ts);
  • anything else (EACCES, ENOSPC, …) is an External error: printed as one line and captured.

The callback type is widened to void | Promise<void> in handlers.ts and the two command configs.

Tests

  • Helper: the write is awaited before the written to line; ENOENT, ENOTDIR, EISDIR and ENAMETOOLONG give Usage, EACCES gives External; the table is printed before the rejection; no written to line after a failure. Removing the await fails by assertion.
  • One test goes through the real saveOutputs into a temp directory whose parent does not exist.
  • Per command: one thin test that the rejection propagates and the stream is stopped. The rejected write is handled up front in every layer, so a dropped await fails an assertion instead of surfacing as an unhandled rejection.

Checklist

  • I have updated the PR title to match CDKTN's style guide
  • I have run the linter on my code locally
  • I have performed a self-review of my code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation if applicable — n/a, the exit-code contract is unchanged
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works if applicable
  • New and existing unit tests pass locally with my changes

@so0k
so0k marked this pull request as ready for review August 7, 2026 12:23
@so0k
so0k requested a review from a team as a code owner August 7, 2026 12:23
sakul-learning

This comment was marked as resolved.

@jsteinich

Copy link
Copy Markdown
Contributor

Root cause confirmed, and deploy/output are indeed the only two commands using the callback.

The description and the code disagree on one point.

Splitting the guards also means a failing write no longer skips the outputs display

The throw Errors.External(...) sits before the render block, so a failing write does skip the display:

// A failed --outputs-file write is a broken promise to the user and must be fatal: await it and
// rethrow as an External error, which cdktn.ts's top-level `.fail()` handler prints as a single
// clean line rather than a stack trace, since the deploy itself already succeeded.
try {
await onOutputsRetrieved(outputs);
} catch (e) {
throw Errors.External(
`Failed to write outputs${outputsPath ? ` to ${outputsPath}` : ""}: ${
e instanceof Error ? e.message : e
}`,
e instanceof Error ? e : undefined,
);
}
if (outputs && Object.keys(outputs).length > 0) {
try {
console.log(renderOutputs(outputs));

(same ordering in runOutput at L88-L101). Against 0.24.0's actual async behaviour, the table is now skipped where it previously printed before the crash.

Which did you mean?

  • Leftover from an earlier draft → drop the sentence.
  • You want the table to still print → the throw moves below the render block (collect, render, throw), plus a test.

Failing fast is defensible; I'd just like the two to agree.

Two smaller things:

  1. ENOENT on a user-named directory reads more like Errors.Usage than External. Same clean-print branch either way, so semantics only — but EACCES/ENOSPC do fit External. Your call.
  2. The path renders twice: Failed to write outputs to X: ENOENT: no such file or directory, open 'X'.

@so0k

so0k commented Aug 7, 2026

Copy link
Copy Markdown
Contributor Author

You're right that they disagree, and the answer is the second option — I want the table to print, so the
throw moves below the render block.

Not a leftover sentence: I'd written the description from the intent and then not checked it against the final
ordering, which is exactly the kind of thing a description should not get away with. Thanks for catching it.

The reasoning for render-then-throw: at that point the deploy has genuinely succeeded and the outputs exist in
memory. The only thing that failed is persisting them. Skipping the table means the user loses the values
entirely — they can't even copy them out of the terminal — for a failure that is often a mistyped --outputs-file
path they can just retry. Printing the table and then failing gives them both the data and the error. Your point
that this is a regression against 0.24.0's actual behaviour (where the table printed before the crash) makes it
worse than a wash, so failing fast here is losing information users had before.

New ordering: render → save → confirm. The written to line stays after the save, so it is still unreachable on
failure. With a test asserting the table is printed and the run rejects.

Errors.Usage for ENOENT — agreed, and it turns out to matter beyond semantics.

Usage errors are excluded from Sentry crash reporting, so classifying a user's mistyped path as External
would file a crash report for something that isn't a bug. Going with: ENOENT/ENOTDIR → Usage, everything
else (EACCES, ENOSPC, …) → External, keyed off err.code rather than message text.

Double path — fixing. The fs error message already names the path, so the prefix drops it:
Failed to write outputs: ENOENT: no such file or directory, open 'X'.

All three incoming; I'll push and re-request. The PR description gets corrected along with the code.

@so0k
so0k force-pushed the fix/outputs-file-write-failures branch from d301821 to c35fcba Compare August 7, 2026 15:10
@so0k

so0k commented Aug 7, 2026

Copy link
Copy Markdown
Contributor Author

Pushed — rebased onto the updated #375, then 2b03546f6 + c35fcba41. All four points addressed.

  • Render before failing. New ordering is render → save → confirm, in both runDeploy and runOutput. The
    written to line stays after the save so it's still unreachable on failure. Test asserts the table is
    printed and the run still rejects. The description sentence is now true rather than deleted.
  • ENOENT/ENOTDIR → Errors.Usage, everything else stays External, keyed off err.code rather than
    message text. Tests cover both (ENOENT → Usage, EACCES → External).
  • Path printed once — the prefix no longer repeats what the fs error already names.
  • runOutput now has direct coverage (ui/__tests__/output.test.ts), modelled on its real shape —
    returnValue from the project callback, not outputsByConstructId.

Two notes against myself:

The first version of those runOutput tests passed for a partly wrong reason: the mock resolved a returnValue
without ever invoking the project callback, so fetchOutputs never ran and the suite would still have passed if
runOutput stopped fetching outputs altogether. Fixed in c35fcba41.

And a behaviour interaction worth knowing when reading the two PRs together: with --outputs-file and all
outputs dropped, #375 alone prints only No outputs found., whereas after this PR it prints that and the
written to line, since that line is now keyed off write success rather than render state.

@jsteinich jsteinich left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Full pass over the rebased diff. This is much tighter than the previous revision now that the helper lives in #375 — what's left is exactly the defect plus the classification, and the render → write → confirm ordering means the written to line is genuinely unreachable on failure. The helper test that captures what had printed at the moment the write resolved is a nice way to pin the ordering rather than just asserting the end state.

One thing I'd ask for, inline, plus a test-only note.

Two verifications you may find useful:

The clean-exit argument is now stronger than the description says. It cites beforeSend dropping anything containing "Usage Error". Post-#410 that message-sniffing is no longer the mechanism: bin/error-handling.ts has an explicit three-way branch — Usage prints the message and is not captured (:53), External prints and is captured (:58), anything else prints message + stack + debug info. So the Usage/External split is now load-bearing structurally, not incidentally.

The telemetry paragraph in the description is stale and should come out. It says Errors.External fires an unawaited POST to checkpoint-api.hashicorp.com and flags it as a live data point for #62. After #411, errors.ts no longer imports ./checkpoint at all — it calls sendErrorTelemetry from ./telemetry. That POST is gone from the error path, so the note now points at something that doesn't happen. (#411 also fixed the latent bug where each Errors.* factory captured errorScope at module load; the scope is read at call time now.)

so0k added 6 commits October 26, 2026 23:29
`onOutputsRetrieved` was typed `(outputs) => void`, which accepts an async
function, and `handlers.ts` wires it to the async `saveOutputs`. The call was
not awaited, so a failed write became an unhandled rejection: the command
printed "The outputs have been written to <path>" and then crashed with a
stack trace.

`presentOutputs` now prints the outputs, awaits the callback, and only then
prints the "written to" line. A rejected write is rethrown as a typed error:
`ENOENT` / `ENOTDIR` (a path that does not exist) as a Usage error, anything
else as an External error. The outputs stay printed when the write fails.

`runDeploy` and `runOutput` await the helper, and the callback type is widened
to `void | Promise<void>`.
The helper owns the suite: the write is awaited before the "written to" line, a
failed write is typed by errno and leaves the outputs printed, and a real
saveOutputs into a missing directory surfaces as a Usage error. runDeploy and
runOutput each keep one test that the rejection propagates and the stream is
stopped.
Restores the up-front .catch so a dropped await in presentOutputs fails the
assertion instead of surfacing as an unhandled rejection, matching the helper test.
EISDIR (the path is an existing directory) and ENAMETOOLONG are user path
mistakes like ENOENT/ENOTDIR, so they should not be reported as External.
@so0k
so0k force-pushed the fix/outputs-file-write-failures branch from a92f9c8 to 10dc892 Compare October 26, 2026 16:31
@so0k

so0k commented Oct 26, 2026

Copy link
Copy Markdown
Contributor Author

On the review-level notes: the checkpoint/telemetry paragraph is already gone from the body, and the Usage/External description now points at the explicit branch in bin/error-handling.ts (Usage printed and not captured, External printed and captured) rather than the old beforeSend message sniffing. The inline points are addressed in e32aa3e, 72a3798 and 10dc892, and the stack is rebased onto main (ea82124).

@so0k
so0k requested a review from jsteinich October 26, 2026 17:11

@jsteinich jsteinich left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

EISDIR/ENAMETOOLONG classification and the test-layer consistency look good. Approving to land together with #375.

@so0k
so0k merged commit 8f9d03a into main Oct 27, 2026
276 of 278 checks passed
@so0k
so0k deleted the fix/outputs-file-write-failures branch October 27, 2026 05:26
so0k added a commit that referenced this pull request Oct 27, 2026
…sing (#375)

### Related issue

No GitHub issue. Related: #361 / #410 (the top-level failure path),
which is why this crash surfaced as a raw stack trace.

### Description

A `cdktn deploy` that applied successfully could then crash while
printing its outputs:

```
TypeError: Cannot convert undefined or null to object
    at Object.entries (<anonymous>)
    at renderNested (helper/format.ts)
    at renderOutputs (helper/format.ts)
    at runDeploy (ui/deploy.ts)
```

#### Root cause

`getConstructIdsForOutputs` maps the synthesized metadata
(`"//".outputs` in `cdk.tf.json`) onto the result of `terraform output
-json` with an unchecked lookup:

```ts
return { ...acc, [key]: outputs[value] }; // undefined when terraform did not return it
```

An output declared in the metadata but absent from Terraform's result
(state drift, `--skip-synth` against older state, an output renamed or
removed between applies) was kept with the value `undefined`. A group is
only pruned when every output in it is missing, so a partial miss
survived and the renderer then called `Object.entries(undefined)`.

The data path is unchanged since before 0.23. It surfaced in 0.24.0
because #264 removed the Ink renderer, whose `try/catch` used to turn
this throw into a one-line message.

#### The fix

- **Protect at the source.** `getConstructIdsForOutputs` omits an output
Terraform did not return (`undefined` or `null`) and logs one warning
per stack naming the stack and every omitted output, by construct path
and terraform output name:

  ```
2 outputs declared by stack "app" were not returned by terraform and are
omitted: app/network/a (a), app/db/port (db_port_ABC123)
  ```

The check is explicit, not a truthiness test, so an output whose value
is `""`, `0` or `false` still renders. There is no distinction between
cross-stack and user-declared outputs.
- **`isTerraformOutput` excludes `null`**, where it previously threw on
`null.sensitive`.
- **One helper presents outputs for both commands.** `presentOutputs`
(`cmds/helper/outputs.ts`) is used by `runDeploy` and `runOutput`:
  - no outputs: prints `No outputs found.`;
- otherwise renders the table; a render failure is reported on stderr
and does not fail the command, since its real work already succeeded;
- with `--outputs-file`: prints the `written to` line, independent of
whether the table rendered.

The renderer (`helper/format.ts`) and `unpackTerraformOutput` are
unchanged from `main`. Neither command can hand the renderer an
`undefined` leaf any more, for two different reasons:

- `deploy` presents the construct-id-mapped outputs, where the omission
above now applies.
- `output` presents the raw `terraform output -json` map (see *Scope*
below), in which every key Terraform returns is a full `{ value, type,
sensitive }` object.

#### Scope: `deploy` and `output` present different maps

`presentOutputs` is shared, but its two callers pass semantically
different maps, and this PR keeps that as it is on `main`:

- `runDeploy` presents `CdktfProject.outputsByConstructId`, mapped
through the stack metadata and nested by construct path.
- `runOutput` presents `CdktfProject.fetchOutputs()`, which returns each
stack's raw terraform outputs (`CdktfStack.fetchOutputs` computes the
mapped form for its state update and the warning, but returns
`this.outputs`).

So for a nested `TerraformOutput`, `deploy --outputs-file` writes
`{"stack":{"parent":{"child":…}}}` while `output --outputs-file` writes
`{"stack":{"parent_child_<HASH>":…}}`. Unifying them changes a file
shape that users read, and deploy's shape is the one consumers depend on
(cdktn-io/action#12 documents `stackOutputs` from `cdktn apply
--outputs-file` as `.my-stack.url`), so it is tracked separately in #525
rather than changed here.

#### Behaviour notes

- With `--outputs-file` and no outputs, the CLI now prints `No outputs
found.` followed by the `written to` line. The file is written in that
case (it contains `{}`), so the line is true; `main` printed only `No
outputs found.`.
- The warning is logged once per stack, and again on each `watch`
redeploy while the state stays out of date. It is also logged by `cdktn
output`, which fetches the same way.
- **Known limitation:** in an interactive terminal the warning can be
garbled. It is a direct log4js write to stdout while the progress bar is
live, and the bar's next repaint can overwrite that row. Non-TTY and CI
output is unaffected. Routing it through the renderer means carrying the
notice on the state update to the UI layer, which is more than the
warning is worth here.

#### Tests

- Mapping: partial miss, whole group missing, `null`, the aggregated
warning naming the stack and each output, pass-through without metadata.
- Stack execution: on both `deploy` and `fetchOutputs`, a real
`CdktfProject` warns with the stack's own name.
- Helper: empty and undefined outputs, the table, a throwing render, the
`written to` line with and without a path.
- Per command: thin wiring tests (including the options `runOutput`
forwards to `fetchOutputs`), plus the end-to-end regression (a missing
output does not crash a successful deploy).

Each test was checked against a broken source: removing the omission,
the `null` handling, the warning, the empty check or the render guard
fails at least one of them.

---

#### Stacked PR

#376 builds on this branch and makes the `--outputs-file` write awaited,
inside the same helper.

### Checklist

- [x] I have updated the PR title to match [CDKTN's style
guide](https://github.com/open-constructs/cdk-terrain/blob/main/CONTRIBUTING.md#pull-requests-1)
- [x] I have run the linter on my code locally
- [x] I have performed a self-review of my code
- [x] I have commented my code, particularly in hard-to-understand areas
- [ ] I have made corresponding changes to the
[documentation](https://github.com/open-constructs/cdk-terrain-docs/tree/main/content)
if applicable — n/a, no documented behaviour changes
- [x] My changes generate no new warnings
- [x] I have added tests that prove my fix is effective or that my
feature works if applicable
- [x] New and existing unit tests pass locally with my changes
so0k pushed a commit that referenced this pull request Oct 27, 2026
🤖 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>
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.

3 participants