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
45 changes: 45 additions & 0 deletions .github/actions/combine-dependabot-prs/action.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
name: "Combine Dependabot PRs"
description: "Combine open dependency PRs into a single branch and PR"
Comment on lines +1 to +2
inputs:
labels:
description: "Label used to select PRs"
required: false
default: "dependencies"
pr_title:
description: "Combined pull request title"
required: false
default: "CCM-9336: Combined Dependabot PRs"
combine_branch_name:
description: "Branch used for the combined pull request"
required: false
default: "dependabotCombined"
pr_body_header:
description: "Header text used at the top of the combined pull request body"
required: false
default: "CCM-9336: Combined Dependabot PRs"
runs:
using: "composite"
steps:
- name: "Combine pull requests"
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
env:
INPUT_LABEL: ${{ inputs.labels }}
INPUT_PR_TITLE: ${{ inputs.pr_title }}
INPUT_COMBINE_BRANCH: ${{ inputs.combine_branch_name }}
INPUT_PR_BODY_HEADER: ${{ inputs.pr_body_header }}
with:
script: |
const path = require("node:path");
const run = require(path.join(process.env.GITHUB_ACTION_PATH, "combine-dependabot-prs.js"));

await run({
github,
context,
core,
inputs: {
label: process.env.INPUT_LABEL,
prTitle: process.env.INPUT_PR_TITLE,
combineBranch: process.env.INPUT_COMBINE_BRANCH,
prBodyHeader: process.env.INPUT_PR_BODY_HEADER,
},
});
111 changes: 111 additions & 0 deletions .github/actions/combine-dependabot-prs/combine-dependabot-prs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
module.exports = async ({ github, context, core, inputs }) => {
const owner = context.repo.owner;
const repo = context.repo.repo;
const baseBranch = "main";
const combineBranch = inputs.combineBranch;
const prTitle = inputs.prTitle;
const prBodyHeader = inputs.prBodyHeader;
const requiredLabel = (inputs.label || "dependencies").trim();
Comment on lines +4 to +8

const allOpenPrs = await github.paginate(github.rest.pulls.list, {
owner,
repo,
state: "open",
per_page: 100,
});

const dependencyPrs = allOpenPrs
.filter((pr) => pr.labels.some((label) => label.name === requiredLabel))
.sort((a, b) => a.number - b.number);

if (dependencyPrs.length === 0) {
core.info("No open dependency PRs found; nothing to combine.");
return;
}

const { data: baseRef } = await github.rest.git.getRef({
owner,
repo,
ref: `heads/${baseBranch}`,
});

const baseSha = baseRef.object.sha;

try {
await github.rest.git.getRef({
owner,
repo,
ref: `heads/${combineBranch}`,
});

await github.rest.git.updateRef({
owner,
repo,
ref: `heads/${combineBranch}`,
sha: baseSha,
force: true,
});
} catch (error) {
if (error.status !== 404) {
throw error;
}

await github.rest.git.createRef({
owner,
repo,
ref: `refs/heads/${combineBranch}`,
sha: baseSha,
});
}

for (const pr of dependencyPrs) {
try {
await github.rest.repos.merge({
owner,
repo,
base: combineBranch,
head: pr.head.sha,
commit_message: `Merge #${pr.number} into ${combineBranch}`,
});
core.info(`Merged PR #${pr.number} (${pr.head.ref}) into ${combineBranch}`);
} catch (error) {
core.setFailed(
`Failed to merge PR #${pr.number} (${pr.head.ref}) into ${combineBranch}: ${error.message}`,
);
throw error;
}
}

const includedPrLines = dependencyPrs.map((pr) => `- #${pr.number}`).join("\n");
const prBody = `${prBodyHeader}\n\nIncluded PRs:\n${includedPrLines}`;

const existingCombinedPr = allOpenPrs.find(
(pr) =>
pr.base.ref === baseBranch &&
pr.head.ref === combineBranch &&
pr.user?.login !== "dependabot[bot]",
);

if (existingCombinedPr) {
await github.rest.pulls.update({
owner,
repo,
pull_number: existingCombinedPr.number,
title: prTitle,
body: prBody,
});
core.info(`Updated existing combined PR #${existingCombinedPr.number}`);
return;
}

const { data: newPr } = await github.rest.pulls.create({
owner,
repo,
title: prTitle,
head: combineBranch,
base: baseBranch,
body: prBody,
});

core.info(`Created combined PR #${newPr.number}`);
};