diff --git a/.github/workflows/super-linter.lock.yml b/.github/workflows/super-linter.lock.yml index 101418272a..ff3c5be1e6 100644 --- a/.github/workflows/super-linter.lock.yml +++ b/.github/workflows/super-linter.lock.yml @@ -169,7 +169,7 @@ jobs: with: persist-credentials: false - name: Download super-linter log - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 + uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6 with: name: super-linter-log path: /tmp/gh-aw/ @@ -4541,7 +4541,7 @@ jobs: persist-credentials: false - name: Super-linter id: super-linter - uses: super-linter/super-linter@2bdd90ed3262e023ac84bf8fe35dc480721fc1f2 # v8.2.1 + uses: super-linter/super-linter@v8.2.1 env: CREATE_LOG_FILE: "true" DEFAULT_BRANCH: main @@ -4563,7 +4563,7 @@ jobs: fi - name: Upload super-linter log if: always() - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5 with: name: super-linter-log path: super-linter.log diff --git a/pkg/cli/commands_file_watching_test.go b/pkg/cli/commands_file_watching_test.go index bede7de417..52ff9c5e66 100644 --- a/pkg/cli/commands_file_watching_test.go +++ b/pkg/cli/commands_file_watching_test.go @@ -398,3 +398,155 @@ func TestHandleFileDeleted(t *testing.T) { handleFileDeleted(markdownFile, false) }) } + +// TestCompileSingleFile tests the compileSingleFile helper function +func TestCompileSingleFile(t *testing.T) { + t.Run("compile single file successfully", func(t *testing.T) { + tempDir := t.TempDir() + workflowsDir := filepath.Join(tempDir, ".github/workflows") + os.MkdirAll(workflowsDir, 0755) + + // Create a valid workflow file + filePath := filepath.Join(workflowsDir, "test.md") + content := "---\non: push\nengine: claude\n---\n# Test\n\nTest workflow content" + os.WriteFile(filePath, []byte(content), 0644) + + compiler := workflow.NewCompiler(false, "", "test") + stats := &CompilationStats{} + + // Compile without checking existence + result := compileSingleFile(compiler, filePath, stats, false, false) + + if !result { + t.Error("Expected compilation to be attempted") + } + + if stats.Total != 1 { + t.Errorf("Expected Total to be 1, got %d", stats.Total) + } + + if stats.Errors != 0 { + t.Errorf("Expected no errors, got %d", stats.Errors) + } + + // Check that lock file was created + lockFile := filepath.Join(workflowsDir, "test.lock.yml") + if _, err := os.Stat(lockFile); os.IsNotExist(err) { + t.Error("Expected lock file to be created") + } + }) + + t.Run("compile single file with error", func(t *testing.T) { + tempDir := t.TempDir() + workflowsDir := filepath.Join(tempDir, ".github/workflows") + os.MkdirAll(workflowsDir, 0755) + + // Create an invalid workflow file + filePath := filepath.Join(workflowsDir, "invalid.md") + content := "---\nmalformed: yaml: content:\n - missing\n proper: structure\n---\n# Invalid\n" + os.WriteFile(filePath, []byte(content), 0644) + + compiler := workflow.NewCompiler(false, "", "test") + stats := &CompilationStats{} + + // Compile without checking existence + result := compileSingleFile(compiler, filePath, stats, false, false) + + if !result { + t.Error("Expected compilation to be attempted") + } + + if stats.Total != 1 { + t.Errorf("Expected Total to be 1, got %d", stats.Total) + } + + if stats.Errors != 1 { + t.Errorf("Expected 1 error, got %d", stats.Errors) + } + + if len(stats.FailedWorkflows) != 1 { + t.Errorf("Expected 1 failed workflow, got %d", len(stats.FailedWorkflows)) + } + + if stats.FailedWorkflows[0] != "invalid.md" { + t.Errorf("Expected failed workflow to be 'invalid.md', got '%s'", stats.FailedWorkflows[0]) + } + }) + + t.Run("compile single file with checkExists true and file exists", func(t *testing.T) { + tempDir := t.TempDir() + workflowsDir := filepath.Join(tempDir, ".github/workflows") + os.MkdirAll(workflowsDir, 0755) + + // Create a valid workflow file + filePath := filepath.Join(workflowsDir, "test.md") + content := "---\non: push\nengine: claude\n---\n# Test\n\nTest workflow content" + os.WriteFile(filePath, []byte(content), 0644) + + compiler := workflow.NewCompiler(false, "", "test") + stats := &CompilationStats{} + + // Compile with existence check + result := compileSingleFile(compiler, filePath, stats, false, true) + + if !result { + t.Error("Expected compilation to be attempted") + } + + if stats.Total != 1 { + t.Errorf("Expected Total to be 1, got %d", stats.Total) + } + }) + + t.Run("compile single file with checkExists true and file does not exist", func(t *testing.T) { + tempDir := t.TempDir() + workflowsDir := filepath.Join(tempDir, ".github/workflows") + os.MkdirAll(workflowsDir, 0755) + + // Use a non-existent file path + filePath := filepath.Join(workflowsDir, "nonexistent.md") + + compiler := workflow.NewCompiler(false, "", "test") + stats := &CompilationStats{} + + // Compile with existence check - should skip + result := compileSingleFile(compiler, filePath, stats, false, true) + + if result { + t.Error("Expected compilation to be skipped for non-existent file") + } + + if stats.Total != 0 { + t.Errorf("Expected Total to be 0 (not attempted), got %d", stats.Total) + } + }) + + t.Run("compile single file verbose mode", func(t *testing.T) { + tempDir := t.TempDir() + workflowsDir := filepath.Join(tempDir, ".github/workflows") + os.MkdirAll(workflowsDir, 0755) + + // Create a valid workflow file + filePath := filepath.Join(workflowsDir, "verbose-test.md") + content := "---\non: push\nengine: claude\n---\n# Verbose Test\n\nTest workflow content" + os.WriteFile(filePath, []byte(content), 0644) + + compiler := workflow.NewCompiler(false, "", "test") + stats := &CompilationStats{} + + // Compile in verbose mode + result := compileSingleFile(compiler, filePath, stats, true, false) + + if !result { + t.Error("Expected compilation to be attempted") + } + + if stats.Total != 1 { + t.Errorf("Expected Total to be 1, got %d", stats.Total) + } + + if stats.Errors != 0 { + t.Errorf("Expected no errors, got %d", stats.Errors) + } + }) +} diff --git a/pkg/cli/compile_command.go b/pkg/cli/compile_command.go index bd34cbe4b2..4148b25586 100644 --- a/pkg/cli/compile_command.go +++ b/pkg/cli/compile_command.go @@ -858,6 +858,37 @@ func watchAndCompileWorkflows(markdownFile string, compiler *workflow.Compiler, } } +// compileSingleFile compiles a single markdown workflow file and updates compilation statistics +// If checkExists is true, the function will check if the file exists before compiling +// Returns true if compilation was attempted (file exists or checkExists is false), false otherwise +func compileSingleFile(compiler *workflow.Compiler, file string, stats *CompilationStats, verbose bool, checkExists bool) bool { + // Check if file exists if requested (for watch mode) + if checkExists { + if _, err := os.Stat(file); os.IsNotExist(err) { + compileLog.Printf("File %s was deleted, skipping compilation", file) + return false + } + } + + stats.Total++ + + compileLog.Printf("Compiling: %s", file) + if verbose { + fmt.Fprintf(os.Stderr, "🔨 Compiling: %s\n", file) + } + + if err := CompileWorkflowWithValidation(compiler, file, verbose, false, false, false, false, false); err != nil { + // Always show compilation errors on new line + fmt.Fprintln(os.Stderr, err.Error()) + stats.Errors++ + stats.FailedWorkflows = append(stats.FailedWorkflows, filepath.Base(file)) + } else { + compileLog.Printf("Successfully compiled: %s", file) + } + + return true +} + // compileAllWorkflowFiles compiles all markdown files in the workflows directory func compileAllWorkflowFiles(compiler *workflow.Compiler, workflowsDir string, verbose bool) (*CompilationStats, error) { compileLog.Printf("Compiling all workflow files in directory: %s", workflowsDir) @@ -885,20 +916,7 @@ func compileAllWorkflowFiles(compiler *workflow.Compiler, workflowsDir string, v // Compile each file for _, file := range mdFiles { - stats.Total++ - - compileLog.Printf("Compiling: %s", file) - if verbose { - fmt.Printf("🔨 Compiling: %s\n", file) - } - if err := CompileWorkflowWithValidation(compiler, file, verbose, false, false, false, false, false); err != nil { - // Always show compilation errors on new line - fmt.Fprintln(os.Stderr, err.Error()) - stats.Errors++ - stats.FailedWorkflows = append(stats.FailedWorkflows, filepath.Base(file)) - } else { - compileLog.Printf("Successfully compiled: %s", file) - } + compileSingleFile(compiler, file, stats, verbose, false) } // Get warning count from compiler @@ -935,27 +953,7 @@ func compileModifiedFiles(compiler *workflow.Compiler, files []string, verbose b stats := &CompilationStats{} for _, file := range files { - // Check if file still exists (might have been deleted between detection and compilation) - if _, err := os.Stat(file); os.IsNotExist(err) { - compileLog.Printf("File %s was deleted, skipping compilation", file) - continue - } - - stats.Total++ - - compileLog.Printf("Compiling: %s", file) - if verbose { - fmt.Fprintf(os.Stderr, "🔨 Compiling: %s\n", file) - } - - if err := CompileWorkflowWithValidation(compiler, file, verbose, false, false, false, false, false); err != nil { - // Always show compilation errors on new line - fmt.Fprintln(os.Stderr, err.Error()) - stats.Errors++ - stats.FailedWorkflows = append(stats.FailedWorkflows, filepath.Base(file)) - } else { - compileLog.Printf("Successfully compiled: %s", file) - } + compileSingleFile(compiler, file, stats, verbose, true) } // Get warning count from compiler