name: Auto-move Dependabot PRs to Code Review permissions: contents: read pull-requests: read on: pull_request_target: 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}'`);