Skip to content

Commit 0d21c29

Browse files
committed
CI: add a workflow for the project assignment
1 parent 2bdcaa8 commit 0d21c29

File tree

1 file changed

+133
-0
lines changed

1 file changed

+133
-0
lines changed
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
name: Check PR Project Assignment
2+
3+
on:
4+
pull_request:
5+
types: [opened, synchronize, reopened]
6+
7+
jobs:
8+
check-project:
9+
name: Verify PR is in project with iteration
10+
runs-on: ubuntu-latest
11+
# Only run on PRs to the o1-labs repository (not forks)
12+
if: github.event.pull_request.head.repo.full_name == github.repository
13+
14+
steps:
15+
- name: Check project assignment
16+
uses: actions/github-script@v7
17+
with:
18+
github-token: ${{ secrets.PROJECT_PAT || secrets.GITHUB_TOKEN }}
19+
script: |
20+
const owner = 'o1-labs';
21+
const projectNumber = 24; // Rust node project number
22+
23+
// Get PR details
24+
const pr = context.payload.pull_request;
25+
console.log(`Checking PR #${pr.number}: ${pr.title}`);
26+
27+
// GraphQL query to check if PR is in project with iteration
28+
const query = `
29+
query($owner: String!, $number: Int!, $prId: ID!) {
30+
organization(login: $owner) {
31+
projectV2(number: $number) {
32+
id
33+
title
34+
items(first: 100) {
35+
nodes {
36+
id
37+
content {
38+
... on PullRequest {
39+
id
40+
number
41+
title
42+
}
43+
}
44+
fieldValues(first: 20) {
45+
nodes {
46+
... on ProjectV2ItemFieldIterationValue {
47+
field {
48+
... on ProjectV2IterationField {
49+
name
50+
}
51+
}
52+
title
53+
startDate
54+
duration
55+
}
56+
}
57+
}
58+
}
59+
}
60+
}
61+
}
62+
}
63+
`;
64+
65+
try {
66+
const result = await github.graphql(query, {
67+
owner: owner,
68+
number: projectNumber,
69+
prId: pr.node_id
70+
});
71+
72+
const project = result.organization.projectV2;
73+
console.log(`Found project: ${project.title}`);
74+
75+
// Find this PR in the project
76+
const projectItem = project.items.nodes.find(item =>
77+
item.content &&
78+
item.content.number === pr.number
79+
);
80+
81+
if (!projectItem) {
82+
core.setFailed(`❌ PR #${pr.number} is not added to the "${project.title}" project.\n\n` +
83+
`Please add this PR to the project at: https://github.com/orgs/${owner}/projects/${projectNumber}\n\n` +
84+
`Instructions:\n` +
85+
`1. Go to the project board\n` +
86+
`2. Click "Add item" button\n` +
87+
`3. Search for this PR (#${pr.number})\n` +
88+
`4. Add it to the project\n` +
89+
`5. Set an iteration for the PR`);
90+
return;
91+
}
92+
93+
console.log('PR found in project, checking for iteration...');
94+
95+
// Check if PR has an iteration assigned
96+
const iterationField = projectItem.fieldValues.nodes.find(field =>
97+
field.field && field.field.name === 'Iteration'
98+
);
99+
100+
if (!iterationField || !iterationField.title) {
101+
core.setFailed(`❌ PR #${pr.number} is in the project but has no iteration assigned.\n\n` +
102+
`Please assign an iteration to this PR in the project at: https://github.com/orgs/${owner}/projects/${projectNumber}\n\n` +
103+
`Instructions:\n` +
104+
`1. Go to the project board\n` +
105+
`2. Find this PR (#${pr.number})\n` +
106+
`3. Click on the Iteration field for this item\n` +
107+
`4. Select the appropriate iteration`);
108+
return;
109+
}
110+
111+
console.log(`✅ PR #${pr.number} is in project "${project.title}" with iteration: ${iterationField.title}`);
112+
core.notice(`PR is properly assigned to project with iteration: ${iterationField.title}`);
113+
114+
} catch (error) {
115+
console.error('Error checking project:', error);
116+
117+
// Check if it's a permission issue
118+
if (error.message.includes('Bad credentials') || error.message.includes('401')) {
119+
core.warning(`Cannot verify project assignment due to missing permissions.\n\n` +
120+
`To enable this check, please ensure:\n` +
121+
`1. The PROJECT_PAT secret is configured with 'project' scope\n` +
122+
`2. The PAT has access to organization projects\n\n` +
123+
`Note: This check only works for PRs from the main repository, not forks.`);
124+
// Don't fail the check for permission issues
125+
return;
126+
}
127+
128+
core.setFailed(`Failed to check project assignment: ${error.message}\n\n` +
129+
`This might happen if:\n` +
130+
`- The PROJECT_PAT secret is not configured correctly\n` +
131+
`- The project number (${projectNumber}) is incorrect\n` +
132+
`- There are API permission issues`);
133+
}

0 commit comments

Comments
 (0)