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