From 2ec81842923c549314674629b2c8030305c4ba3a Mon Sep 17 00:00:00 2001 From: Scott Dickerson Date: Fri, 15 Sep 2023 09:59:10 -0400 Subject: [PATCH] :seedling: Add checks for package-lock.json to lint-staged and CI (#1365) Following up on #1357, we now know that while a `package-lock.json` can be valid and build upstream without a `resolved` field for each external package, for a downstream build it is not valid. In a network detached downstream build environment, a dependency downloader will be needed to fetch and archive all of the project's dependencies (see cachito). Without the lock file's resolved fields, the downloader cannot ensure the source of the packages and will fail. To help identify this situation, the `verify_lock.mjs` script will look for this invalid pattern and report back. The script will be called automatically when: - (via lint-staged) a git commit is being created and it includes a change to `package-lock.json` - (via github actions) as part of CI tests when a PR is submitted or updated With both of these test points in place, it should be difficult to merge a change to the lockfile that will break a downstream build. Signed-off-by: Scott J Dickerson Co-authored-by: Ian Bolton --- .github/workflows/ci-actions.yml | 5 ++- package.json | 3 +- scripts/verify_lock.mjs | 66 ++++++++++++++++++++++++++++++++ 3 files changed, 72 insertions(+), 2 deletions(-) create mode 100755 scripts/verify_lock.mjs diff --git a/.github/workflows/ci-actions.yml b/.github/workflows/ci-actions.yml index 1456ef1fa0..cec3dd8a9f 100644 --- a/.github/workflows/ci-actions.yml +++ b/.github/workflows/ci-actions.yml @@ -27,8 +27,11 @@ jobs: with: node-version: ${{ matrix.node-version }} + - name: Verify package-lock.json + run: ./scripts/verify_lock.mjs + - name: Install - run: npm clean-install + run: npm clean-install --ignore-scripts - name: Lint sources run: npm run lint diff --git a/package.json b/package.json index bcf12b0411..d31cefbc8c 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,8 @@ "port-forward": "concurrently -c auto 'npm:port-forward:*'" }, "lint-staged": { - "*": "prettier --ignore-unknown --write" + "package-lock.json": "./scripts/verify_lock.mjs", + "!(package-lock.json)*": "prettier --ignore-unknown --write" }, "workspaces": [ "common", diff --git a/scripts/verify_lock.mjs b/scripts/verify_lock.mjs new file mode 100755 index 0000000000..99ffdec310 --- /dev/null +++ b/scripts/verify_lock.mjs @@ -0,0 +1,66 @@ +#!/usr/bin/env node + +import process from "node:process"; +import path from "node:path"; +import { readFileSync } from "node:fs"; + +// set the working directory to project root +// fs.accessSync("./package-lock.json") +const getProjectRoot = () => path.resolve(path.dirname(process.argv[1]), "../"); +process.chdir(getProjectRoot()); + +// load the lock file +const lockFilePath = path.resolve(process.cwd(), "package-lock.json"); +const lockFile = JSON.parse(readFileSync(lockFilePath)); + +const toLog = { + name: lockFile.name, + version: lockFile.version, + lockfileVersion: lockFile.lockfileVersion, +}; + +// check the packages packages +const removeUndefined = (obj) => + Object.fromEntries(Object.entries(obj).filter((e) => e[1] !== undefined)); + +const results = { + project: [], + resolved: [], + unresolved: [], +}; +Object.entries(lockFile.packages).forEach(([name, p]) => { + const bucket = p.name?.startsWith("@konveyor-ui") + ? results.project + : p.resolved + ? results.resolved + : results.unresolved; + + bucket.push( + removeUndefined({ + name, + version: p.version, + resolved: p.resolved, + packageName: p.name, + }) + ); +}); + +// log findings +toLog.packages = results.project; +toLog.dependencies = { + countResolved: results.resolved.length, + countUnresolved: results.unresolved.length, + unresolved: results.unresolved, +}; + +console.log(`package-lock.json (${lockFilePath}) status:`); +console.dir(toLog, { depth: 3 }); +console.log(); +if (results.unresolved.length === 0) { + console.log("\u{1f600} lock file is ok!"); +} else { + console.log("\u{1f621} lock file contains unresolved dependencies!"); +} + +// exit the script with an appropriate error code +process.exit(results.unresolved.length === 0 ? 0 : 1);