{"id":33680920,"url":"https://github.com/preminger/goctx","last_synced_at":"2026-04-08T17:00:42.104Z","repository":{"id":321240890,"uuid":"1084368602","full_name":"preminger/goctx","owner":"preminger","description":"Command-line Go utility that automatically adds missing \"plumbing\" for `context.Context` parameters along the call-graph leading to a given function.","archived":false,"fork":false,"pushed_at":"2026-03-31T21:36:07.000Z","size":408,"stargazers_count":4,"open_issues_count":0,"forks_count":2,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-04-01T00:41:02.038Z","etag":null,"topics":["go","go-cli","go-tools","golang","golang-cli","golang-tools","open-source","static-analysis"],"latest_commit_sha":null,"homepage":"https://github.com/preminger/goctx","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/preminger.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-10-27T15:32:43.000Z","updated_at":"2026-03-31T21:31:49.000Z","dependencies_parsed_at":"2025-10-28T16:35:12.006Z","dependency_job_id":null,"html_url":"https://github.com/preminger/goctx","commit_stats":null,"previous_names":["preminger/goctx"],"tags_count":113,"template":false,"template_full_name":null,"purl":"pkg:github/preminger/goctx","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/preminger%2Fgoctx","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/preminger%2Fgoctx/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/preminger%2Fgoctx/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/preminger%2Fgoctx/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/preminger","download_url":"https://codeload.github.com/preminger/goctx/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/preminger%2Fgoctx/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31564915,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-08T14:31:17.711Z","status":"ssl_error","status_checked_at":"2026-04-08T14:31:17.202Z","response_time":54,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["go","go-cli","go-tools","golang","golang-cli","golang-tools","open-source","static-analysis"],"created_at":"2025-12-03T07:02:10.575Z","updated_at":"2026-04-08T17:00:42.082Z","avatar_url":"https://github.com/preminger.png","language":"Go","readme":"# goctx\n\n## tl;dr\n\n\"I'm in a function 10 calls deep from the closest `context.Context` object, and I suddenly find myself in need of a context; I know that calling `context.Background()` deep in my call stack is a no-no, and `context.TODO()` just kicks the can down the road. Is there a tool out there that can just automagically add all the necessary `context.Context` function/method parameter plumbing for me?\"\n\nNow there is.\n\n(It also handles existing unused context parameters - i.e., `_ context.Context` - intelligently by just renaming them instead of adding a new parameter, and also reuses existing `context.Context` parameters that are not named `ctx`, if present.)\n\n### Example\n\n#### Command-line\n\n```shell\ngoctx ./main.go:targetFunc\n```\n\n#### Before\n\n```go\npackage main\n\nimport \"context\"\n\nfunc targetFunc() {}\n\nfunc funcOne() {\n\ttargetFunc()\n}\n\nfunc funcTwo() {\n\tfuncOne()\n}\n\nfunc funcThreeA(_ context.Context) {\n\tfuncTwo()\n}\n\nfunc funcThreeB() {\n\tfuncOne()\n}\n\nfunc funcThreeC(myWeirdlyNamedCtx context.Context) {\n\tfuncTwo()\n}\n\nfunc funcFour() {\n\tfuncThreeB()\n}\n\nfunc main() {\n\tctx := context.Background()\n\tfuncFour()\n\tfuncThreeA(ctx)\n\tfuncThreeC(ctx)\n}\n```\n\n#### After\n\n```go\npackage main\n\nimport \"context\"\n\nfunc targetFunc(ctx context.Context) {}\n\nfunc funcOne(ctx context.Context) {\n\ttargetFunc(ctx)\n}\n\nfunc funcTwo(ctx context.Context) {\n\tfuncOne(ctx)\n}\n\nfunc funcThreeA(ctx context.Context) {\n\tfuncTwo(ctx)\n}\n\nfunc funcThreeB(ctx context.Context) {\n\tfuncOne(ctx)\n}\n\nfunc funcThreeC(myWeirdlyNamedCtx context.Context) {\n\tfuncTwo(myWeirdlyNamedCtx)\n}\n\nfunc funcFour(ctx context.Context) {\n\tfuncThreeB(ctx)\n}\n\nfunc main() {\n\tctx := context.Background()\n\tfuncFour(ctx)\n\tfuncThreeA(ctx)\n\tfuncThreeC(ctx)\n}\n```\n\n## Description\n\nPropagate `context.Context` through Go call graphs automatically.\n\nThis tool analyzes your Go source, ensures a `context.Context` parameter exists where needed, and propagates it through call chains by updating function signatures and call sites. It can also stop at well-defined boundaries such as `http.HandlerFunc`, deriving the context from `req.Context()`.\n\n- Input: a target function specified as `path/to/file.go:FuncName[:N]` (with optional line number).\n- Output: in-place source edits to add or thread a `context.Context` (named `ctx`) through your code.\n\n## Changelog\n\nIf you are looking for the CHANGELOG for the project, it can be found [here](./CHANGELOG.md).\n\n\u003e[!WARNING]\n\u003e Any use of `goctx` is analogous to passing the `-w` flag to tools like `gofmt`, `goimports`, etc. - in other words, `goctx` CHANGES YOUR SOURCE FILES, writing the modified files in place. (The changes are strictly additive; but they are changes nonetheless.)\n\u003e There is no built-in mechanism for undoing these changes. _Please make diligent use of version-control!_\n\n## Why\n\n- Standardize context propagation without tedious, error-prone manual edits.\n- Enforce best practices around cancellation, deadlines, and request-scoped values.\n- Make large refactors safer by relying on static analysis of call graphs.\n\n## Installation\n\nYou can install the CLI via Homebrew, with `go install`, or build from source.\n\n### Latest version using `go install`\n\n```shell\ngo install github.com/preminger/goctx/app/goctx@latest\n```\n\nThis will install a `goctx` binary in your `GOBIN` (or `$GOPATH/bin`).\n\n### Homebrew (recommended for macOS/Linux)\n\n- Option A: tap once, then install/update normally\n\n```shell\nbrew tap preminger/tap\nbrew install goctx\n# later updates\nbrew upgrade goctx\n```\n\n- Option B: install directly from the tap without adding it globally\n\n```shell\nbrew install preminger/tap/goctx\n```\n\n### Build from source (requires Go and optionally goreleaser)\n\n- Simple build:\n\n  ```shell\n  go build -o bin/goctx ./app/goctx\n  ```\n\n- Using Stave and GoReleaser (snapshot build):\n\n  ```shell\n  stave build\n  ```\n\n  Artifacts will be placed under the `dist/` directory when using GoReleaser.\n\n## Quick start\n\n- Ensure your working directory is the root of the module you want to modify (the tool defaults to `WorkDir: \".\"`).\n- Run `goctx` with a target function path of the form `path/to/file.go:FuncName[:N]`.\n\nExamples:\n\n- Add/propagate ctx into a function and its callers:\n\n```shell\ngoctx ./internal/foo/bar.go:FuncInNeedOfContext\n```\n\n- Stop propagation at an explicit boundary function:\n\n```shell\ngoctx --stop-at ./internal/foo/baz.go:FuncServingAsBoundary ./internal/foo/bar.go:FuncInNeedOfContext\n```\n\n- Stop at HTTP boundaries and derive ctx from `req.Context()` automatically:\n\n```shell\ngoctx --http ./internal/http/handler.go:ServeHTTP\n```\n\nNotes:\n\n- The optional `:N` suffix can be used if there are multiple functions with the same name in the file and you want to disambiguate by line number (N is the 1-based starting line of the function).\n- The tool will rename a blank `_` `context.Context` parameter to `ctx` in the target when needed.\n\n\u003e[!NOTE]\n\u003e goctx will NOT work unless you have a `go.mod` file.\n\u003e That's because it uses Go internals to parse your code into packages!\n\n## Usage\n\nThe CLI surface is intentionally small. Flags are also visible via `goctx --help`.\n\nUsage:\n\n  goctx [flags] \"\u003cpath/to/file.go:FuncName[:N]\u003e\"\n\nFlags:\n\n- --stop-at string\n  Optional terminating function path `path/to/file.go:FuncName[:N]` where propagation should stop.\n- --http\n  Treat HTTP handlers (`http.HandlerFunc`) as boundaries and derive `ctx` from `req.Context()`.\n\nBehavior summary:\n\n- If the target function has no `context.Context` parameter, one named `ctx` will be added.\n- All call sites to the modified function will be updated to pass `ctx` (or a derived context if required by boundaries).\n- Propagation continues along the call graph until a stopping point (explicit `--stop-at`, HTTP boundary when `--http` is set, the `main` function, or other analysis-defined limits).\n- Only files actually modified are written back to disk.\n\n## Examples in this repo\n\nThe repository contains end-to-end test inputs and golden outputs under:\n\n- pkg/goctx/testdata/input/\n- pkg/goctx/testdata/golden/\n\nYou can inspect pairs like these to understand before/after transformations:\n\n- pkg/goctx/testdata/input/e2e_ctxparam_blank_to_ctx/main.go\n- pkg/goctx/testdata/golden/e2e_ctxparam_blank_to_ctx_main_go.golden\n\nThere are additional scenarios covering propagation across multiple packages and HTTP boundaries.\n\n## Development\n\nRequirements:\n\n- Go (a recent version per go.mod)\n- [Stave](https://github.com/yaklabco/stave) build tool\n- Optional: Homebrew for developer tooling via Brewfile\n\nSetup developer tools via Homebrew:\n\n```shell\nstave init\n```\n\nLint:\n\n```shell\nstave lint\n```\n\nRun Go tests with coverage (generates coverage.out and coverage.html):\n\n```shell\nstave testGo\n```\n\nOr run lint and Go tests together:\n\n```shell\nstave test\n```\n\nRun a snapshot build using GoReleaser:\n\n```shell\nstave build\n```\n\nRun all targets (init, test, build):\n\n```shell\nstave\n```\n\n### Project layout\n\n- app/goctx: CLI entrypoint (main package), produces the `goctx` binary\n- cmd/goctx: Cobra command and flags\n- pkg/goctx: Core analysis and rewrite logic\n- pkg/goctx/testdata: End-to-end fixtures used by tests (input and golden files)\n\n## Troubleshooting\n\n- Nothing happens / no files change\n  - Ensure your target function path is correct and relative to your module root, and that you run the tool from the module root (or specify the correct working directory before running).\n  - Use the optional line suffix `:N` to pinpoint the exact function if there are duplicates.\n\n- Build breaks after propagation\n  - Run `go build ./...` and inspect errors to identify where additional manual adjustments might be needed, especially around interfaces or third-party APIs.\n\n- Undoing changes\n  - The tool writes in-place. Commit your work before running, or use your VCS to review/undo.\n\n## Contributing\n\nSee [CONTRIBUTING.md](CONTRIBUTING.md) for details.\n\n## License\n\nThis project is licensed under the terms of the Apache License, Version 2.0. See the [LICENSE](LICENSE) file for details.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpreminger%2Fgoctx","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpreminger%2Fgoctx","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpreminger%2Fgoctx/lists"}