webui: add "6. Scenario" page — run scriptable test scenarios from removable media through the normal task queue
Exposes the scenario engine (platform.System.RunScenario, added earlier) in the web UI instead of only the `bee run` CLI: a new nav item lists every scenarios/*.json found on mounted removable media (GET /api/scenario/list) and runs one with a click (POST /api/scenario/run), enqueued as a normal Task with target "scenario" — progress/logs live in Tasks like any other SAT pack, no separate live-output UI needed. - app.go: satRunner gains RunScenario, exportManager gains ListScenarioFilesOnRemovableMedia/ReadScenarioFromRemovableMedia — both already implemented on platform.System, just newly exposed through App. - webui/tasks.go: taskParams.ScenarioName; runTask's "scenario" case reads the file from removable media, parses it, and runs it. - webui/page_scenario.go: the page itself. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
20cd317c87
commit
bcf02e0515
@@ -203,3 +203,47 @@ func (s *System) ReadScenarioFromRemovableMedia(name string) ([]byte, error) {
|
||||
}
|
||||
return nil, fmt.Errorf("scenarios/%s.json not found on any removable media", name)
|
||||
}
|
||||
|
||||
// ScenarioFileOnRemovableMedia is one scenarios/*.json file found on a
|
||||
// mounted removable target, as returned by ListScenarioFilesOnRemovableMedia.
|
||||
type ScenarioFileOnRemovableMedia struct {
|
||||
Name string // filename without ".json" — what ReadScenarioFromRemovableMedia/bee run expects
|
||||
Device string // which removable target it was found on
|
||||
}
|
||||
|
||||
// ListScenarioFilesOnRemovableMedia mounts each removable target in turn
|
||||
// (unmounting again afterward if it mounted it itself) and collects every
|
||||
// *.json file under a top-level scenarios/ directory. Used by the webui's
|
||||
// Scenario page to show what's available to run without requiring the
|
||||
// operator to already know a file's exact name.
|
||||
func (s *System) ListScenarioFilesOnRemovableMedia() ([]ScenarioFileOnRemovableMedia, error) {
|
||||
targets, err := s.ListRemovableTargets()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var out []ScenarioFileOnRemovableMedia
|
||||
for _, target := range targets {
|
||||
mountpoint, mountedHere, mountErr := mountRemovableTargetReadOnly(target)
|
||||
if mountErr != nil {
|
||||
continue
|
||||
}
|
||||
entries, readErr := os.ReadDir(filepath.Join(mountpoint, "scenarios"))
|
||||
if readErr == nil {
|
||||
for _, e := range entries {
|
||||
if e.IsDir() || !strings.HasSuffix(e.Name(), ".json") {
|
||||
continue
|
||||
}
|
||||
out = append(out, ScenarioFileOnRemovableMedia{
|
||||
Name: strings.TrimSuffix(e.Name(), ".json"),
|
||||
Device: target.Device,
|
||||
})
|
||||
}
|
||||
}
|
||||
if mountedHere {
|
||||
_, _ = exportExecCommand("umount", mountpoint).CombinedOutput()
|
||||
_ = os.Remove(mountpoint)
|
||||
}
|
||||
}
|
||||
sort.Slice(out, func(i, j int) bool { return out[i].Name < out[j].Name })
|
||||
return out, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user