chore: keep host/client specifics out of the tree (env & config)
Release Please / Release Please (push) Skipped
Release / GoReleaser (push) Skipped
Tests / Secret scan (gitleaks) (push) Skipped
Tests / Test (Go 1.25) (push) Skipped
Tests / Test (Go stable) (push) Skipped
Tests / Secret scan (gitleaks) (pull_request) Successful in 4s
Tests / Test (Go 1.25) (pull_request) Successful in 59s
Tests / Test (Go stable) (pull_request) Successful in 1m7s

Showroom-safe: the tree no longer carries internal hosts, IPs, ports,
personal names, client domains or client file names. Behaviour is
unchanged and now supplied per deployment.

- catalog: hardcoded mail-org / project classifiers become a YAML-driven
  Classifier (OO_CATALOG_CONFIG or --config); neutral default classifies
  nothing as work. New catalog/classify.go + example + tests.
- storage_fallback: drop the baked-in MinIO endpoint IP; require
  MINIO_ENDPOINT (+ keys) from the env.
- kontolink: build DocEditor links from $ONLYOFFICE_URL instead of a
  hardcoded portal host; kontoblatt: no client file id in the output name.
- oo: load .env CLI-wide (bootstrap.LoadEnv in execute) so
  non-authenticating commands (catalog scans) also see config.
- genericize comments/docs/fixtures (AGENTS, README, .env.example,
  crm-associations, catalog tests, ES/pdfattach tests, mails).
This commit is contained in:
2026-09-22 22:03:36 +01:00
parent b493472f2e
commit afb93feae5
24 changed files with 377 additions and 110 deletions
+1 -1
View File
@@ -162,7 +162,7 @@ func main() {
}
fmt.Printf("source: id=%s title=%q folder=%s\n", fileID, title, folder)
name := "Kontoblatt-1591-2025-Zusammenfassung.xlsx"
name := "Kontoblatt-Zusammenfassung.xlsx"
tmp := "/tmp/opencode/" + name
data, _ := os.ReadFile(outPath)
if err := os.WriteFile(tmp, data, 0o600); err != nil {
+16 -2
View File
@@ -76,7 +76,15 @@ func nearest(cands []entry, rd time.Time) (entry, bool) {
return cands[best], true
}
const linkPrefix = "https://office.pro-dukt.de/Products/Files/DocEditor.aspx?fileid="
// portalBaseFromEnv resolves the portal base URL used to build document links.
func portalBaseFromEnv() string {
for _, k := range []string{"ONLYOFFICE_URL", "ONLYOFFICE_HOST", "OO_URL"} {
if v := strings.TrimSpace(os.Getenv(k)); v != "" {
return strings.TrimRight(v, "/")
}
}
return ""
}
type entry struct {
id, path, title, norm string
@@ -91,6 +99,12 @@ func main() {
}
in, idxPath, out := os.Args[1], os.Args[2], os.Args[3]
portal := portalBaseFromEnv()
if portal == "" {
fmt.Fprintln(os.Stderr, "set ONLYOFFICE_URL (or ONLYOFFICE_HOST/OO_URL) to build document links")
os.Exit(2)
}
idxRaw, err := os.ReadFile(idxPath)
if err != nil {
panic(err)
@@ -153,7 +167,7 @@ func main() {
continue
}
ref, _ := excelize.CoordinatesToCellName(8, i+1)
if err := f.SetCellValue(sheet, ref, linkPrefix+e.id); err != nil {
if err := f.SetCellValue(sheet, ref, portal+"/Products/Files/DocEditor.aspx?fileid="+e.id); err != nil {
panic(err)
}
used[e.id] = true
+25 -3
View File
@@ -64,6 +64,7 @@ func catalogScanContactsCmd() *cobra.Command {
func catalogScanProjectsCmd() *cobra.Command {
var outPath string
var maxDepth int
var configPath string
cmd := &cobra.Command{
Use: "scan-projects",
Short: "Git roots / remotes / top-level dirs → company rows",
@@ -72,7 +73,11 @@ func catalogScanProjectsCmd() *cobra.Command {
if root == "" {
return fmt.Errorf("--root is required")
}
doc, err := catalog.ScanProjectsRoot(root, maxDepth)
cl, err := catalogClassifier(configPath)
if err != nil {
return err
}
doc, err := catalog.ScanProjectsRootOpts(root, maxDepth, catalog.ScanOptions{Classifier: cl})
if err != nil {
return err
}
@@ -81,6 +86,7 @@ func catalogScanProjectsCmd() *cobra.Command {
}
cmd.Flags().String("root", "", "projects directory (local path)")
cmd.Flags().IntVar(&maxDepth, "max-depth", 4, "max directory depth for git roots")
cmd.Flags().StringVar(&configPath, "config", "", "classification rules YAML (default $OO_CATALOG_CONFIG)")
cmd.Flags().StringVarP(&outPath, "out", "O", "", "write YAML to this path")
_ = cmd.MarkFlagRequired("root")
return cmd
@@ -89,6 +95,7 @@ func catalogScanProjectsCmd() *cobra.Command {
func catalogScanThunderbirdCmd() *cobra.Command {
var outPath string
var mboxHeaders bool
var configPath string
cmd := &cobra.Command{
Use: "scan-thunderbird",
Short: "Thunderbird profiles: abook/history.mab + Gloda SQLite contacts",
@@ -98,13 +105,18 @@ func catalogScanThunderbirdCmd() *cobra.Command {
- optional --mbox-headers: From/To/Cc/Reply-To from mbox folder files (no bodies)
Noisy senders (noreply, Amazon marketplace, GitHub reply, …) are skipped.
Default zone is private; known work domains (e.g. wheregroup.com) get zone=warm role=work.`,
Default zone is private; work domains/names are classified from the rules in
$OO_CATALOG_CONFIG (or --config) — none are hardcoded.`,
RunE: func(cmd *cobra.Command, args []string) error {
root, _ := cmd.Flags().GetString("root")
if root == "" {
return fmt.Errorf("--root is required")
}
doc, err := catalog.ScanThunderbirdRootOpts(root, catalog.ScanOptions{MboxHeaders: mboxHeaders})
cl, err := catalogClassifier(configPath)
if err != nil {
return err
}
doc, err := catalog.ScanThunderbirdRootOpts(root, catalog.ScanOptions{MboxHeaders: mboxHeaders, Classifier: cl})
if err != nil {
return err
}
@@ -113,11 +125,21 @@ Default zone is private; known work domains (e.g. wheregroup.com) get zone=warm
}
cmd.Flags().String("root", "", "Thunderbird profile or parent directory (local path)")
cmd.Flags().BoolVar(&mboxHeaders, "mbox-headers", false, "also extract emails from mbox From/To/Cc headers")
cmd.Flags().StringVar(&configPath, "config", "", "classification rules YAML (default $OO_CATALOG_CONFIG)")
cmd.Flags().StringVarP(&outPath, "out", "O", "", "write YAML to this path")
_ = cmd.MarkFlagRequired("root")
return cmd
}
// catalogClassifier loads classification rules from --config, else
// $OO_CATALOG_CONFIG, else the neutral default (no rules).
func catalogClassifier(configPath string) (*catalog.Classifier, error) {
if configPath == "" {
return catalog.LoadClassifierFromEnv()
}
return catalog.LoadClassifier(configPath)
}
func catalogMergeCmd() *cobra.Command {
var inputs []string
var outPath string
+3 -1
View File
@@ -31,7 +31,9 @@ func init() {
}
// execute runs the root command. Exported only to main.go in the same package.
func execute() error { return rootCmd.Execute() }
// .env is loaded CLI-wide so non-authenticating commands (e.g. catalog scans)
// also see configuration such as OO_CATALOG_CONFIG.
func execute() error { bootstrap.LoadEnv(); return rootCmd.Execute() }
// newOO loads env (only .env in CWD) and returns an authenticated client.
// godotenv is a CLI-only concern; the library itself never loads dotfiles.