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
33 changes: 23 additions & 10 deletions pkg/cli/add_command.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ The --force flag overwrites existing workflow files.`,
nameFlag, _ := cmd.Flags().GetString("name")
prFlag, _ := cmd.Flags().GetBool("pr")
forceFlag, _ := cmd.Flags().GetBool("force")
appendText, _ := cmd.Flags().GetString("append")
verbose, _ := cmd.Flags().GetBool("verbose")

// If no arguments provided and not in CI, automatically use interactive mode
Expand All @@ -66,12 +67,12 @@ The --force flag overwrites existing workflow files.`,

// Handle normal mode
if prFlag {
if err := AddWorkflows(workflows, numberFlag, verbose, engineOverride, nameFlag, forceFlag, true); err != nil {
if err := AddWorkflows(workflows, numberFlag, verbose, engineOverride, nameFlag, forceFlag, appendText, true); err != nil {
fmt.Fprintln(os.Stderr, console.FormatErrorMessage(err.Error()))
os.Exit(1)
}
} else {
if err := AddWorkflows(workflows, numberFlag, verbose, engineOverride, nameFlag, forceFlag, false); err != nil {
if err := AddWorkflows(workflows, numberFlag, verbose, engineOverride, nameFlag, forceFlag, appendText, false); err != nil {
fmt.Fprintln(os.Stderr, console.FormatErrorMessage(err.Error()))
os.Exit(1)
}
Expand All @@ -97,12 +98,15 @@ The --force flag overwrites existing workflow files.`,
// Add force flag to add command
cmd.Flags().Bool("force", false, "Overwrite existing workflow files")

// Add append flag to add command
cmd.Flags().String("append", "", "Append extra content to the end of agentic workflow on installation")

return cmd
}

// AddWorkflows adds one or more workflows from components to .github/workflows
// with optional repository installation and PR creation
func AddWorkflows(workflows []string, number int, verbose bool, engineOverride string, name string, force bool, createPR bool) error {
func AddWorkflows(workflows []string, number int, verbose bool, engineOverride string, name string, force bool, appendText string, createPR bool) error {
if len(workflows) == 0 {
return fmt.Errorf("at least one workflow name is required")
}
Expand Down Expand Up @@ -170,15 +174,15 @@ func AddWorkflows(workflows []string, number int, verbose bool, engineOverride s

// Handle PR creation workflow
if createPR {
return addWorkflowsWithPR(processedWorkflows, number, verbose, engineOverride, name, force)
return addWorkflowsWithPR(processedWorkflows, number, verbose, engineOverride, name, force, appendText)
}

// Handle normal workflow addition
return addWorkflowsNormal(processedWorkflows, number, verbose, engineOverride, name, force)
return addWorkflowsNormal(processedWorkflows, number, verbose, engineOverride, name, force, appendText)
}

// addWorkflowsNormal handles normal workflow addition without PR creation
func addWorkflowsNormal(workflows []*WorkflowSpec, number int, verbose bool, engineOverride string, name string, force bool) error {
func addWorkflowsNormal(workflows []*WorkflowSpec, number int, verbose bool, engineOverride string, name string, force bool, appendText string) error {
// Create file tracker for all operations
tracker, err := NewFileTracker()
if err != nil {
Expand All @@ -205,7 +209,7 @@ func addWorkflowsNormal(workflows []*WorkflowSpec, number int, verbose bool, eng
currentName = name
}

if err := addWorkflowWithTracking(workflow, number, verbose, engineOverride, currentName, force, tracker); err != nil {
if err := addWorkflowWithTracking(workflow, number, verbose, engineOverride, currentName, force, appendText, tracker); err != nil {
return fmt.Errorf("failed to add workflow '%s': %w", workflow.String(), err)
}
}
Expand All @@ -218,7 +222,7 @@ func addWorkflowsNormal(workflows []*WorkflowSpec, number int, verbose bool, eng
}

// addWorkflowsWithPR handles workflow addition with PR creation
func addWorkflowsWithPR(workflows []*WorkflowSpec, number int, verbose bool, engineOverride string, name string, force bool) error {
func addWorkflowsWithPR(workflows []*WorkflowSpec, number int, verbose bool, engineOverride string, name string, force bool, appendText string) error {
// Get current branch for restoration later
currentBranch, err := getCurrentBranch()
if err != nil {
Expand Down Expand Up @@ -247,7 +251,7 @@ func addWorkflowsWithPR(workflows []*WorkflowSpec, number int, verbose bool, eng
}()

// Add workflows using the normal function logic
if err := addWorkflowsNormal(workflows, number, verbose, engineOverride, name, force); err != nil {
if err := addWorkflowsNormal(workflows, number, verbose, engineOverride, name, force, appendText); err != nil {
// Rollback on error
if rollbackErr := tracker.RollbackAllFiles(verbose); rollbackErr != nil && verbose {
fmt.Fprintln(os.Stderr, console.FormatWarningMessage(fmt.Sprintf("Failed to rollback files: %v", rollbackErr)))
Expand Down Expand Up @@ -326,7 +330,7 @@ func addWorkflowsWithPR(workflows []*WorkflowSpec, number int, verbose bool, eng
}

// addWorkflowWithTracking adds a workflow from components to .github/workflows with file tracking
func addWorkflowWithTracking(workflow *WorkflowSpec, number int, verbose bool, engineOverride string, name string, force bool, tracker *FileTracker) error {
func addWorkflowWithTracking(workflow *WorkflowSpec, number int, verbose bool, engineOverride string, name string, force bool, appendText string, tracker *FileTracker) error {
if verbose {
fmt.Fprintln(os.Stderr, console.FormatInfoMessage(fmt.Sprintf("Adding workflow: %s", workflow.String())))
fmt.Fprintln(os.Stderr, console.FormatInfoMessage(fmt.Sprintf("Number of copies: %d", number)))
Expand Down Expand Up @@ -472,6 +476,15 @@ func addWorkflowWithTracking(workflow *WorkflowSpec, number int, verbose bool, e
}
}

// Append text if provided
if appendText != "" {
// Ensure we have a newline before appending
if !strings.HasSuffix(content, "\n") {
content += "\n"
}
content += "\n" + appendText
}

// Track the file based on whether it existed before (if tracker is available)
if tracker != nil {
if fileExists {
Expand Down
2 changes: 1 addition & 1 deletion pkg/cli/local_workflow_trial_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ This is a test workflow.
}

// Test the local installation function
err = installLocalWorkflowInTrialMode(originalDir, tempDir, spec, false)
err = installLocalWorkflowInTrialMode(originalDir, tempDir, spec, "", false)
if err != nil {
t.Fatalf("Failed to install local workflow: %v", err)
}
Expand Down
27 changes: 20 additions & 7 deletions pkg/cli/trial_command.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,14 +109,15 @@ Trial results are saved both locally (in trials/ directory) and in the host repo
repeatCount, _ := cmd.Flags().GetInt("repeat")
autoMergePRs, _ := cmd.Flags().GetBool("auto-merge-prs")
engineOverride, _ := cmd.Flags().GetString("engine")
appendText, _ := cmd.Flags().GetString("append")
verbose, _ := cmd.Root().PersistentFlags().GetBool("verbose")

if err := validateEngine(engineOverride); err != nil {
fmt.Fprintln(os.Stderr, console.FormatErrorMessage(err.Error()))
os.Exit(1)
}

if err := RunWorkflowTrials(workflowSpecs, logicalRepoSpec, cloneRepoSpec, hostRepoSpec, deleteHostRepo, forceDeleteHostRepo, yes, timeout, triggerContext, repeatCount, autoMergePRs, engineOverride, verbose); err != nil {
if err := RunWorkflowTrials(workflowSpecs, logicalRepoSpec, cloneRepoSpec, hostRepoSpec, deleteHostRepo, forceDeleteHostRepo, yes, timeout, triggerContext, repeatCount, autoMergePRs, engineOverride, appendText, verbose); err != nil {
fmt.Fprintln(os.Stderr, console.FormatErrorMessage(err.Error()))
os.Exit(1)
}
Expand All @@ -143,12 +144,13 @@ Trial results are saved both locally (in trials/ directory) and in the host repo
cmd.Flags().Int("repeat", 0, "Number of times to repeat running workflows (0 = run once)")
cmd.Flags().Bool("auto-merge-prs", false, "Auto-merge any pull requests created during the trial (requires --clone-repo)")
cmd.Flags().StringP("engine", "a", "", "Override AI engine (claude, codex, copilot, custom)")
cmd.Flags().String("append", "", "Append extra content to the end of agentic workflow on installation")

return cmd
}

// RunWorkflowTrials executes the main logic for trialing one or more workflows
func RunWorkflowTrials(workflowSpecs []string, logicalRepoSpec string, cloneRepoSpec string, hostRepoSpec string, deleteHostRepo, forceDeleteHostRepo, quiet bool, timeoutMinutes int, triggerContext string, repeatCount int, autoMergePRs bool, engineOverride string, verbose bool) error {
func RunWorkflowTrials(workflowSpecs []string, logicalRepoSpec string, cloneRepoSpec string, hostRepoSpec string, deleteHostRepo, forceDeleteHostRepo, quiet bool, timeoutMinutes int, triggerContext string, repeatCount int, autoMergePRs bool, engineOverride string, appendText string, verbose bool) error {
// Parse all workflow specifications
var parsedSpecs []*WorkflowSpec
for _, spec := range workflowSpecs {
Expand Down Expand Up @@ -296,7 +298,7 @@ func RunWorkflowTrials(workflowSpecs []string, logicalRepoSpec string, cloneRepo
fmt.Fprintln(os.Stderr, console.FormatInfoMessage(fmt.Sprintf("=== Running trial for workflow: %s ===", parsedSpec.WorkflowName)))

// Install workflow with trial mode compilation
if err := installWorkflowInTrialMode(tempDir, parsedSpec, logicalRepoSlug, cloneRepoSlug, hostRepoSlug, secretTracker, engineOverride, verbose); err != nil {
if err := installWorkflowInTrialMode(tempDir, parsedSpec, logicalRepoSlug, cloneRepoSlug, hostRepoSlug, secretTracker, engineOverride, appendText, verbose); err != nil {
return fmt.Errorf("failed to install workflow '%s' in trial mode: %w", parsedSpec.WorkflowName, err)
}

Expand Down Expand Up @@ -699,7 +701,7 @@ func cloneTrialHostRepository(repoSlug string, verbose bool) (string, error) {
}

// installWorkflowInTrialMode installs a workflow in trial mode using a parsed spec
func installWorkflowInTrialMode(tempDir string, parsedSpec *WorkflowSpec, logicalRepoSlug, cloneRepoSlug, hostRepoSlug string, secretTracker *TrialSecretTracker, engineOverride string, verbose bool) error {
func installWorkflowInTrialMode(tempDir string, parsedSpec *WorkflowSpec, logicalRepoSlug, cloneRepoSlug, hostRepoSlug string, secretTracker *TrialSecretTracker, engineOverride string, appendText string, verbose bool) error {
// Change to temp directory
originalDir, err := os.Getwd()
if err != nil {
Expand All @@ -718,7 +720,7 @@ func installWorkflowInTrialMode(tempDir string, parsedSpec *WorkflowSpec, logica
}

// For local workflows, copy the file directly from the filesystem
if err := installLocalWorkflowInTrialMode(originalDir, tempDir, parsedSpec, verbose); err != nil {
if err := installLocalWorkflowInTrialMode(originalDir, tempDir, parsedSpec, appendText, verbose); err != nil {
return fmt.Errorf("failed to install local workflow: %w", err)
}
} else {
Expand All @@ -732,7 +734,7 @@ func installWorkflowInTrialMode(tempDir string, parsedSpec *WorkflowSpec, logica
}

// Add the workflow from the installed package
if err := AddWorkflows([]string{parsedSpec.String()}, 1, verbose, "", "", true, false); err != nil {
if err := AddWorkflows([]string{parsedSpec.String()}, 1, verbose, "", "", true, appendText, false); err != nil {
return fmt.Errorf("failed to add workflow: %w", err)
}
}
Expand Down Expand Up @@ -779,7 +781,7 @@ func installWorkflowInTrialMode(tempDir string, parsedSpec *WorkflowSpec, logica
}

// installLocalWorkflowInTrialMode installs a local workflow file for trial mode
func installLocalWorkflowInTrialMode(originalDir, tempDir string, parsedSpec *WorkflowSpec, verbose bool) error {
func installLocalWorkflowInTrialMode(originalDir, tempDir string, parsedSpec *WorkflowSpec, appendText string, verbose bool) error {
// Construct the source path (relative to original directory)
sourcePath := filepath.Join(originalDir, parsedSpec.WorkflowPath)

Expand All @@ -803,6 +805,17 @@ func installLocalWorkflowInTrialMode(originalDir, tempDir string, parsedSpec *Wo
return fmt.Errorf("failed to read local workflow file: %w", err)
}

// Append text if provided
if appendText != "" {
contentStr := string(content)
// Ensure we have a newline before appending
if !strings.HasSuffix(contentStr, "\n") {
contentStr += "\n"
}
contentStr += "\n" + appendText
content = []byte(contentStr)
}

// Write the content to the destination
if err := os.WriteFile(destPath, content, 0644); err != nil {
return fmt.Errorf("failed to write workflow to destination: %w", err)
Expand Down
Loading