feat: CLI fn ops — init, entity, relation, graph, snapshot
Subcomando ops integrado en el CLI fn. Soporta CRUD de entities con snapshot automatico de tipos, CRUD de relations con validacion de ciclos, dump ASCII del grafo, y listado de tipos snapshotted. Variable de entorno FN_REGISTRY_ROOT para acceder al registry desde cualquier directorio.
This commit is contained in:
+4
-1
@@ -29,6 +29,8 @@ func main() {
|
||||
cmdShow(os.Args[2:])
|
||||
case "add":
|
||||
cmdAdd(os.Args[2:])
|
||||
case "ops":
|
||||
cmdOps(os.Args[2:])
|
||||
case "help", "-h", "--help":
|
||||
printUsage()
|
||||
default:
|
||||
@@ -46,7 +48,8 @@ Usage:
|
||||
fn search [-k kind] [-p purity] [-l lang] [-d domain] <query>
|
||||
fn list [-k kind] [-d domain] [-l lang]
|
||||
fn show <id> Muestra entrada completa
|
||||
fn add [-k kind] Abre $EDITOR con template`)
|
||||
fn add [-k kind] Abre $EDITOR con template
|
||||
fn ops <subcommand> Gestiona operations.db (fn ops help)`)
|
||||
}
|
||||
|
||||
func root() string {
|
||||
|
||||
+634
@@ -0,0 +1,634 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
|
||||
ops "fn-registry/fn_operations"
|
||||
"fn-registry/registry"
|
||||
)
|
||||
|
||||
const opsDBName = "operations.db"
|
||||
|
||||
func cmdOps(args []string) {
|
||||
if len(args) < 1 {
|
||||
printOpsUsage()
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
switch args[0] {
|
||||
case "init":
|
||||
cmdOpsInit(args[1:])
|
||||
case "entity":
|
||||
cmdOpsEntity(args[1:])
|
||||
case "relation":
|
||||
cmdOpsRelation(args[1:])
|
||||
case "graph":
|
||||
cmdOpsGraph()
|
||||
case "snapshot":
|
||||
cmdOpsSnapshot(args[1:])
|
||||
case "help", "-h", "--help":
|
||||
printOpsUsage()
|
||||
default:
|
||||
fmt.Fprintf(os.Stderr, "unknown ops command: %s\n", args[0])
|
||||
printOpsUsage()
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func printOpsUsage() {
|
||||
fmt.Println(`fn ops — operations CLI
|
||||
|
||||
Usage:
|
||||
fn ops init [path] Crea operations.db en path (default: .)
|
||||
fn ops entity add <flags> Añade entity
|
||||
fn ops entity list [-d domain] [-s status] Lista entities
|
||||
fn ops entity show <id> Muestra entity
|
||||
fn ops entity delete <id> Elimina entity
|
||||
fn ops relation add <flags> Añade relation
|
||||
fn ops relation list [entity_id] Lista relations
|
||||
fn ops relation show <id> Muestra relation
|
||||
fn ops relation delete <id> Elimina relation
|
||||
fn ops graph Grafo ASCII de entities y relations
|
||||
fn ops snapshot list Lista tipos snapshotted
|
||||
|
||||
Entity flags:
|
||||
--id <id> --name <name> --type-ref <type_id> --source <source>
|
||||
--domain <domain> --status <status> --description <desc>
|
||||
--tags <t1,t2> --metadata <json> --notes <text>
|
||||
|
||||
Relation flags:
|
||||
--id <id> --name <name> --from <entity_id> --to <entity_id>
|
||||
--via <function_id> --direction <uni|bi|inverse> --status <status>
|
||||
--purity <pure|impure> --weight <0.0-1.0> --description <desc>
|
||||
--tags <t1,t2> --notes <text>`)
|
||||
}
|
||||
|
||||
// --- ops init ---
|
||||
|
||||
func cmdOpsInit(args []string) {
|
||||
dir := "."
|
||||
if len(args) > 0 {
|
||||
dir = args[0]
|
||||
}
|
||||
|
||||
path := filepath.Join(dir, opsDBName)
|
||||
if _, err := os.Stat(path); err == nil {
|
||||
fmt.Fprintf(os.Stderr, "operations.db already exists at %s\n", path)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Copy from template if available, otherwise create fresh
|
||||
templatePath := filepath.Join(root(), "fn_operations", "project_template", "operations.db")
|
||||
if _, err := os.Stat(templatePath); err == nil {
|
||||
src, err := os.Open(templatePath)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "error opening template: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
defer src.Close()
|
||||
|
||||
if err := os.MkdirAll(dir, 0o755); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "error creating directory: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
dst, err := os.Create(path)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "error creating db: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
defer dst.Close()
|
||||
|
||||
if _, err := io.Copy(dst, src); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "error copying template: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Printf("operations.db created at %s (from template)\n", path)
|
||||
return
|
||||
}
|
||||
|
||||
// Create fresh
|
||||
db, err := ops.Open(path)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "error: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
db.Close()
|
||||
fmt.Printf("operations.db created at %s\n", path)
|
||||
}
|
||||
|
||||
// --- ops entity ---
|
||||
|
||||
func cmdOpsEntity(args []string) {
|
||||
if len(args) < 1 {
|
||||
fmt.Fprintln(os.Stderr, "usage: fn ops entity <add|list|show|delete> ...")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
switch args[0] {
|
||||
case "add":
|
||||
cmdOpsEntityAdd(args[1:])
|
||||
case "list":
|
||||
cmdOpsEntityList(args[1:])
|
||||
case "show":
|
||||
cmdOpsEntityShow(args[1:])
|
||||
case "delete":
|
||||
cmdOpsEntityDelete(args[1:])
|
||||
default:
|
||||
fmt.Fprintf(os.Stderr, "unknown entity command: %s\n", args[0])
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func cmdOpsEntityAdd(args []string) {
|
||||
var e ops.Entity
|
||||
e.Status = ops.StatusActive
|
||||
var tagsStr, metadataStr string
|
||||
|
||||
for i := 0; i < len(args); i++ {
|
||||
switch args[i] {
|
||||
case "--id":
|
||||
i++; e.ID = args[i]
|
||||
case "--name":
|
||||
i++; e.Name = args[i]
|
||||
case "--type-ref":
|
||||
i++; e.TypeRef = args[i]
|
||||
case "--source":
|
||||
i++; e.Source = args[i]
|
||||
case "--domain":
|
||||
i++; e.Domain = args[i]
|
||||
case "--status":
|
||||
i++; e.Status = ops.EntityStatus(args[i])
|
||||
case "--description":
|
||||
i++; e.Description = args[i]
|
||||
case "--tags":
|
||||
i++; tagsStr = args[i]
|
||||
case "--metadata":
|
||||
i++; metadataStr = args[i]
|
||||
case "--notes":
|
||||
i++; e.Notes = args[i]
|
||||
}
|
||||
}
|
||||
|
||||
if e.Name == "" || e.TypeRef == "" || e.Source == "" {
|
||||
fmt.Fprintln(os.Stderr, "required: --name, --type-ref, --source")
|
||||
os.Exit(1)
|
||||
}
|
||||
if e.ID == "" {
|
||||
e.ID = e.Name
|
||||
}
|
||||
|
||||
if tagsStr != "" {
|
||||
e.Tags = strings.Split(tagsStr, ",")
|
||||
}
|
||||
if metadataStr != "" {
|
||||
json.Unmarshal([]byte(metadataStr), &e.Metadata)
|
||||
}
|
||||
|
||||
opsDB := openOpsDB()
|
||||
defer opsDB.Close()
|
||||
|
||||
// Try to open registry for type snapshot
|
||||
regDB := tryOpenRegistryDB()
|
||||
if regDB != nil {
|
||||
defer regDB.Close()
|
||||
}
|
||||
|
||||
if err := ops.InsertEntityWithSnapshot(opsDB, regDB, &e); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "error: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Printf("Entity %s added\n", e.ID)
|
||||
}
|
||||
|
||||
func cmdOpsEntityList(args []string) {
|
||||
var domain string
|
||||
var status ops.EntityStatus
|
||||
for i := 0; i < len(args); i++ {
|
||||
switch args[i] {
|
||||
case "-d":
|
||||
i++; domain = args[i]
|
||||
case "-s":
|
||||
i++; status = ops.EntityStatus(args[i])
|
||||
}
|
||||
}
|
||||
|
||||
db := openOpsDB()
|
||||
defer db.Close()
|
||||
|
||||
entities, err := db.ListEntities(domain, status)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "error: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if len(entities) == 0 {
|
||||
fmt.Println("No entities.")
|
||||
return
|
||||
}
|
||||
|
||||
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
|
||||
fmt.Fprintln(w, "ID\tTYPE_REF\tSTATUS\tSOURCE\tDOMAIN")
|
||||
for _, e := range entities {
|
||||
fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\n", e.ID, e.TypeRef, e.Status, e.Source, e.Domain)
|
||||
}
|
||||
w.Flush()
|
||||
}
|
||||
|
||||
func cmdOpsEntityShow(args []string) {
|
||||
if len(args) < 1 {
|
||||
fmt.Fprintln(os.Stderr, "usage: fn ops entity show <id>")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
db := openOpsDB()
|
||||
defer db.Close()
|
||||
|
||||
e, err := db.GetEntity(args[0])
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "error: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
if e == nil {
|
||||
fmt.Fprintf(os.Stderr, "entity not found: %s\n", args[0])
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
fmt.Printf("ID: %s\n", e.ID)
|
||||
fmt.Printf("Name: %s\n", e.Name)
|
||||
fmt.Printf("Type ref: %s\n", e.TypeRef)
|
||||
fmt.Printf("Status: %s\n", e.Status)
|
||||
fmt.Printf("Source: %s\n", e.Source)
|
||||
fmt.Printf("Domain: %s\n", e.Domain)
|
||||
fmt.Printf("Description: %s\n", e.Description)
|
||||
fmt.Printf("Tags: %s\n", strings.Join(e.Tags, ", "))
|
||||
if len(e.Metadata) > 0 {
|
||||
meta, _ := json.MarshalIndent(e.Metadata, " ", " ")
|
||||
fmt.Printf("Metadata: %s\n", meta)
|
||||
}
|
||||
if e.Notes != "" {
|
||||
fmt.Printf("Notes: %s\n", e.Notes)
|
||||
}
|
||||
fmt.Printf("Created: %s\n", e.CreatedAt.Format("2006-01-02 15:04:05"))
|
||||
fmt.Printf("Updated: %s\n", e.UpdatedAt.Format("2006-01-02 15:04:05"))
|
||||
}
|
||||
|
||||
func cmdOpsEntityDelete(args []string) {
|
||||
if len(args) < 1 {
|
||||
fmt.Fprintln(os.Stderr, "usage: fn ops entity delete <id>")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
db := openOpsDB()
|
||||
defer db.Close()
|
||||
|
||||
if err := db.DeleteEntity(args[0]); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "error: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Printf("Entity %s deleted\n", args[0])
|
||||
}
|
||||
|
||||
// --- ops relation ---
|
||||
|
||||
func cmdOpsRelation(args []string) {
|
||||
if len(args) < 1 {
|
||||
fmt.Fprintln(os.Stderr, "usage: fn ops relation <add|list|show|delete> ...")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
switch args[0] {
|
||||
case "add":
|
||||
cmdOpsRelationAdd(args[1:])
|
||||
case "list":
|
||||
cmdOpsRelationList(args[1:])
|
||||
case "show":
|
||||
cmdOpsRelationShow(args[1:])
|
||||
case "delete":
|
||||
cmdOpsRelationDelete(args[1:])
|
||||
default:
|
||||
fmt.Fprintf(os.Stderr, "unknown relation command: %s\n", args[0])
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func cmdOpsRelationAdd(args []string) {
|
||||
var r ops.Relation
|
||||
r.Direction = ops.DirUnidirectional
|
||||
r.Status = ops.RelDesigned
|
||||
var tagsStr string
|
||||
|
||||
for i := 0; i < len(args); i++ {
|
||||
switch args[i] {
|
||||
case "--id":
|
||||
i++; r.ID = args[i]
|
||||
case "--name":
|
||||
i++; r.Name = args[i]
|
||||
case "--from":
|
||||
i++; r.FromEntity = args[i]
|
||||
case "--to":
|
||||
i++; r.ToEntity = args[i]
|
||||
case "--via":
|
||||
i++; r.Via = args[i]
|
||||
case "--direction":
|
||||
i++; r.Direction = ops.Direction(args[i])
|
||||
case "--status":
|
||||
i++; r.Status = ops.RelationStatus(args[i])
|
||||
case "--purity":
|
||||
i++; r.Purity = args[i]
|
||||
case "--weight":
|
||||
i++
|
||||
var w float64
|
||||
fmt.Sscanf(args[i], "%f", &w)
|
||||
r.Weight = &w
|
||||
case "--description":
|
||||
i++; r.Description = args[i]
|
||||
case "--tags":
|
||||
i++; tagsStr = args[i]
|
||||
case "--notes":
|
||||
i++; r.Notes = args[i]
|
||||
}
|
||||
}
|
||||
|
||||
if r.Name == "" || r.ToEntity == "" {
|
||||
fmt.Fprintln(os.Stderr, "required: --name, --to (and --from for simple relations)")
|
||||
os.Exit(1)
|
||||
}
|
||||
if r.ID == "" && r.FromEntity != "" {
|
||||
via := "semantic"
|
||||
if r.Via != "" {
|
||||
via = r.Via
|
||||
}
|
||||
r.ID = fmt.Sprintf("%s__to__%s__via__%s", r.FromEntity, r.ToEntity, via)
|
||||
}
|
||||
if tagsStr != "" {
|
||||
r.Tags = strings.Split(tagsStr, ",")
|
||||
}
|
||||
|
||||
db := openOpsDB()
|
||||
defer db.Close()
|
||||
|
||||
if err := ops.InsertRelationSafe(db, &r); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "error: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Printf("Relation %s added\n", r.ID)
|
||||
}
|
||||
|
||||
func cmdOpsRelationList(args []string) {
|
||||
var entityID string
|
||||
if len(args) > 0 {
|
||||
entityID = args[0]
|
||||
}
|
||||
|
||||
db := openOpsDB()
|
||||
defer db.Close()
|
||||
|
||||
rels, err := db.ListRelations(entityID)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "error: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if len(rels) == 0 {
|
||||
fmt.Println("No relations.")
|
||||
return
|
||||
}
|
||||
|
||||
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
|
||||
fmt.Fprintln(w, "ID\tNAME\tFROM\tTO\tVIA\tDIRECTION\tSTATUS")
|
||||
for _, r := range rels {
|
||||
from := r.FromEntity
|
||||
if from == "" {
|
||||
from = "(inputs)"
|
||||
}
|
||||
via := r.Via
|
||||
if via == "" {
|
||||
via = "-"
|
||||
}
|
||||
fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\t%s\n",
|
||||
r.ID, r.Name, from, r.ToEntity, via, r.Direction, r.Status)
|
||||
}
|
||||
w.Flush()
|
||||
}
|
||||
|
||||
func cmdOpsRelationShow(args []string) {
|
||||
if len(args) < 1 {
|
||||
fmt.Fprintln(os.Stderr, "usage: fn ops relation show <id>")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
db := openOpsDB()
|
||||
defer db.Close()
|
||||
|
||||
r, err := db.GetRelation(args[0])
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "error: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
if r == nil {
|
||||
fmt.Fprintf(os.Stderr, "relation not found: %s\n", args[0])
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
fmt.Printf("ID: %s\n", r.ID)
|
||||
fmt.Printf("Name: %s\n", r.Name)
|
||||
fmt.Printf("From: %s\n", r.FromEntity)
|
||||
fmt.Printf("To: %s\n", r.ToEntity)
|
||||
fmt.Printf("Via: %s\n", r.Via)
|
||||
fmt.Printf("Description: %s\n", r.Description)
|
||||
fmt.Printf("Purity: %s\n", r.Purity)
|
||||
fmt.Printf("Direction: %s\n", r.Direction)
|
||||
if r.Weight != nil {
|
||||
fmt.Printf("Weight: %.2f\n", *r.Weight)
|
||||
}
|
||||
fmt.Printf("Status: %s\n", r.Status)
|
||||
fmt.Printf("Tags: %s\n", strings.Join(r.Tags, ", "))
|
||||
if r.Notes != "" {
|
||||
fmt.Printf("Notes: %s\n", r.Notes)
|
||||
}
|
||||
|
||||
// Show inputs if any
|
||||
inputs, _ := db.GetRelationInputs(r.ID)
|
||||
if len(inputs) > 0 {
|
||||
fmt.Println("\nInputs:")
|
||||
for _, ri := range inputs {
|
||||
ord := ""
|
||||
if ri.Order != nil {
|
||||
ord = fmt.Sprintf(" (order: %d)", *ri.Order)
|
||||
}
|
||||
fmt.Printf(" %s [%s]%s\n", ri.EntityID, ri.Role, ord)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func cmdOpsRelationDelete(args []string) {
|
||||
if len(args) < 1 {
|
||||
fmt.Fprintln(os.Stderr, "usage: fn ops relation delete <id>")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
db := openOpsDB()
|
||||
defer db.Close()
|
||||
|
||||
if err := db.DeleteRelation(args[0]); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "error: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Printf("Relation %s deleted\n", args[0])
|
||||
}
|
||||
|
||||
// --- ops graph ---
|
||||
|
||||
func cmdOpsGraph() {
|
||||
db := openOpsDB()
|
||||
defer db.Close()
|
||||
|
||||
g, err := ops.GetEntityGraph(db)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "error: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if len(g.Entities) == 0 {
|
||||
fmt.Println("Empty graph.")
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println("Entities:")
|
||||
for _, e := range g.Entities {
|
||||
fmt.Printf(" [%s] (%s) status:%s source:%s\n", e.ID, e.TypeRef, e.Status, e.Source)
|
||||
}
|
||||
|
||||
if len(g.Relations) > 0 {
|
||||
fmt.Println("\nRelations:")
|
||||
for _, r := range g.Relations {
|
||||
via := ""
|
||||
if r.Via != "" {
|
||||
via = fmt.Sprintf(" via:%s", r.Via)
|
||||
}
|
||||
|
||||
inputs, hasInputs := g.Inputs[r.ID]
|
||||
if hasInputs {
|
||||
sources := make([]string, len(inputs))
|
||||
for i, ri := range inputs {
|
||||
sources[i] = fmt.Sprintf("%s[%s]", ri.EntityID, ri.Role)
|
||||
}
|
||||
fmt.Printf(" (%s) %s → %s%s\n",
|
||||
strings.Join(sources, " + "), r.Name, r.ToEntity, via)
|
||||
} else {
|
||||
from := r.FromEntity
|
||||
if from == "" {
|
||||
from = "?"
|
||||
}
|
||||
dir := "→"
|
||||
if r.Direction == ops.DirBidirectional {
|
||||
dir = "↔"
|
||||
}
|
||||
fmt.Printf(" %s %s %s %s%s\n", from, dir, r.Name, r.ToEntity, via)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- ops snapshot ---
|
||||
|
||||
func cmdOpsSnapshot(args []string) {
|
||||
if len(args) < 1 || args[0] != "list" {
|
||||
fmt.Fprintln(os.Stderr, "usage: fn ops snapshot list")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
db := openOpsDB()
|
||||
defer db.Close()
|
||||
|
||||
snaps, err := db.ListTypeSnapshots()
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "error: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if len(snaps) == 0 {
|
||||
fmt.Println("No type snapshots.")
|
||||
return
|
||||
}
|
||||
|
||||
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
|
||||
fmt.Fprintln(w, "ID\tVERSION\tLANG\tALGEBRAIC\tSNAPPED_AT")
|
||||
for _, s := range snaps {
|
||||
fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\n",
|
||||
s.ID, s.Version, s.Lang, s.Algebraic, s.SnappedAt.Format("2006-01-02 15:04"))
|
||||
}
|
||||
w.Flush()
|
||||
}
|
||||
|
||||
// --- helpers ---
|
||||
|
||||
func findOpsDB() string {
|
||||
dir, _ := os.Getwd()
|
||||
for {
|
||||
path := filepath.Join(dir, opsDBName)
|
||||
if _, err := os.Stat(path); err == nil {
|
||||
return path
|
||||
}
|
||||
parent := filepath.Dir(dir)
|
||||
if parent == dir {
|
||||
break
|
||||
}
|
||||
dir = parent
|
||||
}
|
||||
return filepath.Join(".", opsDBName)
|
||||
}
|
||||
|
||||
func openOpsDB() *ops.DB {
|
||||
path := findOpsDB()
|
||||
db, err := ops.Open(path)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "error opening operations.db: %v\n", err)
|
||||
fmt.Fprintln(os.Stderr, "Run 'fn ops init' first to create one.")
|
||||
os.Exit(1)
|
||||
}
|
||||
return db
|
||||
}
|
||||
|
||||
func tryOpenRegistryDB() *registry.DB {
|
||||
// Try FN_REGISTRY_ROOT env var first
|
||||
if envRoot := os.Getenv("FN_REGISTRY_ROOT"); envRoot != "" {
|
||||
path := filepath.Join(envRoot, dbName)
|
||||
if _, err := os.Stat(path); err == nil {
|
||||
db, err := registry.Open(path)
|
||||
if err == nil {
|
||||
return db
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Try root() (finds go.mod walking up from cwd)
|
||||
path := filepath.Join(root(), dbName)
|
||||
if _, err := os.Stat(path); err == nil {
|
||||
db, err := registry.Open(path)
|
||||
if err == nil {
|
||||
return db
|
||||
}
|
||||
}
|
||||
|
||||
// Try executable's directory
|
||||
if exe, err := os.Executable(); err == nil {
|
||||
exeDir := filepath.Dir(exe)
|
||||
path := filepath.Join(exeDir, dbName)
|
||||
if _, err := os.Stat(path); err == nil {
|
||||
db, err := registry.Open(path)
|
||||
if err == nil {
|
||||
return db
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user