diff --git a/.github/workflows/artifacts-summary.lock.yml b/.github/workflows/artifacts-summary.lock.yml index 6b1c2fb3a5c..1f2a68f1695 100644 --- a/.github/workflows/artifacts-summary.lock.yml +++ b/.github/workflows/artifacts-summary.lock.yml @@ -1952,7 +1952,7 @@ jobs: create_issue: needs: agent - if: contains(needs.agent.outputs.output_types, 'create-issue') + if: (always()) && (contains(needs.agent.outputs.output_types, 'create-issue')) runs-on: ubuntu-latest permissions: contents: read @@ -2134,7 +2134,7 @@ jobs: missing_tool: needs: agent - if: ${{ always() }} + if: (always()) && (contains(needs.agent.outputs.output_types, 'missing-tool')) runs-on: ubuntu-latest permissions: contents: read diff --git a/.github/workflows/ci-doctor.lock.yml b/.github/workflows/ci-doctor.lock.yml index 5f83ddd4756..0472c348986 100644 --- a/.github/workflows/ci-doctor.lock.yml +++ b/.github/workflows/ci-doctor.lock.yml @@ -2355,7 +2355,7 @@ jobs: create_issue: needs: agent - if: contains(needs.agent.outputs.output_types, 'create-issue') + if: (always()) && (contains(needs.agent.outputs.output_types, 'create-issue')) runs-on: ubuntu-latest permissions: contents: read @@ -2534,9 +2534,11 @@ jobs: await main(); })(); - create_issue_comment: + add_comment: needs: agent - if: github.event.issue.number || github.event.pull_request.number + if: > + ((always()) && (contains(needs.agent.outputs.output_types, 'add-comment'))) && ((github.event.issue.number) || + (github.event.pull_request.number)) runs-on: ubuntu-latest permissions: contents: read @@ -2700,7 +2702,7 @@ jobs: missing_tool: needs: agent - if: ${{ always() }} + if: (always()) && (contains(needs.agent.outputs.output_types, 'missing-tool')) runs-on: ubuntu-latest permissions: contents: read diff --git a/.github/workflows/dev.lock.yml b/.github/workflows/dev.lock.yml index fdfcd1f99f8..83cbecbf189 100644 --- a/.github/workflows/dev.lock.yml +++ b/.github/workflows/dev.lock.yml @@ -1968,7 +1968,7 @@ jobs: create_issue: needs: agent - if: contains(needs.agent.outputs.output_types, 'create-issue') + if: (always()) && (contains(needs.agent.outputs.output_types, 'create-issue')) runs-on: ubuntu-latest permissions: contents: read @@ -2149,7 +2149,7 @@ jobs: missing_tool: needs: agent - if: ${{ always() }} + if: (always()) && (contains(needs.agent.outputs.output_types, 'missing-tool')) runs-on: ubuntu-latest permissions: contents: read diff --git a/.github/workflows/tidy.lock.yml b/.github/workflows/tidy.lock.yml index f44b7198533..d783b41fc17 100644 --- a/.github/workflows/tidy.lock.yml +++ b/.github/workflows/tidy.lock.yml @@ -2186,7 +2186,7 @@ jobs: create_pull_request: needs: agent - if: contains(needs.agent.outputs.output_types, 'create-pull-request') + if: (always()) && (contains(needs.agent.outputs.output_types, 'create-pull-request')) runs-on: ubuntu-latest permissions: contents: write @@ -2516,7 +2516,10 @@ jobs: push_to_pull_request_branch: needs: agent - if: (github.event.issue.number && github.event.issue.pull_request) || github.event.pull_request + if: > + ((always()) && (contains(needs.agent.outputs.output_types, 'push-to-pull-request-branch'))) && + (((github.event.issue.number) && + (github.event.issue.pull_request)) || (github.event.pull_request)) runs-on: ubuntu-latest permissions: contents: write @@ -2814,7 +2817,7 @@ jobs: missing_tool: needs: agent - if: ${{ always() }} + if: (always()) && (contains(needs.agent.outputs.output_types, 'missing-tool')) runs-on: ubuntu-latest permissions: contents: read diff --git a/pkg/cli/workflows/test-all.lock.yml b/pkg/cli/workflows/test-all.lock.yml index ee736c21ab1..334571995b3 100644 --- a/pkg/cli/workflows/test-all.lock.yml +++ b/pkg/cli/workflows/test-all.lock.yml @@ -2895,7 +2895,7 @@ jobs: create_issue: needs: agent - if: contains(needs.agent.outputs.output_types, 'create-issue') + if: (always()) && (contains(needs.agent.outputs.output_types, 'create-issue')) runs-on: ubuntu-latest permissions: contents: read @@ -3076,9 +3076,9 @@ jobs: await main(); })(); - create_issue_comment: + add_comment: needs: agent - if: always() + if: (always()) && (contains(needs.agent.outputs.output_types, 'add-comment')) runs-on: ubuntu-latest permissions: contents: read @@ -3244,7 +3244,9 @@ jobs: create_pr_review_comment: needs: agent - if: (github.event.issue.number && github.event.issue.pull_request) || github.event.pull_request + if: > + ((always()) && (contains(needs.agent.outputs.output_types, 'create-pull-request-review-comment'))) && + (((github.event.issue.number) && (github.event.issue.pull_request)) || (github.event.pull_request)) runs-on: ubuntu-latest permissions: contents: read @@ -3437,7 +3439,7 @@ jobs: create_pull_request: needs: agent - if: contains(needs.agent.outputs.output_types, 'create-pull-request') + if: (always()) && (contains(needs.agent.outputs.output_types, 'create-pull-request')) runs-on: ubuntu-latest permissions: contents: write @@ -3768,7 +3770,9 @@ jobs: add_labels: needs: agent - if: github.event.issue.number || github.event.pull_request.number + if: > + ((always()) && (contains(needs.agent.outputs.output_types, 'add-labels'))) && ((github.event.issue.number) || + (github.event.pull_request.number)) runs-on: ubuntu-latest permissions: contents: read @@ -3965,7 +3969,7 @@ jobs: update_issue: needs: agent - if: always() + if: (always()) && (contains(needs.agent.outputs.output_types, 'update-issue')) runs-on: ubuntu-latest permissions: contents: read @@ -4158,7 +4162,10 @@ jobs: push_to_pull_request_branch: needs: agent - if: (github.event.issue.number && github.event.issue.pull_request) || github.event.pull_request + if: > + ((always()) && (contains(needs.agent.outputs.output_types, 'push-to-pull-request-branch'))) && + (((github.event.issue.number) && + (github.event.issue.pull_request)) || (github.event.pull_request)) runs-on: ubuntu-latest permissions: contents: write @@ -4457,7 +4464,7 @@ jobs: missing_tool: needs: agent - if: ${{ always() }} + if: (always()) && (contains(needs.agent.outputs.output_types, 'missing-tool')) runs-on: ubuntu-latest permissions: contents: read diff --git a/pkg/cli/workflows/test-claude-max-patch-size.lock.yml b/pkg/cli/workflows/test-claude-max-patch-size.lock.yml index 5f309d18c63..59123c82fad 100644 --- a/pkg/cli/workflows/test-claude-max-patch-size.lock.yml +++ b/pkg/cli/workflows/test-claude-max-patch-size.lock.yml @@ -2394,7 +2394,7 @@ jobs: create_pull_request: needs: agent - if: contains(needs.agent.outputs.output_types, 'create-pull-request') + if: (always()) && (contains(needs.agent.outputs.output_types, 'create-pull-request')) runs-on: ubuntu-latest permissions: contents: write @@ -2723,7 +2723,7 @@ jobs: missing_tool: needs: agent - if: ${{ always() }} + if: (always()) && (contains(needs.agent.outputs.output_types, 'missing-tool')) runs-on: ubuntu-latest permissions: contents: read diff --git a/pkg/cli/workflows/test-claude-missing-tool.lock.yml b/pkg/cli/workflows/test-claude-missing-tool.lock.yml index d546d37e686..9cb050a2bc5 100644 --- a/pkg/cli/workflows/test-claude-missing-tool.lock.yml +++ b/pkg/cli/workflows/test-claude-missing-tool.lock.yml @@ -2344,7 +2344,7 @@ jobs: missing_tool: needs: agent - if: ${{ always() }} + if: (always()) && (contains(needs.agent.outputs.output_types, 'missing-tool')) runs-on: ubuntu-latest permissions: contents: read diff --git a/pkg/cli/workflows/test-claude-patch-size-exceeded.lock.yml b/pkg/cli/workflows/test-claude-patch-size-exceeded.lock.yml index 7f695205a48..804f71b0651 100644 --- a/pkg/cli/workflows/test-claude-patch-size-exceeded.lock.yml +++ b/pkg/cli/workflows/test-claude-patch-size-exceeded.lock.yml @@ -2396,7 +2396,10 @@ jobs: push_to_pull_request_branch: needs: agent - if: (github.event.issue.number && github.event.issue.pull_request) || github.event.pull_request + if: > + ((always()) && (contains(needs.agent.outputs.output_types, 'push-to-pull-request-branch'))) && + (((github.event.issue.number) && + (github.event.issue.pull_request)) || (github.event.pull_request)) runs-on: ubuntu-latest permissions: contents: write @@ -2694,7 +2697,7 @@ jobs: missing_tool: needs: agent - if: ${{ always() }} + if: (always()) && (contains(needs.agent.outputs.output_types, 'missing-tool')) runs-on: ubuntu-latest permissions: contents: read diff --git a/pkg/cli/workflows/test-claude-playwright-accessibility-contrast.lock.yml b/pkg/cli/workflows/test-claude-playwright-accessibility-contrast.lock.yml index 60636b30aad..4c4052c9057 100644 --- a/pkg/cli/workflows/test-claude-playwright-accessibility-contrast.lock.yml +++ b/pkg/cli/workflows/test-claude-playwright-accessibility-contrast.lock.yml @@ -2306,7 +2306,7 @@ jobs: create_issue: needs: agent - if: contains(needs.agent.outputs.output_types, 'create-issue') + if: (always()) && (contains(needs.agent.outputs.output_types, 'create-issue')) runs-on: ubuntu-latest permissions: contents: read @@ -2488,7 +2488,7 @@ jobs: missing_tool: needs: agent - if: ${{ always() }} + if: (always()) && (contains(needs.agent.outputs.output_types, 'missing-tool')) runs-on: ubuntu-latest permissions: contents: read diff --git a/pkg/cli/workflows/test-claude-playwright-screenshots.lock.yml b/pkg/cli/workflows/test-claude-playwright-screenshots.lock.yml index 938648d43ee..a05371631ea 100644 --- a/pkg/cli/workflows/test-claude-playwright-screenshots.lock.yml +++ b/pkg/cli/workflows/test-claude-playwright-screenshots.lock.yml @@ -2472,7 +2472,7 @@ jobs: create_issue: needs: agent - if: contains(needs.agent.outputs.output_types, 'create-issue') + if: (always()) && (contains(needs.agent.outputs.output_types, 'create-issue')) runs-on: ubuntu-latest permissions: contents: read @@ -2654,7 +2654,7 @@ jobs: missing_tool: needs: agent - if: ${{ always() }} + if: (always()) && (contains(needs.agent.outputs.output_types, 'missing-tool')) runs-on: ubuntu-latest permissions: contents: read @@ -2759,6 +2759,7 @@ jobs: upload_assets: needs: agent + if: (always()) && (contains(needs.agent.outputs.output_types, 'upload-asset')) runs-on: ubuntu-latest permissions: contents: write # Required for creating orphaned branch and pushing assets diff --git a/pkg/cli/workflows/test-claude-safe-jobs.lock.yml b/pkg/cli/workflows/test-claude-safe-jobs.lock.yml index d5b6ddc9e56..07dff60fbb8 100644 --- a/pkg/cli/workflows/test-claude-safe-jobs.lock.yml +++ b/pkg/cli/workflows/test-claude-safe-jobs.lock.yml @@ -2492,7 +2492,7 @@ jobs: missing_tool: needs: agent - if: ${{ always() }} + if: (always()) && (contains(needs.agent.outputs.output_types, 'missing-tool')) runs-on: ubuntu-latest permissions: contents: read diff --git a/pkg/cli/workflows/test-copilot-max-patch-size.lock.yml b/pkg/cli/workflows/test-copilot-max-patch-size.lock.yml index 3ee9d522e1c..ad5eebd84b7 100644 --- a/pkg/cli/workflows/test-copilot-max-patch-size.lock.yml +++ b/pkg/cli/workflows/test-copilot-max-patch-size.lock.yml @@ -2014,7 +2014,7 @@ jobs: create_pull_request: needs: agent - if: contains(needs.agent.outputs.output_types, 'create-pull-request') + if: (always()) && (contains(needs.agent.outputs.output_types, 'create-pull-request')) runs-on: ubuntu-latest permissions: contents: write @@ -2343,7 +2343,7 @@ jobs: missing_tool: needs: agent - if: ${{ always() }} + if: (always()) && (contains(needs.agent.outputs.output_types, 'missing-tool')) runs-on: ubuntu-latest permissions: contents: read diff --git a/pkg/cli/workflows/test-copilot-missing-tool.lock.yml b/pkg/cli/workflows/test-copilot-missing-tool.lock.yml index 224f13bf307..e595b21b5e1 100644 --- a/pkg/cli/workflows/test-copilot-missing-tool.lock.yml +++ b/pkg/cli/workflows/test-copilot-missing-tool.lock.yml @@ -1963,7 +1963,7 @@ jobs: missing_tool: needs: agent - if: ${{ always() }} + if: (always()) && (contains(needs.agent.outputs.output_types, 'missing-tool')) runs-on: ubuntu-latest permissions: contents: read diff --git a/pkg/cli/workflows/test-copilot-patch-size-exceeded.lock.yml b/pkg/cli/workflows/test-copilot-patch-size-exceeded.lock.yml index a8fb9712309..1ad71db5b94 100644 --- a/pkg/cli/workflows/test-copilot-patch-size-exceeded.lock.yml +++ b/pkg/cli/workflows/test-copilot-patch-size-exceeded.lock.yml @@ -2016,7 +2016,10 @@ jobs: push_to_pull_request_branch: needs: agent - if: (github.event.issue.number && github.event.issue.pull_request) || github.event.pull_request + if: > + ((always()) && (contains(needs.agent.outputs.output_types, 'push-to-pull-request-branch'))) && + (((github.event.issue.number) && + (github.event.issue.pull_request)) || (github.event.pull_request)) runs-on: ubuntu-latest permissions: contents: write @@ -2314,7 +2317,7 @@ jobs: missing_tool: needs: agent - if: ${{ always() }} + if: (always()) && (contains(needs.agent.outputs.output_types, 'missing-tool')) runs-on: ubuntu-latest permissions: contents: read diff --git a/pkg/cli/workflows/test-copilot-playwright-screenshots.lock.yml b/pkg/cli/workflows/test-copilot-playwright-screenshots.lock.yml index 67a34e7ffa5..8b8df1c3bd9 100644 --- a/pkg/cli/workflows/test-copilot-playwright-screenshots.lock.yml +++ b/pkg/cli/workflows/test-copilot-playwright-screenshots.lock.yml @@ -2064,7 +2064,7 @@ jobs: create_issue: needs: agent - if: contains(needs.agent.outputs.output_types, 'create-issue') + if: (always()) && (contains(needs.agent.outputs.output_types, 'create-issue')) runs-on: ubuntu-latest permissions: contents: read @@ -2246,7 +2246,7 @@ jobs: missing_tool: needs: agent - if: ${{ always() }} + if: (always()) && (contains(needs.agent.outputs.output_types, 'missing-tool')) runs-on: ubuntu-latest permissions: contents: read @@ -2351,6 +2351,7 @@ jobs: upload_assets: needs: agent + if: (always()) && (contains(needs.agent.outputs.output_types, 'upload-asset')) runs-on: ubuntu-latest permissions: contents: write # Required for creating orphaned branch and pushing assets diff --git a/pkg/cli/workflows/test-copilot-safe-jobs.lock.yml b/pkg/cli/workflows/test-copilot-safe-jobs.lock.yml index 7ba72cc4b63..25e59ccfab2 100644 --- a/pkg/cli/workflows/test-copilot-safe-jobs.lock.yml +++ b/pkg/cli/workflows/test-copilot-safe-jobs.lock.yml @@ -2115,7 +2115,7 @@ jobs: missing_tool: needs: agent - if: ${{ always() }} + if: (always()) && (contains(needs.agent.outputs.output_types, 'missing-tool')) runs-on: ubuntu-latest permissions: contents: read diff --git a/pkg/cli/workflows/test-playwright-args.lock.yml b/pkg/cli/workflows/test-playwright-args.lock.yml index bde7fbf7a8c..32113e8fbf2 100644 --- a/pkg/cli/workflows/test-playwright-args.lock.yml +++ b/pkg/cli/workflows/test-playwright-args.lock.yml @@ -2327,7 +2327,7 @@ jobs: create_issue: needs: agent - if: contains(needs.agent.outputs.output_types, 'create-issue') + if: (always()) && (contains(needs.agent.outputs.output_types, 'create-issue')) runs-on: ubuntu-latest permissions: contents: read @@ -2509,7 +2509,7 @@ jobs: missing_tool: needs: agent - if: ${{ always() }} + if: (always()) && (contains(needs.agent.outputs.output_types, 'missing-tool')) runs-on: ubuntu-latest permissions: contents: read @@ -2614,6 +2614,7 @@ jobs: upload_assets: needs: agent + if: (always()) && (contains(needs.agent.outputs.output_types, 'upload-asset')) runs-on: ubuntu-latest permissions: contents: write # Required for creating orphaned branch and pushing assets diff --git a/pkg/parser/schemas/main_workflow_schema.json b/pkg/parser/schemas/main_workflow_schema.json index 40bdbed96b5..72035df8690 100644 --- a/pkg/parser/schemas/main_workflow_schema.json +++ b/pkg/parser/schemas/main_workflow_schema.json @@ -1588,6 +1588,10 @@ "description": "Minimum number of labels to add (default: 0 - no requirement)", "minimum": 0 }, + "target": { + "type": "string", + "description": "Target for labels: 'triggering' (default), '*' (any issue/PR), or explicit issue/PR number" + }, "github-token": { "type": "string", "description": "GitHub token to use for this specific output type. Overrides global github-token if specified." diff --git a/pkg/workflow/add_comment.go b/pkg/workflow/add_comment.go index e77d29eaf51..435b58ceda8 100644 --- a/pkg/workflow/add_comment.go +++ b/pkg/workflow/add_comment.go @@ -15,7 +15,7 @@ type AddCommentsConfig struct { Target string `yaml:"target,omitempty"` // Target for comments: "triggering" (default), "*" (any issue), or explicit issue number } -// buildCreateOutputAddCommentJob creates the create_issue_comment job +// buildCreateOutputAddCommentJob creates the add_comment job func (c *Compiler) buildCreateOutputAddCommentJob(data *WorkflowData, mainJobName string) (*Job, error) { if data.SafeOutputs == nil || data.SafeOutputs.AddComments == nil { return nil, fmt.Errorf("safe-outputs.add-comment configuration is required") @@ -57,39 +57,18 @@ func (c *Compiler) buildCreateOutputAddCommentJob(data *WorkflowData, mainJobNam "comment_url": "${{ steps.add_comment.outputs.comment_url }}", } - // Determine the job condition based on target configuration - var baseCondition string - if data.SafeOutputs.AddComments.Target == "*" { - // Allow the job to run in any context when target is "*" - baseCondition = "always()" // This allows the job to run even without triggering issue/PR - } else { - // Default behavior: only run in issue or PR context - baseCondition = "github.event.issue.number || github.event.pull_request.number" - } - - // If this is a command workflow, combine the command trigger condition with the base condition - var jobCondition string - if data.Command != "" { - // Build the command trigger condition - commandCondition := buildCommandOnlyCondition(data.Command) - commandConditionStr := commandCondition.Render() - - // Combine command condition with base condition using AND - if baseCondition == "always()" { - // If base condition is always(), just use the command condition - jobCondition = commandConditionStr - } else { - // Combine both conditions with AND - jobCondition = fmt.Sprintf("(%s) && (%s)", commandConditionStr, baseCondition) - } - } else { - // No command trigger, just use the base condition - jobCondition = baseCondition + var jobCondition = BuildSafeOutputType("add-comment") + if data.SafeOutputs.AddComments != nil && data.SafeOutputs.AddComments.Target == "" { + eventCondition := buildOr( + BuildPropertyAccess("github.event.issue.number"), + BuildPropertyAccess("github.event.pull_request.number"), + ) + jobCondition = buildAnd(jobCondition, eventCondition) } job := &Job{ - Name: "create_issue_comment", - If: jobCondition, + Name: "add_comment", + If: jobCondition.Render(), RunsOn: c.formatSafeOutputsRunsOn(data.SafeOutputs), Permissions: "permissions:\n contents: read\n issues: write\n pull-requests: write", TimeoutMinutes: 10, // 10-minute timeout as required diff --git a/pkg/workflow/add_labels.go b/pkg/workflow/add_labels.go index 363474b2c92..08c896e6bf3 100644 --- a/pkg/workflow/add_labels.go +++ b/pkg/workflow/add_labels.go @@ -11,6 +11,7 @@ type AddLabelsConfig struct { MaxCount *int `yaml:"max,omitempty"` // Optional maximum number of labels to add (default: 3) MinCount *int `yaml:"min,omitempty"` // Optional minimum number of labels to add GitHubToken string `yaml:"github-token,omitempty"` // GitHub token for this specific output type + Target string `yaml:"target,omitempty"` // Target for labels: "triggering" (default), "*" (any issue/PR), or explicit issue/PR number } // buildCreateOutputLabelJob creates the add_labels job @@ -45,6 +46,11 @@ func (c *Compiler) buildCreateOutputLabelJob(data *WorkflowData, mainJobName str // Pass the max limit steps = append(steps, fmt.Sprintf(" GITHUB_AW_LABELS_MAX_COUNT: %d\n", maxCount)) + // Pass the target configuration + if data.SafeOutputs.AddLabels != nil && data.SafeOutputs.AddLabels.Target != "" { + steps = append(steps, fmt.Sprintf(" GITHUB_AW_LABELS_TARGET: %q\n", data.SafeOutputs.AddLabels.Target)) + } + // Pass the staged flag if it's set to true if data.SafeOutputs.Staged != nil && *data.SafeOutputs.Staged { steps = append(steps, " GITHUB_AW_SAFE_OUTPUTS_STAGED: \"true\"\n") @@ -71,23 +77,18 @@ func (c *Compiler) buildCreateOutputLabelJob(data *WorkflowData, mainJobName str "labels_added": "${{ steps.add_labels.outputs.labels_added }}", } - // Determine the job condition for command workflows - var baseCondition = "github.event.issue.number || github.event.pull_request.number" // Only run in issue or PR context - var jobCondition string - if data.Command != "" { - // Build the command trigger condition - commandCondition := buildCommandOnlyCondition(data.Command) - commandConditionStr := commandCondition.Render() - // Combine command condition with base condition using AND - jobCondition = fmt.Sprintf("(%s) && (%s)", commandConditionStr, baseCondition) - } else { - // No command trigger, just use the base condition - jobCondition = baseCondition + var jobCondition = BuildSafeOutputType("add-labels") + if data.SafeOutputs.AddLabels == nil || data.SafeOutputs.AddLabels.Target == "" { + eventCondition := buildOr( + BuildPropertyAccess("github.event.issue.number"), + BuildPropertyAccess("github.event.pull_request.number"), + ) + jobCondition = buildAnd(jobCondition, eventCondition) } job := &Job{ Name: "add_labels", - If: jobCondition, + If: jobCondition.Render(), RunsOn: c.formatSafeOutputsRunsOn(data.SafeOutputs), Permissions: "permissions:\n contents: read\n issues: write\n pull-requests: write", TimeoutMinutes: 10, // 10-minute timeout as required diff --git a/pkg/workflow/command.go b/pkg/workflow/command.go index ef884b824e7..a93cd6d675f 100644 --- a/pkg/workflow/command.go +++ b/pkg/workflow/command.go @@ -2,36 +2,6 @@ package workflow import "fmt" -// buildCommandOnlyCondition creates a condition that only applies to command mentions in comment-related events -// Unlike buildEventAwareCommandCondition, this does NOT allow non-comment events to pass through -func buildCommandOnlyCondition(commandName string) ConditionNode { - // Define the command condition using proper expression nodes - commandText := fmt.Sprintf("/%s", commandName) - - // Build command checks for different content sources using expression nodes - issueBodyCheck := BuildContains( - BuildPropertyAccess("github.event.issue.body"), - BuildStringLiteral(commandText), - ) - commentBodyCheck := BuildContains( - BuildPropertyAccess("github.event.comment.body"), - BuildStringLiteral(commandText), - ) - prBodyCheck := BuildContains( - BuildPropertyAccess("github.event.pull_request.body"), - BuildStringLiteral(commandText), - ) - - // Combine all command checks with OR - only true when command is mentioned - return &DisjunctionNode{ - Terms: []ConditionNode{ - issueBodyCheck, - commentBodyCheck, - prBodyCheck, - }, - } -} - // buildEventAwareCommandCondition creates a condition that only applies command checks to comment-related events func buildEventAwareCommandCondition(commandName string, hasOtherEvents bool) ConditionNode { // Define the command condition using proper expression nodes diff --git a/pkg/workflow/compile_test.go b/pkg/workflow/compile_test.go index f4d806b6ab5..489f49496f8 100644 --- a/pkg/workflow/compile_test.go +++ b/pkg/workflow/compile_test.go @@ -529,7 +529,7 @@ safe-outputs: # Test Output Issue Comment Job Generation -This workflow tests the create_issue_comment job generation. +This workflow tests the add_comment job generation. ` testFile := filepath.Join(tmpDir, "test-output-issue-comment.md") @@ -554,33 +554,46 @@ This workflow tests the create_issue_comment job generation. lockContent := string(content) - // Verify create_issue_comment job exists - if !strings.Contains(lockContent, "create_issue_comment:") { - t.Error("Expected 'create_issue_comment' job to be in generated workflow") + // Verify add_comment job exists + if !strings.Contains(lockContent, "add_comment:") { + t.Error("Expected 'add_comment' job to be in generated workflow") } // Verify job properties if !strings.Contains(lockContent, "timeout-minutes: 10") { - t.Error("Expected 10-minute timeout in create_issue_comment job") + t.Error("Expected 10-minute timeout in add_comment job") } if !strings.Contains(lockContent, "permissions:\n contents: read\n issues: write\n pull-requests: write") { - t.Error("Expected correct permissions in create_issue_comment job") + t.Error("Expected correct permissions in add_comment job") } // Verify the job uses github-script if !strings.Contains(lockContent, "uses: actions/github-script@v8") { - t.Error("Expected github-script action to be used in create_issue_comment job") + t.Error("Expected github-script action to be used in add_comment job") } - // Verify job has conditional execution - if !strings.Contains(lockContent, "if: github.event.issue.number || github.event.pull_request.number") { - t.Error("Expected create_issue_comment job to have conditional execution") + // Verify job has conditional execution using BuildSafeOutputType combined with base condition + expectedConditionParts := []string{ + "always()", + "contains(needs.agent.outputs.output_types, 'add-comment')", + "github.event.issue.number", + "github.event.pull_request.number", + } + conditionFound := true + for _, part := range expectedConditionParts { + if !strings.Contains(lockContent, part) { + conditionFound = false + break + } + } + if !conditionFound { + t.Error("Expected add_comment job to have conditional execution with always()") } // Verify job dependencies if !strings.Contains(lockContent, "needs: agent") { - t.Error("Expected create_issue_comment job to depend on main job") + t.Error("Expected add_comment job to depend on main job") } // Verify JavaScript content includes environment variable for agent output @@ -638,14 +651,27 @@ This workflow tests that issue comment job is skipped for non-issue/PR events. lockContent := string(content) - // Verify create_issue_comment job exists (it should be generated regardless of trigger) - if !strings.Contains(lockContent, "create_issue_comment:") { - t.Error("Expected 'create_issue_comment' job to be in generated workflow") + // Verify add_comment job exists (it should be generated regardless of trigger) + if !strings.Contains(lockContent, "add_comment:") { + t.Error("Expected 'add_comment' job to be in generated workflow") } - // Verify job has conditional execution to skip when not in issue/PR context - if !strings.Contains(lockContent, "if: github.event.issue.number || github.event.pull_request.number") { - t.Error("Expected create_issue_comment job to have conditional execution for skipping") + // Verify job has conditional execution using BuildSafeOutputType combined with base condition + expectedConditionParts := []string{ + "always()", + "contains(needs.agent.outputs.output_types, 'add-comment')", + "github.event.issue.number", + "github.event.pull_request.number", + } + conditionFound := true + for _, part := range expectedConditionParts { + if !strings.Contains(lockContent, part) { + conditionFound = false + break + } + } + if !conditionFound { + t.Error("Expected add_comment job to have conditional execution with always() for skipping") } // t.Logf("Generated workflow content:\n%s", lockContent) @@ -1116,12 +1142,23 @@ This workflow tests the add_labels job generation. t.Error("Expected github-script action to be used in add_labels job") } - // Verify job has conditional execution - if !strings.Contains(lockContent, "if: github.event.issue.number || github.event.pull_request.number") { - t.Error("Expected add_labels job to have conditional execution") + // Verify job has conditional execution using BuildSafeOutputType combined with base condition + expectedConditionParts := []string{ + "always()", + "contains(needs.agent.outputs.output_types, 'add-labels')", + "github.event.issue.number", + "github.event.pull_request.number", + } + conditionFound := true + for _, part := range expectedConditionParts { + if !strings.Contains(lockContent, part) { + conditionFound = false + break + } + } + if !conditionFound { + t.Error("Expected add_labels job to have conditional execution with always()") } - - // Verify job dependencies if !strings.Contains(lockContent, "needs: agent") { t.Error("Expected add_labels job to depend on main job") } @@ -1192,9 +1229,22 @@ Write your labels to ${{ env.GITHUB_AW_SAFE_OUTPUTS }}, one per line. } lockContent := string(lockBytes) - // Verify job has conditional execution - if !strings.Contains(lockContent, "if: github.event.issue.number || github.event.pull_request.number") { - t.Error("Expected add_labels job to have conditional execution") + // Verify job has conditional execution using BuildSafeOutputType combined with base condition + expectedConditionParts := []string{ + "always()", + "contains(needs.agent.outputs.output_types, 'add-labels')", + "github.event.issue.number", + "github.event.pull_request.number", + } + conditionFound := true + for _, part := range expectedConditionParts { + if !strings.Contains(lockContent, part) { + conditionFound = false + break + } + } + if !conditionFound { + t.Error("Expected add_labels job to have conditional execution with always()") } // Verify JavaScript content includes environment variables for configuration @@ -1268,9 +1318,22 @@ Write your labels to ${{ env.GITHUB_AW_SAFE_OUTPUTS }}, one per line. t.Error("Expected 'add_labels' job to be in generated workflow") } - // Verify job has conditional execution - if !strings.Contains(lockContent, "if: github.event.issue.number || github.event.pull_request.number") { - t.Error("Expected add_labels job to have conditional execution") + // Verify job has conditional execution using BuildSafeOutputType combined with base condition + expectedConditionParts := []string{ + "always()", + "contains(needs.agent.outputs.output_types, 'add-labels')", + "github.event.issue.number", + "github.event.pull_request.number", + } + conditionFound := true + for _, part := range expectedConditionParts { + if !strings.Contains(lockContent, part) { + conditionFound = false + break + } + } + if !conditionFound { + t.Error("Expected add_labels job to have conditional execution with always()") } // Verify JavaScript content includes environment variables for configuration diff --git a/pkg/workflow/compiler.go b/pkg/workflow/compiler.go index 4d9bde83676..d9b7a1fd4ad 100644 --- a/pkg/workflow/compiler.go +++ b/pkg/workflow/compiler.go @@ -1326,14 +1326,14 @@ func (c *Compiler) buildSafeOutputsJobs(data *WorkflowData, jobName string, task } } - // Build create_issue_comment job if output.add-comment is configured + // Build add_comment job if output.add-comment is configured if data.SafeOutputs.AddComments != nil { createCommentJob, err := c.buildCreateOutputAddCommentJob(data, jobName) if err != nil { - return fmt.Errorf("failed to build create_issue_comment job: %w", err) + return fmt.Errorf("failed to build add_comment job: %w", err) } if err := c.jobManager.AddJob(createCommentJob); err != nil { - return fmt.Errorf("failed to add create_issue_comment job: %w", err) + return fmt.Errorf("failed to add add_comment job: %w", err) } } diff --git a/pkg/workflow/create_code_scanning_alert.go b/pkg/workflow/create_code_scanning_alert.go index 9ead3461e03..763d18dc2ce 100644 --- a/pkg/workflow/create_code_scanning_alert.go +++ b/pkg/workflow/create_code_scanning_alert.go @@ -81,17 +81,7 @@ func (c *Compiler) buildCreateOutputCodeScanningAlertJob(data *WorkflowData, mai "codeql_uploaded": "${{ steps.create_code_scanning_alert.outputs.codeql_uploaded }}", } - // Build job condition - repository security advisories can run in any context unlike PR review comments - var jobCondition string - if data.Command != "" { - // Build the command trigger condition - commandCondition := buildCommandOnlyCondition(data.Command) - commandConditionStr := commandCondition.Render() - jobCondition = commandConditionStr - } else { - // No specific condition needed - repository security advisories can run anytime - jobCondition = "" - } + jobCondition := BuildSafeOutputType("create-code-scanning-alert").Render() job := &Job{ Name: "create_code_scanning_alert", diff --git a/pkg/workflow/create_discussion.go b/pkg/workflow/create_discussion.go index baa9ffc2058..32a054ca9b7 100644 --- a/pkg/workflow/create_discussion.go +++ b/pkg/workflow/create_discussion.go @@ -90,20 +90,11 @@ func (c *Compiler) buildCreateOutputDiscussionJob(data *WorkflowData, mainJobNam "discussion_url": "${{ steps.create_discussion.outputs.discussion_url }}", } - // Determine the job condition based on command configuration - var jobCondition string - if data.Command != "" { - // Build the command trigger condition - commandCondition := buildCommandOnlyCondition(data.Command) - commandConditionStr := commandCondition.Render() - jobCondition = commandConditionStr - } else { - jobCondition = "" // No conditional execution - } + jobCondition := BuildSafeOutputType("create-discussion") job := &Job{ Name: "create_discussion", - If: jobCondition, + If: jobCondition.Render(), RunsOn: c.formatSafeOutputsRunsOn(data.SafeOutputs), Permissions: "permissions:\n contents: read\n discussions: write", TimeoutMinutes: 10, // 10-minute timeout as required diff --git a/pkg/workflow/create_issue.go b/pkg/workflow/create_issue.go index 7c415b68c39..41ce8fe5527 100644 --- a/pkg/workflow/create_issue.go +++ b/pkg/workflow/create_issue.go @@ -124,15 +124,14 @@ func (c *Compiler) buildCreateOutputIssueJob(data *WorkflowData, mainJobName str "issue_url": "${{ steps.create_issue.outputs.issue_url }}", } - // Determine the job condition for command workflows - jobCondition := BuildSafeOutputType("create-issue").Render() + jobCondition := BuildSafeOutputType("create-issue") // Set base permissions permissions := "permissions:\n contents: read\n issues: write" job := &Job{ Name: "create_issue", - If: jobCondition, + If: jobCondition.Render(), RunsOn: c.formatSafeOutputsRunsOn(data.SafeOutputs), Permissions: permissions, TimeoutMinutes: 10, // 10-minute timeout as required diff --git a/pkg/workflow/create_pr_review_comment.go b/pkg/workflow/create_pr_review_comment.go index 1f7419d84fd..dbbc47a0959 100644 --- a/pkg/workflow/create_pr_review_comment.go +++ b/pkg/workflow/create_pr_review_comment.go @@ -52,26 +52,23 @@ func (c *Compiler) buildCreateOutputPullRequestReviewCommentJob(data *WorkflowDa "review_comment_url": "${{ steps.create_pr_review_comment.outputs.review_comment_url }}", } - // We only run in pull request context, Note that in pull request comments only github.event.issue.pull_request is set. - baseCondition := "(github.event.issue.number && github.event.issue.pull_request) || github.event.pull_request" - - // If this is a command workflow, combine the command trigger condition with the base condition - var jobCondition string - if data.Command != "" { - // Build the command trigger condition - commandCondition := buildCommandOnlyCondition(data.Command) - commandConditionStr := commandCondition.Render() - - // Combine command condition with base condition using AND - jobCondition = fmt.Sprintf("(%s) && (%s)", commandConditionStr, baseCondition) - } else { - // No command trigger, just use the base condition - jobCondition = baseCondition + safeOutputCondition := BuildSafeOutputType("create-pull-request-review-comment") + issueWithPR := &AndNode{ + Left: &ExpressionNode{Expression: "github.event.issue.number"}, + Right: &ExpressionNode{Expression: "github.event.issue.pull_request"}, + } + baseCondition := &OrNode{ + Left: issueWithPR, + Right: &ExpressionNode{Expression: "github.event.pull_request"}, + } + jobCondition := &AndNode{ + Left: safeOutputCondition, + Right: baseCondition, } job := &Job{ Name: "create_pr_review_comment", - If: jobCondition, + If: jobCondition.Render(), RunsOn: c.formatSafeOutputsRunsOn(data.SafeOutputs), Permissions: "permissions:\n contents: read\n pull-requests: write", TimeoutMinutes: 10, // 10-minute timeout as required diff --git a/pkg/workflow/create_pr_review_comment_test.go b/pkg/workflow/create_pr_review_comment_test.go index 1f05c5e065f..e2725ee7a0e 100644 --- a/pkg/workflow/create_pr_review_comment_test.go +++ b/pkg/workflow/create_pr_review_comment_test.go @@ -242,9 +242,23 @@ This workflow tests job generation for PR review comments. t.Error("Expected create_pr_review_comment job to be generated") } - // Verify job condition is correct for PR context - if !strings.Contains(workflowContent, "if: (github.event.issue.number && github.event.issue.pull_request) || github.event.pull_request") { - t.Error("Expected job condition to check for pull request context") + // Verify job condition uses BuildSafeOutputType combined with pull request context + expectedConditionParts := []string{ + "always()", + "contains(needs.agent.outputs.output_types, 'create-pull-request-review-comment')", + "github.event.issue.number", + "github.event.issue.pull_request", + "github.event.pull_request", + } + conditionFound := true + for _, part := range expectedConditionParts { + if !strings.Contains(workflowContent, part) { + conditionFound = false + break + } + } + if !conditionFound { + t.Error("Expected job condition to check for pull request context with always()") } // Verify correct permissions are set diff --git a/pkg/workflow/create_pull_request.go b/pkg/workflow/create_pull_request.go index fcbf02cf4f1..ac624dcb2c5 100644 --- a/pkg/workflow/create_pull_request.go +++ b/pkg/workflow/create_pull_request.go @@ -107,12 +107,11 @@ func (c *Compiler) buildCreateOutputPullRequestJob(data *WorkflowData, mainJobNa "fallback_used": "${{ steps.create_pull_request.outputs.fallback_used }}", } - // Determine the job condition for command workflows - jobCondition := BuildSafeOutputType("create-pull-request").Render() + jobCondition := BuildSafeOutputType("create-pull-request") job := &Job{ Name: "create_pull_request", - If: jobCondition, + If: jobCondition.Render(), RunsOn: c.formatSafeOutputsRunsOn(data.SafeOutputs), Permissions: "permissions:\n contents: write\n issues: write\n pull-requests: write", TimeoutMinutes: 10, // 10-minute timeout as required diff --git a/pkg/workflow/expressions.go b/pkg/workflow/expressions.go index ddf26c0cd9f..bd9eef522f3 100644 --- a/pkg/workflow/expressions.go +++ b/pkg/workflow/expressions.go @@ -205,6 +205,14 @@ func buildConditionTree(existingCondition string, draftCondition string) Conditi return &AndNode{Left: existingNode, Right: draftNode} } +func buildOr(left ConditionNode, right ConditionNode) ConditionNode { + return &OrNode{Left: left, Right: right} +} + +func buildAnd(left ConditionNode, right ConditionNode) ConditionNode { + return &AndNode{Left: left, Right: right} +} + // buildReactionCondition creates a condition tree for the add_reaction job func buildReactionCondition() ConditionNode { // Build a list of event types that should trigger reactions using the new expression nodes @@ -305,10 +313,15 @@ func BuildNotFromFork() *ComparisonNode { } func BuildSafeOutputType(outputType string) ConditionNode { - return BuildFunctionCall("contains", + alwaysFunc := BuildFunctionCall("always") + containsFunc := BuildFunctionCall("contains", BuildPropertyAccess(fmt.Sprintf("needs.%s.outputs.output_types", constants.AgentJobName)), BuildStringLiteral(outputType), ) + return &AndNode{ + Left: alwaysFunc, + Right: containsFunc, + } } // BuildFromAllowedForks creates a condition to check if a pull request is from an allowed fork diff --git a/pkg/workflow/missing_tool.go b/pkg/workflow/missing_tool.go index 0c8c493ec56..f0863e7c1e7 100644 --- a/pkg/workflow/missing_tool.go +++ b/pkg/workflow/missing_tool.go @@ -52,11 +52,14 @@ func (c *Compiler) buildCreateOutputMissingToolJob(data *WorkflowData, mainJobNa "total_count": "${{ steps.missing_tool.outputs.total_count }}", } + // Build the job condition using BuildSafeOutputType + jobCondition := BuildSafeOutputType("missing-tool").Render() + // Create the job job := &Job{ Name: "missing_tool", RunsOn: c.formatSafeOutputsRunsOn(data.SafeOutputs), - If: "${{ always() }}", // Always run to capture missing tools + If: jobCondition, Permissions: "permissions:\n contents: read", // Only needs read access for logging TimeoutMinutes: 5, // Short timeout since it's just processing output Steps: steps, diff --git a/pkg/workflow/publish_assets.go b/pkg/workflow/publish_assets.go index 48581abf16c..75a04b1243d 100644 --- a/pkg/workflow/publish_assets.go +++ b/pkg/workflow/publish_assets.go @@ -165,23 +165,15 @@ func (c *Compiler) buildUploadAssetsJob(data *WorkflowData, mainJobName string, "branch_name": "${{ steps.upload_assets.outputs.branch_name }}", } - // Determine the job condition for command workflows - var jobCondition string - if data.Command != "" { - // Build the command trigger condition - commandCondition := buildCommandOnlyCondition(data.Command) - commandConditionStr := commandCondition.Render() - jobCondition = commandConditionStr - } else { - jobCondition = "" // No conditional execution - } + // Build the job condition using expression tree + jobCondition := BuildSafeOutputType("upload-asset") // Set base permissions permissions := "permissions:\n contents: write # Required for creating orphaned branch and pushing assets" job := &Job{ Name: "upload_assets", - If: jobCondition, + If: jobCondition.Render(), RunsOn: c.formatSafeOutputsRunsOn(data.SafeOutputs), Permissions: permissions, TimeoutMinutes: 10, // 10-minute timeout as required diff --git a/pkg/workflow/push_to_pull_request_branch.go b/pkg/workflow/push_to_pull_request_branch.go index 0598300de10..be43edb4704 100644 --- a/pkg/workflow/push_to_pull_request_branch.go +++ b/pkg/workflow/push_to_pull_request_branch.go @@ -95,39 +95,23 @@ func (c *Compiler) buildCreateOutputPushToPullRequestBranchJob(data *WorkflowDat "push_url": "${{ steps.push_to_pull_request_branch.outputs.push_url }}", } - // Determine the job condition based on target configuration - var baseCondition string - if data.SafeOutputs.PushToPullRequestBranch.Target == "*" { - // Allow pushing to any pull request - no specific context required - baseCondition = "always()" - } else { - // Default behavior: only run in pull request context, or issue context with a linked PR - baseCondition = "(github.event.issue.number && github.event.issue.pull_request) || github.event.pull_request" + safeOutputCondition := BuildSafeOutputType("push-to-pull-request-branch") + issueWithPR := &AndNode{ + Left: &ExpressionNode{Expression: "github.event.issue.number"}, + Right: &ExpressionNode{Expression: "github.event.issue.pull_request"}, } - - // If this is a command workflow, combine the command trigger condition with the base condition - var jobCondition string - if data.Command != "" { - // Build the command trigger condition - commandCondition := buildCommandOnlyCondition(data.Command) - commandConditionStr := commandCondition.Render() - - // Combine command condition with base condition using AND - if baseCondition == "always()" { - // If base condition is always(), just use the command condition - jobCondition = commandConditionStr - } else { - // Combine both conditions with AND - jobCondition = fmt.Sprintf("(%s) && (%s)", commandConditionStr, baseCondition) - } - } else { - // No command trigger, just use the base condition - jobCondition = baseCondition + baseCondition := &OrNode{ + Left: issueWithPR, + Right: &ExpressionNode{Expression: "github.event.pull_request"}, + } + jobCondition := &AndNode{ + Left: safeOutputCondition, + Right: baseCondition, } job := &Job{ Name: "push_to_pull_request_branch", - If: jobCondition, + If: jobCondition.Render(), RunsOn: c.formatSafeOutputsRunsOn(data.SafeOutputs), Permissions: "permissions:\n contents: write\n pull-requests: read\n issues: read", TimeoutMinutes: 10, // 10-minute timeout as required diff --git a/pkg/workflow/push_to_pull_request_branch_test.go b/pkg/workflow/push_to_pull_request_branch_test.go index bff569a0aa4..5700f7687a4 100644 --- a/pkg/workflow/push_to_pull_request_branch_test.go +++ b/pkg/workflow/push_to_pull_request_branch_test.go @@ -70,8 +70,23 @@ Please make changes and push them to the feature branch. t.Errorf("Generated workflow should have dependency on main job") } - // Verify conditional execution for pull request context - if !strings.Contains(lockContentStr, "if: (github.event.issue.number && github.event.issue.pull_request) || github.event.pull_request") { + // Verify conditional execution using BuildSafeOutputType combined with pull request context + expectedConditionParts := []string{ + "always()", + "contains(needs.agent.outputs.output_types, 'push-to-pull-request-branch')", + "github.event.issue.number", + "github.event.issue.pull_request", + "github.event.pull_request", + } + conditionFound := true + for _, part := range expectedConditionParts { + if !strings.Contains(lockContentStr, part) { + conditionFound = false + t.Logf("Missing condition part: %s", part) + break + } + } + if !conditionFound { t.Errorf("Generated workflow should have pull request context condition") } } @@ -280,8 +295,22 @@ This workflow has minimal push-to-pull-request-branch configuration. t.Errorf("Generated workflow should not contain target configuration when not specified") } - // Verify default conditional execution for pull request context - if !strings.Contains(lockContentStr, "if: (github.event.issue.number && github.event.issue.pull_request) || github.event.pull_request") { + // Verify default conditional execution using BuildSafeOutputType combined with pull request context + expectedConditionParts := []string{ + "always()", + "contains(needs.agent.outputs.output_types, 'push-to-pull-request-branch')", + "github.event.issue.number", + "github.event.issue.pull_request", + "github.event.pull_request", + } + conditionFound := true + for _, part := range expectedConditionParts { + if !strings.Contains(lockContentStr, part) { + conditionFound = false + break + } + } + if !conditionFound { t.Errorf("Generated workflow should have default pull request context condition") } } diff --git a/pkg/workflow/safe_outputs.go b/pkg/workflow/safe_outputs.go index 842e9c06fa2..5e8042cd4e6 100644 --- a/pkg/workflow/safe_outputs.go +++ b/pkg/workflow/safe_outputs.go @@ -330,6 +330,13 @@ func (c *Compiler) extractSafeOutputsConfig(frontmatter map[string]any) *SafeOut } } + // Parse target + if target, exists := labelsMap["target"]; exists { + if targetStr, ok := target.(string); ok { + labelConfig.Target = targetStr + } + } + config.AddLabels = labelConfig } else if labels == nil { // Handle null case: create empty config (allows any labels) diff --git a/pkg/workflow/update_issue.go b/pkg/workflow/update_issue.go index 025335b1ccb..ff129f49d4a 100644 --- a/pkg/workflow/update_issue.go +++ b/pkg/workflow/update_issue.go @@ -61,42 +61,15 @@ func (c *Compiler) buildCreateOutputUpdateIssueJob(data *WorkflowData, mainJobNa "issue_url": "${{ steps.update_issue.outputs.issue_url }}", } - // Determine the job condition based on target configuration - var baseCondition string - if data.SafeOutputs.UpdateIssues.Target == "*" { - // Allow updates to any issue - no specific context required - baseCondition = "always()" - } else if data.SafeOutputs.UpdateIssues.Target != "" { - // Explicit issue number specified - no specific context required - baseCondition = "always()" - } else { - // Default behavior: only update triggering issue - baseCondition = "github.event.issue.number" - } - - // If this is a command workflow, combine the command trigger condition with the base condition - var jobCondition string - if data.Command != "" { - // Build the command trigger condition - commandCondition := buildCommandOnlyCondition(data.Command) - commandConditionStr := commandCondition.Render() - - // Combine command condition with base condition using AND - if baseCondition == "always()" { - // If base condition is always(), just use the command condition - jobCondition = commandConditionStr - } else { - // Combine both conditions with AND - jobCondition = fmt.Sprintf("(%s) && (%s)", commandConditionStr, baseCondition) - } - } else { - // No command trigger, just use the base condition - jobCondition = baseCondition + var jobCondition = BuildSafeOutputType("update-issue") + if data.SafeOutputs.UpdateIssues != nil && data.SafeOutputs.UpdateIssues.Target == "" { + eventCondition := BuildPropertyAccess("github.event.issue.number") + jobCondition = buildAnd(jobCondition, eventCondition) } job := &Job{ Name: "update_issue", - If: jobCondition, + If: jobCondition.Render(), RunsOn: c.formatSafeOutputsRunsOn(data.SafeOutputs), Permissions: "permissions:\n contents: read\n issues: write", TimeoutMinutes: 10, // 10-minute timeout as required