A-71 (builder): toolchain-wait-minutes reaches the builder as a file the manifest declares, read at its start, never its environment
The manifest gains the file resource toolchain-wait (content ${config:toolchain-wait-minutes}) and names its path in
MESH_BUILDER_TOOLCHAIN_WAIT_FILE; main.go's toolchainWaitOf reads it.
Builds: design 47 G §672, A-71 (G-20)
Claude-Session: https://claude.ai/code/session_01NcbgfGKaxw984XyAPfLm4h
This commit is contained in:
@@ -7,8 +7,10 @@ package main
|
||||
import (
|
||||
"cmp"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/fs"
|
||||
"log"
|
||||
"maps"
|
||||
"os"
|
||||
@@ -200,13 +202,9 @@ func run(ctx context.Context, getenv func(string) string, logOut io.Writer) erro
|
||||
b.Numbers = build.VersionNumbers{N: numberer}
|
||||
// The builder's toolchain waits (design 47 G §672): its state waits, and wait-held and wait-ended on its stream waits; its
|
||||
// bound is its setting toolchain-wait-minutes.
|
||||
waitBound := defaultToolchainWait
|
||||
if v := strings.TrimSpace(getenv("MESH_BUILDER_TOOLCHAIN_WAIT_MINUTES")); v != "" {
|
||||
n, err := strconv.Atoi(v)
|
||||
if err != nil || n < 1 {
|
||||
return fmt.Errorf("MESH_BUILDER_TOOLCHAIN_WAIT_MINUTES %q is not a number of minutes", v)
|
||||
}
|
||||
waitBound = time.Duration(n) * time.Minute
|
||||
waitBound, err := toolchainWaitOf(getenv("MESH_BUILDER_TOOLCHAIN_WAIT_FILE"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
waitsBucket := process.AwaitBucket(ctx, js, conn, build.WaitsBucket, "the toolchain waits are kept in memory only, and builder_waits shows none", logger.Printf)
|
||||
b.Waits = newWaitsKept(waitsBucket, func(ctx context.Context, event, id string, body any) error {
|
||||
@@ -569,3 +567,28 @@ func (s *sourceHeads) head(_ context.Context, rev uint64, repository string) (st
|
||||
}
|
||||
return "", nil
|
||||
}
|
||||
|
||||
// toolchainWaitOf reads the toolchain wait's setting, toolchain-wait-minutes, from the file the manifest declares with the
|
||||
// content ${config:toolchain-wait-minutes} (design 47 G §672, A-71), at the builder's start: no file, or an empty one, is
|
||||
// the default; a content that is no number of minutes is an error naming the file.
|
||||
func toolchainWaitOf(path string) (time.Duration, error) {
|
||||
if path == "" {
|
||||
return defaultToolchainWait, nil
|
||||
}
|
||||
raw, err := os.ReadFile(path)
|
||||
if errors.Is(err, fs.ErrNotExist) {
|
||||
return defaultToolchainWait, nil
|
||||
}
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
v := strings.TrimSpace(string(raw))
|
||||
if v == "" {
|
||||
return defaultToolchainWait, nil
|
||||
}
|
||||
n, err := strconv.Atoi(v)
|
||||
if err != nil || n < 1 {
|
||||
return 0, fmt.Errorf("%s holds %q, which is not a number of minutes", path, v)
|
||||
}
|
||||
return time.Duration(n) * time.Minute, nil
|
||||
}
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// A-71: the toolchain wait's setting is read at the start from the file the manifest declares, never from the environment.
|
||||
func TestToolchainWaitIsReadFromItsFile(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
n := 0
|
||||
file := func(content string) string {
|
||||
n++
|
||||
p := filepath.Join(dir, "w"+string(rune('a'+n)))
|
||||
if err := os.WriteFile(p, []byte(content), 0o600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return p
|
||||
}
|
||||
for _, c := range []struct {
|
||||
name, path string
|
||||
want time.Duration
|
||||
bad bool
|
||||
}{
|
||||
{"no file named", "", defaultToolchainWait, false},
|
||||
{"file absent", filepath.Join(dir, "absent"), defaultToolchainWait, false},
|
||||
{"empty", file(" \n"), defaultToolchainWait, false},
|
||||
{"minutes", file("15\n"), 15 * time.Minute, false},
|
||||
{"not a number", file("soon"), 0, true},
|
||||
{"zero", file("0"), 0, true},
|
||||
} {
|
||||
got, err := toolchainWaitOf(c.path)
|
||||
if (err != nil) != c.bad || got != c.want {
|
||||
t.Errorf("%s: %v, %v; want %v, error %v", c.name, got, err, c.want, c.bad)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ package manifest
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@@ -221,8 +222,22 @@ func TestManifestProcess(t *testing.T) { // 1607/AC1
|
||||
t.Errorf("process = %v", p)
|
||||
}
|
||||
env, _ := p["env"].(map[string]any)
|
||||
if len(env) != 2 || env["MESH_BROKER_FILE"] != "${dir:state}/broker" || env["MESH_BUILDER_TOOLCHAIN_WAIT_MINUTES"] != "${config:toolchain-wait-minutes}" {
|
||||
t.Errorf("env = %v, want the broker file and the toolchain wait's setting (design 47 G §672)", p["env"])
|
||||
if len(env) != 2 || env["MESH_BROKER_FILE"] != "${dir:state}/broker" || env["MESH_BUILDER_TOOLCHAIN_WAIT_FILE"] != "${dir:state}/toolchain-wait-minutes" {
|
||||
t.Errorf("env = %v, want the broker file and the path of the toolchain wait's file, never the setting itself (design 47 G §672, A-71)", p["env"])
|
||||
}
|
||||
for k, v := range env {
|
||||
if strings.Contains(fmt.Sprint(v), "${config:") {
|
||||
t.Errorf("env %s = %v: a setting reaches the process as a file, never its environment (A-71)", k, v)
|
||||
}
|
||||
}
|
||||
found := false
|
||||
for _, r := range m.Resources {
|
||||
if r["type"] == "file" && r["path"] == "${dir:state}/toolchain-wait-minutes" && r["content"] == "${config:toolchain-wait-minutes}" {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Errorf("no file resource with the content ${config:toolchain-wait-minutes}: A-71")
|
||||
}
|
||||
for _, r := range m.Resources {
|
||||
if r["id"] == "settings" {
|
||||
|
||||
@@ -165,6 +165,14 @@
|
||||
"place": ".",
|
||||
"unknown": "program"
|
||||
},
|
||||
{
|
||||
"id": "toolchain-wait",
|
||||
"type": "file",
|
||||
"path": "${dir:state}/toolchain-wait-minutes",
|
||||
"owner": "mesh-delivery-builder",
|
||||
"mode": "0600",
|
||||
"content": "${config:toolchain-wait-minutes}"
|
||||
},
|
||||
{
|
||||
"id": "builder",
|
||||
"type": "process",
|
||||
@@ -175,7 +183,7 @@
|
||||
],
|
||||
"env": {
|
||||
"MESH_BROKER_FILE": "${dir:state}/broker",
|
||||
"MESH_BUILDER_TOOLCHAIN_WAIT_MINUTES": "${config:toolchain-wait-minutes}"
|
||||
"MESH_BUILDER_TOOLCHAIN_WAIT_FILE": "${dir:state}/toolchain-wait-minutes"
|
||||
},
|
||||
"user": "mesh-delivery-builder",
|
||||
"health": {
|
||||
|
||||
Reference in New Issue
Block a user