ReviewOS

pantry-pm/pantry

publish:commit ignores `private: true` and tars whole repo when given a single-path argument like '.'

#203
Open glennmichael123 opened this 24 days ago · 0 comments
24 days ago

Summary

pantry publish:commit '.' (single-path argument) has two bugs that compound:

  1. private: true is not honored when the argument is a direct path (only the /* glob branch checks for private).
  2. The whole repo gets tarred because the monorepo root's package.json lacks a files field, so createTarballDefault is used and the entire workspace is rsync'd — ignoring child packages' files fields.

Combined, this means pantry publish:commit '.' from a monorepo root happily attempts to publish a private workspace-root package and produces tarballs that blow past the 50MB registry limit.

Reproduction

Monorepo root package.json:

{
  "name": "ts-watches",
  "private": true,
  "version": "0.1.0",
  "workspaces": ["packages/*"]
}

Child packages/ts-watches/package.json:

{
  "name": "ts-watches",
  "version": "0.1.0",
  "files": ["README.md", "dist"]
}

Run from repo root:

pantry publish:commit '.'

Output:

Found 1 package(s) to publish:
  - ts-watches (v0.1.0)        ← root pkg, marked private:true, picked up anyway
Publishing ts-watches...
  Scanning for ignore files in: /repo/.
  ...
  Tarball: 73735916 bytes      ← whole repo tarred; child's files:[] field ignored
  Upload error: {"error":"Tarball for ts-watches exceeds maximum size of 50MB"}

Expected behavior:

  • Either skip the private root, or
  • Resolve '.' to discoverable child packages (same as './packages/*') and use each child's files field for the tarball.

Root cause (code review)

In packages/zig/src/cli/commands/publish_commit.zig, resolveGlobPattern:

  • The /* branch (lines ~387–472) iterates child dirs and does check `private`:

    // Skip private packages
    const is_private = if (root.object.get(\"private\")) |p|
        if (p == .bool) p.bool else false
    else false;
    if (is_private) {
        style.print(\"  Skipping {s} (private)\n\", .{entry.name});
        ...
        continue;
    }
  • The single-path branch (lines ~474+, `// Treat as a direct path to a single package`) does not read `private` at all — it just calls `readPackageName` / `readPackageVersion` and appends to `packages`.

Then in `registry.zig` `createTarball`, the package_dir is the repo root. Since the root `package.json` has no `files` field, it falls through to `createTarballDefault`, which rsyncs the entire `package_dir` (the whole repo) with only `.pantryignore` exclusions. The child package's `files: ["README.md", "dist"]` field — which would have made the tarball ~600KB — is never consulted.

Suggested fix

Two small fixes that together resolve this cleanly:

  1. In the single-path branch of `resolveGlobPattern`, mirror the `private: true` check from the `/*` branch and skip the package if it's private (with a clear message — currently it silently proceeds).

  2. For monorepo roots: when the resolved single path's `package.json` declares `workspaces`, either:

    • error out with a hint (`"This looks like a monorepo root — try `pantry publish:commit './packages/*'` to publish individual workspace packages"`), or
    • auto-expand to the workspace globs (closer to what `detectMonorepoPackages` does in the no-args case).

Fix #1 alone would have surfaced the user error here ("private package, refusing to publish") instead of producing a 70MB tarball and a confusing size-limit error. Fix #2 makes the happy path work for monorepos.

Companion to #202 (`.pantryignore` glob behavior) — different bug, same end-user symptom of oversized tarballs from `publish:commit`.

Environment

Sign in to comment on this issue.