// Command imageplace refuses an image artifact whose Dockerfile is not a file inside its own module's directory // (novox/hq ADR 0487 §1, issue 1707): the image builder builds a module from that module's directory alone, so // a `from` that leaves it (../other/Dockerfile) or names a file that is not there could never build. // // go run . // // Exits 1 on a finding. package main import ( "encoding/json" "fmt" "os" "path/filepath" "sort" "strings" ) // Finding is one image refused: Module's artifact Artifact names From, and Why says what is wrong with it. type Finding struct { Module, Artifact, From, Why string } // Check reads every manifest under root/modules and answers which images it refuses. func Check(root string) ([]Finding, error) { manifests, err := filepath.Glob(filepath.Join(root, "modules", "*", "module.json")) if err != nil { return nil, err } sort.Strings(manifests) var out []Finding for _, path := range manifests { raw, err := os.ReadFile(path) if err != nil { return nil, err } var m struct { Module string `json:"module"` Build struct { Artifacts []struct { Name string `json:"name"` Kind string `json:"kind"` From string `json:"from"` } `json:"artifacts"` } `json:"build"` } if err := json.Unmarshal(raw, &m); err != nil { return nil, fmt.Errorf("%s: %w", path, err) } dir := filepath.Dir(path) for _, a := range m.Build.Artifacts { if a.Kind != "image" { continue } f := Finding{Module: m.Module, Artifact: a.Name, From: a.From} clean := filepath.Clean(a.From) switch { case a.From == "" || filepath.IsAbs(clean) || clean == ".." || strings.HasPrefix(clean, ".."+string(filepath.Separator)): f.Why = "is not a path inside the module's own directory" default: info, err := os.Stat(filepath.Join(dir, clean)) if err != nil || info.IsDir() { f.Why = "is not a file in the module's directory" } } if f.Why != "" { out = append(out, f) } } } return out, nil } func main() { root := "." if len(os.Args) > 1 { root = os.Args[1] } found, err := Check(root) if err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } for _, f := range found { fmt.Printf("%s: image %s builds from %q, which %s (novox/hq ADR 0487 §1)\n", f.Module, f.Artifact, f.From, f.Why) } if len(found) > 0 { os.Exit(1) } fmt.Println("imageplace: every image builds from a Dockerfile in its own module's directory") }