diff --git a/cmd/gh-aw/main.go b/cmd/gh-aw/main.go index d7f19a9bbc..08a6f6930a 100644 --- a/cmd/gh-aw/main.go +++ b/cmd/gh-aw/main.go @@ -195,13 +195,19 @@ var disableCmd = &cobra.Command{ } var compileCmd = &cobra.Command{ - Use: "compile [markdown-file]", + Use: "compile [markdown-file]...", Short: "Compile markdown to YAML workflows", + Long: `Compile one or more markdown workflow files to YAML workflows. + +If no files are specified, all markdown files in .github/workflows will be compiled. + +Examples: + ` + constants.CLIExtensionPrefix + ` compile # Compile all markdown files + ` + constants.CLIExtensionPrefix + ` compile weekly-research # Compile a specific workflow + ` + constants.CLIExtensionPrefix + ` compile weekly-research daily-plan # Compile multiple workflows + ` + constants.CLIExtensionPrefix + ` compile workflow.md # Compile by file path + ` + constants.CLIExtensionPrefix + ` compile --watch weekly-research # Watch and auto-compile`, Run: func(cmd *cobra.Command, args []string) { - var file string - if len(args) > 0 { - file = args[0] - } engineOverride, _ := cmd.Flags().GetString("engine") validate, _ := cmd.Flags().GetBool("validate") watch, _ := cmd.Flags().GetBool("watch") @@ -210,7 +216,7 @@ var compileCmd = &cobra.Command{ fmt.Fprintln(os.Stderr, console.FormatErrorMessage(err.Error())) os.Exit(1) } - if err := cli.CompileWorkflows(file, verbose, engineOverride, validate, watch, instructions); err != nil { + if err := cli.CompileWorkflows(args, verbose, engineOverride, validate, watch, instructions); err != nil { fmt.Fprintln(os.Stderr, console.FormatErrorMessage(err.Error())) os.Exit(1) } diff --git a/pkg/cli/commands.go b/pkg/cli/commands.go index 0b028f5faf..7f42b7045e 100644 --- a/pkg/cli/commands.go +++ b/pkg/cli/commands.go @@ -568,7 +568,7 @@ func AddWorkflowWithTracking(workflow string, number int, verbose bool, engineOv } // CompileWorkflows compiles markdown files into GitHub Actions workflow files -func CompileWorkflows(markdownFile string, verbose bool, engineOverride string, validate bool, watch bool, writeInstructions bool) error { +func CompileWorkflows(markdownFiles []string, verbose bool, engineOverride string, validate bool, watch bool, writeInstructions bool) error { // Create compiler with verbose flag and AI engine override compiler := workflow.NewCompiler(verbose, engineOverride, GetVersion()) @@ -577,21 +577,43 @@ func CompileWorkflows(markdownFile string, verbose bool, engineOverride string, if watch { // Watch mode: watch for file changes and recompile automatically + // For watch mode, we only support a single file for now + var markdownFile string + if len(markdownFiles) > 0 { + if len(markdownFiles) > 1 { + fmt.Println(console.FormatWarningMessage("Watch mode only supports a single file, using the first one")) + } + // Resolve the workflow file to get the full path + resolvedFile, err := resolveWorkflowFile(markdownFiles[0], verbose) + if err != nil { + return fmt.Errorf("failed to resolve workflow '%s': %w", markdownFiles[0], err) + } + markdownFile = resolvedFile + } return watchAndCompileWorkflows(markdownFile, compiler, verbose) } - if markdownFile != "" { - // Resolve workflow ID or file path to actual file path - resolvedFile, err := resolveWorkflowFile(markdownFile, verbose) - if err != nil { - return fmt.Errorf("failed to resolve workflow: %w", err) + if len(markdownFiles) > 0 { + // Compile specific workflow files + var compiledCount int + for _, markdownFile := range markdownFiles { + // Resolve workflow ID or file path to actual file path + resolvedFile, err := resolveWorkflowFile(markdownFile, verbose) + if err != nil { + return fmt.Errorf("failed to resolve workflow '%s': %w", markdownFile, err) + } + + if verbose { + fmt.Println(console.FormatInfoMessage(fmt.Sprintf("Compiling %s", resolvedFile))) + } + if err := compiler.CompileWorkflow(resolvedFile); err != nil { + return fmt.Errorf("failed to compile workflow '%s': %w", markdownFile, err) + } + compiledCount++ } if verbose { - fmt.Printf("Compiling %s\n", resolvedFile) - } - if err := compiler.CompileWorkflow(resolvedFile); err != nil { - return err + fmt.Println(console.FormatSuccessMessage(fmt.Sprintf("Successfully compiled %d workflow file(s)", compiledCount))) } // Ensure .gitattributes marks .lock.yml files as generated @@ -3028,7 +3050,12 @@ func resolveWorkflowFile(fileOrWorkflowName string, verbose bool) (string, error if verbose { fmt.Printf("Found workflow file at path: %s\n", fileOrWorkflowName) } - return fileOrWorkflowName, nil + // Return absolute path + absPath, err := filepath.Abs(fileOrWorkflowName) + if err != nil { + return fileOrWorkflowName, nil // fallback to original path + } + return absPath, nil } // If it's not a direct file path, try to resolve it as a workflow name @@ -3081,8 +3108,12 @@ func resolveWorkflowFile(fileOrWorkflowName string, verbose bool) (string, error return tmpFile.Name(), nil } else { - // It's a local file, return the source path - return sourceInfo.SourcePath, nil + // It's a local file, make sure we return an absolute path + absPath, err := filepath.Abs(sourceInfo.SourcePath) + if err != nil { + return sourceInfo.SourcePath, nil // fallback to original path + } + return absPath, nil } } diff --git a/pkg/cli/commands_compile_workflow_test.go b/pkg/cli/commands_compile_workflow_test.go index 7006264007..7af1761b61 100644 --- a/pkg/cli/commands_compile_workflow_test.go +++ b/pkg/cli/commands_compile_workflow_test.go @@ -508,7 +508,11 @@ This is a test workflow for backward compatibility. } // Test CompileWorkflows function with workflow ID - err = CompileWorkflows(tt.workflowID, false, "", false, false, false) + var args []string + if tt.workflowID != "" { + args = []string{tt.workflowID} + } + err = CompileWorkflows(args, false, "", false, false, false) if tt.expectError { if err == nil { diff --git a/pkg/cli/commands_test.go b/pkg/cli/commands_test.go index 637583b833..106573e786 100644 --- a/pkg/cli/commands_test.go +++ b/pkg/cli/commands_test.go @@ -104,7 +104,11 @@ func TestCompileWorkflows(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - err := CompileWorkflows(tt.markdownFile, false, "", false, false, false) + var args []string + if tt.markdownFile != "" { + args = []string{tt.markdownFile} + } + err := CompileWorkflows(args, false, "", false, false, false) if tt.expectError && err == nil { t.Errorf("Expected error for test '%s', got nil", tt.name) @@ -235,7 +239,7 @@ func TestAllCommandsExist(t *testing.T) { }{ {func() error { return ListWorkflows(false) }, false, "ListWorkflows"}, {func() error { return AddWorkflowWithTracking("", 1, false, "", "", false, nil) }, false, "AddWorkflowWithTracking (empty name)"}, // Shows help when empty, doesn't error - {func() error { return CompileWorkflows("", false, "", false, false, false) }, false, "CompileWorkflows"}, // Should compile existing markdown files successfully + {func() error { return CompileWorkflows([]string{}, false, "", false, false, false) }, false, "CompileWorkflows"}, // Should compile existing markdown files successfully {func() error { return RemoveWorkflows("test", false) }, false, "RemoveWorkflows"}, // Should handle missing directory gracefully {func() error { return StatusWorkflows("test", false) }, false, "StatusWorkflows"}, // Should handle missing directory gracefully {func() error { return EnableWorkflows("test") }, false, "EnableWorkflows"}, // Should handle missing directory gracefully