3535 * Usage:
3636 * node scripts/ios-prebuild/headers-verify.js [--flavor Debug|Release]
3737 * [--artifacts <dir>] [--skip-compile] [--update-baseline]
38+ * [--require-stamped-version]
3839 */
3940
4041const { computeInventory} = require ( './headers-inventory' ) ;
@@ -410,6 +411,49 @@ function runCompileGates(
410411 }
411412}
412413
414+ // ---------------------------------------------------------------------------
415+ // Version stamp gate
416+ // ---------------------------------------------------------------------------
417+
418+ /**
419+ * Release/nightly artifacts must not ship ReactNativeVersion.h with the
420+ * 1000.0.0 dev sentinel: the compose step copies headers from the source
421+ * tree, so a compose job that forgot to run set-rn-artifacts-version.js
422+ * would silently publish a sentinel header, breaking every library that
423+ * gates code on REACT_NATIVE_VERSION_MAJOR/MINOR.
424+ */
425+ function verifyVersionStamp ( artifactsDir /*: string */ ) /*: void */ {
426+ const copies = [ ] ;
427+ const walk = ( dir /*: string */ ) => {
428+ for ( const entry of fs . readdirSync ( dir , { withFileTypes : true } ) ) {
429+ const name = String ( entry . name ) ;
430+ const full = path . join ( dir , name ) ;
431+ if ( entry . isDirectory ( ) ) {
432+ walk ( full ) ;
433+ } else if ( name === 'ReactNativeVersion.h' ) {
434+ copies . push ( full ) ;
435+ }
436+ }
437+ } ;
438+ walk ( artifactsDir ) ;
439+ if ( copies . length === 0 ) {
440+ throw new Error (
441+ `no ReactNativeVersion.h found under ${ artifactsDir } — cannot verify the version stamp.` ,
442+ ) ;
443+ }
444+ const unstamped = copies . filter ( f =>
445+ / R E A C T _ N A T I V E _ V E R S I O N _ M A J O R \s + 1 0 0 0 \b / . test ( fs . readFileSync ( f , 'utf8' ) ) ,
446+ ) ;
447+ if ( unstamped . length > 0 ) {
448+ throw new Error (
449+ `ReactNativeVersion.h still contains the 1000.0.0 dev sentinel — run ` +
450+ `scripts/releases/set-rn-artifacts-version.js before composing:\n ` +
451+ unstamped . join ( '\n ' ) ,
452+ ) ;
453+ }
454+ log ( `version stamp OK (${ copies . length } copies checked).` ) ;
455+ }
456+
413457// ---------------------------------------------------------------------------
414458// CLI
415459// ---------------------------------------------------------------------------
@@ -419,11 +463,13 @@ function parseArgs(argv /*: Array<string> */) /*: {
419463 artifacts: ?string,
420464 skipCompile: boolean,
421465 updateBaseline: boolean,
466+ requireStampedVersion: boolean,
422467} */ {
423468 let flavor = 'Debug' ;
424469 let artifacts /*: ?string */ = null ;
425470 let skipCompile = false ;
426471 let updateBaseline = false ;
472+ let requireStampedVersion = false ;
427473 for ( let i = 0 ; i < argv . length ; i ++ ) {
428474 if ( argv [ i ] === '--flavor' ) {
429475 flavor = argv [ ++ i ] ;
@@ -433,9 +479,17 @@ function parseArgs(argv /*: Array<string> */) /*: {
433479 skipCompile = true ;
434480 } else if ( argv [ i ] === '--update-baseline' ) {
435481 updateBaseline = true ;
482+ } else if ( argv [ i ] === '--require-stamped-version' ) {
483+ requireStampedVersion = true ;
436484 }
437485 }
438- return { flavor, artifacts, skipCompile, updateBaseline} ;
486+ return {
487+ flavor,
488+ artifacts,
489+ skipCompile,
490+ updateBaseline,
491+ requireStampedVersion,
492+ } ;
439493}
440494
441495function main ( argv /*:: ?: Array<string> */ ) /*: void */ {
@@ -460,6 +514,10 @@ function main(argv /*:: ?: Array<string> */) /*: void */ {
460514 `(node scripts/ios-prebuild -c -f ${ args . flavor } ).` ,
461515 ) ;
462516 }
517+ if ( args . requireStampedVersion ) {
518+ verifyVersionStamp ( artifactsDir ) ;
519+ }
520+
463521 const { reactSlice, rnhHeaders} = verifyStructural ( plan , artifactsDir ) ;
464522
465523 if ( args . skipCompile ) {
0 commit comments