Skip to content

Commit ce2fb92

Browse files
authored
chore: add project prioritization automation actions (#33043)
### Issue # (if applicable) Closes NA ### Reason for this change New Github actions workflows to add PRs automatically to the Priority Project board based on the existing labels. ### Description of changes The Github priority project board will be used internally by the CDK team to have a single consolidated view of all PR's based on the priority category. This change will enable github action workflow to automate adding PRs to the priority board. ### Describe any new or updated permissions being added N/A ### Description of how you validated changes Ran unit test and test in local repo. ### Checklist - [x] My code adheres to the [CONTRIBUTING GUIDE](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) and [DESIGN GUIDELINES](https://github.com/aws/aws-cdk/blob/main/docs/DESIGN_GUIDELINES.md) ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
1 parent 1a18dc9 commit ce2fb92

14 files changed

+1613
-0
lines changed

.github/workflows/README.md

+15
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,11 @@ When approved this pushes the PR to the testing pipeline,
5555
thus starting the cli integ test build.
5656
Owner: Core CDK team
5757

58+
### Initial Priority Assignment
59+
60+
[project-prioritization-assignment.yml](project-prioritization-assignment.yml): GitHub action for automatically adding PR's with priorities to the project priority board based on their labels.
61+
Owner: CDK Support team
62+
5863
## Issue Triggered
5964

6065
### Closed Issue Message
@@ -103,3 +108,13 @@ Owner: Core CDK team
103108

104109
[update-contributors.yml](update-contributors.yml): GitHub action that runs monthly to create a pull request for updating a CONTRIBUTORS file with the top contributors.
105110
Owner: Core CDK team
111+
112+
### R2 Priority Assignment
113+
114+
[project-prioritization-r2-assignment.yml](project-prioritization-r2-assignment.yml): GitHub action that runs every 6 hours to add PR's to the priority project board that satisfies R2 Priority.
115+
Owner: CDK Support team
116+
117+
### R5 Priority Assignment
118+
119+
[project-prioritization-r5-assignment.yml](project-prioritization-r5-assignment.yml): GitHub action that runs every day to add PR's to the priority project board that satisfies R5 Priority.
120+
Owner: CDK Support team
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
name: PR Prioritization
2+
on:
3+
pull_request:
4+
types:
5+
- labeled
6+
- opened
7+
- reopened
8+
- synchronize
9+
- ready_for_review
10+
11+
12+
jobs:
13+
prioritize:
14+
runs-on: ubuntu-latest
15+
steps:
16+
- uses: actions/checkout@v4
17+
- name: Add PR to Project & Set Priority
18+
uses: actions/github-script@v7
19+
with:
20+
github-token: ${{ secrets.PROJEN_GITHUB_TOKEN }}
21+
script: |
22+
const script = require('./scripts/prioritization/assign-priority.js')
23+
await script({github, context})
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
name: PR Prioritization R2 Check
2+
on:
3+
schedule:
4+
- cron: '0 */6 * * 1-5' # Runs every 6 hours during weekdays
5+
workflow_dispatch: # Manual trigger
6+
7+
jobs:
8+
update_project_status:
9+
runs-on: ubuntu-latest
10+
steps:
11+
- uses: actions/checkout@v4
12+
13+
- name: Check and assign R2 Priority to PRs
14+
uses: actions/github-script@v7
15+
with:
16+
github-token: ${{ secrets.PROJEN_GITHUB_TOKEN }}
17+
script: |
18+
const script = require('./scripts/prioritization/assign-r2-priority.js')
19+
await script({github})
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
name: PR Prioritization R5 Check
2+
on:
3+
schedule:
4+
- cron: '0 6 * * 1-5' # Runs at 6AM every day during weekdays
5+
workflow_dispatch: # Manual trigger
6+
7+
jobs:
8+
update_project_status:
9+
runs-on: ubuntu-latest
10+
steps:
11+
- uses: actions/checkout@v4
12+
- name: Check and Assign R5 Priority to PRs
13+
uses: actions/github-script@v7
14+
with:
15+
github-token: ${{ secrets.PROJEN_GITHUB_TOKEN }}
16+
script: |
17+
const script = require('./scripts/prioritization/assign-r5-priority.js')
18+
await script({github})
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,237 @@
1+
const { PRIORITIES, LABELS, STATUS, ...PROJECT_CONFIG} = require('../../../../scripts/prioritization/project-config');
2+
const {
3+
createMockPR,
4+
createMockGithub,
5+
OPTION_IDS
6+
} = require('./helpers/mock-data');
7+
8+
const assignPriority = require('../../../../scripts/prioritization/assign-priority');
9+
10+
11+
describe('Priority Assignment (R1, R3, R4)', () => {
12+
let mockGithub;
13+
let mockContext;
14+
15+
beforeEach(() => {
16+
mockGithub = createMockGithub();
17+
jest.clearAllMocks();
18+
});
19+
20+
async function verifyProjectState(expectedPriority, expectedStatus) {
21+
const calls = mockGithub.graphql.mock.calls;
22+
23+
if (!expectedPriority) {
24+
const priorityUpdateCall = calls.find(call =>
25+
call[1].input?.fieldId === PROJECT_CONFIG.priorityFieldId
26+
);
27+
expect(priorityUpdateCall).toBeUndefined();
28+
return;
29+
}
30+
31+
const priorityUpdateCall = calls.find(call =>
32+
call[1].input?.fieldId === PROJECT_CONFIG.priorityFieldId
33+
);
34+
const statusUpdateCall = calls.find(call =>
35+
call[1].input?.fieldId === PROJECT_CONFIG.statusFieldId
36+
);
37+
38+
// Verify priority was set correctly
39+
expect(priorityUpdateCall[1].input.value.singleSelectOptionId)
40+
.toBe(OPTION_IDS[expectedPriority]);
41+
42+
// Verify status was set to Ready
43+
expect(statusUpdateCall[1].input.value.singleSelectOptionId)
44+
.toBe(OPTION_IDS[expectedStatus]);
45+
}
46+
47+
describe('R1 Priority Tests', () => {
48+
test('should assign R1 and Ready status to non-draft PR with contribution/core label', async () => {
49+
const pr = createMockPR({
50+
draft: false,
51+
labels: [LABELS.CORE]
52+
});
53+
54+
mockContext = { payload: { pull_request: pr } };
55+
56+
await assignPriority({ github: mockGithub, context: mockContext });
57+
await verifyProjectState(PRIORITIES.R1, STATUS.READY);
58+
});
59+
60+
test('should assign R1 and Ready status to non-draft PR with contribution/core and needs-maintainer-review labels', async () => {
61+
const pr = createMockPR({
62+
draft: false,
63+
labels: [LABELS.CORE, LABELS.MAINTAINER_REVIEW]
64+
});
65+
66+
mockContext = { payload: { pull_request: pr } };
67+
68+
await assignPriority({ github: mockGithub, context: mockContext });
69+
await verifyProjectState(PRIORITIES.R1, STATUS.READY);
70+
});
71+
72+
test('should not add draft PR with contribution/core label to project', async () => {
73+
const pr = createMockPR({
74+
draft: true,
75+
labels: [LABELS.CORE]
76+
});
77+
78+
mockContext = { payload: { pull_request: pr } };
79+
80+
await assignPriority({ github: mockGithub, context: mockContext });
81+
await verifyProjectState(null);
82+
});
83+
});
84+
85+
describe('R3 Priority Tests', () => {
86+
test('should assign R3 and Ready status to non-draft PR with needs-maintainer-review label', async () => {
87+
const pr = createMockPR({
88+
draft: false,
89+
labels: [LABELS.MAINTAINER_REVIEW]
90+
});
91+
92+
mockContext = { payload: { pull_request: pr } };
93+
94+
await assignPriority({ github: mockGithub, context: mockContext });
95+
await verifyProjectState(PRIORITIES.R3, STATUS.READY);
96+
});
97+
98+
test('should not assign R3 to draft PR with needs-maintainer-review label', async () => {
99+
const pr = createMockPR({
100+
draft: true,
101+
labels: [LABELS.MAINTAINER_REVIEW]
102+
});
103+
104+
mockContext = { payload: { pull_request: pr } };
105+
106+
await assignPriority({ github: mockGithub, context: mockContext });
107+
await verifyProjectState(null);
108+
});
109+
});
110+
111+
describe('R4 Priority Tests', () => {
112+
test('should assign R4 and Ready status to PR with pr/reviewer-clarification-requested and needs-community-review labels', async () => {
113+
const pr = createMockPR({
114+
draft: true,
115+
labels: [
116+
LABELS.CLARIFICATION_REQUESTED,
117+
LABELS.COMMUNITY_REVIEW
118+
]
119+
});
120+
121+
mockContext = { payload: { pull_request: pr } };
122+
123+
await assignPriority({ github: mockGithub, context: mockContext });
124+
await verifyProjectState(PRIORITIES.R4, STATUS.READY);
125+
});
126+
127+
test('should assign R4 and Ready status to PR with pr-linter/exemption-requested and needs-community-review labels', async () => {
128+
const pr = createMockPR({
129+
draft: true,
130+
labels: [
131+
LABELS.EXEMPTION_REQUESTED,
132+
LABELS.COMMUNITY_REVIEW
133+
]
134+
});
135+
136+
mockContext = { payload: { pull_request: pr } };
137+
138+
await assignPriority({ github: mockGithub, context: mockContext });
139+
await verifyProjectState(PRIORITIES.R4, STATUS.READY);
140+
});
141+
142+
test('should assign R4 and Ready status to PR with pr/reviewer-clarification-requested and needs-maintainer-review labels', async () => {
143+
const pr = createMockPR({
144+
draft: true,
145+
labels: [
146+
LABELS.CLARIFICATION_REQUESTED,
147+
LABELS.MAINTAINER_REVIEW
148+
]
149+
});
150+
151+
mockContext = { payload: { pull_request: pr } };
152+
153+
await assignPriority({ github: mockGithub, context: mockContext });
154+
await verifyProjectState(PRIORITIES.R4, STATUS.READY);
155+
});
156+
157+
test('should assign R4 and Ready status to PR with pr-linter/exemption-requested and needs-maintainer-review labels', async () => {
158+
const pr = createMockPR({
159+
labels: [
160+
LABELS.EXEMPTION_REQUESTED,
161+
LABELS.MAINTAINER_REVIEW
162+
]
163+
});
164+
165+
mockContext = { payload: { pull_request: pr } };
166+
167+
await assignPriority({ github: mockGithub, context: mockContext });
168+
await verifyProjectState(PRIORITIES.R4, STATUS.READY);
169+
});
170+
171+
test('should assign R4 and Ready status to PR with pr/reviewer-clarification-requested label and no review labels', async () => {
172+
const pr = createMockPR({
173+
labels: [LABELS.CLARIFICATION_REQUESTED]
174+
});
175+
176+
mockContext = { payload: { pull_request: pr } };
177+
178+
await assignPriority({ github: mockGithub, context: mockContext });
179+
await verifyProjectState(PRIORITIES.R4, STATUS.READY);
180+
});
181+
182+
test('should assign R4 and Ready status to PR with pr-linter/exemption-requested label and no review labels', async () => {
183+
const pr = createMockPR({
184+
draft: true,
185+
labels: [LABELS.EXEMPTION_REQUESTED]
186+
});
187+
188+
mockContext = { payload: { pull_request: pr } };
189+
190+
await assignPriority({ github: mockGithub, context: mockContext });
191+
await verifyProjectState(PRIORITIES.R4, STATUS.READY);
192+
});
193+
});
194+
195+
describe('Priority Precedence Tests', () => {
196+
test('should assign R1 over R3 when PR has both contribution/core and needs-maintainer-review labels', async () => {
197+
const pr = createMockPR({
198+
draft: false,
199+
labels: [
200+
LABELS.CORE,
201+
LABELS.MAINTAINER_REVIEW
202+
]
203+
});
204+
205+
mockContext = { payload: { pull_request: pr } };
206+
207+
await assignPriority({ github: mockGithub, context: mockContext });
208+
await verifyProjectState(PRIORITIES.R1, STATUS.READY);
209+
});
210+
211+
test('should assign R1 over R4 when PR has both contribution/core and pr/reviewer-clarification-requested labels', async () => {
212+
const pr = createMockPR({
213+
draft: false,
214+
labels: [
215+
LABELS.CORE,
216+
LABELS.CLARIFICATION_REQUESTED
217+
]
218+
});
219+
220+
mockContext = { payload: { pull_request: pr } };
221+
await assignPriority({ github: mockGithub, context: mockContext });
222+
await verifyProjectState(PRIORITIES.R1, STATUS.READY);
223+
});
224+
225+
test('should not assign any priority when no matching labels', async () => {
226+
const pr = createMockPR({
227+
draft: false,
228+
labels: []
229+
});
230+
231+
mockContext = { payload: { pull_request: pr } };
232+
233+
await assignPriority({ github: mockGithub, context: mockContext });
234+
await verifyProjectState(null);
235+
});
236+
});
237+
});

0 commit comments

Comments
 (0)