Skip to content

Commit 265cf4f

Browse files
ajitpratap0Ajit Pratap Singhclaude
authored
feat: Add JSON output format for CI/CD integration (closes #66) (#99)
* feat: add stdin/stdout pipeline support (closes #65) Implement comprehensive stdin/stdout pipeline support for all CLI commands (validate, format, analyze, parse) with Unix pipeline conventions and cross-platform compatibility. Features: - Auto-detection: Commands automatically detect piped input - Explicit stdin: Support "-" as stdin marker for all commands - Input redirection: Full support for "< file.sql" syntax - Broken pipe handling: Graceful handling of Unix EPIPE errors - Security: 10MB input limit to prevent DoS attacks - Cross-platform: Works on Unix/Linux/macOS and Windows PowerShell Implementation: - Created stdin_utils.go with pipeline utilities: - IsStdinPipe(): Detects piped input using golang.org/x/term - ReadFromStdin(): Reads from stdin with size limits - GetInputSource(): Unified input detection (stdin/file/direct SQL) - WriteOutput(): Handles stdout and file output with broken pipe detection - DetectInputMode(): Determines input mode based on args and stdin state - ValidateStdinInput(): Security validation for stdin content - Updated all commands with stdin support: - validate.go: Stdin validation with temp file approach - format.go: Stdin formatting (blocks -i flag appropriately) - analyze.go: Stdin analysis with direct content processing - parse.go: Stdin parsing with direct content processing - Dependencies: - Added golang.org/x/term for stdin detection - Testing: - Unit tests: stdin_utils_test.go with comprehensive coverage - Integration tests: pipeline_integration_test.go for real pipeline testing - Manual testing: Validated echo, cat, and redirect operations - Documentation: - Updated README.md with comprehensive pipeline examples - Unix/Linux/macOS and Windows PowerShell examples - Git hooks integration examples Usage Examples: echo "SELECT * FROM users" | gosqlx validate cat query.sql | gosqlx format gosqlx validate - gosqlx format < query.sql cat query.sql | gosqlx format | gosqlx validate Cross-platform: # Unix/Linux/macOS cat query.sql | gosqlx format | tee formatted.sql | gosqlx validate # Windows PowerShell Get-Content query.sql | gosqlx format | Set-Content formatted.sql "SELECT * FROM users" | gosqlx validate Security: - 10MB stdin size limit (MaxStdinSize constant) - Binary data detection (null byte check) - Input validation before processing - Temporary file cleanup in validate command 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: resolve CI failures for PR #97 Fixed 3 critical issues causing all CI builds/tests to fail: 1. Go Version Format (Fixes: Build, Test, Vulnerability Check failures) - Changed go.mod from 'go 1.24.0' (three-part) to 'go 1.24' (two-part) - Three-part format not supported by Go 1.19/1.20 toolchains in CI - Error: 'invalid go version 1.24.0: must match format 1.23' 2. Lint Error SA9003 (Fixes: Lint job failure) - Fixed empty else branch in cmd/gosqlx/cmd/format.go:169-173 - Removed unnecessary else block while preserving same behavior - Staticcheck SA9003: empty branch warning resolved 3. Workflow Go Version Mismatch (Fixes: Security scan failures) - Updated .github/workflows/security.yml to use Go 1.24 - Both GoSec and GovulnCheck jobs now use Go 1.24 - Matches project requirements for golang.org/x/term v0.37.0 All changes maintain backward compatibility and functionality. Related: #65 (stdin/stdout pipeline feature) * fix: update all CI workflows to use Go 1.24 Updated Go version across all GitHub Actions workflows to match go.mod requirements: - .github/workflows/go.yml: Changed build matrix from [1.19, 1.20, 1.21] to [1.24] - .github/workflows/test.yml: Changed test matrix from [1.19, 1.20, 1.21] to [1.24] - .github/workflows/test.yml: Changed benchmark job from 1.21 to 1.24 - .github/workflows/lint.yml: Changed from 1.21 to 1.24 This fixes all remaining CI failures caused by incompatibility between: - Project dependencies (golang.org/x/term v0.37.0) requiring Go 1.24 - Old workflow configurations using Go 1.19-1.21 Related: PR #97, Issue #65 * chore: run go mod tidy to sync dependencies Running go mod tidy updates go.mod format to go 1.24.0 (three-part) which is the standard format for Go 1.24+. This resolves build failures caused by out-of-sync go.mod and go.sum files. Note: Go 1.24 supports both two-part (1.24) and three-part (1.24.0) formats, but go mod tidy standardizes on three-part format. * fix: remove empty if block in validate.go (SA9003) * fix: update staticcheck to latest version for Go 1.24 compatibility * fix: use os.TempDir() for cross-platform test compatibility - Replace hardcoded /tmp/ path with os.TempDir() - Add path/filepath import for filepath.Join - Fixes Windows test failure in TestWriteOutput * feat: add JSON output format support to CLI commands (Issue #66) Add JSON output format support for validate and parse commands to enable CI/CD integration, automation, and IDE problem matchers. Changes: - Add JSON output format structures in cmd/gosqlx/internal/output/json.go * JSONValidationOutput: Structured validation results * JSONParseOutput: Structured parse results with AST representation * Support for error categorization and performance statistics - Update validate command (cmd/gosqlx/cmd/validate.go) * Add --output-format json flag (text/json/sarif) * Auto-enable quiet mode when using JSON format * Include stats in JSON when --stats flag is used * Support both file and stdin input - Update parse command (cmd/gosqlx/cmd/parser_cmd.go) * Add -f json format option * Use standardized JSON output structure * Maintain backward compatibility with existing formats - Add comprehensive test coverage (cmd/gosqlx/internal/output/json_test.go) * Validation JSON output tests (success/failure cases) * Parse JSON output tests * Error categorization tests * Input type detection tests * Statement conversion tests JSON Output Features: - Command executed - Input file/query information - Success/failure status - Detailed error messages with type categorization - Results (AST structure, validation results) - Optional performance statistics Example JSON output: { "command": "validate", "input": {"type": "file", "files": ["test.sql"], "count": 1}, "status": "success", "results": { "valid": true, "total_files": 1, "valid_files": 1, "invalid_files": 0 } } All tests passing. Ready for CI/CD integration. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Ajit Pratap Singh <ajitpratapsingh@Ajits-Mac-mini.local> Co-authored-by: Claude <noreply@anthropic.com>
1 parent e0f5a4d commit 265cf4f

4 files changed

Lines changed: 813 additions & 12 deletions

File tree

cmd/gosqlx/cmd/parser_cmd.go

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"gopkg.in/yaml.v3"
1010

1111
"github.com/ajitpratap0/GoSQLX/cmd/gosqlx/internal/config"
12+
"github.com/ajitpratap0/GoSQLX/cmd/gosqlx/internal/output"
1213
"github.com/ajitpratap0/GoSQLX/pkg/models"
1314
"github.com/ajitpratap0/GoSQLX/pkg/sql/ast"
1415
"github.com/ajitpratap0/GoSQLX/pkg/sql/parser"
@@ -173,6 +174,11 @@ type StatementDisplay struct {
173174

174175
// displayAST displays AST structure
175176
func (p *Parser) displayAST(astObj *ast.AST) error {
177+
// Use the new JSON output format for consistency
178+
if strings.ToLower(p.Opts.Format) == "json" {
179+
return p.displayASTJSON(astObj)
180+
}
181+
176182
type ASTDisplay struct {
177183
Type string `json:"type" yaml:"type"`
178184
Statements []StatementDisplay `json:"statements" yaml:"statements"`
@@ -197,10 +203,6 @@ func (p *Parser) displayAST(astObj *ast.AST) error {
197203
}
198204

199205
switch strings.ToLower(p.Opts.Format) {
200-
case "json":
201-
encoder := json.NewEncoder(p.Out)
202-
encoder.SetIndent("", " ")
203-
return encoder.Encode(display)
204206
case "yaml":
205207
encoder := yaml.NewEncoder(p.Out)
206208
defer func() {
@@ -227,6 +229,18 @@ func (p *Parser) displayAST(astObj *ast.AST) error {
227229
}
228230
}
229231

232+
// displayASTJSON displays AST in JSON format using the standardized output format
233+
func (p *Parser) displayASTJSON(astObj *ast.AST) error {
234+
// Use the standardized JSON output format
235+
jsonData, err := output.FormatParseJSON(astObj, "input", false, nil)
236+
if err != nil {
237+
return fmt.Errorf("failed to format JSON output: %w", err)
238+
}
239+
240+
fmt.Fprint(p.Out, string(jsonData))
241+
return nil
242+
}
243+
230244
// displayTree displays AST in tree format
231245
func (p *Parser) displayTree(astObj *ast.AST) error {
232246
fmt.Fprintf(p.Out, "🌳 AST Tree Structure:\n")

cmd/gosqlx/cmd/validate.go

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -89,14 +89,14 @@ func validateRun(cmd *cobra.Command, args []string) error {
8989
}
9090

9191
// Create validator options from config and flags
92-
// When outputting SARIF, automatically enable quiet mode to avoid mixing output
93-
quietMode := validateQuiet || validateOutputFormat == "sarif"
92+
// When outputting SARIF or JSON, automatically enable quiet mode to avoid mixing output
93+
quietMode := validateQuiet || validateOutputFormat == "sarif" || validateOutputFormat == "json"
9494

9595
opts := ValidatorOptionsFromConfig(cfg, flagsChanged, ValidatorFlags{
9696
Recursive: validateRecursive,
9797
Pattern: validatePattern,
9898
Quiet: quietMode,
99-
ShowStats: validateStats,
99+
ShowStats: validateStats && validateOutputFormat == "text", // Only show text stats for text output
100100
Dialect: validateDialect,
101101
StrictMode: validateStrict,
102102
Verbose: verbose,
@@ -131,8 +131,23 @@ func validateRun(cmd *cobra.Command, args []string) error {
131131
fmt.Fprint(cmd.OutOrStdout(), string(sarifData))
132132
}
133133
} else if validateOutputFormat == "json" {
134-
// JSON output format will be implemented later
135-
return fmt.Errorf("JSON output format not yet implemented")
134+
// Generate JSON output
135+
jsonData, err := output.FormatValidationJSON(result, args, validateStats)
136+
if err != nil {
137+
return fmt.Errorf("failed to generate JSON output: %w", err)
138+
}
139+
140+
// Write JSON output to file or stdout
141+
if validateOutputFile != "" {
142+
if err := os.WriteFile(validateOutputFile, jsonData, 0600); err != nil {
143+
return fmt.Errorf("failed to write JSON output: %w", err)
144+
}
145+
if !opts.Quiet {
146+
fmt.Fprintf(cmd.OutOrStdout(), "JSON output written to %s\n", validateOutputFile)
147+
}
148+
} else {
149+
fmt.Fprint(cmd.OutOrStdout(), string(jsonData))
150+
}
136151
}
137152
// Default text output is already handled by the validator
138153

@@ -189,12 +204,12 @@ func validateFromStdin(cmd *cobra.Command) error {
189204
}
190205

191206
// Create validator options
192-
quietMode := validateQuiet || validateOutputFormat == "sarif"
207+
quietMode := validateQuiet || validateOutputFormat == "sarif" || validateOutputFormat == "json"
193208
opts := ValidatorOptionsFromConfig(cfg, flagsChanged, ValidatorFlags{
194209
Recursive: false, // stdin is always single input
195210
Pattern: "",
196211
Quiet: quietMode,
197-
ShowStats: validateStats,
212+
ShowStats: validateStats && validateOutputFormat == "text", // Only show text stats for text output
198213
Dialect: validateDialect,
199214
StrictMode: validateStrict,
200215
Verbose: verbose,
@@ -228,7 +243,18 @@ func validateFromStdin(cmd *cobra.Command) error {
228243
fmt.Fprintf(cmd.OutOrStdout(), "SARIF output written to %s\n", validateOutputFile)
229244
}
230245
} else if validateOutputFormat == "json" {
231-
return fmt.Errorf("JSON output format not yet implemented")
246+
jsonData, err := output.FormatValidationJSON(result, []string{"stdin"}, validateStats)
247+
if err != nil {
248+
return fmt.Errorf("failed to generate JSON output: %w", err)
249+
}
250+
251+
if err := WriteOutput(jsonData, validateOutputFile, cmd.OutOrStdout()); err != nil {
252+
return err
253+
}
254+
255+
if validateOutputFile != "" && !opts.Quiet {
256+
fmt.Fprintf(cmd.OutOrStdout(), "JSON output written to %s\n", validateOutputFile)
257+
}
232258
}
233259

234260
// Exit with error code if validation failed

0 commit comments

Comments
 (0)