-
Notifications
You must be signed in to change notification settings - Fork 264
feat: add gh aw validate command
#18191
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,91 @@ | ||
| package cli | ||
|
|
||
| import ( | ||
| "context" | ||
|
|
||
| "github.com/github/gh-aw/pkg/constants" | ||
| "github.com/github/gh-aw/pkg/logger" | ||
| "github.com/spf13/cobra" | ||
| ) | ||
|
|
||
| var validateLog = logger.New("cli:validate_command") | ||
|
|
||
| // NewValidateCommand creates the validate command | ||
| func NewValidateCommand(validateEngine func(string) error) *cobra.Command { | ||
| cmd := &cobra.Command{ | ||
| Use: "validate [workflow]...", | ||
| Short: "Validate agentic workflows without generating lock files", | ||
| Long: `Validate one or more agentic workflows by compiling and running all linters without | ||
| generating lock files. This is equivalent to: | ||
|
|
||
| gh aw compile --validate --no-emit --zizmor --actionlint --poutine | ||
|
|
||
| If no workflows are specified, all Markdown files in .github/workflows will be validated. | ||
|
|
||
| ` + WorkflowIDExplanation + ` | ||
|
|
||
| Examples: | ||
| ` + string(constants.CLIExtensionPrefix) + ` validate # Validate all workflows | ||
| ` + string(constants.CLIExtensionPrefix) + ` validate ci-doctor # Validate a specific workflow | ||
| ` + string(constants.CLIExtensionPrefix) + ` validate ci-doctor daily # Validate multiple workflows | ||
| ` + string(constants.CLIExtensionPrefix) + ` validate workflow.md # Validate by file path | ||
| ` + string(constants.CLIExtensionPrefix) + ` validate --dir custom/workflows # Validate from custom directory | ||
| ` + string(constants.CLIExtensionPrefix) + ` validate --json # Output results in JSON format | ||
| ` + string(constants.CLIExtensionPrefix) + ` validate --strict # Enforce strict mode validation | ||
| ` + string(constants.CLIExtensionPrefix) + ` validate --fail-fast # Stop at the first error`, | ||
| RunE: func(cmd *cobra.Command, args []string) error { | ||
| engineOverride, _ := cmd.Flags().GetString("engine") | ||
| dir, _ := cmd.Flags().GetString("dir") | ||
| strict, _ := cmd.Flags().GetBool("strict") | ||
| jsonOutput, _ := cmd.Flags().GetBool("json") | ||
| failFast, _ := cmd.Flags().GetBool("fail-fast") | ||
| stats, _ := cmd.Flags().GetBool("stats") | ||
| noCheckUpdate, _ := cmd.Flags().GetBool("no-check-update") | ||
| verbose, _ := cmd.Flags().GetBool("verbose") | ||
|
|
||
| if err := validateEngine(engineOverride); err != nil { | ||
| return err | ||
| } | ||
|
|
||
| // Check for updates (non-blocking, runs once per day) | ||
| CheckForUpdatesAsync(cmd.Context(), noCheckUpdate, verbose) | ||
|
|
||
| validateLog.Printf("Running validate command: workflows=%v, dir=%s", args, dir) | ||
|
|
||
| config := CompileConfig{ | ||
| MarkdownFiles: args, | ||
| Verbose: verbose, | ||
| EngineOverride: engineOverride, | ||
| Validate: true, | ||
| NoEmit: true, | ||
| Zizmor: true, | ||
| Actionlint: true, | ||
| Poutine: true, | ||
| WorkflowDir: dir, | ||
|
Comment on lines
+55
to
+64
|
||
| Strict: strict, | ||
| JSONOutput: jsonOutput, | ||
| FailFast: failFast, | ||
| Stats: stats, | ||
| } | ||
| if _, err := CompileWorkflows(context.Background(), config); err != nil { | ||
| return err | ||
|
Comment on lines
+70
to
+71
|
||
| } | ||
| return nil | ||
| }, | ||
| } | ||
|
|
||
| cmd.Flags().StringP("engine", "e", "", "Override AI engine (claude, codex, copilot, custom)") | ||
| cmd.Flags().StringP("dir", "d", "", "Workflow directory (default: .github/workflows)") | ||
| cmd.Flags().Bool("strict", false, "Enforce strict mode validation for all workflows") | ||
| cmd.Flags().BoolP("json", "j", false, "Output results in JSON format") | ||
| cmd.Flags().Bool("fail-fast", false, "Stop at the first validation error instead of collecting all errors") | ||
| cmd.Flags().Bool("stats", false, "Display statistics table sorted by file size") | ||
| cmd.Flags().Bool("no-check-update", false, "Skip checking for gh-aw updates") | ||
|
|
||
| // Register completions | ||
| cmd.ValidArgsFunction = CompleteWorkflowNames | ||
| RegisterEngineFlagCompletion(cmd) | ||
| RegisterDirFlagCompletion(cmd, "dir") | ||
|
|
||
| return cmd | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| //go:build !integration | ||
|
|
||
| package cli | ||
|
|
||
| import ( | ||
| "testing" | ||
|
|
||
| "github.com/stretchr/testify/assert" | ||
| "github.com/stretchr/testify/require" | ||
| ) | ||
|
|
||
| // TestNewValidateCommand tests that the validate command is created correctly | ||
| func TestNewValidateCommand(t *testing.T) { | ||
| cmd := NewValidateCommand(func(string) error { return nil }) | ||
|
|
||
| require.NotNil(t, cmd, "NewValidateCommand should return a non-nil command") | ||
| assert.Equal(t, "validate", cmd.Name(), "Command name should be 'validate'") | ||
| assert.NotEmpty(t, cmd.Short, "Command should have a short description") | ||
| assert.NotEmpty(t, cmd.Long, "Command should have a long description") | ||
|
|
||
| // Verify key flags exist | ||
| require.NotNil(t, cmd.Flags().Lookup("dir"), "validate command should have a --dir flag") | ||
| assert.Equal(t, "d", cmd.Flags().Lookup("dir").Shorthand, "--dir flag should have -d shorthand") | ||
| require.NotNil(t, cmd.Flags().Lookup("json"), "validate command should have a --json flag") | ||
| assert.Equal(t, "j", cmd.Flags().Lookup("json").Shorthand, "--json flag should have -j shorthand") | ||
| require.NotNil(t, cmd.Flags().Lookup("engine"), "validate command should have a --engine flag") | ||
| require.NotNil(t, cmd.Flags().Lookup("strict"), "validate command should have a --strict flag") | ||
| require.NotNil(t, cmd.Flags().Lookup("fail-fast"), "validate command should have a --fail-fast flag") | ||
| require.NotNil(t, cmd.Flags().Lookup("stats"), "validate command should have a --stats flag") | ||
| require.NotNil(t, cmd.Flags().Lookup("no-check-update"), "validate command should have a --no-check-update flag") | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,2 @@ | ||||||
| // Package cli - verify_command.go is superseded by validate_command.go. | ||||||
|
||||||
| // Package cli - verify_command.go is superseded by validate_command.go. | |
| // verify_command.go is superseded by validate_command.go. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| //go:build !integration | ||
|
|
||
| // Package cli - verify_command_test.go is superseded by validate_command_test.go. | ||
| package cli |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Docs say
gh aw validateruns “compile + all linters” with no file output, but the current implementation forcesNoEmit: true, and the compile pipeline only runs zizmor/actionlint/poutine when!NoEmit. Either adjust the implementation so linters run in validate mode, or update this documentation to match actual behavior.