Merge pull request #13546 from openfoodfoundation/task/13521-move-dependabot-to-code-review

Automate Dependabot PRs to Code Review column via GitHub Action
This commit is contained in:
Gaetan Craig-Riou
2025-10-06 16:21:14 +11:00
committed by GitHub
4 changed files with 172 additions and 0 deletions

15
.github/test-events/dependabot-pr.json vendored Normal file
View File

@@ -0,0 +1,15 @@
{
"pull_request": {
"number": 13545,
"title": "Bump test from 7.0.4 to 7.0.8",
"user": {
"login": "dependabot[bot]"
}
},
"repository": {
"owner": {
"login": "openfoodfoundation"
},
"name": "openfoodnetwork"
}
}

View File

@@ -0,0 +1,152 @@
name: Auto-move Dependabot PRs to Code Review
permissions:
contents: read
pull-requests: read
project: write
on:
pull_request:
types: [opened]
jobs:
move-pr-to-code-review:
runs-on: ubuntu-latest
if: github.event.pull_request.user.login == 'dependabot[bot]' || startsWith(github.event.pull_request.title, 'Bump')
steps:
- name: Generate GitHub App Token
id: app-token
uses: tibdex/github-app-token@v2
with:
app_id: ${{ secrets.DEPENDABOT_PR_APP_ID }}
private_key: ${{ secrets.DEPENDABOT_PR_APP_PRIVATE_KEY }}
installation_retrieval_mode: id
installation_retrieval_payload: ${{ secrets.DEPENDABOT_PR_APP_INSTALLATION_ID }}
- name: Move PR to Code Review in Project v2
uses: actions/github-script@v7
with:
github-token: ${{ steps.app-token.outputs.token }}
script: |
const projectNumber = 8; // for "OFN Delivery board"
const org = "openfoodfoundation";
const repo = context.repo.repo;
const prNumber = context.payload.pull_request.number;
const statusFieldName = "Status";
const statusValue = "Code review 🔎";
// ---- Helper: Get PR Node ID ----
async function getPrNodeId(owner, repo, number) {
const res = await github.graphql(`
query($owner: String!, $repo: String!, $number: Int!) {
repository(owner: $owner, name: $repo) {
pullRequest(number: $number) {
id
number
title
}
}
}
`, { owner, repo, number });
return res.repository.pullRequest.id;
}
console.log("🚀 Starting ProjectV2 automation...");
// ---- Step 1: Get Project and Fields ----
const projectRes = await github.graphql(`
query($org: String!, $number: Int!) {
organization(login: $org) {
projectV2(number: $number) {
id
title
fields(first: 50) {
nodes {
... on ProjectV2Field {
id
name
}
... on ProjectV2SingleSelectField {
id
name
options {
id
name
}
}
}
}
}
}
}
`, { org, number: projectNumber });
const project = projectRes.organization.projectV2;
if (!project) throw new Error(`❌ Project #${projectNumber} not found`);
console.log(`✅ Found project: ${project.title} (${project.id})`);
const statusField = project.fields.nodes.find(f => f.name === statusFieldName);
if (!statusField) throw new Error(`❌ Field '${statusFieldName}' not found`);
const option = statusField.options.find(o => o.name === statusValue);
if (!option) throw new Error(`❌ Option '${statusValue}' not found in '${statusFieldName}'`);
console.log(`✅ Found field '${statusFieldName}' and option '${statusValue}'`);
// ---- Step 2: Get PR Node ID ----
const prNodeId = await getPrNodeId(org, repo, prNumber);
console.log(`✅ PR #${prNumber} node ID: ${prNodeId}`);
// ---- Step 3: Check if PR is already in Project ----
const itemRes = await github.graphql(`
query($prId: ID!) {
node(id: $prId) {
... on PullRequest {
projectItems(first: 50) {
nodes {
id
project { id title }
}
}
}
}
}
`, { prId: prNodeId });
let projectItem = itemRes.node.projectItems.nodes.find(i => i.project.id === project.id);
if (!projectItem) {
console.log(" PR not yet in project, adding...");
const addRes = await github.graphql(`
mutation($projectId: ID!, $contentId: ID!) {
addProjectV2ItemById(input: {projectId: $projectId, contentId: $contentId}) {
item { id }
}
}
`, { projectId: project.id, contentId: prNodeId });
projectItem = addRes.addProjectV2ItemById.item;
console.log(`✅ Added PR to project: ${projectItem.id}`);
} else {
console.log(` PR already in project: ${projectItem.id}`);
}
// ---- Step 4: Update Status ----
await github.graphql(`
mutation($projectId: ID!, $itemId: ID!, $fieldId: ID!, $optionId: String!) {
updateProjectV2ItemFieldValue(input: {
projectId: $projectId,
itemId: $itemId,
fieldId: $fieldId,
value: { singleSelectOptionId: $optionId }
}) {
projectV2Item { id }
}
}
`, {
projectId: project.id,
itemId: projectItem.id,
fieldId: statusField.id,
optionId: option.id,
});
console.log(`🎉 Moved PR #${prNumber} → '${statusValue}'`);

1
.gitignore vendored
View File

@@ -59,3 +59,4 @@ yarn-debug.log*
/config/credentials.yml.enc
/config/master.key
.secrets

4
.secrets.example Normal file
View File

@@ -0,0 +1,4 @@
# .secrets file define github secrets value locally
DEPENDABOT_PR_APP_ID=123456
DEPENDABOT_PR_APP_INSTALLATION_ID=123456
DEPENDABOT_PR_APP_PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----\n....\n-----END RSA PRIVATE KEY-----"