diff --git a/.github/actions/check-wip-cap/package.json b/.github/actions/check-wip-cap/package.json index a3c299c7ba..0745b73b49 100644 --- a/.github/actions/check-wip-cap/package.json +++ b/.github/actions/check-wip-cap/package.json @@ -3,5 +3,8 @@ "private": true, "scripts": { "test": "node --test" + }, + "dependencies": { + "resolve-config": "file:../resolve-config" } } diff --git a/.github/workflows/pr-require-checks.yml b/.github/workflows/pr-require-checks.yml index 56d32717eb..fcf3c0f5cb 100644 --- a/.github/workflows/pr-require-checks.yml +++ b/.github/workflows/pr-require-checks.yml @@ -2948,8 +2948,9 @@ jobs: [[ " $PACKAGES " == *" plugins/knowledge/skills/course-digest/extraction "* ]] || exit 0 bash plugins/knowledge/skills/course-digest/scripts/run-tests.sh test - # resolve-config has pinned dependencies (yaml, ajv) that - # run-outside-node-suites.sh never installs, so it installs and tests here. + # resolve-config has pinned dependencies (yaml, ajv), so it installs and + # tests here; run-outside-node-suites.sh installs them only for a package + # that takes resolve-config as a file: dependency. # # This suite and the next print `::error` lines on purpose, from fixtures # that exercise a script's own failure messages, so each runs with diff --git a/scripts/run-outside-node-suites.sh b/scripts/run-outside-node-suites.sh index 8024c0b043..d93003685b 100755 --- a/scripts/run-outside-node-suites.sh +++ b/scripts/run-outside-node-suites.sh @@ -7,7 +7,8 @@ # With no arguments, run `npm test` in every directory listed in # scripts/outside-node-packages.txt. That file is the package's own test # command, read from package.json "scripts.test". This script does not guess -# node --test versus vitest. +# node --test versus vitest. A package's `file:` dependency that has a lockfile +# is installed (`npm ci`) before its tests run. # # --paths FILE is one repo-relative path per line (the suites affected-tests.sh # declined). A path is classified, not guessed: @@ -95,11 +96,27 @@ run_package() { echo "outside-node: $pkg/package.json has no scripts.test; refusing to guess a runner" >&2 return 1 fi + install_locked_deps "$pkg" || return 1 echo "outside-node: npm test in $pkg ($test_cmd)" (cd "$pkg" && npm test) } -declare -A selected=() +# install_locked_deps : `npm ci` each `file:` dependency of that has +# a lockfile, once per run, so a suite that loads modules from it finds them. +install_locked_deps() { + local dep dir + while IFS= read -r dep; do + [[ -n "$dep" ]] || continue + dir="$(cd "$1/$dep" && pwd)" || return 1 + [[ -f "$dir/package-lock.json" && -z "${installed[$dir]:-}" ]] || continue + installed["$dir"]=1 + echo "outside-node: npm ci in $dir, a file: dependency of $1" + npm ci --ignore-scripts --prefix "$dir" || return 1 + done < <(jq -r '[.dependencies, .devDependencies] | map(. // {}) | add | to_entries[] + | select(.value | startswith("file:")) | .value | ltrimstr("file:")' "$1/package.json" | tr -d '\r') +} + +declare -A selected=() installed=() failed=0 if [[ -z "$paths_file" ]]; then diff --git a/scripts/run-outside-node-suites.test.sh b/scripts/run-outside-node-suites.test.sh index 4f7ee21948..a749ccd99d 100755 --- a/scripts/run-outside-node-suites.test.sh +++ b/scripts/run-outside-node-suites.test.sh @@ -63,6 +63,26 @@ else ok "a failing registered package fails the run" fi +# A package's locked file: dependency is installed before its npm test runs +# (check-wip-cap's suites load yaml from resolve-config's node_modules); one +# with no lockfile is left alone. An npm stub records the calls. +mkdir -p "$fixture/pkgs/app" "$fixture/pkgs/locked" "$fixture/pkgs/unlocked" "$fixture/bin" +printf '{"private":true,"scripts":{"test":"node --test"},"dependencies":{"locked":"file:../locked","unlocked":"file:../unlocked"}}\n' \ + >"$fixture/pkgs/app/package.json" +printf '{"private":true}\n' >"$fixture/pkgs/locked/package.json" +printf '{}\n' >"$fixture/pkgs/locked/package-lock.json" +printf '{"private":true}\n' >"$fixture/pkgs/unlocked/package.json" +printf '#!/usr/bin/env bash\nprintf "%%s\\n" "$*" >>"%s"\n' "$fixture/npm.log" >"$fixture/bin/npm" +chmod +x "$fixture/bin/npm" +printf 'pkgs/app\n' >"$fixture/scripts/outside-node-packages.txt" +: >"$fixture/npm.log" +PATH="$fixture/bin:$PATH" bash "$fixture/$RUNNER" >/dev/null 2>&1 || true +if [[ "$(cat "$fixture/npm.log")" == "ci --ignore-scripts --prefix $fixture/pkgs/locked"$'\n'"test" ]]; then + ok "a locked file: dependency installs before the package's npm test, an unlocked one does not" +else + fail "expected one npm ci of pkgs/locked, then npm test: $(cat "$fixture/npm.log")" +fi + printf '%s\n' "plugins/knowledge/vendor/not-a-suite/missing.test.js" >"$list" if bash "$RUNNER" --paths "$list" >/dev/null 2>"$list.err"; then fail "an unclassified path should fail"