gitlink-cli/shortcuts/workflow/engine.go

83 lines
2.3 KiB
Go

package workflow
import (
"encoding/json"
"fmt"
"strings"
"github.com/gitlink-org/gitlink-cli/shortcuts/common"
)
// WorkflowResult holds the outcome of a full workflow run.
type WorkflowResult struct {
Workflow string `json:"workflow"`
Owner string `json:"owner"`
Repo string `json:"repo"`
Steps []StepResult `json:"steps"`
}
// Run executes every step in a workflow sequentially.
// Steps later in the sequence receive data from their DependsOn predecessors
// via ctx.Args (keyed by step name, stored as JSON).
// Set dryRun to true to skip AI API calls for skill steps.
func Run(ctx *common.RuntimeContext, wf *WorkflowDef, dryRun bool) (*WorkflowResult, error) {
return RunWithMode(ctx, wf, dryRun, "")
}
// RunWithMode executes a workflow with explicit AI mode control.
// aiMode must be "auto", "ai", "no-ai", or "" (equivalent to "auto").
func RunWithMode(ctx *common.RuntimeContext, wf *WorkflowDef, dryRun bool, aiMode string) (*WorkflowResult, error) {
if aiMode != "" {
ctx.AIMode = aiMode
}
return run(ctx, wf, dryRun)
}
func run(ctx *common.RuntimeContext, wf *WorkflowDef, dryRun bool) (*WorkflowResult, error) {
if err := ctx.ResolveOwnerRepo(); err != nil {
return nil, fmt.Errorf("resolve repo: %w", err)
}
if ctx.Args == nil {
ctx.Args = make(map[string]string)
}
if _, ok := ctx.Args["dry_run"]; !ok && dryRun {
ctx.Args["dry_run"] = "true"
}
results := make([]StepResult, 0, len(wf.Steps))
for _, step := range wf.Steps {
sr := ExecuteStep(ctx, step, dryRun)
results = append(results, *sr)
// Feed output of this step as input to downstream steps via Args.
if sr.OK && sr.Data != nil {
raw, err := json.Marshal(sr.Data)
if err == nil {
ctx.Args[step.Name] = string(raw)
} else {
ctx.Args[step.Name] = fmt.Sprint(sr.Data)
}
}
}
return &WorkflowResult{
Workflow: wf.Name,
Owner: ctx.Owner,
Repo: ctx.Repo,
Steps: results,
}, nil
}
// resolvePath replaces template placeholders in a path string.
//
// {base} → /owner/repo
// {v1} → /v1/owner/repo
func resolvePath(template, owner, repo string) string {
base := fmt.Sprintf("/%s/%s", owner, repo)
v1 := fmt.Sprintf("/v1/%s/%s", owner, repo)
s := strings.Replace(template, "{v1}", v1, 1)
s = strings.Replace(s, "{base}", base, 1)
return s
}