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
12 changes: 4 additions & 8 deletions pkg/cli/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,9 +82,7 @@ func resolveWorkflowFileInDir(fileOrWorkflowName string, verbose bool, workflowD
// First, try to use it as a direct file path
if _, err := os.Stat(fileOrWorkflowName); err == nil {
commandsLog.Printf("Found workflow file at path: %s", fileOrWorkflowName)
if verbose {
fmt.Printf("Found workflow file at path: %s\n", fileOrWorkflowName)
}
console.LogVerbose(verbose, fmt.Sprintf("Found workflow file at path: %s", fileOrWorkflowName))
// Return absolute path
absPath, err := filepath.Abs(fileOrWorkflowName)
if err != nil {
Expand Down Expand Up @@ -150,9 +148,7 @@ func NewWorkflow(workflowName string, verbose bool, force bool) error {
workflowName = strings.TrimSuffix(workflowName, ".md")
commandsLog.Printf("Normalized workflow name: %s", workflowName)

if verbose {
fmt.Printf("Creating new workflow: %s\n", workflowName)
}
console.LogVerbose(verbose, fmt.Sprintf("Creating new workflow: %s", workflowName))

// Get current working directory for .github/workflows
workingDir, err := os.Getwd()
Expand Down Expand Up @@ -188,8 +184,8 @@ func NewWorkflow(workflowName string, verbose bool, force bool) error {
return fmt.Errorf("failed to write workflow file '%s': %w", destFile, err)
}

fmt.Printf("Created new workflow: %s\n", destFile)
fmt.Printf("Edit the file to customize your workflow, then run '" + string(constants.CLIExtensionPrefix) + " compile' to generate the GitHub Actions workflow.\n")
fmt.Fprintln(os.Stderr, console.FormatSuccessMessage(fmt.Sprintf("Created new workflow: %s", destFile)))
fmt.Fprintln(os.Stderr, console.FormatInfoMessage(fmt.Sprintf("Edit the file to customize your workflow, then run '%s compile' to generate the GitHub Actions workflow", string(constants.CLIExtensionPrefix))))

return nil
}
Expand Down
20 changes: 6 additions & 14 deletions pkg/cli/file_tracker.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,10 +88,10 @@ func (ft *FileTracker) StageAllFiles(verbose bool) error {
return nil
}

console.LogVerbose(verbose, fmt.Sprintf("Staging %d files...", len(allFiles)))
if verbose {
fmt.Printf("Staging %d files...\n", len(allFiles))
for _, file := range allFiles {
fmt.Printf(" - %s\n", file)
fmt.Fprintln(os.Stderr, console.FormatVerboseMessage(fmt.Sprintf(" - %s", file)))
}
}

Expand All @@ -115,15 +115,11 @@ func (ft *FileTracker) RollbackCreatedFiles(verbose bool) error {
}

fileTrackerLog.Printf("Rolling back %d created files", len(ft.CreatedFiles))
if verbose {
fmt.Printf("Rolling back %d created files...\n", len(ft.CreatedFiles))
}
console.LogVerbose(verbose, fmt.Sprintf("Rolling back %d created files...", len(ft.CreatedFiles)))

var errors []string
for _, file := range ft.CreatedFiles {
if verbose {
fmt.Printf(" - Deleting %s\n", file)
}
console.LogVerbose(verbose, fmt.Sprintf(" - Deleting %s", file))
if err := os.Remove(file); err != nil && !os.IsNotExist(err) {
fileTrackerLog.Printf("Failed to delete %s: %v", file, err)
errors = append(errors, fmt.Sprintf("failed to delete %s: %v", file, err))
Expand All @@ -144,15 +140,11 @@ func (ft *FileTracker) RollbackModifiedFiles(verbose bool) error {
return nil
}

if verbose {
fmt.Printf("Rolling back %d modified files...\n", len(ft.ModifiedFiles))
}
console.LogVerbose(verbose, fmt.Sprintf("Rolling back %d modified files...", len(ft.ModifiedFiles)))

var errors []string
for _, file := range ft.ModifiedFiles {
if verbose {
fmt.Printf(" - Restoring %s\n", file)
}
console.LogVerbose(verbose, fmt.Sprintf(" - Restoring %s", file))

// Restore original content if we have it
if originalContent, exists := ft.OriginalContent[file]; exists {
Expand Down
24 changes: 6 additions & 18 deletions pkg/cli/git.go
Original file line number Diff line number Diff line change
Expand Up @@ -301,9 +301,7 @@ func getCurrentBranch() (string, error) {

// createAndSwitchBranch creates a new branch and switches to it
func createAndSwitchBranch(branchName string, verbose bool) error {
if verbose {
fmt.Printf("Creating and switching to branch: %s\n", branchName)
}
console.LogVerbose(verbose, fmt.Sprintf("Creating and switching to branch: %s", branchName))

cmd := exec.Command("git", "checkout", "-b", branchName)
if err := cmd.Run(); err != nil {
Expand All @@ -315,9 +313,7 @@ func createAndSwitchBranch(branchName string, verbose bool) error {

// switchBranch switches to the specified branch
func switchBranch(branchName string, verbose bool) error {
if verbose {
fmt.Printf("Switching to branch: %s\n", branchName)
}
console.LogVerbose(verbose, fmt.Sprintf("Switching to branch: %s", branchName))

cmd := exec.Command("git", "checkout", branchName)
if err := cmd.Run(); err != nil {
Expand All @@ -329,9 +325,7 @@ func switchBranch(branchName string, verbose bool) error {

// commitChanges commits all staged changes with the given message
func commitChanges(message string, verbose bool) error {
if verbose {
fmt.Printf("Committing changes with message: %s\n", message)
}
console.LogVerbose(verbose, fmt.Sprintf("Committing changes with message: %s", message))

cmd := exec.Command("git", "commit", "-m", message)
if err := cmd.Run(); err != nil {
Expand All @@ -343,9 +337,7 @@ func commitChanges(message string, verbose bool) error {

// pushBranch pushes the specified branch to origin
func pushBranch(branchName string, verbose bool) error {
if verbose {
fmt.Printf("Pushing branch: %s\n", branchName)
}
console.LogVerbose(verbose, fmt.Sprintf("Pushing branch: %s", branchName))

cmd := exec.Command("git", "push", "-u", "origin", branchName)
if err := cmd.Run(); err != nil {
Expand All @@ -357,9 +349,7 @@ func pushBranch(branchName string, verbose bool) error {

// checkCleanWorkingDirectory checks if there are uncommitted changes
func checkCleanWorkingDirectory(verbose bool) error {
if verbose {
fmt.Printf("Checking for uncommitted changes...\n")
}
console.LogVerbose(verbose, "Checking for uncommitted changes...")

cmd := exec.Command("git", "status", "--porcelain")
output, err := cmd.Output()
Expand All @@ -371,9 +361,7 @@ func checkCleanWorkingDirectory(verbose bool) error {
return fmt.Errorf("working directory has uncommitted changes, please commit or stash them first")
}

if verbose {
fmt.Printf("Working directory is clean\n")
}
console.LogVerbose(verbose, "Working directory is clean")
return nil
}

Expand Down
46 changes: 17 additions & 29 deletions pkg/cli/run_workflow_tracking.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,10 @@ func getLatestWorkflowRunWithRetry(lockFileName string, repo string, verbose boo
const initialDelay = 2 * time.Second
const maxDelay = 10 * time.Second

if verbose {
if repo != "" {
fmt.Printf("Getting latest run for workflow: %s in repo: %s (with retry logic)\n", lockFileName, repo)
} else {
fmt.Printf("Getting latest run for workflow: %s (with retry logic)\n", lockFileName)
}
if repo != "" {
console.LogVerbose(verbose, fmt.Sprintf("Getting latest run for workflow: %s in repo: %s (with retry logic)", lockFileName, repo))
} else {
console.LogVerbose(verbose, fmt.Sprintf("Getting latest run for workflow: %s (with retry logic)", lockFileName))
}

// Capture the current time before we start polling
Expand All @@ -57,9 +55,9 @@ func getLatestWorkflowRunWithRetry(lockFileName string, repo string, verbose boo
// Calculate elapsed time since start
elapsed := time.Since(startTime).Round(time.Second)

if verbose {
fmt.Printf("Waiting %v before retry attempt %d/%d...\n", delay, attempt+1, maxRetries)
} else {
console.LogVerbose(verbose, fmt.Sprintf("Waiting %v before retry attempt %d/%d...", delay, attempt+1, maxRetries))

if !verbose {
// Show spinner starting from second attempt to avoid flickering
if attempt == 1 && spinner != nil {
spinner.Start()
Expand Down Expand Up @@ -91,9 +89,7 @@ func getLatestWorkflowRunWithRetry(lockFileName string, repo string, verbose boo

if len(output) == 0 || string(output) == "[]" {
lastErr = fmt.Errorf("no runs found for workflow")
if verbose {
fmt.Printf("Attempt %d/%d: no runs found yet\n", attempt+1, maxRetries)
}
console.LogVerbose(verbose, fmt.Sprintf("Attempt %d/%d: no runs found yet", attempt+1, maxRetries))
continue
}

Expand All @@ -116,9 +112,7 @@ func getLatestWorkflowRunWithRetry(lockFileName string, repo string, verbose boo

if len(runs) == 0 {
lastErr = fmt.Errorf("no runs found")
if verbose {
fmt.Printf("Attempt %d/%d: no runs in parsed JSON\n", attempt+1, maxRetries)
}
console.LogVerbose(verbose, fmt.Sprintf("Attempt %d/%d: no runs in parsed JSON", attempt+1, maxRetries))
continue
}

Expand All @@ -145,23 +139,19 @@ func getLatestWorkflowRunWithRetry(lockFileName string, repo string, verbose boo
// If we found a run and it was created after we started (within 30 seconds tolerance),
// it's likely the run we just triggered
if !createdAt.IsZero() && createdAt.After(startTime.Add(-30*time.Second)) {
if verbose {
fmt.Printf("Found recent run (ID: %d) created at %v (started polling at %v)\n",
run.DatabaseID, createdAt.Format(time.RFC3339), startTime.Format(time.RFC3339))
}
console.LogVerbose(verbose, fmt.Sprintf("Found recent run (ID: %d) created at %v (started polling at %v)",
run.DatabaseID, createdAt.Format(time.RFC3339), startTime.Format(time.RFC3339)))
if spinner != nil {
spinner.StopWithMessage("βœ“ Found workflow run")
}
return runInfo, nil
}

if verbose {
if createdAt.IsZero() {
fmt.Printf("Attempt %d/%d: Found run (ID: %d) but no creation timestamp available\n", attempt+1, maxRetries, run.DatabaseID)
} else {
fmt.Printf("Attempt %d/%d: Found run (ID: %d) but it was created at %v (too old)\n",
attempt+1, maxRetries, run.DatabaseID, createdAt.Format(time.RFC3339))
}
if createdAt.IsZero() {
console.LogVerbose(verbose, fmt.Sprintf("Attempt %d/%d: Found run (ID: %d) but no creation timestamp available", attempt+1, maxRetries, run.DatabaseID))
} else {
console.LogVerbose(verbose, fmt.Sprintf("Attempt %d/%d: Found run (ID: %d) but it was created at %v (too old)",
attempt+1, maxRetries, run.DatabaseID, createdAt.Format(time.RFC3339)))
}

// For the first few attempts, if we have a run but it's too old, keep trying
Expand All @@ -171,9 +161,7 @@ func getLatestWorkflowRunWithRetry(lockFileName string, repo string, verbose boo
}

// For later attempts, return what we found even if timing is uncertain
if verbose {
fmt.Printf("Returning workflow run (ID: %d) after %d attempts (timing uncertain)\n", run.DatabaseID, attempt+1)
}
console.LogVerbose(verbose, fmt.Sprintf("Returning workflow run (ID: %d) after %d attempts (timing uncertain)", run.DatabaseID, attempt+1))
if spinner != nil {
spinner.StopWithMessage("βœ“ Found workflow run")
}
Expand Down
14 changes: 14 additions & 0 deletions pkg/console/verbose.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package console

import (
"fmt"
"os"
)

// LogVerbose outputs a verbose message to stderr only when verbose mode is enabled.
// This is a convenience helper to avoid repetitive if-verbose checks throughout the codebase.
func LogVerbose(verbose bool, message string) {
if verbose {
fmt.Fprintln(os.Stderr, FormatVerboseMessage(message))
}
}
95 changes: 95 additions & 0 deletions pkg/console/verbose_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package console

import (
"bytes"
"os"
"strings"
"testing"

"github.com/stretchr/testify/assert"
)

func TestLogVerbose(t *testing.T) {
tests := []struct {
name string
verbose bool
message string
expected bool // whether output is expected
}{
{
name: "verbose enabled outputs message",
verbose: true,
message: "Processing workflow",
expected: true,
},
{
name: "verbose disabled no output",
verbose: false,
message: "Processing workflow",
expected: false,
},
{
name: "verbose enabled with empty message",
verbose: true,
message: "",
expected: true,
},
{
name: "verbose disabled with empty message",
verbose: false,
message: "",
expected: false,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Capture stderr output
oldStderr := os.Stderr
r, w, _ := os.Pipe()
os.Stderr = w

// Execute the function
LogVerbose(tt.verbose, tt.message)

// Restore stderr and read captured output
w.Close()
os.Stderr = oldStderr
var buf bytes.Buffer
_, _ = buf.ReadFrom(r)
output := buf.String()

if tt.expected {
// Should contain the message
assert.Contains(t, output, tt.message, "Output should contain the message when verbose is enabled")
// Should contain the verbose icon (πŸ”)
assert.True(t, strings.Contains(output, "πŸ”") || strings.Contains(output, tt.message),
"Output should contain verbose formatting or message")
} else {
// Should be empty
assert.Empty(t, output, "Output should be empty when verbose is disabled")
}
})
}
}

func TestLogVerboseFormatting(t *testing.T) {
// Capture stderr output
oldStderr := os.Stderr
r, w, _ := os.Pipe()
os.Stderr = w

message := "Test message"
LogVerbose(true, message)

w.Close()
os.Stderr = oldStderr
var buf bytes.Buffer
_, _ = buf.ReadFrom(r)
output := buf.String()

// Verify the output uses FormatVerboseMessage
// The output should contain the message and end with newline
assert.Contains(t, output, message, "Output should contain the test message")
assert.True(t, strings.HasSuffix(output, "\n"), "Output should end with newline")
}
Loading