Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion cmd/gh-aw/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -480,7 +480,16 @@ This command only works with workflows that have workflow_dispatch triggers.
return errors.New("workflow inputs cannot be specified in interactive mode (they will be collected interactively)")
}

return cli.RunWorkflowInteractively(cmd.Context(), verboseFlag, repoOverride, refOverride, autoMergePRs, push, engineOverride, dryRun, approveRun)
return cli.RunWorkflowInteractively(cmd.Context(), cli.RunWorkflowOptions{
Verbose: verboseFlag,
RepoOverride: repoOverride,
RefOverride: refOverride,
AutoMergePRs: autoMergePRs,
Push: push,
EngineOverride: engineOverride,
DryRun: dryRun,
Approve: approveRun,
})
}

return cli.RunWorkflowsOnGitHub(cmd.Context(), args, cli.RunOptions{
Expand Down
41 changes: 27 additions & 14 deletions pkg/cli/add_workflow_compilation.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,12 +125,23 @@ func compileWorkflowWithTrackingAndRefresh(ctx context.Context, filePath string,
return nil
}

// compileDepsOptions holds shared compilation options for dependency compilation helpers.
type compileDepsOptions struct {
verbose, quiet bool
engineOverride string
force bool
propagateErrors bool
tracker *FileTracker
}

// compileDispatchWorkflowDependencies compiles any dispatch-workflow .md dependencies of
// workflowFile that are present locally but lack a corresponding .lock.yml. This must be
// called before compiling the main workflow, because the dispatch-workflow validator
// requires every referenced .md workflow to have an up-to-date .lock.yml.
func compileDispatchWorkflowDependencies(ctx context.Context, workflowFile string, verbose, quiet bool, engineOverride string, force bool, tracker *FileTracker) {
compileSafeOutputsWorkflowDependencies(ctx, workflowFile, "dispatch-workflow dependency", dispatchWorkflowNamesForCompilation, verbose, quiet, engineOverride, force, false, tracker)
compileSafeOutputsWorkflowDependencies(ctx, workflowFile, "dispatch-workflow dependency", dispatchWorkflowNamesForCompilation, compileDepsOptions{
verbose: verbose, quiet: quiet, engineOverride: engineOverride, force: force, propagateErrors: false, tracker: tracker,
})
}

// compileCallWorkflowDependencies compiles any call-workflow .md worker dependencies of
Expand All @@ -143,18 +154,20 @@ func compileDispatchWorkflowDependencies(ctx context.Context, workflowFile strin
// worker whose lock cannot be produced would leave the orchestrator referencing a file that
// does not exist.
func compileCallWorkflowDependencies(ctx context.Context, workflowFile string, verbose, quiet bool, engineOverride string, force bool, tracker *FileTracker) error {
return compileSafeOutputsWorkflowDependencies(ctx, workflowFile, "call-workflow worker", callWorkflowNamesForCompilation, verbose, quiet, engineOverride, force, true, tracker)
return compileSafeOutputsWorkflowDependencies(ctx, workflowFile, "call-workflow worker", callWorkflowNamesForCompilation, compileDepsOptions{
verbose: verbose, quiet: quiet, engineOverride: engineOverride, force: force, propagateErrors: true, tracker: tracker,
})
}

// compileSafeOutputsWorkflowDependencies is the shared implementation for compiling
// local .md worker/dependency files referenced by a workflow. namesFunc extracts the
// list of referenced workflow names from workflowFile; label is used in log/console
// messages to identify the dependency type (e.g. "dispatch-workflow dependency").
// When propagateErrors is true the first compilation failure is returned to the caller
// instead of being logged and swallowed.
// When force is true, dependencies are recompiled even when a .lock.yml already exists
// (needed after --force re-fetches a stale worker .md).
func compileSafeOutputsWorkflowDependencies(ctx context.Context, workflowFile, label string, namesFunc func(string) []string, verbose, quiet bool, engineOverride string, force bool, propagateErrors bool, tracker *FileTracker) error {
// When opts.propagateErrors is true the first compilation failure is returned to the
// caller instead of being logged and swallowed.
// When opts.force is true, dependencies are recompiled even when a .lock.yml already
// exists (needed after --force re-fetches a stale worker .md).
func compileSafeOutputsWorkflowDependencies(ctx context.Context, workflowFile, label string, namesFunc func(string) []string, opts compileDepsOptions) error {
workflowNames := namesFunc(workflowFile)
if len(workflowNames) == 0 {
return nil
Expand All @@ -171,27 +184,27 @@ func compileSafeOutputsWorkflowDependencies(ctx context.Context, workflowFile, l
continue
}
// Skip recompilation when a lock already exists, unless --force was specified.
if !force && fileutil.FileExists(lockPath) {
if !opts.force && fileutil.FileExists(lockPath) {
continue
}

addWorkflowCompilationLog.Printf("Compiling %s: %s", label, mdPath)
if verbose {
if opts.verbose {
fmt.Fprintln(os.Stderr, console.FormatInfoMessage(fmt.Sprintf("Compiling %s: %s", label, mdPath)))
}

var compileErr error
if tracker != nil {
compileErr = compileWorkflowWithTracking(ctx, mdPath, verbose, quiet, engineOverride, tracker)
if opts.tracker != nil {
compileErr = compileWorkflowWithTracking(ctx, mdPath, opts.verbose, opts.quiet, opts.engineOverride, opts.tracker)
} else {
compileErr = compileWorkflow(ctx, mdPath, verbose, quiet, engineOverride)
compileErr = compileWorkflow(ctx, mdPath, opts.verbose, opts.quiet, opts.engineOverride)
}
if compileErr != nil {
if propagateErrors {
if opts.propagateErrors {
return fmt.Errorf("failed to compile %s %s: %w", label, mdPath, compileErr)
}
// Best-effort: log and continue so the main workflow can still give a clear error.
if verbose {
if opts.verbose {
fmt.Fprintln(os.Stderr, console.FormatWarningMessage(fmt.Sprintf("Failed to compile %s %s: %v", label, mdPath, compileErr)))
}
}
Expand Down
24 changes: 12 additions & 12 deletions pkg/cli/run_interactive.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,20 +28,20 @@ type WorkflowOption struct {
}

// RunWorkflowInteractively runs a workflow in interactive mode
func RunWorkflowInteractively(ctx context.Context, verbose bool, repoOverride string, refOverride string, autoMergePRs bool, push bool, engineOverride string, dryRun bool, approve bool) error {
func RunWorkflowInteractively(ctx context.Context, opts RunWorkflowOptions) error {
runInteractiveLog.Print("Starting interactive workflow run")

// Check if running in CI environment
if IsRunningInCI() {
return errors.New("interactive mode cannot be used in CI environments")
}

if verbose {
if opts.Verbose {
fmt.Fprintln(os.Stderr, console.FormatInfoMessage("Starting interactive workflow run..."))
}

// Step 1: Find workflows with workflow_dispatch trigger
workflows, err := findRunnableWorkflows(verbose)
workflows, err := findRunnableWorkflows(opts.Verbose)
if err != nil {
return fmt.Errorf("failed to find runnable workflows: %w", err)
}
Expand Down Expand Up @@ -74,23 +74,23 @@ func RunWorkflowInteractively(ctx context.Context, verbose bool, repoOverride st
}

// Step 6: Build command string for display
cmdStr := buildCommandString(selectedWorkflow.Name, inputValues, repoOverride, refOverride, autoMergePRs, push, engineOverride, approve)
cmdStr := buildCommandString(selectedWorkflow.Name, inputValues, opts.RepoOverride, opts.RefOverride, opts.AutoMergePRs, opts.Push, opts.EngineOverride, opts.Approve)
fmt.Fprintln(os.Stderr, console.FormatInfoMessage("\nRunning workflow..."))
fmt.Fprintln(os.Stderr, console.FormatCommandMessage("Equivalent command: "+cmdStr))
fmt.Fprintln(os.Stderr, "")

// Step 7: Execute the workflow
err = RunWorkflowOnGitHub(ctx, selectedWorkflow.Name, RunOptions{
Enable: false,
EngineOverride: engineOverride,
RepoOverride: repoOverride,
RefOverride: refOverride,
AutoMergePRs: autoMergePRs,
Push: push,
EngineOverride: opts.EngineOverride,
RepoOverride: opts.RepoOverride,
RefOverride: opts.RefOverride,
AutoMergePRs: opts.AutoMergePRs,
Push: opts.Push,
Inputs: inputValues,
Verbose: verbose,
DryRun: dryRun,
Approve: approve,
Verbose: opts.Verbose,
DryRun: opts.DryRun,
Approve: opts.Approve,
})
if err != nil {
return fmt.Errorf("failed to run workflow: %w", err)
Expand Down
Loading