Test the forge account provider's builder entries, and add the Go build module's manifest, ssh files and README (issues 1481, 1483), part 6 of 17
This commit is contained in:
@@ -0,0 +1,356 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"git.novox.be/novox/mesh-sdk/go/provider"
|
||||
|
||||
"gitea/internal/forgehttp"
|
||||
)
|
||||
|
||||
// Issue 1481 (novox/hq ADR 0466 §3): the build modules' keys on one forge user that reads and pushes nothing. The forge
|
||||
// is faked with the teams, members and keys it keeps, so a team's shape and a key's right can be read back.
|
||||
|
||||
type forgeKey struct {
|
||||
ID int64
|
||||
Title string
|
||||
Key string
|
||||
ReadOnly bool
|
||||
}
|
||||
|
||||
type teamForge struct {
|
||||
mu sync.Mutex
|
||||
users map[string]bool
|
||||
teams map[string]map[string]any // by name: the shape last sent
|
||||
ids map[string]int64
|
||||
members map[string][]string // by team name
|
||||
keys map[string][]forgeKey
|
||||
next int64
|
||||
}
|
||||
|
||||
func newTeamForge() *teamForge {
|
||||
return &teamForge{users: map[string]bool{}, teams: map[string]map[string]any{}, ids: map[string]int64{},
|
||||
members: map[string][]string{}, keys: map[string][]forgeKey{}, next: 10}
|
||||
}
|
||||
|
||||
var (
|
||||
teamRe = regexp.MustCompile(`^/api/v1/teams/(\d+)(?:/members/([^/]+))?$`)
|
||||
userKeyRe = regexp.MustCompile(`^/api/v1/(?:admin/)?users/([^/]+)/keys(?:/(\d+))?$`)
|
||||
)
|
||||
|
||||
func (f *teamForge) teamByID(id string) string {
|
||||
for name, n := range f.ids {
|
||||
if fmt.Sprint(n) == id {
|
||||
return name
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (f *teamForge) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
f.mu.Lock()
|
||||
defer f.mu.Unlock()
|
||||
body, _ := io.ReadAll(r.Body)
|
||||
var in map[string]any
|
||||
_ = json.Unmarshal(body, &in)
|
||||
reply := func(code int, v any) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(code)
|
||||
if v != nil {
|
||||
_ = json.NewEncoder(w).Encode(v)
|
||||
}
|
||||
}
|
||||
p := r.URL.Path
|
||||
if m := userKeyRe.FindStringSubmatch(p); m != nil {
|
||||
switch r.Method {
|
||||
case http.MethodGet:
|
||||
out := []map[string]any{}
|
||||
for _, k := range f.keys[m[1]] {
|
||||
out = append(out, map[string]any{"id": k.ID, "title": k.Title, "key": k.Key})
|
||||
}
|
||||
reply(200, out)
|
||||
case http.MethodPost:
|
||||
f.next++
|
||||
ro, _ := in["read_only"].(bool)
|
||||
f.keys[m[1]] = append(f.keys[m[1]], forgeKey{ID: f.next, Title: in["title"].(string), Key: in["key"].(string), ReadOnly: ro})
|
||||
reply(201, map[string]any{"id": f.next})
|
||||
case http.MethodDelete:
|
||||
var kept []forgeKey
|
||||
for _, k := range f.keys[m[1]] {
|
||||
if fmt.Sprint(k.ID) != m[2] {
|
||||
kept = append(kept, k)
|
||||
}
|
||||
}
|
||||
f.keys[m[1]] = kept
|
||||
reply(204, nil)
|
||||
}
|
||||
return
|
||||
}
|
||||
if m := teamRe.FindStringSubmatch(p); m != nil {
|
||||
name := f.teamByID(m[1])
|
||||
switch {
|
||||
case name == "":
|
||||
reply(404, map[string]any{"message": "no team"})
|
||||
case r.Method == http.MethodPatch && m[2] == "":
|
||||
f.teams[name] = in
|
||||
reply(200, map[string]any{})
|
||||
case r.Method == http.MethodPut:
|
||||
for _, u := range f.members[name] {
|
||||
if u == m[2] {
|
||||
reply(204, nil)
|
||||
return
|
||||
}
|
||||
}
|
||||
f.members[name] = append(f.members[name], m[2])
|
||||
reply(204, nil)
|
||||
default:
|
||||
http.Error(w, "unexpected "+r.Method+" "+p, 400)
|
||||
}
|
||||
return
|
||||
}
|
||||
switch {
|
||||
case r.Method == http.MethodGet && strings.HasPrefix(p, "/api/v1/users/"):
|
||||
if !f.users[strings.TrimPrefix(p, "/api/v1/users/")] {
|
||||
reply(404, map[string]any{"message": "no"})
|
||||
return
|
||||
}
|
||||
reply(200, map[string]any{"active": true})
|
||||
case r.Method == http.MethodPost && p == "/api/v1/admin/users":
|
||||
f.users[in["username"].(string)] = true
|
||||
reply(201, map[string]any{})
|
||||
case p == "/api/v1/orgs/novox/teams" && r.Method == http.MethodGet:
|
||||
out := []map[string]any{}
|
||||
for name, id := range f.ids {
|
||||
out = append(out, map[string]any{"id": id, "name": name})
|
||||
}
|
||||
reply(200, out)
|
||||
case p == "/api/v1/orgs/novox/teams" && r.Method == http.MethodPost:
|
||||
f.next++
|
||||
name := in["name"].(string)
|
||||
f.ids[name], f.teams[name] = f.next, in
|
||||
reply(201, map[string]any{"id": f.next})
|
||||
default:
|
||||
http.Error(w, "unexpected "+r.Method+" "+p, 400)
|
||||
}
|
||||
}
|
||||
|
||||
func (f *teamForge) titles(user string) []string {
|
||||
f.mu.Lock()
|
||||
defer f.mu.Unlock()
|
||||
out := []string{}
|
||||
for _, k := range f.keys[user] {
|
||||
out = append(out, k.Title)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
type teamRig struct {
|
||||
t *testing.T
|
||||
bus *fakeBus
|
||||
forge *teamForge
|
||||
svc *Service
|
||||
runner *fakeRunner
|
||||
}
|
||||
|
||||
func newTeamRig(t *testing.T) *teamRig {
|
||||
t.Helper()
|
||||
forge := newTeamForge()
|
||||
srv := httptest.NewServer(forge)
|
||||
t.Cleanup(srv.Close)
|
||||
dir := t.TempDir()
|
||||
pw, host, origins := filepath.Join(dir, "pw"), filepath.Join(dir, "host"), filepath.Join(dir, "origins")
|
||||
for p, c := range map[string]string{pw: "x\n", host: "novox.internal\n", origins: "https://git.novox.be\n"} {
|
||||
if err := os.WriteFile(p, []byte(c), 0o600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
runner := &fakeRunner{out: scanned}
|
||||
svc := NewService(Config{Node: "novox", HostFile: host, OriginsFile: origins, Port: 222,
|
||||
Forge: forgehttp.Client{URL: srv.URL, User: "mesh-admin", PasswordFile: pw}, Runner: runner, Log: func(string, ...any) {}})
|
||||
svc.loaded = true
|
||||
svc.loadedFrom["mesh-build-go"], svc.loadedFrom["mesh-build-node"] = true, true
|
||||
return &teamRig{t: t, bus: newBus(), forge: forge, svc: svc, runner: runner}
|
||||
}
|
||||
|
||||
func (r *teamRig) pass() []provider.Outcome {
|
||||
r.t.Helper()
|
||||
out, err := r.svc.Provider(r.bus).Pass(context.Background())
|
||||
if err != nil {
|
||||
r.t.Fatal(err)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// bind makes module on node bind gitea's forge-account in the assignments mirror.
|
||||
func (r *teamRig) bind(node, module string) {
|
||||
r.bus.set(provider.AssignmentsBucket, node+"."+module, []byte(fmt.Sprintf(
|
||||
`{"node":%q,"module":%q,"provisioning":{"provisions":[{"provision":"forge-account","provider":"novox","provider-module":"gitea","as":"mesh_%s"}]}}`,
|
||||
node, module, strings.ReplaceAll(node+"_"+module, "-", "_"))))
|
||||
}
|
||||
|
||||
func (r *teamRig) unbind(node, module string) {
|
||||
r.bus.mu.Lock()
|
||||
delete(r.bus.buckets[provider.AssignmentsBucket], node+"."+module)
|
||||
r.bus.mu.Unlock()
|
||||
}
|
||||
|
||||
// key reports a key in module's state keys, of the kind and for the account given.
|
||||
func (r *teamRig) key(module, node, account, kind, public string) {
|
||||
b, _ := json.Marshal(map[string]string{"node": node, "account": account, "kind": kind, "public": public,
|
||||
"fingerprint": "SHA256:x", "at": "2026-10-20T10:00:00Z"})
|
||||
r.svc.SourceKeyChanged(module, node+"."+account, b)
|
||||
}
|
||||
|
||||
const (
|
||||
pubGo = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG"
|
||||
pubNode = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN"
|
||||
pubOps = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO"
|
||||
)
|
||||
|
||||
// 1481/AC1: an entry from mesh-build-go on ace: the forge user nox-builder and the team builders are ensured, the team's
|
||||
// units are code, read only, on every repository, and the entry's values name nox-builder. The key is registered
|
||||
// read-only under its title.
|
||||
func TestABuildModulesEntryIsServedAsNoxBuilderInAReadOnlyTeam(t *testing.T) {
|
||||
r := newTeamRig(t)
|
||||
r.bind("ace", "mesh-build-go")
|
||||
r.key("mesh-build-go", "ace", "mesh-build-go", "builder", pubGo+" mesh-build-go@ace (mesh)")
|
||||
r.pass()
|
||||
|
||||
if !r.forge.users["nox-builder"] || r.forge.users["nox-agent"] {
|
||||
t.Errorf("users = %v, want nox-builder alone", r.forge.users)
|
||||
}
|
||||
shape := r.forge.teams["builders"]
|
||||
if shape == nil {
|
||||
t.Fatalf("no team builders: %v", r.forge.teams)
|
||||
}
|
||||
units, _ := json.Marshal(shape["units_map"])
|
||||
if shape["permission"] != "read" || shape["includes_all_repositories"] != true || shape["can_create_org_repo"] != false || string(units) != `{"repo.code":"read"}` {
|
||||
t.Errorf("the team's shape = %v (units %s), want code read on every repository and no other right", shape, units)
|
||||
}
|
||||
if got := strings.Join(r.forge.members["builders"], ","); got != "nox-builder" || len(r.forge.members["agents"]) != 0 {
|
||||
t.Errorf("members = %v", r.forge.members)
|
||||
}
|
||||
keys := r.forge.keys["nox-builder"]
|
||||
if len(keys) != 1 || keys[0].Title != "mesh:ace.mesh-build-go" || !keys[0].ReadOnly {
|
||||
t.Errorf("nox-builder's keys = %+v, want one read-only key titled mesh:ace.mesh-build-go", keys)
|
||||
}
|
||||
e := r.bus.entry(t, "ace.mesh-build-go.forge-account")
|
||||
if e.Values["user"] != "nox-builder" || e.Values["ssh_alias"] != "mesh-forge" || e.Values["host_key"] != "ssh-ed25519 AAAAC3HOSTKEY" {
|
||||
t.Errorf("values = %v", e.Values)
|
||||
}
|
||||
}
|
||||
|
||||
// 1481/AC2: an entry from ssh-client is what it was: nox-agent, the agents team, a key that may write. (The existing
|
||||
// tests of the provider pass unchanged.)
|
||||
func TestAnSshClientEntryIsStillTheAgentsWithAKeyThatMayWrite(t *testing.T) {
|
||||
r := newTeamRig(t)
|
||||
// the agents team exists, as the forge has it
|
||||
r.forge.ids["agents"], r.forge.teams["agents"] = 5, map[string]any{}
|
||||
r.bind("g14", "ssh-client")
|
||||
r.key("ssh-client", "g14", "nox", "machine", pubA+" nox@g14 (mesh)")
|
||||
r.pass()
|
||||
if e := r.bus.entry(t, "g14.ssh-client.forge-account"); e.Values["user"] != "nox-agent" {
|
||||
t.Errorf("values = %v", e.Values)
|
||||
}
|
||||
keys := r.forge.keys["nox-agent"]
|
||||
if len(keys) != 1 || keys[0].Title != "mesh:g14.nox" || keys[0].ReadOnly {
|
||||
t.Errorf("nox-agent's keys = %+v, want one key that may write", keys)
|
||||
}
|
||||
if r.forge.teams["builders"] != nil || r.forge.users["nox-builder"] {
|
||||
t.Errorf("a machine's entry made the builders' account or team")
|
||||
}
|
||||
if got := strings.Join(r.forge.members["agents"], ","); got != "nox-agent" {
|
||||
t.Errorf("agents = %v", r.forge.members)
|
||||
}
|
||||
}
|
||||
|
||||
// 1481/AC3: a builder entry gone: only the keys titled mesh:<node>.mesh-build-go are removed; the user and the team
|
||||
// stay, and so do the other build module's key on that machine and a key of the operator's.
|
||||
func TestABuilderEntryGoneRemovesItsOwnKeyAndNothingElse(t *testing.T) {
|
||||
r := newTeamRig(t)
|
||||
r.bind("ace", "mesh-build-go")
|
||||
r.bind("ace", "mesh-build-node")
|
||||
r.key("mesh-build-go", "ace", "mesh-build-go", "builder", pubGo+" mesh-build-go@ace (mesh)")
|
||||
r.key("mesh-build-node", "ace", "mesh-build-node", "builder", pubNode+" mesh-build-node@ace (mesh)")
|
||||
r.pass()
|
||||
r.forge.mu.Lock()
|
||||
r.forge.keys["nox-builder"] = append(r.forge.keys["nox-builder"], forgeKey{ID: 99, Title: "my own", Key: pubOps})
|
||||
r.forge.mu.Unlock()
|
||||
if got := strings.Join(r.forge.titles("nox-builder"), ","); got != "mesh:ace.mesh-build-go,mesh:ace.mesh-build-node,my own" {
|
||||
t.Fatalf("before: %s", got)
|
||||
}
|
||||
r.unbind("ace", "mesh-build-go")
|
||||
r.pass()
|
||||
if got := strings.Join(r.forge.titles("nox-builder"), ","); got != "mesh:ace.mesh-build-node,my own" {
|
||||
t.Errorf("after: %s, want only the go module's key gone", got)
|
||||
}
|
||||
if !r.forge.users["nox-builder"] || r.forge.teams["builders"] == nil || len(r.forge.members["builders"]) != 1 {
|
||||
t.Errorf("the user or the team went: users %v, members %v", r.forge.users, r.forge.members)
|
||||
}
|
||||
if r.bus.has("ace.mesh-build-go.forge-account") {
|
||||
t.Errorf("the entry stayed")
|
||||
}
|
||||
}
|
||||
|
||||
// A build module and ssh-client on one machine each have their own entry, keys, user and team.
|
||||
func TestABuildModuleAndSshClientOnOneMachineDoNotShareAnything(t *testing.T) {
|
||||
r := newTeamRig(t)
|
||||
r.forge.ids["agents"], r.forge.teams["agents"] = 5, map[string]any{}
|
||||
r.bind("ace", "ssh-client")
|
||||
r.bind("ace", "mesh-build-go")
|
||||
r.key("ssh-client", "ace", "nox", "machine", pubA+" nox@ace (mesh)")
|
||||
r.key("mesh-build-go", "ace", "mesh-build-go", "builder", pubGo+" mesh-build-go@ace (mesh)")
|
||||
r.pass()
|
||||
if got := strings.Join(r.forge.titles("nox-agent"), ","); got != "mesh:ace.nox" {
|
||||
t.Errorf("nox-agent: %s", got)
|
||||
}
|
||||
if got := strings.Join(r.forge.titles("nox-builder"), ","); got != "mesh:ace.mesh-build-go" {
|
||||
t.Errorf("nox-builder: %s", got)
|
||||
}
|
||||
// ssh-client's entry gone: nox-builder's key stays.
|
||||
r.unbind("ace", "ssh-client")
|
||||
r.pass()
|
||||
if got := strings.Join(r.forge.titles("nox-builder"), ","); got != "mesh:ace.mesh-build-go" {
|
||||
t.Errorf("nox-builder after ssh-client left: %s", got)
|
||||
}
|
||||
if got := strings.Join(r.forge.titles("nox-agent"), ","); got != "" {
|
||||
t.Errorf("nox-agent after ssh-client left: %s", got)
|
||||
}
|
||||
}
|
||||
|
||||
// A build module's entry waits for its own module's builder key: a machine key of the same node, a key of the wrong
|
||||
// kind in the build module's state, or the other build module's key is none.
|
||||
func TestABuildModulesEntryWaitsForItsOwnBuilderKey(t *testing.T) {
|
||||
r := newTeamRig(t)
|
||||
r.bind("ace", "mesh-build-go")
|
||||
r.key("ssh-client", "ace", "nox", "machine", pubA) // a machine's key
|
||||
r.key("mesh-build-go", "ace", "mesh-build-go", "machine", pubGo) // the wrong kind in the build module's state
|
||||
r.key("mesh-build-node", "ace", "mesh-build-node", "builder", pubNode) // the other build module's
|
||||
out := r.pass()
|
||||
if r.bus.has("ace.mesh-build-go.forge-account") || len(r.forge.users) != 0 || len(r.forge.teams) != 0 {
|
||||
t.Fatalf("something was made without a builder key: users %v teams %v", r.forge.users, r.forge.teams)
|
||||
}
|
||||
if len(out) != 1 || !strings.Contains(out[0].Why, "mesh-build-go") || !strings.Contains(out[0].Why, "builder key") {
|
||||
t.Errorf("the wait should name the module and the key: %+v", out)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTheKindOfAModulesKeyAndItsForgeUser(t *testing.T) {
|
||||
for module, want := range map[string]string{"mesh-build-go": "nox-builder", "mesh-build-node": "nox-builder", "ssh-client": "nox-agent", "anything-else": "nox-agent"} {
|
||||
got, err := (&Service{}).valuesForUser("ace", module)
|
||||
if err != nil || got != want {
|
||||
t.Errorf("%s: %q, %v; want %q", module, got, err, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
Host ${bound:forge-account:ssh_alias}
|
||||
HostName ${bound:forge-account:ssh_host}
|
||||
Port ${bound:forge-account:ssh_port}
|
||||
HostKeyAlias ${bound:forge-account:ssh_alias}
|
||||
User git
|
||||
IdentityFile ~/.ssh/mesh_ed25519
|
||||
IdentitiesOnly yes
|
||||
@@ -0,0 +1,2 @@
|
||||
[url "ssh://${bound:forge-account:ssh_alias}/"]
|
||||
insteadOf = ${bound:forge-account:https_base}
|
||||
@@ -0,0 +1 @@
|
||||
${bound:forge-account:ssh_alias} ${bound:forge-account:host_key}
|
||||
@@ -0,0 +1,21 @@
|
||||
module meshbuildgo
|
||||
|
||||
go 1.26.0
|
||||
|
||||
require (
|
||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261020203945-1572e50e0ef0
|
||||
github.com/nats-io/nats-server/v2 v2.11.17
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/antithesishq/antithesis-sdk-go v0.7.0-default-no-op // indirect
|
||||
github.com/google/go-tpm v0.9.8 // indirect
|
||||
github.com/klauspost/compress v1.20.0 // indirect
|
||||
github.com/minio/highwayhash v1.0.4 // indirect
|
||||
github.com/nats-io/jwt/v2 v2.8.1 // indirect
|
||||
github.com/nats-io/nkeys v0.4.16 // indirect
|
||||
github.com/nats-io/nuid v1.0.1 // indirect
|
||||
golang.org/x/crypto v0.57.0 // indirect
|
||||
golang.org/x/sys v0.48.0 // indirect
|
||||
golang.org/x/time v0.15.0 // indirect
|
||||
)
|
||||
@@ -0,0 +1,29 @@
|
||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261020200715-9e296295c05e h1:uf2o2A6dBOrISDUVw6U7+fv8ylwwF+UfRrGWKIQMFOE=
|
||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261020200715-9e296295c05e/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261020203945-1572e50e0ef0 h1:mr9HyARwzeZT7FxFHv1Xc2z7QErvDZ/F/pBMIpjGSd0=
|
||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261020203945-1572e50e0ef0/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||
github.com/antithesishq/antithesis-sdk-go v0.7.0-default-no-op h1:Z/MZK75wC/NSrkgqeNIa7jexam9uWzhLmFTSCPI/kn0=
|
||||
github.com/antithesishq/antithesis-sdk-go v0.7.0-default-no-op/go.mod h1:FQyySiasQQM8735Ddel3MRojmy4dA1IqCeyJ5jmPMbI=
|
||||
github.com/google/go-tpm v0.9.8 h1:slArAR9Ft+1ybZu0lBwpSmpwhRXaa85hWtMinMyRAWo=
|
||||
github.com/google/go-tpm v0.9.8/go.mod h1:h9jEsEECg7gtLis0upRBQU+GhYVH6jMjrFxI8u6bVUY=
|
||||
github.com/klauspost/compress v1.20.0 h1:a3C1ke2ohxFymNlb2HWAHjDeKCI90scRskErZkR0ezA=
|
||||
github.com/klauspost/compress v1.20.0/go.mod h1:LUdAzn7YLVvxLpc7y3V1m40wESHTgc1422pwwBSKYuI=
|
||||
github.com/minio/highwayhash v1.0.4 h1:asJizugGgchQod2ja9NJlGOWq4s7KsAWr5XUc9Clgl4=
|
||||
github.com/minio/highwayhash v1.0.4/go.mod h1:GGYsuwP/fPD6Y9hMiXuapVvlIUEhFhMTh0rxU3ik1LQ=
|
||||
github.com/nats-io/jwt/v2 v2.8.1 h1:V0xpGuD/N8Mi+fQNDynXohVvp7ZztevW5io8CUWlPmU=
|
||||
github.com/nats-io/jwt/v2 v2.8.1/go.mod h1:nWnOEEiVMiKHQpnAy4eXlizVEtSfzacZ1Q43LIRavZg=
|
||||
github.com/nats-io/nats-server/v2 v2.11.17 h1:GKEghcFK6A+aFx11Yf1LjgLC3txAwvyhnYzhBIQZA8I=
|
||||
github.com/nats-io/nats-server/v2 v2.11.17/go.mod h1:B1sFVz4StNosQ903ak4N1G01Fl/9f8e06mXpFIE2K24=
|
||||
github.com/nats-io/nats.go v1.54.0 h1:vsXoOxjHp/GmPUN+EcI7uOf/uB+iAP+kEsAFNQN0yzA=
|
||||
github.com/nats-io/nats.go v1.54.0/go.mod h1:y+DZoD1oBOYfZTU681eTUiUjI0vbqYGixNVFHcjHJ0k=
|
||||
github.com/nats-io/nkeys v0.4.16 h1:rd5oAuLOb8mnAycB0xleuEBNS1pVVnN0fv/FF34Eypg=
|
||||
github.com/nats-io/nkeys v0.4.16/go.mod h1:llLgWoI0o4z/Q57q2R1kHfmocyhGV6VG/U18Glg1Afs=
|
||||
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
|
||||
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
|
||||
golang.org/x/crypto v0.57.0 h1:3ZVCjf8Ggz7zneR/EHRVx68Ctf+2pmIMP2UFhh9cC6M=
|
||||
golang.org/x/crypto v0.57.0/go.mod h1:Fdz0i5U6CoizGwLda9DttjSk6qlZo25zYNtR+ycvuZA=
|
||||
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.48.0 h1:bbX/i/6MgT9BVLM9RT1thmxL04yeTAhbEz4SyadbXoo=
|
||||
golang.org/x/sys v0.48.0/go.mod h1:hNLxWAXmnKAxqDtdwIYC4bM9oQPEecfsnNMuSxOs3og=
|
||||
golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U=
|
||||
golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno=
|
||||
@@ -0,0 +1,142 @@
|
||||
{
|
||||
"module": "mesh-build-go",
|
||||
"version": "1",
|
||||
"runs-as": "mesh-build-go",
|
||||
"capabilities": [
|
||||
"package-manager",
|
||||
"user-namespaces"
|
||||
],
|
||||
"state": [
|
||||
"toolchains",
|
||||
"keys",
|
||||
"steps"
|
||||
],
|
||||
"tools": [
|
||||
"build_go_fetch",
|
||||
"build_go_test",
|
||||
"build_go_digest",
|
||||
"build_go_build",
|
||||
"build_go_status",
|
||||
"build_go_health"
|
||||
],
|
||||
"requires": [
|
||||
"forge-account"
|
||||
],
|
||||
"own-secrets": {
|
||||
"broker": "${dir:state}/broker"
|
||||
},
|
||||
"secrets-owner": "mesh-build-go",
|
||||
"runtime-health": {
|
||||
"kind": "tool",
|
||||
"tool": "build_go_health",
|
||||
"interval": "60s",
|
||||
"timeout": "10s",
|
||||
"looks": 2,
|
||||
"grace": "60s"
|
||||
},
|
||||
"resources": [
|
||||
{
|
||||
"id": "account",
|
||||
"type": "user",
|
||||
"name": "mesh-build-go",
|
||||
"shell": "/usr/bin/nologin",
|
||||
"home": "/var/lib/mesh-build-go"
|
||||
},
|
||||
{
|
||||
"id": "state",
|
||||
"type": "directory",
|
||||
"mode": "0700",
|
||||
"owner": "mesh-build-go",
|
||||
"place": ".",
|
||||
"unknown": "program"
|
||||
},
|
||||
{
|
||||
"id": "work",
|
||||
"type": "directory",
|
||||
"path": "/var/lib/mesh-build-go-work",
|
||||
"mode": "0700",
|
||||
"owner": "mesh-build-go",
|
||||
"unknown": "program"
|
||||
},
|
||||
{
|
||||
"id": "cache",
|
||||
"type": "directory",
|
||||
"path": "/var/lib/mesh-build-go-cache",
|
||||
"mode": "0700",
|
||||
"owner": "mesh-build-go",
|
||||
"unknown": "program"
|
||||
},
|
||||
{
|
||||
"id": "ssh-dir",
|
||||
"type": "directory",
|
||||
"path": "${dir:state}/.ssh",
|
||||
"mode": "0700",
|
||||
"owner": "mesh-build-go",
|
||||
"unknown": "program"
|
||||
},
|
||||
{
|
||||
"id": "ssh-config",
|
||||
"type": "file",
|
||||
"path": "${dir:ssh-dir}/config",
|
||||
"owner": "mesh-build-go",
|
||||
"mode": "0600",
|
||||
"template": "files/forge"
|
||||
},
|
||||
{
|
||||
"id": "known-hosts",
|
||||
"type": "file",
|
||||
"path": "${dir:ssh-dir}/known_hosts",
|
||||
"owner": "mesh-build-go",
|
||||
"mode": "0644",
|
||||
"template": "files/known_hosts"
|
||||
},
|
||||
{
|
||||
"id": "git-config",
|
||||
"type": "file",
|
||||
"path": "${dir:state}/.config/git/config",
|
||||
"owner": "mesh-build-go",
|
||||
"mode": "0644",
|
||||
"template": "files/gitconfig"
|
||||
},
|
||||
{
|
||||
"id": "go",
|
||||
"type": "package",
|
||||
"package": "go"
|
||||
},
|
||||
{
|
||||
"id": "git",
|
||||
"type": "package",
|
||||
"package": "git"
|
||||
},
|
||||
{
|
||||
"id": "openssh",
|
||||
"type": "package",
|
||||
"package": "openssh"
|
||||
},
|
||||
{
|
||||
"id": "postgresql",
|
||||
"type": "package",
|
||||
"package": "postgresql"
|
||||
}
|
||||
],
|
||||
"build": {
|
||||
"artifacts": [
|
||||
{
|
||||
"name": "tools",
|
||||
"kind": "bundle",
|
||||
"language": "go",
|
||||
"system": "arch",
|
||||
"from": "cmd/mesh-build-go",
|
||||
"binary": "mesh-build-go",
|
||||
"loads": [
|
||||
"mesh-build-go"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "files",
|
||||
"kind": "archive",
|
||||
"from": "files"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user