diff --git a/pkg/parser/schedule_parser.go b/pkg/parser/schedule_parser.go index 2fe1d0dd4bf..cb999ac3c88 100644 --- a/pkg/parser/schedule_parser.go +++ b/pkg/parser/schedule_parser.go @@ -385,6 +385,7 @@ func (p *ScheduleParser) parseBase() (string, error) { case "hourly": // hourly -> FUZZY:HOURLY/1 (fuzzy hourly schedule, equivalent to "every 1h") // hourly on weekdays -> FUZZY:HOURLY_WEEKDAYS/1 (fuzzy hourly schedule, Mon-Fri only) + scheduleLog.Printf("Parsing hourly schedule: weekdays=%v", hasWeekdaysSuffix) if len(p.tokens) == 1 || (len(p.tokens) == 3 && hasWeekdaysSuffix) { if hasWeekdaysSuffix { return "FUZZY:HOURLY_WEEKDAYS/1 * * *", nil @@ -399,6 +400,7 @@ func (p *ScheduleParser) parseBase() (string, error) { // weekly on -> FUZZY:WEEKLY:DOW (fuzzy schedule on specific weekday) // weekly on at HH:MM -> MM HH * * DOW // weekly on around HH:MM -> FUZZY:WEEKLY_AROUND:DOW:HH:MM + scheduleLog.Printf("Parsing weekly schedule: token_count=%d", len(p.tokens)) if len(p.tokens) == 1 { // Just "weekly" with no day specified - this is a fuzzy schedule return "FUZZY:WEEKLY * * *", nil @@ -453,6 +455,7 @@ func (p *ScheduleParser) parseBase() (string, error) { case "monthly": // monthly on -> rejected (use cron directly) // monthly on at HH:MM -> rejected (use cron directly) + scheduleLog.Printf("Parsing monthly schedule: token_count=%d", len(p.tokens)) if len(p.tokens) < 3 || p.tokens[1] != "on" { return "", errors.New("monthly schedule requires 'on '") } diff --git a/pkg/parser/schema_compiler.go b/pkg/parser/schema_compiler.go index ece435e22c7..c6405dd5485 100644 --- a/pkg/parser/schema_compiler.go +++ b/pkg/parser/schema_compiler.go @@ -260,6 +260,7 @@ func stripDetailLinePrefix(detail string) string { // validateWithSchemaAndLocation validates frontmatter against a JSON schema with location information func validateWithSchemaAndLocation(frontmatter map[string]any, schemaJSON, context, filePath string) error { + schemaCompilerLog.Printf("Validating with location info: context=%s, file=%s", context, filePath) // First try the basic validation err := validateWithSchema(frontmatter, schemaJSON, context) if err == nil { @@ -316,6 +317,7 @@ func validateWithSchemaAndLocation(frontmatter map[string]any, schemaJSON, conte if isJSONSchemaError { // Extract JSON path information from the validation error jsonPaths := ExtractJSONPathFromValidationError(err) + schemaCompilerLog.Printf("Extracted %d JSON path(s) from validation error for %s", len(jsonPaths), context) // If we have paths and frontmatter content, try to get precise locations if len(jsonPaths) > 0 && frontmatterContent != "" { diff --git a/pkg/workflow/checkout_step_generator.go b/pkg/workflow/checkout_step_generator.go index 6654d9710cd..1fb5338e271 100644 --- a/pkg/workflow/checkout_step_generator.go +++ b/pkg/workflow/checkout_step_generator.go @@ -213,6 +213,8 @@ func (cm *CheckoutManager) GenerateDefaultCheckoutStep( // The index parameter identifies the checkout's position in the ordered list, used to // reference the correct app token minting step when app authentication is configured. func generateCheckoutStepLines(entry *resolvedCheckout, index int, getActionPin func(string) string) []string { + checkoutManagerLog.Printf("Generating checkout step lines: index=%d, repo=%q, path=%q, ref=%q, appAuth=%v", + index, entry.key.repository, entry.key.path, entry.ref, entry.githubApp != nil) name := "Checkout " + checkoutStepName(entry.key) var sb strings.Builder fmt.Fprintf(&sb, " - name: %s\n", name) @@ -314,6 +316,8 @@ func generateFetchStepLines(entry *resolvedCheckout, index int) string { return "" } + checkoutManagerLog.Printf("Generating fetch step for index=%d, refs=%v", index, entry.fetchRefs) + // Build step name name := "Fetch additional refs" if entry.key.repository != "" { diff --git a/pkg/workflow/compiler_string_api.go b/pkg/workflow/compiler_string_api.go index 3164e0bcdd7..1597306844e 100644 --- a/pkg/workflow/compiler_string_api.go +++ b/pkg/workflow/compiler_string_api.go @@ -80,6 +80,8 @@ func (c *Compiler) ParseWorkflowString(content string, virtualPath string) (*Wor return nil, errors.New("no frontmatter found") } + compilerStringAPILog.Printf("ParseWorkflowString: extracted frontmatter with %d fields", len(result.Frontmatter)) + // Preprocess schedule fields if err := c.preprocessScheduleFields(result.Frontmatter, cleanPath, content); err != nil { return nil, err @@ -90,11 +92,13 @@ func (c *Compiler) ParseWorkflowString(content string, virtualPath string) (*Wor // Check if shared workflow (no 'on' field) _, hasOnField := frontmatterForValidation["on"] if !hasOnField { + compilerStringAPILog.Printf("ParseWorkflowString: no 'on' field, treating as shared workflow: %s", cleanPath) return nil, &SharedWorkflowError{Path: cleanPath} } // Validate frontmatter against schema if err := parser.ValidateMainWorkflowFrontmatterWithSchemaAndLocation(frontmatterForValidation, cleanPath); err != nil { + compilerStringAPILog.Printf("ParseWorkflowString: schema validation failed for %s", cleanPath) return nil, err } @@ -157,6 +161,7 @@ func (c *Compiler) ParseWorkflowString(content string, virtualPath string) (*Wor // Merge features from imports if len(engineSetup.importsResult.MergedFeatures) > 0 { + compilerStringAPILog.Printf("ParseWorkflowString: merging %d features from imports", len(engineSetup.importsResult.MergedFeatures)) mergedFeatures, err := c.MergeFeatures(workflowData.Features, engineSetup.importsResult.MergedFeatures) if err != nil { return nil, fmt.Errorf("failed to merge features from imports: %w", err) diff --git a/pkg/workflow/concurrency.go b/pkg/workflow/concurrency.go index 6a45fb9fdcd..80b0ad28918 100644 --- a/pkg/workflow/concurrency.go +++ b/pkg/workflow/concurrency.go @@ -251,6 +251,7 @@ func buildConcurrencyGroupKeys(workflowData *WorkflowData, isCommandTrigger bool // For command/slash_command workflows: use issue/PR number; fall back to run_id when // neither is available (e.g. manual workflow_dispatch of the outer workflow). // When bot risk is detected, prepend the bot-actor isolation check. + concurrencyLog.Print("Building concurrency key for command/slash_command workflow") if botRisk { keys = append(keys, "${{ contains(github.actor, '[bot]') && github.run_id || github.event.issue.number || github.event.pull_request.number || github.run_id }}") } else { @@ -258,6 +259,7 @@ func buildConcurrencyGroupKeys(workflowData *WorkflowData, isCommandTrigger bool } } else if isPullRequestWorkflow(workflowData.On) && isIssueWorkflow(workflowData.On) { // Mixed workflows with both issue and PR triggers + concurrencyLog.Print("Building concurrency key for mixed PR+issue workflow") keys = append(keys, entityKey( []string{"github.event.issue.number", "github.event.pull_request.number"}, []string{"github.run_id"}, @@ -277,6 +279,7 @@ func buildConcurrencyGroupKeys(workflowData *WorkflowData, isCommandTrigger bool )) } else if isPullRequestWorkflow(workflowData.On) { // PR workflows: use PR number, fall back to ref then run_id + concurrencyLog.Print("Building concurrency key for PR workflow") keys = append(keys, entityConcurrencyKey( []string{"github.event.pull_request.number"}, []string{"github.ref", "github.run_id"}, @@ -285,6 +288,7 @@ func buildConcurrencyGroupKeys(workflowData *WorkflowData, isCommandTrigger bool } else if isIssueWorkflow(workflowData.On) { // Issue workflows: run_id is the fallback when no issue context is available // (e.g. when a mixed-trigger workflow is started via workflow_dispatch). + concurrencyLog.Print("Building concurrency key for issue workflow") keys = append(keys, entityKey( []string{"github.event.issue.number"}, []string{"github.run_id"}, @@ -298,6 +302,7 @@ func buildConcurrencyGroupKeys(workflowData *WorkflowData, isCommandTrigger bool )) } else if isPushWorkflow(workflowData.On) { // Push workflows: use ref to differentiate between branches + concurrencyLog.Print("Building concurrency key for push workflow") keys = append(keys, "${{ github.ref || github.run_id }}") } @@ -318,9 +323,12 @@ func buildConcurrencyGroupKeys(workflowData *WorkflowData, isCommandTrigger bool func shouldEnableCancelInProgress(workflowData *WorkflowData, isCommandTrigger bool) bool { // Never enable cancellation for command workflows if isCommandTrigger { + concurrencyLog.Print("cancel-in-progress disabled: command trigger workflow") return false } // Enable cancellation for pull request workflows (including mixed workflows) - return isPullRequestWorkflow(workflowData.On) + result := isPullRequestWorkflow(workflowData.On) + concurrencyLog.Printf("cancel-in-progress=%v for workflow on=%s", result, workflowData.On) + return result }