Skip to content

chore: declare complete Nx build outputs so cache hits restore them - #474

Merged
so0k merged 1 commit into
open-constructs:mainfrom
jsteinich:fix/nx-build-outputs
Oct 7, 2026
Merged

so0k merged 1 commit into
open-constructs:mainfrom
jsteinich:fix/nx-build-outputs

Conversation

@jsteinich

Copy link
Copy Markdown
Contributor

Description

The Release run for #467 failed in every unit_test_next job, but not because of #467's changes. Each job got an Nx remote cache hit for the build targets (nx run cdktn:build [remote cache]), and Nx restores only a target's declared outputs. The shared build target declares {projectRoot}/build and {projectRoot}/.jsii, but three projects write elsewhere:

Project Undeclared build output Failure on a cache hit
cdktn (jsii) lib/, generated tsconfig.json Cannot find module '.../packages/cdktn/lib', which then breaks every package that depends on cdktn
@cdktn/hcl2json main.wasm.gz (build-go.sh), wasm/wasm_exec.js (prebuild.sh) ENOENT .../main.wasm.gz, global.Go is not a constructor
@cdktn/hcl-tools same as hcl2json same

PR CI didn't catch this because those jobs build fresh and then only get local cache hits within the same job, where the files are still on disk.

This declares the real outputs per project in each package.json nx.targets.build.outputs. The other projects keep the shared defaults.

Re-running the failed Release won't help on its own: the cache keys are unchanged, so it hits the same entries. A Release run after this merges will compute new hashes (the package.json files are inputs) and store complete entries.

Open question: nothing in nx.json or the workflows configures a remote cache, and the Release run for #471 six minutes earlier built everything fresh. This looks like the Depot runners' Nx cache started serving hits. Worth confirming in the Depot settings.

Verification

Locally (Nx's local cache restores declared outputs the same way the remote one does):

  1. nx show project lists the new outputs; cache and dependsOn are still inherited, and other projects are unchanged.
  2. Built cdktn, hcl2json and hcl-tools, deleted the generated outputs (keeping the tracked packages/cdktn/lib/.gitignore, as in a fresh checkout), and rebuilt. All three were [local cache] hits that restored lib/, .jsii, tsconfig.json, build/, main.wasm.gz and wasm/wasm_exec.js.
  3. On the cache-restored outputs, hcl2json's tests pass (56/56) and cdktn's asset tests that failed in the Release run pass, apart from two asset-hash tests that hit a Windows-only EBUSY while deleting a temp dir that is the process cwd.

Checklist

  • I have updated the PR title to match CDKTN's style guide
  • I have performed a self-review of my code
  • I have made corresponding changes to the documentation if applicable
  • My changes generate no new warnings

🤖 Generated with Claude Code

The shared build target only declares `{projectRoot}/build` and
`{projectRoot}/.jsii` as outputs. Three projects write elsewhere:

- cdktn (jsii): lib/, plus the generated tsconfig.json that
  tsconfig.test.json extends
- @cdktn/hcl2json and @cdktn/hcl-tools: main.wasm.gz from build-go.sh
  and wasm/wasm_exec.js copied by prebuild.sh

On a cache hit Nx restores only declared outputs, so these files were
missing. The Release run for open-constructs#467 hit the remote cache on the Depot
runners and every unit_test_next job failed with
`Cannot find module '.../packages/cdktn/lib'` or
`ENOENT .../hcl2json/main.wasm.gz`.

Declare the real outputs per project so local and remote cache hits
restore everything the tests and packaging read.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@jsteinich
jsteinich marked this pull request as ready for review October 7, 2026 01:04
@jsteinich
jsteinich requested a review from a team as a code owner October 7, 2026 01:04
@so0k
so0k merged commit 794cf77 into open-constructs:main Oct 7, 2026
262 checks passed
so0k added a commit that referenced this pull request Oct 9, 2026
### Description

Depot runners set `NX_SELF_HOSTED_REMOTE_CACHE_SERVER`, so Nx tasks on
`depot-*` runners read and write the Depot remote cache. The five
`*-documentation` examples run there, and their `build` target (`cdktn
get`) started getting `[remote cache]` hits:

```
nx run @examples/go-documentation:build  [remote cache]
...
nx run @examples/go-documentation:synth
ERROR: synthesis failed, run "cdktn get" to generate providers in generated
```

Two things are wrong with caching example builds at all:

- The example projects have no Nx dependency edge to `cdktn`, so their
task hash covers only the example's own files, not the dist bundle the
job installs. A cache hit replays bindings generated against whatever
build was under test when the entry was stored.
- The generated bindings directory (`generated/`, `.gen/`, `imports/`)
is not a declared output, so nothing is restored and `synth` fails.

Declaring outputs would fix the second point but keep the first, so this
passes `--skip-nx-cache` to the single `nx run-many` call in
`tools/build-example.mjs`. It covers every example and every target the
runner invokes. The core packages keep their cache; #474 made their
outputs complete.

Seen on the Release runs for #467, #445 and #475 (a rotating set of
documentation examples depending on which keys were warm).

Draft until #476 lands; will rebase then.

### 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 performed a self-review of my code
- [ ] I have made corresponding changes to the documentation if
applicable
- [x] My changes generate no new warnings

🤖 Generated with [Claude Code](https://claude.com/claude-code)

https://claude.ai/code/session_01RvtnNBvNuTHU4dG3NFuBeb

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
so0k added a commit that referenced this pull request Oct 9, 2026
…arget (#482)

### Description

Same class of bug as #474, in a package it did not cover. Nx restores
only a target's declared `outputs` on a cache hit, and Depot runners
serve remote hits for the `build` targets.

The edge provider bindings were a `postbuild` side effect of
`@cdktn/provider-generator`'s build, written to
`edge-provider-bindings/`, which the shared default outputs do not
cover. On a cache hit the directory stayed empty, the integration
prepare job uploaded nothing (`No files were found with the provided
path: packages/@cdktn/provider-generator/edge-provider-bindings`), and
every `linux_integration` job failed with `Artifact not found for name:
edge-provider-bindings`. Seen on every integration matrix job of #478
(https://github.com/open-constructs/cdk-terrain/actions/runs/37799770873).
The Release workflow is not affected because its prepare jobs run on
`ubuntu-latest`; the PR integration prepare job runs on a Depot runner.

Following review, the bindings now come from their own cached target
instead of a `postbuild` hook:

- `build:edge-bindings` on `@cdktn/provider-generator` depends on
`build`, declares `edge-provider-bindings/` as its output, and names
`src/__tests__/edge-provider-schema/**` as an input alongside
`production` and `^production`. Moving it out of `postbuild` also keeps
the five-language jsii-pacmak run off every local `pnpm build`.
- A root `build:edge-bindings` script runs it, and the three prepare
jobs (`integration.yml`, both `release.yml` prepare jobs) call it after
`pnpm run package`, right before the upload.
- `if-no-files-found: error` on the three edge-bindings uploads
(`integration.yml` and both `release.yml` prepare jobs), so an empty
upload fails the prepare job and the matrix is skipped instead of every
job failing on download.
- `test/package.json`'s `edge:install` runs the target before copying,
so the documented local flow (`pnpm package`, then `pnpm integration`)
and `cd test && pnpm edge:install` keep working; `test/Readme.md`
describes it.
- `@cdktn/provider-generator`'s `build` target adds the edge schema
sources to its inputs, so the compiled generator can never come from a
cache entry older than the sources the bindings target hashes (review
finding by @sakul-learning and @jsteinich).

The `cdktn-cli` output declaration from the first revision moved to
#485, since making `bundle/` cacheable needs its own inputs fixed first.

### Verification

In a clean worktree with Nx 22.7.9:

- Fresh `pnpm run build:edge-bindings` generates
`edge-provider-bindings/{csharp,go,java,python,providers}`.
- Deleting the directory and re-running is a cache hit that restores it.
- Appending to `src/__tests__/edge-provider-schema/builder.ts` re-runs
`build:edge-bindings` while the upstream builds stay cached.
- `pnpm run edge:install` from `test/` builds the chain, generates the
five language directories and copies them into
`test/edge-provider-bindings`.

One observation from that testing: in this repo the negative patterns in
the `production` named input do not exclude anything from task hashes.
Editing a test file in `@cdktn/commons` re-runs its `build`, and
spelling the exclusion several ways changes nothing, while a
positive-only input list behaves correctly. So the stale-bindings
scenario could not have occurred as described, because any `__tests__`
edit already invalidates `build`. The explicit input on the new target
keeps that true by declaration. The broader effect, every test edit
rebuilding every package, is pre-existing and not addressed here.

Integration is left enabled on this PR so the Depot-runner prepare job
demonstrates the artifact uploading again.

### 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 performed a self-review of my code
- [x] I have made corresponding changes to the documentation if
applicable
- [x] My changes generate no new warnings

🤖 Generated with [Claude Code](https://claude.com/claude-code)

https://claude.ai/code/session_01RvtnNBvNuTHU4dG3NFuBeb

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
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>
@github-actions

github-actions Bot commented Nov 6, 2026

Copy link
Copy Markdown
Contributor

I'm going to lock this pull request because it has been closed for 30 days. This helps our maintainers find and focus on the active issues. If you've found a problem that seems related to this change, please open a new issue and complete the issue template so we can capture all the details necessary to investigate further.

@github-actions github-actions Bot locked as resolved and limited conversation to collaborators Nov 6, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants