99 allAssetNames,
1010 allReleaseAssetNames,
1111 BUNDLE_ASSET_NAMES ,
12+ LEGACY_TUI_BRIDGE_ASSET_NAMES ,
1213} = require ( path . join ( repoRoot , "npm" , "codewhale" , "scripts" , "artifacts" ) ) ;
1314
1415function read ( relativePath ) {
@@ -21,10 +22,14 @@ function valuesForKey(source, key) {
2122}
2223
2324const ci = read ( ".github/workflows/ci.yml" ) ;
25+ const nightly = read ( ".github/workflows/nightly.yml" ) ;
2426const candidate = read ( ".github/workflows/release-candidate.yml" ) ;
2527const artifacts = read ( ".github/workflows/release-artifacts.yml" ) ;
2628const release = read ( ".github/workflows/release.yml" ) ;
29+ const cnb = read ( ".cnb.yml" ) ;
2730const bundles = read ( "scripts/release/create-release-bundles.sh" ) ;
31+ const archiveInstaller = read ( "scripts/release/install.sh" ) ;
32+ const cliDispatcher = read ( "crates/cli/src/lib.rs" ) ;
2833const runbook = read ( "docs/RELEASE_RUNBOOK.md" ) ;
2934
3035assert . match ( ci , / ^ w o r k f l o w _ d i s p a t c h : \n i n p u t s : \n e x p e c t e d _ s h a : / m) ;
@@ -37,6 +42,53 @@ for (const output of ["heavy", "workflow", "mobile", "actions"]) {
3742}
3843assert . match ( manualForceBlock [ 1 ] , / # E X P E C T E D _ S H A .* - n e 4 0 / s) ;
3944assert . match ( manualForceBlock [ 1 ] , / a c t u a l .* E X P E C T E D _ S H A / s) ;
45+ assert . match (
46+ ci ,
47+ / r u n : c a r g o t e s t - p c o d e w h a l e - t u i - - t e s t p t y q a _ p t y : : s k i l l s _ o p e n s _ m a n a g e r _ o w n e d _ t h e n _ c o m p a t i b l e - - - - i g n o r e d - - e x a c t / ,
48+ "CI must run the isolated Skills Manager acceptance from the consolidated PTY target" ,
49+ ) ;
50+ assert . doesNotMatch ( ci , / - - t e s t q a _ p t y \b / , "CI must not name the removed qa_pty target" ) ;
51+
52+ const expectedNightlyTargets = [
53+ "x86_64-unknown-linux-gnu" ,
54+ "aarch64-unknown-linux-gnu" ,
55+ "x86_64-apple-darwin" ,
56+ "aarch64-apple-darwin" ,
57+ "x86_64-pc-windows-msvc" ,
58+ "aarch64-pc-windows-msvc" ,
59+ ] . sort ( ) ;
60+ assert . deepEqual ( [ ...new Set ( valuesForKey ( nightly , "target" ) ) ] . sort ( ) , expectedNightlyTargets ) ;
61+ assert . deepEqual (
62+ [
63+ ...valuesForKey ( nightly , "primary_artifact" ) ,
64+ ...valuesForKey ( nightly , "alias_artifact" ) ,
65+ ] . sort ( ) ,
66+ [
67+ "codewhale-linux-x64" ,
68+ "codew-linux-x64" ,
69+ "codewhale-linux-arm64" ,
70+ "codew-linux-arm64" ,
71+ "codewhale-macos-x64" ,
72+ "codew-macos-x64" ,
73+ "codewhale-macos-arm64" ,
74+ "codew-macos-arm64" ,
75+ "codewhale-windows-x64.exe" ,
76+ "codew-windows-x64.exe" ,
77+ "codewhale-windows-arm64.exe" ,
78+ "codew-windows-arm64.exe" ,
79+ ] . sort ( ) ,
80+ ) ;
81+ assert . match (
82+ nightly ,
83+ / c a r g o b u i l d - - r e l e a s e - - l o c k e d - - t a r g e t \$ \{ \{ m a t r i x \. t a r g e t \} \} - p c o d e w h a l e - c l i / ,
84+ ) ;
85+ assert . match ( nightly , / s t a r t s W i t h \( m a t r i x \. t a r g e t , ' x 8 6 _ 6 4 - ' \) .* r u n n e r \. a r c h = = ' X 6 4 ' / s) ;
86+ assert . match ( nightly , / s t a r t s W i t h \( m a t r i x \. t a r g e t , ' a a r c h 6 4 - ' \) .* r u n n e r \. a r c h = = ' A R M 6 4 ' / s) ;
87+ assert . doesNotMatch ( nightly , / c o d e w h a l e - t u i / ) ;
88+ assert . doesNotMatch ( nightly , / t a r g e t \/ [ ^ \n ] * \/ c o d e w (?: \. e x e ) ? / ) ;
89+ assert . match ( nightly , / c p " \$ \{ b i n _ p a t h \} " " \$ \{ d i r \} \/ \$ \{ a r t i f a c t \} " / ) ;
90+ assert . match ( nightly , / c m p - s [ \s \S ] * n i g h t l y - p r i m a r y [ \s \S ] * n i g h t l y - a l i a s / ) ;
91+ assert . equal ( ( nightly . match ( / r e t e n t i o n - d a y s : 1 4 / g) || [ ] ) . length , 2 ) ;
4092
4193assert . match ( candidate , / ^ w o r k f l o w _ d i s p a t c h : \n i n p u t s : \n e x p e c t e d _ s h a : / m) ;
4294assert . doesNotMatch ( candidate , / ^ ( p u s h | p u l l _ r e q u e s t | s c h e d u l e ) : / m) ;
@@ -118,10 +170,18 @@ const builtAssetNames = [
118170assert . equal ( builtAssetNames . length , 21 ) ;
119171assert . deepEqual (
120172 [ ...new Set ( builtAssetNames ) ] . sort ( ) ,
121- allAssetNames ( ) . filter ( ( name ) => name !== "codewhale.bat" ) . sort ( ) ,
173+ [
174+ ...allAssetNames ( ) . filter ( ( name ) => name !== "codewhale.bat" ) ,
175+ ...LEGACY_TUI_BRIDGE_ASSET_NAMES ,
176+ ] . sort ( ) ,
177+ ) ;
178+ assert . match (
179+ artifacts ,
180+ / s t a g e _ b i n a r y " \$ \{ \{ m a t r i x \. c l i _ b i n a r y \} \} " " \$ \{ \{ m a t r i x \. t u i _ a r t i f a c t \} \} " / ,
181+ "legacy TUI bridge assets must be staged from the one compiled codewhale binary" ,
122182) ;
123183const bundleInvocations = [ ...bundles . matchAll (
124- / ^ b u n d l e ( \S + ) \\ \n \s + \S + \S + \S + ( t a r \. g z | z i p ) ( " " | p o r t a b l e ) $ / gm,
184+ / ^ b u n d l e ( \S + ) \\ \n \s + \S + \S + ( t a r \. g z | z i p ) ( " " | p o r t a b l e ) $ / gm,
125185) ] . map ( ( match ) => {
126186 const variant = match [ 3 ] === "portable" ? "-portable" : "" ;
127187 return `codewhale-${ match [ 1 ] } ${ variant } .${ match [ 2 ] } ` ;
@@ -154,4 +214,42 @@ assert.match(runbook, /34/);
154214assert . match ( runbook , / d o e s n o t c r e a t e a t a g / i) ;
155215assert . match ( runbook , / e x p l i c i t .* a p p r o v a l / i) ;
156216
157- console . log ( "Release workflow contracts OK: exact-head full CI and 7-target/34-asset non-publishing candidate." ) ;
217+ const cnbPreflight = cnb . match (
218+ / \. l i n u x _ r e l e a s e _ p r e f l i g h t : & l i n u x _ r e l e a s e _ p r e f l i g h t ( [ \s \S ] * ?) \n m a i n : / ,
219+ ) ;
220+ assert . ok ( cnbPreflight , "CNB must retain a dedicated release preflight" ) ;
221+ const cnbBuild = cnbPreflight [ 1 ] . indexOf (
222+ "cargo build --jobs 2 --release --locked -p codewhale-cli" ,
223+ ) ;
224+ const cnbAlias = cnbPreflight [ 1 ] . indexOf (
225+ "cp target/release/codewhale target/release/codew" ,
226+ ) ;
227+ const cnbSmoke = cnbPreflight [ 1 ] . indexOf ( "node scripts/release/npm-wrapper-smoke.js" ) ;
228+ assert . ok ( cnbBuild >= 0 , "CNB release preflight must build the consolidated runtime" ) ;
229+ assert . ok ( cnbAlias > cnbBuild , "CNB release preflight must materialize codew after the build" ) ;
230+ assert . ok ( cnbSmoke > cnbAlias , "CNB release preflight must materialize codew before smoke" ) ;
231+
232+ assert . doesNotMatch (
233+ archiveInstaller ,
234+ / c a r g o i n s t a l l c o d e w h a l e - - l o c k e d / ,
235+ "glibc recovery must name the published codewhale-cli crate" ,
236+ ) ;
237+ assert . equal (
238+ ( archiveInstaller . match ( / c a r g o i n s t a l l c o d e w h a l e - c l i - - l o c k e d / g) || [ ] ) . length ,
239+ 2 ,
240+ "both glibc recovery branches must name codewhale-cli" ,
241+ ) ;
242+ assert . match (
243+ archiveInstaller ,
244+ / l e g a c y _ t u i = " \$ B I N _ D I R \/ c o d e w h a l e - t u i " [ \s \S ] * i n s t a l l _ b i n a r y " \$ S C R I P T _ D I R \/ c o d e w h a l e " " \$ l e g a c y _ t u i " / ,
245+ "archive upgrades must refresh the retired TUI path from consolidated bytes" ,
246+ ) ;
247+ assert . doesNotMatch (
248+ cliDispatcher ,
249+ / c o d e w h a l e _ c o n f i g : : a u t o _ m o d e l : : c l a s s i f y / ,
250+ "the CLI dispatcher must leave auto routing to the provider-aware runtime" ,
251+ ) ;
252+
253+ console . log (
254+ "Workflow contracts OK: 6-target/12-asset single-runtime nightly and exact-head 7-target/34-asset release candidate." ,
255+ ) ;
0 commit comments