@@ -69,14 +69,52 @@ for (const file of opts.checksums) {
6969
7070let manifest = fs . readFileSync ( opts . package , "utf8" ) ;
7171
72- manifest = manifest . split ( "NS_SPM_VERSION" ) . join ( opts . version ) ;
72+ // Stamping is IDEMPOTENT: each slot is rewritten whether it still holds the
73+ // NS_SPM_VERSION / NS_CHECKSUM_* token or an already-stamped concrete value.
74+ // This matters because the release flow pushes the stamped manifest back to
75+ // `main`, so by the second release there are no tokens left. A one-shot token
76+ // replace would then silently no-op, the "no changes to commit" tag would land
77+ // on the prior commit, and every new tag would resolve to the FIRST release's
78+ // artifacts (the 9.0.4-next.* incident). Anchoring on the structural shape
79+ // instead of the token lets us re-stamp correctly forever.
80+ const escapeRegExp = ( s ) => s . replace ( / [ . * + ? ^ $ { } ( ) | [ \] \\ ] / g, "\\$&" ) ;
7381
82+ // Each checksum env key ↔ the artifact zip its binaryTarget references (the
83+ // pairing baked into the manifest template). Lets us find the right checksum
84+ // slot by URL once the NS_CHECKSUM_* tokens are gone.
85+ const KEY_TO_ARTIFACT = {
86+ NS_CHECKSUM_NATIVESCRIPT_IOS : "NativeScript.xcframework.zip" ,
87+ NS_CHECKSUM_TKLIVESYNC_IOS : "TKLiveSync.xcframework.zip" ,
88+ NS_CHECKSUM_NATIVESCRIPT_VISIONOS : "NativeScript.visionos.xcframework.zip" ,
89+ NS_CHECKSUM_TKLIVESYNC_VISIONOS : "TKLiveSync.visionos.xcframework.zip" ,
90+ } ;
91+
92+ // Version: rewrite the value inside `let nsVersion = "..."`.
93+ const versionRe = / ( l e t \s + n s V e r s i o n \s * = \s * " ) [ ^ " ] * ( " ) / ;
94+ if ( ! versionRe . test ( manifest ) ) {
95+ console . error ( `ERROR: could not find 'let nsVersion = "..."' in ${ opts . package } ` ) ;
96+ process . exit ( 1 ) ;
97+ }
98+ manifest = manifest . replace ( versionRe , `$1${ opts . version } $2` ) ;
99+
100+ // Checksums: for each provided checksum, rewrite the value in the binaryTarget
101+ // whose url points at the matching artifact zip.
74102const applied = [ ] ;
75103for ( const [ key , value ] of Object . entries ( checksums ) ) {
76- if ( manifest . includes ( key ) ) {
77- manifest = manifest . split ( key ) . join ( value ) ;
78- applied . push ( key ) ;
104+ const artifact = KEY_TO_ARTIFACT [ key ] ;
105+ if ( ! artifact ) {
106+ console . error ( `ERROR: no artifact mapping for checksum key ${ key } ; update KEY_TO_ARTIFACT.` ) ;
107+ process . exit ( 1 ) ;
108+ }
109+ const re = new RegExp (
110+ `(url:\\s*"[^"]*${ escapeRegExp ( artifact ) } ",\\s*checksum:\\s*")[^"]*(")`
111+ ) ;
112+ if ( ! re . test ( manifest ) ) {
113+ console . error ( `ERROR: no binaryTarget checksum slot for ${ artifact } in ${ opts . package } ` ) ;
114+ process . exit ( 1 ) ;
79115 }
116+ manifest = manifest . replace ( re , `$1${ value } $2` ) ;
117+ applied . push ( key ) ;
80118}
81119
82120fs . writeFileSync ( opts . package , manifest ) ;
@@ -85,10 +123,13 @@ console.log(`Stamped ${opts.package}`);
85123console . log ( ` version: ${ opts . version } ` ) ;
86124console . log ( ` checksums applied: ${ applied . length ? applied . join ( ", " ) : "(none)" } ` ) ;
87125
88- const leftover = ( manifest . match ( / N S _ S P M _ V E R S I O N | N S _ C H E C K S U M _ [ A - Z _ ] + / g) || [ ] ) ;
126+ // Safety net: no placeholder tokens may remain (they'd resolve to a broken URL
127+ // or a checksum mismatch). With idempotent stamping a leftover token signals a
128+ // real manifest/key drift (e.g. a missing platform's checksums), not a routine
129+ // 2nd-release no-op.
130+ const leftover = [ ...new Set ( manifest . match ( / N S _ S P M _ V E R S I O N | N S _ C H E C K S U M _ [ A - Z _ ] + / g) || [ ] ) ] ;
89131if ( leftover . length ) {
90- const unique = [ ...new Set ( leftover ) ] ;
91- const msg = `Unstamped tokens remain: ${ unique . join ( ", " ) } ` ;
132+ const msg = `Unstamped tokens remain: ${ leftover . join ( ", " ) } ` ;
92133 if ( opts . strict ) {
93134 console . error ( `ERROR: ${ msg } ` ) ;
94135 process . exit ( 1 ) ;
0 commit comments