Files
oxc-project__oxc/.github/scripts/get-changed-files.js

141 lines
4.1 KiB
JavaScript

#!/usr/bin/env node
// oxlint-disable no-console
/**
* Get changed files from GitHub events (pull request or push).
* This module provides a reusable function for detecting changed files.
*/
const https = require("https");
const process = require("process");
/**
* Make a GitHub API request
* @param {string} path - API path
* @returns {Promise<any>} API response
*/
function githubApi(path) {
return new Promise((resolve, reject) => {
const options = {
hostname: "api.github.com",
path,
headers: {
"User-Agent": "oxc-changed-files",
Accept: "application/vnd.github.v3+json",
},
};
// Add authorization if token is available
const token = process.env.GITHUB_TOKEN;
if (token) {
options.headers["Authorization"] = `token ${token}`;
}
https
.get(options, (res) => {
let data = "";
res.on("data", (chunk) => (data += chunk));
res.on("end", () => {
if (res.statusCode === 200) {
resolve(JSON.parse(data));
} else {
reject(new Error(`GitHub API error: ${res.statusCode} ${data}`));
}
});
})
.on("error", reject);
});
}
/**
* Get all changed files for a PR, paginating through all pages
* @param {string} repository - Repository name (owner/repo)
* @param {string} prNumber - Pull request number
* @returns {Promise<string[]>} Array of changed file paths
*/
async function getPrChangedFiles(repository, prNumber) {
const perPage = 100;
const files = [];
let page = 1;
let prFiles;
do {
// oxlint-disable-next-line no-await-in-loop -- sequential pagination requires await in loop
prFiles = await githubApi(
`/repos/${repository}/pulls/${prNumber}/files?per_page=${perPage}&page=${page}`,
);
for (const f of prFiles) {
files.push(f.filename);
}
page++;
} while (prFiles.length >= perPage);
return files;
}
/**
* Get changed files based on the GitHub event type
* @returns {Promise<string[] | null>} Array of changed file paths, or null to signal "run all"
*/
async function getChangedFiles() {
const eventName = process.env.GITHUB_EVENT_NAME;
const repository = process.env.GITHUB_REPOSITORY;
const sha = process.env.GITHUB_SHA;
const prNumber = process.env.GITHUB_PR_NUMBER;
const ref = process.env.GITHUB_REF;
console.error(`Event: ${eventName}`);
console.error(`Repository: ${repository}`);
console.error(`SHA: ${sha}`);
console.error(`Ref: ${ref}`);
if (eventName === "workflow_dispatch") {
console.error("Manual trigger - returning null (run all)");
return null; // Signal to run all
}
let files = [];
try {
if (eventName === "pull_request" && prNumber) {
// For PR, use GitHub API to get changed files (paginated)
console.error(`Getting changed files for PR #${prNumber}`);
files = await getPrChangedFiles(repository, prNumber);
} else if (sha && repository) {
// For push to main, get the commit and compare with parent
console.error(`Getting changed files for commit ${sha}`);
const commit = await githubApi(`/repos/${repository}/commits/${sha}`);
files = commit.files ? commit.files.map((f) => f.filename) : [];
} else {
// No valid parameters for API calls
console.error("Error: Missing required environment variables for GitHub API");
console.error("Returning null (run all) as fallback");
return null; // Signal to run all
}
} catch (error) {
console.error(`Error getting changed files via API: ${error.message}`);
console.error("Returning null (run all) as fallback");
return null; // Signal to run all
}
console.error(`Changed files (${files.length}):`);
files.forEach((f) => console.error(` - ${f}`));
return files;
}
module.exports = { getChangedFiles };
// If run directly as a script, output changed files as JSON
if (require.main === module) {
getChangedFiles()
.then((files) => {
console.log(JSON.stringify(files));
process.exit(0);
})
.catch((error) => {
console.error("Error:", error);
console.log(JSON.stringify(null));
process.exit(1);
});
}