Skip to content

fix(provider-generator): pick the docs registry from targetVersions - #443

Merged
jsteinich merged 2 commits into
open-constructs:mainfrom
jsteinich:feat/registry-from-target-versions
Sep 23, 2026
Merged

jsteinich merged 2 commits into
open-constructs:mainfrom
jsteinich:feat/registry-from-target-versions

Conversation

@jsteinich

Copy link
Copy Markdown
Contributor

Related issue

Fixes #393. Introduces the registry mechanism that #208 will reuse.

Description

ResourceModel.linkToDocs built documentation links from the FQPN hostname, which comes from whichever CLI fetched the schema. Under OpenTofu that produced registry.opentofu.org/providers/.../docs links, which return a hard 404 — that host does not serve the Terraform registry's path shape.

The deeper defect was that the host came from the fetching CLI at all: the same provider at the same version produced different generated output depending on whether cdktn get ran under terraform or tofu. Now that both run in CI, that is a real problem. The project's declared targetVersions is the stable signal, so linkToDocs keys off that instead.

declared targetVersions registry
opentofu only OpenTofu
terraform only Terraform
both Terraform
nothing declared Terraform (DEFAULT_TARGET_VERSIONS declares both)

The undefined case matters most, since it is what most projects hit: it resolves to Terraform, deterministically, regardless of CLI. That is why no snapshots move — every fixture in the suite has undefined targets.

The registries are not a hostname swap

Taken from getDocumentationUrl.ts in opentofu/registry-ui — the registry's own URL builder — and each shape confirmed against a live render rather than an HTTP status, because both registry front-ends soft-404 (they return 200 for nonsense paths, so curl proves nothing):

Terraform OpenTofu
host registry.terraform.io search.opentofu.org
path /providers/{ns}/{n}/{v}/docs /provider/{ns}/{n}/**v**{v}/docs
data sources data-sources/ datasources/
ephemeral ephemeral-resources/ no route

The v prefix is required — the unprefixed form renders "Page Not Found".

OpenTofu's registry has no ephemeral-resource page (opentofu/registry-ui#348), so those fall back to the provider page: it always resolves, keeps one host per project, and upgrades cleanly if upstream adds the route.

Why the helper lives in @cdktn/commons

@cdktn/cli-core needs the same mapping for #208 — both the provider-browse URL in its "could not find a version" error text, and the hostname it resolves providers from. Commons is depended on by both packages and already owns the targetVersions type re-export.

Testing

  • New registry.test.ts: 7 tests covering the selection rule for all four target shapes and the URL builders for both registries, including the ephemeral fallback.
  • @cdktn/provider-generator: 23 suites / 117 tests / 101 snapshots, none changed.

The URL shapes were verified by rendering each in a browser — …/docs/resources/password renders "random_password Resource", …/docs/datasources/file renders "local_file Data Source", …/docs/functions/rfc3339_parse renders the function page, while …/docs/data-sources/file errors.

Follow-up

An issue to revisit the ephemeral fallback once opentofu/registry-ui#348 lands.

Note search.opentofu.org self-describes as a beta preview of the OpenTofu Registry Search, so this URL shape sits on a surface that could still move.

🤖 Generated with Claude Code

Generated bindings built their documentation links from the FQPN hostname,
which comes from whichever CLI fetched the schema. Under OpenTofu that
produced registry.opentofu.org/providers/... links, which 404 - that host
does not serve the Terraform registry's path shape.

Deriving the host from the *fetching CLI* was the real defect: the same
provider at the same version generated different output depending on whether
`cdktn get` ran under terraform or tofu. The project's declared targets are
the stable signal, so linkToDocs now keys off those.

OpenTofu is used only when it is the sole declared product. Declaring both,
or declaring nothing (DEFAULT_TARGET_VERSIONS declares both), gives the
Terraform registry - so the default case is deterministic and output no
longer depends on the CLI. That is why no snapshots move.

The two registries are not a hostname swap. Confirmed against
getDocumentationUrl.ts in opentofu/registry-ui and checked against live
renders, OpenTofu uses /provider/ singular, a v-prefixed version, and
"datasources" unhyphenated. It has no ephemeral-resource route yet
(opentofu/registry-ui#348), so those fall back to the provider page.

The registry identity lives in @cdktn/commons because @cdktn/cli-core needs
the same mapping for open-constructs#208 - the provider-browse URL in its error text and the
hostname it resolves providers from.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@jsteinich
jsteinich force-pushed the feat/registry-from-target-versions branch from dcd575a to dc5ec7a Compare September 19, 2026 14:48
jsteinich added a commit to jsteinich/cdk-terrain that referenced this pull request Sep 19, 2026
Re-lands open-constructs#439 with the fixtures actually fixed. That PR passed vacuously -
its diff touched only pr-unit.yml, so `nx affected` resolved to no
terraform-tagged projects and the tofu job ran zero tests. It then failed on
every PR that touched one, and was reverted in open-constructs#446.

This branch touches provider-generator and provider-schema, so the tofu job
runs for real on its own PR.

Four things had to change, in two classes.

Unsigned providers OpenTofu rejects:

- versions-file.test.ts pinned kreuzwerker/docker 2.16.0 and provider.test.ts
  pinned 3.0.2. Both move to 3.9.0; signing starts at 3.7.0, not at the 3.x
  major (open-constructs#440).
- provider.test.ts used andsafe-AG/bitbucket, which exists on the Terraform
  registry but not on OpenTofu's. It 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 rather
than anything about the code under test:

- provider-schema.test.ts snapshots the raw schema. Its sanitizer already
  stubs format_version, cli_name and cli_version for this exact reason;
  provider_schemas and provider_versions keys now get the same treatment.
  Keys only - a host inside provider documentation text is identical either
  way and stays as authored.
- versions-file.test.ts asserted keys equal to `registry.terraform.io/${fqn}`;
  it now compares the provider part.
- provider.test.ts snapshots a generated directory including versions.json;
  FQPN keys are normalized there too.

Verified against both CLIs rather than assumed: @cdktn/provider-generator
23 suites / 117 tests / 101 snapshots and @cdktn/provider-schema 6 suites /
58 tests / 10 snapshots pass under terraform and under OpenTofu 1.12.6.

The generated docs links that also differed by CLI are fixed by open-constructs#443, which
this is stacked on.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@jsteinich
jsteinich marked this pull request as ready for review September 23, 2026 01:41
@jsteinich
jsteinich requested a review from a team as a code owner September 23, 2026 01:41
Comment thread packages/@cdktn/commons/src/registry.test.ts
@so0k

so0k commented Sep 23, 2026

Copy link
Copy Markdown
Contributor

i think the second finding belongs here

#447 (comment)

@sakul-learning

Copy link
Copy Markdown
Contributor

I found this while reviewing the stacked follow-up PR #445: #445

ResourceModel.linkToDocs in packages/@cdktn/provider-generator/src/get/generator/models/resource-model.ts now selects a documentation registry solely through registryForTargetVersions(this.targetVersions) and discards the hostname parsed from this.fqpn. This redirects explicitly hosted and private providers to an unrelated public registry. For example, registry.example.com/acme/custom in a Terraform-targeted project produces a registry.terraform.io/providers/acme/custom/... documentation link; an explicitly qualified OpenTofu provider in a Terraform-targeted project similarly links to Terraform documentation even though the provider source and available versions may differ.

The previous implementation preserved the FQPN hostname. I understand that simply reverting to the fetched schema hostname would reintroduce the CLI-dependent output this PR is fixing, so the source identity may need to be threaded separately: use the project-selected registry for implicit public sources, but preserve an explicitly declared or private source when constructing documentation links.

Could this include a generator-level regression test for an explicit public host and a private host? The current registry.test.ts verifies each public URL builder in isolation but does not exercise this source-selection behavior.

…issue

Review feedback on open-constructs#443. The tracking issue already exists as open-constructs#444, filed
when the fallback was introduced; this adds the pointer at the assertion that
will fail once upstream ships the route.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
jsteinich added a commit to jsteinich/cdk-terrain that referenced this pull request Sep 23, 2026
Re-lands open-constructs#439 with the fixtures actually fixed. That PR passed vacuously -
its diff touched only pr-unit.yml, so `nx affected` resolved to no
terraform-tagged projects and the tofu job ran zero tests. It then failed on
every PR that touched one, and was reverted in open-constructs#446.

This branch touches provider-generator and provider-schema, so the tofu job
runs for real on its own PR.

Four things had to change, in two classes.

Unsigned providers OpenTofu rejects:

- versions-file.test.ts pinned kreuzwerker/docker 2.16.0 and provider.test.ts
  pinned 3.0.2. Both move to 3.9.0; signing starts at 3.7.0, not at the 3.x
  major (open-constructs#440).
- provider.test.ts used andsafe-AG/bitbucket, which exists on the Terraform
  registry but not on OpenTofu's. It 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 rather
than anything about the code under test:

- provider-schema.test.ts snapshots the raw schema. Its sanitizer already
  stubs format_version, cli_name and cli_version for this exact reason;
  provider_schemas and provider_versions keys now get the same treatment.
  Keys only - a host inside provider documentation text is identical either
  way and stays as authored.
- versions-file.test.ts asserted keys equal to `registry.terraform.io/${fqn}`;
  it now compares the provider part.
- provider.test.ts snapshots a generated directory including versions.json;
  FQPN keys are normalized there too.

Verified against both CLIs rather than assumed: @cdktn/provider-generator
23 suites / 117 tests / 101 snapshots and @cdktn/provider-schema 6 suites /
58 tests / 10 snapshots pass under terraform and under OpenTofu 1.12.6.

The generated docs links that also differed by CLI are fixed by open-constructs#443, which
this is stacked on.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@jsteinich
jsteinich merged commit 00f2884 into open-constructs:main Sep 23, 2026
260 checks passed
@jsteinich
jsteinich deleted the feat/registry-from-target-versions branch September 23, 2026 12:58
jsteinich added a commit to jsteinich/cdk-terrain that referenced this pull request Sep 23, 2026
Re-lands open-constructs#439 with the fixtures actually fixed. That PR passed vacuously -
its diff touched only pr-unit.yml, so `nx affected` resolved to no
terraform-tagged projects and the tofu job ran zero tests. It then failed on
every PR that touched one, and was reverted in open-constructs#446.

This branch touches provider-generator and provider-schema, so the tofu job
runs for real on its own PR.

Four things had to change, in two classes.

Unsigned providers OpenTofu rejects:

- versions-file.test.ts pinned kreuzwerker/docker 2.16.0 and provider.test.ts
  pinned 3.0.2. Both move to 3.9.0; signing starts at 3.7.0, not at the 3.x
  major (open-constructs#440).
- provider.test.ts used andsafe-AG/bitbucket, which exists on the Terraform
  registry but not on OpenTofu's. It 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 rather
than anything about the code under test:

- provider-schema.test.ts snapshots the raw schema. Its sanitizer already
  stubs format_version, cli_name and cli_version for this exact reason;
  provider_schemas and provider_versions keys now get the same treatment.
  Keys only - a host inside provider documentation text is identical either
  way and stays as authored.
- versions-file.test.ts asserted keys equal to `registry.terraform.io/${fqn}`;
  it now compares the provider part.
- provider.test.ts snapshots a generated directory including versions.json;
  FQPN keys are normalized there too.

Verified against both CLIs rather than assumed: @cdktn/provider-generator
23 suites / 117 tests / 101 snapshots and @cdktn/provider-schema 6 suites /
58 tests / 10 snapshots pass under terraform and under OpenTofu 1.12.6.

The generated docs links that also differed by CLI are fixed by open-constructs#443, which
this is stacked on.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
jsteinich added a commit to jsteinich/cdk-terrain that referenced this pull request Sep 23, 2026
…nts at

Review finding on open-constructs#447. open-constructs#443 made linkToDocs follow the project's declared
targetVersions, but the prose around it stayed hardcoded: an OpenTofu-only
project got "Refer to the Terraform Registry for docs" above a
search.opentofu.org link, and "Docs at Terraform Registry" on every struct
attribute.

The registry now carries its own display name, and ResourceModel exposes it
beside the link it already derives, so the two cannot disagree. Taking the
abstraction option rather than neutral wording keeps the text specific -
readers of an OpenTofu project see "OpenTofu Registry", not "provider
documentation".

No snapshots move: every fixture declares no targetVersions, which resolves
to Terraform, so the emitted text is unchanged. provider-generator stays at
23 suites / 117 tests / 101 snapshots. A unit test covers the OpenTofu branch
that no fixture exercises, asserting the name and the link host together.

Not changed: module-generator's "Docs at Terraform Registry" for registry
modules. That one hardcodes its own registry.terraform.io module URL, so it
is self-consistent; module sources are a separate question from provider
docs.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

provider-generator: generated docs links use the fetching CLI's registry hostname, producing 404s under OpenTofu

3 participants