Files
mesh-catalog/modules/gitea/cmd/repositories/main.go
T

70 lines
2.7 KiB
Go

// 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
}