[Experiment] Migrate Bun to Go#78
Draft
travis-hoover-glean wants to merge 3 commits into
Draft
Conversation
Replace the TypeScript/bun implementation with a functionally-identical Go port, cross-compiled with the native Go toolchain. Output binary names and release URLs are unchanged so already-deployed bun binaries self-update straight into the Go binary. The MCP client registry stays sourced from @gleanwork/mcp-config-schema: a build-time Node generator (scripts/gen-registry.mjs) snapshots it into internal/registry/registry.json, which the Go binary embeds. At runtime Go only substitutes the server name/URL tokens, preserving every per-client default field verbatim.
…riven configurators Behavior-preserving cleanups to the new Go code: - Collapse configureJSON/TOML/YAMLFile into one codec-driven configureFile - Extract shared ResolveWritePath/AtomicWrite into internal/fsutil, used by hosts and configwriter - Add a fail(err) closure in updater downloadAndInstall - Derive MCP/MDM config and Windows log paths from GetDefaultConfigDir() - Unify timeout-exec helpers into internal/executil (RunWithTimeout/ErrTimeout) - Share serve/writePort between the two e2e mock servers via ci/mockutil Verified with go build, go vet, gofmt -l, and go test ./...
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
glean-mdmis built with bun (bun build --compile). We want to move off the bun toolchain to the native Go toolchain while preserving behavior exactly. A hard constraint: already-deployed bun binaries self-update by downloading the next release, so the Go binaries must keep identical output filenames and release URLs, and reproduce the runtime contract the E2E scripts assert.Solution
Functionally-identical Go rewrite in the same repo, cross-compiled with
go build(nobun --compile). Clean replacement: TypeScript sources,tsconfig,vitest, and bun lockfiles are removed.Behavior preserved (covered by the reused
ci/e2e-*.shscripts):--versionprints the bare version string.Configured JSON/TOML/YAML: <path>,Already up to date, etc.).mcp-config.jsonshape (array of{serverName, url}, 2-space indent, trailing newline) and skip/append/dedup semantics.--skip-update; launchd/systemd/schtasks scheduling.The MCP client registry (the tricky part)
The original depends on
@gleanwork/mcp-config-glean→@gleanwork/mcp-config-schema, whose builder injects per-client default fields that are not present in the raw config data (e.g. Goose YAML getsenabled,timeout,env_keys,available_tools). A hand-written Go builder would silently drift from upstream.Instead, the npm package stays the source of truth:
scripts/gen-registry.mjs(dev-only Node, pinned to@gleanwork/mcp-config-glean@4.3.0) runs the real builder and snapshots each user-configurable HTTP client intointernal/registry/registry.json.go:embedand, at runtime, only substitutes the server-name and server-URL tokens — every other per-client field is preserved verbatim.Layout
cmd/glean-mdm(cobra) +internal/{cli,config,configwriter,registry,hosts,extensions,users,updater,scheduler,uninstaller,platform,logger,jsonutil,version}. Library mapping: commander→cobra, zod→manual validation, smol-toml→go-toml/v2, yaml→yaml.v3, fetch→net/http, createHash→crypto/sha256,BUILD_VERSIONdefine→-ldflags -X .../version.BuildVersion.Build & CI
build.shcross-compiles all 5 targets with identical filenames and regeneratesversion.jsonwith sha256 checksums.setup-bunforsetup-go(+ a Node step for registry generation). The twoBun.serveE2E mock servers are rewritten as Gonet/httpprograms; allci/e2e-*.shscripts are reused. Thee2e-updatejob tolerates a pre-Gomainso it stays green on this migration PR (it then exercises the real bun→Go self-update path).Testing
go vet ./...,go test ./..., andgofmt -lare clean locally. Unit tests port the*.spec.tssuites (configurator merge/dedup/idempotency, updater version compare/shouldUpdate, config validation, registry substitution incl. Goose's embedded name, scheduler args/plist)../build.shproduces all binaries +version.json; the binary was exercised for--version,config, andrun --dry-run(10 hosts configured, JetBrains correctly skipped).sudo,/usr/local/bin, real home dirs) run on the CI matrix (ubuntu/macos/windows), not locally.Notes / risks
registry.json; bumping the schema version requires re-running the generator (gated in CI).glean_prefix unless already present) is implemented directly in Go rather than codegen — it is a small, stable rule, unit-tested, and exercised end-to-end via Goose's embeddednamefield.