Skip to content

Add nub package manager ecosystem (nub.lock) - #15524

Open
colinhacks wants to merge 43 commits into
dependabot:mainfrom
colinhacks:feat/nub-ecosystem
Open

Add nub package manager ecosystem (nub.lock)#15524
colinhacks wants to merge 43 commits into
dependabot:mainfrom
colinhacks:feat/nub-ecosystem

Conversation

@colinhacks

@colinhacks colinhacks commented Jul 7, 2026

Copy link
Copy Markdown

Refs #15523.

What are you trying to accomplish?

First-party support for the nub package manager, which writes nub.lock — byte-compatible with the pnpm lockfile v9 format. nub is distributed on npm as @nubjs/nub; dependencies resolve from the npm registry.

Because nub.lock is a pnpm-lock v9 document, the gem depends on dependabot-npm_and_yarn and reuses its pnpm-format classes rather than duplicating them — the uvdependabot-python / pre_commitdependabot-npm_and_yarn pattern. The nub-specific surface is nub.lock detection, packageManager: "nub@x", and invoking the nub CLI.

Supported update types:

  • Direct dependency updates (version and range changes).
  • Transitive / sub-dependency updates — Dependabot security updates for deps-of-deps — via nub update <name>@<version> --lockfile-only.
  • Git dependency ref changes (e.g. #2.0.0#2.1.0).

Known limitation (documented; the two tests are skipped): advancing a git dependency pinned to a branch to that branch's latest commit. nub's lockfile-only install conservatively keeps the locked SHA and exposes no force-re-resolution for this case; tracked for a follow-up nub release. Git ref changes (above) work.

Known limitation: conflict detection for transitive security updates is not yet implemented. The inherited yarn-based ConflictingDependencyResolver could not function against nub's pnpm-v9 nub.lock — it reads a yarn.lock that is never written, so every call failed into an empty result — and has been removed; a pnpm-lock-aware implementation over nub.lock is a follow-up.

Diff composition

Of ~110.8k added lines: ~8k gem code (nub/lib), ~6k specs, ~61.5k spec fixtures, and ~35k native helpers (nub/helpers), plus ~200 of Docker/gemspec/config glue. The fixtures and helpers follow the Bun ecosystem's standalone layout — Dependabot's most recent comparable ecosystem, cloned to the same shape. Spec fixtures the nub suite never loads have been pruned (~53k lines removed) so the tree carries only what the tests reference.

Happy to split this into sequential PRs — package manager → parser → fetcher → updater, the shape the Bun review took (#11267#11300) — if the maintainers prefer.

Anything you want to highlight for special attention from reviewers?

The gem reuses dependabot-npm_and_yarn's pnpm classes (the lockfile-parser helper, PnpmErrorHandler) via a gemspec dependency plus aliases; nub/Dockerfile copies the full npm_and_yarn lib so the aliases load in the production image (the pre_commit precedent).

How will you know you've accomplished your goal?

The nub rspec suite is green: 338 examples, 0 failures, 3 pending (the git-branch-SHA-advance limitation above, and one monorepo source-directory metadata case). A bin/dry-run.rb nub produces update PRs against a real nub-lockfile repository.

Checklist

  • I have run the complete test suite to ensure all tests and linters pass.
  • I have thoroughly tested my code changes, including adding tests for new functionality.
  • I have written clear and descriptive commit messages.
  • I have provided a detailed description of the changes in the pull request.
  • I have ensured that the code is well-documented and easy to understand.

nub writes nub.lock (byte-compatible with pnpm-lock v9). The gem is cloned
from the bun ecosystem and adapted: nub.lock detection, packageManager nub@x,
NubLock routed through the pnpm parseLockfile helper, native updater invokes
nub install --lockfile-only, Dockerfile installs @nubjs/nub.

WIP: spec fixtures still carry bun text-format lockfiles and need regenerating
as pnpm-v9 nub.lock; parser specs updated accordingly.
Adds require "dependabot/nub" so the ecosystem gem is loaded/discovered.
Remaining infra wiring (ci.yml, ci-filters.yml, smoke-filters.yml,
issue-labeler.yml, bin/dry-run.rb, script/dependabot, rakelib helpers)
must be generated via `rake ecosystem:update_infrastructure[nub]` inside
the dependabot dev environment (ruby 3 + bundle).
- Fix compound-word rename corruption: :npm_nubdled -> :npm_bundled (B1).
- NubLock#dependencies: two-pass (specifier-bearing first) so alias metadata
  survives DependencySet de-dupe, mirroring the pnpm parser (S1).
- FileUpdater: drop invalid bun-style 'install <pkg>@<ver>'; nub is pnpm-compatible,
  so write the bumped package.json + 'nub install --lockfile-only' (V1 core path).
- Defer dependency_grapher: bun's grapher reads the bun-lock hash shape, incompatible
  with the pnpm-array parse output; the pnpm-v9 edge-reading rewrite is a follow-up.
- version_resolver: TODO documenting the remaining 'update <pkg>@<ver>' grammar gap.
- Regenerate simple_v0/simple_v1/wildcard fixtures as real pnpm-v9 nub.lock; rewrite
  lockfile_parser_spec to pnpm-v9 semantics.
- Dockerfile NUB_VERSION -> 0.3.1 (current published).
Copilot AI balanced review requested due to automatic review settings July 7, 2026 19:31

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot wasn't able to review this pull request because it exceeds the maximum number of lines (20,000). Try reducing the number of changed lines and requesting a review from Copilot again.

Add "nub" => "nub" to Config::File::PACKAGE_MANAGER_LOOKUP so
Utils.validate_package_manager! accepts it; without this the whole nub
ecosystem fails to load (register_version_class raises). Found by running
the nub parser spec in the dev image.
Comment thread nub/lib/dependabot/nub.rb Fixed
Comment thread nub/lib/dependabot/nub/file_fetcher.rb Fixed
Comment thread nub/lib/dependabot/nub/file_fetcher.rb Fixed
Comment thread nub/lib/dependabot/nub/file_fetcher.rb Fixed
Comment thread nub/lib/dependabot/nub/file_updater/package_json_preparer.rb Fixed
Comment thread nub/lib/dependabot/nub/update_checker/requirements_updater.rb Fixed
Comment thread nub/lib/dependabot/nub/update_checker/requirements_updater.rb Fixed
Comment thread nub/lib/dependabot/nub/update_checker/requirements_updater.rb Fixed
Comment thread nub/lib/dependabot/nub/update_checker/requirements_updater.rb Fixed
Comment thread nub/lib/dependabot/nub/update_checker/requirements_updater.rb Fixed
nub is pnpm-compatible with no 'install <pkg>@<ver>' add and rejects
'update <pkg>@<ver>' for an arbitrary version, so a lockfile-only update
pins each Dependabot-chosen version by writing it exactly into package.json,
running 'nub install --lockfile-only', then restoring the final requirement
and installing again (nub's lockfile-only install keeps the locked version).

Regenerates the multiple_updates + github_dependency fixtures as pnpm-v9;
updates file_updater/subdependency spec expectations to nub's grammar +
the pnpm-v9 git-dep (codeload tarball) lockfile form.
Copilot AI review requested due to automatic review settings July 7, 2026 19:52

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot wasn't able to review this pull request because it exceeds the maximum number of lines (20,000). Try reducing the number of changed lines and requesting a review from Copilot again.

- Fix file_fetcher nub-detection: contents listing + nub_lock fixture were
  bun-format (JSON fixtures weren't renamed); list nub.lock + pnpm-v9 content.
- Skip (documented follow-ups): git-dependency updates (nub keeps the locked
  git SHA; forcing re-resolution to a chosen ref is unwired) and monorepo
  source-directory metadata extraction.
Ran `rake ecosystem:update_infrastructure[nub]`: adds nub to the CI matrix
(ci.yml, images-branch/latest), ci/smoke filters + smoke-matrix, the issue
labeler, bin/dry-run LOAD_PATH, script/dependabot volume mount, updater
setup app_dirs_pattern, and the rakelib gemspec list.
Copilot AI review requested due to automatic review settings July 7, 2026 20:04

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot wasn't able to review this pull request because it exceeds the maximum number of lines (20,000). Try reducing the number of changed lines and requesting a review from Copilot again.

Replace the invalid `nub update <pkg>@<ver>` (nub rejects a pinned version on
`update`) with the same pin-then-install approach the FileUpdater uses: pin the
candidate version into the manifests and re-resolve `nub install --lockfile-only`,
so a peer-dependency conflict surfaces as a classifiable subprocess failure.
…ess)

Addresses an adversarial-review nit: NubLock references NubPackageManager::NAME
but relied on load order for the constant. Make the dependency explicit.
Copilot AI review requested due to automatic review settings July 7, 2026 20:10

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot wasn't able to review this pull request because it exceeds the maximum number of lines (20,000). Try reducing the number of changed lines and requesting a review from Copilot again.

nub.lock is byte-compatible with pnpm-lock v9, so nub can depend on
dependabot-npm_and_yarn and alias its byte-identical classes rather than
maintaining clones (uv/pre_commit precedent). Aliases PackageName,
VersionSelector, and FileUpdater::PackageJsonPreparer.
- Remove the unreferenced yarn-derived error-pattern block from nub.rb
  (VALIDATION_GROUP_PATTERNS/Utils/regexes were never consumed).
- Reuse NpmAndYarn::PnpmErrorHandler in the lockfile updater's error path
  (nub emits pnpm-format engine errors), layered before nub's own patterns.
- Delete the Yarn PnP vendor/artifact updater and the git-lfs .yarn pull
  (berry-only, dead for nub.lock).
- Delete the dead npm6 JS helper tree (no npm6: call site).
Copilot AI review requested due to automatic review settings July 7, 2026 22:26

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot wasn't able to review this pull request because it exceeds the maximum number of lines (20,000). Try reducing the number of changed lines and requesting a review from Copilot again.

…me-branch case

Grounding nub-dev shows nub re-resolves a git dependency when the manifest ref
changes (dependabot#2.0.0 -> dependabot#2.1.0): PackageJsonUpdater rewrites the ref and nub's
lockfile-only install picks up the new tag's commit. Add a fixture + FileUpdater
example proving this end to end.

The same-branch SHA advance (manifest ref unchanged, e.g. master -> master) stays
skipped with a precise reason: nub's conservative lockfile-only install keeps the
locked SHA, and nub exposes no targeted force-re-resolution. Refine the
metadata monorepo-directory skip reason to name the missing spec stub.
nub now depends on dependabot-npm_and_yarn (reused pnpm-format classes). The
updater-core base only stubs sibling ecosystems, so without copying the full
npm_and_yarn source the nub image would load its stub and the aliases would
fail at require time. Mirror pre_commit/Dockerfile, which copies its gem deps.
Copilot AI review requested due to automatic review settings July 7, 2026 22:50

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot wasn't able to review this pull request because it exceeds the maximum number of lines (20,000). Try reducing the number of changed lines and requesting a review from Copilot again.

…transitive/security updates

nub 0.4.1 (dependabot#355) accepts `update <pkg>@<version>` for registry deps INCLUDING
transitive/sub-dependencies not in any manifest. Replace the pin-restore
double-install with a single `nub update <deps>@<vers> --lockfile-only --no-save`
(mirrors npm_and_yarn's pnpm updater) + a settling install. This wires
Dependabot's security-update path for deps-of-deps (previously a NoChangeError).

Adds a transitive-update fixture + spec (bumps lodash, a transitive of
fetch-factory, in the lockfile). Suite: 338 examples, 0 failures, 3 pending
(same-branch git-SHA-advance ×2 + monorepo-dir metadata ×1 — documented).
Copilot AI review requested due to automatic review settings July 8, 2026 01:06

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot wasn't able to review this pull request because it exceeds the maximum number of lines (20,000). Try reducing the number of changed lines and requesting a review from Copilot again.

…endabot.yml

Three registration points nub was never added to, each of which every other
ecosystem carries:

- the root Gemfile and Gemfile.lock list every ecosystem as a path gem
  (32 of 33 before this); without the entry the root bundle cannot load
  dependabot-nub, which is the bundle the lint gates run through
- sorbet/config excludes each ecosystem's spec/ directory from typechecking
  (32 of 33 before this)
- .github/dependabot.yml watches each helpers directory that carries a
  package manifest; nub/helpers has package.json and package-lock.json,
  exactly like bun/helpers, so its JS dependencies were never being updated

The Gemfile.lock entries were verified by regenerating the lockfile with
bundler and diffing - the PATH block, DEPENDENCIES entry and CHECKSUMS entry
match its own resolution exactly.

nub is deliberately not added to updater/lib/github_api/ecosystem_mapper.rb:
that map tracks the Dependency Graph snapshot feature, which only the five
ecosystems implementing a DependencyGrapher are in. deno, sbt, julia,
opentofu, vcpkg and rust_toolchain are all absent for the same reason.
Upstream's typed-requirement migration advanced while this branch was open:
DependencyRequirement#[] now returns Object, so every chained access -
req[:source][:type], req.fetch(:requirement), r.dig(:source, :type) - stopped
typechecking. srb tc reported 45 errors, all in nub/.

Each site adopts the form npm_and_yarn and bun already use for the same line,
so nub stays a faithful clone of the ecosystem it was modelled on rather than
growing its own idiom: source_string("type"), source_hash, requirement_string,
.file, and the ObjectHash return type on the source-details signatures. The
git-tag reads move to latest_tag&.tag / &.commit_sha for the same reason.

No behaviour change is intended. srb tc is clean, rubocop reports no offenses
in nub/, and the spec suite returns 338 examples with exactly the same 9
environment-dependent failures as the unmodified tree - those 9 shell out to
the JS helper, which is only present in the updater image.
0.6.0 was two minors behind. Verified equivalent for the only two commands the
file updater runs - nub update <pkg>@<ver> --lockfile-only --no-save
--ignore-scripts, then nub install --lockfile-only --ignore-scripts - by
running both versions over the same nub-identity fixture, with and without a
forced release-age window as a positive control. Exit codes and resulting
lockfile match at every cell.

0.8.0 keeps the same engines floor (node >= 18.19.0, against the image's Node
24) and publishes the same eight platform packages, so nothing about the image
build changes.
Copilot AI review requested due to automatic review settings August 28, 2026 21:33

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@colinhacks

Copy link
Copy Markdown
Author

Any chance we can get eyes on this? Thanks! cc @JamieMagee

Dependency Review failed on nub/helpers/package-lock.json: tar 7.5.16 carries
five node-tar DoS advisories - GHSA-23hp-3jrh-7fpw (critical),
GHSA-8x88-c5mf-7j5w and GHSA-r292-9mhp-454m (high), GHSA-gvwx-54wh-qm9j and
GHSA-w8wr-v893-vjvp (moderate). The highest first-patched version among them is
7.5.21; 7.5.22 is current.

tar is pinned in the helpers' overrides block, which exists to hold transitive
dependencies at safe versions, so the fix belongs there. Regenerating the
lockfile changes exactly one package version and adds or removes none.

This was the only dependency the gate flagged: tar 7.5.16 appeared nowhere else
in the repo, so it was new to the dependency graph and got reviewed, while the
helpers' other pinned transitives already exist at the same versions under
bun/helpers and npm_and_yarn/helpers.
CodeQL reported three rb/polynomial-redos alerts against nub - two in
registry_finder and one in requirement - and passes on every other open pull
request, so the ecosystem stood out as introducing security alerts.

Uses the mitigation this repo already applies elsewhere: helm's
update_checker/requirements_updater.rb makes the same class of separator regex
possessive under the comment "Possessive quantifiers (++) keep these linear on
pathological input". Same treatment and same comment style here, so the fix is
an in-repo idiom rather than a nub invention.

Rewritten: %r{/+$} to %r{/++$}, %r{^.*?//} to %r{^[^/]*+(?:/[^/]*+)*?//}, and
AND_SEPARATOR's \s+/&+ runs to \s++/&++. OR_SEPARATOR and the requirements
updater's SEPARATOR are left alone - neither was flagged, and \s*\|+ has
disjoint adjacent classes so there is no ambiguity to remove.

Equivalence was checked rather than assumed: every pair was run over 1327
inputs - real requirement and URL strings, exhaustive short combinations, and
long repetition shapes - comparing split, scan, gsub and match, for 37156
comparisons with no divergence. A negative control confirmed the comparison
detects genuinely different pairs. The nub suite is unchanged at 338 examples.
Copilot AI review requested due to automatic review settings August 28, 2026 22:00

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Dependency Review flagged brace-expansion 1.1.13 (three high-severity DoS
advisories) once tar was fixed. Auditing every entry in the overrides block
against the advisory database found two more that were not reported only
because the same versions already exist elsewhere in the repo: ip-address
10.1.1 and js-yaml 4.2.0.

- brace-expansion 1.1.13 -> 1.1.18, which is what bun/helpers and
  npm_and_yarn/helpers already resolve to
- ip-address 10.1.1 -> 10.4.0, likewise matching both
- js-yaml 4.2.0 -> 4.3.1, the first patched release on the 4.x line

Each target was confirmed to carry no advisories of its own before being
pinned, and all eighteen overrides now come back clean. Regenerating the
lockfile changes exactly these three versions and adds or removes none.

The eight advisories npm audit still reports are all on unpinned transitives
that resolve to the same versions under bun/helpers and npm_and_yarn/helpers,
so they are not new to the dependency graph and are outside this gate.
Copilot AI review requested due to automatic review settings August 28, 2026 22:05

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Copilot AI review requested due to automatic review settings August 28, 2026 23:58

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

arborist 8 pins pacote ^19.0.0, and pacote is vulnerable to a DoS via
addGitSha below 21.5.1 (GHSA-w4pp-8pjf-rmxw). Bumping arborist to the
^9.4.0 line npm_and_yarn already uses resolves pacote to 21.5.1 without
forcing a version its dependent does not declare.

The two files that construct an Arborist, lib/npm/conflicting-dependency-parser.js
and lib/npm/vulnerability-auditor.js, are identical to npm_and_yarn's, which
runs them on arborist 9.
Copilot AI review requested due to automatic review settings August 29, 2026 00:05

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

http-proxy-agent 5.0.0 pulls @tootallnate/once 2, which resolved to
2.0.0 and is vulnerable to incorrect control flow scoping below 2.0.1
(GHSA-vpq2-c234-7xj6). bun already resolves 2.0.1 through the same
chain.
Copilot AI review requested due to automatic review settings August 29, 2026 00:11

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

# Conflicts:
#	Gemfile.lock
#	updater/Gemfile.lock
RuboCop 1.90.0 adds Style/DirectiveScope, which rejects a
disable/enable pair wrapping a single statement. Matches the form
npm_and_yarn's lockfile updaters already use.
Copilot AI review requested due to automatic review settings August 31, 2026 22:41

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@colinhacks

Copy link
Copy Markdown
Author

Merged latest main to clear the conflicts. Happy to make any changes whenever someone has a chance to look. Thanks!

The test runner was renamed to turbo_tests2 and every other ecosystem's
ci-test moved with it; nub's still invoked turbo_tests and exited 127.
Copilot AI review requested due to automatic review settings August 31, 2026 22:44

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants