| name | go-codemod |
| description | Implement and test Go codemods for the gh aw fix command. |
Go Codemod Implementation Guide
Use this skill when adding or updating codemods used by gh aw fix.
Understand the fix pipeline first
- Review
pkg/cli/fix_command.go to understand execution flow:
- codemods are loaded once via
GetAllCodemods()
- each codemod is applied in registry order
- frontmatter is re-parsed before each codemod
- codemods must return
(newContent, applied, error) and be safe for no-op input
- Review the codemod registry in
pkg/cli/fix_codemods.go.
- Review helper utilities in
pkg/cli/yaml_frontmatter_utils.go and reusable codemod helper constructors in pkg/cli/codemod_factory.go.
Implementation steps
- Create a new codemod file in
pkg/cli/ named codemod_<feature>.go.
- Add a package logger with
logger.New("cli:codemod_<feature>").
- Implement
get<Feature>Codemod() Codemod and populate all metadata fields:
ID (stable, unique)
Name (human-readable)
Description (clear migration behavior)
IntroducedIn (release version)
- In
Apply:
- check frontmatter preconditions first
- return unchanged content and
applied=false when migration is not needed
- transform only frontmatter using
applyFrontmatterLineTransform from pkg/cli/yaml_frontmatter_utils.go
- preserve comments/formatting/markdown body
- avoid lossy rewrites and avoid touching unrelated keys
- Prefer existing helpers before writing custom parsing logic:
findAndReplaceInLine
removeFieldFromBlock
removeParentBlockIfTrulyEmpty
newFieldRemovalCodemod
newMoveTopLevelKeyToOnBlockCodemod
- If the codemod depends on external data or side effects, inject dependencies through a
...WithDeps constructor so tests can mock behavior.
- Register the codemod in
pkg/cli/fix_codemods.go within GetAllCodemods().
Testing requirements
Create pkg/cli/codemod_<feature>_test.go and cover:
- Metadata correctness (
ID, Name, Description, IntroducedIn, Apply != nil).
- Happy path migration with expected output.
- No-op behavior when deprecated input is absent.
- Idempotence behavior (already-migrated input remains unchanged).
- Preservation guarantees:
- inline comments
- indentation
- markdown body after frontmatter
- Edge cases specific to the codemod (nested fields, mixed forms, ordering constraints, strict-mode checks, etc.).
- Dependency-injection behavior if
...WithDeps exists (success and fallback/error paths).
When complexity is high (expression rewriting, parser-like behavior), add fuzz tests in codemod_<feature>_fuzz_test.go using the pattern in pkg/cli/codemod_steps_run_secrets_env_fuzz_test.go.
Registry/order tests
After adding a codemod, update pkg/cli/fix_codemods_test.go:
- Expected codemod IDs list.
- Expected codemod order list.
Order matters because codemods run sequentially and later codemods observe prior transformations.
Validation commands
Run targeted checks first:
go test -v ./pkg/cli -run Codemod -count=1
go test -v ./pkg/cli -run Fix -count=1
Then run repository standards:
make build
make test-unit
make lint
Quality bar
A codemod is ready only when it is:
- deterministic
- safe on repeated runs
- conservative (no unrelated rewrites)
- fully covered by tests for migration and no-op paths
- registered and order-verified in fix codemod registry tests