ワンクリックで
port-hadolint-rule
Port a Hadolint rule from Haskell to Go implementation
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
メニュー
Port a Hadolint rule from Haskell to Go implementation
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
SOC 職業分類に基づく
| name | port-hadolint-rule |
| description | Port a Hadolint rule from Haskell to Go implementation |
| argument-hint | rule-code (e.g. DL3022) |
| disable-model-invocation | true |
| allowed-tools | Read, Write, Edit, Grep, Glob, Bash(go *), Bash(git status), mcp__github__get_file_contents, mcp__github__search_code |
You are porting a Hadolint rule from Haskell to Go for the tally project.
Use the GitHub MCP tools to fetch the original Haskell implementation:
First, try to get the rule implementation file directly:
mcp__github__get_file_contents with:
owner: "hadolint"repo: "hadolint"path: "src/Hadolint/Rule/$ARGUMENTS.hs"branch: "master"If that fails, use mcp__github__search_code to find the rule:
repo:hadolint/hadolint for the rule codeCarefully analyze the Haskell implementation to understand:
Use GitHub MCP tools to get the test specification:
Try to get the test file directly:
mcp__github__get_file_contents with:
owner: "hadolint"repo: "hadolint"path: "test/Hadolint/Rule/$ARGUMENTSSpec.hs"branch: "master"If that fails, search for the test file:
mcp__github__search_code in repo:hadolint/hadolint for "$ARGUMENTSSpec"Extract ALL test cases from the spec file - both passing and failing cases
ruleCatches indicates the rule SHOULD trigger (expect violation)ruleCatchesNot indicates the rule should NOT trigger (expect no violation)Before implementing, read these files to understand the patterns:
internal/rules/hadolint/dl3004.go - a standard rule implementationinternal/rules/hadolint/dl3012.go - a pointer file for semantic-based rulesinternal/shell/shell.go - shell parsing utilitiesinternal/shell/packages.go - package manager parsinginternal/semantic/semantic.go - semantic modelinternal/semantic/builder.go - semantic model builderinternal/rules/rule.go - Rule interface and LintInputDecide where to implement based on rule nature:
internal/rules/hadolint/$ARGUMENTS.go documenting the semantic implementationNEVER parse shell commands using regex or string operations.
Always use the internal/shell package:
import "github.com/wharflab/tally/internal/shell"
// To check if a command contains a specific command name:
if shell.ContainsCommandWithVariant(cmdStr, "sudo", shellVariant) {
// violation
}
// To get all command names:
commands := shell.CommandNamesWithVariant(cmdStr, shellVariant)
// To extract package installations:
installs := shell.ExtractPackageInstalls(cmdStr, shellVariant)
Always leverage the semantic model (internal/semantic/):
// Get semantic model from input
sem, ok := input.Semantic.(*semantic.Model)
if !ok {
sem = nil
}
// Use for shell variant detection
if sem != nil {
if info := sem.StageInfo(stageIdx); info != nil {
shellVariant = info.ShellSetting.Variant
// Skip non-POSIX shells if rule is shell-specific
if shellVariant.IsNonPOSIX() {
continue
}
}
}
// Use for stage information
for info := range sem.ExternalImageStages() {
// Check external image references
}
If the rule requires semantic information not yet tracked:
StageInfo in internal/semantic/stage_info.gointernal/semantic/builder.gopackage hadolint
import (
"github.com/moby/buildkit/frontend/dockerfile/instructions"
"github.com/wharflab/tally/internal/rules"
"github.com/wharflab/tally/internal/semantic"
"github.com/wharflab/tally/internal/shell"
)
// $ARGUMENTSRule implements the $ARGUMENTS linting rule.
type $ARGUMENTSRule struct{}
// New$ARGUMENTSRule creates a new $ARGUMENTS rule instance.
func New$ARGUMENTSRule() *$ARGUMENTSRule {
return &$ARGUMENTSRule{}
}
// Metadata returns the rule metadata.
func (r *$ARGUMENTSRule) Metadata() rules.RuleMetadata {
return rules.RuleMetadata{
Code: rules.HadolintRulePrefix + "$ARGUMENTS",
Name: "...", // Short name from Hadolint wiki
Description: "...", // Description from Hadolint wiki
DocURL: rules.HadolintDocURL("$ARGUMENTS"),
DefaultSeverity: rules.SeverityWarning, // or SeverityError based on original
Category: "...", // security, performance, style, etc.
IsExperimental: false,
}
}
// Check runs the $ARGUMENTS rule.
func (r *$ARGUMENTSRule) Check(input rules.LintInput) []rules.Violation {
var violations []rules.Violation
meta := r.Metadata()
// Get semantic model
sem, ok := input.Semantic.(*semantic.Model)
if !ok {
sem = nil
}
// Implementation...
return violations
}
// init registers the rule with the default registry.
func init() {
rules.Register(New$ARGUMENTSRule())
}
Create test file internal/rules/hadolint/$ARGUMENTS_test.go:
internal/rules/hadolint/dl3004_test.gotestutil.MakeLintInput to create test inputsfunc Test$ARGUMENTSRule_Check(t *testing.T) {
tests := []struct {
name string
dockerfile string
wantCount int
}{
// Add ALL cases from original Hadolint spec
// ruleCatches cases -> wantCount: 1 (or more)
// ruleCatchesNot cases -> wantCount: 0
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
input := testutil.MakeLintInput(t, "Dockerfile", tt.dockerfile)
r := New$ARGUMENTSRule()
violations := r.Check(input)
if len(violations) != tt.wantCount {
t.Errorf("got %d violations, want %d", len(violations), tt.wantCount)
}
})
}
}
Run the tests to verify:
go test ./internal/rules/hadolint/... -run $ARGUMENTS -v
Ensure ALL original Hadolint test cases pass.
Add a directory fixture for the new rule:
internal/integration/fixtures/lint/$ARGUMENTS/Dockerfile.internal/integration/fixtures/lint/$ARGUMENTS/.tally.toml selecting the Hadolint rule and any overlap rules needed by the case.internal/integration/fixtures/fix/$ARGUMENTS/Dockerfile and optional .tally.toml.UPDATE_SNAPS=true go test ./internal/integration -run 'Test(Lint|Fix)Fixtures' -count=1.UPDATE_SNAPS.The fixture harness writes go-snaps files next to the Dockerfile: result_1.snap.json for lint, fixed_1.snap.Dockerfile for fix output, and
optional report_1.snap.md for fix reports. Use explicit Go integration tests only for behavior that needs CLI/config/discovery/stdin/multi-file
coverage outside the directory harness.
Evaluate whether the rule can provide automatic fixes:
For fixes that can be computed immediately:
func (r *MyRule) Check(input rules.LintInput) []rules.Violation {
// Use helper for RUN commands
return ScanRunCommandsWithPOSIXShell(input, func(run *instructions.RunCommand, shellVariant shell.Variant, file string) []rules.Violation {
// ... detection logic ...
fix := &rules.SuggestedFix{
Description: "Replace X with Y",
Safety: rules.FixSafe, // or FixSuggestion, FixUnsafe
Edits: []rules.TextEdit{{
Location: rules.NewRangeLocation(file, startLine, startCol, endLine, endCol),
NewText: "replacement text",
}},
}
return []rules.Violation{
rules.NewViolation(loc, meta.Code, msg, meta.DefaultSeverity).
WithSuggestedFix(fix),
}
})
}
For fixes requiring network I/O (image digests, checksums):
fix := &rules.SuggestedFix{
Description: "Add image digest",
Safety: rules.FixSafe,
NeedsResolve: true,
ResolverID: "image-digest",
ResolverData: map[string]string{"image": "alpine", "tag": "3.18"},
}
FixSafe: Always correct, won't change behaviorFixSuggestion: Likely correct but may need reviewFixUnsafe: May change behavior significantlyFor precise command replacement within RUN instructions, use shell.FindCommandOccurrences() to get exact byte offsets within shell scripts.
After implementation is complete, update the tracking files:
Update hadolint-status.json:
Add an entry to internal/rules/hadolint-status.json:
"$ARGUMENTS": {
"status": "implemented",
"tally_rule": "hadolint/$ARGUMENTS"
}
Place it in alphabetical order among the other rules.
Regenerate documentation:
./scripts/generate-hadolint-table.sh --update
This updates the Hadolint compatibility table in the documentation.
Update integration test snapshots (if needed):
UPDATE_SNAPS=true go test ./internal/integration/...
Create _docs/rules/hadolint/$ARGUMENTS.mdx following the pattern in _docs/rules/hadolint/DL3004.mdx:
# hadolint/$ARGUMENTS## Auto-fix section with before/after example (if auto-fix is supported)## Reference section with named link: - [hadolint/$ARGUMENTS](https://github.com/hadolint/hadolint/wiki/$ARGUMENTS)Add the rule to _docs/rules/hadolint/overview.mdx in the main table.
internal/shell for command parsing (no regex)init() function registers the rulego test ./internal/rules/hadolint/... -run $ARGUMENTS -vinternal/integration/fixtures/lint/ and, when fix-capable, internal/integration/fixtures/fix/hadolint-status.json updated with new rulegenerate-hadolint-table.sh --update_docs/rules/hadolint/$ARGUMENTS.mdx created and added to overviewAdd auto-fix support to an existing BuildKit linter rule
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 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.