Skip to content

Commit e8f3784

Browse files
pfrederiksenclaude
andcommitted
feat: Implement GitHub Action outputs and golden tests
Add GitHub Actions integration with has-changes and diff-output outputs. Convert output format tests to use golden files for better maintainability. Changes: - Add writeGitHubOutputs() to write GitHub Actions outputs - Detect GITHUB_OUTPUT env var and write outputs in correct format - Convert TestGenerateStat to golden file tests (4 test cases) - Convert TestGenerateSideBySide to golden file tests (5 test cases) - Convert TestGenerateGitDiff to golden file tests (5 test cases) - Add comprehensive tests for GitHub Actions output functionality - Fix variable redeclaration in compareFiles() Addresses PR review comments from #11: - Issue 3: GitHub Action outputs now fully implemented - Issue 4: Golden tests added for all new output formats Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
1 parent 3580551 commit e8f3784

17 files changed

+379
-68
lines changed

cmd/configdiff/compare.go

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,8 +117,9 @@ func compareFiles(oldFile, newFile string) (bool, error) {
117117
}
118118

119119
// Format and output results (unless quiet mode)
120+
var output string
120121
if !quiet {
121-
output, err := cli.FormatOutput(result, cli.OutputOptions{
122+
output, err = cli.FormatOutput(result, cli.OutputOptions{
122123
Format: outputFormat,
123124
NoColor: noColor,
124125
MaxValueLength: maxValueLength,
@@ -132,8 +133,17 @@ func compareFiles(oldFile, newFile string) (bool, error) {
132133
fmt.Println(output)
133134
}
134135

136+
// Write GitHub Actions outputs if in GHA environment
137+
hasChanges := cli.HasChanges(result)
138+
if githubOutput := os.Getenv("GITHUB_OUTPUT"); githubOutput != "" {
139+
if err := writeGitHubOutputs(githubOutput, hasChanges, output); err != nil {
140+
// Log error but don't fail the command
141+
fmt.Fprintf(os.Stderr, "Warning: Failed to write GitHub Actions outputs: %v\n", err)
142+
}
143+
}
144+
135145
// Return whether changes were found
136-
return cli.HasChanges(result), nil
146+
return hasChanges, nil
137147
}
138148

139149
// compareDirectories recursively compares two directories.
@@ -255,3 +265,24 @@ func fileExists(path string) bool {
255265
}
256266
return !info.IsDir()
257267
}
268+
269+
// writeGitHubOutputs writes GitHub Actions outputs to the GITHUB_OUTPUT file
270+
func writeGitHubOutputs(outputFile string, hasChanges bool, diffOutput string) error {
271+
f, err := os.OpenFile(outputFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
272+
if err != nil {
273+
return err
274+
}
275+
defer f.Close()
276+
277+
// Write has-changes output
278+
if _, err := fmt.Fprintf(f, "has-changes=%v\n", hasChanges); err != nil {
279+
return err
280+
}
281+
282+
// Write diff-output using heredoc format
283+
if _, err := fmt.Fprintf(f, "diff-output<<EOF\n%s\nEOF\n", diffOutput); err != nil {
284+
return err
285+
}
286+
287+
return nil
288+
}

cmd/configdiff/main_test.go

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -415,3 +415,111 @@ func TestDirectoryComparisonDoesNotExitEarly(t *testing.T) {
415415
// If we get here, the function completed successfully without os.Exit
416416
// The os.Exit would happen in the caller (compare function), not in compareDirectories
417417
}
418+
419+
func TestWriteGitHubOutputs(t *testing.T) {
420+
tmpDir := t.TempDir()
421+
outputFile := filepath.Join(tmpDir, "github_output.txt")
422+
423+
tests := []struct {
424+
name string
425+
hasChanges bool
426+
diffOutput string
427+
wantErr bool
428+
}{
429+
{
430+
name: "with changes",
431+
hasChanges: true,
432+
diffOutput: "path: /config/value\nold: 1\nnew: 2",
433+
wantErr: false,
434+
},
435+
{
436+
name: "no changes",
437+
hasChanges: false,
438+
diffOutput: "",
439+
wantErr: false,
440+
},
441+
{
442+
name: "multiline output",
443+
hasChanges: true,
444+
diffOutput: "Changes:\n Modified: /config/value\n Added: /config/newkey\n Removed: /config/oldkey",
445+
wantErr: false,
446+
},
447+
}
448+
449+
for _, tt := range tests {
450+
t.Run(tt.name, func(t *testing.T) {
451+
// Remove output file between tests
452+
os.Remove(outputFile)
453+
454+
err := writeGitHubOutputs(outputFile, tt.hasChanges, tt.diffOutput)
455+
if (err != nil) != tt.wantErr {
456+
t.Errorf("writeGitHubOutputs() error = %v, wantErr %v", err, tt.wantErr)
457+
return
458+
}
459+
460+
if tt.wantErr {
461+
return
462+
}
463+
464+
// Read the output file
465+
content, err := os.ReadFile(outputFile)
466+
if err != nil {
467+
t.Fatalf("Failed to read output file: %v", err)
468+
}
469+
470+
output := string(content)
471+
472+
// Verify has-changes output
473+
expectedHasChanges := fmt.Sprintf("has-changes=%v\n", tt.hasChanges)
474+
if !contains(output, expectedHasChanges) {
475+
t.Errorf("Output missing expected has-changes line: %q", expectedHasChanges)
476+
}
477+
478+
// Verify diff-output heredoc format
479+
if !contains(output, "diff-output<<EOF\n") {
480+
t.Error("Output missing diff-output heredoc start")
481+
}
482+
if !contains(output, "\nEOF\n") {
483+
t.Error("Output missing diff-output heredoc end")
484+
}
485+
if tt.diffOutput != "" && !contains(output, tt.diffOutput) {
486+
t.Errorf("Output missing expected diff content: %q", tt.diffOutput)
487+
}
488+
})
489+
}
490+
}
491+
492+
func TestWriteGitHubOutputsAppend(t *testing.T) {
493+
tmpDir := t.TempDir()
494+
outputFile := filepath.Join(tmpDir, "github_output.txt")
495+
496+
// Write initial content
497+
initialContent := "existing-output=test\n"
498+
if err := os.WriteFile(outputFile, []byte(initialContent), 0644); err != nil {
499+
t.Fatalf("Failed to write initial content: %v", err)
500+
}
501+
502+
// Append GitHub outputs
503+
err := writeGitHubOutputs(outputFile, true, "diff content")
504+
if err != nil {
505+
t.Fatalf("writeGitHubOutputs() error = %v", err)
506+
}
507+
508+
// Read file
509+
content, err := os.ReadFile(outputFile)
510+
if err != nil {
511+
t.Fatalf("Failed to read output file: %v", err)
512+
}
513+
514+
output := string(content)
515+
516+
// Verify initial content is preserved
517+
if !contains(output, initialContent) {
518+
t.Error("Initial content was not preserved")
519+
}
520+
521+
// Verify new content was appended
522+
if !contains(output, "has-changes=true") {
523+
t.Error("New content was not appended")
524+
}
525+
}

0 commit comments

Comments
 (0)