@@ -52,6 +52,12 @@ interface PublishSummary {
5252 } >
5353}
5454
55+ interface LocalPackage {
56+ dirName : string
57+ npmName : string
58+ localVersion : string
59+ }
60+
5561/**
5662 * Read published packages from pnpm's publish summary file.
5763 * See https://pnpm.io/cli/publish#--report-summary
@@ -74,6 +80,32 @@ function readPublishSummary(): PublishedPackage[] {
7480 } ) )
7581}
7682
83+ /**
84+ * Get local package metadata from the workspace.
85+ */
86+ function getLocalPackages ( ) : LocalPackage [ ] {
87+ let packageDirNames = getAllPackageDirNames ( )
88+ let localPackages : LocalPackage [ ] = [ ]
89+
90+ for ( let packageDirName of packageDirNames ) {
91+ let packageJsonPath = getPackageFile ( packageDirName , 'package.json' )
92+
93+ // Skip directories without a package.json
94+ if ( ! fileExists ( packageJsonPath ) ) {
95+ continue
96+ }
97+
98+ let packageJson = readJson ( packageJsonPath )
99+ localPackages . push ( {
100+ dirName : packageDirName ,
101+ npmName : packageJson . name as string ,
102+ localVersion : packageJson . version as string ,
103+ } )
104+ }
105+
106+ return localPackages
107+ }
108+
77109/**
78110 * Check if a specific version of a package is published on npm.
79111 */
@@ -90,35 +122,11 @@ async function isVersionPublished(packageName: string, version: string): Promise
90122 } )
91123}
92124
93- interface LocalPackage {
94- dirName : string
95- npmName : string
96- localVersion : string
97- }
98-
99125/**
100126 * Get all packages that have versions not yet published to npm.
101127 */
102128async function getUnpublishedPackages ( ) : Promise < PublishedPackage [ ] > {
103- let packageDirNames = getAllPackageDirNames ( )
104-
105- // Collect all local package info first
106- let localPackages : LocalPackage [ ] = [ ]
107- for ( let packageDirName of packageDirNames ) {
108- let packageJsonPath = getPackageFile ( packageDirName , 'package.json' )
109-
110- // Skip directories without a package.json
111- if ( ! fileExists ( packageJsonPath ) ) {
112- continue
113- }
114-
115- let packageJson = readJson ( packageJsonPath )
116- localPackages . push ( {
117- dirName : packageDirName ,
118- npmName : packageJson . name as string ,
119- localVersion : packageJson . version as string ,
120- } )
121- }
129+ let localPackages = getLocalPackages ( )
122130
123131 // Query npm for all packages in parallel
124132 let npmResults = await Promise . all (
@@ -143,6 +151,54 @@ async function getUnpublishedPackages(): Promise<PublishedPackage[]> {
143151 return unpublished
144152}
145153
154+ /**
155+ * Find package versions that are already published to npm but missing local git tags.
156+ * This enables release recovery after partial publish failures.
157+ */
158+ async function getPublishedPackagesMissingTags ( ) : Promise < PublishedPackage [ ] > {
159+ let localPackages = getLocalPackages ( )
160+
161+ let npmResults = await Promise . all (
162+ localPackages . map ( async ( pkg ) => ( {
163+ pkg,
164+ isPublished : await isVersionPublished ( pkg . npmName , pkg . localVersion ) ,
165+ } ) ) ,
166+ )
167+
168+ let missingTags : PublishedPackage [ ] = [ ]
169+ for ( let { pkg, isPublished } of npmResults ) {
170+ if ( ! isPublished ) {
171+ continue
172+ }
173+
174+ let tag = getGitTag ( pkg . npmName , pkg . localVersion )
175+ if ( ! tagExists ( tag ) ) {
176+ missingTags . push ( {
177+ packageName : pkg . npmName ,
178+ version : pkg . localVersion ,
179+ tag,
180+ } )
181+ }
182+ }
183+
184+ return missingTags
185+ }
186+
187+ function dedupePublishedPackages ( packages : PublishedPackage [ ] ) : PublishedPackage [ ] {
188+ let seenTags = new Set < string > ( )
189+ let deduped : PublishedPackage [ ] = [ ]
190+
191+ for ( let pkg of packages ) {
192+ if ( seenTags . has ( pkg . tag ) ) {
193+ continue
194+ }
195+ seenTags . add ( pkg . tag )
196+ deduped . push ( pkg )
197+ }
198+
199+ return deduped
200+ }
201+
146202interface ChangelogWarning {
147203 packageName : string
148204 version : string
@@ -300,14 +356,23 @@ async function main() {
300356 return
301357 }
302358
303- if ( published . length === 0 ) {
304- console . log ( '\nNo packages were published.' )
305- return
359+ if ( published . length > 0 ) {
360+ console . log ( `\n${ published . length } package${ published . length === 1 ? '' : 's' } published:` )
361+ for ( let pkg of published ) {
362+ console . log ( ` • ${ pkg . packageName } @${ pkg . version } ` )
363+ }
364+ } else {
365+ console . log ( '\nNo new packages were published.' )
306366 }
307367
308- console . log ( `\n${ published . length } package${ published . length === 1 ? '' : 's' } published:` )
309- for ( let pkg of published ) {
310- console . log ( ` • ${ pkg . packageName } @${ pkg . version } ` )
368+ let packagesNeedingTagsOrReleases = dedupePublishedPackages ( [
369+ ...published ,
370+ ...( await getPublishedPackagesMissingTags ( ) ) ,
371+ ] )
372+
373+ if ( packagesNeedingTagsOrReleases . length === 0 ) {
374+ console . log ( '\nNo packages need git tags or GitHub releases.' )
375+ return
311376 }
312377
313378 // Configure git
@@ -316,9 +381,11 @@ async function main() {
316381 logAndExec ( 'git config user.email "hello@remix.run"' )
317382
318383 // Create tags (skip if already exist)
319- console . log ( `\nCreating tag${ published . length === 1 ? '' : 's' } ...` )
384+ console . log (
385+ `\nCreating tag${ packagesNeedingTagsOrReleases . length === 1 ? '' : 's' } for published packages...` ,
386+ )
320387 let tagsCreated = 0
321- for ( let pkg of published ) {
388+ for ( let pkg of packagesNeedingTagsOrReleases ) {
322389 if ( tagExists ( pkg . tag ) ) {
323390 console . log ( ` ⊘ ${ pkg . tag } (already exists)` )
324391 } else {
@@ -340,7 +407,7 @@ async function main() {
340407 console . log ( '\nCreating GitHub releases...' )
341408 let failedReleases : Array < { pkg : PublishedPackage ; error : string } > = [ ]
342409
343- for ( let pkg of published ) {
410+ for ( let pkg of packagesNeedingTagsOrReleases ) {
344411 let result = await createRelease ( pkg . packageName , pkg . version )
345412 if ( result . status === 'created' ) {
346413 console . log ( ` ✓ ${ pkg . packageName } v${ pkg . version } ` )
0 commit comments