code wiki / (root) / nx_site_publish_multi_candidate_t322.nx

nx_site_publish_multi_candidate_t322.nx source

↩ module page · 156 lines · 11511 B

1// Versioned multi-dependency composition for the existing site publisher. 2// CLI/job scope, as rm_main: all allocations reclaimed on process exit. 3import "nx_site_publish_lib.nx" 4const RM2_COMMON:i64=12 5const RM2_DEP_WORDS:i64=5 6const RM2_STATE_WORDS:i64=3 7const RM2_SCHEMA:*u8="site-release/v2" 8func rm2_dep_key(k:*u8)->i64{ 9 if fsx_seq(k,"source")==1{return 0};if fsx_seq(k,"path")==1{return 1} 10 if fsx_seq(k,"sha256")==1{return 2};if fsx_seq(k,"bytes")==1{return 3};return -1 11} 12func rm2_dep(t:*NxJsonTok,d:*i64)->i64{ 13 var count:i64=0;while count<4{ 14 let k:*u8=rm_string(t);if (k as i64)<=0{return PB_RC_USAGE};let idx:i64=rm2_dep_key(k);sys_munmap(k,vw_slen(k)+1) 15 if idx<0||d[idx]!=0{return PB_RC_USAGE};if nx_json_next(t)!=NX_JSON_COLON{return PB_RC_USAGE} 16 let val:*u8=rm_string(t);if (val as i64)<=0{return PB_RC_USAGE};d[idx]=val as i64;count=count+1 17 let sep:i64=nx_json_next(t);if sep==NX_JSON_RBRACE{if count==4{return 0};return PB_RC_USAGE};if sep!=NX_JSON_COMMA{return PB_RC_USAGE} 18 };return PB_RC_USAGE 19} 20func rm2_parse(body:*u8,n:i64,v:*i64,state:*i64)->i64{ 21 let t:*NxJsonTok=nx_json_new(body,n);if (t as i64)<=0{return FSX_RC_IO} 22 if nx_json_next(t)!=NX_JSON_LBRACE{return PB_RC_USAGE};var count:i64=0;var deps:i64=0;var tail:*i64=0 as *i64 23 while count<9{ 24 let k:*u8=rm_string(t);if (k as i64)<=0{return PB_RC_USAGE};let depkey:i64=fsx_seq(k,"dependencies");let idx:i64=rm_key(k);sys_munmap(k,vw_slen(k)+1) 25 if nx_json_next(t)!=NX_JSON_COLON{return PB_RC_USAGE} 26 if depkey==1{ 27 if deps!=0{return PB_RC_USAGE};deps=1;if nx_json_next(t)!=NX_JSON_LBRACKET{return PB_RC_USAGE} 28 var more:i64=1;while more==1{ 29 if nx_json_next(t)!=NX_JSON_LBRACE{return PB_RC_USAGE} 30 let d:*i64=sys_mmap_try(RM2_DEP_WORDS*8) as *i64;if (d as i64)<=0{return FSX_RC_IO};var j:i64=0;while j<RM2_DEP_WORDS{d[j]=0;j=j+1} 31 let rc:i64=rm2_dep(t,d);if rc!=0{return rc} 32 if (tail as i64)==0{state[0]=d as i64}else{tail[4]=d as i64};tail=d;if state[1]>=PB_I64_MAX/8{return PB_RC_USAGE};state[1]=state[1]+1 33 let sep:i64=nx_json_next(t);if sep==NX_JSON_RBRACKET{more=0}else{if sep!=NX_JSON_COMMA{return PB_RC_USAGE}} 34 } 35 }else{ 36 if idx<0||(idx>=7&&idx<=10)||v[idx]!=0{return PB_RC_USAGE} 37 let val:*u8=rm_string(t);if (val as i64)<=0{return PB_RC_USAGE};v[idx]=val as i64 38 } 39 count=count+1;let sep:i64=nx_json_next(t) 40 if sep==NX_JSON_RBRACE{if count!=9||deps!=1{return PB_RC_USAGE};if nx_json_next(t)!=NX_JSON_EOF{return PB_RC_USAGE};if fsx_seq(v[0] as *u8,RM2_SCHEMA)!=1{return PB_RC_USAGE};return 0} 41 if sep!=NX_JSON_COMMA{return PB_RC_USAGE} 42 };return PB_RC_USAGE 43} 44func rm2_view(v:*i64,d:*i64)->*i64{ 45 let one:*i64=sys_mmap_try(RM_VALUES_BYTES) as *i64;if (one as i64)<=0{return 0 as *i64} 46 var i:i64=0;while i<RM_FIELDS{one[i]=v[i];i=i+1};one[0]=RM_SCHEMA as i64 47 i=0;while i<4{one[7+i]=d[i];i=i+1};return one 48} 49func rm2_path(site:*u8,rel:*u8)->*u8{ 50 let b:*u8=sys_mmap_try(PB_PATH);let p:*i64=sys_mmap_try(8) as *i64;if (b as i64)<=0||(p as i64)<=0{return 0 as *u8} 51 p[0]=0;pb_app(b,p,"sites/");pb_app(b,p,site);pb_app(b,p,"/");pb_app(b,p,rel);sys_munmap(p as *u8,8);return b 52} 53// Observe exact existing bytes without treating an IO failure as absence. 54func rm2_existing(path:*u8,size:*u8,sha:*u8)->i64{ 55 let fd:i64=sys_openat_rd(path);if fd==-2{return PR_MISSING};if fd<0{return FSX_RC_IO} 56 if sys_close(fd)!=0{return FSX_RC_IO};let out:*i64=sys_mmap_try(16) as *i64;if (out as i64)<=0{return FSX_RC_IO} 57 let rc:i64=rm_artifact(path,size,sha,out);if rc==0{sys_munmap(out[0] as *u8,out[1]+1)};sys_munmap(out as *u8,16);return rc 58} 59func rm2_live(v:*i64)->i64{ 60 let path:*u8=rm2_path(v[1] as *u8,v[3] as *u8);if (path as i64)<=0{return FSX_RC_IO} 61 let same:i64=rm2_existing(path,v[5] as *u8,v[4] as *u8);if same==0{return 0} 62 if same==PR_MISSING{if fsx_seq(v[6] as *u8,"absent")==1{return 0};return PB_RC_CONFLICT} 63 let fd:i64=sys_openat_rd(path);if fd<0{return FSX_RC_IO};let extent:i64=sys_lseek(fd,0,FSX_SEEK_END);let closed:i64=sys_close(fd) 64 if extent<0||extent>=PB_I64_MAX/2||closed!=0{return FSX_RC_IO} 65 let b:*u8=sys_mmap_try(extent+1);let h:*u8=sys_mmap_try(PB_SHA_HEX+1);if (b as i64)<=0||(h as i64)<=0{return FSX_RC_IO} 66 let got:i64=pb_read_bounded(path,b,extent+1);if got!=extent{return FSX_RC_IO};pb_hash(b,got,h) 67 let matched:i64=fsx_seq(h,v[6] as *u8);sys_munmap(b,extent+1);sys_munmap(h,PB_SHA_HEX+1);sys_munmap(path,PB_PATH) 68 if matched==1{return 0};return PB_RC_CONFLICT 69} 70func rm2_preflight(v:*i64,state:*i64,prefix:*u8)->i64{ 71 if state[1]<1{return PB_RC_USAGE};var d:*i64=state[0] as *i64 72 while (d as i64)!=0{ 73 var prior:*i64=state[0] as *i64;while (prior as i64)!=(d as i64){if fsx_seq(prior[1] as *u8,d[1] as *u8)==1{return PB_RC_CONFLICT};prior=prior[4] as *i64} 74 let one:*i64=rm2_view(v,d);if (one as i64)<=0{return FSX_RC_IO};let rc:i64=rm_preflight(one,prefix);sys_munmap(one as *u8,RM_VALUES_BYTES);if rc!=0{return rc} 75 let observed:i64=pr_observe(prefix,d[1] as *u8,d[2] as *u8) 76 if observed!=PR_OK{if observed!=PR_MISSING||fsx_seq(v[11] as *u8,"allow")!=1{if observed==PR_IO{return FSX_RC_IO};return PB_RC_CONFLICT}} 77 let dst:*u8=rm2_path(v[1] as *u8,d[1] as *u8);if (dst as i64)<=0{return FSX_RC_IO};let existing:i64=rm2_existing(dst,d[3] as *u8,d[2] as *u8);sys_munmap(dst,PB_PATH) 78 if existing!=0&&existing!=PR_MISSING{return existing};d=d[4] as *i64 79 };return rm2_live(v) 80} 81func rm2_phase(receipt:*u8,digest:*u8,kind:*u8,index:i64,rc:i64)->i64{ 82 let phase:*u8=sys_mmap_try(PB_PATH);let pos:*i64=sys_mmap_try(8) as *i64;if (phase as i64)<=0||(pos as i64)<=0{return FSX_RC_IO} 83 pos[0]=0;pb_app(phase,pos,kind);pb_app(phase,pos,"-");rm_num(phase,pos,index);let result:i64=rm_finish(receipt,digest,phase,rc) 84 sys_munmap(phase,PB_PATH);sys_munmap(pos as *u8,8);return result 85} 86// Locks remain held for every dependency through the one consumer publish. 87// Nonblocking locks avoid deadlock between overlapping manifest collections. 88func rm2_publish(v:*i64,state:*i64,prefix:*u8)->i64{ 89 if state[1]<1||state[1]>PB_I64_MAX/8{return PB_RC_USAGE} 90 let fds:*i64=sys_mmap_try(state[1]*8) as *i64;if (fds as i64)<=0{return FSX_RC_IO} 91 var held:i64=0;var rc:i64=0;var d:*i64=state[0] as *i64 92 while (d as i64)!=0&&rc==0{ 93 let path:*u8=rm2_path(v[1] as *u8,d[1] as *u8);let pos:*i64=sys_mmap_try(8) as *i64;if (path as i64)<=0||(pos as i64)<=0{rc=FSX_RC_IO;break} 94 if fsx_write_denied(path)!=0{rc=FSX_RC_DENIED;break};pos[0]=vw_slen(path);pb_app(path,pos,".publish-lock") 95 let fd:i64=sys_openat_wr(path,FSX_MODE_RW);sys_munmap(path,PB_PATH);sys_munmap(pos as *u8,8) 96 if fd<0{rc=FSX_RC_IO;break};if sys_flock(fd,PB_LOCK_EX_NB)!=0{sys_close(fd);rc=PB_RC_BUSY;break};fds[held]=fd;held=held+1;d=d[4] as *i64 97 } 98 d=state[0] as *i64;while (d as i64)!=0&&rc==0{ 99 if pr_check(prefix,d[1] as *u8,d[2] as *u8)!=PR_OK{rc=PB_RC_CONFLICT;break} 100 let path:*u8=rm2_path(v[1] as *u8,d[1] as *u8);if (path as i64)<=0{rc=FSX_RC_IO;break};rc=rm2_existing(path,d[3] as *u8,d[2] as *u8);sys_munmap(path,PB_PATH);if rc==PR_MISSING{rc=PB_RC_CONFLICT};d=d[4] as *i64 101 } 102 if rc==0{ 103 let args:*i64=sys_mmap_try((PB_GUARDED_SIZED_ARGC+1)*8) as *i64;if (args as i64)<=0{rc=FSX_RC_IO}else{ 104 args[0]="nx_site_publish" as *u8 as i64;args[1]="publish-guarded" as *u8 as i64;args[2]=v[2];args[3]=v[1];args[4]=v[3];args[5]=v[4];args[6]=v[6];args[7]=v[5];args[8]=0 105 rc=pb_main_edge(PB_GUARDED_SIZED_ARGC,args,0 as *u8,0 as *u8);sys_munmap(args as *u8,(PB_GUARDED_SIZED_ARGC+1)*8) 106 } 107 } 108 var i:i64=0;while i<held{if sys_close(fds[i])!=0{rc=FSX_RC_IO};i=i+1};sys_munmap(fds as *u8,state[1]*8);return rc 109} 110func rm2_execute(v:*i64,state:*i64,prefix:*u8,receipt:*u8,digest:*u8)->i64{ 111 var rc:i64=rm2_preflight(v,state,prefix);rc=rm_finish(receipt,digest,"preflight-all",rc);if rc!=0{return rc} 112 var d:*i64=state[0] as *i64;var index:i64=0 113 while (d as i64)!=0{ 114 if fsx_seq(v[11] as *u8,"allow")==1{rc=pr_register(prefix,d[1] as *u8,d[2] as *u8);if rc==PR_CREATED{rc=0}}else{rc=pr_check(prefix,d[1] as *u8,d[2] as *u8)} 115 rc=rm2_phase(receipt,digest,"registration",index,rc);if rc!=0{return rc};d=d[4] as *i64;index=index+1 116 } 117 d=state[0] as *i64;index=0;while (d as i64)!=0{ 118 let args:*i64=sys_mmap_try((PB_SIZED_ARGC+1)*8) as *i64;if (args as i64)<=0{return FSX_RC_IO} 119 args[0]="nx_site_publish" as *u8 as i64;args[1]="stage-sized" as *u8 as i64;args[2]=d[0];args[3]=v[1];args[4]=d[1];args[5]=d[2];args[6]=d[3];args[7]=0 120 rc=pb_main_edge(PB_SIZED_ARGC,args,0 as *u8,0 as *u8);sys_munmap(args as *u8,(PB_SIZED_ARGC+1)*8) 121 rc=rm2_phase(receipt,digest,"stage",index,rc);if rc!=0{return rc};d=d[4] as *i64;index=index+1 122 } 123 rc=rm2_publish(v,state,prefix);rc=rm_finish(receipt,digest,"verify-all-publish-once",rc);if rc!=0{return rc} 124 return rm_receipt(receipt,digest,"delivery","published-served-review-pending",0) 125} 126// Size comes from the actual regular file; exact read and caller digest detect extent/content races. 127func rm2_load(path:*u8,out:*i64)->i64{ 128 out[0]=0;out[1]=0;let fd:i64=sys_openat_rd(path);if fd<0{return FSX_RC_IO} 129 let n:i64=sys_lseek(fd,0,FSX_SEEK_END);let closed:i64=sys_close(fd) 130 if n<=0||n>=PB_I64_MAX||closed!=0{return FSX_RC_IO} 131 let b:*u8=sys_mmap_try(n+1);if (b as i64)<=0{return FSX_RC_IO} 132 let got:i64=pb_read_bounded(path,b,n+1);if got!=n{sys_munmap(b,n+1);return PB_RC_CONFLICT} 133 out[0]=b as i64;out[1]=n;return 0 134} 135func rm2_main(argc:i64,argv:*i64)->i64{ 136 if argc!=4||pr_hash_ok(argv[3] as *u8)!=1{return PB_RC_USAGE} 137 let loaded:*i64=sys_mmap_try(16) as *i64;if (loaded as i64)<=0{return FSX_RC_IO};let readrc:i64=rm2_load(argv[2] as *u8,loaded);if readrc!=0{return readrc};let body:*u8=loaded[0] as *u8;let n:i64=loaded[1];let digest:*u8=sys_mmap_try(PB_SHA_HEX+1);let v:*i64=sys_mmap_try(RM_VALUES_BYTES) as *i64;let state:*i64=sys_mmap_try(RM2_STATE_WORDS*8) as *i64;let prefix:*u8=sys_mmap_try(PB_PATH) 138 if (body as i64)<=0||(digest as i64)<=0||(v as i64)<=0||(state as i64)<=0||(prefix as i64)<=0{return FSX_RC_IO} 139 var i:i64=0;while i<RM_FIELDS{v[i]=0;i=i+1};i=0;while i<RM2_STATE_WORDS{state[i]=0;i=i+1} 140 pb_hash(body,n,digest);if fsx_seq(digest,argv[3] as *u8)!=1{return PB_RC_CONFLICT} 141 // Preserve the v1 parser and executor unchanged. 142 if rm_parse(body,n,v)==0{if fsx_seq(v[0] as *u8,RM_SCHEMA)==1{if fsx_seq(argv[1] as *u8,"prepare-release")==1{return rm_prepare_main(argc,argv)};if fsx_seq(argv[1] as *u8,"release-manifest")==1{return rm_main(argc,argv)};return PB_RC_USAGE}} 143 i=0;while i<RM_FIELDS{v[i]=0;i=i+1};let parsed:i64=rm2_parse(body,n,v,state);if parsed!=0{return parsed} 144 if fsx_seq(argv[1] as *u8,"prepare-release")==1{ 145 let rc:i64=rm2_preflight(v,state,prefix);fsx_puts("NX-RELEASE-V2 PREPARE dependencies=");fsx_putn(state[1]);fsx_puts(" rc=");fsx_putn(rc);fsx_puts(" registration_writes=0 publication_writes=0\n");return rc 146 } 147 if fsx_seq(argv[1] as *u8,"release-manifest")!=1{return PB_RC_USAGE} 148 let receipt:*u8=sys_mmap_try(PB_PATH);let pos:*i64=sys_mmap_try(8) as *i64;if (receipt as i64)<=0||(pos as i64)<=0{return FSX_RC_IO} 149 pos[0]=0;pb_app(receipt,pos,RM_RECEIPT_ROOT);pb_app(receipt,pos,"site-release-");pb_app(receipt,pos,digest);pb_app(receipt,pos,".jsonl") 150 fsx_puts("NX-RELEASE-V2 receipt=");fsx_puts(receipt);fsx_puts(" dependencies=");fsx_putn(state[1]);fsx_puts(" consumer_publishes_max=1\n") 151 let bank:i64=rm_bank(body,n,digest);if bank!=0{return bank};return rm2_execute(v,state,prefix,receipt,digest) 152} 153func main(argc:i64,argv:*i64)->i64{ 154 if argc>1{if fsx_seq(argv[1] as *u8,"prepare-release")==1||fsx_seq(argv[1] as *u8,"release-manifest")==1{return rm2_main(argc,argv)}} 155 return sp_main(argc,argv) 156}