ワンクリックで
add-buildkit-fix
Add auto-fix support to an existing BuildKit linter rule
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
メニュー
Add auto-fix support to an existing BuildKit linter rule
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
SOC 職業分類に基づく
Implement a new custom `tally/*` Dockerfile lint rule end-to-end (rule code, overlap research, fix coordination, realistic tests/fixtures, snapshots, and docs). Use when a user describes desired behavior for a new tally-specific rule.
Port a Hadolint rule from Haskell to Go implementation
Port a ShellCheck SC rule to native Go in tally while preserving shellcheck/SC#### compatibility (config, reporting, fixes, and docs ownership).
Implement a new custom `tally/*` Dockerfile lint rule end-to-end (rule code, overlap research, fix coordination, realistic tests/fixtures, snapshots, and docs). Use when a user describes desired behavior for a new tally-specific rule.
Create a new custom Go analysis linter (analyzer) in _tools/customlint/ for the tally project. Use this skill whenever the user wants to add a new lint check, custom analyzer, static analysis rule, or code quality check to the customlint plugin. Also use when the user says things like "add a linter for X", "catch bad pattern Y at CI time", "flag hardcoded Z values", or "enforce convention W in code".
Build and maintain documentation sites with Mintlify. Use when creating docs pages, configuring navigation, adding components, or setting up API references.
| name | add-buildkit-fix |
| description | Add auto-fix support to an existing BuildKit linter rule |
| argument-hint | rule-name (e.g. StageNameCasing, FromAsCasing, NoEmptyContinuation) |
| disable-model-invocation | true |
You are adding auto-fix support to an existing BuildKit linter rule for the tally project.
First, confirm tally is already reporting this rule as a violation.
# Create a test Dockerfile that should trigger the rule
# (adjust content based on the specific rule)
echo 'FROM alpine AS Builder' > /tmp/test.dockerfile
go run . check --format json /tmp/test.dockerfile 2>&1 | jq '.files[0].violations[] | {rule, message}'
If the rule doesn't appear:
internal/rules/buildkit/registry.go to see if the rule is registeredast.Warnings) rather than the linter callbackinternal/dockerfile/parser.go firstFetch Docker documentation:
https://docs.docker.com/reference/build-checks/$ARGUMENTS-in-kebab-case/
Example: StageNameCasing → stage-name-casing
Check existing snapshots for the message format:
grep -r "$ARGUMENTS" internal/integration/__snapshots__/
Read the enricher pattern in internal/rules/buildkit/fixes/enricher.go
Create internal/rules/buildkit/fixes/$ARGUMENTS_snake_case.go:
package fixes
import (
"github.com/wharflab/tally/internal/rules"
)
// enrich${ARGUMENTS}Fix adds auto-fix for BuildKit's $ARGUMENTS rule.
func enrich${ARGUMENTS}Fix(v *rules.Violation, source []byte) {
// 1. Get the source line (getLine uses 0-based index)
lineIdx := v.Location.Start.Line - 1
line := getLine(source, lineIdx)
if line == nil {
return
}
// 2. Find what needs to change (use position helpers or tokenizer)
// ...
// 3. Create the fix
v.SuggestedFix = &rules.SuggestedFix{
Description: "Description of what the fix does",
Safety: rules.FixSafe,
Edits: []rules.TextEdit{{
// createEditLocation takes 1-based line numbers
Location: createEditLocation(v.Location.File, v.Location.Start.Line, startCol, endCol),
NewText: "replacement",
}},
IsPreferred: true,
}
}
If the fix needs the semantic model (for cross-instruction references):
func enrich${ARGUMENTS}Fix(v *rules.Violation, sem *semantic.Model, source []byte) {
if sem == nil {
return
}
// Use sem.StageIndexByName(), sem.StageInfo(), etc.
}
Add to the switch in internal/rules/buildkit/fixes/enricher.go:
case "$ARGUMENTS":
enrich${ARGUMENTS}Fix(v, source)
// Or with semantic model:
// enrich${ARGUMENTS}Fix(v, sem, source)
Location: createEditLocation(file, lineNum, startCol, endCol),
NewText: "replacement",
To delete an entire line, span from line N to line N+1:
// Delete line 3 (including its newline)
Location: rules.NewRangeLocation(file, lineNum, 0, lineNum+1, 0),
NewText: "",
The fixer applies edits from end to start, so line shifts are handled automatically.
| Context | Convention |
|---|---|
v.Location.Start.Line | 1-based (from BuildKit) |
getLine(source, idx) | 0-based index |
createEditLocation(file, line, ...) | 1-based line |
rules.NewRangeLocation(file, line, ...) | 1-based line |
Common pattern:
lineIdx := v.Location.Start.Line - 1 // Convert to 0-based for getLine
line := getLine(source, lineIdx)
// ... find positions within line ...
// Use v.Location.Start.Line (1-based) for createEditLocation
Add to internal/rules/buildkit/fixes/fixes_test.go:
func Test${ARGUMENTS}Fix(t *testing.T) {
tests := []struct {
name string
source string
wantFix bool
wantEdits int
}{
{
name: "should fix",
source: "...",
wantFix: true,
wantEdits: 1,
},
{
name: "already correct",
source: "...",
wantFix: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
source := []byte(tt.source)
v := rules.Violation{
Location: rules.NewRangeLocation("test.Dockerfile", 1, 0, 1, len(tt.source)),
RuleCode: rules.BuildKitRulePrefix + "$ARGUMENTS",
Message: "...", // Match BuildKit's actual message
}
enrich${ARGUMENTS}Fix(&v, source)
if tt.wantFix {
require.NotNil(t, v.SuggestedFix)
assert.Len(t, v.SuggestedFix.Edits, tt.wantEdits)
} else {
assert.Nil(t, v.SuggestedFix)
}
})
}
}
Use the directory harness for normal BuildKit fix coverage:
internal/integration/fixtures/lint/$ARGUMENTS-kebab-case/Dockerfileinternal/integration/fixtures/lint/$ARGUMENTS-kebab-case/.tally.tomlinternal/integration/fixtures/fix/$ARGUMENTS-kebab-case/Dockerfileinternal/integration/fixtures/fix/$ARGUMENTS-kebab-case/.tally.tomlSelect the target rule in .tally.toml; add overlap rules there when the fix must be validated in a combined run. Set unsafe-fixes = true if
the fixture needs unsafe fix application.
Create/update snapshots with go-snaps:
UPDATE_SNAPS=true go test ./internal/integration -run 'Test(Lint|Fix)Fixtures' -count=1
go test ./internal/integration -run 'Test(Lint|Fix)Fixtures' -count=1
Expected snapshot files are fixture-local: result_1.snap.json, fixed_1.snap.Dockerfile, and optional report_1.snap.md.
Add explicit Go integration tests only for behavior the directory harness cannot express, such as custom CLI formats, config discovery, stdin-only behavior, or multi-file contexts.
# Unit tests
go test ./internal/rules/buildkit/fixes/... -v
# All tests
go test ./...
# Linter
make lint
# Update snapshots
UPDATE_SNAPS=true go test ./internal/integration/...
# Manual verification
go run . check --fix /tmp/test.dockerfile && cat /tmp/test.dockerfile
Update _docs/rules/buildkit/$ARGUMENTS.mdx — add an ## Auto-fix section with a before/after example. Set the Auto-fix property row to
Yes (\--fix`)(orYes (`--fix-unsafe`)for non-safe fixes). Follow the pattern in_docs/rules/buildkit/StageNameCasing.mdx`.
| Level | When to Use |
|---|---|
rules.FixSafe | Casing changes, removing whitespace, formatting |
rules.FixSuggestion | Semantic changes that are usually correct |
rules.FixUnsafe | Changes that might alter behavior |
getLine(source, lineIdx) - Get line content (0-based index)createEditLocation(file, line, startCol, endCol) - Create edit location (1-based line)ParseInstruction(line) - Tokenizer for instruction parsing
.FindKeyword("AS") - Find keyword token.FindFlag("from") - Find flag like --from.Arguments() - Get argument tokensgo run . check --format jsoninternal/rules/buildkit/fixes/enricher.go switchfixes_test.gointernal/integration/fixtures/lint/internal/integration/fixtures/fix/go test ./... passesmake lint passes--fix verification works_docs/rules/buildkit/$ARGUMENTS.mdx updated with Auto-fix section