92 lines
2.4 KiB
Go
92 lines
2.4 KiB
Go
// 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 . <catalogue root>
|
|
//
|
|
// 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")
|
|
}
|