refactor: rewrite library#1
Conversation
|
| GitGuardian id | GitGuardian status | Secret | Commit | Filename | |
|---|---|---|---|---|---|
| 12334303 | Triggered | Generic Password | 68ec9d1 | example/name-conflict/domain/user/user_repository.go | View secret |
🛠 Guidelines to remediate hardcoded secrets
- Understand the implications of revoking this secret by investigating where it is used in your code.
- Replace and store your secret safely. Learn here the best practices.
- Revoke and rotate this secret.
- If possible, rewrite git history. Rewriting git history is not a trivial act. You might completely break other contributing developers' workflow and you risk accidentally deleting legitimate data.
To avoid such incidents in the future consider
- following these best practices for managing and storing secrets including API keys and other credentials
- install secret detection on pre-commit to catch secret before it leaves your machine and ease remediation.
🦉 GitGuardian detects secrets in your source code to help developers and security teams secure the modern development process. You are seeing this because you or someone else with access to this repository has authorized GitGuardian to scan your pull request.
There was a problem hiding this comment.
Pull request overview
This PR rewrites injector’s core pipeline, replacing the previous scan/resolve/gen/workspace implementation with a new scan → plan → emit architecture plus a new CLI, and updates docs/examples accordingly.
Changes:
- Introduces new
internal/{scan,plan,emit,packages,ir,diag}packages and removes the legacyworkspace/,resolve/, andgen/implementations. - Reworks tag/directive parsing (
inject:"..."+//injector:container) and updates examples/README to match the new DSL. - Adds CI/lint configuration and updates Makefile targets (unit + e2e regeneration).
Reviewed changes
Copilot reviewed 68 out of 68 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| main.go | Switches CLI entry wiring to new cli.New and passes args without argv[0]. |
| internal/workspace/loader.go | Removes legacy workspace loader. |
| internal/scan/tags.go | Removes legacy inject tag parser. |
| internal/scan/tag.go | Adds new inject:"..." tag parsing (marker/with/arg/returns). |
| internal/scan/tag_test.go | Adds tests for new tag parsing. |
| internal/scan/strings.go | Removes legacy directive parsing helpers. |
| internal/scan/providers.go | Removes legacy provider collection. |
| internal/scan/provider.go | Adds provider scanning producing ir.Provider. |
| internal/scan/provider_test.go | Adds tests for provider scanning behavior. |
| internal/scan/directive.go | Adds //injector:container directive parser. |
| internal/scan/directive_test.go | Adds tests for directive parsing. |
| internal/scan/containers.go | Removes legacy container scanning. |
| internal/scan/container.go | Adds new container scanning producing ir.Container + diags. |
| internal/scan/container_test.go | Adds tests for container scanning and roles/returns/directive. |
| internal/resolve/types.go | Removes legacy resolve graph types. |
| internal/resolve/order.go | Removes legacy topological ordering. |
| internal/resolve/graph.go | Removes legacy graph resolution logic. |
| internal/resolve/embedded.go | Removes legacy embedded container handling. |
| internal/resolve/convert.go | Removes legacy scan→resolve conversion helpers. |
| internal/prints/fmt.go | Removes legacy print wrappers. |
| internal/plan/plan.go | Adds planner/resolver producing an execution plan from scan+index. |
| internal/plan/plan_test.go | Adds tests for planning (errors, overrides, returns, must modes). |
| internal/plan/lookup.go | Adds provider index for lookup by type and textual ref. |
| internal/plan/lookup_test.go | Adds tests for index lookups and formatting. |
| internal/packages/packages.go | Adds wrapper around x/tools/go/packages (tags/tests/mode). |
| internal/packages/packages_test.go | Adds minimal test for packages loader input validation. |
| internal/ir/types.go | Adds new IR boundary types for scan/plan/emit. |
| internal/ir/types_test.go | Adds test for ProviderRef.HasRef. |
| internal/gen/emitter.go | Removes legacy code emitter implementation. |
| internal/emit/emit.go | Adds new emitter for plan.Plan → formatted Go source. |
| internal/emit/emit_test.go | Adds golden-ish tests for generated output and compilation. |
| internal/emit/imports.go | Adds import tracker / type qualification utilities. |
| internal/emit/imports_test.go | Adds tests for aliasing and type import collection. |
| internal/diag/diag.go | Adds diagnostics model with severity + hints + formatting. |
| internal/diag/diag_test.go | Adds tests for diagnostics formatting and helpers. |
| internal/config/config.go | Removes legacy must/on-error config (panic/fatal strategies). |
| internal/cli/generate.go | Removes legacy subcommand-based CLI implementation. |
| internal/cli/app.go | Removes legacy CLI App implementation. |
| internal/cli/cli.go | Adds new single-command CLI driving scan/plan/emit end-to-end. |
| internal/cli/cli_test.go | Adds tests for CLI help/version/unknown flag/no-args behavior. |
| example/with-error/main.go | Updates example to new with= syntax and must constructor usage. |
| example/with-error/injector_gen.go | Adds MustNewContainer to generated output. |
| example/with-error/go.mod | Removes per-example module file. |
| example/with-error/config/database_config.go | Adds gosec lint suppression comments for example DSNs. |
| example/simple/main.go | Updates example to new with= syntax and main entrypoint. |
| example/simple/go.mod | Removes per-example module file. |
| example/simple/config/database_config.go | Adds gosec lint suppression comments for example DSNs. |
| example/returns/main.go | Updates returns example to new returns usage + directive naming. |
| example/name-conflict/main.go | Updates example to new with= syntax. |
| example/name-conflict/go.mod | Removes per-example module file. |
| example/name-conflict/domain/user/user_service.go | Wraps repository errors with context. |
| example/name-conflict/domain/task/task_service.go | Wraps repository errors with context. |
| example/name-conflict/config/database_config.go | Adds gosec lint suppression comments for example DSNs. |
| example/embedded/main.go | Updates example from embedded container param to explicit arg DB input. |
| example/embedded/injector_gen.go | Updates generated constructor signature/imports to match arg input. |
| example/embedded/di/infra.go | Removes legacy override tag in Infra container. |
| example/embedded/config/database_config.go | Adds gosec lint suppression comments for example DSNs. |
| example/embedded-error/main.go | Updates embedded-error example to MustNew* flow and arg input. |
| example/embedded-error/injector_gen.go | Updates generated constructors + adds MustNewApp. |
| example/embedded-error/di/injector_gen.go | Adds MustNewInfra wrapper. |
| example/embedded-error/di/infra.go | Removes legacy override tag in Infra container. |
| example/embedded-error/config/database_config.go | Adds gosec lint suppression comments for example DSNs. |
| README.md | Rewrites documentation for new tags/directive/CLI and new resolution rules. |
| Makefile | Adds unit vs e2e test targets and example regeneration checks. |
| .golangci.yaml | Adds golangci-lint configuration (formatters + broad linter set). |
| .github/workflows/ci.yml | Adds CI workflow (lint + test). |
| .github/workflows/release.yml | Adds release workflow using goreleaser. |
| .github/FUNDING.yaml | Adds GitHub Sponsors configuration. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // NewIndex builds an Index from the given providers. Each entry is copied | ||
| // into the index so that pointer identity is preserved across lookups. | ||
| func NewIndex(providers []ir.Provider) *Index { | ||
| idx := &Index{ | ||
| byFull: make(map[string]*ir.Provider, len(providers)), | ||
| byShort: make(map[string][]*ir.Provider), | ||
| byType: make(map[string][]*ir.Provider), | ||
| } | ||
| for i := range providers { | ||
| p := &providers[i] | ||
| idx.all = append(idx.all, p) | ||
|
|
| // collectPkgs walks t and returns each named-type package it references. | ||
| func collectPkgs(t types.Type) []*types.Package { | ||
| var out []*types.Package | ||
| seen := map[*types.Package]struct{}{} | ||
|
|
||
| var walk func(types.Type) | ||
| walk = func(t types.Type) { | ||
| switch tt := t.(type) { | ||
| case nil: | ||
| return | ||
| case *types.Named: | ||
| if obj := tt.Obj(); obj != nil { | ||
| if pkg := obj.Pkg(); pkg != nil { | ||
| if _, ok := seen[pkg]; !ok { | ||
| seen[pkg] = struct{}{} | ||
| out = append(out, pkg) | ||
| } | ||
| } | ||
| } | ||
| if ta := tt.TypeArgs(); ta != nil { | ||
| for arg := range ta.Types() { | ||
| walk(arg) | ||
| } | ||
| } | ||
| case *types.Pointer: | ||
| walk(tt.Elem()) | ||
| case *types.Slice: | ||
| walk(tt.Elem()) | ||
| case *types.Array: | ||
| walk(tt.Elem()) | ||
| case *types.Map: | ||
| walk(tt.Key()) | ||
| walk(tt.Elem()) | ||
| case *types.Chan: | ||
| walk(tt.Elem()) | ||
| case *types.Signature: | ||
| if params := tt.Params(); params != nil { | ||
| for v := range params.Variables() { | ||
| walk(v.Type()) | ||
| } | ||
| } | ||
| if results := tt.Results(); results != nil { | ||
| for v := range results.Variables() { | ||
| walk(v.Type()) | ||
| } | ||
| } | ||
| } |
| func buildInputs(c ir.Container) ([]Input, []diag.Diag) { | ||
| var inputs []Input | ||
| var diags []diag.Diag | ||
| seen := map[string]token.Position{} | ||
| for _, f := range c.Fields { | ||
| if f.Role != ir.RoleArg { | ||
| continue | ||
| } | ||
| name := f.ArgName | ||
| if name == "" { | ||
| name = deriveInputName(f.Type) | ||
| } | ||
| tk := TypeKey(f.Type) | ||
| if prev, ok := seen[tk]; ok { | ||
| diags = append(diags, diag.Errorf(f.Pos, | ||
| "duplicate input type %s (first declared at %s)", TypeString(f.Type), prev)) | ||
| continue | ||
| } | ||
| seen[tk] = f.Pos | ||
| inputs = append(inputs, Input{Name: name, Type: f.Type}) | ||
| } |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 68 out of 68 changed files in this pull request and generated 1 comment.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
|
||
| outDir := filepath.Dir(group.containers[0].Pos.Filename) | ||
| outPath := filepath.Join(outDir, outputFile) | ||
| if err := os.WriteFile(outPath, out, 0o600); err != nil { |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 68 out of 68 changed files in this pull request and generated 4 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| func writeContainer(w io.Writer, im *Imports, p plan.Plan) error { | ||
| name := p.ConstructorName | ||
| structName := p.Container.StructName | ||
| paramSig := formatParams(im, p.Inputs) | ||
| retSig := im.QualifyType(p.ReturnType) | ||
|
|
| writeStructLiteral(w, p) | ||
| if p.ReturnsError { | ||
| fmt.Fprint(w, ", nil") | ||
| } | ||
| fmt.Fprint(w, "\n}\n\n") | ||
|
|
||
| if p.EmitMust { | ||
| writeMustVariant(w, im, p) | ||
| } | ||
| return nil | ||
| } | ||
|
|
||
| func writeSteps(w io.Writer, im *Imports, p plan.Plan) error { | ||
| for _, s := range p.Steps { | ||
| switch s.Kind { | ||
| case plan.StepKindInput: | ||
| // The input is already a function parameter — nothing to emit. | ||
| case plan.StepKindProvider: | ||
| if s.Provider == nil { | ||
| return fmt.Errorf("provider step %q has nil Provider", s.VarName) | ||
| } | ||
| args := make([]string, 0, len(s.ArgSteps)) | ||
| for _, idx := range s.ArgSteps { | ||
| args = append(args, p.Steps[idx].VarName) | ||
| } | ||
| call := im.QualifyProvider(s.Provider) | ||
| if s.Provider.ReturnsError { | ||
| fmt.Fprintf(w, "\t%s, err := %s(%s)\n", s.VarName, call, strings.Join(args, ", ")) | ||
| fmt.Fprint(w, "\tif err != nil {\n") | ||
| fmt.Fprint(w, "\t\treturn nil, err\n") | ||
| fmt.Fprint(w, "\t}\n") | ||
| } else { | ||
| fmt.Fprintf(w, "\t%s := %s(%s)\n", s.VarName, call, strings.Join(args, ", ")) | ||
| } | ||
| } | ||
| } | ||
| return nil | ||
| } | ||
|
|
||
| func writeStructLiteral(w io.Writer, p plan.Plan) { | ||
| fmt.Fprintf(w, "\n\treturn &%s{\n", p.Container.StructName) | ||
| for _, o := range p.Outputs { | ||
| v := p.Steps[o.StepIndex].VarName | ||
| fmt.Fprintf(w, "\t\t%s: %s,\n", o.FieldName, v) | ||
| } | ||
| fmt.Fprint(w, "\t}") | ||
| } |
| if s.Provider.ReturnsError { | ||
| fmt.Fprintf(w, "\t%s, err := %s(%s)\n", s.VarName, call, strings.Join(args, ", ")) | ||
| fmt.Fprint(w, "\tif err != nil {\n") | ||
| fmt.Fprint(w, "\t\treturn nil, err\n") | ||
| fmt.Fprint(w, "\t}\n") |
| var outputs []Output | ||
| for _, f := range c.Fields { | ||
| if f.Role != ir.RoleOut { | ||
| continue | ||
| } | ||
| stepIdx, ds := r.resolveField(f) | ||
| diags = append(diags, ds...) | ||
| if stepIdx < 0 { | ||
| continue | ||
| } | ||
| outputs = append(outputs, Output{FieldName: f.Name, StepIndex: stepIdx}) | ||
| } |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 68 out of 68 changed files in this pull request and generated 3 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // stripDirectivePrefix matches "injector:container" exactly or followed by | ||
| // whitespace, returning the trailing arguments. | ||
| func stripDirectivePrefix(body string) (string, bool) { | ||
| if body == directiveTag { | ||
| return "", true | ||
| } | ||
| rest, ok := strings.CutPrefix(body, directiveTag+" ") | ||
| if !ok { | ||
| return "", false | ||
| } | ||
| return strings.TrimSpace(rest), true | ||
| } |
| // collectPkgs walks t and returns each named-type package it references. | ||
| // | ||
| // The walk descends through the type shapes that may appear in the generated | ||
| // source: pointers, slices/arrays/maps/channels, signature parameters and | ||
| // results, and named types' type arguments. It deliberately does NOT descend | ||
| // into a *types.Named's Underlying form, nor into *types.Interface methods | ||
| // or *types.Struct fields — those internals are not written verbatim into | ||
| // the generated file (only the named type itself is qualified), so adding | ||
| // their packages would cause "imported and not used" errors. If we ever | ||
| // inline anonymous interface/struct shapes into the output, those cases | ||
| // would need to be added here. | ||
| func collectPkgs(t types.Type) []*types.Package { | ||
| var out []*types.Package | ||
| seen := map[*types.Package]struct{}{} | ||
|
|
||
| var walk func(types.Type) | ||
| walk = func(t types.Type) { | ||
| if t == nil { | ||
| return | ||
| } | ||
| switch tt := t.(type) { | ||
| case *types.Named: | ||
| if obj := tt.Obj(); obj != nil { | ||
| if pkg := obj.Pkg(); pkg != nil { | ||
| if _, ok := seen[pkg]; !ok { | ||
| seen[pkg] = struct{}{} | ||
| out = append(out, pkg) | ||
| } | ||
| } | ||
| } | ||
| if ta := tt.TypeArgs(); ta != nil { | ||
| for arg := range ta.Types() { | ||
| walk(arg) | ||
| } | ||
| } | ||
| case *types.Pointer: | ||
| walk(tt.Elem()) | ||
| case *types.Slice: | ||
| walk(tt.Elem()) | ||
| case *types.Array: | ||
| walk(tt.Elem()) | ||
| case *types.Map: | ||
| walk(tt.Key()) | ||
| walk(tt.Elem()) | ||
| case *types.Chan: | ||
| walk(tt.Elem()) | ||
| case *types.Signature: | ||
| if params := tt.Params(); params != nil { | ||
| for v := range params.Variables() { | ||
| walk(v.Type()) | ||
| } | ||
| } | ||
| if results := tt.Results(); results != nil { | ||
| for v := range results.Variables() { | ||
| walk(v.Type()) | ||
| } | ||
| } | ||
| } |
| all: build | ||
|
|
||
| build: | ||
| @echo "Building $(APP_NAME)..." |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 68 out of 69 changed files in this pull request and generated no new comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
No description provided.