Skip to content
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
5 changes: 5 additions & 0 deletions .github/workflows/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,3 +128,8 @@ Owner: CDK Support team

[project-prioritization-needs-attention.yml](project-prioritization-needs-attention.yml): GitHub action that runs every day to update Needs Attention field in the prioritization project board.
Owner: CDK Support team

### PR Prioritization AddedOn update

[project-prioritization-added-on.yml](project-prioritization-added-on.yml): GitHub action that runs every day to update AddedOn field in the prioritization project board.
Owner: CDK Support team
21 changes: 21 additions & 0 deletions .github/workflows/project-prioritization-added-on.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
name: PR Prioritization AddedOn update

on:
schedule:
- cron: '0 */6 * * 1-5' # Runs every 6 hours during weekdays
workflow_dispatch: # Manual trigger

jobs:
update_added_on:
if: github.repository == 'aws/aws-cdk'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Update AddedOn field
uses: actions/github-script@v7
with:
github-token: ${{ secrets.PROJEN_GITHUB_TOKEN }}
script: |
const script = require('./scripts/prioritization/update-added-on.js')
await script({github})
Comment on lines +9 to +21
Copy link
Member

Choose a reason for hiding this comment

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

Just a design question: why do we decide to have separate workflow files? i.e. If there's 5 more columns we want to add in the future then there'll be 5 separate workflows for it right?

Copy link
Member Author

Choose a reason for hiding this comment

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

Valid concern. we decided to have separate workflow based on the trigger type for specific automation. For this case, this workflow will have a cron schedule to check every 6 hours. We will be using the same workflow or any other existing workflow in future for other columns as a additional job step if it is also needed similar schedule.

12 changes: 10 additions & 2 deletions scripts/prioritization/assign-bug-priority.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
const { PRIORITIES, LABELS, STATUS, ...PROJECT_CONFIG } = require('./project-config');
const {
updateProjectField,
updateProjectDateField,
addItemToProject,
fetchProjectFields,
fetchProjectItem,
Expand Down Expand Up @@ -74,7 +75,7 @@ module.exports = async ({ github, context }) => {
(option) => option.name === STATUS.READY
)?.id;

// Set both priority and Ready status for new items only
// Set priority, Ready status and current date for new items
await Promise.all([
updateProjectField({
github,
Expand All @@ -89,7 +90,14 @@ module.exports = async ({ github, context }) => {
itemId: itemId,
fieldId: PROJECT_CONFIG.statusFieldId,
value: readyStatusId,
})
}),
updateProjectDateField({
github,
projectId: PROJECT_CONFIG.projectId,
itemId: itemId,
fieldId: PROJECT_CONFIG.addedOnFieldId,
date: new Date().toISOString(),
})
]);
}
}
Expand Down
10 changes: 9 additions & 1 deletion scripts/prioritization/assign-priority.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
const { PRIORITIES, LABELS, STATUS, ...PROJECT_CONFIG } = require('./project-config');
const {
updateProjectField,
updateProjectDateField,
addItemToProject,
fetchProjectFields,
fetchProjectItem,
Expand Down Expand Up @@ -129,7 +130,7 @@ module.exports = async ({ github, context }) => {
(option) => option.name === STATUS.READY
)?.id;

// Set both priority and Ready status for new items
// Set priority, Ready status and current date for new items
await Promise.all([
updateProjectField({
github,
Expand All @@ -144,6 +145,13 @@ module.exports = async ({ github, context }) => {
itemId: itemId,
fieldId: PROJECT_CONFIG.statusFieldId,
value: readyStatusId,
}),
updateProjectDateField({
github,
projectId: PROJECT_CONFIG.projectId,
itemId: itemId,
fieldId: PROJECT_CONFIG.addedOnFieldId,
date: new Date().toISOString(),
})
]);
}
Expand Down
10 changes: 9 additions & 1 deletion scripts/prioritization/assign-r2-priority.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const { PRIORITIES, LABELS, STATUS, ...PROJECT_CONFIG } = require("./project-con

const {
updateProjectField,
updateProjectDateField,
addItemToProject,
fetchProjectFields,
fetchOpenPullRequests,
Expand Down Expand Up @@ -126,7 +127,7 @@ module.exports = async ({ github }) => {
});
itemId = addResult.addProjectV2ItemById.item.id;

// Set both priority and initial status for new items
// Set priority, Ready status and current date for new items
await Promise.all([
updateProjectField({
github,
Expand All @@ -141,6 +142,13 @@ module.exports = async ({ github }) => {
itemId: itemId,
fieldId: PROJECT_CONFIG.statusFieldId,
value: readyStatusId,
}),
updateProjectDateField({
github,
projectId: PROJECT_CONFIG.projectId,
itemId: itemId,
fieldId: PROJECT_CONFIG.addedOnFieldId,
date: new Date().toISOString(),
})
]);
}
Expand Down
10 changes: 9 additions & 1 deletion scripts/prioritization/assign-r5-priority.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const { PRIORITIES, LABELS, STATUS, DAYS_THRESHOLD, ...PROJECT_CONFIG } = requir

const {
updateProjectField,
updateProjectDateField,
addItemToProject,
fetchProjectFields,
fetchOpenPullRequests,
Expand Down Expand Up @@ -120,7 +121,7 @@ module.exports = async ({ github }) => {
contentId: pr.id,
});

// Set initial priority and status
// Set priority, Ready status and current date for new items
await Promise.all([
updateProjectField({
github,
Expand All @@ -135,6 +136,13 @@ module.exports = async ({ github }) => {
itemId: addResult.addProjectV2ItemById.item.id,
fieldId: PROJECT_CONFIG.statusFieldId,
value: readyStatusId,
}),
updateProjectDateField({
github,
projectId: PROJECT_CONFIG.projectId,
itemId: itemId,
fieldId: PROJECT_CONFIG.addedOnFieldId,
date: new Date().toISOString(),
})
]);
} catch (error) {
Expand Down
61 changes: 60 additions & 1 deletion scripts/prioritization/project-api.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,45 @@ const updateProjectField = async ({
}
);
};


/**
* Updates a date field value for an item in a GitHub Project.
* @param {Object} params - The parameters for updating the project field
* @param {Object} params.github - The GitHub API client
* @param {string} params.projectId - The ID of the project
* @param {string} params.itemId - The ID of the item to update
* @param {string} params.fieldId - The ID of the field to update
* @param {string} params.date - The date string in ISO format
* @returns {Promise<Object>} The GraphQL mutation response
*/
const updateProjectDateField = async ({
github,
projectId,
itemId,
fieldId,
date,
}) => {
return github.graphql(
`
mutation($input: UpdateProjectV2ItemFieldValueInput!) {
updateProjectV2ItemFieldValue(input: $input) {
projectV2Item {
id
}
}
}
`,
{
input: {
projectId,
itemId,
fieldId,
value: { date },
},
}
);
};

/**
* Adds an item (PR) to a GitHub Project.
* @param {Object} params - The parameters for adding an item to the project
Expand Down Expand Up @@ -233,6 +271,18 @@ const updateProjectField = async ({
items(first: 100, after: $cursor) {
nodes {
id
createdAt
type
content {
... on Issue {
id
number
}
... on PullRequest {
id
number
}
}
fieldValues(first: 20) {
nodes {
... on ProjectV2ItemFieldSingleSelectValue {
Expand All @@ -244,6 +294,14 @@ const updateProjectField = async ({
}
updatedAt
}
... on ProjectV2ItemFieldDateValue {
date
field {
... on ProjectV2Field {
name
}
}
}
}
}
}
Expand All @@ -261,6 +319,7 @@ const updateProjectField = async ({

module.exports = {
updateProjectField,
updateProjectDateField,
addItemToProject,
fetchProjectFields,
fetchOpenPullRequests,
Expand Down
3 changes: 2 additions & 1 deletion scripts/prioritization/project-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ const NEEDS_ATTENTION_STATUS = {

/**
* Project configuration for GitHub project automation.
* Note: For projectId, priorityFieldId, statusFieldId, and attentionFieldId,
* Note: For projectId, priorityFieldId, statusFieldId, attentionFieldId and addedOnFieldId,
* refer to Setup section in README.md on how to retrieve these values using GraphQL query.
* These IDs need to be updated only when project fields are modified.
*/
Expand All @@ -57,6 +57,7 @@ module.exports = {
priorityFieldId: 'PVTSSF_lADOACIPmc4Av_32zgmVmPs',
statusFieldId: 'PVTSSF_lADOACIPmc4Av_32zgmVmF8',
attentionFieldId: 'PVTSSF_lADOACIPmc4Av_32zgmZDdo',
addedOnFieldId: 'PVTF_lADOACIPmc4Av_32zgqbglg',
LABELS,
PRIORITIES,
STATUS,
Expand Down
94 changes: 94 additions & 0 deletions scripts/prioritization/update-added-on.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/**
* Updates the "Added On" date for project items that have empty "Added On" field
* by using the project item's createdAt timestamp from GitHub API.
* This handles items that were added to the board manually.
*/

const { ...PROJECT_CONFIG } = require('./project-config');
const {
fetchProjectItems,
updateProjectDateField,
} = require('./project-api');

module.exports = async ({ github }) => {
// Get today's date at start of day (UTC)
const today = new Date();
today.setUTCHours(0, 0, 0, 0);

try {
let allItems = [];
let hasNextPage = true;
let cursor = null;

// Fetch project items with pagination
while (hasNextPage) {
const result = await fetchProjectItems({
github,
org: PROJECT_CONFIG.org,
number: PROJECT_CONFIG.projectNumber,
cursor,
});

const items = result.organization.projectV2.items;

// Filter items created today
const todayItems = items.nodes.filter(item => {
const itemDate = new Date(item.createdAt);
return itemDate >= today;
});

if (todayItems.length > 0) {
allItems = allItems.concat(todayItems);
}

hasNextPage = items.pageInfo.hasNextPage;
cursor = items.pageInfo.endCursor;
}

console.log(`Processing ${allItems.length} items created today for AddedOn dates`);

// Process each item
for (const item of allItems) {
const itemType = item.type;
const itemNumber = item.content?.number;

try {
// Find the AddedOn field value
const addedOnField = item.fieldValues.nodes.find(
(field) => field.field?.name === 'AddedOn'
);

// If AddedOn is empty
if (!addedOnField || !addedOnField.date) {
console.log(`Processing ${itemType} #${itemNumber} - Adding missing AddedOn date`);

await updateProjectDateField({
github,
projectId: PROJECT_CONFIG.projectId,
itemId: item.id,
fieldId: PROJECT_CONFIG.addedOnFieldId,
date: item.createdAt,
});

console.log(`Updated AddedOn date for ${itemType} #${itemNumber} with creation date ${item.createdAt}`);
} else {
console.log(`Skipping ${itemType} #${itemNumber} - AddedOn date already set`);
}
} catch (error) {
console.error(`Error processing ${itemType} #${itemNumber}:`, error.message);
continue;
}
}

if (allItems.length === 0) {
console.log('No items found that were created today');
} else {
console.log(`Successfully processed ${allItems.length} items`);
}

console.log('Completed processing today\'s items for AddedOn dates');
} catch (error) {
console.error('Error updating AddedOn dates:', error.message);
throw error;
}
};
Loading