codegen

package
v0.0.0-...-043add5 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 20, 2026 License: MIT Imports: 17 Imported by: 0

Documentation

Overview

Package codegen provides the shared rendering, writing, and verification pipeline for GoCell code generators.

The framework exposes three primitives:

  • Render: text/template → FormatGoSource (goimports → gofumpt) — single producer-side formatter outlet; mirrors `.golangci.yml formatters.enable: gofumpt` + `goimports.local-prefixes: github.com/ghbvf/gocell` so generated bytes match the CI gate's verdict on first try
  • Write: filesystem write with non-generated-file overwrite guard (uses governance.IsGoCellGenerated as the boundary), with DryRun and Verify modes for drift detection
  • VerifyInWorktree: K8s-style hack/lib/verify-generated.sh — runs the generator inside an ephemeral git worktree sharing the .git object store, then uses `git status --porcelain` to detect drift

Subpackages adapt the framework to a specific input source:

  • tools/codegen/cellgen — cell.yaml / slice.yaml → cell_gen.go / slice_gen.go (K#04, this PR)
  • tools/codegen/contractgen — contract.yaml → DTO/iface/errors (K#06, planned)
  • tools/codegen/markergen — cell.go marker → cell.yaml (K#05, planned)

ref: kubernetes/kubernetes hack/lib/verify-generated.sh — git worktree sandbox pattern. ref: ent/ent entc/gen/template.go — text/template + goimports.Process pipeline.

Index

Constants

View Source
const LangVersion = "go1.25"

LangVersion is the producer-side gofumpt language version. It tracks the `go` directive in go.mod (go 1.25) so gofumpt formats to the same language level the CI gate enforces.

SYNC: golangci-lint auto-injects LangVersion from the go.mod `go` directive; this literal is a second manual truth point — when the go.mod `go` directive changes, update "go1.25" here in lockstep (the .golangci.yml gofumpt comment carries the matching note).

The other formatter knob — module-local import grouping — is NOT a constant: it is the target repo's module path, supplied per-call to FormatGoSource / Render so generated files group local imports the way the *consuming* repo's golangci-lint gate expects. In the framework repo the module path resolved from go.mod equals .golangci.yml's hardcoded github.com/ghbvf/gocell, so producer output is byte-identical to the old const-based behavior; external repos developing cells supply their own module path (#1083).

Variables

View Source
var SharedTemplates = template.Must(template.ParseFS(sharedTemplateFS, "templates/*.tmpl"))

SharedTemplates is the parsed set of shared templates available to all codegen subpackages (cellgen, contractgen, markergen). It includes tools/codegen/templates/header.tmpl which defines the "header" template.

Subpackages MUST NOT embed header.tmpl themselves. Instead, clone SharedTemplates and layer subpackage-local templates on top:

tmpl := template.Must(codegen.SharedTemplates.Clone())
template.Must(tmpl.ParseFS(localFS, "templates/*.tmpl"))

Functions

func FormatGoSource

func FormatGoSource(modulePath, filename string, src []byte) ([]byte, error)

FormatGoSource normalizes Go source bytes through goimports → gofumpt and returns the canonical formatted output. It is the single producer-side formatter outlet; every codegen / scaffold path must funnel its rendered bytes through here so generated and scaffolded files match what the CI `golangci-lint` gate (.golangci.yml formatters.enable: gofumpt) enforces.

modulePath is the consuming repo's Go module path (from its go.mod); it drives both module-local import grouping (imports.LocalPrefix) and gofumpt's module-locality detection. It is a required positional argument — "format without a module path" is a compile error, not a silent framework-default fallback (#1083). Empty modulePath is rejected (fail-closed).

filename is the path goimports uses to resolve module-local imports — pass empty string when the source is not a file on disk.

Pipeline order is goimports → gofumpt: gofumpt requires its input to be canonical gofmt-shaped, and goimports.Process produces exactly that while also resolving and ordering the import block.

On failure, FormatGoSource returns the latest intermediate bytes (raw input on goimports failure, goimports output on gofumpt failure) so callers can pretty-print the offending source for debugging — these bytes MUST NOT be written to disk.

ref: mvdan.cc/gofumpt format/format.go — gopls and golangci-lint adopt the same goimports → gofumpt ordering.

func Render

func Render(modulePath string, opts RenderOptions) ([]byte, error)

Render executes a template and runs the producer formatter pipeline (goimports → gofumpt) over the output.

modulePath is the consuming repo's module path (required positional; see FormatGoSource) — it is threaded straight through to the formatter.

Two-stage pipeline (each stage gates the next):

  1. text/template.Execute renders raw source bytes
  2. FormatGoSource applies goimports + gofumpt — failure here typically means the template emitted invalid Go syntax; the raw bytes are returned with the error so callers can pretty-print the offending source for template debugging.

Returns

Returns rendered bytes on success. When the formatter pipeline fails, the returned bytes contain raw template output for debugging — callers MUST NOT write them to disk.

ref: ent/ent entc/gen/template.go — same staged-pipeline ordering.

Types

type RenderOptions

type RenderOptions struct {
	// TemplateName is the named template invoked from the parsed Templates set.
	TemplateName string
	// Templates is a parsed *template.Template containing TemplateName plus
	// any templates it references (e.g. via {{template "header" .}}).
	Templates *template.Template
	// Data is bound to the template's "." context.
	Data any
	// Filename is the absolute path of the file being rendered. goimports
	// uses it to resolve module-local imports. Empty filenames disable
	// path-aware import resolution.
	Filename string
}

RenderOptions configures a template-driven Go source render pass.

type VerifyResult

type VerifyResult struct {
	// Drifted lists the relative paths whose disk content differs from the
	// freshly-generated content (per `git status --porcelain`). Empty when
	// the worktree is clean after running the generator.
	Drifted []string
	// DiffSummary is the truncated `git diff` output for the drifted files,
	// suitable for printing to CI logs. Empty when Drifted is empty.
	DiffSummary string
}

VerifyResult reports the outcome of a worktree-sandboxed verify pass.

func VerifyInWorktree

func VerifyInWorktree(repoRoot string, generateFn func(workdir string) error) (VerifyResult, error)

VerifyInWorktree runs generateFn inside an ephemeral git worktree detached at HEAD, then reports any resulting diff as drift.

Pattern (K8s hack/lib/verify-generated.sh):

  1. `git worktree add --detach <tmp> HEAD` — shares .git object store, no history copy
  2. generateFn(tmp) — caller runs the codegen pipeline rooted at tmp
  3. `git status --porcelain` — precise diff list
  4. `git diff` per file (truncated) — DiffSummary for CI logs
  5. `git worktree remove --force <tmp>` + os.RemoveAll — cleanup

generateFn receives the absolute path of the temporary worktree and is expected to invoke the codegen pipeline with that path as the project root. It must not mutate state outside tmp (the K8s pattern relies on worktree isolation; cross-tree writes break the diff signal).

type WriteAction

type WriteAction string

WriteAction is an outcome enum for a Write call.

Drift is reported as ActionDrifted (not an error) so verify-mode callers can iterate Results and aggregate drift counts without errors.Is plumbing.

const (
	// ActionWritten reports that the file was created or rewritten on disk.
	ActionWritten WriteAction = "written"
	// ActionUnchanged reports that the on-disk content already matches the
	// requested content; no write occurred.
	ActionUnchanged WriteAction = "unchanged"
	// ActionWouldWrite reports that DryRun was true; no write occurred but
	// would have created or changed the file.
	ActionWouldWrite WriteAction = "would-write"
	// ActionDrifted reports that Verify was true and the requested content
	// differs from the on-disk file (or the file is missing). No write
	// occurred. Drift is a normal verify outcome — not an error.
	ActionDrifted WriteAction = "drifted"
)

type WriteOptions

type WriteOptions struct {
	// Path is the absolute target file path.
	Path string
	// Content is the bytes to compare against / write to the target.
	Content []byte
	// RepoRoot gates Path through fspath.IsWithinRoot to reject path
	// traversal. Must be non-empty (the output of pathsafe.ResolveRoot);
	// Write returns an error immediately if RepoRoot is empty.
	RepoRoot string
	// DryRun suppresses the filesystem mutation and returns ActionWouldWrite
	// (or ActionUnchanged when content matches).
	DryRun bool
	// Verify suppresses the filesystem mutation and returns ActionDrifted
	// (or ActionUnchanged) by comparing Content with the on-disk file.
	// DryRun and Verify are mutually exclusive at the CLI layer; combining
	// them here is harmless — Verify dominates (no write either way).
	Verify bool
	// Headerless opts out of the "existing file must carry the gocell
	// generated header" guard. Reserved exclusively for artifacts that
	// cannot carry a Go-style line comment (e.g. JSON schema files).
	// When true, Write will overwrite any existing file without checking
	// for the generated header; the within-root and Action state-machine
	// invariants are unaffected.
	//
	// Caller allowlist: only tools/codegen/sharedschema may set this field.
	// Enforced by archtest SHARED-SCHEMA-MIRROR-FUNNEL-01 A2.
	Headerless bool
}

WriteOptions controls a single Write call.

type WriteResult

type WriteResult struct {
	Action WriteAction
	Path   string
}

WriteResult reports the outcome of a Write call.

func Write

func Write(opts WriteOptions) (WriteResult, error)

Write persists Content to opts.Path with two safety guards:

  1. opts.Path must be inside opts.RepoRoot (fspath.IsWithinRoot); opts.RepoRoot must be non-empty — Write returns an error otherwise. Out-of-root writes are refused.
  2. If opts.Path already exists on disk, its first bytes must match governance.IsGoCellGenerated. User-edited files cannot be silently overwritten — the caller must move the file or delete it first.

Returns a WriteResult.Action describing what happened. The error return is non-nil only for real failures (IO errors, path traversal, refusal to overwrite a user file). Drift is reported via ActionDrifted, not error.

Directories

Path Synopsis
Package cellgen renders cell_gen.go and slice_gen.go from cell.yaml / slice.yaml metadata.
Package cellgen renders cell_gen.go and slice_gen.go from cell.yaml / slice.yaml metadata.
Package contractgen renders typed Go scaffolding from a single contract.yaml + its referenced JSON schemas.
Package contractgen renders typed Go scaffolding from a single contract.yaml + its referenced JSON schemas.
Package markergen scans cell.go marker comments (// +cell:listener, // +slice:route) and projects them into a per-cell WireBundle that drives cellgen wire generation.
Package markergen scans cell.go marker comments (// +cell:listener, // +slice:route) and projects them into a per-cell WireBundle that drives cellgen wire generation.
Package requireddepsgen generates validateRequired() methods for Service structs by reading gocell:"required" struct field tags.
Package requireddepsgen generates validateRequired() methods for Service structs by reading gocell:"required" struct field tags.
Package sagacoveragegen renders the saga fanout artifacts derived from the saga.Status / journal.EventKind const sets — the single source of truth for SAGA-STATUS-FANOUT-COVERAGE-01.
Package sagacoveragegen renders the saga fanout artifacts derived from the saga.Status / journal.EventKind const sets — the single source of truth for SAGA-STATUS-FANOUT-COVERAGE-01.
Package sharedschema derives byte-identical mirror copies of the shared error envelope schema from its single canonical source.
Package sharedschema derives byte-identical mirror copies of the shared error envelope schema from its single canonical source.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL