بنقرة واحدة
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