Skip to content

Reconsider building the registry on install, which pnpm consumers must allowlist #169

Description

@lmcorbalan

User story / Problem statement

The registry service is consumed as a git dependency and builds itself on
install: registry/dist is gitignored, so the root manifest's prepare script
(tsc -p registry/tsconfig.json) is the only thing that produces the bin the
bin field points at. npm runs it without being asked. pnpm does not.

pnpm refuses to run a git-hosted package's build scripts unless the consumer
allowlists the package by name, and it fails closed: the install stops with
ERR_PNPM_GIT_DEP_PREPARE_NOT_ALLOWED, links no bin, and leaves nothing behind
to run. Measured against this repository at d33f80b on both pnpm versions in
current use:

  • pnpm 11.25.0 reads allowBuilds in pnpm-workspace.yaml, a map of specifier
    to true.
  • pnpm 10 reads onlyBuiltDependencies in the same file, a list. Different key,
    same refusal.
  • The key has to be the full resolved git specifier. Three shorter forms were
    tried and all three are refused: the bare package name,
    @bootnodedev/canton-token-forge@0.2.0, and @bootnodedev/canton-token-forge@*,
    the last with ERR_PNPM_INVALID_VERSION_UNION ... Use exact versions only.
  • A tag pin resolves to its commit sha inside that key, proved by installing
    a locally tagged clone. So the allowlist entry is invalidated by every version
    bump, and a consumer updating the pin has to edit two places or the install
    breaks.

The consumer this package was built for (canton-dappbooster) is a pnpm
repository, so this is the path that matters. The README's consumer section
documents the allowlist entry as a precondition (added by the change closing
#163), which unblocks a reader, but it is a requirement this repository imposes
on every consumer for the life of the package.

Committing registry/dist would remove it entirely: with the build output
present in the git tree there is nothing for prepare to do at install time,
and no package manager has to be told anything.

Expected outcome

A consumer adds one dependency line and installs, on npm or pnpm, with no
allowlist entry and nothing to keep in step with the pin.

Acceptance criteria

  • A decision is recorded either way, with the reason, in whichever document
    owns the packaging story
  • If build output ships: registry/dist is committed, the root prepare
    script no longer builds it on install, and the ignore rules and the CI job
    that guards them are updated to match
  • If build output ships: a check keeps the committed output in step with
    registry/src, so a source change with a stale dist reds rather than
    publishing the old service
  • If build output ships: pnpm add of a tag pin installs, links the bin and
    runs it, from a scratch consumer with no pnpm-workspace.yaml at all
  • If the prepare build stays: the README's pnpm precondition is kept
    correct against the pnpm versions in use, and the reason for keeping it is
    written down

Alternatives considered

  • Publishing to a registry instead of consuming from git. An npm-registry
    tarball carries registry/dist already built, so the whole question
    disappears. Rejected for now because this repository follows
    canton-wallet-service, which is consumed from git and is not on the public
    registry, and because publishing a private package needs registry credentials
    in every consumer.
  • Telling consumers to run the build themselves after install. A postinstall
    step in the consumer is the same allowlist problem with more moving parts, and
    it puts a TypeScript toolchain in the consumer's dependency tree.
  • Leaving it documented. This is what ships today. It works, and the cost is
    paid once per consumer plus once per version bump, but it is a cost this
    repository chose to impose and it is invisible until an install fails.

Technical notes

  • The ignore rule that keeps registry/dist out of git is registry/**/dist in
    the ROOT .gitignore, deliberately placed there rather than in
    registry/.gitignore, because npm applies a nested ignore file to the pack
    walk even for a path the manifest's files allowlist names. The package CI
    job asserts both what that rule covers and which file carries it. Committing
    build output means unpicking that, and the job's checks are what will catch a
    half-finished change.
  • npm run smoke:registry packs, installs and runs the package end to end. It
    is the check that would prove a prebuilt tarball still works, and it does not
    currently exercise pnpm at all.
  • registry/tsconfig.json sets no noEmitOnError, so a prepare that
    type-errors still writes files into registry/dist. Any staleness check has
    to compare content, not mere presence.
  • The key's shape follows repository visibility, not how the consumer writes
    the spec. Measured on pnpm 11.25.0: a git+https:// and a git+ssh:// spec
    for this repository both resolve to the same key,
    @bootnodedev/canton-token-forge@git+https://github.com/BootNodeDev/canton-token-forge.git#<sha>,
    because pnpm normalizes a hosted-git spec before resolving it. The public
    canton-wallet-service, installed the same way, resolves instead to
    @bootnodedev/canton-wallet-service@https://codeload.github.com/BootNodeDev/canton-wallet-service/tar.gz/<sha>:
    pnpm prefers an anonymous codeload tarball and falls back to a git fetch only
    when it cannot have one, which for a private repository is always. So the form
    the README documents is right for every consumer while this repository is
    private, and it would change on the day the repository goes public, with
    nothing in the repository having changed.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions