await-github-file-change-cli
Version:
CLI tool to monitor GitHub file changes by polling for etag changes
112 lines (94 loc) • 3.17 kB
JavaScript
import { Octokit } from "octokit";
/**
* Parse GitHub URL to extract owner, repo, ref, and path
* @param {string} url - GitHub URL like https://github.com/owner/repo/blob/branch/path/to/file
* @returns {{owner: string, repo: string, ref: string, path: string}} Parsed components
*/
function parseGitHubUrl(url) {
const regex = /github\.com\/([^/]+)\/([^/]+)\/blob\/([^/]+)\/(.+)/;
const match = url.match(regex);
if (!match) {
throw new Error("Invalid GitHub URL. Expected format: https://github.com/owner/repo/blob/branch/path/to/file");
}
return {
owner: match[1],
repo: match[2],
ref: match[3],
path: match[4]
};
}
/**
* Get the etag for a file
* @param {Octokit} octokit - Octokit instance
* @param {string} owner - Repository owner
* @param {string} repo - Repository name
* @param {string} path - File path
* @param {string} ref - Branch name or commit SHA
* @returns {Promise<string>} The etag value
*/
async function getEtag(octokit, owner, repo, path, ref) {
const response = await octokit.request('HEAD /repos/{owner}/{repo}/contents/{path}', {
owner,
repo,
path,
ref,
request: {
method: 'HEAD'
}
});
// normalize, e.g. "W/"abc123"" -> "abc123"
return response.headers.etag.replace(/^W\//, '');
}
/**
* Wait for etag to change
* @param {Octokit} octokit - Octokit instance
* @param {string} owner - Repository owner
* @param {string} repo - Repository name
* @param {string} path - File path
* @param {string} ref - Branch name or commit SHA
* @param {string} initialEtag - Initial etag to compare against
*/
async function waitForEtagChange(octokit, owner, repo, path, ref, initialEtag) {
return new Promise((resolve, reject) => {
const interval = setInterval(async () => {
try {
const currentEtag = await getEtag(octokit, owner, repo, path, ref);
if (currentEtag !== initialEtag) {
clearInterval(interval);
resolve(currentEtag);
}
} catch (error) {
clearInterval(interval);
reject(error);
}
}, 1000);
});
}
/**
* Main function
*/
async function main() {
const url = process.argv[2];
if (!url) {
console.error("Error: GitHub URL is required");
console.error("Usage: await-github-file-change <github-url>");
console.error("Example: await-github-file-change https://github.com/gr2m/sandbox/blob/main/test-file");
process.exit(1);
}
try {
const { owner, repo, ref, path } = parseGitHubUrl(url);
const octokit = new Octokit({
auth: process.env.GITHUB_TOKEN
});
console.log(`Monitoring ${owner}/${repo}/${path} (ref: ${ref}) for changes...`);
const initialEtag = await getEtag(octokit, owner, repo, path, ref);
console.log(`Initial etag: ${initialEtag}`);
const newEtag = await waitForEtagChange(octokit, owner, repo, path, ref, initialEtag);
console.log(`File changed! New etag: ${newEtag}`);
process.exit(0);
} catch (error) {
console.error(`Error: ${error.message}`);
process.exit(1);
}
}
export { parseGitHubUrl, getEtag, waitForEtagChange, main };