feat(lifecycle): add AppMode type + DetectMode()
New pkg/lifecycle/mode.go introduces AppMode (Manager | Worker) and
DetectMode() that returns:
- Manager by default (explicit or empty env)
- Worker when CORE_APP_MODE=worker, or when empty env + CI=true signals
a headless/compute context
Prerequisite for the BugSETI compute worker pattern (core/gui RFC.md §9.1) —
manager GUI runs the full app UI, workers run headless compute with only an
RPC surface.
Tests follow the core/go {Good,Bad,Ugly} naming convention; go test
./pkg/lifecycle/... passes.
Co-authored-by: Codex <noreply@openai.com>
Closes tasks.lthn.sh/view.php?id=32
This commit is contained in:
parent
49198acd3a
commit
6e62d4a20c
2 changed files with 109 additions and 0 deletions
52
pkg/lifecycle/mode.go
Normal file
52
pkg/lifecycle/mode.go
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
package lifecycle
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type AppMode string
|
||||
|
||||
const (
|
||||
ModeManager AppMode = "manager"
|
||||
ModeWorker AppMode = "worker"
|
||||
)
|
||||
|
||||
const (
|
||||
appModeEnv = "CORE_APP_MODE"
|
||||
ciEnv = "CI"
|
||||
)
|
||||
|
||||
// DetectMode returns ModeWorker when CORE_APP_MODE=worker.
|
||||
//
|
||||
// mode := DetectMode()
|
||||
func DetectMode() AppMode {
|
||||
if value, ok := os.LookupEnv(appModeEnv); ok {
|
||||
if mode, valid := parseAppMode(value); valid {
|
||||
return mode
|
||||
}
|
||||
}
|
||||
|
||||
if value, ok := os.LookupEnv(ciEnv); ok && isTrue(value) {
|
||||
return ModeWorker
|
||||
}
|
||||
|
||||
return ModeManager
|
||||
}
|
||||
|
||||
func parseAppMode(value string) (AppMode, bool) {
|
||||
switch strings.ToLower(strings.TrimSpace(value)) {
|
||||
case string(ModeManager):
|
||||
return ModeManager, true
|
||||
case string(ModeWorker):
|
||||
return ModeWorker, true
|
||||
case "":
|
||||
return "", false
|
||||
default:
|
||||
return ModeManager, true
|
||||
}
|
||||
}
|
||||
|
||||
func isTrue(value string) bool {
|
||||
return strings.EqualFold(strings.TrimSpace(value), "true")
|
||||
}
|
||||
57
pkg/lifecycle/mode_test.go
Normal file
57
pkg/lifecycle/mode_test.go
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
package lifecycle
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestMode_DetectMode_Good(t *testing.T) {
|
||||
unsetEnv(t, appModeEnv)
|
||||
t.Setenv(ciEnv, "")
|
||||
|
||||
mode := DetectMode()
|
||||
if mode != ModeManager {
|
||||
t.Fatalf("expected manager mode, got %q", mode)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMode_DetectMode_Bad(t *testing.T) {
|
||||
t.Setenv(appModeEnv, "bogus")
|
||||
t.Setenv(ciEnv, "")
|
||||
|
||||
mode := DetectMode()
|
||||
if mode != ModeManager {
|
||||
t.Fatalf("expected manager mode after invalid env value, got %q", mode)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMode_DetectMode_Ugly(t *testing.T) {
|
||||
t.Setenv(appModeEnv, "")
|
||||
t.Setenv(ciEnv, "true")
|
||||
|
||||
mode := DetectMode()
|
||||
if mode != ModeWorker {
|
||||
t.Fatalf("expected worker mode in CI headless context, got %q", mode)
|
||||
}
|
||||
}
|
||||
|
||||
func unsetEnv(t *testing.T, key string) {
|
||||
t.Helper()
|
||||
|
||||
value, ok := os.LookupEnv(key)
|
||||
if err := os.Unsetenv(key); err != nil {
|
||||
t.Fatalf("unset %s: %v", key, err)
|
||||
}
|
||||
|
||||
t.Cleanup(func() {
|
||||
if ok {
|
||||
if err := os.Setenv(key, value); err != nil {
|
||||
t.Fatalf("restore %s: %v", key, err)
|
||||
}
|
||||
return
|
||||
}
|
||||
if err := os.Unsetenv(key); err != nil {
|
||||
t.Fatalf("restore unset %s: %v", key, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue