From 09f7713c7b78e9d73bb9f7dfad57906d5d0e4aac Mon Sep 17 00:00:00 2001 From: jdalton Date: Mon, 24 Aug 2026 12:43:24 -0400 Subject: [PATCH 1/2] fix(release): don't require version field to start a line writeManifestVersion anchored on ^ + multiline, requiring the version field to open its own line. package.json always parses fine with JSON.parse regardless of formatting, but the anchor made the rewrite itself fragile - it hit the anchor-mismatch case on CI at least twice (2026-08-18, 2026-08-24), aborting the v1.x publish workflow with a false "no top-level version line" error. Match the fleet's own canonical scripts/fleet/bump.mts, which drops the anchor and matches the first "version": " occurrence anywhere in the manifest. --- scripts/release/bump.mts | 5 +---- test/release-bump.test.mts | 40 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 4 deletions(-) create mode 100644 test/release-bump.test.mts diff --git a/scripts/release/bump.mts b/scripts/release/bump.mts index e3579f4d7..53ae2f72e 100644 --- a/scripts/release/bump.mts +++ b/scripts/release/bump.mts @@ -158,10 +158,7 @@ function readPackageJson(): { parsed: PackageJsonShape; raw: string } { * is the one line a reviewer expects. */ export function writeManifestVersion(raw: string, version: string): string { - const replaced = raw.replace( - /^(\s*"version":\s*")[^"]*(")/m, - `$1${version}$2`, - ) + const replaced = raw.replace(/("version":\s*")[^"]*(")/, `$1${version}$2`) if (replaced === raw) { throw new Error( '[bump] could not rewrite the package.json version field.\n' + diff --git a/test/release-bump.test.mts b/test/release-bump.test.mts new file mode 100644 index 000000000..33da4011c --- /dev/null +++ b/test/release-bump.test.mts @@ -0,0 +1,40 @@ +import { describe, expect, it } from 'vitest' + +import { writeManifestVersion } from '../scripts/release/bump.mts' + +describe('writeManifestVersion', () => { + it('rewrites the top-level version field, leaving everything else byte-for-byte', () => { + const raw = [ + '{', + ' "name": "socket",', + ' "version": "1.1.159",', + ' "description": "CLI for Socket.dev"', + '}', + '', + ].join('\n') + expect(writeManifestVersion(raw, '1.1.160')).toBe( + raw.replace('1.1.159', '1.1.160'), + ) + }) + + // The prior `^(\s*"version":\s*")[^"]*(")/m` anchor required the field to + // start a line. A minified (or otherwise reformatted) manifest still + // parses fine with JSON.parse, but broke that anchor and threw a false + // "no top-level version line" error — this is the exact failure this repo + // has hit intermittently on CI. The unanchored, non-multiline pattern + // matches the first "version": "…" occurrence regardless of surrounding + // whitespace. + it('rewrites the version field even when the manifest is minified', () => { + const raw = '{"name":"socket","version":"1.1.159","private":false}' + expect(writeManifestVersion(raw, '1.1.160')).toBe( + '{"name":"socket","version":"1.1.160","private":false}', + ) + }) + + it('throws when the manifest has no version field', () => { + const raw = '{\n "name": "socket"\n}\n' + expect(() => writeManifestVersion(raw, '1.1.160')).toThrow( + /could not rewrite the package\.json version field/, + ) + }) +}) From 3cfee11be4ec069a393aa5f7caa06491a08daabe Mon Sep 17 00:00:00 2001 From: jdalton Date: Mon, 24 Aug 2026 12:49:12 -0400 Subject: [PATCH 2/2] fix(release): match the fleet-canonical replacer exactly Bring writeManifestVersion to byte-for-byte parity with the fleet's scripts/fleet/bump.mts replaceVersion: require at least one character in the existing version (an empty version is as broken as a missing one) and use a replacer function instead of a $1${version}$2 template, which would mis-substitute if the derived version ever contained a literal $. --- scripts/release/bump.mts | 5 ++++- test/release-bump.test.mts | 7 +++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/scripts/release/bump.mts b/scripts/release/bump.mts index 53ae2f72e..960cac22e 100644 --- a/scripts/release/bump.mts +++ b/scripts/release/bump.mts @@ -158,7 +158,10 @@ function readPackageJson(): { parsed: PackageJsonShape; raw: string } { * is the one line a reviewer expects. */ export function writeManifestVersion(raw: string, version: string): string { - const replaced = raw.replace(/("version":\s*")[^"]*(")/, `$1${version}$2`) + const replaced = raw.replace( + /("version":\s*")[^"]+(")/, + (_m, pre: string, post: string) => `${pre}${version}${post}`, + ) if (replaced === raw) { throw new Error( '[bump] could not rewrite the package.json version field.\n' + diff --git a/test/release-bump.test.mts b/test/release-bump.test.mts index 33da4011c..1dcd8bfad 100644 --- a/test/release-bump.test.mts +++ b/test/release-bump.test.mts @@ -37,4 +37,11 @@ describe('writeManifestVersion', () => { /could not rewrite the package\.json version field/, ) }) + + it('throws when the version field is already empty', () => { + const raw = '{\n "name": "socket",\n "version": ""\n}\n' + expect(() => writeManifestVersion(raw, '1.1.160')).toThrow( + /could not rewrite the package\.json version field/, + ) + }) })