Skip to content

Commit 920bb71

Browse files
authored
Fix update_project to use custom GitHub token for cross-org project access (#13276)
1 parent ee76275 commit 920bb71

File tree

1 file changed

+28
-14
lines changed

1 file changed

+28
-14
lines changed

actions/setup/js/update_project.cjs

Lines changed: 28 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -103,9 +103,10 @@ function parseProjectUrl(projectUrl) {
103103
/**
104104
* List accessible Projects v2 for org or user
105105
* @param {{ scope: string, ownerLogin: string, projectNumber: string }} projectInfo - Project info
106+
* @param {Object} github - GitHub client (Octokit instance) to use for GraphQL queries
106107
* @returns {Promise<{ nodes: Array<{ id: string, number: number, title: string, closed?: boolean, url: string }>, totalCount?: number, diagnostics: { rawNodesCount: number, nullNodesCount: number, rawEdgesCount: number, nullEdgeNodesCount: number } }>} List result
107108
*/
108-
async function listAccessibleProjectsV2(projectInfo) {
109+
async function listAccessibleProjectsV2(projectInfo, github) {
109110
const baseQuery = `projectsV2(first: 100) {
110111
totalCount
111112
nodes {
@@ -204,9 +205,10 @@ function summarizeEmptyProjectsV2List(list) {
204205
* Resolve a project by number
205206
* @param {{ scope: string, ownerLogin: string, projectNumber: string }} projectInfo - Project info
206207
* @param {number} projectNumberInt - Project number
208+
* @param {Object} github - GitHub client (Octokit instance) to use for GraphQL queries
207209
* @returns {Promise<{ id: string, number: number, title: string, url: string }>} Project details
208210
*/
209-
async function resolveProjectV2(projectInfo, projectNumberInt) {
211+
async function resolveProjectV2(projectInfo, projectNumberInt, github) {
210212
try {
211213
const query =
212214
projectInfo.scope === "orgs"
@@ -243,7 +245,7 @@ async function resolveProjectV2(projectInfo, projectNumberInt) {
243245
core.warning(`Direct projectV2(number) query failed; falling back to projectsV2 list search: ${getErrorMessage(error)}`);
244246
}
245247

246-
const list = await listAccessibleProjectsV2(projectInfo);
248+
const list = await listAccessibleProjectsV2(projectInfo, github);
247249
const nodes = Array.isArray(list.nodes) ? list.nodes : [];
248250
const found = nodes.find(p => p && typeof p.number === "number" && p.number === projectNumberInt);
249251

@@ -334,18 +336,20 @@ function checkFieldTypeMismatch(fieldName, field, expectedDataType) {
334336
);
335337
return false; // Continue with existing field type
336338
}
337-
/**
338-
* Update a GitHub Project v2
339-
* @param {any} output - Safe output configuration
340-
* @returns {Promise<void>}
341-
*/
342339
/**
343340
* Update a GitHub Project v2
344341
* @param {any} output - Safe output configuration
345342
* @param {Map<string, any>} temporaryIdMap - Map of temporary IDs to resolved issue numbers
343+
* @param {Object} githubClient - GitHub client (Octokit instance) to use for GraphQL queries
346344
* @returns {Promise<void>}
347345
*/
348-
async function updateProject(output, temporaryIdMap) {
346+
async function updateProject(output, temporaryIdMap = new Map(), githubClient = null) {
347+
// Use the provided github client, or fall back to the global github object
348+
// @ts-ignore - global.github is set by setupGlobals() from github-script context
349+
const github = githubClient || global.github;
350+
if (!github) {
351+
throw new Error("GitHub client is required but not provided. Either pass a github client to updateProject() or ensure global.github is set.");
352+
}
349353
const { owner, repo } = context.repo;
350354
const projectInfo = parseProjectUrl(output.project);
351355
const projectNumberFromUrl = projectInfo.projectNumber;
@@ -415,7 +419,7 @@ async function updateProject(output, temporaryIdMap) {
415419
if (!Number.isFinite(projectNumberInt)) {
416420
throw new Error(`Invalid project number parsed from URL: ${projectNumberFromUrl}`);
417421
}
418-
const project = await resolveProjectV2(projectInfo, projectNumberInt);
422+
const project = await resolveProjectV2(projectInfo, projectNumberInt, github);
419423
projectId = project.id;
420424
resolvedProjectNumber = String(project.number);
421425
core.info(`✓ Resolved project #${resolvedProjectNumber} (${projectInfo.ownerLogin}) (ID: ${projectId})`);
@@ -997,9 +1001,19 @@ async function updateProject(output, temporaryIdMap) {
9971001
* @param {number} [config.max] - Maximum number of update_project items to process
9981002
* @param {Array<Object>} [config.views] - Views to create from configuration
9991003
* @param {Array<Object>} [config.field_definitions] - Field definitions to create from configuration
1004+
* @param {Object} githubClient - GitHub client (Octokit instance) to use for API calls
10001005
* @returns {Promise<Function>} Message handler function
10011006
*/
1002-
async function main(config = {}) {
1007+
async function main(config = {}, githubClient = null) {
1008+
// Use the provided github client, or fall back to the global github object
1009+
// The global github object is available when running via github-script action
1010+
// @ts-ignore - global.github is set by setupGlobals() from github-script context
1011+
const github = githubClient || global.github;
1012+
1013+
if (!github) {
1014+
throw new Error("GitHub client is required but not provided. Either pass a github client to main() or ensure global.github is set by github-script action.");
1015+
}
1016+
10031017
// Extract configuration
10041018
// Default is intentionally configurable via safe-outputs.update-project.max,
10051019
// but we keep a sane global default to avoid surprising truncation.
@@ -1113,7 +1127,7 @@ async function main(config = {}) {
11131127
};
11141128

11151129
try {
1116-
await updateProject(fieldsOutput, temporaryIdMap);
1130+
await updateProject(fieldsOutput, temporaryIdMap, github);
11171131
core.info("✓ Created configured fields");
11181132
} catch (err) {
11191133
// prettier-ignore
@@ -1131,7 +1145,7 @@ async function main(config = {}) {
11311145
}
11321146

11331147
// Process the update_project message
1134-
await updateProject(effectiveMessage, temporaryIdMap);
1148+
await updateProject(effectiveMessage, temporaryIdMap, github);
11351149

11361150
// After processing the first message, create configured views if any
11371151
// Views are created after the first item is processed to ensure the project exists
@@ -1156,7 +1170,7 @@ async function main(config = {}) {
11561170
},
11571171
};
11581172

1159-
await updateProject(viewOutput, temporaryIdMap);
1173+
await updateProject(viewOutput, temporaryIdMap, github);
11601174
core.info(`✓ Created view ${i + 1}/${configuredViews.length}: ${viewConfig.name} (${viewConfig.layout})`);
11611175
} catch (err) {
11621176
// prettier-ignore

0 commit comments

Comments
 (0)