11name : " Setup OpenClaw"
2- description : " Install OpenClaw (Node CLI) globally and expose it on PATH for downstream E2E steps."
2+ description : " Install the pinned OpenClaw (Node CLI) and expose it on PATH for downstream E2E steps."
33
44# Why npm and not pip:
5- # OpenClaw is published to npm as `openclaw` (Node 22.16+) .
5+ # OpenClaw is published to npm as `openclaw`.
66# The PyPI package named `openclaw` is an unrelated CMDOP plugin.
77# See: https://github.com/openclaw/openclaw
88#
99# Why a composite action:
1010# Multiple workflows (pr-screenshots, e2e-nightly, openclaw-boot, future
1111# real-data tests) need a working `openclaw` on PATH. Centralising the
12- # install lets us bump the pin once and cache per-runner.
12+ # install lets us move the pin once, in one place.
13+
14+ # The installed openclaw version is not an input. It is pinned in this
15+ # directory's package.json and package-lock.json, so `npm ci` installs exactly
16+ # the recorded tree and checks every tarball against its integrity hash. An
17+ # upgrade is a Dependabot pull request against that lockfile, not a dist-tag
18+ # resolved fresh on each run.
19+ #
20+ # Why the pin is 2026.5.12 and not the newest release:
21+ # `latest` was never actually resolved on a run. The actions/cache step this
22+ # action used to carry was keyed on the literal string "latest", so the key
23+ # never changed and never invalidated; every job since May restored that
24+ # cache and logged "openclaw cache hit, skipping install". CI has therefore
25+ # been testing against 2026.5.12 for months while appearing to track latest.
26+ # 2026.5.12 is what the E2E suites are green against today, so pinning it
27+ # here is a no-op for behaviour and confines this change to the supply-chain
28+ # fix. Moving off it is real work, not a version bump — see the note on the
29+ # Dependabot entry in .github/dependabot.yml.
30+ #
31+ # The mirror of this action in vivekchand/clawmetry-cloud (cloud #2300) pins
32+ # 2026.9.2, deliberately not the same version. Cloud has no live-OpenClaw
33+ # transcript E2E, so the JSONL/SQLite boundary described above does not reach
34+ # it. The two copies share a mechanism, not a version; keep them in step on the
35+ # mechanism and let each pin what its own suites are green against.
1336
1437inputs :
15- version :
16- description : " openclaw npm dist-tag or version (e.g. latest, beta, 2026.5.12)"
17- required : false
18- default : " latest"
1938 gateway-token :
2039 description : " Value to export as OPENCLAW_GATEWAY_TOKEN for downstream steps."
2140 required : false
2241 default : " ci-openclaw-token"
2342 node-version :
24- description : " Node major to install. OpenClaw requires Node 22.16+."
43+ description : >-
44+ Node major to install. The pinned openclaw 2026.5.12 declares
45+ `>=22.16.0`, so the default names the major and lets setup-node resolve
46+ the newest 22.x. Newer openclaw releases enforce a much narrower range in
47+ their own install script rather than only warning, so a Dependabot bump
48+ is what would make this need changing.
2549 required : false
2650 default : " 22"
2751
@@ -36,47 +60,40 @@ outputs:
3660runs :
3761 using : " composite"
3862 steps :
63+ # The hand-rolled actions/cache step this action used to carry is gone with
64+ # the dist-tag it keyed on. setup-node's own `cache: npm` is deliberately
65+ # NOT enabled in its place: its `cache-dependency-path` is resolved against
66+ # the workspace root, and pr-screenshots.yml checks this repository out
67+ # under `head/`, where that path matches no file and the step fails. The
68+ # install is a few seconds either way; correctness on every caller is worth
69+ # more than the cache.
3970 - name : Setup Node
4071 uses : actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
4172 with :
4273 node-version : ${{ inputs.node-version }}
4374
44- # Cache the global npm prefix so repeat runs skip the network fetch.
45- # Keyed on (OS, requested version) so a version bump invalidates cleanly.
46- - name : Cache global openclaw install
47- id : cache
48- uses : actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
49- with :
50- path : |
51- ~/.npm-openclaw-global
52- key : openclaw-${{ runner.os }}-${{ inputs.version }}-v1
53-
54- # `version` is supplied by the calling workflow, so it reaches the shell
55- # through the environment and is referenced as a quoted variable rather
56- # than being expanded into the script text by the expression evaluator.
57- - name : Install OpenClaw (npm -g)
75+ # `npm ci` installs the tree recorded in package-lock.json and nothing else:
76+ # no re-resolution against the registry's moving tags, and every tarball is
77+ # checked against its sha512 integrity hash. `--prefix` is what lets the
78+ # step run from the caller's working directory while installing into this
79+ # action's own directory.
80+ - name : Install OpenClaw (npm ci from the pinned lockfile)
5881 shell : bash
5982 env :
60- OPENCLAW_VERSION : ${{ inputs.version }}
61- OPENCLAW_CACHE_HIT : ${{ steps.cache.outputs.cache-hit }}
83+ ACTION_DIR : ${{ github.action_path }}
6284 run : |
6385 set -e
64- PREFIX="$HOME/.npm-openclaw-global"
65- mkdir -p "$PREFIX"
66- npm config set prefix "$PREFIX"
67- echo "$PREFIX/bin" >> "$GITHUB_PATH"
68- if [ "$OPENCLAW_CACHE_HIT" = "true" ] && [ -x "$PREFIX/bin/openclaw" ]; then
69- echo "openclaw cache hit, skipping install"
70- else
71- npm install -g --no-audit --no-fund "openclaw@$OPENCLAW_VERSION"
72- fi
86+ npm ci --no-audit --no-fund --prefix "$ACTION_DIR"
87+ echo "$ACTION_DIR/node_modules/.bin" >> "$GITHUB_PATH"
7388
7489 - name : Verify openclaw on PATH
7590 id : verify
7691 shell : bash
92+ env :
93+ ACTION_DIR : ${{ github.action_path }}
7794 run : |
7895 set -e
79- export PATH="$HOME/.npm-openclaw-global/ bin:$PATH"
96+ export PATH="$ACTION_DIR/node_modules/. bin:$PATH"
8097 which openclaw
8198 ver=$(openclaw --version 2>&1 | head -1)
8299 echo "openclaw version: $ver"
0 commit comments