Implemented narratio analyze as a shortcut to run the analyze stage only
Some checks failed
ci/woodpecker/tag/release Pipeline failed
Some checks failed
ci/woodpecker/tag/release Pipeline failed
This commit is contained in:
40
docs/cli.md
40
docs/cli.md
@@ -19,6 +19,7 @@ Implemented commands:
|
|||||||
- `resume`: continue from first non-succeeded stage unless forced.
|
- `resume`: continue from first non-succeeded stage unless forced.
|
||||||
- `status`: read an existing manifest or inspect local/remote state for a session.
|
- `status`: read an existing manifest or inspect local/remote state for a session.
|
||||||
- `run-stage`: execute exactly one stage.
|
- `run-stage`: execute exactly one stage.
|
||||||
|
- `analyze`: force-rerun the analyze stage.
|
||||||
- `restore`: restore durable local session state from the committed remote archive state.
|
- `restore`: restore durable local session state from the committed remote archive state.
|
||||||
- `session validate`: run read-only preflight checks for a session.
|
- `session validate`: run read-only preflight checks for a session.
|
||||||
- `session init`: create local or remote `session.yml`.
|
- `session init`: create local or remote `session.yml`.
|
||||||
@@ -72,6 +73,17 @@ For config semantics, see [docs/config.md](./config.md). For operator lifecycle
|
|||||||
- `--artifacts <names>`: analyze artifact keys to execute (repeatable or comma-separated).
|
- `--artifacts <names>`: analyze artifact keys to execute (repeatable or comma-separated).
|
||||||
- positional `<stage>`: required stage name.
|
- positional `<stage>`: required stage name.
|
||||||
|
|
||||||
|
### `analyze`
|
||||||
|
|
||||||
|
- `--config <path>`
|
||||||
|
- `--campaign <path>`
|
||||||
|
- `--session <path>`
|
||||||
|
- `--session-id <value>`
|
||||||
|
- `--previous-session-id <value>`
|
||||||
|
- `--artifacts <names>`: analyze artifact keys to execute (repeatable or comma-separated).
|
||||||
|
|
||||||
|
`analyze` is force-by-design and does not accept `--force`.
|
||||||
|
|
||||||
Valid stage names:
|
Valid stage names:
|
||||||
|
|
||||||
- `prepare`
|
- `prepare`
|
||||||
@@ -355,6 +367,26 @@ Common failure cases:
|
|||||||
- unknown stage name.
|
- unknown stage name.
|
||||||
- using `--artifacts` with any non-`analyze` stage.
|
- using `--artifacts` with any non-`analyze` stage.
|
||||||
|
|
||||||
|
### `analyze`
|
||||||
|
|
||||||
|
Purpose:
|
||||||
|
- Force-rerun the analyze stage.
|
||||||
|
- Provide a shorter equivalent for `narratio run-stage --force analyze`.
|
||||||
|
|
||||||
|
Syntax:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
narratio analyze [--config <pipeline.yml>] [--campaign <campaign.yml>] [--session <session.yml>] [--session-id <id>] [--previous-session-id <id>] [--artifacts <name[,name...]>]
|
||||||
|
```
|
||||||
|
|
||||||
|
Success output:
|
||||||
|
- `narratio analyze: executed=<n> skipped=<n> force=true; manifest=<path>`
|
||||||
|
|
||||||
|
Common failure cases:
|
||||||
|
- positional arguments.
|
||||||
|
- `--force`, because force is implicit.
|
||||||
|
- unknown configured artifact keys.
|
||||||
|
|
||||||
### `restore`
|
### `restore`
|
||||||
|
|
||||||
Purpose:
|
Purpose:
|
||||||
@@ -442,10 +474,10 @@ Resume with selected analyze artifacts:
|
|||||||
narratio resume --session-id 2026-04-04 --artifacts player_handout
|
narratio resume --session-id 2026-04-04 --artifacts player_handout
|
||||||
```
|
```
|
||||||
|
|
||||||
Run only analyze stage with selected artifacts:
|
Force-rerun analyze with selected artifacts:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
narratio run-stage --session-id 2026-04-04 --artifacts player_handout analyze
|
narratio analyze --session-id 2026-04-04 --artifacts player_handout
|
||||||
```
|
```
|
||||||
|
|
||||||
Preview restore actions without writes:
|
Preview restore actions without writes:
|
||||||
@@ -458,7 +490,7 @@ Restore and then force analyze:
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
narratio restore --session-id 2026-04-04
|
narratio restore --session-id 2026-04-04
|
||||||
narratio run-stage --session-id 2026-04-04 --force analyze
|
narratio analyze --session-id 2026-04-04
|
||||||
```
|
```
|
||||||
|
|
||||||
Rehydrate canonical previous-session inputs after artifact-input changes:
|
Rehydrate canonical previous-session inputs after artifact-input changes:
|
||||||
@@ -490,7 +522,7 @@ narratio status --manifest <manifest.json>
|
|||||||
```
|
```
|
||||||
|
|
||||||
Get manifest path from previous output:
|
Get manifest path from previous output:
|
||||||
- `run`, `resume`, and `run-stage` print `manifest=<path>` on success.
|
- `run`, `resume`, `run-stage`, and `analyze` print `manifest=<path>` on success.
|
||||||
|
|
||||||
## `--artifacts` and `--force`
|
## `--artifacts` and `--force`
|
||||||
|
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ narratio restore --session-id 2026-04-04
|
|||||||
Post-restore analyze rerun pattern:
|
Post-restore analyze rerun pattern:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
narratio run-stage --session-id 2026-04-04 --force analyze
|
narratio analyze --session-id 2026-04-04
|
||||||
```
|
```
|
||||||
|
|
||||||
Restore source-of-truth:
|
Restore source-of-truth:
|
||||||
@@ -124,8 +124,9 @@ Configured artifact source reuse:
|
|||||||
- reused configured artifact provenance is `filesystem.disabled_artifact_output`.
|
- reused configured artifact provenance is `filesystem.disabled_artifact_output`.
|
||||||
|
|
||||||
`--artifacts` behavior:
|
`--artifacts` behavior:
|
||||||
- accepted on `run`, `resume`, and `run-stage analyze`.
|
- accepted on `run`, `resume`, `run-stage analyze`, and `analyze`.
|
||||||
- filters analyze execution only; does not force stage rerun.
|
- filters analyze execution only.
|
||||||
|
- does not imply force on `run`, `resume`, or `run-stage`; `narratio analyze` is force-by-design.
|
||||||
|
|
||||||
Canonical previous-session input behavior:
|
Canonical previous-session input behavior:
|
||||||
- canonical sources use `narratio.previous_session.artifact.<artifact_key>`.
|
- canonical sources use `narratio.previous_session.artifact.<artifact_key>`.
|
||||||
|
|||||||
@@ -9,7 +9,9 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"gitea.maximumdirect.net/eric/narratio/internal/config"
|
||||||
"gitea.maximumdirect.net/eric/narratio/internal/manifest"
|
"gitea.maximumdirect.net/eric/narratio/internal/manifest"
|
||||||
|
"gitea.maximumdirect.net/eric/narratio/internal/stage"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestExecuteRunStageArtifactsNonAnalyzeFails(t *testing.T) {
|
func TestExecuteRunStageArtifactsNonAnalyzeFails(t *testing.T) {
|
||||||
@@ -104,6 +106,150 @@ func TestResumeArtifactsWithSucceededAnalyzeSkipsUnlessForced(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestExecuteAnalyzeForceRunsAnalyze(t *testing.T) {
|
||||||
|
workspaceRoot := t.TempDir()
|
||||||
|
pipelinePath, campaignPath, sessionPath := writeValidConfigFilesWithScriptoriumArtifacts(t, workspaceRoot)
|
||||||
|
|
||||||
|
var capturedStages []string
|
||||||
|
var capturedForce bool
|
||||||
|
origExecuteStagesFn := executeStagesFn
|
||||||
|
t.Cleanup(func() {
|
||||||
|
executeStagesFn = origExecuteStagesFn
|
||||||
|
})
|
||||||
|
executeStagesFn = func(_ context.Context, _ *config.Config, stages []stage.Stage, opts RunOptions) (*RunSummary, error) {
|
||||||
|
for _, s := range stages {
|
||||||
|
capturedStages = append(capturedStages, s.Name())
|
||||||
|
}
|
||||||
|
capturedForce = opts.Force
|
||||||
|
return &RunSummary{
|
||||||
|
ManifestPath: filepath.Join(workspaceRoot, "work", "sample-campaign", "2026-05-03", "manifest.json"),
|
||||||
|
Executed: []string{"analyze"},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var stdout bytes.Buffer
|
||||||
|
var stderr bytes.Buffer
|
||||||
|
code := Execute(
|
||||||
|
[]string{"analyze", "--config", pipelinePath, "--campaign", campaignPath, "--session", sessionPath},
|
||||||
|
&stdout,
|
||||||
|
&stderr,
|
||||||
|
)
|
||||||
|
if code != 0 {
|
||||||
|
t.Fatalf("exit code = %d, want 0; stderr=%q", code, stderr.String())
|
||||||
|
}
|
||||||
|
if len(capturedStages) != 1 || capturedStages[0] != "analyze" {
|
||||||
|
t.Fatalf("captured stages = %#v, want [analyze]", capturedStages)
|
||||||
|
}
|
||||||
|
if !capturedForce {
|
||||||
|
t.Fatal("captured force = false, want true")
|
||||||
|
}
|
||||||
|
if !strings.Contains(stdout.String(), "narratio analyze: executed=1 skipped=0 force=true; manifest=") {
|
||||||
|
t.Fatalf("stdout = %q, want analyze summary", stdout.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestExecuteAnalyzePropagatesSelectedArtifacts(t *testing.T) {
|
||||||
|
workspaceRoot := t.TempDir()
|
||||||
|
pipelinePath, campaignPath, sessionPath := writeValidConfigFilesWithScriptoriumArtifacts(t, workspaceRoot)
|
||||||
|
|
||||||
|
var capturedArtifacts []string
|
||||||
|
origExecuteStagesFn := executeStagesFn
|
||||||
|
t.Cleanup(func() {
|
||||||
|
executeStagesFn = origExecuteStagesFn
|
||||||
|
})
|
||||||
|
executeStagesFn = func(_ context.Context, _ *config.Config, _ []stage.Stage, opts RunOptions) (*RunSummary, error) {
|
||||||
|
capturedArtifacts = append([]string(nil), opts.SelectedArtifacts...)
|
||||||
|
return &RunSummary{ManifestPath: filepath.Join(workspaceRoot, "manifest.json"), Executed: []string{"analyze"}}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var stdout bytes.Buffer
|
||||||
|
var stderr bytes.Buffer
|
||||||
|
code := Execute(
|
||||||
|
[]string{
|
||||||
|
"analyze",
|
||||||
|
"--config", pipelinePath,
|
||||||
|
"--campaign", campaignPath,
|
||||||
|
"--session", sessionPath,
|
||||||
|
"--artifacts", "player_handout,session_recap",
|
||||||
|
},
|
||||||
|
&stdout,
|
||||||
|
&stderr,
|
||||||
|
)
|
||||||
|
if code != 0 {
|
||||||
|
t.Fatalf("exit code = %d, want 0; stderr=%q", code, stderr.String())
|
||||||
|
}
|
||||||
|
if strings.Join(capturedArtifacts, ",") != "player_handout,session_recap" {
|
||||||
|
t.Fatalf("captured artifacts = %#v, want sorted selected artifacts", capturedArtifacts)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestExecuteAnalyzeUnknownArtifactFailsValidation(t *testing.T) {
|
||||||
|
workspaceRoot := t.TempDir()
|
||||||
|
pipelinePath, campaignPath, sessionPath := writeValidConfigFilesWithScriptoriumArtifacts(t, workspaceRoot)
|
||||||
|
|
||||||
|
var stdout bytes.Buffer
|
||||||
|
var stderr bytes.Buffer
|
||||||
|
code := Execute(
|
||||||
|
[]string{"analyze", "--config", pipelinePath, "--campaign", campaignPath, "--session", sessionPath, "--artifacts", "unknown_artifact"},
|
||||||
|
&stdout,
|
||||||
|
&stderr,
|
||||||
|
)
|
||||||
|
if code == 0 {
|
||||||
|
t.Fatal("exit code = 0, want non-zero")
|
||||||
|
}
|
||||||
|
if !strings.Contains(stderr.String(), `analyze: --artifacts includes unknown artifact "unknown_artifact"`) {
|
||||||
|
t.Fatalf("stderr = %q, want unknown-artifact validation error", stderr.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestExecuteAnalyzeRejectsPositionalArgsAndForceFlag(t *testing.T) {
|
||||||
|
cases := []struct {
|
||||||
|
name string
|
||||||
|
args []string
|
||||||
|
want string
|
||||||
|
}{
|
||||||
|
{name: "positional", args: []string{"analyze", "extra"}, want: "analyze: unexpected positional arguments"},
|
||||||
|
{name: "force flag", args: []string{"analyze", "--force"}, want: "analyze: invalid flags: flag provided but not defined: -force"},
|
||||||
|
}
|
||||||
|
for _, tc := range cases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
var stdout bytes.Buffer
|
||||||
|
var stderr bytes.Buffer
|
||||||
|
code := Execute(tc.args, &stdout, &stderr)
|
||||||
|
if code == 0 {
|
||||||
|
t.Fatal("exit code = 0, want non-zero")
|
||||||
|
}
|
||||||
|
if !strings.Contains(stderr.String(), tc.want) {
|
||||||
|
t.Fatalf("stderr = %q, want %q", stderr.String(), tc.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestExecuteAnalyzeMissingConfigUsesRunStageLoadingPath(t *testing.T) {
|
||||||
|
var stdout bytes.Buffer
|
||||||
|
var stderr bytes.Buffer
|
||||||
|
code := Execute([]string{"analyze"}, &stdout, &stderr)
|
||||||
|
if code == 0 {
|
||||||
|
t.Fatal("exit code = 0, want non-zero")
|
||||||
|
}
|
||||||
|
if !strings.Contains(stderr.String(), "analyze: no pipeline config path provided and no default pipeline config found; searched:") {
|
||||||
|
t.Fatalf("stderr = %q, want pipeline discovery error", stderr.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestExecuteUsageIncludesAnalyze(t *testing.T) {
|
||||||
|
var stdout bytes.Buffer
|
||||||
|
var stderr bytes.Buffer
|
||||||
|
code := Execute(nil, &stdout, &stderr)
|
||||||
|
if code == 0 {
|
||||||
|
t.Fatal("exit code = 0, want non-zero")
|
||||||
|
}
|
||||||
|
if !strings.Contains(stderr.String(), "analyze") {
|
||||||
|
t.Fatalf("stderr = %q, want usage to include analyze", stderr.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func writeValidConfigFilesWithScriptoriumArtifacts(t *testing.T, workspaceRoot string) (string, string, string) {
|
func writeValidConfigFilesWithScriptoriumArtifacts(t *testing.T, workspaceRoot string) (string, string, string) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
var supportedCommands = []string{"run", "plan", "status", "resume", "run-stage", "restore", "session", "artifacts", "locks", "clean"}
|
var supportedCommands = []string{"run", "plan", "status", "resume", "run-stage", "analyze", "restore", "session", "artifacts", "locks", "clean"}
|
||||||
|
|
||||||
// Execute dispatches CLI commands and returns a process exit code.
|
// Execute dispatches CLI commands and returns a process exit code.
|
||||||
func Execute(args []string, stdout, stderr io.Writer) int {
|
func Execute(args []string, stdout, stderr io.Writer) int {
|
||||||
@@ -32,6 +32,8 @@ func Execute(args []string, stdout, stderr io.Writer) int {
|
|||||||
err = Resume(ctx, cmdArgs, stdout)
|
err = Resume(ctx, cmdArgs, stdout)
|
||||||
case "run-stage":
|
case "run-stage":
|
||||||
err = RunStage(ctx, cmdArgs, stdout)
|
err = RunStage(ctx, cmdArgs, stdout)
|
||||||
|
case "analyze":
|
||||||
|
err = Analyze(ctx, cmdArgs, stdout)
|
||||||
case "restore":
|
case "restore":
|
||||||
err = Restore(ctx, cmdArgs, stdout)
|
err = Restore(ctx, cmdArgs, stdout)
|
||||||
case "session":
|
case "session":
|
||||||
|
|||||||
@@ -35,45 +35,34 @@ func RunStage(ctx context.Context, args []string, out io.Writer) error {
|
|||||||
if fs.NArg() != 1 {
|
if fs.NArg() != 1 {
|
||||||
return fmt.Errorf("run-stage: expected exactly one stage name")
|
return fmt.Errorf("run-stage: expected exactly one stage name")
|
||||||
}
|
}
|
||||||
stageName := fs.Arg(0)
|
|
||||||
normalizedArtifacts, err := selectedArtifacts.Normalize()
|
normalizedArtifacts, err := selectedArtifacts.Normalize()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("run-stage: invalid --artifacts: %w", err)
|
return fmt.Errorf("run-stage: invalid --artifacts: %w", err)
|
||||||
}
|
}
|
||||||
|
stageName := fs.Arg(0)
|
||||||
if len(normalizedArtifacts) > 0 && stageName != "analyze" {
|
if len(normalizedArtifacts) > 0 && stageName != "analyze" {
|
||||||
return fmt.Errorf("run-stage: --artifacts is only supported for stage \"analyze\"")
|
return fmt.Errorf("run-stage: --artifacts is only supported for stage \"analyze\"")
|
||||||
}
|
}
|
||||||
stages, err := BuildSingleStagePlan(stageName)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("run-stage: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
cfg, err := loadCommandConfig(ctx, pipelinePath, campaignPath, sessionPath, config.SessionLoadOptions{
|
summary, err := runSingleStageCommand(ctx, singleStageCommand{
|
||||||
|
CommandName: "run-stage",
|
||||||
|
StageName: stageName,
|
||||||
|
PipelinePath: pipelinePath,
|
||||||
|
CampaignPath: campaignPath,
|
||||||
|
SessionPath: sessionPath,
|
||||||
SessionID: sessionID,
|
SessionID: sessionID,
|
||||||
PreviousSessionID: previousSessionID,
|
PreviousSessionID: previousSessionID,
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("run-stage: %w", err)
|
|
||||||
}
|
|
||||||
if err := config.Validate(cfg); err != nil {
|
|
||||||
return fmt.Errorf("run-stage: %w", err)
|
|
||||||
}
|
|
||||||
if err := validateSelectedAnalyzeArtifacts(cfg, normalizedArtifacts); err != nil {
|
|
||||||
return fmt.Errorf("run-stage: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
summary, err := executeStagesFn(ctx, cfg, stages, RunOptions{
|
|
||||||
Force: force,
|
Force: force,
|
||||||
SelectedArtifacts: normalizedArtifacts,
|
SelectedArtifacts: normalizedArtifacts,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("run-stage: %w", err)
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = fmt.Fprintf(
|
_, err = fmt.Fprintf(
|
||||||
out,
|
out,
|
||||||
"narratio run-stage: stage=%s executed=%d skipped=%d force=%t; manifest=%s\n",
|
"narratio run-stage: stage=%s executed=%d skipped=%d force=%t; manifest=%s\n",
|
||||||
stages[0].Name(),
|
stageName,
|
||||||
len(summary.Executed),
|
len(summary.Executed),
|
||||||
len(summary.Skipped),
|
len(summary.Skipped),
|
||||||
force,
|
force,
|
||||||
@@ -81,3 +70,99 @@ func RunStage(ctx context.Context, args []string, out io.Writer) error {
|
|||||||
)
|
)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Analyze force-runs the analyze stage.
|
||||||
|
func Analyze(ctx context.Context, args []string, out io.Writer) error {
|
||||||
|
fs := flag.NewFlagSet("analyze", flag.ContinueOnError)
|
||||||
|
fs.SetOutput(io.Discard)
|
||||||
|
|
||||||
|
var pipelinePath string
|
||||||
|
var campaignPath string
|
||||||
|
var sessionPath string
|
||||||
|
var sessionID string
|
||||||
|
var previousSessionID string
|
||||||
|
var selectedArtifacts artifactSelectionFlag
|
||||||
|
fs.StringVar(&pipelinePath, "config", "", "path to pipeline.yml (optional; defaults searched)")
|
||||||
|
fs.StringVar(&campaignPath, "campaign", "", "path to campaign.yml (optional; defaults searched)")
|
||||||
|
fs.StringVar(&sessionPath, "session", "", "path to session.yml")
|
||||||
|
fs.StringVar(&sessionID, "session-id", "", "session identifier for session.yml templates")
|
||||||
|
fs.StringVar(&previousSessionID, "previous-session-id", "", "previous session identifier for session.yml templates")
|
||||||
|
fs.Var(&selectedArtifacts, "artifacts", "artifact names to execute during analyze (comma-separated or repeatable)")
|
||||||
|
|
||||||
|
if err := fs.Parse(args); err != nil {
|
||||||
|
return fmt.Errorf("analyze: invalid flags: %w", err)
|
||||||
|
}
|
||||||
|
if fs.NArg() != 0 {
|
||||||
|
return fmt.Errorf("analyze: unexpected positional arguments")
|
||||||
|
}
|
||||||
|
normalizedArtifacts, err := selectedArtifacts.Normalize()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("analyze: invalid --artifacts: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
summary, err := runSingleStageCommand(ctx, singleStageCommand{
|
||||||
|
CommandName: "analyze",
|
||||||
|
StageName: "analyze",
|
||||||
|
PipelinePath: pipelinePath,
|
||||||
|
CampaignPath: campaignPath,
|
||||||
|
SessionPath: sessionPath,
|
||||||
|
SessionID: sessionID,
|
||||||
|
PreviousSessionID: previousSessionID,
|
||||||
|
Force: true,
|
||||||
|
SelectedArtifacts: normalizedArtifacts,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = fmt.Fprintf(
|
||||||
|
out,
|
||||||
|
"narratio analyze: executed=%d skipped=%d force=true; manifest=%s\n",
|
||||||
|
len(summary.Executed),
|
||||||
|
len(summary.Skipped),
|
||||||
|
summary.ManifestPath,
|
||||||
|
)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
type singleStageCommand struct {
|
||||||
|
CommandName string
|
||||||
|
StageName string
|
||||||
|
PipelinePath string
|
||||||
|
CampaignPath string
|
||||||
|
SessionPath string
|
||||||
|
SessionID string
|
||||||
|
PreviousSessionID string
|
||||||
|
Force bool
|
||||||
|
SelectedArtifacts []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func runSingleStageCommand(ctx context.Context, req singleStageCommand) (*RunSummary, error) {
|
||||||
|
stages, err := BuildSingleStagePlan(req.StageName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("%s: %w", req.CommandName, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg, err := loadCommandConfig(ctx, req.PipelinePath, req.CampaignPath, req.SessionPath, config.SessionLoadOptions{
|
||||||
|
SessionID: req.SessionID,
|
||||||
|
PreviousSessionID: req.PreviousSessionID,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("%s: %w", req.CommandName, err)
|
||||||
|
}
|
||||||
|
if err := config.Validate(cfg); err != nil {
|
||||||
|
return nil, fmt.Errorf("%s: %w", req.CommandName, err)
|
||||||
|
}
|
||||||
|
if err := validateSelectedAnalyzeArtifacts(cfg, req.SelectedArtifacts); err != nil {
|
||||||
|
return nil, fmt.Errorf("%s: %w", req.CommandName, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
summary, err := executeStagesFn(ctx, cfg, stages, RunOptions{
|
||||||
|
Force: req.Force,
|
||||||
|
SelectedArtifacts: req.SelectedArtifacts,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("%s: %w", req.CommandName, err)
|
||||||
|
}
|
||||||
|
return summary, nil
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user