bootstrap: iniciar bitácora operativa

This commit is contained in:
2026-08-13 00:43:15 -06:00
commit 1ecfb022b1
85 changed files with 7967 additions and 0 deletions
+227
View File
@@ -0,0 +1,227 @@
package clusterlog
import (
"fmt"
"path/filepath"
)
func (a *App) runProject(root string, args []string) error {
if len(args) == 0 {
return NewError(ExitUsage, "project_usage", "uso: project create | list", nil)
}
switch args[0] {
case "create":
fs := newFlagSet("project create")
title := fs.String("title", "", "título")
description := fs.String("description", "", "resumen")
objective := fs.String("objective", "", "objetivo")
owners := fs.String("owners", "", "administradores separados por coma")
targetDate := fs.String("target-date", "", "fecha YYYY-MM-DD")
status := fs.String("status", "", "planned|active|blocked|done|cancelled")
tags := fs.String("tags", "", "tags separados por coma")
systems := fs.String("systems", "", "sistemas separados por coma")
scope := fs.String("scope", "", "elementos de alcance separados por coma")
outOfScope := fs.String("out-of-scope", "", "elementos fuera de alcance separados por coma")
milestones := fs.String("milestones", "", "hitos separados por coma")
success := fs.String("success", "", "criterios de éxito separados por coma")
bodyFile := fs.String("body-file", "", "Markdown o -")
fromJSON := fs.String("from-json", "", "JSON o -")
if err := fs.Parse(args[1:]); err != nil {
return NewError(ExitUsage, "invalid_flags", err.Error(), nil)
}
var input ProjectInput
if *fromJSON != "" {
if err := decodeJSONInput(*fromJSON, a.Options.Stdin, &input); err != nil {
return err
}
}
if wasSet(fs, "title") {
input.Title = *title
}
if wasSet(fs, "description") {
input.Description = *description
}
if wasSet(fs, "objective") {
input.Objective = *objective
}
if wasSet(fs, "owners") {
input.Owners = splitCSV(*owners)
}
if wasSet(fs, "target-date") {
input.TargetDate = *targetDate
}
if wasSet(fs, "status") {
input.Status = *status
}
if wasSet(fs, "tags") {
input.Tags = splitCSV(*tags)
}
if wasSet(fs, "systems") {
input.Systems = splitCSV(*systems)
}
if wasSet(fs, "scope") {
input.Scope = splitCSV(*scope)
}
if wasSet(fs, "out-of-scope") {
input.OutOfScope = splitCSV(*outOfScope)
}
if wasSet(fs, "milestones") {
input.Milestones = splitCSV(*milestones)
}
if wasSet(fs, "success") {
input.Success = splitCSV(*success)
}
if *bodyFile != "" {
data, err := readAll(*bodyFile, a.Options.Stdin)
if err != nil {
return err
}
input.BodyMarkdown = string(data)
}
identity, err := a.ResolveIdentity(root)
if err != nil {
return err
}
result, warnings, err := a.createProject(root, identity, input)
if err != nil {
return err
}
if a.Options.DryRun {
return a.printResult("project.create", result, warnings...)
}
state, changed, statePath, err := a.Sync(root, false)
if err != nil {
return err
}
paths := []string{filepath.FromSlash(result["path"].(string))}
if changed {
paths = append(paths, statePath)
}
sha, err := a.commitPaths(root, identity, fmt.Sprintf("project(%s): %s", result["project_id"], input.Title), paths)
if err != nil {
return err
}
result["commit"] = sha
result["active_projects"] = state.Stats.ActiveProjects
return a.printResult("project.create", result, warnings...)
case "list":
fs := newFlagSet("project list")
status := fs.String("status", "", "filtrar por estado")
if err := fs.Parse(args[1:]); err != nil {
return NewError(ExitUsage, "invalid_flags", err.Error(), nil)
}
state, err := a.BuildState(root)
if err != nil {
return err
}
items := []ProjectState{}
for _, item := range state.Projects {
if *status == "" || item.Status == *status {
items = append(items, item)
}
}
return a.printResult("project.list", map[string]any{"count": len(items), "items": items})
default:
return NewError(ExitUsage, "project_usage", "uso: project create | list", args[0])
}
}
func (a *App) runContent(root string, args []string) error {
if len(args) == 0 || args[0] != "create" {
return NewError(ExitUsage, "content_usage", "uso: content create --section documentacion|manuales --title ... --body-file ...", nil)
}
fs := newFlagSet("content create")
section := fs.String("section", "", "documentacion|manuales")
title := fs.String("title", "", "título")
description := fs.String("description", "", "resumen")
tags := fs.String("tags", "", "tags separados por coma")
systems := fs.String("systems", "", "sistemas separados por coma")
bodyFile := fs.String("body-file", "", "Markdown o -")
weight := fs.Int("weight", 0, "orden opcional")
fromJSON := fs.String("from-json", "", "JSON o -")
if err := fs.Parse(args[1:]); err != nil {
return NewError(ExitUsage, "invalid_flags", err.Error(), nil)
}
var input ContentInput
if *fromJSON != "" {
if err := decodeJSONInput(*fromJSON, a.Options.Stdin, &input); err != nil {
return err
}
}
if wasSet(fs, "section") {
input.Section = *section
}
if wasSet(fs, "title") {
input.Title = *title
}
if wasSet(fs, "description") {
input.Description = *description
}
if wasSet(fs, "tags") {
input.Tags = splitCSV(*tags)
}
if wasSet(fs, "systems") {
input.Systems = splitCSV(*systems)
}
if wasSet(fs, "weight") {
input.Weight = *weight
}
if *bodyFile != "" {
data, err := readAll(*bodyFile, a.Options.Stdin)
if err != nil {
return err
}
input.BodyMarkdown = string(data)
}
identity, err := a.ResolveIdentity(root)
if err != nil {
return err
}
result, warnings, err := a.createContent(root, identity, input)
if err != nil {
return err
}
if a.Options.DryRun {
return a.printResult("content.create", result, warnings...)
}
createdPath := filepath.FromSlash(result["path"].(string))
sha, err := a.commitPaths(root, identity, fmt.Sprintf("%s(%s): %s", input.Section, result["content_id"], input.Title), []string{createdPath})
if err != nil {
return err
}
result["commit"] = sha
return a.printResult("content.create", result, warnings...)
}
func (a *App) runSync(root string, args []string) error {
fs := newFlagSet("sync")
check := fs.Bool("check", false, "no escribe; falla si el estado generado está desactualizado")
if err := fs.Parse(args); err != nil {
return NewError(ExitUsage, "invalid_flags", err.Error(), nil)
}
state, changed, path, err := a.Sync(root, *check)
if err != nil {
return err
}
return a.printResult("sync", map[string]any{
"path": filepath.ToSlash(path), "changed": changed, "check": *check,
"source_sha256": state.SourceSHA256, "stats": state.Stats,
})
}
func (a *App) runVerify(root string, args []string) error {
if len(args) == 0 || args[0] != "commit" {
return NewError(ExitUsage, "verify_usage", "uso: verify commit [SHA]", nil)
}
sha := "HEAD"
if len(args) > 1 {
sha = args[1]
}
if len(args) > 2 {
return NewError(ExitUsage, "unexpected_arguments", "uso: verify commit [SHA]", args[2:])
}
if err := verifyCommit(root, sha); err != nil {
return err
}
return a.printResult("verify.commit", map[string]any{"commit": sha, "valid": true})
}