fix(#1224): accept --pr 0 placeholder at changeset creation (#1231)

* fix(#1224): accept --pr 0 placeholder at changeset creation

The required-field guard `!opts.pr` treated the integer 0 as falsy,
rejecting the documented `pr: 0` two-push placeholder with a usage
error (exit 2). Non-numeric `--pr abc` (NaN) was also silently
accepted before (passes `!NaN === true`... actually `!NaN` is true, so
NaN would trigger the guard already). The new explicit checks use
`opts.pr === null` for missing flag and `Number.isNaN` for non-numeric
input, accepting all finite integer values including 0.

The merge-time safety net in parse.cjs (`pr <= 0` → INVALID_PR) is
unchanged — a pr:0 fragment is still rejected at lint/render time.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* chore(#1224): backfill changeset PR number (#1231)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Tom Boucher
2026-06-14 15:02:54 -04:00
committed by GitHub
parent 15fbdf6879
commit cafb874c4a
3 changed files with 109 additions and 4 deletions

View File

@@ -106,8 +106,14 @@ function parseArgs(argv) {
const r = requireValue(a, i);
if (!r.ok) return { ok: false, error: r.error };
if (a === '--type') opts.type = r.value;
else if (a === '--pr') opts.pr = Number(r.value);
else if (a === '--body') opts.body = r.value;
else if (a === '--pr') {
// Accept only decimal-integer strings (digits only, no sign, no dot,
// no hex prefix, no scientific notation). Non-integer input — including
// empty string and whitespace — is normalized to NaN so the prNaN
// guard below rejects it with the usage error.
const trimmed = r.value.trim();
opts.pr = /^\d+$/.test(trimmed) ? Number(trimmed) : NaN;
} else if (a === '--body') opts.body = r.value;
else if (a === '--repo') opts.repo = r.value;
i++;
continue;
@@ -125,7 +131,15 @@ function main() {
throw new ExitError(2);
}
const { opts } = parsed;
if (!opts.type || !opts.pr || !opts.body) {
// opts.pr starts as null (missing flag) and is set by parseArgs to a Number when
// the raw value is a pure decimal-integer string (digits only), or to NaN for any
// other input (empty, whitespace, floats, hex, negatives, scientific notation, etc.).
// Accept integer 0 (the documented pr:0 placeholder); reject a missing flag (null)
// and any non-decimal-integer value (NaN). The merge/lint gate separately
// enforces pr > 0 before a fragment can land, so 0 still cannot be merged.
const prMissing = opts.pr === null;
const prNaN = typeof opts.pr === 'number' && Number.isNaN(opts.pr);
if (!opts.type || prMissing || prNaN || !opts.body) {
throw new ExitError(2, 'usage: changeset/new.cjs --type <Fixed|Added|...> --pr NNNN --body "..."');
}
const file = scaffoldFragment(opts);