@skyramp/testbot-core 0.12.0-v3.18 → 0.12.0-v3.19

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.
Files changed (2) hide show
  1. package/dist/main.js +1 -1
  2. package/package.json +1 -1
package/dist/main.js CHANGED
@@ -709,7 +709,7 @@ ${e?.summary??"run finished"} \u2014 no report was produced.
709
709
  ### Stage breakdown
710
710
 
711
711
  ${wtt(s,{order:t.stages.map(o=>o.name),wallMinutes:VH(s)})}
712
- `:"";await YI.addRaw(n+i).write()}catch(r){J(`could not write the run summary: ${r instanceof Error?r.message:String(r)}`)}}var FS=H(require("fs")),LS=H(require("path"));We();var Ttt=require("child_process"),$c=H(require("fs")),OS=H(require("path"));var ztr=10*6e4;function Ptt(t,e){return t.getTime()-e.getTime()-ztr}var Btt="This attempt revoked its scoped GitHub credentials; it mints no more.",Gtr=5e3,jtr=10*6e4,ktt=3e4,Vtr=3,Wtr=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;function _tt(t){return(t?.GITHUB_API_URL||process.env.GITHUB_API_URL||"https://api.github.com").replace(/\/+$/,"")}var ug=class extends Error{constructor(r,n){super(r);this.status=n}status};function Dtt(t){let e;try{e=new URL(t)}catch{throw new Error(`skyrampAppOrigin "${t}" is not a URL`)}if(e.protocol!=="https:"||e.origin!==t.replace(/\/+$/,""))throw new Error(`skyrampAppOrigin must be an https origin, got "${t}"`);return e.origin}function Ytr(t,e){if(!Wtr.test(t))throw new Error(`The ${e} "${t}" is not a uuid`);return t}async function Qtt(t,e,r,n){let s=Dtt(t.origin),i;for(let o=1;o<=Vtr;o+=1){o>1&&await new Promise(a=>setTimeout(a,(t.retryDelayMs??2e3)*(o-1)));try{let a=await t.idToken(s),c=await(t.fetch??fetch)(new URL(r,s).toString(),{method:e,headers:{Authorization:`Bearer ${a}`,"Content-Type":"application/json",Accept:"application/json"},body:e==="GET"?void 0:JSON.stringify(n),redirect:"error",signal:AbortSignal.timeout(ktt)}),l=await c.json().catch(()=>({}));if(c.ok)return l.data;let u=`The Skyramp app refused ${e} ${r}: ${c.status} ${l.error?.code??"no error code"}`;if(c.status<500&&c.status!==429)throw new ug(u,c.status);i=new Error(u)}catch(a){if(a instanceof ug)throw a;i=a}}throw i}var eue=class{constructor(e,r,n,s,i,o){this.attemptId=e;this.attemptNumber=r;this.deadlineAt=n;this.manifest=s;this.deps=i;this.license=o}attemptId;attemptNumber;deadlineAt;manifest;deps;license;budgetMs(e){return Ptt(this.deadlineAt,e)}send(e,r,n){return Qtt(this.deps,e,r,n)}issuedTokens=[];closure;credentials;revocation;async token(){if(this.revocation)throw new Error(Btt);let e=(this.deps.now??(()=>new Date))().getTime(),r=this.credentials,n=r&&await r.catch(()=>{});if(this.credentials!==r)return(await this.credentials).token;if(n&&n.expiresAt-e>=jtr)return n.token;if(this.revocation)throw new Error(Btt);let s=this.send("POST",`/api/v1/runner/attempts/${this.attemptId}/credentials`,{schemaVersion:1}).then(i=>{this.issuedTokens.push(i.token),this.deps.mask?.(i.token);let o=n?"Renewed":"Received";return this.deps.log?.(`${o} scoped GitHub credentials; they expire at ${i.expiresAt}.`),{token:i.token,expiresAt:new Date(i.expiresAt).getTime()}});return this.credentials=s,(await s).token}revokeTokens(e={}){return this.revocation??=this.revokeIssued(e),this.revocation}async revokeIssued(e){let r=[...this.issuedTokens],n=Promise.all(r.map(o=>this.revokeOne(o,e)));await this.credentials?.catch(()=>{});let s=this.issuedTokens.filter(o=>!r.includes(o));return[...await n,...await Promise.all(s.map(o=>this.revokeOne(o,e)))].filter(o=>o!==void 0)}async revokeOne(e,r){try{let n=await(r.fetch??this.deps.fetch??fetch)(`${_tt(r.env)}/installation/token`,{method:"DELETE",headers:{Authorization:`Bearer ${e}`,Accept:"application/vnd.github+json"},redirect:"error",signal:AbortSignal.timeout(Gtr)});if(!n.ok&&n.status!==401)throw new Error(`GitHub answered ${n.status}`);return}catch(n){return`token ${this.issuedTokens.indexOf(e)+1} of ${this.issuedTokens.length}: ${n instanceof Error?n.message:String(n)}`}}};async function Mtt(t,e,r){Ytr(t,"run id"),Dtt(r.origin);let n=NZe(t,e),s=BZe(await Qtt(r,"POST",`/api/v1/runner/runs/${t}/attempts`,n));if(s.manifest.runId!==t)throw new Error(`The app answered the handshake of run ${t} with the manifest of another run (${s.manifest.runId})`);s.runToken&&r.mask?.(s.runToken);let i=s.runToken?{entitlements:s.entitlements??[],runToken:s.runToken}:void 0;return new eue(s.attempt.id,s.attempt.number,new Date(s.attempt.deadlineAt),s.manifest,r,i)}var Jtr=6e4;async function Ott(t){return IZe.parse(await t.send("POST",`/api/v1/runner/attempts/${t.attemptId}/heartbeat`,{schemaVersion:1}))}async function Ltt(t,e=()=>new Date){if(t.closure)return t.closure;let r=await Ott(t).then(n=>ole(n,e())).catch(()=>null);return r&&(t.closure=r),r}function Ftt(t,e,r={}){let n=!1,s,i=async()=>{try{let a=ole(await Ott(t),(r.now??(()=>new Date))());a&&!n&&(n=!0,t.closure=a,e(a))}catch(a){a instanceof ug&&(n=!0)}n||o(r.intervalMs??Jtr)},o=a=>{s=setTimeout(()=>{i()},a),s.unref?.()};return o(0),{stop(){n=!0,clearTimeout(s)}}}var Ktr=10*6e4,Xtr=(t,e,r)=>(0,Ttt.execFileSync)("git",e,{cwd:t,encoding:"utf-8",stdio:["ignore","pipe","pipe"],env:r??process.env,timeout:Ktr}).trim(),Utt=/^(?!\.\.?$)[A-Za-z0-9._-]{1,100}$/,Ztr=["TRIGGER","AUTHORITATIVE_RELATED_CHANGE"];function err(t,e=process.env){return`${(e.GITHUB_SERVER_URL||"https://github.com").replace(/\/+$/,"")}/${t.namespace}/${t.name}.git`}async function qtt(t,e){let r=e.git??Xtr,n=e.cloneUrl??(o=>err(o,e.env)),{sources:s}=t.manifest;for(let o of s){if(Ztr.includes(o.reason)&&!o.change?.base)throw new Error(`${o.repository.namespace}/${o.repository.name} was selected as ${o.reason}, but its change names no base revision to compare it with`);for(let a of[o.repository.namespace,o.repository.name])if(!Utt.test(a))throw new Error(`"${a}" is not a repository name`)}if($c.existsSync(e.workspace)&&$c.lstatSync(e.workspace).isSymbolicLink())throw new Error(`The sources workspace ${e.workspace} is a symlink; refusing to clear it`);$c.rmSync(e.workspace,{recursive:!0,force:!0});let i=[];for(let o of s){let{namespace:a,name:c}=o.repository,l=OS.join(e.workspace,a,c);$c.mkdirSync(l,{recursive:!0});let u={...process.env,GIT_TERMINAL_PROMPT:"0",...Jae(await t.token())};r(l,["init","-q"]),r(l,["remote","add","origin",n(o.repository)]);let d=o.change?.base;r(l,["fetch","-q","--no-tags","origin",o.revision,...d?[d.revision]:[]],u);let p=d??(o.ref?{ref:o.ref,revision:o.revision}:null);p&&(r(l,["update-ref",`refs/remotes/origin/${p.ref}`,p.revision]),r(l,["symbolic-ref","refs/remotes/origin/HEAD",`refs/remotes/origin/${p.ref}`])),o.ref?r(l,["checkout","-q","-B",o.ref,o.revision]):r(l,["checkout","-q","--detach",o.revision]),i.push({repository:o.repository,revision:r(l,["rev-parse","HEAD"]),path:l,slug:`${a}/${c}`})}return i}async function $tt(t,e={}){let r=_tt(e.env),n=[];for(let s of t.manifest.sources){if(s.change?.kind!=="pull_request")continue;let{namespace:i,name:o}=s.repository,a=`${i}/${o}`,c=s.change.number;try{if(![i,o].every(d=>Utt.test(d)))throw new Error("the repository name is not valid");let l=await(e.fetch??fetch)(`${r}/repos/${i}/${o}/pulls/${c}`,{headers:{Authorization:`Bearer ${await t.token()}`,Accept:"application/vnd.github+json"},redirect:"error",signal:AbortSignal.timeout(ktt)});if(!l.ok)throw new Error(`GitHub answered ${l.status}`);let u=await l.json();n.push({repository:a,number:c,...typeof u.title=="string"?{title:u.title}:{},...typeof u.body=="string"?{body:u.body}:{}})}catch(l){let u=l instanceof Error?l.message:String(l);n.push({repository:a,number:c,note:`could not be read: ${u}`})}}return n}function Htt(t,e){let r=t.sources.find(s=>s.reason==="TRIGGER"&&s.change?.kind==="pull_request"),n=r&&e.find(s=>s.repository.providerRepositoryId===r.repository.providerRepositoryId);return n&&r.change?{repository:n.slug,path:n.path,number:Number(r.change.number)}:void 0}var trr=[".skyramp/sut/setup.sh",".skyramp/workspace.yml"];function ztt(t){let e=t.find(r=>trr.some(n=>$c.existsSync(OS.join(r.path,n))))??t[0];return{repositoryPath:e.path,repositorySlug:e.slug,relatedRepos:t.filter(r=>r!==e).map(r=>({slug:r.slug,path:r.path,writable:!0}))}}var rrr=R.object({tests:R.object({placements:R.array(R.unknown()).optional()}).passthrough().optional()}).passthrough(),nrr=R.object({repository:Dm.passthrough(),testType:R.nativeEnum(WC),directory:R.string()}).passthrough();function Gtt(t,e){let r=rrr.safeParse(t.settings.target??{}),n=[];for(let s of(r.success?r.data.tests?.placements:void 0)??[]){let i=nrr.safeParse(s);if(!i.success)continue;let{repository:o,testType:a}=i.data,c=e.find(d=>d.repository.provider===o.provider&&d.repository.providerRepositoryId===o.providerRepositoryId),l=i.data.directory.trim(),u=!l||OS.isAbsolute(l)||l.split(/[\\/]/).includes("..");!c||u||n.push({repository:c.slug,testType:a,directory:l})}return n}function jtt(t){let e=r=>t.testResults.filter(n=>n.status===r).length;return{passed:e("Pass"),failed:e("Fail"),skipped:e("Skipped")}}function srr(t){let{passed:e,failed:r,skipped:n}=jtt(t);return e+r===0?{sufficiency:"insufficient",rationale:n>0?`Every test was skipped (${n}), so nothing was validated.`:"No test ran, so nothing was validated.",gaps:["No test ran against the frozen sources."],acceptedEvidence:[],actionRequired:[]}:{sufficiency:"sufficient",rationale:`${e+r} tests ran against the frozen sources: ${e} passed, ${r} failed${n?`, ${n} skipped`:""}.`,gaps:[],acceptedEvidence:[],actionRequired:[]}}function irr(t,e){let r=t.testResults.map(c=>({result:c,repository:c.repository??e,path:c.fileName||c.testType})),n=new Map;for(let c of r){let l=`${c.repository}:${c.result.testType}:${c.path}`;n.set(l,(n.get(l)??0)+1)}let s=r.map(({result:c,repository:l,path:u})=>{let d=(n.get(`${l}:${c.testType}:${u}`)??0)>1;return c.resultId??gH(l,c.testType,u,d&&c.endpoint||void 0)}),i=new Map;for(let c of s)i.set(c,(i.get(c)??0)+1);let o=new Set(s.filter(c=>i.get(c)===1)),a=r.map(({result:c,repository:l},u)=>{let d=s[u],p=d;if(i.get(d)>1&&o.has(d))for(let m=2;o.has(p)||p===d;m+=1)p=d.includes("#")?`${d}.${m}`:`${d}#${m}`;return o.add(p),{...c,repository:l,resultId:p}});return{...t,testResults:a,validation:orr(t,t.validation??srr(t))}}function orr(t,e){let{passed:r,failed:n}=jtt(t);return e.sufficiency!=="sufficient"||r+n>0||e.acceptedEvidence.length>0?e:{...e,sufficiency:"insufficient",gaps:["No test ran and no existing evidence was accepted, so nothing was validated."]}}async function Vtt(t,e){let r=wE([...t.issuedTokens,...e.secrets??[]]),n=PZe({manifest:t.manifest,attemptNumber:t.attemptNumber,checkedOut:e.sources.map(s=>({repository:s.repository,revision:s.revision})),generatedTests:Yo(e.delivery,r),producer:e.producer,report:Yo(irr(e.report,e.workingSlug),r)});await t.send("PUT",`/api/v1/runner/attempts/${t.attemptId}/report`,n)}async function rue(t){let e=CZe.parse(await t.send("GET",`/api/v1/runner/attempts/${t.attemptId}/configuration`,void 0)),r=t.manifest.configuration,{configuration:n,capture:s}=e;if(n.captureId!==r.captureId||n.revision!==r.revision||n.hash!==r.hash||s.hash!==r.hash||vZe(s)!==r.hash)throw new Error("The app answered the configuration of another capture than the manifest names");return e}var tue=class extends Error{};async function Wtt(t){let{runtimePlan:e}=SZe.parse(await t.send("GET",`/api/v1/runner/attempts/${t.attemptId}/runtime-plan`,void 0));if(e&&RZe(e.files)!==e.contentHash)throw new tue(`Stored SUT recipe version ${e.version} does not match its hash`);return e}async function Ytt(t,e){await t.send("POST",`/api/v1/runner/attempts/${t.attemptId}/progress`,xZe.parse(e))}async function Jtt(t,e){let r=wE([...t.issuedTokens,...e.secrets??[]]),n=kZe(t.manifest,t.attemptNumber,e.sources.map(s=>({repository:s.repository,revision:s.revision})),Yo(e.recipe,r),e.producer);await t.send("PUT",`/api/v1/runner/attempts/${t.attemptId}/sut-recipe`,n)}function Ktt(t,e,r,n=e[0].slug){let s=OS.join(t,"deliver.json"),i=$c.existsSync(s)?JSON.parse($c.readFileSync(s,"utf-8")):void 0,o=p=>{let m=e.find(h=>h.slug.toLowerCase()===(p??n).toLowerCase());return m&&{provider:m.repository.provider,providerAccountId:m.repository.providerAccountId,providerRepositoryId:m.repository.providerRepositoryId}},a=i?.deliveries??(i?[i]:[]),c=[],l=[];for(let p of a){let m=o(p.repository),h=/\/pull\/([1-9][0-9]*)$/.exec(p.prUrl??"")?.[1];if(m&&h)c.push({repository:m,number:h});else if(m&&p.prFailure){let f=p.prDetail?`${p.prFailure}: ${p.prDetail}`:p.prFailure;l.push({repository:m,message:f,slug:p.repository??n})}else m&&p.committed&&l.push({repository:m,message:p.reason??"The tests were committed but no pull request was opened.",slug:p.repository??n})}let u=new Set([...c,...l].map(({repository:p})=>p.providerRepositoryId)),d=[...new Set(r.newTestsCreated.map(p=>(p.repository??n).toLowerCase()))];for(let p of d){let m=o(p);m&&!u.has(m.providerRepositoryId)&&l.push({repository:m,message:"Generated tests were not proposed in a pull request.",slug:p})}return c.length&&l.length?{status:"partial",pullRequests:c,failures:l.map(({repository:p,message:m})=>({repository:p,message:m}))}:c.length?{status:"delivered",pullRequests:c}:l.length?{status:"failed",message:i?.deliveries?l.map(p=>`${p.slug}: ${p.message}`).join("; "):l[0].message}:r.newTestsCreated.length>0?{status:"failed",message:"Generated tests were not proposed in a pull request."}:{status:"none"}}function Xtt(t,e){return t.credentials.filter(r=>r.required&&!e[r.name]).map(r=>r.name)}var xi=H(require("fs")),Jd=H(require("path"));var arr=["/.skyramp/sut/","/.skyramp/workspace.yml"];function nue(t){xi.rmSync(t,{recursive:!0,force:!0})}function Ztt(t,e){for(let s of e)if(!_E(s.path))throw new Error(`Not a recipe file path: ${s.path}`);let r=Jd.join(t,".skyramp"),n=xi.lstatSync(r,{throwIfNoEntry:!1});n&&!n.isDirectory()&&nue(r),xi.mkdirSync(r,{recursive:!0}),nue(Jd.join(r,"sut")),nue(Jd.join(r,"workspace.yml"));for(let s of e){let i=Jd.join(t,s.path);xi.mkdirSync(Jd.dirname(i),{recursive:!0}),xi.writeFileSync(i,s.content,{flag:"wx",mode:s.path.endsWith(".sh")?493:420})}sue(t)}function sue(t){let e=Jd.join(t,".git");if(!xi.lstatSync(e,{throwIfNoEntry:!1})?.isDirectory())return;let r=Jd.join(e,"info","exclude");xi.mkdirSync(Jd.dirname(r),{recursive:!0});let n=xi.existsSync(r)?xi.readFileSync(r,"utf8"):"",s=n.split(`
712
+ `:"";await YI.addRaw(n+i).write()}catch(r){J(`could not write the run summary: ${r instanceof Error?r.message:String(r)}`)}}var FS=H(require("fs")),LS=H(require("path"));We();var Ttt=require("child_process"),$c=H(require("fs")),OS=H(require("path"));var ztr=10*6e4;function Ptt(t,e){return t.getTime()-e.getTime()-ztr}var Btt="This attempt revoked its scoped GitHub credentials; it mints no more.",Gtr=5e3,jtr=10*6e4,ktt=3e4,Vtr=3,Wtr=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;function _tt(t){return(t?.GITHUB_API_URL||process.env.GITHUB_API_URL||"https://api.github.com").replace(/\/+$/,"")}var ug=class extends Error{constructor(r,n){super(r);this.status=n}status};function Dtt(t){let e;try{e=new URL(t)}catch{throw new Error(`skyrampAppOrigin "${t}" is not a URL`)}if(e.protocol!=="https:"||e.origin!==t.replace(/\/+$/,""))throw new Error(`skyrampAppOrigin must be an https origin, got "${t}"`);return e.origin}function Ytr(t,e){if(!Wtr.test(t))throw new Error(`The ${e} "${t}" is not a uuid`);return t}async function Qtt(t,e,r,n){let s=Dtt(t.origin),i;for(let o=1;o<=Vtr;o+=1){o>1&&await new Promise(a=>setTimeout(a,(t.retryDelayMs??2e3)*(o-1)));try{let a=await t.idToken(s),c=await(t.fetch??fetch)(new URL(r,s).toString(),{method:e,headers:{Authorization:`Bearer ${a}`,"Content-Type":"application/json",Accept:"application/json"},body:e==="GET"?void 0:JSON.stringify(n),redirect:"error",signal:AbortSignal.timeout(ktt)}),l=await c.json().catch(()=>({}));if(c.ok)return l.data;let u=`The Skyramp app refused ${e} ${r}: ${c.status} ${l.error?.code??"no error code"}`;if(c.status<500&&c.status!==429)throw new ug(u,c.status);i=new Error(u)}catch(a){if(a instanceof ug)throw a;i=a}}throw i}var eue=class{constructor(e,r,n,s,i,o){this.attemptId=e;this.attemptNumber=r;this.deadlineAt=n;this.manifest=s;this.deps=i;this.license=o}attemptId;attemptNumber;deadlineAt;manifest;deps;license;budgetMs(e){return Ptt(this.deadlineAt,e)}send(e,r,n){return Qtt(this.deps,e,r,n)}issuedTokens=[];closure;credentials;revocation;async token(){if(this.revocation)throw new Error(Btt);let e=(this.deps.now??(()=>new Date))().getTime(),r=this.credentials,n=r&&await r.catch(()=>{});if(this.credentials!==r)return(await this.credentials).token;if(n&&n.expiresAt-e>=jtr)return n.token;if(this.revocation)throw new Error(Btt);let s=this.send("POST",`/api/v1/runner/attempts/${this.attemptId}/credentials`,{schemaVersion:1}).then(i=>{this.issuedTokens.push(i.token),this.deps.mask?.(i.token);let o=n?"Renewed":"Received";return this.deps.log?.(`${o} scoped GitHub credentials; they expire at ${i.expiresAt}.`),{token:i.token,expiresAt:new Date(i.expiresAt).getTime()}});return this.credentials=s,(await s).token}githubFetch(e){return e.fetch??this.deps.fetch??fetch}revokeTokens(e={}){return this.revocation??=this.revokeIssued(e),this.revocation}async revokeIssued(e){let r=[...this.issuedTokens],n=Promise.all(r.map(o=>this.revokeOne(o,e)));await this.credentials?.catch(()=>{});let s=this.issuedTokens.filter(o=>!r.includes(o));return[...await n,...await Promise.all(s.map(o=>this.revokeOne(o,e)))].filter(o=>o!==void 0)}async revokeOne(e,r){try{let n=await this.githubFetch(r)(`${_tt(r.env)}/installation/token`,{method:"DELETE",headers:{Authorization:`Bearer ${e}`,Accept:"application/vnd.github+json"},redirect:"error",signal:AbortSignal.timeout(Gtr)});if(!n.ok&&n.status!==401)throw new Error(`GitHub answered ${n.status}`);return}catch(n){return`token ${this.issuedTokens.indexOf(e)+1} of ${this.issuedTokens.length}: ${n instanceof Error?n.message:String(n)}`}}};async function Mtt(t,e,r){Ytr(t,"run id"),Dtt(r.origin);let n=NZe(t,e),s=BZe(await Qtt(r,"POST",`/api/v1/runner/runs/${t}/attempts`,n));if(s.manifest.runId!==t)throw new Error(`The app answered the handshake of run ${t} with the manifest of another run (${s.manifest.runId})`);s.runToken&&r.mask?.(s.runToken);let i=s.runToken?{entitlements:s.entitlements??[],runToken:s.runToken}:void 0;return new eue(s.attempt.id,s.attempt.number,new Date(s.attempt.deadlineAt),s.manifest,r,i)}var Jtr=6e4;async function Ott(t){return IZe.parse(await t.send("POST",`/api/v1/runner/attempts/${t.attemptId}/heartbeat`,{schemaVersion:1}))}async function Ltt(t,e=()=>new Date){if(t.closure)return t.closure;let r=await Ott(t).then(n=>ole(n,e())).catch(()=>null);return r&&(t.closure=r),r}function Ftt(t,e,r={}){let n=!1,s,i=async()=>{try{let a=ole(await Ott(t),(r.now??(()=>new Date))());a&&!n&&(n=!0,t.closure=a,e(a))}catch(a){a instanceof ug&&(n=!0)}n||o(r.intervalMs??Jtr)},o=a=>{s=setTimeout(()=>{i()},a),s.unref?.()};return o(0),{stop(){n=!0,clearTimeout(s)}}}var Ktr=10*6e4,Xtr=(t,e,r)=>(0,Ttt.execFileSync)("git",e,{cwd:t,encoding:"utf-8",stdio:["ignore","pipe","pipe"],env:r??process.env,timeout:Ktr}).trim(),Utt=/^(?!\.\.?$)[A-Za-z0-9._-]{1,100}$/,Ztr=["TRIGGER","AUTHORITATIVE_RELATED_CHANGE"];function err(t,e=process.env){return`${(e.GITHUB_SERVER_URL||"https://github.com").replace(/\/+$/,"")}/${t.namespace}/${t.name}.git`}async function qtt(t,e){let r=e.git??Xtr,n=e.cloneUrl??(o=>err(o,e.env)),{sources:s}=t.manifest;for(let o of s){if(Ztr.includes(o.reason)&&!o.change?.base)throw new Error(`${o.repository.namespace}/${o.repository.name} was selected as ${o.reason}, but its change names no base revision to compare it with`);for(let a of[o.repository.namespace,o.repository.name])if(!Utt.test(a))throw new Error(`"${a}" is not a repository name`)}if($c.existsSync(e.workspace)&&$c.lstatSync(e.workspace).isSymbolicLink())throw new Error(`The sources workspace ${e.workspace} is a symlink; refusing to clear it`);$c.rmSync(e.workspace,{recursive:!0,force:!0});let i=[];for(let o of s){let{namespace:a,name:c}=o.repository,l=OS.join(e.workspace,a,c);$c.mkdirSync(l,{recursive:!0});let u={...process.env,GIT_TERMINAL_PROMPT:"0",...Jae(await t.token())};r(l,["init","-q"]),r(l,["remote","add","origin",n(o.repository)]);let d=o.change?.base;r(l,["fetch","-q","--no-tags","origin",o.revision,...d?[d.revision]:[]],u);let p=d??(o.ref?{ref:o.ref,revision:o.revision}:null);p&&(r(l,["update-ref",`refs/remotes/origin/${p.ref}`,p.revision]),r(l,["symbolic-ref","refs/remotes/origin/HEAD",`refs/remotes/origin/${p.ref}`])),o.ref?r(l,["checkout","-q","-B",o.ref,o.revision]):r(l,["checkout","-q","--detach",o.revision]),i.push({repository:o.repository,revision:r(l,["rev-parse","HEAD"]),path:l,slug:`${a}/${c}`})}return i}async function $tt(t,e={}){let r=_tt(e.env),n=[];for(let s of t.manifest.sources){if(s.change?.kind!=="pull_request")continue;let{namespace:i,name:o}=s.repository,a=`${i}/${o}`,c=s.change.number;try{if(![i,o].every(d=>Utt.test(d)))throw new Error("the repository name is not valid");let l=await t.githubFetch(e)(`${r}/repos/${i}/${o}/pulls/${c}`,{headers:{Authorization:`Bearer ${await t.token()}`,Accept:"application/vnd.github+json"},redirect:"error",signal:AbortSignal.timeout(ktt)});if(!l.ok)throw new Error(`GitHub answered ${l.status}`);let u=await l.json();n.push({repository:a,number:c,...typeof u.title=="string"?{title:u.title}:{},...typeof u.body=="string"?{body:u.body}:{}})}catch(l){let u=l instanceof Error?l.message:String(l);n.push({repository:a,number:c,note:`could not be read: ${u}`})}}return n}function Htt(t,e){let r=t.sources.find(s=>s.reason==="TRIGGER"&&s.change?.kind==="pull_request"),n=r&&e.find(s=>s.repository.providerRepositoryId===r.repository.providerRepositoryId);return n&&r.change?{repository:n.slug,path:n.path,number:Number(r.change.number)}:void 0}var trr=[".skyramp/sut/setup.sh",".skyramp/workspace.yml"];function ztt(t){let e=t.find(r=>trr.some(n=>$c.existsSync(OS.join(r.path,n))))??t[0];return{repositoryPath:e.path,repositorySlug:e.slug,relatedRepos:t.filter(r=>r!==e).map(r=>({slug:r.slug,path:r.path,writable:!0}))}}var rrr=R.object({tests:R.object({placements:R.array(R.unknown()).optional()}).passthrough().optional()}).passthrough(),nrr=R.object({repository:Dm.passthrough(),testType:R.nativeEnum(WC),directory:R.string()}).passthrough();function Gtt(t,e){let r=rrr.safeParse(t.settings.target??{}),n=[];for(let s of(r.success?r.data.tests?.placements:void 0)??[]){let i=nrr.safeParse(s);if(!i.success)continue;let{repository:o,testType:a}=i.data,c=e.find(d=>d.repository.provider===o.provider&&d.repository.providerRepositoryId===o.providerRepositoryId),l=i.data.directory.trim(),u=!l||OS.isAbsolute(l)||l.split(/[\\/]/).includes("..");!c||u||n.push({repository:c.slug,testType:a,directory:l})}return n}function jtt(t){let e=r=>t.testResults.filter(n=>n.status===r).length;return{passed:e("Pass"),failed:e("Fail"),skipped:e("Skipped")}}function srr(t){let{passed:e,failed:r,skipped:n}=jtt(t);return e+r===0?{sufficiency:"insufficient",rationale:n>0?`Every test was skipped (${n}), so nothing was validated.`:"No test ran, so nothing was validated.",gaps:["No test ran against the frozen sources."],acceptedEvidence:[],actionRequired:[]}:{sufficiency:"sufficient",rationale:`${e+r} tests ran against the frozen sources: ${e} passed, ${r} failed${n?`, ${n} skipped`:""}.`,gaps:[],acceptedEvidence:[],actionRequired:[]}}function irr(t,e){let r=t.testResults.map(c=>({result:c,repository:c.repository??e,path:c.fileName||c.testType})),n=new Map;for(let c of r){let l=`${c.repository}:${c.result.testType}:${c.path}`;n.set(l,(n.get(l)??0)+1)}let s=r.map(({result:c,repository:l,path:u})=>{let d=(n.get(`${l}:${c.testType}:${u}`)??0)>1;return c.resultId??gH(l,c.testType,u,d&&c.endpoint||void 0)}),i=new Map;for(let c of s)i.set(c,(i.get(c)??0)+1);let o=new Set(s.filter(c=>i.get(c)===1)),a=r.map(({result:c,repository:l},u)=>{let d=s[u],p=d;if(i.get(d)>1&&o.has(d))for(let m=2;o.has(p)||p===d;m+=1)p=d.includes("#")?`${d}.${m}`:`${d}#${m}`;return o.add(p),{...c,repository:l,resultId:p}});return{...t,testResults:a,validation:orr(t,t.validation??srr(t))}}function orr(t,e){let{passed:r,failed:n}=jtt(t);return e.sufficiency!=="sufficient"||r+n>0||e.acceptedEvidence.length>0?e:{...e,sufficiency:"insufficient",gaps:["No test ran and no existing evidence was accepted, so nothing was validated."]}}async function Vtt(t,e){let r=wE([...t.issuedTokens,...e.secrets??[]]),n=PZe({manifest:t.manifest,attemptNumber:t.attemptNumber,checkedOut:e.sources.map(s=>({repository:s.repository,revision:s.revision})),generatedTests:Yo(e.delivery,r),producer:e.producer,report:Yo(irr(e.report,e.workingSlug),r)});await t.send("PUT",`/api/v1/runner/attempts/${t.attemptId}/report`,n)}async function rue(t){let e=CZe.parse(await t.send("GET",`/api/v1/runner/attempts/${t.attemptId}/configuration`,void 0)),r=t.manifest.configuration,{configuration:n,capture:s}=e;if(n.captureId!==r.captureId||n.revision!==r.revision||n.hash!==r.hash||s.hash!==r.hash||vZe(s)!==r.hash)throw new Error("The app answered the configuration of another capture than the manifest names");return e}var tue=class extends Error{};async function Wtt(t){let{runtimePlan:e}=SZe.parse(await t.send("GET",`/api/v1/runner/attempts/${t.attemptId}/runtime-plan`,void 0));if(e&&RZe(e.files)!==e.contentHash)throw new tue(`Stored SUT recipe version ${e.version} does not match its hash`);return e}async function Ytt(t,e){await t.send("POST",`/api/v1/runner/attempts/${t.attemptId}/progress`,xZe.parse(e))}async function Jtt(t,e){let r=wE([...t.issuedTokens,...e.secrets??[]]),n=kZe(t.manifest,t.attemptNumber,e.sources.map(s=>({repository:s.repository,revision:s.revision})),Yo(e.recipe,r),e.producer);await t.send("PUT",`/api/v1/runner/attempts/${t.attemptId}/sut-recipe`,n)}function Ktt(t,e,r,n=e[0].slug){let s=OS.join(t,"deliver.json"),i=$c.existsSync(s)?JSON.parse($c.readFileSync(s,"utf-8")):void 0,o=p=>{let m=e.find(h=>h.slug.toLowerCase()===(p??n).toLowerCase());return m&&{provider:m.repository.provider,providerAccountId:m.repository.providerAccountId,providerRepositoryId:m.repository.providerRepositoryId}},a=i?.deliveries??(i?[i]:[]),c=[],l=[];for(let p of a){let m=o(p.repository),h=/\/pull\/([1-9][0-9]*)$/.exec(p.prUrl??"")?.[1];if(m&&h)c.push({repository:m,number:h});else if(m&&p.prFailure){let f=p.prDetail?`${p.prFailure}: ${p.prDetail}`:p.prFailure;l.push({repository:m,message:f,slug:p.repository??n})}else m&&p.committed&&l.push({repository:m,message:p.reason??"The tests were committed but no pull request was opened.",slug:p.repository??n})}let u=new Set([...c,...l].map(({repository:p})=>p.providerRepositoryId)),d=[...new Set(r.newTestsCreated.map(p=>(p.repository??n).toLowerCase()))];for(let p of d){let m=o(p);m&&!u.has(m.providerRepositoryId)&&l.push({repository:m,message:"Generated tests were not proposed in a pull request.",slug:p})}return c.length&&l.length?{status:"partial",pullRequests:c,failures:l.map(({repository:p,message:m})=>({repository:p,message:m}))}:c.length?{status:"delivered",pullRequests:c}:l.length?{status:"failed",message:i?.deliveries?l.map(p=>`${p.slug}: ${p.message}`).join("; "):l[0].message}:r.newTestsCreated.length>0?{status:"failed",message:"Generated tests were not proposed in a pull request."}:{status:"none"}}function Xtt(t,e){return t.credentials.filter(r=>r.required&&!e[r.name]).map(r=>r.name)}var xi=H(require("fs")),Jd=H(require("path"));var arr=["/.skyramp/sut/","/.skyramp/workspace.yml"];function nue(t){xi.rmSync(t,{recursive:!0,force:!0})}function Ztt(t,e){for(let s of e)if(!_E(s.path))throw new Error(`Not a recipe file path: ${s.path}`);let r=Jd.join(t,".skyramp"),n=xi.lstatSync(r,{throwIfNoEntry:!1});n&&!n.isDirectory()&&nue(r),xi.mkdirSync(r,{recursive:!0}),nue(Jd.join(r,"sut")),nue(Jd.join(r,"workspace.yml"));for(let s of e){let i=Jd.join(t,s.path);xi.mkdirSync(Jd.dirname(i),{recursive:!0}),xi.writeFileSync(i,s.content,{flag:"wx",mode:s.path.endsWith(".sh")?493:420})}sue(t)}function sue(t){let e=Jd.join(t,".git");if(!xi.lstatSync(e,{throwIfNoEntry:!1})?.isDirectory())return;let r=Jd.join(e,"info","exclude");xi.mkdirSync(Jd.dirname(r),{recursive:!0});let n=xi.existsSync(r)?xi.readFileSync(r,"utf8"):"",s=n.split(`
713
713
  `),i=arr.filter(o=>!s.includes(o));i.length>0&&xi.appendFileSync(r,`${n&&!n.endsWith(`
714
714
  `)?`
715
715
  `:""}${i.join(`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@skyramp/testbot-core",
3
- "version": "0.12.0-v3.18",
3
+ "version": "0.12.0-v3.19",
4
4
  "description": "Skyramp Testbot engine: AI-powered deterministic test generation, execution and maintenance",
5
5
  "main": "dist/main.js",
6
6
  "//files": "An explicit allowlist, never a glob. `dist/` is gitignored, so without this key npm's .gitignore fallback would publish a package with no engine in it; with a glob it would publish the sourcemaps, whose sourcesContent embeds every src/*.ts verbatim. eval-entry.js is a development entry and must not ship.",