Fix Node dependency install quoting and failure reporting#44
Merged
Conversation
The versioned JS dependency install always failed because name@range tokens were escaped with printf %q before being word-split into the command array: npm received a literal \^ and rejected it as an invalid dist-tag (EINVALIDTAGNAME). The failure was then masked because maybe_install_missing_root_deps returned 1 both for "nothing missing" and "install failed", so the caller fell through to a bare npm install that succeeded trivially, printed "Node toolchain installed", and listed the toolchain in the final summary. - Pass name@range tokens to npm/yarn as raw argv elements; run_command execs them without a shell reparse, so no escaping is needed. - Give maybe_install_missing_root_deps distinct return codes (installed / failed / nothing missing / skipped) and only fall through to the bare install when nothing was missing or the add was skipped. Failures now produce a "Node toolchain: FAILED" summary entry with retry instructions, matching the PHP dev-tools pattern. - Capture the return code at the existing-toolchain top-up call site too, so a failed top-up is reported as FAILED instead of the pre-existing node_modules reading as "already present". - Print versioned retry commands (constraints resolved from Drupal core) instead of the unversioned package list, which hits peer dependency conflicts (latest eslint-plugin-yml requires eslint >= 9 while eslint-config-airbnb-base requires eslint 7/8), and note the version-alignment caveat on the static fallback list. Add stub-based installer tests covering the unescaped argv path, the FAILED reporting on install failure, and the existing-toolchain top-up failure, plus real-ddev tests for the versioned dependency add and the FAILED summary.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #42.
The problem
The Phase 3 JS dependency install always failed, and the failure was then masked as success:
name@rangetokens (versions resolved from Drupal core'spackage.json) were escaped withprintf '%q'and word-split into the command array. Word splitting doesn't do quote removal, andrun_commandexecs the array without a shell reparse, so npm received a literalcspell@\^9.8.0and rejected it as an invalid dist-tag (EINVALIDTAGNAME). The%qpredates the switch fromddev exec bash -lc "<string>"to a direct argv array, where it was still correct.maybe_install_missing_root_depsreturned 1 for both "nothing missing" and "install failed", so after the failed versioned install the caller fell through to the bareddev npm installfallback (intended for the nothing-missing case), which succeeded trivially. The installer then printed "Node toolchain installed" and listed the toolchain in the final summary.eslint-plugin-ymlto 3.x (peereslint >=9.38), which conflicts witheslint-config-airbnb-base(peer eslint 7/8) — the peer conflict reported in the issue.Note on the issue's Bug 1: the installer's constraint set was actually consistent —
eslint-plugin-yml@^1.19.1and@^1both resolve to 1.19.1, whose peer range (eslint >=6) accepts the pinnedeslint@^8.57.1. The constraints simply never reached npm because of the escaping, and the peer conflict came from following the unversioned manual-install guidance. No version pins needed to change.The fix
name@rangetokens to npm/yarn as raw argv elements; no escaping.maybe_install_missing_root_depsdistinct return codes (installed / failed / nothing missing / skipped) and only run the bare-install fallback when nothing was missing or the add was skipped. A failure now printsNode toolchain install FAILED, showsNode toolchain: FAILEDin the summary with a numbered FIX step, and never lists the toolchain as installed — mirroring the existing PHP dev-tools failure handling (non-fatal, prominent).package.jsonif conflicts appear. When the bare install of already-declared deps fails, the retry guidance is a plainddev npm installinstead of the package list.Testing
nodetag): versioned dependency add with real^constraints succeeds; unresolvable constraint produces the FAILED summary while the installer still completes.node-tagged suite and the fast installer suite pass; shellcheck removes two warnings (SC2206) and adds none;bash -nclean; no bash 4+ constructs (macOS bash 3.2 stays supported).