Summary
Note: This issue is raised by a non-native English speaker. The text was prepared, translated, and refined with AI assistance, but the source-code review, downstream incident analysis, and reproduction reasoning were checked against the actual code.
On Windows, running relizy release / relizy bump for a fresh monorepo still does not provide a complete first-release path, especially in independent mode:
- core logic still depends on GNU commands such as
grep, head, and sed, which are not reliably available in PowerShell / cmd;
- the "no baseline tag yet" path is only partially modeled internally, so downstream projects still need an external wrapper or manual bootstrap tags to complete the first release.
In practice, this means the first-release workflow still depends on downstream compatibility code instead of being handled end-to-end by relizy itself.
This is not the same problem as the Windows path separator bug already fixed in issue #52 / PR #53. It is also separate from the "real git root differs from relizy working directory" issue.
Environment
| Item |
Value |
| OS |
Windows 10 / 11 (win32) |
| Shell |
PowerShell / cmd |
| relizy |
issue still visible on current develop branch |
| Node |
>=20 |
| Typical scenario |
first release with monorepo.versionMode = "independent" |
Current downstream symptom
Downstream projects currently need an extra compatibility wrapper before invoking relizy, and that wrapper has to do three things:
- prepend Git for Windows
usr/bin to PATH so grep / head / sed are executable;
- scan workspace packages before
release / bump and check whether each package already has a pkg@x.y.z baseline tag;
- abort and print manual
git tag commands if those baseline tags are missing.
That is strong evidence that relizy core still does not provide a native "Windows + first independent release" workflow on its own.
Current downstream workaround: relizy-runner
To work around these incompatibilities, downstream projects currently use a hand-written compatibility wrapper:
- implementation:
https://github.com/ruan-cat/monorepo/blob/dev/packages/utils/src/node-esm/scripts/relizy-runner/index.ts
- design notes:
https://utils.ruan-cat.com/node-esm/scripts/relizy-runner.md
This wrapper is not intended to replace relizy. Its role is to add a thin layer of "environment compatibility + first-release prechecks" before calling the real relizy CLI. In its current form, it does three things:
-
Windows GNU tool injection
- on Windows, it tries to locate Git for Windows
usr/bin
- it prepends
grep / head / sed to PATH
- this is required only because current
relizy core still contains shell-pipeline paths that depend on those tools
-
independent baseline-tag precheck
- before
release / bump, it scans workspace packages from pnpm-workspace.yaml
- it checks whether each package already has a
pkg@version tag
- if not, it aborts and prints the manual tag commands
-
automatic --yes for release / bump
- this prevents
relizy from blocking on interactive confirmation in CI or non-TTY shells
- this is only a workflow convenience and does not change version-calculation behavior
It is important to be explicit about scope: relizy-runner does not change relizy release semantics. It is only a downstream compatibility layer:
- it does patch the environment, run baseline prechecks, and inject
--yes
- it does not solve the underlying
relizy core-modeling issues around tag resolution, commit ranges, and first-release baseline handling
That is why a downstream repository can be made to "work" today while the underlying problem still exists in relizy core. The purpose of this issue is to move as much of that currently wrapper-only behavior as possible back into relizy itself.
Root cause
1. GNU pipeline dependencies still exist in core code
There are still several shell pipelines in the current source:
src/core/tags.ts
git tag --sort=-creatordate | grep -E '...' | head -n 1
git tag --sort=-creatordate | head -n 1
git tag --sort=-creatordate | grep -E '^pkg@' | head -n ${limit}
git tag --sort=-creatordate | grep -E '...' | sed -n '1p'
src/core/repo.ts
git log --reverse --format="%H" -- "${relativePath}" | head -1
These commands are usually available on Linux / macOS / Git Bash, but they are not a safe assumption in Windows PowerShell / cmd. As a result:
- tag discovery can fail;
- the new-package path in
getFirstPackageCommitHash() also fails because it still uses head;
- users need external PATH patching or extra GNU tooling just to make core release logic work.
So Windows compatibility is still effectively delegated to downstream wrappers instead of being handled inside relizy.
2. The "no baseline tag yet" first-release path is only partially modeled
Current code already shows an intention to support first releases without real tags:
src/core/tags.ts introduces NEW_PACKAGE_MARKER
src/core/repo.ts special-cases from === NEW_PACKAGE_MARKER
- documentation already presents first-release changelogs as
v0.0.0...vX.Y.Z
However, the flow is still incomplete:
NEW_PACKAGE_MARKER mainly solves "do not diff from the first commit of the whole repository and cause ENOBUFS", but it does not elevate "no baseline tag yet" into a unified core concept.
src/core/changelog.ts still mostly organizes behavior around real tags or getFirstCommit() fallback rather than a formal no-baseline state shared across bump / changelog / compare / provider-release steps.
src/core/tags.ts still has fallbacks like getLastRepoTag(...) || getFirstCommit(config.cwd) in repo-wide paths.
Inference from the current source:
Inference from code inspection: if the repository's first commit itself is used directly as from, the left boundary commit can be excluded from the effective diff range. The independent new-package path already works around this by using firstPackageCommit^, which suggests that "first release without tags" should be modeled as an explicit baseline-resolution problem rather than mixing three meanings (real tag, first commit, and special marker) across different code paths.
In other words, the codebase already contains pieces that point toward first-release support, but not a full end-to-end model. That is why downstream projects still need wrapper-level baseline checks.
Expected behavior
relizy should treat the following as a native supported scenario:
- On Windows,
relizy release / relizy bump should work in a normal Node + Git for Windows setup without requiring extra grep / head / sed availability.
- In
independent mode, if a package has no historical tag yet, relizy should still treat it as a valid first release instead of requiring users to manually create bootstrap tags first.
- For first releases, version calculation, commit-range resolution, changelog titles, compare ranges, and provider-release text should all rely on one consistent "bootstrap baseline" model rather than forcing downstream wrappers to fill the gap.
Proposed fix
Core direction
Move both concerns into relizy core so the "first release on Windows" path becomes a single supported and testable workflow.
Concrete changes
1. Replace shell pipelines with pure Node filtering
Suggested approach:
- use
git tag --sort=-creatordate once to get the tag list;
- filter stable tags / package tags in JavaScript / TypeScript;
- change
getFirstPackageCommitHash() to a single git log --reverse --format="%H" -- <path> call and pick the first line in Node;
- remove reliance on
grep, head, and sed.
That would bring Windows compatibility back into the core library instead of keeping it as an external PATH workaround.
2. Promote "no baseline tag yet" into a formal baseline-resolution layer
Instead of treating first release as "the downstream user must create a real bootstrap tag first", relizy could model it explicitly inside core, for example as:
- repo-level no-tag baseline
- package-level no-tag baseline
That layer could return two kinds of information:
- calculation boundary: the git revision actually used for commit-range computation, for example "parent of the first relevant commit";
- display boundary: the virtual baseline shown in changelog / compare / release text, for example
v0.0.0 or pkg@0.0.0.
This means:
- calculation should not require a real pre-existing git tag;
- display can still preserve the documented
v0.0.0...vX.Y.Z / pkg@0.0.0...pkg@X.Y.Z presentation.
3. Reuse the same baseline semantics across the whole workflow
At minimum, this should be consistent across:
src/core/tags.ts
src/core/repo.ts
src/core/changelog.ts
- compare / provider-release generation logic
The goal is:
- bump resolves the correct first-release commit range;
- changelog generates the expected
0.0.0 -> new version presentation;
- provider release / compare links no longer assume that a real bootstrap tag must already exist.
4. Add Vitest regression coverage
Suggested regression tests:
- Windows-safe tag lookup and first-package-commit discovery without GNU tools;
- first release in independent mode when package tags do not exist yet;
- first release in unified / selective mode when repo tags do not exist yet;
- changelog output still renders
v0.0.0...vX.Y.Z or pkg@0.0.0...pkg@X.Y.Z;
- existing tagged upgrade flows remain unchanged.
Why this is better than continuing to require manual bootstrap tags
The documentation already presents "first release" as a normal supported capability, not as a semi-manual bootstrap procedure. Since core already contains building blocks such as NEW_PACKAGE_MARKER and v0.0.0 display behavior, the more coherent direction is to complete that design inside relizy rather than leaving first-release handling to every downstream wrapper.
Additional notes
References
Summary
On Windows, running
relizy release/relizy bumpfor a fresh monorepo still does not provide a complete first-release path, especially inindependentmode:grep,head, andsed, which are not reliably available in PowerShell / cmd;In practice, this means the first-release workflow still depends on downstream compatibility code instead of being handled end-to-end by
relizyitself.This is not the same problem as the Windows path separator bug already fixed in issue #52 / PR #53. It is also separate from the "real git root differs from relizy working directory" issue.
Environment
win32)developbranch>=20monorepo.versionMode = "independent"Current downstream symptom
Downstream projects currently need an extra compatibility wrapper before invoking
relizy, and that wrapper has to do three things:usr/binto PATH sogrep/head/sedare executable;release/bumpand check whether each package already has apkg@x.y.zbaseline tag;git tagcommands if those baseline tags are missing.That is strong evidence that
relizycore still does not provide a native "Windows + first independent release" workflow on its own.Current downstream workaround:
relizy-runnerTo work around these incompatibilities, downstream projects currently use a hand-written compatibility wrapper:
https://github.com/ruan-cat/monorepo/blob/dev/packages/utils/src/node-esm/scripts/relizy-runner/index.tshttps://utils.ruan-cat.com/node-esm/scripts/relizy-runner.mdThis wrapper is not intended to replace
relizy. Its role is to add a thin layer of "environment compatibility + first-release prechecks" before calling the realrelizyCLI. In its current form, it does three things:Windows GNU tool injection
usr/bingrep/head/sedto PATHrelizycore still contains shell-pipeline paths that depend on those toolsindependent baseline-tag precheck
release/bump, it scans workspace packages frompnpm-workspace.yamlpkg@versiontagautomatic
--yesforrelease/bumprelizyfrom blocking on interactive confirmation in CI or non-TTY shellsIt is important to be explicit about scope:
relizy-runnerdoes not changerelizyrelease semantics. It is only a downstream compatibility layer:--yesrelizycore-modeling issues around tag resolution, commit ranges, and first-release baseline handlingThat is why a downstream repository can be made to "work" today while the underlying problem still exists in
relizycore. The purpose of this issue is to move as much of that currently wrapper-only behavior as possible back intorelizyitself.Root cause
1. GNU pipeline dependencies still exist in core code
There are still several shell pipelines in the current source:
src/core/tags.tssrc/core/repo.tsThese commands are usually available on Linux / macOS / Git Bash, but they are not a safe assumption in Windows PowerShell / cmd. As a result:
getFirstPackageCommitHash()also fails because it still useshead;So Windows compatibility is still effectively delegated to downstream wrappers instead of being handled inside
relizy.2. The "no baseline tag yet" first-release path is only partially modeled
Current code already shows an intention to support first releases without real tags:
src/core/tags.tsintroducesNEW_PACKAGE_MARKERsrc/core/repo.tsspecial-casesfrom === NEW_PACKAGE_MARKERv0.0.0...vX.Y.ZHowever, the flow is still incomplete:
NEW_PACKAGE_MARKERmainly solves "do not diff from the first commit of the whole repository and cause ENOBUFS", but it does not elevate "no baseline tag yet" into a unified core concept.src/core/changelog.tsstill mostly organizes behavior around real tags orgetFirstCommit()fallback rather than a formal no-baseline state shared across bump / changelog / compare / provider-release steps.src/core/tags.tsstill has fallbacks likegetLastRepoTag(...) || getFirstCommit(config.cwd)in repo-wide paths.Inference from the current source:
In other words, the codebase already contains pieces that point toward first-release support, but not a full end-to-end model. That is why downstream projects still need wrapper-level baseline checks.
Expected behavior
relizyshould treat the following as a native supported scenario:relizy release/relizy bumpshould work in a normal Node + Git for Windows setup without requiring extragrep/head/sedavailability.independentmode, if a package has no historical tag yet,relizyshould still treat it as a valid first release instead of requiring users to manually create bootstrap tags first.Proposed fix
Core direction
Move both concerns into
relizycore so the "first release on Windows" path becomes a single supported and testable workflow.Concrete changes
1. Replace shell pipelines with pure Node filtering
Suggested approach:
git tag --sort=-creatordateonce to get the tag list;getFirstPackageCommitHash()to a singlegit log --reverse --format="%H" -- <path>call and pick the first line in Node;grep,head, andsed.That would bring Windows compatibility back into the core library instead of keeping it as an external PATH workaround.
2. Promote "no baseline tag yet" into a formal baseline-resolution layer
Instead of treating first release as "the downstream user must create a real bootstrap tag first",
relizycould model it explicitly inside core, for example as:That layer could return two kinds of information:
v0.0.0orpkg@0.0.0.This means:
v0.0.0...vX.Y.Z/pkg@0.0.0...pkg@X.Y.Zpresentation.3. Reuse the same baseline semantics across the whole workflow
At minimum, this should be consistent across:
src/core/tags.tssrc/core/repo.tssrc/core/changelog.tsThe goal is:
0.0.0 -> new versionpresentation;4. Add Vitest regression coverage
Suggested regression tests:
v0.0.0...vX.Y.Zorpkg@0.0.0...pkg@X.Y.Z;Why this is better than continuing to require manual bootstrap tags
The documentation already presents "first release" as a normal supported capability, not as a semi-manual bootstrap procedure. Since core already contains building blocks such as
NEW_PACKAGE_MARKERandv0.0.0display behavior, the more coherent direction is to complete that design insiderelizyrather than leaving first-release handling to every downstream wrapper.Additional notes
gitRootmismatch issue.References
src/core/tags.tssrc/core/repo.tssrc/core/changelog.tsdocs/src/guide/getting-started.mddocs/src/guide/changelog.md