diff --git a/README.md b/README.md index 4cb114e..db8f947 100644 --- a/README.md +++ b/README.md @@ -68,6 +68,13 @@ GH_ADMIN_TOKEN= pin-github-action /path/to/.github/workflows/yo Run it as many times as you like! Each time you run the tool the exact sha will be updated to the latest available sha for your pinned ref. +If you're having issues, run with debug logging enabled and open an issue: + +```bash +DEBUG="pin-github-action*" pin-github-action /path/to/.github/workflows/your-name.yml + +``` + ## Leaving Actions unpinned To leave an action unpinned, pass the `--allow` option when running `pin-github-action`. diff --git a/bin.js b/bin.js index 7985d99..39dcf1b 100755 --- a/bin.js +++ b/bin.js @@ -3,6 +3,7 @@ const fs = require("fs"); const path = require("path"); const program = require("commander"); +const debug = require("debug")("pin-github-action"); const run = require("."); @@ -40,7 +41,7 @@ const packageDetails = require(path.join(__dirname, "package.json")); const input = fs.readFileSync(filename).toString(); let allowEmpty = program.opts().allowEmpty; - const output = await run(input, allowed, ignoreShas, allowEmpty); + const output = await run(input, allowed, ignoreShas, allowEmpty, debug); fs.writeFileSync(filename, output.workflow); diff --git a/checkAllowedRepos.js b/checkAllowedRepos.js index f550a9e..3d387c1 100644 --- a/checkAllowedRepos.js +++ b/checkAllowedRepos.js @@ -1,6 +1,8 @@ const matcher = require("matcher"); -module.exports = function (input, ignored) { +let debug = () => {}; +module.exports = function (input, ignored, log) { + debug = log.extend("check-allowed-repos"); // Nothing ignored, so it can't match if (ignored.length === 0) { return false; @@ -8,9 +10,15 @@ module.exports = function (input, ignored) { // Exact match if (ignored.includes(input)) { + debug(`Skipping ${input} due to exact match in ${ignored}`); return true; } // Glob match - return matcher([input], ignored).length > 0; + const isMatch = matcher([input], ignored).length > 0; + + if (isMatch) { + debug(`Skipping ${input} due to pattern match in ${ignored}`); + } + return isMatch; }; diff --git a/checkIgnoredRepos.test.js b/checkIgnoredRepos.test.js index a2e467d..a19372c 100644 --- a/checkIgnoredRepos.test.js +++ b/checkIgnoredRepos.test.js @@ -1,4 +1,8 @@ -const checkAllowedRepos = require("./checkAllowedRepos"); +const debug = require("debug")("pin-github-action-test"); +const run = require("./checkAllowedRepos"); +const checkAllowedRepos = (input, ignore) => { + return run.apply(null, [input, ignore, debug]); +}; test("empty allow list", () => { const actual = checkAllowedRepos("mheap/demo", []); diff --git a/extractActions.js b/extractActions.js index cad4c6a..7d5f7c4 100644 --- a/extractActions.js +++ b/extractActions.js @@ -1,10 +1,14 @@ -module.exports = function (input, allowEmpty) { +let debug = () => {}; +module.exports = function (input, allowEmpty, log) { + debug = log.extend("extract-actions"); // Check if it's a composite action let runs = input.contents.items.filter((n) => n.key == "runs"); if (runs.length) { + debug("Processing composite action"); return extractFromComposite(input, allowEmpty); } + debug("Processing workflow"); return extractFromWorkflow(input, allowEmpty); }; @@ -66,9 +70,11 @@ function handleStep(actions, step) { const line = use.value.value.toString(); if (line.substr(0, 9) == "docker://") { + debug(`Skipping docker:// action: '${line}'`); continue; } if (line.substr(0, 2) == "./") { + debug(`Skipping local action: '${line}'`); continue; } diff --git a/extractActions.test.js b/extractActions.test.js index 49452a8..07e19f7 100644 --- a/extractActions.test.js +++ b/extractActions.test.js @@ -1,5 +1,10 @@ -const extractActions = require("./extractActions"); const YAML = require("yaml"); +const debug = require("debug")("pin-github-action-test"); + +const run = require("./extractActions"); +const extractActions = (input, allowEmpty) => { + return run.apply(null, [input, allowEmpty, debug]); +}; test("extracts a single version", () => { const input = convertToAst({ diff --git a/findRefOnGithub.js b/findRefOnGithub.js index cc8eadf..c78f8b2 100644 --- a/findRefOnGithub.js +++ b/findRefOnGithub.js @@ -3,11 +3,14 @@ const github = new Octokit({ auth: process.env.GH_ADMIN_TOKEN, }); -module.exports = function (action) { +let debug = () => {}; +module.exports = function (action, log) { + debug = log.extend("find-ref-on-github"); return new Promise(async function (resolve, reject) { const owner = action.owner; const repo = action.repo; const pinned = action.pinnedVersion; + const name = `${owner}/${repo}`; let error; @@ -15,6 +18,7 @@ module.exports = function (action) { const possibleRefs = [`tags/${pinned}`, `heads/${pinned}`]; for (let ref of possibleRefs) { try { + debug(`Fetching ref ${ref}`); const object = ( await github.git.getRef({ owner, @@ -25,28 +29,34 @@ module.exports = function (action) { // If it's a tag, fetch the commit hash instead if (object.type === "tag") { + debug(`[${name}] Ref is a tag. Fetch commit hash instead`); // Fetch the commit hash instead const tag = await github.git.getTag({ owner, repo, tag_sha: object.sha, }); + debug(`[${name}] Fetched commit. Found sha.`); return resolve(tag.data.object.sha); } // If it's already a commit, return that if (object.type === "commit") { + debug(`[${name}] Ref is a commit. Found sha.`); return resolve(object.sha); } } catch (e) { // We can ignore failures as we validate later - //console.log(e); - error = handleCommonErrors(e); + debug(`[${name}] Error fetching ref: ${e.message}`); + error = handleCommonErrors(e, name); } } // If we get this far, have we been provided with a specific commit SHA? try { + debug( + `[${name}] Provided version is not a ref. Checking if it's a commit SHA` + ); const commit = await github.repos.getCommit({ owner, repo, @@ -55,8 +65,8 @@ module.exports = function (action) { return resolve(commit.data.sha); } catch (e) { // If it's not a commit, it doesn't matter - //console.log(e); - error = handleCommonErrors(e); + debug(`[${name}] Error fetching commit: ${e.message}`); + error = handleCommonErrors(e, name); } return reject( @@ -65,12 +75,16 @@ module.exports = function (action) { }); }; -function handleCommonErrors(e) { +function handleCommonErrors(e, name) { if (e.status == 404) { + debug( + `[${name}] ERROR: Could not find repo. It may be private, or it may not exist` + ); return "Private repos require you to set process.env.GH_ADMIN_TOKEN to fetch the latest SHA"; } if (e.message.includes("API rate limit exceeded")) { + debug(`[${name}] ERROR: Rate Limiting error`); return e.message; } return; diff --git a/findRefOnGithub.test.js b/findRefOnGithub.test.js index 75c1ce3..c41586d 100644 --- a/findRefOnGithub.test.js +++ b/findRefOnGithub.test.js @@ -1,7 +1,12 @@ -const findRef = require("./findRefOnGithub"); const nock = require("nock"); nock.disableNetConnect(); +const debug = require("debug")("pin-github-action-test"); +const run = require("./findRefOnGithub"); +const findRef = (action) => { + return run.apply(null, [action, debug]); +}; + const action = { owner: "nexmo", repo: "github-actions", diff --git a/index.js b/index.js index f32ece9..1d8bab0 100644 --- a/index.js +++ b/index.js @@ -6,7 +6,13 @@ const findRefOnGithub = require("./findRefOnGithub"); const checkAllowedRepos = require("./checkAllowedRepos"); const isSha = require("./isSha"); -module.exports = async function (input, allowed, ignoreShas, allowEmpty) { +module.exports = async function ( + input, + allowed, + ignoreShas, + allowEmpty, + debug +) { allowed = allowed || []; ignoreShas = ignoreShas || false; @@ -14,12 +20,12 @@ module.exports = async function (input, allowed, ignoreShas, allowEmpty) { let workflow = YAML.parseDocument(input); // Extract list of actions - let actions = extractActions(workflow, allowEmpty); + let actions = extractActions(workflow, allowEmpty, debug); for (let i in actions) { // Should this action be updated? const action = `${actions[i].owner}/${actions[i].repo}`; - if (checkAllowedRepos(action, allowed)) { + if (checkAllowedRepos(action, allowed, debug)) { continue; } @@ -28,7 +34,7 @@ module.exports = async function (input, allowed, ignoreShas, allowEmpty) { } // Look up those actions on Github - const newVersion = await findRefOnGithub(actions[i]); + const newVersion = await findRefOnGithub(actions[i], debug); actions[i].newVersion = newVersion; // Rewrite each action, replacing the uses block with a specific sha diff --git a/package-lock.json b/package-lock.json index 9fab3c0..fb1980c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,11 +6,12 @@ "packages": { "": { "name": "pin-github-action", - "version": "1.4.0", + "version": "1.5.0", "license": "MIT", "dependencies": { "@octokit/rest": "^18", "commander": "^8", + "debug": "^4.3.4", "matcher": "^4.0.0", "yaml": "^1" }, @@ -1776,10 +1777,9 @@ } }, "node_modules/debug": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", - "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", - "dev": true, + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dependencies": { "ms": "2.1.2" }, @@ -3804,8 +3804,7 @@ "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "node_modules/natural-compare": { "version": "1.4.0", @@ -6364,10 +6363,9 @@ } }, "debug": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", - "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", - "dev": true, + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "requires": { "ms": "2.1.2" } @@ -7908,8 +7906,7 @@ "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "natural-compare": { "version": "1.4.0", diff --git a/package.json b/package.json index 3b9b181..39f8d21 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "dependencies": { "@octokit/rest": "^18", "commander": "^8", + "debug": "^4.3.4", "matcher": "^4.0.0", "yaml": "^1" },