From ea2f9c9c40f6d64c6b9377bd1e941074c2d988aa Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 21 May 2026 00:46:49 +0000 Subject: [PATCH 1/4] Initial plan From c225946d69cf6d5682f20c89b8cb60deb2fb8aeb Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 21 May 2026 00:55:06 +0000 Subject: [PATCH 2/4] initial plan Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- .github/workflows/contribution-check.lock.yml | 70 +++++++++++++------ .github/workflows/pr-triage-agent.lock.yml | 1 + 2 files changed, 48 insertions(+), 23 deletions(-) diff --git a/.github/workflows/contribution-check.lock.yml b/.github/workflows/contribution-check.lock.yml index 9cf41854df9..8714e69ce98 100644 --- a/.github/workflows/contribution-check.lock.yml +++ b/.github/workflows/contribution-check.lock.yml @@ -1,5 +1,5 @@ -# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"3269d4963b7380a06b954cc5a928af2b72e63d92b3e7b32ab4a6c4e1fcd506b9","compiler_version":"v0.74.8","strict":true,"agent_id":"copilot"} -# gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GH_AW_OTEL_GRAFANA_AUTHORIZATION","GH_AW_OTEL_GRAFANA_ENDPOINT","GH_AW_OTEL_SENTRY_AUTHORIZATION","GH_AW_OTEL_SENTRY_ENDPOINT","GITHUB_TOKEN"],"actions":[{"repo":"actions/checkout","sha":"de0fac2e4500dabe0009e67214ff5f5447ce83dd","version":"v6.0.2"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-node","sha":"48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e","version":"v6.4.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"},{"repo":"github/gh-aw-actions/setup","sha":"v0.74.8","version":"v0.74.8"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.25.49"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.25.49"},{"image":"ghcr.io/github/gh-aw-firewall/cli-proxy:0.25.49"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.25.49"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.9","digest":"sha256:64828b42a4482f58fab16509d7f8f495a6d97c972a98a68aff20543531ac0388","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.9@sha256:64828b42a4482f58fab16509d7f8f495a6d97c972a98a68aff20543531ac0388"},{"image":"ghcr.io/github/github-mcp-server:v1.0.4"},{"image":"node:lts-alpine","digest":"sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f","pinned_image":"node:lts-alpine@sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f"}]} +# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"3269d4963b7380a06b954cc5a928af2b72e63d92b3e7b32ab4a6c4e1fcd506b9","strict":true,"agent_id":"copilot"} +# gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GH_AW_OTEL_GRAFANA_AUTHORIZATION","GH_AW_OTEL_GRAFANA_ENDPOINT","GH_AW_OTEL_SENTRY_AUTHORIZATION","GH_AW_OTEL_SENTRY_ENDPOINT","GITHUB_TOKEN"],"actions":[{"repo":"actions/checkout","sha":"de0fac2e4500dabe0009e67214ff5f5447ce83dd","version":"v6.0.2"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-node","sha":"48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e","version":"v6.4.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.25.49"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.25.49"},{"image":"ghcr.io/github/gh-aw-firewall/cli-proxy:0.25.49"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.25.49"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.9","digest":"sha256:64828b42a4482f58fab16509d7f8f495a6d97c972a98a68aff20543531ac0388","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.9@sha256:64828b42a4482f58fab16509d7f8f495a6d97c972a98a68aff20543531ac0388"},{"image":"ghcr.io/github/github-mcp-server:v1.0.4"},{"image":"node:lts-alpine","digest":"sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f","pinned_image":"node:lts-alpine@sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f"}]} # ___ _ _ # / _ \ | | (_) # | |_| | __ _ ___ _ __ | |_ _ ___ @@ -14,7 +14,7 @@ # \ /\ / (_) | | | | ( | | | | (_) \ V V /\__ \ # \/ \/ \___/|_| |_|\_\|_| |_|\___/ \_/\_/ |___/ # -# This file was automatically generated by gh-aw (v0.74.8). DO NOT EDIT. +# This file was automatically generated by gh-aw. DO NOT EDIT. # # To update this file, edit the corresponding .md file and run: # gh aw compile @@ -46,7 +46,6 @@ # - actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 # - actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 # - actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 -# - github/gh-aw-actions/setup@v0.74.8 # # Container images used: # - ghcr.io/github/gh-aw-firewall/agent:0.25.49 @@ -60,7 +59,7 @@ name: "Contribution Check" on: schedule: - - cron: "46 */4 * * *" + - cron: "49 */4 * * *" # Friendly format: every 4 hours (scattered) workflow_dispatch: inputs: @@ -103,9 +102,16 @@ jobs: setup-trace-id: ${{ steps.setup.outputs.trace-id }} stale_lock_file_failed: ${{ steps.check-lock-file.outputs.stale_lock_file_failed == 'true' }} steps: + - name: Checkout actions folder + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + repository: github/gh-aw + sparse-checkout: | + actions + persist-credentials: false - name: Setup Scripts id: setup - uses: github/gh-aw-actions/setup@v0.74.8 + uses: ./actions/setup with: destination: ${{ runner.temp }}/gh-aw/actions job-name: ${{ github.job }} @@ -124,7 +130,6 @@ jobs: GH_AW_INFO_MODEL: ${{ vars.GH_AW_MODEL_AGENT_COPILOT || 'claude-sonnet-4.5' }} GH_AW_INFO_VERSION: "1.0.48" GH_AW_INFO_AGENT_VERSION: "1.0.48" - GH_AW_INFO_CLI_VERSION: "v0.74.8" GH_AW_INFO_WORKFLOW_NAME: "Contribution Check" GH_AW_INFO_EXPERIMENTAL: "false" GH_AW_INFO_SUPPORTS_TOOLS_ALLOWLIST: "true" @@ -155,6 +160,7 @@ jobs: sparse-checkout: | .github .agents + actions/setup .claude .codex .crush @@ -181,16 +187,6 @@ jobs: setupGlobals(core, github, context, exec, io, getOctokit); const { main } = require('${{ runner.temp }}/gh-aw/actions/check_workflow_timestamp_api.cjs'); await main(); - - name: Check compile-agentic version - uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 - env: - GH_AW_COMPILED_VERSION: "v0.74.8" - with: - script: | - const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io, getOctokit); - const { main } = require('${{ runner.temp }}/gh-aw/actions/check_version_updates.cjs'); - await main(); - name: Create prompt with built-in context env: GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt @@ -373,9 +369,16 @@ jobs: setup-span-id: ${{ steps.setup.outputs.span-id }} setup-trace-id: ${{ steps.setup.outputs.trace-id }} steps: + - name: Checkout actions folder + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + repository: github/gh-aw + sparse-checkout: | + actions + persist-credentials: false - name: Setup Scripts id: setup - uses: github/gh-aw-actions/setup@v0.74.8 + uses: ./actions/setup with: destination: ${{ runner.temp }}/gh-aw/actions job-name: ${{ github.job }} @@ -881,7 +884,7 @@ jobs: GH_AW_PHASE: agent GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }} - GH_AW_VERSION: v0.74.8 + GH_AW_VERSION: dev GH_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN || github.token }} GITHUB_API_URL: ${{ github.api_url }} GITHUB_AW: true @@ -1098,9 +1101,16 @@ jobs: tools_reported: ${{ steps.missing_tool.outputs.tools_reported }} total_count: ${{ steps.missing_tool.outputs.total_count }} steps: + - name: Checkout actions folder + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + repository: github/gh-aw + sparse-checkout: | + actions + persist-credentials: false - name: Setup Scripts id: setup - uses: github/gh-aw-actions/setup@v0.74.8 + uses: ./actions/setup with: destination: ${{ runner.temp }}/gh-aw/actions job-name: ${{ github.job }} @@ -1237,9 +1247,16 @@ jobs: detection_reason: ${{ steps.detection_conclusion.outputs.reason }} detection_success: ${{ steps.detection_conclusion.outputs.success }} steps: + - name: Checkout actions folder + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + repository: github/gh-aw + sparse-checkout: | + actions + persist-credentials: false - name: Setup Scripts id: setup - uses: github/gh-aw-actions/setup@v0.74.8 + uses: ./actions/setup with: destination: ${{ runner.temp }}/gh-aw/actions job-name: ${{ github.job }} @@ -1370,7 +1387,7 @@ jobs: COPILOT_MODEL: ${{ vars.GH_AW_MODEL_DETECTION_COPILOT || 'claude-sonnet-4.5' }} GH_AW_PHASE: detection GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt - GH_AW_VERSION: v0.74.8 + GH_AW_VERSION: dev GITHUB_API_URL: ${{ github.api_url }} GITHUB_AW: true GITHUB_COPILOT_INTEGRATION_ID: agentic-workflows @@ -1460,9 +1477,16 @@ jobs: process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: + - name: Checkout actions folder + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + repository: github/gh-aw + sparse-checkout: | + actions + persist-credentials: false - name: Setup Scripts id: setup - uses: github/gh-aw-actions/setup@v0.74.8 + uses: ./actions/setup with: destination: ${{ runner.temp }}/gh-aw/actions job-name: ${{ github.job }} diff --git a/.github/workflows/pr-triage-agent.lock.yml b/.github/workflows/pr-triage-agent.lock.yml index dd646c94bdd..2351faa29de 100644 --- a/.github/workflows/pr-triage-agent.lock.yml +++ b/.github/workflows/pr-triage-agent.lock.yml @@ -256,6 +256,7 @@ jobs: {{#runtime-import .github/workflows/shared/otlp.md}} {{#runtime-import .github/workflows/shared/github-guard-policy.md}} {{#runtime-import .github/workflows/shared/pr-code-review-config.md}} + {{#runtime-import .github/triage.md}} {{#runtime-import .github/workflows/shared/noop-reminder.md}} {{#runtime-import .github/workflows/pr-triage-agent.md}} GH_AW_PROMPT_cff59b2a1dc6993c_EOF From 171a8677e97ad6f2ce18f643caa75826751fbaba Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 21 May 2026 01:06:15 +0000 Subject: [PATCH 3/4] fix: push branch before creating fallback issue for protected-files fallback-to-issue When safe-outputs.create-pull-request is configured with protected-files: fallback-to-issue, both the bundle and am/patch transport paths were unconditionally skipping the branch push and setting manifestProtectionPushFailedError. This caused the fallback issue to render the push-failed template (with git am download instructions) instead of the normal compare-URL template. Fix: remove the unconditional skip-and-mark-failed blocks from both transport paths. The push now runs normally. If the push actually fails (e.g. missing workflows permission), the existing try/catch blocks still set manifestProtectionPushFailedError and the push-failed template still renders. Also adds manifestProtectionFallback detection into the bundle path's push-failure catch block to match the existing logic already present in the am/patch path. Tests: update the two tests that verified old incorrect behavior (expecting push skipped, patch-artifact instructions) to assert the correct behavior (push attempted, compare URL present). Add two new tests covering genuine push failure during protected-file fallback for both transports. Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- actions/setup/js/create_pull_request.cjs | 236 +++++++++--------- actions/setup/js/create_pull_request.test.cjs | 99 +++++++- 2 files changed, 205 insertions(+), 130 deletions(-) diff --git a/actions/setup/js/create_pull_request.cjs b/actions/setup/js/create_pull_request.cjs index 3031f792dde..0f60ea169c3 100644 --- a/actions/setup/js/create_pull_request.cjs +++ b/actions/setup/js/create_pull_request.cjs @@ -1576,79 +1576,81 @@ async function main(config = {}) { } // Push the commits from the bundle to the remote branch - if (manifestProtectionFallback) { - core.info("Skipping branch push because protected-files fallback-to-issue was triggered"); - manifestProtectionPushFailedError = new Error("Push skipped because protected-files fallback-to-issue was triggered"); - } else { - try { - branchName = await handleRemoteBranchCollision(branchName, preserveBranchName, { recreateRef, githubClient, owner: repoParts.owner, repo: repoParts.repo }); - - await pushSignedCommits({ - githubClient, - owner: repoParts.owner, - repo: repoParts.repo, - branch: branchName, - baseRef: `origin/${baseBranch}`, - cwd: process.cwd(), - signedCommits, - resolvedTemporaryIds, - currentRepo: itemRepo, - }); - core.info("Changes pushed to branch (from bundle)"); + try { + branchName = await handleRemoteBranchCollision(branchName, preserveBranchName, { recreateRef, githubClient, owner: repoParts.owner, repo: repoParts.repo }); + + await pushSignedCommits({ + githubClient, + owner: repoParts.owner, + repo: repoParts.repo, + branch: branchName, + baseRef: `origin/${baseBranch}`, + cwd: process.cwd(), + signedCommits, + resolvedTemporaryIds, + currentRepo: itemRepo, + }); + core.info("Changes pushed to branch (from bundle)"); - // Count new commits on PR branch relative to base + // Count new commits on PR branch relative to base + try { + const { stdout: countStr } = await exec.getExecOutput("git", ["rev-list", "--count", `origin/${baseBranch}..HEAD`]); + newCommitCount = parseInt(countStr.trim(), 10); + core.info(`${newCommitCount} new commit(s) on branch relative to origin/${baseBranch}`); + } catch { + core.info("Could not count new commits - extra empty commit will be skipped"); + } + } catch (initialPushError) { + /** @type {unknown} */ + let pushError = initialPushError; + let pushRecovered = false; + const pushErrorMessage = pushError instanceof Error ? pushError.message : String(pushError); + const isSignedMergeReplayRefusal = signedCommits && /pushSignedCommits: refusing unsigned push/.test(pushErrorMessage) && /merge commit/i.test(pushErrorMessage); + + if (isSignedMergeReplayRefusal) { + core.warning("Signed push rejected merge commit topology from bundle; rewriting branch and retrying signed push"); try { - const { stdout: countStr } = await exec.getExecOutput("git", ["rev-list", "--count", `origin/${baseBranch}..HEAD`]); - newCommitCount = parseInt(countStr.trim(), 10); - core.info(`${newCommitCount} new commit(s) on branch relative to origin/${baseBranch}`); - } catch { - core.info("Could not count new commits - extra empty commit will be skipped"); - } - } catch (initialPushError) { - /** @type {unknown} */ - let pushError = initialPushError; - let pushRecovered = false; - const pushErrorMessage = pushError instanceof Error ? pushError.message : String(pushError); - const isSignedMergeReplayRefusal = signedCommits && /pushSignedCommits: refusing unsigned push/.test(pushErrorMessage) && /merge commit/i.test(pushErrorMessage); - - if (isSignedMergeReplayRefusal) { - core.warning("Signed push rejected merge commit topology from bundle; rewriting branch and retrying signed push"); - try { - await rewriteBundleBranchAsSingleCommit(baseBranch, exec); - await pushSignedCommits({ - githubClient, - owner: repoParts.owner, - repo: repoParts.repo, - branch: branchName, - baseRef: `origin/${baseBranch}`, - cwd: process.cwd(), - signedCommits, - resolvedTemporaryIds, - currentRepo: itemRepo, - }); - core.info("Changes pushed to branch after bundle rewrite retry"); + await rewriteBundleBranchAsSingleCommit(baseBranch, exec); + await pushSignedCommits({ + githubClient, + owner: repoParts.owner, + repo: repoParts.repo, + branch: branchName, + baseRef: `origin/${baseBranch}`, + cwd: process.cwd(), + signedCommits, + resolvedTemporaryIds, + currentRepo: itemRepo, + }); + core.info("Changes pushed to branch after bundle rewrite retry"); - try { - const { stdout: countStr } = await exec.getExecOutput("git", ["rev-list", "--count", `origin/${baseBranch}..HEAD`]); - newCommitCount = parseInt(countStr.trim(), 10); - core.info(`${newCommitCount} new commit(s) on branch relative to origin/${baseBranch}`); - } catch { - core.info("Could not count new commits - extra empty commit will be skipped"); - } - pushRecovered = true; - } catch (retryPushError) { - pushError = retryPushError; + try { + const { stdout: countStr } = await exec.getExecOutput("git", ["rev-list", "--count", `origin/${baseBranch}..HEAD`]); + newCommitCount = parseInt(countStr.trim(), 10); + core.info(`${newCommitCount} new commit(s) on branch relative to origin/${baseBranch}`); + } catch { + core.info("Could not count new commits - extra empty commit will be skipped"); } + pushRecovered = true; + } catch (retryPushError) { + pushError = retryPushError; } + } - if (!pushRecovered) { - core.error(`Git push failed: ${pushError instanceof Error ? pushError.message : String(pushError)}`); - - if (!fallbackAsIssue) { - const error = `Failed to push changes: ${pushError instanceof Error ? pushError.message : String(pushError)}`; - return { success: false, error, error_type: "push_failed" }; - } - + if (!pushRecovered) { + core.error(`Git push failed: ${pushError instanceof Error ? pushError.message : String(pushError)}`); + + if (manifestProtectionFallback) { + // Push failed specifically for a protected-file modification. Don't create + // a generic push-failed issue — fall through to the manifestProtectionFallback + // block below, which will create the proper protected-file review issue with + // patch artifact download instructions (since the branch was not pushed). + core.warning("Git push failed for protected-file modification - deferring to protected-file review issue"); + manifestProtectionPushFailedError = pushError; + } else if (!fallbackAsIssue) { + const error = `Failed to push changes: ${pushError instanceof Error ? pushError.message : String(pushError)}`; + return { success: false, error, error_type: "push_failed" }; + } else { core.warning("Git push operation failed - creating fallback issue instead of pull request"); const runUrl = buildWorkflowRunUrl(context, context.repo); @@ -1864,58 +1866,54 @@ gh pr create --title '${title}' --base ${baseBranch} --head ${branchName} --repo } // Push the applied commits to the branch (with fallback to issue creation on failure) - if (manifestProtectionFallback) { - core.info("Skipping branch push because protected-files fallback-to-issue was triggered"); - manifestProtectionPushFailedError = new Error("Push skipped because protected-files fallback-to-issue was triggered"); - } else { - try { - branchName = await handleRemoteBranchCollision(branchName, preserveBranchName, { recreateRef, githubClient, owner: repoParts.owner, repo: repoParts.repo }); + try { + branchName = await handleRemoteBranchCollision(branchName, preserveBranchName, { recreateRef, githubClient, owner: repoParts.owner, repo: repoParts.repo }); - await pushSignedCommits({ - githubClient, - owner: repoParts.owner, - repo: repoParts.repo, - branch: branchName, - baseRef: `origin/${baseBranch}`, - cwd: process.cwd(), - signedCommits, - resolvedTemporaryIds, - currentRepo: itemRepo, - }); - core.info("Changes pushed to branch"); + await pushSignedCommits({ + githubClient, + owner: repoParts.owner, + repo: repoParts.repo, + branch: branchName, + baseRef: `origin/${baseBranch}`, + cwd: process.cwd(), + signedCommits, + resolvedTemporaryIds, + currentRepo: itemRepo, + }); + core.info("Changes pushed to branch"); - // Count new commits on PR branch relative to base, used to restrict - // the extra empty CI-trigger commit to exactly 1 new commit. - try { - const { stdout: countStr } = await exec.getExecOutput("git", ["rev-list", "--count", `origin/${baseBranch}..HEAD`]); - newCommitCount = parseInt(countStr.trim(), 10); - core.info(`${newCommitCount} new commit(s) on branch relative to origin/${baseBranch}`); - } catch { - // Non-fatal - newCommitCount stays 0, extra empty commit will be skipped - core.info("Could not count new commits - extra empty commit will be skipped"); - } - } catch (pushError) { - // Push failed - create fallback issue instead of PR (if fallback is enabled) - core.error(`Git push failed: ${pushError instanceof Error ? pushError.message : String(pushError)}`); - - if (manifestProtectionFallback) { - // Push failed specifically for a protected-file modification. Don't create - // a generic push-failed issue — fall through to the manifestProtectionFallback - // block below, which will create the proper protected-file review issue with - // patch artifact download instructions (since the branch was not pushed). - core.warning("Git push failed for protected-file modification - deferring to protected-file review issue"); - manifestProtectionPushFailedError = pushError; - } else if (!fallbackAsIssue) { - // Fallback is disabled - return error without creating issue - core.error("fallback-as-issue is disabled - not creating fallback issue"); - const error = `Failed to push changes: ${pushError instanceof Error ? pushError.message : String(pushError)}`; - return { - success: false, - error, - error_type: "push_failed", - }; - } else { - core.warning("Git push operation failed - creating fallback issue instead of pull request"); + // Count new commits on PR branch relative to base, used to restrict + // the extra empty CI-trigger commit to exactly 1 new commit. + try { + const { stdout: countStr } = await exec.getExecOutput("git", ["rev-list", "--count", `origin/${baseBranch}..HEAD`]); + newCommitCount = parseInt(countStr.trim(), 10); + core.info(`${newCommitCount} new commit(s) on branch relative to origin/${baseBranch}`); + } catch { + // Non-fatal - newCommitCount stays 0, extra empty commit will be skipped + core.info("Could not count new commits - extra empty commit will be skipped"); + } + } catch (pushError) { + // Push failed - create fallback issue instead of PR (if fallback is enabled) + core.error(`Git push failed: ${pushError instanceof Error ? pushError.message : String(pushError)}`); + + if (manifestProtectionFallback) { + // Push failed specifically for a protected-file modification. Don't create + // a generic push-failed issue — fall through to the manifestProtectionFallback + // block below, which will create the proper protected-file review issue with + // patch artifact download instructions (since the branch was not pushed). + core.warning("Git push failed for protected-file modification - deferring to protected-file review issue"); + manifestProtectionPushFailedError = pushError; + } else if (!fallbackAsIssue) { + // Fallback is disabled - return error without creating issue + core.error("fallback-as-issue is disabled - not creating fallback issue"); + const error = `Failed to push changes: ${pushError instanceof Error ? pushError.message : String(pushError)}`; + return { + success: false, + error, + error_type: "push_failed", + }; + } else { + core.warning("Git push operation failed - creating fallback issue instead of pull request"); const runUrl = buildWorkflowRunUrl(context, context.repo); const runId = context.runId; @@ -2004,7 +2002,6 @@ ${patchPreview}`; } } // end else (generic push-failed fallback) } - } } else { core.info("Skipping patch application (empty patch)"); @@ -2072,7 +2069,8 @@ ${patchPreview}`; } // end else (!hasBundleFile - patch path) // Protected file protection – fallback-to-issue path: - // The patch has been applied (and pushed, unless manifestProtectionPushFailedError is set). + // The patch has been applied and the branch has been pushed to origin (unless the push + // actually failed, in which case manifestProtectionPushFailedError is set). // Instead of creating a pull request, we create a review issue so a human can carefully // inspect the protected file changes before merging. // - Normal case (push succeeded): provides a GitHub compare URL to click and create the PR. diff --git a/actions/setup/js/create_pull_request.test.cjs b/actions/setup/js/create_pull_request.test.cjs index e3235f1666a..c99b367c4da 100644 --- a/actions/setup/js/create_pull_request.test.cjs +++ b/actions/setup/js/create_pull_request.test.cjs @@ -1472,7 +1472,7 @@ ${diffs} expect(result.error).toContain("protected files"); }); - it("should use patch-artifact fallback instructions when protected-files fallback skips push", async () => { + it("should push branch and use compare URL for protected-files fallback (am path)", async () => { const patchPath = writePatch(createPatchWithFiles(".github/aw/instructions.md")); const promptsDir = path.join(tempDir, "prompts"); fs.mkdirSync(promptsDir, { recursive: true }); @@ -1497,17 +1497,18 @@ ${diffs} expect(result.success).toBe(true); expect(result.fallback_used).toBe(true); expect(result.issue_number).toBe(77); - expect(pushSignedSpy).not.toHaveBeenCalled(); + // Branch push is attempted — fallback-to-issue should push the branch so the issue can link to it + expect(pushSignedSpy).toHaveBeenCalledTimes(1); expect(global.github.rest.issues.create).toHaveBeenCalledTimes(1); - expect(global.github.rest.issues.update).not.toHaveBeenCalled(); const createCall = global.github.rest.issues.create.mock.calls[0][0]; - expect(createCall.body).toContain("gh run download"); - expect(createCall.body).toContain("git am --3way"); - expect(createCall.body).not.toContain("/compare/main..."); + // Issue body should contain a compare URL, not patch-artifact download instructions + expect(createCall.body).toContain("/compare/"); + expect(createCall.body).not.toContain("gh run download"); + expect(createCall.body).not.toContain("git am --3way"); }); - it("should use patch-artifact fallback instructions for protected-files fallback in bundle transport", async () => { + it("should push branch and use compare URL for protected-files fallback (bundle transport)", async () => { const patchPath = writePatch(createPatchWithFiles(".github/aw/instructions.md")); const bundlePath = path.join(tempDir, "aw-protected.bundle"); fs.writeFileSync(bundlePath, "bundle content"); @@ -1534,13 +1535,89 @@ ${diffs} expect(result.success).toBe(true); expect(result.fallback_used).toBe(true); expect(result.issue_number).toBe(77); - expect(pushSignedSpy).not.toHaveBeenCalled(); - expect(global.github.rest.issues.update).not.toHaveBeenCalled(); + // Branch push is attempted — fallback-to-issue should push the branch so the issue can link to it + expect(pushSignedSpy).toHaveBeenCalledTimes(1); + expect(global.github.rest.issues.create).toHaveBeenCalledTimes(1); + + const createCall = global.github.rest.issues.create.mock.calls[0][0]; + // Issue body should contain a compare URL, not patch-artifact download instructions + expect(createCall.body).toContain("/compare/"); + expect(createCall.body).not.toContain("gh run download"); + expect(createCall.body).not.toContain("git am --3way"); + }); + + it("should use push-failed template when push actually fails for protected-files fallback (am path)", async () => { + const patchPath = writePatch(createPatchWithFiles(".github/aw/instructions.md")); + const promptsDir = path.join(tempDir, "prompts"); + fs.mkdirSync(promptsDir, { recursive: true }); + const templateSrc = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../md/manifest_protection_create_pr_fallback.md"); + fs.copyFileSync(templateSrc, path.join(promptsDir, "manifest_protection_create_pr_fallback.md")); + const pushFailedTemplateSrc = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../md/manifest_protection_push_failed_fallback.md"); + fs.copyFileSync(pushFailedTemplateSrc, path.join(promptsDir, "manifest_protection_push_failed_fallback.md")); + process.env.GH_AW_PROMPTS_DIR = promptsDir; + + global.github.rest.issues = { + create: vi.fn().mockResolvedValue({ data: { number: 78, html_url: "https://github.com/test-owner/test-repo/issues/78" } }), + update: vi.fn().mockResolvedValue({ data: {} }), + }; + + // Simulate push failing (e.g. missing workflows permission) + pushSignedSpy.mockRejectedValue(new Error("Push rejected: missing workflows permission")); + const { main } = require("./create_pull_request.cjs"); + const handler = await main({ + protected_path_prefixes: [".github/"], + protected_files_policy: "fallback-to-issue", + }); + const result = await handler({ patch_path: patchPath, title: "Test PR", body: "Test body", branch: "feature/protected" }, {}); + + expect(result.success).toBe(true); + expect(result.fallback_used).toBe(true); + expect(result.issue_number).toBe(78); + expect(pushSignedSpy).toHaveBeenCalledTimes(1); + expect(global.github.rest.issues.create).toHaveBeenCalledTimes(1); + // Push failed — issue body should use the push-failed template with artifact download instructions + const createCall = global.github.rest.issues.create.mock.calls[0][0]; + expect(createCall.body).toContain("gh run download"); + expect(createCall.body).not.toContain("/compare/"); + }); + + it("should use push-failed template when push actually fails for protected-files fallback (bundle transport)", async () => { + const patchPath = writePatch(createPatchWithFiles(".github/aw/instructions.md")); + const bundlePath = path.join(tempDir, "aw-protected.bundle"); + fs.writeFileSync(bundlePath, "bundle content"); + const promptsDir = path.join(tempDir, "prompts"); + fs.mkdirSync(promptsDir, { recursive: true }); + const templateSrc = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../md/manifest_protection_create_pr_fallback.md"); + fs.copyFileSync(templateSrc, path.join(promptsDir, "manifest_protection_create_pr_fallback.md")); + const pushFailedTemplateSrc = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../md/manifest_protection_push_failed_fallback.md"); + fs.copyFileSync(pushFailedTemplateSrc, path.join(promptsDir, "manifest_protection_push_failed_fallback.md")); + process.env.GH_AW_PROMPTS_DIR = promptsDir; + + global.github.rest.issues = { + create: vi.fn().mockResolvedValue({ data: { number: 78, html_url: "https://github.com/test-owner/test-repo/issues/78" } }), + update: vi.fn().mockResolvedValue({ data: {} }), + }; + + // Simulate push failing (e.g. missing workflows permission) + pushSignedSpy.mockRejectedValue(new Error("Push rejected: missing workflows permission")); + + const { main } = require("./create_pull_request.cjs"); + const handler = await main({ + protected_path_prefixes: [".github/"], + protected_files_policy: "fallback-to-issue", + }); + const result = await handler({ patch_path: patchPath, bundle_path: bundlePath, title: "Test PR", body: "Test body", branch: "feature/protected" }, {}); + + expect(result.success).toBe(true); + expect(result.fallback_used).toBe(true); + expect(result.issue_number).toBe(78); + expect(pushSignedSpy).toHaveBeenCalledTimes(1); + expect(global.github.rest.issues.create).toHaveBeenCalledTimes(1); + // Push failed — issue body should use the push-failed template with artifact download instructions const createCall = global.github.rest.issues.create.mock.calls[0][0]; expect(createCall.body).toContain("gh run download"); - expect(createCall.body).toContain("git am --3way"); - expect(createCall.body).not.toContain("/compare/main..."); + expect(createCall.body).not.toContain("/compare/"); }); }); From 743b9a15d39ee18d496b5a82d8d00e3999646210 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 21 May 2026 03:08:19 +0000 Subject: [PATCH 4/4] revert: undo unrelated lock file changes from initial plan commit Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- .github/workflows/contribution-check.lock.yml | 70 ++++++------------- .github/workflows/pr-triage-agent.lock.yml | 1 - 2 files changed, 23 insertions(+), 48 deletions(-) diff --git a/.github/workflows/contribution-check.lock.yml b/.github/workflows/contribution-check.lock.yml index 8714e69ce98..9cf41854df9 100644 --- a/.github/workflows/contribution-check.lock.yml +++ b/.github/workflows/contribution-check.lock.yml @@ -1,5 +1,5 @@ -# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"3269d4963b7380a06b954cc5a928af2b72e63d92b3e7b32ab4a6c4e1fcd506b9","strict":true,"agent_id":"copilot"} -# gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GH_AW_OTEL_GRAFANA_AUTHORIZATION","GH_AW_OTEL_GRAFANA_ENDPOINT","GH_AW_OTEL_SENTRY_AUTHORIZATION","GH_AW_OTEL_SENTRY_ENDPOINT","GITHUB_TOKEN"],"actions":[{"repo":"actions/checkout","sha":"de0fac2e4500dabe0009e67214ff5f5447ce83dd","version":"v6.0.2"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-node","sha":"48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e","version":"v6.4.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.25.49"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.25.49"},{"image":"ghcr.io/github/gh-aw-firewall/cli-proxy:0.25.49"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.25.49"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.9","digest":"sha256:64828b42a4482f58fab16509d7f8f495a6d97c972a98a68aff20543531ac0388","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.9@sha256:64828b42a4482f58fab16509d7f8f495a6d97c972a98a68aff20543531ac0388"},{"image":"ghcr.io/github/github-mcp-server:v1.0.4"},{"image":"node:lts-alpine","digest":"sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f","pinned_image":"node:lts-alpine@sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f"}]} +# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"3269d4963b7380a06b954cc5a928af2b72e63d92b3e7b32ab4a6c4e1fcd506b9","compiler_version":"v0.74.8","strict":true,"agent_id":"copilot"} +# gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GH_AW_OTEL_GRAFANA_AUTHORIZATION","GH_AW_OTEL_GRAFANA_ENDPOINT","GH_AW_OTEL_SENTRY_AUTHORIZATION","GH_AW_OTEL_SENTRY_ENDPOINT","GITHUB_TOKEN"],"actions":[{"repo":"actions/checkout","sha":"de0fac2e4500dabe0009e67214ff5f5447ce83dd","version":"v6.0.2"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-node","sha":"48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e","version":"v6.4.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"},{"repo":"github/gh-aw-actions/setup","sha":"v0.74.8","version":"v0.74.8"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.25.49"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.25.49"},{"image":"ghcr.io/github/gh-aw-firewall/cli-proxy:0.25.49"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.25.49"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.9","digest":"sha256:64828b42a4482f58fab16509d7f8f495a6d97c972a98a68aff20543531ac0388","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.9@sha256:64828b42a4482f58fab16509d7f8f495a6d97c972a98a68aff20543531ac0388"},{"image":"ghcr.io/github/github-mcp-server:v1.0.4"},{"image":"node:lts-alpine","digest":"sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f","pinned_image":"node:lts-alpine@sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f"}]} # ___ _ _ # / _ \ | | (_) # | |_| | __ _ ___ _ __ | |_ _ ___ @@ -14,7 +14,7 @@ # \ /\ / (_) | | | | ( | | | | (_) \ V V /\__ \ # \/ \/ \___/|_| |_|\_\|_| |_|\___/ \_/\_/ |___/ # -# This file was automatically generated by gh-aw. DO NOT EDIT. +# This file was automatically generated by gh-aw (v0.74.8). DO NOT EDIT. # # To update this file, edit the corresponding .md file and run: # gh aw compile @@ -46,6 +46,7 @@ # - actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 # - actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 # - actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 +# - github/gh-aw-actions/setup@v0.74.8 # # Container images used: # - ghcr.io/github/gh-aw-firewall/agent:0.25.49 @@ -59,7 +60,7 @@ name: "Contribution Check" on: schedule: - - cron: "49 */4 * * *" + - cron: "46 */4 * * *" # Friendly format: every 4 hours (scattered) workflow_dispatch: inputs: @@ -102,16 +103,9 @@ jobs: setup-trace-id: ${{ steps.setup.outputs.trace-id }} stale_lock_file_failed: ${{ steps.check-lock-file.outputs.stale_lock_file_failed == 'true' }} steps: - - name: Checkout actions folder - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - repository: github/gh-aw - sparse-checkout: | - actions - persist-credentials: false - name: Setup Scripts id: setup - uses: ./actions/setup + uses: github/gh-aw-actions/setup@v0.74.8 with: destination: ${{ runner.temp }}/gh-aw/actions job-name: ${{ github.job }} @@ -130,6 +124,7 @@ jobs: GH_AW_INFO_MODEL: ${{ vars.GH_AW_MODEL_AGENT_COPILOT || 'claude-sonnet-4.5' }} GH_AW_INFO_VERSION: "1.0.48" GH_AW_INFO_AGENT_VERSION: "1.0.48" + GH_AW_INFO_CLI_VERSION: "v0.74.8" GH_AW_INFO_WORKFLOW_NAME: "Contribution Check" GH_AW_INFO_EXPERIMENTAL: "false" GH_AW_INFO_SUPPORTS_TOOLS_ALLOWLIST: "true" @@ -160,7 +155,6 @@ jobs: sparse-checkout: | .github .agents - actions/setup .claude .codex .crush @@ -187,6 +181,16 @@ jobs: setupGlobals(core, github, context, exec, io, getOctokit); const { main } = require('${{ runner.temp }}/gh-aw/actions/check_workflow_timestamp_api.cjs'); await main(); + - name: Check compile-agentic version + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + env: + GH_AW_COMPILED_VERSION: "v0.74.8" + with: + script: | + const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io, getOctokit); + const { main } = require('${{ runner.temp }}/gh-aw/actions/check_version_updates.cjs'); + await main(); - name: Create prompt with built-in context env: GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt @@ -369,16 +373,9 @@ jobs: setup-span-id: ${{ steps.setup.outputs.span-id }} setup-trace-id: ${{ steps.setup.outputs.trace-id }} steps: - - name: Checkout actions folder - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - repository: github/gh-aw - sparse-checkout: | - actions - persist-credentials: false - name: Setup Scripts id: setup - uses: ./actions/setup + uses: github/gh-aw-actions/setup@v0.74.8 with: destination: ${{ runner.temp }}/gh-aw/actions job-name: ${{ github.job }} @@ -884,7 +881,7 @@ jobs: GH_AW_PHASE: agent GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }} - GH_AW_VERSION: dev + GH_AW_VERSION: v0.74.8 GH_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN || github.token }} GITHUB_API_URL: ${{ github.api_url }} GITHUB_AW: true @@ -1101,16 +1098,9 @@ jobs: tools_reported: ${{ steps.missing_tool.outputs.tools_reported }} total_count: ${{ steps.missing_tool.outputs.total_count }} steps: - - name: Checkout actions folder - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - repository: github/gh-aw - sparse-checkout: | - actions - persist-credentials: false - name: Setup Scripts id: setup - uses: ./actions/setup + uses: github/gh-aw-actions/setup@v0.74.8 with: destination: ${{ runner.temp }}/gh-aw/actions job-name: ${{ github.job }} @@ -1247,16 +1237,9 @@ jobs: detection_reason: ${{ steps.detection_conclusion.outputs.reason }} detection_success: ${{ steps.detection_conclusion.outputs.success }} steps: - - name: Checkout actions folder - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - repository: github/gh-aw - sparse-checkout: | - actions - persist-credentials: false - name: Setup Scripts id: setup - uses: ./actions/setup + uses: github/gh-aw-actions/setup@v0.74.8 with: destination: ${{ runner.temp }}/gh-aw/actions job-name: ${{ github.job }} @@ -1387,7 +1370,7 @@ jobs: COPILOT_MODEL: ${{ vars.GH_AW_MODEL_DETECTION_COPILOT || 'claude-sonnet-4.5' }} GH_AW_PHASE: detection GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt - GH_AW_VERSION: dev + GH_AW_VERSION: v0.74.8 GITHUB_API_URL: ${{ github.api_url }} GITHUB_AW: true GITHUB_COPILOT_INTEGRATION_ID: agentic-workflows @@ -1477,16 +1460,9 @@ jobs: process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - - name: Checkout actions folder - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - repository: github/gh-aw - sparse-checkout: | - actions - persist-credentials: false - name: Setup Scripts id: setup - uses: ./actions/setup + uses: github/gh-aw-actions/setup@v0.74.8 with: destination: ${{ runner.temp }}/gh-aw/actions job-name: ${{ github.job }} diff --git a/.github/workflows/pr-triage-agent.lock.yml b/.github/workflows/pr-triage-agent.lock.yml index 2351faa29de..dd646c94bdd 100644 --- a/.github/workflows/pr-triage-agent.lock.yml +++ b/.github/workflows/pr-triage-agent.lock.yml @@ -256,7 +256,6 @@ jobs: {{#runtime-import .github/workflows/shared/otlp.md}} {{#runtime-import .github/workflows/shared/github-guard-policy.md}} {{#runtime-import .github/workflows/shared/pr-code-review-config.md}} - {{#runtime-import .github/triage.md}} {{#runtime-import .github/workflows/shared/noop-reminder.md}} {{#runtime-import .github/workflows/pr-triage-agent.md}} GH_AW_PROMPT_cff59b2a1dc6993c_EOF