From 0d5fe6210f194ee9999131e83e05f3ed08e2b750 Mon Sep 17 00:00:00 2001 From: avwu99 Date: Wed, 22 Apr 2026 17:37:53 -0500 Subject: [PATCH 1/4] feat(merge-safety): Comment Matching Feature for Merge Safety Check (#778) Co-authored-by: Austin Wu --- README.md | 59 +++++++ action.yml | 3 + src/helpers/check-merge-safety.ts | 56 ++++++- src/types/generated.ts | 1 + test/helpers/check-merge-safety.test.ts | 197 ++++++++++++++++++++++++ 5 files changed, 314 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 8abe8854..e89eaa73 100644 --- a/README.md +++ b/README.md @@ -136,6 +136,65 @@ The following parameters can be used for additional control over when it is safe - `override_filter_paths`: These are the file paths that, if out of date on a PR, will prevent merge no matter what files the PR is changing - example: `override_filter_paths: package.json,package-lock.json` - `override_filter_globs`: These are glob patterns for `override_filter_paths` +- `match_comment_paths`: When set to `'true'`, enables comment path matching. This reads paths from a specially formatted PR comment and checks if the branch is behind on any of those paths. This is useful when a PR's scope extends beyond the files it directly modifies (e.g., due to transitive dependencies in a monorepo triggering tests in other packages). + +#### Comment Path Matching + +When `match_comment_paths` is enabled, the helper looks for a PR comment containing the marker `` followed by a JSON array of paths in a fenced code block: + +```markdown + +```json +["path/to/package1", "path/to/package2"] +``` +``` + +This is useful for monorepos with selective testing, where changing one package (e.g., a shared data model) triggers tests for dependent packages. Without this feature, merging could introduce bugs if the dependent packages were updated on main after the PR's tests ran. + +Example workflow integration: + +```yaml +# After determining affected paths, post them as a comment +- name: Post affected paths + uses: actions/github-script@v7 + with: + script: | + const marker = ''; + const paths = ${{ steps.get-affected-paths.outputs.paths }}; + const body = `${marker}\n\`\`\`json\n${JSON.stringify(paths)}\n\`\`\``; + + const { data: comments } = await github.rest.issues.listComments({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number + }); + const existing = comments.find(c => c.body.includes(marker)); + + if (existing) { + await github.rest.issues.updateComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: existing.id, + body + }); + } else { + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + body + }); + } + +# Then run check-merge-safety with comment path matching enabled +- uses: ExpediaGroup/github-helpers@v1 + with: + helper: check-merge-safety + paths: | + packages/package-1 + packages/package-2 + match_comment_paths: 'true' +``` ### [close-pr](.github/workflows/close-pr.yml) diff --git a/action.yml b/action.yml index 5b68ffd5..3ebf24e4 100644 --- a/action.yml +++ b/action.yml @@ -167,6 +167,9 @@ inputs: description: 'The commit message to use' required: false default: 'Automated PR creation' + match_comment_paths: + description: 'Enable comment path matching for check-merge-safety. When enabled, reads paths from a specially formatted PR comment to determine if the branch needs rebasing.' + required: false outputs: output: description: 'The output of the helper' diff --git a/src/helpers/check-merge-safety.ts b/src/helpers/check-merge-safety.ts index 88935cbc..f71ea108 100644 --- a/src/helpers/check-merge-safety.ts +++ b/src/helpers/check-merge-safety.ts @@ -25,12 +25,15 @@ import * as core from '@actions/core'; const git = simpleGit(); const maxBranchNameLength = 50; +const COMMENT_PATHS_MARKER = ''; + export class CheckMergeSafety extends HelperInputs { declare context?: string; declare paths?: string; declare ignore_globs?: string; declare override_filter_paths?: string; declare override_filter_globs?: string; + declare match_comment_paths?: string; } export const checkMergeSafety = async (inputs: CheckMergeSafety) => { @@ -147,7 +150,7 @@ const getDiff = async (compareBase: DiffRefs, compareHead: DiffRefs, basehead: s const getMergeSafetyStateAndMessage = async ( pullRequest: PullRequest, - { paths, ignore_globs, override_filter_paths, override_filter_globs }: CheckMergeSafety + { paths, ignore_globs, override_filter_paths, override_filter_globs, match_comment_paths }: CheckMergeSafety ) => { const { base: { @@ -175,6 +178,31 @@ const getMergeSafetyStateAndMessage = async ( const truncatedRef = ref.length > maxBranchNameLength ? `${ref.substring(0, maxBranchNameLength)}...` : ref; const truncatedBranchName = `${username}:${truncatedRef}`; + + if (match_comment_paths === 'true') { + const commentPaths = await getPathsFromComment(pullRequest.number); + + if (commentPaths.length) { + core.info(`Found ${commentPaths.length} paths from PR comment`); + + const outdatedCommentPaths = commentPaths.filter(commentPath => + fileNamesWhichBranchIsBehindOn.some(file => file.startsWith(commentPath + '/') || file === commentPath) + ); + + if (outdatedCommentPaths.length) { + core.error(buildErrorMessage(outdatedCommentPaths, 'comment paths', truncatedBranchName)); + const displayPaths = outdatedCommentPaths.slice(0, 3).join(', '); + const suffix = outdatedCommentPaths.length > 3 ? '...' : ''; + return { + state: 'failure', + message: `Branch is behind on paths from comment: ${displayPaths}${suffix}. Please update with ${default_branch}.` + } as const; + } + } else { + core.info('No paths found in PR comment, skipping comment path matching check'); + } + } + const globalFilesOutdatedOnBranch = override_filter_globs ? micromatch(fileNamesWhichBranchIsBehindOn, override_filter_globs.split(/[\n,]/)) : override_filter_paths @@ -223,7 +251,7 @@ const getMergeSafetyStateAndMessage = async ( } as const; }; -const buildErrorMessage = (paths: string[], pathType: 'projects' | 'global files', branchName: string) => +const buildErrorMessage = (paths: string[], pathType: 'projects' | 'global files' | 'comment paths', branchName: string) => ` The following ${pathType} are outdated on branch ${branchName} @@ -234,3 +262,27 @@ const diffErrorMessage = (basehead: string, message = '') => `Failed to generate diff for ${basehead}. Please verify SHAs are valid and try again.${message ? `\nError: ${message}` : ''}`; const buildSuccessMessage = (branchName: string) => `Branch ${branchName} is safe to merge!`; + +const getPathsFromComment = async (pullNumber: number): Promise => { + const { data: comments } = await octokit.issues.listComments({ + ...githubContext.repo, + issue_number: pullNumber + }); + + const pathsComment = comments.find(c => c.body?.includes(COMMENT_PATHS_MARKER)); + if (!pathsComment?.body) { + return []; + } + + const jsonMatch = pathsComment.body.match(/```json\n([\s\S]*?)\n```/); + if (!jsonMatch) { + return []; + } + + try { + return JSON.parse(jsonMatch[1]); + } catch { + core.warning(`Failed to parse paths from PR #${pullNumber} comment`); + return []; + } +}; diff --git a/src/types/generated.ts b/src/types/generated.ts index 7d91f6f1..a6013668 100644 --- a/src/types/generated.ts +++ b/src/types/generated.ts @@ -67,4 +67,5 @@ export class HelperInputs { declare packages?: string; declare branch_name?: string; declare commit_message?: string; + declare match_comment_paths?: string; } diff --git a/test/helpers/check-merge-safety.test.ts b/test/helpers/check-merge-safety.test.ts index 33283a20..43a314b1 100644 --- a/test/helpers/check-merge-safety.test.ts +++ b/test/helpers/check-merge-safety.test.ts @@ -515,4 +515,201 @@ describe('checkMergeSafety', () => { ...context.repo }); }); + + describe('comment path matching', () => { + const mockListCommentsWithPaths = (paths: string[]) => { + const commentBody = `\n\`\`\`json\n${JSON.stringify(paths)}\n\`\`\``; + (octokit.issues.listComments as unknown as Mock).mockResolvedValue({ + data: [{ body: commentBody }] + }); + }; + + const mockListCommentsEmpty = () => { + (octokit.issues.listComments as unknown as Mock).mockResolvedValue({ + data: [] + }); + }; + + const mockListCommentsNoMarker = () => { + (octokit.issues.listComments as unknown as Mock).mockResolvedValue({ + data: [{ body: 'Some other comment' }] + }); + }; + + it('should prevent merge when branch is behind on comment paths', async () => { + const filesOutOfDate = ['Modules/EGSharedUI/EGSharedUI_Retail/EGSharedUI_Checkout/src/file.swift']; + const changedFilesOnPr = ['Modules/EGSharedUI/EGSharedUI_DataModel/src/schema.swift']; + mockGithubRequests(filesOutOfDate, changedFilesOnPr); + mockListCommentsWithPaths([ + 'Modules/EGSharedUI/EGSharedUI_DataModel', + 'Modules/EGSharedUI/EGSharedUI_Retail/EGSharedUI_Checkout' + ]); + + await checkMergeSafety({ + paths: allProjectPaths, + match_comment_paths: 'true', + ...context.repo + }); + + expect(octokit.repos.createCommitStatus).toHaveBeenCalledWith({ + sha, + state: 'failure', + context: 'Merge Safety', + description: 'Branch is behind on paths from comment: Modules/EGSharedUI/EGSharedUI_Retail/EGSharedUI_Checkout. Please update with main.', + repo: 'repo', + owner: 'owner' + }); + expect(core.setFailed).toHaveBeenCalled(); + }); + + it('should allow merge when branch is up to date on all comment paths', async () => { + const filesOutOfDate = ['Modules/EGSharedUI/EGSharedUI_Retail/EGSharedUI_Trips/src/file.swift']; + const changedFilesOnPr = ['Modules/EGSharedUI/EGSharedUI_DataModel/src/schema.swift']; + mockGithubRequests(filesOutOfDate, changedFilesOnPr); + mockListCommentsWithPaths([ + 'Modules/EGSharedUI/EGSharedUI_DataModel', + 'Modules/EGSharedUI/EGSharedUI_Retail/EGSharedUI_Checkout' + ]); + + await checkMergeSafety({ + paths: allProjectPaths, + match_comment_paths: 'true', + ...context.repo + }); + + expect(octokit.repos.createCommitStatus).toHaveBeenCalledWith({ + sha, + state: 'success', + context: 'Merge Safety', + description: 'Branch username:some-branch-name is safe to merge!', + repo: 'repo', + owner: 'owner' + }); + expect(core.setFailed).not.toHaveBeenCalled(); + }); + + it('should skip comment path check when no comment with marker is found', async () => { + const filesOutOfDate = ['Modules/EGSharedUI/EGSharedUI_Retail/EGSharedUI_Checkout/src/file.swift']; + const changedFilesOnPr = ['Modules/EGSharedUI/EGSharedUI_DataModel/src/schema.swift']; + mockGithubRequests(filesOutOfDate, changedFilesOnPr); + mockListCommentsNoMarker(); + + await checkMergeSafety({ + paths: allProjectPaths, + match_comment_paths: 'true', + ...context.repo + }); + + expect(octokit.repos.createCommitStatus).toHaveBeenCalledWith({ + sha, + state: 'success', + context: 'Merge Safety', + description: 'Branch username:some-branch-name is safe to merge!', + repo: 'repo', + owner: 'owner' + }); + }); + + it('should skip comment path check when no comments exist', async () => { + const filesOutOfDate = ['Modules/EGSharedUI/EGSharedUI_Retail/EGSharedUI_Checkout/src/file.swift']; + const changedFilesOnPr = ['Modules/EGSharedUI/EGSharedUI_DataModel/src/schema.swift']; + mockGithubRequests(filesOutOfDate, changedFilesOnPr); + mockListCommentsEmpty(); + + await checkMergeSafety({ + paths: allProjectPaths, + match_comment_paths: 'true', + ...context.repo + }); + + expect(octokit.repos.createCommitStatus).toHaveBeenCalledWith({ + sha, + state: 'success', + context: 'Merge Safety', + description: 'Branch username:some-branch-name is safe to merge!', + repo: 'repo', + owner: 'owner' + }); + }); + + it('should not perform comment path check when match_comment_paths is not set', async () => { + const filesOutOfDate = ['Modules/EGSharedUI/EGSharedUI_Retail/EGSharedUI_Checkout/src/file.swift']; + const changedFilesOnPr = ['Modules/EGSharedUI/EGSharedUI_DataModel/src/schema.swift']; + mockGithubRequests(filesOutOfDate, changedFilesOnPr); + mockListCommentsWithPaths([ + 'Modules/EGSharedUI/EGSharedUI_DataModel', + 'Modules/EGSharedUI/EGSharedUI_Retail/EGSharedUI_Checkout' + ]); + + await checkMergeSafety({ + paths: allProjectPaths, + ...context.repo + }); + + expect(octokit.issues.listComments).not.toHaveBeenCalled(); + expect(octokit.repos.createCommitStatus).toHaveBeenCalledWith({ + sha, + state: 'success', + context: 'Merge Safety', + description: 'Branch username:some-branch-name is safe to merge!', + repo: 'repo', + owner: 'owner' + }); + }); + + it('should still check existing path overlap when comment path matching passes', async () => { + const filesOutOfDate = ['packages/package-1/src/another-file.ts']; + const changedFilesOnPr = ['packages/package-1/src/some-file.ts']; + mockGithubRequests(filesOutOfDate, changedFilesOnPr); + mockListCommentsWithPaths(['Modules/EGSharedUI/EGSharedUI_DataModel']); + + await checkMergeSafety({ + paths: allProjectPaths, + match_comment_paths: 'true', + ...context.repo + }); + + expect(octokit.repos.createCommitStatus).toHaveBeenCalledWith({ + sha, + state: 'failure', + context: 'Merge Safety', + description: 'This branch has one or more outdated projects. Please update with main.', + repo: 'repo', + owner: 'owner' + }); + }); + + it('should truncate long list of outdated comment paths in status message', async () => { + const filesOutOfDate = [ + 'Modules/EGSharedUI/EGSharedUI_Retail/EGSharedUI_Checkout/src/file.swift', + 'Modules/EGSharedUI/EGSharedUI_Retail/EGSharedUI_Trips/src/file.swift', + 'Modules/EGSharedUI/EGSharedUI_Retail/EGSharedUI_Shopping/src/file.swift', + 'Modules/EGSharedUI/EGSharedUI_Retail/EGSharedUI_Deals/src/file.swift' + ]; + const changedFilesOnPr = ['Modules/EGSharedUI/EGSharedUI_DataModel/src/schema.swift']; + mockGithubRequests(filesOutOfDate, changedFilesOnPr); + mockListCommentsWithPaths([ + 'Modules/EGSharedUI/EGSharedUI_DataModel', + 'Modules/EGSharedUI/EGSharedUI_Retail/EGSharedUI_Checkout', + 'Modules/EGSharedUI/EGSharedUI_Retail/EGSharedUI_Trips', + 'Modules/EGSharedUI/EGSharedUI_Retail/EGSharedUI_Shopping', + 'Modules/EGSharedUI/EGSharedUI_Retail/EGSharedUI_Deals' + ]); + + await checkMergeSafety({ + paths: allProjectPaths, + match_comment_paths: 'true', + ...context.repo + }); + + expect(octokit.repos.createCommitStatus).toHaveBeenCalledWith({ + sha, + state: 'failure', + context: 'Merge Safety', + description: expect.stringContaining('...'), + repo: 'repo', + owner: 'owner' + }); + }); + }); }); From eb70c3fbe218793bef5ba781b95c5e7135409817 Mon Sep 17 00:00:00 2001 From: Austin Wu Date: Wed, 22 Apr 2026 17:45:58 -0500 Subject: [PATCH 2/4] Run prettier --- README.md | 13 ++++++++----- test/helpers/check-merge-safety.test.ts | 18 +++++------------- 2 files changed, 13 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index e89eaa73..aee103ce 100644 --- a/README.md +++ b/README.md @@ -142,12 +142,15 @@ The following parameters can be used for additional control over when it is safe When `match_comment_paths` is enabled, the helper looks for a PR comment containing the marker `` followed by a JSON array of paths in a fenced code block: -```markdown +````markdown + ```json ["path/to/package1", "path/to/package2"] ``` -``` +```` + +```` This is useful for monorepos with selective testing, where changing one package (e.g., a shared data model) triggers tests for dependent packages. Without this feature, merging could introduce bugs if the dependent packages were updated on main after the PR's tests ran. @@ -162,14 +165,14 @@ Example workflow integration: const marker = ''; const paths = ${{ steps.get-affected-paths.outputs.paths }}; const body = `${marker}\n\`\`\`json\n${JSON.stringify(paths)}\n\`\`\``; - + const { data: comments } = await github.rest.issues.listComments({ owner: context.repo.owner, repo: context.repo.repo, issue_number: context.issue.number }); const existing = comments.find(c => c.body.includes(marker)); - + if (existing) { await github.rest.issues.updateComment({ owner: context.repo.owner, @@ -194,7 +197,7 @@ Example workflow integration: packages/package-1 packages/package-2 match_comment_paths: 'true' -``` +```` ### [close-pr](.github/workflows/close-pr.yml) diff --git a/test/helpers/check-merge-safety.test.ts b/test/helpers/check-merge-safety.test.ts index 43a314b1..910c9cd5 100644 --- a/test/helpers/check-merge-safety.test.ts +++ b/test/helpers/check-merge-safety.test.ts @@ -540,10 +540,7 @@ describe('checkMergeSafety', () => { const filesOutOfDate = ['Modules/EGSharedUI/EGSharedUI_Retail/EGSharedUI_Checkout/src/file.swift']; const changedFilesOnPr = ['Modules/EGSharedUI/EGSharedUI_DataModel/src/schema.swift']; mockGithubRequests(filesOutOfDate, changedFilesOnPr); - mockListCommentsWithPaths([ - 'Modules/EGSharedUI/EGSharedUI_DataModel', - 'Modules/EGSharedUI/EGSharedUI_Retail/EGSharedUI_Checkout' - ]); + mockListCommentsWithPaths(['Modules/EGSharedUI/EGSharedUI_DataModel', 'Modules/EGSharedUI/EGSharedUI_Retail/EGSharedUI_Checkout']); await checkMergeSafety({ paths: allProjectPaths, @@ -555,7 +552,8 @@ describe('checkMergeSafety', () => { sha, state: 'failure', context: 'Merge Safety', - description: 'Branch is behind on paths from comment: Modules/EGSharedUI/EGSharedUI_Retail/EGSharedUI_Checkout. Please update with main.', + description: + 'Branch is behind on paths from comment: Modules/EGSharedUI/EGSharedUI_Retail/EGSharedUI_Checkout. Please update with main.', repo: 'repo', owner: 'owner' }); @@ -566,10 +564,7 @@ describe('checkMergeSafety', () => { const filesOutOfDate = ['Modules/EGSharedUI/EGSharedUI_Retail/EGSharedUI_Trips/src/file.swift']; const changedFilesOnPr = ['Modules/EGSharedUI/EGSharedUI_DataModel/src/schema.swift']; mockGithubRequests(filesOutOfDate, changedFilesOnPr); - mockListCommentsWithPaths([ - 'Modules/EGSharedUI/EGSharedUI_DataModel', - 'Modules/EGSharedUI/EGSharedUI_Retail/EGSharedUI_Checkout' - ]); + mockListCommentsWithPaths(['Modules/EGSharedUI/EGSharedUI_DataModel', 'Modules/EGSharedUI/EGSharedUI_Retail/EGSharedUI_Checkout']); await checkMergeSafety({ paths: allProjectPaths, @@ -636,10 +631,7 @@ describe('checkMergeSafety', () => { const filesOutOfDate = ['Modules/EGSharedUI/EGSharedUI_Retail/EGSharedUI_Checkout/src/file.swift']; const changedFilesOnPr = ['Modules/EGSharedUI/EGSharedUI_DataModel/src/schema.swift']; mockGithubRequests(filesOutOfDate, changedFilesOnPr); - mockListCommentsWithPaths([ - 'Modules/EGSharedUI/EGSharedUI_DataModel', - 'Modules/EGSharedUI/EGSharedUI_Retail/EGSharedUI_Checkout' - ]); + mockListCommentsWithPaths(['Modules/EGSharedUI/EGSharedUI_DataModel', 'Modules/EGSharedUI/EGSharedUI_Retail/EGSharedUI_Checkout']); await checkMergeSafety({ paths: allProjectPaths, From 20de0e16ff7838257b7e977de0bb7920a4a464dd Mon Sep 17 00:00:00 2001 From: Austin Wu Date: Wed, 22 Apr 2026 17:53:38 -0500 Subject: [PATCH 3/4] Build and Compile --- dist/helpers/check-merge-safety.js | 50 ++++++++++++++++++++++++-- dist/helpers/check-merge-safety.js.map | 6 ++-- dist/main-8h70j5cy.js.map | 4 +-- src/helpers/check-merge-safety.ts | 8 +++-- 4 files changed, 58 insertions(+), 10 deletions(-) diff --git a/dist/helpers/check-merge-safety.js b/dist/helpers/check-merge-safety.js index 61778724..16bbe59a 100644 --- a/dist/helpers/check-merge-safety.js +++ b/dist/helpers/check-merge-safety.js @@ -27,7 +27,8 @@ import"../main-9m3k9gt0.js"; import { error, info, - setFailed + setFailed, + warning } from "../main-q70tmm6g.js"; import { __toESM @@ -38,6 +39,7 @@ var import_micromatch = __toESM(require_micromatch(), 1); var import_bluebird = __toESM(require_bluebird(), 1); var git = simpleGit(); var maxBranchNameLength = 50; +var COMMENT_PATHS_MARKER = ""; class CheckMergeSafety extends HelperInputs { } @@ -140,7 +142,7 @@ var getDiff = async (compareBase, compareHead, basehead) => { } return changedFileNames; }; -var getMergeSafetyStateAndMessage = async (pullRequest, { paths, ignore_globs, override_filter_paths, override_filter_globs }) => { +var getMergeSafetyStateAndMessage = async (pullRequest, { paths, ignore_globs, override_filter_paths, override_filter_globs, match_comment_paths }) => { const { base: { repo: { @@ -165,6 +167,24 @@ var getMergeSafetyStateAndMessage = async (pullRequest, { paths, ignore_globs, o } const truncatedRef = ref.length > maxBranchNameLength ? `${ref.substring(0, maxBranchNameLength)}...` : ref; const truncatedBranchName = `${username}:${truncatedRef}`; + if (match_comment_paths === "true") { + const commentPaths = await getPathsFromComment(pullRequest.number); + if (commentPaths.length) { + info(`Found ${commentPaths.length} paths from PR comment`); + const outdatedCommentPaths = commentPaths.filter((commentPath) => fileNamesWhichBranchIsBehindOn.some((file) => file.startsWith(commentPath + "/") || file === commentPath)); + if (outdatedCommentPaths.length) { + error(buildErrorMessage(outdatedCommentPaths, "comment paths", truncatedBranchName)); + const displayPaths = outdatedCommentPaths.slice(0, 3).join(", "); + const suffix = outdatedCommentPaths.length > 3 ? "..." : ""; + return { + state: "failure", + message: `Branch is behind on paths from comment: ${displayPaths}${suffix}. Please update with ${default_branch}.` + }; + } + } else { + info("No paths found in PR comment, skipping comment path matching check"); + } + } const globalFilesOutdatedOnBranch = override_filter_globs ? import_micromatch.default(fileNamesWhichBranchIsBehindOn, override_filter_globs.split(/[\n,]/)) : override_filter_paths ? fileNamesWhichBranchIsBehindOn.filter((changedFile) => override_filter_paths.split(/[\n,]/).includes(changedFile)) : []; if (globalFilesOutdatedOnBranch.length) { error(buildErrorMessage(globalFilesOutdatedOnBranch, "global files", truncatedBranchName)); @@ -209,9 +229,33 @@ ${paths.map((path) => `* ${path}`).join(` var diffErrorMessage = (basehead, message = "") => `Failed to generate diff for ${basehead}. Please verify SHAs are valid and try again.${message ? ` Error: ${message}` : ""}`; var buildSuccessMessage = (branchName) => `Branch ${branchName} is safe to merge!`; +var getPathsFromComment = async (pullNumber) => { + const { data: comments } = await octokit.issues.listComments({ + ...context.repo, + issue_number: pullNumber + }); + const pathsComment = comments.find((c) => c.body?.includes(COMMENT_PATHS_MARKER)); + if (!pathsComment?.body) { + return []; + } + const jsonMatch = pathsComment.body.match(/```json\n([\s\S]*?)\n```/); + if (!jsonMatch?.[1]) { + return []; + } + try { + const parsed = JSON.parse(jsonMatch[1]); + if (Array.isArray(parsed) && parsed.every((item) => typeof item === "string")) { + return parsed; + } + return []; + } catch { + warning(`Failed to parse paths from PR #${pullNumber} comment`); + return []; + } +}; export { checkMergeSafety, CheckMergeSafety }; -//# debugId=852D8F69D0103B1E64756E2164756E21 +//# debugId=8CCB3211353B88AF64756E2164756E21 diff --git a/dist/helpers/check-merge-safety.js.map b/dist/helpers/check-merge-safety.js.map index 84f9c18e..1435e6b9 100644 --- a/dist/helpers/check-merge-safety.js.map +++ b/dist/helpers/check-merge-safety.js.map @@ -2,9 +2,9 @@ "version": 3, "sources": ["../src/helpers/check-merge-safety.ts"], "sourcesContent": [ - "/*\nCopyright 2021 Expedia, Inc.\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n https://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport { HelperInputs } from '../types/generated';\nimport { context as githubContext } from '@actions/github';\nimport { simpleGit } from 'simple-git';\nimport { octokit } from '../octokit';\nimport micromatch from 'micromatch';\nimport { GithubError, PullRequest } from '../types/github';\nimport { paginateAllOpenPullRequests } from '../utils/paginate-open-pull-requests';\nimport { map } from 'bluebird';\nimport { setCommitStatus } from './set-commit-status';\nimport * as core from '@actions/core';\n\nconst git = simpleGit();\n\nconst maxBranchNameLength = 50;\nexport class CheckMergeSafety extends HelperInputs {\n declare context?: string;\n declare paths?: string;\n declare ignore_globs?: string;\n declare override_filter_paths?: string;\n declare override_filter_globs?: string;\n}\n\nexport const checkMergeSafety = async (inputs: CheckMergeSafety) => {\n const isPrWorkflow = Boolean(githubContext.issue.number);\n if (!isPrWorkflow) {\n return handlePushWorkflow(inputs);\n }\n const { data: pullRequest } = await octokit.pulls.get({ pull_number: githubContext.issue.number, ...githubContext.repo });\n\n const { state, message } = await setMergeSafetyStatus(pullRequest, inputs);\n if (state === 'failure') {\n core.setFailed(message);\n }\n};\n\nconst setMergeSafetyStatus = async (pullRequest: PullRequest, { context = 'Merge Safety', ...inputs }: CheckMergeSafety) => {\n const { state, message } = await getMergeSafetyStateAndMessage(pullRequest, inputs);\n const hasExistingFailureStatus = await checkForExistingFailureStatus(pullRequest, context);\n if (hasExistingFailureStatus && state === 'failure') {\n const {\n head: {\n ref,\n user: { login: username }\n }\n } = pullRequest;\n const truncatedRef = ref.length > maxBranchNameLength ? `${ref.substring(0, maxBranchNameLength)}...` : ref;\n const truncatedBranchName = `${username}:${truncatedRef}`;\n core.info(`Found existing failure status for ${truncatedBranchName}, skipping setting new status`);\n } else {\n await setCommitStatus({\n sha: pullRequest.head.sha,\n state,\n context,\n description: message,\n ...githubContext.repo\n });\n }\n\n return { state, message };\n};\n\nconst handlePushWorkflow = async (inputs: CheckMergeSafety) => {\n const pullRequests = await paginateAllOpenPullRequests();\n const filteredPullRequests = pullRequests.filter(({ base, draft }) => !draft && base.ref === base.repo.default_branch);\n await map(filteredPullRequests, pullRequest => setMergeSafetyStatus(pullRequest as PullRequest, inputs));\n};\n\nconst checkForExistingFailureStatus = async (pullRequest: PullRequest, context: string) => {\n const { data } = await octokit.repos.getCombinedStatusForRef({\n ...githubContext.repo,\n ref: pullRequest.head.sha\n });\n if (data.state === 'failure') {\n const existingContext = data.statuses.find(status => status.context === context);\n return Boolean(existingContext);\n }\n return false;\n};\n\nconst fetchSha = async (repoUrl: string, sha: string) => {\n try {\n await git.fetch(repoUrl, sha, { '--depth': 1 });\n core.info(`Fetched ${sha} from ${repoUrl}`);\n } catch (err) {\n core.info(`Failed to fetch ${sha} from ${repoUrl}: ${(err as GithubError).message}`);\n throw new Error(`Failed to fetch ${sha} from ${repoUrl}: ${(err as GithubError).message}`);\n }\n};\n\nconst getDiffUsingGitCommand = async (repoUrl: string, baseSha: string, headSha: string): Promise => {\n // update local repo copy\n await fetchSha(repoUrl, baseSha);\n await fetchSha(repoUrl, headSha);\n\n try {\n const diff = await git.diff(['--name-only', baseSha, headSha]);\n return (diff ?? '').split('\\n').filter(Boolean);\n } catch (err) {\n core.error(`Failed to run local git diff for ${repoUrl}: ${(err as GithubError).message}`);\n throw new Error(`Failed to run local git diff for ${repoUrl}: ${(err as GithubError).message}`);\n }\n};\n\ntype DiffRefs = PullRequest['base' | 'head'];\nconst getDiff = async (compareBase: DiffRefs, compareHead: DiffRefs, basehead: string) => {\n let changedFileNames: string[] = [];\n try {\n const { data: { files: changedFiles } = {}, status } = await octokit.repos.compareCommitsWithBasehead({\n ...githubContext.repo,\n basehead\n });\n if (status > 400) {\n throw { status };\n }\n changedFileNames = changedFiles?.map(file => file.filename) ?? [];\n } catch (err) {\n core.info(`Failed to fetch diff: ${(err as GithubError).message} Status: ${(err as GithubError).status}`);\n\n // diff too large error\n if ((err as GithubError)?.status === 406 || (err as GithubError)?.message.includes('diff is taking too long to generate')) {\n core.info(`Attempting to generate diff using local git command`);\n if (compareBase.repo?.html_url) {\n changedFileNames = await getDiffUsingGitCommand(compareBase.repo?.html_url, compareBase.sha, compareHead.sha);\n } else {\n core.error(`Could not fetch repo url to run local git diff`);\n throw err;\n }\n } else {\n throw err;\n }\n }\n return changedFileNames;\n};\n\nconst getMergeSafetyStateAndMessage = async (\n pullRequest: PullRequest,\n { paths, ignore_globs, override_filter_paths, override_filter_globs }: CheckMergeSafety\n) => {\n const {\n base: {\n repo: {\n default_branch,\n owner: { login: baseOwner }\n }\n },\n head: {\n ref,\n user: { login: username }\n }\n } = pullRequest;\n\n const branchName = `${username}:${ref}`;\n const diffAgainstUserBranch = `${branchName}...${baseOwner}:${default_branch}`;\n let fileNamesWhichBranchIsBehindOn;\n try {\n fileNamesWhichBranchIsBehindOn = await getDiff(pullRequest.head, pullRequest.base, diffAgainstUserBranch);\n } catch (err) {\n const message = diffErrorMessage(diffAgainstUserBranch, (err as GithubError).message);\n core.error(message);\n return { state: 'failure', message } as const;\n }\n\n const truncatedRef = ref.length > maxBranchNameLength ? `${ref.substring(0, maxBranchNameLength)}...` : ref;\n const truncatedBranchName = `${username}:${truncatedRef}`;\n const globalFilesOutdatedOnBranch = override_filter_globs\n ? micromatch(fileNamesWhichBranchIsBehindOn, override_filter_globs.split(/[\\n,]/))\n : override_filter_paths\n ? fileNamesWhichBranchIsBehindOn.filter(changedFile => override_filter_paths.split(/[\\n,]/).includes(changedFile))\n : [];\n\n if (globalFilesOutdatedOnBranch.length) {\n core.error(buildErrorMessage(globalFilesOutdatedOnBranch, 'global files', truncatedBranchName));\n return {\n state: 'failure',\n message: `This branch has one or more outdated global files. Please update with ${default_branch}.`\n } as const;\n }\n\n const diffAgainstDefaultBranch = `${baseOwner}:${default_branch}...${branchName}`;\n let changedFileNames;\n try {\n changedFileNames = await getDiff(pullRequest.base, pullRequest.head, diffAgainstDefaultBranch);\n } catch (err) {\n const message = diffErrorMessage(diffAgainstDefaultBranch, (err as GithubError).message);\n core.error(message);\n return { state: 'failure', message } as const;\n }\n\n const changedFilesToIgnore = changedFileNames && ignore_globs ? micromatch(changedFileNames, ignore_globs.split(/[\\n,]/)) : [];\n const filteredFileNames = changedFileNames?.filter(file => !changedFilesToIgnore.includes(file));\n const allProjectDirectories = paths?.split(/[\\n,]/);\n\n const changedProjectsOutdatedOnBranch = allProjectDirectories?.filter(\n dir => fileNamesWhichBranchIsBehindOn.some(file => file.includes(dir)) && filteredFileNames?.some(file => file.includes(dir))\n );\n\n if (changedProjectsOutdatedOnBranch?.length) {\n core.error(buildErrorMessage(changedProjectsOutdatedOnBranch, 'projects', truncatedBranchName));\n return {\n state: 'failure',\n message: `This branch has one or more outdated projects. Please update with ${default_branch}.`\n } as const;\n }\n\n const safeMessage = buildSuccessMessage(truncatedBranchName);\n core.info(safeMessage);\n return {\n state: 'success',\n message: safeMessage\n } as const;\n};\n\nconst buildErrorMessage = (paths: string[], pathType: 'projects' | 'global files', branchName: string) =>\n `\nThe following ${pathType} are outdated on branch ${branchName}\n\n${paths.map(path => `* ${path}`).join('\\n')}\n`;\n\nconst diffErrorMessage = (basehead: string, message = '') =>\n `Failed to generate diff for ${basehead}. Please verify SHAs are valid and try again.${message ? `\\nError: ${message}` : ''}`;\n\nconst buildSuccessMessage = (branchName: string) => `Branch ${branchName} is safe to merge!`;\n" + "/*\nCopyright 2021 Expedia, Inc.\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n https://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport { HelperInputs } from '../types/generated';\nimport { context as githubContext } from '@actions/github';\nimport { simpleGit } from 'simple-git';\nimport { octokit } from '../octokit';\nimport micromatch from 'micromatch';\nimport { GithubError, PullRequest } from '../types/github';\nimport { paginateAllOpenPullRequests } from '../utils/paginate-open-pull-requests';\nimport { map } from 'bluebird';\nimport { setCommitStatus } from './set-commit-status';\nimport * as core from '@actions/core';\n\nconst git = simpleGit();\n\nconst maxBranchNameLength = 50;\nconst COMMENT_PATHS_MARKER = '';\n\nexport class CheckMergeSafety extends HelperInputs {\n declare context?: string;\n declare paths?: string;\n declare ignore_globs?: string;\n declare override_filter_paths?: string;\n declare override_filter_globs?: string;\n declare match_comment_paths?: string;\n}\n\nexport const checkMergeSafety = async (inputs: CheckMergeSafety) => {\n const isPrWorkflow = Boolean(githubContext.issue.number);\n if (!isPrWorkflow) {\n return handlePushWorkflow(inputs);\n }\n const { data: pullRequest } = await octokit.pulls.get({ pull_number: githubContext.issue.number, ...githubContext.repo });\n\n const { state, message } = await setMergeSafetyStatus(pullRequest, inputs);\n if (state === 'failure') {\n core.setFailed(message);\n }\n};\n\nconst setMergeSafetyStatus = async (pullRequest: PullRequest, { context = 'Merge Safety', ...inputs }: CheckMergeSafety) => {\n const { state, message } = await getMergeSafetyStateAndMessage(pullRequest, inputs);\n const hasExistingFailureStatus = await checkForExistingFailureStatus(pullRequest, context);\n if (hasExistingFailureStatus && state === 'failure') {\n const {\n head: {\n ref,\n user: { login: username }\n }\n } = pullRequest;\n const truncatedRef = ref.length > maxBranchNameLength ? `${ref.substring(0, maxBranchNameLength)}...` : ref;\n const truncatedBranchName = `${username}:${truncatedRef}`;\n core.info(`Found existing failure status for ${truncatedBranchName}, skipping setting new status`);\n } else {\n await setCommitStatus({\n sha: pullRequest.head.sha,\n state,\n context,\n description: message,\n ...githubContext.repo\n });\n }\n\n return { state, message };\n};\n\nconst handlePushWorkflow = async (inputs: CheckMergeSafety) => {\n const pullRequests = await paginateAllOpenPullRequests();\n const filteredPullRequests = pullRequests.filter(({ base, draft }) => !draft && base.ref === base.repo.default_branch);\n await map(filteredPullRequests, pullRequest => setMergeSafetyStatus(pullRequest as PullRequest, inputs));\n};\n\nconst checkForExistingFailureStatus = async (pullRequest: PullRequest, context: string) => {\n const { data } = await octokit.repos.getCombinedStatusForRef({\n ...githubContext.repo,\n ref: pullRequest.head.sha\n });\n if (data.state === 'failure') {\n const existingContext = data.statuses.find(status => status.context === context);\n return Boolean(existingContext);\n }\n return false;\n};\n\nconst fetchSha = async (repoUrl: string, sha: string) => {\n try {\n await git.fetch(repoUrl, sha, { '--depth': 1 });\n core.info(`Fetched ${sha} from ${repoUrl}`);\n } catch (err) {\n core.info(`Failed to fetch ${sha} from ${repoUrl}: ${(err as GithubError).message}`);\n throw new Error(`Failed to fetch ${sha} from ${repoUrl}: ${(err as GithubError).message}`);\n }\n};\n\nconst getDiffUsingGitCommand = async (repoUrl: string, baseSha: string, headSha: string): Promise => {\n // update local repo copy\n await fetchSha(repoUrl, baseSha);\n await fetchSha(repoUrl, headSha);\n\n try {\n const diff = await git.diff(['--name-only', baseSha, headSha]);\n return (diff ?? '').split('\\n').filter(Boolean);\n } catch (err) {\n core.error(`Failed to run local git diff for ${repoUrl}: ${(err as GithubError).message}`);\n throw new Error(`Failed to run local git diff for ${repoUrl}: ${(err as GithubError).message}`);\n }\n};\n\ntype DiffRefs = PullRequest['base' | 'head'];\nconst getDiff = async (compareBase: DiffRefs, compareHead: DiffRefs, basehead: string) => {\n let changedFileNames: string[] = [];\n try {\n const { data: { files: changedFiles } = {}, status } = await octokit.repos.compareCommitsWithBasehead({\n ...githubContext.repo,\n basehead\n });\n if (status > 400) {\n throw { status };\n }\n changedFileNames = changedFiles?.map(file => file.filename) ?? [];\n } catch (err) {\n core.info(`Failed to fetch diff: ${(err as GithubError).message} Status: ${(err as GithubError).status}`);\n\n // diff too large error\n if ((err as GithubError)?.status === 406 || (err as GithubError)?.message.includes('diff is taking too long to generate')) {\n core.info(`Attempting to generate diff using local git command`);\n if (compareBase.repo?.html_url) {\n changedFileNames = await getDiffUsingGitCommand(compareBase.repo?.html_url, compareBase.sha, compareHead.sha);\n } else {\n core.error(`Could not fetch repo url to run local git diff`);\n throw err;\n }\n } else {\n throw err;\n }\n }\n return changedFileNames;\n};\n\nconst getMergeSafetyStateAndMessage = async (\n pullRequest: PullRequest,\n { paths, ignore_globs, override_filter_paths, override_filter_globs, match_comment_paths }: CheckMergeSafety\n) => {\n const {\n base: {\n repo: {\n default_branch,\n owner: { login: baseOwner }\n }\n },\n head: {\n ref,\n user: { login: username }\n }\n } = pullRequest;\n\n const branchName = `${username}:${ref}`;\n const diffAgainstUserBranch = `${branchName}...${baseOwner}:${default_branch}`;\n let fileNamesWhichBranchIsBehindOn;\n try {\n fileNamesWhichBranchIsBehindOn = await getDiff(pullRequest.head, pullRequest.base, diffAgainstUserBranch);\n } catch (err) {\n const message = diffErrorMessage(diffAgainstUserBranch, (err as GithubError).message);\n core.error(message);\n return { state: 'failure', message } as const;\n }\n\n const truncatedRef = ref.length > maxBranchNameLength ? `${ref.substring(0, maxBranchNameLength)}...` : ref;\n const truncatedBranchName = `${username}:${truncatedRef}`;\n\n if (match_comment_paths === 'true') {\n const commentPaths = await getPathsFromComment(pullRequest.number);\n\n if (commentPaths.length) {\n core.info(`Found ${commentPaths.length} paths from PR comment`);\n\n const outdatedCommentPaths = commentPaths.filter(commentPath =>\n fileNamesWhichBranchIsBehindOn.some(file => file.startsWith(commentPath + '/') || file === commentPath)\n );\n\n if (outdatedCommentPaths.length) {\n core.error(buildErrorMessage(outdatedCommentPaths, 'comment paths', truncatedBranchName));\n const displayPaths = outdatedCommentPaths.slice(0, 3).join(', ');\n const suffix = outdatedCommentPaths.length > 3 ? '...' : '';\n return {\n state: 'failure',\n message: `Branch is behind on paths from comment: ${displayPaths}${suffix}. Please update with ${default_branch}.`\n } as const;\n }\n } else {\n core.info('No paths found in PR comment, skipping comment path matching check');\n }\n }\n\n const globalFilesOutdatedOnBranch = override_filter_globs\n ? micromatch(fileNamesWhichBranchIsBehindOn, override_filter_globs.split(/[\\n,]/))\n : override_filter_paths\n ? fileNamesWhichBranchIsBehindOn.filter(changedFile => override_filter_paths.split(/[\\n,]/).includes(changedFile))\n : [];\n\n if (globalFilesOutdatedOnBranch.length) {\n core.error(buildErrorMessage(globalFilesOutdatedOnBranch, 'global files', truncatedBranchName));\n return {\n state: 'failure',\n message: `This branch has one or more outdated global files. Please update with ${default_branch}.`\n } as const;\n }\n\n const diffAgainstDefaultBranch = `${baseOwner}:${default_branch}...${branchName}`;\n let changedFileNames;\n try {\n changedFileNames = await getDiff(pullRequest.base, pullRequest.head, diffAgainstDefaultBranch);\n } catch (err) {\n const message = diffErrorMessage(diffAgainstDefaultBranch, (err as GithubError).message);\n core.error(message);\n return { state: 'failure', message } as const;\n }\n\n const changedFilesToIgnore = changedFileNames && ignore_globs ? micromatch(changedFileNames, ignore_globs.split(/[\\n,]/)) : [];\n const filteredFileNames = changedFileNames?.filter(file => !changedFilesToIgnore.includes(file));\n const allProjectDirectories = paths?.split(/[\\n,]/);\n\n const changedProjectsOutdatedOnBranch = allProjectDirectories?.filter(\n dir => fileNamesWhichBranchIsBehindOn.some(file => file.includes(dir)) && filteredFileNames?.some(file => file.includes(dir))\n );\n\n if (changedProjectsOutdatedOnBranch?.length) {\n core.error(buildErrorMessage(changedProjectsOutdatedOnBranch, 'projects', truncatedBranchName));\n return {\n state: 'failure',\n message: `This branch has one or more outdated projects. Please update with ${default_branch}.`\n } as const;\n }\n\n const safeMessage = buildSuccessMessage(truncatedBranchName);\n core.info(safeMessage);\n return {\n state: 'success',\n message: safeMessage\n } as const;\n};\n\nconst buildErrorMessage = (paths: string[], pathType: 'projects' | 'global files' | 'comment paths', branchName: string) =>\n `\nThe following ${pathType} are outdated on branch ${branchName}\n\n${paths.map(path => `* ${path}`).join('\\n')}\n`;\n\nconst diffErrorMessage = (basehead: string, message = '') =>\n `Failed to generate diff for ${basehead}. Please verify SHAs are valid and try again.${message ? `\\nError: ${message}` : ''}`;\n\nconst buildSuccessMessage = (branchName: string) => `Branch ${branchName} is safe to merge!`;\n\nconst getPathsFromComment = async (pullNumber: number): Promise => {\n const { data: comments } = await octokit.issues.listComments({\n ...githubContext.repo,\n issue_number: pullNumber\n });\n\n const pathsComment = comments.find(c => c.body?.includes(COMMENT_PATHS_MARKER));\n if (!pathsComment?.body) {\n return [];\n }\n\n const jsonMatch = pathsComment.body.match(/```json\\n([\\s\\S]*?)\\n```/);\n if (!jsonMatch?.[1]) {\n return [];\n }\n\n try {\n const parsed: unknown = JSON.parse(jsonMatch[1]);\n if (Array.isArray(parsed) && parsed.every(item => typeof item === 'string')) {\n return parsed;\n }\n return [];\n } catch {\n core.warning(`Failed to parse paths from PR #${pullNumber} comment`);\n return [];\n }\n};\n" ], - "mappings": "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", - "debugId": "852D8F69D0103B1E64756E2164756E21", + "mappings": "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", + "debugId": "8CCB3211353B88AF64756E2164756E21", "names": [] } \ No newline at end of file diff --git a/dist/main-8h70j5cy.js.map b/dist/main-8h70j5cy.js.map index 5b0f891a..92a2acac 100644 --- a/dist/main-8h70j5cy.js.map +++ b/dist/main-8h70j5cy.js.map @@ -2,9 +2,9 @@ "version": 3, "sources": ["../src/types/generated.ts"], "sourcesContent": [ - "/*\nCopyright 2021 Expedia, Inc.\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n https://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nexport class HelperInputs {\n declare helper?: string;\n declare github_token?: string;\n declare body?: string;\n declare project_name?: string;\n declare project_destination_column_name?: string;\n declare note?: string;\n declare project_origin_column_name?: string;\n declare sha?: string;\n declare context?: string;\n declare state?: string;\n declare description?: string;\n declare target_url?: string;\n declare environment?: string;\n declare environment_url?: string;\n declare label?: string;\n declare labels?: string;\n declare paths?: string;\n declare ignore_globs?: string;\n declare override_filter_paths?: string;\n declare batches?: string;\n declare pattern?: string;\n declare teams?: string;\n declare users?: string;\n declare login?: string;\n declare paths_no_filter?: string;\n declare slack_webhook_url?: string;\n declare number_of_assignees?: string;\n declare number_of_reviewers?: string;\n declare globs?: string;\n declare override_filter_globs?: string;\n declare title?: string;\n declare seconds?: string;\n declare pull_number?: string;\n declare base?: string;\n declare head?: string;\n declare days?: string;\n declare no_evict_upon_conflict?: string;\n declare skip_if_already_set?: string;\n declare delimiter?: string;\n declare team?: string;\n declare ignore_deleted?: string;\n declare return_full_payload?: string;\n declare skip_auto_merge?: string;\n declare repo_name?: string;\n declare repo_owner_name?: string;\n declare load_balancing_sizes?: string;\n declare required_review_overrides?: string;\n declare codeowners_overrides?: string;\n declare max_queue_size?: string;\n declare allow_only_for_maintainers?: string;\n declare use_basic_matrix_configuration?: string;\n declare merge_queue_enabled?: string;\n declare packages?: string;\n declare branch_name?: string;\n declare commit_message?: string;\n}\n" + "/*\nCopyright 2021 Expedia, Inc.\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n https://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nexport class HelperInputs {\n declare helper?: string;\n declare github_token?: string;\n declare body?: string;\n declare project_name?: string;\n declare project_destination_column_name?: string;\n declare note?: string;\n declare project_origin_column_name?: string;\n declare sha?: string;\n declare context?: string;\n declare state?: string;\n declare description?: string;\n declare target_url?: string;\n declare environment?: string;\n declare environment_url?: string;\n declare label?: string;\n declare labels?: string;\n declare paths?: string;\n declare ignore_globs?: string;\n declare override_filter_paths?: string;\n declare batches?: string;\n declare pattern?: string;\n declare teams?: string;\n declare users?: string;\n declare login?: string;\n declare paths_no_filter?: string;\n declare slack_webhook_url?: string;\n declare number_of_assignees?: string;\n declare number_of_reviewers?: string;\n declare globs?: string;\n declare override_filter_globs?: string;\n declare title?: string;\n declare seconds?: string;\n declare pull_number?: string;\n declare base?: string;\n declare head?: string;\n declare days?: string;\n declare no_evict_upon_conflict?: string;\n declare skip_if_already_set?: string;\n declare delimiter?: string;\n declare team?: string;\n declare ignore_deleted?: string;\n declare return_full_payload?: string;\n declare skip_auto_merge?: string;\n declare repo_name?: string;\n declare repo_owner_name?: string;\n declare load_balancing_sizes?: string;\n declare required_review_overrides?: string;\n declare codeowners_overrides?: string;\n declare max_queue_size?: string;\n declare allow_only_for_maintainers?: string;\n declare use_basic_matrix_configuration?: string;\n declare merge_queue_enabled?: string;\n declare packages?: string;\n declare branch_name?: string;\n declare commit_message?: string;\n declare match_comment_paths?: string;\n}\n" ], - "mappings": ";AAaO,MAAM,aAAa;AAwD1B;", + "mappings": ";AAaO,MAAM,aAAa;AAyD1B;", "debugId": "F3A2414DFAF3188664756E2164756E21", "names": [] } \ No newline at end of file diff --git a/src/helpers/check-merge-safety.ts b/src/helpers/check-merge-safety.ts index f71ea108..9a47d568 100644 --- a/src/helpers/check-merge-safety.ts +++ b/src/helpers/check-merge-safety.ts @@ -275,12 +275,16 @@ const getPathsFromComment = async (pullNumber: number): Promise => { } const jsonMatch = pathsComment.body.match(/```json\n([\s\S]*?)\n```/); - if (!jsonMatch) { + if (!jsonMatch?.[1]) { return []; } try { - return JSON.parse(jsonMatch[1]); + const parsed: unknown = JSON.parse(jsonMatch[1]); + if (Array.isArray(parsed) && parsed.every(item => typeof item === 'string')) { + return parsed; + } + return []; } catch { core.warning(`Failed to parse paths from PR #${pullNumber} comment`); return []; From 0e35ecc9f7fe7cff6b4d3c6b8af08b9ca28003d4 Mon Sep 17 00:00:00 2001 From: Austin Wu Date: Thu, 23 Apr 2026 10:24:57 -0500 Subject: [PATCH 4/4] Fix tests --- test/helpers/check-merge-safety.test.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/test/helpers/check-merge-safety.test.ts b/test/helpers/check-merge-safety.test.ts index 910c9cd5..935e519e 100644 --- a/test/helpers/check-merge-safety.test.ts +++ b/test/helpers/check-merge-safety.test.ts @@ -109,6 +109,13 @@ const allProjectPaths = ['packages/package-1/', 'packages/package-2/', 'packages describe('checkMergeSafety', () => { beforeEach(() => { mock.clearAllMocks(); + context.issue.number = 123; + (octokit.pulls.get as unknown as Mock).mockImplementation(async () => ({ + data: { + base: { repo: { default_branch: defaultBranch, owner: { login: baseOwner }, html_url: baseRepoHtmlUrl }, sha: baseSha }, + head: { sha, ref: branchName, user: { login: username }, repo: { html_url: headRepoHtmlUrl } } + } + })); }); it('should prevent merge when branch is out of date for a changed project', async () => {