Reuse parsed validation content by blob

This commit is contained in:
2026-10-09 01:37:33 +00:00
parent 182da397e3
commit cfd8ba7cb2
7 changed files with 190 additions and 57 deletions

View File

@@ -4,6 +4,7 @@ import (
"context"
"fmt"
"path"
"strings"
"gitea.maximumdirect.net/eric/ReleaseKit/internal/git"
"golang.org/x/mod/modfile"
@@ -11,12 +12,16 @@ import (
func checkLocalReplacements(ctx context.Context, tree *CandidateTree) error {
var findings []Finding
replacementsByObjectID := make(map[string][]moduleReplacementSummary)
for _, entry := range tree.Entries() {
if err := ctx.Err(); err != nil {
return &Failure{Check: LocalReplacements, Findings: findings, Cause: err}
}
if path.Base(entry.Path) != "go.mod" {
continue
}
contents, err := readModuleDeclaration(ctx, tree, entry)
declaration, err := resolveModuleDeclaration(ctx, tree, entry)
if err != nil {
return &Failure{
Check: LocalReplacements,
@@ -24,26 +29,49 @@ func checkLocalReplacements(ctx context.Context, tree *CandidateTree) error {
Cause: fmt.Errorf("inspect committed %s: %w", entry.Path, err),
}
}
moduleFile, err := modfile.Parse(entry.Path, contents, nil)
if err != nil {
return &Failure{
Check: LocalReplacements,
Findings: []Finding{{Path: entry.Path, Message: "could not parse committed module declaration"}},
Cause: fmt.Errorf("parse committed %s: %w", entry.Path, err),
replacements, ok := replacementsByObjectID[declaration.ObjectID]
if !ok {
contents, err := tree.ReadFile(ctx, declaration.Path)
if err != nil {
return &Failure{
Check: LocalReplacements,
Findings: []Finding{{Path: entry.Path, Message: "could not inspect committed module declaration"}},
Cause: fmt.Errorf("inspect committed %s: read module declaration: %w", entry.Path, err),
}
}
moduleFile, err := modfile.Parse(entry.Path, contents, nil)
if err != nil {
return &Failure{
Check: LocalReplacements,
Findings: []Finding{{Path: entry.Path, Message: "could not parse committed module declaration"}},
Cause: fmt.Errorf("parse committed %s: %w", entry.Path, err),
}
}
replacements = make([]moduleReplacementSummary, 0, len(moduleFile.Replace))
for _, replacement := range moduleFile.Replace {
if replacement.New.Version != "" {
continue
}
line := 0
if replacement.Syntax != nil {
line = replacement.Syntax.Start.Line
}
replacements = append(replacements, moduleReplacementSummary{
target: strings.Clone(replacement.New.Path),
line: line,
})
}
contents = nil
moduleFile = nil
replacementsByObjectID[declaration.ObjectID] = replacements
}
for _, replacement := range moduleFile.Replace {
if replacement.New.Version != "" {
continue
}
for _, replacement := range replacements {
finding := Finding{
Path: entry.Path,
Message: "filesystem module replacement is committed",
Target: replacement.New.Path,
Target: replacement.target,
Directive: "replace",
}
if replacement.Syntax != nil {
finding.Line = replacement.Syntax.Start.Line
Line: replacement.line,
}
findings = append(findings, finding)
}
@@ -54,39 +82,36 @@ func checkLocalReplacements(ctx context.Context, tree *CandidateTree) error {
return nil
}
func readModuleDeclaration(ctx context.Context, tree *CandidateTree, entry git.TreeEntry) ([]byte, error) {
type moduleReplacementSummary struct {
target string
line int
}
func resolveModuleDeclaration(ctx context.Context, tree *CandidateTree, entry git.TreeEntry) (git.TreeEntry, error) {
if entry.Type != "blob" {
return nil, fmt.Errorf("entry has type %q, not a blob", entry.Type)
return git.TreeEntry{}, fmt.Errorf("entry has type %q, not a blob", entry.Type)
}
if entry.Mode != "120000" {
if entry.Mode != "100644" && entry.Mode != "100755" {
return nil, fmt.Errorf("entry has unsupported mode %q", entry.Mode)
return git.TreeEntry{}, fmt.Errorf("entry has unsupported mode %q", entry.Mode)
}
contents, err := tree.ReadFile(ctx, entry.Path)
if err != nil {
return nil, fmt.Errorf("read module declaration: %w", err)
}
return contents, nil
return entry, nil
}
target, err := tree.readSymlinkTarget(ctx, entry)
if err != nil {
return nil, fmt.Errorf("read symlink declaration: %w", err)
return git.TreeEntry{}, fmt.Errorf("read symlink declaration: %w", err)
}
destination, err := tree.ResolveDestination(ctx, entry.Path, target)
if err != nil {
return nil, fmt.Errorf("resolve symlink declaration target %q: %w", target, err)
return git.TreeEntry{}, fmt.Errorf("resolve symlink declaration target %q: %w", target, err)
}
if destination.Kind != DestinationEntry {
return nil, fmt.Errorf("symlink declaration target %q resolves to %s", target, destination.Kind)
return git.TreeEntry{}, fmt.Errorf("symlink declaration target %q resolves to %s", target, destination.Kind)
}
resolved, ok := tree.Entry(destination.Path)
if !ok || resolved.Type != "blob" || (resolved.Mode != "100644" && resolved.Mode != "100755") {
return nil, fmt.Errorf("symlink declaration target %q does not resolve to a regular committed file", target)
return git.TreeEntry{}, fmt.Errorf("symlink declaration target %q does not resolve to a regular committed file", target)
}
contents, err := tree.ReadFile(ctx, destination.Path)
if err != nil {
return nil, fmt.Errorf("read resolved module declaration %q: %w", destination.Path, err)
}
return contents, nil
return resolved, nil
}