Skip to content

Instantly share code, notes, and snippets.

@slaughtr
Created March 19, 2024 20:18
Show Gist options
  • Save slaughtr/14e3e69c8c1eba17a8576390ddc71f56 to your computer and use it in GitHub Desktop.
Save slaughtr/14e3e69c8c1eba17a8576390ddc71f56 to your computer and use it in GitHub Desktop.
Find if a specific action is present in the workflows of all repos in an org. Outputs CSV for review. Not perfect, Github API doesn't like some things.
const { Octokit } = require("@octokit/rest");
const createCsvWriter = require('csv-writer').createObjectCsvWriter;
// Initialize Octokit with your GitHub token from environment variable
const octokit = new Octokit({ auth: process.env.GITHUB_TOKEN });
// CSV setup
const csvWriter = createCsvWriter({
path: 'compliance_check_result.csv',
header: [
{id: 'repo_name', title: 'Repo Name'},
{id: 'action_present', title: 'Action Present'},
{id: 'repo_status', title: 'Repo Status'}
]
});
// Delay function to avoid hitting rate limits
function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function fetchFileContent(owner, repo, path) {
try {
await delay(3000); // Delay added before fetching content to reduce rate limit hits
const { data } = await octokit.repos.getContent({
owner,
repo,
path,
});
console.log(`Fetched ${owner}/${repo}/${path} successfully.`)
return Buffer.from(data.content, 'base64').toString();
} catch (error) {
console.error(`Error fetching ${owner}/${repo}/${path} content: ${error}`);
return null;
}
}
async function checkWorkflowForAction(org, actionToFind) {
let results = [];
const repos = await octokit.paginate(octokit.repos.listForOrg, {
org,
type: 'all'
});
for (const repo of repos) {
let actionPresent = 'no';
await delay(1000); // Delay added between repository checks
const workflows = await octokit.paginate(octokit.actions.listRepoWorkflows, {
owner: org,
repo: repo.name
});
for (const workflow of workflows) {
await delay(2000); // Delay added between workflow checks
const content = await fetchFileContent(org, repo.name, workflow.path);
if (content && content.includes(actionToFind)) {
actionPresent = 'yes';
break;
}
}
const pullRequests = await octokit.paginate(octokit.pulls.list, {
owner: org,
repo: repo.name,
state: 'open'
});
for (const pr of pullRequests) {
await delay(2000); // Delay added between pull request checks
const files = await octokit.paginate(octokit.pulls.listFiles, {
owner: org,
repo: repo.name,
pull_number: pr.number
});
for (const file of files) {
if (file.filename.endsWith('.yml')) {
const content = await fetchFileContent(org, repo.name, file.filename);
if (content && content.includes(actionToFind)) {
actionPresent = 'pull request';
break;
}
}
}
if (actionPresent === 'pull request') {
break;
}
}
results.push({
repo_name: repo.name,
action_present: actionPresent,
repo_status: repo.private ? 'private' : repo.is_template ? 'template' : repo.archived ? 'archived' : 'public'
});
}
await csvWriter.writeRecords(results)
.then(() => console.log('The CSV file was written successfully'));
}
// Use environment variables for org name and action name
const orgName = process.env.ORG_NAME;
const actionName = process.env.ACTION_NAME;
if (!orgName || !actionName || !process.env.GITHUB_TOKEN) {
console.log('Please set the GITHUB_TOKEN, ACTION_NAME, and ORG_NAME environment variables.');
} else {
checkWorkflowForAction(orgName, actionName).catch(console.error);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment