@intentius/terragucci 0.4.32 → 0.4.33
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/terragucci.mjs +2 -2
- package/dist/terragucci.mjs.map +1 -1
- package/package.json +1 -1
package/dist/terragucci.mjs
CHANGED
|
@@ -438,7 +438,7 @@ ${Yse(e)}
|
|
|
438
438
|
${JSON.stringify(e).replace(/<\//g,"<\\/").replace(/<!--/g,"<\\u0021--")}
|
|
439
439
|
</script>
|
|
440
440
|
</main></body></html>
|
|
441
|
-
`}async function QG(e,t,n,r,o=new Date().toISOString(),s){let i=Yg(n.project,n.commit,t),{value:a}=await aa(e,`${i}/run.json`,c=>Jse(c,n,r,o),`wave ${r.number}'s row`,s);return await e.put(`${i}/run.html`,Qse(a),"text/html; charset=utf-8"),`${i}/run.html`}import{spawnSync as Jfe}from"node:child_process";import{spawn as Qpe,spawnSync as fO}from"node:child_process";import{chmodSync as BB,existsSync as dp,mkdirSync as pp,mkdtempSync as Vm,readdirSync as WB,readFileSync as Ic,realpathSync as HB,rmSync as Km,statSync as efe,writeFileSync as Ra}from"node:fs";import{tmpdir as gO}from"node:os";import{basename as mO,join as Ne,relative as qB,resolve as Rs,sep as JB}from"node:path";var Pr="0.4.
|
|
441
|
+
`}async function QG(e,t,n,r,o=new Date().toISOString(),s){let i=Yg(n.project,n.commit,t),{value:a}=await aa(e,`${i}/run.json`,c=>Jse(c,n,r,o),`wave ${r.number}'s row`,s);return await e.put(`${i}/run.html`,Qse(a),"text/html; charset=utf-8"),`${i}/run.html`}import{spawnSync as Jfe}from"node:child_process";import{spawn as Qpe,spawnSync as fO}from"node:child_process";import{chmodSync as BB,existsSync as dp,mkdirSync as pp,mkdtempSync as Vm,readdirSync as WB,readFileSync as Ic,realpathSync as HB,rmSync as Km,statSync as efe,writeFileSync as Ra}from"node:fs";import{tmpdir as gO}from"node:os";import{basename as mO,join as Ne,relative as qB,resolve as Rs,sep as JB}from"node:path";var Pr="0.4.33";import{existsSync as tie,readFileSync as nie}from"node:fs";import{join as rie}from"node:path";function eF(e,t,n,r){return e?{mode:e,source:`approval: ${e} in the config ${r}`,...e!=="sealed"&&t>0?{note:`chant.workspace.json ${r} still lists gates under identity.gates, and terragucci approve refuses an unsigned approval of a gate it lists; run terragucci init to drop them`}:{}}:t>0?{mode:"sealed",source:`identity.gates in chant.workspace.json ${r}, with no approval key`,note:"set approval: sealed in terragucci.yml to keep sealed approvals, or approval: ledger and run terragucci init to drop the gates"}:n?{mode:n,source:"the pipeline's --approval"}:{mode:"ledger",source:"the default"}}function Xg(e){if(e===void 0)return 0;try{return Object.keys(JSON.parse(e).identity?.gates??{}).length}catch{return 0}}async function Yr(e,t={}){let n=t.at??vs(e),r=JM(e,n),o=await ln(e,n,t.config?{config:t.config}:{});if("error"in o)throw new _(`approval is read from the config at base, and it could not be read (${o.error}), so the gate cannot be decided`);let s=!o.config.projects,i=s?Bs(o.config.approval):void 0,a=s&&VC(o.config.approval),c=s&&typeof o.config.agent?.token_env=="string"?o.config.agent.token_env:void 0,u=s&&typeof o.config.agent?.login=="string"&&o.config.agent.login?o.config.agent.login:void 0,l=s&&Array.isArray(o.config.policy?.override)?o.config.policy.override.filter(p=>typeof p=="string"):[],d=s&&typeof o.config.apply?.point=="string"?o.config.apply.point:void 0;return{...eF(i,r.gates.size,t.flag,"at base"),...r,base:n,overriders:l,...d?{point:d}:{},...a?{requireSigned:a}:{},...c?{agentTokenEnv:c}:{},...u?{agentLogin:u}:{}}}function Yu(e,t){if(t.projects)return;let n=rie(e,"chant.workspace.json");return eF(Bs(t.approval),Xg(tie(n)?nie(n,"utf-8"):void 0),void 0,"here")}var oie=(e,t)=>`-estate-outputs-from=${e}=${t}`;function tF(e,t,n,r,o){let s=[],i=[],a=new Map;for(let c of t){let u=n.get(c.estate);if(!u||u===e)continue;let l=f=>{i.push({upstream:u,data:c.name,outputs:"applied",why:f})};if(!r(u)){l("this change does not reach it, so its records stand");continue}let d=o(u);if(!d){l("it did not plan in this run");continue}let p=Ho(d.plan);p?p.changed?(i.push({upstream:u,data:c.name,outputs:"planned"}),a.has(c.estate)||(a.set(c.estate,u),s.push(oie(c.estate,d.file)))):l("its plan changes no output, so its records stand"):l("its plan names no output changes")}return{args:s,reads:i,upstreamOf:a}}function nF(e){let t=e.replace(/^[ \t]*[│╷╵]/gm," ").replace(/\s+/g," "),n=[],r=/Estate "([^"]+)"'s planned output "([^"]+)" \(in .*?\) is (known only after|sensitive)/g;for(let o of t.matchAll(r)){let s={kind:o[3]==="sensitive"?"sensitive":"unknown",estate:o[1],output:o[2]};n.some(i=>i.kind===s.kind&&i.estate===s.estate&&i.output===s.output)||n.push(s)}return n}var rF=e=>"`"+e+"`";function oF(e,t,n){let r=[...new Set(e.map(s=>s.output))].sort(),o=[...new Set(e.map(s=>t.get(s.estate)??s.estate))].sort();return`reads ${r.map(rF).join(", ")} of ${o.join(", ")}, unknown until ${n.length?`wave ${n.join(", ")}`:"it"} applies`}function sF(e,t){let n=[...new Set(e.map(o=>o.output))].sort(),r=[...new Set(e.map(o=>t.get(o.estate)??o.estate))].sort();return`reads sensitive ${n.length===1?"output":"outputs"} ${n.map(rF).join(", ")} of ${r.join(", ")}, which choudoufu never hands to another estate`}import{existsSync as sie,readdirSync as iie,readFileSync as uF,rmSync as aie,statSync as cie,writeFileSync as rT}from"node:fs";import{join as lF}from"node:path";function oT(e){return JSON.stringify(e).replace(/\$\{/g,()=>"$${").replace(/%\{/g,()=>"%%{")}function Qg(e,t,n){if(t===!0)return n;if(Array.isArray(e)||Array.isArray(t)){let o=Array.isArray(e)?e:[],s=Array.isArray(t)?t:[],i=Math.max(o.length,s.length);return`[${Array.from({length:i},(a,c)=>Qg(o[c],s[c],n)).join(", ")}]`}let r=o=>!!o&&typeof o=="object";if(r(e)||r(t)){let o=r(e)?e:{},s=r(t)?t:{};return`{ ${[...new Set([...Object.keys(o),...Object.keys(s).filter(a=>hc(s[a]))])].sort().map(a=>`${oT(a)} = ${Qg(o[a],s[a],n)}`).join(", ")} }`}return e==null?"null":typeof e=="string"?oT(e):JSON.stringify(e)}var uie=e=>e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),em=(e,t="g")=>new RegExp(`\\bdata\\.terraform_remote_state\\.${uie(e)}(?![\\w-])`,t);function ov(e,t){let n=new RegExp(`${em(t,"").source}(\\.outputs(?:\\.([A-Za-z_][\\w-]*)|\\[\\s*"([^"]+)"\\s*\\])?)?`,"g"),r=new Set;for(let o of e)for(let s of o.matchAll(n)){let i=s[2]??s[3];if(!i)return;r.add(i)}return r}function tm(e){try{return iie(e).filter(t=>t.endsWith(".tf")||t.endsWith(".tofu")||t.endsWith(".tf.json")||t.endsWith(".tofu.json")).map(t=>lF(e,t)).filter(t=>cie(t).isFile())}catch{return[]}}function sT(e,t){return ov(tm(e).map(n=>uF(n,"utf-8")),t)}var iF="terragucci_linked.tf",aF="terragucci_unknown",cF=e=>`terragucci_linked_${e}`;function dF(e,t){let n=lF(e,iF);if(sie(n))throw new Error(`${iF} is already in the root`);let r=tm(e),o=new Map(r.map(l=>[l,uF(l,"utf-8")])),s=[...o.values()],i=[],a=!1,c=[` ${aF} = jsondecode(timestamp())`];for(let l of t){let d=Gb(l.upstream,l.name,l.outputs,ov(s,l.name));d.unknown&&(a=!0),i.push(d);let p=[...l.outputs].sort(([f],[g])=>f<g?-1:1).map(([f,g])=>{let m=Qg(g.value,g.unknown,`local.${aF}`);return`${oT(f)} = ${g.sensitive?`sensitive(${m})`:m}`});c.push(` ${cF(l.name)} = { outputs = { ${p.join(", ")} } }`)}let u=()=>{for(let[l,d]of o)rT(l,d);aie(n,{force:!0})};try{for(let[l,d]of o){let p=d;for(let f of t)p=p.replace(em(f.name),`local.${cF(f.name)}`);p!==d&&rT(l,p)}rT(n,`# Written by terragucci for a linked plan, and removed once it is made.
|
|
442
442
|
locals {
|
|
443
443
|
${c.join(`
|
|
444
444
|
`)}
|
|
@@ -468,7 +468,7 @@ ${n}`).digest("hex").slice(0,16)}`;if(kv("git",["-C",e,"fetch","-q","--no-tags",
|
|
|
468
468
|
`).join(`
|
|
469
469
|
`)}function WT(e,t){let[n,r]=t.split(".");if(!n||!r||!hce(e))return;let o=new RegExp(`^\\s*resource\\s+"${n}"\\s+"${r}"\\s*\\{`,"m");return yce(e).filter(s=>s.endsWith(".tf")).sort().find(s=>o.test(wce(bce(e,s),"utf-8")))}function hU(e,t){return`${e.replace(/\n*$/,`
|
|
470
470
|
`)}
|
|
471
|
-
${t}`}import{spawnSync as Oce}from"node:child_process";import{createHash as $ce}from"node:crypto";var HT={"ghcr.io/intentius/terragucci-tofu:0.4.32-tofu1.13.1":"sha256:71558f6e91a83406d0218ef95c8fc3aafc75ab57aee96150c7dae7a3bcf960ff","ghcr.io/intentius/terragucci-terraform:0.4.32-tf1.14.9":"sha256:e91c9419bd166d2832cd250d9f2121626421a829e4ed35f23bd659ae8147722d","ghcr.io/intentius/terragucci-terragrunt:0.4.32-tg1.1.6-tofu1.13.1":"sha256:a2e9153d5320711d65498bb3c2a2d32ad237c5d005195524f1e031a2c0490521","ghcr.io/intentius/terragucci-choudoufu:0.4.32-choudoufu0.28.0":"sha256:5790094d5380ff33f31753056c4f90c8d6c89ce142124cd6884a6ad9e124f437"};var yU="ghcr.io/intentius",ai={tofu:"1.13.1",terraform:"1.14.9",terragrunt:"1.1.6",choudoufu:"0.28.0"};function wU(e,t=Pr){return e==="terragrunt"?`${t}-tg${ai.terragrunt}-tofu${ai.tofu}`:`${t}-${e==="terraform"?"tf":e}${ai[e]}`}function JT(e,t=HT){let n=`${yU}/terragucci-${e}`,r=wU(e);return{repository:n,tag:r,digest:t[`${n}:${r}`]}}function bU(e=HT){let t=`${yU}/terragucci-terragrunt`,n=wU("terragrunt");return{repository:t,tag:n,digest:e[`${t}:${n}`]}}function KT(e){return e.digest?`${e.repository}:${e.tag}@${e.digest}`:`${e.repository}:${e.tag}`}var qT={laya:"0.3.28",torch:"2.14.0",pip:"26.2.1",checkpoint:{repo:"convaiinnovations/laya",name:"english",revision:"55cf4c4ebb4ebe31b2550e8bdf3bd21b99753851",weightsSha256:"891102d372688fc2a094dac56a384bc537b87c63f21f9f3dac0be2b7cbc8d86c"},port:8790},vU=`laya-${qT.laya}-${qT.checkpoint.name}@${qT.checkpoint.revision.slice(0,12)}`;var kce="https://api.typesafe.ai",_ce={noul:.8,choice:.7,score:.7},Sce={laya:!0,jev:!0,von:!1,decider:!1};function Rce(e,t){let n=r=>{throw new Error(`decide: question ${e} ${r}`)};if(/^[A-Za-z0-9_-]{1,64}$/.test(e)||n("needs a name of letters, digits, - and _"),t.instructions||n("has no instructions"),t.threshold!==void 0&&!(t.threshold>0&&t.threshold<=1)&&n("has a threshold outside (0, 1]"),t.type==="noul")(!t.criteria?.true||!t.criteria?.false)&&n("is a noul and needs criteria.true and criteria.false");else if(t.type==="choice"){let r=Object.keys(t.criteria??{}).length;(r<2||r>255)&&n(`is a choice with ${r} options; use 2 to 255`)}else if(t.type==="score"){let r=t.criteria?.length??0;(r<2||r>10)&&n(`is a score with ${r} levels; use 2 to 10`)}else n(`has type ${JSON.stringify(t.type)}; use noul, choice or score`)}function $U(e,t){return e.threshold??t?.thresholds?.[e.type]??_ce[e.type]}var Ev=e=>typeof e=="number"&&Number.isFinite(e)&&e>=0&&e<=1;function xce(e){let t=e.url??(e.backend==="jev"?kce:void 0);if(!t)return;let n=t.replace(/\/+$/,"");return n.endsWith("/v1/systemone")?n:`${n}/v1/systemone`}function Ece(e){return e.model??(e.backend==="laya"?vU:void 0)}function Tce(e,t,n,r){let o={question:e,type:t.type,threshold:r},s=d=>({...o,status:"unavailable",reason:d});if(n===null||typeof n!="object")return s("the response has no answer for it");let i=n;if(i.type!==t.type)return s(`the answer's type is ${JSON.stringify(i.type)}, not ${t.type}`);let a,c,u,l;if(t.type==="noul"){if(!Ev(i.noul))return s("the noul answer is not a probability");a=i.noul>=.5?"true":"false",c=Math.max(i.noul,1-i.noul),u={true:i.noul,false:1-i.noul}}else{let d=i.probabilities;if(d===null||typeof d!="object"||!Object.values(d).every(Ev))return s("the answer has no probabilities");if(u=d,t.type==="choice"){if(typeof i.choice!="string"||!(i.choice in t.criteria))return s(`the choice ${JSON.stringify(i.choice)} is not one of the options`);if(!Ev(u[i.choice]))return s("the chosen option has no probability");a=i.choice,c=u[i.choice]}else{let p=t.criteria.map((g,m)=>String(m));if(!p.every(g=>Ev(u[g])))return s("the score's probabilities do not cover every level");let f=p.reduce((g,m)=>u[m]>u[g]?m:g,p[0]);a=t.criteria[Number(f)],c=u[f],typeof i.score=="number"&&Number.isFinite(i.score)&&(l=i.score)}}return{...o,status:c>=r?"confident":"not-confident",answer:a,probability:c,probabilities:u,...l===void 0?{}:{score:l}}}async function Xd(e,t,n,r={}){for(let[v,$]of Object.entries(n))Rce(v,$);let o=`sha256:${$ce("sha256").update(JSON.stringify(t)).digest("hex")}`,s=(v,$)=>Object.fromEntries(Object.entries(n).map(([S,R])=>[S,{question:S,type:R.type,status:v,threshold:$U(R,e),reason:$}]));if(!e)return{stateDigest:o,decisions:s("off","decide is not configured")};let i=xce(e),a=Ece(e),c={backend:e.backend,url:i,model:a,stateDigest:o};if(!i)return{...c,decisions:s("unavailable","decide.url is not set")};let u={"content-type":"application/json",accept:"application/json"};if(e.token_env){let v=(r.env??process.env)[e.token_env];if(!v)return{...c,decisions:s("unavailable",`${e.token_env} is not set`)};u.authorization=`Bearer ${v}`}let l=Object.fromEntries(Object.entries(n).map(([v,$])=>[v,{type:$.type,instructions:$.instructions,criteria:$.criteria}])),d=JSON.stringify({...a?{model:a}:{},state:t,questions:l}),p=r.fetch??globalThis.fetch,f=Date.now(),g,m;try{let v=await p(i,{method:"POST",headers:u,body:d,signal:AbortSignal.timeout(r.timeoutMs??3e4)});if(g=v.status,m=await v.text(),!v.ok){let $=m.slice(0,300);try{let S=JSON.parse(m),R=S.detail??S.error;R!==void 0&&($=typeof R=="string"?R:JSON.stringify(R))}catch{}return{...c,ms:Date.now()-f,decisions:s("unavailable",`the service answered HTTP ${g}: ${$}`)}}}catch(v){return{...c,ms:Date.now()-f,decisions:s("unavailable",`the service did not answer: ${v.message}`)}}let h=Date.now()-f,y;try{y=JSON.parse(m)}catch{return{...c,ms:h,decisions:s("unavailable","the response is not JSON")}}if(y===null||typeof y!="object"||y.answers===null||typeof y.answers!="object")return{...c,ms:h,decisions:s("unavailable","the response has no answers")};let w=typeof y.model=="string"?y.model:void 0;if(Sce[e.backend]&&a&&w!==a)return{...c,model:w??a,ms:h,decisions:s("unavailable",`the service answered as ${w??"no model"}, and decide.model pins ${a}`)};let b=y.answers,k=Object.fromEntries(Object.entries(n).map(([v,$])=>[v,Tce(v,$,b[v],$U($,e))]));return{...c,model:w??a,ms:h,decisions:k}}function Qd(e){return e?.status==="confident"}function Sc(e,t){let n=t.model?`, ${t.model}`:"";if(e.status==="off")return`${e.question}: not asked; decide is not configured`;if(e.status==="unavailable")return`${e.question}: no decision; ${e.reason}`;let r=(e.probability??0).toFixed(2),o=e.threshold.toFixed(2);return e.status==="confident"?`${e.question}: ${e.answer} (${r}, threshold ${o}${n})`:`${e.question}: ${e.answer} (${r}, below the ${o} threshold, so not used${n})`}var VT={type:"noul",instructions:"Read the pull request's title and description, then the summary of what its plan does. Does the description leave out or misstate a destroy or a replacement the plan makes?",criteria:{true:"the description does not describe the plan: a destroy or replacement in the plan is missing from it or contradicted by it",false:"the description describes what the plan does, including every destroy and replacement"}},ZT={type:"choice",instructions:"An attribute of a resource managed by Terraform changed outside Terraform. From the resource, the attribute and the two values, which kind of change is it most likely?",criteria:{controller:"a controller or the cloud service wrote it, as autoscaling, a scheduler or a service that adds tags does",human:"a person edited it by hand in a console or with a CLI","provider-default":"the provider or the cloud API changed a default value it reports"}},Ace={type:"choice",instructions:"These are the commits to a Terraform module since its last release, and a summary of its diff. Which semantic version bump does the change call for?",criteria:{major:"a breaking change: a variable or output removed or renamed, a required variable added, or a resource replaced for every caller",minor:"a new feature that keeps every existing caller working, such as a new optional variable, output or resource",patch:"a fix or an internal change that callers do not see"}},kU={prIntent:VT,driftActor:ZT,versionBump:Ace};var wm=e=>e!==null&&typeof e=="object"&&!Array.isArray(e),tl=e=>typeof e=="number",Pce=/^(aws:|eks:|elasticbeanstalk:|kubernetes\.io\/|k8s\.io\/|karpenter\.sh\/)/;function jce(e){let t=wm(e.before)?e.before:{},n=wm(e.live)?e.live:{},r=[...new Set([...Object.keys(t),...Object.keys(n)])].filter(o=>JSON.stringify(t[o]??null)!==JSON.stringify(n[o]??null));return r.length>0&&r.every(o=>Pce.test(o))}var Ice=e=>t=>{let n=Array.isArray(t.before)?t.before[0]:t.before,r=Array.isArray(t.live)?t.live[0]:t.live;if(!wm(n)||!wm(r))return!1;let o=[...new Set([...Object.keys(n),...Object.keys(r)])].filter(s=>JSON.stringify(n[s]??null)!==JSON.stringify(r[s]??null));return o.length>0&&o.every(s=>s===e)},Cce=[{type:"aws_ecs_service",path:"desired_count",actor:"controller",why:"Application Auto Scaling sets an ECS service's task count",when:e=>tl(e.live)},{type:"aws_autoscaling_group",path:"desired_capacity",actor:"controller",why:"the group's scaling policies and scheduled actions set its desired capacity",when:e=>tl(e.live)},{type:"aws_eks_node_group",path:"scaling_config",actor:"controller",why:"Cluster Autoscaler or Karpenter sets a node group's desired size",when:Ice("desired_size")},{type:"aws_dynamodb_table",path:"read_capacity",actor:"controller",why:"Application Auto Scaling adjusts a provisioned table's read capacity",when:e=>tl(e.live)},{type:"aws_dynamodb_table",path:"write_capacity",actor:"controller",why:"Application Auto Scaling adjusts a provisioned table's write capacity",when:e=>tl(e.live)},{type:"aws_appautoscaling_target",path:"min_capacity",actor:"controller",why:"a scheduled action on the scalable target moves its minimum",when:e=>tl(e.live)},{type:"aws_appautoscaling_target",path:"max_capacity",actor:"controller",why:"a scheduled action on the scalable target moves its maximum",when:e=>tl(e.live)},{type:"aws_db_instance",path:"engine_version",actor:"controller",why:"RDS applies minor engine upgrades itself when auto_minor_version_upgrade is on",when:e=>typeof e.live=="string"&&typeof e.before=="string"},{type:"aws_launch_template",path:"latest_version",actor:"controller",why:"a service that publishes a launch template version (EC2 Image Builder, Karpenter) moves latest_version",when:e=>tl(e.live)},{type:"aws_elasticache_replication_group",path:"engine_version_actual",actor:"controller",why:"ElastiCache applies minor engine upgrades itself"},...["tags","tags_all"].map(e=>({type:"*",path:e,actor:"controller",why:"a service adds tags in its own namespace (aws:, eks:, karpenter.sh/ and the like)",when:jce}))];function Nce(e,t,n){return Cce.find(r=>(r.type===e||r.type==="*")&&r.path===t&&(r.when?.(n)??!0))}var Dce=/terraform|opentofu|tofu/i;function Mce(e){let t=e.userIdentity??{},n=t.invokedBy??e.invokedBy;if(t.type==="AWSService"||n&&/\.amazonaws\.com$/.test(n))return{actor:"controller",who:n??"an AWS service"};let o=(t.arn??"").split("/").at(-1)??"";if(t.type==="IAMUser")return{actor:"human",who:t.userName??o};if(t.type==="Root")return{actor:"human",who:"the root user"};if(t.type==="IdentityCenterUser"||t.type==="FederatedUser")return{actor:"human",who:t.userName??o};if(t.type==="AssumedRole"){let s=t.sessionContext?.sessionIssuer?.userName??"";if(/^AWSReservedSSO_/.test(s)||o.includes("@"))return{actor:"human",who:o||s}}}function Lce(e){let n=e.filter(o=>o.readOnly!==!0&&o.readOnly!=="true"&&!Dce.test(o.userAgent??"")).sort((o,s)=>String(s.eventTime??"").localeCompare(String(o.eventTime??"")))[0],r=n&&Mce(n);return n&&r?{status:"found",...r,event:n.eventName??"an event",at:n.eventTime??""}:{status:"silent"}}function bm(e={}){let t=e.run??(n=>{let r=Oce("aws",n,{encoding:"utf-8",maxBuffer:67108864});return{status:r.status,stdout:r.stdout??"",stderr:r.stderr??"",error:r.error}});return{async lookup(n){if(!n.type.startsWith("aws_"))return{status:"unavailable",reason:`no audit log reader for ${n.type}`};let r=new Date((e.now?.()??new Date).getTime()-(e.days??14)*864e5).toISOString(),o=t(["cloudtrail","lookup-events","--output","json","--max-results","50","--start-time",r,"--lookup-attributes",`AttributeKey=ResourceName,AttributeValue=${n.ref}`,...e.region?["--region",e.region]:[]]);if(o.error?.code==="ENOENT")return{status:"unavailable",reason:"the aws CLI is not installed"};if(o.error||o.status!==0)return{status:"unavailable",reason:(o.error?.message??(o.stderr.trim()||`aws exited ${o.status}`)).split(`
|
|
471
|
+
${t}`}import{spawnSync as Oce}from"node:child_process";import{createHash as $ce}from"node:crypto";var HT={"ghcr.io/intentius/terragucci-tofu:0.4.33-tofu1.13.1":"sha256:641faf40dbaf057b16948b99ce9bb830c7c9f7abb69b01b3526d2086cee484ff","ghcr.io/intentius/terragucci-terraform:0.4.33-tf1.14.9":"sha256:365f2057d003cb0335421aaf2a02d5e12597c5d03126fdeeeaed0aba92f9849e","ghcr.io/intentius/terragucci-terragrunt:0.4.33-tg1.1.6-tofu1.13.1":"sha256:db853030173010a53ff322f3bcc7527dcf14b864b2fc892dccaa1b095cb51513","ghcr.io/intentius/terragucci-choudoufu:0.4.33-choudoufu0.28.0":"sha256:0b805c45c9f903cce3c40415c1a73eeb916c84234f7285e244e765591c14a555"};var yU="ghcr.io/intentius",ai={tofu:"1.13.1",terraform:"1.14.9",terragrunt:"1.1.6",choudoufu:"0.28.0"};function wU(e,t=Pr){return e==="terragrunt"?`${t}-tg${ai.terragrunt}-tofu${ai.tofu}`:`${t}-${e==="terraform"?"tf":e}${ai[e]}`}function JT(e,t=HT){let n=`${yU}/terragucci-${e}`,r=wU(e);return{repository:n,tag:r,digest:t[`${n}:${r}`]}}function bU(e=HT){let t=`${yU}/terragucci-terragrunt`,n=wU("terragrunt");return{repository:t,tag:n,digest:e[`${t}:${n}`]}}function KT(e){return e.digest?`${e.repository}:${e.tag}@${e.digest}`:`${e.repository}:${e.tag}`}var qT={laya:"0.3.28",torch:"2.14.0",pip:"26.2.1",checkpoint:{repo:"convaiinnovations/laya",name:"english",revision:"55cf4c4ebb4ebe31b2550e8bdf3bd21b99753851",weightsSha256:"891102d372688fc2a094dac56a384bc537b87c63f21f9f3dac0be2b7cbc8d86c"},port:8790},vU=`laya-${qT.laya}-${qT.checkpoint.name}@${qT.checkpoint.revision.slice(0,12)}`;var kce="https://api.typesafe.ai",_ce={noul:.8,choice:.7,score:.7},Sce={laya:!0,jev:!0,von:!1,decider:!1};function Rce(e,t){let n=r=>{throw new Error(`decide: question ${e} ${r}`)};if(/^[A-Za-z0-9_-]{1,64}$/.test(e)||n("needs a name of letters, digits, - and _"),t.instructions||n("has no instructions"),t.threshold!==void 0&&!(t.threshold>0&&t.threshold<=1)&&n("has a threshold outside (0, 1]"),t.type==="noul")(!t.criteria?.true||!t.criteria?.false)&&n("is a noul and needs criteria.true and criteria.false");else if(t.type==="choice"){let r=Object.keys(t.criteria??{}).length;(r<2||r>255)&&n(`is a choice with ${r} options; use 2 to 255`)}else if(t.type==="score"){let r=t.criteria?.length??0;(r<2||r>10)&&n(`is a score with ${r} levels; use 2 to 10`)}else n(`has type ${JSON.stringify(t.type)}; use noul, choice or score`)}function $U(e,t){return e.threshold??t?.thresholds?.[e.type]??_ce[e.type]}var Ev=e=>typeof e=="number"&&Number.isFinite(e)&&e>=0&&e<=1;function xce(e){let t=e.url??(e.backend==="jev"?kce:void 0);if(!t)return;let n=t.replace(/\/+$/,"");return n.endsWith("/v1/systemone")?n:`${n}/v1/systemone`}function Ece(e){return e.model??(e.backend==="laya"?vU:void 0)}function Tce(e,t,n,r){let o={question:e,type:t.type,threshold:r},s=d=>({...o,status:"unavailable",reason:d});if(n===null||typeof n!="object")return s("the response has no answer for it");let i=n;if(i.type!==t.type)return s(`the answer's type is ${JSON.stringify(i.type)}, not ${t.type}`);let a,c,u,l;if(t.type==="noul"){if(!Ev(i.noul))return s("the noul answer is not a probability");a=i.noul>=.5?"true":"false",c=Math.max(i.noul,1-i.noul),u={true:i.noul,false:1-i.noul}}else{let d=i.probabilities;if(d===null||typeof d!="object"||!Object.values(d).every(Ev))return s("the answer has no probabilities");if(u=d,t.type==="choice"){if(typeof i.choice!="string"||!(i.choice in t.criteria))return s(`the choice ${JSON.stringify(i.choice)} is not one of the options`);if(!Ev(u[i.choice]))return s("the chosen option has no probability");a=i.choice,c=u[i.choice]}else{let p=t.criteria.map((g,m)=>String(m));if(!p.every(g=>Ev(u[g])))return s("the score's probabilities do not cover every level");let f=p.reduce((g,m)=>u[m]>u[g]?m:g,p[0]);a=t.criteria[Number(f)],c=u[f],typeof i.score=="number"&&Number.isFinite(i.score)&&(l=i.score)}}return{...o,status:c>=r?"confident":"not-confident",answer:a,probability:c,probabilities:u,...l===void 0?{}:{score:l}}}async function Xd(e,t,n,r={}){for(let[v,$]of Object.entries(n))Rce(v,$);let o=`sha256:${$ce("sha256").update(JSON.stringify(t)).digest("hex")}`,s=(v,$)=>Object.fromEntries(Object.entries(n).map(([S,R])=>[S,{question:S,type:R.type,status:v,threshold:$U(R,e),reason:$}]));if(!e)return{stateDigest:o,decisions:s("off","decide is not configured")};let i=xce(e),a=Ece(e),c={backend:e.backend,url:i,model:a,stateDigest:o};if(!i)return{...c,decisions:s("unavailable","decide.url is not set")};let u={"content-type":"application/json",accept:"application/json"};if(e.token_env){let v=(r.env??process.env)[e.token_env];if(!v)return{...c,decisions:s("unavailable",`${e.token_env} is not set`)};u.authorization=`Bearer ${v}`}let l=Object.fromEntries(Object.entries(n).map(([v,$])=>[v,{type:$.type,instructions:$.instructions,criteria:$.criteria}])),d=JSON.stringify({...a?{model:a}:{},state:t,questions:l}),p=r.fetch??globalThis.fetch,f=Date.now(),g,m;try{let v=await p(i,{method:"POST",headers:u,body:d,signal:AbortSignal.timeout(r.timeoutMs??3e4)});if(g=v.status,m=await v.text(),!v.ok){let $=m.slice(0,300);try{let S=JSON.parse(m),R=S.detail??S.error;R!==void 0&&($=typeof R=="string"?R:JSON.stringify(R))}catch{}return{...c,ms:Date.now()-f,decisions:s("unavailable",`the service answered HTTP ${g}: ${$}`)}}}catch(v){return{...c,ms:Date.now()-f,decisions:s("unavailable",`the service did not answer: ${v.message}`)}}let h=Date.now()-f,y;try{y=JSON.parse(m)}catch{return{...c,ms:h,decisions:s("unavailable","the response is not JSON")}}if(y===null||typeof y!="object"||y.answers===null||typeof y.answers!="object")return{...c,ms:h,decisions:s("unavailable","the response has no answers")};let w=typeof y.model=="string"?y.model:void 0;if(Sce[e.backend]&&a&&w!==a)return{...c,model:w??a,ms:h,decisions:s("unavailable",`the service answered as ${w??"no model"}, and decide.model pins ${a}`)};let b=y.answers,k=Object.fromEntries(Object.entries(n).map(([v,$])=>[v,Tce(v,$,b[v],$U($,e))]));return{...c,model:w??a,ms:h,decisions:k}}function Qd(e){return e?.status==="confident"}function Sc(e,t){let n=t.model?`, ${t.model}`:"";if(e.status==="off")return`${e.question}: not asked; decide is not configured`;if(e.status==="unavailable")return`${e.question}: no decision; ${e.reason}`;let r=(e.probability??0).toFixed(2),o=e.threshold.toFixed(2);return e.status==="confident"?`${e.question}: ${e.answer} (${r}, threshold ${o}${n})`:`${e.question}: ${e.answer} (${r}, below the ${o} threshold, so not used${n})`}var VT={type:"noul",instructions:"Read the pull request's title and description, then the summary of what its plan does. Does the description leave out or misstate a destroy or a replacement the plan makes?",criteria:{true:"the description does not describe the plan: a destroy or replacement in the plan is missing from it or contradicted by it",false:"the description describes what the plan does, including every destroy and replacement"}},ZT={type:"choice",instructions:"An attribute of a resource managed by Terraform changed outside Terraform. From the resource, the attribute and the two values, which kind of change is it most likely?",criteria:{controller:"a controller or the cloud service wrote it, as autoscaling, a scheduler or a service that adds tags does",human:"a person edited it by hand in a console or with a CLI","provider-default":"the provider or the cloud API changed a default value it reports"}},Ace={type:"choice",instructions:"These are the commits to a Terraform module since its last release, and a summary of its diff. Which semantic version bump does the change call for?",criteria:{major:"a breaking change: a variable or output removed or renamed, a required variable added, or a resource replaced for every caller",minor:"a new feature that keeps every existing caller working, such as a new optional variable, output or resource",patch:"a fix or an internal change that callers do not see"}},kU={prIntent:VT,driftActor:ZT,versionBump:Ace};var wm=e=>e!==null&&typeof e=="object"&&!Array.isArray(e),tl=e=>typeof e=="number",Pce=/^(aws:|eks:|elasticbeanstalk:|kubernetes\.io\/|k8s\.io\/|karpenter\.sh\/)/;function jce(e){let t=wm(e.before)?e.before:{},n=wm(e.live)?e.live:{},r=[...new Set([...Object.keys(t),...Object.keys(n)])].filter(o=>JSON.stringify(t[o]??null)!==JSON.stringify(n[o]??null));return r.length>0&&r.every(o=>Pce.test(o))}var Ice=e=>t=>{let n=Array.isArray(t.before)?t.before[0]:t.before,r=Array.isArray(t.live)?t.live[0]:t.live;if(!wm(n)||!wm(r))return!1;let o=[...new Set([...Object.keys(n),...Object.keys(r)])].filter(s=>JSON.stringify(n[s]??null)!==JSON.stringify(r[s]??null));return o.length>0&&o.every(s=>s===e)},Cce=[{type:"aws_ecs_service",path:"desired_count",actor:"controller",why:"Application Auto Scaling sets an ECS service's task count",when:e=>tl(e.live)},{type:"aws_autoscaling_group",path:"desired_capacity",actor:"controller",why:"the group's scaling policies and scheduled actions set its desired capacity",when:e=>tl(e.live)},{type:"aws_eks_node_group",path:"scaling_config",actor:"controller",why:"Cluster Autoscaler or Karpenter sets a node group's desired size",when:Ice("desired_size")},{type:"aws_dynamodb_table",path:"read_capacity",actor:"controller",why:"Application Auto Scaling adjusts a provisioned table's read capacity",when:e=>tl(e.live)},{type:"aws_dynamodb_table",path:"write_capacity",actor:"controller",why:"Application Auto Scaling adjusts a provisioned table's write capacity",when:e=>tl(e.live)},{type:"aws_appautoscaling_target",path:"min_capacity",actor:"controller",why:"a scheduled action on the scalable target moves its minimum",when:e=>tl(e.live)},{type:"aws_appautoscaling_target",path:"max_capacity",actor:"controller",why:"a scheduled action on the scalable target moves its maximum",when:e=>tl(e.live)},{type:"aws_db_instance",path:"engine_version",actor:"controller",why:"RDS applies minor engine upgrades itself when auto_minor_version_upgrade is on",when:e=>typeof e.live=="string"&&typeof e.before=="string"},{type:"aws_launch_template",path:"latest_version",actor:"controller",why:"a service that publishes a launch template version (EC2 Image Builder, Karpenter) moves latest_version",when:e=>tl(e.live)},{type:"aws_elasticache_replication_group",path:"engine_version_actual",actor:"controller",why:"ElastiCache applies minor engine upgrades itself"},...["tags","tags_all"].map(e=>({type:"*",path:e,actor:"controller",why:"a service adds tags in its own namespace (aws:, eks:, karpenter.sh/ and the like)",when:jce}))];function Nce(e,t,n){return Cce.find(r=>(r.type===e||r.type==="*")&&r.path===t&&(r.when?.(n)??!0))}var Dce=/terraform|opentofu|tofu/i;function Mce(e){let t=e.userIdentity??{},n=t.invokedBy??e.invokedBy;if(t.type==="AWSService"||n&&/\.amazonaws\.com$/.test(n))return{actor:"controller",who:n??"an AWS service"};let o=(t.arn??"").split("/").at(-1)??"";if(t.type==="IAMUser")return{actor:"human",who:t.userName??o};if(t.type==="Root")return{actor:"human",who:"the root user"};if(t.type==="IdentityCenterUser"||t.type==="FederatedUser")return{actor:"human",who:t.userName??o};if(t.type==="AssumedRole"){let s=t.sessionContext?.sessionIssuer?.userName??"";if(/^AWSReservedSSO_/.test(s)||o.includes("@"))return{actor:"human",who:o||s}}}function Lce(e){let n=e.filter(o=>o.readOnly!==!0&&o.readOnly!=="true"&&!Dce.test(o.userAgent??"")).sort((o,s)=>String(s.eventTime??"").localeCompare(String(o.eventTime??"")))[0],r=n&&Mce(n);return n&&r?{status:"found",...r,event:n.eventName??"an event",at:n.eventTime??""}:{status:"silent"}}function bm(e={}){let t=e.run??(n=>{let r=Oce("aws",n,{encoding:"utf-8",maxBuffer:67108864});return{status:r.status,stdout:r.stdout??"",stderr:r.stderr??"",error:r.error}});return{async lookup(n){if(!n.type.startsWith("aws_"))return{status:"unavailable",reason:`no audit log reader for ${n.type}`};let r=new Date((e.now?.()??new Date).getTime()-(e.days??14)*864e5).toISOString(),o=t(["cloudtrail","lookup-events","--output","json","--max-results","50","--start-time",r,"--lookup-attributes",`AttributeKey=ResourceName,AttributeValue=${n.ref}`,...e.region?["--region",e.region]:[]]);if(o.error?.code==="ENOENT")return{status:"unavailable",reason:"the aws CLI is not installed"};if(o.error||o.status!==0)return{status:"unavailable",reason:(o.error?.message??(o.stderr.trim()||`aws exited ${o.status}`)).split(`
|
|
472
472
|
`)[0]};let s;try{s=JSON.parse(o.stdout)}catch{return{status:"unavailable",reason:"aws printed no JSON"}}let i=[];for(let a of s.Events??[])try{i.push({eventName:a.EventName,eventTime:a.EventTime,...JSON.parse(a.CloudTrailEvent??"{}")})}catch{}return Lce(i)}}}function _U(e,t){return t?"(sensitive)":e==null?null:typeof e=="number"||typeof e=="boolean"?e:typeof e=="string"?`(a string of ${e.length} characters)`:Array.isArray(e)?`(a list of ${e.length})`:wm(e)?`(a map with keys ${Object.keys(e).slice(0,12).join(", ")})`:"(a value)"}var vm="attributions.json";async function ep(e,t,n={}){let r=[],o=[],s=new Map,i;for(let a of t)for(let c of a.attributes){let u={root:e,address:a.address,path:c.path},l=c.sensitive?void 0:Nce(a.type,c.path,c);if(l){r.push({...u,actor:l.actor,source:"table",detail:l.why});continue}if(n.audit&&a.ref&&i===void 0){let d=`${a.type}\0${a.ref}`,p=s.get(d);if(p||(p=await n.audit.lookup({type:a.type,address:a.address,ref:a.ref}),s.set(d,p)),p.status==="found"){r.push({...u,actor:p.actor,source:"audit",detail:`${p.event} by ${p.who}${p.at?` at ${p.at}`:""}`});continue}p.status==="unavailable"&&(i=p.reason,o.push(`audit log unavailable: ${p.reason}`))}if(n.decide){let d={resource_type:a.type,attribute:c.path,value_in_code:_U(c.before,c.sensitive),live_value:_U(c.live,c.sensitive)},p=await Xd(n.decide,d,{actor:ZT},n.options),f=p.decisions.actor;if(Qd(f)){r.push({...u,actor:f.answer,source:"model",detail:Sc(f,p),probability:f.probability});continue}o.push(`${a.address} ${c.path}: ${Sc(f,p)}`)}r.push({...u,source:"unattributed",detail:"neither the known-writes table nor the audit log answered"+(n.decide?", and the decision was not used":"")})}return{attributions:r,notes:o}}var Gce={table:"known write",audit:"audit log",model:"model",unattributed:"unattributed"};function YT(e){let t=new Map,n=[];for(let r of e){let o=`\`${r.address}\` \`${r.path}\``;if(r.source==="unattributed"){n.push(`- unattributed: ${o}: ${r.detail}; the default response runs`);continue}let s=`${Gce[r.source]}: ${r.detail}`;if(r.actor==="human"){n.push(`- human edit: ${o} (${s}); the pull request codifies it for the owner to accept or revert`);continue}let i=t.get(r.address)??new Set;i.add(r.path),t.set(r.address,i),r.actor==="controller"?n.push(`- controller write: ${o} (${s}); add \`lifecycle { ignore_changes = [${r.path}] }\` to the resource, and leave it out of the code`):n.push(`- provider default change: ${o} (${s}); pin the version that kept the old value, or set \`${r.path}\` to the value you want`)}return{leave:t,lines:n}}function XT(e,t){return e.map(n=>({...n,attributes:n.attributes.filter(r=>!t.get(n.address)?.has(r.path))})).filter((n,r)=>n.attributes.length>0||e[r].attributes.length===0)}import{existsSync as RU,readdirSync as xU,readFileSync as Fce}from"node:fs";import{join as Uce,posix as zce}from"node:path";var EU=(e,t)=>JSON.stringify(e??null)===JSON.stringify(t??null);function nl(e){return(e?.resource_drift??[]).filter(n=>n.mode!=="data").map(n=>{let r=n.change.before??{},o=n.change.after??{},s=n.change.actions.includes("delete")?"delete":"update",i=u=>{let l=n.change.before_sensitive?.[u]??n.change.after_sensitive?.[u];return l!==void 0&&l!==!1&&!(typeof l=="object"&&l!==null&&Object.keys(l).length===0)},a=s==="delete"?[]:[...new Set([...Object.keys(r),...Object.keys(o)])].sort().filter(u=>!EU(r[u],o[u])).map(u=>({path:u,before:r[u],live:o[u],...i(u)?{sensitive:!0}:{}})),c=["name","id","function_name","bucket"].map(u=>o[u]??r[u]).find(u=>typeof u=="string"&&u!=="");return{address:n.address,action:s,attributes:a,type:n.type,name:n.name,...c?{ref:c}:{},...n.module_address?{module:n.module_address}:{},...n.index!==void 0?{index:n.index}:{}}})}function Tv(e){let t=e.trim().replace(/\s*(#|\/\/).*$/,"");if(/^-?\d+(\.\d+)?$/.test(t))return{value:Number(t)};if(t==="true"||t==="false")return{value:t==="true"};if(/^"(?:[^"\\$%]|\\.|\$(?!\{)|%(?!\{))*"$/.test(t))try{return{value:JSON.parse(t)}}catch{return}}function QT(e){if(typeof e=="number"||typeof e=="boolean")return String(e);if(typeof e=="string")return JSON.stringify(e).replace(/\$\{/g,()=>"$${").replace(/%\{/g,()=>"%%{")}function Bce(e){return RU(e)?xU(e).filter(t=>t.endsWith(".tf")||t.endsWith(".tofu")).sort():[]}function SU(e,t,n){let r=new RegExp(`^\\s*resource\\s+"${t}"\\s+"${n}"\\s*\\{`),o=e.findIndex(i=>r.test(i));if(o<0)return;let s=0;for(let i=o;i<e.length;i++){for(let a of e[i].replace(/"(?:[^"\\]|\\.)*"/g,'""').replace(/#.*$/,""))(a==="{"||a==="["||a==="(")&&s++,(a==="}"||a==="]"||a===")")&&s--;if(s===0)return[o,i]}}function Av(e,t,n,r=new Map){let o=[],s=[],i=a=>r.get(a)??Fce(Uce(t,a),"utf-8");for(let a of n){let c=(g,m)=>s.push({root:e,address:a.address,...m?{path:m}:{},reason:g});if(a.action==="delete"){c("deleted outside Terraform; the next apply makes it again, or remove it from the code if that was the intent");continue}if(a.module){c(`reached through ${a.module}; the value is set in a module`);continue}if(a.index!==void 0){c("an instance of count or for_each; one literal sets every instance");continue}let u=Bce(t).find(g=>SU(i(g).split(`
|
|
473
473
|
`),a.type,a.name));if(!u){c("its resource block is not in the root's files");continue}let l=i(u).split(`
|
|
474
474
|
`),[d,p]=SU(l,a.type,a.name);for(let g of a.attributes){let m=0,h=-1;for(let k=d+1;k<p&&h<0;k++){m===0&&new RegExp(`^\\s*${g.path}\\s*=`).test(l[k])&&(h=k);for(let v of l[k].replace(/"(?:[^"\\]|\\.)*"/g,'""').replace(/#.*$/,""))(v==="{"||v==="["||v==="(")&&m++,(v==="}"||v==="]"||v===")")&&m--}if(h<0){c("not set in the root's block; it comes from a default or the provider",g.path);continue}let y=/^(\s*\S+\s*=\s*)(.*?)(\s*(?:#.*|\/\/.*)?)$/.exec(l[h]),w=Tv(y[2]);if(!w){c(`set from an expression (${y[2].trim()})`,g.path);continue}if(!EU(w.value,g.before)){c("the root's value is not the one last applied, so the code moved too",g.path);continue}let b=QT(g.live);if(b===void 0){c("its live value is a list, a map or null",g.path);continue}l[h]=`${y[1]}${b}${y[3]}`,o.push({root:e,address:a.address,path:g.path,file:zce.join(e,u),from:y[2].trim(),to:b})}let f=l.join(`
|