From 319ffd0188d832074fad8b596e03122a87d5391e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 25 Mar 2026 10:50:03 +0000 Subject: [PATCH 1/7] Initial plan From 463c7dbf3e1463136a67ad53a587cf6864b132ca Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 25 Mar 2026 11:04:51 +0000 Subject: [PATCH 2/7] feat: apply progressive disclosure to protected files warning messages - Wrap protected file details (file list + explanation) in
/ blocks - Generate valid GitHub URLs for protected files, formatted as markdown lists - Update all three protected files templates: - manifest_protection_push_failed_fallback.md - manifest_protection_create_pr_fallback.md - manifest_protection_push_to_pr_fallback.md - Update create_pull_request.cjs and push_to_pull_request_branch.cjs to generate file list as markdown bullet points with clickable GitHub URLs Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> Agent-Logs-Url: https://github.com/github/gh-aw/sessions/310c5718-65a3-45e1-9085-6bbd833e94b7 --- actions/setup/js/create_pull_request.cjs | 8 ++++---- actions/setup/js/push_to_pull_request_branch.cjs | 3 ++- .../md/manifest_protection_create_pr_fallback.md | 11 ++++++++--- .../md/manifest_protection_push_failed_fallback.md | 13 ++++++++++--- .../md/manifest_protection_push_to_pr_fallback.md | 9 ++++++++- 5 files changed, 32 insertions(+), 12 deletions(-) diff --git a/actions/setup/js/create_pull_request.cjs b/actions/setup/js/create_pull_request.cjs index 981db2d394b..bb64ebc9c60 100644 --- a/actions/setup/js/create_pull_request.cjs +++ b/actions/setup/js/create_pull_request.cjs @@ -977,7 +977,8 @@ ${patchPreview}`; // patch artifact download instructions instead of the compare URL. if (manifestProtectionFallback) { const allFound = manifestProtectionFallback; - const filesFormatted = allFound.map(f => `\`${f}\``).join(", "); + const githubServer = process.env.GITHUB_SERVER_URL || "https://github.com"; + const fileList = allFound.map(f => `- [${f}](${githubServer}/${repoParts.owner}/${repoParts.repo}/blob/${baseBranch}/${f})`).join("\n"); let fallbackBody; if (manifestProtectionPushFailedError) { @@ -989,7 +990,7 @@ ${patchPreview}`; fallbackBody = renderTemplateFromFile(pushFailedTemplatePath, { main_body: mainBodyContent, footer: footerContent, - files: filesFormatted, + files: fileList, run_id: String(runId), branch_name: branchName, base_branch: baseBranch, @@ -999,7 +1000,6 @@ ${patchPreview}`; }); } else { // Normal case — push succeeded, provide compare URL. - const githubServer = process.env.GITHUB_SERVER_URL || "https://github.com"; const encodedBase = baseBranch.split("/").map(encodeURIComponent).join("/"); const encodedHead = branchName.split("/").map(encodeURIComponent).join("/"); const createPrUrl = `${githubServer}/${repoParts.owner}/${repoParts.repo}/compare/${encodedBase}...${encodedHead}?expand=1&title=${encodeURIComponent(title)}`; @@ -1007,7 +1007,7 @@ ${patchPreview}`; fallbackBody = renderTemplateFromFile(templatePath, { main_body: mainBodyContent, footer: footerContent, - files: filesFormatted, + files: fileList, create_pr_url: createPrUrl, }); } diff --git a/actions/setup/js/push_to_pull_request_branch.cjs b/actions/setup/js/push_to_pull_request_branch.cjs index c8351960008..77d509eeb4d 100644 --- a/actions/setup/js/push_to_pull_request_branch.cjs +++ b/actions/setup/js/push_to_pull_request_branch.cjs @@ -397,9 +397,10 @@ async function main(config = {}) { const githubServer = process.env.GITHUB_SERVER_URL || "https://github.com"; const prUrl = `${githubServer}/${repoParts.owner}/${repoParts.repo}/pull/${pullNumber}`; const issueTitle = `[gh-aw] Protected Files: ${prTitle || `PR #${pullNumber}`}`; + const fileList = protectedFilesForFallback.map(f => `- [${f}](${githubServer}/${repoParts.owner}/${repoParts.repo}/blob/${branchName}/${f})`).join("\n"); const templatePath = `${process.env.RUNNER_TEMP}/gh-aw/prompts/manifest_protection_push_to_pr_fallback.md`; const issueBody = renderTemplateFromFile(templatePath, { - files: protectedFilesForFallback.map(f => `\`${f}\``).join(", "), + files: fileList, pull_number: pullNumber, pr_url: prUrl, run_url: runUrl, diff --git a/actions/setup/md/manifest_protection_create_pr_fallback.md b/actions/setup/md/manifest_protection_create_pr_fallback.md index ee94f7deef1..e68d6eab368 100644 --- a/actions/setup/md/manifest_protection_create_pr_fallback.md +++ b/actions/setup/md/manifest_protection_create_pr_fallback.md @@ -5,12 +5,17 @@ > [!WARNING] > 🛡️ **Protected Files** > -> This was originally intended as a pull request, but the patch modifies protected files: {files}. -> -> These files may affect project dependencies, CI/CD pipelines, or agent behaviour. **Please review the changes carefully** before creating the pull request. +> This was originally intended as a pull request, but the patch modifies protected files. These files may affect project dependencies, CI/CD pipelines, or agent behaviour. **Please review the changes carefully** before creating the pull request. > > **[Click here to create the pull request once you have reviewed the changes]({create_pr_url})** +
+🔒 Protected files + +{files} + +
+ To route changes like this to a review issue instead of blocking, configure `protected-files: fallback-to-issue` in your workflow configuration. {footer} diff --git a/actions/setup/md/manifest_protection_push_failed_fallback.md b/actions/setup/md/manifest_protection_push_failed_fallback.md index b38890d13f7..75a693f7b1f 100644 --- a/actions/setup/md/manifest_protection_push_failed_fallback.md +++ b/actions/setup/md/manifest_protection_push_failed_fallback.md @@ -5,9 +5,16 @@ > [!WARNING] > 🛡️ **Protected Files — Push Permission Denied** > -> This was originally intended as a pull request, but the patch modifies protected files: {files}. -> -> The push was rejected because GitHub Actions does not have `workflows` permission to push these changes, and is never allowed to make such changes, or other authorization being used does not have this permission. A human must create the pull request manually. +> This was originally intended as a pull request, but the patch modifies protected files. A human must create the pull request manually. + +
+🔒 Protected files + +{files} + +The push was rejected because GitHub Actions does not have `workflows` permission to push these changes, and is never allowed to make such changes, or other authorization being used does not have this permission. + +
📋 Create the pull request manually diff --git a/actions/setup/md/manifest_protection_push_to_pr_fallback.md b/actions/setup/md/manifest_protection_push_to_pr_fallback.md index 567ad7ef57e..a6c64fab09e 100644 --- a/actions/setup/md/manifest_protection_push_to_pr_fallback.md +++ b/actions/setup/md/manifest_protection_push_to_pr_fallback.md @@ -1,12 +1,19 @@ > [!WARNING] > 🛡️ **Protected Files** > -> The push to pull request branch was blocked because the patch modifies protected files: {files}. +> The push to pull request branch was blocked because the patch modifies protected files. > > **Target Pull Request:** [#{pull_number}]({pr_url}) > > **Please review the changes carefully** before pushing them to the pull request branch. These files may affect project dependencies, CI/CD pipelines, or agent behaviour. +
+🔒 Protected files + +{files} + +
+ ---
From 65ac2f8486540c8cb1f7596d1c41c5f67a025806 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 25 Mar 2026 11:06:34 +0000 Subject: [PATCH 3/7] fix: URL-encode file paths and branch names in protected file GitHub URLs Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> Agent-Logs-Url: https://github.com/github/gh-aw/sessions/310c5718-65a3-45e1-9085-6bbd833e94b7 --- actions/setup/js/create_pull_request.cjs | 4 ++-- actions/setup/js/push_to_pull_request_branch.cjs | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/actions/setup/js/create_pull_request.cjs b/actions/setup/js/create_pull_request.cjs index bb64ebc9c60..ff80cdf4d0c 100644 --- a/actions/setup/js/create_pull_request.cjs +++ b/actions/setup/js/create_pull_request.cjs @@ -978,7 +978,8 @@ ${patchPreview}`; if (manifestProtectionFallback) { const allFound = manifestProtectionFallback; const githubServer = process.env.GITHUB_SERVER_URL || "https://github.com"; - const fileList = allFound.map(f => `- [${f}](${githubServer}/${repoParts.owner}/${repoParts.repo}/blob/${baseBranch}/${f})`).join("\n"); + const encodedBase = baseBranch.split("/").map(encodeURIComponent).join("/"); + const fileList = allFound.map(f => `- [${f}](${githubServer}/${repoParts.owner}/${repoParts.repo}/blob/${encodedBase}/${f.split("/").map(encodeURIComponent).join("/")})`).join("\n"); let fallbackBody; if (manifestProtectionPushFailedError) { @@ -1000,7 +1001,6 @@ ${patchPreview}`; }); } else { // Normal case — push succeeded, provide compare URL. - const encodedBase = baseBranch.split("/").map(encodeURIComponent).join("/"); const encodedHead = branchName.split("/").map(encodeURIComponent).join("/"); const createPrUrl = `${githubServer}/${repoParts.owner}/${repoParts.repo}/compare/${encodedBase}...${encodedHead}?expand=1&title=${encodeURIComponent(title)}`; const templatePath = `${process.env.RUNNER_TEMP}/gh-aw/prompts/manifest_protection_create_pr_fallback.md`; diff --git a/actions/setup/js/push_to_pull_request_branch.cjs b/actions/setup/js/push_to_pull_request_branch.cjs index 77d509eeb4d..e07f7851f41 100644 --- a/actions/setup/js/push_to_pull_request_branch.cjs +++ b/actions/setup/js/push_to_pull_request_branch.cjs @@ -397,7 +397,8 @@ async function main(config = {}) { const githubServer = process.env.GITHUB_SERVER_URL || "https://github.com"; const prUrl = `${githubServer}/${repoParts.owner}/${repoParts.repo}/pull/${pullNumber}`; const issueTitle = `[gh-aw] Protected Files: ${prTitle || `PR #${pullNumber}`}`; - const fileList = protectedFilesForFallback.map(f => `- [${f}](${githubServer}/${repoParts.owner}/${repoParts.repo}/blob/${branchName}/${f})`).join("\n"); + const encodedBranch = branchName.split("/").map(encodeURIComponent).join("/"); + const fileList = protectedFilesForFallback.map(f => `- [${f}](${githubServer}/${repoParts.owner}/${repoParts.repo}/blob/${encodedBranch}/${f.split("/").map(encodeURIComponent).join("/")})`).join("\n"); const templatePath = `${process.env.RUNNER_TEMP}/gh-aw/prompts/manifest_protection_push_to_pr_fallback.md`; const issueBody = renderTemplateFromFile(templatePath, { files: fileList, From c53d10668da174694c26c2b95e08f635cd4c5028 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 25 Mar 2026 11:09:21 +0000 Subject: [PATCH 4/7] refactor: extract buildProtectedFileList helper to messages_core.cjs to avoid duplication Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> Agent-Logs-Url: https://github.com/github/gh-aw/sessions/310c5718-65a3-45e1-9085-6bbd833e94b7 --- actions/setup/js/create_pull_request.cjs | 4 ++-- actions/setup/js/messages_core.cjs | 20 +++++++++++++++++++ .../setup/js/push_to_pull_request_branch.cjs | 4 ++-- 3 files changed, 24 insertions(+), 4 deletions(-) diff --git a/actions/setup/js/create_pull_request.cjs b/actions/setup/js/create_pull_request.cjs index ff80cdf4d0c..0c588bceace 100644 --- a/actions/setup/js/create_pull_request.cjs +++ b/actions/setup/js/create_pull_request.cjs @@ -25,7 +25,7 @@ const { getBaseBranch } = require("./get_base_branch.cjs"); const { createAuthenticatedGitHubClient } = require("./handler_auth.cjs"); const { buildWorkflowRunUrl } = require("./workflow_metadata_helpers.cjs"); const { checkFileProtection } = require("./manifest_file_helpers.cjs"); -const { renderTemplateFromFile } = require("./messages_core.cjs"); +const { renderTemplateFromFile, buildProtectedFileList } = require("./messages_core.cjs"); const { COPILOT_REVIEWER_BOT, FAQ_CREATE_PR_PERMISSIONS_URL } = require("./constants.cjs"); const { isStagedMode } = require("./safe_output_helpers.cjs"); @@ -979,7 +979,7 @@ ${patchPreview}`; const allFound = manifestProtectionFallback; const githubServer = process.env.GITHUB_SERVER_URL || "https://github.com"; const encodedBase = baseBranch.split("/").map(encodeURIComponent).join("/"); - const fileList = allFound.map(f => `- [${f}](${githubServer}/${repoParts.owner}/${repoParts.repo}/blob/${encodedBase}/${f.split("/").map(encodeURIComponent).join("/")})`).join("\n"); + const fileList = buildProtectedFileList(allFound, githubServer, repoParts.owner, repoParts.repo, encodedBase); let fallbackBody; if (manifestProtectionPushFailedError) { diff --git a/actions/setup/js/messages_core.cjs b/actions/setup/js/messages_core.cjs index 2a29f94aec0..7c261223d40 100644 --- a/actions/setup/js/messages_core.cjs +++ b/actions/setup/js/messages_core.cjs @@ -111,9 +111,29 @@ function toSnakeCase(obj) { ); } +/** + * Build a markdown list of protected files with clickable GitHub URLs. + * Each file path segment is individually URL-encoded to handle special characters. + * @param {string[]} files - Array of file paths (may include subdirectories) + * @param {string} githubServer - GitHub server URL (e.g. "https://github.com") + * @param {string} owner - Repository owner + * @param {string} repo - Repository name + * @param {string} encodedBranch - Branch name with path segments already URL-encoded + * @returns {string} Markdown list with one `- [file](url)` entry per line + */ +function buildProtectedFileList(files, githubServer, owner, repo, encodedBranch) { + return files + .map(f => { + const encodedPath = f.split("/").map(encodeURIComponent).join("/"); + return `- [${f}](${githubServer}/${owner}/${repo}/blob/${encodedBranch}/${encodedPath})`; + }) + .join("\n"); +} + module.exports = { getMessages, renderTemplate, renderTemplateFromFile, toSnakeCase, + buildProtectedFileList, }; diff --git a/actions/setup/js/push_to_pull_request_branch.cjs b/actions/setup/js/push_to_pull_request_branch.cjs index e07f7851f41..cdb94bfd312 100644 --- a/actions/setup/js/push_to_pull_request_branch.cjs +++ b/actions/setup/js/push_to_pull_request_branch.cjs @@ -15,7 +15,7 @@ const { resolveTargetRepoConfig, resolveAndValidateRepo } = require("./repo_help const { createAuthenticatedGitHubClient } = require("./handler_auth.cjs"); const { checkFileProtection } = require("./manifest_file_helpers.cjs"); const { buildWorkflowRunUrl } = require("./workflow_metadata_helpers.cjs"); -const { renderTemplateFromFile } = require("./messages_core.cjs"); +const { renderTemplateFromFile, buildProtectedFileList } = require("./messages_core.cjs"); const { getGitAuthEnv } = require("./git_helpers.cjs"); /** @@ -398,7 +398,7 @@ async function main(config = {}) { const prUrl = `${githubServer}/${repoParts.owner}/${repoParts.repo}/pull/${pullNumber}`; const issueTitle = `[gh-aw] Protected Files: ${prTitle || `PR #${pullNumber}`}`; const encodedBranch = branchName.split("/").map(encodeURIComponent).join("/"); - const fileList = protectedFilesForFallback.map(f => `- [${f}](${githubServer}/${repoParts.owner}/${repoParts.repo}/blob/${encodedBranch}/${f.split("/").map(encodeURIComponent).join("/")})`).join("\n"); + const fileList = buildProtectedFileList(protectedFilesForFallback, githubServer, repoParts.owner, repoParts.repo, encodedBranch); const templatePath = `${process.env.RUNNER_TEMP}/gh-aw/prompts/manifest_protection_push_to_pr_fallback.md`; const issueBody = renderTemplateFromFile(templatePath, { files: fileList, From 7cf55a12dc05d69b2a2df82ded52bde1252b0207 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 25 Mar 2026 11:11:44 +0000 Subject: [PATCH 5/7] refactor: add encodePathSegments helper and simplify buildProtectedFileList API Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> Agent-Logs-Url: https://github.com/github/gh-aw/sessions/310c5718-65a3-45e1-9085-6bbd833e94b7 --- actions/setup/js/create_pull_request.cjs | 8 ++++---- actions/setup/js/messages_core.cjs | 20 +++++++++++++++---- .../setup/js/push_to_pull_request_branch.cjs | 3 +-- 3 files changed, 21 insertions(+), 10 deletions(-) diff --git a/actions/setup/js/create_pull_request.cjs b/actions/setup/js/create_pull_request.cjs index 0c588bceace..99e391e468a 100644 --- a/actions/setup/js/create_pull_request.cjs +++ b/actions/setup/js/create_pull_request.cjs @@ -25,7 +25,7 @@ const { getBaseBranch } = require("./get_base_branch.cjs"); const { createAuthenticatedGitHubClient } = require("./handler_auth.cjs"); const { buildWorkflowRunUrl } = require("./workflow_metadata_helpers.cjs"); const { checkFileProtection } = require("./manifest_file_helpers.cjs"); -const { renderTemplateFromFile, buildProtectedFileList } = require("./messages_core.cjs"); +const { renderTemplateFromFile, buildProtectedFileList, encodePathSegments } = require("./messages_core.cjs"); const { COPILOT_REVIEWER_BOT, FAQ_CREATE_PR_PERMISSIONS_URL } = require("./constants.cjs"); const { isStagedMode } = require("./safe_output_helpers.cjs"); @@ -978,8 +978,7 @@ ${patchPreview}`; if (manifestProtectionFallback) { const allFound = manifestProtectionFallback; const githubServer = process.env.GITHUB_SERVER_URL || "https://github.com"; - const encodedBase = baseBranch.split("/").map(encodeURIComponent).join("/"); - const fileList = buildProtectedFileList(allFound, githubServer, repoParts.owner, repoParts.repo, encodedBase); + const fileList = buildProtectedFileList(allFound, githubServer, repoParts.owner, repoParts.repo, baseBranch); let fallbackBody; if (manifestProtectionPushFailedError) { @@ -1001,7 +1000,8 @@ ${patchPreview}`; }); } else { // Normal case — push succeeded, provide compare URL. - const encodedHead = branchName.split("/").map(encodeURIComponent).join("/"); + const encodedBase = encodePathSegments(baseBranch); + const encodedHead = encodePathSegments(branchName); const createPrUrl = `${githubServer}/${repoParts.owner}/${repoParts.repo}/compare/${encodedBase}...${encodedHead}?expand=1&title=${encodeURIComponent(title)}`; const templatePath = `${process.env.RUNNER_TEMP}/gh-aw/prompts/manifest_protection_create_pr_fallback.md`; fallbackBody = renderTemplateFromFile(templatePath, { diff --git a/actions/setup/js/messages_core.cjs b/actions/setup/js/messages_core.cjs index 7c261223d40..6ec3cadbb96 100644 --- a/actions/setup/js/messages_core.cjs +++ b/actions/setup/js/messages_core.cjs @@ -111,20 +111,31 @@ function toSnakeCase(obj) { ); } +/** + * URL-encode each segment of a slash-separated path. + * Preserves the slash separators while encoding special characters in each segment. + * @param {string} path - A slash-separated path (e.g. branch name or file path) + * @returns {string} Path with each segment individually URL-encoded + */ +function encodePathSegments(path) { + return path.split("/").map(encodeURIComponent).join("/"); +} + /** * Build a markdown list of protected files with clickable GitHub URLs. - * Each file path segment is individually URL-encoded to handle special characters. + * Both the branch name and each file path segment are individually URL-encoded. * @param {string[]} files - Array of file paths (may include subdirectories) * @param {string} githubServer - GitHub server URL (e.g. "https://github.com") * @param {string} owner - Repository owner * @param {string} repo - Repository name - * @param {string} encodedBranch - Branch name with path segments already URL-encoded + * @param {string} branch - Branch name (will be URL-encoded internally) * @returns {string} Markdown list with one `- [file](url)` entry per line */ -function buildProtectedFileList(files, githubServer, owner, repo, encodedBranch) { +function buildProtectedFileList(files, githubServer, owner, repo, branch) { + const encodedBranch = encodePathSegments(branch); return files .map(f => { - const encodedPath = f.split("/").map(encodeURIComponent).join("/"); + const encodedPath = encodePathSegments(f); return `- [${f}](${githubServer}/${owner}/${repo}/blob/${encodedBranch}/${encodedPath})`; }) .join("\n"); @@ -135,5 +146,6 @@ module.exports = { renderTemplate, renderTemplateFromFile, toSnakeCase, + encodePathSegments, buildProtectedFileList, }; diff --git a/actions/setup/js/push_to_pull_request_branch.cjs b/actions/setup/js/push_to_pull_request_branch.cjs index cdb94bfd312..02edf8f0244 100644 --- a/actions/setup/js/push_to_pull_request_branch.cjs +++ b/actions/setup/js/push_to_pull_request_branch.cjs @@ -397,8 +397,7 @@ async function main(config = {}) { const githubServer = process.env.GITHUB_SERVER_URL || "https://github.com"; const prUrl = `${githubServer}/${repoParts.owner}/${repoParts.repo}/pull/${pullNumber}`; const issueTitle = `[gh-aw] Protected Files: ${prTitle || `PR #${pullNumber}`}`; - const encodedBranch = branchName.split("/").map(encodeURIComponent).join("/"); - const fileList = buildProtectedFileList(protectedFilesForFallback, githubServer, repoParts.owner, repoParts.repo, encodedBranch); + const fileList = buildProtectedFileList(protectedFilesForFallback, githubServer, repoParts.owner, repoParts.repo, branchName); const templatePath = `${process.env.RUNNER_TEMP}/gh-aw/prompts/manifest_protection_push_to_pr_fallback.md`; const issueBody = renderTemplateFromFile(templatePath, { files: fileList, From c064af24912d593a057d55b5afbece552405d20d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 25 Mar 2026 11:28:43 +0000 Subject: [PATCH 6/7] fix: RFC3986 URL encoding, basename-only file handling, and branch link correctness - Add encodeRFC3986URIComponent to also percent-encode !'()* chars that break Markdown link syntax - buildProtectedFileList renders basename-only entries (no slash) as code spans instead of blob links, to avoid incorrect root-path links - create_pull_request.cjs: use branchName (head branch) for file links in the normal push-succeeded case; only fall back to baseBranch when push failed Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> Agent-Logs-Url: https://github.com/github/gh-aw/sessions/3ba55709-7936-4710-9cb7-c7cfc7b4afa4 --- actions/setup/js/create_pull_request.cjs | 5 +++- actions/setup/js/messages_core.cjs | 33 +++++++++++++++++++----- 2 files changed, 31 insertions(+), 7 deletions(-) diff --git a/actions/setup/js/create_pull_request.cjs b/actions/setup/js/create_pull_request.cjs index 99e391e468a..6ddede69153 100644 --- a/actions/setup/js/create_pull_request.cjs +++ b/actions/setup/js/create_pull_request.cjs @@ -978,7 +978,10 @@ ${patchPreview}`; if (manifestProtectionFallback) { const allFound = manifestProtectionFallback; const githubServer = process.env.GITHUB_SERVER_URL || "https://github.com"; - const fileList = buildProtectedFileList(allFound, githubServer, repoParts.owner, repoParts.repo, baseBranch); + // Use head branch (branchName) for links when push succeeded; fall back to baseBranch + // for the push-failed case where the head branch is not yet on the remote. + const branchForLinks = manifestProtectionPushFailedError ? baseBranch : branchName; + const fileList = buildProtectedFileList(allFound, githubServer, repoParts.owner, repoParts.repo, branchForLinks); let fallbackBody; if (manifestProtectionPushFailedError) { diff --git a/actions/setup/js/messages_core.cjs b/actions/setup/js/messages_core.cjs index 6ec3cadbb96..ae9caac2c4c 100644 --- a/actions/setup/js/messages_core.cjs +++ b/actions/setup/js/messages_core.cjs @@ -111,32 +111,53 @@ function toSnakeCase(obj) { ); } +/** + * RFC3986-compliant encoding for individual URI components. + * Starts with encodeURIComponent and then additionally percent-encodes + * characters that are still reserved in RFC3986 (`!`, `'`, `(`, `)`, `*`). + * This prevents these characters from breaking Markdown link parsing. + * @param {string} value + * @returns {string} + */ +function encodeRFC3986URIComponent(value) { + return encodeURIComponent(value).replace(/[!'()*]/g, c => "%" + c.charCodeAt(0).toString(16).toUpperCase()); +} + /** * URL-encode each segment of a slash-separated path. - * Preserves the slash separators while encoding special characters in each segment. + * Preserves the slash separators while encoding special characters in each segment + * using RFC3986-compliant encoding to avoid breaking Markdown link syntax. * @param {string} path - A slash-separated path (e.g. branch name or file path) * @returns {string} Path with each segment individually URL-encoded */ function encodePathSegments(path) { - return path.split("/").map(encodeURIComponent).join("/"); + return path.split("/").map(encodeRFC3986URIComponent).join("/"); } /** * Build a markdown list of protected files with clickable GitHub URLs. * Both the branch name and each file path segment are individually URL-encoded. - * @param {string[]} files - Array of file paths (may include subdirectories) + * Basename-only entries (no slash) are rendered as code spans to avoid linking + * to a potentially incorrect root-level path. + * @param {string[]} files - Array of file paths or basenames * @param {string} githubServer - GitHub server URL (e.g. "https://github.com") * @param {string} owner - Repository owner * @param {string} repo - Repository name * @param {string} branch - Branch name (will be URL-encoded internally) - * @returns {string} Markdown list with one `- [file](url)` entry per line + * @returns {string} Markdown list with one entry per line */ function buildProtectedFileList(files, githubServer, owner, repo, branch) { const encodedBranch = encodePathSegments(branch); return files .map(f => { - const encodedPath = encodePathSegments(f); - return `- [${f}](${githubServer}/${owner}/${repo}/blob/${encodedBranch}/${encodedPath})`; + // If the entry looks like a full path (contains a slash), render it as a blob link. + // Otherwise, treat it as a basename-only entry (e.g. from manifest matching) and + // render it as a code span to avoid linking to a potentially incorrect root path. + if (f.includes("/")) { + const encodedPath = encodePathSegments(f); + return `- [${f}](${githubServer}/${owner}/${repo}/blob/${encodedBranch}/${encodedPath})`; + } + return `- \`${f}\``; }) .join("\n"); } From f506f724495209f97cf2e11134e73cf50b42fb7d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 25 Mar 2026 11:29:48 +0000 Subject: [PATCH 7/7] fix: use padStart(2, '0') in encodeRFC3986URIComponent for correct two-digit hex encoding Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> Agent-Logs-Url: https://github.com/github/gh-aw/sessions/3ba55709-7936-4710-9cb7-c7cfc7b4afa4 --- actions/setup/js/messages_core.cjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actions/setup/js/messages_core.cjs b/actions/setup/js/messages_core.cjs index ae9caac2c4c..ecd4fa40e58 100644 --- a/actions/setup/js/messages_core.cjs +++ b/actions/setup/js/messages_core.cjs @@ -120,7 +120,7 @@ function toSnakeCase(obj) { * @returns {string} */ function encodeRFC3986URIComponent(value) { - return encodeURIComponent(value).replace(/[!'()*]/g, c => "%" + c.charCodeAt(0).toString(16).toUpperCase()); + return encodeURIComponent(value).replace(/[!'()*]/g, c => "%" + c.charCodeAt(0).toString(16).toUpperCase().padStart(2, "0")); } /**