Skip to content

feat(cli): resolve providers from the registry the project targets - #445

Merged
so0k merged 4 commits into
open-constructs:mainfrom
jsteinich:feat/opentofu-registry-resolution
Oct 7, 2026
Merged

so0k merged 4 commits into
open-constructs:mainfrom
jsteinich:feat/opentofu-registry-resolution

Conversation

@jsteinich

@jsteinich jsteinich commented Sep 18, 2026 •

Copy link
Copy Markdown
Contributor

Related issue

Addresses the code-level readings of #208. Stacked on #443 — merge that first; this branch contains its commit and reuses the registry mechanism it adds.

Description

#208's title reads like a dependency-manager change while its body is about sample code on registries, so it admits three readings. This takes the two that are code-level, using the same targetVersions-derived registry as #443 — OpenTofu only when it is the sole declared product, Terraform otherwise.

The through-line: OpenTofu users have been told to fully qualify their providers, because everything here hardcoded registry.terraform.io. These changes make that advice optional rather than required, without breaking anyone already following it.

1. Bare sources expand against the project's registry

normalizeProviderSource hardcoded the Terraform host, so cdktn provider add aws always produced registry.terraform.io/hashicorp/aws. It now expands against the registry the project targets, so an OpenTofu project gets registry.opentofu.org/hashicorp/aws.

A source that already names a host is still left untouched, so an explicit registry.opentofu.org/... always wins — over the project's target, and over the default for a project that declares no targetVersions.

2. Explicitly qualified OpenTofu providers silently skipped version resolution

This is a live bug on main, and it hits exactly the users who followed the fully-qualify advice. Version resolution was gated on:

if (!constraint.version && constraint.isFromTerraformRegistry()) {

where isFromTerraformRegistry() is hostname === "registry.terraform.io". A provider written as registry.opentofu.org/hashicorp/aws with no version failed that check, so no version was resolved and none was written.

It becomes isFromPublicRegistry() — whether the host is a registry cdktn can query at all. Private and self-hosted registries are still correctly excluded, since they expose no versions API.

3. Resolution and the browse URL follow the constraint

Both now derive from the constraint's own hostname rather than a registry threaded down the call chain. That is what makes explicit qualification win, and it let the DependencyManager parameter from the first commit go away again.

Both registries expose the same /v1/providers/{ns}/{name}/versions shape, and their version lists differ — 42 versus 41 for hashicorp/random at time of writing — so an OpenTofu project could previously be told to pin a version its own registry does not carry.

The error text's browse URL follows too: search.opentofu.org/providers for OpenTofu, since registry.opentofu.org/browse/providers is a 404.

simplifiedName drops whichever public host it carries, not just Terraform's.

What is still out of scope

The third reading of #208 — publishing CDK Terrain samples to the OpenTofu registry — is untouched, so the issue stays open for it.

cdktf-config-manager still strips only the Terraform prefix when writing cdktf.json, which means an OpenTofu project's providers are written fully qualified. That round-trips correctly and matches what those users already write by hand, so it is left alone deliberately rather than risking a strip that normalization would not re-add.

Testing

registry-api.test.ts covers all four paths with the MockAgent pattern the neighbouring tests use: default resolves against Terraform; a bare source under an OpenTofu target expands and resolves against OpenTofu; an explicitly qualified OpenTofu source resolves even with no declared target; a private registry is not queryable.

nx test @cdktn/cli-core (so the dependency chain is built): all suites pass.

Running jest directly in the package without building siblings reports failures on Cannot find module '@cdktn/hcl2cdk' / '@cdktn/hcl-tools' — pre-existing and unrelated.

🤖 Generated with Claude Code

@jsteinich
jsteinich force-pushed the feat/opentofu-registry-resolution branch from 5912d8f to a3c68a6 Compare September 19, 2026 14:49
@jsteinich
jsteinich marked this pull request as ready for review September 23, 2026 01:45
@jsteinich
jsteinich requested a review from a team as a code owner September 23, 2026 01:45
@sakul-learning

Copy link
Copy Markdown
Contributor

providerAdd() selects the provider registry from the process working directory rather than from the project identified by its projectDirectory argument.

In packages/@cdktn/cli-core/src/lib/provider-add.ts, the registry comes from readConfigSync().targetVersions; without a path, readConfigSync() reads process.cwd()/cdktf.json. The same function then constructs DependencyManager with projectDirectory. These can differ: packages/@cdktn/cli-core/src/lib/init.ts reads the newly scaffolded config from destination and calls providerAdd({ projectDirectory: destination }) without changing the working directory.

Consequently, adding a bare provider to an OpenTofu-only destination can normalize and resolve it against registry.terraform.io based on the caller's CWD instead of against registry.opentofu.org based on the destination project.

Could the registry selection read targetVersions from projectDirectory (or receive the already-read target versions from the caller)? A focused regression test with different CWD and project-directory configs would cover this boundary.

jsteinich and others added 3 commits September 23, 2026 08:07
Addresses the two code-level readings of open-constructs#208 using the mechanism added for
open-constructs#393: the registry comes from the project's declared targetVersions, so
OpenTofu only when it is the sole declared product.

1. The "could not find a version" error pointed everyone at
   registry.terraform.io/browse/providers. An OpenTofu project now gets
   search.opentofu.org/providers - registry.opentofu.org/browse/providers is
   a 404, the listing lives on the search host.

2. `cdktn provider add` resolved available versions from
   registry.terraform.io regardless of target. Both registries expose the
   same /v1/providers/<ns>/<name>/versions shape and their version lists
   differ - 42 versions vs 41 for hashicorp/random at time of writing - so an
   OpenTofu project could be told to pin a version its own registry does not
   carry.

Deliberately out of scope: DEFAULT_HOSTNAME, which normalizes a bare
"hashicorp/aws" into a fully qualified source. That is provider *identity*,
not resolution, and it is consumed by more than this path -
cdktf-config-manager and prebuilt-providers both hardcode stripping the
"registry.terraform.io/" prefix, so making normalization registry-aware would
break prebuilt matching and cdktf.json round-tripping for OpenTofu projects.
It needs its own change with those consumers updated together.

The registry parameter defaults to Terraform, so every caller that does not
yet thread targetVersions keeps its current behaviour.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Folds DEFAULT_HOSTNAME into the registry work, per review.

OpenTofu users have been told to fully qualify their providers because
normalization hardcoded registry.terraform.io. Two changes so that advice
becomes optional rather than required, without breaking anyone following it:

- A bare or namespace-only source now expands against the registry the
  project targets, so `cdktn provider add aws` in an OpenTofu project yields
  registry.opentofu.org/hashicorp/aws.
- A source that already names a host is still left alone, so an explicit
  registry.opentofu.org/... always wins over the project's target - and over
  the default, for a project that declares no targetVersions.

`isFromTerraformRegistry` becomes `isFromPublicRegistry`. That predicate
gated automatic version resolution on the host being Terraform's, so an
explicitly qualified OpenTofu provider - exactly what tofu users were told to
write - silently skipped resolution and got no version. It now asks whether
the host is a registry we can query at all, so private and self-hosted
registries are still correctly excluded.

Resolution and the browse URL in the error text both follow the constraint's
own hostname rather than a registry passed down the call chain, which is what
makes explicit qualification win and let the DependencyManager parameter go
away again.

simplifiedName drops whichever public host it carries, not just Terraform's.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Review finding on open-constructs#445. `providerAdd` picked the registry from
`readConfigSync()`, which with no argument reads `process.cwd()/cdktf.json`,
while passing `projectDirectory` to DependencyManager. Those are not the same
directory: init() scaffolds into `destination` and calls providerAdd with
`projectDirectory: destination` without changing cwd
(cli-core/src/lib/init.ts:93).

So adding a bare provider to a freshly scaffolded OpenTofu-only project would
normalize and resolve it against registry.terraform.io, chosen from whatever
project the CLI happened to be run from.

Regression test covers the boundary directly: cwd is a Terraform-targeting
project, the destination targets OpenTofu only, and the selection must follow
the destination.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@jsteinich
jsteinich force-pushed the feat/opentofu-registry-resolution branch from 7304164 to b17afc6 Compare September 23, 2026 13:15
jsteinich added a commit that referenced this pull request Oct 3, 2026
### Related issue

Re-lands #439, reverted in #446. **Stacked on #443** — merge that first;
this branch contains its commit.

If this is approved before #446, #446 can be dropped. Otherwise this is
the follow-up to it.

### Why #439 failed

Its diff touched only `.github/workflows/pr-unit.yml`. The job it adds
runs `nx affected -t test --exclude='*,!tag:unit-test:terraform'`, so on
a workflow-only change `nx affected` resolved to **no terraform-tagged
projects** and the OpenTofu job ran zero tests. The green check proved
nothing, and it then failed on every PR that touched a tagged project.

**This branch touches `provider-generator` and `provider-schema`, so the
tofu job runs for real on its own PR.** That is the point — a change to
`pr-unit.yml` alone can never demonstrate this.

### What actually had to be fixed

Four fixtures, in two classes. The first was expected; the second was
not, and is the more interesting one.

#### Providers OpenTofu will not install

- `versions-file.test.ts` pinned `kreuzwerker/docker` **2.16.0**,
`provider.test.ts` pinned **3.0.2**. Both rejected as unsigned. Moved to
3.9.0 — signing starts at 3.7.0, **not** at the 3.x major (#440).
- `provider.test.ts` used `andsafe-AG/bitbucket`, which is on the
Terraform registry but **404s on OpenTofu's**. That test needs two
same-named providers from different namespaces, so it moves to
`zahiar/bitbucket`, which is on both.

#### Fully qualified names leaking the fetching CLI into expectations

The schema JSON is keyed by FQPN, so the host records *which binary
fetched it*, not anything about the code under test. Three places baked
Terraform's in:

| file | was | now |
| --- | --- | --- |
| `provider-schema.test.ts` | snapshots raw schema incl. FQPN keys |
keys stubbed in the existing sanitizer |
| `versions-file.test.ts` | asserted `registry.terraform.io/${fqn}` |
compares the provider part |
| `provider.test.ts` | directory snapshot incl. `versions.json` | FQPN
keys normalized |

For `provider-schema.test.ts` this extends a pattern already there: its
`sanitizeJson` stubs `format_version`, `cli_name` and `cli_version` for
exactly this reason — `cli_name` is already stubbed because it stamps
the fetching binary, and the FQPN host is the same signal through a
different field.

**Keys only.** A registry URL appearing inside provider *documentation
text* is identical under either CLI and stays as authored.

Normalizing happens in the tests, not the product: the FQPN host is
meaningful at runtime — #443 and #445 depend on it — so stripping it in
the product would destroy information.

### Verified against both CLIs

Not assumed, and not inferred from a green matrix job:

| package | terraform | OpenTofu 1.12.6 |
| --- | --- | --- |
| `@cdktn/provider-generator` | 23 suites / 117 tests / 101 snapshots |
same, all pass |
| `@cdktn/provider-schema` | 6 suites / 58 tests / 10 snapshots | same,
all pass |

The generated documentation links that also differed by CLI are fixed by
#443, which this is stacked on — without it, `provider.test.ts` still
diverges.

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

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Review feedback on open-constructs#445: the previous test called readConfigSync directly and
asserted helper semantics, so it passed against the pathless call it was
meant to guard. It documented behaviour without protecting the fix.

It now calls providerAdd() with a Terraform project as cwd, an OpenTofu-only
projectDirectory and a bare provider source, and asserts the constraint that
reaches the DependencyManager carries registry.opentofu.org. A second case
covers the mirror - an OpenTofu cwd must not leak into a Terraform
destination.

Confirmed it is a real guard: reverting provider-add.ts to the pathless
readConfigSync() fails both cases.

Spying on DependencyManager.prototype.addLocalProvider rather than mocking
the module keeps the real ProviderConstraint doing the normalizing, so the
assertion is on emitted behaviour. It also avoids the circular import between
dependency-manager and registry-api, which makes requireActual on that module
throw a TDZ error on DEFAULT_HOSTNAME.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

@so0k so0k 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.

reviewed discord bot findings and final diff - LGTM

@so0k
so0k merged commit 78898ae into open-constructs:main Oct 7, 2026
518 of 520 checks passed
jsteinich added a commit to jsteinich/cdk-terrain that referenced this pull request Oct 8, 2026
…istry

`CdktfConfigManager` stripped the literal `registry.terraform.io/` prefix and
compared existing entries via `ProviderConstraint.fromConfigEntry()` with no
registry, which defaults to Terraform. Both only hold for a Terraform project,
so open-constructs#445 left this file behind when it made provider resolution registry-aware.

In an OpenTofu project `cdktn provider upgrade random@=3.2.0` produced:

    ["hashicorp/random@=3.1.3", "registry.opentofu.org/hashicorp/random@=3.2.0"]

The prefix did not match so the source stayed qualified, and the comparison
normalised the existing entry under the wrong registry, so it was not recognised
as the same provider and was left in place next to the new one. The project was
left with two conflicting constraints for one provider.

The manager now derives its registry from the `targetVersions` of the very file
it manages, which is the same source of truth open-constructs#445 used, and needs no plumbing
from callers. The host is dropped only when it matches that registry, so a
source explicitly qualified with another registry stays qualified - OpenTofu
users have been told to fully qualify providers and that must keep working.

Terraform behaviour is unchanged: of the five new test cases, the four covering
OpenTofu fail against the previous implementation and the Terraform one passes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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
#478)

### Description

The `pip` variant of `python/provider-add-command` and
`python/provider-upgrade-command` installs `cdktn` in `beforeAll`
without awaiting the call, while the `pipenv` block above does await its
equivalent. `TestDriver.exec` returns a promise, so the first test can
run `cdktn debug` before pip has finished:

```
External Error: Could not determine cdktf/cdktn version. Please make sure you are in a directory containing a cdktn project and have all dependencies installed.
    at TestDriver.exec (test-helper.ts:171:17)
    at Object.<anonymous> (python/provider-add-command/test.ts:89:21)
✕ detects correct cdktn version (1364 ms)
Tests: 1 failed, 3 passed, 4 total
```

Later tests in the block pass once pip completes, which is why exactly
one test fails. The slower Terraform 1.5.7 matrix entry loses the race
consistently; seen on the Release runs for #445 and #475. #188 added
`--no-compile` to the same line but left it un-awaited, and upstream
terraform-cdk has the same call.

Adds the missing `await` in both files.

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 pushed a commit to jsteinich/cdk-terrain that referenced this pull request Oct 26, 2026
…ness

Three assumptions in the integration layer only hold under Terraform, so the
fixtures could not run against OpenTofu.

versions.json is keyed by fully qualified provider name, whose host is whichever
registry the fetching CLI used. Five assertions indexed those keys with a
literal `registry.terraform.io/...`, which reads `undefined` under OpenTofu.
Adds `providerVersion()` to the test helper, matching on the provider part the
way the provider-generator unit test already does, and uses it at every site.
Two of the five are in `onWindows` branches that no job runs today, since
`windows_integration` is `if: false`; they are corrected so re-enabling that job
does not reintroduce the problem. The helper still returns `undefined` for an
absent provider, so the assertions stay real guards.

`TestDriver.validate()` shelled out to a bare `terraform`. The CI image points
that symlink at DEFAULT_TERRAFORM_VERSION, so the helper validated with 1.16.1
even on the 1.5.7 matrix rows, and would have run Terraform on an OpenTofu row.
It now honours TERRAFORM_BINARY_NAME, which the workflow already exports.

Pins the two provider-command targets to OpenTofu as well. They drive provider
resolution through the CLI's own registry, so they are what would catch a
regression in the registry selection added by open-constructs#445. Their Terraform entries
restate the `tested` default so the pin does not silently drop 1.5.7; the matrix
grows by two jobs.

Also normalises path separators in the matrix builder. `jest --listTests` emits
Windows separators, so no target matched the `test/` prefix and every configured
pin tripped the "not a discovered test target" guard, making the builder
impossible to run outside CI.

Verified every provider, module and exact version the fixtures pin is present on
registry.opentofu.org, checking a known-absent provider and a nonsense path
first to confirm that endpoint 404s rather than soft-404ing like search.opentofu.org.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
so0k pushed a commit to jsteinich/cdk-terrain that referenced this pull request Oct 26, 2026
…istry

`CdktfConfigManager` stripped the literal `registry.terraform.io/` prefix and
compared existing entries via `ProviderConstraint.fromConfigEntry()` with no
registry, which defaults to Terraform. Both only hold for a Terraform project,
so open-constructs#445 left this file behind when it made provider resolution registry-aware.

In an OpenTofu project `cdktn provider upgrade random@=3.2.0` produced:

    ["hashicorp/random@=3.1.3", "registry.opentofu.org/hashicorp/random@=3.2.0"]

The prefix did not match so the source stayed qualified, and the comparison
normalised the existing entry under the wrong registry, so it was not recognised
as the same provider and was left in place next to the new one. The project was
left with two conflicting constraints for one provider.

The manager now derives its registry from the `targetVersions` of the very file
it manages, which is the same source of truth open-constructs#445 used, and needs no plumbing
from callers. The host is dropped only when it matches that registry, so a
source explicitly qualified with another registry stays qualified - OpenTofu
users have been told to fully qualify providers and that must keep working.

Terraform behaviour is unchanged: of the five new test cases, the four covering
OpenTofu fail against the previous implementation and the Terraform one passes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
so0k pushed a commit to jsteinich/cdk-terrain that referenced this pull request Oct 27, 2026
…ness

Three assumptions in the integration layer only hold under Terraform, so the
fixtures could not run against OpenTofu.

versions.json is keyed by fully qualified provider name, whose host is whichever
registry the fetching CLI used. Five assertions indexed those keys with a
literal `registry.terraform.io/...`, which reads `undefined` under OpenTofu.
Adds `providerVersion()` to the test helper, matching on the provider part the
way the provider-generator unit test already does, and uses it at every site.
Two of the five are in `onWindows` branches that no job runs today, since
`windows_integration` is `if: false`; they are corrected so re-enabling that job
does not reintroduce the problem. The helper still returns `undefined` for an
absent provider, so the assertions stay real guards.

`TestDriver.validate()` shelled out to a bare `terraform`. The CI image points
that symlink at DEFAULT_TERRAFORM_VERSION, so the helper validated with 1.16.1
even on the 1.5.7 matrix rows, and would have run Terraform on an OpenTofu row.
It now honours TERRAFORM_BINARY_NAME, which the workflow already exports.

Pins the two provider-command targets to OpenTofu as well. They drive provider
resolution through the CLI's own registry, so they are what would catch a
regression in the registry selection added by open-constructs#445. Their Terraform entries
restate the `tested` default so the pin does not silently drop 1.5.7; the matrix
grows by two jobs.

Also normalises path separators in the matrix builder. `jest --listTests` emits
Windows separators, so no target matched the `test/` prefix and every configured
pin tripped the "not a discovered test target" guard, making the builder
impossible to run outside CI.

Verified every provider, module and exact version the fixtures pin is present on
registry.opentofu.org, checking a known-absent provider and a nonsense path
first to confirm that endpoint 404s rather than soft-404ing like search.opentofu.org.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
so0k pushed a commit to jsteinich/cdk-terrain that referenced this pull request Oct 27, 2026
…istry

`CdktfConfigManager` stripped the literal `registry.terraform.io/` prefix and
compared existing entries via `ProviderConstraint.fromConfigEntry()` with no
registry, which defaults to Terraform. Both only hold for a Terraform project,
so open-constructs#445 left this file behind when it made provider resolution registry-aware.

In an OpenTofu project `cdktn provider upgrade random@=3.2.0` produced:

    ["hashicorp/random@=3.1.3", "registry.opentofu.org/hashicorp/random@=3.2.0"]

The prefix did not match so the source stayed qualified, and the comparison
normalised the existing entry under the wrong registry, so it was not recognised
as the same provider and was left in place next to the new one. The project was
left with two conflicting constraints for one provider.

The manager now derives its registry from the `targetVersions` of the very file
it manages, which is the same source of truth open-constructs#445 used, and needs no plumbing
from callers. The host is dropped only when it matches that registry, so a
source explicitly qualified with another registry stays qualified - OpenTofu
users have been told to fully qualify providers and that must keep working.

Terraform behaviour is unchanged: of the five new test cases, the four covering
OpenTofu fail against the previous implementation and the Terraform one passes.

Co-Authored-By: Claude Opus 5 <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.

3 participants