@r5n/hydra 0.3.9 → 0.3.10
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 @@ ${g.default.gray(an)} ${t}
|
|
|
40
40
|
`);let w=0,N=0;d(),s=setInterval(()=>{if(e&&a===c)return;E(),c=a;let I=g.default.magenta(r[w]);if(e)process.stdout.write(`${I} ${a}...`);else if(t==="timer")process.stdout.write(`${I} ${a} ${q(l)}`);else{let Y=".".repeat(Math.floor(N)).slice(0,3);process.stdout.write(`${I} ${a}${Y}`)}w=w+1<r.length?w+1:0,N=N<r.length?N+0.125:0},o)},B=(x="",w=0)=>{n=!1,clearInterval(s),E();let N=w===0?g.default.green(In):w===1?g.default.red(bt):g.default.red(yt);a=k(x??a),t==="timer"?process.stdout.write(`${N} ${a} ${q(l)}
|
|
41
41
|
`):process.stdout.write(`${N} ${a}
|
|
42
42
|
`),u(),i()};return{start:y,stop:B,message:(x="")=>{a=k(x??a)}}},Rt=async(t,r)=>{let o={},e=Object.keys(t);for(let i of e){let s=t[i],n=await s({results:o})?.catch((a)=>{throw a});if(typeof r?.onCancel=="function"&&T(n)){o[i]="canceled",r.onCancel({results:o});continue}o[i]=n}return o};var _r=!process.env.NO_COLOR&&(process.env.FORCE_COLOR||process.stdout.isTTY),Ar=["blue","cyan","gray","green","magenta","red","white","yellow"],Ur=["bold","dim","italic","underline","reset"],St={bold:"\x1B[1m",dim:"\x1B[2m",italic:"\x1B[3m",reset:"\x1B[0m",underline:"\x1B[4m"},qr={"\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 Wn(t){return(r)=>_r?`${t}${r}${St.reset}`:r}function Ot({bright:t=!1,color:r}){let o=Bun.color(r,"ansi");if(!o)return"";return t?qr[o]||o:o}var Hr=Ar.reduce((t,r)=>{return t[r]=Wn(Ot({color:r})),t[`${r}Bright`]=Wn(Ot({bright:!0,color:r})),t},{}),Lr=Ur.reduce((t,r)=>{return t[r]=Wn(St[r]),t},{}),b={...Hr,...Lr};var O=({prefix:t="",level:r=process.env.DEBUG?0:1}={})=>{function o(...a){if(r<=0){let c=a.join(" ");console.log(wn({level:b.dim("DEBUG"),message:c,prefix:t}))}}function e(...a){if(r<=1){let c=a.join(" ");console.info(wn({level:b.cyan("INFO "),message:c,prefix:t}))}}function i(...a){if(r<=2){let c=a.join(" ");console.warn(wn({level:b.yellow("WARN "),message:c,prefix:t}))}}function s(...a){if(r<=3){let c=a.join(" ");console.error(wn({level:b.red("ERROR"),message:c,prefix:t}))}}function n(a){return O({level:r,prefix:`${t}:${a}`})}return{child:n,debug:o,error:s,info:e,warn:i}},zn=O();function wn({prefix:t,level:r,message:o}){let e=new Date().toISOString().split("T")[1]?.split(".")[0]||"",i=t?`[${t}] `:"";return`${b.dim(e||"")} ${r} ${i}${o}`}var h={arrow:b.yellow,bold:b.bold,boolean:b.cyan,bullet:b.yellow,busy:b.yellow,command:b.green,dim:b.dim,error:b.red,flag:b.yellow,info:b.cyan,number:b.magenta,offline:b.red,online:b.green,separator:b.dim,string:b.green,success:b.green,value:b.cyan,warning:b.yellow};var xe=O({prefix:"cli-core"}).child("error");var j=new Uint32Array(65536),Br=(t,r)=>{let o=t.length,e=r.length,i=1<<o-1,s=-1,n=0,a=o,c=o;while(c--)j[t.charCodeAt(c)]|=1<<c;for(c=0;c<e;c++){let l=j[r.charCodeAt(c)],f=l|n;if(l|=(l&s)+s^s,n|=~(l|s),s&=l,n&i)a++;if(s&i)a--;n=n<<1|1,s=s<<1|~(f|n),n&=f}c=o;while(c--)j[t.charCodeAt(c)]=0;return a},Ir=(t,r)=>{let o=r.length,e=t.length,i=[],s=[],n=Math.ceil(o/32),a=Math.ceil(e/32);for(let u=0;u<n;u++)s[u]=-1,i[u]=0;let c=0;for(;c<a-1;c++){let u=0,E=-1,k=c*32,q=Math.min(32,e)+k;for(let y=k;y<q;y++)j[t.charCodeAt(y)]|=1<<y;for(let y=0;y<o;y++){let B=j[r.charCodeAt(y)],x=s[y/32|0]>>>y&1,w=i[y/32|0]>>>y&1,N=B|u,I=((B|w)&E)+E^E|B|w,Y=u|~(I|E),gn=E&I;if(Y>>>31^x)s[y/32|0]^=1<<y;if(gn>>>31^w)i[y/32|0]^=1<<y;Y=Y<<1|x,gn=gn<<1|w,E=gn|~(N|Y),u=Y&N}for(let y=k;y<q;y++)j[t.charCodeAt(y)]=0}let l=0,f=-1,m=c*32,p=Math.min(32,e-m)+m;for(let u=m;u<p;u++)j[t.charCodeAt(u)]|=1<<u;let d=e;for(let u=0;u<o;u++){let E=j[r.charCodeAt(u)],k=s[u/32|0]>>>u&1,q=i[u/32|0]>>>u&1,y=E|l,B=((E|q)&f)+f^f|E|q,x=l|~(B|f),w=f&B;if(d+=x>>>e-1&1,d-=w>>>e-1&1,x>>>31^k)s[u/32|0]^=1<<u;if(w>>>31^q)i[u/32|0]^=1<<u;x=x<<1|k,w=w<<1|q,f=w|~(y|x),l=x&y}for(let u=m;u<p;u++)j[t.charCodeAt(u)]=0;return d},Ct=(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 Br(t,r);return Ir(t,r)};function Tt(t,r,o=3){return r.map((s)=>({distance:Ct(t.toLowerCase(),s.toLowerCase()),option:s})).filter(({distance:s})=>s<=Math.max(3,t.length/2)).sort((s,n)=>s.distance-n.distance).slice(0,o).map(({option:s})=>s)}function kt(t,r){return r[t.toLowerCase()]||null}var Nt=O({prefix:"cli-core"}).child("graceful");async function tn(t,r={}){let{fallback:o,logLevel:e="warn",message:i="Operation failed gracefully"}=r;try{return await t()}catch(s){let n=s instanceof Error?s.message:String(s);if(Nt[e](`${i}: ${n}`),o)try{await o()}catch(a){Nt.error(`Fallback also failed: ${a instanceof Error?a.message:String(a)}`)}return}}var jr={backoffMultiplier:2,initialDelay:1000,maxAttempts:3,maxDelay:30000,onRetry:(t,r)=>{zn.warn(`Retry attempt ${t}: ${r.message}`)},retryableErrors:["ECONNREFUSED","ECONNRESET","ETIMEDOUT","ENOTFOUND","rate limit","429","503","502","504","npm ERR!","EPUBLISHCONFLICT"]};async function cn(t,r={}){let o={...jr,...r},e=new Error("Operation failed");for(let i=1;i<=o.maxAttempts;i++)try{return await t()}catch(s){if(e=s instanceof Error?s:new Error(String(s)),!o.retryableErrors.some((c)=>e.message.toLowerCase().includes(c.toLowerCase()))||i===o.maxAttempts)throw e;o.onRetry(i,e);let a=Math.min(o.initialDelay*o.backoffMultiplier**(i-1),o.maxDelay);await new Promise((c)=>setTimeout(c,a))}throw e}var H=(t)=>{return t.map=(r)=>H((o)=>r(t(o))),t.or=(r)=>H((o)=>{try{return t(o)}catch(e){return r(o)}}),t},En=()=>"bad banditype"();var vt=(t)=>H((r)=>t.includes(r)?r:En()),Rn=(t)=>H((r)=>typeof r===typeof t?r:En()),fn=()=>Rn(""),Pt=()=>Rn(0),Gt=()=>Rn(!0);var Mt=()=>Rn();var Xt=(t)=>H((r)=>r instanceof t?r:En()),Kn=(t)=>Xt(Object).map((r)=>{let o={};for(let e in r){let i=t(r[e]);i!==void 0&&(o[e]=i)}return o}),rn=(t)=>Xt(Object).map((r)=>{let o={};for(let e in t){let i=t[e](r[e]);i!==void 0&&(o[e]=i)}return o});function Wr(t,r){return H((o)=>{try{return t(o)}catch{try{return r(o)}catch{throw new Error("No matching schema in union")}}})}function Q(t,r){return(o)=>{if(o===void 0)return r;return t(o)}}function _t(){return H((t)=>{let r=Pt()(t);if(!Number.isInteger(r)||r<=0)throw new Error("Must be a positive integer");return r})}function At(){return H((t)=>{let r=fn()(t);try{return new URL(r),r}catch{throw new Error("Must be a valid URL")}})}function Ut(t,r){return H((o)=>{let e=fn()(o);if(!t.test(e))throw new Error(r||`Must match pattern ${t}`);return e})}function mn(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 Z(){return Wr(Mt(),fn())}function V(t){return Q(fn(),t)}function on(t){return Q(Gt(),t)}function On(t,r){return Q(vt(t),r)}import{parseArgs as ro}from"util";var{$:X}=globalThis.Bun;import{join as Bt}from"path";var{$:Lt}=globalThis.Bun;import{tmpdir as zr}from"os";var qt={MACHINES_DIR:"machines",TEMP_DIR:zr()};var Kr=rn({directory:V("./gh-runners"),labels:Z(),name:V("runner"),numberOfMachines:Q(_t(),1),os:On(["osx","linux","windows"],"osx"),overwrite:on(!1),run:on(!1),runnerGroup:Z(),url:Z()}),Jr=mn(Kr),Ht=mn(rn({defaultProfile:Z(),profiles:Kn(Jr.validator)}));var Yr=["hydra.json",".hydra.json","hydra.toml",".hydra.toml"];async function Qr(t=process.cwd()){for(let r of Yr){let o=Bt(t,r);if(await Bun.file(o).exists())return o}return null}async function K(t){let r=t?.configPath||await Qr();if(!r)return null;let o=Bun.file(r);if(!await o.exists())return null;try{let e=await o.text(),i=r.endsWith(".toml")?await Zr(e):JSON.parse(e);return Ht.parse(i)}catch(e){if(e instanceof Error)throw new Error(`Invalid config file: ${e.message}`);throw e}}async function J(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=Bt(qt.TEMP_DIR,`hydra-config-${Date.now()}.toml`);await Bun.write(r,t);try{let o=await import(r);return await Lt`rm -f ${r}`.quiet(),o.default}catch(o){throw await Lt`rm -f ${r}`.quiet(),new Error(`Failed to parse TOML configuration: ${o}`)}}function ln(){let t=import.meta.path,r=t.substring(0,t.lastIndexOf("/"));return r.substring(0,r.lastIndexOf("/"))}var Vr=rn({directory:V(`${process.cwd()}/gh-runners`),labels:Z(),name:V("runner"),numberOfMachines:Q(Ut(/^\d+$/,"Number of machines must be a positive integer"),"1"),os:On(["osx","linux","windows"],"osx"),overwrite:on(!1),run:on(!1),token:V(process.env.GITHUB_PERSONAL_ACCESS_TOKEN??""),url:At()}),ri=mn(Vr);var F=O({prefix:"WorkerManager"}),G=new Set,It=!1;function Fr(){if(It)return;It=!0;let t=()=>{if(G.size>0)F.info("Cleaning up workers...");jt()};process.on("exit",t),process.on("SIGINT",t),process.on("SIGTERM",t),process.on("uncaughtException",(r)=>{F.error("Uncaught exception:",r),t(),process.exit(1)})}function Dr(t){G.add(t),F.debug(`Worker added. Total workers: ${G.size}`),t.addEventListener("exit",()=>{G.delete(t),F.debug(`Worker exited. Remaining workers: ${G.size}`)})}function no(t){if(G.has(t))t.terminate(),G.delete(t),F.debug(`Worker removed. Remaining workers: ${G.size}`)}function jt(){if(G.size>0){F.info(`Terminating ${G.size} workers...`);for(let t of G)try{t.terminate()}catch(r){F.error("Failed to terminate worker:",r)}G.clear()}}function to(){return G.size}Fr();var Sn={add:Dr,remove:no,get size(){return to()},terminateAll:jt};var M=O({prefix:"createMachines"});async function Wt(){let t=await eo(),{overwrite:r,directory:o,numberOfMachines:e,os:i,run:s,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=oo(f),u=await z({message:`No token provided. Would you like to open GitHub to get a runner token?
|
|
43
|
-
${d}`});if(T(u)||!u)throw new Error("Token is required to create runners");M.info("Opening GitHub runner settings..."),await X`open ${d}`.quiet();let E=await P({message:"Enter the runner registration token:",placeholder:"AXXXXXXXXXXXXXXXXXXXXXXXXX",validate:(k)=>{if(!k||k.trim().length===0)return"Token is required";return}});if(T(E))throw new Error("Token is required to create runners");l=E.trim()}if(M.info(`Creating ${e} runners in ${o}`),M.debug(`OS: ${i}, Name: ${n}, Labels: ${a||"none"}`),await Bun.file(o).exists())if(r)M.warn(`Directory ${o} exists, removing...`),await X`rm -rf ${o}`;else throw new Error(`Directory ${o} already exists`);let p=await io({directory:o,os:i});if(await so({directory:o,numberOfMachines:e,overwrite:r,runnerBase:p}),!s){M.info("Runners created but not started");return}ao({directory:o,labels:a,name:n,numberOfMachines:e,runnerGroup:c,token:l,url:f})}function oo(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 eo(){let{values:t}=ro({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 K({configPath:t.config}),o=await J(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 io({directory:t,os:r}){let o=M.child("download");o.info(`Fetching latest runner version for ${r}...`);let e=await cn(async()=>(await X`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}`)}}),s=(await X`basename ${e}`).text().trim().replace("v",""),n=`actions-runner-${r}-x64-${s}.tar.gz`,a=`https://github.com/actions/runner/releases/download/v${s}/${n}`;if(o.debug(`Runner version: ${s}`),o.debug(`Download URL: ${a}`),await X`mkdir -p ${t}`,await X`mkdir -p ${t}/machines`,!await Bun.file(`${t}/${n}`).exists())o.info("Downloading runner image..."),await cn(async()=>await X`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 so({overwrite:t,numberOfMachines:r,directory:o,runnerBase:e}){let i=M.child("extract"),s=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)),s,8);i.info(`Extracting ${s} runners with concurrency of ${a}`);let c=Array.from({length:s},(f,m)=>m+1).map((f)=>({execute:async()=>{let m=`${o}/machines/${f}`;if(await Bun.file(m).exists())if(t)i.debug(`Machine ${f} already exists, removing...`),await X`rm -rf ${m}`;else{i.debug(`Machine ${f} already exists, skipping...`);return}await X`mkdir -p ${m}`,await X`tar xzf ${e} -C ${m.replace(`${o}/`,"")}`.cwd(o);let d=`${ln()}/src/runner`;await X`cp -R ${d}/hooks ${m}`,await X`cp -R ${d}/.runner-env ${m}/.runner-env`,i.debug(`Machine ${f} extracted successfully`)},runnerIndex:f})),l=[];for(let f=0;f<c.length;f+=a){let m=c.slice(f,f+a),p=await Promise.allSettled(m.map((d)=>d.execute()));for(let[d,u]of p.entries())if(u.status==="rejected")throw i.error(`Failed to extract runner ${m[d]?.runnerIndex}: ${u.reason}`),u.reason;l.push(...p)}i.info(`Successfully extracted ${s} runners`)}function ao({directory:t,numberOfMachines:r,token:o,url:e,name:i,labels:s,runnerGroup:n}){M.info(`Starting ${r} runners...`);for(let a=1;a<=Number(r);a++){let c=`${ln()}/src/runner-worker.ts`,l=`${i}-${a}`,f=new Worker(c,{name:l,ref:!0});f.addEventListener("open",()=>{M.info(`Worker ${l} started`),f.postMessage({index:a,labels:s,machineCwd:`${t}/machines/${a}`,name:i,runnerGroup:n,token:o,url:e})}),f.addEventListener("message",({data:m})=>{let p=m.type==="error"?"error":"info";M[p](`${l}: ${m.message}`)}),f.addEventListener("error",(m)=>{M.error(`Worker ${l} error:`,m)}),f.addEventListener("exit",(m)=>{M.info(`Worker ${l} exited with code ${m}`)}),Sn.add(f)}M.info("All workers started. Press Ctrl+C to stop all machines")}var zt="0.3.
|
|
43
|
+
${d}`});if(T(u)||!u)throw new Error("Token is required to create runners");M.info("Opening GitHub runner settings..."),await X`open ${d}`.quiet();let E=await P({message:"Enter the runner registration token:",placeholder:"AXXXXXXXXXXXXXXXXXXXXXXXXX",validate:(k)=>{if(!k||k.trim().length===0)return"Token is required";return}});if(T(E))throw new Error("Token is required to create runners");l=E.trim()}if(M.info(`Creating ${e} runners in ${o}`),M.debug(`OS: ${i}, Name: ${n}, Labels: ${a||"none"}`),await Bun.file(o).exists())if(r)M.warn(`Directory ${o} exists, removing...`),await X`rm -rf ${o}`;else throw new Error(`Directory ${o} already exists`);let p=await io({directory:o,os:i});if(await so({directory:o,numberOfMachines:e,overwrite:r,runnerBase:p}),!s){M.info("Runners created but not started");return}ao({directory:o,labels:a,name:n,numberOfMachines:e,runnerGroup:c,token:l,url:f})}function oo(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 eo(){let{values:t}=ro({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 K({configPath:t.config}),o=await J(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 io({directory:t,os:r}){let o=M.child("download");o.info(`Fetching latest runner version for ${r}...`);let e=await cn(async()=>(await X`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}`)}}),s=(await X`basename ${e}`).text().trim().replace("v",""),n=`actions-runner-${r}-x64-${s}.tar.gz`,a=`https://github.com/actions/runner/releases/download/v${s}/${n}`;if(o.debug(`Runner version: ${s}`),o.debug(`Download URL: ${a}`),await X`mkdir -p ${t}`,await X`mkdir -p ${t}/machines`,!await Bun.file(`${t}/${n}`).exists())o.info("Downloading runner image..."),await cn(async()=>await X`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 so({overwrite:t,numberOfMachines:r,directory:o,runnerBase:e}){let i=M.child("extract"),s=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)),s,8);i.info(`Extracting ${s} runners with concurrency of ${a}`);let c=Array.from({length:s},(f,m)=>m+1).map((f)=>({execute:async()=>{let m=`${o}/machines/${f}`;if(await Bun.file(m).exists())if(t)i.debug(`Machine ${f} already exists, removing...`),await X`rm -rf ${m}`;else{i.debug(`Machine ${f} already exists, skipping...`);return}await X`mkdir -p ${m}`,await X`tar xzf ${e} -C ${m.replace(`${o}/`,"")}`.cwd(o);let d=`${ln()}/src/runner`;await X`cp -R ${d}/hooks ${m}`,await X`cp -R ${d}/.runner-env ${m}/.runner-env`,i.debug(`Machine ${f} extracted successfully`)},runnerIndex:f})),l=[];for(let f=0;f<c.length;f+=a){let m=c.slice(f,f+a),p=await Promise.allSettled(m.map((d)=>d.execute()));for(let[d,u]of p.entries())if(u.status==="rejected")throw i.error(`Failed to extract runner ${m[d]?.runnerIndex}: ${u.reason}`),u.reason;l.push(...p)}i.info(`Successfully extracted ${s} runners`)}function ao({directory:t,numberOfMachines:r,token:o,url:e,name:i,labels:s,runnerGroup:n}){M.info(`Starting ${r} runners...`);for(let a=1;a<=Number(r);a++){let c=`${ln()}/src/runner-worker.ts`,l=`${i}-${a}`,f=new Worker(c,{name:l,ref:!0});f.addEventListener("open",()=>{M.info(`Worker ${l} started`),f.postMessage({index:a,labels:s,machineCwd:`${t}/machines/${a}`,name:i,runnerGroup:n,token:o,url:e})}),f.addEventListener("message",({data:m})=>{let p=m.type==="error"?"error":"info";M[p](`${l}: ${m.message}`)}),f.addEventListener("error",(m)=>{M.error(`Worker ${l} error:`,m)}),f.addEventListener("exit",(m)=>{M.info(`Worker ${l} exited with code ${m}`)}),Sn.add(f)}M.info("All workers started. Press Ctrl+C to stop all machines")}var zt="0.3.10";var Kt={create:`
|
|
44
44
|
hydra create - Create and register new GitHub runner machines
|
|
45
45
|
|
|
46
46
|
USAGE
|
package/package.json
CHANGED