Files
go-onlyoffice/catalog/scan_projects.go
T
eSlider afb93feae5
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
chore: keep host/client specifics out of the tree (env & config)
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).
2026-09-22 22:03:36 +01:00

118 lines
2.8 KiB
Go

package catalog
import (
"fmt"
"os"
"os/exec"
"path/filepath"
"strings"
)
// ScanProjectsRoot finds git roots under root (max depth) and emits company rows.
func ScanProjectsRoot(root string, maxDepth int) (*Document, error) {
return ScanProjectsRootOpts(root, maxDepth, ScanOptions{})
}
// ScanProjectsRootOpts is ScanProjectsRoot with deployment classification rules.
func ScanProjectsRootOpts(root string, maxDepth int, opts ScanOptions) (*Document, error) {
root = filepath.Clean(root)
if maxDepth <= 0 {
maxDepth = 4
}
cl := opts.Classifier
if cl == nil {
cl = DefaultClassifier()
}
st, err := os.Stat(root)
if err != nil {
return nil, err
}
if !st.IsDir() {
return nil, fmt.Errorf("not a directory: %s", root)
}
var entries []Entry
err = walkGitRoots(root, root, 0, maxDepth, cl, &entries)
if err != nil {
return nil, err
}
// Also top-level dirs as company stubs (even without git) — coverage 1B.
dents, _ := os.ReadDir(root)
for _, d := range dents {
if !d.IsDir() || strings.HasPrefix(d.Name(), ".") {
continue
}
name := d.Name()
skip := map[string]struct{}{
"node_modules": {}, "vendor": {}, ".cache": {},
}
if _, ok := skip[name]; ok {
continue
}
path := filepath.Join(root, name)
id := EntryID("company", "", name)
role, zone := cl.ClassifyProject(name, "")
entries = append(entries, Entry{
ID: id,
Kind: "company",
Name: name,
Sources: []string{path},
Zone: zone,
Role: role,
Status: "new",
Notes: "top_level_dir",
})
}
return MergeDocs(&Document{Entries: entries}), nil
}
func walkGitRoots(root, dir string, depth, maxDepth int, cl *Classifier, out *[]Entry) error {
if depth > maxDepth {
return nil
}
// is this a git root?
if st, err := os.Stat(filepath.Join(dir, ".git")); err == nil && (st.IsDir() || st.Mode().IsRegular()) {
name := filepath.Base(dir)
remote := gitRemoteOrigin(dir)
role, zone := cl.ClassifyProject(name, remote)
*out = append(*out, Entry{
ID: EntryID("company", "", name),
Kind: "company",
Name: name,
Sources: []string{dir},
Remote: remote,
GitRoot: dir,
Zone: zone,
Role: role,
Status: "new",
})
return nil // do not descend into nested repos from a git root
}
dents, err := os.ReadDir(dir)
if err != nil {
return nil
}
for _, d := range dents {
if !d.IsDir() {
continue
}
name := d.Name()
if name == ".git" || name == "node_modules" || name == "vendor" || name == ".venv" || name == "dist" {
continue
}
_ = walkGitRoots(root, filepath.Join(dir, name), depth+1, maxDepth, cl, out)
}
return nil
}
func gitRemoteOrigin(dir string) string {
cmd := exec.Command("git", "-C", dir, "remote", "get-url", "origin")
b, err := cmd.Output()
if err != nil {
return ""
}
return strings.TrimSpace(string(b))
}