Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: add project prioritization automation actions #33050

Merged
merged 2 commits into from
Jan 22, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions .github/workflows/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,11 @@ When approved this pushes the PR to the testing pipeline,
thus starting the cli integ test build.
Owner: Core CDK team

### Initial Priority Assignment

[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.
Owner: CDK Support team

## Issue Triggered

### Closed Issue Message
Expand Down Expand Up @@ -103,3 +108,13 @@ Owner: Core CDK team

[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.
Owner: Core CDK team

### R2 Priority Assignment

[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.
Owner: CDK Support team

### R5 Priority Assignment

[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.
Owner: CDK Support team
22 changes: 22 additions & 0 deletions .github/workflows/project-prioritization-assignment.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
name: PR Prioritization
on:
pull_request_target:
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the change made from the previous PR that was reverted due to issue when running the github actions on forked PR's. This change will run in the context of base repository which will have access to github token whereas pull_request will run in the context of the forked repo branch and doesn't have access to the github token which was the reason for the invalid token error.

types:
- labeled
- opened
- reopened
- synchronize
- ready_for_review

jobs:
prioritize:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Add PR to Project & Set Priority
uses: actions/github-script@v7
with:
github-token: ${{ secrets.PROJEN_GITHUB_TOKEN }}
script: |
const script = require('./scripts/prioritization/assign-priority.js')
await script({github, context})
19 changes: 19 additions & 0 deletions .github/workflows/project-prioritization-r2-assignment.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
name: PR Prioritization R2 Check
on:
schedule:
- cron: '0 */6 * * 1-5' # Runs every 6 hours during weekdays
workflow_dispatch: # Manual trigger

jobs:
update_project_status:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Check and assign R2 Priority to PRs
uses: actions/github-script@v7
with:
github-token: ${{ secrets.PROJEN_GITHUB_TOKEN }}
script: |
const script = require('./scripts/prioritization/assign-r2-priority.js')
await script({github})
18 changes: 18 additions & 0 deletions .github/workflows/project-prioritization-r5-assignment.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
name: PR Prioritization R5 Check
on:
schedule:
- cron: '0 6 * * 1-5' # Runs at 6AM every day during weekdays
workflow_dispatch: # Manual trigger

jobs:
update_project_status:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Check and Assign R5 Priority to PRs
uses: actions/github-script@v7
with:
github-token: ${{ secrets.PROJEN_GITHUB_TOKEN }}
script: |
const script = require('./scripts/prioritization/assign-r5-priority.js')
await script({github})
237 changes: 237 additions & 0 deletions scripts/@aws-cdk/script-tests/prioritization/assign-priority.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,237 @@
const { PRIORITIES, LABELS, STATUS, ...PROJECT_CONFIG} = require('../../../../scripts/prioritization/project-config');
const {
createMockPR,
createMockGithub,
OPTION_IDS
} = require('./helpers/mock-data');

const assignPriority = require('../../../../scripts/prioritization/assign-priority');


describe('Priority Assignment (R1, R3, R4)', () => {
let mockGithub;
let mockContext;

beforeEach(() => {
mockGithub = createMockGithub();
jest.clearAllMocks();
});

async function verifyProjectState(expectedPriority, expectedStatus) {
const calls = mockGithub.graphql.mock.calls;

if (!expectedPriority) {
const priorityUpdateCall = calls.find(call =>
call[1].input?.fieldId === PROJECT_CONFIG.priorityFieldId
);
expect(priorityUpdateCall).toBeUndefined();
return;
}

const priorityUpdateCall = calls.find(call =>
call[1].input?.fieldId === PROJECT_CONFIG.priorityFieldId
);
const statusUpdateCall = calls.find(call =>
call[1].input?.fieldId === PROJECT_CONFIG.statusFieldId
);

// Verify priority was set correctly
expect(priorityUpdateCall[1].input.value.singleSelectOptionId)
.toBe(OPTION_IDS[expectedPriority]);

// Verify status was set to Ready
expect(statusUpdateCall[1].input.value.singleSelectOptionId)
.toBe(OPTION_IDS[expectedStatus]);
}

describe('R1 Priority Tests', () => {
test('should assign R1 and Ready status to non-draft PR with contribution/core label', async () => {
const pr = createMockPR({
draft: false,
labels: [LABELS.CORE]
});

mockContext = { payload: { pull_request: pr } };

await assignPriority({ github: mockGithub, context: mockContext });
await verifyProjectState(PRIORITIES.R1, STATUS.READY);
});

test('should assign R1 and Ready status to non-draft PR with contribution/core and needs-maintainer-review labels', async () => {
const pr = createMockPR({
draft: false,
labels: [LABELS.CORE, LABELS.MAINTAINER_REVIEW]
});

mockContext = { payload: { pull_request: pr } };

await assignPriority({ github: mockGithub, context: mockContext });
await verifyProjectState(PRIORITIES.R1, STATUS.READY);
});

test('should not add draft PR with contribution/core label to project', async () => {
const pr = createMockPR({
draft: true,
labels: [LABELS.CORE]
});

mockContext = { payload: { pull_request: pr } };

await assignPriority({ github: mockGithub, context: mockContext });
await verifyProjectState(null);
});
});

describe('R3 Priority Tests', () => {
test('should assign R3 and Ready status to non-draft PR with needs-maintainer-review label', async () => {
const pr = createMockPR({
draft: false,
labels: [LABELS.MAINTAINER_REVIEW]
});

mockContext = { payload: { pull_request: pr } };

await assignPriority({ github: mockGithub, context: mockContext });
await verifyProjectState(PRIORITIES.R3, STATUS.READY);
});

test('should not assign R3 to draft PR with needs-maintainer-review label', async () => {
const pr = createMockPR({
draft: true,
labels: [LABELS.MAINTAINER_REVIEW]
});

mockContext = { payload: { pull_request: pr } };

await assignPriority({ github: mockGithub, context: mockContext });
await verifyProjectState(null);
});
});

describe('R4 Priority Tests', () => {
test('should assign R4 and Ready status to PR with pr/reviewer-clarification-requested and needs-community-review labels', async () => {
const pr = createMockPR({
draft: true,
labels: [
LABELS.CLARIFICATION_REQUESTED,
LABELS.COMMUNITY_REVIEW
]
});

mockContext = { payload: { pull_request: pr } };

await assignPriority({ github: mockGithub, context: mockContext });
await verifyProjectState(PRIORITIES.R4, STATUS.READY);
});

test('should assign R4 and Ready status to PR with pr-linter/exemption-requested and needs-community-review labels', async () => {
const pr = createMockPR({
draft: true,
labels: [
LABELS.EXEMPTION_REQUESTED,
LABELS.COMMUNITY_REVIEW
]
});

mockContext = { payload: { pull_request: pr } };

await assignPriority({ github: mockGithub, context: mockContext });
await verifyProjectState(PRIORITIES.R4, STATUS.READY);
});

test('should assign R4 and Ready status to PR with pr/reviewer-clarification-requested and needs-maintainer-review labels', async () => {
const pr = createMockPR({
draft: true,
labels: [
LABELS.CLARIFICATION_REQUESTED,
LABELS.MAINTAINER_REVIEW
]
});

mockContext = { payload: { pull_request: pr } };

await assignPriority({ github: mockGithub, context: mockContext });
await verifyProjectState(PRIORITIES.R4, STATUS.READY);
});

test('should assign R4 and Ready status to PR with pr-linter/exemption-requested and needs-maintainer-review labels', async () => {
const pr = createMockPR({
labels: [
LABELS.EXEMPTION_REQUESTED,
LABELS.MAINTAINER_REVIEW
]
});

mockContext = { payload: { pull_request: pr } };

await assignPriority({ github: mockGithub, context: mockContext });
await verifyProjectState(PRIORITIES.R4, STATUS.READY);
});

test('should assign R4 and Ready status to PR with pr/reviewer-clarification-requested label and no review labels', async () => {
const pr = createMockPR({
labels: [LABELS.CLARIFICATION_REQUESTED]
});

mockContext = { payload: { pull_request: pr } };

await assignPriority({ github: mockGithub, context: mockContext });
await verifyProjectState(PRIORITIES.R4, STATUS.READY);
});

test('should assign R4 and Ready status to PR with pr-linter/exemption-requested label and no review labels', async () => {
const pr = createMockPR({
draft: true,
labels: [LABELS.EXEMPTION_REQUESTED]
});

mockContext = { payload: { pull_request: pr } };

await assignPriority({ github: mockGithub, context: mockContext });
await verifyProjectState(PRIORITIES.R4, STATUS.READY);
});
});

describe('Priority Precedence Tests', () => {
test('should assign R1 over R3 when PR has both contribution/core and needs-maintainer-review labels', async () => {
const pr = createMockPR({
draft: false,
labels: [
LABELS.CORE,
LABELS.MAINTAINER_REVIEW
]
});

mockContext = { payload: { pull_request: pr } };

await assignPriority({ github: mockGithub, context: mockContext });
await verifyProjectState(PRIORITIES.R1, STATUS.READY);
});

test('should assign R1 over R4 when PR has both contribution/core and pr/reviewer-clarification-requested labels', async () => {
const pr = createMockPR({
draft: false,
labels: [
LABELS.CORE,
LABELS.CLARIFICATION_REQUESTED
]
});

mockContext = { payload: { pull_request: pr } };
await assignPriority({ github: mockGithub, context: mockContext });
await verifyProjectState(PRIORITIES.R1, STATUS.READY);
});

test('should not assign any priority when no matching labels', async () => {
const pr = createMockPR({
draft: false,
labels: []
});

mockContext = { payload: { pull_request: pr } };

await assignPriority({ github: mockGithub, context: mockContext });
await verifyProjectState(null);
});
});
});
Loading
Loading