| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140 |
- name: Project Board Status
- on:
- pull_request_target:
- types:
- - opened
- - reopened
- - ready_for_review
- - edited
- - closed
- branches:
- - develop
- permissions: {}
- concurrency:
- group: project-board-status-${{ github.event.pull_request.number }}
- jobs:
- project-board-status:
- if: github.repository_owner == 'GameServerManagers'
- runs-on: ubuntu-latest
- steps:
- - name: Move linked issues on the project boards
- uses: actions/github-script@v9
- with:
- github-token: ${{ secrets.ADD_TO_PROJECT_PAT }}
- script: |
- const { owner, repo } = context.repo;
- const pr = context.payload.pull_request;
- const org = 'GameServerManagers';
- const boardNumber = 8;
- if (context.payload.action === 'closed' && !pr.merged) {
- return;
- }
- const targets = pr.merged ? ['Awaiting Release', 'In Review'] : ['In Progress'];
- const movableFrom = pr.merged
- ? ['New Issues', 'Sentenced Issues', 'The Fridge', 'On Hold', 'In Progress']
- : ['New Issues', 'Sentenced Issues', 'The Fridge'];
- const columnOf = (name) => (name || '').replace(/^[^A-Za-z]+/, '').trim();
- const body = (pr.body || '').replace(/<!--[\s\S]*?-->/g, '');
- const closingRef = /\b(?:close[sd]?|fix(?:e[sd])?|resolve[sd]?):?\s+(?:([\w.-]+)\/([\w.-]+))?#(\d+)/gi;
- const numbers = new Set();
- for (const m of body.matchAll(closingRef)) {
- if (m[1] && (m[1].toLowerCase() !== owner.toLowerCase() || m[2].toLowerCase() !== repo.toLowerCase())) {
- continue;
- }
- numbers.add(Number(m[3]));
- }
- if (numbers.size === 0) {
- core.info('No closing issue references in the PR body.');
- return;
- }
- const projectCache = new Map();
- async function project(number) {
- if (!projectCache.has(number)) {
- const data = await github.graphql(
- `query($org:String!,$number:Int!){
- organization(login:$org){
- projectV2(number:$number){
- id title
- field(name:"Status"){ ... on ProjectV2SingleSelectField { id options { id name } } }
- }
- }
- }`,
- { org, number }
- );
- projectCache.set(number, data.organization.projectV2);
- }
- return projectCache.get(number);
- }
- async function setStatus(proj, itemId) {
- const option = targets.map((t) => proj.field.options.find((o) => columnOf(o.name) === t)).find(Boolean);
- if (!option) {
- throw new Error(`${proj.title} has no "${targets.join('" or "')}" column`);
- }
- await github.graphql(
- `mutation($project:ID!,$item:ID!,$field:ID!,$option:String!){
- updateProjectV2ItemFieldValue(input:{projectId:$project,itemId:$item,fieldId:$field,value:{singleSelectOptionId:$option}}){ projectV2Item { id } }
- }`,
- { project: proj.id, item: itemId, field: proj.field.id, option: option.id }
- );
- return columnOf(option.name);
- }
- for (const number of numbers) {
- try {
- const data = await github.graphql(
- `query($owner:String!,$repo:String!,$number:Int!){
- repository(owner:$owner,name:$repo){
- issueOrPullRequest(number:$number){
- __typename
- ... on Issue {
- id state
- projectItems(first:20){
- nodes {
- id
- project { number owner { ... on Organization { login } } }
- fieldValueByName(name:"Status"){ ... on ProjectV2ItemFieldSingleSelectValue { name } }
- }
- }
- }
- }
- }
- }`,
- { owner, repo, number }
- );
- const issue = data.repository.issueOrPullRequest;
- if (!issue || issue.__typename !== 'Issue' || issue.state !== 'OPEN') {
- continue;
- }
- let items = issue.projectItems.nodes.filter((i) => i.project.owner?.login === org && i.project.number === boardNumber);
- if (items.length === 0) {
- const proj = await project(boardNumber);
- const added = await github.graphql(
- `mutation($project:ID!,$content:ID!){ addProjectV2ItemById(input:{projectId:$project,contentId:$content}){ item { id } } }`,
- { project: proj.id, content: issue.id }
- );
- items = [{ id: added.addProjectV2ItemById.item.id, project: { number: boardNumber }, fieldValueByName: null }];
- core.info(`Added #${number} to ${proj.title}.`);
- }
- for (const item of items) {
- const proj = await project(item.project.number);
- const current = columnOf(item.fieldValueByName?.name);
- if (current && !movableFrom.includes(current)) {
- core.info(`#${number} stays in ${current} on ${proj.title}.`);
- continue;
- }
- const moved = await setStatus(proj, item.id);
- core.info(`Moved #${number} from ${current || 'no status'} to ${moved} on ${proj.title}.`);
- }
- } catch (error) {
- core.warning(`#${number}: ${error.message}`);
- }
- }
|