|
'use strict'; |
|
|
|
// Built-in modules |
|
const fs = require('node:fs'); |
|
const path = require('node:path'); |
|
|
|
// External dependencies |
|
const axios = require('axios'); |
|
|
|
// Load environment variables |
|
require('dotenv').config(); |
|
|
|
// Constants |
|
const GITHUB_API_URL = process.env.GITHUB_API_URL || 'https://api.github.com'; |
|
|
|
const GITHUB_ORG = process.env.GITHUB_ORG; |
|
if (!GITHUB_ORG || GITHUB_ORG === '') { |
|
console.error('Error: GITHUB_ORG is not defined or empty'); |
|
process.exit(1); |
|
} |
|
|
|
const GITHUB_TOKEN = process.env.GITHUB_TOKEN; |
|
if (!GITHUB_TOKEN || GITHUB_TOKEN === '') { |
|
console.error('Error: GITHUB_TOKEN is not defined or empty'); |
|
process.exit(1); |
|
} |
|
|
|
const headers = { |
|
Authorization: `Bearer ${GITHUB_TOKEN}`, |
|
Accept: 'application/vnd.github+json' |
|
}; |
|
|
|
/** |
|
* Delays execution for a specified amount of milliseconds. |
|
* |
|
* @param {number} ms - The number of milliseconds to delay. |
|
* @returns {Promise<void>} A promise that resolves after the specified delay. |
|
*/ |
|
const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms)); |
|
|
|
/** |
|
* Handles GitHub API rate limiting. |
|
* |
|
* @param {Object} headers - The response headers from the GitHub API. |
|
* @returns {Promise<void>} A promise that resolves after the necessary delay. |
|
*/ |
|
const handleRateLimit = async (headers) => { |
|
const remaining = parseInt(headers['x-ratelimit-remaining'], 10); |
|
const resetTime = parseInt(headers['x-ratelimit-reset'], 10) * 1000; |
|
const currentTime = Date.now(); |
|
|
|
if (remaining <= 1) { |
|
const delayTime = resetTime - currentTime; |
|
console.log(`Rate limit reached. Waiting for ${Math.max(0, delayTime) / 1000} seconds`); |
|
await delay(Math.max(0, delayTime)); |
|
} else { |
|
await delay(2000); // Slight delay to stay under rate limit |
|
} |
|
}; |
|
|
|
/** |
|
* Fetches all repositories in the specified organization. |
|
* |
|
* @param {string} orgName - The name of the GitHub organization. |
|
* @param {number} [page=1] - The page number for pagination. |
|
* @returns {Promise<Array<Object>>} List of repository objects. |
|
*/ |
|
const fetchAllRepos = async (orgName, page = 1) => { |
|
try { |
|
const response = await axios.get(`${GITHUB_API_URL}/orgs/${orgName}/repos`, { |
|
headers, |
|
params: { per_page: 100, page } |
|
}); |
|
|
|
// Handle rate limit |
|
await handleRateLimit(response.headers); |
|
|
|
// Filter out archived repositories |
|
const repos = response.data.filter(repo => !repo.archived && !repo.fork && !repo.disabled); |
|
|
|
// If there are more pages, recursively fetch |
|
if (repos.length === 100) { |
|
const nextPageRepos = await fetchAllRepos(orgName, page + 1); |
|
return repos.concat(nextPageRepos); |
|
} |
|
|
|
return repos; |
|
} catch (error) { |
|
console.error('Error fetching repositories:', error.message); |
|
if (error.response && error.response.status === 403) { |
|
await handleRateLimit(error.response.headers); |
|
return fetchAllRepos(orgName, page); |
|
} |
|
return []; |
|
} |
|
}; |
|
|
|
/** |
|
* Checks if a CODEOWNERS file exists in the specified repository. |
|
* |
|
* @param {string} owner - The owner of the repository. |
|
* @param {string} repo - The name of the repository. |
|
* @returns {Promise<boolean>} True if the CODEOWNERS file exists, false otherwise. |
|
*/ |
|
const checkCodeownersFile = async (owner, repo) => { |
|
const codeownersPath = 'CODEOWNERS'; |
|
try { |
|
await axios.get(`${GITHUB_API_URL}/repos/${owner}/${repo}/contents/${codeownersPath}`, { |
|
headers |
|
}); |
|
return true; // File exists |
|
} catch (error) { |
|
if (error.response && error.response.status === 404) { |
|
return false; // File does not exist |
|
} |
|
console.error(`Error checking CODEOWNERS for ${repo}:`, error.message); |
|
return false; // Assume file doesn't exist on error |
|
} |
|
}; |
|
|
|
/** |
|
* Main function to find repositories without CODEOWNERS. |
|
*/ |
|
const findReposWithoutCodeowners = async () => { |
|
const repos = await fetchAllRepos(GITHUB_ORG); |
|
const reposWithoutCodeowners = []; |
|
|
|
for (const repo of repos) { |
|
const hasCodeowners = await checkCodeownersFile(repo.owner.login, repo.name); |
|
if (!hasCodeowners) { |
|
reposWithoutCodeowners.push(repo.name); |
|
} |
|
} |
|
|
|
console.log(`Repositories without CODEOWNERS: ${reposWithoutCodeowners}`); |
|
}; |
|
|
|
// Execute the script |
|
findReposWithoutCodeowners() |
|
.catch(error => { |
|
console.error('An unexpected error occurred:', error.message); |
|
}); |