// repositories: the organisation's repositories from the gitea module's config value `repositories` (novox/hq // ADR 0428, issue 1124). `repositories sync` is a run-once process the node-engine runs when the module starts and // again whenever the value changes: it reads the file the mesh renders from that value and, with the setup login, // makes every listed repository that does not exist under that login's own account, protects its main, writes // main's first commit, narrows main's rule and only then moves it into the organisation; one that exists only has // its main rule put right. A repository the value does not list is never touched. It keeps what it did, per // repository, in the outcome file, says each change on stdout in one line and never a secret, and exits 1 when a // step was refused, its last stderr lines naming each (outcome.go Report): the node-engine's gate on a run-once // step (ADR 0136) reports it and runs it again on every apply until it passes. package main import ( "context" "fmt" "os" "time" "gitea/internal/forgehttp" ) func main() { if len(os.Args) != 2 || os.Args[1] != "sync" { fmt.Fprintln(os.Stderr, "usage: repositories sync") os.Exit(2) } os.Exit(runSync()) } func runSync() int { forge, missing := forgehttp.FromEnv() file, outcome := os.Getenv("MESH_REPOSITORIES_FILE"), os.Getenv("MESH_REPOSITORIES_OUTCOME") for k, v := range map[string]string{"MESH_REPOSITORIES_FILE": file, "MESH_REPOSITORIES_OUTCOME": outcome} { if v == "" { missing = append(missing, k) } } if forge.TokenFile != "" { // The tools' account cannot make or protect a repository, and must not (ADR 0428 ยง8). missing = append(missing, "the setup login (MESH_GITEA_TOKEN_FILE is set instead)") } if len(missing) > 0 { fmt.Fprintf(os.Stderr, "repositories: %v not set by the manifest; nothing was changed\n", missing) return 1 } toolsAccount := os.Getenv("MESH_GITEA_TOOLS_USER") if toolsAccount == "" { toolsAccount = forgehttp.ToolsAccount } s := &Syncer{Forge: forge, Org: "novox", Admin: forge.User, Tools: toolsAccount, Log: func(format string, a ...any) { fmt.Printf(format+"\n", a...) }, WaitFor: 3 * time.Minute, Poll: 5 * time.Second} value, err := ReadValue(file) if err != nil { o := Outcome{At: time.Now().UTC(), Org: s.Org, Failed: err.Error()} _ = WriteOutcome(outcome, o) return Report(o, os.Stderr) } ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute) defer cancel() o, _ := s.Sync(ctx, value) // a forge not reachable is in o.Failed werr := WriteOutcome(outcome, o) if werr != nil { fmt.Fprintln(os.Stderr, "repositories: the outcome could not be kept:", werr) } if code := Report(o, os.Stderr); code != 0 || werr == nil { return code } return 1 }