Skip to content

Node tooling install silently reports success after a failed npm install (version constraints are shell-escaped) #42

Description

@UltraBob

Summary

During install of drupal-code-quality (v1.0.6), the Phase 3 "JS toolchain dependencies" step fails to install the DCQ npm dev-dependencies, but the installer continues and reports the Node toolchain as successfully installed. A user who does not read the console output line-by-line is left without ESLint / Prettier / Stylelint / CSpell tooling while believing it is present.

There are two distinct problems. The more important one for reproducing the failure is Bug 1 (a version-compatibility issue), which is separate from the cosmetic shell-escaping in Bug 2.

Bug 1 — a pinned dependency version cannot be resolved

The installer requests eslint-plugin-yml@^1.19.1 alongside eslint@^8.57.1. Recent eslint-plugin-yml majors declare a peer dependency on eslint@^9, which conflicts with DCQ's pinned eslint@^8.57.1, so the install cannot resolve.

The working fix (confirmed locally) was to pin the plugin down:

ddev npm install --save-dev cspell eslint eslint-config-airbnb-base eslint-config-prettier \
  eslint-plugin-import eslint-plugin-jsdoc eslint-plugin-no-jquery eslint-plugin-prettier \
  eslint-plugin-yml@^1 prettier stylelint stylelint-config-standard stylelint-order stylelint-prettier

eslint-plugin-yml@^1 selects a build whose peer range still includes eslint 8. This added 459 packages with no errors. Suggested change: align the pinned eslint-plugin-yml constraint with the pinned eslint version so the declared dependency set actually resolves.

Bug 2 — version constraints are escaped with a literal backslash

Separately, the install command shell-escapes the caret in each constraint:

ddev npm install --save-dev --package-lock cspell@\^9.8.0 eslint@\^8.57.1 ... stylelint-prettier@\^5.0.3

npm reads the backslash literally:

npm error code EINVALIDTAGNAME
npm error Invalid tag name "\^9.8.0" of package "cspell@\^9.8.0": Tags may not have any characters that encodeURIComponent encodes.

npm treats \^9.8.0 as a dist-tag rather than a semver range. Constraints should reach npm unescaped (build the name@range tokens as an argv array rather than a shell string, or quote the whole token instead of escaping the caret).

Bug 3 — the failure is non-fatal and success is reported anyway

After the failed versioned install, the installer runs a bare ddev npm install:

Installing JS deps in project root using npm (this may take several minutes).
Running: /opt/homebrew/bin/ddev npm install
removed 132 packages, and audited 1 package in 175ms
found 0 vulnerabilities
Node toolchain installed (project root).

That bare install only reconciles already-declared dependencies (note "removed 132 packages" — nothing DCQ-related was added). Despite this, the installer prints Node toolchain installed (project root). and the final summary lists:

What was installed:
  ...
  - Node toolchain (ESLint, Prettier, Stylelint)

So the exit is "success" and the summary claims the toolchain is present, even though the required dev-deps were never installed.

Suggested changes

  1. Fix the version constraint (Bug 1): align eslint-plugin-yml with the pinned eslint@^8.57.1 (e.g. eslint-plugin-yml@^1) so the dependency set resolves.
  2. Fix the quoting (Bug 2): pass name@range arguments to npm without shell-escaping the ^.
  3. Do not report success after a failed install (Bug 3): make the npm-install failure fatal, or after the install verify the required packages are actually present and print a prominent warning (and omit the toolchain from the "What was installed" summary) if any are missing. Do not treat a bare ddev npm install as satisfying the requirement.

The core concern: the current behavior can leave users without the code-quality tooling while telling them it was installed.

Environment

  • drupal-code-quality: v1.0.6 (installed from UltraBob/ddev-drupal-code-quality)
  • Host: macOS (Darwin, arm64 — homebrew ddev at /opt/homebrew/bin/ddev)
  • npm run inside DDEV web container

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions