`changeset publish --no-git-tag` still creates git tags for private packages
Hello folks!! Thanks for the awesome toolset -- this is by far my favorite path for managing releases . I think I found a bug that I'd love to see fixed; I've worked around it with some hacky manual scripting but I think the functionality is just broken in its current form so thought I'd raise an issue here for feedback and hopefully resolution :).
Summary
The --no-git-tag flag on changeset publish is advertised as suppressing git tag creation during publish, but it only suppresses tags for packages that were published to npm. When config.privatePackages.tag: true, publish continues to create annotated git tags for untagged private packages even when --no-git-tag is passed. The flag is non-functional for any repo that uses the built-in private-package tagging feature.
Reproduction
Monorepo config:
// .changeset/config.json
{
"privatePackages": { "version": true, "tag": true }
// ...
}A single private package at 0.1.0, add a minor changeset, changeset version, then:
$ changeset publish --no-git-tag
No unpublished projects to publish
success found untagged projects:
[email protected]
$ git tag -l
[email protected]Expected: no tags created.
Actual: [email protected] is created locally as an annotated tag.
Root cause
In packages/cli/src/commands/publish/index.ts at v2.31.0 (unchanged on main):
export default async function publish(
cwd,
{ otp, tag, gitTag = true }: { ...; gitTag?: boolean },
config
) {
// ...
const tagPrivatePackages = config.privatePackages && config.privatePackages.tag;
// ...
if (gitTag) {
await tagPublish(tool, successfulNpmPublishes, cwd); // public: guarded
}
// ...
if (untaggedPrivatePackageReleases.length > 0) {
await tagPublish(tool, untaggedPrivatePackageReleases, cwd); // private: NOT guarded
}
}The gitTag parameter (set to false by --no-git-tag) guards the public-package call but not the private-package call. Git-blame shows the private branch landed in #662 (Oct 2022), after --no-git-tag was added in #701 (Dec 2021); the new code path simply didn't honor the existing flag.
Proposed fix
if (untaggedPrivatePackageReleases.length > 0) {
success("found untagged projects:");
logReleases(untaggedPrivatePackageReleases);
- await tagPublish(tool, untaggedPrivatePackageReleases, cwd);
+ if (gitTag) {
+ await tagPublish(tool, untaggedPrivatePackageReleases, cwd);
+ }
}Honors the CLI flag uniformly across both publish paths. Does not change anything about the privatePackages.tag config — that still controls whether private packages are considered for tagging at all; --no-git-tag becomes the per-invocation override it's already documented to be.
Breaking change analysis
This is a bug, not an intentional behavior AFAICT. Consider who is affected:
- Users not passing
--no-git-tag— no change. Both branches still tag; identical behavior. - Users passing
--no-git-tagwith only public packages — no change. The guarded branch was already respecting the flag. - Users passing
--no-git-tagwithprivatePackages.tag: true— fewer tags created than today. This group is the only one affected, and they are already in an inconsistent state: they asked for no tags but got some anyway. No reasonable use case requires selectively tagging private packages while suppressing public ones via--no-git-tag; anyone who wants private tags created can either omit the flag or runchangeset tagseparately, which tags both public and private packages deterministically. - Users passing
--no-git-tagwithprivatePackages.tag: false— no change. Private packages weren't eligible for tagging to begin with.
The asymmetry has no documented rationale, and the current behavior means --no-git-tag cannot be used as a reliable "never create tags in this step" primitive — which is exactly what users need when they drive tagging from a separate step (e.g. CD pipelines that tag only after a post-publish lockfile commit). Treat as a straightforward bug fix, not a breaking change.
Why this matters
Downstream CI that needs to stage publish and tag as separate commits (for reasons like post-publish lockfile updates, tag-triggered Docker builds, or signed-tag requirements) currently has to sweep local tags between publish and tag to avoid tags pointing at the wrong commit. A one-line fix removes that class of workaround entirely :)
Source: changesets/changesets