From 26f6d4f2c533a43e6b5da0b4a5dd983f98f7b49a Mon Sep 17 00:00:00 2001 From: Ben Quarmby Date: Thu, 30 Apr 2026 13:28:41 -0700 Subject: [PATCH] fix: use npm co-located with the action node binary (#239) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: use npm co-located with the action node binary * fix: resolve npm by absolute path; guard against unset PATH Follow-up to 5a9e198. Two refinements to the GHE self-hosted runner fix: - Spawn npm via `path.join(dirname(process.execPath), 'npm[.cmd]')` instead of relying on PATH lookup. This matches the original PR description and is robust against PATH-shadowed npm installations. - Avoid `":undefined"` leaking into PATH when `process.env.PATH` is unset (rare, but possible in stripped environments). PATH still has the node directory prepended so npm's `#!/usr/bin/env node` shebang can resolve node on Linux/macOS. * fix: revert npm to PATH lookup; runner externals lacks npm Revert 42e75a1's switch to absolute-path npm resolution. The premise that npm is co-located with the action's node binary is false on GitHub-hosted runners: `process.execPath` points into `runner/externals/node24/bin/`, which contains node only — not npm. The absolute-path spawn produced ENOENT on Linux/macOS and "not recognized" on Windows. Go back to spawning `'npm'` and relying on PATH lookup, which works on standard runners (npm is on PATH from the runner image) and on the GHE self-hosted setup that motivated the original fix. Keep the node-directory prepend so npm's `#!/usr/bin/env node` shebang resolves, and keep the unset-PATH guard. --------- Co-authored-by: Zoltan Kochan --- dist/index.js | Bin 1433273 -> 1433466 bytes src/install-pnpm/run.ts | 15 +++++++++++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/dist/index.js b/dist/index.js index ec942a31c8932f3c1955cace5f5b434144ce245f..453a9a6f09b5ac97716343dd352ae273467606ff 100644 GIT binary patch delta 7428 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 B7MuV8 delta 7176 zcmZu#dwdl2wg3EfXJ&Vkkeyw2LLNw7BxG@k3DA@#j*u52B%9Y}6Ox$1W_OZovYFlN zBM%^eic%ltQH7(nQb4VO)>@VRuD2>u?eztSx7v@lR>jgHK?_J#EQ;PUJG&vY{UiCF z^F6QM&iT#Enct-s+Mj*CebX}}FOxLV{7f=QNF(ssq!D3FHW3katMKP+@$Jc z_vR2g!l@imkFY40R3nV%k}8B3a>)${msL=+(6w`k)k<9SAv>w#hzrg;h!3H=l{|XHk+q2>PIP?am}s`m~`1zzj=^-;DqECpRXz8C}DZR9A1UE5BQHJGKegJk8Q zpX~QYcGrMEcztn6G;oR#MX|UfKi@?+c92AZA$N9?5d=*a!BuUsbrJm2P+OPwvD=BA zzWZ10{e;xh&?)UhMp6%{UuqvEb~yZ{wvsFd_M)~p!!CDE^hx2as&IFKbzM*jM}l%z zxZfMug8^mWR1FZ z)x(xc+IF&>et1dyJT3qp_(uCI7H+?+U4rcP%i5i0#C)FVVGwV(Fv}Pn){(FVJ)X+E zs=>wIm%+S(2(CGd5_JC@=2@))tB5XJsH>jdn8Q4wm=p7uc?gU0809yO<}phVXdcsq zDfIb_Pw|NqFh_9}l~(3#3aDMgs+V9F7k4n{)wt%JD_Bb=?wbxOpoPG$t*8z+;2klMy%A}ncR z+R1ZnK@q5OzMNy9UeEIo3MFr9BJK@+`W-hw@V=c28VQd{^#k~90 zG0ScuJ~;Ue^OD9b!{N(}g+6zgF*8WMN!UiC8#^RRqgi%Wpg`(!!vYOkPu#Fp!`7ss zt+3w}Dv+!bZlB}|;*N_JbHkrBY!7;gT6Q5qK+B2?2B8T^gH>FqFpr7Qvd%Zc0$1qd#VtKAp}!tDQ!`$Q*V_@|1&;e;A&d z!h5!&ra;#FtR99l*&9-(B~=`yV9*~dT|s}J$?B8I06n&Vou?rK@a1*v1gha8 zb`3#%r(-NVjRP z!!TD^QngUT7vE<`CT5|2CBbb4g~#uxC29qf6S_get9Y|pvzR> z>BEQEub0Bsr&t5s{~epItMFia(A(#gOHuNXV|5$ieUe!z>7J90OT8t}z9@-1uHpyhmN3N_>1Mxu<#U(Q!=+(Aw+uNwRAw z>gn^!lI&Mv+vaz9|y0W#lp+>BM zeU)6E0m;Uu>IPJgS8{1dicfu`Lv{F~lFLktG1XP*l3B%l57RlTxP?ShBg2|1&PESb zbKhf-{iK%DRgqpO{?;hy+Zx1PIX}Ob@>{uPqUn`kZ>=F?p3=LuP(OQT&=<$!r zjw_VIX}6cRbgZswtaPl#_KUsnuv(DSj6_8%qw4F;OsCsvBPE-%=;Ew8T{~=dZ zaT&7lVpYX>Nk&}3tifV9_hT+Cq0%LZ{kXng2MO%lB^I4D!Ov(#e5XegmRq5tu!e1f$1Sm=)j0zbdN=iLrR9= zkSfQchhm}eMJT9vgvN)|OnBi{QDGypO45xFaWAKmq3Hfd7{=>6gbf&ycM{#EZ3!j{ScSUMq^@|YDm6&FQ1MXKY9@#IYab|7r8A8PQ1;vkQhx* zzQeu6Lgn*Z3TcDR=egGt+ObvuO9$j=E$v*F1kb&|?SMn?bJiMZZVX6$Yst7;A6?_^4VQscR#w~{*MaucC+p&YI(QQC@-J-LTahfXV9#3RC z=a&*-Q#$4Uq2oL7!ni4Qp+4B;5 zUCz~=RJ=aQ)8&v3%H->wU=W|4ulr%50W%M|2Bj7WUYxH>p+zM+{Kn{@=}UCmO{AJW zR;!y!5Rb3ar6a=XSIsHFzhs^P$@Mw|5^px>X4A#>x}7m=8l1H2YE?rf)HmpEj>(wh zT!Zdg#qDg9&WV7z7Q8}4Rh<3hg)^@;a2_OthP#gB3E0*8`f3Ig~~1J3YQ1_LZZVXl_3*yjY#5{Dl0Dv zaYU6PA*s^u^9MuXXv{I}590G$Z1G41*0M@?E~0;pRMOi<^m#f`36D?c%Ms2_=&wOY z+7Q(mHt1)gHn>6mD7soU>QfD*R^9AQk0jRO)~Jm?ZHA`^b-PDa#O%s;zXyJKhkmi9 zHoCj{TIGHmMswjn$S;evFyl`B6PWn9JN5S>IPcQ`@2r?ljYo<-=BgZGf5FOVaYNq< zaV6GX8L!=ewN*l`JruXAwV&Un&rVeBg{xOfV^Ds`Y=O($^nT4s89r+4{1H{H%TSx6MDM!AkOlr* z3>Va>ihgkI#TRPPW&;bGHyh3~N^xks)sO}|ZpDffTLx}3;9mm%CDMW0lK4qYyA0(! zX3V79{4;bdq#e$^HKYBJvxjH!q@AK%y8yZ4rsOJ^_-@8$r2U{jxs8zao&Mxpm?X)k zmH1^dlV{U&n&kaD)wc}ZPDuVNZrQml*+?%WCTEj41L!NzVoFV)F4E(gsiV7*ps^PbU9Ei{!SClXI3KJ~hwyXF<_L z_*neYcapF(=*8V$cyi+GOZKhhw+rRN_O4Gpr`5X{eS z2vXBlu8F{#HGD6bOG{Sp#RRdxmcNmZ1}JagL$IWd52F0*CO#Vi5~qVd)S=g0VC(ra zrn$;vKJ1X(_>ERjZne$bW#`k-m~Q7A)u4H>!_K!~mB7@%JF!Y%1OE_KgQOui^|c`$Z-!C&XFp$62<|?<2FfiaK@c4{k}2Qxjk4tLZFBh|lIVaP zyZKqUXu)nx=jB429i|iXjorM35C@$2Id<6r<~{rKyC({KX2+r;-ioG zEh?W5=RY&eg8NV7j;n)ZXLt*2y3`#s;oHY}PAiLbP&dhcu9};tg)#8t8Gb$z z@14QE)xkBN@IAPoO`q^hw?fu#zNYo6UXA^0uaLO9S8-xmucm_Hy&41TRVKY-*fd7S z7}bVM*sw7u4V%6|wJ2i3hK<2}Bc`9BS~6>;if5OjKBn!OqxhW4eDr&UknzJSyyzq%y$Vi*MFg^C)q(z-x zhHz+V!KY_S{OK8(BGjkn_%vM^UnZWyq3+-#rhl1uTjCRM3*f{%n`)mnKTNR5{%6b^ z5DE^O?WBc{95k0DlGXI77tJ<8Rs&9*PFVaq^Y2lPPo60#D?=yX(CMbXc*(p_@kDDH zTzJ`Bt6HOz=XG$$@6C=Ffq>&@Ev@+=5E<;### zhEA#sodT&+FOMC6eHC7ws`%xppCZ%&tHKkrs{>Z;NplVx4WNOO=B0#-1+VGUT_o`M zDf28%mHJ^*MNgbEzpW)z^xzrukt8w*H(xZLB!jf~E4(&|gL3>FZxA+pV_pN7|7p&+ zicPv~z6O?EGG|`J4qP%H!6e`R+I+*mCAsM;PmHg)ionp^E$c6v>H5nv57m;U=oO&N zH5M0x%F}fgE5iGAmOKP)y(M>J6W(soV;*nsxEO50Z^XD(hNcm3$Qu@Aq@$8TRW#bg z&Ot@OBLJtpx}jB+niO?;ePjFT>V_&Y9912a<@Mz)m{PHMLpZ+s@Sx9&6SB8SQaOj) zHQVP3iXoK<;0rZ8-s&I3&tktq(ZltYO+VFi%Cz#dpRMJYfIE5D&rD#p3P&i)^MmGx|=+Zjz*>xp@Ef~wuDT92MP=bE& zi@Ste#s7`FgxiuZQ_FqAONsLKe(EA|xx}in8lnfrFMK!pvN^)mBhs zYZxlTLkZ>J#9APE3q4Y=3y<^)2mD^Szzsh-B3z4S-_INoau|Gqrx)2*XFTKOen{tKGhmuS`y4TCR`UwvV=t7N5_PD z8za_rimwNKeWiFrIG|?N8qk9+%-xNZ)Ci>u;!d{Lx{86wO{vXCp ByzBq~ diff --git a/src/install-pnpm/run.ts b/src/install-pnpm/run.ts index 922d79b..e22ee79 100644 --- a/src/install-pnpm/run.ts +++ b/src/install-pnpm/run.ts @@ -29,7 +29,17 @@ export async function runSelfInstaller(inputs: Inputs): Promise { await writeFile(path.join(dest, 'package.json'), packageJson) await writeFile(path.join(dest, 'package-lock.json'), JSON.stringify(lockfile)) - const npmExitCode = await runCommand('npm', ['ci'], { cwd: dest }) + // Prepend the action's node directory to PATH so npm's + // `#!/usr/bin/env node` shebang resolves on runners (e.g. GHE + // self-hosted) where node isn't already on PATH. npm itself is + // resolved via PATH — on the GitHub Actions runner it is not + // co-located with `process.execPath`. + const nodeDir = path.dirname(process.execPath) + // On Windows, the PATH key casing varies; search case-insensitively. + const pathKey = Object.keys(process.env).find(k => k.toUpperCase() === 'PATH') ?? 'PATH' + const currentPath = process.env[pathKey] + const npmEnv = { ...process.env, [pathKey]: currentPath ? nodeDir + path.delimiter + currentPath : nodeDir } + const npmExitCode = await runCommand('npm', ['ci'], { cwd: dest, env: npmEnv }) if (npmExitCode !== 0) { return npmExitCode } @@ -155,10 +165,11 @@ function getSystemNodeVersion(): Promise<{ major: number; minor: number }> { }) } -function runCommand(cmd: string, args: string[], opts: { cwd: string }): Promise { +function runCommand(cmd: string, args: string[], opts: { cwd: string; env?: Record }): Promise { return new Promise((resolve, reject) => { const cp = spawn(cmd, args, { cwd: opts.cwd, + env: opts.env, stdio: ['pipe', 'inherit', 'inherit'], shell: process.platform === 'win32', })