Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion actions/setup/js/update_pull_request.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ function isNonFatalUpdateBranchError(error) {
// Require both permission wording and update-branch context to avoid treating unrelated
// "workflows permission" errors as non-fatal for pull request branch updates.
const hasWorkflowsPermissionError = hasWorkflowsPermissionPhrase && (hasWorkflowMutationRefusal || message.includes("update pull request"));
const hasVerifiedSignaturesRuleViolation = message.includes("repository rule violations found") && /commits?\s+must\s+have\s+verified\s+signatures\b/i.test(message);

if (status !== undefined) {
if (status === 403 && hasWorkflowsPermissionError) {
Expand All @@ -51,8 +52,10 @@ function isNonFatalUpdateBranchError(error) {
// GitHub update-branch API can return these 422 messages for benign conditions:
// - already up to date ("There are no new commits on the base branch")
// - cannot auto-update due to conflict ("merge conflict between base and head")
// - branch protection/ruleset refusal when the synthetic merge commit would be unsigned
// ("Repository rule violations found" + "Commits must have verified signatures")
// These should not fail safe output processing.
return message.includes("there are no new commits on the base branch") || message.includes("merge conflict between base and head") || hasWorkflowsPermissionError;
return message.includes("there are no new commits on the base branch") || message.includes("merge conflict between base and head") || hasWorkflowsPermissionError || hasVerifiedSignaturesRuleViolation;
}

/**
Expand Down
36 changes: 36 additions & 0 deletions actions/setup/js/update_pull_request.test.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -941,4 +941,40 @@ describe("update_pull_request.cjs - update_branch behavior", () => {
});
expect(mockCore.warning).toHaveBeenCalledWith(expect.stringContaining("branch from base (non-fatal)"));
});

it("should treat verified-signatures ruleset rejection as non-fatal for update-branch-only requests", async () => {
const ruleViolationError = new Error("Repository rule violations found\n\nCommits must have verified signatures.");
ruleViolationError.status = 422;
mockGithub.rest.pulls.updateBranch.mockRejectedValueOnce(ruleViolationError);

const handler = await updatePRModule.main({ update_branch: true });
const result = await handler({ pull_request_number: 100 });

expect(result.success).toBe(true);
expect(mockGithub.rest.pulls.updateBranch).toHaveBeenCalledTimes(1);
expect(mockGithub.rest.pulls.update).not.toHaveBeenCalled();
expect(mockCore.warning).toHaveBeenCalledWith(expect.stringContaining("branch from base (non-fatal)"));
});

it("should continue title/body updates when updateBranch hits verified-signatures ruleset rejection", async () => {
const ruleViolationError = new Error("Repository rule violations found\n\nCommits must have verified signatures.");
ruleViolationError.status = 422;
mockGithub.rest.pulls.updateBranch.mockRejectedValueOnce(ruleViolationError);

const handler = await updatePRModule.main({ update_branch: true });
const result = await handler({
pull_request_number: 100,
title: "Updated PR",
});

expect(result.success).toBe(true);
expect(mockGithub.rest.pulls.updateBranch).toHaveBeenCalledTimes(1);
expect(mockGithub.rest.pulls.update).toHaveBeenCalledWith({
owner: "testowner",
repo: "testrepo",
pull_number: 100,
title: "Updated PR",
});
expect(mockCore.warning).toHaveBeenCalledWith(expect.stringContaining("branch from base (non-fatal)"));
});
});