package infra import ( "os" "path/filepath" ) // AppLocationViolation describes an artefact found in a language-specific // directory where it does not belong (e.g. cpp/apps/, python/analysis/). type AppLocationViolation struct { Path string // relative from repoRoot (e.g. "cpp/apps/foo") Kind string // "app" | "analysis" Lang string // "cpp" | "python" | "bash" | "frontend" } // AuditAppLocation scans language-specific directories that must NOT contain // artefacts (apps or analyses) and returns every subdirectory that holds an // app.md or analysis.md manifest. // // Prohibited directories checked: // - cpp/apps/, cpp/analysis/ // - python/apps/, python/analysis/ // - bash/apps/, bash/analysis/ // - frontend/apps/, frontend/analysis/ // // If a prohibited directory does not exist it is silently skipped. // Artefacts under the canonical apps/ and analysis/ roots are not flagged. func AuditAppLocation(repoRoot string) ([]AppLocationViolation, error) { type candidate struct { lang string kind string dir string // relative dir inside repoRoot } candidates := []candidate{ {lang: "cpp", kind: "app", dir: "cpp/apps"}, {lang: "cpp", kind: "analysis", dir: "cpp/analysis"}, {lang: "python", kind: "app", dir: "python/apps"}, {lang: "python", kind: "analysis", dir: "python/analysis"}, {lang: "bash", kind: "app", dir: "bash/apps"}, {lang: "bash", kind: "analysis", dir: "bash/analysis"}, {lang: "frontend", kind: "app", dir: "frontend/apps"}, {lang: "frontend", kind: "analysis", dir: "frontend/analysis"}, } manifest := map[string]string{ "app": "app.md", "analysis": "analysis.md", } var out []AppLocationViolation for _, c := range candidates { absDir := filepath.Join(repoRoot, c.dir) entries, err := os.ReadDir(absDir) if err != nil { if os.IsNotExist(err) { continue } return nil, err } mf := manifest[c.kind] for _, e := range entries { if !e.IsDir() { continue } mdPath := filepath.Join(absDir, e.Name(), mf) if _, err := os.Stat(mdPath); err == nil { out = append(out, AppLocationViolation{ Path: filepath.Join(c.dir, e.Name()), Kind: c.kind, Lang: c.lang, }) } } } return out, nil }