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+
12+ steps :
13+ - name : Check project assignment
14+ uses : actions/github-script@v7
15+ with :
16+ github-token : ${{ secrets.GITHUB_TOKEN }}
17+ script : |
18+ const owner = 'o1-labs';
19+ const projectNumber = 24; // Rust node project number
20+
21+ // Get PR details
22+ const pr = context.payload.pull_request;
23+ console.log(`Checking PR #${pr.number}: ${pr.title}`);
24+
25+ // GraphQL query to check if PR is in project with iteration
26+ const query = `
27+ query($owner: String!, $number: Int!) {
28+ organization(login: $owner) {
29+ projectV2(number: $number) {
30+ id
31+ title
32+ items(first: 100) {
33+ nodes {
34+ id
35+ content {
36+ ... on PullRequest {
37+ id
38+ number
39+ title
40+ }
41+ }
42+ fieldValues(first: 20) {
43+ nodes {
44+ ... on ProjectV2ItemFieldIterationValue {
45+ field {
46+ ... on ProjectV2IterationField {
47+ name
48+ }
49+ }
50+ title
51+ startDate
52+ duration
53+ }
54+ }
55+ }
56+ }
57+ }
58+ }
59+ }
60+ }
61+ `;
62+
63+ try {
64+ const result = await github.graphql(query, {
65+ owner: owner,
66+ number: projectNumber
67+ });
68+
69+ const project = result.organization.projectV2;
70+ console.log(`Found project: ${project.title}`);
71+
72+ // Find this PR in the project
73+ const projectItem = project.items.nodes.find(item =>
74+ item.content &&
75+ item.content.number === pr.number
76+ );
77+
78+ if (!projectItem) {
79+ core.setFailed(`❌ PR #${pr.number} is not added to the "${project.title}" project.\n\n` +
80+ `Please add this PR to the project at: https://github.com/orgs/${owner}/projects/${projectNumber}\n\n` +
81+ `Instructions:\n` +
82+ `1. Go to the project board\n` +
83+ `2. Click "Add item" button\n` +
84+ `3. Search for this PR (#${pr.number})\n` +
85+ `4. Add it to the project\n` +
86+ `5. Set an iteration for the PR`);
87+ return;
88+ }
89+
90+ console.log('PR found in project, checking for iteration...');
91+
92+ // Check if PR has an iteration assigned
93+ const iterationField = projectItem.fieldValues.nodes.find(field =>
94+ field.field && field.field.name === 'Iteration'
95+ );
96+
97+ if (!iterationField || !iterationField.title) {
98+ core.setFailed(`❌ PR #${pr.number} is in the project but has no iteration assigned.\n\n` +
99+ `Please assign an iteration to this PR in the project at: https://github.com/orgs/${owner}/projects/${projectNumber}\n\n` +
100+ `Instructions:\n` +
101+ `1. Go to the project board\n` +
102+ `2. Find this PR (#${pr.number})\n` +
103+ `3. Click on the Iteration field for this item\n` +
104+ `4. Select the appropriate iteration`);
105+ return;
106+ }
107+
108+ console.log(`✅ PR #${pr.number} is in project "${project.title}" with iteration: ${iterationField.title}`);
109+ core.notice(`PR is properly assigned to project with iteration: ${iterationField.title}`);
110+
111+ } catch (error) {
112+ console.error('Error checking project:', error);
113+
114+ // Check if it's a permission issue
115+ if (error.message.includes('Bad credentials') || error.message.includes('401') || error.message.includes('403')) {
116+ core.warning(`Cannot verify project assignment due to missing permissions.\n\n` +
117+ `Note: GITHUB_TOKEN may not have sufficient permissions to access organization projects.\n` +
118+ `This check requires organization-level project access which may not be available to the default token.`);
119+ // Don't fail the check for permission issues
120+ return;
121+ }
122+
123+ core.setFailed(`Failed to check project assignment: ${error.message}\n\n` +
124+ `This might happen if:\n` +
125+ `- The GITHUB_TOKEN doesn't have sufficient permissions\n` +
126+ `- The project number (${projectNumber}) is incorrect\n` +
127+ `- There are API permission issues`);
128+ }
0 commit comments