Files
egutierrez bf1efb2099 feat: externalize apps/analysis to Gitea repos, add analysis table
- Migration 007: repo_url on apps table + analysis table with FTS5
- Analysis struct, parser, CRUD, validation, hash computation
- Selective purge: remote-only apps/analysis preserved across fn index
- CLI: fn app list/clone/pull, fn analysis list/clone/pull
- search/show/list now include analysis results
- Apps removed from git tracking (content lives in Gitea repos)
- .gitkeep for apps/ and analysis/ dirs
- Bash functions: jupyter analysis pipeline, shell utilities
- Browser domain: CDP functions moved from infra to browser

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 04:23:51 +02:00

144 lines
3.1 KiB
Go

package main
import (
"fmt"
"os"
"os/exec"
"path/filepath"
"text/tabwriter"
)
func cmdAnalysis(args []string) {
if len(args) < 1 {
printAnalysisUsage()
os.Exit(1)
}
switch args[0] {
case "list":
analysisList()
case "clone":
if len(args) < 2 {
fmt.Fprintln(os.Stderr, "usage: fn analysis clone <id>")
os.Exit(1)
}
analysisClone(args[1])
case "pull":
if len(args) < 2 {
fmt.Fprintln(os.Stderr, "usage: fn analysis pull <id>")
os.Exit(1)
}
analysisPull(args[1])
case "help", "-h":
printAnalysisUsage()
default:
fmt.Fprintf(os.Stderr, "unknown analysis command: %s\n", args[0])
printAnalysisUsage()
os.Exit(1)
}
}
func printAnalysisUsage() {
fmt.Println(`fn analysis — manage registry analyses
Usage:
fn analysis list List all analyses (local + remote)
fn analysis clone <id> Clone analysis repo to analysis/<name>/
fn analysis pull <id> Git pull in existing clone`)
}
func analysisList() {
db := openDB()
defer db.Close()
items, err := db.ListAllAnalysis()
if err != nil {
fmt.Fprintf(os.Stderr, "error: %v\n", err)
os.Exit(1)
}
if len(items) == 0 {
fmt.Println("No analyses registered.")
return
}
r := root()
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
fmt.Fprintln(w, "ID\tLANG\tDOMAIN\tSTATUS\tREPO_URL")
for _, a := range items {
status := "local"
if a.RepoURL != "" {
dirPath := filepath.Join(r, "analysis", a.Name)
if _, err := os.Stat(dirPath); os.IsNotExist(err) {
status = "remote"
} else {
status = "cloned"
}
}
fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\n", a.ID, a.Lang, a.Domain, status, a.RepoURL)
}
w.Flush()
}
func analysisClone(id string) {
db := openDB()
defer db.Close()
a, err := db.GetAnalysis(id)
if err != nil {
fmt.Fprintf(os.Stderr, "error: %v\n", err)
os.Exit(1)
}
if a.RepoURL == "" {
fmt.Fprintf(os.Stderr, "analysis %s has no repo_url set\n", id)
os.Exit(1)
}
r := root()
target := filepath.Join(r, "analysis", a.Name)
if _, err := os.Stat(target); err == nil {
fmt.Fprintf(os.Stderr, "directory already exists: %s\n", target)
os.Exit(1)
}
fmt.Printf("Cloning %s → %s\n", a.RepoURL, target)
cmd := exec.Command("git", "clone", a.RepoURL, target)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
fmt.Fprintf(os.Stderr, "git clone failed: %v\n", err)
os.Exit(1)
}
fmt.Println("Done. Run 'fn index' to re-index.")
}
func analysisPull(id string) {
db := openDB()
defer db.Close()
a, err := db.GetAnalysis(id)
if err != nil {
fmt.Fprintf(os.Stderr, "error: %v\n", err)
os.Exit(1)
}
r := root()
dir := filepath.Join(r, "analysis", a.Name)
if _, err := os.Stat(dir); os.IsNotExist(err) {
fmt.Fprintf(os.Stderr, "analysis not cloned locally: %s\n", dir)
fmt.Fprintln(os.Stderr, "Run 'fn analysis clone "+id+"' first.")
os.Exit(1)
}
fmt.Printf("Pulling %s\n", dir)
cmd := exec.Command("git", "-C", dir, "pull")
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
fmt.Fprintf(os.Stderr, "git pull failed: %v\n", err)
os.Exit(1)
}
fmt.Println("Done. Run 'fn index' to re-index.")
}