Skip to content

🕵🏻 Get Node Version ​

This repository implements a very simple GitHub composite action that pulls the Node.js version from the package.json file of the project and ensures that it is used for the current pipeline execution. The action relies on the Node.js version specified within the engines section of the package.json, e.g:

json
{
  "engines": {
    "node": "24.x.x",
  },
}

Usage ​

Why? ​

What you will often notice in Node.js projects is that you quickly get to the point where the correct version relevant to the project is defined and needs to be maintained in different places, e.g. different pipeline / workflow definitions. Ideally, however, you would like to maintain the Node.js version only in one place and thus at the same time ensure that all developers use the same version for their local development, which is also used by the CI/CD automations of the project. Exactly this is what this action enables you to do!

How? ​

In a GitHub workflow, the use of the action after the initial checkout step would look like this:

yaml
name: build

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

# Least-privilege token: this workflow only reads the repository.
permissions:
  contents: read

jobs:
  build:
    name: Build
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v7.0.1
        with:
          persist-credentials: false # the GITHUB_TOKEN is not needed after the checkout - don't leave it in .git/config

      - name: Select Node Version
        uses: svierk/get-node-version@v1.6.0

Every uses: reference above is pinned to an exact release tag rather than a floating major tag, so adopting a new version is always an explicit, reviewable change of that pin.

Inputs ​

NameRequiredDefaultDescription
node-versionno–Explicit Node.js version spec (e.g. 24.x) that takes precedence over the package.json lookup. An empty value falls back to engines.node.
pathno./Path where the package.json file can be found.
package-managernoautoPackage manager used for dependency caching. Supported: auto, npm, yarn, pnpm. See Package manager and dependency cache.
step-summarynotrueWrite a result section to the GitHub Actions job summary. Set to false to avoid collisions with a custom workflow summary.

Outputs ​

NameDescription
node-versionThe Node.js version spec resolved from the node-version input or the engines.node field.
sourceWhere the version spec was resolved from: node-version input or package.json.
package-managerThe package manager used for the dependency cache, either set explicitly or detected with auto. Falls back to npm if nothing is detected.

The action fails with a clear error if no node-version input is provided and the package.json or its engines.node field is missing, so a misconfiguration surfaces immediately instead of silently falling back to an undefined version.

Package manager and dependency cache ​

By default (package-manager: auto), the action detects the package manager of the project and enables the matching dependency cache of actions/setup-node:

  1. The packageManager field in the package.json, e.g. "packageManager": "yarn@4.18.1".
  2. Otherwise the lock file: package-lock.json or npm-shrinkwrap.json → npm, yarn.lock → yarn, pnpm-lock.yaml → pnpm.
  3. If no lock file is found, Node.js is set up without a dependency cache and the action only emits a notice. This keeps projects that do not commit a lock file working.

For yarn and pnpm, the action runs corepack enable before setting up Node.js, so the version pinned in the packageManager field is used and pnpm does not have to be installed separately. A pnpm installed beforehand, e.g. via pnpm/action-setup, keeps working as well. The resolved value is available as the package-manager output for subsequent steps:

yaml
- name: Select Node Version
  id: node
  uses: svierk/get-node-version@v1.6.0

- name: Install dependencies
  run: ${{ steps.node.outputs.package-manager }} install

Setting package-manager explicitly to npm, yarn or pnpm skips the detection. In this case the matching lock file is required and the action fails if it is missing.

Overriding the version via repository variable ​

The package.json remains the recommended single source of truth, since it is the only place local developer tooling picks the version up from as well. However, sometimes you temporarily need to pin the pipeline to a fixed, known-good Node.js version - ideally without touching the workflow file at all. For this, wire the node-version input to a repository variable (organization and environment variables work the same way):

yaml
- name: Select Node Version
  uses: svierk/get-node-version@v1.6.0
  with:
    node-version: ${{ vars.NODE_VERSION_OVERRIDE }}

This wiring is safe to keep in place permanently: as long as the variable is unset or blank, the expression evaluates to an empty string and the action falls back to the engines.node field as usual. Only when the variable is populated does it take precedence - so a temporary override becomes a pure configuration change, and removing the variable's value restores the default behaviour. The job summary and the source output always show which of the two sources was used.

Note that when an explicit version is provided this way, a package.json is no longer required for the version lookup itself. With package-manager: auto, the package manager is then detected from the lock file alone.

References ​

The idea for the creation of this action and some further details are described in the following Medium post: How to enforce a specific node version within a project and the associated CI/CD automations

Releases ​

Latest release notes can be found on the release page.

License ​

The scripts and documentation in this project are released under the MIT License.


➡️ Full source & releases: svierk/get-node-version

Released under the MIT License. · llms.txt for AI agents