@@ -183,28 +183,8 @@ async function getPrForCommit(commitSha) {
183183// --- Parse .server-changes/ files ---
184184
185185async function parseServerChanges ( ) {
186- const dir = join ( ROOT_DIR , ".server-changes" ) ;
187186 const entries = [ ] ;
188-
189- let files ;
190- try {
191- files = await fs . readdir ( dir ) ;
192- } catch {
193- return entries ;
194- }
195-
196- // Collect file info and look up commits in parallel
197- const fileData = [ ] ;
198- for ( const file of files ) {
199- if ( ! file . endsWith ( ".md" ) || file === "README.md" ) continue ;
200-
201- const filePath = join ( ".server-changes" , file ) ;
202- const content = await fs . readFile ( join ( dir , file ) , "utf-8" ) ;
203- const parsed = parseFrontmatter ( content ) ;
204- if ( ! parsed . body . trim ( ) ) continue ;
205-
206- fileData . push ( { filePath, parsed } ) ;
207- }
187+ const fileData = await getServerChangeFileData ( ) ;
208188
209189 // Look up commits for all files in parallel
210190 const commits = await Promise . all ( fileData . map ( ( f ) => getCommitForFile ( f . filePath ) ) ) ;
@@ -232,6 +212,98 @@ async function parseServerChanges() {
232212 return entries ;
233213}
234214
215+ async function getServerChangeFileData ( ) {
216+ // The changesets version command deletes .server-changes before this script
217+ // enhances the release PR body. We combine files still live on disk with the
218+ // ones recovered from the release branch diff, deduped by filename, rather
219+ // than picking one source or the other. This is additive so a partial cleanup
220+ // (some files deleted, some still live) can't silently drop entries. Live
221+ // files win on collision since they are the current on-disk truth.
222+ const [ live , deleted ] = await Promise . all ( [
223+ getLiveServerChangeFileData ( ) ,
224+ getDeletedServerChangeFileDataFromReleaseBranch ( ) ,
225+ ] ) ;
226+
227+ const byName = new Map ( ) ;
228+ for ( const fileData of deleted ) {
229+ byName . set ( fileData . filePath . split ( "/" ) . pop ( ) , fileData ) ;
230+ }
231+ for ( const fileData of live ) {
232+ byName . set ( fileData . filePath . split ( "/" ) . pop ( ) , fileData ) ;
233+ }
234+
235+ return [ ...byName . values ( ) ] . sort ( ( a , b ) => a . filePath . localeCompare ( b . filePath ) ) ;
236+ }
237+
238+ async function getLiveServerChangeFileData ( ) {
239+ const dir = join ( ROOT_DIR , ".server-changes" ) ;
240+
241+ let files ;
242+ try {
243+ files = await fs . readdir ( dir ) ;
244+ } catch {
245+ return [ ] ;
246+ }
247+
248+ const fileData = [ ] ;
249+ for ( const file of files . sort ( ) ) {
250+ if ( ! file . endsWith ( ".md" ) || file === "README.md" ) continue ;
251+
252+ const filePath = join ( ".server-changes" , file ) ;
253+ const content = await fs . readFile ( join ( dir , file ) , "utf-8" ) ;
254+ const parsed = parseFrontmatter ( content ) ;
255+ if ( ! parsed . body . trim ( ) ) continue ;
256+
257+ fileData . push ( { filePath, parsed } ) ;
258+ }
259+
260+ return fileData ;
261+ }
262+
263+ async function getDeletedServerChangeFileDataFromReleaseBranch ( ) {
264+ const baseRef = process . env . SERVER_CHANGES_BASE_REF || "origin/main" ;
265+ const releaseRef = process . env . SERVER_CHANGES_RELEASE_REF || "origin/changeset-release/main" ;
266+
267+ let mergeBase ;
268+ let deletedFiles ;
269+ try {
270+ mergeBase = await gitExec ( [ "merge-base" , baseRef , releaseRef ] ) ;
271+ deletedFiles = await gitExec ( [
272+ "diff" ,
273+ "--name-only" ,
274+ "--diff-filter=D" ,
275+ `${ mergeBase } ..${ releaseRef } ` ,
276+ "--" ,
277+ ".server-changes" ,
278+ ] ) ;
279+ } catch ( err ) {
280+ console . error (
281+ "[enhance-release-pr] failed to recover deleted server-changes from release branch:" ,
282+ err
283+ ) ;
284+ return [ ] ;
285+ }
286+
287+ const fileData = [ ] ;
288+ for ( const filePath of deletedFiles . split ( "\n" ) . filter ( Boolean ) . sort ( ) ) {
289+ const file = filePath . split ( "/" ) . pop ( ) ;
290+ if ( ! file ?. endsWith ( ".md" ) || file === "README.md" ) continue ;
291+
292+ try {
293+ const content = await gitExec ( [ "show" , `${ mergeBase } :${ filePath } ` ] ) ;
294+ const parsed = parseFrontmatter ( content ) ;
295+ if ( ! parsed . body . trim ( ) ) continue ;
296+ fileData . push ( { filePath, parsed } ) ;
297+ } catch ( err ) {
298+ // If an individual file cannot be recovered, skip it rather than hiding
299+ // all other server changes.
300+ console . error ( `[enhance-release-pr] failed to read deleted server-change ${ filePath } :` , err ) ;
301+ }
302+ }
303+
304+ return fileData ;
305+ }
306+
235307function parseFrontmatter ( content ) {
236308 const match = content . match ( / ^ - - - \n ( [ \s \S ] * ?) \n - - - \n ? ( [ \s \S ] * ) $ / ) ;
237309 if ( ! match ) return { frontmatter : { } , body : content } ;
0 commit comments