@r5n/hydra 0.3.3 → 0.3.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -40,7 +40,7 @@ ${u.default.gray(rn)} ${t}
|
|
|
40
40
|
`);let $=0,T=0;d(),i=setInterval(()=>{if(e&&a===c)return;w(),c=a;let L=u.default.magenta(r[$]);if(e)process.stdout.write(`${L} ${a}...`);else if(t==="timer")process.stdout.write(`${L} ${a} ${A(l)}`);else{let K=".".repeat(Math.floor(T)).slice(0,3);process.stdout.write(`${L} ${a}${K}`)}$=$+1<r.length?$+1:0,T=T<r.length?T+0.125:0},o)},H=(y="",$=0)=>{n=!1,clearInterval(i),w();let T=$===0?u.default.green(Un):$===1?u.default.red(pt):u.default.red(ut);a=C(y??a),t==="timer"?process.stdout.write(`${T} ${a} ${A(l)}
|
|
41
41
|
`):process.stdout.write(`${T} ${a}
|
|
42
42
|
`),h(),s()};return{start:b,stop:H,message:(y="")=>{a=C(y??a)}}},xt=async(t,r)=>{let o={},e=Object.keys(t);for(let s of e){let i=t[s],n=await i({results:o})?.catch((a)=>{throw a});if(typeof r?.onCancel=="function"&&S(n)){o[s]="canceled",r.onCancel({results:o});continue}o[s]=n}return o};var vr=!process.env.NO_COLOR&&(process.env.FORCE_COLOR||process.stdout.isTTY),Pr=["blue","cyan","gray","green","magenta","red","white","yellow"],Mr=["bold","dim","italic","underline","reset"],wt={bold:"\x1B[1m",dim:"\x1B[2m",italic:"\x1B[3m",reset:"\x1B[0m",underline:"\x1B[4m"},Gr={"\x1B[30m":"\x1B[90m","\x1B[31m":"\x1B[91m","\x1B[32m":"\x1B[92m","\x1B[33m":"\x1B[93m","\x1B[34m":"\x1B[94m","\x1B[35m":"\x1B[95m","\x1B[36m":"\x1B[96m","\x1B[37m":"\x1B[97m"};function Hn(t){return(r)=>vr?`${t}${r}${wt.reset}`:r}function $t({bright:t=!1,color:r}){let o=Bun.color(r,"ansi");if(!o)return"";return t?Gr[o]||o:o}var Xr=Pr.reduce((t,r)=>{return t[r]=Hn($t({color:r})),t[`${r}Bright`]=Hn($t({bright:!0,color:r})),t},{}),_r=Mr.reduce((t,r)=>{return t[r]=Hn(wt[r]),t},{}),p={...Xr,..._r};var _=({prefix:t="",level:r=process.env.DEBUG?0:1}={})=>{function o(...a){if(r<=0){let c=a.join(" ");console.log(bn({level:p.dim("DEBUG"),message:c,prefix:t}))}}function e(...a){if(r<=1){let c=a.join(" ");console.info(bn({level:p.cyan("INFO "),message:c,prefix:t}))}}function s(...a){if(r<=2){let c=a.join(" ");console.warn(bn({level:p.yellow("WARN "),message:c,prefix:t}))}}function i(...a){if(r<=3){let c=a.join(" ");console.error(bn({level:p.red("ERROR"),message:c,prefix:t}))}}function n(a){return _({level:r,prefix:`${t}:${a}`})}return{child:n,debug:o,error:i,info:e,warn:s}},Ln=_();function bn({prefix:t,level:r,message:o}){let e=new Date().toISOString().split("T")[1]?.split(".")[0]||"",s=t?`[${t}] `:"";return`${p.dim(e||"")} ${r} ${s}${o}`}var B=new Uint32Array(65536),Ar=(t,r)=>{let o=t.length,e=r.length,s=1<<o-1,i=-1,n=0,a=o,c=o;while(c--)B[t.charCodeAt(c)]|=1<<c;for(c=0;c<e;c++){let l=B[r.charCodeAt(c)],f=l|n;if(l|=(l&i)+i^i,n|=~(l|i),i&=l,n&s)a++;if(i&s)a--;n=n<<1|1,i=i<<1|~(f|n),n&=f}c=o;while(c--)B[t.charCodeAt(c)]=0;return a},Ur=(t,r)=>{let o=r.length,e=t.length,s=[],i=[],n=Math.ceil(o/32),a=Math.ceil(e/32);for(let h=0;h<n;h++)i[h]=-1,s[h]=0;let c=0;for(;c<a-1;c++){let h=0,w=-1,C=c*32,A=Math.min(32,e)+C;for(let b=C;b<A;b++)B[t.charCodeAt(b)]|=1<<b;for(let b=0;b<o;b++){let H=B[r.charCodeAt(b)],y=i[b/32|0]>>>b&1,$=s[b/32|0]>>>b&1,T=H|h,L=((H|$)&w)+w^w|H|$,K=h|~(L|w),fn=w&L;if(K>>>31^y)i[b/32|0]^=1<<b;if(fn>>>31^$)s[b/32|0]^=1<<b;K=K<<1|y,fn=fn<<1|$,w=fn|~(T|K),h=K&T}for(let b=C;b<A;b++)B[t.charCodeAt(b)]=0}let l=0,f=-1,m=c*32,g=Math.min(32,e-m)+m;for(let h=m;h<g;h++)B[t.charCodeAt(h)]|=1<<h;let d=e;for(let h=0;h<o;h++){let w=B[r.charCodeAt(h)],C=i[h/32|0]>>>h&1,A=s[h/32|0]>>>h&1,b=w|l,H=((w|A)&f)+f^f|w|A,y=l|~(H|f),$=f&H;if(d+=y>>>e-1&1,d-=$>>>e-1&1,y>>>31^C)i[h/32|0]^=1<<h;if($>>>31^A)s[h/32|0]^=1<<h;y=y<<1|C,$=$<<1|A,f=$|~(b|y),l=y&b}for(let h=m;h<g;h++)B[t.charCodeAt(h)]=0;return d},Et=(t,r)=>{if(t.length<r.length){let o=r;r=t,t=o}if(r.length===0)return t.length;if(t.length<=32)return Ar(t,r);return Ur(t,r)};function Rt(t,r,o=3){return r.map((i)=>({distance:Et(t.toLowerCase(),i.toLowerCase()),option:i})).filter(({distance:i})=>i<=Math.max(3,t.length/2)).sort((i,n)=>i.distance-n.distance).slice(0,o).map(({option:i})=>i)}function Ot(t,r){return r[t.toLowerCase()]||null}async function F(t,r={}){let{fallback:o,logLevel:e="warn",message:s="Operation failed gracefully"}=r;try{return await t()}catch(i){let n=i instanceof Error?i.message:String(i);if(console[e](`${s}: ${n}`),o)try{await o()}catch(a){console.error(`Fallback also failed: ${a instanceof Error?a.message:String(a)}`)}return}}var qr={backoffMultiplier:2,initialDelay:1000,maxAttempts:3,maxDelay:30000,onRetry:(t,r)=>{Ln.warn(`Retry attempt ${t}: ${r.message}`)},retryableErrors:["ECONNREFUSED","ECONNRESET","ETIMEDOUT","ENOTFOUND","rate limit","429","503","502","504","npm ERR!","EPUBLISHCONFLICT"]};async function on(t,r={}){let o={...qr,...r},e=new Error("Operation failed");for(let s=1;s<=o.maxAttempts;s++)try{return await t()}catch(i){if(e=i instanceof Error?i:new Error(String(i)),!o.retryableErrors.some((c)=>e.message.toLowerCase().includes(c.toLowerCase()))||s===o.maxAttempts)throw e;o.onRetry(s,e);let a=Math.min(o.initialDelay*o.backoffMultiplier**(s-1),o.maxDelay);await new Promise((c)=>setTimeout(c,a))}throw e}var U=(t)=>{return t.map=(r)=>U((o)=>r(t(o))),t.or=(r)=>U((o)=>{try{return t(o)}catch(e){return r(o)}}),t},yn=()=>"bad banditype"();var St=(t)=>U((r)=>t.includes(r)?r:yn()),xn=(t)=>U((r)=>typeof r===typeof t?r:yn()),en=()=>xn(""),Ct=()=>xn(0),Tt=()=>xn(!0);var kt=()=>xn();var Nt=(t)=>U((r)=>r instanceof t?r:yn()),Bn=(t)=>Nt(Object).map((r)=>{let o={};for(let e in r){let s=t(r[e]);s!==void 0&&(o[e]=s)}return o}),D=(t)=>Nt(Object).map((r)=>{let o={};for(let e in t){let s=t[e](r[e]);s!==void 0&&(o[e]=s)}return o});function Hr(t,r){return U((o)=>{try{return t(o)}catch{try{return r(o)}catch{throw new Error("No matching schema in union")}}})}function J(t,r){return(o)=>{if(o===void 0)return r;return t(o)}}function vt(){return U((t)=>{let r=Ct()(t);if(!Number.isInteger(r)||r<=0)throw new Error("Must be a positive integer");return r})}function Pt(){return U((t)=>{let r=en()(t);try{return new URL(r),r}catch{throw new Error("Must be a valid URL")}})}function Mt(t,r){return U((o)=>{let e=en()(o);if(!t.test(e))throw new Error(r||`Must match pattern ${t}`);return e})}function sn(t){return{parse(r){try{return t(r)}catch(o){throw new Error(o instanceof Error?o.message:"Validation failed")}},safeParse(r){try{return{data:t(r),success:!0}}catch(o){return{error:o instanceof Error?o:new Error("Validation failed"),success:!1}}},validator:t}}function Y(){return Hr(kt(),en())}function Q(t){return J(en(),t)}function nn(t){return J(Tt(),t)}function $n(t,r){return J(St(t),r)}import{parseArgs as Vr}from"util";var{$:M}=globalThis.Bun;import{join as At}from"path";var{$:_t}=globalThis.Bun;import{tmpdir as Lr}from"os";var Gt={MACHINES_DIR:"machines",TEMP_DIR:Lr()};var Br=D({directory:Q("./gh-runners"),labels:Y(),name:Q("runner"),numberOfMachines:J(vt(),1),os:$n(["osx","linux","windows"],"osx"),overwrite:nn(!1),run:nn(!1),runnerGroup:Y(),url:Y()}),Ir=sn(Br),Xt=sn(D({defaultProfile:Y(),profiles:Bn(Ir.validator)}));var jr=["hydra.json",".hydra.json","hydra.toml",".hydra.toml"];async function Wr(t=process.cwd()){for(let r of jr){let o=At(t,r);if(await Bun.file(o).exists())return o}return null}async function W(t){let r=t?.configPath||await Wr();if(!r)return null;let o=Bun.file(r);if(!await o.exists())return null;try{let e=await o.text(),s=r.endsWith(".toml")?await zr(e):JSON.parse(e);return Xt.parse(s)}catch(e){if(e instanceof Error)throw new Error(`Invalid config file: ${e.message}`);throw e}}async function z(t,r){if(!t)return null;let o=r||t.defaultProfile;if(!o)return null;return t.profiles[o]||null}async function zr(t){let r=At(Gt.TEMP_DIR,`hydra-config-${Date.now()}.toml`);await Bun.write(r,t);try{let o=await import(r);return await _t`rm -f ${r}`.quiet(),o.default}catch(o){throw await _t`rm -f ${r}`.quiet(),new Error(`Failed to parse TOML configuration: ${o}`)}}function an(){let t=import.meta.path,r=t.substring(0,t.lastIndexOf("/"));return r.substring(0,r.lastIndexOf("/"))}var Kr=D({directory:Q(`${process.cwd()}/gh-runners`),labels:Y(),name:Q("runner"),numberOfMachines:J(Mt(/^\d+$/,"Number of machines must be a positive integer"),"1"),os:$n(["osx","linux","windows"],"osx"),overwrite:nn(!1),run:nn(!1),token:Q(process.env.GITHUB_PERSONAL_ACCESS_TOKEN??""),url:Pt()}),Ie=sn(Kr);var Z=_({prefix:"WorkerManager"}),v=new Set,Ut=!1;function Jr(){if(Ut)return;Ut=!0;let t=()=>{if(v.size>0)Z.info("Cleaning up workers...");qt()};process.on("exit",t),process.on("SIGINT",t),process.on("SIGTERM",t),process.on("uncaughtException",(r)=>{Z.error("Uncaught exception:",r),t(),process.exit(1)})}function Yr(t){v.add(t),Z.debug(`Worker added. Total workers: ${v.size}`),t.addEventListener("exit",()=>{v.delete(t),Z.debug(`Worker exited. Remaining workers: ${v.size}`)})}function Qr(t){if(v.has(t))t.terminate(),v.delete(t),Z.debug(`Worker removed. Remaining workers: ${v.size}`)}function qt(){if(v.size>0){Z.info(`Terminating ${v.size} workers...`);for(let t of v)try{t.terminate()}catch(r){Z.error("Failed to terminate worker:",r)}v.clear()}}function Zr(){return v.size}Jr();var wn={add:Yr,remove:Qr,get size(){return Zr()},terminateAll:qt};var P=_({prefix:"createMachines"});async function Ht(){let t=await Dr(),{overwrite:r,directory:o,numberOfMachines:e,os:s,run:i,name:n,labels:a,runnerGroup:c}=t,{token:l,url:f}=t;if(!f)throw new Error("URL (-u) parameter is required");if(!l){let d=Fr(f),h=await j({message:`No token provided. Would you like to open GitHub to get a runner token?
|
|
43
|
-
${d}`});if(S(h)||!h)throw new Error("Token is required to create runners");P.info("Opening GitHub runner settings..."),await M`open ${d}`.quiet();let w=await N({message:"Enter the runner registration token:",placeholder:"AXXXXXXXXXXXXXXXXXXXXXXXXX",validate:(C)=>{if(!C||C.trim().length===0)return"Token is required";return}});if(S(w))throw new Error("Token is required to create runners");l=w.trim()}if(P.info(`Creating ${e} runners in ${o}`),P.debug(`OS: ${s}, Name: ${n}, Labels: ${a||"none"}`),await Bun.file(o).exists())if(r)P.warn(`Directory ${o} exists, removing...`),await M`rm -rf ${o}`;else throw new Error(`Directory ${o} already exists`);let g=await no({directory:o,os:s});if(await to({directory:o,numberOfMachines:e,overwrite:r,runnerBase:g}),!i){P.info("Runners created but not started");return}ro({directory:o,labels:a,name:n,numberOfMachines:e,runnerGroup:c,token:l,url:f})}function Fr(t){try{let r=new URL(t),o=r.pathname.split("/").filter(Boolean);if(r.hostname==="github.com"||r.hostname==="www.github.com"){if(o[0]==="orgs"&&o[1])return`https://github.com/organizations/${o[1]}/settings/actions/runners/new`;else if(o.length>=2)return`https://github.com/${o[0]}/${o[1]}/settings/actions/runners/new`}else if(o[0]==="orgs"&&o[1])return`${r.origin}/organizations/${o[1]}/settings/actions/runners/new`;else if(o.length>=2)return`${r.origin}/${o[0]}/${o[1]}/settings/actions/runners/new`;return t}catch{return t}}async function Dr(){let{values:t}=Vr({allowPositionals:!0,args:Bun.argv,options:{config:{short:"c",type:"string"},directory:{default:`${process.cwd()}/gh-runners`,short:"d",type:"string"},labels:{short:"l",type:"string"},name:{default:"runner",short:"n",type:"string"},numberOfMachines:{default:"1",short:"m",type:"string"},os:{default:"osx",short:"s",type:"string"},overwrite:{default:!1,short:"f",type:"boolean"},profile:{short:"p",type:"string"},run:{default:!1,short:"r",type:"boolean"},runnerGroup:{short:"g",type:"string"},token:{short:"t",type:"string"},url:{short:"u",type:"string"}},strict:!0}),r=await W({configPath:t.config}),o=await z(r,t.profile),e={directory:t.directory,labels:t.labels,name:t.name,numberOfMachines:t.numberOfMachines,os:t.os,overwrite:t.overwrite,run:t.run,runnerGroup:t.runnerGroup,token:t.token,url:t.url};if(o)return{directory:e.directory??o.directory??`${process.cwd()}/gh-runners`,labels:e.labels??o.labels,name:e.name??o.name??"runner",numberOfMachines:e.numberOfMachines??String(o.numberOfMachines)??"1",os:e.os??o.os??"osx",overwrite:e.overwrite??o.overwrite??!1,run:e.run??o.run??!1,runnerGroup:e.runnerGroup??o.runnerGroup,token:e.token,url:e.url??o.url};return{directory:e.directory,labels:e.labels,name:e.name,numberOfMachines:e.numberOfMachines,os:e.os,overwrite:e.overwrite,run:e.run,runnerGroup:e.runnerGroup,token:e.token,url:e.url}}async function no({directory:t,os:r}){let o=P.child("download");o.info(`Fetching latest runner version for ${r}...`);let e=await on(async()=>(await M`curl -Ls -o /dev/null -w "%{url_effective}" https://github.com/actions/runner/releases/latest`).text(),{onRetry:(l,f)=>{o.warn(`Retrying GitHub API call (attempt ${l}): ${f.message}`)}}),i=(await M`basename ${e}`).text().trim().replace("v",""),n=`actions-runner-${r}-x64-${i}.tar.gz`,a=`https://github.com/actions/runner/releases/download/v${i}/${n}`;if(o.debug(`Runner version: ${i}`),o.debug(`Download URL: ${a}`),await M`mkdir -p ${t}`,await M`mkdir -p ${t}/machines`,!await Bun.file(`${t}/${n}`).exists())o.info("Downloading runner image..."),await on(async()=>await M`curl -o ${t}/${n} -L ${a}`,{maxAttempts:5,onRetry:(l,f)=>{o.warn(`Retrying runner download (attempt ${l}): ${f.message}`)}}),o.info("Download complete");else o.info("Runner image already exists, skipping download");return n}async function to({overwrite:t,numberOfMachines:r,directory:o,runnerBase:e}){let s=P.child("extract"),i=Number(r),n=(()=>{try{let f=Bun.spawnSync(["nproc"]);return Number.parseInt(f.stdout.toString().trim(),10)||4}catch{try{let f=Bun.spawnSync(["sysctl","-n","hw.ncpu"]);return Number.parseInt(f.stdout.toString().trim(),10)||4}catch{return 4}}})(),a=Math.min(Math.max(2,Math.floor(n/2)),i,8);s.info(`Extracting ${i} runners with concurrency of ${a}`);let c=Array.from({length:i},(f,m)=>m+1).map((f)=>({execute:async()=>{let m=`${o}/machines/${f}`;if(await Bun.file(m).exists())if(t)s.debug(`Machine ${f} already exists, removing...`),await M`rm -rf ${m}`;else{s.debug(`Machine ${f} already exists, skipping...`);return}await M`mkdir -p ${m}`,await M`tar xzf ${e} -C ${m.replace(`${o}/`,"")}`.cwd(o);let d=`${an()}/src/runner`;await M`cp -R ${d}/hooks ${m}`,await M`cp -R ${d}/.runner-env ${m}/.runner-env`,s.debug(`Machine ${f} extracted successfully`)},runnerIndex:f})),l=[];for(let f=0;f<c.length;f+=a){let m=c.slice(f,f+a),g=await Promise.allSettled(m.map((d)=>d.execute()));for(let[d,h]of g.entries())if(h.status==="rejected")throw s.error(`Failed to extract runner ${m[d]?.runnerIndex}: ${h.reason}`),h.reason;l.push(...g)}s.info(`Successfully extracted ${i} runners`)}function ro({directory:t,numberOfMachines:r,token:o,url:e,name:s,labels:i,runnerGroup:n}){P.info(`Starting ${r} runners...`);for(let a=1;a<=Number(r);a++){let c=`${an()}/src/runner-worker.ts`,l=`${s}-${a}`,f=new Worker(c,{name:l,ref:!0});f.addEventListener("open",()=>{P.info(`Worker ${l} started`),f.postMessage({index:a,labels:i,machineCwd:`${t}/machines/${a}`,name:s,runnerGroup:n,token:o,url:e})}),f.addEventListener("message",({data:m})=>{let g=m.type==="error"?"error":"info";P[g](`${l}: ${m.message}`)}),f.addEventListener("error",(m)=>{P.error(`Worker ${l} error:`,m)}),f.addEventListener("exit",(m)=>{P.info(`Worker ${l} exited with code ${m}`)}),wn.add(f)}P.info("All workers started. Press Ctrl+C to stop all machines")}var Lt="0.3.
|
|
43
|
+
${d}`});if(S(h)||!h)throw new Error("Token is required to create runners");P.info("Opening GitHub runner settings..."),await M`open ${d}`.quiet();let w=await N({message:"Enter the runner registration token:",placeholder:"AXXXXXXXXXXXXXXXXXXXXXXXXX",validate:(C)=>{if(!C||C.trim().length===0)return"Token is required";return}});if(S(w))throw new Error("Token is required to create runners");l=w.trim()}if(P.info(`Creating ${e} runners in ${o}`),P.debug(`OS: ${s}, Name: ${n}, Labels: ${a||"none"}`),await Bun.file(o).exists())if(r)P.warn(`Directory ${o} exists, removing...`),await M`rm -rf ${o}`;else throw new Error(`Directory ${o} already exists`);let g=await no({directory:o,os:s});if(await to({directory:o,numberOfMachines:e,overwrite:r,runnerBase:g}),!i){P.info("Runners created but not started");return}ro({directory:o,labels:a,name:n,numberOfMachines:e,runnerGroup:c,token:l,url:f})}function Fr(t){try{let r=new URL(t),o=r.pathname.split("/").filter(Boolean);if(r.hostname==="github.com"||r.hostname==="www.github.com"){if(o[0]==="orgs"&&o[1])return`https://github.com/organizations/${o[1]}/settings/actions/runners/new`;else if(o.length>=2)return`https://github.com/${o[0]}/${o[1]}/settings/actions/runners/new`}else if(o[0]==="orgs"&&o[1])return`${r.origin}/organizations/${o[1]}/settings/actions/runners/new`;else if(o.length>=2)return`${r.origin}/${o[0]}/${o[1]}/settings/actions/runners/new`;return t}catch{return t}}async function Dr(){let{values:t}=Vr({allowPositionals:!0,args:Bun.argv,options:{config:{short:"c",type:"string"},directory:{default:`${process.cwd()}/gh-runners`,short:"d",type:"string"},labels:{short:"l",type:"string"},name:{default:"runner",short:"n",type:"string"},numberOfMachines:{default:"1",short:"m",type:"string"},os:{default:"osx",short:"s",type:"string"},overwrite:{default:!1,short:"f",type:"boolean"},profile:{short:"p",type:"string"},run:{default:!1,short:"r",type:"boolean"},runnerGroup:{short:"g",type:"string"},token:{short:"t",type:"string"},url:{short:"u",type:"string"}},strict:!0}),r=await W({configPath:t.config}),o=await z(r,t.profile),e={directory:t.directory,labels:t.labels,name:t.name,numberOfMachines:t.numberOfMachines,os:t.os,overwrite:t.overwrite,run:t.run,runnerGroup:t.runnerGroup,token:t.token,url:t.url};if(o)return{directory:e.directory??o.directory??`${process.cwd()}/gh-runners`,labels:e.labels??o.labels,name:e.name??o.name??"runner",numberOfMachines:e.numberOfMachines??String(o.numberOfMachines)??"1",os:e.os??o.os??"osx",overwrite:e.overwrite??o.overwrite??!1,run:e.run??o.run??!1,runnerGroup:e.runnerGroup??o.runnerGroup,token:e.token,url:e.url??o.url};return{directory:e.directory,labels:e.labels,name:e.name,numberOfMachines:e.numberOfMachines,os:e.os,overwrite:e.overwrite,run:e.run,runnerGroup:e.runnerGroup,token:e.token,url:e.url}}async function no({directory:t,os:r}){let o=P.child("download");o.info(`Fetching latest runner version for ${r}...`);let e=await on(async()=>(await M`curl -Ls -o /dev/null -w "%{url_effective}" https://github.com/actions/runner/releases/latest`).text(),{onRetry:(l,f)=>{o.warn(`Retrying GitHub API call (attempt ${l}): ${f.message}`)}}),i=(await M`basename ${e}`).text().trim().replace("v",""),n=`actions-runner-${r}-x64-${i}.tar.gz`,a=`https://github.com/actions/runner/releases/download/v${i}/${n}`;if(o.debug(`Runner version: ${i}`),o.debug(`Download URL: ${a}`),await M`mkdir -p ${t}`,await M`mkdir -p ${t}/machines`,!await Bun.file(`${t}/${n}`).exists())o.info("Downloading runner image..."),await on(async()=>await M`curl -o ${t}/${n} -L ${a}`,{maxAttempts:5,onRetry:(l,f)=>{o.warn(`Retrying runner download (attempt ${l}): ${f.message}`)}}),o.info("Download complete");else o.info("Runner image already exists, skipping download");return n}async function to({overwrite:t,numberOfMachines:r,directory:o,runnerBase:e}){let s=P.child("extract"),i=Number(r),n=(()=>{try{let f=Bun.spawnSync(["nproc"]);return Number.parseInt(f.stdout.toString().trim(),10)||4}catch{try{let f=Bun.spawnSync(["sysctl","-n","hw.ncpu"]);return Number.parseInt(f.stdout.toString().trim(),10)||4}catch{return 4}}})(),a=Math.min(Math.max(2,Math.floor(n/2)),i,8);s.info(`Extracting ${i} runners with concurrency of ${a}`);let c=Array.from({length:i},(f,m)=>m+1).map((f)=>({execute:async()=>{let m=`${o}/machines/${f}`;if(await Bun.file(m).exists())if(t)s.debug(`Machine ${f} already exists, removing...`),await M`rm -rf ${m}`;else{s.debug(`Machine ${f} already exists, skipping...`);return}await M`mkdir -p ${m}`,await M`tar xzf ${e} -C ${m.replace(`${o}/`,"")}`.cwd(o);let d=`${an()}/src/runner`;await M`cp -R ${d}/hooks ${m}`,await M`cp -R ${d}/.runner-env ${m}/.runner-env`,s.debug(`Machine ${f} extracted successfully`)},runnerIndex:f})),l=[];for(let f=0;f<c.length;f+=a){let m=c.slice(f,f+a),g=await Promise.allSettled(m.map((d)=>d.execute()));for(let[d,h]of g.entries())if(h.status==="rejected")throw s.error(`Failed to extract runner ${m[d]?.runnerIndex}: ${h.reason}`),h.reason;l.push(...g)}s.info(`Successfully extracted ${i} runners`)}function ro({directory:t,numberOfMachines:r,token:o,url:e,name:s,labels:i,runnerGroup:n}){P.info(`Starting ${r} runners...`);for(let a=1;a<=Number(r);a++){let c=`${an()}/src/runner-worker.ts`,l=`${s}-${a}`,f=new Worker(c,{name:l,ref:!0});f.addEventListener("open",()=>{P.info(`Worker ${l} started`),f.postMessage({index:a,labels:i,machineCwd:`${t}/machines/${a}`,name:s,runnerGroup:n,token:o,url:e})}),f.addEventListener("message",({data:m})=>{let g=m.type==="error"?"error":"info";P[g](`${l}: ${m.message}`)}),f.addEventListener("error",(m)=>{P.error(`Worker ${l} error:`,m)}),f.addEventListener("exit",(m)=>{P.info(`Worker ${l} exited with code ${m}`)}),wn.add(f)}P.info("All workers started. Press Ctrl+C to stop all machines")}var Lt="0.3.4";var Bt={create:`
|
|
44
44
|
hydra create - Create and register new GitHub runner machines
|
|
45
45
|
|
|
46
46
|
USAGE
|
package/package.json
CHANGED