Files
narratio/internal/app/resume.go

92 lines
2.3 KiB
Go

package app
import (
"context"
"flag"
"fmt"
"io"
"gitea.maximumdirect.net/eric/narratio/internal/config"
"gitea.maximumdirect.net/eric/narratio/internal/manifest"
)
// Resume continues execution from the first non-succeeded stage in the manifest.
func Resume(ctx context.Context, args []string, out io.Writer) error {
fs := flag.NewFlagSet("resume", flag.ContinueOnError)
fs.SetOutput(io.Discard)
var pipelinePath string
var sessionPath string
var force bool
fs.StringVar(&pipelinePath, "config", "", "path to pipeline.yml")
fs.StringVar(&sessionPath, "session", "", "path to session.yml")
fs.BoolVar(&force, "force", false, "force stage execution")
if err := fs.Parse(args); err != nil {
return fmt.Errorf("resume: invalid flags: %w", err)
}
if fs.NArg() != 0 {
return fmt.Errorf("resume: unexpected positional arguments")
}
if pipelinePath == "" || sessionPath == "" {
return fmt.Errorf("resume: --config and --session are required")
}
cfg, err := config.Load(pipelinePath, sessionPath)
if err != nil {
return fmt.Errorf("resume: %w", err)
}
if err := config.Validate(cfg); err != nil {
return fmt.Errorf("resume: %w", err)
}
full := BuildFullPlan()
selected := full
if !force {
m, err := loadManifestIfPresent(ctx, cfg)
if err != nil {
return fmt.Errorf("resume: %w", err)
}
if m != nil {
start := firstNonSucceededIndex(full, m)
if start >= len(full) {
_, err := fmt.Fprintf(out, "narratio resume: session %s has no remaining stages\n", cfg.Session.SessionID)
return err
}
selected = full[start:]
}
}
summary, err := executeStages(ctx, cfg, selected, RunOptions{Force: force})
if err != nil {
return fmt.Errorf("resume: %w", err)
}
_, err = fmt.Fprintf(
out,
"narratio resume: session %s; executed=%d skipped=%d; manifest=%s\n",
summary.SessionID,
len(summary.Executed),
len(summary.Skipped),
summary.ManifestPath,
)
return err
}
func loadManifestIfPresent(ctx context.Context, cfg *config.Config) (*manifest.Manifest, error) {
path := manifestPathFor(cfg)
exists, err := fileExists(path)
if err != nil {
return nil, fmt.Errorf("check manifest %q: %w", path, err)
}
if !exists {
return nil, nil
}
store := &manifest.LocalStore{}
m, err := store.Load(ctx, path)
if err != nil {
return nil, fmt.Errorf("load manifest %q: %w", path, err)
}
return m, nil
}