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
2 changes: 1 addition & 1 deletion .github/workflows/cloclo.lock.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

20 changes: 10 additions & 10 deletions pkg/cli/compile_command.go
Original file line number Diff line number Diff line change
Expand Up @@ -472,12 +472,12 @@ func CompileWorkflows(config CompileConfig) ([]*workflow.WorkflowData, error) {
if err := actionCache.Save(); err != nil {
compileLog.Printf("Failed to save action cache: %v", err)
if verbose {
fmt.Fprintf(os.Stderr, "⚠️ Failed to save action cache: %v\n", err)
fmt.Fprintln(os.Stderr, console.FormatWarningMessage(fmt.Sprintf("Failed to save action cache: %v", err)))
}
} else {
compileLog.Print("Action cache saved successfully")
if verbose {
fmt.Fprintf(os.Stderr, "✓ Action cache saved to %s\n", actionCache.GetCachePath())
fmt.Fprintln(os.Stderr, console.FormatSuccessMessage(fmt.Sprintf("Action cache saved to %s", actionCache.GetCachePath())))
}
}
}
Expand Down Expand Up @@ -698,12 +698,12 @@ func CompileWorkflows(config CompileConfig) ([]*workflow.WorkflowData, error) {
if err := actionCache.Save(); err != nil {
compileLog.Printf("Failed to save action cache: %v", err)
if verbose {
fmt.Fprintf(os.Stderr, "⚠️ Failed to save action cache: %v\n", err)
fmt.Fprintln(os.Stderr, console.FormatWarningMessage(fmt.Sprintf("Failed to save action cache: %v", err)))
}
} else {
compileLog.Print("Action cache saved successfully")
if verbose {
fmt.Fprintf(os.Stderr, "✓ Action cache saved to %s\n", actionCache.GetCachePath())
fmt.Fprintln(os.Stderr, console.FormatSuccessMessage(fmt.Sprintf("Action cache saved to %s", actionCache.GetCachePath())))
}
}
}
Expand Down Expand Up @@ -812,7 +812,7 @@ func watchAndCompileWorkflows(markdownFile string, compiler *workflow.Compiler,

fmt.Fprintln(os.Stderr, "Watching for file changes")
if verbose {
fmt.Fprintf(os.Stderr, "🔨 Initial compilation of %s...\n", markdownFile)
fmt.Fprintln(os.Stderr, console.FormatProgressMessage(fmt.Sprintf("Initial compilation of %s...", markdownFile)))
}
if err := CompileWorkflowWithValidation(compiler, markdownFile, verbose, false, false, false, false, false); err != nil {
// Always show initial compilation errors on new line without wrapping
Expand Down Expand Up @@ -913,7 +913,7 @@ func compileSingleFile(compiler *workflow.Compiler, file string, stats *Compilat

compileLog.Printf("Compiling: %s", file)
if verbose {
fmt.Fprintf(os.Stderr, "🔨 Compiling: %s\n", file)
fmt.Fprintln(os.Stderr, console.FormatProgressMessage(fmt.Sprintf("Compiling: %s", file)))
}

if err := CompileWorkflowWithValidation(compiler, file, verbose, false, false, false, false, false); err != nil {
Expand Down Expand Up @@ -967,12 +967,12 @@ func compileAllWorkflowFiles(compiler *workflow.Compiler, workflowsDir string, v
if err := actionCache.Save(); err != nil {
compileLog.Printf("Failed to save action cache: %v", err)
if verbose {
fmt.Fprintf(os.Stderr, "⚠️ Failed to save action cache: %v\n", err)
fmt.Fprintln(os.Stderr, console.FormatWarningMessage(fmt.Sprintf("Failed to save action cache: %v", err)))
}
} else {
compileLog.Print("Action cache saved successfully")
if verbose {
fmt.Fprintf(os.Stderr, "✓ Action cache saved to %s\n", actionCache.GetCachePath())
fmt.Fprintln(os.Stderr, console.FormatSuccessMessage(fmt.Sprintf("Action cache saved to %s", actionCache.GetCachePath())))
}
}
}
Expand All @@ -998,7 +998,7 @@ func compileModifiedFiles(compiler *workflow.Compiler, files []string, verbose b

fmt.Fprintln(os.Stderr, "Watching for file changes")
if verbose {
fmt.Fprintf(os.Stderr, "🔨 Compiling %d modified file(s)...\n", len(files))
fmt.Fprintln(os.Stderr, console.FormatProgressMessage(fmt.Sprintf("Compiling %d modified file(s)...", len(files))))
}

// Reset warning count before compilation
Expand All @@ -1020,7 +1020,7 @@ func compileModifiedFiles(compiler *workflow.Compiler, files []string, verbose b
if err := actionCache.Save(); err != nil {
compileLog.Printf("Failed to save action cache: %v", err)
if verbose {
fmt.Fprintf(os.Stderr, "⚠️ Failed to save action cache: %v\n", err)
fmt.Fprintln(os.Stderr, console.FormatWarningMessage(fmt.Sprintf("Failed to save action cache: %v", err)))
}
} else {
compileLog.Print("Action cache saved successfully")
Expand Down
10 changes: 5 additions & 5 deletions pkg/cli/enable.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ func toggleWorkflowsByNames(workflowNames []string, enable bool, repoOverride st
// Get GitHub workflows status for comparison; warn but continue if unavailable
githubWorkflows, err := fetchGitHubWorkflows("", false)
if err != nil {
fmt.Fprintf(os.Stderr, "Warning: Unable to fetch GitHub workflows (gh CLI may not be authenticated): %v\n", err)
fmt.Fprintln(os.Stderr, console.FormatWarningMessage(fmt.Sprintf("Unable to fetch GitHub workflows (gh CLI may not be authenticated): %v", err)))
githubWorkflows = make(map[string]*GitHubWorkflow)
}

Expand Down Expand Up @@ -126,7 +126,7 @@ func toggleWorkflowsByNames(workflowNames []string, enable bool, repoOverride st
if enable {
if _, err := os.Stat(lockFile); os.IsNotExist(err) {
if err := compileWorkflow(file, false, ""); err != nil {
fmt.Fprintf(os.Stderr, "Warning: Failed to compile workflow %s to create lock file: %v\n", name, err)
fmt.Fprintln(os.Stderr, console.FormatWarningMessage(fmt.Sprintf("Failed to compile workflow %s to create lock file: %v", name, err)))
// If we can't compile and there's no GitHub entry, skip because we can't address it
if !exists {
continue
Expand Down Expand Up @@ -218,7 +218,7 @@ func toggleWorkflowsByNames(workflowNames []string, enable bool, repoOverride st
// First cancel any running workflows (by ID when available, else by lock file name)
if t.ID != 0 {
if err := cancelWorkflowRuns(t.ID); err != nil {
fmt.Fprintf(os.Stderr, "Warning: Failed to cancel runs for workflow %s: %v\n", t.Name, err)
fmt.Fprintln(os.Stderr, console.FormatWarningMessage(fmt.Sprintf("Failed to cancel runs for workflow %s: %v", t.Name, err)))
}
// Prefer disabling by lock file name for reliability
args := []string{"workflow", "disable", t.LockFileBase}
Expand All @@ -228,7 +228,7 @@ func toggleWorkflowsByNames(workflowNames []string, enable bool, repoOverride st
cmd = exec.Command("gh", args...)
} else {
if err := cancelWorkflowRunsByLockFile(t.LockFileBase); err != nil {
fmt.Fprintf(os.Stderr, "Warning: Failed to cancel runs for workflow %s: %v\n", t.Name, err)
fmt.Fprintln(os.Stderr, console.FormatWarningMessage(fmt.Sprintf("Failed to cancel runs for workflow %s: %v", t.Name, err)))
}
args := []string{"workflow", "disable", t.LockFileBase}
if repoOverride != "" {
Expand Down Expand Up @@ -351,7 +351,7 @@ func DisableAllWorkflowsExcept(repoSlug string, exceptWorkflows []string, verbos
cmd := exec.Command("gh", args...)
if output, err := cmd.CombinedOutput(); err != nil {
if verbose {
fmt.Fprintf(os.Stderr, "Warning: Failed to disable workflow %s: %v\n%s\n", workflow, err, string(output))
fmt.Fprintln(os.Stderr, console.FormatWarningMessage(fmt.Sprintf("Failed to disable workflow %s: %v\n%s", workflow, err, string(output))))
}
failures = append(failures, workflow)
} else {
Expand Down
3 changes: 2 additions & 1 deletion pkg/cli/packages.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"regexp"
"strings"

"github.com/githubnext/gh-aw/pkg/console"
"github.com/githubnext/gh-aw/pkg/logger"
"github.com/githubnext/gh-aw/pkg/parser"
)
Expand Down Expand Up @@ -193,7 +194,7 @@ func downloadWorkflows(repo, version, targetDir string, verbose bool) error {
metadataPath := filepath.Join(targetDir, ".commit-sha")
if err := os.WriteFile(metadataPath, []byte(commitSHA), 0644); err != nil {
if verbose {
fmt.Fprintf(os.Stderr, "Warning: Failed to write commit SHA metadata: %v\n", err)
fmt.Fprintln(os.Stderr, console.FormatWarningMessage(fmt.Sprintf("Failed to write commit SHA metadata: %v", err)))
}
}

Expand Down
4 changes: 2 additions & 2 deletions pkg/cli/update_command.go
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,7 @@ func showUpdateSummary(successfulUpdates []string, failedUpdates []updateFailure
if len(successfulUpdates) > 0 {
fmt.Fprintln(os.Stderr, console.FormatSuccessMessage(fmt.Sprintf("Successfully updated and compiled %d workflow(s):", len(successfulUpdates))))
for _, name := range successfulUpdates {
fmt.Fprintf(os.Stderr, " ✓ %s\n", name)
fmt.Fprintln(os.Stderr, console.FormatListItem(name))
}
fmt.Fprintln(os.Stderr, "")
}
Expand All @@ -311,7 +311,7 @@ func showUpdateSummary(successfulUpdates []string, failedUpdates []updateFailure
if len(failedUpdates) > 0 {
fmt.Fprintln(os.Stderr, console.FormatErrorMessage(fmt.Sprintf("Failed to update %d workflow(s):", len(failedUpdates))))
for _, failure := range failedUpdates {
fmt.Fprintf(os.Stderr, " %s: %s\n", failure.Name, failure.Error)
fmt.Fprintf(os.Stderr, " %s %s: %s\n", console.FormatErrorMessage("✗"), failure.Name, failure.Error)
Copy link

Copilot AI Nov 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will result in duplicate error symbols ("✗ ✗") being displayed. The FormatErrorMessage() function already adds a "✗ " prefix, so wrapping "✗" with it creates a double symbol.

Consider using a similar pattern to the success messages above (line 305) which uses FormatListItem():

fmt.Fprintf(os.Stderr, "  ✗ %s: %s\n", failure.Name, failure.Error)

Or if you want to use console formatting for consistency, create a different approach that doesn't duplicate the symbol.

Suggested change
fmt.Fprintf(os.Stderr, " %s %s: %s\n", console.FormatErrorMessage("✗"), failure.Name, failure.Error)
fmt.Fprintln(os.Stderr, " "+console.FormatErrorMessage(fmt.Sprintf("%s: %s", failure.Name, failure.Error)))

Copilot uses AI. Check for mistakes.
}
fmt.Fprintln(os.Stderr, "")
}
Expand Down
76 changes: 38 additions & 38 deletions pkg/workflow/shell_backslash_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,47 +4,47 @@
package workflow

import (
"testing"
"testing"
)

func TestSingleQuoteEscapingPreservesBackslashes(t *testing.T) {
// Test that the shell escaping function preserves backslashes
// This is a regression test for the typist workflow control character issue
testCases := []struct {
name string
input string
expected string
}{
{
name: "grep with word boundaries",
input: "grep -r '\\bany\\b' pkg",
expected: "'grep -r '\\''\\bany\\b'\\'' pkg'",
},
{
name: "echo with escape sequences",
input: "echo '\\n\\t'",
expected: "'echo '\\''\\n\\t'\\'''",
},
{
name: "path with backslashes",
input: "path\\to\\file",
expected: "'path\\to\\file'",
},
}
// Test that the shell escaping function preserves backslashes
// This is a regression test for the typist workflow control character issue
testCases := []struct {
name string
input string
expected string
}{
{
name: "grep with word boundaries",
input: "grep -r '\\bany\\b' pkg",
expected: "'grep -r '\\''\\bany\\b'\\'' pkg'",
},
{
name: "echo with escape sequences",
input: "echo '\\n\\t'",
expected: "'echo '\\''\\n\\t'\\'''",
},
{
name: "path with backslashes",
input: "path\\to\\file",
expected: "'path\\to\\file'",
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
result := shellEscapeArg(tc.input)
if result != tc.expected {
t.Errorf("shellEscapeArg(%q) = %q, expected %q", tc.input, result, tc.expected)
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
result := shellEscapeArg(tc.input)
if result != tc.expected {
t.Errorf("shellEscapeArg(%q) = %q, expected %q", tc.input, result, tc.expected)
}

// Verify no control characters in the result
for i, ch := range result {
if ch < 32 && ch != '\n' && ch != '\t' && ch != '\r' {
t.Errorf("Found control character in result at position %d: %q (0x%02x)", i, ch, ch)
}
}
})
}
// Verify no control characters in the result
for i, ch := range result {
if ch < 32 && ch != '\n' && ch != '\t' && ch != '\r' {
t.Errorf("Found control character in result at position %d: %q (0x%02x)", i, ch, ch)
}
}
})
}
}
Loading