diff --git a/.github/workflows/agent-performance-analyzer.lock.yml b/.github/workflows/agent-performance-analyzer.lock.yml index b521b94f41..c574a5d472 100644 --- a/.github/workflows/agent-performance-analyzer.lock.yml +++ b/.github/workflows/agent-performance-analyzer.lock.yml @@ -41,8 +41,6 @@ run-name: "Agent Performance Analyzer - Meta-Orchestrator" jobs: activation: - needs: pre_activation - if: needs.pre_activation.outputs.activated == 'true' runs-on: ubuntu-slim permissions: contents: read @@ -1849,36 +1847,6 @@ jobs: path: /tmp/gh-aw/threat-detection/detection.log if-no-files-found: ignore - pre_activation: - runs-on: ubuntu-slim - permissions: - contents: read - outputs: - activated: ${{ steps.check_membership.outputs.is_team_member == 'true' }} - steps: - - name: Checkout actions folder - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - sparse-checkout: | - actions - persist-credentials: false - - name: Setup Scripts - uses: ./actions/setup - with: - destination: /opt/gh-aw/actions - - name: Check team membership for workflow - id: check_membership - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 - env: - GH_AW_REQUIRED_ROLES: admin,maintainer,write - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - script: | - const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/opt/gh-aw/actions/check_membership.cjs'); - await main(); - push_repo_memory: needs: - agent diff --git a/.github/workflows/agent-persona-explorer.lock.yml b/.github/workflows/agent-persona-explorer.lock.yml index 9f140d878a..4ce8680612 100644 --- a/.github/workflows/agent-persona-explorer.lock.yml +++ b/.github/workflows/agent-persona-explorer.lock.yml @@ -41,8 +41,6 @@ run-name: "Agent Persona Explorer" jobs: activation: - needs: pre_activation - if: needs.pre_activation.outputs.activated == 'true' runs-on: ubuntu-slim permissions: contents: read @@ -1316,36 +1314,6 @@ jobs: path: /tmp/gh-aw/threat-detection/detection.log if-no-files-found: ignore - pre_activation: - runs-on: ubuntu-slim - permissions: - contents: read - outputs: - activated: ${{ steps.check_membership.outputs.is_team_member == 'true' }} - steps: - - name: Checkout actions folder - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - sparse-checkout: | - actions - persist-credentials: false - - name: Setup Scripts - uses: ./actions/setup - with: - destination: /opt/gh-aw/actions - - name: Check team membership for workflow - id: check_membership - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 - env: - GH_AW_REQUIRED_ROLES: admin,maintainer,write - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - script: | - const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/opt/gh-aw/actions/check_membership.cjs'); - await main(); - safe_outputs: needs: - agent diff --git a/.github/workflows/daily-issues-report.lock.yml b/.github/workflows/daily-issues-report.lock.yml index b0b89b4104..f6f6577110 100644 --- a/.github/workflows/daily-issues-report.lock.yml +++ b/.github/workflows/daily-issues-report.lock.yml @@ -45,8 +45,6 @@ run-name: "Daily Issues Report Generator" jobs: activation: - needs: pre_activation - if: needs.pre_activation.outputs.activated == 'true' runs-on: ubuntu-slim permissions: contents: read @@ -2148,36 +2146,6 @@ jobs: path: /tmp/gh-aw/threat-detection/detection.log if-no-files-found: ignore - pre_activation: - runs-on: ubuntu-slim - permissions: - contents: read - outputs: - activated: ${{ steps.check_membership.outputs.is_team_member == 'true' }} - steps: - - name: Checkout actions folder - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - sparse-checkout: | - actions - persist-credentials: false - - name: Setup Scripts - uses: ./actions/setup - with: - destination: /opt/gh-aw/actions - - name: Check team membership for workflow - id: check_membership - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 - env: - GH_AW_REQUIRED_ROLES: admin,maintainer,write - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - script: | - const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/opt/gh-aw/actions/check_membership.cjs'); - await main(); - safe_outputs: needs: - agent diff --git a/.github/workflows/daily-observability-report.lock.yml b/.github/workflows/daily-observability-report.lock.yml index bec50703c0..24a0ea2247 100644 --- a/.github/workflows/daily-observability-report.lock.yml +++ b/.github/workflows/daily-observability-report.lock.yml @@ -41,8 +41,6 @@ run-name: "Daily Observability Report for AWF Firewall and MCP Gateway" jobs: activation: - needs: pre_activation - if: needs.pre_activation.outputs.activated == 'true' runs-on: ubuntu-slim permissions: contents: read @@ -1553,36 +1551,6 @@ jobs: path: /tmp/gh-aw/threat-detection/detection.log if-no-files-found: ignore - pre_activation: - runs-on: ubuntu-slim - permissions: - contents: read - outputs: - activated: ${{ steps.check_membership.outputs.is_team_member == 'true' }} - steps: - - name: Checkout actions folder - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - sparse-checkout: | - actions - persist-credentials: false - - name: Setup Scripts - uses: ./actions/setup - with: - destination: /opt/gh-aw/actions - - name: Check team membership for workflow - id: check_membership - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 - env: - GH_AW_REQUIRED_ROLES: admin,maintainer,write - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - script: | - const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/opt/gh-aw/actions/check_membership.cjs'); - await main(); - safe_outputs: needs: - agent diff --git a/.github/workflows/metrics-collector.lock.yml b/.github/workflows/metrics-collector.lock.yml index 53905f5b3c..8c735f17a6 100644 --- a/.github/workflows/metrics-collector.lock.yml +++ b/.github/workflows/metrics-collector.lock.yml @@ -37,8 +37,6 @@ run-name: "Metrics Collector - Infrastructure Agent" jobs: activation: - needs: pre_activation - if: needs.pre_activation.outputs.activated == 'true' runs-on: ubuntu-slim permissions: contents: read @@ -770,36 +768,6 @@ jobs: /tmp/gh-aw/agent-stdio.log if-no-files-found: ignore - pre_activation: - runs-on: ubuntu-slim - permissions: - contents: read - outputs: - activated: ${{ steps.check_membership.outputs.is_team_member == 'true' }} - steps: - - name: Checkout actions folder - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - sparse-checkout: | - actions - persist-credentials: false - - name: Setup Scripts - uses: ./actions/setup - with: - destination: /opt/gh-aw/actions - - name: Check team membership for workflow - id: check_membership - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 - env: - GH_AW_REQUIRED_ROLES: admin,maintainer,write - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - script: | - const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/opt/gh-aw/actions/check_membership.cjs'); - await main(); - push_repo_memory: needs: agent if: always() diff --git a/pkg/workflow/compiler_orchestrator_workflow.go b/pkg/workflow/compiler_orchestrator_workflow.go index 26b17088c9..c414479ca3 100644 --- a/pkg/workflow/compiler_orchestrator_workflow.go +++ b/pkg/workflow/compiler_orchestrator_workflow.go @@ -329,7 +329,7 @@ func (c *Compiler) extractAdditionalConfigurations( workflowData.RepoMemoryConfig = repoMemoryConfig // Extract and process safe-inputs and safe-outputs - workflowData.Command, workflowData.CommandEvents = c.extractCommandConfig(frontmatter) + workflowData.Command, workflowData.CommandEvents = c.extractCommandConfig(frontmatter, workflowData) workflowData.Jobs = c.extractJobsFromFrontmatter(frontmatter) workflowData.Roles = c.extractRoles(frontmatter) workflowData.Bots = c.extractBots(frontmatter) diff --git a/pkg/workflow/compiler_safe_outputs.go b/pkg/workflow/compiler_safe_outputs.go index 198ec79c7a..b91fa635d6 100644 --- a/pkg/workflow/compiler_safe_outputs.go +++ b/pkg/workflow/compiler_safe_outputs.go @@ -154,7 +154,7 @@ func (c *Compiler) parseOnSection(frontmatter map[string]any, workflowData *Work // Post-process YAML to ensure cron expressions are quoted yamlStr = parser.QuoteCronExpressions(yamlStr) // Apply comment processing to filter fields (draft, forks, names) - yamlStr = c.commentOutProcessedFieldsInOnSection(yamlStr, frontmatter) + yamlStr = c.commentOutProcessedFieldsInOnSection(yamlStr, frontmatter, workflowData) // Add zizmor ignore comment if workflow_run trigger is present yamlStr = c.addZizmorIgnoreForWorkflowRun(yamlStr) // Keep "on" quoted as it's a YAML boolean keyword diff --git a/pkg/workflow/data/action_pins.json b/pkg/workflow/data/action_pins.json index 6e52975040..fb2eed827c 100644 --- a/pkg/workflow/data/action_pins.json +++ b/pkg/workflow/data/action_pins.json @@ -30,6 +30,11 @@ "version": "v5", "sha": "93cb6efe18208431cddfb8368fd83d5badbf9bfd" }, + "actions/checkout@v6": { + "repo": "actions/checkout", + "version": "v6", + "sha": "8e8c483db84b4bee98b60c0593521ed34d9990e8" + }, "actions/checkout@v6.0.2": { "repo": "actions/checkout", "version": "v6.0.2", diff --git a/pkg/workflow/error_message_quality_test.go b/pkg/workflow/error_message_quality_test.go index 1967063cca..8cd4fe6645 100644 --- a/pkg/workflow/error_message_quality_test.go +++ b/pkg/workflow/error_message_quality_test.go @@ -28,7 +28,7 @@ func TestErrorMessageQuality(t *testing.T) { "manual-approval": 123, // Wrong type }, } - _, err := c.extractManualApprovalFromOn(frontmatter) + _, err := c.extractManualApprovalFromOn(frontmatter, nil) return err }, shouldContain: []string{ @@ -45,7 +45,7 @@ func TestErrorMessageQuality(t *testing.T) { frontmatter := map[string]any{ "on": []string{"invalid"}, // Wrong type } - _, err := c.extractManualApprovalFromOn(frontmatter) + _, err := c.extractManualApprovalFromOn(frontmatter, nil) return err }, shouldContain: []string{ diff --git a/pkg/workflow/frontmatter_extraction_yaml.go b/pkg/workflow/frontmatter_extraction_yaml.go index 4c2439d35a..e1c22dd6e0 100644 --- a/pkg/workflow/frontmatter_extraction_yaml.go +++ b/pkg/workflow/frontmatter_extraction_yaml.go @@ -124,12 +124,22 @@ func (c *Compiler) extractTopLevelYAMLSection(frontmatter map[string]any, key st // commentOutProcessedFieldsInOnSection comments out draft, fork, forks, names, manual-approval, stop-after, skip-if-match, skip-if-no-match, reaction, and lock-for-agent fields in the on section // These fields are processed separately and should be commented for documentation // Exception: names fields in sections with __gh_aw_native_label_filter__ marker in frontmatter are NOT commented out -func (c *Compiler) commentOutProcessedFieldsInOnSection(yamlStr string, frontmatter map[string]any) string { +func (c *Compiler) commentOutProcessedFieldsInOnSection(yamlStr string, frontmatter map[string]any, workflowData ...*WorkflowData) string { frontmatterLog.Print("Processing 'on' section to comment out processed fields") + // Use cached On field from ParsedFrontmatter if available (when workflowData is provided) + var onValue any + var exists bool + if len(workflowData) > 0 && workflowData[0] != nil && workflowData[0].ParsedFrontmatter != nil && workflowData[0].ParsedFrontmatter.On != nil { + onValue = workflowData[0].ParsedFrontmatter.On + exists = true + } else { + onValue, exists = frontmatter["on"] + } + // Check frontmatter for native label filter markers nativeLabelFilterSections := make(map[string]bool) - if onValue, exists := frontmatter["on"]; exists { + if exists { if onMap, ok := onValue.(map[string]any); ok { for _, sectionKey := range []string{"issues", "pull_request", "discussion", "issue_comment"} { if sectionValue, hasSec := onMap[sectionKey]; hasSec { @@ -507,10 +517,20 @@ func (c *Compiler) extractExpressionFromIfString(ifString string) string { } // extractCommandConfig extracts command configuration from frontmatter including name and events -func (c *Compiler) extractCommandConfig(frontmatter map[string]any) (commandNames []string, commandEvents []string) { +func (c *Compiler) extractCommandConfig(frontmatter map[string]any, workflowData ...*WorkflowData) (commandNames []string, commandEvents []string) { + // Use cached On field from ParsedFrontmatter if available (when workflowData is provided) + var onValue any + var exists bool + if len(workflowData) > 0 && workflowData[0] != nil && workflowData[0].ParsedFrontmatter != nil && workflowData[0].ParsedFrontmatter.On != nil { + onValue = workflowData[0].ParsedFrontmatter.On + exists = true + } else { + onValue, exists = frontmatter["on"] + } + // Check new format: on.slash_command or on.slash_command.name (preferred) // Also check legacy format: on.command or on.command.name (deprecated) - if onValue, exists := frontmatter["on"]; exists { + if exists { if onMap, ok := onValue.(map[string]any); ok { var commandValue any var hasCommand bool diff --git a/pkg/workflow/manual_approval.go b/pkg/workflow/manual_approval.go index 34d9f20cb9..7c62d036d8 100644 --- a/pkg/workflow/manual_approval.go +++ b/pkg/workflow/manual_approval.go @@ -9,8 +9,17 @@ import ( var manualApprovalLog = logger.New("workflow:manual_approval") // extractManualApprovalFromOn extracts the manual-approval value from the on: section -func (c *Compiler) extractManualApprovalFromOn(frontmatter map[string]any) (string, error) { - onSection, exists := frontmatter["on"] +func (c *Compiler) extractManualApprovalFromOn(frontmatter map[string]any, workflowData *WorkflowData) (string, error) { + // Use cached On field from ParsedFrontmatter if available, otherwise fall back to map access + var onSection any + var exists bool + if workflowData != nil && workflowData.ParsedFrontmatter != nil && workflowData.ParsedFrontmatter.On != nil { + onSection = workflowData.ParsedFrontmatter.On + exists = true + } else { + onSection, exists = frontmatter["on"] + } + if !exists { manualApprovalLog.Print("No on: section found in frontmatter") return "", nil @@ -42,8 +51,8 @@ func (c *Compiler) extractManualApprovalFromOn(frontmatter map[string]any) (stri func (c *Compiler) processManualApprovalConfiguration(frontmatter map[string]any, workflowData *WorkflowData) error { manualApprovalLog.Print("Processing manual-approval configuration") - // Extract manual-approval from the on: section - manualApproval, err := c.extractManualApprovalFromOn(frontmatter) + // Extract manual-approval from the on: section (uses cached ParsedFrontmatter.On if available) + manualApproval, err := c.extractManualApprovalFromOn(frontmatter, workflowData) if err != nil { manualApprovalLog.Printf("Failed to extract manual-approval: %v", err) return err diff --git a/pkg/workflow/manual_approval_test.go b/pkg/workflow/manual_approval_test.go index 44cee7929d..c02f16ba59 100644 --- a/pkg/workflow/manual_approval_test.go +++ b/pkg/workflow/manual_approval_test.go @@ -69,7 +69,7 @@ func TestExtractManualApprovalFromOn(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { c := &Compiler{} - got, err := c.extractManualApprovalFromOn(tt.frontmatter) + got, err := c.extractManualApprovalFromOn(tt.frontmatter, nil) if (err != nil) != tt.wantErr { t.Errorf("extractManualApprovalFromOn() error = %v, wantErr %v", err, tt.wantErr) return diff --git a/pkg/workflow/role_checks.go b/pkg/workflow/role_checks.go index d400fbeec4..df693a92f5 100644 --- a/pkg/workflow/role_checks.go +++ b/pkg/workflow/role_checks.go @@ -133,8 +133,18 @@ func (c *Compiler) hasSafeEventsOnly(data *WorkflowData, frontmatter map[string] return true } + // Use cached On field from ParsedFrontmatter if available, otherwise fall back to map access + var onValue any + var exists bool + if data.ParsedFrontmatter != nil && data.ParsedFrontmatter.On != nil { + onValue = data.ParsedFrontmatter.On + exists = true + } else { + onValue, exists = frontmatter["on"] + } + // Parse the "on" section to determine events - if onValue, exists := frontmatter["on"]; exists { + if exists { if onMap, ok := onValue.(map[string]any); ok { // Check if only safe events are present hasUnsafeEvents := false @@ -210,13 +220,24 @@ func (c *Compiler) hasSafeEventsOnly(data *WorkflowData, frontmatter map[string] } // hasWorkflowRunTrigger checks if the agentic workflow's frontmatter declares a workflow_run trigger -func (c *Compiler) hasWorkflowRunTrigger(frontmatter map[string]any) bool { +// If workflowData is provided and has ParsedFrontmatter, uses the cached On field +func (c *Compiler) hasWorkflowRunTrigger(frontmatter map[string]any, workflowData ...*WorkflowData) bool { if frontmatter == nil { return false } + // Use cached On field from ParsedFrontmatter if available (when workflowData is provided) + var onValue any + var exists bool + if len(workflowData) > 0 && workflowData[0] != nil && workflowData[0].ParsedFrontmatter != nil && workflowData[0].ParsedFrontmatter.On != nil { + onValue = workflowData[0].ParsedFrontmatter.On + exists = true + } else { + onValue, exists = frontmatter["on"] + } + // Check the "on" section in frontmatter - if onValue, exists := frontmatter["on"]; exists { + if exists { // Handle map format (most common) if onMap, ok := onValue.(map[string]any); ok { _, hasWorkflowRun := onMap["workflow_run"]