Repository navigation
refactor(runtimes): split provider files to slim shim binary #265
Description
Context
Follow-up to #262 / #264. After the -trimpath flag win, the shim is at 5.70 MB. Symbol analysis shows ~70% of that is dead weight pulled in transitively from blank-imports of the runtime providers:
| Package | Symbols in shim | Used by shim? |
|---|---|---|
klauspost/compress |
235 | No |
bodgit/sevenzip |
173 | No |
net/http |
114 | No |
ulikunitz/xz, pierrec/lz4, andybalholm/brotli, compress/*, archive/zip, crypto/tls, afero, briandowns/spinner, schollz/progressbar |
~370 combined | No |
Plus 1.3 MB of embedded JSON manifests (internal/manifest/data/*.json) |
— | No |
PR #75 narrowed the call site with the ShimProvider interface, but the linker still keeps Install/Uninstall/ListAvailable reachable because the registered concrete types (*node.Provider etc.) carry those methods. The fix has to happen at the source level: make the heavy methods physically uncompiled in the shim build.
Target
Push the shim toward ~3.0–3.5 MB (a measured floor for a stub doing only the shim's hot path is 2.3 MB).
Approach
Use Go build tags to split each runtime provider into a "shim-relevant" half and a "full" half, then build the shim with -tags shim.
Per src/runtimes/<name>/:
- Keep in
provider.go:Name,DisplayName,Shims,ExecutablePath,IsInstalled,InstallPath,ShouldReshimAfter,GetEnvironment, theProviderstruct,NewProvider, andinit(). These need onlyos,filepath,runtime,internal/config,internal/constants. - Move to a new
provider_full.gowith//go:build !shim:Install,Uninstall,ListInstalled,ListAvailable,DetectInstalled,GlobalPackages,InstallGlobalPackages,ManualPackageInstallCommand, plus all install helpers (downloadAndExtract,getDownloadURL,createShims, etc.) and the global/local/current version setters that need onlyinternal/config. Heavy imports (internal/download,internal/manifest) live only in this file.
internal/runtime/registry.go:
- Build-tag the storage type. Under
//go:build shimthe registry storesShimProvider; otherwise it storesProvider.RegisteracceptsShimProviderin shim builds andProviderin main builds (sinceProviderembedsShimProvider, no caller change needed in main builds).
internal/shim/manager.go:
RehashandRuntimeShimsreach into the fullProviderinterface viaruntime.Get. The shim binary doesn't call them. Move both to a//go:build !shimfile so they're not compiled in.
Build configuration:
- Add
-tags shimto thebuild-shimtask inrnr.yamland to the corresponding steps in.github/workflows/build.ymland.github/workflows/release.yml.
Acceptance criteria
-
go build -tags shim ./src/cmd/shimproduces a working shim binary in the 3.0–3.5 MB range on Windows amd64 -
go build ./src(the main CLI) is unchanged in behavior and size - All existing tests pass under the default (no-tag) build
- Shim runtime behavior is identical (verified via
python --version,node --version, etc. against a configured runtime, plus the "no version configured" fallback path) - Symbol audit (
go tool nm) confirmsklauspost/compress,sevenzip,net/http, etc. are no longer present in the shim binary - CI green across all five release platforms
Risk
Mostly mechanical. Watch points:
- Any code outside
cmd/shimthat calls full-Providermethods needs to live in!shimfiles so the shim build compiles. - Test files in the runtime packages may exercise full-
Providermethods; they should not need a build tag because tests aren't run with-tags shim. - The
internal/migrationpackages register full-Providertypes into their own registry; need to verify they don't get pulled into the shim transitively.
Out of scope
- Further slimming below the build-tag floor (e.g., trying to drop
internal/ui's color libraries) — separate effort if needed. - Fixing the rnr quoting bug (fix(rnr): build task fails due to YAML quoting of -ldflags #263 / upstream fix(parser): cmd string with internally-quoted args is mistokenized rnr.cli#55).
Activity