Repository navigation
fix(provider-generator): pick the module docs registry from targetVersions - #484
Conversation
so0k
left a comment
There was a problem hiding this comment.
Test-focused pass, comments inline.
For release notes: a module source with a // path outside modules/ used to render a version-less link with the // kept (…/modules/ns/name/tgt//examples/foo). It now renders …/modules/ns/name/tgt/<version>/submodules/examples/foo.
|
Re the release-notes point in the review summary: thanks. That note was out of date once the rule changed, and the PR description now has the corrected version: any |
`cdktn provider add random@=3.9.0` in an OpenTofu-only project reported
Pre-built provider does not exist for the given constraints.
and fell back to local generation, even though @cdktn/provider-random exists. The
same command in a Terraform-targeting project finds it.
`getNpmPackageName` compared the full normalized source of each entry in the
published prebuilt map against the incoming constraint. The map keys providers
without a hostname, so its entries normalize under the default Terraform
registry, while the constraint normalizes against whatever the project declares.
In an OpenTofu-only project those never matched. A prebuilt package identifies a
provider rather than a registry, so the comparison is now on namespace and name.
The existing `getNpmPackageName` block is `describe.skip` with a TODO about an
unknown URL - it mocks www.cdk.tf while the code now reads provider.json from
raw.githubusercontent.com - which is part of why this went unnoticed. The three
new cases mock the real URL and are not skipped; only the OpenTofu one fails
against the previous implementation, so Terraform behaviour and the
no-such-package path are unchanged.
Found while auditing what remained after open-constructs#479, open-constructs#489, open-constructs#484 and open-constructs#490: no
integration fixture declares `targetVersions`, so the OpenTofu matrix rows cover
the CLI being OpenTofu rather than the project targeting it. Confirmed by
pointing the fixture at `targetVersions.opentofu` and running under tofu 1.12.6.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ry (#493) Independent of #479 / #489 / #484 / #490 — branched off `main`, touches only `getNpmPackageName`. ## Symptom `cdktn provider add random@=3.9.0` in an **OpenTofu-only** project (`targetVersions: { opentofu: ... }`): ``` Checking whether pre-built provider exists for the following constraints: provider: random version : =3.9.0 Pre-built provider does not exist for the given constraints. Adding local provider registry.opentofu.org/hashicorp/random with version constraint =3.9.0 to cdktf.json ``` `@cdktn/provider-random@14.1.0` exists, and the identical command in a Terraform-targeting project finds it. So OpenTofu-only projects silently never get prebuilt providers — they always fall back to local generation, which is slower and produces different output. ## Cause `getNpmPackageName` compared full normalized sources: ```ts ProviderConstraint.fromConfigEntry(p).source.toLowerCase() === constraint.source.toLowerCase() ``` The published map keys providers *without* a hostname (`{ "random": "hashicorp/random" }`), so its entries normalize under the default Terraform registry, while `constraint` normalizes against whatever the project declares. In an OpenTofu-only project that is `registry.opentofu.org/...` versus `registry.terraform.io/...` — never equal. A prebuilt package identifies a **provider**, not a registry, so the comparison is now on namespace and name. ## Why no test caught it The existing `getNpmPackageName` block is `describe.skip`, with `// TODO rebuild these tests when final url is known` — it mocks `www.cdk.tf` while the code now reads `provider.json` from `raw.githubusercontent.com`. So this function had no live unit coverage at all. The three new cases mock the real URL and are **not** skipped. Only the OpenTofu one fails against the previous implementation; the Terraform case and the no-such-package case pass either way, so behaviour there is provably unchanged. I left the stale skipped block alone rather than widen scope. `@cdktn/cli-core`: 18 suites, 201 passed, 6 pre-existing skips. ## The gap this came from Found while auditing what remained after #479, #489, #484 and #490. **No integration fixture declares `targetVersions`**, so the OpenTofu matrix rows exercise *the CLI being OpenTofu*, not *the project targeting OpenTofu* — and every defect in this family only appears when `registryForTargetVersions` returns OpenTofu. Confirmed by pointing the fixture at `targetVersions.opentofu` and running under tofu 1.12.6; that is also how the `provider upgrade` failure fixed in #489 was found. Worth closing that gap properly with a fixture that declares an OpenTofu target. Filing separately rather than bundling it here. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
…sions open-constructs#393 made generated provider and resource docs links follow the project's declared `targetVersions`. Module links were missed and stayed hardcoded to the Terraform Registry, so an OpenTofu-only project got Terraform links and prose reading "Docs at Terraform Registry". `ModuleGenerator` had no `targetVersions` at all, so this threads it through from ConstructsMaker, and adds `moduleDocsUrl()` to the `Registry` abstraction alongside the existing provider `docsUrl()`. The OpenTofu shapes come from getDocumentationUrl.ts and router.tsx in opentofu/registry-ui, the same source as the provider paths: `/module/` singular, a v-prefixed version, and `submodule` singular - where Terraform uses `/modules/` and `submodules`. Both were checked against a rendered page, because search.opentofu.org does not 404 on a bad path: without the v prefix it quietly serves a version list instead of the module, so an HTTP check would have passed on a URL that shows the user the wrong page. Terraform output is unchanged - the existing module-generator snapshots, which cover a submodule source, pass untouched. Submodule parsing is now a split on `//` rather than a literal replace of `//modules`, so a source pointing at a subdirectory other than `modules/` also gets its version applied instead of emitting a version-less link. The generic "based on a Terraform module" wording is left alone; it names the HCL construct rather than a registry. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The new cases went into a second test file for the same module, at src/__tests__/registry.test.ts, while commons keeps its tests beside the source as src/*.test.ts and already had src/registry.test.ts covering docsUrl and registryForTargetVersions. I had checked src/__tests__/ was empty and concluded there were no registry tests, without looking where the convention actually puts them. Moves the moduleDocsUrl cases into that file as their own describe and drops the duplicate registryForTargetVersions coverage, which the existing suite already states more thoroughly. The generator-level tests stay where they are: they cover targetVersions reaching ModuleGenerator, which is a different contract from the URL shapes. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The earlier tests construct ModuleGenerator directly and pass targetVersions themselves, so they would keep passing if ConstructsMaker stopped forwarding `this.options.targetVersions` - the forwarding that was the actual fix for OpenTofu-only module links. Raised in review. This case drives ConstructsMaker.generate() with an OpenTofu-only target and a mocked module schema, and asserts the emitted search.opentofu.org link. With the forwarding reverted it fails; no network or terraform binary is needed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
From review, which asked whether `/submodules/examples/foo` is a real page. It is not. Checked against rendered pages on both registries: - `//modules/vpc-endpoints` -> Terraform `/submodules/vpc-endpoints` and OpenTofu `/submodule/vpc-endpoints` (including `/latest/`) are real submodule pages - `//examples/complete` -> what the previous commit emitted, Terraform `/submodules/examples/complete` is a 404 and OpenTofu shows an error page - the module's own versioned page is real on both So splitting on `//` swapped one broken link for another. A deep link is now emitted only for a direct child of `modules/`, the only shape either registry serves; any other `//` path - examples, nested paths, other directories - links to the module page. OpenTofu's router also takes a single path segment for submodules, so nested paths have no page there either. Tests: an examples path and a nested modules path, which the previous rule fails; the versionless OpenTofu submodule URL the reviewer asked about. Drops the two cases flagged as redundant: both-products precedence, pinned in registry.test.ts, and the local-module branch this PR does not change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
20cee82 to
4386625
Compare
|
Follow-up (non-blocking): version ranges produce broken module links.
The space also splits the Terraform links on Suggested fix for both registries: use the version only when it is an exact semver, and fall back to
Tests (test-audit, nit): the "nothing declared" and "OpenTofu only" cases in |
|
Thanks — agreed it's pre-existing on the Terraform side and needs a release note, so it's tracked separately as #526, with a fix ready to follow this PR: exact versions (with a leading |
… changes (#490) Fixes [#462](#462). Independent of #479 / #489 / #484 — branched off `main` and touches only `filterAlreadyGenerated`. ## The problem #443 made generated docs links follow the project's declared `targetVersions`. `filterAlreadyGenerated` decided freshness from the provider version map and the CDKTN version alone, so this left stale output: 1. `cdktn get` with no `targetVersions` → bindings carry `registry.terraform.io` links 2. set `targetVersions: { opentofu: ... }`, provider versions untouched 3. `cdktn get` → provider considered already generated, skipped `constraints.json` already stamped `targetVersions`, but as a diagnostic that was explicitly *never read back*, so the information was there and unused. ## The fix Compare the registry class, derived through `registryForTargetVersions`, and regenerate when it differs. **The class, not the raw ranges, is the correct key — and that isn't a judgement call.** I traced every use of `targetVersions` inside the generator: it is consumed *only* through `registryForTargetVersions`, for `ResourceModel.docsRegistryName` and `linkToDocs`. Nothing else. `ResourceParser` takes it but only forwards it to `ResourceModel`; it does not narrow the generated surface. So a version range moving within one class provably cannot change any generated output, and forcing a regeneration for it would be pure waste. That also means the docblock calling the field "purely informational — never read back" is now wrong on both counts, and its stated rationale ("doesn't affect the generated surface") was already misleading. Corrected. ## Tests Five cases; **the two transitions fail against the previous implementation**, the three that should keep skipping already passed: | case | expected | |---|---| | nothing changed | skip | | → OpenTofu-only | **regenerate** | | ← away from OpenTofu-only | **regenerate** | | range moves within a class | skip | | both products declared (same class as declaring nothing) | skip | They drive `filterAlreadyGenerated` directly against a hand-written `constraints.json`, so they need no network and no terraform binary. `constructs-maker` and `module-generator` suites unchanged: 27 tests, 10 snapshots. ## Scope note This also covers generated **module** links once #484 lands, since those derive from the same class. Not a dependency — the two are independent. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Fixes [#483](#483), #488 and #494. **Supersedes [#479](#479, whose commits are all included here (see below). Independent of #484, #490 and #493. ## The bug is worse than the symptom The visible failure was a stale version in `versions.json`. The actual behaviour: **`cdktn provider upgrade` in an OpenTofu project downloaded the provider from `registry.terraform.io`**, silently ignoring the product the project targets. `readProviderSchema` writes a throwaway `main.tf.json` and asks the CLI for the schema: ```ts config.terraform.required_providers![target.name] = { version: target.version, source: target.source, }; ``` A source that names a registry **pins the CLI to it**. The two commands disagreed about whether the source carried a host: - **`provider add`** passes no `constraintsToGenerate`, so `get` derives constraints from `cdktf.json` — the short `hashicorp/random`. Bare → OpenTofu uses its own registry. - **`provider upgrade`** passed `constraintsToUpdate.map((c) => new TerraformProviderConstraint(c))`, built from `ProviderConstraint.fromConfigEntry(provider)` with **no registry argument**, so normalized to `registry.terraform.io/hashicorp/random`. Hence one key per host in the same file, old one first, winning any lookup by provider name: ```json { "registry.opentofu.org/hashicorp/random": "3.1.3", "registry.terraform.io/hashicorp/random": "3.2.0" } ``` This also explains the `!explicitHostname ||` guard in `provider-generator.ts`: bare sources are the designed contract, and the upgrade path was violating it. ## Three fixes 1. **`handlers.ts`** — pass `namespace/name`, leaving resolution to the CLI. I checked all ten `new TerraformProviderConstraint(...)` sites; this was the only one handing on a normalized FQPN. 2. **`constructs-maker.ts`** — `updateVersionsFile` no longer carries a previous entry forward for a provider regenerated this run. It matched the old entry by provider part but re-inserted it under its *old* fully qualified key. Defence in depth now that the host above is stable. 3. **`local-provider-versions.ts`** — a third hardcoded `registry.terraform.io/` strip, found by tracing `DEFAULT_HOSTNAME`. This one is a *reader*: under OpenTofu, `versionForProvider("random")` returned nothing, so version detection and `provider list` came up empty. Now strips any public registry host. Plus the restored OpenTofu pin, putting the matrix back to three tofu rows. ## Verification Run against both CLIs locally, each leaving a single correctly-keyed entry: | CLI | `.gen/versions.json` | |---|---| | terraform 1.14.3 | `{ "registry.terraform.io/hashicorp/random": "3.2.0" }` | | opentofu 1.12.6 | `{ "registry.opentofu.org/hashicorp/random": "3.2.0" }` | `typescript/provider-upgrade-command` goes from `1 failed, 3 passed, 1 skipped` to `4 passed, 1 skipped` under OpenTofu, and still passes under Terraform — that path matters because this changes the source for *every* upgrade, not just OpenTofu ones. Of the four new `LocalProviderVersions` tests, the two OpenTofu cases fail against the previous implementation. `@cdktn/cli-core` 20 suites / 207 tests and `@cdktn/provider-generator` 22 tests / 10 snapshots unchanged. ## Also folded in: deploy after a provider bump, and coverage for OpenTofu-targeting projects Fixes #488 and closes #494 as well. Four more commits, from auditing what remained once the above was fixed. **`cdktn deploy` failed for every OpenTofu project after a provider version bump:** ``` Could not resolve provider hashicorp/random: locked provider registry.opentofu.org/hashicorp/random 3.6.0 does not match configured version constraint 3.6.1; must use tofu init -upgrade ``` `CdktfStack` compares the synthesized `required_providers` against `.terraform.lock.hcl` to decide whether to run `init` and whether `init -upgrade` is needed, using exact fully-qualified addresses — and it normalized every source to `registry.terraform.io`. Synthesized sources are bare in practice: generated bindings copy the source as written in cdktf.json, and published prebuilt packages ship bare too (checked `@cdktn/provider-aws` 25.7.2). OpenTofu locks a bare source under `registry.opentofu.org`, so both checks missed: `init` ran on every deploy, and a bump never triggered `init -upgrade`, which the plain `init` then rejected. `lockAddressesFor` now offers every public registry for a bare source, while an explicit host still has to match exactly. Projects that fully qualify providers were never affected. **`typescript/opentofu-target`** is the first fixture that *declares* an OpenTofu target. The other OpenTofu matrix rows only change which CLI runs, so nothing exercised registry resolution that follows `targetVersions` — which is where this whole run of defects lived. It walks get → synth → deploy → `provider list` → `provider upgrade` → redeploy, pinned to `tofu1.12.6`. Reverting the lock fix makes its bump step fail with the error above. **Two verification-tooling fixes**, both found because they produced misleading local results: - `run-against-dist.mjs` installs the CLI from its tarball instead of `pnpm add cdktn-cli`, which could resolve an earlier build of the same `0.0.0` version (#488). The glob is `cdktn-cli-[0-9]*.tgz`, so release builds with stamped versions still work and `cdktn-cli-core-*.tgz` is excluded. - `package:js|python|java|dotnet|go` now depend on `build`. They had no target defaults, so they repacked whatever compiled output was lying around — a fresh tarball timestamp over stale contents. No CI workflow invokes them. Matrix +1 job. `@cdktn/cli-core` passes; the fixture and the Terraform `diff-deploy-output-destroy` fixture both pass locally against tarballs extracted and checked for these changes. ## Now also carries #479, plus review fixes #479 is folded in and closed. On its own it regressed `provider upgrade` in OpenTofu-only projects: it moved `CdktfConfigManager` onto the project's registry while the upgrade handler still normalized against Terraform, so an installed provider looked missing. The handler fix and the regression test both live here, so the two now land together. Fixes from review: - **Lock matching (revised after a second review).** An earlier fix here made a bare source match only the *running* CLI's registry, identified with `terraform version` before every init. That was reverted (b668906). `init` always runs, so nothing was ever skipped, and OpenTofu migrates a Terraform lock entry on its own. The filter broke the realistic case: after deploying with Terraform, a provider bump under OpenTofu got no `-upgrade` and `tofu init` failed, as reproduced with Terraform 1.14.3 and OpenTofu 1.12.6. It also leaked `terraform version` output into every diff/deploy/destroy log. A bare source now matches its lock entry under either public registry; an explicit host still matches only itself. The decisions stay in `needsLockfileUpdate` / `needsUpgrade`, and the tests cover the Terraform→OpenTofu bump. - **`provider upgrade` keeps explicitly named hosts.** Schema generation had dropped every host. `ProviderConstraint.requiredProvidersSource` keeps a public or private host the user wrote and drops only one added by normalization. Writing the upgraded entry back to cdktf.json had the same problem, found by sakul-learning's scenario runner (368efb3): an update now keeps the entry's own spelling of its source and changes only the version. - **Matrix comment** for the provider-command OpenTofu pins now says they exercise the CLI, not a project that targets OpenTofu. Each new test was mutation-checked against the reviewed behaviour. Locally, against tarballs checked for these changes: `opentofu-target` passes 6/6 under tofu 1.12.6, `diff-deploy-output-destroy` passes 5/5 under Terraform, and cli-core passes with lint clean. ## Worth knowing about the verification Two earlier "verification" runs of this fix were **invalid**, and both looked like real failures: - `nx run-many -t package` reported success while serving a two-week-old cached `cdktn-cli` tgz. - After rebuilding with `--skip-nx-cache`, `run-against-dist.mjs` still installed an older CLI, because it does `pnpm add cdktn-cli` and the version is permanently `0.0.0`. Filed as [#488](#488) with a fix. Anyone reproducing this locally should confirm the *installed* CLI contains their change — and grep the shipped `*.js.map`, since esbuild strips comments and minifies the bundle. ## Release notes - **Behaviour change:** in a project whose `targetVersions` declares only OpenTofu, `provider add` / `provider upgrade` write providers to `cdktf.json` in the short form (`hashicorp/random`) rather than with `registry.terraform.io/`. An existing entry keeps the host it was written with. - **Behaviour change:** the lock-file checks before `init` match a bare provider source against a lock entry from either public registry. A provider bump after switching between Terraform and OpenTofu now runs `init -upgrade`. - **Known limitation (OpenTofu 1.12 and later, not caused by this PR):** a provider whose source names `registry.terraform.io` explicitly fails to deploy under OpenTofu. `tofu providers lock` adds a second, hash-less `registry.opentofu.org` entry, and `apply` then fails with `there is no package for registry.opentofu.org/... cached in .terraform/providers`. Plain HCL fails the same way (`tofu init`, `tofu providers lock -platform=linux_amd64`, `tofu apply`); OpenTofu 1.8.2 and 1.9.3 work. Workaround: use the bare `hashicorp/x` source, or run `tofu init` again in the stack directory. - Follow-up: #532 (an explicit host is still dropped from the regenerated binding and from object-form entries on upgrade). 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
🤖 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>
Closes #480.
#393 made generated provider and resource docs links follow the project's declared
targetVersions. Module links were missed and stayed hardcoded, so an OpenTofu-only project got Terraform Registry links and prose reading "Docs at Terraform Registry".Changes
ModuleGeneratorhad notargetVersionsat all — threaded through fromConstructsMaker.moduleDocsUrl()added to theRegistryabstraction, beside the existing providerdocsUrl()./modules/{ns}/{name}/{tgt}/{ver}/module/{ns}/{name}/{tgt}/**v**{ver}.../{ver}/submodules/{sub}.../v{ver}/submodule/{sub}(singular)How the OpenTofu shapes were established
From
getDocumentationUrl.tsandrouter.tsxinopentofu/registry-ui— the same source as the provider paths in #393:Then confirmed against rendered pages, which matters here:
search.opentofu.orgdoes not 404 on a bad path. Without thevprefix it quietly serves a version list instead of the module —/module/terraform-aws-modules/iam/aws/v3.12.0→ "Viewing version v3.12.0" ✅/module/terraform-aws-modules/iam/aws/3.12.0→ falls back to a version index ❌/module/terraform-aws-modules/iam/aws/v3.12.0/submodule/iam-account→ "Submodule: iam-account" ✅An HTTP status check would have reported the broken URL as fine. This is the same soft-404 trap noted in #480 and #444.
Verification
module-generatorsnapshots — 9 tests, 10 snapshots, including a//modules/submodule source — pass untouched.Registryunit tests for both registries, pinned and latest, submodule and not../modulestill rendering "Source at". These driveModuleGeneratordirectly, so unlike the existing module tests they need no network or terraform binary.@cdktn/commons101 tests pass;@cdktn/provider-generatorbuilds clean.Note on a behaviour change
For release notes: a module source whose
//path is not a direct child ofmodules/(for example//examples/fooor//modules/x/y) used to render a version-less link with the//kept (…/modules/ns/name/tgt//examples/foo), which neither registry serves. It now links to the module's own versioned page. Deep links are emitted only for//modules/<name>, the one shape both registries serve as a page; checked against rendered pages,/submodules/examples/completeis a 404 on the Terraform Registry and an error page on OpenTofu.//modules/<name>sources render exactly as before.The generic "based on a Terraform module" wording is deliberately left alone — it names the HCL construct, not a registry.
🤖 Generated with Claude Code