diff --git a/.github/actions-scripts/fr-add-docs-reviewers-requests.js b/.github/actions-scripts/fr-add-docs-reviewers-requests.js index 4b67312abb..ec31eaf529 100644 --- a/.github/actions-scripts/fr-add-docs-reviewers-requests.js +++ b/.github/actions-scripts/fr-add-docs-reviewers-requests.js @@ -5,7 +5,7 @@ import { isDocsTeamMember, findFieldID, findSingleSelectID, - generateUpdateProjectNextItemFieldMutation, + generateUpdateProjectV2ItemFieldMutation, } from './projects.js' async function getAllOpenPRs() { @@ -104,7 +104,7 @@ async function run() { ` query ($organization: String!, $projectNumber: Int!) { organization(login: $organization) { - projectNext(number: $projectNumber) { + projectV2(number: $projectNumber) { id items(last: 100) { nodes { @@ -113,9 +113,18 @@ async function run() { } fields(first: 100) { nodes { - id - name - settings + ... on ProjectV2Field { + id + name + } + ... on ProjectV2SingleSelectField { + id + name + options { + id + name + } + } } } } @@ -132,13 +141,13 @@ async function run() { ) // Get the project ID - const projectID = projectData.organization.projectNext.id + const projectID = projectData.organization.projectV2.id // Get the IDs of the last 100 items on the board. // Until we have a way to check from a PR whether the PR is in a project, // this is how we (roughly) avoid overwriting PRs that are already on the board. // If we are overwriting items, query for more items. - const existingItemIDs = projectData.organization.projectNext.items.nodes.map((node) => node.id) + const existingItemIDs = projectData.organization.projectV2.items.nodes.map((node) => node.id) // Get the ID of the fields that we want to populate const datePostedID = findFieldID('Date posted', projectData) @@ -153,6 +162,7 @@ async function run() { const readyForReviewID = findSingleSelectID('Ready for review', 'Status', projectData) const hubberTypeID = findSingleSelectID('Hubber or partner', 'Contributor type', projectData) const docsMemberTypeID = findSingleSelectID('Docs team', 'Contributor type', projectData) + const sizeMediumID = findSingleSelectID('M', 'Size', projectData) // Add the PRs to the project const itemIDs = await addItemsToProject(prIDs, projectID) @@ -178,7 +188,7 @@ async function run() { // Populate fields for the new project items // (Using for...of instead of forEach since the function uses await) for (const [index, itemID] of newItemIDs.entries()) { - const updateProjectNextItemMutation = generateUpdateProjectNextItemFieldMutation({ + const updateProjectV2ItemMutation = generateUpdateProjectV2ItemFieldMutation({ item: itemID, author: newItemAuthors[index], turnaround: 2, @@ -189,7 +199,7 @@ async function run() { : hubberTypeID console.log(`Populating fields for item: ${itemID} with author ${newItemAuthors[index]}`) - await graphql(updateProjectNextItemMutation, { + await graphql(updateProjectV2ItemMutation, { project: projectID, statusID, statusValueID: readyForReviewID, @@ -198,12 +208,11 @@ async function run() { contributorTypeID, contributorType, sizeTypeID, - sizeType: '', // Although we aren't populating size, we are passing the variable so that we can use the shared mutation function + sizeType: sizeMediumID, // We need to provide something here, defaulting to 'medium' or 'M' featureID, authorID, headers: { authorization: `token ${process.env.TOKEN}`, - 'GraphQL-Features': 'projects_next_graphql', }, }) console.log('Done populating fields for item') diff --git a/.github/actions-scripts/projects.js b/.github/actions-scripts/projects.js index df36ee1128..1668a63124 100644 --- a/.github/actions-scripts/projects.js +++ b/.github/actions-scripts/projects.js @@ -4,7 +4,7 @@ import { graphql } from '@octokit/graphql' // Pull out the node ID of a project field export function findFieldID(fieldName, data) { - const field = data.organization.projectNext.fields.nodes.find((field) => field.name === fieldName) + const field = data.organization.projectV2.fields.nodes.find((field) => field.name === fieldName) if (field && field.id) { return field.id @@ -15,14 +15,12 @@ export function findFieldID(fieldName, data) { // Pull out the node ID of a single select field value export function findSingleSelectID(singleSelectName, fieldName, data) { - const field = data.organization.projectNext.fields.nodes.find((field) => field.name === fieldName) + const field = data.organization.projectV2.fields.nodes.find((field) => field.name === fieldName) if (!field) { throw new Error(`A field called "${fieldName}" was not found. Check if the field was renamed.`) } - const singleSelect = JSON.parse(field.settings).options.find( - (field) => field.name === singleSelectName - ) + const singleSelect = field.options.find((field) => field.name === singleSelectName) if (singleSelect && singleSelect.id) { return singleSelect.id @@ -41,11 +39,11 @@ export async function addItemsToProject(items, project) { const mutations = items.map( (item, index) => ` - item_${index}: addProjectNextItem(input: { + item_${index}: addProjectV2ItemById(input: { projectId: $project contentId: "${item}" }) { - projectNextItem { + item { id } } @@ -62,16 +60,14 @@ export async function addItemsToProject(items, project) { project, headers: { authorization: `token ${process.env.TOKEN}`, - 'GraphQL-Features': 'projects_next_graphql', }, }) // The output of the mutation is // {"item_0":{"projectNextItem":{"id":ID!}},...} // Pull out the ID for each new item - const newItemIDs = Object.entries(newItems).map((item) => item[1].projectNextItem.id) - console.log(`New item IDs: ${newItemIDs}`) + const newItemIDs = Object.entries(newItems).map((item) => item[1].item.id) return newItemIDs } @@ -139,7 +135,7 @@ export async function isGitHubOrgMember(login) { // Formats a date object into the required format for projects export function formatDateForProject(date) { - return date.getFullYear() + '-' + (date.getMonth() + 1) + '-' + date.getDate() + return date.toISOString() } // Given a date object and optional turnaround time @@ -173,7 +169,7 @@ export function calculateDueDate(datePosted, turnaround = 2) { // - "Contributor type" (as variable passed with the request) // - "Feature" (as {feature}) // - "Author" (as {author})" -export function generateUpdateProjectNextItemFieldMutation({ +export function generateUpdateProjectV2ItemFieldMutation({ item, author, turnaround = 2, @@ -184,8 +180,8 @@ export function generateUpdateProjectNextItemFieldMutation({ // Build the mutation to update a single project field // Specify literal=true to indicate that the value should be used as a string, not a variable - function generateMutationToUpdateField({ item, fieldID, value, literal = false }) { - const parsedValue = literal ? `value: "${value}"` : `value: ${value}` + function generateMutationToUpdateField({ item, fieldID, value, fieldType, literal = false }) { + const parsedValue = literal ? `${fieldType}: "${value}"` : `${fieldType}: ${value}` // Strip all non-alphanumeric out of the item ID when creating the mutation ID to avoid a GraphQL parsing error // (statistically, this should still give us a unique mutation ID) @@ -193,13 +189,13 @@ export function generateUpdateProjectNextItemFieldMutation({ set_${fieldID.slice(1)}_item_${item.replaceAll( /[^a-z0-9]/g, '' - )}: updateProjectNextItemField(input: { + )}: updateProjectV2ItemFieldValue(input: { projectId: $project itemId: "${item}" fieldId: ${fieldID} - ${parsedValue} + value: { ${parsedValue} } }) { - projectNextItem { + projectV2Item { id } } @@ -224,39 +220,46 @@ export function generateUpdateProjectNextItemFieldMutation({ item, fieldID: '$statusID', value: '$statusValueID', + fieldType: 'singleSelectOptionId', })} ${generateMutationToUpdateField({ item, fieldID: '$datePostedID', value: formatDateForProject(datePosted), + fieldType: 'date', literal: true, })} ${generateMutationToUpdateField({ item, fieldID: '$reviewDueDateID', value: formatDateForProject(dueDate), + fieldType: 'date', literal: true, })} ${generateMutationToUpdateField({ item, fieldID: '$contributorTypeID', value: '$contributorType', + fieldType: 'singleSelectOptionId', })} ${generateMutationToUpdateField({ item, fieldID: '$sizeTypeID', value: '$sizeType', + fieldType: 'singleSelectOptionId', })} ${generateMutationToUpdateField({ item, fieldID: '$featureID', value: feature, + fieldType: 'text', literal: true, })} ${generateMutationToUpdateField({ item, fieldID: '$authorID', value: author, + fieldType: 'text', literal: true, })} } @@ -274,5 +277,5 @@ export default { findSingleSelectID, formatDateForProject, calculateDueDate, - generateUpdateProjectNextItemFieldMutation, + generateUpdateProjectV2ItemFieldMutation, } diff --git a/.github/actions-scripts/ready-for-docs-review.js b/.github/actions-scripts/ready-for-docs-review.js index 636bae554f..beeedf2564 100644 --- a/.github/actions-scripts/ready-for-docs-review.js +++ b/.github/actions-scripts/ready-for-docs-review.js @@ -6,7 +6,7 @@ import { isGitHubOrgMember, findFieldID, findSingleSelectID, - generateUpdateProjectNextItemFieldMutation, + generateUpdateProjectV2ItemFieldMutation, } from './projects.js' async function run() { @@ -15,13 +15,22 @@ async function run() { ` query ($organization: String!, $projectNumber: Int!, $id: ID!) { organization(login: $organization) { - projectNext(number: $projectNumber) { + projectV2(number: $projectNumber) { id - fields(first: 20) { + fields(first: 100) { nodes { - id - name - settings + ... on ProjectV2Field { + id + name + } + ... on ProjectV2SingleSelectField { + id + name + options { + id + name + } + } } } } @@ -46,13 +55,12 @@ async function run() { projectNumber: parseInt(process.env.PROJECT_NUMBER), headers: { authorization: `token ${process.env.TOKEN}`, - 'GraphQL-Features': 'projects_next_graphql', }, } ) // Get the project ID - const projectID = data.organization.projectNext.id + const projectID = data.organization.projectV2.id // Get the ID of the fields that we want to populate const datePostedID = findFieldID('Date posted', data) @@ -168,7 +176,7 @@ async function run() { } const turnaround = process.env.REPO === 'github/docs' ? 3 : 2 // Generate a mutation to populate fields for the new project item - const updateProjectNextItemMutation = generateUpdateProjectNextItemFieldMutation({ + const updateProjectV2ItemMutation = generateUpdateProjectV2ItemFieldMutation({ item: newItemID, author: firstTimeContributor ? 'first time contributor' : process.env.AUTHOR_LOGIN, turnaround, @@ -190,7 +198,7 @@ async function run() { console.log(`Populating fields for item: ${newItemID}`) - await graphql(updateProjectNextItemMutation, { + await graphql(updateProjectV2ItemMutation, { project: projectID, statusID, statusValueID: readyForReviewID,