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
33 changes: 33 additions & 0 deletions .github/actions/install-dependencies/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,39 @@ runs:
- name: Install pnpm
uses: ./.github/actions/install-pnpm

# When the node_modules layout changes, `pnpm install` keeps the `.bin` scripts of the workspace packages, which
# then point into a folder that the new layout does not have. Branches that change `nodeLinker` share this cache
# volume, so start from empty node_modules folders when the layout on the volume differs, or when an earlier
# install already left such broken `.bin` scripts behind.
- name: Remove node_modules of another layout
run: |
mapfile -t dirs < <(grep '/node_modules$' <<< "$DEPENDENCY_PATHS")
expected=$(pnpm config get node-linker)
if [[ "$expected" == undefined ]]; then
expected=isolated
fi
actual=$(node -p 'try { JSON.parse(require("fs").readFileSync("node_modules/.modules.yaml", "utf8")).nodeLinker ?? "" } catch { "" }')
if [[ -n "$actual" && "$actual" != "$expected" ]]; then
reason="The node_modules folders use the $actual layout, but nodeLinker is $expected."
else
broken=$(node "$GITHUB_ACTION_PATH/find-broken-bin.mjs" "${dirs[@]}")
if [[ -n "$broken" ]]; then
reason="The target of $broken does not exist."
fi
fi
if [[ -n "$reason" ]]; then
echo "$reason Removing the node_modules folders."
for dir in "${dirs[@]}"; do
if [[ -d "$dir" ]]; then
# The folders are mounted from the cache volume, so empty them instead of removing them
find "$dir/" -mindepth 1 -maxdepth 1 -exec rm -rf {} +
fi
done
fi
env:
DEPENDENCY_PATHS: ${{ steps.paths.outputs.paths }}
shell: bash

# The cache volume can hold node_modules of another commit or branch. This is a no-op when they match
# pnpm-lock.yaml.
- name: Install dependencies
Expand Down
34 changes: 34 additions & 0 deletions .github/actions/install-dependencies/find-broken-bin.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Prints the first `.bin` entry in the given node_modules folders whose target does not exist.
import { existsSync, lstatSync, readdirSync, readFileSync } from 'node:fs';
import { join, resolve } from 'node:path';

// The pnpm shell shim runs the target with `exec <node> "$basedir_abs/<path>"` (`$basedir` in older versions)
const SHIM_TARGET = /^\s*exec \S+\s+"\$basedir(?:_abs)?\/([^"]+)"/m;

for (const dir of process.argv.slice(2)) {
const binDir = join(dir, '.bin');
let names;
try {
names = readdirSync(binDir);
} catch {
continue;
}

for (const name of names) {
const bin = join(binDir, name);
const stat = lstatSync(bin);
let target;
if (stat.isSymbolicLink()) {
target = bin;
} else if (stat.isFile() && stat.size < 64 * 1024) {
const match = readFileSync(bin, 'utf8').match(SHIM_TARGET);
Comment thread
isaacs marked this conversation as resolved.
Dismissed
target = match && resolve(binDir, match[1]);
}

if (target && !existsSync(target)) {
// eslint-disable-next-line no-console
console.log(bin);
process.exit(0);
}
}
}
Loading