Add artifact workdir layout and locking

This commit is contained in:
2026-05-02 10:56:42 -05:00
parent 6caecb1baa
commit fb659e0d3d
8 changed files with 615 additions and 46 deletions

View File

@@ -13,7 +13,8 @@ import (
)
func TestExecuteValidCommands(t *testing.T) {
pipelinePath, sessionPath := writeValidConfigFiles(t)
workspaceRoot := t.TempDir()
pipelinePath, sessionPath := writeValidConfigFiles(t, workspaceRoot)
manifestPath := writeManifestPathForExecute(t)
cases := []struct {
@@ -22,7 +23,7 @@ func TestExecuteValidCommands(t *testing.T) {
wantOut string
}{
{name: "run", args: []string{"run", "--config", pipelinePath, "--session", sessionPath}, wantOut: "narratio run: configuration loaded and valid"},
{name: "plan", args: []string{"plan", "--config", pipelinePath, "--session", sessionPath}, wantOut: "narratio plan: configuration loaded and valid"},
{name: "plan", args: []string{"plan", "--config", pipelinePath, "--session", sessionPath}, wantOut: "narratio plan: configuration loaded and valid; workdir prepared at"},
{name: "status", args: []string{"status", "--manifest", manifestPath}, wantOut: "session_id: 2026-05-03"},
{name: "resume", args: []string{"resume"}, wantOut: "narratio resume: not yet implemented"},
{name: "run-stage", args: []string{"run-stage", "polish"}, wantOut: "narratio run-stage: not yet implemented"},
@@ -113,7 +114,7 @@ func TestExecuteMissingCommand(t *testing.T) {
}
}
func writeValidConfigFiles(t *testing.T) (string, string) {
func writeValidConfigFiles(t *testing.T, workspaceRoot string) (string, string) {
t.Helper()
dir := t.TempDir()
@@ -121,7 +122,7 @@ func writeValidConfigFiles(t *testing.T) (string, string) {
sessionPath := filepath.Join(dir, "session.yml")
pipelineYAML := `workspace:
root: /tmp/narratio
root: ` + workspaceRoot + `
storage:
backend: s3
whisperx:

View File

@@ -6,10 +6,11 @@ import (
"fmt"
"io"
"gitea.maximumdirect.net/eric/narratio/internal/artifacts"
"gitea.maximumdirect.net/eric/narratio/internal/config"
)
// Plan validates configuration inputs and reports readiness for future planning.
// Plan validates configuration inputs and prepares the local session workdir.
func Plan(_ context.Context, args []string, out io.Writer) error {
fs := flag.NewFlagSet("plan", flag.ContinueOnError)
fs.SetOutput(io.Discard)
@@ -37,6 +38,12 @@ func Plan(_ context.Context, args []string, out io.Writer) error {
return fmt.Errorf("plan: %w", err)
}
_, err = fmt.Fprintln(out, "narratio plan: configuration loaded and valid")
store := artifacts.NewLocalStore(cfg.Pipeline.Workspace.Root)
paths, err := store.EnsureLayout(cfg.Session.SessionID)
if err != nil {
return fmt.Errorf("plan: prepare workdir: %w", err)
}
_, err = fmt.Fprintf(out, "narratio plan: configuration loaded and valid; workdir prepared at %s\n", paths.Root)
return err
}

61
internal/app/plan_test.go Normal file
View File

@@ -0,0 +1,61 @@
package app
import (
"bytes"
"context"
"os"
"path/filepath"
"strings"
"testing"
"gitea.maximumdirect.net/eric/narratio/internal/artifacts"
)
func TestPlanCreatesAndReusesWorkdir(t *testing.T) {
workspaceRoot := t.TempDir()
pipelinePath, sessionPath := writeValidConfigFiles(t, workspaceRoot)
var out bytes.Buffer
args := []string{"--config", pipelinePath, "--session", sessionPath}
if err := Plan(context.Background(), args, &out); err != nil {
t.Fatalf("first Plan() error = %v", err)
}
if !strings.Contains(out.String(), "workdir prepared") {
t.Fatalf("first output = %q, want workdir prepared", out.String())
}
sessionWorkdir := artifacts.SessionWorkDir(workspaceRoot, "2026-05-03")
expectedDirs := []string{
sessionWorkdir,
filepath.Join(sessionWorkdir, "inputs"),
filepath.Join(sessionWorkdir, "audio"),
filepath.Join(sessionWorkdir, "transcripts", "raw"),
filepath.Join(sessionWorkdir, "transcripts", "normalized"),
filepath.Join(sessionWorkdir, "artifacts"),
filepath.Join(sessionWorkdir, "config"),
filepath.Join(sessionWorkdir, "logs"),
}
for _, dir := range expectedDirs {
assertDir(t, dir)
}
out.Reset()
if err := Plan(context.Background(), args, &out); err != nil {
t.Fatalf("second Plan() error = %v", err)
}
if !strings.Contains(out.String(), "workdir prepared") {
t.Fatalf("second output = %q, want workdir prepared", out.String())
}
}
func assertDir(t *testing.T, path string) {
t.Helper()
info, err := os.Stat(path)
if err != nil {
t.Fatalf("Stat(%q) error = %v", path, err)
}
if !info.IsDir() {
t.Fatalf("%q is not a directory", path)
}
}