feat: formulario de flags y filtro launcher en pipeline_launcher
Parsea flags de -help de cada pipeline para mostrar formulario de argumentos antes de ejecutar. Filtra pipelines por tag 'launcher'. Corrige selección en historial delegando enter al list antes de leer item.
This commit is contained in:
@@ -4,14 +4,15 @@ go 1.22.2
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
fn-registry v0.0.0
|
fn-registry v0.0.0
|
||||||
|
github.com/charmbracelet/bubbles v0.18.0
|
||||||
github.com/charmbracelet/bubbletea v0.25.0
|
github.com/charmbracelet/bubbletea v0.25.0
|
||||||
github.com/charmbracelet/lipgloss v0.9.1
|
github.com/charmbracelet/lipgloss v0.9.1
|
||||||
github.com/lucasdataproyects/devfactory v0.0.0
|
github.com/lucasdataproyects/devfactory v0.0.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/atotto/clipboard v0.1.4 // indirect
|
||||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
|
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
|
||||||
github.com/charmbracelet/bubbles v0.18.0 // indirect
|
|
||||||
github.com/charmbracelet/harmonica v0.2.0 // indirect
|
github.com/charmbracelet/harmonica v0.2.0 // indirect
|
||||||
github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 // indirect
|
github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 // indirect
|
||||||
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
|
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4=
|
||||||
|
github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
|
||||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
|
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
|
||||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
|
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
|
||||||
github.com/charmbracelet/bubbles v0.18.0 h1:PYv1A036luoBGroX6VWjQIE9Syf2Wby2oOl/39KLfy0=
|
github.com/charmbracelet/bubbles v0.18.0 h1:PYv1A036luoBGroX6VWjQIE9Syf2Wby2oOl/39KLfy0=
|
||||||
|
|||||||
@@ -106,6 +106,9 @@ func (m HistoryModel) Update(msg tea.Msg) (HistoryModel, tea.Cmd) {
|
|||||||
m.spinner = tui.NewSpinner("Loading history...")
|
m.spinner = tui.NewSpinner("Loading history...")
|
||||||
return m, tea.Batch(m.spinner.Init(), m.loadHistory())
|
return m, tea.Batch(m.spinner.Init(), m.loadHistory())
|
||||||
case "enter":
|
case "enter":
|
||||||
|
// Delegate enter to list first so it selects the cursor item
|
||||||
|
updated, _ := m.list.Update(msg)
|
||||||
|
m.list = updated.(tui.FilteredListModel)
|
||||||
if item := m.list.SelectedItem(); item != nil {
|
if item := m.list.SelectedItem(); item != nil {
|
||||||
e := item.Value.(ops.Execution)
|
e := item.Value.(ops.Execution)
|
||||||
m.detail = formatExecution(e)
|
m.detail = formatExecution(e)
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
ops "fn-registry/fn_operations"
|
ops "fn-registry/fn_operations"
|
||||||
"fn-registry/registry"
|
"fn-registry/registry"
|
||||||
|
|
||||||
|
"github.com/charmbracelet/bubbles/textinput"
|
||||||
tea "github.com/charmbracelet/bubbletea"
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
"github.com/charmbracelet/lipgloss"
|
"github.com/charmbracelet/lipgloss"
|
||||||
"github.com/lucasdataproyects/devfactory/tui"
|
"github.com/lucasdataproyects/devfactory/tui"
|
||||||
@@ -17,12 +18,14 @@ type pipelinesState int
|
|||||||
const (
|
const (
|
||||||
pipelinesLoading pipelinesState = iota
|
pipelinesLoading pipelinesState = iota
|
||||||
pipelinesList
|
pipelinesList
|
||||||
|
pipelinesArgs
|
||||||
pipelinesRunning
|
pipelinesRunning
|
||||||
pipelinesOutput
|
pipelinesOutput
|
||||||
)
|
)
|
||||||
|
|
||||||
type pipelinesLoadedMsg []registry.Function
|
type pipelinesLoadedMsg []registry.Function
|
||||||
type pipelineFinishedMsg RunResult
|
type pipelineFinishedMsg RunResult
|
||||||
|
type pipelineFlagsMsg []PipelineFlag
|
||||||
|
|
||||||
// PipelinesModel lists and launches pipelines.
|
// PipelinesModel lists and launches pipelines.
|
||||||
type PipelinesModel struct {
|
type PipelinesModel struct {
|
||||||
@@ -31,6 +34,10 @@ type PipelinesModel struct {
|
|||||||
spinner tui.SpinnerModel
|
spinner tui.SpinnerModel
|
||||||
styles tui.Styles
|
styles tui.Styles
|
||||||
pipelines []registry.Function
|
pipelines []registry.Function
|
||||||
|
selectedFn *registry.Function
|
||||||
|
flags []PipelineFlag
|
||||||
|
inputs []textinput.Model
|
||||||
|
focusIdx int
|
||||||
output string
|
output string
|
||||||
lastResult *RunResult
|
lastResult *RunResult
|
||||||
scrollOff int
|
scrollOff int
|
||||||
@@ -66,10 +73,67 @@ func (m PipelinesModel) loadPipelines() tea.Cmd {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return pipelinesLoadedMsg(nil)
|
return pipelinesLoadedMsg(nil)
|
||||||
}
|
}
|
||||||
return pipelinesLoadedMsg(fns)
|
// Only show pipelines tagged with "launcher"
|
||||||
|
var launchable []registry.Function
|
||||||
|
for _, f := range fns {
|
||||||
|
for _, t := range f.Tags {
|
||||||
|
if t == "launcher" {
|
||||||
|
launchable = append(launchable, f)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return pipelinesLoadedMsg(launchable)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// buildInputs creates a textinput for each flag, pre-filled with defaults.
|
||||||
|
func (m *PipelinesModel) buildInputs() tea.Cmd {
|
||||||
|
m.inputs = make([]textinput.Model, len(m.flags))
|
||||||
|
for i, f := range m.flags {
|
||||||
|
ti := textinput.New()
|
||||||
|
ti.CharLimit = 256
|
||||||
|
ti.Width = 40
|
||||||
|
if f.Default != "" {
|
||||||
|
ti.SetValue(f.Default)
|
||||||
|
}
|
||||||
|
if f.Required {
|
||||||
|
ti.Placeholder = "(requerido)"
|
||||||
|
}
|
||||||
|
m.inputs[i] = ti
|
||||||
|
}
|
||||||
|
m.focusIdx = 0
|
||||||
|
if len(m.inputs) > 0 {
|
||||||
|
m.inputs[0].Focus()
|
||||||
|
return textinput.Blink
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *PipelinesModel) focusInput(idx int) tea.Cmd {
|
||||||
|
if idx < 0 || idx >= len(m.inputs) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
for i := range m.inputs {
|
||||||
|
m.inputs[i].Blur()
|
||||||
|
}
|
||||||
|
m.focusIdx = idx
|
||||||
|
m.inputs[idx].Focus()
|
||||||
|
return textinput.Blink
|
||||||
|
}
|
||||||
|
|
||||||
|
// collectArgs builds CLI args from the form inputs.
|
||||||
|
func (m PipelinesModel) collectArgs() []string {
|
||||||
|
var args []string
|
||||||
|
for i, f := range m.flags {
|
||||||
|
val := strings.TrimSpace(m.inputs[i].Value())
|
||||||
|
if val != "" {
|
||||||
|
args = append(args, "--"+f.Name, val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return args
|
||||||
|
}
|
||||||
|
|
||||||
func (m PipelinesModel) Update(msg tea.Msg) (PipelinesModel, tea.Cmd) {
|
func (m PipelinesModel) Update(msg tea.Msg) (PipelinesModel, tea.Cmd) {
|
||||||
switch msg := msg.(type) {
|
switch msg := msg.(type) {
|
||||||
case pipelinesLoadedMsg:
|
case pipelinesLoadedMsg:
|
||||||
@@ -86,19 +150,23 @@ func (m PipelinesModel) Update(msg tea.Msg) (PipelinesModel, tea.Cmd) {
|
|||||||
m.state = pipelinesList
|
m.state = pipelinesList
|
||||||
return m, nil
|
return m, nil
|
||||||
|
|
||||||
|
case pipelineFlagsMsg:
|
||||||
|
m.flags = []PipelineFlag(msg)
|
||||||
|
cmd := m.buildInputs()
|
||||||
|
return m, cmd
|
||||||
|
|
||||||
case pipelineFinishedMsg:
|
case pipelineFinishedMsg:
|
||||||
result := RunResult(msg)
|
result := RunResult(msg)
|
||||||
m.lastResult = &result
|
m.lastResult = &result
|
||||||
// Build output
|
|
||||||
var sb strings.Builder
|
var sb strings.Builder
|
||||||
if result.Status == ops.ExecSuccess {
|
if result.Status == ops.ExecSuccess {
|
||||||
sb.WriteString("[OK] ")
|
sb.WriteString("[OK] ")
|
||||||
} else {
|
} else {
|
||||||
sb.WriteString("[FAIL] ")
|
sb.WriteString("[FAIL] ")
|
||||||
}
|
}
|
||||||
sb.WriteString(fmt.Sprintf("Pipeline: %s\n", result.PipelineID))
|
fmt.Fprintf(&sb, "Pipeline: %s\n", result.PipelineID)
|
||||||
sb.WriteString(fmt.Sprintf("Execution: %s\n", result.ExecID))
|
fmt.Fprintf(&sb, "Execution: %s\n", result.ExecID)
|
||||||
sb.WriteString(fmt.Sprintf("Duration: %dms\n", result.DurationMs))
|
fmt.Fprintf(&sb, "Duration: %dms\n", result.DurationMs)
|
||||||
sb.WriteString("\n--- stdout ---\n")
|
sb.WriteString("\n--- stdout ---\n")
|
||||||
if result.Stdout != "" {
|
if result.Stdout != "" {
|
||||||
sb.WriteString(result.Stdout)
|
sb.WriteString(result.Stdout)
|
||||||
@@ -110,7 +178,7 @@ func (m PipelinesModel) Update(msg tea.Msg) (PipelinesModel, tea.Cmd) {
|
|||||||
sb.WriteString(result.Stderr)
|
sb.WriteString(result.Stderr)
|
||||||
}
|
}
|
||||||
if result.Err != nil {
|
if result.Err != nil {
|
||||||
sb.WriteString(fmt.Sprintf("\n--- error ---\n%v", result.Err))
|
fmt.Fprintf(&sb, "\n--- error ---\n%v", result.Err)
|
||||||
}
|
}
|
||||||
m.output = sb.String()
|
m.output = sb.String()
|
||||||
m.state = pipelinesOutput
|
m.state = pipelinesOutput
|
||||||
@@ -126,12 +194,42 @@ func (m PipelinesModel) Update(msg tea.Msg) (PipelinesModel, tea.Cmd) {
|
|||||||
m.spinner = tui.NewSpinner("Loading pipelines...")
|
m.spinner = tui.NewSpinner("Loading pipelines...")
|
||||||
return m, tea.Batch(m.spinner.Init(), m.loadPipelines())
|
return m, tea.Batch(m.spinner.Init(), m.loadPipelines())
|
||||||
case "enter":
|
case "enter":
|
||||||
|
updated, _ := m.list.Update(msg)
|
||||||
|
m.list = updated.(tui.FilteredListModel)
|
||||||
if item := m.list.SelectedItem(); item != nil {
|
if item := m.list.SelectedItem(); item != nil {
|
||||||
fn := item.Value.(registry.Function)
|
fn := item.Value.(registry.Function)
|
||||||
m.state = pipelinesRunning
|
m.selectedFn = &fn
|
||||||
m.spinner = tui.NewSpinner(fmt.Sprintf("Running %s...", fn.Name))
|
m.flags = nil
|
||||||
return m, tea.Batch(m.spinner.Init(), m.runPipelineCmd(&fn))
|
m.inputs = nil
|
||||||
|
m.state = pipelinesArgs
|
||||||
|
root := m.registryRoot
|
||||||
|
fnCopy := fn
|
||||||
|
return m, func() tea.Msg {
|
||||||
|
return pipelineFlagsMsg(GetPipelineFlags(&fnCopy, root))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
case pipelinesArgs:
|
||||||
|
switch msg.String() {
|
||||||
|
case "tab", "down":
|
||||||
|
cmd := m.focusInput((m.focusIdx + 1) % max(len(m.inputs), 1))
|
||||||
|
return m, cmd
|
||||||
|
case "shift+tab", "up":
|
||||||
|
idx := m.focusIdx - 1
|
||||||
|
if idx < 0 {
|
||||||
|
idx = max(len(m.inputs)-1, 0)
|
||||||
|
}
|
||||||
|
cmd := m.focusInput(idx)
|
||||||
|
return m, cmd
|
||||||
|
case "ctrl+enter", "ctrl+s":
|
||||||
|
args := m.collectArgs()
|
||||||
|
m.state = pipelinesRunning
|
||||||
|
m.spinner = tui.NewSpinner(fmt.Sprintf("Running %s...", m.selectedFn.Name))
|
||||||
|
return m, tea.Batch(m.spinner.Init(), m.runPipelineCmd(m.selectedFn, args))
|
||||||
|
case "esc":
|
||||||
|
m.state = pipelinesList
|
||||||
|
return m, nil
|
||||||
}
|
}
|
||||||
case pipelinesOutput:
|
case pipelinesOutput:
|
||||||
switch msg.String() {
|
switch msg.String() {
|
||||||
@@ -157,16 +255,20 @@ func (m PipelinesModel) Update(msg tea.Msg) (PipelinesModel, tea.Cmd) {
|
|||||||
var listModel tea.Model
|
var listModel tea.Model
|
||||||
listModel, cmd = m.list.Update(msg)
|
listModel, cmd = m.list.Update(msg)
|
||||||
m.list = listModel.(tui.FilteredListModel)
|
m.list = listModel.(tui.FilteredListModel)
|
||||||
|
case pipelinesArgs:
|
||||||
|
if m.focusIdx >= 0 && m.focusIdx < len(m.inputs) {
|
||||||
|
m.inputs[m.focusIdx], cmd = m.inputs[m.focusIdx].Update(msg)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return m, cmd
|
return m, cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m PipelinesModel) runPipelineCmd(fn *registry.Function) tea.Cmd {
|
func (m PipelinesModel) runPipelineCmd(fn *registry.Function, args []string) tea.Cmd {
|
||||||
regRoot := m.registryRoot
|
regRoot := m.registryRoot
|
||||||
opsDB := m.opsDB
|
opsDB := m.opsDB
|
||||||
fnCopy := *fn
|
fnCopy := *fn
|
||||||
return func() tea.Msg {
|
return func() tea.Msg {
|
||||||
result := RunPipeline(&fnCopy, regRoot, opsDB)
|
result := RunPipeline(&fnCopy, regRoot, opsDB, args)
|
||||||
return pipelineFinishedMsg(result)
|
return pipelineFinishedMsg(result)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -174,6 +276,9 @@ func (m PipelinesModel) runPipelineCmd(fn *registry.Function) tea.Cmd {
|
|||||||
// HandleBack retrocede un nivel. Retorna true si ya en estado base.
|
// HandleBack retrocede un nivel. Retorna true si ya en estado base.
|
||||||
func (m *PipelinesModel) HandleBack() bool {
|
func (m *PipelinesModel) HandleBack() bool {
|
||||||
switch m.state {
|
switch m.state {
|
||||||
|
case pipelinesArgs:
|
||||||
|
m.state = pipelinesList
|
||||||
|
return false
|
||||||
case pipelinesOutput:
|
case pipelinesOutput:
|
||||||
m.state = pipelinesList
|
m.state = pipelinesList
|
||||||
return false
|
return false
|
||||||
@@ -192,6 +297,8 @@ func (m PipelinesModel) View() string {
|
|||||||
}
|
}
|
||||||
help := m.styles.Muted.Render(" Enter: launch │ r: refresh │ /: filter")
|
help := m.styles.Muted.Render(" Enter: launch │ r: refresh │ /: filter")
|
||||||
return m.list.View() + "\n" + help
|
return m.list.View() + "\n" + help
|
||||||
|
case pipelinesArgs:
|
||||||
|
return m.renderArgsForm()
|
||||||
case pipelinesRunning:
|
case pipelinesRunning:
|
||||||
return m.spinner.View()
|
return m.spinner.View()
|
||||||
case pipelinesOutput:
|
case pipelinesOutput:
|
||||||
@@ -200,6 +307,48 @@ func (m PipelinesModel) View() string {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m PipelinesModel) renderArgsForm() string {
|
||||||
|
header := m.styles.Header.Render(m.selectedFn.Name)
|
||||||
|
|
||||||
|
var parts []string
|
||||||
|
parts = append(parts, header, "")
|
||||||
|
|
||||||
|
if len(m.flags) == 0 {
|
||||||
|
parts = append(parts, m.styles.Muted.Render(" Loading flags..."))
|
||||||
|
} else if len(m.inputs) == 0 {
|
||||||
|
parts = append(parts, m.styles.Muted.Render(" No flags available. Ctrl+S to run."))
|
||||||
|
} else {
|
||||||
|
for i, f := range m.flags {
|
||||||
|
marker := " "
|
||||||
|
if f.Required {
|
||||||
|
marker = m.styles.Error.Render("* ")
|
||||||
|
}
|
||||||
|
|
||||||
|
name := fmt.Sprintf("--%-16s", f.Name)
|
||||||
|
cursor := " "
|
||||||
|
if i == m.focusIdx {
|
||||||
|
cursor = m.styles.Info.Render("> ")
|
||||||
|
}
|
||||||
|
|
||||||
|
label := fmt.Sprintf("%s%s%s", cursor, marker, m.styles.Label.Render(name))
|
||||||
|
input := m.inputs[i].View()
|
||||||
|
|
||||||
|
desc := f.Desc
|
||||||
|
if f.Default != "" {
|
||||||
|
desc += m.styles.Muted.Render(fmt.Sprintf(" (default: %s)", f.Default))
|
||||||
|
}
|
||||||
|
|
||||||
|
parts = append(parts, label+input)
|
||||||
|
parts = append(parts, " "+m.styles.Muted.Render(desc))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
parts = append(parts, "")
|
||||||
|
parts = append(parts, m.styles.Muted.Render(" ↑/↓: navigate │ Ctrl+S: run │ Esc: cancel"))
|
||||||
|
|
||||||
|
return lipgloss.JoinVertical(lipgloss.Left, parts...)
|
||||||
|
}
|
||||||
|
|
||||||
func (m PipelinesModel) renderOutput() string {
|
func (m PipelinesModel) renderOutput() string {
|
||||||
lines := splitLines(m.output)
|
lines := splitLines(m.output)
|
||||||
maxLines := 20
|
maxLines := 20
|
||||||
|
|||||||
@@ -5,12 +5,69 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
ops "fn-registry/fn_operations"
|
ops "fn-registry/fn_operations"
|
||||||
"fn-registry/registry"
|
"fn-registry/registry"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// PipelineFlag describes a CLI flag parsed from -help output.
|
||||||
|
type PipelineFlag struct {
|
||||||
|
Name string // e.g. "project"
|
||||||
|
Type string // e.g. "string"
|
||||||
|
Desc string // description text
|
||||||
|
Default string // default value, empty if none
|
||||||
|
Required bool // true if no default
|
||||||
|
}
|
||||||
|
|
||||||
|
var flagLineRe = regexp.MustCompile(`^\s+-(\S+)\s+(\S+)$`)
|
||||||
|
var defaultRe = regexp.MustCompile(`\(default "(.*)"\)`)
|
||||||
|
|
||||||
|
// GetPipelineFlags runs `go run . -help` and parses the flag output.
|
||||||
|
func GetPipelineFlags(fn *registry.Function, registryRoot string) []PipelineFlag {
|
||||||
|
absPath := filepath.Join(registryRoot, fn.FilePath)
|
||||||
|
dir := filepath.Dir(absPath)
|
||||||
|
|
||||||
|
cmd := exec.Command("go", "run", ".", "-help")
|
||||||
|
cmd.Dir = dir
|
||||||
|
var stderr bytes.Buffer
|
||||||
|
cmd.Stderr = &stderr
|
||||||
|
cmd.Run() // -help exits with code 2, ignore error
|
||||||
|
|
||||||
|
return parseFlags(stderr.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseFlags(output string) []PipelineFlag {
|
||||||
|
var flags []PipelineFlag
|
||||||
|
lines := strings.Split(output, "\n")
|
||||||
|
|
||||||
|
for i := 0; i < len(lines); i++ {
|
||||||
|
m := flagLineRe.FindStringSubmatch(lines[i])
|
||||||
|
if m == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
f := PipelineFlag{Name: m[1], Type: m[2]}
|
||||||
|
|
||||||
|
// Next line is the description
|
||||||
|
if i+1 < len(lines) {
|
||||||
|
desc := strings.TrimSpace(lines[i+1])
|
||||||
|
if dm := defaultRe.FindStringSubmatch(desc); dm != nil {
|
||||||
|
f.Default = dm[1]
|
||||||
|
f.Desc = strings.TrimSpace(defaultRe.ReplaceAllString(desc, ""))
|
||||||
|
} else {
|
||||||
|
f.Desc = desc
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
|
||||||
|
f.Required = f.Default == "" && !strings.Contains(strings.ToLower(f.Desc), "opcional")
|
||||||
|
flags = append(flags, f)
|
||||||
|
}
|
||||||
|
return flags
|
||||||
|
}
|
||||||
|
|
||||||
// RunResult holds the outcome of a pipeline execution.
|
// RunResult holds the outcome of a pipeline execution.
|
||||||
type RunResult struct {
|
type RunResult struct {
|
||||||
Stdout string
|
Stdout string
|
||||||
@@ -23,13 +80,14 @@ type RunResult struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// RunPipeline executes a pipeline as a subprocess and records the execution.
|
// RunPipeline executes a pipeline as a subprocess and records the execution.
|
||||||
func RunPipeline(fn *registry.Function, registryRoot string, opsDB *ops.DB) RunResult {
|
func RunPipeline(fn *registry.Function, registryRoot string, opsDB *ops.DB, args []string) RunResult {
|
||||||
absPath := filepath.Join(registryRoot, fn.FilePath)
|
absPath := filepath.Join(registryRoot, fn.FilePath)
|
||||||
dir := filepath.Dir(absPath)
|
dir := filepath.Dir(absPath)
|
||||||
|
|
||||||
startedAt := time.Now().UTC()
|
startedAt := time.Now().UTC()
|
||||||
|
|
||||||
cmd := exec.Command("go", "run", ".")
|
cmdArgs := append([]string{"run", "."}, args...)
|
||||||
|
cmd := exec.Command("go", cmdArgs...)
|
||||||
cmd.Dir = dir
|
cmd.Dir = dir
|
||||||
|
|
||||||
var stdout, stderr bytes.Buffer
|
var stdout, stderr bytes.Buffer
|
||||||
|
|||||||
Reference in New Issue
Block a user