Skip to content

feat(registry): Go module support - #126

Merged
moshest merged 5 commits into
neuledge:mainfrom
JayOfTheKeyboard:feat/go-module-support
Sep 27, 2026
Merged

moshest merged 5 commits into
neuledge:mainfrom
JayOfTheKeyboard:feat/go-module-support

Conversation

@JayOfTheKeyboard

Copy link
Copy Markdown
Contributor

What this adds

go as a registry: version discovery through proxy.golang.org, and the recursive definition walk that Go module paths require.

Three things Go does differently, each measured against the live proxy

Uppercase letters must be escaped as ! + lowercase.

https://proxy.golang.org/github.com/BurntSushi/toml/@v/list   -> 404
https://proxy.golang.org/github.com/!burnt!sushi/toml/@v/list -> 200

Slashes are path separators and have to survive, so encodeURIComponent is the wrong tool here.

The v prefix is stripped before the shared helpers see it. isPrerelease("v1.10.2") is true — it reads the leading letter as a prerelease tag — so every Go version would be silently discarded. compareSemver("v1.10.2", "v1.9.1") is NaN, so sorting breaks too. Stripping it in the fetcher and restoring it with tag_pattern: "v{version}" keeps both shared functions untouched, which is why this PR does not go near them.

/@v/list carries no publish dates. Getting them costs one /@v/<version>.info request per version, so --since filtering is unavailable for Go rather than merely slow. publishedAt is left undefined.

The blocker underneath it

listDefinitions only recursed into directories starting with @, so registry/go/github.com/spf13/cobra.yaml was never loaded — exit code 0, no warning. Every Go module path contains slashes, so this blocked the whole ecosystem rather than one package.

The general recursive walk that replaces the special case is smaller than the case it removes. Scoped npm packages still resolve, because loadDefinition already derives expectedName from the path relative to the manager directory.

Taken end to end before opening this

registry build → 56 sections / 19631 tokens → context add → context query 'github.com/spf13/cobra' 'persistent flags' returned the correct section. registry/go/github.com/spf13/cobra.yaml is included as the first Go definition.

Tests

Four new. Three on the fetcher — prefix stripping, case escaping, a module with no tagged releases — and one on a definition nested several directories deep.

Each was mutation-checked rather than just watched to pass: dropping the escaping, keeping the v prefix, and removing the recursion each turn exactly the matching test red. 45/45 green, biome clean.

One question for a maintainer

std is not on the module proxy, so the Go standard library would need an unversioned definition like python/python.yaml uses, not a go one. Not included here — it seemed like your call which shape you want.

Adds `go` as a registry: version discovery through proxy.golang.org, plus the
recursive definition walk that Go module paths require.

Three things differ from the existing fetchers, each measured against the live
proxy rather than inferred:

- Uppercase letters must be escaped as "!" + lowercase.
  github.com/BurntSushi/toml/@v/list returns 404; github.com/!burnt!sushi/toml
  returns 200. Slashes are path separators and must survive, so
  encodeURIComponent is the wrong tool.
- The "v" prefix is stripped before the shared helpers see it.
  isPrerelease("v1.10.2") is true, so every Go version would be silently
  discarded, and compareSemver("v1.10.2", "v1.9.1") is NaN, so sorting breaks.
  Definitions restore the prefix with tag_pattern "v{version}", which keeps
  both shared functions untouched.
- /@v/list carries no publish dates. Getting them costs one /@v/<version>.info
  request per version, so --since filtering is unavailable for Go rather than
  merely slow. publishedAt is left undefined.

listDefinitions only recursed into directories starting with "@", so
registry/go/github.com/spf13/cobra.yaml was never loaded — exit 0, no warning.
Every Go module path contains slashes, so this blocked the ecosystem rather
than one package. The general recursive walk that replaces the special case is
smaller than the case it removes, and scoped npm packages still resolve because
loadDefinition already derives expectedName from the path relative to the
manager directory.

registry/go/github.com/spf13/cobra.yaml is included as the first definition. It
was taken end to end before this PR: registry build produced 56 sections /
19631 tokens, context add installed it, and `context query
'github.com/spf13/cobra' 'persistent flags'` returned the right section.

Tests: four new ones. Three cover the fetcher (prefix stripping, case escaping,
a module with no tags) and one covers a definition nested several directories
deep. Each was mutation-checked — dropping the escaping, keeping the v prefix,
and removing the recursion each turn exactly the matching test red. 45/45 green,
biome clean.

One open question for a maintainer: `std` is not on the module proxy, so the Go
standard library would need an unversioned definition like python/python.yaml
rather than a `go` one. Not included here.
@changeset-bot

changeset-bot Bot commented Aug 29, 2026 •

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 11c6cad

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

moshest commented Aug 31, 2026

Copy link
Copy Markdown
Member

Thanks for this — I reviewed it closely and verified your central claims against the live proxy. The case-escaping is right (.../@v/list 404s, .../!burnt!sushi/toml/@v/list 200s), the v-stripping is right, and making listDefinitions recursive is a genuine fix with no regression: registry list goes 140 → 141 with exactly one added line, every existing definition unchanged. cobra@1.10.2 builds at 56 sections, and I confirmed that's not a wrong docs_path — cobra only has 13 markdown files.

Four things block merge. The first is serious.

1. A 4xx from the Go proxy kills the entire nightly run.

discoverVersions is called at cli.ts:228, outside the try that starts at line 235. And version-check.ts:283 aborts immediately on anything below 500 — so 404, 410 (which Go's proxy protocol uses for retracted modules) and 429 all throw straight out of the loop.

Reproduced with a bogus module plus a valid npm definition after it:

Error: Go module proxy returned 404 for github.com/nonexistent-org-xyz/nope
Node.js v22.22.2 — Exit status 1

The npm definition was never reached and no --- Summary --- printed. Definitions sort by registry/name, and go/github.com/spf13/cobra lands at #2 of 141 — so one proxy hiccup takes down the other 139 packages.

This is our fragility, not yours — discoverVersions was never guarded. I'm fixing that separately so a discovery failure is recorded per-package like a build failure. Flagging it so you know it's handled.

2. --since is silently ignored for Go. fetchGoVersions never sets publishedAt, and the filter is if (sinceDate && v.publishedAt) — so it's a no-op that includes everything, not "unavailable":

registry check github.com/spf13/cobra --since 2   → 1.10.2, 1.9.1, 1.8.1   (1.8.1 is from December)
registry check next --since 2                     → only today's versions

The nightly runs --since 2, so every Go definition re-enumerates its full range forever. The rationale in your description — one .info request per version — doesn't quite hold: dates are only needed for versions surviving dedup, which for cobra is 3, not 27. And /@v/v1.10.2.info does return "Time". Either resolve dates post-dedup, or at minimum console.warn so the gap is loud.

3. +incompatible modules discover zero versions, silently. isPrerelease is /[-+]/, so 25.0.10+incompatible is filtered as a prerelease. A definition for github.com/docker/docker with min_version: 20.10.0 gives (0 versions) and exit 0 — the whole v20.10→v28.x range invisible, no signal. Please warn when versions exist but none match, and note the limitation in registry/README.md.

4. Go submodules resolve the wrong tag and skip forever. Default tag_pattern is v{version}, but a module in a subdirectory tags as config/v1.31.1, not v1.31.1. The clone fails with "Remote branch not found", which matches the missing-ref pattern, so cli.ts:301 treats it as "tag not published yet" and skips silently — every night. That covers aws-sdk-go-v2/*, k8s.io/*, google.golang.org/*. Worth documenting with a worked tag_pattern: "<subdir>/v{version}" example.

Docs need updating. registry/README.md:87-93 still says the versions: shape "only works in npm/, pip/, maven/ and hex/" and that anything else fails with Unsupported registry: — which a contributor will now read while a working go/ definition sits in the tree. Also registry/README.md:75, the directory list in README.md:623, and README.md:664 ("npm, PyPI, and Maven Central"). Note packages/context/README.md is byte-identical and ships to npm, so it needs the same edit.

Two smaller notes: deleting .filter(line => line.startsWith("v")) leaves all 12 tests green, so that guard is untested — a non-version line in the fixture body would fix that. And the module path is interpolated raw at version-check.ts:253 while every other fetcher uses encodeURIComponent; not exploitable here since names come from readdirSync, but a ? or # in a filename would silently retarget the request.

Good contribution — the mechanism is right and the verification in your description held up. Happy to review again once these are in.


Generated by Claude Code

moshest added a commit that referenced this pull request Aug 31, 2026
)

publish-all called discoverVersions outside the try that guards the rest of
the loop, so anything it threw escaped and ended the process. Definitions are
walked in sorted order, so one third-party registry API returning 404, 410 or
429 silently abandoned every package after it and printed no summary at all.
version-check.ts aborts immediately below status 500, so there was no retry
either.

Discovery talks to an external API and fails for reasons unrelated to the
package — a renamed module, a rate limit, an upstream blip. Those now record a
failure for that definition and continue, exactly like a failed build.

Found while reviewing #126, which adds a Go registry and makes this trivially
reachable: go/github.com/spf13/cobra sorts second of 141, so one proxy hiccup
would take down the other 139. The fragility is ours and predates it.

The new tests run the real CLI against a scratch registry using a registry
with no version fetcher, so discovery throws before any network call. Both
were verified load-bearing: they fail with the guard reverted and pass with it.

@moshest moshest left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Still at cac38d9 with no new commits since my Aug 31 review, so items 2–4 are open. One update in your favour: blocker 1 landed separately as #128, so a 4xx from the proxy now records a per-package failure instead of abandoning the nightly run — that one is off your plate, and the branch still merges clean against current main. Answering your std question too: an unversioned definition in the python/python.yaml shape is what we'd want, not a go/ one.

What still blocks merge is that go/github.com/spf13/cobra.yaml ships the versions: + min_version: git shape while Go discovery silently no-ops --since and drops +incompatible releases, so it would go into the nightly for all ~140 packages with known-wrong version discovery. It's been three weeks — if I don't hear back by early October I'll close this as stale, and you're very welcome to reopen when you have time.


Generated by Claude Code

@JayOfTheKeyboard

Copy link
Copy Markdown
Contributor Author

Thanks for the patience, and for landing #128 separately.

I'm picking this back up now and will have items 2 to 4 pushed by Friday 3 October: dates resolved for the versions that survive dedup so --since works for Go, a warning plus a README note for +incompatible, and a worked tag_pattern example for submodules. The README updates and the two smaller notes (a test for the v filter and encoding the module path) will be in the same push.

If you'd rather not wait, I'm also happy to drop cobra.yaml from this PR so the fetcher can land on its own, and add the definition back in a follow-up once discovery is solid. Your call.

Thanks for the std answer too. I'll do that as a separate PR in the python/python.yaml shape.

Resolve Go publish dates from /@v/<version>.info for versions that survive
dedup, only under --since. Warn when a package has published versions but
none match its ranges, which is how Go +incompatible releases showed up
before: zero versions and exit 0. Percent-encode module path segments, and
cover the v-prefix guard with a test.

Document Go module paths, subdirectory tag patterns and the +incompatible
limitation in the registry README, and list Go in the package README.
@JayOfTheKeyboard

Copy link
Copy Markdown
Contributor Author

Pushed, with main merged in (no force-push, so your review comments still line up). Going through your list:

2. --since for Go. Dates are now looked up from /@v/<version>.info, only under --since and only for versions that survive dedup, as you suggested. Checked live: registry check github.com/spf13/cobra --since 2 gives 0 versions, --since 400 gives 1.10.2 (2025-12-03), and without --since it's still one request with the same three versions. npm and the other registries go through the same filter as before.

3. +incompatible. Discovery now warns when a package has published versions but none match its ranges. Against the live proxy with a github.com/docker/docker definition (not committed):

WARNING go/github.com/docker/docker: 347 versions published, none match the defined ranges (prereleases and build metadata such as "+incompatible" are skipped)

The warning isn't Go-specific, and it doesn't fire when --since alone empties the list, so the nightly doesn't warn for every package with no new release. There's a test for each. The limitation is in registry/README.md, which points these modules at the unversioned shape for now.

Heads up: running registry check across the whole registry, the warning fires for two existing definitions, so you'll see them in the nightly log. npm/angular asks npm about angular, which is AngularJS (latest 1.8.3), against min_version: 17.0.0. npm/strapi asks about strapi (latest 3.6.11) against min_version: 5.0.0. Both have discovered nothing since they were added. The packages they want are @angular/core (22.2.0) and @strapi/strapi (5.55.1). Happy to send that as a separate PR, or leave it to you if you'd rather handle the rename.

4. Submodule tags. Documented under a new "Go modules" section in registry/README.md, with a worked tag_pattern: "config/v{version}" example for aws-sdk-go-v2/config. I checked the tag format against the real repo (config/v1.31.9).

Docs. registry/README.md now lists go/ everywhere it listed the other four, plus a naming example for module paths. packages/context/README.md (the root README is a symlink to it) lists registry/go/ and names Hex and the Go proxy in the discovery line.

Smaller notes. There's a test for the v guard: a list line without the prefix used to be sliced into a bogus release. Module path segments are now percent-encoded after the ! escaping, and slashes are kept. The changeset is updated to match.

Each new test fails when I remove the code it covers, and pnpm lint, build and test pass (registry 103, context 227).

I'll send the std definition as a separate PR.

moshest commented Sep 26, 2026

Copy link
Copy Markdown
Member

Re-reviewed at 91cc529. I checked each fix myself. Install, lint, build and test all pass (context 227, registry 103). For cobra, registry check returns 0 versions with --since 2 and 3 without it, about 2s either way, and the three .info lookups only happen under --since. A --since 2 check across the whole registry takes 110s and exits 0. The only warnings are the angular/strapi ones you pointed out, which we'll fix on our side. The uppercase path (BurntSushi/toml) and the docker +incompatible warning both work against the live proxy. test-registry test github.com/spf13/cobra builds 1.10.2 with 56 sections and 19,631 tokens.

One thing left: please delete .changeset/spotty-carrots-invent.md. @neuledge/registry is private, so a changeset that names only that package opens a release PR that bumps it to 0.1.0 and publishes nothing. The README edit is docs-only, so it doesn't need a changeset either. Once that's gone, this looks good and I'll merge.


Generated by Claude Code

@neuledge/registry is private, so this changeset would only open a
release PR that bumps it and publishes nothing.
@JayOfTheKeyboard

Copy link
Copy Markdown
Contributor Author

Thanks for checking it all again. Removed .changeset/spotty-carrots-invent.md in 11c6cad, so there's no changeset left on this PR.

moshest added a commit that referenced this pull request Sep 26, 2026
)

npm/angular asked npm about `angular` (AngularJS, latest 1.8.3) against
min_version 17.0.0, and npm/strapi asked about `strapi` (v3, latest 3.6.11)
against min_version 5.0.0, so neither ever discovered a version or
published docs. Reported by @JayOfTheKeyboard on #126.

- npm/@angular/core: from 20.3.0 with tag_pattern v{version}; older minors
  were tagged without the "v" prefix.
- npm/@strapi/strapi: unversioned, built from strapi/documentation
  docusaurus/docs/cms. strapi/strapi's docs/ is contributor documentation.

Built locally: @angular/core 22.2.0 1956 sections, 20.3.32 1493 sections;
@strapi/strapi 1392 sections.
@moshest
moshest merged commit ad6e777 into neuledge:main Sep 27, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants