@@ -1585,16 +1585,63 @@ program
15851585 . description ( 'Serve the generated static UI locally (no Python required)' )
15861586 . option ( '--port <n>' , 'Port to listen on' , '3000' )
15871587 . option ( '--host <host>' , 'Host to bind (default: 127.0.0.1)' , '127.0.0.1' )
1588- . action ( ( buildDir : string , opts : { port : string ; host : string } ) => {
1588+ . option ( '--no-deploy' , 'Do not auto-deploy when the manifest has a placeholder 0x0 address' )
1589+ . option ( '--no-start-anvil' , 'Do not start anvil automatically (anvil chain only)' )
1590+ . action ( async ( buildDir : string , opts : { port : string ; host : string ; deploy : boolean ; startAnvil : boolean } ) => {
1591+ let anvilChild : ReturnType < typeof spawn > | null = null ;
15891592 try {
1593+ const resolvedBuildDir = path . resolve ( buildDir ) ;
1594+ const manifestPath = path . join ( resolvedBuildDir , 'manifest.json' ) ;
1595+ const zeroAddress = '0x0000000000000000000000000000000000000000' ;
1596+
1597+ // If the manifest is still at the placeholder address, auto-deploy on anvil by default.
1598+ if ( opts . deploy && fs . existsSync ( manifestPath ) ) {
1599+ const manifest = readJsonFile ( manifestPath ) as any ;
1600+ const deployments = Array . isArray ( manifest ?. deployments ) ? manifest . deployments : [ ] ;
1601+ const d = deployments . find ( ( x : any ) => x && x . role === 'primary' ) ?? deployments [ 0 ] ?? null ;
1602+ const addr = String ( d ?. deploymentEntrypointAddress ?? '' ) ;
1603+ const chainId = Number ( d ?. chainId ?? NaN ) ;
1604+
1605+ if ( addr && addr . toLowerCase ( ) === zeroAddress && Number . isFinite ( chainId ) ) {
1606+ const chainNameFromId = chainId === anvil . id ? ( 'anvil' as const ) : chainId === sepolia . id ? ( 'sepolia' as const ) : null ;
1607+ if ( chainNameFromId === 'anvil' ) {
1608+ const { chainName, chain } = resolveKnownChain ( 'anvil' ) ;
1609+ const rpcUrl = resolveRpcUrl ( chainName , chain , undefined ) ;
1610+ console . log ( `Manifest is not deployed (0x0). Deploying automatically to ${ chainName } ...` ) ;
1611+ const ensured = await ensureAnvilRunning ( rpcUrl , { start : Boolean ( opts . startAnvil ) , expectedChainId : chain . id } ) ;
1612+ anvilChild = ensured . child ;
1613+ await deployBuildDir ( resolvedBuildDir , { chain : 'anvil' , role : 'primary' } ) ;
1614+ console . log ( 'Auto-deploy complete.' ) ;
1615+ console . log ( '' ) ;
1616+ }
1617+ }
1618+ }
1619+
15901620 const port = Number ( opts . port ) ;
1591- const { server } = startUiSiteServer ( { buildDir, host : opts . host , port } ) ;
1592- process . on ( 'SIGINT' , ( ) => {
1593- server . close ( ( ) => process . exit ( 0 ) ) ;
1594- } ) ;
1621+ const { server } = startUiSiteServer ( { buildDir : resolvedBuildDir , host : opts . host , port } ) ;
1622+
1623+ const cleanup = ( ) => {
1624+ try {
1625+ server . close ( ( ) => { } ) ;
1626+ } catch { }
1627+ if ( anvilChild ) {
1628+ try {
1629+ anvilChild . kill ( 'SIGTERM' ) ;
1630+ } catch { }
1631+ }
1632+ process . exit ( 0 ) ;
1633+ } ;
1634+
1635+ process . on ( 'SIGINT' , cleanup ) ;
1636+ process . on ( 'SIGTERM' , cleanup ) ;
15951637 } catch ( e : any ) {
15961638 console . error ( String ( e ?. message ?? e ) ) ;
15971639 process . exitCode = 1 ;
1640+ if ( anvilChild ) {
1641+ try {
1642+ anvilChild . kill ( 'SIGTERM' ) ;
1643+ } catch { }
1644+ }
15981645 }
15991646 } ) ;
16001647
0 commit comments