WorkingDirectory icon
Clone icon
Commands icon
Process icon
Docker icon
If icon
Comment icon
Switch icon
Fail icon
AllowFailure icon
Log icon
Webhook icon

Verify an npm dependency upgrade against the project's own tests before it is merged

Check whether an npm upgrade is safe before merging. Kestra runs your tests on the current and upgraded lockfile, offline, and blocks real breakage.

Categories
Infrastructure

Answer one question before a dependency upgrade is merged: can this repository move package X from version A to version B?

Bots such as Dependabot and Renovate open upgrade pull requests, and audit or "outdated" checks tell you what is old or vulnerable. Neither tells you whether the upgrade still works with your code. This blueprint verifies one proposed upgrade. It runs the project's own npm test on the current lockfile (baseline), re-resolves the lockfile with the requested version (candidate), runs the same tests on the candidate, compares the two dependency trees, and returns a verdict with deterministic reason codes. It does not scan for vulnerabilities, licences or outdated packages, and it never opens upgrade pull requests.

How it works

  1. clone (io.kestra.plugin.git.Clone) checks out the exact commit, without submodules.
  2. verify runs on the Kestra worker before any container starts. It checks the checkout is that commit, refuses repositories containing symbolic links, and keeps project_path inside the clone.
  3. prepare (network on, install scripts off) removes the repository's .npmrc, forces the public npm registry, and refuses lockfiles that download anything from another host. It then runs npm install <package>@<version> --package-lock-only --ignore-scripts to build the candidate lockfile, compares it with the baseline (direct change, added, removed and changed packages, deprecations, install scripts, Node engines), records SHA-256 hashes of both lockfiles, and fills an npm cache for both.
  4. test runs both installs and both test suites in one offline container (networkMode: none, CPU and memory limits, timeouts, no secrets). Each lockfile is checked against its recorded hash, npm ci --offline --ignore-scripts installs from the prepared cache only, and npm test --ignore-scripts runs as the unprivileged node user. node_modules is deleted before the task ends, so it never crosses a task boundary.
  5. classify turns the signals into a verdict and writes report.json.
  6. pr_comment (io.kestra.plugin.github.issues.Comment) posts the verdict on the pull request when pr_number is set. updateTag keeps one comment per pull request, project and package, updated on every run.
  7. verdict (Switch) fails the execution on BLOCK and ends INCONCLUSIVE runs in WARNING, so a pipeline waiting on the gate can tell them apart.

Verdicts

The first group that matches wins.

Verdict Execution state Reason codes
INCONCLUSIVE WARNING BASELINE_FAILED (current lockfile already fails install or tests), INFRASTRUCTURE_ERROR (registry unreachable or a step timed out), LOCKFILE_MISSING, UNSUPPORTED_PROJECT (lockfile v1, npm workspaces or linked packages)
BLOCK FAILED RESOLUTION_FAILED, PACKAGE_NOT_DIRECT, CANDIDATE_INSTALL_FAILED, CANDIDATE_TEST_FAILED (tests fail with the upgrade and pass without it), NODE_ENGINE_CONFLICT (a changed package does not support the runtime Node version), NON_REGISTRY_DEPENDENCY, LOCKFILE_HASH_MISMATCH
WARN SUCCESS DEPRECATED_PACKAGE (a package the upgrade adds or changes is deprecated), INSTALL_SCRIPT_PRESENT (it adds install scripts, which were not run), NO_TEST_SCRIPT (installed, but nothing to test)
PASS SUCCESS none

Inputs

Name Type Default Description
repo_url STRING demo repository Public GitHub repository, https://github.com/<owner>/<repo>. No other host, scheme, port, credentials or query.
commit STRING demo commit Full 40-character commit SHA. Branch names are rejected.
project_path STRING npm/app Folder holding package.json and package-lock.json, or . for the repository root. No absolute paths or ...
package STRING lodash npm package to upgrade. It must already be a direct dependency.
target_version STRING 4.17.21 Exact version. Ranges, latest, *, tags, URLs, git/github/file/npm: specifiers are rejected.
pr_number INT none Optional. When set, the verdict is posted on that pull request of the same repository.

There is no test-command input: the project's own npm test script is the contract.

The defaults verify a patch upgrade of lodash in a small public demo project, end in PASS, and never write to GitHub.

Outputs

  • verdict: PASS, WARN, BLOCK or INCONCLUSIVE.
  • reason_codes: the codes behind the verdict, for example ["CANDIDATE_TEST_FAILED"].
  • report: report.json (contract kestra-dependency-upgrade-readiness/1.0) with the repository, the upgrade, baseline and candidate install and test results (exit codes and durations), lockfile hashes, the capped dependency diff with counts, warnings, and the security settings used.

Prerequisites

  • A Kestra worker that can run Docker containers, with outbound access to github.com and registry.npmjs.org.
  • The project must have a package-lock.json (lockfile v2 or v3) and use packages from the public npm registry.

Secrets

  • GITHUB_TOKEN: only when pr_number is set. A fine-grained token for the repository with Pull requests: read and write (or Issues: read and write) to post and update the comment. It is used by the comment task only and never reaches the test container.
  • DEPENDENCY_GATE_WEBHOOK_KEY: random key that forms the webhook URL, only if you use the webhook.

Quick start

  1. Run the flow with the defaults to see a PASS. The defaults use the small projects in kestra-dependency-upgrade-demo, pinned to one commit. Try package: uuid, target_version: 9.0.1 for a BLOCK, package: rimraf, target_version: 2.7.1 for a WARN, and project_path: npm/failing for an INCONCLUSIVE.
  2. Point the inputs at your own repository, commit, project folder and the upgrade you want to check.
  3. To gate bot pull requests, call the webhook from CI and pass pr_number:
curl -fsS -X POST -H 'Content-Type: application/json' \
  -d "{\"repo_url\": \"https://github.com/${GITHUB_REPOSITORY}\", \"commit\": \"${BASE_SHA}\", \"project_path\": \".\", \"package\": \"lodash\", \"target_version\": \"4.17.21\", \"pr_number\": ${PR_NUMBER}}" \
  "https://<your-kestra-host>/api/v1/<tenant>/executions/webhook/company.team/dependency-upgrade-readiness-gate/${DEPENDENCY_GATE_WEBHOOK_KEY}"

Webhook values go through the same validators as a manual run: an invalid request gets HTTP 422 and no execution. project_path defaults to . when omitted. Pass the commit of the base branch the pull request targets, since the gate applies the upgrade itself.

Security model

  • Repository code is untrusted. Install scripts are never run, the repository's .npmrc is removed, the registry is forced, and any lockfile entry downloaded from another host blocks the upgrade before anything is installed.
  • The tests themselves are the repository's code, so they do run. They run offline, without secrets, with CPU and memory limits and timeouts, as an unprivileged user that cannot alter the gate's own files. This contains honest-but-buggy test suites; a deliberately malicious repository can still make its own tests lie. This is not a malware scanner.
  • Lockfiles are hashed when they are built and checked again just before each install, so tests on the baseline cannot change what the candidate installs.

Limitations

  • npm only, one package per run, lockfile v2 or v3, no workspaces.
  • Packages that need install scripts (native add-ons) may fail tests because the scripts are skipped. When that happens the report says so next to the failure.
  • Only the public npm registry is supported; private registries and scoped registry tokens are not.

Links

See How

New to Kestra?

Use blueprints to kickstart your first workflows.