Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 71 additions & 0 deletions .github/workflows/smoke.yml
Original file line number Diff line number Diff line change
Expand Up @@ -384,3 +384,74 @@ jobs:
v="$(pnpm --version)"
echo "pnpm --version in a pnpm@10.15.1 project: $v"
[[ "$v" == "10.15.1" ]] || { echo "::error::expected the pinned pnpm 10.15.1, got $v"; exit 1; }

multi-dep-path:
name: newline-delimited cache-dependency-path keys the cache (ubuntu-latest)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: |
printf '{ "name": "multi", "version": "1.0.0" }\n' > package.json
echo "${{ github.run_id }}" > run.txt
- uses: ./
id: setup
with:
cache: true
cache-dependency-path: |
package.json
run.txt
- run: |
set -euo pipefail
[ "${{ steps.setup.outputs.caching-enabled }}" = "true" ] || { echo "::error::caching-enabled is '${{ steps.setup.outputs.caching-enabled }}'"; exit 1; }
echo "OK: a two-line cache-dependency-path reached the cache step"

additive:
name: beside setup-node — nub install from package-lock.json (${{ matrix.os }})
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "20"
# A pin that disagrees with setup-node: with provision-node left on, the
# action would front Node 22 over setup-node's 20 and the check below fails.
- run: echo 22 > .nvmrc
shell: bash
- uses: ./
id: nub
with:
provision-node: false
Comment thread
pullfrog[bot] marked this conversation as resolved.
- name: nub installs the npm lockfile; bare node and npm stay 20.x for later steps
shell: bash
run: |
set -euo pipefail
# provision-node: false leaves the node-version output empty.
[ -z "${{ steps.nub.outputs.node-version }}" ] || { echo "::error::node-version output is '${{ steps.nub.outputs.node-version }}' with provision-node: false, expected empty"; exit 1; }
d="$RUNNER_TEMP/additive" && mkdir -p "$d/local-dep" && cd "$d"
cat > package.json <<'PKG'
{ "name": "additive", "version": "1.0.0", "dependencies": { "local-dep": "file:local-dep" },
"scripts": { "hello": "node -e \"console.log(42)\"" } }
PKG
printf '{ "name": "local-dep", "version": "1.0.0" }\n' > local-dep/package.json
cat > package-lock.json <<'LOCK'
{
"name": "additive", "version": "1.0.0", "lockfileVersion": 3, "requires": true,
"packages": {
"": { "name": "additive", "version": "1.0.0", "dependencies": { "local-dep": "file:local-dep" } },
"local-dep": { "version": "1.0.0" },
"node_modules/local-dep": { "resolved": "local-dep", "link": true }
}
}
LOCK
nub install --frozen-lockfile
[ -f node_modules/.nub-engine ] || { echo "::error::nub install did not run on Nub's engine"; exit 1; }
[ -e node_modules/local-dep ] || { echo "::error::the file: dependency is not linked"; exit 1; }
nv="$(node -p 'process.versions.node')"
[[ "$nv" == 20.* ]] || { echo "::error::bare node is $nv, expected 20.x"; exit 1; }
out="$(npm run --silent hello)"
[[ "$out" == "42" ]] || { echo "::error::npm run hello printed '$out', expected 42"; exit 1; }
echo "additive OK: nub installed package-lock.json, node $nv, npm run works"
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,23 @@ Because the eager provision reads the project's pin files off disk, **`actions/c

Like setup-node, **caching is on by default** — `cache: npm` (or `yarn`/`pnpm`/`bun`) keeps working but is no longer required to get a warm store. Disable with `package-manager-cache: false`.

## Beside setup-node

`setup-nub` also runs after `actions/setup-node` in a workflow that keeps it. The install line is what changes:

```yaml
- uses: actions/setup-node@v4
with:
node-version: 22
- uses: nubjs/setup-nub@v0
with:
provision-node: false # setup-node's Node stays on PATH
- run: nub install --frozen-lockfile # was: npm ci
- run: npm test # the real npm, as before
```

`nub install --frozen-lockfile` reads the existing `package-lock.json` (or `pnpm-lock.yaml`, `yarn.lock`, `bun.lock`) unchanged. `provision-node: false` leaves the Node that setup-node put on PATH in place; without it setup-nub fronts the project's own pin, which a matrix job does not want. Drop `cache: npm` from setup-node — it restores npm's tarball cache, which Nub does not read, and setup-nub caches Nub's store by default.

## Node on the global PATH

Like `actions/setup-node`, setup-nub provisions a Node toolchain and **adds its bin dir to the global `PATH`**, so bare `node`/`npm`/`npx`/`corepack` in subsequent steps resolve to that version — a swap from `actions/setup-node@v4` to `nubjs/setup-nub@v0` leaves later steps that call bare `node`/`npm` behaving the same. The provisioned bin holds the real Node binaries (no nub-branded shim is fronted), and it is placed ahead of `nub`'s own bin so real `npm`/`npx` win.
Expand Down Expand Up @@ -65,6 +82,7 @@ The remaining nuance vs setup-node: when an explicit `node-version` is set, it g
| `scope` | repo owner | Scope for a scoped registry. Falls back to the repo owner for GitHub Packages. |
| `always-auth` | `false` | Write `always-auth=true` into the `.npmrc`. |
| `token` | `github.token` | GitHub-API rate-limit relief when resolving nub's version range. |
| `provision-node` | `true` | Set to `false` to leave Node alone: no eager provision, nothing fronted on PATH, `node-version` output empty. For a job where `actions/setup-node` already put the wanted Node on PATH; `nub` still resolves the project's pin at its own invocation. |
| `shim` | `false` | Run `nub pm shim` after installing and put its directory first on PATH: `npm`/`npx`/`pnpm`/`pnpx`/`yarn`/`yarnpkg` in later steps run the package manager the project pins, provisioned on demand. |

Accepted for setup-node compatibility but **ignored** (never errors): `check-latest`, `architecture`, `mirror`, `mirror-token`.
Expand Down
21 changes: 15 additions & 6 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@ inputs:
description: "Token for GitHub-API rate-limit relief when resolving nub's version range (and Node downloads on GHES). Defaults to github.token on github.com."
required: false
default: ${{ github.server_url == 'https://github.com' && github.token || '' }}
provision-node:
description: "Set to false to leave Node alone: no eager provision, nothing fronted on PATH, and the node-version output empty. For a job where actions/setup-node has already put the wanted Node on PATH. nub still resolves the project's pin at its own invocation. Default: true."
required: false
default: "true"
shim:
description: "Run `nub pm shim` after installing and put its directory first on PATH, so npm/npx/pnpm/pnpx/yarn/yarnpkg invocations in later steps run the package manager the project pins (packageManager / devEngines), provisioned on demand — corepack's job. An unpinned project falls through to the runner's own tool. Default: false."
required: false
Expand Down Expand Up @@ -270,10 +274,6 @@ runs:
false|"0"|no|off) enabled=false ;;
*) enabled=true ;;
esac
case "$lc" in
npm|yarn|pnpm|bun)
echo "::notice::setup-nub's \`cache\` is a boolean (nub has one store regardless of package manager); the value \"${raw}\" enables caching." ;;
esac
elif [ "$pmc" = "false" ] || [ "$pmc" = "0" ] || [ "$pmc" = "no" ] || [ "$pmc" = "off" ]; then
# 2. caching explicitly disabled.
enabled=false
Expand Down Expand Up @@ -345,7 +345,12 @@ runs:
done
dep_glob="${dep_glob:-${prefix}package.json}"
fi
echo "dep-glob=$dep_glob" >> "$GITHUB_OUTPUT"
# A newline-delimited list needs the heredoc form, as pin-glob below.
{
echo "dep-glob<<DEPEOF"
printf '%s\n' "$dep_glob"
echo "DEPEOF"
} >> "$GITHUB_OUTPUT"

# Fold the project's Node pin into the cache key so a Node-version bump
# invalidates the toolchain cache (setup-node#1171). We hash the pin SOURCE
Expand Down Expand Up @@ -444,7 +449,10 @@ runs:
echo "Wrote registry auth to ${npmrc}"

# 6. Eagerly provision the project's Node so later steps are WARM. This runs
# UNCONDITIONALLY — the common (no-input) case is the whole point: a bare
# by default; provision-node: false is the opt-out, for a job where
# actions/setup-node already put the wanted Node on PATH (then nothing is
# provisioned, nothing fronted, and the node-version output is empty).
# The common (no-input) case is the whole point: a bare
# `nub node install` resolves the full pin chain (devEngines.runtime ->
# .node-version -> .nvmrc -> engines.node), resolves ranges against the
# live Node index, and installs the concrete version up front, instead of
Expand All @@ -458,6 +466,7 @@ runs:
# MUST run before setup-nub.
- name: Provision Node
id: provision
if: inputs.provision-node != 'false'
Comment thread
pullfrog[bot] marked this conversation as resolved.
shell: bash
env:
NODE_VERSION: ${{ inputs.node-version }}
Expand Down
Loading