1package main
2
3import (
4 "context"
5 "encoding/json"
6 "errors"
7 "flag"
8 "fmt"
9 "io"
10 "os"
11 "os/signal"
12 "path/filepath"
13 "strings"
14 "syscall"
15 "time"
16
17 "github.com/bnema/nefergui/internal/harness"
18)
19
20func main() { os.Exit(run(os.Args[1:])) }
21func run(args []string) int {
22 if len(args) == 0 || args[0] != "run" {
23 fmt.Fprintln(os.Stderr, "usage: nefergui-harness run [--size WxH --scale 1.5 --layout fr --input script --expect expect.json --out dir --keep --allow-unpinned --timeout 15s] -- <client cmd...>")
24 return harness.Usage
25 }
26 f := flag.NewFlagSet("run", flag.ContinueOnError)
27 f.SetOutput(os.Stderr)
28 size := f.String("size", "800x600", "headless size")
29 scale := f.String("scale", "1", "output scale")
30 layout := f.String("layout", "us", "keyboard layout")
31 background := f.String("background", "#111111", "background #RRGGBB")
32 input := f.String("input", "", "input script")
33 expect := f.String("expect", "", "expectations JSON")
34 out := f.String("out", "", "new artifact directory")
35 keep := f.Bool("keep", false, "keep temporary runtime/config")
36 allowUnpinned := f.Bool("allow-unpinned", false, "permit NEFERGUI_NEFERWL override")
37 timeout := f.Duration("timeout", 15*time.Second, "maximum session duration")
38 ready := f.Duration("ready-timeout", 8*time.Second, "socket readiness deadline")
39 if err := f.Parse(args[1:]); err != nil {
40 return harness.Usage
41 }
42 if *out == "" {
43 fmt.Fprintln(os.Stderr, "--out required")
44 return harness.Usage
45 }
46 var ex harness.Expectations
47 if *expect != "" {
48 data, err := os.ReadFile(*expect)
49 if err != nil {
50 fmt.Fprintln(os.Stderr, err)
51 return harness.Usage
52 }
53 d := json.NewDecoder(strings.NewReader(string(data)))
54 d.DisallowUnknownFields()
55 if err = d.Decode(&ex); err != nil {
56 fmt.Fprintln(os.Stderr, err)
57 return harness.Usage
58 }
59 var extra any
60 if err = d.Decode(&extra); !errors.Is(err, io.EOF) {
61 fmt.Fprintln(os.Stderr, "unexpected JSON after expectations:", err)
62 return harness.Usage
63 }
64 }
65 base := "."
66 if *expect != "" {
67 base = filepath.Dir(*expect)
68 }
69 ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
70 defer stop()
71 res, code := harness.Run(ctx, harness.Options{Size: *size, Scale: *scale, Layout: *layout, Background: *background, Input: *input, Out: *out, Keep: *keep, AllowUnpinned: *allowUnpinned, Timeout: *timeout, ReadyTimeout: *ready, Client: f.Args(), Expect: ex, GoldenBase: base})
72 b, _ := json.Marshal(res)
73 fmt.Println(string(b))
74 return code
75}