Files
narratio/internal/app/bounded_run.go

119 lines
3.4 KiB
Go

package app
import (
"flag"
"fmt"
"io"
"strconv"
)
type boundedRunRequest struct {
Config commonConfigFlags
Plan BoundedPlan
Force bool
SelectedArtifacts []string
}
type singletonStringFlag struct {
name string
value string
set bool
}
func (f *singletonStringFlag) String() string { return f.value }
func (f *singletonStringFlag) Set(value string) error {
if f.set {
return fmt.Errorf("--%s may be specified only once", f.name)
}
f.value = value
f.set = true
return nil
}
func (f singletonStringFlag) pointer() *string {
if !f.set {
return nil
}
value := f.value
return &value
}
type singletonBoolFlag struct {
name string
value bool
set bool
}
func (f *singletonBoolFlag) String() string { return strconv.FormatBool(f.value) }
func (f *singletonBoolFlag) IsBoolFlag() bool { return true }
func (f *singletonBoolFlag) Set(raw string) error {
if f.set {
return fmt.Errorf("--%s may be specified only once", f.name)
}
value, err := strconv.ParseBool(raw)
if err != nil {
return fmt.Errorf("--%s requires a boolean value: %w", f.name, err)
}
f.value = value
f.set = true
return nil
}
func parseBoundedRunRequest(command string, args []string, help io.Writer) (boundedRunRequest, error) {
fs := flag.NewFlagSet(command, flag.ContinueOnError)
fs.SetOutput(help)
var configFlags commonConfigFlags
var from singletonStringFlag
var through singletonStringFlag
var force singletonBoolFlag
var selectedArtifacts artifactSelectionFlag
from.name = "from"
through.name = "through"
force.name = "force"
addCommonConfigFlags(fs, &configFlags)
fs.Var(&from, "from", "first canonical stage to select (inclusive)")
fs.Var(&through, "through", "last canonical stage to select (inclusive)")
fs.Var(&force, "force", "rerun selected stages even when already succeeded")
fs.Var(&selectedArtifacts, "artifacts", "configured artifact names to execute and publish (comma-separated or repeatable)")
fs.Usage = func() {
invocation := "narratio run"
if command == "plan" {
invocation = "narratio session plan"
}
_, _ = fmt.Fprintf(help, "Usage: %s <session_id> [--from <stage>] [--through <stage>] [--force] [--artifacts <name[,name...]>] [common config flags]\n\n", invocation)
_, _ = fmt.Fprintln(help, "Bounds are inclusive; omitted --from or --through selects the beginning or end of the canonical pipeline.")
_, _ = fmt.Fprintln(help)
_, _ = fmt.Fprintln(help, "Flags:")
fs.PrintDefaults()
}
if err := parseSessionAwareFlags(command, fs, args, &configFlags.sessionID); err != nil {
return boundedRunRequest{}, err
}
if configFlags.sessionID == "" {
return boundedRunRequest{}, fmt.Errorf("%s: session_id is required", command)
}
plan, err := BuildBoundedPlan(from.value, through.value)
if err != nil {
return boundedRunRequest{}, fmt.Errorf("%s: %w", command, err)
}
normalizedArtifacts, err := selectedArtifacts.Normalize()
if err != nil {
return boundedRunRequest{}, fmt.Errorf("%s: invalid --artifacts: %w", command, err)
}
if len(normalizedArtifacts) > 0 && !plan.Contains("analyze") && !plan.Contains("publish") {
return boundedRunRequest{}, fmt.Errorf("%s: --artifacts requires a selected range containing analyze or publish", command)
}
return boundedRunRequest{
Config: configFlags,
Plan: plan,
Force: force.value,
SelectedArtifacts: normalizedArtifacts,
}, nil
}