@intentius/terragucci 0.4.16 → 0.4.17
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 +41 -41
- package/dist/terragucci.mjs.map +1 -1
- package/package.json +1 -1
package/dist/terragucci.mjs
CHANGED
|
@@ -80,7 +80,7 @@ ${o.stdout.slice(0,400)}`)}}var hw=["namespace","tenant","environment","stage"];
|
|
|
80
80
|
Install it once: npm install -D @cdktn/hcl2json
|
|
81
81
|
(underlying error: ${t instanceof Error?t.message:String(t)})`),this.name="Hcl2JsonNotInstalled"}},jQ="CHANT_HCL2JSON_RECORD";function IQ(e,t){let n=r=>{PQ(t,`${JSON.stringify(r)}
|
|
82
82
|
`)};return{parse:(r,o)=>(n({kind:"parse",filename:r,text:o}),e.parse(r,o)),getReferencesInExpression:(r,o)=>(n({kind:"refs",filename:r,text:o}),e.getReferencesInExpression(r,o))}}async function AD(){let e;try{e=await import("@cdktn/hcl2json")}catch(n){throw new ox(n)}let t=process.env[jQ];return t?IQ(e,t):e}var CQ=/^\s*(terraform|locals|remote_state)\s*\{/,NQ=/^\s*(provider|module|variable|output|dependency|generate)\s+"([^"]*)"\s*\{/,DQ=/^\s*(resource|data)\s+"([^"]*)"\s+"([^"]*)"\s*\{/,MQ=/^\s*(?:#|\/\/)\s*chant-ignore(-file|-block)?\s*:?\s*(.*)$/;function sx(e,t){switch(e){case"terraform":return"terraform";case"locals":return"locals";case"remote_state":return"remote_state";case"dependency":return`dependency.${t[0]}`;case"generate":return`generate.${t[0]}`;case"provider":return`provider.${t[0]}`;case"module":return`module.${t[0]}`;case"variable":return`var.${t[0]}`;case"output":return`output.${t[0]}`;case"resource":return`${t[0]}.${t[1]}`;case"data":return`data.${t[0]}.${t[1]}`;default:return""}}function LQ(e){return e.replace(/"(?:[^"\\]|\\.)*"/g,'""')}function GQ(e){let t=LQ(e),n=0;for(let r of t)r==="{"?n++:r==="}"&&n--;return n}function FQ(e){let t=e.split(/\s+/).filter(Boolean),n,r=[];for(let i of t){let a=/^exp:(\d{4}-\d{2}-\d{2})$/.exec(i);if(a){n=a[1];continue}r.push(i)}let o=r.join(" ").trim();return o===""||o.toLowerCase()==="all"?{ids:"all",expires:n}:{ids:new Set(o.split(/[,\s]+/).map(i=>i.trim().toUpperCase()).filter(Boolean)),expires:n}}function ix(e,t,n){let r=e.get(t)??[];r.push(n),e.set(t,r)}function ax(e,t){let n=t.split(`
|
|
83
|
-
`),r=new Map,o=new Map,s,i,a=0;for(let c=0;c<n.length;c++){let u=n[c],l=c+1,d=u.trim();i===void 0&&d!==""&&(i=l);let p=MQ.exec(u);if(p){let f=p[1],{ids:g,expires:m}=FQ(p[2].trim());if(f==="-file")s={form:"chant-ignore-file",ids:g,expires:m,file:e,line:l,misplaced:l!==i,key:`${e}:${l}:chant-ignore-file`};else{let h=f==="-block"?"chant-ignore-block":"chant-ignore",y={form:h,ids:g,expires:m,file:e,line:l,key:`${e}:${l}:${h}`},w=l+1,b=o.get(w)??[];b.push(y),o.set(w,b)}}else if(a===0){let f=CQ.exec(u);f?ix(r,sx(f[1],[]),l):(f=NQ.exec(u))?ix(r,sx(f[1],[f[2]]),l):(f=DQ.exec(u))&&ix(r,sx(f[1],[f[2],f[3]]),l)}a+=GQ(u)}return{blockLineQueues:r,byAnchorLine:o,fileDirective:s}}function zQ(e,t){let n=e.blockLineQueues.get(t);if(!(!n||n.length===0))return n.shift()}function OD(e,t){let n=zQ(e,t),r=n!==void 0?e.byAnchorLine.get(n)??[]:[],o=e.fileDirective?[...r,e.fileDirective]:r;return{line:n,suppressions:o.length>0?o:void 0}}var cg="Terraform::Terraform",dx="Terraform::Provider",px="Terraform::Resource",fx="Terraform::Data",Zi="Terraform::Module",BQ="Terraform::Variable",WQ="Terraform::Output",HQ="Terraform::Locals",cx="Terraform::Live",qQ="Terraform::TerragruntConfig",gx="Terraform::TerragruntSource",ug="Terraform::TerragruntDependency",mx="Terraform::TerragruntRemoteState",hx="Terraform::TerragruntGenerate",jD=["root.hcl","terragrunt.hcl"];function JQ(e){return jD.includes(e)}var ux="estate.chdf.hcl";function KQ(e){if("count"in e)return"count";if("for_each"in e)return"for_each"}function VQ(e,t,n,r,o,s,i="",a,c){let u=KQ(n);return{[gu]:!0,lexicon:"terraform",entityType:e,kind:"resource",stack:o,props:{address:t,body:n,file:r,root:o,source:i,line:a,...u?{expansion:u}:{},...s?.mode!==void 0?{mode:s.mode}:{},...s?.estate!==void 0?{estate:s.estate}:{},...s?.workspace!==void 0?{workspace:s.workspace}:{},...s?.delete!==void 0?{delete:s.delete}:{},...s?.configEstate!==void 0?{configEstate:s.configEstate}:{},...s?.callers!==void 0&&s.callers.length>0?{callers:s.callers}:{}},suppressions:c}}function ZQ(e){return UQ(e).filter(t=>t.endsWith(".tf")).sort()}function YQ(e){return jD.filter(t=>lx(Mw(e,t)))}function XQ(e){let t=Mw(e,ux);if(lx(t))return{name:ux,source:PD(t,"utf-8")}}function Vi(e){return Array.isArray(e)?e:[]}function rc(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)?e:{}}function QQ(e){let t=[];for(let n of Vi(e.terraform)){let r=rc(n);for(let o of Vi(r.live))t.push(rc(o))}return t}var yx=".terraform.lock.hcl";async function eee(e,t,n,r,o,s=[]){let i=n??await AD(),a=new Map,c=s.length>0?`${t}/${s.join("/")}`:t,u=(m,h,y,w,b,k=h)=>{let{line:v,suppressions:$}=OD(b,k),_=VQ(m,h,typeof y=="object"&&y!==null?y:{},w.name,t,{...r?.workspace!==void 0?{workspace:r.workspace}:{},...r?.delete!==void 0?{delete:r.delete}:{},...r?.configEstate!==void 0?{configEstate:r.configEstate}:{},...s.length>0?{callers:s}:{}},w.source,v,$),E=`${c}/${h}`;for(let R=2;a.has(E);R++)E=`${c}/${h}~${R}`;a.set(E,_)},l=(m,h,y,w,b)=>{for(let k of Vi(m[h]))u(y,h,k,w,b)},d=(m,h,y,w,b,k)=>{for(let[v,$]of Object.entries(rc(m[h])))for(let _ of Vi($))u(y,w(v),_,b,k)},p=(m,h,y,w,b,k)=>{for(let[v,$]of Object.entries(rc(m[h])))for(let[_,E]of Object.entries(rc($)))for(let R of Vi(E))u(y,w(v,_),R,b,k)};for(let m of e){let h=await i.parse(m.name,m.source),y=ax(m.name,m.source);if(JQ(m.name)){tee(h,m,y,u);continue}l(h,"terraform",cg,m,y);for(let w of QQ(h))u(cx,"live",w,m,y);l(h,"locals",HQ,m,y),d(h,"provider",dx,w=>`provider.${w}`,m,y),d(h,"module",Zi,w=>`module.${w}`,m,y),d(h,"variable",BQ,w=>`var.${w}`,m,y),d(h,"output",WQ,w=>`output.${w}`,m,y),p(h,"resource",px,(w,b)=>`${w}.${b}`,m,y),p(h,"data",fx,(w,b)=>`data.${w}.${b}`,m,y)}if(o){let m=await i.parse(o.name,o.source);typeof m.estate=="string"&&u(cx,"live",m,o,ax(o.name,o.source))}let f;for(let m of a.values()){if(m.entityType!==cx)continue;let h=m.props,y=typeof h.body.estate=="string"?h.body.estate:void 0;y!==void 0&&(f===void 0||h.file===ux)&&(f=y)}let g=r?.binary==="choudoufu"&&f!==void 0?"live":"state";for(let[m,h]of a){let y=h,w={...y,props:{...y.props,mode:g,...g==="live"?{estate:f}:{},...r?.lockFile!==void 0?{lockFile:r.lockFile}:{}}};a.set(m,w)}return a}function tee(e,t,n,r){let o={includes:Object.keys(rc(e.include))};typeof e.terragrunt_version_constraint=="string"&&(o.terragrunt_version_constraint=e.terragrunt_version_constraint),r(qQ,"terragrunt",o,t,n,"");for(let s of Vi(e.terraform))r(gx,"terragrunt.terraform",s,t,n,"terraform");for(let s of Vi(e.remote_state))r(mx,"remote_state",s,t,n);for(let[s,i]of Object.entries(rc(e.dependency)))for(let a of Vi(i))r(ug,`dependency.${s}`,a,t,n);for(let[s,i]of Object.entries(rc(e.generate)))for(let a of Vi(i))r(hx,`generate.${s}`,a,t,n)}async function wx(e,t,n,r,o=[]){let i=[...ZQ(e),...o.length===0?YQ(e):[]].map(u=>({name:u,source:PD(Mw(e,u),"utf-8")})),a=o.length===0?XQ(e):void 0,c=o.length===0?{...r,lockFile:lx(Mw(e,yx))}:r;return eee(i,t,n,c,a,o)}function nee(e){if(!Xe(e))return[];let t=e.props;return Array.isArray(t.callers)?t.callers:[]}function Ou(e){return nee(e).length===0}var bx=new Set(["terraform"]);function ID(e){return e.split("_")[0]}function vx(e,t){let n=new Map,r=new Set;for(let[o,s]of e){if(!Xe(s)||!Ou(s))continue;let i=s.props;if(i.root!==t)continue;let a=typeof i.address=="string"?i.address:"";if(s.entityType===dx){let c=a.slice(9);if(bx.has(c))continue;n.set(c,o),r.add(c)}else if(s.entityType===px){let c=a.split(".")[0]??"",u=ID(c);if(bx.has(u)||r.has(u))continue;n.has(u)||n.set(u,o)}else if(s.entityType===fx){let c=a.split(".")[1]??"",u=ID(c);if(bx.has(u)||r.has(u))continue;n.has(u)||n.set(u,o)}}return n}function ree(e){let t={},n=e.required_providers;if(!Array.isArray(n))return t;for(let r of n)typeof r=="object"&&r!==null&&Object.assign(t,r);return t}function oee(e){if(e===void 0)return"no `required_providers` entry";if(typeof e=="string")return"the legacy shorthand form (a bare version string), which carries no `source`";if(typeof e!="object"||e===null)return"an entry that is not a valid object";let t=e,n=[];return(typeof t.source!="string"||t.source==="")&&n.push("`source`"),(typeof t.version!="string"||t.version==="")&&n.push("`version`"),n.length>0?`an entry missing ${n.join(" and ")}`:void 0}var CD={id:"TF002",description:"A provider the root implies has no required_providers entry, or an incomplete one",check(e){let t=[],n=new Set,r=new Map;for(let o of e.entities.values()){if(o.entityType!==cg||!Xe(o)||!Ou(o))continue;let s=o.props,i=typeof s.root=="string"?s.root:"";n.add(i);let a=typeof s.body=="object"&&s.body!==null?s.body:{},c=r.get(i)??{};Object.assign(c,ree(a)),r.set(i,c)}for(let o of[...n].sort()){let s=r.get(o)??{},i=vx(e.entities,o);for(let a of[...i.keys()].sort()){let c=oee(s[a]);c&&t.push({checkId:"TF002",severity:"warning",message:`Provider "${a}" is used in root module "${o}" but has ${c} in \`required_providers\`. Add \`source\` and \`version\` for it in the terraform block's \`required_providers\`, so a future provider release can't silently change behavior.`,entity:i.get(a),lexicon:"terraform"})}}return t}};function see(e){return typeof e.required_version=="string"&&e.required_version.trim()!==""}var ND={id:"TF003",description:"Root module's terraform block has no required_version",check(e){let t=[],n=new Map;for(let[r,o]of e.entities){if(o.entityType!==cg||!Xe(o)||!Ou(o))continue;let s=o.props,i=typeof s.root=="string"?s.root:"",a=typeof s.body=="object"&&s.body!==null?s.body:{},c=n.get(i),u=(c?.hasVersion??!1)||see(a);n.set(i,{anchor:c?.anchor??r,hasVersion:u})}for(let[r,{anchor:o,hasVersion:s}]of[...n].sort(([i],[a])=>i.localeCompare(a)))s||t.push({checkId:"TF003",severity:"warning",message:`Root module "${r}"'s terraform block has no \`required_version\`. Without it, an upgraded Terraform binary can change behavior with no version-check failure to flag it first. Add \`required_version = ">= <lowest supported version>"\`.`,entity:o,lexicon:"terraform"});return t}};var DD=/^[0-9A-Za-z](?:[0-9A-Za-z_-]{0,62}[0-9A-Za-z])?$/,iee=/^[0-9a-z]{1,64}$/,aee=new Set(["github.com","bitbucket.org"]);function MD(e){let t=new Map;for(let n of e.split("&")){if(n==="")continue;let r=n.indexOf("="),o=r===-1?n:n.slice(0,r),s=r===-1?"":decodeURIComponent(n.slice(r+1));t.set(decodeURIComponent(o),s)}return t}function LD(e){let t=e.indexOf("://"),n=t===-1?0:t+3,r=e.indexOf("//",n);return r===-1?e:e.slice(0,r)}function cee(e){let t=LD(e).split("/"),n,r,o;if(t.length===3)[n,r,o]=t;else if(t.length===4){let s=t[0];if(!s.includes(".")||aee.has(s.toLowerCase()))return!1;[,n,r,o]=t}else return!1;return DD.test(n)&&DD.test(r)&&iee.test(o)}function uee(e){if(/^[\w.-]+@[^:/]+:/.test(e))return!0;let t=e.replace(/^[a-z][a-z0-9+.-]*:\/\//i,"");return!!(/^(github\.com|bitbucket\.org)\//i.test(t)||/\.git($|[/?])/i.test(t))}function lee(e,t){let n=MD(t),r=LD(e).replace(/^oci:\/\//i,""),o=n.get("tag")||void 0,s=n.get("digest")||void 0,i=r.indexOf("@");i!==-1&&(s=s??(r.slice(i+1)||void 0),r=r.slice(0,i));let a=r.lastIndexOf("/"),c=r.indexOf(":",a+1);return c!==-1&&(o=o??(r.slice(c+1)||void 0)),{kind:"oci",...o!==void 0?{tag:o}:{},...s!==void 0?{digest:s}:{}}}function Ys(e){let t=e.trim();if(t.startsWith("./")||t.startsWith("../")||t.startsWith(".\\")||t.startsWith("..\\"))return{kind:"local"};let n=t.indexOf("?"),r=n===-1?t:t.slice(0,n),o=n===-1?"":t.slice(n+1);if(/^oci:\/\//i.test(r))return lee(r,o);let s=/^(git|hg)::/i.exec(r),i=s?r.slice(s[0].length):r;if(s||uee(i)){let a=MD(o);return{kind:"git",ref:a.get("ref")||a.get("rev")||void 0}}return cee(i)?{kind:"registry"}:{kind:"other"}}var dee=/^v?\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/,pee=/^[0-9a-f]{40}$/i,fee=new Set(["main","master","develop","trunk"]);function GD(e){return dee.test(e)||pee.test(e)}function FD(e){return fee.has(e)}var gee=new Set(["latest","main","master","develop","dev","stable","edge","nightly"]);function zD(e){return gee.has(e.toLowerCase())}function UD(e){return/^(?:=\s*)?v?\d+(?:\.\d+){0,2}(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/.test(e.trim())&&!e.includes(",")}var BD={id:"TF004",description:"Registry-sourced module block has no version",check(e){let t=[];for(let[n,r]of e.entities){if(r.entityType!==Zi||!Xe(r))continue;let o=r.props,s=typeof o.body=="object"&&o.body!==null?o.body:{},i=typeof s.source=="string"?s.source:void 0;if(i===void 0||Ys(i).kind!=="registry")continue;let a=s.version;if(typeof a=="string"&&a.trim()!=="")continue;let c=typeof o.address=="string"?o.address:n;t.push({checkId:"TF004",severity:"warning",message:`Module "${c}" sources "${i}" from a registry with no \`version\`. \`terraform init\` resolves to the newest release with no constraint to hold it back. Add a \`version\` constraint (e.g. \`~> 5.0\`).`,entity:n,lexicon:"terraform"})}return t}};var WD={id:"TF005",description:"Git/hg module source is unpinned, or pinned to a mutable ref",check(e){let t=[];for(let[n,r]of e.entities){if(r.entityType!==Zi||!Xe(r))continue;let o=r.props,s=typeof o.body=="object"&&o.body!==null?o.body:{},i=typeof s.source=="string"?s.source:void 0;if(i===void 0)continue;let a=Ys(i);if(a.kind!=="git")continue;let c=typeof o.address=="string"?o.address:n,u;a.ref?FD(a.ref)?u=`Module "${c}" sources "${i}" pinned to "${a.ref}", a mutable branch. Pin to a tag or a full commit SHA instead.`:GD(a.ref)||(u=`Module "${c}" sources "${i}" pinned to "${a.ref}", which is neither a tag nor a full commit SHA and can move. Pin to a tag or a full commit SHA instead.`):u=`Module "${c}" sources "${i}" from git/hg with no \`?ref=\`. Add one pinned to a tag or a full commit SHA.`,u&&t.push({checkId:"TF005",severity:"warning",message:u,entity:n,lexicon:"terraform"})}return t}};var HD={id:"TF038",description:"OCI module source has no tag or digest, or a mutable tag",check(e){let t=[];for(let[n,r]of e.entities){if(r.entityType!==Zi||!Xe(r))continue;let o=r.props,s=typeof o.body=="object"&&o.body!==null?o.body:{},i=typeof s.source=="string"?s.source:void 0;if(i===void 0)continue;let a=Ys(i);if(a.kind!=="oci"||a.digest)continue;let c=typeof o.address=="string"?o.address:n,u;a.tag?zD(a.tag)&&(u=`Module "${c}" sources "${i}" with the mutable tag "${a.tag}". Pin it to a digest (\`?digest=sha256:...\`) or an exact version tag (\`?tag=1.4.0\`).`):u=`Module "${c}" sources "${i}" with no tag or digest, so OpenTofu pulls the \`latest\` tag. Pin it to a digest (\`?digest=sha256:...\`) or an exact version tag (\`?tag=1.4.0\`).`,u&&t.push({checkId:"TF038",severity:"warning",message:u,entity:n,lexicon:"terraform"})}return t}};var qD={id:"TF039",description:"Registry module version is a range, not an exact version",check(e){let t=[];for(let[n,r]of e.entities){if(r.entityType!==Zi||!Xe(r))continue;let o=r.props,s=typeof o.body=="object"&&o.body!==null?o.body:{},i=typeof s.source=="string"?s.source:void 0;if(i===void 0||Ys(i).kind!=="registry")continue;let a=s.version;if(typeof a!="string"||a.trim()===""||UD(a))continue;let c=typeof o.address=="string"?o.address:n;t.push({checkId:"TF039",severity:"warning",message:`Module "${c}" sources "${i}" with the version range "${a}". A release inside the range changes what the root provisions with no diff to review. Pin an exact version (\`version = "1.4.0"\`).`,entity:n,lexicon:"terraform"})}return t}};var JD={id:"TF040",description:"Root has no committed .terraform.lock.hcl",auditOptIn:!0,check(e){let t=[],n=new Set;for(let r of e.entities.values()){if(!Xe(r)||!Ou(r))continue;let o=r.props;o.lockFile!==!1||o.mode==="live"||typeof o.root!="string"||n.add(o.root)}for(let r of[...n].sort()){let o=vx(e.entities,r);if(o.size===0)continue;let s=[...o.keys()].sort();t.push({checkId:"TF040",severity:"warning",message:`Root module "${r}" uses ${s.length===1?"provider":"providers"} ${s.map(i=>`"${i}"`).join(", ")} but has no ${yx} beside its .tf files. Run \`terraform init\` and commit the file, so every init selects the same provider versions.`,entity:o.get(s[0]),lexicon:"terraform"})}return t}};function Yi(e,t){let n=[];for(let[r,o]of e){if(o.entityType!==t||!Xe(o))continue;let s=o.props;n.push({key:r,address:typeof s.address=="string"?s.address:r,file:typeof s.file=="string"?s.file:"terragrunt.hcl",body:typeof s.body=="object"&&s.body!==null?s.body:{}})}return n}function KD(e){return typeof e=="string"&&e.includes("${")}function VD(e){let t=e.trim();if(t.includes("${"))return{kind:"other"};if(/^tfr:\/\//i.test(t)){let r=t.includes("?")?t.slice(t.indexOf("?")+1):"",o=new URLSearchParams(r).get("version");return{kind:"tfr",pinned:o!==null&&o.trim()!==""}}if(t.startsWith("/"))return{kind:"local"};let n=Ys(t);switch(n.kind){case"local":return{kind:"local"};case"git":return{kind:"git",pinned:n.ref!==void 0};case"oci":return{kind:"oci",pinned:n.tag!==void 0||n.digest!==void 0};default:return{kind:"other"}}}var ZD={id:"TF041",description:"Terragrunt dependency lets mock_outputs stand in for apply",check(e){let t=[];for(let n of Yi(e.entities,ug)){if(n.body.mock_outputs===void 0)continue;let r=n.body.mock_outputs_allowed_terraform_commands;if(KD(r))continue;let o;r===void 0||Array.isArray(r)&&r.length===0?o="sets no `mock_outputs_allowed_terraform_commands`, and Terragrunt reads a missing or empty list as every command":Array.isArray(r)&&r.includes("apply")&&(o='lists "apply" in `mock_outputs_allowed_terraform_commands`'),o!==void 0&&t.push({checkId:"TF041",severity:"warning",message:`Dependency "${n.address.replace(/^dependency\./,"")}" in ${n.file} sets \`mock_outputs\` and ${o}. An apply that runs before the upstream unit is applied writes the mock values to real state. Set \`mock_outputs_allowed_terraform_commands = ["validate", "plan"]\`.`,entity:n.key,lexicon:"terraform"})}return t}};var YD={id:"TF042",description:"Terragrunt dependency sets skip_outputs together with mock_outputs",check(e){let t=[];for(let n of Yi(e.entities,ug))n.body.skip_outputs!==!0||n.body.mock_outputs===void 0||t.push({checkId:"TF042",severity:"warning",message:`Dependency "${n.address.replace(/^dependency\./,"")}" in ${n.file} sets \`skip_outputs = true\` with \`mock_outputs\`. Terragrunt then never reads the upstream unit's outputs, so every plan and apply runs on the mock values. Drop \`skip_outputs\`, or pass the values as \`inputs\` so they are visible as configuration.`,entity:n.key,lexicon:"terraform"});return t}};var mee={git:"Add `?ref=` with a tag or commit SHA.",tfr:"Add `?version=` with an exact version.",oci:"Add `?digest=sha256:...` or `?tag=` with an exact version tag."},XD={id:"TF043",description:"Terragrunt terraform.source names no version (git ref, tfr version or oci tag)",check(e){let t=[];for(let n of Yi(e.entities,gx)){let r=n.body.source;if(typeof r!="string")continue;let o=VD(r);o.pinned===!1&&t.push({checkId:"TF043",severity:"warning",message:`The \`terraform\` block in ${n.file} sources "${r}" with no version, so a push or a release changes the unit with no diff to review. `+mee[o.kind],entity:n.key,lexicon:"terraform"})}return t}};var hee=/\bbackend\s+"local"/,QD={id:"TF044",description:"Terragrunt config keeps state in a local backend",check(e){let t=[],n=(r,o,s)=>{t.push({checkId:"TF044",severity:"warning",message:`${s} in ${o} keeps state in a local backend. The state lives on the runner's disk, so a later job cannot read it and a git-range run, which checks out each side in a temporary directory, never sees it. Use a shared backend such as S3 or GCS.`,entity:r,lexicon:"terraform"})};for(let r of Yi(e.entities,mx))r.body.backend==="local"&&n(r.key,r.file,'`remote_state` sets `backend = "local"`');for(let r of Yi(e.entities,hx)){let o=r.body.contents;typeof o=="string"&&hee.test(o)&&n(r.key,r.file,`\`generate "${r.address.replace(/^generate\./,"")}"\` writes a \`backend "local"\` block`)}return t}};var tM={TF002:CD,TF003:ND,TF004:BD,TF005:WD,TF038:HD,TF039:qD,TF040:JD,TF041:ZD,TF042:YD,TF043:XD,TF044:QD},yee="https://intentius.io/chant/lexicons/terraform/lint-rules/",wee="https://intentius.io/terragucci/reference/tips/",$x="terragucci-shared-subject",kx="terragucci-persistent-runner",iM="terragucci-environment-role",aM="terragucci-gitlab-audience",bee=10,vee=5,$ee=100,nM=(e,t,n)=>({rule:e,root:t,message:n,url:`${yee}#${e.toLowerCase()}`}),Pu=(e,t,n)=>({rule:e,...n!==void 0?{root:n}:{},message:t,url:`${wee}#${e}`});async function cM(e,t,n){let r=[];if(!rM(oc(e,t)))return r;for(let o of oM(oc(e,t)).filter(s=>s.endsWith(".tf")).sort()){let s=await n.parse(o,sM(oc(e,t,o),"utf-8"));for(let i of Object.values(s.module??{}))for(let a of i)typeof a.source=="string"&&r.push({source:a.source,...typeof a.version=="string"?{version:a.version}:{}})}return r}async function kee(e,t,n){let r=[];if(!rM(oc(e,t)))return r;for(let o of oM(oc(e,t)).filter(s=>s.endsWith(".tf")).sort()){let s=await n.parse(o,sM(oc(e,t,o),"utf-8"));for(let i of s.terraform??[])for(let a of i.required_providers??[])for(let[c,u]of Object.entries(a)){let l=u?.version;typeof l=="string"&&ag(l)===null&&r.push({name:c,version:l})}}return r}async function uM(e,t,n,r=new Set){let o=new Set;if(r.has(t))return o;r.add(t);for(let{source:s}of await cM(e,t,n)){if(!/^\.\.?\//.test(s))continue;let i=eM.normalize(eM.join(t==="."?"":t,s))||".";o.add(i);for(let a of await uM(e,i,n,r))o.add(a)}return o}async function Lw(e,t,n){if(n.settings.tips===!1)return[];let r=[],{parser:o}=n;if(o){let a=(n.rules??Object.keys(tM)).map(l=>tM[l]).filter(l=>l!==void 0),c=new Set,u=new Map;for(let l of n.configDirs??[]){let d=await wx(oc(e,l),l,o),p={outputs:new Map,entities:d};for(let f of a)for(let g of await f.check(p))r.push(nM(f.id,l,g.message))}for(let l of t){let d=await wx(oc(e,l),l,o),p={outputs:new Map,entities:d};for(let f of a)for(let g of await f.check(p))r.push(nM(f.id,l,g.message)),(f.id==="TF038"||f.id==="TF039")&&c.add(l);for(let f of await uM(e,l,o))u.set(f,(u.get(f)??new Set).add(l));for(let{name:f,version:g}of await kee(e,l,o))r.push(Pu("terragucci-floating-range",`Provider ${f} is constrained to the range "${g}". tf-rollout --provider moves the lock file, and a version outside the range fails init, so a rollout cannot cross it. Pin the exact version, or widen the range on purpose.`,l));if(!c.has(l))for(let{source:f,version:g}of await cM(e,l,o))Ki(f,g).unpinned?.includes("is a constraint")&&r.push(Pu("terragucci-floating-range",`${f} is pinned to the range "${g}". A range cannot be bumped by a pin, so tf-rollout cannot move it in waves. Pin one version.`,l))}for(let[l,d]of[...u].sort(([p],[f])=>p<f?-1:1))d.size<bee||r.push(Pu("terragucci-shared-module",`${l} is included by ${d.size} roots, so every change under it plans every one of them. Publish it with tf-publish and pin it in each root, and only the roots whose pin moves are planned.`,l))}t.length>vee&&(n.settings.waves?.canary??[]).length===0&&r.push(Pu("terragucci-no-canary",`The project has ${t.length} roots and no waves.canary, so the first wave is chosen by apply order. Name one or two low-risk roots there, since the first wave is the riskiest.`));let s=n.destroying??[];if(n.settings.gate==="never"&&s.length>0)for(let a of[...s].sort())r.push(Pu("terragucci-ungated-destroy",`${a} plans a destroy or replacement and gate is never, so nothing stops it but the forge's own reviewers. Use gate: on-destructive.`,a));for(let a of n.renames??[])r.push(Pu("terragucci-moved",`${a.from} is destroyed and ${a.to} created with the same configuration, so the block was renamed. A moved block (from = ${a.from}, to = ${a.to}) makes the plan move it instead; terragucci respond tips --report opens a pull request that adds it.`,a.root));let i=n.planned??t.length;return i>=$ee&&r.push(Pu("terragucci-many-roots",`${i} roots are planned on every change. Publishing shared modules and pinning them in each root shrinks the set a change reaches.`)),r}function Gw(e){return e.map(t=>`tip (${t.rule}): ${t.root?`${t.root}: `:""}${t.message}`)}var lg=(e,t)=>e?Object.keys(e).find(n=>Pe(n,t)):void 0;function Sx(e){if(e!==void 0)return typeof e=="string"||Array.isArray(e)||"group"in e?e:e.apply}function _x(e,t){if(!e)return;let n=i=>{if(!i)return;let a=new Set(t.map(u=>lg(i,u)).filter(u=>u!==void 0)),c=Object.fromEntries(Object.entries(i).filter(([u])=>a.has(u)));return Object.keys(c).length>0?c:void 0},r=i=>i!==void 0&&t.every(a=>lg(i,a)!==void 0),o={...e},s=n(e.roles);if(s?o.roles=s:delete o.roles,r(e.roles)&&(delete o.plan_role,delete o.apply_role),e.gcp){let i=n(e.gcp.roles),{roles:a,...c}=e.gcp;if(o.gcp={...c,...i?{roles:i}:{}},i&&r(e.gcp.roles)){let u=Object.values(i)[0];o.gcp={...o.gcp,plan_service_account:u.plan,apply_service_account:u.apply}}}if(e.azure){let i=n(e.azure.roles),{roles:a,...c}=e.azure;if(o.azure={...c,...i?{roles:i}:{}},i&&r(e.azure.roles)){let u=Object.values(i)[0];o.azure={...o.azure,plan_client_id:u.plan,apply_client_id:u.apply}}}return o}function _ee(e){return e.toLowerCase().replace(/\*+/g,"").replace(/[^a-z0-9_-]+/g,"-").replace(/^-+|-+$/g,"")}function sc(e,t){let n=Zs(e.oidc,e.environments),r=[...t].sort(),o=[],s=u=>{u.length>0&&o.push({name:Ka,selectors:[],roots:u,...n?{oidc:_x(n,u)}:{}})};if(e.environments&&Object.keys(e.environments).length>0){for(let[u,l]of Object.entries(e.environments)){let d=r.filter(f=>hs(f,e)===u);if(d.length===0)continue;let p=Sx(l.runner);o.push({name:u,selectors:[...l.roots??[]],roots:d,environment:u,...n?{oidc:_x(n,d)}:{},...p!==void 0?{runner:p}:{},forgeEnvironment:l.forge_environment??u,...l.oidc?.audience!==void 0?{audience:l.oidc.audience}:{}})}return s(r.filter(u=>hs(u,e)===void 0)),o.some(u=>u.environment!==void 0)?o:void 0}if(!n||!(n.roles||n.gcp?.roles||n.azure?.roles))return;let i=new Map,a=[];for(let u of r){let l=[lg(n.roles,u),lg(n.gcp?.roles,u),lg(n.azure?.roles,u)];if(l.every(f=>f===void 0)){a.push(u);continue}let d=JSON.stringify(l),p=i.get(d)??{globs:l,roots:[]};p.roots.push(u),i.set(d,p)}if(i.size+(a.length>0?1:0)<2)return;let c=new Set;for(let{globs:u,roots:l}of i.values()){let d=_ee(u.find(f=>f!==void 0))||"group";mR(d)&&(d=`${d}-roots`);let p=d;for(let f=2;c.has(p);f++)p=`${d}-${f}`;c.add(p),o.push({name:p,selectors:l,roots:l,oidc:_x(n,l)})}return s(a),o}var lM=e=>e.length<2?e.join(""):`${e.slice(0,-1).join(", ")} and ${e[e.length-1]}`;function dM(e,t){if(!e||e.length<2)return;if(e.some(o=>o.environment!==void 0)&&t!=="forgejo"){let o=new Map;for(let i of e)i.environment!==void 0&&i.forgeEnvironment!==void 0&&o.set(i.forgeEnvironment,[...o.get(i.forgeEnvironment)??[],i.name]);let s=[...o].filter(([,i])=>i.length>1);return s.length===0?void 0:s.map(([i,a])=>`the apply jobs of the environments ${lM(a)} run in the same forge environment ${i}, so their tokens carry the same environment and a role's trust cannot tell one environment's jobs from another's; give each environment a forge_environment of its own`).join("; ")}let r=`the apply jobs of the groups ${lM(e.map(o=>o.name))} carry the same OIDC token subject, so a role's trust cannot tell one group's jobs from another's`;return t==="forgejo"?`${r}; they apply from the same ref, and Forgejo has no deployment environments, so its token names the ref alone: keep the groups' roles apart with a repo or a protected branch each`:`${r}; on GitHub or GitLab, name each group's roots in environments, each with a forge_environment, and have each apply role trust its environment alone`}function Fw(e,t){return Object.entries(t).find(([,n])=>n.some(r=>r===e||Pe(r,e)))?.[0]??Ka}function Rx(e,t){let n=new Map([...Object.keys(t).map(r=>[r,[]]),[Ka,[]]]);for(let r of e)n.get(Fw(r,t)).push(r);return[...n].filter(([,r])=>r.length>0).map(([r,o])=>({name:r,roots:[...o].sort()}))}var pM=e=>e.filter(t=>t.name!==Ka).map(t=>`${t.name}=${t.selectors.join(",")}`).join(";");var See="terragucci-check-job",zw=e=>e==null||e===""?"none":e;function Ree(e){if(e===void 0)return;let t=e.trim();if(t.startsWith("["))try{let n=JSON.parse(t);if(Array.isArray(n))return n.map(String)}catch{}return t===""?[]:t.split(",").map(n=>n.trim()).filter(n=>n!=="")}function xee(e){let t=e.split(".")[1];if(!t)throw new Error("the forge's OIDC token is not a JWT");let n=Buffer.from(t.replace(/-/g,"+").replace(/_/g,"/"),"base64").toString("utf-8"),r=JSON.parse(n);if(!r||typeof r!="object")throw new Error("the forge's OIDC token has no claims");return r}function fM(e,t){let n={problems:[],checked:[],skipped:[]},r=e.forgeEnvironment;r!==void 0&&e.forge!=="forgejo"?t.forgeEnvironmentError!==void 0?n.problems.push(`forge environment: expected ${r}, and it could not be read: ${t.forgeEnvironmentError}`):t.forgeEnvironment!==r?n.problems.push(`forge environment: expected ${r}, found ${zw(t.forgeEnvironment)}`):n.checked.push(`forge environment ${r}`):e.forge==="forgejo"&&n.skipped.push("forge environment (Forgejo has none)");let o=e.branches.filter(s=>s!=="");if(o.length===0)n.problems.push("ref: no branch to apply from was given (the default branch's name was empty)");else if(e.forge==="gitlab")!t.ref||!o.includes(t.ref)?n.problems.push(`ref: expected ${o.join(" or ")}, found ${zw(t.ref)}`):n.checked.push(`ref ${t.ref}`),t.refProtected!=="true"?n.problems.push(`ref: expected a protected ref (CI_COMMIT_REF_PROTECTED true), found ${zw(t.refProtected)}`):n.checked.push("protected ref");else{let s=o.map(i=>`refs/heads/${i}`);!t.ref||!s.includes(t.ref)?n.problems.push(`ref: expected ${s.join(" or ")}, found ${zw(t.ref)}`):n.checked.push(`ref ${t.ref}`)}if(e.forge==="gitlab"){let s=e.runnerTags??[];if(s.length>0){let i=t.runnerTags,a=s.filter(c=>!(i??[]).includes(c));i===void 0?n.problems.push(`runner: expected tags ${s.join(", ")}, and the job has no CI_RUNNER_TAGS`):a.length>0?n.problems.push(`runner: expected tags ${s.join(", ")}, found ${i.length?i.join(", "):"none"}`):n.checked.push(`runner tags ${s.join(", ")}`)}}else e.selfHosted&&(t.runnerEnvironment===void 0?n.skipped.push("runner (the runner says nothing about itself: no RUNNER_ENVIRONMENT)"):t.runnerEnvironment!=="self-hosted"?n.problems.push(`runner: expected self-hosted, found ${t.runnerEnvironment}`):n.checked.push("self-hosted runner"));return n}async function Eee(e,t){let n=e.ACTIONS_ID_TOKEN_REQUEST_URL,r=e.ACTIONS_ID_TOKEN_REQUEST_TOKEN;if(!n||!r)throw new Error("the job may request no OIDC token (no ACTIONS_ID_TOKEN_REQUEST_URL); it needs permissions: id-token: write");let o=await t(`${n}&audience=${encodeURIComponent(See)}`,{headers:{authorization:`bearer ${r}`}});if(!o.ok)throw new Error(`the OIDC token request answered ${o.status}`);let s=(await o.json()).value;if(typeof s!="string")throw new Error("the OIDC token request answered no token");return xee(s)}async function gM(e,t=process.env,n=fetch){if(e.forge==="gitlab")return{forgeEnvironment:t.CI_ENVIRONMENT_NAME||null,...t.CI_COMMIT_REF_NAME!==void 0?{ref:t.CI_COMMIT_REF_NAME}:{},...t.CI_COMMIT_REF_PROTECTED!==void 0?{refProtected:t.CI_COMMIT_REF_PROTECTED}:{},...t.CI_RUNNER_TAGS!==void 0?{runnerTags:Ree(t.CI_RUNNER_TAGS)}:{}};let r={...t.GITHUB_REF!==void 0?{ref:t.GITHUB_REF}:{}},o;if(e.forge==="github"&&e.forgeEnvironment!==void 0)try{o=await Eee(t,n),r.forgeEnvironment=typeof o.environment=="string"&&o.environment!==""?o.environment:null}catch(i){r.forgeEnvironmentError=i.message}let s=typeof o?.runner_environment=="string"?o.runner_environment:t.RUNNER_ENVIRONMENT;return s&&(r.runnerEnvironment=s),r}function mM(e,t){let n=`terragucci check-job: environment ${e.environment}`;return t.problems.length>0?{ok:!1,out:[],err:[...t.problems.map(r=>`${n}: ${r}`),`${n}: this job does not run where ${e.environment}'s apply is set to run, so it stops before any cloud credential is taken`]}:{ok:!0,out:[`${n}: ${t.checked.length?t.checked.join(", "):"nothing to check"}${t.skipped.length?`; not checked: ${t.skipped.join(", ")}`:""}`],err:[]}}import{spawnSync as Kte}from"node:child_process";import{mkdtempSync as Vte,rmSync as Zte}from"node:fs";import{hostname as iL,tmpdir as Yte}from"node:os";import{basename as Xte,join as sL}from"node:path";import{existsSync as Zx,readFileSync as qte}from"node:fs";import{join as Nu}from"node:path";import{spawn as Ste}from"node:child_process";import{join as Rte}from"node:path";import{spawn as Tee,spawnSync as Aee}from"node:child_process";import{createHash as Oee}from"node:crypto";import{chmodSync as Pee,existsSync as cc,mkdirSync as xM,mkdtempSync as pg,readdirSync as EM,readFileSync as Ox,renameSync as SM,rmSync as fg,statSync as Tx,writeFileSync as gg}from"node:fs";import{tmpdir as Sd}from"node:os";import{dirname as Px,join as Ut,relative as jee,resolve as jx}from"node:path";var hM="terragucci.report/v1";var Ln="(sensitive, redacted by terragucci)";function Vr(e){let t=e.terragrunt?.pin?` under Terragrunt ${e.terragrunt.version} (${e.terragrunt.pin})`:"";return`${e.name}${e.version?` ${e.version}`:""}${e.pin?` (${e.pin})`:""}${t}`}function Uw(e,t){return e==="tf-drift"&&t==="delete"?"deleted outside Terraform":{delete:"destroy",replace:"replace",refused:"refused to plan",forget:"forget",import:"import"}[t]}var Ae=e=>e!==null&&typeof e=="object"&&!Array.isArray(e);function Xi(e,t,n){if(e===void 0)return e;if(t===!0)return e===null?null:(n.n++,Ln);if(Array.isArray(t)&&Array.isArray(e))return e.map((r,o)=>Xi(r,t[o],n));if(Ae(t)&&Ae(e)){let r={...e};for(let o of Object.keys(t))o in r&&(r[o]=Xi(r[o],t[o],n));return r}return e}function xx(e,t){return Ae(e)?{...e,..."before"in e?{before:Xi(e.before,e.before_sensitive,t)}:{},..."after"in e?{after:Xi(e.after,e.after_sensitive,t)}:{}}:e}function wM(e,t){if(!Ae(e))return e;let n={...e};return Array.isArray(e.resources)&&(n.resources=e.resources.map(r=>Ae(r)?{...r,values:Xi(r.values,r.sensitive_values,t)}:r)),Array.isArray(e.child_modules)&&(n.child_modules=e.child_modules.map(r=>wM(r,t))),n}function yM(e,t){if(!Ae(e))return e;let n={...e};return Ae(e.outputs)&&(n.outputs=Object.fromEntries(Object.entries(e.outputs).map(([r,o])=>[r,Ae(o)&&o.sensitive===!0?{...o,value:Xi(o.value,!0,t)}:o]))),"root_module"in e&&(n.root_module=wM(e.root_module,t)),n}function ys(e){let t=kM(e),n=$M(e,t.plan);if(n.length===0)return t;let r={n:t.values};return{plan:Bw(t.plan,n,r),values:r.n}}function bM(e){return $M(e,kM(e).plan)}function vM(e,t){return t.length===0?e:Bw(e,t,{n:0})}function $M(e,t){let n=new Set,r=s=>{typeof s=="string"?s!==""&&s!==Ln&&n.add(s):Array.isArray(s)?s.forEach(r):Ae(s)&&Object.values(s).forEach(r)},o=(s,i)=>{if(i===Ln&&s!==Ln)r(s);else if(Array.isArray(s)&&Array.isArray(i))s.forEach((a,c)=>o(a,i[c]));else if(Ae(s)&&Ae(i))for(let a of Object.keys(s))o(s[a],i[a])};return o(e,t),[...n].sort((s,i)=>i.length-s.length)}function Bw(e,t,n){if(typeof e=="string"){if(e===Ln)return e;if(t.includes(e))return n.n++,Ln;let r=e;for(let o of t)o.length>=8&&r.includes(o)&&(n.n++,r=r.split(o).join(Ln));return r}return Array.isArray(e)?e.map(r=>Bw(r,t,n)):Ae(e)?Object.fromEntries(Object.entries(e).map(([r,o])=>[r,Bw(o,t,n)])):e}function kM(e){if(!Ae(e))return{plan:e,values:0};let t={n:0},n={...e};for(let s of["resource_changes","resource_drift"]){let i=e[s];Array.isArray(i)&&(n[s]=i.map(a=>Ae(a)?{...a,change:xx(a.change,t)}:a))}Array.isArray(e.deferred_changes)&&(n.deferred_changes=e.deferred_changes.map(s=>Ae(s)&&Ae(s.resource_change)?{...s,resource_change:{...s.resource_change,change:xx(s.resource_change.change,t)}}:s)),Array.isArray(e.action_invocations)&&(n.action_invocations=e.action_invocations.map(s=>Ae(s)&&"config_values"in s?{...s,config_values:Xi(s.config_values,s.config_sensitive,t)}:s)),Ae(e.output_changes)&&(n.output_changes=Object.fromEntries(Object.entries(e.output_changes).map(([s,i])=>[s,xx(i,t)])));for(let s of["planned_values","prior_state"])s in e&&(n[s]=s==="prior_state"&&Ae(e.prior_state)?{...e.prior_state,values:yM(e.prior_state.values,t)}:yM(e[s],t));let r=Ae(e.configuration)&&Ae(e.configuration.root_module)?e.configuration.root_module:{},o=Ae(r.variables)?r.variables:{};return Ae(e.variables)&&(n.variables=Object.fromEntries(Object.entries(e.variables).map(([s,i])=>{let a=o[s];return[s,Ae(a)&&a.sensitive===!0&&Ae(i)?{...i,value:Xi(i.value,!0,t)}:i]}))),Ae(e.configuration)&&(n.configuration={...e.configuration,root_module:_M(e.configuration.root_module,t)}),{plan:n,values:t.n}}function _M(e,t){if(!Ae(e))return e;let n={...e};return Ae(e.variables)&&(n.variables=Object.fromEntries(Object.entries(e.variables).map(([r,o])=>[r,Ae(o)&&o.sensitive===!0&&"default"in o?{...o,default:Xi(o.default,!0,t)}:o]))),Ae(e.module_calls)&&(n.module_calls=Object.fromEntries(Object.entries(e.module_calls).map(([r,o])=>[r,Ae(o)?{...o,module:_M(o.module,t)}:o]))),n}var Ww="0.71.0",Iee={x86_64:"765dfefdf0730693d7541ea0147e10d530a54ed257bbfec055ff34c837ffdf72",arm64:"542f255081cfab9919cba327d3c4014fd46d6ee3f0441c634b14b1465e9469d9"},Ex="1.21.1",Cee={x86_64:"668506eb17a2eaa1fce6cc0d1f42ef85125d4ac5bda5fc74d1152d0c77145031",arm64:"9a1f3625529c6f01240fe68286dde06aa0b23c5253da700ac48f4d943ff8a4de"},mg=(e,t,n)=>new Promise(r=>{let o=Tee(e,t,{cwd:n,stdio:["ignore","pipe","pipe"],env:Ve(process.env)}),s=[],i=[],a;o.stdout.on("data",c=>s.push(c)),o.stderr.on("data",c=>i.push(c)),o.on("error",c=>a=c),o.on("close",c=>r({status:a?null:c,stdout:Buffer.concat(s).toString("utf-8"),stderr:`${Buffer.concat(i).toString("utf-8")}${a?a.message:""}`}))});async function Ix(e,t,n={}){let r=n.exec??mg,o=e.engine??"conftest";if((await r(o,o==="opa"?["version"]:["--version"],t)).status===0)return o;let i=(n.arch??process.arch)==="arm64"?"arm64":"x86_64",a=Ut(n.cache??Sd(),`terragucci-${o}-${o==="opa"?Ex:Ww}-${i}`),c=Ut(a,o);if(cc(c))return c;let u=o==="conftest",l=u?Ww:Ex,d=u?`conftest_${Ww}_Linux_${i}.tar.gz`:`opa_linux_${i==="arm64"?"arm64":"amd64"}_static`,p=u?`https://github.com/open-policy-agent/conftest/releases/download/v${Ww}/${d}`:`https://github.com/open-policy-agent/opa/releases/download/v${Ex}/${d}`,f=await(n.download??Nee)(p),g=Oee("sha256").update(f).digest("hex");if(g!==(u?Iee:Cee)[i])throw new Error(`${o} ${l} from ${p} does not match its pinned digest (got ${g}); not running it`);let m=pg(Ut(Sd(),`terragucci-${o}-`));try{if(gg(Ut(m,d),f),u){let h=await r("tar",["-xzf",Ut(m,d),"-C",m,"conftest"],m);if(h.status!==0)throw new Error(`could not unpack conftest: ${h.stderr.trim()}`)}else SM(Ut(m,d),Ut(m,"opa"));xM(a,{recursive:!0}),SM(Ut(m,o),c),Pee(c,493)}finally{fg(m,{recursive:!0,force:!0})}return c}async function Nee(e){let t=await fetch(e);if(!t.ok)throw new Error(`${e}: ${t.status}`);return Buffer.from(await t.arrayBuffer())}var Dee=/^(deny|violation)(_[A-Za-z0-9]+)*$/,Mee=/^warn(_[A-Za-z0-9]+)*$/,Ax=e=>e.replace(/^data\./,"");function TM(e){return typeof e=="string"?e:e!==null&&typeof e=="object"&&typeof e.msg=="string"?e.msg:JSON.stringify(e)}function Lee(e){let t;try{t=JSON.parse(e)}catch{return}if(!Array.isArray(t))return;let n={violations:[],warnings:[],rules:[]};for(let r of t){for(let o of r.failures??[])n.violations.push(typeof o.msg=="string"?o.msg:JSON.stringify(o.msg)),n.rules.push(typeof o.metadata?.query=="string"?Ax(o.metadata.query):`${r.namespace??"main"}.deny`);for(let o of r.warnings??[])n.warnings.push(typeof o.msg=="string"?o.msg:JSON.stringify(o.msg))}return n}function AM(e,t,n,r){for(let[o,s]of Object.entries(e).sort(([i],[a])=>i<a?-1:i>a?1:0)){let i=Dee.test(o),a=i?t.violations:Mee.test(o)?t.warnings:void 0;if(a){let c=a.length;if(Array.isArray(s)?a.push(...s.map(TM)):s===!0?a.push(o):typeof s=="string"&&a.push(s),i)for(let u=c;u<a.length;u++)t.rules.push(`${r}.${o}`);continue}n&&s!==null&&typeof s=="object"&&!Array.isArray(s)&&AM(s,t,!1,`${r}.${o}`)}}function Gee(e,t=!1,n="main"){let r=OM(e);if(r!==void 0)return r.value===void 0?{violations:[],warnings:[],rules:[]}:PM(r.value,t,n)}function OM(e){let t;try{t=JSON.parse(e)}catch{return}if(t===null||typeof t!="object"||t.errors!==void 0)return;let n=t.result?.[0]?.expressions?.[0]?.value;return n===void 0?{}:{value:n}}function PM(e,t,n){if(Array.isArray(e))return{violations:e.map(TM),warnings:[],rules:e.map(()=>Ax(n))};if(e===null||typeof e!="object")return;let r={violations:[],warnings:[],rules:[]};return AM(e,r,t,Ax(n)),r}function Fee(e){let t=n=>n===null?"":n.toFixed(2);return{prior_monthly_cost:t(e.root.past_monthly_total),proposed_monthly_cost:t(e.root.monthly_total),delta_monthly_cost:t(e.root.monthly_delta)}}function zee(e){let t=(e.project??"").split("/"),n=t.length>=3?t.slice(1,-1).join("/"):"",r=t.length>=3?t[t.length-1]:e.project??"";return{id:"",message:e.pullRequest?`pull request ${e.pullRequest}`:"",commit_sha:e.commit??"",is_destroy:!1,refresh:!0,refresh_only:!1,replace_addrs:[],speculative:e.stage!=="tf-apply",target_addrs:[],variables:{},organization:{name:n},project:{id:"",name:r},workspace:{id:"",name:e.root,description:"",execution_mode:"agent",auto_apply:!1,tags:[],working_directory:e.root,vcs_repo:{identifier:[n,r].filter(Boolean).join("/"),display_identifier:[n,r].filter(Boolean).join("/"),branch:"",ingress_submodules:!1}}}}function Uee(e,t,n){let r=n?.cost,o=n?.review;if((e.input??"plan")!=="hcp"){if(!r&&!o)return t;let i=JSON.parse(t);return JSON.stringify({...i,...r?{cost:r}:{},...o?{review:o}:{}})}let s=zee(n??{root:""});return r&&(s.cost_estimate=Fee(r)),`{"plan":${t},"run":${JSON.stringify(s)}${r?`,"cost":${JSON.stringify(r)}`:""}${o?`,"review":${JSON.stringify(o)}`:""}}`}function Bee(e,t,n){return(e.engine??"conftest")==="opa"?["eval","--format","json","--data",t,"--input",n,`data.${Cx(e)}`]:["test","--no-color","--output","json","--policy",t,...e.namespace?["--namespace",e.namespace]:["--all-namespaces"],n]}function Cx(e){return e.namespace??(e.input==="hcp"?"terraform.policies":"main")}var ic="policies.hcl";function Wee(e){let t=Ut(e,ic);if(!cc(t))return;let n=Ox(t,"utf-8").replace(/^\s*(#|\/\/).*$/gm,""),r=[];for(let o of n.matchAll(/policy\s+"([^"]+)"\s*\{([^}]*)\}/g)){let s=Object.fromEntries([...o[2].matchAll(/([A-Za-z_]+)\s*=\s*"((?:[^"\\]|\\.)*)"/g)].map(a=>[a[1],a[2].replace(/\\(.)/g,"$1")]));if(!s.query)return`${ic}: policy "${o[1]}" has no query`;let i=s.enforcement_level??"advisory";if(i!=="advisory"&&i!=="mandatory")return`${ic}: policy "${o[1]}" has enforcement_level ${i}; HCP Terraform reads advisory or mandatory`;r.push({name:o[1],query:s.query,level:i})}return r.length===0?`${ic} declares no policy block`:r}function Hee(e,t){if(t===void 0)return e;let n=`data.${t}`;return e.filter(r=>r.query===n||r.query.startsWith(`${n}.`))}async function qee(e,t,n,r,o,s,i){let a=Hee(t,n.namespace);if(a.length===0)return{violations:[],error:`no policy in ${ic} has a query under data.${n.namespace}`};let c={violations:[],warnings:[],rules:[]};for(let u of a){let l=await i(e,["eval","--format","json","--data",r,"--input",o,u.query],s),d=OM(l.stdout);if(d===void 0)return{violations:[],error:`opa gave no verdict for policy ${u.name} (exit ${l.status}): ${(l.stderr||l.stdout).trim().split(`
|
|
83
|
+
`),r=new Map,o=new Map,s,i,a=0;for(let c=0;c<n.length;c++){let u=n[c],l=c+1,d=u.trim();i===void 0&&d!==""&&(i=l);let p=MQ.exec(u);if(p){let f=p[1],{ids:g,expires:m}=FQ(p[2].trim());if(f==="-file")s={form:"chant-ignore-file",ids:g,expires:m,file:e,line:l,misplaced:l!==i,key:`${e}:${l}:chant-ignore-file`};else{let h=f==="-block"?"chant-ignore-block":"chant-ignore",y={form:h,ids:g,expires:m,file:e,line:l,key:`${e}:${l}:${h}`},w=l+1,b=o.get(w)??[];b.push(y),o.set(w,b)}}else if(a===0){let f=CQ.exec(u);f?ix(r,sx(f[1],[]),l):(f=NQ.exec(u))?ix(r,sx(f[1],[f[2]]),l):(f=DQ.exec(u))&&ix(r,sx(f[1],[f[2],f[3]]),l)}a+=GQ(u)}return{blockLineQueues:r,byAnchorLine:o,fileDirective:s}}function zQ(e,t){let n=e.blockLineQueues.get(t);if(!(!n||n.length===0))return n.shift()}function OD(e,t){let n=zQ(e,t),r=n!==void 0?e.byAnchorLine.get(n)??[]:[],o=e.fileDirective?[...r,e.fileDirective]:r;return{line:n,suppressions:o.length>0?o:void 0}}var cg="Terraform::Terraform",dx="Terraform::Provider",px="Terraform::Resource",fx="Terraform::Data",Zi="Terraform::Module",BQ="Terraform::Variable",WQ="Terraform::Output",HQ="Terraform::Locals",cx="Terraform::Live",qQ="Terraform::TerragruntConfig",gx="Terraform::TerragruntSource",ug="Terraform::TerragruntDependency",mx="Terraform::TerragruntRemoteState",hx="Terraform::TerragruntGenerate",jD=["root.hcl","terragrunt.hcl"];function JQ(e){return jD.includes(e)}var ux="estate.chdf.hcl";function KQ(e){if("count"in e)return"count";if("for_each"in e)return"for_each"}function VQ(e,t,n,r,o,s,i="",a,c){let u=KQ(n);return{[gu]:!0,lexicon:"terraform",entityType:e,kind:"resource",stack:o,props:{address:t,body:n,file:r,root:o,source:i,line:a,...u?{expansion:u}:{},...s?.mode!==void 0?{mode:s.mode}:{},...s?.estate!==void 0?{estate:s.estate}:{},...s?.workspace!==void 0?{workspace:s.workspace}:{},...s?.delete!==void 0?{delete:s.delete}:{},...s?.configEstate!==void 0?{configEstate:s.configEstate}:{},...s?.callers!==void 0&&s.callers.length>0?{callers:s.callers}:{}},suppressions:c}}function ZQ(e){return UQ(e).filter(t=>t.endsWith(".tf")).sort()}function YQ(e){return jD.filter(t=>lx(Mw(e,t)))}function XQ(e){let t=Mw(e,ux);if(lx(t))return{name:ux,source:PD(t,"utf-8")}}function Vi(e){return Array.isArray(e)?e:[]}function rc(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)?e:{}}function QQ(e){let t=[];for(let n of Vi(e.terraform)){let r=rc(n);for(let o of Vi(r.live))t.push(rc(o))}return t}var yx=".terraform.lock.hcl";async function eee(e,t,n,r,o,s=[]){let i=n??await AD(),a=new Map,c=s.length>0?`${t}/${s.join("/")}`:t,u=(m,h,y,w,b,k=h)=>{let{line:v,suppressions:$}=OD(b,k),_=VQ(m,h,typeof y=="object"&&y!==null?y:{},w.name,t,{...r?.workspace!==void 0?{workspace:r.workspace}:{},...r?.delete!==void 0?{delete:r.delete}:{},...r?.configEstate!==void 0?{configEstate:r.configEstate}:{},...s.length>0?{callers:s}:{}},w.source,v,$),E=`${c}/${h}`;for(let R=2;a.has(E);R++)E=`${c}/${h}~${R}`;a.set(E,_)},l=(m,h,y,w,b)=>{for(let k of Vi(m[h]))u(y,h,k,w,b)},d=(m,h,y,w,b,k)=>{for(let[v,$]of Object.entries(rc(m[h])))for(let _ of Vi($))u(y,w(v),_,b,k)},p=(m,h,y,w,b,k)=>{for(let[v,$]of Object.entries(rc(m[h])))for(let[_,E]of Object.entries(rc($)))for(let R of Vi(E))u(y,w(v,_),R,b,k)};for(let m of e){let h=await i.parse(m.name,m.source),y=ax(m.name,m.source);if(JQ(m.name)){tee(h,m,y,u);continue}l(h,"terraform",cg,m,y);for(let w of QQ(h))u(cx,"live",w,m,y);l(h,"locals",HQ,m,y),d(h,"provider",dx,w=>`provider.${w}`,m,y),d(h,"module",Zi,w=>`module.${w}`,m,y),d(h,"variable",BQ,w=>`var.${w}`,m,y),d(h,"output",WQ,w=>`output.${w}`,m,y),p(h,"resource",px,(w,b)=>`${w}.${b}`,m,y),p(h,"data",fx,(w,b)=>`data.${w}.${b}`,m,y)}if(o){let m=await i.parse(o.name,o.source);typeof m.estate=="string"&&u(cx,"live",m,o,ax(o.name,o.source))}let f;for(let m of a.values()){if(m.entityType!==cx)continue;let h=m.props,y=typeof h.body.estate=="string"?h.body.estate:void 0;y!==void 0&&(f===void 0||h.file===ux)&&(f=y)}let g=r?.binary==="choudoufu"&&f!==void 0?"live":"state";for(let[m,h]of a){let y=h,w={...y,props:{...y.props,mode:g,...g==="live"?{estate:f}:{},...r?.lockFile!==void 0?{lockFile:r.lockFile}:{}}};a.set(m,w)}return a}function tee(e,t,n,r){let o={includes:Object.keys(rc(e.include))};typeof e.terragrunt_version_constraint=="string"&&(o.terragrunt_version_constraint=e.terragrunt_version_constraint),r(qQ,"terragrunt",o,t,n,"");for(let s of Vi(e.terraform))r(gx,"terragrunt.terraform",s,t,n,"terraform");for(let s of Vi(e.remote_state))r(mx,"remote_state",s,t,n);for(let[s,i]of Object.entries(rc(e.dependency)))for(let a of Vi(i))r(ug,`dependency.${s}`,a,t,n);for(let[s,i]of Object.entries(rc(e.generate)))for(let a of Vi(i))r(hx,`generate.${s}`,a,t,n)}async function wx(e,t,n,r,o=[]){let i=[...ZQ(e),...o.length===0?YQ(e):[]].map(u=>({name:u,source:PD(Mw(e,u),"utf-8")})),a=o.length===0?XQ(e):void 0,c=o.length===0?{...r,lockFile:lx(Mw(e,yx))}:r;return eee(i,t,n,c,a,o)}function nee(e){if(!Xe(e))return[];let t=e.props;return Array.isArray(t.callers)?t.callers:[]}function Ou(e){return nee(e).length===0}var bx=new Set(["terraform"]);function ID(e){return e.split("_")[0]}function vx(e,t){let n=new Map,r=new Set;for(let[o,s]of e){if(!Xe(s)||!Ou(s))continue;let i=s.props;if(i.root!==t)continue;let a=typeof i.address=="string"?i.address:"";if(s.entityType===dx){let c=a.slice(9);if(bx.has(c))continue;n.set(c,o),r.add(c)}else if(s.entityType===px){let c=a.split(".")[0]??"",u=ID(c);if(bx.has(u)||r.has(u))continue;n.has(u)||n.set(u,o)}else if(s.entityType===fx){let c=a.split(".")[1]??"",u=ID(c);if(bx.has(u)||r.has(u))continue;n.has(u)||n.set(u,o)}}return n}function ree(e){let t={},n=e.required_providers;if(!Array.isArray(n))return t;for(let r of n)typeof r=="object"&&r!==null&&Object.assign(t,r);return t}function oee(e){if(e===void 0)return"no `required_providers` entry";if(typeof e=="string")return"the legacy shorthand form (a bare version string), which carries no `source`";if(typeof e!="object"||e===null)return"an entry that is not a valid object";let t=e,n=[];return(typeof t.source!="string"||t.source==="")&&n.push("`source`"),(typeof t.version!="string"||t.version==="")&&n.push("`version`"),n.length>0?`an entry missing ${n.join(" and ")}`:void 0}var CD={id:"TF002",description:"A provider the root implies has no required_providers entry, or an incomplete one",check(e){let t=[],n=new Set,r=new Map;for(let o of e.entities.values()){if(o.entityType!==cg||!Xe(o)||!Ou(o))continue;let s=o.props,i=typeof s.root=="string"?s.root:"";n.add(i);let a=typeof s.body=="object"&&s.body!==null?s.body:{},c=r.get(i)??{};Object.assign(c,ree(a)),r.set(i,c)}for(let o of[...n].sort()){let s=r.get(o)??{},i=vx(e.entities,o);for(let a of[...i.keys()].sort()){let c=oee(s[a]);c&&t.push({checkId:"TF002",severity:"warning",message:`Provider "${a}" is used in root module "${o}" but has ${c} in \`required_providers\`. Add \`source\` and \`version\` for it in the terraform block's \`required_providers\`, so a future provider release can't silently change behavior.`,entity:i.get(a),lexicon:"terraform"})}}return t}};function see(e){return typeof e.required_version=="string"&&e.required_version.trim()!==""}var ND={id:"TF003",description:"Root module's terraform block has no required_version",check(e){let t=[],n=new Map;for(let[r,o]of e.entities){if(o.entityType!==cg||!Xe(o)||!Ou(o))continue;let s=o.props,i=typeof s.root=="string"?s.root:"",a=typeof s.body=="object"&&s.body!==null?s.body:{},c=n.get(i),u=(c?.hasVersion??!1)||see(a);n.set(i,{anchor:c?.anchor??r,hasVersion:u})}for(let[r,{anchor:o,hasVersion:s}]of[...n].sort(([i],[a])=>i.localeCompare(a)))s||t.push({checkId:"TF003",severity:"warning",message:`Root module "${r}"'s terraform block has no \`required_version\`. Without it, an upgraded Terraform binary can change behavior with no version-check failure to flag it first. Add \`required_version = ">= <lowest supported version>"\`.`,entity:o,lexicon:"terraform"});return t}};var DD=/^[0-9A-Za-z](?:[0-9A-Za-z_-]{0,62}[0-9A-Za-z])?$/,iee=/^[0-9a-z]{1,64}$/,aee=new Set(["github.com","bitbucket.org"]);function MD(e){let t=new Map;for(let n of e.split("&")){if(n==="")continue;let r=n.indexOf("="),o=r===-1?n:n.slice(0,r),s=r===-1?"":decodeURIComponent(n.slice(r+1));t.set(decodeURIComponent(o),s)}return t}function LD(e){let t=e.indexOf("://"),n=t===-1?0:t+3,r=e.indexOf("//",n);return r===-1?e:e.slice(0,r)}function cee(e){let t=LD(e).split("/"),n,r,o;if(t.length===3)[n,r,o]=t;else if(t.length===4){let s=t[0];if(!s.includes(".")||aee.has(s.toLowerCase()))return!1;[,n,r,o]=t}else return!1;return DD.test(n)&&DD.test(r)&&iee.test(o)}function uee(e){if(/^[\w.-]+@[^:/]+:/.test(e))return!0;let t=e.replace(/^[a-z][a-z0-9+.-]*:\/\//i,"");return!!(/^(github\.com|bitbucket\.org)\//i.test(t)||/\.git($|[/?])/i.test(t))}function lee(e,t){let n=MD(t),r=LD(e).replace(/^oci:\/\//i,""),o=n.get("tag")||void 0,s=n.get("digest")||void 0,i=r.indexOf("@");i!==-1&&(s=s??(r.slice(i+1)||void 0),r=r.slice(0,i));let a=r.lastIndexOf("/"),c=r.indexOf(":",a+1);return c!==-1&&(o=o??(r.slice(c+1)||void 0)),{kind:"oci",...o!==void 0?{tag:o}:{},...s!==void 0?{digest:s}:{}}}function Ys(e){let t=e.trim();if(t.startsWith("./")||t.startsWith("../")||t.startsWith(".\\")||t.startsWith("..\\"))return{kind:"local"};let n=t.indexOf("?"),r=n===-1?t:t.slice(0,n),o=n===-1?"":t.slice(n+1);if(/^oci:\/\//i.test(r))return lee(r,o);let s=/^(git|hg)::/i.exec(r),i=s?r.slice(s[0].length):r;if(s||uee(i)){let a=MD(o);return{kind:"git",ref:a.get("ref")||a.get("rev")||void 0}}return cee(i)?{kind:"registry"}:{kind:"other"}}var dee=/^v?\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/,pee=/^[0-9a-f]{40}$/i,fee=new Set(["main","master","develop","trunk"]);function GD(e){return dee.test(e)||pee.test(e)}function FD(e){return fee.has(e)}var gee=new Set(["latest","main","master","develop","dev","stable","edge","nightly"]);function zD(e){return gee.has(e.toLowerCase())}function UD(e){return/^(?:=\s*)?v?\d+(?:\.\d+){0,2}(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/.test(e.trim())&&!e.includes(",")}var BD={id:"TF004",description:"Registry-sourced module block has no version",check(e){let t=[];for(let[n,r]of e.entities){if(r.entityType!==Zi||!Xe(r))continue;let o=r.props,s=typeof o.body=="object"&&o.body!==null?o.body:{},i=typeof s.source=="string"?s.source:void 0;if(i===void 0||Ys(i).kind!=="registry")continue;let a=s.version;if(typeof a=="string"&&a.trim()!=="")continue;let c=typeof o.address=="string"?o.address:n;t.push({checkId:"TF004",severity:"warning",message:`Module "${c}" sources "${i}" from a registry with no \`version\`. \`terraform init\` resolves to the newest release with no constraint to hold it back. Add a \`version\` constraint (e.g. \`~> 5.0\`).`,entity:n,lexicon:"terraform"})}return t}};var WD={id:"TF005",description:"Git/hg module source is unpinned, or pinned to a mutable ref",check(e){let t=[];for(let[n,r]of e.entities){if(r.entityType!==Zi||!Xe(r))continue;let o=r.props,s=typeof o.body=="object"&&o.body!==null?o.body:{},i=typeof s.source=="string"?s.source:void 0;if(i===void 0)continue;let a=Ys(i);if(a.kind!=="git")continue;let c=typeof o.address=="string"?o.address:n,u;a.ref?FD(a.ref)?u=`Module "${c}" sources "${i}" pinned to "${a.ref}", a mutable branch. Pin to a tag or a full commit SHA instead.`:GD(a.ref)||(u=`Module "${c}" sources "${i}" pinned to "${a.ref}", which is neither a tag nor a full commit SHA and can move. Pin to a tag or a full commit SHA instead.`):u=`Module "${c}" sources "${i}" from git/hg with no \`?ref=\`. Add one pinned to a tag or a full commit SHA.`,u&&t.push({checkId:"TF005",severity:"warning",message:u,entity:n,lexicon:"terraform"})}return t}};var HD={id:"TF038",description:"OCI module source has no tag or digest, or a mutable tag",check(e){let t=[];for(let[n,r]of e.entities){if(r.entityType!==Zi||!Xe(r))continue;let o=r.props,s=typeof o.body=="object"&&o.body!==null?o.body:{},i=typeof s.source=="string"?s.source:void 0;if(i===void 0)continue;let a=Ys(i);if(a.kind!=="oci"||a.digest)continue;let c=typeof o.address=="string"?o.address:n,u;a.tag?zD(a.tag)&&(u=`Module "${c}" sources "${i}" with the mutable tag "${a.tag}". Pin it to a digest (\`?digest=sha256:...\`) or an exact version tag (\`?tag=1.4.0\`).`):u=`Module "${c}" sources "${i}" with no tag or digest, so OpenTofu pulls the \`latest\` tag. Pin it to a digest (\`?digest=sha256:...\`) or an exact version tag (\`?tag=1.4.0\`).`,u&&t.push({checkId:"TF038",severity:"warning",message:u,entity:n,lexicon:"terraform"})}return t}};var qD={id:"TF039",description:"Registry module version is a range, not an exact version",check(e){let t=[];for(let[n,r]of e.entities){if(r.entityType!==Zi||!Xe(r))continue;let o=r.props,s=typeof o.body=="object"&&o.body!==null?o.body:{},i=typeof s.source=="string"?s.source:void 0;if(i===void 0||Ys(i).kind!=="registry")continue;let a=s.version;if(typeof a!="string"||a.trim()===""||UD(a))continue;let c=typeof o.address=="string"?o.address:n;t.push({checkId:"TF039",severity:"warning",message:`Module "${c}" sources "${i}" with the version range "${a}". A release inside the range changes what the root provisions with no diff to review. Pin an exact version (\`version = "1.4.0"\`).`,entity:n,lexicon:"terraform"})}return t}};var JD={id:"TF040",description:"Root has no committed .terraform.lock.hcl",auditOptIn:!0,check(e){let t=[],n=new Set;for(let r of e.entities.values()){if(!Xe(r)||!Ou(r))continue;let o=r.props;o.lockFile!==!1||o.mode==="live"||typeof o.root!="string"||n.add(o.root)}for(let r of[...n].sort()){let o=vx(e.entities,r);if(o.size===0)continue;let s=[...o.keys()].sort();t.push({checkId:"TF040",severity:"warning",message:`Root module "${r}" uses ${s.length===1?"provider":"providers"} ${s.map(i=>`"${i}"`).join(", ")} but has no ${yx} beside its .tf files. Run \`terraform init\` and commit the file, so every init selects the same provider versions.`,entity:o.get(s[0]),lexicon:"terraform"})}return t}};function Yi(e,t){let n=[];for(let[r,o]of e){if(o.entityType!==t||!Xe(o))continue;let s=o.props;n.push({key:r,address:typeof s.address=="string"?s.address:r,file:typeof s.file=="string"?s.file:"terragrunt.hcl",body:typeof s.body=="object"&&s.body!==null?s.body:{}})}return n}function KD(e){return typeof e=="string"&&e.includes("${")}function VD(e){let t=e.trim();if(t.includes("${"))return{kind:"other"};if(/^tfr:\/\//i.test(t)){let r=t.includes("?")?t.slice(t.indexOf("?")+1):"",o=new URLSearchParams(r).get("version");return{kind:"tfr",pinned:o!==null&&o.trim()!==""}}if(t.startsWith("/"))return{kind:"local"};let n=Ys(t);switch(n.kind){case"local":return{kind:"local"};case"git":return{kind:"git",pinned:n.ref!==void 0};case"oci":return{kind:"oci",pinned:n.tag!==void 0||n.digest!==void 0};default:return{kind:"other"}}}var ZD={id:"TF041",description:"Terragrunt dependency lets mock_outputs stand in for apply",check(e){let t=[];for(let n of Yi(e.entities,ug)){if(n.body.mock_outputs===void 0)continue;let r=n.body.mock_outputs_allowed_terraform_commands;if(KD(r))continue;let o;r===void 0||Array.isArray(r)&&r.length===0?o="sets no `mock_outputs_allowed_terraform_commands`, and Terragrunt reads a missing or empty list as every command":Array.isArray(r)&&r.includes("apply")&&(o='lists "apply" in `mock_outputs_allowed_terraform_commands`'),o!==void 0&&t.push({checkId:"TF041",severity:"warning",message:`Dependency "${n.address.replace(/^dependency\./,"")}" in ${n.file} sets \`mock_outputs\` and ${o}. An apply that runs before the upstream unit is applied writes the mock values to real state. Set \`mock_outputs_allowed_terraform_commands = ["validate", "plan"]\`.`,entity:n.key,lexicon:"terraform"})}return t}};var YD={id:"TF042",description:"Terragrunt dependency sets skip_outputs together with mock_outputs",check(e){let t=[];for(let n of Yi(e.entities,ug))n.body.skip_outputs!==!0||n.body.mock_outputs===void 0||t.push({checkId:"TF042",severity:"warning",message:`Dependency "${n.address.replace(/^dependency\./,"")}" in ${n.file} sets \`skip_outputs = true\` with \`mock_outputs\`. Terragrunt then never reads the upstream unit's outputs, so every plan and apply runs on the mock values. Drop \`skip_outputs\`, or pass the values as \`inputs\` so they are visible as configuration.`,entity:n.key,lexicon:"terraform"});return t}};var mee={git:"Add `?ref=` with a tag or commit SHA.",tfr:"Add `?version=` with an exact version.",oci:"Add `?digest=sha256:...` or `?tag=` with an exact version tag."},XD={id:"TF043",description:"Terragrunt terraform.source names no version (git ref, tfr version or oci tag)",check(e){let t=[];for(let n of Yi(e.entities,gx)){let r=n.body.source;if(typeof r!="string")continue;let o=VD(r);o.pinned===!1&&t.push({checkId:"TF043",severity:"warning",message:`The \`terraform\` block in ${n.file} sources "${r}" with no version, so a push or a release changes the unit with no diff to review. `+mee[o.kind],entity:n.key,lexicon:"terraform"})}return t}};var hee=/\bbackend\s+"local"/,QD={id:"TF044",description:"Terragrunt config keeps state in a local backend",check(e){let t=[],n=(r,o,s)=>{t.push({checkId:"TF044",severity:"warning",message:`${s} in ${o} keeps state in a local backend. The state lives on the runner's disk, so a later job cannot read it and a git-range run, which checks out each side in a temporary directory, never sees it. Use a shared backend such as S3 or GCS.`,entity:r,lexicon:"terraform"})};for(let r of Yi(e.entities,mx))r.body.backend==="local"&&n(r.key,r.file,'`remote_state` sets `backend = "local"`');for(let r of Yi(e.entities,hx)){let o=r.body.contents;typeof o=="string"&&hee.test(o)&&n(r.key,r.file,`\`generate "${r.address.replace(/^generate\./,"")}"\` writes a \`backend "local"\` block`)}return t}};var tM={TF002:CD,TF003:ND,TF004:BD,TF005:WD,TF038:HD,TF039:qD,TF040:JD,TF041:ZD,TF042:YD,TF043:XD,TF044:QD},yee="https://intentius.io/chant/lexicons/terraform/lint-rules/",wee="https://intentius.io/terragucci/reference/tips/",$x="terragucci-shared-subject",kx="terragucci-persistent-runner",iM="terragucci-environment-role",aM="terragucci-gitlab-audience",bee=10,vee=5,$ee=100,nM=(e,t,n)=>({rule:e,root:t,message:n,url:`${yee}#${e.toLowerCase()}`}),Pu=(e,t,n)=>({rule:e,...n!==void 0?{root:n}:{},message:t,url:`${wee}#${e}`});async function cM(e,t,n){let r=[];if(!rM(oc(e,t)))return r;for(let o of oM(oc(e,t)).filter(s=>s.endsWith(".tf")).sort()){let s=await n.parse(o,sM(oc(e,t,o),"utf-8"));for(let i of Object.values(s.module??{}))for(let a of i)typeof a.source=="string"&&r.push({source:a.source,...typeof a.version=="string"?{version:a.version}:{}})}return r}async function kee(e,t,n){let r=[];if(!rM(oc(e,t)))return r;for(let o of oM(oc(e,t)).filter(s=>s.endsWith(".tf")).sort()){let s=await n.parse(o,sM(oc(e,t,o),"utf-8"));for(let i of s.terraform??[])for(let a of i.required_providers??[])for(let[c,u]of Object.entries(a)){let l=u?.version;typeof l=="string"&&ag(l)===null&&r.push({name:c,version:l})}}return r}async function uM(e,t,n,r=new Set){let o=new Set;if(r.has(t))return o;r.add(t);for(let{source:s}of await cM(e,t,n)){if(!/^\.\.?\//.test(s))continue;let i=eM.normalize(eM.join(t==="."?"":t,s))||".";o.add(i);for(let a of await uM(e,i,n,r))o.add(a)}return o}async function Lw(e,t,n){if(n.settings.tips===!1)return[];let r=[],{parser:o}=n;if(o){let a=(n.rules??Object.keys(tM)).map(l=>tM[l]).filter(l=>l!==void 0),c=new Set,u=new Map;for(let l of n.configDirs??[]){let d=await wx(oc(e,l),l,o),p={outputs:new Map,entities:d};for(let f of a)for(let g of await f.check(p))r.push(nM(f.id,l,g.message))}for(let l of t){let d=await wx(oc(e,l),l,o),p={outputs:new Map,entities:d};for(let f of a)for(let g of await f.check(p))r.push(nM(f.id,l,g.message)),(f.id==="TF038"||f.id==="TF039")&&c.add(l);for(let f of await uM(e,l,o))u.set(f,(u.get(f)??new Set).add(l));for(let{name:f,version:g}of await kee(e,l,o))r.push(Pu("terragucci-floating-range",`Provider ${f} is constrained to the range "${g}". tf-rollout --provider moves the lock file, and a version outside the range fails init, so a rollout cannot cross it. Pin the exact version, or widen the range on purpose.`,l));if(!c.has(l))for(let{source:f,version:g}of await cM(e,l,o))Ki(f,g).unpinned?.includes("is a constraint")&&r.push(Pu("terragucci-floating-range",`${f} is pinned to the range "${g}". A range cannot be bumped by a pin, so tf-rollout cannot move it in waves. Pin one version.`,l))}for(let[l,d]of[...u].sort(([p],[f])=>p<f?-1:1))d.size<bee||r.push(Pu("terragucci-shared-module",`${l} is included by ${d.size} roots, so every change under it plans every one of them. Publish it with tf-publish and pin it in each root, and only the roots whose pin moves are planned.`,l))}t.length>vee&&(n.settings.waves?.canary??[]).length===0&&r.push(Pu("terragucci-no-canary",`The project has ${t.length} roots and no waves.canary, so the first wave is chosen by apply order. Name one or two low-risk roots there, since the first wave is the riskiest.`));let s=n.destroying??[];if(n.settings.gate==="never"&&s.length>0)for(let a of[...s].sort())r.push(Pu("terragucci-ungated-destroy",`${a} plans a destroy or replacement and gate is never, so nothing stops it but the forge's own reviewers. Use gate: on-destructive.`,a));for(let a of n.renames??[])r.push(Pu("terragucci-moved",`${a.from} is destroyed and ${a.to} created with the same configuration, so the block was renamed. A moved block (from = ${a.from}, to = ${a.to}) makes the plan move it instead; terragucci respond tips --report opens a pull request that adds it.`,a.root));let i=n.planned??t.length;return i>=$ee&&r.push(Pu("terragucci-many-roots",`${i} roots are planned on every change. Publishing shared modules and pinning them in each root shrinks the set a change reaches.`)),r}function Gw(e){return e.map(t=>`tip (${t.rule}): ${t.root?`${t.root}: `:""}${t.message}`)}var lg=(e,t)=>e?Object.keys(e).find(n=>Pe(n,t)):void 0;function Sx(e){if(e!==void 0)return typeof e=="string"||Array.isArray(e)||"group"in e?e:e.apply}function _x(e,t){if(!e)return;let n=i=>{if(!i)return;let a=new Set(t.map(u=>lg(i,u)).filter(u=>u!==void 0)),c=Object.fromEntries(Object.entries(i).filter(([u])=>a.has(u)));return Object.keys(c).length>0?c:void 0},r=i=>i!==void 0&&t.every(a=>lg(i,a)!==void 0),o={...e},s=n(e.roles);if(s?o.roles=s:delete o.roles,r(e.roles)&&(delete o.plan_role,delete o.apply_role),e.gcp){let i=n(e.gcp.roles),{roles:a,...c}=e.gcp;if(o.gcp={...c,...i?{roles:i}:{}},i&&r(e.gcp.roles)){let u=Object.values(i)[0];o.gcp={...o.gcp,plan_service_account:u.plan,apply_service_account:u.apply}}}if(e.azure){let i=n(e.azure.roles),{roles:a,...c}=e.azure;if(o.azure={...c,...i?{roles:i}:{}},i&&r(e.azure.roles)){let u=Object.values(i)[0];o.azure={...o.azure,plan_client_id:u.plan,apply_client_id:u.apply}}}return o}function _ee(e){return e.toLowerCase().replace(/\*+/g,"").replace(/[^a-z0-9_-]+/g,"-").replace(/^-+|-+$/g,"")}function sc(e,t){let n=Zs(e.oidc,e.environments),r=[...t].sort(),o=[],s=u=>{u.length>0&&o.push({name:Ka,selectors:[],roots:u,...n?{oidc:_x(n,u)}:{}})};if(e.environments&&Object.keys(e.environments).length>0){for(let[u,l]of Object.entries(e.environments)){let d=r.filter(f=>hs(f,e)===u);if(d.length===0)continue;let p=Sx(l.runner);o.push({name:u,selectors:[...l.roots??[]],roots:d,environment:u,...n?{oidc:_x(n,d)}:{},...p!==void 0?{runner:p}:{},forgeEnvironment:l.forge_environment??u,...l.oidc?.audience!==void 0?{audience:l.oidc.audience}:{}})}return s(r.filter(u=>hs(u,e)===void 0)),o.some(u=>u.environment!==void 0)?o:void 0}if(!n||!(n.roles||n.gcp?.roles||n.azure?.roles))return;let i=new Map,a=[];for(let u of r){let l=[lg(n.roles,u),lg(n.gcp?.roles,u),lg(n.azure?.roles,u)];if(l.every(f=>f===void 0)){a.push(u);continue}let d=JSON.stringify(l),p=i.get(d)??{globs:l,roots:[]};p.roots.push(u),i.set(d,p)}if(i.size+(a.length>0?1:0)<2)return;let c=new Set;for(let{globs:u,roots:l}of i.values()){let d=_ee(u.find(f=>f!==void 0))||"group";mR(d)&&(d=`${d}-roots`);let p=d;for(let f=2;c.has(p);f++)p=`${d}-${f}`;c.add(p),o.push({name:p,selectors:l,roots:l,oidc:_x(n,l)})}return s(a),o}var lM=e=>e.length<2?e.join(""):`${e.slice(0,-1).join(", ")} and ${e[e.length-1]}`;function dM(e,t){if(!e||e.length<2)return;if(e.some(o=>o.environment!==void 0)&&t!=="forgejo"){let o=new Map;for(let i of e)i.environment!==void 0&&i.forgeEnvironment!==void 0&&o.set(i.forgeEnvironment,[...o.get(i.forgeEnvironment)??[],i.name]);let s=[...o].filter(([,i])=>i.length>1);return s.length===0?void 0:s.map(([i,a])=>`the apply jobs of the environments ${lM(a)} run in the same forge environment ${i}, so their tokens carry the same environment and a role's trust cannot tell one environment's jobs from another's; give each environment a forge_environment of its own`).join("; ")}let r=`the apply jobs of the groups ${lM(e.map(o=>o.name))} carry the same OIDC token subject, so a role's trust cannot tell one group's jobs from another's`;return t==="forgejo"?`${r}; they apply from the same ref, and Forgejo has no deployment environments, so its token names the ref alone: keep the groups' roles apart with a repo or a protected branch each`:`${r}; on GitHub or GitLab, name each group's roots in environments, each with a forge_environment, and have each apply role trust its environment alone`}function Fw(e,t){return Object.entries(t).find(([,n])=>n.some(r=>r===e||Pe(r,e)))?.[0]??Ka}function Rx(e,t){let n=new Map([...Object.keys(t).map(r=>[r,[]]),[Ka,[]]]);for(let r of e)n.get(Fw(r,t)).push(r);return[...n].filter(([,r])=>r.length>0).map(([r,o])=>({name:r,roots:[...o].sort()}))}var pM=e=>e.filter(t=>t.name!==Ka).map(t=>`${t.name}=${t.selectors.join(",")}`).join(";");var See="terragucci-check-job",zw=e=>e==null||e===""?"none":e;function Ree(e){if(e===void 0)return;let t=e.trim();if(t.startsWith("["))try{let n=JSON.parse(t);if(Array.isArray(n))return n.map(String)}catch{}return t===""?[]:t.split(",").map(n=>n.trim()).filter(n=>n!=="")}function xee(e){let t=e.split(".")[1];if(!t)throw new Error("the forge's OIDC token is not a JWT");let n=Buffer.from(t.replace(/-/g,"+").replace(/_/g,"/"),"base64").toString("utf-8"),r=JSON.parse(n);if(!r||typeof r!="object")throw new Error("the forge's OIDC token has no claims");return r}function fM(e,t){let n={problems:[],checked:[],skipped:[]},r=e.forgeEnvironment;r!==void 0&&e.forge!=="forgejo"?t.forgeEnvironmentError!==void 0?n.problems.push(`forge environment: expected ${r}, and it could not be read: ${t.forgeEnvironmentError}`):t.forgeEnvironment!==r?n.problems.push(`forge environment: expected ${r}, found ${zw(t.forgeEnvironment)}`):n.checked.push(`forge environment ${r}`):e.forge==="forgejo"&&n.skipped.push("forge environment (Forgejo has none)");let o=e.branches.filter(s=>s!=="");if(o.length===0)n.problems.push("ref: no branch to apply from was given (the default branch's name was empty)");else if(e.forge==="gitlab")!t.ref||!o.includes(t.ref)?n.problems.push(`ref: expected ${o.join(" or ")}, found ${zw(t.ref)}`):n.checked.push(`ref ${t.ref}`),t.refProtected!=="true"?n.problems.push(`ref: expected a protected ref (CI_COMMIT_REF_PROTECTED true), found ${zw(t.refProtected)}`):n.checked.push("protected ref");else{let s=o.map(i=>`refs/heads/${i}`);!t.ref||!s.includes(t.ref)?n.problems.push(`ref: expected ${s.join(" or ")}, found ${zw(t.ref)}`):n.checked.push(`ref ${t.ref}`)}if(e.forge==="gitlab"){let s=e.runnerTags??[];if(s.length>0){let i=t.runnerTags,a=s.filter(c=>!(i??[]).includes(c));i===void 0?n.problems.push(`runner: expected tags ${s.join(", ")}, and the job has no CI_RUNNER_TAGS`):a.length>0?n.problems.push(`runner: expected tags ${s.join(", ")}, found ${i.length?i.join(", "):"none"}`):n.checked.push(`runner tags ${s.join(", ")}`)}}else e.selfHosted&&(t.runnerEnvironment===void 0?n.skipped.push("runner (the runner says nothing about itself: no RUNNER_ENVIRONMENT)"):t.runnerEnvironment!=="self-hosted"?n.problems.push(`runner: expected self-hosted, found ${t.runnerEnvironment}`):n.checked.push("self-hosted runner"));return n}async function Eee(e,t){let n=e.ACTIONS_ID_TOKEN_REQUEST_URL,r=e.ACTIONS_ID_TOKEN_REQUEST_TOKEN;if(!n||!r)throw new Error("the job may request no OIDC token (no ACTIONS_ID_TOKEN_REQUEST_URL); it needs permissions: id-token: write");let o=await t(`${n}&audience=${encodeURIComponent(See)}`,{headers:{authorization:`bearer ${r}`}});if(!o.ok)throw new Error(`the OIDC token request answered ${o.status}`);let s=(await o.json()).value;if(typeof s!="string")throw new Error("the OIDC token request answered no token");return xee(s)}async function gM(e,t=process.env,n=fetch){if(e.forge==="gitlab")return{forgeEnvironment:t.CI_ENVIRONMENT_NAME||null,...t.CI_COMMIT_REF_NAME!==void 0?{ref:t.CI_COMMIT_REF_NAME}:{},...t.CI_COMMIT_REF_PROTECTED!==void 0?{refProtected:t.CI_COMMIT_REF_PROTECTED}:{},...t.CI_RUNNER_TAGS!==void 0?{runnerTags:Ree(t.CI_RUNNER_TAGS)}:{}};let r={...t.GITHUB_REF!==void 0?{ref:t.GITHUB_REF}:{}},o;if(e.forge==="github"&&e.forgeEnvironment!==void 0)try{o=await Eee(t,n),r.forgeEnvironment=typeof o.environment=="string"&&o.environment!==""?o.environment:null}catch(i){r.forgeEnvironmentError=i.message}let s=typeof o?.runner_environment=="string"?o.runner_environment:t.RUNNER_ENVIRONMENT;return s&&(r.runnerEnvironment=s),r}function mM(e,t){let n=`terragucci check-job: environment ${e.environment}`;return t.problems.length>0?{ok:!1,out:[],err:[...t.problems.map(r=>`${n}: ${r}`),`${n}: this job does not run where ${e.environment}'s apply is set to run, so it stops before any cloud credential is taken`]}:{ok:!0,out:[`${n}: ${t.checked.length?t.checked.join(", "):"nothing to check"}${t.skipped.length?`; not checked: ${t.skipped.join(", ")}`:""}`],err:[]}}import{spawnSync as Kte}from"node:child_process";import{mkdtempSync as Vte,rmSync as Zte}from"node:fs";import{hostname as i1,tmpdir as Yte}from"node:os";import{basename as Xte,join as s1}from"node:path";import{existsSync as Zx,readFileSync as qte}from"node:fs";import{join as Nu}from"node:path";import{spawn as Ste}from"node:child_process";import{join as Rte}from"node:path";import{spawn as Tee,spawnSync as Aee}from"node:child_process";import{createHash as Oee}from"node:crypto";import{chmodSync as Pee,existsSync as cc,mkdirSync as xM,mkdtempSync as pg,readdirSync as EM,readFileSync as Ox,renameSync as SM,rmSync as fg,statSync as Tx,writeFileSync as gg}from"node:fs";import{tmpdir as Sd}from"node:os";import{dirname as Px,join as Ut,relative as jee,resolve as jx}from"node:path";var hM="terragucci.report/v1";var Ln="(sensitive, redacted by terragucci)";function Vr(e){let t=e.terragrunt?.pin?` under Terragrunt ${e.terragrunt.version} (${e.terragrunt.pin})`:"";return`${e.name}${e.version?` ${e.version}`:""}${e.pin?` (${e.pin})`:""}${t}`}function Uw(e,t){return e==="tf-drift"&&t==="delete"?"deleted outside Terraform":{delete:"destroy",replace:"replace",refused:"refused to plan",forget:"forget",import:"import"}[t]}var Ae=e=>e!==null&&typeof e=="object"&&!Array.isArray(e);function Xi(e,t,n){if(e===void 0)return e;if(t===!0)return e===null?null:(n.n++,Ln);if(Array.isArray(t)&&Array.isArray(e))return e.map((r,o)=>Xi(r,t[o],n));if(Ae(t)&&Ae(e)){let r={...e};for(let o of Object.keys(t))o in r&&(r[o]=Xi(r[o],t[o],n));return r}return e}function xx(e,t){return Ae(e)?{...e,..."before"in e?{before:Xi(e.before,e.before_sensitive,t)}:{},..."after"in e?{after:Xi(e.after,e.after_sensitive,t)}:{}}:e}function wM(e,t){if(!Ae(e))return e;let n={...e};return Array.isArray(e.resources)&&(n.resources=e.resources.map(r=>Ae(r)?{...r,values:Xi(r.values,r.sensitive_values,t)}:r)),Array.isArray(e.child_modules)&&(n.child_modules=e.child_modules.map(r=>wM(r,t))),n}function yM(e,t){if(!Ae(e))return e;let n={...e};return Ae(e.outputs)&&(n.outputs=Object.fromEntries(Object.entries(e.outputs).map(([r,o])=>[r,Ae(o)&&o.sensitive===!0?{...o,value:Xi(o.value,!0,t)}:o]))),"root_module"in e&&(n.root_module=wM(e.root_module,t)),n}function ys(e){let t=kM(e),n=$M(e,t.plan);if(n.length===0)return t;let r={n:t.values};return{plan:Bw(t.plan,n,r),values:r.n}}function bM(e){return $M(e,kM(e).plan)}function vM(e,t){return t.length===0?e:Bw(e,t,{n:0})}function $M(e,t){let n=new Set,r=s=>{typeof s=="string"?s!==""&&s!==Ln&&n.add(s):Array.isArray(s)?s.forEach(r):Ae(s)&&Object.values(s).forEach(r)},o=(s,i)=>{if(i===Ln&&s!==Ln)r(s);else if(Array.isArray(s)&&Array.isArray(i))s.forEach((a,c)=>o(a,i[c]));else if(Ae(s)&&Ae(i))for(let a of Object.keys(s))o(s[a],i[a])};return o(e,t),[...n].sort((s,i)=>i.length-s.length)}function Bw(e,t,n){if(typeof e=="string"){if(e===Ln)return e;if(t.includes(e))return n.n++,Ln;let r=e;for(let o of t)o.length>=8&&r.includes(o)&&(n.n++,r=r.split(o).join(Ln));return r}return Array.isArray(e)?e.map(r=>Bw(r,t,n)):Ae(e)?Object.fromEntries(Object.entries(e).map(([r,o])=>[r,Bw(o,t,n)])):e}function kM(e){if(!Ae(e))return{plan:e,values:0};let t={n:0},n={...e};for(let s of["resource_changes","resource_drift"]){let i=e[s];Array.isArray(i)&&(n[s]=i.map(a=>Ae(a)?{...a,change:xx(a.change,t)}:a))}Array.isArray(e.deferred_changes)&&(n.deferred_changes=e.deferred_changes.map(s=>Ae(s)&&Ae(s.resource_change)?{...s,resource_change:{...s.resource_change,change:xx(s.resource_change.change,t)}}:s)),Array.isArray(e.action_invocations)&&(n.action_invocations=e.action_invocations.map(s=>Ae(s)&&"config_values"in s?{...s,config_values:Xi(s.config_values,s.config_sensitive,t)}:s)),Ae(e.output_changes)&&(n.output_changes=Object.fromEntries(Object.entries(e.output_changes).map(([s,i])=>[s,xx(i,t)])));for(let s of["planned_values","prior_state"])s in e&&(n[s]=s==="prior_state"&&Ae(e.prior_state)?{...e.prior_state,values:yM(e.prior_state.values,t)}:yM(e[s],t));let r=Ae(e.configuration)&&Ae(e.configuration.root_module)?e.configuration.root_module:{},o=Ae(r.variables)?r.variables:{};return Ae(e.variables)&&(n.variables=Object.fromEntries(Object.entries(e.variables).map(([s,i])=>{let a=o[s];return[s,Ae(a)&&a.sensitive===!0&&Ae(i)?{...i,value:Xi(i.value,!0,t)}:i]}))),Ae(e.configuration)&&(n.configuration={...e.configuration,root_module:_M(e.configuration.root_module,t)}),{plan:n,values:t.n}}function _M(e,t){if(!Ae(e))return e;let n={...e};return Ae(e.variables)&&(n.variables=Object.fromEntries(Object.entries(e.variables).map(([r,o])=>[r,Ae(o)&&o.sensitive===!0&&"default"in o?{...o,default:Xi(o.default,!0,t)}:o]))),Ae(e.module_calls)&&(n.module_calls=Object.fromEntries(Object.entries(e.module_calls).map(([r,o])=>[r,Ae(o)?{...o,module:_M(o.module,t)}:o]))),n}var Ww="0.71.0",Iee={x86_64:"765dfefdf0730693d7541ea0147e10d530a54ed257bbfec055ff34c837ffdf72",arm64:"542f255081cfab9919cba327d3c4014fd46d6ee3f0441c634b14b1465e9469d9"},Ex="1.21.1",Cee={x86_64:"668506eb17a2eaa1fce6cc0d1f42ef85125d4ac5bda5fc74d1152d0c77145031",arm64:"9a1f3625529c6f01240fe68286dde06aa0b23c5253da700ac48f4d943ff8a4de"},mg=(e,t,n)=>new Promise(r=>{let o=Tee(e,t,{cwd:n,stdio:["ignore","pipe","pipe"],env:Ve(process.env)}),s=[],i=[],a;o.stdout.on("data",c=>s.push(c)),o.stderr.on("data",c=>i.push(c)),o.on("error",c=>a=c),o.on("close",c=>r({status:a?null:c,stdout:Buffer.concat(s).toString("utf-8"),stderr:`${Buffer.concat(i).toString("utf-8")}${a?a.message:""}`}))});async function Ix(e,t,n={}){let r=n.exec??mg,o=e.engine??"conftest";if((await r(o,o==="opa"?["version"]:["--version"],t)).status===0)return o;let i=(n.arch??process.arch)==="arm64"?"arm64":"x86_64",a=Ut(n.cache??Sd(),`terragucci-${o}-${o==="opa"?Ex:Ww}-${i}`),c=Ut(a,o);if(cc(c))return c;let u=o==="conftest",l=u?Ww:Ex,d=u?`conftest_${Ww}_Linux_${i}.tar.gz`:`opa_linux_${i==="arm64"?"arm64":"amd64"}_static`,p=u?`https://github.com/open-policy-agent/conftest/releases/download/v${Ww}/${d}`:`https://github.com/open-policy-agent/opa/releases/download/v${Ex}/${d}`,f=await(n.download??Nee)(p),g=Oee("sha256").update(f).digest("hex");if(g!==(u?Iee:Cee)[i])throw new Error(`${o} ${l} from ${p} does not match its pinned digest (got ${g}); not running it`);let m=pg(Ut(Sd(),`terragucci-${o}-`));try{if(gg(Ut(m,d),f),u){let h=await r("tar",["-xzf",Ut(m,d),"-C",m,"conftest"],m);if(h.status!==0)throw new Error(`could not unpack conftest: ${h.stderr.trim()}`)}else SM(Ut(m,d),Ut(m,"opa"));xM(a,{recursive:!0}),SM(Ut(m,o),c),Pee(c,493)}finally{fg(m,{recursive:!0,force:!0})}return c}async function Nee(e){let t=await fetch(e);if(!t.ok)throw new Error(`${e}: ${t.status}`);return Buffer.from(await t.arrayBuffer())}var Dee=/^(deny|violation)(_[A-Za-z0-9]+)*$/,Mee=/^warn(_[A-Za-z0-9]+)*$/,Ax=e=>e.replace(/^data\./,"");function TM(e){return typeof e=="string"?e:e!==null&&typeof e=="object"&&typeof e.msg=="string"?e.msg:JSON.stringify(e)}function Lee(e){let t;try{t=JSON.parse(e)}catch{return}if(!Array.isArray(t))return;let n={violations:[],warnings:[],rules:[]};for(let r of t){for(let o of r.failures??[])n.violations.push(typeof o.msg=="string"?o.msg:JSON.stringify(o.msg)),n.rules.push(typeof o.metadata?.query=="string"?Ax(o.metadata.query):`${r.namespace??"main"}.deny`);for(let o of r.warnings??[])n.warnings.push(typeof o.msg=="string"?o.msg:JSON.stringify(o.msg))}return n}function AM(e,t,n,r){for(let[o,s]of Object.entries(e).sort(([i],[a])=>i<a?-1:i>a?1:0)){let i=Dee.test(o),a=i?t.violations:Mee.test(o)?t.warnings:void 0;if(a){let c=a.length;if(Array.isArray(s)?a.push(...s.map(TM)):s===!0?a.push(o):typeof s=="string"&&a.push(s),i)for(let u=c;u<a.length;u++)t.rules.push(`${r}.${o}`);continue}n&&s!==null&&typeof s=="object"&&!Array.isArray(s)&&AM(s,t,!1,`${r}.${o}`)}}function Gee(e,t=!1,n="main"){let r=OM(e);if(r!==void 0)return r.value===void 0?{violations:[],warnings:[],rules:[]}:PM(r.value,t,n)}function OM(e){let t;try{t=JSON.parse(e)}catch{return}if(t===null||typeof t!="object"||t.errors!==void 0)return;let n=t.result?.[0]?.expressions?.[0]?.value;return n===void 0?{}:{value:n}}function PM(e,t,n){if(Array.isArray(e))return{violations:e.map(TM),warnings:[],rules:e.map(()=>Ax(n))};if(e===null||typeof e!="object")return;let r={violations:[],warnings:[],rules:[]};return AM(e,r,t,Ax(n)),r}function Fee(e){let t=n=>n===null?"":n.toFixed(2);return{prior_monthly_cost:t(e.root.past_monthly_total),proposed_monthly_cost:t(e.root.monthly_total),delta_monthly_cost:t(e.root.monthly_delta)}}function zee(e){let t=(e.project??"").split("/"),n=t.length>=3?t.slice(1,-1).join("/"):"",r=t.length>=3?t[t.length-1]:e.project??"";return{id:"",message:e.pullRequest?`pull request ${e.pullRequest}`:"",commit_sha:e.commit??"",is_destroy:!1,refresh:!0,refresh_only:!1,replace_addrs:[],speculative:e.stage!=="tf-apply",target_addrs:[],variables:{},organization:{name:n},project:{id:"",name:r},workspace:{id:"",name:e.root,description:"",execution_mode:"agent",auto_apply:!1,tags:[],working_directory:e.root,vcs_repo:{identifier:[n,r].filter(Boolean).join("/"),display_identifier:[n,r].filter(Boolean).join("/"),branch:"",ingress_submodules:!1}}}}function Uee(e,t,n){let r=n?.cost,o=n?.review;if((e.input??"plan")!=="hcp"){if(!r&&!o)return t;let i=JSON.parse(t);return JSON.stringify({...i,...r?{cost:r}:{},...o?{review:o}:{}})}let s=zee(n??{root:""});return r&&(s.cost_estimate=Fee(r)),`{"plan":${t},"run":${JSON.stringify(s)}${r?`,"cost":${JSON.stringify(r)}`:""}${o?`,"review":${JSON.stringify(o)}`:""}}`}function Bee(e,t,n){return(e.engine??"conftest")==="opa"?["eval","--format","json","--data",t,"--input",n,`data.${Cx(e)}`]:["test","--no-color","--output","json","--policy",t,...e.namespace?["--namespace",e.namespace]:["--all-namespaces"],n]}function Cx(e){return e.namespace??(e.input==="hcp"?"terraform.policies":"main")}var ic="policies.hcl";function Wee(e){let t=Ut(e,ic);if(!cc(t))return;let n=Ox(t,"utf-8").replace(/^\s*(#|\/\/).*$/gm,""),r=[];for(let o of n.matchAll(/policy\s+"([^"]+)"\s*\{([^}]*)\}/g)){let s=Object.fromEntries([...o[2].matchAll(/([A-Za-z_]+)\s*=\s*"((?:[^"\\]|\\.)*)"/g)].map(a=>[a[1],a[2].replace(/\\(.)/g,"$1")]));if(!s.query)return`${ic}: policy "${o[1]}" has no query`;let i=s.enforcement_level??"advisory";if(i!=="advisory"&&i!=="mandatory")return`${ic}: policy "${o[1]}" has enforcement_level ${i}; HCP Terraform reads advisory or mandatory`;r.push({name:o[1],query:s.query,level:i})}return r.length===0?`${ic} declares no policy block`:r}function Hee(e,t){if(t===void 0)return e;let n=`data.${t}`;return e.filter(r=>r.query===n||r.query.startsWith(`${n}.`))}async function qee(e,t,n,r,o,s,i){let a=Hee(t,n.namespace);if(a.length===0)return{violations:[],error:`no policy in ${ic} has a query under data.${n.namespace}`};let c={violations:[],warnings:[],rules:[]};for(let u of a){let l=await i(e,["eval","--format","json","--data",r,"--input",o,u.query],s),d=OM(l.stdout);if(d===void 0)return{violations:[],error:`opa gave no verdict for policy ${u.name} (exit ${l.status}): ${(l.stderr||l.stdout).trim().split(`
|
|
84
84
|
`).slice(-3).join(" ")}`};if(d.value===void 0)return{violations:[],error:`policy ${u.name}: its query ${u.query} matches no rule`};let p=PM(d.value,!0,u.query);if(p===void 0)return{violations:[],error:`policy ${u.name}: its query ${u.query} gives ${JSON.stringify(d.value)}, not a set of messages`};let f=[...p.violations,...p.warnings].map(g=>`${u.name}: ${g}`);(u.level==="mandatory"?c.violations:c.warnings).push(...f),u.level==="mandatory"&&c.rules.push(...f.map(()=>u.name))}return{violations:c.violations,...jM(c.rules),...c.warnings.length>0?{warnings:c.warnings}:{}}}async function Jee(e,t,n,r,o={},s){let i=o.exec??mg,a=pg(Ut(Sd(),"terragucci-policy-")),c=Ut(a,"plan.json"),u=jx(n,t.path??"policy"),l=t.engine??"conftest";try{gg(c,Uee(t,r,s));let d=l==="opa";if(d&&t.input==="hcp"){let g=Wee(u);if(typeof g=="string")return{violations:[],error:g};if(g)return await qee(e,g,t,u,c,n,i)}let p=await i(e,Bee(t,u,c),n),f=d?Gee(p.stdout,t.input==="hcp"&&t.namespace===void 0,Cx(t)):Lee(p.stdout);return f===void 0?{violations:[],error:`${l} gave no verdict (exit ${p.status}): ${(p.stderr||p.stdout).trim().split(`
|
|
85
85
|
`).slice(-3).join(" ")}`}:f.violations.length===0&&p.status!==0?{violations:[],error:`${l} exited ${p.status}: ${p.stderr.trim().split(`
|
|
86
86
|
`).slice(-3).join(" ")}`}:{violations:f.violations,...jM(f.rules),...f.warnings.length>0?{warnings:f.warnings}:{}}}finally{fg(a,{recursive:!0,force:!0})}}var jM=e=>e.length>0?{rules:[...new Set(e)].sort()}:{};function Kee(e,t){return t.error?`policy could not be checked, so the root fails: ${t.error}`:`policy violation (${e}):
|
|
@@ -115,7 +115,7 @@ ${Ate(c.out)}`:""}`,r}return r}var Ote="a Terragrunt repo inits each unit inside
|
|
|
115
115
|
`).filter(t=>t.trim()).slice(0,12).join(`
|
|
116
116
|
`);function Mte(e){let t=new Map;for(let r of e.matchAll(/^\s*(\w+)\s*\[\s*label\s*=\s*"([^"]*)"/gm))t.set(r[1],r[2].replace(/^\/+/,"")||".");let n=[];for(let r of e.matchAll(/^\s*(\w+)\s*->\s*(\w+)/gm)){let o=t.get(r[1]),s=t.get(r[2]);o&&s&&o!==s&&n.push([o,s])}return n}function Lte(e){let t="";for(let n=0;n<e.length;){let r=e[n];if(r==='"'){let o=n+1;for(;o<e.length&&e[o]!=='"';)o+=e[o]==="\\"?2:1;t+=e.slice(n,o+1),n=o+1}else if(r==="#"||r==="/"&&e[n+1]==="/")for(;n<e.length&&e[n]!==`
|
|
117
117
|
`;)n++;else if(r==="/"&&e[n+1]==="*"){let o=e.indexOf("*/",n+2);n=o<0?e.length:o+2}else t+=r,n++}return t}function XM(e,t){let n=Lte(e),r=[],o=new RegExp(`(^|\\n)\\s*${t}((?:\\s+"[^"]*")*)\\s*\\{`,"g");for(let s of n.matchAll(o)){let i=1,a=s.index+s[0].length,c=a;for(;a<n.length&&i>0;){let u=n[a];if(u==='"')for(a++;a<n.length&&n[a]!=='"';)a+=n[a]==="\\"?2:1;else u==="{"?i++:u==="}"&&i--;a++}r.push({labels:[...s[2].matchAll(/"([^"]*)"/g)].map(u=>u[1]),body:n.slice(c,a-1)})}return r}function Jx(e,t){let n=e.match(new RegExp(`(^|\\n)\\s*${t}\\s*=\\s*([^\\n]*)`));return n?n[2].trim():void 0}var QM=e=>e?.match(/^"([^"\\]*)"$/)?.[1];function Gte(e){try{return Kx(e).filter(t=>t.endsWith(".tm")||t.endsWith(".tm.hcl")).sort().map(t=>Cu(e,t))}catch{return[]}}function Fte(e,t){let n=[];for(let r of XM(t,"input")){let o=r.labels[0];if(!o)continue;let s=`${e} declares input "${o}"`,i=QM(Jx(r.body,"from_stack_id"));if(!i)throw new S(`${s} without a literal from_stack_id; terragucci reads an input from the stack whose id it names`);let a=Jx(r.body,"value")??"",c=a.match(/^outputs\.([A-Za-z_][\w-]*)\.value((?:\.[A-Za-z_][\w-]*|\["[^"\\]+"\])*)$/);if(!c)throw new S(`${s} with value = ${a||"nothing"}, which terragucci does not evaluate; read an output by name, outputs.<name>.value, or a path of keys into it, or read the upstream's state with a terraform_remote_state data source`);let u=[...c[2].matchAll(/\.([A-Za-z_][\w-]*)|\["([^"\\]+)"\]/g)].map(l=>l[1]??l[2]);n.push({var:o,fromStackId:i,output:[c[1],...u],function:`input "${o}" from stack ${i}, ${a}`})}return n}function zte(e){for(let t of XM(e,"stack")){let n=QM(Jx(t.body,"id"));if(n)return n}}function Ute(e){try{return Kx(e).some(t=>/\.(tf|tofu)(\.json)?$/.test(t))}catch{return!1}}function Bte(e,t,n,r={}){let o=m=>Gte(Cu(e,m)).map(h=>jte(h,"utf-8")).join(`
|
|
118
|
-
`),s=r.id??(m=>zte(o(m))),i=r.inputs??(m=>Fte(m,o(m))),a=r.terraform??(m=>Ute(Cu(e,m))),c=new Set(t),u=new Map(t.map(m=>[m,new Set]));for(let[m,h]of n)c.has(m)&&c.has(h)&&u.get(h).add(m);for(let m of t){for(let h=Tg.dirname(m);h!=="."&&h!=="/"&&h!=="";h=Tg.dirname(h))if(c.has(h)){u.get(m).add(h);break}m!=="."&&c.has(".")&&u.get(m).add(".")}let l=new Map;for(let m of t){let h=s(m);if(h){if(l.has(h))throw new S(`stacks ${l.get(h)} and ${m} both have id ${h}`);l.set(h,m)}}let d=new Map([...l].map(([m,h])=>[h,m])),p=new Map(t.map(m=>[m,a(m)])),f=new Map;for(let m of t){let h=i(m);if(h.length>0&&!p.get(m))throw new S(`${m} declares inputs but holds no Terraform for them to fill`);for(let y of h){let w=l.get(y.fromStackId),b=`${m} reads ${y.var} from stack ${y.fromStackId}`;if(!w)throw new S(`${b}, which no stack has as its id`);if(w===m)throw new S(`${b}, its own outputs`);if(!p.get(w))throw new S(`${b} (${w}), which holds no Terraform, so it has no outputs`);u.get(m).add(w),f.has(m)||f.set(m,[]),f.get(m).push({var:y.var,upstream:w,output:y.output,function:y.function})}}let g=(m,h=new Set)=>{let y=new Set;for(let w of u.get(m)??[])if(!h.has(w))if(h.add(w),p.get(w))y.add(w);else for(let b of g(w,h))y.add(b);return y};return[...t].sort().map(m=>({path:m,...d.has(m)?{id:d.get(m)}:{},terraform:p.get(m),dependencies:[...g(m)].filter(h=>h!==m).sort(),reads:f.get(m)??[]}))}async function
|
|
118
|
+
`),s=r.id??(m=>zte(o(m))),i=r.inputs??(m=>Fte(m,o(m))),a=r.terraform??(m=>Ute(Cu(e,m))),c=new Set(t),u=new Map(t.map(m=>[m,new Set]));for(let[m,h]of n)c.has(m)&&c.has(h)&&u.get(h).add(m);for(let m of t){for(let h=Tg.dirname(m);h!=="."&&h!=="/"&&h!=="";h=Tg.dirname(h))if(c.has(h)){u.get(m).add(h);break}m!=="."&&c.has(".")&&u.get(m).add(".")}let l=new Map;for(let m of t){let h=s(m);if(h){if(l.has(h))throw new S(`stacks ${l.get(h)} and ${m} both have id ${h}`);l.set(h,m)}}let d=new Map([...l].map(([m,h])=>[h,m])),p=new Map(t.map(m=>[m,a(m)])),f=new Map;for(let m of t){let h=i(m);if(h.length>0&&!p.get(m))throw new S(`${m} declares inputs but holds no Terraform for them to fill`);for(let y of h){let w=l.get(y.fromStackId),b=`${m} reads ${y.var} from stack ${y.fromStackId}`;if(!w)throw new S(`${b}, which no stack has as its id`);if(w===m)throw new S(`${b}, its own outputs`);if(!p.get(w))throw new S(`${b} (${w}), which holds no Terraform, so it has no outputs`);u.get(m).add(w),f.has(m)||f.set(m,[]),f.get(m).push({var:y.var,upstream:w,output:y.output,function:y.function})}}let g=(m,h=new Set)=>{let y=new Set;for(let w of u.get(m)??[])if(!h.has(w))if(h.add(w),p.get(w))y.add(w);else for(let b of g(w,h))y.add(b);return y};return[...t].sort().map(m=>({path:m,...d.has(m)?{id:d.get(m)}:{},terraform:p.get(m),dependencies:[...g(m)].filter(h=>h!==m).sort(),reads:f.get(m)??[]}))}async function e1(e,t={}){let n=await Hx(e,["list"],t);if(n.code!==0)throw new S(`terramate list failed (exit ${n.code}):
|
|
119
119
|
${qx(n.stderr||n.stdout)}`);let r=n.stdout.split(`
|
|
120
120
|
`).map(i=>i.trim().replace(/\/+$/,"")).filter(Boolean).map(i=>Tg.normalize(i)),o=await Hx(e,["experimental","run-graph","--label","stack.dir"],t),s=`${o.stderr}
|
|
121
121
|
${o.stdout}`.split(`
|
|
@@ -124,33 +124,33 @@ ${qx(o.stderr||o.stdout)}`);return Bte(e,r,Mte(o.stdout))}function Vx(e){return
|
|
|
124
124
|
${n.stderr}`);if(n.code===2)return`the generated code is stale: terramate generate changed files the repo commits
|
|
125
125
|
${r}
|
|
126
126
|
run terramate generate and commit what it writes`;throw new S(`terramate generate failed (exit ${n.code}):
|
|
127
|
-
${r}`)}async function
|
|
128
|
-
`);return["terramate generate: the generated code is up to date",...r.map(o=>`${o.path}: ${o.dependencies.length?`after ${o.dependencies.join(", ")}`:"first"}${o.reads.length?`, reads ${o.reads.map(s=>`${s.var} from ${s.upstream}`).join(", ")}`:""}`)]}async function
|
|
129
|
-
`,s=Xs(e,["hash-object","-w","--stdin"],o).stdout.trim(),i=Vte(
|
|
127
|
+
${r}`)}async function t1(e,t={}){let n=await Hte(e,t);if(n)throw new S(n);let r=Vx(await e1(e,t));if(r.length===0)throw new S("terramate list names no stack that holds Terraform");for(let o of r)Cte(Cu(e,o.path,HR),`${JSON.stringify({dependencies:o.dependencies,reads:o.reads},null,2)}
|
|
128
|
+
`);return["terramate generate: the generated code is up to date",...r.map(o=>`${o.path}: ${o.dependencies.length?`after ${o.dependencies.join(", ")}`:"first"}${o.reads.length?`, reads ${o.reads.map(s=>`${s.var} from ${s.upstream}`).join(", ")}`:""}`)]}async function n1(e,t,n,r={}){let o=await e1(e,r),s=Vx(o);if(s.length===0)throw new S(`found no Terramate stacks that hold Terraform (${t} turned Terramate mode on): terramate list names ${o.length===0?"no stack":o.map(l=>l.path).join(", ")}`);let i=wn(e,s.map(l=>l.path)),a=o.filter(l=>!l.terraform).map(l=>l.path),c=new Map(s.map(l=>[l.path,new Set([...l.reads.map(d=>d.upstream),...i.get(l.path)??[]])]).filter(([,l])=>l.size>0)),u=new Map(s.map(l=>[l.path,new Set([...l.dependencies,...i.get(l.path)??[]])]).filter(([,l])=>l.size>0));return{roots:s.map(l=>({root:l.path,reason:`terramate list${l.dependencies.length?`, after ${l.dependencies.join(", ")}`:""}`})),layers:Wte(o,n??[],i),reads:c,order:u,notes:a.length>0?[`stacks with no Terraform are no roots: ${a.join(", ")}; the order through them is kept`]:[],skipped:a}}var ea={tofu:{releaseInRequiredVersion:!0,test:!0,refreshOnlyOnLiveRoots:!0,applyScope:"root",estateOutputsFrom:!1},terraform:{releaseInRequiredVersion:!0,test:!0,refreshOnlyOnLiveRoots:!0,applyScope:"root",estateOutputsFrom:!1},choudoufu:{releaseInRequiredVersion:!1,test:!1,refreshOnlyOnLiveRoots:!1,applyScope:"resource",estateOutputsFrom:!0}};function ob(e){let t=e.split("/").pop();return Object.hasOwn(ea,t)&&!ea[t].refreshOnlyOnLiveRoots}function r1(e){let t=e.split("/").pop().replace(/\.exe$/,"");return Object.hasOwn(ea,t)&&ea[t].estateOutputsFrom}var Jte="found no roots: no directory has Terraform files with a backend or a provider block";function et(e,t){let n=vd(e),r=vw(e),o=[];t.terragrunt&&!n&&o.push("terragucci.yml has a terragrunt block, but the repo has no root.hcl, terragrunt.hcl or terragrunt.stack.hcl"),t.atmos&&!r&&o.push(`terragucci.yml has an atmos block, but the repo has no ${ww} at its root`),r&&n&&o.push(`the repo has ${r} and ${n.reason}; terragucci runs an Atmos repo or a Terragrunt repo, not both`);let s=YM(e);s&&(r||n)&&o.push(`the repo has ${s} and ${r??n.reason}; terragucci runs a Terramate repo, an Atmos repo or a Terragrunt repo, one at a time`);let i=!r&&!n?s:void 0,a=typeof t.synth=="string"&&t.synth.trim()!==""?t.synth:void 0,c=r?"atmos":n?"terragrunt":i?"terramate":a?"synth":"roots",u=r??n?.reason??i??(a?`synth: ${a}`:"roots"),l=t.waves?.canary,d=(c==="roots"||c==="synth")&&t.waves?.after&&Object.keys(t.waves.after).length?t.waves.after:void 0,p,f=y=>{if(c==="terragrunt"){let w=Qa(y);return w===void 0?y:bd(w)}return c!=="atmos"?y:p?.find(w=>w.path===y)?.componentPath??ec(Nu(e,y))?.component??y};return{kind:c,reason:u,engine:c==="terragrunt"?"terragrunt":"per-root",rootsInGit:c!=="synth"&&c!=="atmos",...c==="atmos"?{prepare:bw}:c==="terramate"?{prepare:ZM}:a&&c==="synth"?{prepare:a}:{},...n&&c==="terragrunt"?{terragrunt:n}:{},...d?{after:d}:{},discover:async(y={})=>{if(c==="atmos"){if(p=Hi(await Wi(e,{...y.atmos?{atmos:y.atmos}:{},...y.exec?{exec:y.exec}:{}})),p.length===0)throw new S(`found no Atmos instances (${r} turned Atmos mode on): atmos describe stacks lists no Terraform component that is neither abstract nor disabled`);let E=new Map(p.filter(x=>x.reads.length>0).map(x=>[x.path,new Set(x.reads.map(A=>A.upstream))])),R=new Map(p.filter(x=>x.dependencies.length>0).map(x=>[x.path,new Set(x.dependencies)]));return{roots:p.map(x=>({root:x.path,reason:`atmos describe stacks: ${x.componentPath} in workspace ${x.workspace}`})),layers:QN(p,l),reads:E,order:R,notes:[]}}if(c==="terramate")return n1(e,i,l,{...y.terramate?{terramate:y.terramate}:{},...y.exec?{exec:y.exec}:{}});if(c==="terragrunt"){let E=await qs(e,{exclude:t.terragrunt?.exclude,...y.binary?{binary:y.binary}:{},...y.terragrunt?{terragrunt:y.terragrunt}:{},...y.exec?{exec:y.exec}:{}});if(E.units.length===0)throw new S(`found no Terragrunt units (${n.reason} turned Terragrunt mode on): no directory outside catalog/ holds a terragrunt.hcl`);let R;try{R=Js(E.units,l)}catch(A){throw new S(A.message)}let x=new Map(E.units.filter(A=>A.dependencies.length>0).map(A=>[A.path,new Set(A.dependencies)]));return{roots:E.units.map(A=>({root:A.path,reason:E.source==="terragrunt find"?"terragrunt find":"terragrunt.hcl"})),layers:R,reads:x,order:x,notes:E.notes,source:E.source}}let w=KR(e,t.roots);if(w.length===0)throw new S((t.roots?`no directory matches roots ${JSON.stringify(t.roots)}`:Jte)+(a?`; run the synth command (${a}) first, then init`:""));let b=w.map(E=>E.root),k=wn(e,b),v=d,$=v?Vs(e,b,v):k,_=Fo(v,b);return{roots:w,layers:tc(e,b,v),reads:k,order:$,notes:_.size?[`waves.after orders ${[..._].map(([E,R])=>`${E} after ${[...R].sort().join(", ")}`).join("; ")}`]:[]}},binary:(y=[])=>{if(c==="terragrunt")return Ks(e,[]);let w=[...new Set(y.map(f))].filter(b=>Zx(Nu(e,b)));return Ks(e,w)},rootEnv:(y,w)=>ls(w,Nu(e,y)),rootInit:(y,w)=>Fi(w,Nu(e,y)),stateAddress:y=>{if(c!=="terragrunt"){if(c==="atmos"){let w=p?.find($=>$.path===y);if(w)return rg([w]).states.get(y);let b=Nu(e,y),k=hd(b),v=Nu(b,"backend.tf.json");if(!k||!Zx(v))return;try{let $=JSON.parse(qte(v,"utf-8")).terraform?.backend??{},[_,E]=Object.entries($)[0]??[];if(!_||!E)return;let R=ec(b)?.component?.split("/").pop()??y.split("/").pop();return rg([{path:y,stack:"",component:R,componentPath:"",workspace:k,vars:{},backendType:_,backend:E,providers:{},dependencies:[],reads:[]}]).states.get(y)}catch{return}}return Aw(e,[y]).get(y)?.state}},refuses:y=>y==="steps"&&c==="terragrunt"?Iu(t.steps):xS(c,y),problems:(y="config")=>{let w=c==="terragrunt"?Iu(t.steps):void 0;return[...o,...zy(c,t,y),...w?[`${y}.${w}`]:[]]},sourceOf:f,prepareRoots:async(y={})=>{if(c!=="terragrunt"||!n?.stacks.length)return[];let w={...y.binary?{binary:y.binary}:{},...y.terragrunt?{terragrunt:y.terragrunt}:{},...y.exec?{exec:y.exec}:{}};return y.roots&&y.roots.every(b=>Qa(b)===void 0||Zx(Nu(e,b,"terragrunt.hcl")))?[]:Bi(e,w)}}}function o1(e,t){let n=e.problems(t);if(n.length>0)throw new S(n.join("; "))}function go(e){let t=Xte(e).replace(/\.exe$/,"");return Object.hasOwn(ea,t)?ea[t].applyScope:"root"}var Du="chant/lifecycle",Yx=`refs/remotes/origin/${Du}`,a1="_locks/apply-rows.json",Qte={GIT_AUTHOR_NAME:"terragucci",GIT_AUTHOR_EMAIL:"terragucci@localhost",GIT_COMMITTER_NAME:"terragucci",GIT_COMMITTER_EMAIL:"terragucci@localhost"},ene=7200,tne=new Set(["create","update","delete","forget"]);function c1(e,t,n){let r=t?`estate ${t}`:`root ${e}`,o=new Map,s=n?.resource_changes??[];for(let i of s){if(i?.mode==="data"||typeof i?.address!="string")continue;let a=Array.isArray(i.change?.actions)?i.change.actions:[];if(!a.some(d=>tne.has(d)))continue;let c=i.address,u=d=>o.has(d)||o.set(d,{key:d,root:e,address:c});u(`${r} ${c}`),typeof i.previous_address=="string"&&i.previous_address!==c&&u(`${r} ${i.previous_address}`);let l=i.change?.before?.id;a.some(d=>d!=="create")&&(typeof l=="string"||typeof l=="number")&&String(l)!==""&&u(`object ${String(l)}`)}return[...o.values()].sort((i,a)=>i.key<a.key?-1:1)}function sb(e,t=new Date){let n=!!e.CI_PIPELINE_ID,r=e.GITHUB_RUN_ID||e.CI_PIPELINE_ID||`local:${i1()}:${process.pid}`,o=n?e.CI_PIPELINE_URL:e.GITHUB_RUN_ID&&e.GITHUB_SERVER_URL&&e.GITHUB_REPOSITORY?`${e.GITHUB_SERVER_URL}/${e.GITHUB_REPOSITORY}/actions/runs/${e.GITHUB_RUN_ID}`:void 0,s=e.TG_SHA||e.GITHUB_SHA||e.CI_COMMIT_SHA;return{run:r,at:t.toISOString(),...o?{url:o}:{},...s?{sha:s}:{}}}function ib(e,t=globalThis.fetch,n=Date.now){let r=Number(e.TG_LOCK_STALE)>0?Number(e.TG_LOCK_STALE):ene;return async o=>{let s=new Date(o.at).getTime();if(Number.isFinite(s)&&n()-s>=r*1e3)return"dead";let i=/^local:(.*):(\d+)$/.exec(o.run);if(i){if(i[1]!==i1())return"alive";try{return process.kill(Number(i[2]),0),"alive"}catch(c){return c.code==="ESRCH"?"dead":"alive"}}let a=e.TG_TOKEN;if(!a)return"alive";try{if(e.CI_API_V4_URL&&e.CI_PROJECT_ID){let d=await t(`${e.CI_API_V4_URL}/projects/${e.CI_PROJECT_ID}/pipelines/${o.run}`,{headers:{"private-token":a}});if(!d.ok)return"alive";let p=String((await d.json()).status??"");return["success","failed","canceled","skipped"].includes(p)?"dead":"alive"}let c=e.GITHUB_API_URL||(e.GITHUB_SERVER_URL?`${e.GITHUB_SERVER_URL}/api/v1`:"");if(!c||!e.GITHUB_REPOSITORY)return"alive";let u=await t(`${c}/repos/${e.GITHUB_REPOSITORY}/actions/runs/${o.run}`,{headers:{authorization:`token ${a}`}});if(!u.ok)return"alive";let l=String((await u.json()).status??"");return["success","failure","cancelled","skipped","completed"].includes(l)?"dead":"alive"}catch{return"alive"}}}function Xs(e,t,n,r=process.env){return Kte("git",t,{cwd:e,encoding:"utf-8",input:n,env:r})}function nne(e){try{let t=JSON.parse(e);if(t?.version===1&&t.rows&&typeof t.rows=="object"&&t.holders&&typeof t.holders=="object"){let n={};for(let[o,s]of Object.entries(t.holders))typeof s?.run=="string"&&typeof s.at=="string"&&(n[o]={run:s.run,at:s.at,...typeof s.url=="string"?{url:s.url}:{},...typeof s.sha=="string"?{sha:s.sha}:{}});let r={};for(let[o,s]of Object.entries(t.rows))typeof s=="string"&&n[s]&&(r[o]=s);return{version:1,holders:n,rows:r}}}catch{}return{version:1,holders:{},rows:{}}}function Xx(e){let t=Xs(e,["ls-remote","--heads","origin",Du]);if(t.status!==0)throw new S(`cannot read ${Du} from origin, so the rows this apply changes cannot be held: ${t.stderr.trim()}`);if(!t.stdout.trim())return{parent:"",file:{version:1,holders:{},rows:{}}};let n=Xs(e,["fetch","-q","origin",`+refs/heads/${Du}:${Yx}`]);if(n.status!==0)throw new S(`cannot fetch ${Du}, so the rows this apply changes cannot be held: ${n.stderr.trim()}`);let r=Xs(e,["rev-parse",Yx]).stdout.trim(),o=Xs(e,["show",`${Yx}:${a1}`]);return{parent:r,file:nne(o.status===0?o.stdout:"")}}function u1(e,t,n,r){let o=`${JSON.stringify(n,null,2)}
|
|
129
|
+
`,s=Xs(e,["hash-object","-w","--stdin"],o).stdout.trim(),i=Vte(s1(Yte(),"terragucci-rows-")),a={...process.env,...Qte,GIT_INDEX_FILE:s1(i,"index")};try{t&&Xs(e,["read-tree",t],void 0,a),Xs(e,["update-index","--add","--cacheinfo",`100644,${s},${a1}`],void 0,a);let c=Xs(e,["write-tree"],void 0,a).stdout.trim(),u=Xs(e,["commit-tree",c,...t?["-p",t]:[],"-m",r],void 0,a).stdout.trim();if(!u)throw new S("could not write the rows this apply holds");return Xs(e,["push","-q","origin",`${u}:refs/heads/${Du}`]).status===0}finally{Zte(i,{recursive:!0,force:!0})}}function l1(e){return Xx(e).file}async function Qx(e,t,n,r){let o=new Map,s=[],i=new Set;for(let a of t){let c=e.rows[a];if(!c||c===n)continue;let u=e.holders[c];o.has(c)||o.set(c,await r(u)),o.get(c)==="dead"?i.add(c):s.push({key:a,holder:u})}return{live:s,gone:[...i].sort()}}var d1=e=>new Promise(t=>setTimeout(t,e));async function p1(e,t,n,r){let o=[...new Set(t.map(s=>s.key))];if(o.length===0)return{ok:!0,tookOver:[]};for(let s=0;s<30;s++){let{parent:i,file:a}=Xx(e),{live:c,gone:u}=await Qx(a,o,n.run,r);if(c.length>0)return{ok:!1,held:c};let l={version:1,holders:{...a.holders},rows:{...a.rows}},d=u.map(f=>a.holders[f]);for(let f of u){delete l.holders[f];for(let[g,m]of Object.entries(l.rows))m===f&&delete l.rows[g]}l.holders[n.run]=n;for(let f of o)l.rows[f]=n.run;let p=u.length?`, taken over from run ${u.join(", ")}`:"";if(u1(e,i,l,`Hold ${o.length} row${o.length===1?"":"s"} for run ${n.run}${p}`))return{ok:!0,tookOver:d};await d1(200+Math.floor(Math.random()*800))}throw new S(`could not push the rows this apply holds to ${Du}; check that the job may push to it`)}async function f1(e,t,n){try{for(let r=0;r<30;r++){let{parent:o,file:s}=Xx(e),i=t.filter(c=>s.rows[c]===n);if(i.length===0)return!0;let a={version:1,holders:{...s.holders},rows:{...s.rows}};for(let c of i)delete a.rows[c];if(Object.values(a.rows).includes(n)||delete a.holders[n],u1(e,o,a,`Release ${i.length} row${i.length===1?"":"s"} of run ${n}`))return!0;await d1(200+Math.floor(Math.random()*800))}}catch{}return!1}function g1(e,t){let n=new Map,r=new Map(t.map(o=>[o.key,`${o.root}: ${o.address}`]));for(let o of e){let s=n.get(o.holder.run)??{holder:o.holder,addresses:new Set};s.addresses.add(r.get(o.key)??o.key),n.set(o.holder.run,s)}return[...n.values()].map(({holder:o,addresses:s})=>`run ${o.run}${o.url?` (${o.url})`:""} is applying ${[...s].sort().join(", ")}`).join("; ")}var Td=class{[Fs]=!0;parent;attribute;logicalName;constructor(t,n){this.parent=new WeakRef(t),this.attribute=n}_setLogicalName(t){this.logicalName=t}getLogicalName(){return this.logicalName}toString(){let t=this.logicalName?`${this.logicalName}.${this.attribute}`:`.${this.attribute}`;throw new Error(`A resource attribute reference (${t}) has no string form: it stands for a value the build resolves later, and interpolating it into a plain template literal would produce "[object Object]". Use the lexicon's own intrinsic, whose interior handles references \u2014 \`Sub\`\${\u2026}\`\` for CloudFormation \u2014 or move the reference out of the template.`)}[Symbol.toPrimitive](){return this.toString()}toJSON(){if(!this.logicalName)throw new Error(`Cannot serialize AttrRef for attribute "${this.attribute}": logical name not set`);return{__attrRef:{entity:this.logicalName,attribute:this.attribute}}}};function Mu(e,t,n){let r=function(o,s){Object.defineProperty(this,gu,{value:!0,enumerable:!1}),Object.defineProperty(this,"lexicon",{value:t,enumerable:!1}),Object.defineProperty(this,"entityType",{value:e,enumerable:!1}),Object.defineProperty(this,"kind",{value:"resource",enumerable:!1}),Object.defineProperty(this,"props",{value:o??{},enumerable:!1,configurable:!0}),Object.defineProperty(this,"attributes",{value:s??{},enumerable:!1,configurable:!0}),Object.defineProperty(this,"Ref",{value:this,enumerable:!1});for(let[i,a]of Object.entries(n))Object.defineProperty(this,i,{value:new Td(this,a),enumerable:!0,writable:!1})};return Object.defineProperty(r,"name",{value:e.split("::").pop()??e}),r}function Lu(e,t){let n=function(r){Object.defineProperty(this,gu,{value:!0,enumerable:!1}),Object.defineProperty(this,"lexicon",{value:t,enumerable:!1}),Object.defineProperty(this,"entityType",{value:e,enumerable:!1}),Object.defineProperty(this,"kind",{value:"property",enumerable:!1}),Object.defineProperty(this,"props",{value:r??{},enumerable:!1,configurable:!0})};return Object.defineProperty(n,"name",{value:e.split(".").pop()??e}),n}var pt=Mu("GitHub::Actions::Job","github",{});var Gu=Mu("GitHub::Actions::Workflow","github",{});var se=Lu("GitHub::Actions::Step","github");var rne=Symbol.for("chant.heldElsewhere");function ab(e){return typeof e=="object"&&e!==null&&e[rne]===!0}function eE(e){return e instanceof Td?!0:typeof e=="object"&&e!==null&&"parent"in e&&"attribute"in e&&"_setLogicalName"in e&&typeof e.parent=="object"&&typeof e.attribute=="string"}function one(e,t,n){for(let[r,o]of n){if(o!==e)continue;let s=r[t];return eE(s)?s.attribute:t}return t}function ta(e,t,n){if(e==null)return e;if(!ab(e)){if(eE(e)){let r=e.getLogicalName();if(!r)throw new Error(`Cannot serialize AttrRef for attribute "${e.attribute}": logical name not set`);return n.attrRef(r,e.attribute)}if(typeof e=="object"&&e!==null&&Fs in e&&"toJSON"in e&&typeof e.toJSON=="function")return ta(e.toJSON(),t,n);if(typeof e=="object"&&e!==null&&"entityType"in e){let r=e;if(Wl(r))return n.propertyDeclarable(r,s=>ta(s,t,n));let o=t.get(r);return o?n.resourceRef(o):n.propertyDeclarable(r,s=>ta(s,t,n))}if(Array.isArray(e))return e.filter(r=>!ab(r)).map(r=>ta(r,t,n));if(typeof e=="object"&&"__attrRef"in e){let r=e.__attrRef;return n.attrRef(r.entity,one(r.entity,r.attribute,t))}if(typeof e=="object"){let r={};for(let[o,s]of Object.entries(e)){if(ab(s))continue;let i=o;r[i]=ta(s,t,n)}return r}return e}}function zu(e){return e.replace(/([a-z0-9])([A-Z])/g,"$1-$2").toLowerCase()}function sne(e){return e.replace(/([a-z0-9])([A-Z])/g,"$1_$2").toLowerCase()}var sE={"GitHub::Actions::PushTrigger":"push","GitHub::Actions::PullRequestTrigger":"pull_request","GitHub::Actions::PullRequestTargetTrigger":"pull_request_target","GitHub::Actions::ScheduleTrigger":"schedule","GitHub::Actions::WorkflowDispatchTrigger":"workflow_dispatch","GitHub::Actions::WorkflowCallTrigger":"workflow_call","GitHub::Actions::WorkflowRunTrigger":"workflow_run","GitHub::Actions::RepositoryDispatchTrigger":"repository_dispatch"};function ine(e){return e in sE}var h1=new Set(["GitHub::Actions::Workflow","GitHub::Actions::Job","GitHub::Actions::ReusableWorkflowCallJob","GitHub::Actions::Step"]),Fu=new WeakMap;function cb(e){if(!e||typeof e!="object"||!("entityType"in e)||!("props"in e)||!h1.has(e.entityType))return;let t=e.props?.comment;return typeof t=="string"&&t!==""?t:void 0}function y1(e){if(!e||typeof e!="object"||!("comment"in e))return e;let{comment:t,...n}=e;return n}function tE(e,t){let n=" ".repeat(t);return e.split(`
|
|
130
130
|
`).map(r=>`${n}#${r.trimEnd()?` ${r.trimEnd()}`:""}`).join(`
|
|
131
|
-
`)}function
|
|
131
|
+
`)}function w1(e,t){if(!t)return;let n=cb(e);n&&Fu.set(t,n);let r=e?.props?.steps;!Array.isArray(r)||!Array.isArray(t.steps)||r.forEach((o,s)=>{let i=cb(o),a=t.steps[s];i&&a&&typeof a=="object"&&Fu.set(a,i)})}function ane(e){return{attrRef:(t,n)=>t,resourceRef:t=>zu(t),propertyDeclarable:(t,n)=>{if(!Xe(t)||typeof t.props!="object"||t.props===null)return;let r=t.props,o=h1.has(t.entityType),s={};for(let[i,a]of Object.entries(r))o&&i==="comment"||a!==void 0&&(s[i]=n(a));return Object.keys(s).length>0?s:void 0}}}function nE(e){if(e==null)return e;if(typeof e=="object"&&Fs in e&&"toYAML"in e&&typeof e.toYAML=="function")return e.toYAML();if(typeof e=="object"&&e!==null&&"entityType"in e)return e;if(Array.isArray(e))return e.map(nE);if(typeof e=="object"){let t={};for(let[n,r]of Object.entries(e))t[n]=nE(r);return t}return e}function Ad(e,t){let n=nE(e);return ta(n,t,ane(t))}var cne=new Set(["GitHub::Actions::Job","GitHub::Actions::ReusableWorkflowCallJob"]);function une(e,t){if(!e||typeof e!="object")return;let n=e;if("entityType"in n&&"props"in n&&cne.has(n.entityType)){let r=Ad(y1(n.props),t),o=r&&typeof r=="object"?rE(r):void 0;return w1(n,o),o}return rE(mo(n))}function b1(e,t){if(!e.jobs||typeof e.jobs!="object"||Array.isArray(e.jobs))return;let n={};for(let[r,o]of Object.entries(e.jobs)){let s=une(o,t);s&&(n[zu(r)]=s)}return Object.keys(n).length>0?n:void 0}function v1(e,t){if(e.length===0)return;let n={};for(let[r,o]of e){let s=Ad(y1(Xe(o)?o.props:void 0),t);if(!s)continue;let i=rE(s);w1(o,i),n[zu(r)]=i}return Object.keys(n).length>0?n:void 0}var $1=new Set(["env","with","outputs","secrets","variables"]);function rE(e){let t={};for(let[n,r]of Object.entries(e)){if(r==null)continue;let o=zu(n);t[o]=mo(r,$1.has(o))}return t}function mo(e,t=!1){if(e==null||typeof e!="object")return e;if(Array.isArray(e))return e.map(o=>mo(o,t));let n=e,r={};for(let[o,s]of Object.entries(n)){if(s==null)continue;let i=t?o:zu(o);r[i]=mo(s,t||$1.has(i))}return r}var iE={name:"github",rulePrefix:"GHA",serialize(e,t){let n=new Map;for(let[u,l]of e)n.set(l,u);let r=[],o=[],s=[],i=[],a=[];for(let[u,l]of e){if(Wl(l))continue;let d=l.entityType;d==="GitHub::Actions::Workflow"?r.push([u,l]):d==="GitHub::Actions::Job"||d==="GitHub::Actions::ReusableWorkflowCallJob"?o.push([u,l]):ine(d)?s.push([u,l]):d==="GitHub::Dependabot::Config"?i.push([u,l]):a.push([u,l])}let c=r.length>1?pne(r,o,s,e,n):dne(r,o,s,e,n);if(i.length>0){let[,u]=i[0],l=Xe(u)?u.props:void 0,d=lne(l);return typeof c=="string"?{primary:c,files:{"dependabot.yml":d}}:{...c,files:{...c.files??{},"dependabot.yml":d}}}return c}};function oE(e){if(Array.isArray(e))return e.map(oE);if(e!==null&&typeof e=="object"){let t={};for(let[n,r]of Object.entries(e))t[zu(n)]=oE(r);return t}return e}function lne(e){let t=oE(e);return aE(t)}function dne(e,t,n,r,o){let s={};if(e.length>0){let[,a]=e[0],c=Ad(Xe(a)?a.props:void 0,o);c&&(c.name&&(s.name=c.name),(c["run-name"]||c.runName)&&(s["run-name"]=c["run-name"]??c.runName),c.permissions&&(s.permissions=mo(c.permissions)),c.env&&(s.env=c.env),c.concurrency&&(s.concurrency=mo(c.concurrency)),c.defaults&&(s.defaults=mo(c.defaults)),c.on&&(s.on=k1(c.on)))}if(n.length>0){let a=s.on??{};for(let[,c]of n){let u=c.entityType,l=sE[u];if(!l)continue;let d=Ad(Xe(c)?c.props:void 0,o);d&&Object.keys(d).length>0?a[l]=mo(d):a[l]=null}s.on=a}let i;if(e.length>0){let a=Xe(e[0][1])?e[0][1].props:void 0;i=b1(a,o)}return i||(i=v1(t,o)),i&&(s.jobs=i),aE(s,e.length>0?cb(e[0][1]):void 0)}function pne(e,t,n,r,o){let s={},i="";for(let a=0;a<e.length;a++){let[c,u]=e[a],l={},d=Ad(Xe(u)?u.props:void 0,o);if(d&&(d.name&&(l.name=d.name),d.on&&(l.on=k1(d.on)),d.permissions&&(l.permissions=mo(d.permissions)),d.env&&(l.env=d.env),d.concurrency&&(l.concurrency=mo(d.concurrency)),d.defaults&&(l.defaults=mo(d.defaults))),a===0&&n.length>0){let h=l.on??{};for(let[,y]of n){let w=y.entityType,b=sE[w];if(!b)continue;let k=Ad(Xe(y)?y.props:void 0,o);k&&Object.keys(k).length>0?h[b]=mo(k):h[b]=null}l.on=h}let p=Xe(u)?u.props:void 0,g=b1(p,o)??(a===0?v1(t,o):void 0);g&&(l.jobs=g);let m=aE(l,cb(u));a===0?i=m:s[`${zu(c)}.yml`]=m}return{primary:i,files:s}}function k1(e){if(typeof e!="object"||e===null||Array.isArray(e))return e;let t={};for(let[n,r]of Object.entries(e)){let o=sne(n);typeof r=="object"&&r!==null&&!Array.isArray(r)?t[o]=mo(r):t[o]=r}return t}function aE(e,t){let n=[];t&&n.push(tE(t,0));let r=["name","run-name","on","permissions","env","concurrency","defaults","jobs"],o=new Set;for(let s of r)if(s in e&&e[s]!==void 0){o.add(s);let i=e[s];i===null?n.push(`${s}:`):s==="jobs"&&fne(i)?n.push(gne(i)):typeof i=="string"||typeof i=="number"||typeof i=="boolean"?n.push(`${s}: ${m1(i)}`):n.push(or(s,i))}for(let[s,i]of Object.entries(e))o.has(s)||i===void 0||(typeof i=="string"||typeof i=="number"||typeof i=="boolean"?n.push(`${s}: ${m1(i)}`):n.push(or(s,i)));return mne(n.join(`
|
|
132
132
|
|
|
133
133
|
`)+`
|
|
134
|
-
`)}function fne(e){return!e||typeof e!="object"?!1:Object.values(e).some(t=>!!t&&typeof t=="object"&&(Fu.has(t)||
|
|
135
|
-
`)}function mne(e){return e.replace(/^(\s*(?:- )?uses: )'([^\s']+) # ([^'\n]*)'$/gm,"$1$2 # $3")}function
|
|
134
|
+
`)}function fne(e){return!e||typeof e!="object"?!1:Object.values(e).some(t=>!!t&&typeof t=="object"&&(Fu.has(t)||_1(t).some(n=>!!n&&typeof n=="object"&&Fu.has(n))))}var _1=e=>{let t=e.steps;return Array.isArray(t)?t:[]};function gne(e){let t=["jobs:"];for(let[n,r]of Object.entries(e)){if(r===void 0)continue;if(!r||typeof r!="object"||Array.isArray(r)){t.push(or(n,r,1));continue}let o=Fu.get(r);o&&t.push(tE(o,1));let s=_1(r);if(!s.some(i=>!!i&&typeof i=="object"&&Fu.has(i))){t.push(or(n,r,1));continue}t.push(` ${n}:`);for(let[i,a]of Object.entries(r))if(a!==void 0){if(i!=="steps"){t.push(or(i,a,2));continue}t.push(" steps:");for(let c of s){let u=c&&typeof c=="object"?Fu.get(c):void 0;u&&t.push(tE(u,3)),t.push(xr([c],3).slice(1))}}}return t.join(`
|
|
135
|
+
`)}function mne(e){return e.replace(/^(\s*(?:- )?uses: )'([^\s']+) # ([^'\n]*)'$/gm,"$1$2 # $3")}function m1(e){return typeof e=="boolean"?e?"true":"false":typeof e=="number"?String(e):/^[\d]|[:#\[\]{}|>&*!%@`]|true|false|null|yes|no/i.test(e)?`'${e.replace(/'/g,"''")}'`:e}var hne="https://code.forgejo.org",yne={"actions/checkout":{kind:"mirror",repo:"actions/checkout"},"actions/setup-node":{kind:"mirror",repo:"actions/setup-node"},"actions/setup-go":{kind:"mirror",repo:"actions/setup-go"},"actions/setup-python":{kind:"mirror",repo:"actions/setup-python"},"actions/setup-java":{kind:"mirror",repo:"actions/setup-java"},"actions/setup-dotnet":{kind:"mirror",repo:"actions/setup-dotnet"},"actions/cache":{kind:"mirror",repo:"actions/cache"},"actions/upload-artifact":{kind:"mirror",repo:"actions/upload-artifact"},"actions/download-artifact":{kind:"mirror",repo:"actions/download-artifact"},"docker/build-push-action":{kind:"url",url:"https://github.com/docker/build-push-action"},"docker/login-action":{kind:"url",url:"https://github.com/docker/login-action"},"docker/setup-buildx-action":{kind:"url",url:"https://github.com/docker/setup-buildx-action"},"docker/setup-qemu-action":{kind:"url",url:"https://github.com/docker/setup-qemu-action"},"docker/metadata-action":{kind:"url",url:"https://github.com/docker/metadata-action"}};function wne(e){return e.startsWith("./")||e.startsWith("../")||e.startsWith(".\\")||e.startsWith("docker://")||/^https?:\/\//.test(e)}function R1(e,t={}){let n=e.trim();if(n===""||wne(n))return{rewritten:e};let r=(t.actionsRoot??hne).replace(/\/+$/,""),o=t.known??yne,s=n.lastIndexOf("@"),i=s>=0?n.slice(0,s):n,a=s>=0?n.slice(s+1):"",c=i.split("/");if(c.length<2)return{rewritten:e,warning:S1(n)};let u=`${c[0]}/${c[1]}`,l=c.slice(2).join("/"),d=o[u];if(!d)return{rewritten:e,warning:S1(n)};let p=d.kind==="mirror"?`${r}/${d.repo}`:d.url,f=l?`${p}/${l}`:p;return{rewritten:a?`${f}@${a}`:f}}function S1(e){return`forgejo: unresolved action ref '${e}' \u2014 it has no built-in mapping and won't resolve from a GitHub Marketplace on Forgejo. Use a full repository URL, or mirror it under forgejo.actionsRoot in chant.config.ts.`}var bne=new Set(["continue-on-error"]),x1="id-token",vne=new Set(["read","write","none"]);function $ne(e){return typeof e=="string"?e==="read-all"||e==="write-all":e===null||typeof e!="object"||Array.isArray(e)?!1:Object.values(e).every(n=>typeof n=="string"&&vne.has(n))}function kne(e){if(e===null||typeof e!="object")return;let t=e[x1];return t===void 0?void 0:{[x1]:t}}var _ne="runs-on",Sne="uses",Rne={"ubuntu-latest":"docker","ubuntu-24.04":"docker","ubuntu-22.04":"docker","ubuntu-20.04":"docker"};function xne(e){return e.replace(/([a-z0-9])([A-Z])/g,"$1-$2").toLowerCase()}function E1(e,t){return t.labels[e]??e}function Ene(e,t){return typeof e=="string"?E1(e,t):Array.isArray(e)&&e.every(n=>typeof n=="string")?e.map(n=>E1(n,t)):e}function cE(e,t){if(e===null||typeof e!="object")return e;if(HI(e))return T1(e,t);if(Array.isArray(e))return e.map(r=>cE(r,t));let n={};for(let[r,o]of Object.entries(e)){let s=xne(r);if(bne.has(s)){t.warnings.push(`forgejo: dropped '${s}' in ${t.where} \u2014 the Forgejo runner ignores it. Re-establish this control through your Forgejo/runner configuration.`);continue}if(s==="permissions"&&$ne(o)){let i=kne(o);i&&(n[r]=i);continue}if(s===_ne){n[r]=Ene(o,t);continue}if(s===Sne&&typeof o=="string"){n[r]=R1(o,{actionsRoot:t.actionsRoot}).rewritten;continue}n[r]=cE(o,t)}return n}function T1(e,t){let n=Object.getOwnPropertyDescriptors(e),r=Object.create(Object.getPrototypeOf(e),n),o=Xe(e)?e.props:void 0,s=cE(o,t);return Object.defineProperty(r,"props",{value:s,enumerable:n.props?.enumerable??!1,configurable:!0,writable:n.props?.writable??!0}),r}function A1(e,t={}){let n={...Rne,...t.runnerLabels??{}},r=[],o=new Map;for(let[s,i]of e){let a={labels:n,actionsRoot:t.actionsRoot,warnings:r,where:s};o.set(s,T1(i,a))}return{entities:o,warnings:r}}var rn=Mu("GitLab::CI::Job","gitlab",{});var O1=Lu("GitLab::CI::Image","gitlab");var on=Lu("GitLab::CI::Rule","gitlab");function P1(e){return e.split(`
|
|
136
136
|
`).map(t=>`#${t.trimEnd()?` ${t.trimEnd()}`:""}`).join(`
|
|
137
|
-
`)}function
|
|
137
|
+
`)}function j1(e){let t=Xe(e)?e.props:void 0;if(!t||!("comment"in t))return{props:t};let{comment:n,...r}=t;return{props:r,comment:typeof n=="string"&&n!==""?n:void 0}}function Tne(e){return{attrRef:(t,n)=>t,resourceRef:t=>t.replace(/([a-z0-9])([A-Z])/g,"$1-$2").toLowerCase(),propertyDeclarable:(t,n)=>{if(!Xe(t)||typeof t.props!="object"||t.props===null)return;let r=t.props,o={};for(let[s,i]of Object.entries(r))i!==void 0&&(o[s]=n(i));return Object.keys(o).length>0?o:void 0}}}function uE(e){if(e==null)return e;if(typeof e=="object"&&Fs in e&&"toYAML"in e&&typeof e.toYAML=="function")return e.toYAML();if(typeof e=="object"&&e!==null&&"entityType"in e)return e;if(Array.isArray(e))return e.map(uE);if(typeof e=="object"){let t={};for(let[n,r]of Object.entries(e))t[n]=uE(r);return t}return e}function ub(e,t){let n=uE(e);return ta(n,t,Tne(t))}var I1={name:"gitlab",rulePrefix:"WGL",serialize(e,t){let n=new Map;for(let[g,m]of e)n.set(m,g);let r=[],o=[],s=[],i=[],a=[],c=[];for(let[g,m]of e){if(Wl(m))continue;let h=m.entityType;h==="GitLab::CI::Job"?o.push([g,m]):h==="GitLab::CI::Default"?i.push([g,m]):h==="GitLab::CI::Workflow"?a.push([g,m]):h==="GitLab::CI::Pipeline"?s.push([g,m]):c.push([g,m])}let u=[],l=[],d=[],p;for(let[,g]of s){let{props:m,comment:h}=j1(g);h&&u.push(h);let y=ub(m,n);y&&(y.include!==void 0&&l.push(...Array.isArray(y.include)?y.include:[y.include]),Array.isArray(y.stages)&&d.push(...y.stages.map(String)),y.variables&&typeof y.variables=="object"&&(p={...p,...y.variables}))}u.length>0&&r.push(P1(u.join(`
|
|
138
138
|
|
|
139
|
-
`))),l.length>0&&r.push(or("include",l));let f=new Set(d);for(let[,g]of o){let m=Xe(g)?g.props:void 0;m?.stage&&typeof m.stage=="string"&&f.add(m.stage)}f.size>0&&r.push(or("stages",[...f])),p&&Object.keys(p).length>0&&r.push(or("variables",p));for(let[,g]of i){let m=ub(Xe(g)?g.props:void 0,n);m&&r.push(or("default",m))}for(let[,g]of a){let m=ub(Xe(g)?g.props:void 0,n);m&&r.push(or("workflow",m))}for(let[g,m]of o){let{props:h,comment:y}=
|
|
139
|
+
`))),l.length>0&&r.push(or("include",l));let f=new Set(d);for(let[,g]of o){let m=Xe(g)?g.props:void 0;m?.stage&&typeof m.stage=="string"&&f.add(m.stage)}f.size>0&&r.push(or("stages",[...f])),p&&Object.keys(p).length>0&&r.push(or("variables",p));for(let[,g]of i){let m=ub(Xe(g)?g.props:void 0,n);m&&r.push(or("default",m))}for(let[,g]of a){let m=ub(Xe(g)?g.props:void 0,n);m&&r.push(or("workflow",m))}for(let[g,m]of o){let{props:h,comment:y}=j1(m),w=ub(h,n);if(w){let b=g.replace(/([a-z0-9])([A-Z])/g,"$1-$2").toLowerCase(),k=or(b,w);r.push(y?`${P1(y)}
|
|
140
140
|
${k}`:k)}}return r.join(`
|
|
141
141
|
|
|
142
142
|
`)+`
|
|
143
|
-
`}};import{readFileSync as Ane}from"node:fs";var One=["pending","running","success","failed","failure","canceled","error"],Pne={github:{pending:"pending",running:"pending",success:"success",failed:"failure",failure:"failure",canceled:"error",error:"error"},gitlab:{pending:"pending",running:"running",success:"success",failed:"failed",failure:"failed",canceled:"canceled",error:"failed"}};function ML(e,t){let n=Pne[e==="gitlab"?"gitlab":"github"][t];if(n===void 0)throw new Error(`a commit status state is one of ${One.join(", ")}, not ${JSON.stringify(t)}`);return n}var jne=new Set([405,406,409,422]);function LL(e){if(e instanceof Zr&&jne.has(e.status)){let t=e.detail;try{let n=JSON.parse(e.detail),r=n.message??n.error;typeof r=="string"&&(t=r)}catch{}return{merged:!1,reason:t.slice(0,300)||`the forge refused the merge (${e.status})`}}throw e}var Zr=class extends Error{constructor(n,r,o,s){super(`${n} ${r} answered ${o}${s?`: ${s.slice(0,300)}`:""}`);this.method=n;this.url=r;this.status=o;this.detail=s;this.name="ForgeApiError"}method;url;status;detail},Ine={github:"github.com",gitlab:"gitlab.com",forgejo:null};function GL(e,t,n){let r=t.toLowerCase(),o=n.toLowerCase();return r===Ine[e]?`${e}:${o}`:`${e}@${r}:${o}`}function FL(e,t,n){let r=e.replace(/\/+$/,""),o=async(s,i,a)=>{let c=`${r}${i}`,u=await n(c,{method:s,headers:{...t,...a!==void 0?{"Content-Type":"application/json"}:{}},...a!==void 0?{body:JSON.stringify(a)}:{}}),l=await u.text();if(!u.ok)throw new Zr(s,c,u.status,l);return l?JSON.parse(l):null};return{call:o,async list(s,i,a){let c=[];for(let u=1;u<=50;u++){let l=s.includes("?")?"&":"?",d=await o("GET",`${s}${l}${i(u)}`);if(!Array.isArray(d)||(c.push(...d),d.length<a))break}return c}}}var We=e=>typeof e=="object"&&e!==null&&!Array.isArray(e),CL=e=>We(e)&&We(e.user)&&typeof e.user.login=="string"?e.user.login:void 0,it=e=>typeof e=="string"?e:void 0,Qs=e=>typeof e=="number"?e:typeof e=="string"&&/^\d+$/.test(e)?Number(e):void 0,zL=(e,t,n)=>n==="anywhere"?e.includes(t):e.startsWith(t),Cne=(e,t)=>{if(e===void 0||e==="")return!0;let n=(typeof e=="string"?[e]:e).map(r=>r.toLowerCase());return t!==void 0&&n.includes(t.toLowerCase())},lE=(e,t)=>n=>zL(n.body,e,t.match)&&Cne(t.author,n.author);function UL(e,t,n,r){if(!t)throw new Error(`the ${e} needs a marker to be found again`);if(!zL(n,t,r))throw new Error(`the ${e} body ${r==="anywhere"?"does not carry":"does not start with"} its marker ${JSON.stringify(t)}, so the next run could not find it`)}async function BL(e,t,n,r){if(!n)throw new Error("the comment needs a marker to be found");return(await e.comments(t)).filter(lE(n,r)).sort((o,s)=>o.id-s.id)[0]}async function NL(e,t,n,r,o,s){s&&UL("comment",n,r,o.match);let i=await BL(e,t,n,o);if(!i){let u=await e.addComment(t,r);return{action:"created",id:u?.id??0,...u?.url?{url:u.url}:{}}}if(i.body===r)return{action:"unchanged",id:i.id,...i.url?{url:i.url}:{}};let c=(await e.editComment(t,i.id,r))?.url??i.url;return{action:"updated",id:i.id,...c?{url:c}:{}}}async function DL(e,t,n,r,o){n&&o&&UL("issue",t,n.body,r.match);let s=(await e.openIssues(t)).filter(lE(t,r)).sort((c,u)=>c.id-u.id)[0],i=c=>c?{number:c.id,...c.url?{url:c.url}:{},...c.created?{created:c.created}:{}}:{};if(n===null)return s?r.closeOnClear?(await e.closeIssue(s.id,r.clearComment),{action:"closed",...i(s)}):{action:"left",...i(s)}:{action:"none"};if(!s)return{action:"opened",...i(await e.openIssue(n.title,n.body))};if(Nne({title:s.title??"",body:s.body},n,r.sameWhen))return{action:"unchanged",...i(s)};let a=await e.editIssue(s.id,n.title,n.body);return{action:"updated",...i(s),...a?.url?{url:a.url}:{}}}function Nne(e,t,n){return typeof n=="function"?n(e,t):e.title!==t.title?!1:typeof n=="string"&&n!==""?e.body.includes(n):e.body===t.body}function WL(e){return{async addComment(t,n){let r=await e.addComment(t,n);return{id:r?.id??0,...r?.url?{url:r.url}:{}}},async findIssue(t,n={}){if(!t)throw new Error("the issue needs a marker to be found");let r=(await e.openIssues(t)).filter(lE(t,n)).sort((o,s)=>o.id-s.id)[0];return r?{number:r.id,title:r.title??"",body:r.body,...r.created?{created:r.created}:{},...r.url?{url:r.url}:{}}:void 0},async findComment(t,n,r={}){let o=await BL(e,t,n,r);return o?{id:o.id,body:o.body,...o.url?{url:o.url}:{}}:void 0},upsertComment:(t,n,r,o={})=>NL(e,t,n,r,o,!0),upsertNote:async(t,n,r)=>(await NL(e,t,n,r,{},!1)).url,trackIssue:(t,n,r={})=>DL(e,t,n,r,!0),upsertIssue:async(t,n,r)=>(await DL(e,t,{title:n,body:r},{},!1)).url}}var Dne=140,Mne=255;function HL(e,t){let n=(t.host??"github.com").toLowerCase(),r=FL(t.apiBase,e==="github"?{...t.token?{Authorization:`Bearer ${t.token}`}:{},Accept:"application/vnd.github+json"}:{...t.token?{Authorization:`token ${t.token}`}:{},Accept:"application/json"},t.fetch??globalThis.fetch),o=`/repos/${t.repo}`,s=e==="github"?100:50,i=p=>e==="github"?`per_page=100&page=${p}`:`limit=50&page=${p}`,a=p=>{if(!We(p))return;let f=Qs(p.number)??Qs(p.id);if(f===void 0)return;let g=CL(p),m=it(p.html_url),h=it(p.created_at);return{id:f,body:it(p.body)??"",...h?{created:h}:{},...it(p.title)!==void 0?{title:it(p.title)}:{},...g?{author:g}:{},...m?{url:m}:{}}},c=p=>p.map(a).filter(f=>f!==void 0),u=p=>{if(!We(p))return;let f=Qs(p.number),g=We(p.head)?it(p.head.ref):void 0;if(f===void 0)return;let m=We(p.base)?it(p.base.ref):void 0,h=it(p.html_url);return{number:f,title:it(p.title)??"",body:it(p.body)??"",head:g??"",...m?{base:m}:{},...h?{url:h}:{}}},l=(p,f)=>{if(!We(p)||!We(p.head)||p.head.ref!==f)return!1;let g=We(p.head.repo)?it(p.head.repo.full_name):void 0;return g===void 0||g.toLowerCase()===t.repo.toLowerCase()},d={comments:async p=>c(await r.list(`${o}/issues/${p}/comments`,i,s)),addComment:async(p,f)=>a(await r.call("POST",`${o}/issues/${p}/comments`,{body:f})),editComment:async(p,f,g)=>a(await r.call("PATCH",`${o}/issues/comments/${f}`,{body:g})),openIssues:async()=>c((await r.list(`${o}/issues?state=open`,i,s)).filter(p=>We(p)&&!We(p.pull_request))),openIssue:async(p,f)=>a(await r.call("POST",`${o}/issues`,{title:p,body:f})),editIssue:async(p,f,g)=>a(await r.call("PATCH",`${o}/issues/${p}`,{title:f,body:g})),async closeIssue(p,f){f&&await r.call("POST",`${o}/issues/${p}/comments`,{body:f}),await r.call("PATCH",`${o}/issues/${p}`,{state:"closed"})}};return{kind:e,host:n,...t.runUrl?{runUrl:t.runUrl}:{},principalOf:p=>GL(e,n,p),...WL(d),async setStatus(p,f){await r.call("POST",`${o}/statuses/${p}`,{state:ML(e,f.state),context:f.context,description:f.description.slice(0,Dne),...f.url?{target_url:f.url}:{}})},async approvers(p){let f=await r.list(`${o}/pulls/${p}/reviews`,i,s),g=new Map;for(let m of f){let h=CL(m);if(!h||!We(m)||typeof m.state!="string")continue;let y=m.dismissed===!0?"DISMISSED":m.state.toUpperCase();y==="COMMENTED"||y==="COMMENT"||y==="PENDING"||g.set(h,y)}return[...g].filter(([,m])=>m==="APPROVED").map(([m])=>m).sort()},async pullRequestFor(p){if(e==="forgejo")try{let y=await r.call("GET",`${o}/commits/${p}/pull`);return We(y)&&typeof y.number=="number"?y.number:null}catch(y){if(y instanceof Zr&&y.status===404)return null;throw y}let f=await r.call("GET",`${o}/commits/${p}/pulls`);if(!Array.isArray(f))return null;let g=f.filter(y=>We(y)&&y.merged_at),h=g.find(y=>We(y)&&y.merge_commit_sha===p)??g[0];return h&&typeof h.number=="number"?h.number:null},async openPullRequest(p){let f=p.base??await qL(r,o,"default_branch"),g=p.draft&&e==="forgejo"?`WIP: ${p.title}`:p.title,m=await r.call("POST",`${o}/pulls`,{title:g,body:p.body,head:p.head,base:f,...p.draft&&e==="github"?{draft:!0}:{}});return u(m)??{number:0,title:g,body:p.body,head:p.head,base:f}},async findPullRequest(p){let f=t.repo.split("/")[0],g=e==="github"?`?state=open&head=${encodeURIComponent(`${f}:${p}`)}`:"?state=open",m;try{m=await r.list(`${o}/pulls${g}`,i,s)}catch(y){if(e!=="forgejo"||!(y instanceof Zr)||y.status!==404)throw y;await r.call("GET",o);return}return m.filter(y=>l(y,p)).map(u).filter(y=>y!==void 0).sort((y,w)=>y.number-w.number)[0]},async editPullRequest(p,f){return u(await r.call("PATCH",`${o}/pulls/${p}`,{...f.title!==void 0?{title:f.title}:{},...f.body!==void 0?{body:f.body}:{}}))},async addLabels(p,f){f.length!==0&&await r.call("POST",`${o}/issues/${p}/labels`,{labels:[...f]})},async mergePullRequest(p,f){let g=f.method??"merge",m;try{m=e==="github"?await r.call("PUT",`${o}/pulls/${p}/merge`,{sha:f.sha,merge_method:g,...f.title!==void 0?{commit_title:f.title}:{},...f.message!==void 0?{commit_message:f.message}:{}}):await r.call("POST",`${o}/pulls/${p}/merge`,{Do:g,head_commit_id:f.sha,...f.title!==void 0?{MergeTitleField:f.title}:{},...f.message!==void 0?{MergeMessageField:f.message}:{},...f.deleteBranch?{delete_branch_after_merge:!0}:{}})}catch(w){return LL(w)}if(We(m)&&m.merged===!1)return{merged:!1,reason:it(m.message)??"the forge did not merge it"};let h=await r.call("GET",`${o}/pulls/${p}`).catch(()=>{}),y=(We(m)?it(m.sha):void 0)??(We(h)?it(h.merge_commit_sha):void 0);if(e==="github"&&f.deleteBranch&&We(h)&&We(h.head)&&it(h.head.ref)&&l(h,it(h.head.ref))){let w=it(h.head.ref).split("/").map(encodeURIComponent).join("/");await r.call("DELETE",`${o}/git/refs/heads/${w}`).catch(()=>{})}return{merged:!0,...y?{sha:y}:{}}}}}async function qL(e,t,n){let r=await e.call("GET",t),o=We(r)?it(r[n]):void 0;if(!o)throw new Error(`${t} names no default branch to open the pull request against; pass base`);return o}function dE(e){return HL("github",e)}function Ag(e){return HL("forgejo",e)}function pE(e){let t=(e.host??"gitlab.com").toLowerCase(),n=FL(e.apiBase,e.token?{[e.tokenHeader??"PRIVATE-TOKEN"]:e.token}:{},e.fetch??globalThis.fetch),r=`/projects/${encodeURIComponent(decodeURIComponent(e.project))}`,o=d=>`per_page=100&page=${d}`,s=d=>We(d.author)?it(d.author.username):void 0,i=d=>{if(!We(d))return;let p=Qs(d.id);if(p===void 0)return;let f=s(d);return{id:p,body:it(d.body)??"",...f?{author:f}:{}}},a=d=>{if(!We(d))return;let p=Qs(d.iid);if(p===void 0)return;let f=s(d),g=it(d.web_url),m=it(d.created_at);return{id:p,body:it(d.description)??"",...m?{created:m}:{},...it(d.title)!==void 0?{title:it(d.title)}:{},...f?{author:f}:{},...g?{url:g}:{}}},c=`${r}/merge_requests`,u=d=>{if(!We(d))return;let p=Qs(d.iid);if(p===void 0)return;let f=it(d.target_branch),g=it(d.web_url);return{number:p,title:it(d.title)??"",body:it(d.description)??"",head:it(d.source_branch)??"",...f?{base:f}:{},...g?{url:g}:{}}},l={comments:async d=>(await n.list(`${c}/${d}/notes`,o,100)).filter(p=>!(We(p)&&p.system===!0)).map(i).filter(p=>p!==void 0),addComment:async(d,p)=>i(await n.call("POST",`${c}/${d}/notes`,{body:p})),editComment:async(d,p,f)=>i(await n.call("PUT",`${c}/${d}/notes/${p}`,{body:f})),openIssues:async d=>(await n.list(`${r}/issues?state=opened&search=${encodeURIComponent(d)}&in=description`,o,100)).map(a).filter(p=>p!==void 0),openIssue:async(d,p)=>a(await n.call("POST",`${r}/issues`,{title:d,description:p})),editIssue:async(d,p,f)=>a(await n.call("PUT",`${r}/issues/${d}`,{title:p,description:f})),async closeIssue(d,p){p&&await n.call("POST",`${r}/issues/${d}/notes`,{body:p}),await n.call("PUT",`${r}/issues/${d}`,{state_event:"close"})}};return{kind:"gitlab",host:t,...e.runUrl?{runUrl:e.runUrl}:{},principalOf:d=>GL("gitlab",t,d),...WL(l),async setStatus(d,p){try{await n.call("POST",`${r}/statuses/${d}`,{state:ML("gitlab",p.state),name:p.context,description:p.description.slice(0,Mne),...p.url?{target_url:p.url}:{},...p.pipelineId!==void 0?{pipeline_id:p.pipelineId}:{}})}catch(f){if(f instanceof Zr&&f.status===400&&/cannot transition status/i.test(f.message))return;throw f}},async approvers(d){let p=await n.call("GET",`${r}/merge_requests/${d}/approvals`),g=(We(p)&&Array.isArray(p.approved_by)?p.approved_by:[]).map(m=>We(m)&&We(m.user)&&typeof m.user.username=="string"?m.user.username:void 0).filter(m=>m!==void 0);return[...new Set(g)].sort()},async pullRequestFor(d){let p=await n.call("GET",`${r}/repository/commits/${d}/merge_requests`);if(!Array.isArray(p))return null;let f=p.find(g=>We(g)&&g.state==="merged"&&(g.merge_commit_sha===d||g.squash_commit_sha===d))??p.find(g=>We(g)&&g.state==="merged");return We(f)&&typeof f.iid=="number"?f.iid:null},async openPullRequest(d){let p=d.base??await qL(n,r,"default_branch"),f=d.draft?`Draft: ${d.title}`:d.title,g=await n.call("POST",c,{source_branch:d.head,target_branch:p,title:f,description:d.body});return u(g)??{number:0,title:f,body:d.body,head:d.head,base:p}},async findPullRequest(d){return(await n.list(`${c}?state=opened&source_branch=${encodeURIComponent(d)}`,o,100)).filter(f=>We(f)&&f.source_branch===d&&(f.source_project_id===void 0||f.source_project_id===f.project_id)).map(u).filter(f=>f!==void 0).sort((f,g)=>f.number-g.number)[0]},async editPullRequest(d,p){return u(await n.call("PUT",`${c}/${d}`,{...p.title!==void 0?{title:p.title}:{},...p.body!==void 0?{description:p.body}:{}}))},async addLabels(d,p){p.length!==0&&await n.call("PUT",`${c}/${d}`,{add_labels:p.join(",")})},async mergePullRequest(d,p){let f=p.method??"merge";if(f==="rebase")throw new Error("GitLab has no one-call rebase merge; use method merge or squash");let g=p.title!==void 0?p.message?`${p.title}
|
|
143
|
+
`}};import{readFileSync as Ane}from"node:fs";var One=["pending","running","success","failed","failure","canceled","error"],Pne={github:{pending:"pending",running:"pending",success:"success",failed:"failure",failure:"failure",canceled:"error",error:"error"},gitlab:{pending:"pending",running:"running",success:"success",failed:"failed",failure:"failed",canceled:"canceled",error:"failed"}};function M1(e,t){let n=Pne[e==="gitlab"?"gitlab":"github"][t];if(n===void 0)throw new Error(`a commit status state is one of ${One.join(", ")}, not ${JSON.stringify(t)}`);return n}var jne=new Set([405,406,409,422]);function L1(e){if(e instanceof Zr&&jne.has(e.status)){let t=e.detail;try{let n=JSON.parse(e.detail),r=n.message??n.error;typeof r=="string"&&(t=r)}catch{}return{merged:!1,reason:t.slice(0,300)||`the forge refused the merge (${e.status})`}}throw e}var Zr=class extends Error{constructor(n,r,o,s){super(`${n} ${r} answered ${o}${s?`: ${s.slice(0,300)}`:""}`);this.method=n;this.url=r;this.status=o;this.detail=s;this.name="ForgeApiError"}method;url;status;detail},Ine={github:"github.com",gitlab:"gitlab.com",forgejo:null};function G1(e,t,n){let r=t.toLowerCase(),o=n.toLowerCase();return r===Ine[e]?`${e}:${o}`:`${e}@${r}:${o}`}function F1(e,t,n){let r=e.replace(/\/+$/,""),o=async(s,i,a)=>{let c=`${r}${i}`,u=await n(c,{method:s,headers:{...t,...a!==void 0?{"Content-Type":"application/json"}:{}},...a!==void 0?{body:JSON.stringify(a)}:{}}),l=await u.text();if(!u.ok)throw new Zr(s,c,u.status,l);return l?JSON.parse(l):null};return{call:o,async list(s,i,a){let c=[];for(let u=1;u<=50;u++){let l=s.includes("?")?"&":"?",d=await o("GET",`${s}${l}${i(u)}`);if(!Array.isArray(d)||(c.push(...d),d.length<a))break}return c}}}var We=e=>typeof e=="object"&&e!==null&&!Array.isArray(e),C1=e=>We(e)&&We(e.user)&&typeof e.user.login=="string"?e.user.login:void 0,it=e=>typeof e=="string"?e:void 0,Qs=e=>typeof e=="number"?e:typeof e=="string"&&/^\d+$/.test(e)?Number(e):void 0,z1=(e,t,n)=>n==="anywhere"?e.includes(t):e.startsWith(t),Cne=(e,t)=>{if(e===void 0||e==="")return!0;let n=(typeof e=="string"?[e]:e).map(r=>r.toLowerCase());return t!==void 0&&n.includes(t.toLowerCase())},lE=(e,t)=>n=>z1(n.body,e,t.match)&&Cne(t.author,n.author);function U1(e,t,n,r){if(!t)throw new Error(`the ${e} needs a marker to be found again`);if(!z1(n,t,r))throw new Error(`the ${e} body ${r==="anywhere"?"does not carry":"does not start with"} its marker ${JSON.stringify(t)}, so the next run could not find it`)}async function B1(e,t,n,r){if(!n)throw new Error("the comment needs a marker to be found");return(await e.comments(t)).filter(lE(n,r)).sort((o,s)=>o.id-s.id)[0]}async function N1(e,t,n,r,o,s){s&&U1("comment",n,r,o.match);let i=await B1(e,t,n,o);if(!i){let u=await e.addComment(t,r);return{action:"created",id:u?.id??0,...u?.url?{url:u.url}:{}}}if(i.body===r)return{action:"unchanged",id:i.id,...i.url?{url:i.url}:{}};let c=(await e.editComment(t,i.id,r))?.url??i.url;return{action:"updated",id:i.id,...c?{url:c}:{}}}async function D1(e,t,n,r,o){n&&o&&U1("issue",t,n.body,r.match);let s=(await e.openIssues(t)).filter(lE(t,r)).sort((c,u)=>c.id-u.id)[0],i=c=>c?{number:c.id,...c.url?{url:c.url}:{},...c.created?{created:c.created}:{}}:{};if(n===null)return s?r.closeOnClear?(await e.closeIssue(s.id,r.clearComment),{action:"closed",...i(s)}):{action:"left",...i(s)}:{action:"none"};if(!s)return{action:"opened",...i(await e.openIssue(n.title,n.body))};if(Nne({title:s.title??"",body:s.body},n,r.sameWhen))return{action:"unchanged",...i(s)};let a=await e.editIssue(s.id,n.title,n.body);return{action:"updated",...i(s),...a?.url?{url:a.url}:{}}}function Nne(e,t,n){return typeof n=="function"?n(e,t):e.title!==t.title?!1:typeof n=="string"&&n!==""?e.body.includes(n):e.body===t.body}function W1(e){return{async addComment(t,n){let r=await e.addComment(t,n);return{id:r?.id??0,...r?.url?{url:r.url}:{}}},async findIssue(t,n={}){if(!t)throw new Error("the issue needs a marker to be found");let r=(await e.openIssues(t)).filter(lE(t,n)).sort((o,s)=>o.id-s.id)[0];return r?{number:r.id,title:r.title??"",body:r.body,...r.created?{created:r.created}:{},...r.url?{url:r.url}:{}}:void 0},async findComment(t,n,r={}){let o=await B1(e,t,n,r);return o?{id:o.id,body:o.body,...o.url?{url:o.url}:{}}:void 0},upsertComment:(t,n,r,o={})=>N1(e,t,n,r,o,!0),upsertNote:async(t,n,r)=>(await N1(e,t,n,r,{},!1)).url,trackIssue:(t,n,r={})=>D1(e,t,n,r,!0),upsertIssue:async(t,n,r)=>(await D1(e,t,{title:n,body:r},{},!1)).url}}var Dne=140,Mne=255;function H1(e,t){let n=(t.host??"github.com").toLowerCase(),r=F1(t.apiBase,e==="github"?{...t.token?{Authorization:`Bearer ${t.token}`}:{},Accept:"application/vnd.github+json"}:{...t.token?{Authorization:`token ${t.token}`}:{},Accept:"application/json"},t.fetch??globalThis.fetch),o=`/repos/${t.repo}`,s=e==="github"?100:50,i=p=>e==="github"?`per_page=100&page=${p}`:`limit=50&page=${p}`,a=p=>{if(!We(p))return;let f=Qs(p.number)??Qs(p.id);if(f===void 0)return;let g=C1(p),m=it(p.html_url),h=it(p.created_at);return{id:f,body:it(p.body)??"",...h?{created:h}:{},...it(p.title)!==void 0?{title:it(p.title)}:{},...g?{author:g}:{},...m?{url:m}:{}}},c=p=>p.map(a).filter(f=>f!==void 0),u=p=>{if(!We(p))return;let f=Qs(p.number),g=We(p.head)?it(p.head.ref):void 0;if(f===void 0)return;let m=We(p.base)?it(p.base.ref):void 0,h=it(p.html_url);return{number:f,title:it(p.title)??"",body:it(p.body)??"",head:g??"",...m?{base:m}:{},...h?{url:h}:{}}},l=(p,f)=>{if(!We(p)||!We(p.head)||p.head.ref!==f)return!1;let g=We(p.head.repo)?it(p.head.repo.full_name):void 0;return g===void 0||g.toLowerCase()===t.repo.toLowerCase()},d={comments:async p=>c(await r.list(`${o}/issues/${p}/comments`,i,s)),addComment:async(p,f)=>a(await r.call("POST",`${o}/issues/${p}/comments`,{body:f})),editComment:async(p,f,g)=>a(await r.call("PATCH",`${o}/issues/comments/${f}`,{body:g})),openIssues:async()=>c((await r.list(`${o}/issues?state=open`,i,s)).filter(p=>We(p)&&!We(p.pull_request))),openIssue:async(p,f)=>a(await r.call("POST",`${o}/issues`,{title:p,body:f})),editIssue:async(p,f,g)=>a(await r.call("PATCH",`${o}/issues/${p}`,{title:f,body:g})),async closeIssue(p,f){f&&await r.call("POST",`${o}/issues/${p}/comments`,{body:f}),await r.call("PATCH",`${o}/issues/${p}`,{state:"closed"})}};return{kind:e,host:n,...t.runUrl?{runUrl:t.runUrl}:{},principalOf:p=>G1(e,n,p),...W1(d),async setStatus(p,f){await r.call("POST",`${o}/statuses/${p}`,{state:M1(e,f.state),context:f.context,description:f.description.slice(0,Dne),...f.url?{target_url:f.url}:{}})},async approvers(p){let f=await r.list(`${o}/pulls/${p}/reviews`,i,s),g=new Map;for(let m of f){let h=C1(m);if(!h||!We(m)||typeof m.state!="string")continue;let y=m.dismissed===!0?"DISMISSED":m.state.toUpperCase();y==="COMMENTED"||y==="COMMENT"||y==="PENDING"||g.set(h,y)}return[...g].filter(([,m])=>m==="APPROVED").map(([m])=>m).sort()},async pullRequestFor(p){if(e==="forgejo")try{let y=await r.call("GET",`${o}/commits/${p}/pull`);return We(y)&&typeof y.number=="number"?y.number:null}catch(y){if(y instanceof Zr&&y.status===404)return null;throw y}let f=await r.call("GET",`${o}/commits/${p}/pulls`);if(!Array.isArray(f))return null;let g=f.filter(y=>We(y)&&y.merged_at),h=g.find(y=>We(y)&&y.merge_commit_sha===p)??g[0];return h&&typeof h.number=="number"?h.number:null},async openPullRequest(p){let f=p.base??await q1(r,o,"default_branch"),g=p.draft&&e==="forgejo"?`WIP: ${p.title}`:p.title,m=await r.call("POST",`${o}/pulls`,{title:g,body:p.body,head:p.head,base:f,...p.draft&&e==="github"?{draft:!0}:{}});return u(m)??{number:0,title:g,body:p.body,head:p.head,base:f}},async findPullRequest(p){let f=t.repo.split("/")[0],g=e==="github"?`?state=open&head=${encodeURIComponent(`${f}:${p}`)}`:"?state=open",m;try{m=await r.list(`${o}/pulls${g}`,i,s)}catch(y){if(e!=="forgejo"||!(y instanceof Zr)||y.status!==404)throw y;await r.call("GET",o);return}return m.filter(y=>l(y,p)).map(u).filter(y=>y!==void 0).sort((y,w)=>y.number-w.number)[0]},async editPullRequest(p,f){return u(await r.call("PATCH",`${o}/pulls/${p}`,{...f.title!==void 0?{title:f.title}:{},...f.body!==void 0?{body:f.body}:{}}))},async addLabels(p,f){f.length!==0&&await r.call("POST",`${o}/issues/${p}/labels`,{labels:[...f]})},async mergePullRequest(p,f){let g=f.method??"merge",m;try{m=e==="github"?await r.call("PUT",`${o}/pulls/${p}/merge`,{sha:f.sha,merge_method:g,...f.title!==void 0?{commit_title:f.title}:{},...f.message!==void 0?{commit_message:f.message}:{}}):await r.call("POST",`${o}/pulls/${p}/merge`,{Do:g,head_commit_id:f.sha,...f.title!==void 0?{MergeTitleField:f.title}:{},...f.message!==void 0?{MergeMessageField:f.message}:{},...f.deleteBranch?{delete_branch_after_merge:!0}:{}})}catch(w){return L1(w)}if(We(m)&&m.merged===!1)return{merged:!1,reason:it(m.message)??"the forge did not merge it"};let h=await r.call("GET",`${o}/pulls/${p}`).catch(()=>{}),y=(We(m)?it(m.sha):void 0)??(We(h)?it(h.merge_commit_sha):void 0);if(e==="github"&&f.deleteBranch&&We(h)&&We(h.head)&&it(h.head.ref)&&l(h,it(h.head.ref))){let w=it(h.head.ref).split("/").map(encodeURIComponent).join("/");await r.call("DELETE",`${o}/git/refs/heads/${w}`).catch(()=>{})}return{merged:!0,...y?{sha:y}:{}}}}}async function q1(e,t,n){let r=await e.call("GET",t),o=We(r)?it(r[n]):void 0;if(!o)throw new Error(`${t} names no default branch to open the pull request against; pass base`);return o}function dE(e){return H1("github",e)}function Ag(e){return H1("forgejo",e)}function pE(e){let t=(e.host??"gitlab.com").toLowerCase(),n=F1(e.apiBase,e.token?{[e.tokenHeader??"PRIVATE-TOKEN"]:e.token}:{},e.fetch??globalThis.fetch),r=`/projects/${encodeURIComponent(decodeURIComponent(e.project))}`,o=d=>`per_page=100&page=${d}`,s=d=>We(d.author)?it(d.author.username):void 0,i=d=>{if(!We(d))return;let p=Qs(d.id);if(p===void 0)return;let f=s(d);return{id:p,body:it(d.body)??"",...f?{author:f}:{}}},a=d=>{if(!We(d))return;let p=Qs(d.iid);if(p===void 0)return;let f=s(d),g=it(d.web_url),m=it(d.created_at);return{id:p,body:it(d.description)??"",...m?{created:m}:{},...it(d.title)!==void 0?{title:it(d.title)}:{},...f?{author:f}:{},...g?{url:g}:{}}},c=`${r}/merge_requests`,u=d=>{if(!We(d))return;let p=Qs(d.iid);if(p===void 0)return;let f=it(d.target_branch),g=it(d.web_url);return{number:p,title:it(d.title)??"",body:it(d.description)??"",head:it(d.source_branch)??"",...f?{base:f}:{},...g?{url:g}:{}}},l={comments:async d=>(await n.list(`${c}/${d}/notes`,o,100)).filter(p=>!(We(p)&&p.system===!0)).map(i).filter(p=>p!==void 0),addComment:async(d,p)=>i(await n.call("POST",`${c}/${d}/notes`,{body:p})),editComment:async(d,p,f)=>i(await n.call("PUT",`${c}/${d}/notes/${p}`,{body:f})),openIssues:async d=>(await n.list(`${r}/issues?state=opened&search=${encodeURIComponent(d)}&in=description`,o,100)).map(a).filter(p=>p!==void 0),openIssue:async(d,p)=>a(await n.call("POST",`${r}/issues`,{title:d,description:p})),editIssue:async(d,p,f)=>a(await n.call("PUT",`${r}/issues/${d}`,{title:p,description:f})),async closeIssue(d,p){p&&await n.call("POST",`${r}/issues/${d}/notes`,{body:p}),await n.call("PUT",`${r}/issues/${d}`,{state_event:"close"})}};return{kind:"gitlab",host:t,...e.runUrl?{runUrl:e.runUrl}:{},principalOf:d=>G1("gitlab",t,d),...W1(l),async setStatus(d,p){try{await n.call("POST",`${r}/statuses/${d}`,{state:M1("gitlab",p.state),name:p.context,description:p.description.slice(0,Mne),...p.url?{target_url:p.url}:{},...p.pipelineId!==void 0?{pipeline_id:p.pipelineId}:{}})}catch(f){if(f instanceof Zr&&f.status===400&&/cannot transition status/i.test(f.message))return;throw f}},async approvers(d){let p=await n.call("GET",`${r}/merge_requests/${d}/approvals`),g=(We(p)&&Array.isArray(p.approved_by)?p.approved_by:[]).map(m=>We(m)&&We(m.user)&&typeof m.user.username=="string"?m.user.username:void 0).filter(m=>m!==void 0);return[...new Set(g)].sort()},async pullRequestFor(d){let p=await n.call("GET",`${r}/repository/commits/${d}/merge_requests`);if(!Array.isArray(p))return null;let f=p.find(g=>We(g)&&g.state==="merged"&&(g.merge_commit_sha===d||g.squash_commit_sha===d))??p.find(g=>We(g)&&g.state==="merged");return We(f)&&typeof f.iid=="number"?f.iid:null},async openPullRequest(d){let p=d.base??await q1(n,r,"default_branch"),f=d.draft?`Draft: ${d.title}`:d.title,g=await n.call("POST",c,{source_branch:d.head,target_branch:p,title:f,description:d.body});return u(g)??{number:0,title:f,body:d.body,head:d.head,base:p}},async findPullRequest(d){return(await n.list(`${c}?state=opened&source_branch=${encodeURIComponent(d)}`,o,100)).filter(f=>We(f)&&f.source_branch===d&&(f.source_project_id===void 0||f.source_project_id===f.project_id)).map(u).filter(f=>f!==void 0).sort((f,g)=>f.number-g.number)[0]},async editPullRequest(d,p){return u(await n.call("PUT",`${c}/${d}`,{...p.title!==void 0?{title:p.title}:{},...p.body!==void 0?{description:p.body}:{}}))},async addLabels(d,p){p.length!==0&&await n.call("PUT",`${c}/${d}`,{add_labels:p.join(",")})},async mergePullRequest(d,p){let f=p.method??"merge";if(f==="rebase")throw new Error("GitLab has no one-call rebase merge; use method merge or squash");let g=p.title!==void 0?p.message?`${p.title}
|
|
144
144
|
|
|
145
|
-
${p.message}`:p.title:p.message,m;try{m=await n.call("PUT",`${c}/${d}/merge`,{sha:p.sha,...f==="squash"?{squash:!0,...g!==void 0?{squash_commit_message:g}:{}}:{},...g!==void 0?{merge_commit_message:g}:{},...p.deleteBranch?{should_remove_source_branch:!0}:{}})}catch(y){return
|
|
146
|
-
`,{mode:384}),s}function Wne(e,t){let n=VL(e,t);if(ZL(n))for(let r of Une(n).filter(o=>o.endsWith(".tf"))){let o=/\bbackend\s+"([^"]+)"/.exec(YL(VL(n,r),"utf-8"));if(o)return o[1]}}function Hne(e,t){if(t==="gcp"){let n=e.gcp;return n?.roles?{cloud:t,key:"oidc.gcp",roles:n.roles,fallback:{name:"plan_service_account/apply_service_account",pair:{plan:n.plan_service_account,apply:n.apply_service_account}},scheme:"gs://",backend:"gcs"}:void 0}if(t==="azure"){let n=e.azure;return n?.roles?{cloud:t,key:"oidc.azure",roles:n.roles,fallback:{name:"plan_client_id/apply_client_id",pair:{plan:n.plan_client_id,apply:n.apply_client_id}},scheme:"",backend:"azurerm"}:void 0}return{key:"oidc",roles:e.roles??{},...e.plan_role&&e.apply_role?{fallback:{name:"plan_role/apply_role",pair:{plan:e.plan_role,apply:e.apply_role}}}:{},scheme:"s3://"}}function XL(e,t,n,r,o="aws"){let s=Hne(n,o);if(!s)return{roles:[],warnings:[]};let i=s.roles,a=Object.keys(i),c=s.fallback?.name,u=k=>k.bucket&&s.scheme?`${s.scheme}${k.bucket}/${k.key}`:k.key,l=new Map,d=[];for(let k of t){let $=a.find(_=>Pe(_,k))??c;l.set(k,$),$||d.push(`oidc: ${k} matches no oidc.roles glob, and oidc names no plan_role and apply_role, so it plans and applies with no AWS role`)}let p=k=>k===c?s.fallback.pair:i[k],f=k=>!s.backend||r!==void 0||Wne(e,k)===s.backend,g=new Map(t.map(k=>[k,r?{own:r.states.get(k)}:f(k)?Lo(e,k):{own:void 0}])),m=r?.reads??wn(e,t,{estates:!1}),h=r?.via??"terraform_remote_state",y=[...new Set([...l.values()].filter(k=>k!==void 0))];for(let k of a)y.includes(k)||y.push(k);let w=[];for(let k of y){let v=t.filter(E=>l.get(E)===k),$=v.map(E=>g.get(E)?.own).filter(E=>E!==void 0).map(u),_=[];for(let E of v)for(let R of m.get(E)??[]){let x=l.get(R);if(x===k||!f(R))continue;let A=g.get(R)?.own,O=A?u(A):R;_.includes(O)||_.push(O);let I=p(k);d.push(`${s.key}: ${E} (${k}) reads the state of ${R} (${x??"no role"}) through ${h}, so ${I.plan} and ${I.apply} reach ${O}, another environment's state`)}for(let E of["plan","apply"])w.push({role:p(k)[E],stage:E,environment:k,...s.cloud?{cloud:s.cloud}:{},roots:v,states:[...new Set($)].sort(),reads:_.sort()})}let b=new Map;for(let k of w)b.set(k.role,[...b.get(k.role)??[],k]);for(let[k,v]of b){let $=[...new Set(v.map(R=>R.environment))];if($.length<2)continue;let _=[...new Set(v.flatMap(R=>R.states))].sort(),E=s.cloud==="gcp"?"service account":s.cloud==="azure"?"client":"role";d.push(`${s.key}: ${k} is the ${E} of ${$.join(" and ")}, so it reaches the state of each${_.length?`: ${_.join(", ")}`:""}; give each environment ${E==="role"?"roles":`${E}s`} of its own`)}return{roles:w,warnings:d}}function QL(e){let t=[];for(let n of["plan","apply"]){let r=new Map;for(let[o,s]of Object.entries(e))r.set(s[n],[...r.get(s[n])??[],o]);for(let[o,s]of r)s.length>1&&t.push(`terragrunt.credentials: ${o} is the ${n} role of ${s.join(" and ")}, so it reaches the state of each; give each environment roles of its own`)}return t}import{spawnSync as nhe}from"node:child_process";import{spawn as Xge,spawnSync as TH}from"node:child_process";import{copyFileSync as _H,existsSync as YO,mkdirSync as _p,mkdtempSync as ih,readFileSync as Qge,rmSync as AH,writeFileSync as ml}from"node:fs";import{tmpdir as OH}from"node:os";import{dirname as SH,join as Fe}from"node:path";var gb=e=>e.replace(/\/+$/,"");function Id(e=process.env){if(e.GITLAB_CI==="true")return!e.CI_API_V4_URL||!e.CI_PROJECT_ID||!e.CI_PIPELINE_ID||!e.CI_JOB_ID?void 0:{forge:"gitlab",api:gb(e.CI_API_V4_URL),repo:e.CI_PROJECT_ID,run:e.CI_PIPELINE_ID,job:e.CI_JOB_ID,...e.CI_JOB_NAME?{jobName:e.CI_JOB_NAME}:{},...e.CI_JOB_URL?{url:e.CI_JOB_URL}:{}};if(e.GITHUB_ACTIONS!=="true"||!e.GITHUB_REPOSITORY||!e.GITHUB_RUN_ID)return;let t=e.FORGEJO_ACTIONS==="true"||e.GITEA_ACTIONS==="true",n=e.GITHUB_SERVER_URL?gb(e.GITHUB_SERVER_URL):void 0,r=n?`${n}/${e.GITHUB_REPOSITORY}/actions/runs/${e.GITHUB_RUN_ID}`:void 0;if(t){let s=qne(e),i=e.GITHUB_API_URL??(n?`${n}/api/v1`:void 0);return!i||!s||!e.GITHUB_REF_NAME?void 0:{forge:"forgejo",api:gb(i),repo:e.GITHUB_REPOSITORY,run:e.GITHUB_RUN_ID,workflow:s,ref:e.GITHUB_REF_NAME,...r?{url:r}:{}}}let o=Number(e.GITHUB_RUN_ATTEMPT);return{forge:"github",api:gb(e.GITHUB_API_URL??"https://api.github.com"),repo:e.GITHUB_REPOSITORY,run:e.GITHUB_RUN_ID,...Number.isInteger(o)&&o>0?{attempt:o}:{},...r?{url:r}:{}}}function qne(e){let t=e.GITHUB_WORKFLOW_REF?.split("@")[0]?.split("/").pop();if(t&&/\.ya?ml$/.test(t))return t;let n=e.GITHUB_WORKFLOW;return n&&/\.ya?ml$/.test(n)?n.split("/").pop():void 0}function Pg(e){if(!e||typeof e!="object")return!1;let t=e;return typeof t.api!="string"||typeof t.repo!="string"||typeof t.run!="string"?!1:t.forge==="github"?!0:t.forge==="gitlab"?typeof t.job=="string":t.forge==="forgejo"?typeof t.workflow=="string"&&typeof t.ref=="string":!1}var Jne=e=>e.replace(/\/+$/,""),ra=e=>typeof e=="object"&&e!==null&&!Array.isArray(e);function yE(e,t,n){return async(r,o,s)=>{let i=`${Jne(e)}${o}`,a=await n(i,{method:r,headers:{...t,...s!==void 0?{"Content-Type":"application/json"}:{}},...s!==void 0?{body:JSON.stringify(s)}:{}}),c=await a.text();if(!a.ok)throw new Zr(r,i,a.status,c);if(!c)return null;try{return JSON.parse(c)}catch{return c}}}async function e1(e,t){let n=t.fetch??globalThis.fetch,r=e.url?{url:e.url}:{},o=g=>({status:"skipped",forge:e.forge,reason:g,...r}),s=g=>({status:"would-resume",forge:e.forge,how:`would ${g}`,...r});if(e.forge==="github"){let g=yE(e.api,{Authorization:`Bearer ${t.token}`,Accept:"application/vnd.github+json"},n),m=`/repos/${e.repo}/actions/runs/${e.run}`,h=await g("GET",m);if(!ra(h))throw new Error(`GET ${m} returned no run`);if(h.status!=="completed")return o(`run ${e.run} is ${String(h.status)}, not finished`);let y=typeof h.run_attempt=="number"?h.run_attempt:void 0;return e.attempt!==void 0&&y!==void 0&&y>e.attempt?o(`run ${e.run} was already re-run (attempt ${y}; attempt ${e.attempt} waited)`):h.conclusion==="success"?o(`run ${e.run} succeeded`):t.dryRun?s(`re-run the failed jobs of run ${e.run}`):(await g("POST",`${m}/rerun-failed-jobs`,{}),{status:"resumed",forge:"github",how:`re-ran the failed jobs of run ${e.run}`,...r})}if(e.forge==="gitlab"){let g=yE(e.api,{"PRIVATE-TOKEN":t.token},n),m=`/projects/${encodeURIComponent(decodeURIComponent(e.repo))}`,h=await g("GET",`${m}/jobs/${e.job}`);if(!ra(h))throw new Error(`GET ${m}/jobs/${e.job} returned no job`);let y=String(h.status);if(y==="success")return o(`job ${e.job} succeeded`);if(!["failed","canceled","skipped"].includes(y))return o(`job ${e.job} is ${y}, not finished`);let w=e.jobName??(typeof h.name=="string"?h.name:void 0);if(w){let v=await g("GET",`${m}/pipelines/${e.run}/jobs?per_page=100`),$=Array.isArray(v)?v.find(_=>ra(_)&&_.name===w):void 0;if(ra($)&&String($.id)!==String(e.job))return o(`job ${e.job} (${w}) was already retried as job ${String($.id)}`)}if(t.dryRun)return s(`retry job ${e.job}`);let b=await g("POST",`${m}/jobs/${e.job}/retry`),k=ra(b)&&b.id!==void 0?` as job ${String(b.id)}`:"";return{status:"resumed",forge:"gitlab",how:`retried job ${e.job}${k}`,...r}}let i=yE(e.api,{Authorization:`token ${t.token}`,Accept:"application/json"},n),a=`/repos/${e.repo}`,c=await i("GET",`${a}/actions/runs/${e.run}`);if(!ra(c))throw new Error(`GET ${a}/actions/runs/${e.run} returned no run`);let u=String(c.status);if(u==="success")return o(`run ${e.run} succeeded`);if(["waiting","running","blocked","unknown"].includes(u))return o(`run ${e.run} is ${u}, not finished`);let l=await i("GET",`${a}/actions/runs?workflow_id=${encodeURIComponent(e.workflow)}&limit=50`),d=ra(l)&&Array.isArray(l.workflow_runs)?l.workflow_runs:Array.isArray(l)?l:[],p=t.since?Math.floor(new Date(t.since).getTime()/1e3)*1e3:void 0,f=d.find(g=>ra(g)&&Number(g.id)>Number(e.run)&&(g.prettyref===void 0||g.prettyref===e.ref)&&(p===void 0||typeof g.created=="string"&&new Date(g.created).getTime()>=p));return ra(f)?o(`run ${String(f.id)} of ${e.workflow} started after the approval`):t.dryRun?s(`dispatch ${e.workflow} on ${e.ref}`):(await i("POST",`${a}/actions/workflows/${encodeURIComponent(e.workflow)}/dispatches`,{ref:e.ref}),{status:"resumed",forge:"forgejo",how:`dispatched ${e.workflow} on ${e.ref}`,...r})}var vE="https://intentius.io/chant/schemas/workspace/plan-summary/v1/plan-summary.schema.json",$E=1,kE=65536,l1=1e6,wE="<estate>",t1="<address>",Kne="<key>",Vne="<known after apply>",Zne="tofu-estate",mb="tofu-address",Yne="choudoufu.intentius.io/tofu-address",n1=4;function d1(e,t,n){let r=[],o=new Set,s=(i,a)=>{for(let c of[i,i.replaceAll("-","_"),i.replaceAll("_","-")])c===""||o.has(c)||(o.add(c),r.push({text:c,placeholder:a}))};e&&s(e,wE);for(let i of n)s(i,wE);for(let i of t)s(i,Kne);return r.sort((i,a)=>a.text.length-i.text.length),{tokens:r,...e?{estate:e}:{}}}var r1=e=>e!==void 0&&/^[A-Za-z0-9]$/.test(e),o1=e=>e==="-"||e==="_";function Xne(e,t){if(!e.includes(t.text))return e;let n="",r=0;for(;r<e.length;){let o=e.indexOf(t.text,r);if(o<0){n+=e.slice(r);break}let s=o+t.text.length,i=!r1(e[o-1])&&!r1(e[s]);i&&t.text.length<3&&(i=o1(e[o-1])||o1(e[s])),i?(n+=e.slice(r,o)+t.placeholder,r=s):(n+=e.slice(r,o+1),r=o+1)}return n}function bE(e,t){for(let n of t.tokens)e=Xne(e,n);return e}function p1(e){let t="",n=[],r=0;for(;r<e.length;){if(e[r]!=="["){t+=e[r++];continue}let o=r+1,s=!1;for(;o<e.length;o++)if(s&&e[o]==="\\")o++;else if(e[o]==='"')s=!s;else if(!s&&e[o]==="]")break;let i=e.slice(r+1,Math.min(o,e.length)),a=i;if(i.startsWith('"'))try{a=JSON.parse(i)}catch{}n.push(a),r=o+1}return{bare:t,keys:n}}function f1(e,t){let n=p1(e).bare.split(".");for(let r=0;r<n.length;r++)n[r]==="module"&&r+1<n.length?(n[r+1]=bE(n[r+1],t),r++):r===n.length-1&&(n[r]=bE(n[r],t));return n.join(".")}var Qne="+-=_/@.:",s1=e=>/^[\p{L}\p{Nd} ]$/u.test(e)||Qne.includes(e);function ere(e){let t=[...e];return t.some(n=>n==="+"||!s1(n))?t.map(n=>n==="+"?"++":s1(n)?n:"+"+n.codePointAt(0).toString(16).toUpperCase().padStart(6,"0")).join(""):e}var tre=e=>ere(e).replaceAll("@","@@").replaceAll(".","@d").replaceAll(":","@c");function nre(e){if(!e.includes("["))return e;let t=[...e],n="";for(let r=0;r<t.length;){if(t[r]!=="["){t[r]!=="]"&&t[r]!=='"'&&(n+=t[r]),r++;continue}let o=r+1;for(;o<t.length&&t[o]!=="]";)o++;n+=":"+tre(t.slice(r+1,o).join("").replace(/^"+|"+$/g,"")),r=o<t.length?o+1:o}return n}function rre(e){let t=s=>s===1?mb:`${mb}-${s}`,n=s=>typeof e[s]=="string"?e[s]:void 0;if(n(mb)===void 0)return;let r="",o=1;for(;o<=n1&&n(t(o))!==void 0;o++)r+=n(t(o));for(let s=o+1;s<=n1;s++)if(n(t(s))!==void 0)return;return r}var ore=e=>e===mb||/^tofu-address-\d+$/.test(e);function hb(e,t,n){if(typeof e=="string")return bE(e,t);if(Array.isArray(e))return e.map(a=>hb(a,t,n));if(e===null||typeof e!="object")return e;let r=e,o=nre(n),s=rre(r)===o,i={};for(let[a,c]of Object.entries(r))typeof c=="string"&&a===Zne&&t.estate!==void 0&&c===t.estate?i[a]=wE:typeof c=="string"&&s&&ore(a)||typeof c=="string"&&a===Yne&&c===o?i[a]=t1:i[a]=hb(c,t,n);return i}var sre={create:"+",update:"~",replace:"-/+",delete:"-",read:"<=","no-op":"",forget:"forget"};function ire(e,t,n){return e.sensitive?{sensitive:!0,...e.forcesReplacement?{forcesReplacement:!0}:{}}:{...e.before!==void 0&&e.before!==null?{before:hb(e.before,t,n)}:{},...e.unknown?{after:Vne}:e.after!==void 0&&e.after!==null?{after:hb(e.after,t,n)}:{},...e.forcesReplacement?{forcesReplacement:!0}:{}}}function g1(e,t){if(e.action==="no-op"&&!e.importing)return;let n=f1(e.address,t)+(e.deposed!==void 0?" (deposed)":""),r=new Map,o={};if(e.action!=="delete"&&e.action!=="forget")for(let c of[...e.attributes].sort((u,l)=>u.path<l.path?-1:u.path>l.path?1:0))o[c.path]=ire(c,t,e.address),r.set(c.path,JSON.stringify(o[c.path]));let s=yb({type:e.type,action:e.action,address:n,attrs:o,...e.importing?{importing:!0}:{}}),i=[...r.keys()],a=`${sre[e.action]||"import"} ${n}`;return e.importing&&e.action!=="no-op"&&(a+=" (import)"),i.length>0&&e.action!=="create"&&e.action!=="read"&&(a+=`: ${i.join(", ")}`),{key:s,line:a,action:e.action,address:n,attrs:r}}function yb(e){if(Array.isArray(e))return`[${e.map(yb).join(",")}]`;if(e!==null&&typeof e=="object"){let t=e;return`{${Object.keys(t).sort().map(n=>`${JSON.stringify(n)}:${yb(t[n])}`).join(",")}}`}return JSON.stringify(e)??"null"}var ur=(e,t)=>e<t?-1:e>t?1:0;function _E(e){if(!(e.action!=="delete"&&e.action!=="replace"))return{member:e.member,address:e.address,type:e.type,action:e.action,...e.deposed!==void 0?{deposed:e.deposed}:{}}}function m1(e,t){return yb({type:e.type,trigger:e.trigger===void 0?null:f1(e.trigger,t),event:e.event??null})}function are(e){let t=e.replaceAll("\\","/").split("/").filter(n=>n!==""&&n!==".");return t[t.length-1]??""}function cre(e,t){let n=new Map;for(let o of e.entries){let s=n.get(o.member);s?s.push(o):n.set(o.member,[o])}let r=[];for(let o of e.members){if(t.has(o.member))continue;let s=are(o.member),i=d1(o.scope,[],s&&s!==o.scope?[s]:[]),a={name:o.member,family:"",provisional:o.provisional===!0,changes:[],destroys:[],effects:[],sideEffects:[]};for(let c of e.sideEffects??[])c.member===o.member&&(a.effects.push(m1(c,i)),a.sideEffects.push(c));for(let c of n.get(o.member)??[]){let u=g1(c,i);if(!u)continue;a.changes.push(u);let l=_E(c);l&&a.destroys.push(l)}r.push(a)}return r}function ure(e,t){let n=new Set(e.members.filter(o=>o.provisional===!0).map(o=>o.member)),r=[];for(let o of e.entries){if(t.has(o.member))continue;let{bare:s,keys:i}=p1(o.address),a=d1(void 0,i,[]),c=g1(o,a);if(!c)continue;let u=_E(o),l=o.deposed!==void 0?`${o.address} (deposed ${o.deposed})`:o.address,d=(e.sideEffects??[]).filter(p=>p.member===o.member&&p.trigger===o.address);r.push({name:l,family:s,provisional:n.has(o.member),changes:[c],destroys:u?[u]:[],effects:d.map(p=>m1(p,a)),sideEffects:d})}return r}function lre(e){let t=new Map;for(let n of e){let r=t.get(n.key);r?r.count++:t.set(n.key,{line:n.line,action:n.action,count:1,key:n.key,norm:n})}return[...t.values()].sort((n,r)=>ur(n.line,r.line)||ur(n.key,r.key))}function dre(e,t){let n=new Map(e.map(o=>[o.key,o.count])),r=[];for(let o of t){let s=n.get(o.key)??0;s===0?r.push(o):o.count>s&&r.push({...o,count:o.count-s}),n.delete(o.key)}return[...n.values()].some(o=>o>0)?void 0:r}function i1(e,t){if(t.lines.some(o=>o.key===e.key))return;let n=t.lines.find(o=>o.norm.address===e.norm.address&&o.norm.action===e.norm.action);if(!n)return;let r=new Set;for(let[o,s]of e.norm.attrs)n.norm.attrs.get(o)!==s&&r.add(o);for(let o of n.norm.attrs.keys())e.norm.attrs.has(o)||r.add(o);e.differsFrom=t.id,e.differsIn=[...r].sort()}var a1=e=>({line:e.line,action:e.action,count:e.count,...e.differsFrom!==void 0?{differsFrom:e.differsFrom,differsIn:e.differsIn??[]}:{}});function pre(e,t){let n=new Map;for(let a of e){let c=a.changes.map(p=>p.key).sort(ur),u=[...a.effects].sort(ur),l=Rn("plan-summary-group",{resource:a.family,changes:c,...u.length>0?{effects:u}:{},...a.provisional?{provisional:!0}:{}}).slice(Ty.length,Ty.length+12),d=n.get(l);d||(d={id:l,family:a.family,provisional:a.provisional,units:[],destroys:[],sideEffects:[],lines:lre(a.changes),noChanges:a.changes.length===0&&a.effects.length===0},n.set(l,d)),d.units.push(a.name),d.destroys.push(...a.destroys),d.sideEffects.push(...a.sideEffects)}let r=[...n.values()];for(let a of r)a.units.sort(ur);r.sort((a,c)=>Number(a.provisional)-Number(c.provisional)||(t&&a.family!==c.family?ur(a.family,c.family):0)||c.units.length-a.units.length||ur(a.units[0],c.units[0]));let o=a=>a.provisional?`${a.family}\0provisional`:a.family,s=new Map,i=new Map;for(let a of r)i.set(o(a),(i.get(o(a))??0)+1),!s.has(o(a))&&!a.noChanges&&s.set(o(a),a);return r.map(a=>{let c=s.get(o(a)),u;if(c&&c!==a&&!a.noChanges){let l=dre(c.lines,a.lines);l&&l.length>0&&(u=l);for(let d of a.lines)i1(d,c);for(let d of u??[])i1(d,c)}return{id:a.id,...a.family?{resource:a.family}:{},units:a.units,outlier:a.units.length===1&&(i.get(o(a))??0)>1,noChanges:a.noChanges,changes:a.lines.map(a1),...u&&c?{extends:c.id,plus:u.map(a1)}:{},destroys:a.destroys.sort(h1),sideEffects:a.sideEffects.sort(y1),...a.provisional?{provisional:!0}:{}}})}var h1=(e,t)=>ur(e.member,t.member)||ur(e.address,t.address)||ur(e.deposed??"",t.deposed??""),y1=(e,t)=>ur(e.member,t.member)||ur(e.address,t.address)||ur(e.trigger??"",t.trigger??""),c1=(e,t)=>ur(e.member,t.member)||ur(e.address,t.address)||ur(e.deposed??"",t.deposed??"");function fre(e){return e.error??"the member failed to plan"}function w1(e,t={}){let n=t.unit??(e.members.length===1?"instance":"member"),r=e.members.filter(c=>c.status==="failed"),o=new Set(r.map(c=>c.member)),s=n==="member"?cre(e,o):ure(e,o),i=e.entries.map(_E).filter(c=>c!==void 0).sort(h1),a=e.members.flatMap(c=>c.holes.map(u=>({member:c.member,address:u.address,...u.type?{type:u.type}:{},reason:u.reason})));return{$schema:vE,contract:$E,changeSet:e.digest,unit:n,units:n==="member"?e.members.length:s.length,groups:pre(s,n==="instance"),failed:r.map(c=>({member:c.member,...c.scope?{scope:c.scope}:{},reason:fre(c)})),destroys:i,imports:e.entries.filter(c=>c.importing).map(c=>({member:c.member,address:c.address,type:c.type,action:c.action})).sort(c1),forgets:e.entries.filter(c=>c.action==="forget").map(c=>({member:c.member,address:c.address,type:c.type,...c.deposed!==void 0?{deposed:c.deposed}:{}})).sort(c1),sideEffects:[...e.sideEffects??[]].sort(y1),holes:a}}var pc=(e,t,n)=>`${e} ${e===1?t:n}`;function b1(e,t){return e.unit==="instance"?pc(t,"instance","instances"):pc(t,"member","members")}function gre(e){let t=[pc(e.groups.length,"group","groups")],n=e.groups.filter(r=>r.provisional).length;return n>0&&t.push(`${n} provisional`),e.failed.length>0&&t.push(`${e.failed.length} failed`),t.push(pc(e.destroys.length,"destroy or replacement","destroys or replacements")),e.forgets.length>0&&t.push(pc(e.forgets.length,"forget","forgets")),e.imports.length>0&&t.push(pc(e.imports.length,"import","imports")),e.sideEffects.length>0&&t.push(pc(e.sideEffects.length,"triggered action","triggered actions")),e.holes.length>0&&t.push(pc(e.holes.length,"hole","holes")),`${b1(e,e.units)}: ${t.join(", ")}.`}function mre(e,t){let n=`Group ${t.id}: ${b1(e,t.units.length)}`;return t.resource&&(n+=` of ${t.resource}`),t.outlier&&(n+=" (outlier)"),t.provisional&&(n+=" (provisional)"),t.noChanges?n+=", no changes":t.extends?n+=`, group ${t.extends}'s change plus`:t.units.length>1?n+=", identical change":n+=", change",n}function hre(e){let t=e.count>1?`${e.line} (x${e.count})`:e.line;return e.differsFrom&&(t+=e.differsIn&&e.differsIn.length>0?` [differs from group ${e.differsFrom} in: ${e.differsIn.join(", ")}]`:` [differs from group ${e.differsFrom}]`),t}var yre=e=>e.extends?e.plus??[]:e.changes;function wre(e){return`${e.member}: ${e.address}${e.deposed!==void 0?` (deposed ${e.deposed})`:""} (${e.action})`}var bre=e=>`${e.member}: ${e.address}${e.deposed!==void 0?` (deposed ${e.deposed})`:""} (forget, the object keeps running)`,vre=e=>`${e.member}: ${e.address} (${e.action==="no-op"?"import, no other change":`import, then ${e.action}`})`,$re=e=>`${e.member}: ${e.address}${e.trigger!==void 0?`, triggered by ${e.trigger}${e.event!==void 0?` ${e.event}`:""}`:""}`,kre="Provisional: planned before what it reads applied, so it may show stand-in values. No approval covers it; it is planned again in its own wave.",u1=e=>e.split(/\s+/).filter(Boolean).join(" "),_re=e=>e.scope&&e.scope!==e.member?`${e.member} (${e.scope})`:e.member;function v1(e){let t=[gre(e)];if(e.destroys.length>0){t.push("",`Destroys and replacements (${e.destroys.length}):`);for(let n of e.destroys)t.push(` ${wre(n)}`)}if(e.forgets.length>0){t.push("",`Forgets (${e.forgets.length}), no longer tracked, not destroyed:`);for(let n of e.forgets)t.push(` ${bre(n)}`)}if(e.imports.length>0){t.push("",`Imports (${e.imports.length}):`);for(let n of e.imports)t.push(` ${vre(n)}`)}if(e.sideEffects.length>0){t.push("",`Triggered actions (${e.sideEffects.length}), side effects of apply:`);for(let n of e.sideEffects)t.push(` ${$re(n)}`)}if(e.failed.length>0){t.push("",`Failed (${e.failed.length}), not grouped:`);for(let n of e.failed)t.push(` ${_re(n)}: ${u1(n.reason)}`)}if(e.holes.length>0){t.push("",`Holes (${e.holes.length}), addresses the plan says nothing about:`);for(let n of e.holes)t.push(` ${n.member}: ${n.address}: ${u1(n.reason)}`)}for(let n of e.groups){t.push("",`${mre(e,n)}:`);for(let r of yre(n))t.push(` ${hre(r)}`);t.push(` ${e.unit==="instance"?"instances":"members"}: ${n.units.join(", ")}`),n.provisional&&t.push(` ${kre}`),n.destroys.length>0&&t.push(` destroys and replacements: ${n.destroys.length}, listed above`),n.sideEffects.length>0&&t.push(` triggered actions: ${n.sideEffects.length}, listed above`)}return t.join(`
|
|
145
|
+
${p.message}`:p.title:p.message,m;try{m=await n.call("PUT",`${c}/${d}/merge`,{sha:p.sha,...f==="squash"?{squash:!0,...g!==void 0?{squash_commit_message:g}:{}}:{},...g!==void 0?{merge_commit_message:g}:{},...p.deleteBranch?{should_remove_source_branch:!0}:{}})}catch(y){return L1(y)}let h=We(m)?it(m.merge_commit_sha)??it(m.squash_commit_sha):void 0;return We(m)&&m.state!==void 0&&m.state!=="merged"?{merged:!1,reason:`the merge request is ${String(m.state)}`}:{merged:!0,...h?{sha:h}:{}}}}}var Uu=class extends Error{constructor(n,r){super(n?`--forge ${n} needs ${r.join(", ")} in the environment`:"the forge is not known: this is not a GitHub Actions, GitLab CI or Forgejo Actions job, so name the forge");this.kind=n;this.missing=r;this.name="ForgeEnvironmentError"}kind;missing},lb=e=>{if(e)try{return new URL(e).host.toLowerCase()}catch{return}};var db=e=>e==="true"||e==="1";function Od(e=process.env){if(db(e.GITLAB_CI))return"gitlab";if(db(e.FORGEJO_ACTIONS)||db(e.GITEA_ACTIONS))return"forgejo";if(db(e.GITHUB_ACTIONS))return/\/api\/v1\/?$/.test(e.GITHUB_API_URL??"")?"forgejo":"github"}var Lne={github:["CHANT_GITHUB_TOKEN","GH_TOKEN","GITHUB_TOKEN"],forgejo:["CHANT_FORGEJO_TOKEN","FORGEJO_TOKEN","GH_TOKEN","GITHUB_TOKEN"],gitlab:["CHANT_GITLAB_TOKEN","GITLAB_TOKEN","CI_JOB_TOKEN"]};function Gne(e,t){let n={};if(We(e)){let r=We(e.pull_request)?e.pull_request:void 0,o=We(e.issue)?e.issue:void 0,s=Qs(r?.number)??(o&&We(o.pull_request)?Qs(o.number):void 0)??(r?Qs(e.number):void 0);s!==void 0&&s>0&&(n.pr=s);let i=r&&We(r.head)?it(r.head.sha):void 0;i&&(n.sha=i)}if(n.pr===void 0){let r=/^refs\/pull\/(\d+)\/(?:merge|head)$/.exec(t??"");r&&(n.pr=Number(r[1]))}return n}function pb(e=process.env,t={}){let n=t.kind??Od(e);if(!n)throw new Uu(void 0,[]);let r=(...v)=>v.map($=>e[$]?.trim()).find($=>$!==void 0&&$!==""),o=t.tokenEnv?[t.tokenEnv]:Lne[n],s=o.find(v=>e[v]?.trim()),i=s?e[s].trim():void 0,a=[],c=`a token (${o.join(", ")})`;if(n==="gitlab"){let v=r("CI_SERVER_URL")?.replace(/\/+$/,""),$=r("CI_API_V4_URL")?.replace(/\/+$/,"")??(v?`${v}/api/v4`:void 0),_=r("CI_MERGE_REQUEST_PROJECT_ID","CI_PROJECT_ID","CI_MERGE_REQUEST_PROJECT_PATH","CI_PROJECT_PATH"),E=Qs(r("CI_MERGE_REQUEST_IID"));if($||a.push("CI_API_V4_URL"),_||a.push("CI_PROJECT_ID"),t.requirePr&&!E&&a.push("CI_MERGE_REQUEST_IID (a merge request pipeline)"),i||a.push(c),a.length>0)throw new Uu(n,a);let R=r("CI_MERGE_REQUEST_SOURCE_BRANCH_SHA","CI_COMMIT_SHA"),x=r("CI_PIPELINE_URL");return{kind:n,apiBase:$,repo:_,host:r("CI_SERVER_HOST")?.toLowerCase()??lb(v)??lb($)??"gitlab.com",token:i,tokenSource:s,tokenHeader:s==="CI_JOB_TOKEN"?"JOB-TOKEN":"PRIVATE-TOKEN",...E?{pr:E}:{},...R?{sha:R}:{},...x?{runUrl:x}:{}}}let u=n==="forgejo",l=v=>u?r(`FORGEJO_${v}`,`GITHUB_${v}`):r(`GITHUB_${v}`),d=l("REPOSITORY"),p=l("SERVER_URL")?.replace(/\/+$/,""),f=l("API_URL")?.replace(/\/+$/,"")??(u?p?`${p}/api/v1`:void 0:"https://api.github.com"),g,m=l("EVENT_PATH");if(m)try{g=JSON.parse((t.readFile??(v=>Ane(v,"utf8")))(m))}catch{g=void 0}let{pr:h,sha:y}=Gne(g,l("REF"));if(d||a.push(u?"FORGEJO_REPOSITORY or GITHUB_REPOSITORY":"GITHUB_REPOSITORY"),f||a.push("GITHUB_SERVER_URL or GITHUB_API_URL"),t.requirePr&&!h&&a.push("a pull request (a pull_request event, or refs/pull/<n>/ in GITHUB_REF)"),i||a.push(c),a.length>0)throw new Uu(n,a);let w=y??l("SHA"),b=l("RUN_ID"),k=p&&b?`${p}/${d}/actions/runs/${b}`:void 0;return{kind:n,apiBase:f,repo:d,host:lb(p)??(u?lb(f):"github.com"),token:i,tokenSource:s,...h?{pr:h}:{},...w?{sha:w}:{},...k?{runUrl:k}:{}}}function J1(e,t){let n=t?{fetch:t}:{},r=e.runUrl?{runUrl:e.runUrl}:{};if(e.kind==="gitlab")return pE({apiBase:e.apiBase,project:e.repo,token:e.token,host:e.host,...e.tokenHeader?{tokenHeader:e.tokenHeader}:{},...r,...n});let o={apiBase:e.apiBase,repo:e.repo,token:e.token,host:e.host,...r,...n};return e.kind==="forgejo"?Ag(o):dE(o)}var ei=class extends Error{constructor(n,r){super(n);this.status=r}status};function K1(e){return e.api?e.api.replace(/\/+$/,""):e.forge==="github"?/^https?:\/\/github\.com$/i.test(e.origin)?"https://api.github.com":`${e.origin}/api/v3`:e.forge==="gitlab"?`${e.origin}/api/v4`:`${e.origin}/api/v1`}function Fne(e){let t={"content-type":"application/json",accept:"application/json"};return e.token&&(e.forge==="github"?t.authorization=`Bearer ${e.token}`:e.forge==="gitlab"?t["private-token"]=e.token:t.authorization=`token ${e.token}`),t}async function kt(e,t,n,r,o){let s=await e(`${K1(t)}${r}`,{method:n,headers:Fne(t),body:o===void 0?void 0:JSON.stringify(o)});if(!s.ok)throw new ei(`${n} ${r} on ${t.origin} answered ${s.status}: ${(await s.text()).slice(0,300)}`,s.status);return s.json()}async function lc(e,t){return t.forge==="gitlab"?(await kt(e,t,"GET",`/projects/${encodeURIComponent(t.path)}`)).default_branch??"main":(await kt(e,t,"GET",`/repos/${t.path}`)).default_branch??"main"}async function dc(e,t,n){let r=Pd(t,e),o=await r.findPullRequest(n.head);if(o)return{url:o.url??"",number:o.number,existing:!0};let s=await r.openPullRequest(n);return{url:s.url??"",number:s.number,existing:!1}}function jt(e,t,n,r="TG_TOKEN"){try{return J1(pb(t,{kind:e,tokenEnv:r}),n)}catch(o){throw o instanceof Uu?new S(o.message):o}}function Pd(e,t){let n=new URL(e.origin).host.toLowerCase(),r={apiBase:K1(e),...e.token?{token:e.token}:{},host:n,...t?{fetch:t}:{}};return e.forge==="gitlab"?pE({...r,project:e.path}):e.forge==="forgejo"?Ag({...r,repo:e.path}):dE({...r,repo:e.path})}var Gn={github:"GITHUB_TOKEN",gitlab:"GITLAB_TOKEN",forgejo:"FORGEJO_TOKEN"};import{createHash as zne}from"node:crypto";import{existsSync as Z1,readdirSync as Une,readFileSync as Y1,writeFileSync as Bne}from"node:fs";import{join as V1}from"node:path";var Og="TERRAGUCCI_ROOT_ROLES",na="TERRAGUCCI_ROOT_GCP_SERVICE_ACCOUNTS",mE="TERRAGUCCI_ROOT_AZURE_CLIENTS";function fb(e,t){return Object.entries(e).map(([n,r])=>[n,r[t]])}function jd(e,t=Og){let n=e[t];if(!n)return[];try{let r=JSON.parse(n);if(Array.isArray(r)&&r.every(o=>Array.isArray(o)&&o.length===2&&typeof o[0]=="string"&&typeof o[1]=="string"))return r}catch{}throw new Error(`${t} is not a list of [root glob, ${t===Og?"role":"identity"}] pairs`)}function fE(e,t){return t.find(([n])=>Pe(n,e))?.[1]}function dn(e,t){let n=e,r=Li(e),o=fE(t,jd(e));if(o){let{AWS_ACCESS_KEY_ID:a,AWS_SECRET_ACCESS_KEY:c,AWS_SESSION_TOKEN:u,...l}=n;if(r){let d=Ru(r,o);n={...l,AWS_ACCESS_KEY_ID:d.accessKeyId,AWS_SECRET_ACCESS_KEY:d.secretAccessKey,...d.sessionToken?{AWS_SESSION_TOKEN:d.sessionToken}:{}}}else n={...l,AWS_ROLE_ARN:o}}let s=fE(t,jd(e,na));if(s&&r){let a=r.gcp[s];if(!a)throw new Error(`the re-plan traded no token for ${s}`);let{GOOGLE_APPLICATION_CREDENTIALS:c,...u}=n;n={...u,GOOGLE_OAUTH_ACCESS_TOKEN:a.token}}else s&&(n={...n,GOOGLE_APPLICATION_CREDENTIALS:hE(e,s)});let i=fE(t,jd(e,mE));return i&&(n={...n,ARM_CLIENT_ID:i,...e.AZURE_CLIENT_ID?{AZURE_CLIENT_ID:i}:{}}),n}var gE=/\/serviceAccounts\/([^/:]+):generateAccessToken$/;function hE(e,t){let n=e.GOOGLE_APPLICATION_CREDENTIALS;if(!n)throw new Error(`${na} gives this root ${t}, and GOOGLE_APPLICATION_CREDENTIALS is not set: the job needs oidc.gcp`);let r;try{r=JSON.parse(Y1(n,"utf-8"))}catch(i){throw new Error(`${na} gives this root ${t}, and ${n} cannot be read: ${i.message}`)}let o=r.service_account_impersonation_url;if(r.type!=="external_account"||typeof o!="string"||!gE.test(o))throw new Error(`${na} gives this root ${t}, and ${n} is not an external_account file that impersonates a service account, as oidc.gcp writes`);if(decodeURIComponent(gE.exec(o)[1])===t)return n;let s=`${n}.${zne("sha256").update(t).digest("hex").slice(0,12)}.json`;return Z1(s)||Bne(s,JSON.stringify({...r,service_account_impersonation_url:o.replace(gE,`/serviceAccounts/${t}:generateAccessToken`)})+`
|
|
146
|
+
`,{mode:384}),s}function Wne(e,t){let n=V1(e,t);if(Z1(n))for(let r of Une(n).filter(o=>o.endsWith(".tf"))){let o=/\bbackend\s+"([^"]+)"/.exec(Y1(V1(n,r),"utf-8"));if(o)return o[1]}}function Hne(e,t){if(t==="gcp"){let n=e.gcp;return n?.roles?{cloud:t,key:"oidc.gcp",roles:n.roles,fallback:{name:"plan_service_account/apply_service_account",pair:{plan:n.plan_service_account,apply:n.apply_service_account}},scheme:"gs://",backend:"gcs"}:void 0}if(t==="azure"){let n=e.azure;return n?.roles?{cloud:t,key:"oidc.azure",roles:n.roles,fallback:{name:"plan_client_id/apply_client_id",pair:{plan:n.plan_client_id,apply:n.apply_client_id}},scheme:"",backend:"azurerm"}:void 0}return{key:"oidc",roles:e.roles??{},...e.plan_role&&e.apply_role?{fallback:{name:"plan_role/apply_role",pair:{plan:e.plan_role,apply:e.apply_role}}}:{},scheme:"s3://"}}function X1(e,t,n,r,o="aws"){let s=Hne(n,o);if(!s)return{roles:[],warnings:[]};let i=s.roles,a=Object.keys(i),c=s.fallback?.name,u=k=>k.bucket&&s.scheme?`${s.scheme}${k.bucket}/${k.key}`:k.key,l=new Map,d=[];for(let k of t){let $=a.find(_=>Pe(_,k))??c;l.set(k,$),$||d.push(`oidc: ${k} matches no oidc.roles glob, and oidc names no plan_role and apply_role, so it plans and applies with no AWS role`)}let p=k=>k===c?s.fallback.pair:i[k],f=k=>!s.backend||r!==void 0||Wne(e,k)===s.backend,g=new Map(t.map(k=>[k,r?{own:r.states.get(k)}:f(k)?Lo(e,k):{own:void 0}])),m=r?.reads??wn(e,t,{estates:!1}),h=r?.via??"terraform_remote_state",y=[...new Set([...l.values()].filter(k=>k!==void 0))];for(let k of a)y.includes(k)||y.push(k);let w=[];for(let k of y){let v=t.filter(E=>l.get(E)===k),$=v.map(E=>g.get(E)?.own).filter(E=>E!==void 0).map(u),_=[];for(let E of v)for(let R of m.get(E)??[]){let x=l.get(R);if(x===k||!f(R))continue;let A=g.get(R)?.own,O=A?u(A):R;_.includes(O)||_.push(O);let I=p(k);d.push(`${s.key}: ${E} (${k}) reads the state of ${R} (${x??"no role"}) through ${h}, so ${I.plan} and ${I.apply} reach ${O}, another environment's state`)}for(let E of["plan","apply"])w.push({role:p(k)[E],stage:E,environment:k,...s.cloud?{cloud:s.cloud}:{},roots:v,states:[...new Set($)].sort(),reads:_.sort()})}let b=new Map;for(let k of w)b.set(k.role,[...b.get(k.role)??[],k]);for(let[k,v]of b){let $=[...new Set(v.map(R=>R.environment))];if($.length<2)continue;let _=[...new Set(v.flatMap(R=>R.states))].sort(),E=s.cloud==="gcp"?"service account":s.cloud==="azure"?"client":"role";d.push(`${s.key}: ${k} is the ${E} of ${$.join(" and ")}, so it reaches the state of each${_.length?`: ${_.join(", ")}`:""}; give each environment ${E==="role"?"roles":`${E}s`} of its own`)}return{roles:w,warnings:d}}function Q1(e){let t=[];for(let n of["plan","apply"]){let r=new Map;for(let[o,s]of Object.entries(e))r.set(s[n],[...r.get(s[n])??[],o]);for(let[o,s]of r)s.length>1&&t.push(`terragrunt.credentials: ${o} is the ${n} role of ${s.join(" and ")}, so it reaches the state of each; give each environment roles of its own`)}return t}import{spawnSync as nhe}from"node:child_process";import{spawn as Xge,spawnSync as TH}from"node:child_process";import{copyFileSync as _H,existsSync as YO,mkdirSync as _p,mkdtempSync as ih,readFileSync as Qge,rmSync as AH,writeFileSync as ml}from"node:fs";import{tmpdir as OH}from"node:os";import{dirname as SH,join as Fe}from"node:path";var gb=e=>e.replace(/\/+$/,"");function Id(e=process.env){if(e.GITLAB_CI==="true")return!e.CI_API_V4_URL||!e.CI_PROJECT_ID||!e.CI_PIPELINE_ID||!e.CI_JOB_ID?void 0:{forge:"gitlab",api:gb(e.CI_API_V4_URL),repo:e.CI_PROJECT_ID,run:e.CI_PIPELINE_ID,job:e.CI_JOB_ID,...e.CI_JOB_NAME?{jobName:e.CI_JOB_NAME}:{},...e.CI_JOB_URL?{url:e.CI_JOB_URL}:{}};if(e.GITHUB_ACTIONS!=="true"||!e.GITHUB_REPOSITORY||!e.GITHUB_RUN_ID)return;let t=e.FORGEJO_ACTIONS==="true"||e.GITEA_ACTIONS==="true",n=e.GITHUB_SERVER_URL?gb(e.GITHUB_SERVER_URL):void 0,r=n?`${n}/${e.GITHUB_REPOSITORY}/actions/runs/${e.GITHUB_RUN_ID}`:void 0;if(t){let s=qne(e),i=e.GITHUB_API_URL??(n?`${n}/api/v1`:void 0);return!i||!s||!e.GITHUB_REF_NAME?void 0:{forge:"forgejo",api:gb(i),repo:e.GITHUB_REPOSITORY,run:e.GITHUB_RUN_ID,workflow:s,ref:e.GITHUB_REF_NAME,...r?{url:r}:{}}}let o=Number(e.GITHUB_RUN_ATTEMPT);return{forge:"github",api:gb(e.GITHUB_API_URL??"https://api.github.com"),repo:e.GITHUB_REPOSITORY,run:e.GITHUB_RUN_ID,...Number.isInteger(o)&&o>0?{attempt:o}:{},...r?{url:r}:{}}}function qne(e){let t=e.GITHUB_WORKFLOW_REF?.split("@")[0]?.split("/").pop();if(t&&/\.ya?ml$/.test(t))return t;let n=e.GITHUB_WORKFLOW;return n&&/\.ya?ml$/.test(n)?n.split("/").pop():void 0}function Pg(e){if(!e||typeof e!="object")return!1;let t=e;return typeof t.api!="string"||typeof t.repo!="string"||typeof t.run!="string"?!1:t.forge==="github"?!0:t.forge==="gitlab"?typeof t.job=="string":t.forge==="forgejo"?typeof t.workflow=="string"&&typeof t.ref=="string":!1}var Jne=e=>e.replace(/\/+$/,""),ra=e=>typeof e=="object"&&e!==null&&!Array.isArray(e);function yE(e,t,n){return async(r,o,s)=>{let i=`${Jne(e)}${o}`,a=await n(i,{method:r,headers:{...t,...s!==void 0?{"Content-Type":"application/json"}:{}},...s!==void 0?{body:JSON.stringify(s)}:{}}),c=await a.text();if(!a.ok)throw new Zr(r,i,a.status,c);if(!c)return null;try{return JSON.parse(c)}catch{return c}}}async function eL(e,t){let n=t.fetch??globalThis.fetch,r=e.url?{url:e.url}:{},o=g=>({status:"skipped",forge:e.forge,reason:g,...r}),s=g=>({status:"would-resume",forge:e.forge,how:`would ${g}`,...r});if(e.forge==="github"){let g=yE(e.api,{Authorization:`Bearer ${t.token}`,Accept:"application/vnd.github+json"},n),m=`/repos/${e.repo}/actions/runs/${e.run}`,h=await g("GET",m);if(!ra(h))throw new Error(`GET ${m} returned no run`);if(h.status!=="completed")return o(`run ${e.run} is ${String(h.status)}, not finished`);let y=typeof h.run_attempt=="number"?h.run_attempt:void 0;return e.attempt!==void 0&&y!==void 0&&y>e.attempt?o(`run ${e.run} was already re-run (attempt ${y}; attempt ${e.attempt} waited)`):h.conclusion==="success"?o(`run ${e.run} succeeded`):t.dryRun?s(`re-run the failed jobs of run ${e.run}`):(await g("POST",`${m}/rerun-failed-jobs`,{}),{status:"resumed",forge:"github",how:`re-ran the failed jobs of run ${e.run}`,...r})}if(e.forge==="gitlab"){let g=yE(e.api,{"PRIVATE-TOKEN":t.token},n),m=`/projects/${encodeURIComponent(decodeURIComponent(e.repo))}`,h=await g("GET",`${m}/jobs/${e.job}`);if(!ra(h))throw new Error(`GET ${m}/jobs/${e.job} returned no job`);let y=String(h.status);if(y==="success")return o(`job ${e.job} succeeded`);if(!["failed","canceled","skipped"].includes(y))return o(`job ${e.job} is ${y}, not finished`);let w=e.jobName??(typeof h.name=="string"?h.name:void 0);if(w){let v=await g("GET",`${m}/pipelines/${e.run}/jobs?per_page=100`),$=Array.isArray(v)?v.find(_=>ra(_)&&_.name===w):void 0;if(ra($)&&String($.id)!==String(e.job))return o(`job ${e.job} (${w}) was already retried as job ${String($.id)}`)}if(t.dryRun)return s(`retry job ${e.job}`);let b=await g("POST",`${m}/jobs/${e.job}/retry`),k=ra(b)&&b.id!==void 0?` as job ${String(b.id)}`:"";return{status:"resumed",forge:"gitlab",how:`retried job ${e.job}${k}`,...r}}let i=yE(e.api,{Authorization:`token ${t.token}`,Accept:"application/json"},n),a=`/repos/${e.repo}`,c=await i("GET",`${a}/actions/runs/${e.run}`);if(!ra(c))throw new Error(`GET ${a}/actions/runs/${e.run} returned no run`);let u=String(c.status);if(u==="success")return o(`run ${e.run} succeeded`);if(["waiting","running","blocked","unknown"].includes(u))return o(`run ${e.run} is ${u}, not finished`);let l=await i("GET",`${a}/actions/runs?workflow_id=${encodeURIComponent(e.workflow)}&limit=50`),d=ra(l)&&Array.isArray(l.workflow_runs)?l.workflow_runs:Array.isArray(l)?l:[],p=t.since?Math.floor(new Date(t.since).getTime()/1e3)*1e3:void 0,f=d.find(g=>ra(g)&&Number(g.id)>Number(e.run)&&(g.prettyref===void 0||g.prettyref===e.ref)&&(p===void 0||typeof g.created=="string"&&new Date(g.created).getTime()>=p));return ra(f)?o(`run ${String(f.id)} of ${e.workflow} started after the approval`):t.dryRun?s(`dispatch ${e.workflow} on ${e.ref}`):(await i("POST",`${a}/actions/workflows/${encodeURIComponent(e.workflow)}/dispatches`,{ref:e.ref}),{status:"resumed",forge:"forgejo",how:`dispatched ${e.workflow} on ${e.ref}`,...r})}var vE="https://intentius.io/chant/schemas/workspace/plan-summary/v1/plan-summary.schema.json",$E=1,kE=65536,lL=1e6,wE="<estate>",tL="<address>",Kne="<key>",Vne="<known after apply>",Zne="tofu-estate",mb="tofu-address",Yne="choudoufu.intentius.io/tofu-address",nL=4;function dL(e,t,n){let r=[],o=new Set,s=(i,a)=>{for(let c of[i,i.replaceAll("-","_"),i.replaceAll("_","-")])c===""||o.has(c)||(o.add(c),r.push({text:c,placeholder:a}))};e&&s(e,wE);for(let i of n)s(i,wE);for(let i of t)s(i,Kne);return r.sort((i,a)=>a.text.length-i.text.length),{tokens:r,...e?{estate:e}:{}}}var rL=e=>e!==void 0&&/^[A-Za-z0-9]$/.test(e),oL=e=>e==="-"||e==="_";function Xne(e,t){if(!e.includes(t.text))return e;let n="",r=0;for(;r<e.length;){let o=e.indexOf(t.text,r);if(o<0){n+=e.slice(r);break}let s=o+t.text.length,i=!rL(e[o-1])&&!rL(e[s]);i&&t.text.length<3&&(i=oL(e[o-1])||oL(e[s])),i?(n+=e.slice(r,o)+t.placeholder,r=s):(n+=e.slice(r,o+1),r=o+1)}return n}function bE(e,t){for(let n of t.tokens)e=Xne(e,n);return e}function pL(e){let t="",n=[],r=0;for(;r<e.length;){if(e[r]!=="["){t+=e[r++];continue}let o=r+1,s=!1;for(;o<e.length;o++)if(s&&e[o]==="\\")o++;else if(e[o]==='"')s=!s;else if(!s&&e[o]==="]")break;let i=e.slice(r+1,Math.min(o,e.length)),a=i;if(i.startsWith('"'))try{a=JSON.parse(i)}catch{}n.push(a),r=o+1}return{bare:t,keys:n}}function fL(e,t){let n=pL(e).bare.split(".");for(let r=0;r<n.length;r++)n[r]==="module"&&r+1<n.length?(n[r+1]=bE(n[r+1],t),r++):r===n.length-1&&(n[r]=bE(n[r],t));return n.join(".")}var Qne="+-=_/@.:",sL=e=>/^[\p{L}\p{Nd} ]$/u.test(e)||Qne.includes(e);function ere(e){let t=[...e];return t.some(n=>n==="+"||!sL(n))?t.map(n=>n==="+"?"++":sL(n)?n:"+"+n.codePointAt(0).toString(16).toUpperCase().padStart(6,"0")).join(""):e}var tre=e=>ere(e).replaceAll("@","@@").replaceAll(".","@d").replaceAll(":","@c");function nre(e){if(!e.includes("["))return e;let t=[...e],n="";for(let r=0;r<t.length;){if(t[r]!=="["){t[r]!=="]"&&t[r]!=='"'&&(n+=t[r]),r++;continue}let o=r+1;for(;o<t.length&&t[o]!=="]";)o++;n+=":"+tre(t.slice(r+1,o).join("").replace(/^"+|"+$/g,"")),r=o<t.length?o+1:o}return n}function rre(e){let t=s=>s===1?mb:`${mb}-${s}`,n=s=>typeof e[s]=="string"?e[s]:void 0;if(n(mb)===void 0)return;let r="",o=1;for(;o<=nL&&n(t(o))!==void 0;o++)r+=n(t(o));for(let s=o+1;s<=nL;s++)if(n(t(s))!==void 0)return;return r}var ore=e=>e===mb||/^tofu-address-\d+$/.test(e);function hb(e,t,n){if(typeof e=="string")return bE(e,t);if(Array.isArray(e))return e.map(a=>hb(a,t,n));if(e===null||typeof e!="object")return e;let r=e,o=nre(n),s=rre(r)===o,i={};for(let[a,c]of Object.entries(r))typeof c=="string"&&a===Zne&&t.estate!==void 0&&c===t.estate?i[a]=wE:typeof c=="string"&&s&&ore(a)||typeof c=="string"&&a===Yne&&c===o?i[a]=tL:i[a]=hb(c,t,n);return i}var sre={create:"+",update:"~",replace:"-/+",delete:"-",read:"<=","no-op":"",forget:"forget"};function ire(e,t,n){return e.sensitive?{sensitive:!0,...e.forcesReplacement?{forcesReplacement:!0}:{}}:{...e.before!==void 0&&e.before!==null?{before:hb(e.before,t,n)}:{},...e.unknown?{after:Vne}:e.after!==void 0&&e.after!==null?{after:hb(e.after,t,n)}:{},...e.forcesReplacement?{forcesReplacement:!0}:{}}}function gL(e,t){if(e.action==="no-op"&&!e.importing)return;let n=fL(e.address,t)+(e.deposed!==void 0?" (deposed)":""),r=new Map,o={};if(e.action!=="delete"&&e.action!=="forget")for(let c of[...e.attributes].sort((u,l)=>u.path<l.path?-1:u.path>l.path?1:0))o[c.path]=ire(c,t,e.address),r.set(c.path,JSON.stringify(o[c.path]));let s=yb({type:e.type,action:e.action,address:n,attrs:o,...e.importing?{importing:!0}:{}}),i=[...r.keys()],a=`${sre[e.action]||"import"} ${n}`;return e.importing&&e.action!=="no-op"&&(a+=" (import)"),i.length>0&&e.action!=="create"&&e.action!=="read"&&(a+=`: ${i.join(", ")}`),{key:s,line:a,action:e.action,address:n,attrs:r}}function yb(e){if(Array.isArray(e))return`[${e.map(yb).join(",")}]`;if(e!==null&&typeof e=="object"){let t=e;return`{${Object.keys(t).sort().map(n=>`${JSON.stringify(n)}:${yb(t[n])}`).join(",")}}`}return JSON.stringify(e)??"null"}var ur=(e,t)=>e<t?-1:e>t?1:0;function _E(e){if(!(e.action!=="delete"&&e.action!=="replace"))return{member:e.member,address:e.address,type:e.type,action:e.action,...e.deposed!==void 0?{deposed:e.deposed}:{}}}function mL(e,t){return yb({type:e.type,trigger:e.trigger===void 0?null:fL(e.trigger,t),event:e.event??null})}function are(e){let t=e.replaceAll("\\","/").split("/").filter(n=>n!==""&&n!==".");return t[t.length-1]??""}function cre(e,t){let n=new Map;for(let o of e.entries){let s=n.get(o.member);s?s.push(o):n.set(o.member,[o])}let r=[];for(let o of e.members){if(t.has(o.member))continue;let s=are(o.member),i=dL(o.scope,[],s&&s!==o.scope?[s]:[]),a={name:o.member,family:"",provisional:o.provisional===!0,changes:[],destroys:[],effects:[],sideEffects:[]};for(let c of e.sideEffects??[])c.member===o.member&&(a.effects.push(mL(c,i)),a.sideEffects.push(c));for(let c of n.get(o.member)??[]){let u=gL(c,i);if(!u)continue;a.changes.push(u);let l=_E(c);l&&a.destroys.push(l)}r.push(a)}return r}function ure(e,t){let n=new Set(e.members.filter(o=>o.provisional===!0).map(o=>o.member)),r=[];for(let o of e.entries){if(t.has(o.member))continue;let{bare:s,keys:i}=pL(o.address),a=dL(void 0,i,[]),c=gL(o,a);if(!c)continue;let u=_E(o),l=o.deposed!==void 0?`${o.address} (deposed ${o.deposed})`:o.address,d=(e.sideEffects??[]).filter(p=>p.member===o.member&&p.trigger===o.address);r.push({name:l,family:s,provisional:n.has(o.member),changes:[c],destroys:u?[u]:[],effects:d.map(p=>mL(p,a)),sideEffects:d})}return r}function lre(e){let t=new Map;for(let n of e){let r=t.get(n.key);r?r.count++:t.set(n.key,{line:n.line,action:n.action,count:1,key:n.key,norm:n})}return[...t.values()].sort((n,r)=>ur(n.line,r.line)||ur(n.key,r.key))}function dre(e,t){let n=new Map(e.map(o=>[o.key,o.count])),r=[];for(let o of t){let s=n.get(o.key)??0;s===0?r.push(o):o.count>s&&r.push({...o,count:o.count-s}),n.delete(o.key)}return[...n.values()].some(o=>o>0)?void 0:r}function iL(e,t){if(t.lines.some(o=>o.key===e.key))return;let n=t.lines.find(o=>o.norm.address===e.norm.address&&o.norm.action===e.norm.action);if(!n)return;let r=new Set;for(let[o,s]of e.norm.attrs)n.norm.attrs.get(o)!==s&&r.add(o);for(let o of n.norm.attrs.keys())e.norm.attrs.has(o)||r.add(o);e.differsFrom=t.id,e.differsIn=[...r].sort()}var aL=e=>({line:e.line,action:e.action,count:e.count,...e.differsFrom!==void 0?{differsFrom:e.differsFrom,differsIn:e.differsIn??[]}:{}});function pre(e,t){let n=new Map;for(let a of e){let c=a.changes.map(p=>p.key).sort(ur),u=[...a.effects].sort(ur),l=Rn("plan-summary-group",{resource:a.family,changes:c,...u.length>0?{effects:u}:{},...a.provisional?{provisional:!0}:{}}).slice(Ty.length,Ty.length+12),d=n.get(l);d||(d={id:l,family:a.family,provisional:a.provisional,units:[],destroys:[],sideEffects:[],lines:lre(a.changes),noChanges:a.changes.length===0&&a.effects.length===0},n.set(l,d)),d.units.push(a.name),d.destroys.push(...a.destroys),d.sideEffects.push(...a.sideEffects)}let r=[...n.values()];for(let a of r)a.units.sort(ur);r.sort((a,c)=>Number(a.provisional)-Number(c.provisional)||(t&&a.family!==c.family?ur(a.family,c.family):0)||c.units.length-a.units.length||ur(a.units[0],c.units[0]));let o=a=>a.provisional?`${a.family}\0provisional`:a.family,s=new Map,i=new Map;for(let a of r)i.set(o(a),(i.get(o(a))??0)+1),!s.has(o(a))&&!a.noChanges&&s.set(o(a),a);return r.map(a=>{let c=s.get(o(a)),u;if(c&&c!==a&&!a.noChanges){let l=dre(c.lines,a.lines);l&&l.length>0&&(u=l);for(let d of a.lines)iL(d,c);for(let d of u??[])iL(d,c)}return{id:a.id,...a.family?{resource:a.family}:{},units:a.units,outlier:a.units.length===1&&(i.get(o(a))??0)>1,noChanges:a.noChanges,changes:a.lines.map(aL),...u&&c?{extends:c.id,plus:u.map(aL)}:{},destroys:a.destroys.sort(hL),sideEffects:a.sideEffects.sort(yL),...a.provisional?{provisional:!0}:{}}})}var hL=(e,t)=>ur(e.member,t.member)||ur(e.address,t.address)||ur(e.deposed??"",t.deposed??""),yL=(e,t)=>ur(e.member,t.member)||ur(e.address,t.address)||ur(e.trigger??"",t.trigger??""),cL=(e,t)=>ur(e.member,t.member)||ur(e.address,t.address)||ur(e.deposed??"",t.deposed??"");function fre(e){return e.error??"the member failed to plan"}function wL(e,t={}){let n=t.unit??(e.members.length===1?"instance":"member"),r=e.members.filter(c=>c.status==="failed"),o=new Set(r.map(c=>c.member)),s=n==="member"?cre(e,o):ure(e,o),i=e.entries.map(_E).filter(c=>c!==void 0).sort(hL),a=e.members.flatMap(c=>c.holes.map(u=>({member:c.member,address:u.address,...u.type?{type:u.type}:{},reason:u.reason})));return{$schema:vE,contract:$E,changeSet:e.digest,unit:n,units:n==="member"?e.members.length:s.length,groups:pre(s,n==="instance"),failed:r.map(c=>({member:c.member,...c.scope?{scope:c.scope}:{},reason:fre(c)})),destroys:i,imports:e.entries.filter(c=>c.importing).map(c=>({member:c.member,address:c.address,type:c.type,action:c.action})).sort(cL),forgets:e.entries.filter(c=>c.action==="forget").map(c=>({member:c.member,address:c.address,type:c.type,...c.deposed!==void 0?{deposed:c.deposed}:{}})).sort(cL),sideEffects:[...e.sideEffects??[]].sort(yL),holes:a}}var pc=(e,t,n)=>`${e} ${e===1?t:n}`;function bL(e,t){return e.unit==="instance"?pc(t,"instance","instances"):pc(t,"member","members")}function gre(e){let t=[pc(e.groups.length,"group","groups")],n=e.groups.filter(r=>r.provisional).length;return n>0&&t.push(`${n} provisional`),e.failed.length>0&&t.push(`${e.failed.length} failed`),t.push(pc(e.destroys.length,"destroy or replacement","destroys or replacements")),e.forgets.length>0&&t.push(pc(e.forgets.length,"forget","forgets")),e.imports.length>0&&t.push(pc(e.imports.length,"import","imports")),e.sideEffects.length>0&&t.push(pc(e.sideEffects.length,"triggered action","triggered actions")),e.holes.length>0&&t.push(pc(e.holes.length,"hole","holes")),`${bL(e,e.units)}: ${t.join(", ")}.`}function mre(e,t){let n=`Group ${t.id}: ${bL(e,t.units.length)}`;return t.resource&&(n+=` of ${t.resource}`),t.outlier&&(n+=" (outlier)"),t.provisional&&(n+=" (provisional)"),t.noChanges?n+=", no changes":t.extends?n+=`, group ${t.extends}'s change plus`:t.units.length>1?n+=", identical change":n+=", change",n}function hre(e){let t=e.count>1?`${e.line} (x${e.count})`:e.line;return e.differsFrom&&(t+=e.differsIn&&e.differsIn.length>0?` [differs from group ${e.differsFrom} in: ${e.differsIn.join(", ")}]`:` [differs from group ${e.differsFrom}]`),t}var yre=e=>e.extends?e.plus??[]:e.changes;function wre(e){return`${e.member}: ${e.address}${e.deposed!==void 0?` (deposed ${e.deposed})`:""} (${e.action})`}var bre=e=>`${e.member}: ${e.address}${e.deposed!==void 0?` (deposed ${e.deposed})`:""} (forget, the object keeps running)`,vre=e=>`${e.member}: ${e.address} (${e.action==="no-op"?"import, no other change":`import, then ${e.action}`})`,$re=e=>`${e.member}: ${e.address}${e.trigger!==void 0?`, triggered by ${e.trigger}${e.event!==void 0?` ${e.event}`:""}`:""}`,kre="Provisional: planned before what it reads applied, so it may show stand-in values. No approval covers it; it is planned again in its own wave.",uL=e=>e.split(/\s+/).filter(Boolean).join(" "),_re=e=>e.scope&&e.scope!==e.member?`${e.member} (${e.scope})`:e.member;function vL(e){let t=[gre(e)];if(e.destroys.length>0){t.push("",`Destroys and replacements (${e.destroys.length}):`);for(let n of e.destroys)t.push(` ${wre(n)}`)}if(e.forgets.length>0){t.push("",`Forgets (${e.forgets.length}), no longer tracked, not destroyed:`);for(let n of e.forgets)t.push(` ${bre(n)}`)}if(e.imports.length>0){t.push("",`Imports (${e.imports.length}):`);for(let n of e.imports)t.push(` ${vre(n)}`)}if(e.sideEffects.length>0){t.push("",`Triggered actions (${e.sideEffects.length}), side effects of apply:`);for(let n of e.sideEffects)t.push(` ${$re(n)}`)}if(e.failed.length>0){t.push("",`Failed (${e.failed.length}), not grouped:`);for(let n of e.failed)t.push(` ${_re(n)}: ${uL(n.reason)}`)}if(e.holes.length>0){t.push("",`Holes (${e.holes.length}), addresses the plan says nothing about:`);for(let n of e.holes)t.push(` ${n.member}: ${n.address}: ${uL(n.reason)}`)}for(let n of e.groups){t.push("",`${mre(e,n)}:`);for(let r of yre(n))t.push(` ${hre(r)}`);t.push(` ${e.unit==="instance"?"instances":"members"}: ${n.units.join(", ")}`),n.provisional&&t.push(` ${kre}`),n.destroys.length>0&&t.push(` destroys and replacements: ${n.destroys.length}, listed above`),n.sideEffects.length>0&&t.push(` triggered actions: ${n.sideEffects.length}, listed above`)}return t.join(`
|
|
147
147
|
`)+`
|
|
148
|
-
`}import{existsSync as
|
|
148
|
+
`}import{existsSync as kL,readFileSync as Sre}from"node:fs";import{basename as Rre,join as xre}from"node:path";var Ere=["-target","-exclude","-destroy","-refresh-only","-json","-estate"],Tre=e=>Rre(e)==="choudoufu";function wb(e,t){return!kL(xre(e,t,"estate.chdf.hcl"))&&ms(e,[t]).length===1}function bb(e,t,n,r){return!t||!r||!Tre(e)?[]:n.some(s=>Ere.some(i=>s===i||s.startsWith(`${i}=`)))?[]:[`-cone=${t}`]}var Are=e=>`${e}.cone.json`;function Ore(e){let t=Are(e);if(kL(t))try{let n=JSON.parse(Sre(t,"utf-8"));return!n||typeof n.base!="string"||!Array.isArray(n.targets)||!n.targets.every(r=>typeof r=="string")?void 0:{base:n.base,base_sha:typeof n.base_sha=="string"?n.base_sha:"",targets:[...n.targets].sort()}}catch{return}}function $L(e,t){let n=e.replace(/\r\n/g,`
|
|
149
149
|
`).split(`
|
|
150
|
-
`).map(o=>o.replace(/^[\s│╷╵]*/,"").trimEnd()),r;for(let o=0;o<n.length;o++){if(n[o]!==`Warning: ${t}`)continue;let s=o+1;for(;s<n.length&&n[s]==="";)s++;let i=[];for(;s<n.length&&n[s]!==""&&!/^(Plan: |No changes\.|Changes to Outputs:)/.test(n[s]);s++)i.push(n[s]);r=i.join(" ").replace(/\s+/g," ").trim()}return r}function vb(e,t,n){let r=$
|
|
150
|
+
`).map(o=>o.replace(/^[\s│╷╵]*/,"").trimEnd()),r;for(let o=0;o<n.length;o++){if(n[o]!==`Warning: ${t}`)continue;let s=o+1;for(;s<n.length&&n[s]==="";)s++;let i=[];for(;s<n.length&&n[s]!==""&&!/^(Plan: |No changes\.|Changes to Outputs:)/.test(n[s]);s++)i.push(n[s]);r=i.join(" ").replace(/\s+/g," ").trim()}return r}function vb(e,t,n){let r=$L(e,"Cone plan fell back to a full plan");if(r!==void 0){let i=/^This plan covers the whole estate \(guard ([^)]*)\): (.*?)\.?$/.exec(r);return{base:n,fallback:i?{kind:i[1],reason:i[2]}:{kind:"unknown",reason:r}}}let o=Ore(t);if(!o)return{base:n,fallback:{kind:"unknown",reason:"choudoufu wrote no cone record beside the plan"}};let s=/: (\d+) of the estate's (\d+) blocks/.exec($L(e,"Cone plan")??"");return{base:n,...s?{blocks:Number(s[1]),of:Number(s[2])}:{},record:o}}function Pre(e){if(e.fallback)return`the whole estate, since ${e.fallback.reason} (guard ${e.fallback.kind})`;let t=e.record?.targets.length??0,n=e.blocks!==void 0&&e.of!==void 0?`${e.blocks} of the estate's ${e.of} blocks`:`${t} target${t===1?"":"s"}`;return`the cone of the change since ${e.base}, ${n}`}function _L(e){return e.fallback?{base:e.base,fallback:e.fallback}:{base:e.base,...e.blocks!==void 0&&e.of!==void 0?{blocks:e.blocks,of:e.of}:{},...e.record?{targets:e.record.targets.length}:{}}}var $b=(e,t)=>`${e}: planned ${Pre(t)}`;function kb(e,t){return t?.record?Rn("terragucci-cone-plan",{plan:e,targets:[...t.record.targets].sort()}):e}function Vn(e){let t=e,n=r=>(r??[]).some(o=>o!=="no-op"&&o!=="read");return(t?.resource_changes??[]).some(r=>n(r.change?.actions))||Object.values(t?.output_changes??{}).some(r=>n(r.actions))}function _b(e){return(e?.resource_changes??[]).some(n=>(n.change?.actions??[]).includes("delete"))}import{spawn as jre}from"node:child_process";import{mkdirSync as Ire,writeFileSync as SL}from"node:fs";import{dirname as Cre,join as RL}from"node:path";var Nre='infracost breakdown --path "$TG_PLAN_JSON" --format json --log-level error',Dre=12e4;function Sb(e){return{command:e!==!0&&e.command?e.command:Nre}}var Mre=(e,t,n)=>new Promise(r=>{let o=jre("sh",["-c",e],{cwd:n,env:t,stdio:["ignore","pipe","pipe"]}),s=[],i=[],a=setTimeout(()=>o.kill("SIGKILL"),Dre);o.stdout.on("data",c=>s.push(c)),o.stderr.on("data",c=>i.push(c)),o.on("error",c=>{clearTimeout(a),r({code:127,stdout:"",stderr:c.message})}),o.on("close",c=>{clearTimeout(a),r({code:c,stdout:Buffer.concat(s).toString("utf-8"),stderr:Buffer.concat(i).toString("utf-8")})})});function SE(e){if(e==null||e==="")return null;let t=typeof e=="number"?e:Number(e);return Number.isFinite(t)?t:null}function Lre(e){let t=JSON.parse(e);if(!t||typeof t!="object"||!("totalMonthlyCost"in t||"diffTotalMonthlyCost"in t))throw new Error("the output has no totalMonthlyCost or diffTotalMonthlyCost");let n=SE(t.totalMonthlyCost),r=SE(t.pastTotalMonthlyCost),o=SE(t.diffTotalMonthlyCost);return{currency:typeof t.currency=="string"&&t.currency?t.currency:"USD",monthly_delta:o??(n!==null?n-(r??0):null),monthly_total:n,past_monthly_total:r}}function xL(e){return`roots/${e}/cost.json`}function Gre(e){return e.trim().split(/\s+/)[0]?.split("/").pop()??"estimator"}var RE=e=>Math.round(e*100)/100;async function Rb(e,t,n,r,o,s,i=Mre){let a=new Map,c=[],u=new Set,l=Ve(n);for(let[g,m]of e.entries()){let h=RL(r,`cost-${g}.json`);SL(h,m.json);let y=await i(t.command,{...l,TG_PLAN_JSON:h,TG_ROOT:m.root},o),w=(y.stderr.trim().split(`
|
|
151
151
|
`).filter(b=>b.trim()&&!/^Node\.js v\d/.test(b)).pop()??"").trim().slice(0,200);if(y.code!==0){s(`cost: ${m.root}: the estimator exited ${y.code}${w?`: ${w}`:""}`),c.push({root:m.root,monthly_delta:null,monthly_total:null,past_monthly_total:null,error:`the estimator exited ${y.code}${w?`: ${w}`:""}`});continue}try{let b=Lre(y.stdout);u.add(b.currency),a.set(m.root,y.stdout.endsWith(`
|
|
152
152
|
`)?y.stdout:`${y.stdout}
|
|
153
|
-
`),c.push({root:m.root,monthly_delta:b.monthly_delta,monthly_total:b.monthly_total,past_monthly_total:b.past_monthly_total,output:
|
|
153
|
+
`),c.push({root:m.root,monthly_delta:b.monthly_delta,monthly_total:b.monthly_total,past_monthly_total:b.past_monthly_total,output:xL(m.root)}),s(`cost: ${m.root}: ${b.monthly_delta===null?"no estimate":`${gc(b.monthly_delta)} ${b.currency} a month`}`)}catch(b){s(`cost: ${m.root}: the estimator printed no estimate (${b.message})`),c.push({root:m.root,monthly_delta:null,monthly_total:null,past_monthly_total:null,error:`the estimator printed no estimate: ${b.message}`})}}let d=c.filter(g=>g.monthly_delta!==null),p=g=>d.length>0?RE(d.reduce((m,h)=>m+(h[g]??0),0)):null,f={estimator:Gre(t.command),currency:u.size===1?[...u][0]:u.size===0?"USD":[...u].sort().join(", "),monthly_delta:p("monthly_delta"),monthly_total:p("monthly_total"),past_monthly_total:p("past_monthly_total"),roots:c};return f.monthly_delta!==null&&s(`cost: ${gc(f.monthly_delta)} ${f.currency} a month over ${d.length} of ${c.length} roots`),{cost:f,outputs:a}}function xb(e,t){for(let[n,r]of t){let o=RL(e,xL(n));Ire(Cre(o),{recursive:!0}),SL(o,r)}}function gc(e){let t=RE(e);return`${t>0?"+":t<0?"-":""}${Math.abs(t).toFixed(2)}`}function fc(e){return e&&e!==!0&&typeof e.approve_above=="number"?e.approve_above:void 0}async function Eb(e,t,n,r={}){if(!n)return{...t?{setting:t}:{},...fc(t)!==void 0?{approveAbove:fc(t),source:"the config here"}:{}};let o=await ln(e,n,r);if("error"in o)return fc(t)!==void 0?{...t?{setting:t}:{},error:`cost.approve_above is read from the config at ${n}, and it could not be read (${o.error})`}:t?{setting:t}:{};let s;try{s=o.config.projects?r.project?ot(o.config,r.project).cost:void 0:ue(o.config).cost}catch(u){return fc(t)!==void 0?{...t?{setting:t}:{},error:`cost.approve_above is read from the config at ${n}, and it could not be read (${u.message})`}:t?{setting:t}:{}}let i=fc(s),a=fc(t),c=t??s;return{...c?{setting:c}:{},...i!==void 0?{approveAbove:i,source:`the config at ${n}`}:{},...a!==i?{note:`cost: approve_above is ${a??"unset"} here and ${i??"unset"} at ${n}; the amount at ${n} counts`}:{}}}function Tb(e,t,n){let r=e.roots.filter(c=>t.includes(c.root)),o=r.filter(c=>c.monthly_delta!==null),s=r.filter(c=>c.monthly_delta===null).map(c=>c.root).sort(),i=c=>o.length>0?RE(o.reduce((u,l)=>u+(l[c]??0),0)):null,a=i("monthly_delta");return{currency:e.currency,monthly_delta:a,monthly_total:i("monthly_total"),past_monthly_total:i("past_monthly_total"),...s.length>0?{unestimated:s}:{},...n!==void 0?{approve_above:n,over:s.length>0||a===null||a>n}:{}}}var Fre="(monthly cost)";function Cd(e){if(!(!e||e.approve_above===void 0))return{member:Fre,planDigest:Rn("terragucci-wave-cost",{approve_above:e.approve_above,currency:e.currency,monthly_delta:e.monthly_delta,unestimated:e.unestimated??[]})}}function jg(e,t){let n=t?` in ${t}`:"";return e.unestimated?.length?`the monthly cost of ${e.unestimated.join(", ")} could not be estimated, and cost.approve_above is ${e.approve_above.toFixed(2)} ${e.currency}${n}`:e.monthly_delta===null?`the wave's monthly cost could not be estimated, and cost.approve_above is ${e.approve_above.toFixed(2)} ${e.currency}${n}`:`the monthly cost changes by ${gc(e.monthly_delta)} ${e.currency}, over cost.approve_above ${e.approve_above.toFixed(2)} ${e.currency}${n}`}function Ab(e,t,n,r){let o=e.roots.find(s=>s.root===t);return{estimator:e.estimator,currency:e.currency,root:{monthly_delta:o?.monthly_delta??null,monthly_total:o?.monthly_total??null,past_monthly_total:o?.past_monthly_total??null},...n?{wave:{number:n.number,monthly_delta:n.cost.monthly_delta,monthly_total:n.cost.monthly_total,past_monthly_total:n.cost.past_monthly_total}}:{},approve_above:r??null}}var zre=[{match:/^aws_iam_|^google_.*_iam_|^azurerm_role_(assignment|definition)$|^kubernetes_(cluster_)?role(_binding)?$/,why:"IAM: changes who may do what"},{match:/^aws_(default_)?security_group(_rule)?$|^aws_vpc_security_group_(ingress|egress)_rule$|^google_compute_firewall$|^azurerm_network_security_(group|rule)$/,why:"security group: changes what traffic gets in or out"},{match:/^aws_(default_)?network_acl(_rule|_association)?$/,why:"network ACL: changes what traffic a subnet allows"},{match:/^aws_kms_|^google_kms_|^azurerm_key_vault_key$/,why:"KMS key: data encrypted under it depends on it"},{match:/^aws_route53_(record|zone)$|^google_dns_(record_set|managed_zone)$|^azurerm_dns_|^cloudflare_(dns_)?record$/,why:"DNS: changes where names resolve"}];function Ure(e){return zre.find(t=>t.match.test(e))}var Bre=new Set(["tags","tags_all","labels","effective_labels","terraform_labels"]),Wre=new Set(["description"]);function EL(e,t){if(!(e!=="update"||t.length===0)){if(t.every(n=>Bre.has(n.path)))return"tags";if(t.every(n=>Wre.has(n.path)))return"description";if(t.every(n=>n.unknown===!0))return"unknown"}}function TL(e,t=!1){let n=(e.replace_paths??[]).map(o=>o.join("."));switch(e.action){case"delete":return{fold:"open",why:t?"destroys a resource under prevent_destroy":"destroys"};case"replace":return{fold:"open",why:n.length>0?`replaces, forced by ${n.join(", ")}`:"replaces"};case"forget":return{fold:"open",why:"forgets: leaves the resource running, out of state"}}if(e.kind!==void 0)return{fold:"folded"};if(t)return{fold:"open",why:"under prevent_destroy"};let r=Ure(e.type);return r&&e.action!=="no-op"&&e.action!=="read"?{fold:"open",why:r.why}:{fold:"open"}}import{createHash as Hre}from"node:crypto";var AL="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADMAAAAfCAMAAAB9EQVVAAAAVFBMVEUAAAAJtroqIiI2PCVaNBNdjApk09N9KhGKSxmSbj6W4eOjZyOlyhC06ee4Ski8Gg7U727Wm0DZ9vbbqljcDwjnj4LnwSr0lQr0wmr21pv22Qf39/d/Xc1TAAAAAXRSTlMAQObYZgAAAa1JREFUeNqNlIF2gyAMRSUD5la7GWZXnf//n3uBIKC2p++cUqq5vCRAu+4ggvLYpfGZJJAGSMeO4vgUGSEanBuStgUemEWL0QEQDYNwMlGeHllIpLOtBHenDDze0sJgllrAziGKHlgUH5bIeVPEsNS+JBolZXv7SsTMG8FK/WDRlhLGvk/TFNMqRG01to2INmBiJYihRnjQzwujLNkvqm2mb0HweiYvMuu6GpmQZolWyHZRZlzscCodSAjBryKDidkgaYUy2Ep0035kBHE+IeJkQnFClCPNjMhpKRFB6BoydL1WkJ0yIlvJWr6PWZmMCFOc7HSLkNgMxQalhIoBIFKIsR13yo3mvPVkoj4VuaafxKxGf3+UO83baZFOY+GgNmh6QQAlBn0btM0XdfK5cTKrkdne0+1NR6YclZheiDl5TYz16OGlGAG52RpJkE+1N4nVDNl4ZhooMQekMLBpjAB5tdHEyuWgXzA9mO2u5G8yidm5MJgFzNJTa1KO3R6JPqeMpie7r0gGLw3Tn0FHF2UA0XJmNJ8hM3M6B3zhnQ4Pyot8T/l1UfO/+5ok/B+M50/N53uHFAAAAABJRU5ErkJggg==",zo=`<link rel="icon" type="image/png" href="${AL}">`,Uo=`<img class="taco" src="${AL}" width="51" height="31" alt="">`,Bo="h1.brand{display:flex;align-items:center;gap:10px}.taco{image-rendering:pixelated;flex:none}",OL="https://intentius.io/terragucci/brand/taco-small.png";var Ig="terragucci.changes/v1",qre="terragucci.history/v1",Jre=2e4,Kre=(e,t)=>JSON.stringify(e)===JSON.stringify(t),xE=e=>e===!0||(Array.isArray(e)?e.some(xE):Ae(e)?Object.values(e).some(xE):!1);function Vre(e){let t=Ae(e.before)?e.before:{},n=Ae(e.after)?e.after:{},r=Ae(e.after_unknown)?e.after_unknown:{},o=new Set;for(let s of new Set([...Object.keys(t),...Object.keys(n)]))Kre(t[s],n[s])||o.add(s);for(let[s,i]of Object.entries(r))xE(i)&&o.add(s);return[...o].sort()}function Ob(e){let t=Ae(e)&&Array.isArray(e.resource_changes)?e.resource_changes:[],n=[];for(let r of t){if(!Ae(r)||r.mode!=="managed"||typeof r.address!="string"||typeof r.type!="string"||r.deposed!==void 0)continue;let o=Ae(r.change)?r.change:{},s=Array.isArray(o.actions)?o.actions.map(String):[],i=[];if(typeof r.previous_address=="string"&&r.previous_address!==r.address&&i.push("move"),o.importing!==void 0&&o.importing!==null&&i.push("import"),s.includes("delete")&&s.includes("create")?i.push("replace"):s.includes("create")?i.push("create"):s.includes("update")?i.push("update"):s.includes("delete")?i.push("delete"):s.includes("forget")&&i.push("forget"),i.length===0)continue;let a=i.includes("update")||i.includes("replace")?Vre(o):[];n.push({address:r.address,type:r.type,actions:i,attributes:a,...typeof r.previous_address=="string"&&r.previous_address!==r.address?{previous_address:r.previous_address}:{}})}return n.sort((r,o)=>r.address<o.address?-1:r.address>o.address?1:0)}function PL(e,t){if(e.run.stage!=="tf-apply")return[];let n=e.waves[0];return e.roots.flatMap(r=>(r.applied_changes??[]).map(o=>({...o,root:r.path,commit:e.run.commit,...e.run.wave!==void 0?{wave:e.run.wave}:{},finished:e.run.finished,path:t,plan_digest:r.plan_digest,set_digest:n?.set_digest??null,...e.run.pull_request?{pull_request:e.run.pull_request}:{}})))}function EE(e){if(e)try{let t=JSON.parse(e);if(Array.isArray(t.changes))return{schema:Ig,changes:t.changes}}catch{}return{schema:Ig,changes:[]}}function jL(e,t,n=Jre){let r=new Set(t.map(i=>`${i.path}
|
|
154
154
|
${i.root}`)),s=[...EE(e).changes.filter(i=>!r.has(`${i.path}
|
|
155
155
|
${i.root}`)),...t].sort((i,a)=>Date.parse(a.finished)-Date.parse(i.finished)||(i.path<a.path?-1:i.path>a.path?1:i.root<a.root?-1:i.root>a.root?1:i.address<a.address?-1:1));return{schema:Ig,changes:s.slice(0,n)}}var TE=(e,t,n)=>`h-${Hre("sha256").update(`${e}
|
|
156
156
|
${t}
|
|
@@ -163,7 +163,7 @@ ${c.path}`);c.record_versions&&(!l.record_versions||Date.parse(c.record_versions
|
|
|
163
163
|
<div class="scroll"><table><tr><th>Written</th><th>Version id</th><th></th></tr>
|
|
164
164
|
${r.join(`
|
|
165
165
|
`)}
|
|
166
|
-
</table></div>`}function
|
|
166
|
+
</table></div>`}function IL(e){let t=e.resources.map(r=>{let o=r.applies.map(s=>{let i=s.actions.map(l=>Yre[l]).join(", ")+(s.previous_address?` from <code>${T(s.previous_address)}</code>`:""),a=s.attributes.length?s.attributes.map(l=>`<code>${T(l)}</code>`).join(", "):"",c=s.approver===void 0?`<span class="none">${e.audit?"not in the audit trail":"no audit trail read"}</span>`:s.approver===null?'<span class="none">no gate held it</span>':T(s.approver),u=`${s.wave!==void 0?`wave ${s.wave}`:"apply"} <code>${T(s.commit.slice(0,12))}</code>`;return`<tr><td><time datetime="${T(s.finished)}">${T(s.finished)}</time></td><td>${i}</td><td>${a}</td><td>${c}</td><td>${s.report?`<a href="${T(s.report)}">${u}</a>`:u}${s.pull_request?` #${T(s.pull_request)}`:""}</td><td><code>${T(s.plan_digest??"")}</code></td></tr>`});return`<section id="${r.id}"><h2><code>${T(r.address)}</code></h2><p><small>${T(r.project)}, root <code>${T(r.root)}</code>, <code>${T(r.type)}</code>: ${r.applies.length} ${r.applies.length===1?"apply":"applies"}</small></p>
|
|
167
167
|
<div class="scroll"><table><tr><th>Applied</th><th>What</th><th>Attributes changed</th><th>Approved by</th><th>Run</th><th>Plan digest</th></tr>
|
|
168
168
|
${o.join(`
|
|
169
169
|
`)}
|
|
@@ -181,19 +181,19 @@ ${t.length?t.join(`
|
|
|
181
181
|
</main>
|
|
182
182
|
<script type="application/json" id="terragucci-history">${n}</script>
|
|
183
183
|
</body></html>
|
|
184
|
-
`}var Cg="terragucci.inventory/v1",Qre=(e,t)=>e.address<t.address?-1:e.address>t.address?1:0;function C1(e){let t=[],n=o=>{if(Ae(o)){for(let s of Array.isArray(o.resources)?o.resources:[])!Ae(s)||s.mode!=="managed"||typeof s.address!="string"||typeof s.type!="string"||t.push({address:s.address,type:s.type,provider:typeof s.provider_name=="string"?s.provider_name:""});for(let s of Array.isArray(o.child_modules)?o.child_modules:[])n(s)}},r=Ae(e)?e.planned_values:void 0;return n(Ae(r)?r.root_module:void 0),t.sort(Qre)}function N1(e,t){return e.run.stage!=="tf-apply"?[]:e.roots.filter(n=>n.resources!==void 0).map(n=>({root:n.path,commit:e.run.commit,finished:e.run.finished,...e.run.wave!==void 0?{wave:e.run.wave}:{},path:t,...n.environment?{environment:n.environment}:{},resources:n.resources}))}function eoe(e){if(e)try{let t=JSON.parse(e);if(Array.isArray(t.roots))return{schema:Cg,roots:t.roots}}catch{}return{schema:Cg,roots:[]}}function D1(e,t){let n=new Map(eoe(e).roots.map(r=>[r.root,r]));for(let r of t){let o=n.get(r.root);(!o||!(Date.parse(o.finished)>Date.parse(r.finished)))&&n.set(r.root,r)}return{schema:Cg,roots:[...n.values()].sort((r,o)=>r.root<o.root?-1:r.root>o.root?1:0)}}function M1(e){let t=new Map;for(let n of e)t.set(n.type,(t.get(n.type)??0)+1);return[...t].map(([n,r])=>({type:n,count:r})).sort((n,r)=>r.count-n.count||(n.type<r.type?-1:1))}function jb(e,t){return t?{...e,environment:t}:e}function oa(e){return{text:`roots/${e}/plan.txt`,json:`roots/${e}/plan.json`}}var bn=e=>`root-${e}`,Ng=e=>`group-${e}`;function toe(e){let t=new Map,n=Ae(e)&&Array.isArray(e.resource_changes)?e.resource_changes:[];for(let r of n){if(!Ae(r))continue;let o=Ae(r.change)?r.change:{},s=Array.isArray(o.replace_paths)?o.replace_paths.filter(Array.isArray):[],i=Ae(o.importing)?String(o.importing.id??o.importing.identity??""):void 0;t.set(`${r.address}\0${r.deposed??""}`,{...typeof r.mode=="string"?{mode:r.mode}:{},...s.length>0?{replace_paths:s}:{},...i!==void 0?{importing:i}:{}})}return t}function noe(e){return{member:{member:e.path,lexicon:"terraform",planner:e.planner??"terraform",status:"failed",error:e.error??"the root did not plan",planDigest:null,holes:[]},entries:[]}}function roe(e,t,n){let r=e.attributes.map(c=>c.path).filter(c=>c.endsWith("_wo_version")),o={address:e.address,type:e.type,action:e.action,...e.disruption?{disruption:e.disruption}:{},...e.deposed!==void 0?{deposed:e.deposed}:{},...e.module?{module:e.module}:{},...e.index!==void 0?{index:e.index}:{},attributes:e.attributes,...t?.replace_paths?{replace_paths:t.replace_paths}:{},...t?.importing!==void 0?{importing:t.importing}:{},...r.length>0?{write_only:r}:{}},s=E1(e.action,e.attributes),{fold:i,why:a}=T1({...o,kind:s},n.has(`${e.type}.${e.name??""}`));return{...o,...s?{kind:s}:{},fold:i,...a?{why:a}:{}}}function ooe(e){if(e.length<2)return[];let t=new Map;for(let r of e)for(let o of r.changes){let s=t.get(o.address)??new Map;t.set(o.address,s);for(let i of o.attributes){let a=s.get(i.path)??new Set;s.set(i.path,a),a.add(JSON.stringify(i.sensitive?Ln:i.unknown?"(known after apply)":i.after??i.before??null))}}let n=[];for(let[r,o]of[...t].sort(([s],[i])=>s<i?-1:1)){let s=[...o].filter(([,i])=>i.size>1).map(([i])=>i).sort();s.length>0&&n.push({address:r,paths:s})}return n}var Nd=e=>e.plan!==void 0&&e.error!==void 0&&e.policy!==void 0&&e.policy.result!=="passed";function L1(e){let t=fo({member:e.path,plan:e.plan,planner:e.planner??"terraform",...e.terragrunt?{scope:e.terragrunt.stack}:{}}),n=t.member.planDigest,r=e.cone?.record&&n?{...t,member:{...t.member,planDigest:kb(n,e.cone)}}:t;return Nd(e)?{...r,member:{...r.member,status:"failed",error:e.error,planDigest:null}}:r}var soe=new Set(["create","update","replace","delete","forget"]);function G1(e,t,n){let r={...e},o=new Set;for(let s of t)n(s.member)?r[s.action]>0&&r[s.action]--:soe.has(s.action)&&o.add(s.member);return{totals:r,roots:o.size}}function F1(e){let t=e.filter(o=>o.plan!==void 0&&(o.error===void 0||Nd(o))).map(L1),n=nS(t),r=new Map(e.map(o=>[o.path,o]));return G1(n.summary.actions,n.entries,o=>Nd(r.get(o)))}function Bu(e){let t=e.run,n=new Map(e.roots.map(h=>[h.path,h])),r=e.roots.map(h=>h.plan!==void 0&&(h.error===void 0||Nd(h))?L1(h):noe(h)).map((h,y)=>e.roots[y].terragrunt?.provisional?{...h,member:{...h.member,provisional:!0}}:h),o=nS(r),s=w1(o),i=new Map;if(s.unit==="member")for(let h of s.groups)for(let y of h.units)i.set(y,h.id);let a=new Set(s.groups.filter(h=>h.outlier).flatMap(h=>h.units)),{totals:c,roots:u}=G1(o.summary.actions,o.entries,h=>Nd(n.get(h))),l=[],d=o.members.map(h=>{let y=n.get(h.member),w=toe(y.plan),b=y.plan!==void 0?bM(y.plan):[],k={},v=[];for(let x of o.entries){if(x.member!==h.member)continue;let A=w.get(`${x.address}\0${x.deposed??""}`);if(A?.mode==="ephemeral"||x.address.startsWith("ephemeral.")||(k[x.action]=(k[x.action]??0)+1,x.action==="no-op"&&A?.importing===void 0))continue;let O=roe({...x,attributes:vM(x.attributes,b)},A,y.preventDestroy??new Set);v.push(O);let I=O.action==="delete"||O.action==="replace"||O.action==="forget"?O.action:O.importing!==void 0?"import":void 0;I!==void 0&&l.push({root:h.member,address:O.address,type:O.type,action:I,...O.deposed!==void 0?{deposed:O.deposed}:{},...O.replace_paths?{replace_paths:O.replace_paths}:{}})}h.status==="failed"&&l.push({root:h.member,action:"refused",reason:h.error??"the root did not plan"});let $=v.filter(x=>x.why!==void 0).map(x=>({address:x.address,type:x.type,action:x.action,why:x.why})),_=[];h.status==="failed"&&_.push(Nd(y)?"refused by policy":"refused to plan"),y.policy?.override&&_.push(`policy overridden by ${y.policy.override.by}`);let E=(y.steps??[]).filter(x=>x.status==="approval").map(x=>x.name);E.length&&_.push(`step ${E.join(", ")} asks for an approval`),y.policy?.warnings.length&&_.push(`${y.policy.warnings.length} policy warning${y.policy.warnings.length===1?"":"s"}`),t.stage==="tf-drift"&&v.length>0&&_.push("drifted"),a.has(h.member)&&_.push("outlier: its change matches no other root's"),$.length>0&&_.push(...new Set($.map(x=>x.why)));let R=i.get(h.member);return{path:h.member,...y.environment?{environment:y.environment}:{},...y.binary?{binary:y.binary}:{},...y.terragrunt?{terragrunt:y.terragrunt}:{},status:h.status,...h.error?{error:h.error}:{},plan_digest:h.planDigest,counts:k,...R?{group:R}:{},plan:y.files??(h.status==="planned"?oa(h.member):{}),...y.job_url??t.job_url?{job_url:y.job_url??t.job_url}:{},changes:v,highlights:$,fold:_.length>0?"open":"folded",why:_,...y.policy?{policy:y.policy}:{},...y.applied&&h.status==="planned"&&y.plan!==void 0?{resources:C1(y.plan),applied_changes:ioe(Ob(y.plan),y.recordVersions)}:{},...y.applied&&y.state?{state:y.state}:{},...y.steps?.length?{steps:y.steps}:{},...y.reads?.length?{reads:y.reads}:{},...y.unknownReads?.length?{unknown_reads:y.unknownReads}:{},...y.unaddressed?{unaddressed:y.unaddressed}:{},...y.dependencies?.length?{dependencies:y.dependencies}:{},...y.cone?{cone:_1(y.cone)}:{},...y.composed?{composed:y.composed}:{}}}),p=new Map(d.map(h=>[h.path,h])),f=s.groups.map(h=>{let y=s.unit==="member"?h.units.map(v=>p.get(v)).filter(Boolean):[],w=h.destroys.length,b=[];h.outlier&&b.push("outlier: read it on its own"),w>0&&b.push(`${w} ${w===1?"destroy or replacement":"destroys or replacements"}`);let k=[...new Set(y.flatMap(v=>v.highlights.filter($=>$.action!=="delete"&&$.action!=="replace").map($=>$.why)))];return b.push(...k),t.stage==="tf-drift"&&!h.noChanges&&b.push("drifted"),{id:h.id,...h.resource?{resource:h.resource}:{},units:h.units,outlier:h.outlier,noChanges:h.noChanges,changes:h.changes,...h.extends?{extends:h.extends}:{},...h.plus?{plus:h.plus}:{},destroys:w,varies:ooe(y),fold:b.length>0?"open":"folded",why:b}}),g={refused:0,delete:1,replace:2,forget:3,import:4};l.sort((h,y)=>g[h.action]-g[y.action]||(h.root<y.root?-1:h.root>y.root?1:0)||((h.address??"")<(y.address??"")?-1:1));let m=(e.waves??[]).map(h=>{let y=h.composed,w=new Set(y?.members.map(R=>R.root)??[]),b=o.members.filter(R=>h.roots.includes(R.member)&&!R.provisional&&!w.has(R.member)),k=y?.planDigest?[{member:y.target,planDigest:y.planDigest}]:[],v=b.some(R=>R.planDigest===null)||y!==void 0&&!y.planDigest,$=Cd(h.cost),_=$?[$]:[],E={};if(e.gate){let R=new Map(e.roots.map(L=>[L.path,L.plan])),x=[...b.filter(L=>Vn(R.get(L.member))),...Vn(y?.plan)?k:[]],A=x.some(L=>_b(R.get(L.member)))||y!==void 0&&_b(y.plan),O=(y?.heldByUnknown?.length??0)>0,I=h.policy??e.gate,N=(h.replansAfter?.length??0)>0;E={review_digest:v||x.length===0||N?null:Wa([...x,..._]),waits:N&&I!=="never"||x.length>0&&(I==="always"||I==="on-destructive"&&A||h.cost?.over===!0||(h.heldBySteps?.length??0)>0||O&&I!=="never")}}return{number:h.number,roots:h.roots,set_digest:h.setDigest??(v||b.length+k.length===0?null:Wa([...b,...k,..._])),approval:h.approval??"not-requested",...E,...h.gate?{gate:h.gate}:{},...h.waitingSince&&h.approval==="waiting"?{waiting_since:h.waitingSince}:{},...h.refused?{refused:h.refused}:{},...h.review&&h.approval==="waiting"?{review:h.review}:{},...h.excluded?.length?{excluded_approvers:h.excluded.map(R=>({login:R.login,reason:R.reason}))}:{},...h.heldBySteps?.length?{held_by_steps:[...h.heldBySteps].sort()}:{},...h.cost?{cost:h.cost}:{},...h.state?{state:h.state}:{},...h.reads?.length?{reads:h.reads}:{},...h.replansAfter?.length?{replans_after:h.replansAfter}:{},...h.preview?{preview:h.preview}:{},...h.environments?.length?{environments:h.environments}:{},...y?{composed:{plan_digest:y.planDigest,members:y.members,...y.heldByUnknown?.length?{held_by_unknown:[...y.heldByUnknown].sort()}:{},...y.error?{error:y.error}:{}}}:{}}});return{schema:hM,minor:39,run:e.run,change_set:o.digest,unit:s.unit,units:s.units,totals:c,totals_roots:u,groups:f,roots:d,waves:m,named:l,holes:s.holes.map(h=>({root:h.member,address:h.address,...h.type?{type:h.type}:{},reason:h.reason})),redaction:{marker:Ln,values:e.redacted??0},...e.mockReads?.length?{mock_reads:e.mockReads}:{},...e.deferred?.length?{deferred:e.deferred}:{},...e.tips?{tips:e.tips}:{},...e.policy?{policy:e.policy}:{}}}function ioe(e,t){return!t||t.size===0?e:e.map(n=>{let r=t.get(n.address);return r?{...n,record_versions:r}:n})}import{randomBytes as aoe}from"node:crypto";import{createServer as coe}from"node:http";import{gunzipSync as uoe,inflateSync as loe}from"node:zlib";function*Hu(e){let t=0,n=()=>{let o=0n;for(let s=0n;;s+=7n){if(t>=e.length)throw new Error("truncated varint");let i=e[t++];if(o|=BigInt(i&127)<<s,i<128)return o;if(s>63n)throw new Error("varint too long")}},r=o=>{if(t+o>e.length)throw new Error("truncated field");let s=e.subarray(t,t+o);return t+=o,s};for(;t<e.length;){let o=Number(n()),s=o>>>3,i=o&7;if(i===0)yield{no:s,wire:i,int:n()};else if(i===1)yield{no:s,wire:i,bytes:r(8)};else if(i===2)yield{no:s,wire:i,bytes:r(Number(n()))};else if(i===5)yield{no:s,wire:i,bytes:r(4)};else throw new Error(`unsupported wire type ${i}`)}}function doe(e){for(let t of Hu(e)){if(t.no===1&&t.bytes)return t.bytes.toString("utf-8");if(t.no===2&&t.int!==void 0)return t.int!==0n;if(t.no===3&&t.int!==void 0)return Number(BigInt.asIntN(64,t.int));if(t.no===4&&t.bytes)return t.bytes.readDoubleLE(0)}}function poe(e,t){let n,r;for(let o of Hu(e))o.no===1&&o.bytes?n=o.bytes.toString("utf-8"):o.no===2&&o.bytes&&(r=doe(o.bytes));n!==void 0&&r!==void 0&&(t[n]=r)}function foe(e){let t={attributes:{}};for(let n of Hu(e))if(n.bytes){if(n.no===2)t.spanId=n.bytes.toString("hex");else if(n.no===4&&n.bytes.length>0)t.parentSpanId=n.bytes.toString("hex");else if(n.no===5)t.name=n.bytes.toString("utf-8");else if(n.no===7)t.start=n.bytes.readBigUInt64LE(0);else if(n.no===8)t.end=n.bytes.readBigUInt64LE(0);else if(n.no===9)poe(n.bytes,t.attributes);else if(n.no===15)for(let r of Hu(n.bytes))r.no===3&&r.int===2n&&(t.error=!0)}if(!(!t.spanId||t.name===void 0||t.start===void 0||t.end===void 0))return t}function goe(e){let t=[];for(let n of Hu(e))if(!(n.no!==1||!n.bytes)){for(let r of Hu(n.bytes))if(!(r.no!==2||!r.bytes))for(let o of Hu(r.bytes)){if(o.no!==2||!o.bytes)continue;let s=foe(o.bytes);s&&t.push(s)}}return t}var Wu=e=>typeof e=="object"&&e!==null&&!Array.isArray(e),Ib=e=>Array.isArray(e)?e:[];function moe(e){if(Wu(e)){if(typeof e.stringValue=="string")return e.stringValue;if(typeof e.boolValue=="boolean")return e.boolValue;if(typeof e.intValue=="string"||typeof e.intValue=="number")return Number(e.intValue);if(typeof e.doubleValue=="number")return e.doubleValue}}var z1=e=>{try{return typeof e=="string"||typeof e=="number"?BigInt(e):void 0}catch{return}};function hoe(e){let t=[];for(let n of Ib(Wu(e)?e.resourceSpans:void 0))if(Wu(n)){for(let r of Ib(n.scopeSpans))if(Wu(r))for(let o of Ib(r.spans)){if(!Wu(o)||typeof o.spanId!="string"||typeof o.name!="string")continue;let s=z1(o.startTimeUnixNano),i=z1(o.endTimeUnixNano);if(s===void 0||i===void 0)continue;let a={};for(let c of Ib(o.attributes)){if(!Wu(c)||typeof c.key!="string")continue;let u=moe(c.value);u!==void 0&&(a[c.key]=u)}t.push({spanId:o.spanId,...typeof o.parentSpanId=="string"&&o.parentSpanId?{parentSpanId:o.parentSpanId}:{},name:o.name,start:s,end:i,attributes:a,...Wu(o.status)&&(o.status.code===2||o.status.code==="STATUS_CODE_ERROR")?{error:!0}:{}})}}return t}var U1="x-terragucci-run",Nb=class{constructor(t,n=fetch){this.forward=t;this.fetchFn=n}forward;fetchFn;server;url="";runs=new Map;spans=new Map;forwards=[];unreadable=[];async listen(){let t=coe((r,o)=>this.handle(r,o));try{await new Promise((r,o)=>{t.once("error",o),t.listen(0,"127.0.0.1",()=>r())})}catch(r){return r.message}t.unref();let n=t.address();if(!n||typeof n=="string")return"no port";this.server=t,this.url=`http://127.0.0.1:${n.port}/v1/traces`}get listening(){return this.server!==void 0}env(t,n,r){return{...t,...this.exporter(n,r)}}exporter(t,n){if(!this.server)return{};let r=aoe(12).toString("hex");return this.runs.set(r,{root:t,command:n}),{OTEL_TRACES_EXPORTER:"otlp",OTEL_EXPORTER_OTLP_TRACES_ENDPOINT:this.url,OTEL_EXPORTER_OTLP_TRACES_PROTOCOL:"http/protobuf",OTEL_EXPORTER_OTLP_TRACES_HEADERS:`${U1}=${r}`}}spansOf(t,n){return this.spans.get(`${t}\0${n}`)??[]}handle(t,n){let r=[];t.on("data",o=>r.push(o)),t.on("end",()=>{let s=!String(t.headers["content-type"]??"").includes("json"),i=u=>{n.writeHead(u,{"content-type":s?"application/x-protobuf":"application/json"}),n.end(s?"":"{}")},a=this.runs.get(String(t.headers[U1]??""));if(t.method!=="POST"||!a)return i(t.method==="POST"?401:405);let c=Buffer.concat(r);try{let u=String(t.headers["content-encoding"]??"").toLowerCase(),l=u==="gzip"?uoe(c):u==="deflate"?loe(c):c,d=s?goe(l):hoe(JSON.parse(l.toString("utf-8"))),p=`${a.root}\0${a.command}`;this.spans.set(p,[...this.spans.get(p)??[],...d])}catch(u){this.unreadable.push(`${a.root} ${a.command}: ${u.message}`)}i(200),this.forward&&this.forwards.push(this.relay(c,t))})}async relay(t,n){let r=this.forward,o={...r.headers,"content-type":String(n.headers["content-type"]??"application/x-protobuf")};n.headers["content-encoding"]&&(o["content-encoding"]=String(n.headers["content-encoding"]));try{let s=await this.fetchFn(r.url,{method:"POST",headers:o,body:new Uint8Array(t),signal:AbortSignal.timeout(1e4)});return s.ok?void 0:`${r.url} answered ${s.status}`}catch(s){return`${r.url}: ${s.message}`}}async close(){let t=(await Promise.all(this.forwards)).filter(r=>r!==void 0),n=this.server;return this.server=void 0,n&&await new Promise(r=>n.close(()=>r())),t}},W1=10,B1=new Set(["Plan resource instance changes","Apply resource instance changes"]),yoe="terraform.io/builtin/terraform",woe=/^tfplugin[56]\.Provider\/(\w+)$/,Dd=e=>Math.round(Number(e.end-e.start)/1e5)/10,Fn=e=>e===void 0||e===""?void 0:String(e),Md=e=>typeof e=="number"?e:typeof e=="string"&&e!==""&&!Number.isNaN(Number(e))?Number(e):void 0,Ld=(e,t=W1)=>[...e].sort((n,r)=>r.ms-n.ms).slice(0,t),Cb=e=>Object.fromEntries(Object.entries(e).filter(([,t])=>t!==void 0));function OE(e,t){let n=new Map(e.map(m=>[m.spanId,m])),r=new Map;for(let m of e)m.name==="Refresh resource instance"&&m.parentSpanId&&r.set(m.parentSpanId,(r.get(m.parentSpanId)??0)+Dd(m));let o=e.filter(m=>B1.has(m.name)).map(m=>Cb({address:Fn(m.attributes["opentofu.resource_instance.address"])??"(no address)",type:Fn(m.attributes["opentofu.resource.type"]),action:Fn(m.attributes["opentofu.resource_instance.action"]),provider:Fn(m.attributes["opentofu.provider_instance.address"]),ms:Dd(m),refresh_ms:r.get(m.spanId)})),s=[];for(let m of e){let h=woe.exec(m.name);if(!h)continue;let y=m.parentSpanId?n.get(m.parentSpanId):void 0;s.push(Cb({method:Fn(m.attributes["rpc.method"])??h[1],provider:Fn(m.attributes["opentofu.provider.address"]),type:Fn(m.attributes["opentofu.resource.type"]),address:y&&B1.has(y.name)?Fn(y.attributes["opentofu.resource_instance.address"]):void 0,ms:Dd(m)}))}let i=e.filter(m=>m.name==="Start provider"),a=i.length>0?i:e.filter(m=>m.name==="Configure provider"&&Fn(m.attributes["opentofu.provider.source"])!==yoe),c=new Map;for(let m of a){let h=Fn(m.attributes["opentofu.provider.address"])??Fn(m.attributes["opentofu.provider.source"])??"(unnamed)",y=c.get(h)??{provider:h,count:0,ms:0,max_ms:0};y.count++,y.ms=Math.round((y.ms+Dd(m))*10)/10,y.max_ms=Math.max(y.max_ms,Dd(m)),c.set(h,y)}let u=e.filter(m=>m.name==="State lock wait").map(m=>Cb({backend:Fn(m.attributes["opentofu.state.backend"]),operation:Fn(m.attributes["opentofu.state.lock.operation"]),attempts:Md(m.attributes["opentofu.state.lock.attempts"]),ms:Dd(m)})),l=e.filter(m=>m.name.startsWith("Aggregate: ")).map(m=>{let h=m.attributes;return Cb({of:m.name.slice(11),kind:Fn(h["choudoufu.aggregate.kind"]),type:Fn(h["opentofu.resource.type"]),provider:Fn(h["opentofu.provider.address"]),method:Fn(h["rpc.method"]),count:Md(h["choudoufu.aggregate.count"])??0,detailed:Md(h["choudoufu.aggregate.detailed_count"])??0,ms:Md(h["choudoufu.aggregate.duration_total_ms"])??0,max_ms:Md(h["choudoufu.aggregate.duration_max_ms"])??0,slowest:Fn(h["choudoufu.aggregate.slowest"]),groups:Md(h["choudoufu.aggregate.groups"])})}),d=l.filter(m=>m.kind!=="provider_call").reduce((m,h)=>m+Math.max(0,h.count-h.detailed),0),p=o.length>0?"resources":l.length>0?"aggregate":"none",f=t.applySeconds!==void 0?"plan and apply":"plan",g;return t.source==="terragrunt"&&e.length===0?g=`Terragrunt ran the binary for this unit, so its time is from Terragrunt's run report, and ${t.binary} sent no spans for its plan, so it has no per-resource timings`:t.source==="terragrunt"&&p==="none"?g=`Terragrunt ran the binary for this unit, so its time is from Terragrunt's run report; ${t.binary} sent ${e.length} span${e.length===1?"":"s"} for its plan, none of them per resource`:t.planSeconds===void 0?g="the root did not reach a plan, so no spans were read":e.length===0?g=`${t.binary} sent no spans for the ${f}, so this root has no per-resource timings. A binary that exports per-resource OpenTelemetry spans, such as choudoufu, gives them`:p==="none"?g=`${t.binary} sent ${e.length} span${e.length===1?"":"s"} for the ${f}, none of them per resource, so this root has no per-resource timings`:p==="aggregate"?g=`${t.binary} summed its resources by type instead of a span each, so the slowest are listed by type`:d>0&&(g=`past the span budget, ${d} resource instance${d===1?" is":"s are"} summed by type instead of listed`),{seconds:Math.round(t.seconds*100)/100,...t.planSeconds!==void 0?{plan_seconds:Math.round(t.planSeconds*100)/100}:{},...t.applySeconds!==void 0?{apply_seconds:Math.round(t.applySeconds*100)/100}:{},...t.source==="terragrunt"?{source:"terragrunt"}:{},spans:e.length,detail:p,...g?{note:g}:{},resources:Ld(o),provider_calls:Ld(s),provider_init:Ld([...c.values()]),lock_waits:Ld(u),aggregates:Ld(l,2*W1)}}function H1(e,t){let n=e.filter(r=>r.timings!==void 0);return{roots:[...n].sort((r,o)=>o.timings.seconds-r.timings.seconds).map(r=>({root:r.path,seconds:r.timings.seconds,...r.timings.plan_seconds!==void 0?{plan_seconds:r.timings.plan_seconds}:{},...r.timings.apply_seconds!==void 0?{apply_seconds:r.timings.apply_seconds}:{},...r.timings.source?{source:r.timings.source}:{},detail:r.timings.detail})),resources:Ld(n.flatMap(r=>r.timings.resources.map(o=>({root:r.path,address:o.address,...o.type?{type:o.type}:{},ms:o.ms})))),...t?{note:t}:{}}}function ho(e){if(e<1e3)return`${Math.round(e)}ms`;let t=e/1e3;if(t<59.95)return`${t.toFixed(1)}s`;let n=Math.round(t);return`${Math.floor(n/60)}m${String(n%60).padStart(2,"0")}s`}var boe="(sensitive value)",voe=/^(?:Note: )?Objects have changed outside of /,$oe=/will perform the following actions:\s*$/,koe=/^(?:Plan: |Changes to Outputs:|No changes\.|Warning:|Error:|─|Unless you have made equivalent changes|This is a refresh-only plan|You can apply this plan|Note: You didn't use)/;function _oe(e){let t=[],n,r,o=()=>{if(r){for(;r.lines.length>0&&r.lines[r.lines.length-1].trim()==="";)r.lines.pop();t.push(r),r=void 0}};for(let s of e.replace(/\r\n/g,`
|
|
184
|
+
`}var Cg="terragucci.inventory/v1",Qre=(e,t)=>e.address<t.address?-1:e.address>t.address?1:0;function CL(e){let t=[],n=o=>{if(Ae(o)){for(let s of Array.isArray(o.resources)?o.resources:[])!Ae(s)||s.mode!=="managed"||typeof s.address!="string"||typeof s.type!="string"||t.push({address:s.address,type:s.type,provider:typeof s.provider_name=="string"?s.provider_name:""});for(let s of Array.isArray(o.child_modules)?o.child_modules:[])n(s)}},r=Ae(e)?e.planned_values:void 0;return n(Ae(r)?r.root_module:void 0),t.sort(Qre)}function NL(e,t){return e.run.stage!=="tf-apply"?[]:e.roots.filter(n=>n.resources!==void 0).map(n=>({root:n.path,commit:e.run.commit,finished:e.run.finished,...e.run.wave!==void 0?{wave:e.run.wave}:{},path:t,...n.environment?{environment:n.environment}:{},resources:n.resources}))}function eoe(e){if(e)try{let t=JSON.parse(e);if(Array.isArray(t.roots))return{schema:Cg,roots:t.roots}}catch{}return{schema:Cg,roots:[]}}function DL(e,t){let n=new Map(eoe(e).roots.map(r=>[r.root,r]));for(let r of t){let o=n.get(r.root);(!o||!(Date.parse(o.finished)>Date.parse(r.finished)))&&n.set(r.root,r)}return{schema:Cg,roots:[...n.values()].sort((r,o)=>r.root<o.root?-1:r.root>o.root?1:0)}}function ML(e){let t=new Map;for(let n of e)t.set(n.type,(t.get(n.type)??0)+1);return[...t].map(([n,r])=>({type:n,count:r})).sort((n,r)=>r.count-n.count||(n.type<r.type?-1:1))}function jb(e,t){return t?{...e,environment:t}:e}function oa(e){return{text:`roots/${e}/plan.txt`,json:`roots/${e}/plan.json`}}var bn=e=>`root-${e}`,Ng=e=>`group-${e}`;function toe(e){let t=new Map,n=Ae(e)&&Array.isArray(e.resource_changes)?e.resource_changes:[];for(let r of n){if(!Ae(r))continue;let o=Ae(r.change)?r.change:{},s=Array.isArray(o.replace_paths)?o.replace_paths.filter(Array.isArray):[],i=Ae(o.importing)?String(o.importing.id??o.importing.identity??""):void 0;t.set(`${r.address}\0${r.deposed??""}`,{...typeof r.mode=="string"?{mode:r.mode}:{},...s.length>0?{replace_paths:s}:{},...i!==void 0?{importing:i}:{}})}return t}function noe(e){return{member:{member:e.path,lexicon:"terraform",planner:e.planner??"terraform",status:"failed",error:e.error??"the root did not plan",planDigest:null,holes:[]},entries:[]}}function roe(e,t,n){let r=e.attributes.map(c=>c.path).filter(c=>c.endsWith("_wo_version")),o={address:e.address,type:e.type,action:e.action,...e.disruption?{disruption:e.disruption}:{},...e.deposed!==void 0?{deposed:e.deposed}:{},...e.module?{module:e.module}:{},...e.index!==void 0?{index:e.index}:{},attributes:e.attributes,...t?.replace_paths?{replace_paths:t.replace_paths}:{},...t?.importing!==void 0?{importing:t.importing}:{},...r.length>0?{write_only:r}:{}},s=EL(e.action,e.attributes),{fold:i,why:a}=TL({...o,kind:s},n.has(`${e.type}.${e.name??""}`));return{...o,...s?{kind:s}:{},fold:i,...a?{why:a}:{}}}function ooe(e){if(e.length<2)return[];let t=new Map;for(let r of e)for(let o of r.changes){let s=t.get(o.address)??new Map;t.set(o.address,s);for(let i of o.attributes){let a=s.get(i.path)??new Set;s.set(i.path,a),a.add(JSON.stringify(i.sensitive?Ln:i.unknown?"(known after apply)":i.after??i.before??null))}}let n=[];for(let[r,o]of[...t].sort(([s],[i])=>s<i?-1:1)){let s=[...o].filter(([,i])=>i.size>1).map(([i])=>i).sort();s.length>0&&n.push({address:r,paths:s})}return n}var Nd=e=>e.plan!==void 0&&e.error!==void 0&&e.policy!==void 0&&e.policy.result!=="passed";function LL(e){let t=fo({member:e.path,plan:e.plan,planner:e.planner??"terraform",...e.terragrunt?{scope:e.terragrunt.stack}:{}}),n=t.member.planDigest,r=e.cone?.record&&n?{...t,member:{...t.member,planDigest:kb(n,e.cone)}}:t;return Nd(e)?{...r,member:{...r.member,status:"failed",error:e.error,planDigest:null}}:r}var soe=new Set(["create","update","replace","delete","forget"]);function GL(e,t,n){let r={...e},o=new Set;for(let s of t)n(s.member)?r[s.action]>0&&r[s.action]--:soe.has(s.action)&&o.add(s.member);return{totals:r,roots:o.size}}function FL(e){let t=e.filter(o=>o.plan!==void 0&&(o.error===void 0||Nd(o))).map(LL),n=nS(t),r=new Map(e.map(o=>[o.path,o]));return GL(n.summary.actions,n.entries,o=>Nd(r.get(o)))}function Bu(e){let t=e.run,n=new Map(e.roots.map(h=>[h.path,h])),r=e.roots.map(h=>h.plan!==void 0&&(h.error===void 0||Nd(h))?LL(h):noe(h)).map((h,y)=>e.roots[y].terragrunt?.provisional?{...h,member:{...h.member,provisional:!0}}:h),o=nS(r),s=wL(o),i=new Map;if(s.unit==="member")for(let h of s.groups)for(let y of h.units)i.set(y,h.id);let a=new Set(s.groups.filter(h=>h.outlier).flatMap(h=>h.units)),{totals:c,roots:u}=GL(o.summary.actions,o.entries,h=>Nd(n.get(h))),l=[],d=o.members.map(h=>{let y=n.get(h.member),w=toe(y.plan),b=y.plan!==void 0?bM(y.plan):[],k={},v=[];for(let x of o.entries){if(x.member!==h.member)continue;let A=w.get(`${x.address}\0${x.deposed??""}`);if(A?.mode==="ephemeral"||x.address.startsWith("ephemeral.")||(k[x.action]=(k[x.action]??0)+1,x.action==="no-op"&&A?.importing===void 0))continue;let O=roe({...x,attributes:vM(x.attributes,b)},A,y.preventDestroy??new Set);v.push(O);let I=O.action==="delete"||O.action==="replace"||O.action==="forget"?O.action:O.importing!==void 0?"import":void 0;I!==void 0&&l.push({root:h.member,address:O.address,type:O.type,action:I,...O.deposed!==void 0?{deposed:O.deposed}:{},...O.replace_paths?{replace_paths:O.replace_paths}:{}})}h.status==="failed"&&l.push({root:h.member,action:"refused",reason:h.error??"the root did not plan"});let $=v.filter(x=>x.why!==void 0).map(x=>({address:x.address,type:x.type,action:x.action,why:x.why})),_=[];h.status==="failed"&&_.push(Nd(y)?"refused by policy":"refused to plan"),y.policy?.override&&_.push(`policy overridden by ${y.policy.override.by}`);let E=(y.steps??[]).filter(x=>x.status==="approval").map(x=>x.name);E.length&&_.push(`step ${E.join(", ")} asks for an approval`),y.policy?.warnings.length&&_.push(`${y.policy.warnings.length} policy warning${y.policy.warnings.length===1?"":"s"}`),t.stage==="tf-drift"&&v.length>0&&_.push("drifted"),a.has(h.member)&&_.push("outlier: its change matches no other root's"),$.length>0&&_.push(...new Set($.map(x=>x.why)));let R=i.get(h.member);return{path:h.member,...y.environment?{environment:y.environment}:{},...y.binary?{binary:y.binary}:{},...y.terragrunt?{terragrunt:y.terragrunt}:{},status:h.status,...h.error?{error:h.error}:{},plan_digest:h.planDigest,counts:k,...R?{group:R}:{},plan:y.files??(h.status==="planned"?oa(h.member):{}),...y.job_url??t.job_url?{job_url:y.job_url??t.job_url}:{},changes:v,highlights:$,fold:_.length>0?"open":"folded",why:_,...y.policy?{policy:y.policy}:{},...y.applied&&h.status==="planned"&&y.plan!==void 0?{resources:CL(y.plan),applied_changes:ioe(Ob(y.plan),y.recordVersions)}:{},...y.applied&&y.state?{state:y.state}:{},...y.steps?.length?{steps:y.steps}:{},...y.reads?.length?{reads:y.reads}:{},...y.unknownReads?.length?{unknown_reads:y.unknownReads}:{},...y.unaddressed?{unaddressed:y.unaddressed}:{},...y.dependencies?.length?{dependencies:y.dependencies}:{},...y.cone?{cone:_L(y.cone)}:{},...y.composed?{composed:y.composed}:{}}}),p=new Map(d.map(h=>[h.path,h])),f=s.groups.map(h=>{let y=s.unit==="member"?h.units.map(v=>p.get(v)).filter(Boolean):[],w=h.destroys.length,b=[];h.outlier&&b.push("outlier: read it on its own"),w>0&&b.push(`${w} ${w===1?"destroy or replacement":"destroys or replacements"}`);let k=[...new Set(y.flatMap(v=>v.highlights.filter($=>$.action!=="delete"&&$.action!=="replace").map($=>$.why)))];return b.push(...k),t.stage==="tf-drift"&&!h.noChanges&&b.push("drifted"),{id:h.id,...h.resource?{resource:h.resource}:{},units:h.units,outlier:h.outlier,noChanges:h.noChanges,changes:h.changes,...h.extends?{extends:h.extends}:{},...h.plus?{plus:h.plus}:{},destroys:w,varies:ooe(y),fold:b.length>0?"open":"folded",why:b}}),g={refused:0,delete:1,replace:2,forget:3,import:4};l.sort((h,y)=>g[h.action]-g[y.action]||(h.root<y.root?-1:h.root>y.root?1:0)||((h.address??"")<(y.address??"")?-1:1));let m=(e.waves??[]).map(h=>{let y=h.composed,w=new Set(y?.members.map(R=>R.root)??[]),b=o.members.filter(R=>h.roots.includes(R.member)&&!R.provisional&&!w.has(R.member)),k=y?.planDigest?[{member:y.target,planDigest:y.planDigest}]:[],v=b.some(R=>R.planDigest===null)||y!==void 0&&!y.planDigest,$=Cd(h.cost),_=$?[$]:[],E={};if(e.gate){let R=new Map(e.roots.map(L=>[L.path,L.plan])),x=[...b.filter(L=>Vn(R.get(L.member))),...Vn(y?.plan)?k:[]],A=x.some(L=>_b(R.get(L.member)))||y!==void 0&&_b(y.plan),O=(y?.heldByUnknown?.length??0)>0,I=h.policy??e.gate,N=(h.replansAfter?.length??0)>0;E={review_digest:v||x.length===0||N?null:Wa([...x,..._]),waits:N&&I!=="never"||x.length>0&&(I==="always"||I==="on-destructive"&&A||h.cost?.over===!0||(h.heldBySteps?.length??0)>0||O&&I!=="never")}}return{number:h.number,roots:h.roots,set_digest:h.setDigest??(v||b.length+k.length===0?null:Wa([...b,...k,..._])),approval:h.approval??"not-requested",...E,...h.gate?{gate:h.gate}:{},...h.waitingSince&&h.approval==="waiting"?{waiting_since:h.waitingSince}:{},...h.refused?{refused:h.refused}:{},...h.review&&h.approval==="waiting"?{review:h.review}:{},...h.excluded?.length?{excluded_approvers:h.excluded.map(R=>({login:R.login,reason:R.reason}))}:{},...h.heldBySteps?.length?{held_by_steps:[...h.heldBySteps].sort()}:{},...h.cost?{cost:h.cost}:{},...h.state?{state:h.state}:{},...h.reads?.length?{reads:h.reads}:{},...h.replansAfter?.length?{replans_after:h.replansAfter}:{},...h.preview?{preview:h.preview}:{},...h.environments?.length?{environments:h.environments}:{},...y?{composed:{plan_digest:y.planDigest,members:y.members,...y.heldByUnknown?.length?{held_by_unknown:[...y.heldByUnknown].sort()}:{},...y.error?{error:y.error}:{}}}:{}}});return{schema:hM,minor:39,run:e.run,change_set:o.digest,unit:s.unit,units:s.units,totals:c,totals_roots:u,groups:f,roots:d,waves:m,named:l,holes:s.holes.map(h=>({root:h.member,address:h.address,...h.type?{type:h.type}:{},reason:h.reason})),redaction:{marker:Ln,values:e.redacted??0},...e.mockReads?.length?{mock_reads:e.mockReads}:{},...e.deferred?.length?{deferred:e.deferred}:{},...e.tips?{tips:e.tips}:{},...e.policy?{policy:e.policy}:{}}}function ioe(e,t){return!t||t.size===0?e:e.map(n=>{let r=t.get(n.address);return r?{...n,record_versions:r}:n})}import{randomBytes as aoe}from"node:crypto";import{createServer as coe}from"node:http";import{gunzipSync as uoe,inflateSync as loe}from"node:zlib";function*Hu(e){let t=0,n=()=>{let o=0n;for(let s=0n;;s+=7n){if(t>=e.length)throw new Error("truncated varint");let i=e[t++];if(o|=BigInt(i&127)<<s,i<128)return o;if(s>63n)throw new Error("varint too long")}},r=o=>{if(t+o>e.length)throw new Error("truncated field");let s=e.subarray(t,t+o);return t+=o,s};for(;t<e.length;){let o=Number(n()),s=o>>>3,i=o&7;if(i===0)yield{no:s,wire:i,int:n()};else if(i===1)yield{no:s,wire:i,bytes:r(8)};else if(i===2)yield{no:s,wire:i,bytes:r(Number(n()))};else if(i===5)yield{no:s,wire:i,bytes:r(4)};else throw new Error(`unsupported wire type ${i}`)}}function doe(e){for(let t of Hu(e)){if(t.no===1&&t.bytes)return t.bytes.toString("utf-8");if(t.no===2&&t.int!==void 0)return t.int!==0n;if(t.no===3&&t.int!==void 0)return Number(BigInt.asIntN(64,t.int));if(t.no===4&&t.bytes)return t.bytes.readDoubleLE(0)}}function poe(e,t){let n,r;for(let o of Hu(e))o.no===1&&o.bytes?n=o.bytes.toString("utf-8"):o.no===2&&o.bytes&&(r=doe(o.bytes));n!==void 0&&r!==void 0&&(t[n]=r)}function foe(e){let t={attributes:{}};for(let n of Hu(e))if(n.bytes){if(n.no===2)t.spanId=n.bytes.toString("hex");else if(n.no===4&&n.bytes.length>0)t.parentSpanId=n.bytes.toString("hex");else if(n.no===5)t.name=n.bytes.toString("utf-8");else if(n.no===7)t.start=n.bytes.readBigUInt64LE(0);else if(n.no===8)t.end=n.bytes.readBigUInt64LE(0);else if(n.no===9)poe(n.bytes,t.attributes);else if(n.no===15)for(let r of Hu(n.bytes))r.no===3&&r.int===2n&&(t.error=!0)}if(!(!t.spanId||t.name===void 0||t.start===void 0||t.end===void 0))return t}function goe(e){let t=[];for(let n of Hu(e))if(!(n.no!==1||!n.bytes)){for(let r of Hu(n.bytes))if(!(r.no!==2||!r.bytes))for(let o of Hu(r.bytes)){if(o.no!==2||!o.bytes)continue;let s=foe(o.bytes);s&&t.push(s)}}return t}var Wu=e=>typeof e=="object"&&e!==null&&!Array.isArray(e),Ib=e=>Array.isArray(e)?e:[];function moe(e){if(Wu(e)){if(typeof e.stringValue=="string")return e.stringValue;if(typeof e.boolValue=="boolean")return e.boolValue;if(typeof e.intValue=="string"||typeof e.intValue=="number")return Number(e.intValue);if(typeof e.doubleValue=="number")return e.doubleValue}}var zL=e=>{try{return typeof e=="string"||typeof e=="number"?BigInt(e):void 0}catch{return}};function hoe(e){let t=[];for(let n of Ib(Wu(e)?e.resourceSpans:void 0))if(Wu(n)){for(let r of Ib(n.scopeSpans))if(Wu(r))for(let o of Ib(r.spans)){if(!Wu(o)||typeof o.spanId!="string"||typeof o.name!="string")continue;let s=zL(o.startTimeUnixNano),i=zL(o.endTimeUnixNano);if(s===void 0||i===void 0)continue;let a={};for(let c of Ib(o.attributes)){if(!Wu(c)||typeof c.key!="string")continue;let u=moe(c.value);u!==void 0&&(a[c.key]=u)}t.push({spanId:o.spanId,...typeof o.parentSpanId=="string"&&o.parentSpanId?{parentSpanId:o.parentSpanId}:{},name:o.name,start:s,end:i,attributes:a,...Wu(o.status)&&(o.status.code===2||o.status.code==="STATUS_CODE_ERROR")?{error:!0}:{}})}}return t}var UL="x-terragucci-run",Nb=class{constructor(t,n=fetch){this.forward=t;this.fetchFn=n}forward;fetchFn;server;url="";runs=new Map;spans=new Map;forwards=[];unreadable=[];async listen(){let t=coe((r,o)=>this.handle(r,o));try{await new Promise((r,o)=>{t.once("error",o),t.listen(0,"127.0.0.1",()=>r())})}catch(r){return r.message}t.unref();let n=t.address();if(!n||typeof n=="string")return"no port";this.server=t,this.url=`http://127.0.0.1:${n.port}/v1/traces`}get listening(){return this.server!==void 0}env(t,n,r){return{...t,...this.exporter(n,r)}}exporter(t,n){if(!this.server)return{};let r=aoe(12).toString("hex");return this.runs.set(r,{root:t,command:n}),{OTEL_TRACES_EXPORTER:"otlp",OTEL_EXPORTER_OTLP_TRACES_ENDPOINT:this.url,OTEL_EXPORTER_OTLP_TRACES_PROTOCOL:"http/protobuf",OTEL_EXPORTER_OTLP_TRACES_HEADERS:`${UL}=${r}`}}spansOf(t,n){return this.spans.get(`${t}\0${n}`)??[]}handle(t,n){let r=[];t.on("data",o=>r.push(o)),t.on("end",()=>{let s=!String(t.headers["content-type"]??"").includes("json"),i=u=>{n.writeHead(u,{"content-type":s?"application/x-protobuf":"application/json"}),n.end(s?"":"{}")},a=this.runs.get(String(t.headers[UL]??""));if(t.method!=="POST"||!a)return i(t.method==="POST"?401:405);let c=Buffer.concat(r);try{let u=String(t.headers["content-encoding"]??"").toLowerCase(),l=u==="gzip"?uoe(c):u==="deflate"?loe(c):c,d=s?goe(l):hoe(JSON.parse(l.toString("utf-8"))),p=`${a.root}\0${a.command}`;this.spans.set(p,[...this.spans.get(p)??[],...d])}catch(u){this.unreadable.push(`${a.root} ${a.command}: ${u.message}`)}i(200),this.forward&&this.forwards.push(this.relay(c,t))})}async relay(t,n){let r=this.forward,o={...r.headers,"content-type":String(n.headers["content-type"]??"application/x-protobuf")};n.headers["content-encoding"]&&(o["content-encoding"]=String(n.headers["content-encoding"]));try{let s=await this.fetchFn(r.url,{method:"POST",headers:o,body:new Uint8Array(t),signal:AbortSignal.timeout(1e4)});return s.ok?void 0:`${r.url} answered ${s.status}`}catch(s){return`${r.url}: ${s.message}`}}async close(){let t=(await Promise.all(this.forwards)).filter(r=>r!==void 0),n=this.server;return this.server=void 0,n&&await new Promise(r=>n.close(()=>r())),t}},WL=10,BL=new Set(["Plan resource instance changes","Apply resource instance changes"]),yoe="terraform.io/builtin/terraform",woe=/^tfplugin[56]\.Provider\/(\w+)$/,Dd=e=>Math.round(Number(e.end-e.start)/1e5)/10,Fn=e=>e===void 0||e===""?void 0:String(e),Md=e=>typeof e=="number"?e:typeof e=="string"&&e!==""&&!Number.isNaN(Number(e))?Number(e):void 0,Ld=(e,t=WL)=>[...e].sort((n,r)=>r.ms-n.ms).slice(0,t),Cb=e=>Object.fromEntries(Object.entries(e).filter(([,t])=>t!==void 0));function OE(e,t){let n=new Map(e.map(m=>[m.spanId,m])),r=new Map;for(let m of e)m.name==="Refresh resource instance"&&m.parentSpanId&&r.set(m.parentSpanId,(r.get(m.parentSpanId)??0)+Dd(m));let o=e.filter(m=>BL.has(m.name)).map(m=>Cb({address:Fn(m.attributes["opentofu.resource_instance.address"])??"(no address)",type:Fn(m.attributes["opentofu.resource.type"]),action:Fn(m.attributes["opentofu.resource_instance.action"]),provider:Fn(m.attributes["opentofu.provider_instance.address"]),ms:Dd(m),refresh_ms:r.get(m.spanId)})),s=[];for(let m of e){let h=woe.exec(m.name);if(!h)continue;let y=m.parentSpanId?n.get(m.parentSpanId):void 0;s.push(Cb({method:Fn(m.attributes["rpc.method"])??h[1],provider:Fn(m.attributes["opentofu.provider.address"]),type:Fn(m.attributes["opentofu.resource.type"]),address:y&&BL.has(y.name)?Fn(y.attributes["opentofu.resource_instance.address"]):void 0,ms:Dd(m)}))}let i=e.filter(m=>m.name==="Start provider"),a=i.length>0?i:e.filter(m=>m.name==="Configure provider"&&Fn(m.attributes["opentofu.provider.source"])!==yoe),c=new Map;for(let m of a){let h=Fn(m.attributes["opentofu.provider.address"])??Fn(m.attributes["opentofu.provider.source"])??"(unnamed)",y=c.get(h)??{provider:h,count:0,ms:0,max_ms:0};y.count++,y.ms=Math.round((y.ms+Dd(m))*10)/10,y.max_ms=Math.max(y.max_ms,Dd(m)),c.set(h,y)}let u=e.filter(m=>m.name==="State lock wait").map(m=>Cb({backend:Fn(m.attributes["opentofu.state.backend"]),operation:Fn(m.attributes["opentofu.state.lock.operation"]),attempts:Md(m.attributes["opentofu.state.lock.attempts"]),ms:Dd(m)})),l=e.filter(m=>m.name.startsWith("Aggregate: ")).map(m=>{let h=m.attributes;return Cb({of:m.name.slice(11),kind:Fn(h["choudoufu.aggregate.kind"]),type:Fn(h["opentofu.resource.type"]),provider:Fn(h["opentofu.provider.address"]),method:Fn(h["rpc.method"]),count:Md(h["choudoufu.aggregate.count"])??0,detailed:Md(h["choudoufu.aggregate.detailed_count"])??0,ms:Md(h["choudoufu.aggregate.duration_total_ms"])??0,max_ms:Md(h["choudoufu.aggregate.duration_max_ms"])??0,slowest:Fn(h["choudoufu.aggregate.slowest"]),groups:Md(h["choudoufu.aggregate.groups"])})}),d=l.filter(m=>m.kind!=="provider_call").reduce((m,h)=>m+Math.max(0,h.count-h.detailed),0),p=o.length>0?"resources":l.length>0?"aggregate":"none",f=t.applySeconds!==void 0?"plan and apply":"plan",g;return t.source==="terragrunt"&&e.length===0?g=`Terragrunt ran the binary for this unit, so its time is from Terragrunt's run report, and ${t.binary} sent no spans for its plan, so it has no per-resource timings`:t.source==="terragrunt"&&p==="none"?g=`Terragrunt ran the binary for this unit, so its time is from Terragrunt's run report; ${t.binary} sent ${e.length} span${e.length===1?"":"s"} for its plan, none of them per resource`:t.planSeconds===void 0?g="the root did not reach a plan, so no spans were read":e.length===0?g=`${t.binary} sent no spans for the ${f}, so this root has no per-resource timings. A binary that exports per-resource OpenTelemetry spans, such as choudoufu, gives them`:p==="none"?g=`${t.binary} sent ${e.length} span${e.length===1?"":"s"} for the ${f}, none of them per resource, so this root has no per-resource timings`:p==="aggregate"?g=`${t.binary} summed its resources by type instead of a span each, so the slowest are listed by type`:d>0&&(g=`past the span budget, ${d} resource instance${d===1?" is":"s are"} summed by type instead of listed`),{seconds:Math.round(t.seconds*100)/100,...t.planSeconds!==void 0?{plan_seconds:Math.round(t.planSeconds*100)/100}:{},...t.applySeconds!==void 0?{apply_seconds:Math.round(t.applySeconds*100)/100}:{},...t.source==="terragrunt"?{source:"terragrunt"}:{},spans:e.length,detail:p,...g?{note:g}:{},resources:Ld(o),provider_calls:Ld(s),provider_init:Ld([...c.values()]),lock_waits:Ld(u),aggregates:Ld(l,2*WL)}}function HL(e,t){let n=e.filter(r=>r.timings!==void 0);return{roots:[...n].sort((r,o)=>o.timings.seconds-r.timings.seconds).map(r=>({root:r.path,seconds:r.timings.seconds,...r.timings.plan_seconds!==void 0?{plan_seconds:r.timings.plan_seconds}:{},...r.timings.apply_seconds!==void 0?{apply_seconds:r.timings.apply_seconds}:{},...r.timings.source?{source:r.timings.source}:{},detail:r.timings.detail})),resources:Ld(n.flatMap(r=>r.timings.resources.map(o=>({root:r.path,address:o.address,...o.type?{type:o.type}:{},ms:o.ms})))),...t?{note:t}:{}}}function ho(e){if(e<1e3)return`${Math.round(e)}ms`;let t=e/1e3;if(t<59.95)return`${t.toFixed(1)}s`;let n=Math.round(t);return`${Math.floor(n/60)}m${String(n%60).padStart(2,"0")}s`}var boe="(sensitive value)",voe=/^(?:Note: )?Objects have changed outside of /,$oe=/will perform the following actions:\s*$/,koe=/^(?:Plan: |Changes to Outputs:|No changes\.|Warning:|Error:|─|Unless you have made equivalent changes|This is a refresh-only plan|You can apply this plan|Note: You didn't use)/;function _oe(e){let t=[],n,r,o=()=>{if(r){for(;r.lines.length>0&&r.lines[r.lines.length-1].trim()==="";)r.lines.pop();t.push(r),r=void 0}};for(let s of e.replace(/\r\n/g,`
|
|
185
185
|
`).split(`
|
|
186
186
|
`)){if(voe.test(s)){o(),n="drift";continue}if($oe.test(s)){o(),n="actions";continue}if(n){if(koe.test(s)){o(),/^(?:Plan: |Changes to Outputs:|No changes\.)/.test(s)&&(n=void 0);continue}if(s.startsWith(" # ")){o(),r={header:s.slice(4),lines:[s],section:n};continue}r&&r.lines.push(s)}}return o(),t}function Soe(e,t,n){if(!e.header.startsWith(`${t} `))return!1;let r=e.header.slice(t.length+1);return n!==void 0?r.startsWith(`(deposed object ${n})`):!r.startsWith("(deposed object")}function IE(e,t){let n=_oe(e),r=n.filter(s=>s.section==="actions"),o=r.length>0?r:n;return t===void 0?o:o.filter(s=>Soe(s,t))}function CE(e){return e.map(t=>t.replace(/^(\s+)(-\/\+|\+\/-|[-+~])(?= )/,"$2$1"))}function NE(e){let t=e.join(`
|
|
187
187
|
`),n=Math.max(0,...[...t.matchAll(/`+/g)].map(o=>o[0].length)),r="`".repeat(Math.max(3,n+1));return`${r}diff
|
|
188
188
|
${t}
|
|
189
189
|
${r}
|
|
190
|
-
`}function
|
|
191
|
-
`))i.trim().length>=8&&s.add(i.trim());for(let i of s)r=r.replace(new RegExp(xoe(i),"g"),()=>(n++,boe))}return{text:r,values:n}}function Db(e,t,n=!1){return`terragucci approve wave-${e} --plan ${t}${n?" --sign":""}`}var ME="terragucci:waves";function LE(e){return`<!-- ${ME} ${JSON.stringify(e).replace(/</g,"\\u003c").replace(/>/g,"\\u003e")} -->`}function Mb(e){if(typeof e!="string")return;let t=new RegExp(`<!-- ${ME} (\\{.*?\\}) -->`).exec(e);if(t)try{let n=JSON.parse(t[1]);return typeof n.head!="string"||!Array.isArray(n.waves)?void 0:n}catch{return}}import{createHash as Eoe}from"node:crypto";var
|
|
192
|
-
`);return`sha256:${Eoe("sha256").update(r).digest("hex")}`}var ti=e=>new Date(e).getTime();function GE(e){let t=new Map;for(let n of e){if(n.op!==vs||!n.planDigest)continue;let r=t.get(n.gate);(!r||ti(n.timestamp)>=ti(r.timestamp))&&t.set(n.gate,n)}return[...t.values()].map(n=>{let r=n;return{...n,plan:typeof r.plan=="string"?r.plan:"",rules:Array.isArray(r.rules)?sa(r.rules.filter(o=>typeof o=="string")):[]}}).sort((n,r)=>n.gate<r.gate?-1:n.gate>r.gate?1:0)}var
|
|
190
|
+
`}function qL(e){return/^Plan: .*$/m.exec(e)?.[0]??/^No changes\..*$/m.exec(e)?.[0]??(/^Changes to Outputs:/m.test(e)?"Changes to Outputs only.":void 0)}function PE(e,t,n){if(t===Ln&&e!==Ln){jE(e,n);return}if(Array.isArray(e)&&Array.isArray(t))e.forEach((r,o)=>PE(r,t[o],n));else if(Ae(e)&&Ae(t))for(let r of Object.keys(e))PE(e[r],t[r],n)}function jE(e,t){typeof e=="string"?t.add(e):typeof e=="number"&&String(e).length>=6?t.add(String(e)):Array.isArray(e)?e.forEach(n=>jE(n,t)):Ae(e)&&Object.values(e).forEach(n=>jE(n,t))}function Roe(e){let t=new Set;return PE(e,ys(e).plan,t),[...t].filter(n=>n!=="")}var xoe=e=>e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");function DE(e,t){let n=0,r=e;for(let o of Roe(t).sort((s,i)=>i.length-s.length)){let s=new Set([JSON.stringify(o)]);for(let i of o.split(`
|
|
191
|
+
`))i.trim().length>=8&&s.add(i.trim());for(let i of s)r=r.replace(new RegExp(xoe(i),"g"),()=>(n++,boe))}return{text:r,values:n}}function Db(e,t,n=!1){return`terragucci approve wave-${e} --plan ${t}${n?" --sign":""}`}var ME="terragucci:waves";function LE(e){return`<!-- ${ME} ${JSON.stringify(e).replace(/</g,"\\u003c").replace(/>/g,"\\u003e")} -->`}function Mb(e){if(typeof e!="string")return;let t=new RegExp(`<!-- ${ME} (\\{.*?\\}) -->`).exec(e);if(t)try{let n=JSON.parse(t[1]);return typeof n.head!="string"||!Array.isArray(n.waves)?void 0:n}catch{return}}import{createHash as Eoe}from"node:crypto";var JL="policy-override";var vs=JL,Dg=`_gates/${vs}.jsonl`,Toe=`_gates/${vs}/applied.jsonl`,Aoe=".chant/overriders";function sa(e){return[...new Set(e.map(t=>t.trim()).filter(Boolean))].sort()}function VL(e,t,n){let r=[vs,e,t,...sa(n)].join(`
|
|
192
|
+
`);return`sha256:${Eoe("sha256").update(r).digest("hex")}`}var ti=e=>new Date(e).getTime();function GE(e){let t=new Map;for(let n of e){if(n.op!==vs||!n.planDigest)continue;let r=t.get(n.gate);(!r||ti(n.timestamp)>=ti(r.timestamp))&&t.set(n.gate,n)}return[...t.values()].map(n=>{let r=n;return{...n,plan:typeof r.plan=="string"?r.plan:"",rules:Array.isArray(r.rules)?sa(r.rules.filter(o=>typeof o=="string")):[]}}).sort((n,r)=>n.gate<r.gate?-1:n.gate>r.gate?1:0)}var KL=e=>e.normalize("NFKC").trim().toLowerCase();function Ooe(e,t){let n=t;if(n.via!==void 0)return`the override line was written by a job (via ${String(n.via)}), and an override is a person's`;if(t.review!==void 0)return`the override line came from the review of #${t.review.pr}, and an override is a person's own line`;if(t.approver?.kind==="agent")return`the override by ${t.resolvedBy} was recorded by an agent, and an override is a person's`;let r=`${e.overridersPath??Aoe} at base`;if(!e.overriders.map(KL).includes(KL(t.resolvedBy)))return`${t.resolvedBy} is not listed in ${r}${e.overriders.length?` (${e.overriders.join(", ")})`:", which lists nobody"}`;if(!t.note?.trim())return`the override by ${t.resolvedBy} gives no reason`;if(e.mode==="sealed"){let o=e.signers??{base:null,signersPath:Qi,active:!1,signers:[],problems:["no signers were read for this sealed override"]};return xd(o,t)}return null}function ZL(e){let t=sa(e.rules),n=VL(e.subject,e.planDigest,t),r=e.ledger.pending.filter(m=>m.op===vs),o=yg(r,e.subject),s=o?ti(o.timestamp):0,i=(e.applied??[]).filter(m=>m.op===vs&&m.gate===e.subject),a=m=>m.planDigest===void 0?void 0:i.find(h=>Ie(h.planDigest,m.planDigest)&&ti(h.approvedAt)>=ti(m.timestamp)),c=(m,h)=>!h||ti(m.timestamp)>=ti(h.timestamp),u,l,d,p=[];for(let m of e.ledger.resolutions){if(m.op!==vs||m.gate!==e.subject||ti(m.timestamp)<s)continue;let h=Ooe(e,m),y=a(m);if(Ie(m.planDigest,n))h!==null?p.push(h):y?p.push(`the override by ${m.resolvedBy} at ${m.timestamp} is spent: a run applied under it at ${y.timestamp}`):c(m,u)&&(u=m);else{if(h!==null)continue;y?c(m,d)&&(d=m):c(m,l)&&(l=m)}}if(u)return{status:"overridden",digest:n,override:{subject:e.subject,by:u.resolvedBy,at:u.timestamp,reason:u.note.trim(),rules:t,planDigest:e.planDigest,digest:n,sealed:e.mode==="sealed",resolution:u}};let f=e.now??new Date().toISOString(),g=o&&ti(o.expiresAt)>ti(f)&&Ie(o.planDigest,n)?GE([o])[0]:void 0;return l?{status:"moved",digest:n,by:l.resolvedBy,was:l.planDigest,refusals:p,...g?{standing:g}:{}}:{status:"none",digest:n,refusals:p,...g?{standing:g}:{},...d?{spent:{digest:d.planDigest,by:d.resolvedBy,at:d.timestamp}}:{}}}var Poe="policy.override";function Lb(e,t,n=!1){return`terragucci override ${e} ${sa(t).map(r=>`--rule ${r}`).join(" ")} --reason "<why>"${n?" --sign":""}`}function YL(e,t){return{mode:t.mode,overriders:t.overriders,...t.mode==="sealed"?{signers:tb(e,t.base)}:{}}}var XL=e=>({pending:e.pending,resolutions:e.resolutions});function QL(e,t,n,r,o,s){let i=ZL({ledger:XL(e),applied:(e.applied??[]).filter(c=>c.kind==="applied"),overriders:t.overriders,overridersPath:Poe,mode:t.mode,...t.signers?{signers:t.signers}:{},subject:n,planDigest:r,rules:o,now:s});if(i.status==="overridden"){let c=i.override;return{status:"overridden",override:{by:c.by,at:c.at,rules:c.rules,reason:c.reason,plan_digest:c.planDigest,digest:c.digest,sealed:c.sealed}}}let a=i.standing?{standing:i.standing}:{};return i.status==="moved"?{status:"moved",by:i.by,was:i.was,refusals:i.refusals,...a}:{status:"none",refusals:i.refusals,...a,...i.spent?{spent:{digest:i.spent.digest,by:i.spent.by}}:{}}}function eG(e){return GE(XL(e).pending)}import{createHash as joe}from"node:crypto";import{readFileSync as Ioe}from"node:fs";import{join as Coe,posix as nG}from"node:path";function mc(e){return e===!0?!0:Array.isArray(e)?e.some(mc):e&&typeof e=="object"?Object.values(e).some(mc):!1}function Wo(e){let t=e,n=new Map;if(t?.output_changes&&typeof t.output_changes=="object"){let o=!1;for(let[s,i]of Object.entries(t.output_changes)){let a=i.actions??[];a.some(c=>c!=="no-op")&&(o=!0),!a.includes("delete")&&n.set(s,{value:i.after,unknown:i.after_unknown??!1,sensitive:mc(i.after_sensitive)})}return{outputs:n,changed:o}}let r=t?.planned_values?.outputs;if(!(!r||typeof r!="object")){for(let[o,s]of Object.entries(r))n.set(o,"value"in s?{value:s.value,unknown:!1,sensitive:s.sensitive===!0}:{value:null,unknown:!0,sensitive:s.sensitive===!0});return{outputs:n,changed:!0}}}function Mg(e,t){return[...e].filter(([n,r])=>(t===void 0||t.has(n))&&mc(r.unknown)).map(([n])=>n).sort()}function Gb(e,t,n,r){let o=Mg(n,r);return{upstream:e,data:t,outputs:"planned",...o.length?{unknown:o}:{}}}function tG(e){return[...new Set(e.filter(t=>t.outputs==="planned"&&t.unknown?.length).map(t=>t.upstream))].sort()}function ni(e,t,n){return[...new Set([...t].map(r=>e.get(r)).filter(r=>r!==void 0&&r!==n))].sort((r,o)=>r-o)}function Lg(e,t){return`${e}: plans on the planned outputs of ${t.upstream}${t.unknown?.length?`, ${t.unknown.join(", ")} known once it applies`:""}`}var Gg=e=>`'${e.replace(/'/g,"'\\''")}'`,Noe=e=>`\`${e}\``;function FE(e){return typeof e=="string"||e===null||e===void 0?"string":typeof e=="number"?"number":typeof e=="boolean"?"bool":Array.isArray(e)?["tuple",e.map(FE)]:["object",Object.fromEntries(Object.entries(e).map(([t,n])=>[t,FE(n)]))]}function oG(e){let t={};for(let[n,r]of[...e].sort(([o],[s])=>o<s?-1:1))mc(r.unknown)||r.value===null||r.value===void 0||(t[n]={sensitive:r.sensitive,type:FE(r.value),value:r.value});return t}function sG(e){let t=e?.remote_state?.config;return!t||typeof t!="object"?[]:Object.values(t).filter(n=>typeof n=="string"&&n!==""&&!/["\\\n]|\$\{|%\{/.test(n)).map(n=>`"${n}"`).sort()}var UE="dirs.tsv",Fb="served.tsv";function iG(e,t,n,r){let o=["#!/bin/sh","# terragucci: hands each unit the planned outputs of the units it reads. Written for one wave.",'next="${TERRAGUCCI_TG_NEXT:-}"',`[ -n "$next" ] || next=${Gg(e)}`,`rec=${Gg(n)}`,'out=""','prev=""','json=""','for a in "$@"; do',' case "$a" in -out=*|--out=*) out="${a#*=}" ;; -json) json=1 ;; esac',' if [ "$prev" = "-out" ] || [ "$prev" = "--out" ]; then out="$a"; fi',' prev="$a"',"done",'case "$out" in'];for(let s of[...new Set(t)])o.push(` ${Gg(s)}/*/tfplan.tfplan) u="\${out#${Gg(s)}/}"; printf '%s\\t%s\\n' "\${u%/tfplan.tfplan}" "$(pwd -P)" >> "$rec/${UE}" ;;`);if(o.push("esac"),r.length>0){o.push('if [ "$1" = output ] && [ -n "$json" ]; then',' for a in "$@"; do case "$a" in output|-*) ;; *) json="" ;; esac; done',"fi",'if [ "$1" = output ] && [ -n "$json" ]; then',' here="$(pwd -P)"',' one=""'," n=0",' for g in *.tf *.tf.json; do if [ -f "$g" ]; then n=$((n + 1)); one="$g"; fi; done',` hit() { printf '%s\\t%s\\t%s\\t%s\\n' "$1" "$2" "\${TERRAGUCCI_TG_PHASE:-}" "$here" >> "$rec/${Fb}"; cat "$3"; exit 0; }`," serve() {",' u="$1"; d="$2"; j="$3"; shift 3',' if [ -n "$d" ] && [ "$here" = "$d" ]; then hit "$u" dir "$j"; fi',' { [ "$#" -gt 0 ] && [ "$n" = 1 ] && grep -q backend "$one"; } || return 0',' for s in "$@"; do grep -qF -- "$s" "$one" || return 0; done',' hit "$u" backend "$j"'," }");for(let s of r)o.push(` serve ${[s.unit,s.dir??"",s.outputs,...s.backend].map(Gg).join(" ")}`);o.push("fi")}return o.push('exec "$next" "$@"'),o.join(`
|
|
193
193
|
`)+`
|
|
194
194
|
`}function aG(e){let t=new Map;for(let n of e.split(`
|
|
195
195
|
`)){let[r,...o]=n.split(" ");r&&o.length>0&&t.set(r,o)}return t}var cG="TERRAGUCCI_TG_PHASE";function uG(e){let t=new Map;for(let n of e.split(`
|
|
196
|
-
`)){let[r,o,s]=n.split(" ");!r||!o||(t.has(r)||t.set(r,new Set),t.get(r).add(s??""))}return t}var zE=e=>e.replace(/(^|[^:"$])(#|\/\/).*$/gm,"$1");function rG(e){let t=[],n=/^\s*dependency\s+"([^"]+)"\s*\{/gm;for(let r=n.exec(e);r;r=n.exec(e)){let o=1,s=r.index+r[0].length,i=s;for(let a=!1;s<e.length&&o>0;s++){let c=e[s];a?c==="\\"?s++:c==='"'&&(a=!1):c==='"'?a=!0:c==="{"?o++:c==="}"&&o--}t.push({label:r[1],body:e.slice(i,s-1)}),n.lastIndex=s}return t}var Doe=e=>e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");function Moe(e,t){let n=new RegExp(`\\bdependency\\.${Doe(t)}(?![\\w-])(\\.outputs(?![\\w-])(?:\\.([A-Za-z_][\\w-]*)|\\[\\s*"([^"]+)"\\s*\\])?|\\.[A-Za-z_][\\w-]*)?`,"g"),r=new Set;for(let o of e.map(zE))for(let s of o.matchAll(n)){if(s[1]===void 0)return;if(!s[1].startsWith(".outputs"))continue;let i=s[2]??s[3];if(!i)return;r.add(i)}return r}function zb(e,t,n=[]){let r=s=>{try{return Ioe(Coe(e,s),"utf-8")}catch{return""}},o=`${t}/terragrunt.hcl`;return{own:r(o),others:[...new Set(n)].filter(s=>s!==o&&s.endsWith(".hcl")).map(r)}}function BE(e,t,n){let r=[n.own,...n.others],o=rG(zE(n.own)),s=[],i=!1;for(let c of o){let u=/^\s*config_path\s*=\s*"([^"$]*)"\s*$/m.exec(c.body);if(!u){i=!0;continue}let l=u[1].replace(/\/terragrunt\.hcl$/,"");nG.normalize(nG.join(e,l)).replace(/\/$/,"")===t&&s.push(c.label)}if(n.others.some(c=>rG(zE(c)).length>0)&&(i=!0),s.length===0)return i?{labels:[]}:null;let a=new Set;for(let c of s){let u=Moe(r,c);if(u===void 0){a=void 0;break}u.forEach(l=>a.add(l))}return i&&(a=void 0),{labels:s.sort(),...a?{outputs:a}:{}}}function lG(e,t,n,r){let o=[],s=[],i=[],a=[];for(let c of[...new Set(t)].sort()){let u=r.get(c);if(!u)continue;let l=BE(e,c,n);if(l===null)continue;if(u.plan===void 0){i.push(c),a.push(`reads ${c}, which has no plan in this run, so what it reads is known once wave ${u.wave} applies`);continue}let d=Wo(u.plan);if(!d||!d.changed)continue;let p=Mg(d.outputs,l.outputs);if(p.length>0){i.push(c),a.push(`reads ${p.map(Noe).join(", ")} of ${c}, unknown until wave ${u.wave} applies`);continue}s.push(c),o.push({upstream:c,data:l.labels.length?l.labels.join(", "):"dependency",outputs:"planned"})}return a.length>0?{after:i,why:a.join("; ")}:{reads:o,served:s}}function dG(e){return/does not have an attribute named "([^"]+)"/.exec(e)?.[1]}var Loe=e=>joe("sha256").update(JSON.stringify(e)??"null").digest("hex").slice(0,10);function pG(e){return e.map(t=>[t.address,t.action,Object.fromEntries(t.attributes.map(n=>[n.path,Loe(n.sensitive?["sensitive"]:[n.before??null,n.after??null,n.unknown===!0])]))])}function fG(e,t=3e4){let n=e.filter(o=>o.terragrunt&&o.status==="planned"&&(o.reads??[]).some(s=>s.outputs==="planned")).map(o=>({unit:o.path,plan:o.plan_digest,changes:pG(o.changes)})),r=JSON.stringify(n).length;for(let o of[...n].sort((s,i)=>JSON.stringify(i.changes).length-JSON.stringify(s.changes).length)){if(r<=t)break;r-=JSON.stringify(o.changes).length,delete o.changes}return n}function gG(e,t,n){if(e.plan!==null&&t===e.plan)return[];if(!e.changes)return["its plan differs from the preview; the note had no room for the preview's changes"];let r=new Map(e.changes.map(([i,a,c])=>[i,{action:a,attrs:c}])),o=new Map(pG(n).map(([i,a,c])=>[i,{action:a,attrs:c}])),s=[];for(let[i,a]of o){let c=r.get(i);if(!c){s.push(`${i} (${a.action}) was not in the preview`);continue}if(c.action!==a.action){s.push(`${i}: ${c.action} in the preview, ${a.action} now`);continue}let u=[...new Set([...Object.keys(c.attrs),...Object.keys(a.attrs)])].filter(l=>c.attrs[l]!==a.attrs[l]).sort();u.length>0&&s.push(`${i} (${a.action}): ${u.join(", ")} ${u.length===1?"differs":"differ"} from the preview`)}for(let[i,a]of r)o.has(i)||s.push(`${i} (${a.action}) was in the preview and is not planned now`);return s.length===0&&s.push("its plan differs from the preview outside its resources' changes (its outputs, or a sensitive value)"),s}function Goe(e){let t=e.named.filter(r=>r.address!==void 0&&(r.action==="delete"||r.action==="replace")).map(r=>({member:r.root,address:r.address,type:r.type??"unknown",action:r.action,...r.deposed!==void 0?{deposed:r.deposed}:{}})),n=r=>t.filter(o=>r.includes(o.member)||r.includes(o.address));return{$schema:vE,contract:$E,changeSet:e.change_set,unit:e.unit,units:e.units,groups:e.groups.map(r=>({id:r.id,...r.resource?{resource:r.resource}:{},units:r.units,outlier:r.outlier,noChanges:r.noChanges,changes:r.changes,...r.extends?{extends:r.extends}:{},...r.plus?{plus:r.plus}:{},destroys:n(r.units),sideEffects:[]})),failed:e.named.filter(r=>r.action==="refused").map(r=>({member:r.root,reason:r.reason??"the root did not plan"})),destroys:t,imports:e.named.filter(r=>r.action==="import"&&r.address!==void 0).map(r=>({member:r.root,address:r.address,type:r.type??"unknown",action:e.roots.find(o=>o.path===r.root)?.changes.find(o=>o.address===r.address)?.action??"no-op"})),forgets:e.named.filter(r=>r.action==="forget"&&r.address!==void 0).map(r=>({member:r.root,address:r.address,type:r.type??"unknown",...r.deposed!==void 0?{deposed:r.deposed}:{}})),sideEffects:[],holes:e.holes.map(r=>({member:r.root,address:r.address,...r.type?{type:r.type}:{},reason:r.reason}))}}function Ub(e){return
|
|
196
|
+
`)){let[r,o,s]=n.split(" ");!r||!o||(t.has(r)||t.set(r,new Set),t.get(r).add(s??""))}return t}var zE=e=>e.replace(/(^|[^:"$])(#|\/\/).*$/gm,"$1");function rG(e){let t=[],n=/^\s*dependency\s+"([^"]+)"\s*\{/gm;for(let r=n.exec(e);r;r=n.exec(e)){let o=1,s=r.index+r[0].length,i=s;for(let a=!1;s<e.length&&o>0;s++){let c=e[s];a?c==="\\"?s++:c==='"'&&(a=!1):c==='"'?a=!0:c==="{"?o++:c==="}"&&o--}t.push({label:r[1],body:e.slice(i,s-1)}),n.lastIndex=s}return t}var Doe=e=>e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");function Moe(e,t){let n=new RegExp(`\\bdependency\\.${Doe(t)}(?![\\w-])(\\.outputs(?![\\w-])(?:\\.([A-Za-z_][\\w-]*)|\\[\\s*"([^"]+)"\\s*\\])?|\\.[A-Za-z_][\\w-]*)?`,"g"),r=new Set;for(let o of e.map(zE))for(let s of o.matchAll(n)){if(s[1]===void 0)return;if(!s[1].startsWith(".outputs"))continue;let i=s[2]??s[3];if(!i)return;r.add(i)}return r}function zb(e,t,n=[]){let r=s=>{try{return Ioe(Coe(e,s),"utf-8")}catch{return""}},o=`${t}/terragrunt.hcl`;return{own:r(o),others:[...new Set(n)].filter(s=>s!==o&&s.endsWith(".hcl")).map(r)}}function BE(e,t,n){let r=[n.own,...n.others],o=rG(zE(n.own)),s=[],i=!1;for(let c of o){let u=/^\s*config_path\s*=\s*"([^"$]*)"\s*$/m.exec(c.body);if(!u){i=!0;continue}let l=u[1].replace(/\/terragrunt\.hcl$/,"");nG.normalize(nG.join(e,l)).replace(/\/$/,"")===t&&s.push(c.label)}if(n.others.some(c=>rG(zE(c)).length>0)&&(i=!0),s.length===0)return i?{labels:[]}:null;let a=new Set;for(let c of s){let u=Moe(r,c);if(u===void 0){a=void 0;break}u.forEach(l=>a.add(l))}return i&&(a=void 0),{labels:s.sort(),...a?{outputs:a}:{}}}function lG(e,t,n,r){let o=[],s=[],i=[],a=[];for(let c of[...new Set(t)].sort()){let u=r.get(c);if(!u)continue;let l=BE(e,c,n);if(l===null)continue;if(u.plan===void 0){i.push(c),a.push(`reads ${c}, which has no plan in this run, so what it reads is known once wave ${u.wave} applies`);continue}let d=Wo(u.plan);if(!d||!d.changed)continue;let p=Mg(d.outputs,l.outputs);if(p.length>0){i.push(c),a.push(`reads ${p.map(Noe).join(", ")} of ${c}, unknown until wave ${u.wave} applies`);continue}s.push(c),o.push({upstream:c,data:l.labels.length?l.labels.join(", "):"dependency",outputs:"planned"})}return a.length>0?{after:i,why:a.join("; ")}:{reads:o,served:s}}function dG(e){return/does not have an attribute named "([^"]+)"/.exec(e)?.[1]}var Loe=e=>joe("sha256").update(JSON.stringify(e)??"null").digest("hex").slice(0,10);function pG(e){return e.map(t=>[t.address,t.action,Object.fromEntries(t.attributes.map(n=>[n.path,Loe(n.sensitive?["sensitive"]:[n.before??null,n.after??null,n.unknown===!0])]))])}function fG(e,t=3e4){let n=e.filter(o=>o.terragrunt&&o.status==="planned"&&(o.reads??[]).some(s=>s.outputs==="planned")).map(o=>({unit:o.path,plan:o.plan_digest,changes:pG(o.changes)})),r=JSON.stringify(n).length;for(let o of[...n].sort((s,i)=>JSON.stringify(i.changes).length-JSON.stringify(s.changes).length)){if(r<=t)break;r-=JSON.stringify(o.changes).length,delete o.changes}return n}function gG(e,t,n){if(e.plan!==null&&t===e.plan)return[];if(!e.changes)return["its plan differs from the preview; the note had no room for the preview's changes"];let r=new Map(e.changes.map(([i,a,c])=>[i,{action:a,attrs:c}])),o=new Map(pG(n).map(([i,a,c])=>[i,{action:a,attrs:c}])),s=[];for(let[i,a]of o){let c=r.get(i);if(!c){s.push(`${i} (${a.action}) was not in the preview`);continue}if(c.action!==a.action){s.push(`${i}: ${c.action} in the preview, ${a.action} now`);continue}let u=[...new Set([...Object.keys(c.attrs),...Object.keys(a.attrs)])].filter(l=>c.attrs[l]!==a.attrs[l]).sort();u.length>0&&s.push(`${i} (${a.action}): ${u.join(", ")} ${u.length===1?"differs":"differ"} from the preview`)}for(let[i,a]of r)o.has(i)||s.push(`${i} (${a.action}) was in the preview and is not planned now`);return s.length===0&&s.push("its plan differs from the preview outside its resources' changes (its outputs, or a sensitive value)"),s}function Goe(e){let t=e.named.filter(r=>r.address!==void 0&&(r.action==="delete"||r.action==="replace")).map(r=>({member:r.root,address:r.address,type:r.type??"unknown",action:r.action,...r.deposed!==void 0?{deposed:r.deposed}:{}})),n=r=>t.filter(o=>r.includes(o.member)||r.includes(o.address));return{$schema:vE,contract:$E,changeSet:e.change_set,unit:e.unit,units:e.units,groups:e.groups.map(r=>({id:r.id,...r.resource?{resource:r.resource}:{},units:r.units,outlier:r.outlier,noChanges:r.noChanges,changes:r.changes,...r.extends?{extends:r.extends}:{},...r.plus?{plus:r.plus}:{},destroys:n(r.units),sideEffects:[]})),failed:e.named.filter(r=>r.action==="refused").map(r=>({member:r.root,reason:r.reason??"the root did not plan"})),destroys:t,imports:e.named.filter(r=>r.action==="import"&&r.address!==void 0).map(r=>({member:r.root,address:r.address,type:r.type??"unknown",action:e.roots.find(o=>o.path===r.root)?.changes.find(o=>o.address===r.address)?.action??"no-op"})),forgets:e.named.filter(r=>r.action==="forget"&&r.address!==void 0).map(r=>({member:r.root,address:r.address,type:r.type??"unknown",...r.deposed!==void 0?{deposed:r.deposed}:{}})),sideEffects:[],holes:e.holes.map(r=>({member:r.root,address:r.address,...r.type?{type:r.type}:{},reason:r.reason}))}}function Ub(e){return vL(Goe(e))}function Foe(e){let t=e.roots.filter(o=>o.monthly_delta!==null).length,n=e.roots.filter(o=>o.error).length,r=n>0?`; ${n} could not be estimated`:"";return e.monthly_delta===null?`Monthly cost: no estimate from ${e.estimator}${r}.`:`Monthly cost: **${gc(e.monthly_delta)} ${e.currency}** over ${t} of ${e.roots.length} ${e.roots.length===1?"root":"roots"}, from ${e.estimator}${r}.`}function zoe(e){let t=e.filter(o=>o.cost?.approve_above!==void 0);if(t.length===0)return;let n=t[0].cost,r=t.map(o=>{let s=o.cost.unestimated?.length?`not estimated for ${o.cost.unestimated.join(", ")}`:o.cost.monthly_delta===null?"not estimated":`${gc(o.cost.monthly_delta)} ${o.cost.currency}`;return`wave ${o.number} ${s}${o.cost.over?", over it: it waits for an approval whatever the gate":", within it"}`});return`Against \`cost.approve_above\` at base, ${n.approve_above.toFixed(2)} ${n.currency} a month: ${r.join("; ")}.`}function Uoe(e,t=n=>`\`${n}\``){let n=o=>o===null?"":o.toFixed(2),r=`**Monthly cost (${e.currency}, ${e.estimator}):**
|
|
197
197
|
|
|
198
198
|
| Root | Before | After | Change |
|
|
199
199
|
|---|---|---|---|
|
|
@@ -216,7 +216,7 @@ ${r}
|
|
|
216
216
|
`;for(let p of d.reaches.slice(0,ia))u+=` - ${Te(p.address)}${p.root===d.root?"":` in ${Te(p.root)}`}${p.through?`, through output ${Te(p.through.output)} of ${Te(p.through.root)}`:""}
|
|
217
217
|
`;d.reaches.length>ia&&(u+=` - and ${d.reaches.length-ia} more in the report's JSON
|
|
218
218
|
`)}l.length>ia&&(u+=`- and ${l.length-ia} more changed resources in the report's JSON
|
|
219
|
-
`)}return u}function yG(e){let t=e.totals;return{create:t.create+t.replace,update:t.update,delete:t.delete+t.replace}}var Hoe=`<sub><img src="${
|
|
219
|
+
`)}return u}function yG(e){let t=e.totals;return{create:t.create+t.replace,update:t.update,delete:t.delete+t.replace}}var Hoe=`<sub><img src="${OL}" width="26" height="16" alt=""> Posted by [terragucci](https://intentius.io/terragucci/)</sub>`,qoe=1e6,Joe={github:kE,gitlab:lL,forgejo:qoe};function wG(e,t){let n=qu(t.roots.map(r=>r.path).join(","));return Joe[e]-2*n-400}var lr=(e,t,n=`${t}s`)=>`${e} ${e===1?t:n}`;function Koe(e){let t=e.totals_roots;if(t===void 0||t===0)return;let n=e.totals,r=[`${n.create+n.replace} to add`,`${n.update} to change`,`${n.delete+n.replace} to destroy`];return n.forget>0&&r.push(`${n.forget} to forget`),`${r.join(", ")} across ${lr(t,"root")}`}var Te=e=>"`"+e.replaceAll("`","'")+"`",qu=e=>[...e].length;function Voe(e){let t=e.count>1?`${e.line} (x${e.count})`:e.line;return e.differsFrom&&(t+=e.differsIn?.length?` [differs from group ${e.differsFrom} in: ${e.differsIn.join(", ")}]`:` [differs from group ${e.differsFrom}]`),t}function HE(e){return!/\.html?$/i.test(e.split("#")[0].split("?")[0])}var Zoe=e=>e.replace("T"," ").replace(/:\d{2}(\.\d+)?Z$/," UTC"),mG=e=>e.replaceAll("&","&").replaceAll("<","<").replaceAll(">",">");function hG(e,t,n,r){let o=r.planUrls?.get(t);if(o)return o;if(n===void 0||r.artifacts)return;let s=n.split("#")[0];if(s.includes("?")||!/(^|\/)report\.html$/.test(s))return;let i=e.roots.find(a=>a.path===t)?.plan.text??oa(t).text;return s.replace(/report\.html$/,"")+i.split("/").map(encodeURIComponent).join("/")}function bG(e,t={}){let n=t.reportUrl??(t.runUrl?void 0:"report.html"),r=t.artifacts===!0,o=(j,z)=>n===void 0?j:`[${j}](${n}${z&&!r?`#${z}`:""})`,s=t.expires?` The links to the bucket work until ${Zoe(t.expires)}.`:"",i=n===void 0?`Planned in [this run](${t.runUrl}), which keeps no report artifact; with \`reports\` set, the note links the report in the bucket.`:r?`The full report is \`report.html\` in the \`terragucci-report\` artifact of [this run](${n}).`:`[Full report](${n})${s}`,a=n===void 0?"The full report":r?`The full report, in the artifacts of [this run](${n}),`:`[The full report](${n})`,c=t.limit??kE,{run:u}=e,l=e.unit==="instance"?"instance":"root",d=e.named.filter(j=>j.action==="delete"||j.action==="replace").length,p=e.named.filter(j=>j.action==="refused").length,f=[];f.push(`### terragucci ${u.stage}${u.wave!==void 0?`, wave ${u.wave}`:""}: ${Te(u.commit.slice(0,12))}`,"");let g=[lr(e.groups.length,"group"),lr(d,"destroy or replacement","destroys or replacements")];p>0&&g.push(`${p} refused`),f.push(`${lr(e.units,l)}: ${g.join(", ")}. ${i}`,"");let m=Koe(e);m&&f.push(m,""),e.roots.length===0&&u.stage==="tf-plan"&&f.push("This change reaches no root, so nothing was planned.","");for(let j of t.notices??[])f.push(`> ${j}`,"");e.tips&&e.tips.length>0&&f.push(`${lr(e.tips.length,"tip")} on how the roots are set up, in the ${r||n===void 0?"full report":o("full report","tips")}.`,"");let h=tse(e);h&&f.push(h,"");let y=ese(e);y&&f.push(y,"");let w=Qoe(e);w&&f.push(w,""),e.redaction.values>0&&f.push(`Sensitive values are redacted in the stored plans (${e.redaction.values}).`,""),e.cost&&f.push(Foe(e.cost),"");let b=e.cost?zoe(e.waves):void 0;b&&f.push(b,"");let k=e.timings?.resources.slice(0,3)??[];k.length>0&&f.push(`Slowest: ${k.map(j=>`${Te(`${j.root}: ${j.address}`)} ${ho(j.ms)}`).join(", ")}. ${r||n===void 0?"Where the time went is in the full report.":o("Where the time went","timings")}`,"");let v=[],$=Xoe(e,j=>o(Te(j),bn(j)));$&&v.push({kind:"line",units:0,text:$+`
|
|
220
220
|
`});let _=e.named;if(_.length>0){v.push({kind:"line",units:0,text:`**Destroys, replacements, refusals, imports and forgets (${_.length}):**
|
|
221
221
|
|
|
222
222
|
`});for(let j of _){let z=j.address?`${j.root}: ${j.address}${j.deposed!==void 0?` (deposed ${j.deposed})`:""}`:j.root,X=j.replace_paths?.length?`, forced by ${j.replace_paths.map(W=>Te(W.join("."))).join(", ")}`:"",V=j.reason?`: ${j.reason.split(/\s+/).join(" ")}`:"";v.push({kind:"line",units:0,text:`- ${o(Te(z),bn(j.root))} (${Uw(u.stage,j.action)}${X})${V}
|
|
@@ -266,7 +266,7 @@ ${r}
|
|
|
266
266
|
`;let _e=V.filter(xe=>xe.differsFrom);for(let xe of _e)le+=`- ${Te(xe.line)} differs from group ${xe.differsFrom}${xe.differsIn?.length?` in ${xe.differsIn.map(Te).join(", ")}`:""}.
|
|
267
267
|
`;j.varies.length>0&&(le+=`${_e.length>0?"- ":""}Values differ between its ${l}s in ${j.varies.flatMap(xe=>xe.paths.map(Le=>Te(`${xe.address}.${Le}`))).join(", ")}.
|
|
268
268
|
`),(_e.length>0||j.varies.length>0)&&(le+=`
|
|
269
|
-
`),v.push({kind:"group",units:j.units.length,text:le+C,short:X+W+C})}let te=!0;for(let j of e.roots){let z=t.plans?.get(j.path);if(z===void 0||j.changes.length===0&&!/^Changes to Outputs:/m.test(z))continue;let X=hG(e,j.path,n,t),V=
|
|
269
|
+
`),v.push({kind:"group",units:j.units.length,text:le+C,short:X+W+C})}let te=!0;for(let j of e.roots){let z=t.plans?.get(j.path);if(z===void 0||j.changes.length===0&&!/^Changes to Outputs:/m.test(z))continue;let X=hG(e,j.path,n,t),V=qL(z),W=te?`**Each root's plan:**
|
|
270
270
|
|
|
271
271
|
`:"";te=!1,W+=`<details><summary><code>${mG(j.path)}</code>${V?`: ${mG(V)}`:""}</summary>
|
|
272
272
|
|
|
@@ -411,7 +411,7 @@ ${n.join(`
|
|
|
411
411
|
`)}
|
|
412
412
|
</table></main></body></html>
|
|
413
413
|
`}function WG(e){return Ase(e).flatMap(t=>{let n=YE(e,t);return Ose(n).isDirectory()?WG(n):[n]})}var tv={json:"application/json",html:"text/html; charset=utf-8",md:"text/markdown; charset=utf-8",txt:"text/plain; charset=utf-8"},HG=e=>tv[e.split(".").pop()??""]??"application/octet-stream",zd=8,QE=e=>new Promise(t=>setTimeout(t,Math.round((50+Math.random()*100)*2**Math.min(e,5))));async function aa(e,t,n,r,o=QE){for(let s=1;;s++){let i=await e.read(t),a=n(i.body),c=i.etag?{ifMatch:i.etag}:i.body===void 0?{ifNoneMatch:"*"}:void 0;try{let u=await e.put(t,JSON.stringify(a,null,2)+`
|
|
414
|
-
`,tv.json,c);return{value:a,...u.etag?{etag:u.etag}:{}}}catch(u){if(!(u instanceof Er))throw u;if(s>=zd)throw new Y(`${t} changed under this run ${zd} times in a row; ${r} was not added`);await o(s)}}}async function zse(e,t,n,r=QE){let{value:o,etag:s}=await aa(e,t,i=>BG(i,n),"its row",r);return{index:o,...s?{etag:s}:{}}}var Jg=(e,t="")=>Vu(t,e,"inventory.json"),Kg=(e,t="")=>Vu(t,e,"states.json"),Vg=(e,t="")=>Vu(t,e,"edges.json"),Zg=(e,t="")=>Vu(t,e,"changes.json");async function Use(e,t,n,r,o){let s=(...i)=>i.filter(Boolean).join("/");for(let i=1;;i++){if(await e.put(s(t,"index.html"),Fse(r,n),tv.html),!o||i>=zd)return;let a=await e.read(s(t,"index.json"));if(!a.etag||a.etag===o||a.body===void 0)return;o=a.etag,r=BG(a.body,void 0)}}async function nv(e,t,n,r="",o=QE){let s=ev(r),i=(...b)=>b.filter(Boolean).join("/"),a=n.run.project,c=qg(n),u=FG(n,s),l=WG(t).filter(b=>!MG(t,b).split("\\").join("/").startsWith("unwritten/"));for(let b of l)await e.put(i(u,MG(t,b).split("\\").join("/")),GG(b),HG(b));n.run.trace_id&&await e.put(Ise(n.run.trace_id,s),Cse(n),tv.html);let d=[];for(let[b,k,v]of[[i(s,a),c,`Plan reports: ${a}`],[s,i(a,c),"Plan reports"]]){let $=i(b,"index.json"),{index:_,etag:E}=await zse(e,$,Mse(n,k),o);await Use(e,b,v,_,E),d.push($)}let p=
|
|
414
|
+
`,tv.json,c);return{value:a,...u.etag?{etag:u.etag}:{}}}catch(u){if(!(u instanceof Er))throw u;if(s>=zd)throw new Y(`${t} changed under this run ${zd} times in a row; ${r} was not added`);await o(s)}}}async function zse(e,t,n,r=QE){let{value:o,etag:s}=await aa(e,t,i=>BG(i,n),"its row",r);return{index:o,...s?{etag:s}:{}}}var Jg=(e,t="")=>Vu(t,e,"inventory.json"),Kg=(e,t="")=>Vu(t,e,"states.json"),Vg=(e,t="")=>Vu(t,e,"edges.json"),Zg=(e,t="")=>Vu(t,e,"changes.json");async function Use(e,t,n,r,o){let s=(...i)=>i.filter(Boolean).join("/");for(let i=1;;i++){if(await e.put(s(t,"index.html"),Fse(r,n),tv.html),!o||i>=zd)return;let a=await e.read(s(t,"index.json"));if(!a.etag||a.etag===o||a.body===void 0)return;o=a.etag,r=BG(a.body,void 0)}}async function nv(e,t,n,r="",o=QE){let s=ev(r),i=(...b)=>b.filter(Boolean).join("/"),a=n.run.project,c=qg(n),u=FG(n,s),l=WG(t).filter(b=>!MG(t,b).split("\\").join("/").startsWith("unwritten/"));for(let b of l)await e.put(i(u,MG(t,b).split("\\").join("/")),GG(b),HG(b));n.run.trace_id&&await e.put(Ise(n.run.trace_id,s),Cse(n),tv.html);let d=[];for(let[b,k,v]of[[i(s,a),c,`Plan reports: ${a}`],[s,i(a,c),"Plan reports"]]){let $=i(b,"index.json"),{index:_,etag:E}=await zse(e,$,Mse(n,k),o);await Use(e,b,v,_,E),d.push($)}let p=NL(n,c),f;p.length>0&&(f=Jg(a,s),await aa(e,f,b=>DL(b,p),"its resources",o));let g=PL(n,c),m;g.length>0&&(m=Zg(a,s),await aa(e,m,b=>jL(b,g),"its changes",o));let h=PG(n,c),y;h.length>0&&(y=Kg(a,s),await aa(e,y,b=>jG(b,h),"its state versions",o));let w;return CG(n)&&(w=Vg(a,s),await aa(e,w,b=>NG(b,n,c),"its cross-state edges",o)),{prefix:u,files:l.length,indexes:d,...f?{inventory:f}:{},...m?{changes:m}:{},...y?{states:y}:{},...w?{edges:w}:{}}}async function qG(e,t,n,r,o=""){let s=FG(n,o),i=[];for(let a of r){let c=`${s}/${a.split("\\").join("/")}`;await e.put(c,GG(YE(t,a)),HG(a)),i.push(c)}return i}var rv="terragucci.run/v1",Bse=24,Yg=(e,t,n="")=>[n,e,"runs",t].map(r=>r.replace(/^\/+|\/+$/g,"")).filter(Boolean).join("/");function KG(e,t,n,r,o=new Map,s){let i=new Map(n.flatMap((a,c)=>a.map(u=>[u,c+1])));return{schema:rv,project:e,commit:t,updated:"",roots:n.flatMap((a,c)=>[...a].sort().map(u=>{let l=o.get(u);return{root:u,wave:c+1,reads:[...r.get(u)??[]].filter(d=>i.has(d)).sort(),...l?.state?{state:l.state}:{},...l?.external.length?{external:l.external}:{},...s?.wave!==void 0&&s.modules.has(u)?{composed:{wave:s.wave,module:`module.${s.modules.get(u)}`,own_wave:s.ownWave.get(u)??c+1}}:{}}})),waves:n.map((a,c)=>{let u=c+1,l=[...new Set(a.flatMap(d=>[...r.get(d)??[]]).map(d=>i.get(d)).filter(d=>d!==void 0&&d!==u))].sort((d,p)=>d-p);return{number:u,roots:[...a].sort(),reads:l,state:"not-started",gate:`wave-${u}`,...s?.wave===u?{composed:{roots:s.order}}:{}}})}}function Wse(e,t,n,r){let o;try{let c=e?JSON.parse(e):void 0;c?.schema===rv&&Array.isArray(c.waves)&&(o=c)}catch{o=void 0}let s=new Map((o?.waves??[]).map(c=>[c.number,c])),i=(c,u)=>{let l={...c,...u??{},...n,roots:c.roots,reads:c.reads,updated:r};if((n.spans||u?.spans)&&(l.spans=Hse(u?.spans??[],n.spans??[])),(n.changed||u?.changed)&&(l.changed=[...new Set([...u?.changed??[],...n.changed??[]])].sort()),n.shares_applied){l.shares_applied=[...new Set([...u?.shares_applied??[],...n.shares_applied])].sort((f,g)=>f-g);let d=l.shares??u?.shares,p=u?.state==="failed"||u?.state==="refused";l.state=p?u.state:d!==void 0&&l.shares_applied.length>=d?"applied":"applying"}return l},a=t.waves.map(c=>{if(c.number===n.number)return i(c,s.get(c.number));let u=s.get(c.number);return u?{...c,...u,roots:c.roots,reads:c.reads}:c});for(let[c,u]of s)a.some(l=>l.number===c)||a.push(u);return a.some(c=>c.number===n.number)||a.push({roots:[],reads:[],state:"not-started",gate:`wave-${n.number}`,...n,updated:r}),a.sort((c,u)=>c.number-u.number),{...t,roots:t.roots.length?t.roots:o?.roots??[],waves:a,updated:r}}function Hse(e,t){let n=o=>`${o.phase}|${o.start}|${o.share??""}`,r=new Map(e.map(o=>[n(o),o]));for(let o of t)r.set(n(o),o);return[...r.values()].sort((o,s)=>o.start<s.start?-1:o.start>s.start?1:0).slice(-Bse)}var qse={"not-started":"not started",planned:"planned",waiting:"waiting for an approval",applying:"applying",applied:"applied",refused:"refused",failed:"failed"};function VG(e){let t=e.waves.flatMap(n=>n.changed??[]);return Wg(new Map(e.roots.map(n=>[n.root,n.reads])),t,{waveOf:new Map(e.roots.map(n=>[n.root,n.wave]))})}function Jse(e){if(!e.waves.some(o=>o.changed!==void 0))return'<p class="none">No wave has planned yet, so the roots this apply changes are not known.</p>';let t=VG(e);if(t.roots.length===0)return'<p id="blast" data-roots="0" data-downstream="0">No plan of this apply changes a root.</p>';let n=t.roots.map(o=>`<li class="changed" data-root="${T(o)}"><code>${T(o)}</code></li>`).join(""),r=t.downstream.map(o=>`<li class="downstream" data-root="${T(o.root)}" data-depth="${o.depth}"><code>${T(o.root)}</code>${o.wave!==void 0?`, wave ${o.wave}`:""}: reads ${o.reads.map(s=>`<code>${T(s)}</code>`).join(", ")}</li>`).join("");return`<div id="blast" data-roots="${t.roots.length}" data-downstream="${t.downstream.length}"><p>${t.roots.length} ${t.roots.length===1?"root changes":"roots change"}; ${t.downstream.length===0?"no other root reads their state.":`${t.downstream.length} downstream ${t.downstream.length===1?"root reads":"roots read"} their state, and plan on what they apply.`}</p><ul>${n}</ul>${r?`<p>Downstream:</p><ul>${r}</ul>`:""}</div>`}function Kse(e){let t=AG(e.waves,e.updated);if(!t)return'<p class="none">No wave has a recorded time yet.</p>';let n=e.waves.map(r=>{let o=OG(r.spans??[],e.updated),s=i=>i===void 0?"":T(qb(i));return`<tr data-wave="${r.number}"><td>${r.number}</td><td>${s(o.plan)}</td><td>${s(o.gate)}${o.waiting?" (waiting)":""}</td><td>${s(o.apply)}</td></tr>`});return`<p class="legend"><span class="plan"></span>plan<span class="gate"></span>gate wait<span class="apply"></span>apply</p><div class="graphwrap">${t}</div>
|
|
415
415
|
<table class="times"><tr><th>Wave</th><th>Plan</th><th>Gate wait</th><th>Apply</th></tr>${n.join("")}</table>`}var eT={done:"done","in-flight":"in flight",waiting:"waiting","not-applied":"not applied"},JG=["done","in-flight","waiting","not-applied"];function Vse(e){let t=Ug(e),n=e.resources.length,r=JG.filter(a=>t[a]>0).map(a=>`<span class="p-${a}" style="flex:${t[a]}" title="${t[a]} ${eT[a]}"></span>`).join(""),o=JG.filter(a=>t[a]>0||a!=="not-applied").map(a=>`${t[a]} ${eT[a]}`).join(", "),s=new Set(e.resources.map(a=>a.root)).size,i=e.resources.map(a=>`<li data-status="${a.status}" data-address="${T(a.address)}"><span class="dot p-${a.status}"></span><code>${T(s>1?`${a.root}: ${a.address}`:a.address)}</code> ${T(eT[a.status])}${a.action!=="update"?` <small>${T(a.action)}</small>`:""}</li>`).join("");return`<div class="progress" data-done="${t.done}" data-in-flight="${t["in-flight"]}" data-waiting="${t.waiting}" data-total="${n}"><div class="pbar">${r}</div><p>${t.done} of ${n} resources done: ${T(o)}. Read <time datetime="${T(e.read)}">${T(e.read)}</time>.</p><ul class="res">${i}</ul></div>`}function Zse(e){let t=new Map;for(let c of e.roots)t.set(c.wave,[...t.get(c.wave)??[],c]);let n=e.waves.map(c=>{let u=t.get(c.number)??c.roots.map(m=>({root:m,wave:c.number,reads:[]})),l=[`gate <code>${T(c.gate)}</code>`,c.policy?T(c.policy):"",c.approval&&c.approval!=="not-requested"?T(c.approval):""].filter(Boolean).join(", "),d=c.digest?`<div class="digest"><code>${T(c.digest)}</code></div>`:"",p=c.command&&c.state==="waiting"?`<div><code>${T(c.command)}</code></div>`:"",f=c.report?`<div><a href="../../${T(c.report)}/report.html">report</a></div>`:"",g=u.map(m=>`<li><code>${T(m.root)}</code>${m.reads.length?`<div class="reads">reads ${m.reads.map(h=>`<code>${T(h)}</code>`).join(", ")}</div>`:""}</li>`).join("");return`<section class="wave" data-wave="${c.number}" data-state="${T(c.state)}"><h2>Wave ${c.number}</h2><p class="state s-${T(c.state)}">${T(qse[c.state]??c.state)}</p><p>${l}</p>${c.reads.length?`<p>after wave ${c.reads.join(", ")}</p>`:""}${d}${p}${f}${c.progress?Vse(c.progress):""}<ul>${g}</ul></section>`}),r=`${e.project}: apply of ${e.commit.slice(0,12)}`,o=VG(e),s=new Set(o.roots),i=new Set(o.downstream.map(c=>c.root)),a=Jb([{project:e.project,roots:e.roots.map(c=>({root:c.root,wave:c.wave}))}],e.roots.flatMap(c=>c.reads.map(u=>({from:{project:e.project,root:u},to:{project:e.project,root:c.root}}))),(c,u)=>s.has(u)?"changed":i.has(u)?"downstream":void 0);return`<!doctype html>
|
|
416
416
|
<html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><meta name="color-scheme" content="light dark">
|
|
417
417
|
<title>${T(r)}</title>${e.waves.some(c=>c.state==="applying")?`
|
|
@@ -438,7 +438,7 @@ ${Kse(e)}
|
|
|
438
438
|
${JSON.stringify(e).replace(/<\//g,"<\\/").replace(/<!--/g,"<\\u0021--")}
|
|
439
439
|
</script>
|
|
440
440
|
</main></body></html>
|
|
441
|
-
`}async function ZG(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=>Wse(c,n,r,o),`wave ${r.number}'s row`,s);return await e.put(`${i}/run.html`,Zse(a),"text/html; charset=utf-8"),`${i}/run.html`}import{spawnSync as Bfe}from"node:child_process";import{spawn as Vpe,spawnSync as pO}from"node:child_process";import{chmodSync as FB,existsSync as dp,mkdirSync as pp,mkdtempSync as Km,readdirSync as zB,readFileSync as jc,realpathSync as UB,rmSync as Jm,statSync as Zpe,writeFileSync as Ra}from"node:fs";import{tmpdir as fO}from"node:os";import{basename as gO,join as Ne,relative as BB,resolve as Rs,sep as WB}from"node:path";var Pr="0.4.
|
|
441
|
+
`}async function ZG(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=>Wse(c,n,r,o),`wave ${r.number}'s row`,s);return await e.put(`${i}/run.html`,Zse(a),"text/html; charset=utf-8"),`${i}/run.html`}import{spawnSync as Bfe}from"node:child_process";import{spawn as Vpe,spawnSync as pO}from"node:child_process";import{chmodSync as FB,existsSync as dp,mkdirSync as pp,mkdtempSync as Km,readdirSync as zB,readFileSync as jc,realpathSync as UB,rmSync as Jm,statSync as Zpe,writeFileSync as Ra}from"node:fs";import{tmpdir as fO}from"node:os";import{basename as gO,join as Ne,relative as BB,resolve as Rs,sep as WB}from"node:path";var Pr="0.4.17";import{existsSync as Xse,readFileSync as Qse}from"node:fs";import{join as eie}from"node:path";function YG(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??bs(e),r=WM(e,n),o=await ln(e,n,t.config?{config:t.config}:{});if("error"in o)throw new S(`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&&qC(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{...YG(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=eie(e,"chant.workspace.json");return YG(Bs(t.approval),Xg(Xse(n)?Qse(n,"utf-8"):void 0),void 0,"here")}var tie=(e,t)=>`-estate-outputs-from=${e}=${t}`;function XG(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=Wo(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(tie(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 QG(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 eF=e=>"`"+e+"`";function tF(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(eF).join(", ")} of ${o.join(", ")}, unknown until ${n.length?`wave ${n.join(", ")}`:"it"} applies`}function nF(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(eF).join(", ")} of ${r.join(", ")}, which choudoufu never hands to another estate`}import{existsSync as nie,readdirSync as rie,readFileSync as iF,rmSync as oie,statSync as sie,writeFileSync as tT}from"node:fs";import{join as aF}from"node:path";function nT(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=>mc(s[a]))])].sort().map(a=>`${nT(a)} = ${Qg(o[a],s[a],n)}`).join(", ")} }`}return e==null?"null":typeof e=="string"?nT(e):JSON.stringify(e)}var iie=e=>e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),em=(e,t="g")=>new RegExp(`\\bdata\\.terraform_remote_state\\.${iie(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 rie(e).filter(t=>t.endsWith(".tf")||t.endsWith(".tofu")||t.endsWith(".tf.json")||t.endsWith(".tofu.json")).map(t=>aF(e,t)).filter(t=>sie(t).isFile())}catch{return[]}}function rT(e,t){return ov(tm(e).map(n=>iF(n,"utf-8")),t)}var rF="terragucci_linked.tf",oF="terragucci_unknown",sF=e=>`terragucci_linked_${e}`;function cF(e,t){let n=aF(e,rF);if(nie(n))throw new Error(`${rF} is already in the root`);let r=tm(e),o=new Map(r.map(l=>[l,iF(l,"utf-8")])),s=[...o.values()],i=[],a=!1,c=[` ${oF} = 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.${oF}`);return`${nT(f)} = ${g.sensitive?`sensitive(${m})`:m}`});c.push(` ${sF(l.name)} = { outputs = { ${p.join(", ")} } }`)}let u=()=>{for(let[l,d]of o)tT(l,d);oie(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.${sF(f.name)}`);p!==d&&tT(l,p)}tT(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 UT(e,t){let[n,r]=t.split(".");if(!n||!r||!fce(e))return;let o=new RegExp(`^\\s*resource\\s+"${n}"\\s+"${r}"\\s*\\{`,"m");return gce(e).filter(s=>s.endsWith(".tf")).sort().find(s=>o.test(mce(hce(e,s),"utf-8")))}function fz(e,t){return`${e.replace(/\n*$/,`
|
|
470
470
|
`)}
|
|
471
|
-
${t}`}import{spawnSync as Ece}from"node:child_process";import{createHash as wce}from"node:crypto";var BT={"ghcr.io/intentius/terragucci-tofu:0.4.16-tofu1.13.1":"sha256:03f847df47259ac71ef0bad75fad6db743fddd04b74e69e1c83080dd93ccecd3","ghcr.io/intentius/terragucci-terraform:0.4.16-tf1.14.9":"sha256:3975b648b25980916b8d22f80ffeaf1542fd2e49792437ebcb1c577b406cf9bf","ghcr.io/intentius/terragucci-terragrunt:0.4.16-tg1.1.6-tofu1.13.1":"sha256:05df972e17ef7750564ab24b150fa405e72632f5f4ab24db299f883c70661c45","ghcr.io/intentius/terragucci-choudoufu:0.4.16-choudoufu0.28.0":"sha256:ec18f24808e7d9b25462f9fb87b0514574bc9595599292ce7061e8ffc3759a27"};var gz="ghcr.io/intentius",ai={tofu:"1.13.1",terraform:"1.14.9",terragrunt:"1.1.6",choudoufu:"0.28.0"};function mz(e,t=Pr){return e==="terragrunt"?`${t}-tg${ai.terragrunt}-tofu${ai.tofu}`:`${t}-${e==="terraform"?"tf":e}${ai[e]}`}function HT(e,t=BT){let n=`${gz}/terragucci-${e}`,r=mz(e);return{repository:n,tag:r,digest:t[`${n}:${r}`]}}function hz(e=BT){let t=`${gz}/terragucci-terragrunt`,n=mz("terragrunt");return{repository:t,tag:n,digest:e[`${t}:${n}`]}}function qT(e){return e.digest?`${e.repository}:${e.tag}@${e.digest}`:`${e.repository}:${e.tag}`}var WT={laya:"0.3.28",torch:"2.14.0",pip:"26.2.1",checkpoint:{repo:"convaiinnovations/laya",name:"english",revision:"55cf4c4ebb4ebe31b2550e8bdf3bd21b99753851",weightsSha256:"891102d372688fc2a094dac56a384bc537b87c63f21f9f3dac0be2b7cbc8d86c"},port:8790},yz=`laya-${WT.laya}-${WT.checkpoint.name}@${WT.checkpoint.revision.slice(0,12)}`;var bce="https://api.typesafe.ai",vce={noul:.8,choice:.7,score:.7},$ce={laya:!0,jev:!0,von:!1,decider:!1};function kce(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 wz(e,t){return e.threshold??t?.thresholds?.[e.type]??vce[e.type]}var Ev=e=>typeof e=="number"&&Number.isFinite(e)&&e>=0&&e<=1;function _ce(e){let t=e.url??(e.backend==="jev"?bce:void 0);if(!t)return;let n=t.replace(/\/+$/,"");return n.endsWith("/v1/systemone")?n:`${n}/v1/systemone`}function Sce(e){return e.model??(e.backend==="laya"?yz:void 0)}function Rce(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))kce(v,$);let o=`sha256:${wce("sha256").update(JSON.stringify(t)).digest("hex")}`,s=(v,$)=>Object.fromEntries(Object.entries(n).map(([_,E])=>[_,{question:_,type:E.type,status:v,threshold:wz(E,e),reason:$}]));if(!e)return{stateDigest:o,decisions:s("off","decide is not configured")};let i=_ce(e),a=Sce(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 _=JSON.parse(m),E=_.detail??_.error;E!==void 0&&($=typeof E=="string"?E:JSON.stringify(E))}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($ce[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,Rce(v,$,b[v],wz($,e))]));return{...c,model:w??a,ms:h,decisions:k}}function Qd(e){return e?.status==="confident"}function _c(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 JT={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"}},KT={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"}},xce={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"}},bz={prIntent:JT,driftActor:KT,versionBump:xce};var wm=e=>e!==null&&typeof e=="object"&&!Array.isArray(e),tl=e=>typeof e=="number",Tce=/^(aws:|eks:|elasticbeanstalk:|kubernetes\.io\/|k8s\.io\/|karpenter\.sh\/)/;function Ace(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=>Tce.test(o))}var Oce=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)},Pce=[{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:Oce("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:Ace}))];function jce(e,t,n){return Pce.find(r=>(r.type===e||r.type==="*")&&r.path===t&&(r.when?.(n)??!0))}var Ice=/terraform|opentofu|tofu/i;function Cce(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 Nce(e){let n=e.filter(o=>o.readOnly!==!0&&o.readOnly!=="true"&&!Ice.test(o.userAgent??"")).sort((o,s)=>String(s.eventTime??"").localeCompare(String(o.eventTime??"")))[0],r=n&&Cce(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=Ece("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 Ece}from"node:child_process";import{createHash as wce}from"node:crypto";var BT={"ghcr.io/intentius/terragucci-tofu:0.4.17-tofu1.13.1":"sha256:287f09a52381caeee325f54e28b72c0895f3e4b6972b2c64847e98912c8679e0","ghcr.io/intentius/terragucci-terraform:0.4.17-tf1.14.9":"sha256:a4ecd45f817a3a3513111e908f5ee94c558b708feabec857c1b82db7b91acb6e","ghcr.io/intentius/terragucci-terragrunt:0.4.17-tg1.1.6-tofu1.13.1":"sha256:5d3b489da92fbc0dc01c8b12a92f712e666d2a3e9ef51692b6ecb8be8ecdc5f4","ghcr.io/intentius/terragucci-choudoufu:0.4.17-choudoufu0.28.0":"sha256:36c17f7aec9dae8f6b0b9a2f1f560c086548368af16056cfac381eb2644a1b12"};var gz="ghcr.io/intentius",ai={tofu:"1.13.1",terraform:"1.14.9",terragrunt:"1.1.6",choudoufu:"0.28.0"};function mz(e,t=Pr){return e==="terragrunt"?`${t}-tg${ai.terragrunt}-tofu${ai.tofu}`:`${t}-${e==="terraform"?"tf":e}${ai[e]}`}function HT(e,t=BT){let n=`${gz}/terragucci-${e}`,r=mz(e);return{repository:n,tag:r,digest:t[`${n}:${r}`]}}function hz(e=BT){let t=`${gz}/terragucci-terragrunt`,n=mz("terragrunt");return{repository:t,tag:n,digest:e[`${t}:${n}`]}}function qT(e){return e.digest?`${e.repository}:${e.tag}@${e.digest}`:`${e.repository}:${e.tag}`}var WT={laya:"0.3.28",torch:"2.14.0",pip:"26.2.1",checkpoint:{repo:"convaiinnovations/laya",name:"english",revision:"55cf4c4ebb4ebe31b2550e8bdf3bd21b99753851",weightsSha256:"891102d372688fc2a094dac56a384bc537b87c63f21f9f3dac0be2b7cbc8d86c"},port:8790},yz=`laya-${WT.laya}-${WT.checkpoint.name}@${WT.checkpoint.revision.slice(0,12)}`;var bce="https://api.typesafe.ai",vce={noul:.8,choice:.7,score:.7},$ce={laya:!0,jev:!0,von:!1,decider:!1};function kce(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 wz(e,t){return e.threshold??t?.thresholds?.[e.type]??vce[e.type]}var Ev=e=>typeof e=="number"&&Number.isFinite(e)&&e>=0&&e<=1;function _ce(e){let t=e.url??(e.backend==="jev"?bce:void 0);if(!t)return;let n=t.replace(/\/+$/,"");return n.endsWith("/v1/systemone")?n:`${n}/v1/systemone`}function Sce(e){return e.model??(e.backend==="laya"?yz:void 0)}function Rce(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))kce(v,$);let o=`sha256:${wce("sha256").update(JSON.stringify(t)).digest("hex")}`,s=(v,$)=>Object.fromEntries(Object.entries(n).map(([_,E])=>[_,{question:_,type:E.type,status:v,threshold:wz(E,e),reason:$}]));if(!e)return{stateDigest:o,decisions:s("off","decide is not configured")};let i=_ce(e),a=Sce(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 _=JSON.parse(m),E=_.detail??_.error;E!==void 0&&($=typeof E=="string"?E:JSON.stringify(E))}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($ce[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,Rce(v,$,b[v],wz($,e))]));return{...c,model:w??a,ms:h,decisions:k}}function Qd(e){return e?.status==="confident"}function _c(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 JT={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"}},KT={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"}},xce={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"}},bz={prIntent:JT,driftActor:KT,versionBump:xce};var wm=e=>e!==null&&typeof e=="object"&&!Array.isArray(e),tl=e=>typeof e=="number",Tce=/^(aws:|eks:|elasticbeanstalk:|kubernetes\.io\/|k8s\.io\/|karpenter\.sh\/)/;function Ace(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=>Tce.test(o))}var Oce=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)},Pce=[{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:Oce("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:Ace}))];function jce(e,t,n){return Pce.find(r=>(r.type===e||r.type==="*")&&r.path===t&&(r.when?.(n)??!0))}var Ice=/terraform|opentofu|tofu/i;function Cce(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 Nce(e){let n=e.filter(o=>o.readOnly!==!0&&o.readOnly!=="true"&&!Ice.test(o.userAgent??"")).sort((o,s)=>String(s.eventTime??"").localeCompare(String(o.eventTime??"")))[0],r=n&&Cce(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=Ece("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 Nce(i)}}}function vz(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:jce(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:vz(c.before,c.sensitive),live_value:vz(c.live,c.sensitive)},p=await Xd(n.decide,d,{actor:KT},n.options),f=p.decisions.actor;if(Qd(f)){r.push({...u,actor:f.answer,source:"model",detail:_c(f,p),probability:f.probability});continue}o.push(`${a.address} ${c.path}: ${_c(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 Dce={table:"known write",audit:"audit log",model:"model",unattributed:"unattributed"};function VT(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=`${Dce[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 ZT(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 kz,readdirSync as _z,readFileSync as Mce}from"node:fs";import{join as Lce,posix as Gce}from"node:path";var Sz=(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=>!Sz(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 YT(e){if(typeof e=="number"||typeof e=="boolean")return String(e);if(typeof e=="string")return JSON.stringify(e).replace(/\$\{/g,()=>"$${").replace(/%\{/g,()=>"%%{")}function Fce(e){return kz(e)?_z(e).filter(t=>t.endsWith(".tf")||t.endsWith(".tofu")).sort():[]}function $z(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)??Mce(Lce(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=Fce(t).find(g=>$z(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]=$z(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(!Sz(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=YT(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:Gce.join(e,u),from:y[2].trim(),to:b})}let f=l.join(`
|
|
@@ -498,7 +498,7 @@ ${E.length?`
|
|
|
498
498
|
${E.join(`
|
|
499
499
|
`)}
|
|
500
500
|
`:""}`),b=o(["rev-parse","HEAD"]).trim()}catch($){return{opened:!1,fail:!0,reason:`could not commit the drift agent's change (${Rc($.message)})`}}let k=Buffer.from(`x-access-token:${a}`).toString("base64");try{o(["-c",`http.extraHeader=Authorization: Basic ${k}`,"push","-q","origin",`HEAD:refs/heads/${y}`])}catch($){let _=Rc(($.stderr||$.message).split(a).join("***"));return await u(`could not push the drift agent's change to \`${y}\` (${_}), so it opened no pull request.`),{opened:!1,fail:!0,reason:`could not push to ${y} (${_})`}}let v;try{let $=await lc(r,c),_=[`The drift agent changed the code to describe what is live, for drift issue #${s}, found at \`${i.slice(0,12)}\`.`,"",`It changes ${m.map(hA).join(", ")}. A person reviews it: merging keeps the change made outside the code, and the merge's apply goes through the gate like any other. Close it to undo the drift by applying the code as it is instead.`,"","Nothing was applied, approved or merged."].join(`
|
|
501
|
-
`);v=(await dc(r,c,{head:y,base:$,title:w,body:_})).url}catch($){if(!($ instanceof ei)&&!($ instanceof Zr)&&!($ instanceof TypeError))throw $;return await u(`pushed the drift agent's change to \`${y}\`, and could not open its pull request (${Rc($.message)}).`),{opened:!1,fail:!0,reason:`could not open the pull request (${$.message})`,commit:b,paths:m}}return await u(`the drift agent opened ${v}, changing ${m.map(hA).join(", ")}. It plans like any other pull request; nothing was applied, approved or merged.`),{opened:!0,reason:`opened ${v} from ${y}`,pullRequest:v,commit:b,paths:m}}import{spawnSync as zue}from"node:child_process";import{randomBytes as Lue}from"node:crypto";function Lv(e){let t={};for(let n of(e??"").split(",")){let r=n.indexOf("=");if(r<=0)continue;let o=n.slice(0,r).trim(),s=n.slice(r+1).trim();try{s=decodeURIComponent(s)}catch{}o&&(t[o]=s)}return t}var Gue=e=>Object.entries(e).map(([t,n])=>`${t}=${encodeURIComponent(n)}`).join(",");function Jz(e,t,n){let r=t.toUpperCase(),o=e[`OTEL_${r}_EXPORTER`];if(o!==void 0&&o.trim()!==""&&!o.split(",").map(u=>u.trim()).includes("otlp"))return;let s=e[`OTEL_EXPORTER_OTLP_${r}_ENDPOINT`]?.trim(),i=e.OTEL_EXPORTER_OTLP_ENDPOINT?.trim(),a=s||(i?`${i.replace(/\/+$/,"")}/v1/${t}`:void 0);if(!a)return;if((e[`OTEL_EXPORTER_OTLP_${r}_PROTOCOL`]??e.OTEL_EXPORTER_OTLP_PROTOCOL)==="grpc"){n.push(`${t}: terragucci sends OTLP over HTTP and the protocol is grpc; point it at the collector's HTTP port`);return}if(!/^https?:\/\//.test(a)){n.push(`${t}: ${a} is not an http or https URL`);return}return{url:a,headers:{...Lv(e.OTEL_EXPORTER_OTLP_HEADERS),...Lv(e[`OTEL_EXPORTER_OTLP_${r}_HEADERS`])}}}function ol(e){if(e.OTEL_SDK_DISABLED?.trim().toLowerCase()==="true")return;let t=[],n=Jz(e,"traces",t),r=Jz(e,"metrics",t);if(!n&&!r&&t.length===0)return;let o={...Lv(e.OTEL_RESOURCE_ATTRIBUTES)};return o["service.name"]=e.OTEL_SERVICE_NAME?.trim()||o["service.name"]||"terragucci",{...n?{traces:n}:{},...r?{metrics:r}:{},resource:o,skipped:t}}var _s=()=>BigInt(Math.round((performance.timeOrigin+performance.now())*1e3))*1000n,Kz=e=>Lue(e).toString("hex");function Fue(e){let t=/^[\da-f]{2}-([\da-f]{32})-([\da-f]{16})-[\da-f]{2}$/.exec((e??"").trim().toLowerCase());if(!(!t||/^0+$/.test(t[1])||/^0+$/.test(t[2])))return{traceId:t[1],spanId:t[2]}}var Gv=class{traceId;parent;spans=[];constructor(t){let n=Fue(t);this.traceId=n?.traceId??Kz(16),this.parent=n?.spanId}start(t,n,r={},o=1){let s={traceId:this.traceId,spanId:Kz(8),name:t,kind:o,start:_s(),attributes:{...r}},i=n?.spanId??this.parent;return i&&(s.parentSpanId=i),this.spans.push(s),s}end(t,n={},r){t.end=_s(),Object.assign(t.attributes,n),r&&(t.error=r)}traceparent(t){return`00-${this.traceId}-${t.spanId}-01`}};function Fv(e){return Object.entries(e).filter(t=>t[1]!==void 0).map(([t,n])=>({key:t,value:typeof n=="boolean"?{boolValue:n}:typeof n=="number"?Number.isInteger(n)?{intValue:String(n)}:{doubleValue:n}:{stringValue:n}}))}var Vz=e=>({name:"terragucci",version:e});function Zz(e,t,n){return{resourceSpans:[{resource:{attributes:Fv(t)},scopeSpans:[{scope:Vz(n),spans:e.map(r=>({traceId:r.traceId,spanId:r.spanId,...r.parentSpanId?{parentSpanId:r.parentSpanId}:{},name:r.name,kind:r.kind,startTimeUnixNano:String(r.start),endTimeUnixNano:String(r.end??r.start),attributes:Fv(r.attributes),status:r.error?{code:2,message:r.error}:{code:1}}))}]}]}}function zv(e,t,n,r=_s()){let o=new Map;for(let s of e)o.set(s.name,[...o.get(s.name)??[],s]);return{resourceMetrics:[{resource:{attributes:Fv(t)},scopeMetrics:[{scope:Vz(n),metrics:[...o.values()].map(s=>({name:s[0].name,unit:s[0].unit,description:s[0].description,gauge:{dataPoints:s.map(i=>({attributes:Fv(i.attributes),timeUnixNano:String(r),asDouble:i.value}))}}))}]}]}}async function Am(e,t,n=fetch){try{let r=await n(e.url,{method:"POST",headers:{...e.headers,"content-type":"application/json"},body:JSON.stringify(t),signal:AbortSignal.timeout(1e4)});return r.ok?void 0:`${e.url} answered ${r.status}: ${(await r.text()).slice(0,200)}`}catch(r){return`${e.url}: ${r.message}`}}function Uv(e,t,n,r){let o={...Lv(e.OTEL_RESOURCE_ATTRIBUTES),"terragucci.trace_id":t.traceId,"terragucci.span_id":n.spanId,"terragucci.root":r};return{...e,TRACEPARENT:t.traceparent(n),OTEL_TRACES_EXPORTER:e.OTEL_TRACES_EXPORTER||"otlp",OTEL_RESOURCE_ATTRIBUTES:Gue(o)}}var sn={stageDuration:"terragucci_stage_duration_seconds",lastRun:"terragucci_last_run_seconds",rootsPlanned:"terragucci_roots_planned",rootsChanged:"terragucci_roots_changed",planGroups:"terragucci_plan_groups",planChanges:"terragucci_plan_changes",rootPlan:"terragucci_root_plan_seconds",rootApply:"terragucci_root_apply_seconds",binaryVersion:"terragucci_binary_version",tips:"terragucci_tips",modulePin:"terragucci_module_pin",providerInit:"terragucci_provider_init_seconds",lockWait:"terragucci_lock_wait_seconds",resource:"terragucci_resource_seconds",driftRoots:"terragucci_drift_roots",driftSince:"terragucci_drift_since_seconds",driftClear:"terragucci_drift_clear_seconds",waveWaitingSince:"terragucci_wave_waiting_since_seconds",waveSettled:"terragucci_wave_settled_seconds",waveRoots:"terragucci_wave_roots",doraDeployments:"terragucci_dora_deployments_per_week",doraLeadTime:"terragucci_dora_lead_time_seconds",doraFailureRate:"terragucci_dora_change_failure_ratio",doraRestore:"terragucci_dora_restore_seconds"};function wA(e,t){return e===1?"failed":e===3?"waiting":e===4?"refused":t.nothing?"nothing":"applied"}function Yz(e,t,n,r,o){let s=wA(t,n),i={wave:String(e)},a=[o(sn.waveRoots,"{root}","Roots in the wave",n.roots?.length??0,i)];if(s==="waiting"||s==="refused"){let c=n.waitingSince?Date.parse(n.waitingSince)/1e3:r;a.push(o(sn.waveWaitingSince,"s","When the wave started waiting for an approval, in Unix seconds",c,i))}else a.push(o(sn.waveSettled,"s","When the wave last stopped waiting, in Unix seconds",r,i));return a}function Xz(e){let t=[];for(let n of e){let r=n.plan?.configuration?.root_module?.module_calls??{};for(let o of Object.values(r)){let s=o.source??"";if(!s||s.startsWith(".")||s.startsWith("/"))continue;let[i,a=""]=s.split("?"),c=new URLSearchParams(a).get("ref")??new URLSearchParams(a).get("tag"),u=c?hc(c):o.version_constraint;u&&t.push({root:n.path,module:i.replace(/^git::/,"").replace(/\.git(\/\/|$)/,"$1"),version:u})}}return t}var Bv=(e,t)=>Number(t-e)/1e9;function Uue(e,t){let n=zue(e,["version","-json"],{encoding:"utf-8",env:t});try{let r=JSON.parse(n.stdout).terraform_version;return typeof r=="string"?r:void 0}catch{return}}var sl=class{constructor(t,n,r){this.telemetry=t;this.stage=n;let o=r.TG_PR?.trim();o&&(this.pullRequest=o),t?.traces&&(this.trace=new Gv(r.TRACEPARENT),this.stageSpan=this.trace.start(`terragucci ${n}`,void 0,{"terragucci.stage":n}))}telemetry;stage;trace;stageSpan;start=_s();roots=[];receiver;units=new Map;unitSpans=new Map;pins=[];drift;reportUrl;wave;pullRequest;async collectSpans(t){let n=new Nb(this.trace?this.telemetry?.traces:void 0),r=await n.listen();r?t(`timings: the binary's spans are not collected (${r})`):this.receiver=n}root(t){let n={path:t,start:_s(),commands:[],idle:0n};return this.trace&&(n.span=this.trace.start(`root ${t}`,this.stageSpan,{"terragucci.root":t})),this.roots.push(n),n}endRoot(t){t.end=_s(),t.span&&(t.span.end=t.end)}reopen(t){t.end!==void 0&&(t.idle+=_s()-t.end,t.end=void 0)}unitTimed(t,n,r){if(!Number.isFinite(n)||n<0)return;this.units.set(t,n);let o=this.unitSpans.get(t);o&&r&&(o.start=BigInt(Math.round(r.start*1e3))*1000n,o.end=BigInt(Math.round(r.end*1e3))*1000n)}unitEnv(t,n,r){let o=this.unitSpans.get(t);this.trace&&!o&&(o=this.trace.start(`root ${t}`,this.stageSpan,{"terragucci.root":t}),this.unitSpans.set(t,o));let s=o?Uv(r,this.trace,o,t):r,i={};for(let[a,c]of Object.entries(s))c!==void 0&&r[a]!==c&&(i[a]=c);return{...i,...this.receiver?.exporter(t,n)??{}}}command(t,n,r,o,s){if(!this.trace||!t.span)return s(o);let i=this.trace.start(`${n} ${r[0]}`,t.span,{"process.executable.name":n,"process.command_args":r.join(" ")},3),a=s(Uv(o,this.trace,i,t.path));return this.trace.end(i,{"process.exit.code":a.status??-1},a.status===0?void 0:a.error?.message??`exit ${a.status}`),a}async commandAsync(t,n,r,o,s){let i=_s(),a=this.trace&&t.span?this.trace.start(`${n} ${r[0]}`,t.span,{"process.executable.name":n,"process.command_args":r.join(" ")},3):void 0,c=a?Uv(o,this.trace,a,t.path):o;this.receiver&&(c=this.receiver.env(c,t.path,r[0]));let u=await s(c);return t.commands.push({command:r[0],seconds:Bv(i,_s())}),a&&this.trace.end(a,{"process.exit.code":u.status??-1},u.status===0?void 0:u.error?.message??`exit ${u.status}`),u}addTimings(t,n=["plan"]){let r=t.run.binary.split("/").pop()||t.run.binary;for(let s of t.roots){let i=this.roots.find(l=>l.path===s.path);if(!i||i.end===void 0){let l=this.units.get(s.path);l!==void 0&&(s.timings=OE(this.receiver?.spansOf(s.path,"plan")??[],{binary:r,seconds:l,planSeconds:l,source:"terragrunt"}));continue}let a=l=>i.commands.filter(d=>d.command===l).reduce((d,p)=>(d??0)+p.seconds,void 0),c=a("plan"),u=n.includes("apply")?a("apply"):void 0;s.timings=OE(n.flatMap(l=>this.receiver?.spansOf(i.path,l)??[]),{binary:r,seconds:Bv(i.start,i.end)-Number(i.idle)/1e9,...c!==void 0?{planSeconds:c}:{},...u!==void 0?{applySeconds:u}:{}})}let o=t.roots.length>0&&t.roots.every(s=>s.timings===void 0);t.timings=H1(t.roots,o?"Terragrunt ran the binary for these units and its run report gave no times, so the report has no per-unit timings":void 0)}gauges(t,n,r){let o=t.run.project,s=this.stage,i=t.roots.some(l=>l.status==="failed")?"failure":"success",a=s==="tf-plan"&&this.pullRequest?{pull_request:this.pullRequest}:{},c=(l,d,p,f,g)=>({name:l,unit:d,description:p,value:f,attributes:{project:o,stage:s,...a,...g}}),u=[c("terragucci_stage_duration_seconds","s","How long the stage ran",Bv(this.start,r),{result:i}),c("terragucci_roots_planned","{root}","Roots the stage planned, failed ones included",t.roots.length,{}),c("terragucci_plan_groups","{group}","Groups the plans fold into",t.groups.length,{})];for(let l of this.roots)l.end!==void 0&&u.push(c(sn.rootPlan,"s","How long one root took to plan",Bv(l.start,l.end),{root:l.path}));for(let[l,d]of Object.entries(t.totals))u.push(c("terragucci_plan_changes","{change}","Proposed changes by action",d,{action:l}));return n&&u.push(c("terragucci_binary_version","","The binary and version a stage ran",1,{binary:t.run.binary,version:n})),[...u,...this.dashboardGauges(t,r,c)]}applyGauges(t,n){let r=t.run.project,o=this.stage,s=(c,u,l,d,p)=>({name:c,unit:u,description:l,value:d,attributes:{project:r,stage:o,...p}}),i=this.wave?{wave:String(this.wave.number)}:{},a=[];for(let c of t.roots){let u=c.timings?.apply_seconds;u!==void 0&&a.push(s(sn.rootApply,"s","How long one root took to apply",u,{...i,root:c.path}))}return a.push(...this.initAndLockGauges(t,s)),this.wave&&a.push(...Yz(this.wave.number,this.wave.code??1,this.wave.facts,Number(n/1000000n)/1e3,s)),a}initAndLockGauges(t,n){let r=[];for(let o of t.roots){for(let i of o.timings?.provider_init??[])r.push(n(sn.providerInit,"s","Time spent starting a provider in a root",i.ms/1e3,{root:o.path,provider:i.provider}));let s=(o.timings?.lock_waits??[]).reduce((i,a)=>i+a.ms,0);o.timings?.lock_waits?.length&&r.push(n(sn.lockWait,"s","Time spent waiting for a state lock in a root",s/1e3,{root:o.path}))}return r}dashboardGauges(t,n,r){let o=Number(n/1000000n)/1e3,s=[r(sn.lastRun,"s","When the stage last ran, in Unix seconds",o,{}),r(sn.rootsChanged,"{root}","Roots whose plan changes something",t.roots.filter(i=>i.status==="planned"&&i.changes.length>0).length,{})];if(t.tips){let i=new Map;for(let a of t.tips)i.set(a.rule,(i.get(a.rule)??0)+1);for(let[a,c]of i)s.push(r(sn.tips,"{tip}","Tips the run gave, by rule",c,{rule:a}))}for(let i of this.pins)s.push(r(sn.modulePin,"","A module call's pin in a root",1,{root:i.root,module:i.module,version:i.version}));s.push(...this.initAndLockGauges(t,r));for(let i of t.timings?.resources??[])s.push(r(sn.resource,"s","How long one of the run's slowest resources took",i.ms/1e3,{root:i.root,address:i.address}));if(t.run.stage==="tf-drift"){let i=np(t);s.push(r(sn.driftRoots,"{root}","Roots the drift run found drifted",i.roots,{})),i.roots>0?s.push(r(sn.driftSince,"s","When the open drift was first found, in Unix seconds",Date.parse(this.drift?.since??t.run.finished)/1e3,{})):i.failed===0&&s.push(r(sn.driftClear,"s","When a drift run last found no drift, in Unix seconds",o,{}))}return s}async finish(t,n,r,o){if(this.receiver){for(let p of this.receiver.unreadable)r(`timings: a batch of spans could not be read: ${p}`);let d=await this.receiver.close();d.length&&r(`telemetry: ${d.length} of the binary's span batches were not forwarded: ${d[0]}`)}let s=this.telemetry;if(!s)return;for(let d of s.skipped)r(`telemetry: ${d}`);let i=_s(),a=t.run,c={...s.resource,"service.version":a.terragucci,"terragucci.project":a.project},u={...c,"vcs.ref.head.revision":a.commit,...a.job_url?{"cicd.pipeline.run.url.full":a.job_url}:{}},l=[];if(s.traces&&this.trace&&this.stageSpan){let d=t.roots.filter(m=>m.status==="failed").length,p=this.wave?wA(this.wave.code??1,this.wave.facts):d?"failure":"success";this.trace.end(this.stageSpan,{"terragucci.project":a.project,"vcs.ref.head.revision":a.commit,"terragucci.binary":a.binary,"terragucci.runtime":a.runtime,"terragucci.change_set":t.change_set,"terragucci.roots":t.roots.length,"terragucci.roots_failed":d,"terragucci.result":p,...this.wave?{"terragucci.wave":this.wave.number}:{},...this.reportUrl?{"terragucci.report.url":this.reportUrl}:{},...a.job_url?{"cicd.pipeline.run.url.full":a.job_url}:{},...Object.fromEntries(Object.entries(t.totals).map(([m,h])=>[`terragucci.plan.${m}`,h]))},d?`${d} root(s) failed`:p==="failed"?`wave ${this.wave?.number} failed`:void 0);for(let m of this.unitSpans.values())m.end??=i;let f=[...this.roots.flatMap(m=>m.span?[{path:m.path,span:m.span}]:[]),...[...this.unitSpans].map(([m,h])=>({path:m,span:h}))];for(let m of t.waves){let h=f.filter(w=>m.roots.includes(w.path)&&w.span.end!==void 0);if(h.length===0)continue;let y=this.trace.start(`wave ${m.number}`,this.stageSpan,{"terragucci.wave":m.number,"terragucci.set_digest":m.set_digest??void 0,"terragucci.approval":m.approval});y.start=h.reduce((w,b)=>b.span.start<w?b.span.start:w,h[0].span.start),y.end=h.reduce((w,b)=>b.span.end>w?b.span.end:w,h[0].span.end);for(let w of h)w.span.parentSpanId=y.spanId}for(let m of f){let h=t.roots.find(y=>y.path===m.path);h&&(Object.assign(m.span.attributes,{"terragucci.status":h.status,"terragucci.plan_digest":h.plan_digest??void 0,...Object.fromEntries(Object.entries(h.counts).map(([y,w])=>[`terragucci.plan.${y}`,w]))}),h.error&&(m.span.error=h.error.split(`
|
|
501
|
+
`);v=(await dc(r,c,{head:y,base:$,title:w,body:_})).url}catch($){if(!($ instanceof ei)&&!($ instanceof Zr)&&!($ instanceof TypeError))throw $;return await u(`pushed the drift agent's change to \`${y}\`, and could not open its pull request (${Rc($.message)}).`),{opened:!1,fail:!0,reason:`could not open the pull request (${$.message})`,commit:b,paths:m}}return await u(`the drift agent opened ${v}, changing ${m.map(hA).join(", ")}. It plans like any other pull request; nothing was applied, approved or merged.`),{opened:!0,reason:`opened ${v} from ${y}`,pullRequest:v,commit:b,paths:m}}import{spawnSync as zue}from"node:child_process";import{randomBytes as Lue}from"node:crypto";function Lv(e){let t={};for(let n of(e??"").split(",")){let r=n.indexOf("=");if(r<=0)continue;let o=n.slice(0,r).trim(),s=n.slice(r+1).trim();try{s=decodeURIComponent(s)}catch{}o&&(t[o]=s)}return t}var Gue=e=>Object.entries(e).map(([t,n])=>`${t}=${encodeURIComponent(n)}`).join(",");function Jz(e,t,n){let r=t.toUpperCase(),o=e[`OTEL_${r}_EXPORTER`];if(o!==void 0&&o.trim()!==""&&!o.split(",").map(u=>u.trim()).includes("otlp"))return;let s=e[`OTEL_EXPORTER_OTLP_${r}_ENDPOINT`]?.trim(),i=e.OTEL_EXPORTER_OTLP_ENDPOINT?.trim(),a=s||(i?`${i.replace(/\/+$/,"")}/v1/${t}`:void 0);if(!a)return;if((e[`OTEL_EXPORTER_OTLP_${r}_PROTOCOL`]??e.OTEL_EXPORTER_OTLP_PROTOCOL)==="grpc"){n.push(`${t}: terragucci sends OTLP over HTTP and the protocol is grpc; point it at the collector's HTTP port`);return}if(!/^https?:\/\//.test(a)){n.push(`${t}: ${a} is not an http or https URL`);return}return{url:a,headers:{...Lv(e.OTEL_EXPORTER_OTLP_HEADERS),...Lv(e[`OTEL_EXPORTER_OTLP_${r}_HEADERS`])}}}function ol(e){if(e.OTEL_SDK_DISABLED?.trim().toLowerCase()==="true")return;let t=[],n=Jz(e,"traces",t),r=Jz(e,"metrics",t);if(!n&&!r&&t.length===0)return;let o={...Lv(e.OTEL_RESOURCE_ATTRIBUTES)};return o["service.name"]=e.OTEL_SERVICE_NAME?.trim()||o["service.name"]||"terragucci",{...n?{traces:n}:{},...r?{metrics:r}:{},resource:o,skipped:t}}var _s=()=>BigInt(Math.round((performance.timeOrigin+performance.now())*1e3))*1000n,Kz=e=>Lue(e).toString("hex");function Fue(e){let t=/^[\da-f]{2}-([\da-f]{32})-([\da-f]{16})-[\da-f]{2}$/.exec((e??"").trim().toLowerCase());if(!(!t||/^0+$/.test(t[1])||/^0+$/.test(t[2])))return{traceId:t[1],spanId:t[2]}}var Gv=class{traceId;parent;spans=[];constructor(t){let n=Fue(t);this.traceId=n?.traceId??Kz(16),this.parent=n?.spanId}start(t,n,r={},o=1){let s={traceId:this.traceId,spanId:Kz(8),name:t,kind:o,start:_s(),attributes:{...r}},i=n?.spanId??this.parent;return i&&(s.parentSpanId=i),this.spans.push(s),s}end(t,n={},r){t.end=_s(),Object.assign(t.attributes,n),r&&(t.error=r)}traceparent(t){return`00-${this.traceId}-${t.spanId}-01`}};function Fv(e){return Object.entries(e).filter(t=>t[1]!==void 0).map(([t,n])=>({key:t,value:typeof n=="boolean"?{boolValue:n}:typeof n=="number"?Number.isInteger(n)?{intValue:String(n)}:{doubleValue:n}:{stringValue:n}}))}var Vz=e=>({name:"terragucci",version:e});function Zz(e,t,n){return{resourceSpans:[{resource:{attributes:Fv(t)},scopeSpans:[{scope:Vz(n),spans:e.map(r=>({traceId:r.traceId,spanId:r.spanId,...r.parentSpanId?{parentSpanId:r.parentSpanId}:{},name:r.name,kind:r.kind,startTimeUnixNano:String(r.start),endTimeUnixNano:String(r.end??r.start),attributes:Fv(r.attributes),status:r.error?{code:2,message:r.error}:{code:1}}))}]}]}}function zv(e,t,n,r=_s()){let o=new Map;for(let s of e)o.set(s.name,[...o.get(s.name)??[],s]);return{resourceMetrics:[{resource:{attributes:Fv(t)},scopeMetrics:[{scope:Vz(n),metrics:[...o.values()].map(s=>({name:s[0].name,unit:s[0].unit,description:s[0].description,gauge:{dataPoints:s.map(i=>({attributes:Fv(i.attributes),timeUnixNano:String(r),asDouble:i.value}))}}))}]}]}}async function Am(e,t,n=fetch){try{let r=await n(e.url,{method:"POST",headers:{...e.headers,"content-type":"application/json"},body:JSON.stringify(t),signal:AbortSignal.timeout(1e4)});return r.ok?void 0:`${e.url} answered ${r.status}: ${(await r.text()).slice(0,200)}`}catch(r){return`${e.url}: ${r.message}`}}function Uv(e,t,n,r){let o={...Lv(e.OTEL_RESOURCE_ATTRIBUTES),"terragucci.trace_id":t.traceId,"terragucci.span_id":n.spanId,"terragucci.root":r};return{...e,TRACEPARENT:t.traceparent(n),OTEL_TRACES_EXPORTER:e.OTEL_TRACES_EXPORTER||"otlp",OTEL_RESOURCE_ATTRIBUTES:Gue(o)}}var sn={stageDuration:"terragucci_stage_duration_seconds",lastRun:"terragucci_last_run_seconds",rootsPlanned:"terragucci_roots_planned",rootsChanged:"terragucci_roots_changed",planGroups:"terragucci_plan_groups",planChanges:"terragucci_plan_changes",rootPlan:"terragucci_root_plan_seconds",rootApply:"terragucci_root_apply_seconds",binaryVersion:"terragucci_binary_version",tips:"terragucci_tips",modulePin:"terragucci_module_pin",providerInit:"terragucci_provider_init_seconds",lockWait:"terragucci_lock_wait_seconds",resource:"terragucci_resource_seconds",driftRoots:"terragucci_drift_roots",driftSince:"terragucci_drift_since_seconds",driftClear:"terragucci_drift_clear_seconds",waveWaitingSince:"terragucci_wave_waiting_since_seconds",waveSettled:"terragucci_wave_settled_seconds",waveRoots:"terragucci_wave_roots",doraDeployments:"terragucci_dora_deployments_per_week",doraLeadTime:"terragucci_dora_lead_time_seconds",doraFailureRate:"terragucci_dora_change_failure_ratio",doraRestore:"terragucci_dora_restore_seconds"};function wA(e,t){return e===1?"failed":e===3?"waiting":e===4?"refused":t.nothing?"nothing":"applied"}function Yz(e,t,n,r,o){let s=wA(t,n),i={wave:String(e)},a=[o(sn.waveRoots,"{root}","Roots in the wave",n.roots?.length??0,i)];if(s==="waiting"||s==="refused"){let c=n.waitingSince?Date.parse(n.waitingSince)/1e3:r;a.push(o(sn.waveWaitingSince,"s","When the wave started waiting for an approval, in Unix seconds",c,i))}else a.push(o(sn.waveSettled,"s","When the wave last stopped waiting, in Unix seconds",r,i));return a}function Xz(e){let t=[];for(let n of e){let r=n.plan?.configuration?.root_module?.module_calls??{};for(let o of Object.values(r)){let s=o.source??"";if(!s||s.startsWith(".")||s.startsWith("/"))continue;let[i,a=""]=s.split("?"),c=new URLSearchParams(a).get("ref")??new URLSearchParams(a).get("tag"),u=c?hc(c):o.version_constraint;u&&t.push({root:n.path,module:i.replace(/^git::/,"").replace(/\.git(\/\/|$)/,"$1"),version:u})}}return t}var Bv=(e,t)=>Number(t-e)/1e9;function Uue(e,t){let n=zue(e,["version","-json"],{encoding:"utf-8",env:t});try{let r=JSON.parse(n.stdout).terraform_version;return typeof r=="string"?r:void 0}catch{return}}var sl=class{constructor(t,n,r){this.telemetry=t;this.stage=n;let o=r.TG_PR?.trim();o&&(this.pullRequest=o),t?.traces&&(this.trace=new Gv(r.TRACEPARENT),this.stageSpan=this.trace.start(`terragucci ${n}`,void 0,{"terragucci.stage":n}))}telemetry;stage;trace;stageSpan;start=_s();roots=[];receiver;units=new Map;unitSpans=new Map;pins=[];drift;reportUrl;wave;pullRequest;async collectSpans(t){let n=new Nb(this.trace?this.telemetry?.traces:void 0),r=await n.listen();r?t(`timings: the binary's spans are not collected (${r})`):this.receiver=n}root(t){let n={path:t,start:_s(),commands:[],idle:0n};return this.trace&&(n.span=this.trace.start(`root ${t}`,this.stageSpan,{"terragucci.root":t})),this.roots.push(n),n}endRoot(t){t.end=_s(),t.span&&(t.span.end=t.end)}reopen(t){t.end!==void 0&&(t.idle+=_s()-t.end,t.end=void 0)}unitTimed(t,n,r){if(!Number.isFinite(n)||n<0)return;this.units.set(t,n);let o=this.unitSpans.get(t);o&&r&&(o.start=BigInt(Math.round(r.start*1e3))*1000n,o.end=BigInt(Math.round(r.end*1e3))*1000n)}unitEnv(t,n,r){let o=this.unitSpans.get(t);this.trace&&!o&&(o=this.trace.start(`root ${t}`,this.stageSpan,{"terragucci.root":t}),this.unitSpans.set(t,o));let s=o?Uv(r,this.trace,o,t):r,i={};for(let[a,c]of Object.entries(s))c!==void 0&&r[a]!==c&&(i[a]=c);return{...i,...this.receiver?.exporter(t,n)??{}}}command(t,n,r,o,s){if(!this.trace||!t.span)return s(o);let i=this.trace.start(`${n} ${r[0]}`,t.span,{"process.executable.name":n,"process.command_args":r.join(" ")},3),a=s(Uv(o,this.trace,i,t.path));return this.trace.end(i,{"process.exit.code":a.status??-1},a.status===0?void 0:a.error?.message??`exit ${a.status}`),a}async commandAsync(t,n,r,o,s){let i=_s(),a=this.trace&&t.span?this.trace.start(`${n} ${r[0]}`,t.span,{"process.executable.name":n,"process.command_args":r.join(" ")},3):void 0,c=a?Uv(o,this.trace,a,t.path):o;this.receiver&&(c=this.receiver.env(c,t.path,r[0]));let u=await s(c);return t.commands.push({command:r[0],seconds:Bv(i,_s())}),a&&this.trace.end(a,{"process.exit.code":u.status??-1},u.status===0?void 0:u.error?.message??`exit ${u.status}`),u}addTimings(t,n=["plan"]){let r=t.run.binary.split("/").pop()||t.run.binary;for(let s of t.roots){let i=this.roots.find(l=>l.path===s.path);if(!i||i.end===void 0){let l=this.units.get(s.path);l!==void 0&&(s.timings=OE(this.receiver?.spansOf(s.path,"plan")??[],{binary:r,seconds:l,planSeconds:l,source:"terragrunt"}));continue}let a=l=>i.commands.filter(d=>d.command===l).reduce((d,p)=>(d??0)+p.seconds,void 0),c=a("plan"),u=n.includes("apply")?a("apply"):void 0;s.timings=OE(n.flatMap(l=>this.receiver?.spansOf(i.path,l)??[]),{binary:r,seconds:Bv(i.start,i.end)-Number(i.idle)/1e9,...c!==void 0?{planSeconds:c}:{},...u!==void 0?{applySeconds:u}:{}})}let o=t.roots.length>0&&t.roots.every(s=>s.timings===void 0);t.timings=HL(t.roots,o?"Terragrunt ran the binary for these units and its run report gave no times, so the report has no per-unit timings":void 0)}gauges(t,n,r){let o=t.run.project,s=this.stage,i=t.roots.some(l=>l.status==="failed")?"failure":"success",a=s==="tf-plan"&&this.pullRequest?{pull_request:this.pullRequest}:{},c=(l,d,p,f,g)=>({name:l,unit:d,description:p,value:f,attributes:{project:o,stage:s,...a,...g}}),u=[c("terragucci_stage_duration_seconds","s","How long the stage ran",Bv(this.start,r),{result:i}),c("terragucci_roots_planned","{root}","Roots the stage planned, failed ones included",t.roots.length,{}),c("terragucci_plan_groups","{group}","Groups the plans fold into",t.groups.length,{})];for(let l of this.roots)l.end!==void 0&&u.push(c(sn.rootPlan,"s","How long one root took to plan",Bv(l.start,l.end),{root:l.path}));for(let[l,d]of Object.entries(t.totals))u.push(c("terragucci_plan_changes","{change}","Proposed changes by action",d,{action:l}));return n&&u.push(c("terragucci_binary_version","","The binary and version a stage ran",1,{binary:t.run.binary,version:n})),[...u,...this.dashboardGauges(t,r,c)]}applyGauges(t,n){let r=t.run.project,o=this.stage,s=(c,u,l,d,p)=>({name:c,unit:u,description:l,value:d,attributes:{project:r,stage:o,...p}}),i=this.wave?{wave:String(this.wave.number)}:{},a=[];for(let c of t.roots){let u=c.timings?.apply_seconds;u!==void 0&&a.push(s(sn.rootApply,"s","How long one root took to apply",u,{...i,root:c.path}))}return a.push(...this.initAndLockGauges(t,s)),this.wave&&a.push(...Yz(this.wave.number,this.wave.code??1,this.wave.facts,Number(n/1000000n)/1e3,s)),a}initAndLockGauges(t,n){let r=[];for(let o of t.roots){for(let i of o.timings?.provider_init??[])r.push(n(sn.providerInit,"s","Time spent starting a provider in a root",i.ms/1e3,{root:o.path,provider:i.provider}));let s=(o.timings?.lock_waits??[]).reduce((i,a)=>i+a.ms,0);o.timings?.lock_waits?.length&&r.push(n(sn.lockWait,"s","Time spent waiting for a state lock in a root",s/1e3,{root:o.path}))}return r}dashboardGauges(t,n,r){let o=Number(n/1000000n)/1e3,s=[r(sn.lastRun,"s","When the stage last ran, in Unix seconds",o,{}),r(sn.rootsChanged,"{root}","Roots whose plan changes something",t.roots.filter(i=>i.status==="planned"&&i.changes.length>0).length,{})];if(t.tips){let i=new Map;for(let a of t.tips)i.set(a.rule,(i.get(a.rule)??0)+1);for(let[a,c]of i)s.push(r(sn.tips,"{tip}","Tips the run gave, by rule",c,{rule:a}))}for(let i of this.pins)s.push(r(sn.modulePin,"","A module call's pin in a root",1,{root:i.root,module:i.module,version:i.version}));s.push(...this.initAndLockGauges(t,r));for(let i of t.timings?.resources??[])s.push(r(sn.resource,"s","How long one of the run's slowest resources took",i.ms/1e3,{root:i.root,address:i.address}));if(t.run.stage==="tf-drift"){let i=np(t);s.push(r(sn.driftRoots,"{root}","Roots the drift run found drifted",i.roots,{})),i.roots>0?s.push(r(sn.driftSince,"s","When the open drift was first found, in Unix seconds",Date.parse(this.drift?.since??t.run.finished)/1e3,{})):i.failed===0&&s.push(r(sn.driftClear,"s","When a drift run last found no drift, in Unix seconds",o,{}))}return s}async finish(t,n,r,o){if(this.receiver){for(let p of this.receiver.unreadable)r(`timings: a batch of spans could not be read: ${p}`);let d=await this.receiver.close();d.length&&r(`telemetry: ${d.length} of the binary's span batches were not forwarded: ${d[0]}`)}let s=this.telemetry;if(!s)return;for(let d of s.skipped)r(`telemetry: ${d}`);let i=_s(),a=t.run,c={...s.resource,"service.version":a.terragucci,"terragucci.project":a.project},u={...c,"vcs.ref.head.revision":a.commit,...a.job_url?{"cicd.pipeline.run.url.full":a.job_url}:{}},l=[];if(s.traces&&this.trace&&this.stageSpan){let d=t.roots.filter(m=>m.status==="failed").length,p=this.wave?wA(this.wave.code??1,this.wave.facts):d?"failure":"success";this.trace.end(this.stageSpan,{"terragucci.project":a.project,"vcs.ref.head.revision":a.commit,"terragucci.binary":a.binary,"terragucci.runtime":a.runtime,"terragucci.change_set":t.change_set,"terragucci.roots":t.roots.length,"terragucci.roots_failed":d,"terragucci.result":p,...this.wave?{"terragucci.wave":this.wave.number}:{},...this.reportUrl?{"terragucci.report.url":this.reportUrl}:{},...a.job_url?{"cicd.pipeline.run.url.full":a.job_url}:{},...Object.fromEntries(Object.entries(t.totals).map(([m,h])=>[`terragucci.plan.${m}`,h]))},d?`${d} root(s) failed`:p==="failed"?`wave ${this.wave?.number} failed`:void 0);for(let m of this.unitSpans.values())m.end??=i;let f=[...this.roots.flatMap(m=>m.span?[{path:m.path,span:m.span}]:[]),...[...this.unitSpans].map(([m,h])=>({path:m,span:h}))];for(let m of t.waves){let h=f.filter(w=>m.roots.includes(w.path)&&w.span.end!==void 0);if(h.length===0)continue;let y=this.trace.start(`wave ${m.number}`,this.stageSpan,{"terragucci.wave":m.number,"terragucci.set_digest":m.set_digest??void 0,"terragucci.approval":m.approval});y.start=h.reduce((w,b)=>b.span.start<w?b.span.start:w,h[0].span.start),y.end=h.reduce((w,b)=>b.span.end>w?b.span.end:w,h[0].span.end);for(let w of h)w.span.parentSpanId=y.spanId}for(let m of f){let h=t.roots.find(y=>y.path===m.path);h&&(Object.assign(m.span.attributes,{"terragucci.status":h.status,"terragucci.plan_digest":h.plan_digest??void 0,...Object.fromEntries(Object.entries(h.counts).map(([y,w])=>[`terragucci.plan.${y}`,w]))}),h.error&&(m.span.error=h.error.split(`
|
|
502
502
|
`)[0]))}let g=await Am(s.traces,Zz(this.trace.spans,u,a.terragucci??"0.0.0"),o);g?r(`telemetry: the trace was not sent: ${g}`):l.push(`trace ${this.trace.traceId} (${this.trace.spans.length} spans)`)}if(s.metrics){let d=this.stage==="tf-apply"?this.applyGauges(t,i):this.gauges(t,Uue(a.binary,n),i),p=await Am(s.metrics,zv(d,c,a.terragucci??"0.0.0",i),o);p?r(`telemetry: the metrics were not sent: ${p}`):l.push(`${d.length} metric points`)}l.length&&r(`telemetry: sent ${l.join(" and ")}`)}};import{createHash as qle}from"node:crypto";import{existsSync as Gm,mkdirSync as DU,readdirSync as Jle,readFileSync as ya,rmSync as MU,statSync as Kle,writeFileSync as fi}from"node:fs";import{dirname as Vle,join as lt,relative as LA,resolve as ip}from"node:path";import{spawn as ile,spawnSync as $U}from"node:child_process";import{createHash as kU,randomUUID as ale}from"node:crypto";import{chmodSync as cle,existsSync as Ec,mkdirSync as Jv,mkdtempSync as _U,readdirSync as SU,readFileSync as op,rmSync as RU,writeFileSync as sp}from"node:fs";import{hostname as ule,tmpdir as lle}from"node:os";import{basename as dle,dirname as ple,join as It,resolve as fle}from"node:path";import{readFileSync as Bue,writeFileSync as Wue}from"node:fs";import{join as Qz,resolve as eU}from"node:path";var Hue=e=>/(^|\/)choudoufu[^/]*$/.test(e);async function que(e,t,n,r){let o=await e(t,["init","-input=false","-no-color"],r.dir,r.init?.init??r.env);if(o.code!==0)throw new S(`init in ${n} failed: ${eo(o.out)}`)}function Jue(e,t){let n=Go(Qz(e,t)).estate;if(!n)throw new S(`${t} has a live block that names no estate`);return n}var Kue=e=>{let t=e.lastIndexOf(": ");return{address:e.slice(0,t),actions:e.slice(t+2)}},Vue=(e,t,n)=>t.map(Kue).filter(r=>r.actions===n&&(r.address===e||r.address.startsWith(`${e}[`))).map(r=>r.address).sort();async function tU(e,t,n,r,o,s){let i=await e(t,["live-mv","-no-color","-json",...s?["-dry-run"]:[],`-from-estate=${r}`,o,o],n.dir,n.env),a;try{a=JSON.parse(i.stdout)}catch{a=void 0}let c=a?.refusal?[a.refusal.summary,a.refusal.detail].filter(Boolean).join(": "):void 0;if(i.code!==0||!a||c)throw new S(`live-mv ${s?"-dry-run ":""}refuses ${o}: ${c??eo(i.out)}`);return a}async function Zue(e,t,n,r,o){let s=await tU(e,t,n,r.from_estate,r.address,!1);if(s.written!==!0)throw new S(`live-mv wrote nothing on ${r.address}`);if(s.resource?.live_id!==r.live_id)throw new S(`live-mv rewrote ${s.resource?.live_id??"an unnamed resource"} for ${r.address}, not ${r.live_id}`);o(`${r.address}: retagged from estate ${r.from_estate} to estate ${r.to_estate}`)}function Yue(e){let t=[],n;for(let r of e.split(`
|
|
503
503
|
`)){let o=/^([A-Z_]+) \(\d+\) - /.exec(r);if(o){n=["VERIFIED","DRIFTED","MISSING","UNTAGGABLE","UNADMITTED_TYPE"].includes(o[1])?o[1]:void 0;continue}if(!r.trim())continue;let s=/^ {2}(\S+)\s+(\S+)\s+live id: (\S+)\s*$/.exec(r);n&&s&&t.push({address:s[1],type:s[2],status:n,...s[3]!=="-"?{live_id:s[3]}:{}}),/eligible for stamping/.test(r)&&(n=void 0)}return t}var bA=["VERIFIED","UNTAGGABLE"];function Xue(e){let t=/(\d+) failed, \d+ skipped\./.exec(e);return t?Number(t[1]):void 0}var Que="-approve";async function nU(e,t,n,r,o,s){let i=await e(t,["live-import","-no-color",`-state=${eU(r)}`,`-estate=${o}`,...s?[Que]:[]],n.dir,n.env);if(i.code!==0)throw new S(`live-import ${s?"could not stamp":"could not verify"} ${r.replace(/^.*\//,"")}: ${eo(i.out)}`);return i.stdout}async function rU(e,t,n){let r=n.exec??li,o=n.env??process.env;if(!Hue(n.binary))throw new S(`migration ${t.name} cannot run: its roots keep their resources under live markers, which only choudoufu reads, and this job runs ${n.binary}`);if(t.kind==="revert")throw new S(`migration ${t.name} cannot run: an estate keeps no state file to put back; move the resources back with a moves migration the other way`);let s=[...new Set(t.kind==="backends"?t.backends.map(u=>u.root):t.moves.flatMap(u=>[u.from,u.to]))].sort(),i=await $A(e,s,n,o,et(e,ue({}))),a=new Map(s.map(u=>[u,Jue(e,u)]));for(let u of s)await que(r,n.binary,u,i.get(u));let c=new Map;for(let[u,l]of s.entries()){let d=i.get(l);c.set(l,await Om(r,n.binary,d.dir,d.env,n.work,`estate-${u}`,!0))}return t.kind==="moves"?ele(t,s,a,c,i,r,n):tle(e,t,s,a,c,i,r,n)}async function ele(e,t,n,r,o,s,i){let a=i.log??(()=>{}),c=[],u=[];for(let p of e.moves){let f=n.get(p.from),g=n.get(p.to);if(f===g){c.push(`${p.from} and ${p.to} are both estate ${f}; a move inside one estate is a moved block`);continue}for(let m of p.addresses){if(/(^|\.)module\.[A-Za-z_][\w-]*$/.test(m)){c.push(`${m} is a module call; a retag names each resource in it`);continue}let h=Vue(m,r.get(p.from).changes,"delete");if(h.length===0){c.push(`${m}: the plan of ${p.from} does not destroy it, so estate ${f} holds no live resource it leaves`);continue}for(let y of h){let w=await tU(s,i.binary,o.get(p.to),f,y,!0);if(!w.resource?.live_id)throw new S(`live-mv -dry-run names no live resource for ${y}`);let b=(w.followers??[]).map(k=>k.address).filter(k=>typeof k=="string").sort();u.push({address:y,from:p.from,to:p.to,from_estate:f,to_estate:g,live_id:w.resource.live_id,followers:b}),a(`${y}: ${w.resource.live_id} moves from estate ${f} to estate ${g}${b.length?`, with ${b.join(", ")}`:""}`)}}}if(c.length>0)throw new S(`migration ${e.name}:
|
|
504
504
|
${c.join(`
|
|
@@ -639,7 +639,7 @@ ${t}EOT`}function Npe(e){let t=[],n=r=>{for(let o of sO(r)){if(_B.has(o)||o.star
|
|
|
639
639
|
`}),i&&(c.set(W,gA(Le)),pa(Le)>0&&u.set(W,nl(Le))),d.push({path:W,plan:i?fA(Le):Le,planner:l,files:{json:oa(W).json},preventDestroy:new Set,terragrunt:ee,...xe.length?{reads:xe}:{},..._e?{binary:_e}:{}});for(let st of xe)s(Lg(W,st));s(i?`${W}: ${pa(Le)===0?"no drift":`${pa(Le)} resource${pa(Le)===1?"":"s"} drifted`}`:`${W}: ${C?"previewed (provisional)":"planned"}`)}},ie=o.steps??[],Q=i?"drift":"plan",ne=new Set,ae=new Map,he=async(Z,me,J)=>{let j=new Map;if(ie.length===0)return j;let z=await nb(ie,Z,me,{repo:e,stage:i?"tf-drift":"tf-plan",env:a,log:s,...J?{planFile:X=>Ne(sm(J,X,J[0].workDir),"plans",X,"tfplan.tfplan")}:{}});for(let[X,V]of z)ae.set(X,[...ae.get(X)??[],...V.runs]),V.holds.length&&ne.add(X),V.error&&j.set(X,V.error);return j},$e=(Z,me,J,j)=>{$.set(Z,{wave:J}),d.push({path:Z,planner:l,error:me,preventDestroy:new Set,...ae.has(Z)?{steps:ae.get(Z)}:{},terragrunt:{...Ui(Z),selection:o.selection(Z),provisional:j,run_result:"not run"}})},ye=[];for(let Z of t){let me=new Set(Z.preview),J=new Map,j=new Map,z=[];for(let G of[...Z.roots,...Z.preview]){if(i){z.push(G);continue}let C=lG(G,k.get(G)??[],zb(e,G,v.get(G)),$);if("why"in C){M(G,Z.number,C);continue}z.push(G),C.reads.length>0&&(J.set(G,C.reads),j.set(G,C.served))}for(let G of new Set([...j.values()].flat()))E.has(G)||E.set(G,await F(G));let X=[...new Set([...J.values()].flat().map(G=>G.upstream))];X.length>0&&x.set(Z.number,ni(o.waveOf,X,Z.number)),Ra(Ne(A,Fb),"");let V=[];for(let G of z.filter(C=>L.has(C)))$e(G,L.get(G),Z.number,me.has(G)),s(`${G}: ${L.get(G).split(`
|
|
640
640
|
`)[0]}`);z=z.filter(G=>!L.has(G));for(let G of["before-init",`before-${Q}`])for(let[C,ee]of await he(G,z.filter(le=>!me.has(le))))$e(C,ee,Z.number,!1),s(`${C}: a step ${G.replace("-"," ")} failed`),z=z.filter(le=>le!==C);for(let G=0;z.length>0&&G<2;G++){let C=Ne(r,`wave-${Z.number}${G?`-${G}`:""}`);try{let ee=d.length,le=await U(z,C);te(le,Z.number,me,J);let _e=await he(`after-${Q}`,z.filter(xe=>!me.has(xe)&&d.slice(ee).some(Le=>Le.path===xe&&Le.plan!==void 0)),le.groups);for(let xe=ee;xe<d.length;xe++){let Le=d[xe],Ot=_e.get(Le.path);d[xe]={...Le,...ae.has(Le.path)?{steps:ae.get(Le.path)}:{},...Ot?{error:Ot}:{}},Ot&&s(`${Le.path}: a step after ${Q} failed`)}z=[]}catch(ee){if(!(ee instanceof Hs)){let _e=ee.message;for(let xe of z)$.set(xe,{wave:Z.number}),d.push({path:xe,planner:l,error:_e,preventDestroy:new Set,terragrunt:{...Ui(xe),selection:o.selection(xe),provisional:me.has(xe),run_result:"not run"}});s(`wave ${Z.number}: ${_e}`),z=[];break}s(`wave ${Z.number}: ${ee.message}`),f.push(...ee.reads.map(_e=>({unit:_e.unit,dependency:_e.dependency,upstream:_e.upstream,reason:_e.reason,..._e.keys?{keys:_e.keys}:{}})));let le=new Set(ee.reads.map(_e=>_e.unit));for(let _e of le)$.set(_e,{wave:Z.number});V.push(...le),z=z.filter(_e=>!le.has(_e))}}if(ye.push(...V),i)continue;for(let[G,C]of aG(LB(Ne(A,UE))))_.set(G,C[0]);let W=uG(LB(Ne(A,Fb)));for(let[G,C]of j){let ee=C.filter(le=>!W.get(le)?.has("plan"));ee.length>0&&$.get(G)?.plan!==void 0&&M(G,Z.number,{after:ee,why:`Terragrunt did not ask the binary for the outputs of ${ee.join(", ")}, so its plan is not on their planned outputs`})}}return{inputs:d,plans:p,redacted:g,mockReads:f,waiting:ye,names:c,unpreviewed:R,waveReads:x,found:u,heldBySteps:ne}}function LB(e){try{return jc(e,"utf-8")}catch{return""}}function GB(e){return Date.parse(e.replace(/(\.\d{3})\d+/,"$1"))}function yO(e){let t=new Map,n;try{n=Jf(jc(e,"utf-8"))}catch{return t}for(let[r,o]of n){if(!o.started||!o.ended)continue;let s=GB(o.started),i=GB(o.ended);Number.isFinite(s)&&Number.isFinite(i)&&i>=s&&t.set(r,{start:s,end:i})}return t}var dO=e=>`'${e.replace(/'/g,"'\\''")}'`;function ife(e,t,n){let r=["#!/bin/sh","# terragucci: each unit's plan sends its spans to the stage. Written for one wave.",'out=""','prev=""','for a in "$@"; do',' case "$a" in -out=*|--out=*) out="${a#*=}" ;; esac',' if [ "$prev" = "-out" ] || [ "$prev" = "--out" ]; then out="$a"; fi',' prev="$a"',"done",'case "$out" in'];for(let[o,s]of n){let i=Object.entries(s);if(i.length===0)continue;let a=[...new Set(t.map(c=>Ne(c,o,"tfplan.tfplan")))].map(dO).join(" | ");r.push(` ${a}) export ${i.map(([c,u])=>`${c}=${dO(u)}`).join(" ")} ;;`)}return r.push("esac",`exec ${dO(e)} "$@"`),r.join(`
|
|
641
641
|
`)+`
|
|
642
|
-
`}function afe(e,t,n,r,o,s){let i=new Map(n.map(p=>[p,o.unitEnv(p,"plan",s)]));if([...i.values()].every(p=>Object.keys(p).length===0))return e;pp(r,{recursive:!0});let a=Ne(Rs(r),"plans"),c=a;try{c=Ne(UB(r),"plans")}catch{}let u=`${Rs(r)}.bin`;pp(u,{recursive:!0});let l=Ne(u,gO(t)||"tofu");Ra(l,ife(t,[a,c],i)),FB(l,493);let d=e??Nn;return(p,f,g)=>d(p,f,{...g,env:{...g.env,TG_TF_PATH:l}})}function cfe(e=Nn,t){return(n,r,o)=>{let s=r.indexOf("--");return s<0||r[s+1]!=="plan"?e(n,r,o):e(n,[...r.slice(0,s+2),..._a("plan",t),...r.slice(s+2)],o)}}function ufe(e=Nn){return(t,n,r)=>{let o=n.indexOf("--");if(o<0||n[o+1]!=="plan")return e(t,n,r);let s=[...n.slice(0,o+2),"-refresh-only","-lock=false",...n.slice(o+2)];return e(t,s,r)}}async function ZB(e,t,n={},r=console.error){if(e!=="tf-plan"&&e!=="tf-drift")throw new S(`terragucci stage ${e||"<name>"}: the stages built so far are ${Ype.join(", ")}`);let o=e==="tf-drift",s=n.env??process.env,i=n.config??fe(t),a=i?await Re(i):{},c=n.project?ot(a,n.project):ue(a),u=et(t,c);if(n.terragrunt??u.engine==="terragrunt")return dfe(t,c,u,n,s,r,o,i);let l=n.layers?n.layers.flat():wt(t,c.roots),d=u.after,p=Fo(d,l),f=n.layers??tc(t,l,d),g=f.map(D=>n.root?D.filter(pe=>Pe(n.root,pe)):D).filter(D=>D.length>0);if(g.length===0)throw new S(n.root?`no root matches ${n.root}`:"found no roots");let m=u.prepare;if(!n.root&&g.flat().every(D=>!dp(Ne(t,D))))throw new S(`found no roots: none of the ${g.flat().length} the pipeline names is on disk${m?`; the synth command (${m}) writes them, so run it first`:"; roots a command writes, such as CDK Terrain's stacks, need synth in terragucci.yml"}`);let h=o?void 0:n.base??fp(s),y=[],w;if(h&&m&&!u.rootsInGit){let D=await a$(t,h,i,n.project,c,g.flat(),s,r,!1);if(u.kind==="synth"&&D.synth){let pe=`the synth command changed (${D.synth})`;r(`every root: ${pe}`),y.push(`Every synthesized root is planned: ${pe}.`)}else{let pe=await sB(t,h,m,g.flat(),wn(t,l),s,r,YB(ng(t,l),p));w=pe.selected;for(let[ge,Ye]of D.outside.settings??[])!w||w.has(ge)||(w.add(ge),r(`affected: ${ge}: its terragucci.yml settings changed (${Ye.join(", ")})`));y.push(pe.notice)}}else if(h){let D=await a$(t,h,i,n.project,c,g.flat(),s,r,!0);w=bO(t,h,l,g.flat(),r,d,D.outside)}let b=await xg(t,h,c.steps,{...i?{config:i}:{},...n.project?{project:n.project}:{}});b.note&&r(b.note);let k=Eg(b.steps,e);k.length>0&&r(`steps: ${k.length} read from terragucci.yml at ${b.from}`);let v=w?g.map(D=>D.filter(pe=>w.has(pe))).filter(D=>D.length>0):g,$=v.flat();$.length===0&&r("this change reaches no root, so nothing is planned");let _=n.binary??c.binary??Ks(t,l).value,E=new Set(o&&ob(_)?ms(t,$):[]),R=nn(_),x=h&&!(Bs(c.approval)==="pr-review"&&c.apply?.when!=="pull-request")?h:void 0,A=new jr(t,_,c.version,s,n.installer),O=new Date().toISOString(),I=new sl(ol(s),e,s);$.length>0&&await I.collectSpans(r);let N=Km(Ne(fO(),"terragucci-plan-")),L=o?UA(t,c.apply?.branches,$,N,r):void 0,U=D=>L?.dirOf(D)??t,F=new Map([[t,A]]),M=D=>{let pe=U(D);return F.has(pe)||F.set(pe,new jr(pe,_,c.version,s,n.installer)),F.get(pe)},te=n.parallelism!==void 0?{value:n.parallelism,reason:"--parallelism"}:u$(t,$,c,s);$.length>1&&r(`planning ${te.value===1?"one root at a time":`up to ${te.value} roots at once`} (${te.reason})`);let ie=$.filter(D=>!VB(Ne(U(D),D),M(D).expected(D).name));ie.length>0&&r(`${ie.length===1?`${ie[0]} takes its providers`:`${ie.length} roots take their providers`} from the shared cache with no lock entry to check them against: commit a .terraform.lock.hcl for the binary's registry to pin them`);let Q={...s,TF_PLUGIN_CACHE_DIR:s.TF_PLUGIN_CACHE_DIR||Km(Ne(N,"plugins-"))},ne=hO(),ae=new Map;if(!o&&$.length>0){let D=await Zd(t,c.modules,h,gp(t,n),{env:s});for(let pe of D?$:[]){let ge=await D(pe);ge.refused.length?ae.set(pe,ge.refused):ge.verified.length&&r(`${pe}: attested ${ge.verified.join("; ")}`)}}let he=new Map,$e=$.length>0?await kc(t,c.sources,h,gp(t,n),{log:r}):void 0;for(let D of $e?$:[]){let pe=await $e.before(D,U(D));pe.length&&he.set(D,pe)}let ye=[],Z=new Map,me=new Map,J=new Map,j=o&&Kn(c,"drift")==="attribute",z=j?n.audit??bm({region:c.audit_region}):void 0,X=[],V=new Set,W=new Map,G=new Set,C=new Map,ee=o?new Map:wn(t,l),le=o?ee:Vs(t,l,d),_e=o?new Map:ig(t,l),xe=new Map(o?[]:ZR(t,l).map(D=>[D.root,{...D.reads.length?{unknownReads:D.reads.map(pe=>({data:pe.name,why:pe.why}))}:{},...D.own?{unaddressed:D.own}:{}}])),Le=new Map,Ot=!o&&
|
|
642
|
+
`}function afe(e,t,n,r,o,s){let i=new Map(n.map(p=>[p,o.unitEnv(p,"plan",s)]));if([...i.values()].every(p=>Object.keys(p).length===0))return e;pp(r,{recursive:!0});let a=Ne(Rs(r),"plans"),c=a;try{c=Ne(UB(r),"plans")}catch{}let u=`${Rs(r)}.bin`;pp(u,{recursive:!0});let l=Ne(u,gO(t)||"tofu");Ra(l,ife(t,[a,c],i)),FB(l,493);let d=e??Nn;return(p,f,g)=>d(p,f,{...g,env:{...g.env,TG_TF_PATH:l}})}function cfe(e=Nn,t){return(n,r,o)=>{let s=r.indexOf("--");return s<0||r[s+1]!=="plan"?e(n,r,o):e(n,[...r.slice(0,s+2),..._a("plan",t),...r.slice(s+2)],o)}}function ufe(e=Nn){return(t,n,r)=>{let o=n.indexOf("--");if(o<0||n[o+1]!=="plan")return e(t,n,r);let s=[...n.slice(0,o+2),"-refresh-only","-lock=false",...n.slice(o+2)];return e(t,s,r)}}async function ZB(e,t,n={},r=console.error){if(e!=="tf-plan"&&e!=="tf-drift")throw new S(`terragucci stage ${e||"<name>"}: the stages built so far are ${Ype.join(", ")}`);let o=e==="tf-drift",s=n.env??process.env,i=n.config??fe(t),a=i?await Re(i):{},c=n.project?ot(a,n.project):ue(a),u=et(t,c);if(n.terragrunt??u.engine==="terragrunt")return dfe(t,c,u,n,s,r,o,i);let l=n.layers?n.layers.flat():wt(t,c.roots),d=u.after,p=Fo(d,l),f=n.layers??tc(t,l,d),g=f.map(D=>n.root?D.filter(pe=>Pe(n.root,pe)):D).filter(D=>D.length>0);if(g.length===0)throw new S(n.root?`no root matches ${n.root}`:"found no roots");let m=u.prepare;if(!n.root&&g.flat().every(D=>!dp(Ne(t,D))))throw new S(`found no roots: none of the ${g.flat().length} the pipeline names is on disk${m?`; the synth command (${m}) writes them, so run it first`:"; roots a command writes, such as CDK Terrain's stacks, need synth in terragucci.yml"}`);let h=o?void 0:n.base??fp(s),y=[],w;if(h&&m&&!u.rootsInGit){let D=await a$(t,h,i,n.project,c,g.flat(),s,r,!1);if(u.kind==="synth"&&D.synth){let pe=`the synth command changed (${D.synth})`;r(`every root: ${pe}`),y.push(`Every synthesized root is planned: ${pe}.`)}else{let pe=await sB(t,h,m,g.flat(),wn(t,l),s,r,YB(ng(t,l),p));w=pe.selected;for(let[ge,Ye]of D.outside.settings??[])!w||w.has(ge)||(w.add(ge),r(`affected: ${ge}: its terragucci.yml settings changed (${Ye.join(", ")})`));y.push(pe.notice)}}else if(h){let D=await a$(t,h,i,n.project,c,g.flat(),s,r,!0);w=bO(t,h,l,g.flat(),r,d,D.outside)}let b=await xg(t,h,c.steps,{...i?{config:i}:{},...n.project?{project:n.project}:{}});b.note&&r(b.note);let k=Eg(b.steps,e);k.length>0&&r(`steps: ${k.length} read from terragucci.yml at ${b.from}`);let v=w?g.map(D=>D.filter(pe=>w.has(pe))).filter(D=>D.length>0):g,$=v.flat();$.length===0&&r("this change reaches no root, so nothing is planned");let _=n.binary??c.binary??Ks(t,l).value,E=new Set(o&&ob(_)?ms(t,$):[]),R=nn(_),x=h&&!(Bs(c.approval)==="pr-review"&&c.apply?.when!=="pull-request")?h:void 0,A=new jr(t,_,c.version,s,n.installer),O=new Date().toISOString(),I=new sl(ol(s),e,s);$.length>0&&await I.collectSpans(r);let N=Km(Ne(fO(),"terragucci-plan-")),L=o?UA(t,c.apply?.branches,$,N,r):void 0,U=D=>L?.dirOf(D)??t,F=new Map([[t,A]]),M=D=>{let pe=U(D);return F.has(pe)||F.set(pe,new jr(pe,_,c.version,s,n.installer)),F.get(pe)},te=n.parallelism!==void 0?{value:n.parallelism,reason:"--parallelism"}:u$(t,$,c,s);$.length>1&&r(`planning ${te.value===1?"one root at a time":`up to ${te.value} roots at once`} (${te.reason})`);let ie=$.filter(D=>!VB(Ne(U(D),D),M(D).expected(D).name));ie.length>0&&r(`${ie.length===1?`${ie[0]} takes its providers`:`${ie.length} roots take their providers`} from the shared cache with no lock entry to check them against: commit a .terraform.lock.hcl for the binary's registry to pin them`);let Q={...s,TF_PLUGIN_CACHE_DIR:s.TF_PLUGIN_CACHE_DIR||Km(Ne(N,"plugins-"))},ne=hO(),ae=new Map;if(!o&&$.length>0){let D=await Zd(t,c.modules,h,gp(t,n),{env:s});for(let pe of D?$:[]){let ge=await D(pe);ge.refused.length?ae.set(pe,ge.refused):ge.verified.length&&r(`${pe}: attested ${ge.verified.join("; ")}`)}}let he=new Map,$e=$.length>0?await kc(t,c.sources,h,gp(t,n),{log:r}):void 0;for(let D of $e?$:[]){let pe=await $e.before(D,U(D));pe.length&&he.set(D,pe)}let ye=[],Z=new Map,me=new Map,J=new Map,j=o&&Kn(c,"drift")==="attribute",z=j?n.audit??bm({region:c.audit_region}):void 0,X=[],V=new Set,W=new Map,G=new Set,C=new Map,ee=o?new Map:wn(t,l),le=o?ee:Vs(t,l,d),_e=o?new Map:ig(t,l),xe=new Map(o?[]:ZR(t,l).map(D=>[D.root,{...D.reads.length?{unknownReads:D.reads.map(pe=>({data:pe.name,why:pe.why}))}:{},...D.own?{unaddressed:D.own}:{}}])),Le=new Map,Ot=!o&&r1(_),rt=new Map(Ot?$.map(D=>[D,Pw(Ne(t,D))]):[]),st=new Map;for(let D of Ot?l:[]){let pe=Go(Ne(t,D)).estate;pe&&st.set(pe,D)}let Hn=new Set([...rt.values()].flat().map(D=>D.estate)),Jt=new Map,qn=o?[]:an(f,n.canary??c.waves?.canary),$r=new Map(qn.flatMap((D,pe)=>D.map(ge=>[ge,pe+1]))),Oi=new Map,K=D=>{if(!Oi.has(D)){let pe=Ne(t,D);Oi.set(D,A.resolve(D).then(ge=>ge.path,()=>_).then(ge=>$w(ge,pe,ls(dn(Q,D),pe),ne)))}return Oi.get(D)},de=0,Ge=D=>{let pe=[],ge=[];for(let Ye of _e.get(D)??[]){let Gt=Kt=>{ge.push({upstream:Ye.upstream,data:Ye.name,outputs:"applied",why:Kt})};if(!$.includes(Ye.upstream)){Gt("this change does not reach it, so its state stands");continue}let ht=Le.get(Ye.upstream);if(ht===void 0){Gt("it did not plan in this run");continue}if(Ye.repeated){Gt("the block has count or for_each, which a linked plan does not follow");continue}let _n=Wo(ht);_n?_n.changed?pe.push({name:Ye.name,upstream:Ye.upstream,outputs:_n.outputs}):Gt("its plan changes no output, so its state stands"):Gt("its plan names no output changes")}return{links:pe,reads:ge}},mt=async(D,pe)=>{let ge=[],Ye=Ne(U(D),D),{links:Gt,reads:ht}=Ge(D),_n=XG(D,rt.get(D)??[],st,ct=>$.includes(ct),ct=>Jt.has(ct)?{plan:Le.get(ct),file:Jt.get(ct)}:void 0);ht.push(..._n.reads);for(let ct of xe.get(D)?.unknownReads??[])ge.push(`${D}: reads state through terraform_remote_state "${ct.data}" where the code does not say (${ct.why}), so it is not ordered after the root that writes it`);let Kt=[...ee.get(D)??[]].filter(ct=>W.get(ct)===!0).sort();if(Kt.length>0)return ge.push(`${D}: held back, ${Kt.join(", ")} has no state yet`),{root:D,lines:ge,deferred:{unit:D,after:Kt,why:`reads the state of ${Kt.join(", ")}, which nothing has applied yet, so it cannot plan until then`,previewed:!1}};let zr=Ne(N,`${pe}.tfplan`),Sn=I.root(D),jo=M(D).expected(D),au=_,cu=c$(ls(dn(Q,D),Ye),Ye,jo.name),uu=(ct,...nr)=>I.commandAsync(Sn,au,nr,ct,rr=>i$(au,[`-chdir=${Ye}`,...nr],rr)),lu=(...ct)=>uu(cu,...ct),du=[],cs=[],pu=async(ct,nr)=>{if(k.length===0)return;let rr=await ju(k,ct,{repo:U(D),root:D,stage:o?"tf-drift":"tf-plan",env:cu,...nr?{planFile:nr}:{},log:Io=>ge.push(Io)});return du.push(...rr.runs),cs.push(...rr.holds),rr.error},jn=(ct,nr)=>(ge.push(nr),{root:D,lines:ge,...cs.length?{holds:cs}:{},input:{path:D,planner:R,binary:jo,error:ct,preventDestroy:new Set,...du.length?{steps:du}:{},...ht.length?{reads:ht}:{},...xe.get(D)}}),ff=ae.get(D);if(ff)return I.endRoot(Sn),jn(ff.join(`
|
|
643
643
|
`),`${D}: refused by modules.require: attested`);let _y=he.get(D);if(_y)return I.endRoot(Sn),jn(_y.join(`
|
|
644
644
|
`),`${D}: refused by sources, so it was not initialised`);let Sy=L?.failed.get(D);if(Sy)return I.endRoot(Sn),jn(Sy,`${D}: not checked, its branch could not be checked out`);let Ft=o?"drift":"plan";try{try{let Ee=await M(D).resolve(D);({path:au}=Ee),jo={name:Ee.name,...Ee.version?{version:Ee.version}:{},...Ee.pin?{pin:Ee.pin}:{}}}catch(Ee){return jn(Ee.message,`${D}: ${Ee.message}`)}jo.pin&&ge.push(`${D}: ${Vr(jo)}`);let ct=await pu("before-init");if(ct)return jn(ct,`${D}: a step before init failed`);let nr=Fi(cu,Ye),rr=await ne(()=>nr?uu(nr.init,"init","-input=false","-no-color"):lu("init","-input=false","-no-color"));if(rr.status!==0)return jn(`init failed:
|
|
645
645
|
${lp(rr.stderr||rr.stdout)}`,`${D}: init failed`);if(nr){let Ee=await uu(nr.selectEnv,...nr.select);if(Ee.status!==0)return jn(`workspace select failed:
|
|
@@ -661,7 +661,7 @@ ${v.join(`
|
|
|
661
661
|
`),preventDestroy:new Set,terragrunt:{...Ui(M),selection:y.get(M)??w,provisional:!1,run_result:"not run"}})):te.verified.length&&s(`${M}: attested ${te.verified.join("; ")}`)}}let R;{let F=R=g.length>0?await kc(e,t.sources,d,gp(e,r),{log:s}):void 0,M=new Set(E.map(te=>te.path));for(let te of F?g.flat():[]){if(M.has(te))continue;let ie=await F.before(te);ie.length&&(s(`${te}: refused by sources, so it was not planned`),E.push({path:te,planner:nn(c),error:ie.join(`
|
|
662
662
|
`),preventDestroy:new Set,terragrunt:{...Ui(te),selection:y.get(te)??w,provisional:!1,run_result:"not run"}}))}}if(E.length>0){let F=new Set(E.map(M=>M.path));g=g.map(M=>M.filter(te=>!F.has(te))).filter(M=>M.length>0)}let x=new Date().toISOString(),A=new sl(ol(o),i?"tf-drift":"tf-plan",o);await A.collectSpans(s);let O=Km(Ne(fO(),"terragucci-plan-")),I=new Set(g.flat()),N=new Set(!i&&(t.terragrunt?.dependents??JC)==="plan"?k:[]),L=m.map((F,M)=>({number:M+1,roots:F.filter(te=>I.has(te)),preview:F.filter(te=>N.has(te))})).filter(F=>F.roots.length>0||F.preview.length>0),U=i?UA(e,t.apply?.branches,L.flatMap(F=>F.roots),O,s):void 0;try{let F=r.terragruntPath??o.TERRAGUCCI_TERRAGRUNT??"terragrunt",M=p??Dn(e,t.terragrunt?.exclude),te=async(G,C,ee)=>{G!==e&&await Bi(G,{binary:c,...l});let le=new Hd(G,c,t.version,F,o,r.installer);return sfe(G,C,c,ee,{...r,env:o,drift:i,observer:A,waveOf:h,steps:$,tools:le,...R?{sources:R}:{},graph:M,selection:_e=>y.get(_e)??w},s)},ie;if(!U)ie=await te(e,L,O);else{let G=[];for(let[C,ee]of U.groups(L.flatMap(le=>le.roots)).entries()){let le=new Set(ee.roots),_e=L.map(Le=>({...Le,roots:Le.roots.filter(Ot=>le.has(Ot)),preview:[]})).filter(Le=>Le.roots.length>0),xe=Ne(O,`checkout-${C}`);pp(xe,{recursive:!0}),G.push(await te(ee.dir,_e,xe))}ie=pfe(G);for(let[C,ee]of U.failed)s(`${C}: not checked, its branch could not be checked out`),ie.inputs.push({path:C,planner:nn(c),error:ee,preventDestroy:new Set,terragrunt:{...Ui(C),selection:w,provisional:!1,run_result:"not run"}})}let{inputs:Q,plans:ne,redacted:ae,mockReads:he}=ie;Q.push(...E);let $e=(G,C,ee)=>{let le=b.find(_e=>_e.unit===G);le?(le.after=[...new Set([...le.after,...C])].sort(),le.why=`${le.why}; ${ee}`):b.push({unit:G,after:C,why:ee,previewed:!1})};for(let G of ie.waiting){if(i){Q.push({path:G,planner:nn(c),error:"its upstream has no outputs yet, so Terragrunt would plan it on mock_outputs",preventDestroy:new Set,terragrunt:{...Ui(G),selection:w,provisional:!1,run_result:"not run"}});continue}$e(G,[...new Set(he.filter(C=>C.unit===G).map(C=>C.upstream))].sort(),"would read mock_outputs")}let ye=i&&Kn(t,"drift")==="attribute",Z=new Map;if(ye){let G=r.audit??bm({region:t.audit_region});for(let[C,ee]of ie.found)try{Z.set(C,await ep(C,ee,{audit:G,decide:t.decide,options:r.decideOptions}))}catch(le){s(`${C}: attribution skipped, ${le.message}`)}}for(let[G,C]of ie.unpreviewed)$e(G,[...C.after].sort(),C.why);let me=new Set(Q.filter(G=>G.terragrunt?.provisional&&G.plan!==void 0).map(G=>G.path));for(let G of b)G.previewed=me.has(G.unit);let J=(p??[]).map(G=>G.path),j=Q.map(G=>G.path),z=new Set(Q.filter(G=>!G.terragrunt?.provisional).map(G=>G.path)),X=p??Dn(e,t.terragrunt?.exclude),V=i?[]:Q.filter(G=>G.plan!==void 0&&!G.error&&!G.terragrunt?.provisional&&Vn(G.plan)).map(G=>G.path),W=V.length>0?Wg(new Map(X.map(G=>[G.path,G.dependencies])),V,{waveOf:h,planned:new Set(j)}):void 0;return await XB(e,t,r,o,s,{...W?{blast:W}:{},binary:c,started:x,inputs:Q,plans:ne,redacted:ae,all:J.length?J:j,roots:j,observer:A,mockReads:he,dependencies:new Map(X.map(G=>[G.path,G.dependencies])),...i?{stage:"tf-drift",names:ie.names}:{},...ye?{attributions:Z}:{},deferred:b.sort((G,C)=>G.unit<C.unit?-1:1),...dp(Ne(e,"root.hcl"))?{configDirs:["."]}:{},waves:i?[]:L.map(G=>{let C=ni(h,G.roots.flatMap(xe=>ie.unpreviewed.get(xe)?.after??[]),G.number),ee=ie.waveReads.get(G.number)??[],le=G.roots.filter(xe=>z.has(xe)),_e=le.filter(xe=>ie.heldBySteps.has(xe));return{number:G.number,roots:le,...ee.length?{reads:ee}:{},...C.length?{replansAfter:C}:{},..._e.length?{heldBySteps:_e}:{}}}).filter(G=>G.roots.length>0)})}finally{U?.close(),Jm(O,{recursive:!0,force:!0})}}function pfe(e){let t=new Map;for(let n of e)for(let[r,o]of n.waveReads)t.set(r,[...new Set([...t.get(r)??[],...o])].sort((s,i)=>s-i));return{inputs:e.flatMap(n=>n.inputs).sort((n,r)=>n.path<r.path?-1:n.path>r.path?1:0),plans:new Map(e.flatMap(n=>[...n.plans])),redacted:e.reduce((n,r)=>n+r.redacted,0),mockReads:e.flatMap(n=>n.mockReads),waiting:e.flatMap(n=>n.waiting),names:new Map(e.flatMap(n=>[...n.names])),unpreviewed:new Map(e.flatMap(n=>[...n.unpreviewed])),waveReads:t,found:new Map(e.flatMap(n=>[...n.found])),heldBySteps:new Set(e.flatMap(n=>[...n.heldBySteps]))}}function bO(e,t,n,r,o,s,i={},a=!1){let c=a?[t,"HEAD"]:[`${t}...HEAD`],u=pO("git",["-C",e,"diff","--name-only","--no-renames","--relative",...c],{encoding:"utf-8"});if(u.status!==0){o(`every root: affected selection failed (git diff ${c.join(" ")}: ${(u.stderr||"").trim().split(`
|
|
663
663
|
`)[0]})`);return}let l=u.stdout.split(`
|
|
664
|
-
`).map(k=>k.trim()).filter(Boolean),d=i.watch??{},p=Vf(e,Object.fromEntries(r.map(k=>[k,{dir:k}])),l),f=Object.keys(d).length?Vf(e,Object.fromEntries(r.map(k=>[k,{dir:k,...d[k]?{varFiles:d[k]}:{}}])),l):p,g=[...i.settings?.keys()??[]].filter(k=>r.includes(k)),m=[...new Set([...f,...g])].sort(),h=wn(e,n),y=YB(ng(e,n),Fo(s,n)),w=k=>[...new Set([...h.get(k)??[],...y.get(k)??[]])],b=new Set(m);for(let k=!0;k;){k=!1;for(let v of new Set([...h.keys(),...y.keys()]))!b.has(v)&&w(v).some($=>b.has($))&&(b.add(v),k=!0)}for(let k of m)p.includes(k)?o(`affected: ${k} changed`):f.includes(k)?o(`affected: ${k} reads ${ffe(e,k,d[k]??[],l).join(", ")}, which changed`):o(`affected: ${k}: its terragucci.yml settings changed (${i.settings.get(k).join(", ")})`);for(let k of[...b].filter(v=>!m.includes(v)).sort()){let v=[...h.get(k)??[]].filter(_=>b.has(_)).sort(),$=[...y.get(k)??[]].filter(_=>b.has(_)&&!v.includes(_)).sort();o(`affected: ${k} ${[...v.length?[`reads the state of ${v.join(", ")}`]:[],...$.length?[`depends on ${$.join(", ")}`]:[]].join(" and ")}`)}return o(`affected: ${m.length} of ${r.length} roots against ${t}, ${b.size-m.length} dependents after them`),new Set([...b].filter(k=>r.includes(k)))}function ffe(e,t,n,r){let o=Rs(e),s=n.map(i=>BB(o,Rs(o,t,i)).split(WB).join("/"));return r.filter(i=>s.some(a=>i===a||i.startsWith(`${a}/`))).sort()}async function a$(e,t,n,r,o,s,i,a,c){let u=c?{watch:NB(e,s,{...o.env,...i})}:{};if(r||!gfe(e,t,n))return{outside:u};let l=await ln(e,t,n?{config:n}:{}),d;try{if("error"in l)throw new Error(l.error);d=ue(l.config)}catch(f){return a(`affected: the config at ${t} could not be read (${f.message}), so no root is selected for its settings`),{outside:u}}u.settings=CB(d,o,s);let p=(d.synth??"")!==(o.synth??"")?`${d.synth?`\`${d.synth}\``:"none"} at the base, ${o.synth?`\`${o.synth}\``:"none"} here`:void 0;return{outside:u,...p?{synth:p}:{}}}function gfe(e,t,n){let r=n?BB(Rs(e),Rs(n)).split(WB).join("/"):void 0;if(r!==void 0&&(r.startsWith("../")||r===".."))return!1;let o=r&&r.includes("/")?r.slice(0,r.lastIndexOf("/")+1):"",s=[...new Set([...r?[r]:[],...Di.map(a=>`${o}${a}`)])],i=pO("git",["-C",e,"diff","--name-only","--no-renames","--relative",`${t}...HEAD`,"--",...s],{encoding:"utf-8"});return i.status===0&&i.stdout.trim()!==""}function YB(e,t){let n=new Map([...e].map(([r,o])=>[r,new Set(o)]));for(let[r,o]of t)n.set(r,new Set([...n.get(r)??[],...o]));return n}function fp(e){if(e.TG_BASE)return e.TG_BASE;let t=e.GITHUB_BASE_REF||e.CI_MERGE_REQUEST_TARGET_BRANCH_NAME;return t?`origin/${t}`:void 0}async function mfe(e,t,n,r,o,s={},i,a={},c=()=>{}){let u=n.filter(d=>d.plan!==void 0&&d.error===void 0&&!d.terragrunt?.provisional),l=await Hw(e,t,u.map(d=>{let p=c(d.path);return{path:d.path,plan:d.plan,...p?{cost:p}:{}}}),r,o,s,i,a);return{policy:l.policy,inputs:n.map(d=>{let p=l.roots.get(d.path);if(p===void 0)return d;let f=l.failed.get(d.path);return{...d,policy:p,...f!==void 0?{error:f}:{}}})}}async function hfe(e,t,n,r,o,s){let i=r.filter(u=>u.plan!==void 0&&u.policy?.result==="denied");if(i.length===0||t.project)return r;let a=t.base??fp(n),c=t.config??fe(e);try{let u=await Yr(e,{...a?{at:a}:{},...c?{config:c}:{}});if(u.overriders.length===0)return r;o.overriders=u.overriders;let l=ft(e,Dg),d=
|
|
664
|
+
`).map(k=>k.trim()).filter(Boolean),d=i.watch??{},p=Vf(e,Object.fromEntries(r.map(k=>[k,{dir:k}])),l),f=Object.keys(d).length?Vf(e,Object.fromEntries(r.map(k=>[k,{dir:k,...d[k]?{varFiles:d[k]}:{}}])),l):p,g=[...i.settings?.keys()??[]].filter(k=>r.includes(k)),m=[...new Set([...f,...g])].sort(),h=wn(e,n),y=YB(ng(e,n),Fo(s,n)),w=k=>[...new Set([...h.get(k)??[],...y.get(k)??[]])],b=new Set(m);for(let k=!0;k;){k=!1;for(let v of new Set([...h.keys(),...y.keys()]))!b.has(v)&&w(v).some($=>b.has($))&&(b.add(v),k=!0)}for(let k of m)p.includes(k)?o(`affected: ${k} changed`):f.includes(k)?o(`affected: ${k} reads ${ffe(e,k,d[k]??[],l).join(", ")}, which changed`):o(`affected: ${k}: its terragucci.yml settings changed (${i.settings.get(k).join(", ")})`);for(let k of[...b].filter(v=>!m.includes(v)).sort()){let v=[...h.get(k)??[]].filter(_=>b.has(_)).sort(),$=[...y.get(k)??[]].filter(_=>b.has(_)&&!v.includes(_)).sort();o(`affected: ${k} ${[...v.length?[`reads the state of ${v.join(", ")}`]:[],...$.length?[`depends on ${$.join(", ")}`]:[]].join(" and ")}`)}return o(`affected: ${m.length} of ${r.length} roots against ${t}, ${b.size-m.length} dependents after them`),new Set([...b].filter(k=>r.includes(k)))}function ffe(e,t,n,r){let o=Rs(e),s=n.map(i=>BB(o,Rs(o,t,i)).split(WB).join("/"));return r.filter(i=>s.some(a=>i===a||i.startsWith(`${a}/`))).sort()}async function a$(e,t,n,r,o,s,i,a,c){let u=c?{watch:NB(e,s,{...o.env,...i})}:{};if(r||!gfe(e,t,n))return{outside:u};let l=await ln(e,t,n?{config:n}:{}),d;try{if("error"in l)throw new Error(l.error);d=ue(l.config)}catch(f){return a(`affected: the config at ${t} could not be read (${f.message}), so no root is selected for its settings`),{outside:u}}u.settings=CB(d,o,s);let p=(d.synth??"")!==(o.synth??"")?`${d.synth?`\`${d.synth}\``:"none"} at the base, ${o.synth?`\`${o.synth}\``:"none"} here`:void 0;return{outside:u,...p?{synth:p}:{}}}function gfe(e,t,n){let r=n?BB(Rs(e),Rs(n)).split(WB).join("/"):void 0;if(r!==void 0&&(r.startsWith("../")||r===".."))return!1;let o=r&&r.includes("/")?r.slice(0,r.lastIndexOf("/")+1):"",s=[...new Set([...r?[r]:[],...Di.map(a=>`${o}${a}`)])],i=pO("git",["-C",e,"diff","--name-only","--no-renames","--relative",`${t}...HEAD`,"--",...s],{encoding:"utf-8"});return i.status===0&&i.stdout.trim()!==""}function YB(e,t){let n=new Map([...e].map(([r,o])=>[r,new Set(o)]));for(let[r,o]of t)n.set(r,new Set([...n.get(r)??[],...o]));return n}function fp(e){if(e.TG_BASE)return e.TG_BASE;let t=e.GITHUB_BASE_REF||e.CI_MERGE_REQUEST_TARGET_BRANCH_NAME;return t?`origin/${t}`:void 0}async function mfe(e,t,n,r,o,s={},i,a={},c=()=>{}){let u=n.filter(d=>d.plan!==void 0&&d.error===void 0&&!d.terragrunt?.provisional),l=await Hw(e,t,u.map(d=>{let p=c(d.path);return{path:d.path,plan:d.plan,...p?{cost:p}:{}}}),r,o,s,i,a);return{policy:l.policy,inputs:n.map(d=>{let p=l.roots.get(d.path);if(p===void 0)return d;let f=l.failed.get(d.path);return{...d,policy:p,...f!==void 0?{error:f}:{}}})}}async function hfe(e,t,n,r,o,s){let i=r.filter(u=>u.plan!==void 0&&u.policy?.result==="denied");if(i.length===0||t.project)return r;let a=t.base??fp(n),c=t.config??fe(e);try{let u=await Yr(e,{...a?{at:a}:{},...c?{config:c}:{}});if(u.overriders.length===0)return r;o.overriders=u.overriders;let l=ft(e,Dg),d=YL(e,u),p=new Date().toISOString(),f=[],g=r.map(m=>{if(!i.includes(m))return m;let h=fo({member:m.path,plan:m.plan,planner:m.planner??"terraform"}).member.planDigest;if(!h)return m;let y=QL(l,d,m.path,h,m.policy.rules??[],p);return y.status!=="overridden"?m:(f.push(m.path),s(`${m.path}: the policy denial is overridden by ${y.override.by} at ${y.override.at}, so tf-apply applies this plan; the plan still fails here`),{...m,policy:{...m.policy,override:y.override}})});return f.length>0&&(o.overridden=f.sort()),g}catch(u){return s(`policy override: ${u.message}, so no override is shown`),r}}function gp(e,t){let n=t.config??fe(e);return{...n?{config:n}:{},...t.project?{project:t.project}:{}}}async function XB(e,t,n,r,o,{binary:s,started:i,inputs:a,waves:c,plans:u,redacted:l,all:d,roots:p,observer:f,mockReads:g,deferred:m,configDirs:h,stage:y="tf-plan",names:w,attributions:b,notices:k=[],blast:v,dependencies:$,compose:_}){let E=$?a.map(W=>({...W,...$.get(W.path)?.length?{dependencies:$.get(W.path)}:{}})):a,R,x=y==="tf-drift",A,O,I={};if(!x&&!n.noCost){I=await Eb(e,t.cost,n.base??fp(r),gp(e,n)),I.note&&o(I.note),I.error&&o(`cost: ${I.error}`);let W=I.setting?E.flatMap(G=>{let C=u.get(G.path)?.json;return C!==void 0?[{root:G.path,json:C}]:[]}):[];if(I.setting&&W.length>0){let G=Km(Ne(fO(),"terragucci-cost-"));try{let C=await Rb(W,Sb(I.setting),r,G,e,o,n.costRunner);O=C.cost,A=C.outputs}finally{Jm(G,{recursive:!0,force:!0})}}}let N=O?c.map(W=>({...W,cost:Tb(O,W.roots,I.approveAbove)})):c;for(let W of N)"cost"in W&&W.cost?.over&&o(`cost: wave ${W.number}: ${jg(W.cost,I.source)}, so it waits for an approval when it applies`);let L=W=>{if(!O)return;let G=N.find(C=>C.roots.includes(W));return Ab(O,W,G&&"cost"in G&&G.cost?{number:G.number,cost:G.cost}:void 0,I.approveAbove)},U=x?void 0:await Rd(e,t.policy,n.base??fp(r),gp(e,n));if(U?.note&&o(U.note),U?.policy){let W=zn(e,r,n.forge??t.forge),G={stage:"tf-plan",project:W.project,commit:W.commit,...W.pull_request?{pullRequest:W.pull_request}:{}};({inputs:E,policy:R}=await mfe(e,U.policy,E,n.base??fp(r),U.trust,n.policy,o,G,L)),U.policy.override?.length&&(E=await hfe(e,n,r,E,R,o))}let F=t.environments,M=E.some(W=>W.terragrunt!==void 0),te=W=>[...new Set(W.map(G=>hs(G,t)).filter(G=>G!==void 0))].sort(),ie=Bu({run:{...zn(e,r,n.forge??t.forge),stage:y,binary:s,runtime:t.runtime,started:i,finished:new Date().toISOString(),terragucci:Pr},roots:F?E.map(W=>jb(W,hs(W.path,t))):E,waves:F?N.map(W=>{let G=te(W.roots);return{...W,...x?{}:{policy:M?Iw(t,t.gate):jw(W.roots,t,t.gate)},...G.length?{environments:G}:{}}}):N,redacted:l,...x?{}:{gate:t.gate},...g?.length?{mockReads:g}:{},...m?.length?{deferred:m}:{},...R?{policy:R}:{}});if(v&&(ie.blast=v),_&&(ie.compose=_),t.tips){let W=await yo().catch(()=>{});W||o("tips: the HCL parser is not installed, so the lint tips are left out (npm i -D @cdktn/hcl2json)");let G=[...new Set(ie.named.filter(C=>C.action==="delete"||C.action==="replace").map(C=>C.root))];try{let C=E.flatMap(ee=>ee.plan&&!ee.error?xv(ee.path,ee.plan):[]);ie.tips=await Lw(e,d,{settings:t,...W?{parser:W}:{},destroying:G,planned:p.length,...h?{configDirs:h}:{},...C.length?{renames:C}:{}})}catch(C){o(`tips: skipped, ${C.message}`),ie.tips=[]}for(let C of Gw(ie.tips))o(C)}O&&(ie.cost=O),f.addTimings(ie);let Q=Rs(e,n.out??"terragucci-report"),ne=n.reports,ae=ne&&t.reports?.bucket===ne.bucket?t.reports:void 0,he=ne&&!ne.url?ae?.url:void 0,$e=ne?{...ne,...he?{url:he}:{},...ae?.role?{role:ae.role}:{}}:t.reports,ye=mO(ie,{reports:$e,given:n.reportUrl,traceId:f.trace?.traceId,traceUrl:t.telemetry?.trace_url,env:r});Object.assign(ie.run,ye.run),f.reportUrl=ye.run.report_url;let Z=ye.note;if($e?.bucket&&!ye.run.report_url)try{let W=dr($e,r,n.fetch);Z=await UG(W,ie,$e.prefix,[...u].filter(([,G])=>G.text!==void 0).map(([G])=>G))}catch(W){o(`the note links no copy in the bucket: ${W.message}`)}let me=wG(n.forge??t.forge??Qpe(r),ie),J=[...k];y==="tf-plan"&&Vy(t.agent)&&ie.waves.some(W=>W.waits===!0)&&J.push("The agent's account isn't excluded from approving this change: set agent.login to the login agent.token_env's token acts as.");let j;if(!x&&typeof t.drift=="string"){let W=n.token??r.TG_TOKEN,G=n.forge??t.forge,C=ui(G,r,W,!0),ee=C?await Tz(e,t.drift,C,n.forgeFetch??globalThis.fetch,n.now??new Date,o):void 0;if(ee&&J.push(ee.message),!W&&C&&C.forge!=="gitlab"){let le=QT(e,C.forge);j={cron:t.drift,...le?{added:le}:{}}}}let z=(Bs(t.approval)??(Xg(dp(Ne(e,"chant.workspace.json"))?jc(Ne(e,"chant.workspace.json"),"utf-8"):void 0)>0?"sealed":"ledger"))==="sealed";Qb(Q,ie,u,{...Z,limit:me,...J.length?{notices:J}:{},...z?{sealed:z}:{}}),j?Ra(Ne(Q,km),JSON.stringify(j)+`
|
|
665
665
|
`):Jm(Ne(Q,km),{force:!0}),A&&xb(Q,A);let X;$e?.bucket&&(X=await nv(dr($e,r,n.fetch),Q,ie,$e.prefix));let V;if(x){let W={...w?{names:w}:{},...b?{attributions:b}:{},...ye.note};Ra(Ne(Q,"issue.md"),mA(ie,W)),b?Ra(Ne(Q,vm),JSON.stringify(Object.fromEntries(b),null,2)+`
|
|
666
666
|
`):Jm(Ne(Q,vm),{force:!0});let G=n.token??r.TG_TOKEN,C=ui(n.forge,r,G);if(C)try{V=await zz(Pd(C,n.forgeFetch??globalThis.fetch),ie,W),V.action!=="none"&&o(`drift issue ${V.action}: ${V.issue.url}`)}catch(ee){if(!(ee instanceof Zr)&&!(ee instanceof TypeError))throw ee;V={action:"none",error:ee.message},o(`the drift issue could not be kept: ${ee.message}`)}else{o("no forge token or no forge in the environment, so the drift issue is left alone");let ee=G?void 0:ui(n.forge,r,void 0,!0);if(ee&&np(ie).roots>0)try{let le=await Pd(ee,n.forgeFetch??globalThis.fetch).findIssue(tp,{match:"anywhere"});le?.created&&(f.drift={since:le.created})}catch(le){if(!(le instanceof Zr)&&!(le instanceof TypeError))throw le}}}if(f.pins=Xz(E),x){let W=V&&"issue"in V?{action:V.action,number:V.issue.number,url:V.issue.url}:{action:"none"};Ra(Ne(Q,rp),JSON.stringify(W)+`
|
|
667
667
|
`)}return x&&V&&"issue"in V&&V.action!=="closed"&&(f.drift={since:V.action==="opened"?ie.run.finished:V.issue.created??ie.run.finished}),await f.finish(ie,r,o,n.otlpFetch),{report:ie,dir:Q,...X?{uploaded:X}:{},...V?{issue:V}:{},failed:ie.roots.some(W=>W.status==="failed"&&!W.terragrunt?.provisional)||V?.error!==void 0}}import{readFileSync as Lfe}from"node:fs";import{join as Gfe}from"node:path";import{spawnSync as iW}from"node:child_process";import{readFileSync as aW}from"node:fs";import{join as Sfe}from"node:path";import{posix as rW}from"node:path";import{spawnSync as yfe}from"node:child_process";import{mkdtempSync as wfe,rmSync as bfe}from"node:fs";import{tmpdir as vfe}from"node:os";import{join as QB}from"node:path";var Cc="chant/lifecycle",vO=`refs/remotes/origin/${Cc}`,eW="_locks/tf-apply.json",$fe={GIT_AUTHOR_NAME:"terragucci",GIT_AUTHOR_EMAIL:"terragucci@localhost",GIT_COMMITTER_NAME:"terragucci",GIT_COMMITTER_EMAIL:"terragucci@localhost"};function hi(e,t,n,r=process.env){return yfe("git",t,{cwd:e,encoding:"utf-8",input:n,env:r})}function kfe(e){let t=hi(e,["ls-remote","--heads","origin",Cc]);if(t.status!==0)throw new S(`cannot read ${Cc} from origin, so the locks cannot be read: ${t.stderr.trim()}`);if(!t.stdout.trim())return!1;let n=hi(e,["fetch","-q","origin",`+refs/heads/${Cc}:${vO}`]);if(n.status!==0)throw new S(`cannot fetch ${Cc}, so the locks cannot be read: ${n.stderr.trim()}`);return!0}function _fe(e){try{let t=JSON.parse(e);if(t?.version===1&&t.locks&&typeof t.locks=="object"&&!Array.isArray(t.locks)){let n={};for(let[r,o]of Object.entries(t.locks))Number.isInteger(o?.pr)&&typeof o.by=="string"&&typeof o.at=="string"&&typeof o.head=="string"&&(n[r]={pr:o.pr,by:o.by,at:o.at,head:o.head,...o.via==="lock"?{via:"lock"}:{},...o.stage==="plan"?{stage:"plan"}:{}});return{version:1,locks:n}}}catch{}return{version:1,locks:{}}}function tW(e){if(!kfe(e))return{parent:"",file:{version:1,locks:{}}};let t=hi(e,["rev-parse",vO]).stdout.trim(),n=hi(e,["show",`${vO}:${eW}`]);return{parent:t,file:_fe(n.status===0?n.stdout:"")}}function nW(e,t,n,r){let o=`${JSON.stringify(n,null,2)}
|
|
@@ -761,8 +761,8 @@ ${M.join(`
|
|
|
761
761
|
`)}`}}let R=await kw(e,n,M=>{if(!a.outputs.has(M)){let te=Fe(e,M),ie=ls(dn({...process.env,TF_PLUGIN_CACHE_DIR:a.dir},M),te);a.outputs.set(M,t.resolve(M).then(Q=>Q.path,()=>w).then(Q=>$w(Q,te,ie,a.initTurn)))}return a.outputs.get(M)});if(R.errors.length>0)return{...k,error:R.errors.join(`
|
|
762
762
|
`)};if(R.waiting.length>0)return{...k,error:`not planned: ${R.waiting.map(M=>`it reads ${M.read.var} from ${M.read.upstream}, which ${M.why} (${M.read.function})`).join("; ")}; apply ${[...new Set(R.waiting.map(M=>M.read.upstream))].join(", ")} first`};for(let M of R.filled)console.log(`${n}: ${M} read from the state its stacks name`);if($=await kp(e,k,v,"after-init")??await kp(e,k,v,"before-plan"),$)return{...k,error:$};let x=["-input=false","-no-color",..._a("plan",f),`-out=${g}`],A=bb(w,l,x,wb(e,n)),O=await I$(s,i,w,["plan",...A,...x],f,d);if(O.code!==0)return{...k,error:`plan failed
|
|
763
763
|
${O.out}`};let I=A.length&&l?vb(O.out,g,l):void 0;if(I&&console.log($b(n,I)),$=await kp(e,k,v,"after-plan",g),$)return{...k,error:$};let N=TH(w,[`-chdir=${d}`,"show","-json",g],{encoding:"utf-8",env:Ve(f),maxBuffer:512*1024*1024}),L;try{L=JSON.parse(N.stdout)}catch{return{...k,error:`show -json printed no plan
|
|
764
|
-
${N.stderr||N.stdout}`}}let U=fo({member:n,plan:L,planner:nn(w)}),F=U.entries.filter(M=>M.action!=="no-op"&&M.action!=="read");return{...k,member:{member:n,planDigest:U.member.planDigest?kb(U.member.planDigest,I):""},plan:L,...I?{cone:I}:{},changes:F.length,destroys:F.filter(M=>M.action==="delete"||M.action==="replace").length,summary:O.out.match(/Plan: .*|No changes\..*/)?.[0]??"planned"}}async function lme(e,t,n,r){let o=r?{...r,env:ls(dn({...r.env,TF_PLUGIN_CACHE_DIR:t.env.TF_PLUGIN_CACHE_DIR},t.root),Fe(e,t.root))}:void 0,s=await kp(e,t,o,"before-apply",t.planFile);if(s)return console.log(`FAILED ${t.root}: nothing applied`),console.log(Aa(s)),t.error=s,!1;n.reopen(t.timing);let i;try{i=await I$(n,t.timing,t.binary,["apply","-input=false","-no-color",..._a("apply",t.env),t.planFile],t.env,Fe(e,t.root))}finally{n.endRoot(t.timing)}if(i.code===0){let a=await kp(e,t,o,"after-apply");return a?(console.log(`FAILED ${t.root}: applied, then a step after the apply failed`),console.log(Aa(a)),t.error=a,!1):(console.log(`applied ${t.root}: ${[...i.out.matchAll(/Resources: .*destroyed/g)].pop()?.[0]??"done"}`),!0)}return console.log(`FAILED ${t.root}`),console.log(Aa(i.out)),!1}function dme(e,t){let n=t.GITHUB_REF_NAME||t.CI_COMMIT_BRANCH,r=t.GITHUB_SHA||t.CI_COMMIT_SHA;if(!n||!r)return;let o=xt(e,["ls-remote","origin",`refs/heads/${n}`]),s=o.status===0?o.stdout.split(/\s/)[0]?.trim():"";return s&&s!==r?s:void 0}var pme=e=>new Promise(t=>setTimeout(t,e));async function MH(e,t,n,r,o){let s=t.env??process.env,i={release:async()=>{}},a=g=>{let m=t.standDown?dme(e,s):void 0;if(m)return console.log(`${n}: a newer push to ${s.GITHUB_REF_NAME||s.CI_COMMIT_BRANCH} (${m.slice(0,8)}) applies everything; standing down`),o.refused={reason:"superseded",roots:o.changedRoots??[]},Mr(s,"superseded by a newer push",o),g().then(()=>({code:re.superseded,release:i.release}))},c=r.filter(g=>g.plan&&go(g.binary)==="resource").flatMap(g=>cL(g.root,Go(Fe(e,g.root)).estate,g.plan));if(c.length===0)return await a(i.release)??i;if(xt(e,["remote","get-url","origin"]).status!==0)return console.log(`${n}: this checkout has no origin, so nothing holds the resources it changes`),i;let u=[...new Set(c.map(g=>g.key))],l=sb(s),d=t.liveness??ib(s),p=(Number(s.TG_LOCK_POLL)>0?Number(s.TG_LOCK_POLL):10)*1e3,f=!1;for(let g=0;;g++){let m=await pL(e,c,l,d);if(m.ok){for(let k of m.tookOver)console.log(`${n}: run ${k.run} held resources and is gone; this wave takes them over, and each apply re-reads what that run left`);let w=new Set(c.map(k=>`${k.root} ${k.address}`)).size;console.log(`${n}: holding ${w===1?"the resource":`the ${w} resources`} its plans change, as run ${l.run}`);let b=async()=>{await fL(e,u,l.run)?console.log(`${n}: let go of the resources it held`):console.log(`${n}: could not let go of the resources it held; the next apply takes them over once this run is gone`)};return await a(b)??{release:b}}let h=gL(m.held,c);if(t.onHeld==="refuse"){console.log(`${n}: ${h}, so nothing in it was applied; apply again once that run is done`);let w=m.held[0].holder;return o.refused={reason:"held",roots:[...new Set(m.held.map(b=>c.find(k=>k.key===b.key)?.root??""))].filter(Boolean).sort(),holder:{run:w.run,...w.url?{url:w.url}:{}}},Mr(s,`held: ${h}`,o),{code:re.held,release:i.release}}if(f||console.log(`${n}: ${h}; waiting for it`),f=!0,g>=360)return console.log(`${n}: another run has held resources this wave changes for an hour (${h}), so nothing in it was applied`),Mr(s,`held for an hour: ${h}`,o),{code:re.failed,release:i.release};await pme(p);let y=await Qx(lL(e),u,l.run,d);if(y.live.length===0&&y.gone.length===0)return{code:PH,release:i.release}}}async function LH(e,t){let n=lo(t.gate);if(n===void 0)throw new S(`--gate must be one of ${sd.join(", ")}`);let r={...t,gate:n};if(r.approval!==void 0&&!Do.includes(r.approval))throw new S(`--approval must be one of ${Do.join(", ")}`);if(!Number.isInteger(r.wave)||r.wave<1)throw new S("--wave must be a wave number from 1");if(r.shares!==void 0&&!(Number.isInteger(r.shares)&&r.shares>=1))throw new S("--shares must be a whole number of 1 or more");if(r.share!==void 0&&(r.shares===void 0||!Number.isInteger(r.share)||r.share<1||r.share>r.shares))throw new S("--share must be a share number from 1 to --shares");if(r.rest&&!r.terragrunt)throw new S("--rest runs the waves of a Terragrunt repo, so it needs --terragrunt");if(r.groups&&r.terragrunt)throw new S("--groups splits a wave of plain roots by identity; a Terragrunt repo's units apply with terragrunt.credentials in one job");if(r.group!==void 0&&!r.groups)throw new S("--group names one of --groups, so it needs --groups");if(r.branches&&Object.keys(r.branches).length>0){let o=eme(r.layers,r.branches,r.branch||void 0);if(console.log(`wave ${r.wave}: ${o.note}`),o.layers.flat().length===0)return console.log(`wave ${r.wave}: no ${r.terragrunt?"unit":"root"} applies from ${r.branch||"this branch"}, so there is nothing to apply`),re.applied;let s=r.layers.flat().filter(i=>!o.layers.flat().includes(i));r={...r,layers:o.layers,branches:void 0,...s.length?{elsewhere:s}:{}}}return fme(e,r)}async function fme(e,t){let n=t,r=n.env??process.env,o=ih(Fe(OH(),"terragucci-apply-")),s=new Map,i=d=>{let p=s.get(d);if(!p){let f={},g=new sl(ol(r),Yt,r);g.wave={number:d,facts:f},p={k:d,w:{observer:g},facts:f,label:`wave ${d}`},s.set(d,p)}return p},a=re.failed,c=n.wave,u,l;try{let d;if(n.terragrunt)d=n.rest?n.wave:Math.max(n.layers.filter(_=>_.length>0).length,n.wave);else{let _=an(n.layers,n.canary);if(!_[n.wave-1])return i(n.wave).facts.nothing=!0,console.log(`wave ${n.wave}: this repo has ${_.length} waves, so there is nothing to apply`),a=re.applied;let E=await xme(e,n,_);if(E&&(n={...n,composition:E},_=zm(_,E,!0),_[n.wave-1].length===0))return i(n.wave).facts.nothing=!0,console.log(`wave ${n.wave}: its roots apply in the composed run in wave ${E.wave}, so this wave has nothing to apply`),a=re.applied;if(d=E?HU(an(n.layers,n.canary),E):_,n.groups){let R=n.shares;if(u=Eme(_[n.wave-1],n.groups,n.composition).map(x=>({...x,shares:R!==void 0?oh(x.roots,R):1})),n.group!==void 0){let x=u.findIndex(O=>O.name===n.group),A=u[x];if(!A||(n.share??1)>A.shares)return i(n.wave).facts.nothing=!0,console.log(`wave ${n.wave} of ${_.length}: group ${n.group} has ${A?`${A.roots.length} roots in ${A.shares} share${A.shares===1?"":"s"}`:"no root in this wave"}, so this job has nothing to apply`),a=re.applied;if(A.shares>1&&n.share===void 0)throw new S(`group ${A.name} of wave ${n.wave} spreads across ${A.shares} share jobs, so its job needs --share`);l=u.slice(0,x).reduce((O,I)=>O+I.shares,0)+(n.share??1),n={...n,shares:A.shares>1?A.shares:void 0,share:A.shares>1?n.share:void 0}}}else if(n.shares!==void 0){let R=oh(_[n.wave-1],n.shares);if(n.share!==void 0&&n.share>R)return i(n.wave).facts.nothing=!0,console.log(`wave ${n.wave} of ${_.length}: its ${_[n.wave-1].length} roots make ${R} share${R===1?"":"s"}, so share ${n.share} has nothing to apply`),a=re.applied;n=R>1?{...n,shares:R}:{...n,shares:void 0,share:void 0}}}let p=n.config??fe(e),f=n.base??bs(e),g;try{g=await Yr(e,{...n.base?{at:n.base}:{},...p?{config:p}:{},...n.approval?{flag:n.approval}:{}})}catch(_){if(n.gate!=="never")throw _}if(g?.point&&g.mode!=="ledger")throw new S(`apply.point is set at base and approval is ${g.mode}; a wave gate that asks a decision point takes its quorum from the point, so set approval to ledger or drop apply.point`);let m=await gme(e,f,p,n,n.composition&&typeof d!="number"?zm(an(n.layers,n.canary),n.composition,!1):d);typeof d!="number"&&m[n.wave-1]!==n.gate?(console.log(`wave ${n.wave}: gate ${m[n.wave-1]}, the strictest of its roots' environments at base`),n={...n,gate:m[n.wave-1]}):typeof d=="number"&&m[0]!==n.gate&&(console.log(`wave ${n.wave}: gate ${m[0]}, the strictest of the environments at base, since a Terragrunt wave finds its units when it plans`),n={...n,gate:m[0]});let h={rule:g,configPath:p,work:o,...l!==void 0?{part:l}:{},planned:_=>s.get(_)?.w.planned},y=await vH(e,o,_=>mme(e,n,h,_,i)),w;try{let _=mH({waves:d,...n.rest?{rest:!0}:{},gate:n.gate,...typeof d!="number"&&m.some(R=>R!==n.gate)?{gates:m}:{},approval:g?.mode??"ledger",...u?{jobs:{wave:n.wave,groups:u.map(R=>({name:R.name,runs:R.roots,shares:R.shares}))}}:n.shares!==void 0?{shares:{wave:n.wave,shares:n.shares}}:{},call:bH,migrations:YO(Fe(e,di)),...g?.point?{point:g.point}:{}}),E=hH(e,f,kH(e,_));if(n.share!==void 0||n.group!==void 0){let R=n.decided??Fe(e,RH(n.wave));if(!YO(R))throw new S(`the wave's decision ${R} cannot be read; the wave's own job writes it before its shares run`);_p(SH(Fe(e,j$(n.wave))),{recursive:!0}),_H(R,Fe(e,j$(n.wave)))}w=await $H(e,{wave:n.wave,base:E,...(u||n.shares!==void 0)&&n.share===void 0&&n.group===void 0?{decide:!0}:{},...n.group!==void 0?{group:n.group}:{},...n.share!==void 0?{share:n.share}:{},env:{...process.env,...r},...n.chant?{chant:n.chant}:{}})}finally{await y.close()}let b=w.result?.rest;b?.length&&(c=b[b.length-1].wave);let k=i(c);a=yH(w.code,k.code);let v=n.group!==void 0||n.share!==void 0,$=`${n.group!==void 0?`group ${n.group}`:""}${n.group!==void 0&&n.share!==void 0?" ":""}${n.share!==void 0?`share ${n.share}`:""}`;if(v&&w.result?.decision&&(k.w.digest=w.result.decision.digest,(w.result.decision.status==="approved"||w.result.decision.status==="not-required")&&(k.w.approval=w.result.decision.status)),v&&w.code===re.refused&&w.result?.moved.length){let _=[...w.result.moved].sort();w.result.decision?.status==="approved"&&(k.w.refused={reason:"approval",approved:w.result.decision.digest,roots:_}),Mr(n.env,`wave ${c} ${$} changed since the wave decided: ${_.join(", ")}`,k.w)}if((u||n.shares!==void 0)&&!v&&a===re.applied){let _=n.decided??Fe(e,RH(n.wave));_p(SH(_),{recursive:!0}),_H(Fe(e,j$(n.wave)),_),k.w.decided=!0;let E=u?u.reduce((x,A)=>x+A.shares,0):n.shares;k.w.shareCount=E;let R=u?`its ${u.length} group job${u.length===1?"":"s"} (${u.map(x=>x.name).join(", ")})`:`its ${E} share job${E===1?"":"s"}`;console.log(`${k.label}: ${w.result?.decision?.status==="approved"?"approved":"no approval needed"}; ${R} apply these plans`)}return a}finally{for(let d of s.values())await d.release?.().catch(p=>console.log(`${d.label}: the composed run's locks were not released: ${p.message}`));for(let d of[...s.keys()].sort((p,f)=>p-f)){let{w:p}=s.get(d),f=d===c?a:re.applied;p.observer.wave.code=f,the(r,d,f,p),p.state=yme(f,p),p.ended=new Date().toISOString(),p.planned&&await Sme(e,n,p,r).catch(g=>console.log(`wave ${d}: the report was not written: ${g.message}`))}AH(o,{recursive:!0,force:!0})}}async function gme(e,t,n,r,o){let s=()=>typeof o=="number"?[r.gate]:o.map(()=>r.gate),i=await ln(e,t,n?{config:n}:{});if("error"in i){if(!(n?await Re(n).catch(()=>{}):void 0)?.environments)return s();throw new S(`environments are read from the config at base, and it could not be read (${i.error}), so the wave's gate cannot be decided`)}let a=i.config;return a.environments?typeof o=="number"?[Iw(a,r.gate)]:o.map(c=>jw(c,a,r.gate)):s()}async function mme(e,t,n,r,o){let[s,i,a]=r.args,c=VO(r.cwd,i),u=o(c?.number??t.wave);switch(s){case"plan":{if(!c||!a)throw new Error("chant called the plan with no runs file");if(u.apply||u.w.planned){console.log(`wave ${u.k}: the run it waited for let go; planning again`),await u.release?.(),delete u.release;let p=u.w.observer;u.w={observer:p};for(let f of Object.keys(u.facts))delete u.facts[f];for(let f of["code","apply","held","denials","overriders","reviewMoved","resumes","changes","stored"])delete u[f]}let l=Fe(r.cwd,a),d=t.terragrunt?await Qme(e,t,n,u,c,l):await Rme(e,t,n,u,c,l);return d!==0&&(u.code=d),d}case"apply":return u.apply?u.apply(r.env.CHANT_APPLY_OUTCOME?Fe(r.cwd,r.env.CHANT_APPLY_OUTCOME):void 0,new Set((c?.runs??[]).map(l=>l.target))):(console.log(`${u.label}: chant asked to apply a wave this job did not plan`),re.failed);case"hold":return u.held?1:0;case"migrate-check":case"migrate":return Kme(e,t,u,s==="migrate"?r.env:void 0,r.cwd);case"gate":return Jme(e,t,n,u,r.env,r.cwd);default:return console.log(`terragucci: chant made a call this wave does not know: ${s}`),re.failed}}function ZO(e,t){e&&ml(e,JSON.stringify(t)+`
|
|
765
|
-
`)}function hme(e){let t=e.share!==void 0?{share:e.share}:{},n=[];return e.started&&n.push({phase:"plan",start:e.started,...e.plannedAt?{end:e.plannedAt}:{},...t}),e.gateSince&&n.push({phase:"gate",start:e.gateSince,...e.approvedAt?{end:e.approvedAt}:e.state!==void 0&&e.state!=="waiting"&&e.ended?{end:e.ended}:{}}),e.applyStarted&&n.push({phase:"apply",start:e.applyStarted,...e.ended&&e.state!=="applying"?{end:e.ended}:{},...t}),n}function yme(e,t){return e===re.applied?t.decided?"applying":"applied":e===re.waiting?"waiting":e===re.refused||e===re.held||e===re.superseded||t.refused?.reason==="policy"?"refused":"failed"}function wme(e,t,n,r){let o=ig(e,t.flat()),s=new Map(t.flatMap((a,c)=>a.map(u=>[u,c+1]))),i=new Map(n.map(a=>[a,(o.get(a)??[]).map(c=>({upstream:c.upstream,data:c.name,outputs:"applied"}))]));return{reads:i,waves:ni(s,[...i.values()].flat().map(a=>a.upstream),r)}}function bme(e,t,n,r,o,s){let i=et(e,t).after;if(!i)return o;let a=an(n.layers,n.canary),c=new Map(a.flatMap((d,p)=>d.map(f=>[f,p+1]))),u=Fo(i,n.layers.flat()),l=r.flatMap(d=>[...u.get(d)??[]]);return[...new Set([...o,...ni(c,l,s)])].sort((d,p)=>d-p)}function vme(e,t,n){let r=Fo(et(e,t).after,n);return r.size?new Map([...r].map(([o,s])=>[o,[...s].sort()])):void 0}function $me(e,t,n){let r=new Map(n.flatMap((o,s)=>o.map(i=>[i,s+1])));for(let[o,s]of t){if(s.length===0)continue;let i=[...new Set(s.map(a=>a.upstream))].sort();console.log(`${e}: ${o} plans on the state ${i.map(a=>`${a}${r.has(a)?` (wave ${r.get(a)})`:""}`).join(", ")} applied`)}}function kme(e,t){return!t.planned||t.share!==void 0||e.group!==void 0?{}:(t.totals??=
|
|
764
|
+
${N.stderr||N.stdout}`}}let U=fo({member:n,plan:L,planner:nn(w)}),F=U.entries.filter(M=>M.action!=="no-op"&&M.action!=="read");return{...k,member:{member:n,planDigest:U.member.planDigest?kb(U.member.planDigest,I):""},plan:L,...I?{cone:I}:{},changes:F.length,destroys:F.filter(M=>M.action==="delete"||M.action==="replace").length,summary:O.out.match(/Plan: .*|No changes\..*/)?.[0]??"planned"}}async function lme(e,t,n,r){let o=r?{...r,env:ls(dn({...r.env,TF_PLUGIN_CACHE_DIR:t.env.TF_PLUGIN_CACHE_DIR},t.root),Fe(e,t.root))}:void 0,s=await kp(e,t,o,"before-apply",t.planFile);if(s)return console.log(`FAILED ${t.root}: nothing applied`),console.log(Aa(s)),t.error=s,!1;n.reopen(t.timing);let i;try{i=await I$(n,t.timing,t.binary,["apply","-input=false","-no-color",..._a("apply",t.env),t.planFile],t.env,Fe(e,t.root))}finally{n.endRoot(t.timing)}if(i.code===0){let a=await kp(e,t,o,"after-apply");return a?(console.log(`FAILED ${t.root}: applied, then a step after the apply failed`),console.log(Aa(a)),t.error=a,!1):(console.log(`applied ${t.root}: ${[...i.out.matchAll(/Resources: .*destroyed/g)].pop()?.[0]??"done"}`),!0)}return console.log(`FAILED ${t.root}`),console.log(Aa(i.out)),!1}function dme(e,t){let n=t.GITHUB_REF_NAME||t.CI_COMMIT_BRANCH,r=t.GITHUB_SHA||t.CI_COMMIT_SHA;if(!n||!r)return;let o=xt(e,["ls-remote","origin",`refs/heads/${n}`]),s=o.status===0?o.stdout.split(/\s/)[0]?.trim():"";return s&&s!==r?s:void 0}var pme=e=>new Promise(t=>setTimeout(t,e));async function MH(e,t,n,r,o){let s=t.env??process.env,i={release:async()=>{}},a=g=>{let m=t.standDown?dme(e,s):void 0;if(m)return console.log(`${n}: a newer push to ${s.GITHUB_REF_NAME||s.CI_COMMIT_BRANCH} (${m.slice(0,8)}) applies everything; standing down`),o.refused={reason:"superseded",roots:o.changedRoots??[]},Mr(s,"superseded by a newer push",o),g().then(()=>({code:re.superseded,release:i.release}))},c=r.filter(g=>g.plan&&go(g.binary)==="resource").flatMap(g=>c1(g.root,Go(Fe(e,g.root)).estate,g.plan));if(c.length===0)return await a(i.release)??i;if(xt(e,["remote","get-url","origin"]).status!==0)return console.log(`${n}: this checkout has no origin, so nothing holds the resources it changes`),i;let u=[...new Set(c.map(g=>g.key))],l=sb(s),d=t.liveness??ib(s),p=(Number(s.TG_LOCK_POLL)>0?Number(s.TG_LOCK_POLL):10)*1e3,f=!1;for(let g=0;;g++){let m=await p1(e,c,l,d);if(m.ok){for(let k of m.tookOver)console.log(`${n}: run ${k.run} held resources and is gone; this wave takes them over, and each apply re-reads what that run left`);let w=new Set(c.map(k=>`${k.root} ${k.address}`)).size;console.log(`${n}: holding ${w===1?"the resource":`the ${w} resources`} its plans change, as run ${l.run}`);let b=async()=>{await f1(e,u,l.run)?console.log(`${n}: let go of the resources it held`):console.log(`${n}: could not let go of the resources it held; the next apply takes them over once this run is gone`)};return await a(b)??{release:b}}let h=g1(m.held,c);if(t.onHeld==="refuse"){console.log(`${n}: ${h}, so nothing in it was applied; apply again once that run is done`);let w=m.held[0].holder;return o.refused={reason:"held",roots:[...new Set(m.held.map(b=>c.find(k=>k.key===b.key)?.root??""))].filter(Boolean).sort(),holder:{run:w.run,...w.url?{url:w.url}:{}}},Mr(s,`held: ${h}`,o),{code:re.held,release:i.release}}if(f||console.log(`${n}: ${h}; waiting for it`),f=!0,g>=360)return console.log(`${n}: another run has held resources this wave changes for an hour (${h}), so nothing in it was applied`),Mr(s,`held for an hour: ${h}`,o),{code:re.failed,release:i.release};await pme(p);let y=await Qx(l1(e),u,l.run,d);if(y.live.length===0&&y.gone.length===0)return{code:PH,release:i.release}}}async function LH(e,t){let n=lo(t.gate);if(n===void 0)throw new S(`--gate must be one of ${sd.join(", ")}`);let r={...t,gate:n};if(r.approval!==void 0&&!Do.includes(r.approval))throw new S(`--approval must be one of ${Do.join(", ")}`);if(!Number.isInteger(r.wave)||r.wave<1)throw new S("--wave must be a wave number from 1");if(r.shares!==void 0&&!(Number.isInteger(r.shares)&&r.shares>=1))throw new S("--shares must be a whole number of 1 or more");if(r.share!==void 0&&(r.shares===void 0||!Number.isInteger(r.share)||r.share<1||r.share>r.shares))throw new S("--share must be a share number from 1 to --shares");if(r.rest&&!r.terragrunt)throw new S("--rest runs the waves of a Terragrunt repo, so it needs --terragrunt");if(r.groups&&r.terragrunt)throw new S("--groups splits a wave of plain roots by identity; a Terragrunt repo's units apply with terragrunt.credentials in one job");if(r.group!==void 0&&!r.groups)throw new S("--group names one of --groups, so it needs --groups");if(r.branches&&Object.keys(r.branches).length>0){let o=eme(r.layers,r.branches,r.branch||void 0);if(console.log(`wave ${r.wave}: ${o.note}`),o.layers.flat().length===0)return console.log(`wave ${r.wave}: no ${r.terragrunt?"unit":"root"} applies from ${r.branch||"this branch"}, so there is nothing to apply`),re.applied;let s=r.layers.flat().filter(i=>!o.layers.flat().includes(i));r={...r,layers:o.layers,branches:void 0,...s.length?{elsewhere:s}:{}}}return fme(e,r)}async function fme(e,t){let n=t,r=n.env??process.env,o=ih(Fe(OH(),"terragucci-apply-")),s=new Map,i=d=>{let p=s.get(d);if(!p){let f={},g=new sl(ol(r),Yt,r);g.wave={number:d,facts:f},p={k:d,w:{observer:g},facts:f,label:`wave ${d}`},s.set(d,p)}return p},a=re.failed,c=n.wave,u,l;try{let d;if(n.terragrunt)d=n.rest?n.wave:Math.max(n.layers.filter(_=>_.length>0).length,n.wave);else{let _=an(n.layers,n.canary);if(!_[n.wave-1])return i(n.wave).facts.nothing=!0,console.log(`wave ${n.wave}: this repo has ${_.length} waves, so there is nothing to apply`),a=re.applied;let E=await xme(e,n,_);if(E&&(n={...n,composition:E},_=zm(_,E,!0),_[n.wave-1].length===0))return i(n.wave).facts.nothing=!0,console.log(`wave ${n.wave}: its roots apply in the composed run in wave ${E.wave}, so this wave has nothing to apply`),a=re.applied;if(d=E?HU(an(n.layers,n.canary),E):_,n.groups){let R=n.shares;if(u=Eme(_[n.wave-1],n.groups,n.composition).map(x=>({...x,shares:R!==void 0?oh(x.roots,R):1})),n.group!==void 0){let x=u.findIndex(O=>O.name===n.group),A=u[x];if(!A||(n.share??1)>A.shares)return i(n.wave).facts.nothing=!0,console.log(`wave ${n.wave} of ${_.length}: group ${n.group} has ${A?`${A.roots.length} roots in ${A.shares} share${A.shares===1?"":"s"}`:"no root in this wave"}, so this job has nothing to apply`),a=re.applied;if(A.shares>1&&n.share===void 0)throw new S(`group ${A.name} of wave ${n.wave} spreads across ${A.shares} share jobs, so its job needs --share`);l=u.slice(0,x).reduce((O,I)=>O+I.shares,0)+(n.share??1),n={...n,shares:A.shares>1?A.shares:void 0,share:A.shares>1?n.share:void 0}}}else if(n.shares!==void 0){let R=oh(_[n.wave-1],n.shares);if(n.share!==void 0&&n.share>R)return i(n.wave).facts.nothing=!0,console.log(`wave ${n.wave} of ${_.length}: its ${_[n.wave-1].length} roots make ${R} share${R===1?"":"s"}, so share ${n.share} has nothing to apply`),a=re.applied;n=R>1?{...n,shares:R}:{...n,shares:void 0,share:void 0}}}let p=n.config??fe(e),f=n.base??bs(e),g;try{g=await Yr(e,{...n.base?{at:n.base}:{},...p?{config:p}:{},...n.approval?{flag:n.approval}:{}})}catch(_){if(n.gate!=="never")throw _}if(g?.point&&g.mode!=="ledger")throw new S(`apply.point is set at base and approval is ${g.mode}; a wave gate that asks a decision point takes its quorum from the point, so set approval to ledger or drop apply.point`);let m=await gme(e,f,p,n,n.composition&&typeof d!="number"?zm(an(n.layers,n.canary),n.composition,!1):d);typeof d!="number"&&m[n.wave-1]!==n.gate?(console.log(`wave ${n.wave}: gate ${m[n.wave-1]}, the strictest of its roots' environments at base`),n={...n,gate:m[n.wave-1]}):typeof d=="number"&&m[0]!==n.gate&&(console.log(`wave ${n.wave}: gate ${m[0]}, the strictest of the environments at base, since a Terragrunt wave finds its units when it plans`),n={...n,gate:m[0]});let h={rule:g,configPath:p,work:o,...l!==void 0?{part:l}:{},planned:_=>s.get(_)?.w.planned},y=await vH(e,o,_=>mme(e,n,h,_,i)),w;try{let _=mH({waves:d,...n.rest?{rest:!0}:{},gate:n.gate,...typeof d!="number"&&m.some(R=>R!==n.gate)?{gates:m}:{},approval:g?.mode??"ledger",...u?{jobs:{wave:n.wave,groups:u.map(R=>({name:R.name,runs:R.roots,shares:R.shares}))}}:n.shares!==void 0?{shares:{wave:n.wave,shares:n.shares}}:{},call:bH,migrations:YO(Fe(e,di)),...g?.point?{point:g.point}:{}}),E=hH(e,f,kH(e,_));if(n.share!==void 0||n.group!==void 0){let R=n.decided??Fe(e,RH(n.wave));if(!YO(R))throw new S(`the wave's decision ${R} cannot be read; the wave's own job writes it before its shares run`);_p(SH(Fe(e,j$(n.wave))),{recursive:!0}),_H(R,Fe(e,j$(n.wave)))}w=await $H(e,{wave:n.wave,base:E,...(u||n.shares!==void 0)&&n.share===void 0&&n.group===void 0?{decide:!0}:{},...n.group!==void 0?{group:n.group}:{},...n.share!==void 0?{share:n.share}:{},env:{...process.env,...r},...n.chant?{chant:n.chant}:{}})}finally{await y.close()}let b=w.result?.rest;b?.length&&(c=b[b.length-1].wave);let k=i(c);a=yH(w.code,k.code);let v=n.group!==void 0||n.share!==void 0,$=`${n.group!==void 0?`group ${n.group}`:""}${n.group!==void 0&&n.share!==void 0?" ":""}${n.share!==void 0?`share ${n.share}`:""}`;if(v&&w.result?.decision&&(k.w.digest=w.result.decision.digest,(w.result.decision.status==="approved"||w.result.decision.status==="not-required")&&(k.w.approval=w.result.decision.status)),v&&w.code===re.refused&&w.result?.moved.length){let _=[...w.result.moved].sort();w.result.decision?.status==="approved"&&(k.w.refused={reason:"approval",approved:w.result.decision.digest,roots:_}),Mr(n.env,`wave ${c} ${$} changed since the wave decided: ${_.join(", ")}`,k.w)}if((u||n.shares!==void 0)&&!v&&a===re.applied){let _=n.decided??Fe(e,RH(n.wave));_p(SH(_),{recursive:!0}),_H(Fe(e,j$(n.wave)),_),k.w.decided=!0;let E=u?u.reduce((x,A)=>x+A.shares,0):n.shares;k.w.shareCount=E;let R=u?`its ${u.length} group job${u.length===1?"":"s"} (${u.map(x=>x.name).join(", ")})`:`its ${E} share job${E===1?"":"s"}`;console.log(`${k.label}: ${w.result?.decision?.status==="approved"?"approved":"no approval needed"}; ${R} apply these plans`)}return a}finally{for(let d of s.values())await d.release?.().catch(p=>console.log(`${d.label}: the composed run's locks were not released: ${p.message}`));for(let d of[...s.keys()].sort((p,f)=>p-f)){let{w:p}=s.get(d),f=d===c?a:re.applied;p.observer.wave.code=f,the(r,d,f,p),p.state=yme(f,p),p.ended=new Date().toISOString(),p.planned&&await Sme(e,n,p,r).catch(g=>console.log(`wave ${d}: the report was not written: ${g.message}`))}AH(o,{recursive:!0,force:!0})}}async function gme(e,t,n,r,o){let s=()=>typeof o=="number"?[r.gate]:o.map(()=>r.gate),i=await ln(e,t,n?{config:n}:{});if("error"in i){if(!(n?await Re(n).catch(()=>{}):void 0)?.environments)return s();throw new S(`environments are read from the config at base, and it could not be read (${i.error}), so the wave's gate cannot be decided`)}let a=i.config;return a.environments?typeof o=="number"?[Iw(a,r.gate)]:o.map(c=>jw(c,a,r.gate)):s()}async function mme(e,t,n,r,o){let[s,i,a]=r.args,c=VO(r.cwd,i),u=o(c?.number??t.wave);switch(s){case"plan":{if(!c||!a)throw new Error("chant called the plan with no runs file");if(u.apply||u.w.planned){console.log(`wave ${u.k}: the run it waited for let go; planning again`),await u.release?.(),delete u.release;let p=u.w.observer;u.w={observer:p};for(let f of Object.keys(u.facts))delete u.facts[f];for(let f of["code","apply","held","denials","overriders","reviewMoved","resumes","changes","stored"])delete u[f]}let l=Fe(r.cwd,a),d=t.terragrunt?await Qme(e,t,n,u,c,l):await Rme(e,t,n,u,c,l);return d!==0&&(u.code=d),d}case"apply":return u.apply?u.apply(r.env.CHANT_APPLY_OUTCOME?Fe(r.cwd,r.env.CHANT_APPLY_OUTCOME):void 0,new Set((c?.runs??[]).map(l=>l.target))):(console.log(`${u.label}: chant asked to apply a wave this job did not plan`),re.failed);case"hold":return u.held?1:0;case"migrate-check":case"migrate":return Kme(e,t,u,s==="migrate"?r.env:void 0,r.cwd);case"gate":return Jme(e,t,n,u,r.env,r.cwd);default:return console.log(`terragucci: chant made a call this wave does not know: ${s}`),re.failed}}function ZO(e,t){e&&ml(e,JSON.stringify(t)+`
|
|
765
|
+
`)}function hme(e){let t=e.share!==void 0?{share:e.share}:{},n=[];return e.started&&n.push({phase:"plan",start:e.started,...e.plannedAt?{end:e.plannedAt}:{},...t}),e.gateSince&&n.push({phase:"gate",start:e.gateSince,...e.approvedAt?{end:e.approvedAt}:e.state!==void 0&&e.state!=="waiting"&&e.ended?{end:e.ended}:{}}),e.applyStarted&&n.push({phase:"apply",start:e.applyStarted,...e.ended&&e.state!=="applying"?{end:e.ended}:{},...t}),n}function yme(e,t){return e===re.applied?t.decided?"applying":"applied":e===re.waiting?"waiting":e===re.refused||e===re.held||e===re.superseded||t.refused?.reason==="policy"?"refused":"failed"}function wme(e,t,n,r){let o=ig(e,t.flat()),s=new Map(t.flatMap((a,c)=>a.map(u=>[u,c+1]))),i=new Map(n.map(a=>[a,(o.get(a)??[]).map(c=>({upstream:c.upstream,data:c.name,outputs:"applied"}))]));return{reads:i,waves:ni(s,[...i.values()].flat().map(a=>a.upstream),r)}}function bme(e,t,n,r,o,s){let i=et(e,t).after;if(!i)return o;let a=an(n.layers,n.canary),c=new Map(a.flatMap((d,p)=>d.map(f=>[f,p+1]))),u=Fo(i,n.layers.flat()),l=r.flatMap(d=>[...u.get(d)??[]]);return[...new Set([...o,...ni(c,l,s)])].sort((d,p)=>d-p)}function vme(e,t,n){let r=Fo(et(e,t).after,n);return r.size?new Map([...r].map(([o,s])=>[o,[...s].sort()])):void 0}function $me(e,t,n){let r=new Map(n.flatMap((o,s)=>o.map(i=>[i,s+1])));for(let[o,s]of t){if(s.length===0)continue;let i=[...new Set(s.map(a=>a.upstream))].sort();console.log(`${e}: ${o} plans on the state ${i.map(a=>`${a}${r.has(a)?` (wave ${r.get(a)})`:""}`).join(", ")} applied`)}}function kme(e,t){return!t.planned||t.share!==void 0||e.group!==void 0?{}:(t.totals??=FL(t.planned.map(n=>({path:n.root,plan:n.plan,planner:nn(e.binary),...n.error?{error:n.error}:{},...n.policy?{policy:n.policy}:{}}))),{totals:t.totals.totals,totals_roots:t.totals.roots})}async function sh(e,t,n,r,o=!1){let s=n.settings;if(!s?.reports?.bucket)return;let i=t.env??process.env;try{let a=zn(e,i,s.forge),c=t.terragrunt?n.units?.waves??t.layers:an(t.layers,t.canary),u=t.terragrunt?n.units?.edges??UR(Dn(e,n.settings?.terragrunt?.exclude)):Vs(e,t.layers.flat(),et(e,s).after),l=t.terragrunt?dD(e,c.flat()):Aw(e,t.layers.flat()),d=t.terragrunt?void 0:t.composition,p=KG(a.project,a.commit,d?zm(c,d,!1):c,u,l,d?.wave!==void 0&&!d.fallback?d:void 0),f=hme(n),g={...f.length?{spans:f}:{},...n.changedRoots?{changed:n.changedRoots}:{}},m=await ZG(dr(s.reports,i),s.reports.prefix,p,{number:t.wave,gate:Ct(t.wave),policy:t.gate,...g,...kme(t,n),...r});o||console.log(`wave ${t.wave}: run view at ${m}`)}catch(a){console.log(`wave ${t.wave}: the run view was not written: ${a.message}`)}}function _me(e){if(!e)return{};let t=e.members.some(n=>n.apply!==void 0||n.state!==void 0);return{composed:{roots:e.members.map(n=>n.root),digest:e.planDigest,...e.heldByUnknown?.length?{unknown:e.heldByUnknown}:{},...t?{members:e.members.map(n=>({root:n.root,module:n.module,...n.apply?{apply:n.apply}:{},...n.state?{state:n.state}:{}}))}:{}}}}async function Sme(e,t,n,r){let{wave:o,binary:s}=t,i=n.settings,a=new Map,c=0;for(let w of n.planned){if(w.plan===void 0)continue;let b=ys(w.plan);c+=b.values,a.set(w.root,{json:JSON.stringify(b.plan,null,2)+`
|
|
766
766
|
`})}let u=t.terragrunt?new Map(Dn(e,i.terragrunt?.exclude).map(w=>[w.path,w.dependencies])):vme(e,i,t.layers.flat()),l=w=>i.environments?hs(w,i):void 0,d=[...new Set(n.roots.map(l).filter(w=>w!==void 0))].sort(),p=Bu({run:{...zn(e,r,i.forge),stage:Yt,wave:o,...n.share!==void 0?{share:n.share}:{},binary:s,runtime:i.runtime,started:n.started,finished:new Date().toISOString(),terragucci:Pr},roots:n.planned.map(w=>jb(f(w),l(w.root))),waves:[{number:o,roots:n.roots,...d.length?{environments:d}:{},...n.digest?{setDigest:n.digest}:{},...n.approval?{approval:n.approval}:{},...n.gate?{gate:n.gate}:{},...n.waitingSince?{waitingSince:n.waitingSince}:{},...n.refused?{refused:n.refused}:{},...n.review?{review:n.review}:{},...n.excluded?.length?{excluded:n.excluded}:{},...n.heldBySteps?{heldBySteps:n.heldBySteps}:{},...n.waveCost?{cost:n.waveCost}:{},...n.state?{state:n.state}:{},...n.waveReads?.length?{reads:n.waveReads}:{},...n.preview?{preview:n.preview}:{},...n.composed?{composed:n.composed}:{}}],redacted:c,...n.policy?{policy:n.policy}:{}});t.composition&&(p.compose=wO(t.composition));function f(w){let b=t.composition,k={...w.policy?{policy:w.policy}:{},...b?.wave===o&&b.modules.has(w.root)&&n.composed?{composed:{wave:o,module:`module.${b.modules.get(w.root)}`}}:{}},v=w.steps?.length?{steps:w.steps}:{},$=u?.get(w.root)?.length?{dependencies:u.get(w.root)}:{};if(w.error&&!(w.policy&&w.policy.result!=="passed"&&w.plan!==void 0))return{path:w.root,planner:nn(s),...w.bin?{binary:w.bin}:{},error:w.error.split(`
|
|
767
767
|
`)[0],...k,...v,...$};let _=n.states?.get(w.root),E=n.recordVersions?.get(w.root),R=n.reads?.get(w.root)?.length?{reads:n.reads.get(w.root)}:{};return{path:w.root,plan:w.plan,planner:nn(s),...w.bin?{binary:w.bin}:{},...w.cone?{cone:w.cone}:{},files:{json:oa(w.root).json},...w.error?{error:w.error}:{},...k,...v,...R,...n.applied?.has(w.root)?{applied:!0}:{},..._?{state:_}:{},...E?{recordVersions:E}:{},...$}}n.cost&&(p.cost=n.cost),n.observer.addTimings(p,["plan","apply"]);let g=Fe(e,"terragucci-report"),m=HB(r),h=mO(p,{reports:i.reports,...m?{given:m}:{},traceId:n.observer.trace?.traceId,traceUrl:i.telemetry?.trace_url});Object.assign(p.run,h.run),n.observer.reportUrl=h.run.report_url,Qb(g,p,a,h.note),n.costOutputs&&xb(g,n.costOutputs);let y=p.timings?.roots[0];if(y&&console.log(`wave ${o}: report in terragucci-report/, slowest root ${y.root} (${y.seconds}s)`),i.reports?.bucket&&n.decided)console.log(`wave ${o}: the report stays with the job; the reports of its shares, which apply, go to ${i.reports.bucket}`);else if(i.reports?.bucket)try{let w=await nv(dr(i.reports,r),g,p,i.reports.prefix);console.log(`wave ${o}: report copied to ${w.prefix}${p.run.report_url?`, at ${p.run.report_url}`:""}`)}catch(w){console.log(`wave ${o}: the report was not copied to ${i.reports.bucket}: ${w.message}`)}if(n.state&&n.share!==void 0)await sh(e,t,n,n.state==="applied"?{shares_applied:[n.share]}:{state:n.state});else if(n.state){let w=n.state==="waiting"&&n.command?{command:n.command}:{};await sh(e,t,n,{state:n.state,...n.approval?{approval:n.approval}:{},...n.digest?{digest:n.digest}:{},...w,...n.excluded?.length?{excluded:n.excluded}:{},...n.shareCount?{shares:n.shareCount}:{},report:qg(p),..._me(n.composed)})}await n.observer.finish(p,r,w=>console.log(w))}async function D$(e,t,n){let r=ue(n?await Re(n):{});if(!t.base)return{settings:r};let o=await ln(e,t.base,n?{config:n}:{});if("error"in o)return{error:`the settings are read from ${t.base} and its config could not be read (${o.error})`};let s;try{s=ue(o.config)}catch(c){return{error:`the settings are read from ${t.base} and its config could not be read (${c.message})`}}let{policy:i,...a}=s;return{settings:{...a,...r.policy?{policy:r.policy}:{}}}}async function GH(e,t,n,r){let o;try{o=JM(e,t.base)}catch(s){return r?.length?{error:`steps are read from the config at base, and base could not be found (${s.message})`}:{}}try{let s=await xg(e,o,r,n?{config:n}:{});s.note&&console.log(s.note);let i=Eg(s.steps,"tf-apply");return i.length===0?{}:(console.log(`steps: ${i.length} read from terragucci.yml at ${s.from.length===40?s.from.slice(0,8):s.from}`),{ws:{steps:i,env:t.env??process.env}})}catch(s){return{error:s.message}}}function rh(e,t){_p(Fe(e,".."),{recursive:!0}),ml(e,JSON.stringify(t,null,2)+`
|
|
768
768
|
`)}var FH=(e,t,n,r,o)=>({target:e,planDigest:t,...n?{empty:!0}:{},...r>0?{destructive:!0}:{},...o?{denied:o}:{}});async function Rme(e,t,n,r,o,s){let{binary:i,gate:a}=t,{w:c,facts:u,k:l}=r,d=an(t.layers,t.canary),p=o.runs.map(C=>C.target),f=t.composition?.wave===l&&p.includes(wa(l))?wa(l):void 0,g=f?p.filter(C=>C!==f):p,m=f?[...g,...t.composition.order]:p,h=t.share,y=t.group!==void 0?`, group ${t.group}`:"",w=r.label=h!==void 0?`wave ${l} of ${d.length}${y}, share ${h} of ${t.shares}`:`wave ${l} of ${d.length}${y}`;if(n.part!==void 0?c.share=n.part:h!==void 0&&(c.share=h),p.length===0){u.nothing=!0;let C=t.composition?.wave!==void 0&&t.composition.wave!==l&&t.shares===void 0;return console.log(C?`${w}: its roots apply in wave ${t.composition.wave}'s composed run, so it has nothing to apply`:`${w}: the wave's roots make fewer shares than ${t.shares}, so this one has nothing to apply`),rh(s,{runs:[]}),r.apply=async()=>re.applied,re.applied}u.roots=p,console.log(`${w}: planning ${p.join(", ")}`),f&&console.log(`${w}: ${f} is one composed run of ${t.composition.order.join(", ")}`);let b=wme(e,d,g,l);c.reads=b.reads,c.waveReads=b.waves,$me(w,b.reads,d);let{configPath:k,work:v}=n,$=await D$(e,t,k);if("error"in $)return console.log(`${w}: ${$.error}, so nothing in it was applied`),re.failed;let _=c.settings=$.settings;c.waveReads=bme(e,_,t,g,c.waveReads??[],l);let E=await GH(e,t,k,_.steps);if("error"in E)return console.log(`${w}: ${E.error}, so nothing in it was applied`),re.failed;let R=E.ws,x=t.parallelism!==void 0?{value:t.parallelism,reason:"--parallelism"}:u$(e,m,_,t.env??process.env);p.length>1&&console.log(`${w}: planning ${x.value===1?"one root at a time":`up to ${x.value} roots at once`} (${x.reason})`);let A=new Array(g.length);c.started=new Date().toISOString(),await c.observer.collectSpans(C=>console.log(C));let O=ame(v,process.env),I=new jr(e,i,_.version,t.env??process.env,t.installer),N=new Map,L;try{L=await kc(e,_.sources,FT(e,t.base),k?{config:k}:{},{log:C=>console.log(`${w}: ${C}`)});for(let C of L?m:[]){let ee=await L.before(C);ee.length&&N.set(C,ee)}}catch(C){return console.log(`${w}: ${C.message}, so nothing in it was applied`),re.failed}await Pc(g,x.value,async(C,ee)=>{A[ee]=await cme(e,I,C,v,ee,c.observer,O,R,L?{refused:N.get(C),check:L}:void 0,t.base)});let U=f?await Tme(e,t,{label:w,target:f,work:v,binaries:I,cache:O,ws:R,sourcesOf:L,outside:N},c):void 0;if(U){A.push(U.planned),r.release=()=>U.run.release();for(let[C,ee]of U.reportReads)c.reads.set(C,ee)}c.plannedAt=new Date().toISOString(),c.planned=U?[...A.filter(C=>C.root!==f),...U.views]:A,c.roots=m;let F=A.filter(C=>C.error);for(let C of A)console.log(C.error?`FAILED ${C.root}: ${C.error.split(`
|
|
@@ -818,7 +818,7 @@ ${e.stderr}`.split(`
|
|
|
818
818
|
`)}
|
|
819
819
|
`,o=aq(e,Rp,"report.md");try{vhe(_he(o),{recursive:!0}),iq(o,r),n.GITHUB_STEP_SUMMARY&&iq(n.GITHUB_STEP_SUMMARY,r)}catch{}}var ch=".terragrunt-cache",gq=e=>`'${e.replace(/'/g,"'\\''")}'`;function mq(e,t){return{TG_TF_PATH:e,TG_NON_INTERACTIVE:"true",TG_PARALLELISM:String(t.parallelism),TG_PROVIDER_CACHE:"1"}}function Ep(){return`export TG_DOWNLOAD_DIR="$PWD/${ch}/sources" TG_PROVIDER_CACHE_DIR="$PWD/${ch}/providers"`}function hq(e,t,n,r){let o=[];return n!==r.terragrunt&&o.push({tool:"terragrunt",version:n}),t!==r[e]&&o.push({tool:e,version:t}),o}var Ahe=e=>[...hu,...e].map(t=>`--filter ${gq(hf(t))}`).join(" ");function yq(e,t){return["set -eu",Ep(),`${t} fmt -check -recursive -diff .`,"terragrunt hcl fmt --check --diff --no-color",...e.generate?["terragucci generate --check"]:[],...e.stacks?["terragrunt stack generate --non-interactive --no-color"]:[],...e.sources?["terragucci check-sources --units"]:[],`terragrunt hcl validate --inputs --no-color ${Ahe(e.exclude)}`,'echo "every unit is formatted and its inputs match its module"',"terragucci check-pins","terragucci check-policy"].join(`
|
|
820
820
|
`)}function lP(e,t,n,r=!1){return[...n?['export AWS_WEB_IDENTITY_TOKEN_FILE="$(mktemp)"',n]:[],`export TERRAGUCCI_REPO="$PWD" TERRAGUCCI_PHASE=${t} ${Au}=${gq(JSON.stringify(WN(e,t)))}`,'export TG_AUTH_PROVIDER_CMD="terragucci auth-provider"',...r?[]:['export TG_IAM_ASSUME_ROLE_WEB_IDENTITY_TOKEN="$AWS_WEB_IDENTITY_TOKEN_FILE"']].join(`
|
|
821
|
-
`)}var wq="terragucci-tg-${{ hashFiles('**/*.hcl') }}";function zc(e){return e==="gitlab"?{cache:[{key:"terragucci-terragrunt",paths:[`${ch}/`]}]}:{uses:"actions/cache@v4",with:{path:ch,key:wq,"restore-keys":"terragucci-tg-"}}}function bq(){return{uses:"actions/cache/restore@v4",with:{path:ch,key:wq,"restore-keys":"terragucci-tg-"}}}var Ohe={serialize(e){return iE.serialize(
|
|
821
|
+
`)}var wq="terragucci-tg-${{ hashFiles('**/*.hcl') }}";function zc(e){return e==="gitlab"?{cache:[{key:"terragucci-terragrunt",paths:[`${ch}/`]}]}:{uses:"actions/cache@v4",with:{path:ch,key:wq,"restore-keys":"terragucci-tg-"}}}function bq(){return{uses:"actions/cache/restore@v4",with:{path:ch,key:wq,"restore-keys":"terragucci-tg-"}}}var Ohe={serialize(e){return iE.serialize(A1(e,{}).entities)}},Phe=5,jhe="(env.TF_IN_AUTOMATION || 'set') != ''";function Ihe(e){if(e==="plan"||e==="replan")return"plan";if(e==="drift")return"drift";if(e.startsWith("apply-")||["confirm","mr-apply","resume","ephemeral","ephemeral-sweep","sweep"].includes(e))return"apply"}function kl(e,t){return e===void 0||!JS(e)?e:(t?e[t]:void 0)??e.default}function vq(e,t,n,r){if(!(t===void 0&&!r?.size))for(let[o,s]of e){let i=o==="workflow"?void 0:r?.get(o)??kl(t,Ihe(o)),a=s.props;i===void 0||!a||(n==="gitlab"?a.tags=typeof i=="string"?[i]:Array.isArray(i)?[...i]:[...i.labels??[]]:a["runs-on"]=typeof i=="string"?i:Array.isArray(i)?[...i]:{group:i.group,...i.labels?{labels:[...i.labels]}:{}})}}function Che(e,t){return!e||t==="gitlab"?{}:Object.fromEntries([...(e.secrets??[]).map(n=>[n,`\${{ secrets.${n} }}`]),...(e.vars??[]).map(n=>[n,`\${{ vars.${n} }}`])])}function Nhe(e){let t=new Map;for(let[o,s]of e){if(o==="workflow")continue;let i=s.props?.needs;for(let a of i===void 0?[]:Array.isArray(i)?i:[i])t.set(a,[...t.get(a)??[],o])}let n=new Map,r=(o,s)=>{let i=n.get(o);if(i!==void 0)return i;if(s.has(o))return 0;s.add(o);let a=Math.max(0,...(t.get(o)??[]).map(c=>1+r(c,s)));return n.set(o,a),a};for(let o of e.keys())o!=="workflow"&&r(o,new Set);return n}function Dhe(e){let t=Nhe(e);for(let[n,r]of e){let o=r.props;n==="workflow"||!o||o.needs!==void 0||typeof o.if!="string"||(t.get(n)??0)>Phe&&(o.if=`${jhe} && (${o.if})`)}}var pi="# Generated by terragucci.",gP={github:".github/workflows/terragucci-rollout.yml",forgejo:".forgejo/workflows/terragucci-rollout.yml"},Xr={github:".github/workflows/terragucci.yml",forgejo:".forgejo/workflows/terragucci.yml",gitlab:".gitlab/terragucci.yml"},lh={before:["build","test"],after:["deploy"]},Mhe={github:".github/workflows/terragucci-resume.yml",forgejo:".forgejo/workflows/terragucci-resume.yml"},mP={github:".github/workflows/terragucci-ephemeral.yml",forgejo:".forgejo/workflows/terragucci-ephemeral.yml"};function Lhe(e,t,n,r){return[...ki(e,t,"apply"),...Yo(e,t,"apply","terragucci-ephemeral"),...r?[r]:[],'if [ "${TG_ACTION:-}" = closed ]; then',` terragucci ephemeral down --pr "$TG_PR" --reason closed --binary ${n}`,"else",` terragucci ephemeral up --pr "$TG_PR" --head "$TG_SHA" --binary ${n}`,"fi"].join(`
|
|
822
822
|
`)}function Ghe(e,t,n){return[Uc,'git fetch -q origin "+refs/heads/${CI_DEFAULT_BRANCH}:refs/remotes/origin/${CI_DEFAULT_BRANCH}"',...Yo("gitlab",e,"apply","terragucci-ephemeral"),...n?[n]:[],`terragucci ephemeral up --pr "$CI_MERGE_REQUEST_IID" --head "\${CI_MERGE_REQUEST_SOURCE_BRANCH_SHA:-$CI_COMMIT_SHA}" --base "origin/\${CI_DEFAULT_BRANCH}" --binary ${t}`].join(`
|
|
823
823
|
`)}function $q(e,t,n,r){return[...ki(e,t,"apply"),...e==="gitlab"?[Uc]:[],...Yo(e,t,"apply","terragucci-ephemeral"),...r?[r]:[],`terragucci ephemeral sweep --binary ${n}`].join(`
|
|
824
824
|
`)}var kq=e=>e>=60?"0 * * * *":`*/${e} * * * *`,bt=class extends Error{},Uc='git remote set-url origin "${CI_SERVER_PROTOCOL}://oauth2:${TG_TOKEN}@${CI_SERVER_FQDN}/${CI_PROJECT_PATH}.git"',$i=(e,t)=>Kn({respond:e},t)!=="off";function dh(e,t){return[`export ${t??Gn[e]}="$TG_TOKEN"`,...e==="gitlab"?[Uc]:[]]}function _q(e,t,n){return["set -u",...dh(t,n),`terragucci respond fmt --mode apply --binary ${e} --branch "${t==="gitlab"?"$CI_COMMIT_BRANCH":"$GITHUB_REF_NAME"}" || true`].join(`
|
|
@@ -869,11 +869,11 @@ echo "$dir" >> "$GITHUB_PATH"`}var B$={version:"2.37.9",sha256:{x86_64:"6b3a6a3d
|
|
|
869
869
|
`),tradedPrelude:[Ep(),...g&&Object.keys(g).length>0?[lP(g,"plan",void 0,!0)]:[]].join(`
|
|
870
870
|
`)}}:{},...b?{prReview:!0}:{},...e.synth?{synth:e.synth}:{},...e.cost?{cost:!0}:{}},E=e.drift,R=e.rollouts,x=s.flat().sort();if(x.length===0)throw new bt(l?"there are no Terragrunt units to run":"there are no roots to run");let A={TF_IN_AUTOMATION:"1",TF_INPUT:"0",...l?mq(n,l):{},...i,...e.atlantisComments?{[Cv]:"1"}:{}},O=u?{OTEL_EXPORTER_OTLP_HEADERS:t==="gitlab"?`$${u}`:`\${{ secrets.${u} }}`}:{},I=e.decideTokenEnv?{[e.decideTokenEnv]:`\${{ secrets.${e.decideTokenEnv} }}`}:{},N=$i(e.respond,"description")?I:{},L=Kn({respond:e.respond},"drift")==="attribute"?I:{},U=$i(e.respond,"version-bump"),F=[...l?l.installs:o?[{tool:o.binary,version:o.version}]:[],...e.atmos?[{tool:"atmos",version:e.atmos.version}]:[],...e.terramate?[{tool:"terramate",version:e.terramate.version}]:[]],M=F.length>0?F.map(q=>F$(q.tool,q.version,t)).join(`
|
|
871
871
|
`):void 0,te=`Install ${F.map(q=>`${q.tool} ${q.version}`).join(", ")}`,ie=l?yq(l,n):zhe(n,x,e.synth,e.rootPins===!0,e.generate===!0,e.atmos!==void 0,e.sources===!0),Q=e.synth?{synth:e.synth}:{},ne=e.cost?{INFRACOST_API_KEY:t==="gitlab"?`$${e.cost.keySecret}`:`\${{ secrets.${e.cost.keySecret} }}`}:{},ae=e.cost&&e.cost.keySecret!=="INFRACOST_API_KEY"?ne:{},he=e.cost?.install?F$("infracost",sv,t):void 0,$e=e.notify?{notify:!0}:{},ye=Object.fromEntries([["slack","TERRAGUCCI_SLACK_WEBHOOK"],["teams","TERRAGUCCI_TEAMS_WEBHOOK"],["webhook","TERRAGUCCI_WEBHOOK"],["webhook_key","TERRAGUCCI_WEBHOOK_KEY"]].filter(([q])=>e.notify?.[q]).map(([q,Se])=>[Se,t==="gitlab"?`$${e.notify[q]}`:`\${{ secrets.${e.notify[q]} }}`]));e.notify?.relay&&(ye.TERRAGUCCI_RELAY=e.notify.relay);let Z=Object.fromEntries(Object.entries(ye).filter(([q])=>q==="TERRAGUCCI_SLACK_WEBHOOK"||q==="TERRAGUCCI_TEAMS_WEBHOOK")),me=Object.keys(Z).length>0,J=e.gate??"on-destructive",j=(e.environmentGates??[]).some(q=>q!=="never"),z=l?s.length:an(s,e.canary).length,X=l?{terragrunt:{prelude:[Ep(),...W$(t,"apply",a,g)].join(`
|
|
872
|
-
`)}}:{},V=e.applyWhen==="pull-request",W=l?s:an(s,e.canary),G=q=>f&&W[q]?oh(W[q],f):1,C=!l&&e.applyGroups&&e.applyGroups.length>0?e.applyGroups:void 0,ee=C?.some(q=>q.environment!==void 0)===!0;if(ee){if(e.applyWhen==="pull-request")throw new bt(`apply.when: ${yR}`);if(e.resume&&t!=="gitlab")throw new bt(`apply.resume: ${wR}`);if(e.ephemeral)throw new bt(`ephemeral: ${bR}`)}let le=C?.some(q=>q.audience!==void 0)===!0,_e=(q,Se)=>q&&le?{...q,audience:{plan:Es(q,"plan"),apply:Se??Es(q,"apply"),drift:Es(q,"drift")}}:q,xe=C?pM(C):void 0,Le=C!==void 0||W.some((q,Se)=>G(Se)>1),Ot=go(n),rt=Ot==="repo",st=Le,Hn=[],Jt=[];for(let q=0;q<z;q++){let Se=q+1,ze=`apply-wave-${Se}`;if(C){let tn={wave:Se,canary:e.canary,gate:J,...j?{environmentWaits:j}:{},...e.approval?{approval:e.approval}:{},respond:e.respond,...Q,...$e,...e.policy?{policy:!0}:{},...e.cost?.approveAbove?{costGate:!0}:{},...f?{shares:f}:{},...m?{branches:m}:{},groups:xe},dt=_e(a);Hn.push({name:ze,wave:Se,needs:Jt,step:`Plan wave ${Se} of ${z} and decide its gate`,body:uh(n,s,t,dt,{...tn,sharedLock:ze}),decides:!0,applies:[],...dt!==a?{oidc:dt}:{}}),Jt=[];for(let un of C){let Ur=W[q].filter(Sr=>un.roots.includes(Sr));if(Ur.length===0)continue;let Co=f?oh(Ur,f):1,zt=_e(un.oidc,un.audience),Ba=un.environment!==void 0?sye(t,un,un.runner??kl(e.runner,"apply"),h,$):void 0,fu={group:un.name,...zt?{oidc:zt}:{},...un.runner!==void 0?{runner:un.runner}:{},...un.forgeEnvironment!==void 0&&t!=="forgejo"?{environment:un.forgeEnvironment}:{}};for(let Sr=1;Sr<=Co;Sr++){let Ee=Co>1?`${ze}-${un.name}-share-${Sr}`:`${ze}-${un.name}`;Hn.push({...fu,name:Ee,wave:Se,needs:[ze],step:`Apply group ${un.name}${Co>1?`, share ${Sr} of ${Co},`:""} of wave ${Se}`,body:uh(n,s,t,zt,{...tn,group:un.name,...Co>1?{share:Sr}:{},sharedLock:Ee,...Ba?{checkJob:Ba}:{}}),applies:Co>1?xq(Ur,Sr,Co):Ur,...Co>1?{share:Sr}:{}}),Jt.push(Ee)}}continue}let He=G(q),qe={wave:Se,...l?{}:{canary:e.canary},gate:J,...j?{environmentWaits:j}:{},...e.approval?{approval:e.approval}:{},respond:e.respond,...X,...Q,...$e,...e.policy?{policy:!0}:{},...e.cost?.approveAbove?{costGate:!0}:{},...He>1?{shares:f}:{},...m?{branches:m}:{}};if(Hn.push({name:ze,wave:Se,needs:Jt,step:He>1?`Plan wave ${Se} of ${z} and decide its gate`:`Apply wave ${Se} of ${z}`,body:uh(n,s,t,a,{...qe,...st?{sharedLock:ze}:{}}),applies:He>1?[]:[...W[q]],...He>1?{decides:!0}:{}}),Jt=[ze],He>1){Jt=Array.from({length:He},(tn,dt)=>`${ze}-share-${dt+1}`);for(let[tn,dt]of Jt.entries())Hn.push({name:dt,wave:Se,share:tn+1,needs:[ze],step:`Apply share ${tn+1} of ${He} of wave ${Se}`,body:uh(n,s,t,a,{...qe,share:tn+1,...st?{sharedLock:dt}:{}}),applies:xq(W[q],tn+1,He)})}}if(Jt.length>1&&l){let q={wave:z+1,gate:J,...j?{environmentWaits:j}:{},...e.approval?{approval:e.approval}:{},respond:e.respond,...X,...$e,...e.policy?{policy:!0}:{},...e.cost?.approveAbove?{costGate:!0}:{},...st?{sharedLock:"apply-rest"}:{}};Hn.push({name:"apply-rest",wave:z+1,needs:Jt,step:"Apply any wave past the pipeline's, then say every wave applied",body:uh(n,s,t,a,q),applies:[]})}else(Jt.length>1||C&&Jt.length>0)&&Hn.push({name:"apply-done",wave:z,needs:Jt,step:"Say every wave applied",body:Xhe(s,t),done:!0,applies:[]});if(V&&e.applyMerge==="auto"&&t==="forgejo"&&!e.applyMergeTokenEnv)throw new bt("apply.merge: auto on Forgejo needs apply.merge_token_env: Forgejo refuses a merge made with the job's own token, so name the secret holding the token of a user who may push to the default branch");if(V&&t==="gitlab"&&!e.comments)throw new bt(`apply.when: ${us.comments}`);if(V&&t==="gitlab"&&!e.applyMergeTokenEnv)throw new bt(`apply.when: ${us.token}`);let qn=e.locksPlan===!0;if(qn&&t==="gitlab")throw new bt(`locks: ${Df}`);let $r=V?[]:Hn,Oi=new Map($r.filter(q=>q.runner!==void 0).map(q=>[q.name,q.runner])),K=V&&e.applyMerge==="auto",de=V?"confirm":Hn[Hn.length-1].name,Ge=V?[{name:t==="gitlab"?"mr-apply":"apply-comment",needs:[],applies:x}]:Hn.map(q=>({name:q.name,needs:q.needs.length===0&&t!=="gitlab"&&!q.done?["check"]:[...q.needs],wave:q.wave,applies:q.applies})),mt={...l?X:{canary:e.canary},...Q,...$e,...st&&rt&&t==="github"?{lockTag:!0}:{},gate:J,...e.approval?{approval:e.approval}:{},respond:e.respond,...m?{branches:m}:{},...V?{when:"pull-request",...e.applyMerge?{merge:e.applyMerge}:{},...e.applyRequires?{requires:e.applyRequires}:{}}:{}},kr=J!=="never"||j||e.cost?.approveAbove===!0||t==="github"&&e.migrations===!0,Ze=l?"unit":"root",vt=$i(e.respond,"fmt");if(e.synth&&E&&Kn({respond:e.respond},"drift")==="pull-request")throw new bt(`respond.drift: ${e.atmos?RS:Fy}`);if(e.synth&&R)throw new bt(`rollouts: ${e.atmos?wf:ed}`);let _r=!e.synth&&$i(e.respond,"drift")?{tokenEnv:c}:void 0;if(e.agentDrift&&!E)throw new bt("agent.drift runs when the drift job opens the drift issue; set drift to a cron schedule");if(e.agentDrift&&_r)throw new bt(`respond.drift: ${BS}`);let is=$i(e.respond,"tips"),Ce=Kn({respond:e.respond},"apply-failed")==="agent",Ul=!l&&Kn({respond:e.respond},"drift")==="agent",Bl=Kn({respond:e.respond},"drift")==="attribute"?Fhe(t):void 0;if(e.comments&&t!=="gitlab")throw new bt(`comments: ${sR}`);if(t==="gitlab"){if(e.agentComment&&!e.comments)throw new bt(`agent.comment: ${Pf.agent}`);if(e.agentDrift)throw new bt("agent.drift runs on GitHub and Forgejo; leave agent.drift unset on GitLab");if(e.review&&!e.comments)throw new bt(`review: ${Pf.review}`);let q=e.gitlabToken==="protected";if(q&&!e.comments)throw new bt(`gitlab.token: ${iR}`);let Se=new
|
|
872
|
+
`)}}:{},V=e.applyWhen==="pull-request",W=l?s:an(s,e.canary),G=q=>f&&W[q]?oh(W[q],f):1,C=!l&&e.applyGroups&&e.applyGroups.length>0?e.applyGroups:void 0,ee=C?.some(q=>q.environment!==void 0)===!0;if(ee){if(e.applyWhen==="pull-request")throw new bt(`apply.when: ${yR}`);if(e.resume&&t!=="gitlab")throw new bt(`apply.resume: ${wR}`);if(e.ephemeral)throw new bt(`ephemeral: ${bR}`)}let le=C?.some(q=>q.audience!==void 0)===!0,_e=(q,Se)=>q&&le?{...q,audience:{plan:Es(q,"plan"),apply:Se??Es(q,"apply"),drift:Es(q,"drift")}}:q,xe=C?pM(C):void 0,Le=C!==void 0||W.some((q,Se)=>G(Se)>1),Ot=go(n),rt=Ot==="repo",st=Le,Hn=[],Jt=[];for(let q=0;q<z;q++){let Se=q+1,ze=`apply-wave-${Se}`;if(C){let tn={wave:Se,canary:e.canary,gate:J,...j?{environmentWaits:j}:{},...e.approval?{approval:e.approval}:{},respond:e.respond,...Q,...$e,...e.policy?{policy:!0}:{},...e.cost?.approveAbove?{costGate:!0}:{},...f?{shares:f}:{},...m?{branches:m}:{},groups:xe},dt=_e(a);Hn.push({name:ze,wave:Se,needs:Jt,step:`Plan wave ${Se} of ${z} and decide its gate`,body:uh(n,s,t,dt,{...tn,sharedLock:ze}),decides:!0,applies:[],...dt!==a?{oidc:dt}:{}}),Jt=[];for(let un of C){let Ur=W[q].filter(Sr=>un.roots.includes(Sr));if(Ur.length===0)continue;let Co=f?oh(Ur,f):1,zt=_e(un.oidc,un.audience),Ba=un.environment!==void 0?sye(t,un,un.runner??kl(e.runner,"apply"),h,$):void 0,fu={group:un.name,...zt?{oidc:zt}:{},...un.runner!==void 0?{runner:un.runner}:{},...un.forgeEnvironment!==void 0&&t!=="forgejo"?{environment:un.forgeEnvironment}:{}};for(let Sr=1;Sr<=Co;Sr++){let Ee=Co>1?`${ze}-${un.name}-share-${Sr}`:`${ze}-${un.name}`;Hn.push({...fu,name:Ee,wave:Se,needs:[ze],step:`Apply group ${un.name}${Co>1?`, share ${Sr} of ${Co},`:""} of wave ${Se}`,body:uh(n,s,t,zt,{...tn,group:un.name,...Co>1?{share:Sr}:{},sharedLock:Ee,...Ba?{checkJob:Ba}:{}}),applies:Co>1?xq(Ur,Sr,Co):Ur,...Co>1?{share:Sr}:{}}),Jt.push(Ee)}}continue}let He=G(q),qe={wave:Se,...l?{}:{canary:e.canary},gate:J,...j?{environmentWaits:j}:{},...e.approval?{approval:e.approval}:{},respond:e.respond,...X,...Q,...$e,...e.policy?{policy:!0}:{},...e.cost?.approveAbove?{costGate:!0}:{},...He>1?{shares:f}:{},...m?{branches:m}:{}};if(Hn.push({name:ze,wave:Se,needs:Jt,step:He>1?`Plan wave ${Se} of ${z} and decide its gate`:`Apply wave ${Se} of ${z}`,body:uh(n,s,t,a,{...qe,...st?{sharedLock:ze}:{}}),applies:He>1?[]:[...W[q]],...He>1?{decides:!0}:{}}),Jt=[ze],He>1){Jt=Array.from({length:He},(tn,dt)=>`${ze}-share-${dt+1}`);for(let[tn,dt]of Jt.entries())Hn.push({name:dt,wave:Se,share:tn+1,needs:[ze],step:`Apply share ${tn+1} of ${He} of wave ${Se}`,body:uh(n,s,t,a,{...qe,share:tn+1,...st?{sharedLock:dt}:{}}),applies:xq(W[q],tn+1,He)})}}if(Jt.length>1&&l){let q={wave:z+1,gate:J,...j?{environmentWaits:j}:{},...e.approval?{approval:e.approval}:{},respond:e.respond,...X,...$e,...e.policy?{policy:!0}:{},...e.cost?.approveAbove?{costGate:!0}:{},...st?{sharedLock:"apply-rest"}:{}};Hn.push({name:"apply-rest",wave:z+1,needs:Jt,step:"Apply any wave past the pipeline's, then say every wave applied",body:uh(n,s,t,a,q),applies:[]})}else(Jt.length>1||C&&Jt.length>0)&&Hn.push({name:"apply-done",wave:z,needs:Jt,step:"Say every wave applied",body:Xhe(s,t),done:!0,applies:[]});if(V&&e.applyMerge==="auto"&&t==="forgejo"&&!e.applyMergeTokenEnv)throw new bt("apply.merge: auto on Forgejo needs apply.merge_token_env: Forgejo refuses a merge made with the job's own token, so name the secret holding the token of a user who may push to the default branch");if(V&&t==="gitlab"&&!e.comments)throw new bt(`apply.when: ${us.comments}`);if(V&&t==="gitlab"&&!e.applyMergeTokenEnv)throw new bt(`apply.when: ${us.token}`);let qn=e.locksPlan===!0;if(qn&&t==="gitlab")throw new bt(`locks: ${Df}`);let $r=V?[]:Hn,Oi=new Map($r.filter(q=>q.runner!==void 0).map(q=>[q.name,q.runner])),K=V&&e.applyMerge==="auto",de=V?"confirm":Hn[Hn.length-1].name,Ge=V?[{name:t==="gitlab"?"mr-apply":"apply-comment",needs:[],applies:x}]:Hn.map(q=>({name:q.name,needs:q.needs.length===0&&t!=="gitlab"&&!q.done?["check"]:[...q.needs],wave:q.wave,applies:q.applies})),mt={...l?X:{canary:e.canary},...Q,...$e,...st&&rt&&t==="github"?{lockTag:!0}:{},gate:J,...e.approval?{approval:e.approval}:{},respond:e.respond,...m?{branches:m}:{},...V?{when:"pull-request",...e.applyMerge?{merge:e.applyMerge}:{},...e.applyRequires?{requires:e.applyRequires}:{}}:{}},kr=J!=="never"||j||e.cost?.approveAbove===!0||t==="github"&&e.migrations===!0,Ze=l?"unit":"root",vt=$i(e.respond,"fmt");if(e.synth&&E&&Kn({respond:e.respond},"drift")==="pull-request")throw new bt(`respond.drift: ${e.atmos?RS:Fy}`);if(e.synth&&R)throw new bt(`rollouts: ${e.atmos?wf:ed}`);let _r=!e.synth&&$i(e.respond,"drift")?{tokenEnv:c}:void 0;if(e.agentDrift&&!E)throw new bt("agent.drift runs when the drift job opens the drift issue; set drift to a cron schedule");if(e.agentDrift&&_r)throw new bt(`respond.drift: ${BS}`);let is=$i(e.respond,"tips"),Ce=Kn({respond:e.respond},"apply-failed")==="agent",Ul=!l&&Kn({respond:e.respond},"drift")==="agent",Bl=Kn({respond:e.respond},"drift")==="attribute"?Fhe(t):void 0;if(e.comments&&t!=="gitlab")throw new bt(`comments: ${sR}`);if(t==="gitlab"){if(e.agentComment&&!e.comments)throw new bt(`agent.comment: ${Pf.agent}`);if(e.agentDrift)throw new bt("agent.drift runs on GitHub and Forgejo; leave agent.drift unset on GitLab");if(e.review&&!e.comments)throw new bt(`review: ${Pf.review}`);let q=e.gitlabToken==="protected";if(q&&!e.comments)throw new bt(`gitlab.token: ${iR}`);let Se=new O1({name:r}),ze=Ke=>M?[M,Ke]:[Ke],He=(Ke,Rr)=>`bash <<'${Ke}' || exit $?
|
|
873
873
|
${Rr}
|
|
874
874
|
${Ke}`,qe={...A,...O,TG_TOKEN:`$${c??"GITLAB_TOKEN"}`,TG_SHA:"$CI_COMMIT_SHA",TG_BRANCH:"$CI_DEFAULT_BRANCH"},tn=(Ke,Rr=a)=>$?{id_tokens:{...vP(Rr)||g?{TERRAGUCCI_OIDC:{aud:Es(Rr,Ke)}}:{},...Rr?.gcp?{[wP]:{aud:ph(Rr.gcp.workload_identity_provider)}}:{},...Rr?.azure?{[bP]:{aud:Rr.azure.audience??fh}}:{}}}:{},dt=!!(E||e.comments||e.resume||R||y),Ur=[...V?[wl]:[],...e.agentComment?[gl]:[],...e.review?[Lc]:[]].map(Ke=>` && $${Ke} == null`).join(""),Co=dt?{rules:[new on({if:`$CI_PIPELINE_SOURCE != "schedule"${Ur}`})]}:{},zt=`${dt?'$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH && $CI_PIPELINE_SOURCE != "schedule"':"$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH"}${Ur}`,Ba=h.map(Ke=>` || $CI_COMMIT_BRANCH == "${Ke}"`).join(""),fu=Ba?`($CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH${Ba})${dt?' && $CI_PIPELINE_SOURCE != "schedule"':""}${Ur}`:zt,Sr=h.map(Ke=>` && $CI_COMMIT_BRANCH != "${Ke}"`).join(""),Ee=`$CI_PIPELINE_SOURCE == "api" && $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH && $${wl}`,yt={environment:{name:kye,action:"access"}},gf=vt&&!q,No=[`unset ${[...new Set(["TG_TOKEN","TG_MERGE_TOKEN","GITLAB_TOKEN","CI_JOB_TOKEN",...c?[c]:[]])].join(" ")}`,ie].join(`
|
|
875
875
|
`),Ry=new rn({stage:"check",image:Se,variables:{...A,TG_BRANCH:qe.TG_BRANCH,...e.policy?{GIT_DEPTH:"0"}:{}},...Co,script:ze(No),artifacts:{name:Rp,when:"always",paths:[`${Rp}/`]}}),{TG_TOKEN:xy,...rY}=qe,oY=new rn({stage:"plan",image:Se,variables:{...q?rY:qe,TG_PR:"$CI_MERGE_REQUEST_IID",GIT_DEPTH:"0",...ae},rules:[new on({if:'$CI_PIPELINE_SOURCE == "merge_request_event" && $CI_MERGE_REQUEST_SOURCE_PROJECT_PATH == $CI_PROJECT_PATH'})],...tn("plan"),...l?zc("gitlab"):{},script:[...he?[he]:[],...ze(He("PLAN",q?dye(n,s,a,_,c):lye(n,s,t,a,_)))],artifacts:{name:be,when:"always",paths:[`${be}/`],reports:{terraform:`${be}/gitlab-terraform.json`}}}),Vt=new Map([["check",Ry]]);gf&&Vt.set("fmt",new rn({stage:"check",image:Se,needs:["check"],variables:{...A,TG_TOKEN:qe.TG_TOKEN},rules:[new on({if:`$CI_COMMIT_BRANCH && $CI_COMMIT_BRANCH != $CI_DEFAULT_BRANCH${Sr} && $CI_PIPELINE_SOURCE != "schedule"${Ur}`,when:"on_failure"})],script:ze(He("FMT",_q(n,t,c)))})),Vt.set("plan",oY);for(let Ke of $r){if(Ke.done){Vt.set(Ke.name,new rn({stage:"apply",image:Se,needs:Ke.needs.map(Rr=>({job:Rr,artifacts:!1})),variables:{TG_TOKEN:qe.TG_TOKEN,TG_SHA:qe.TG_SHA},rules:[new on({if:fu})],script:[He("APPLY",Ke.body)]}));continue}Vt.set(Ke.name,new rn({stage:"apply",image:Se,...Ke.needs.length>0?{needs:Ke.needs}:{},variables:{...qe,TG_BEFORE:"$CI_COMMIT_BEFORE_SHA",...ye,...ae},rules:[new on({if:fu})],...rt&&!Le?{resource_group:"terragucci-apply"}:{},...tn(C&&Ke.decides?"plan":"apply",Ke.oidc??a),...Ke.environment!==void 0?{environment:{name:Ke.environment}}:{},...l?zc("gitlab"):{},script:[...he?[he]:[],...ze(He("APPLY",Ke.body))],artifacts:{name:`${be}-${Ke.name}`,when:"always",paths:[`${be}/`,...Ke.decides?[`${hl}/`]:[],...Ce?[`${vl}/`]:[]]}}))}if(V&&(Vt.set("confirm",new rn({stage:"apply",image:Se,variables:qe,rules:[new on({if:zt})],...tn("apply"),...l?zc("gitlab"):{},script:ze(He("CONFIRM",Pq(n,s,t,a,_))),artifacts:{name:`${be}-confirm`,when:"always",paths:[`${be}/`]}})),Vt.set("mr-apply",new rn({stage:"apply",image:Se,variables:{...qe,GIT_DEPTH:"0",...ye,...ae},rules:[new on({if:Ee})],...rt?{resource_group:"terragucci-apply"}:{},...tn("apply"),...l?zc("gitlab"):{},script:[...he?[he]:[],...ze(He("APPLY",nye(n,s,a,mt)))],artifacts:{name:`${be}-mr-apply`,when:"always",paths:[`${be}/`,...Ce?[`${vl}/`]:[]]}})),K&&Vt.set("pr-merge",new rn({stage:"apply",image:Se,needs:[{job:"mr-apply",artifacts:!1}],variables:{TG_TOKEN:qe.TG_TOKEN,TG_MERGE_TOKEN:`$${e.applyMergeTokenEnv}`},rules:[new on({if:Ee})],...yt,script:[He("MERGE",tye())]}))),is&&Vt.set("tips",new rn({stage:"tips",image:Se,needs:[de],variables:{...qe,GIT_DEPTH:"0"},rules:[new on({if:zt})],resource_group:"terragucci-tips",script:ze(He("TIPS",Sq(n,t,c,e.synth)))})),U&&Vt.set("version-bump",new rn({stage:"version-bump",image:Se,needs:[de],variables:{...qe,GIT_DEPTH:"0"},rules:[new on({if:zt})],resource_group:"terragucci-version-bump",script:[He("BUMP",Rq(t,c))]})),e.publish&&Vt.set("publish",new rn({stage:"publish",image:Se,needs:[de],variables:{...A,TG_TOKEN:qe.TG_TOKEN,TG_SHA:qe.TG_SHA,TG_BRANCH:qe.TG_BRANCH,GIT_DEPTH:"0"},rules:[new on({if:zt})],resource_group:"terragucci-publish",script:[...e.attest?[F$("cosign",Ud,t)]:[],...ze(He("PUBLISH",Aq(t)))]})),E&&Vt.set("drift",new rn({stage:"drift",image:Se,variables:{...qe,...Z},rules:[new on({if:`$CI_PIPELINE_SOURCE == "schedule" && $${ci} != "comments"${e.resume?` && $${ci} != "resume"`:""}${R?` && $${ci} != "rollouts"`:""}${y?` && $${ci} != "ephemeral"`:""}`})],...tn("drift"),...l?zc("gitlab"):{},script:[...M?[M]:[],...Bl?[Bl]:[],He("DRIFT",Oq(n,s,t,a,_,_r,me))],artifacts:{name:`${be}-drift`,when:"always",paths:[`${be}/`,...Ul?[`${vl}/`]:[]]}})),R&&Vt.set("rollout",new rn({stage:"rollout",image:Se,variables:{...A,TG_TOKEN:qe.TG_TOKEN,GIT_DEPTH:"0"},rules:[new on({if:`$CI_PIPELINE_SOURCE == "schedule" && $${ci} == "rollouts"`})],resource_group:"terragucci-rollout",script:ze(He("ROLLOUT",Cq(t,c)))})),e.comments&&Vt.set("comments",new rn({stage:"comments",image:Se,variables:{TG_TOKEN:qe.TG_TOKEN,...V?{TG_MERGE_TOKEN:`$${e.applyMergeTokenEnv}`}:{},...e.atlantisComments?{[Cv]:"1"}:{},GIT_STRATEGY:"none"},rules:[new on({if:`$CI_PIPELINE_SOURCE == "schedule" && $${ci} == "comments"`})],resource_group:"terragucci-comments",...V?yt:{},script:[He("COMMENTS",_ye(s,V?{...e.applyRequires?{requires:e.applyRequires}:{}}:void 0,q,{agent:!!e.agentComment,review:!!e.review}))]})),e.agentComment){let Ke=e.agentComment,Rr=`$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH && $${gl}`;Vt.set("agent",new rn({stage:"comments",image:Se,variables:{TG_TOKEN:qe.TG_TOKEN,TG_AGENT_MAX_TURNS:String(Ke.maxTurns)},rules:[new on({if:Rr})],timeout:`${Ke.timeout} minutes`,script:[He("AGENT",QH(Ke,th))],artifacts:{name:th,when:"always",paths:[`${th}/`]}})),Vt.set("agent-push",new rn({stage:"comments",image:Se,needs:["agent"],variables:{TG_TOKEN:`$${Ke.tokenSecret}`},rules:[new on({if:Rr})],script:[He("AGENT_PUSH",eq(Ke.policyDir,th))]}))}if(e.review){let Ke=`$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH && $${Lc}`;Vt.set(wp,new rn({stage:"comments",image:Se,variables:{TG_TOKEN:qe.TG_TOKEN,TG_DEFAULT_BRANCH:"$CI_DEFAULT_BRANCH",GIT_DEPTH:"0"},rules:[new on({if:Ke})],timeout:`${e.review.timeout} minutes`,script:[He("REVIEW",oq(e.review,Mc))],artifacts:{name:Mc,when:"always",paths:[`${Mc}/`]}})),Vt.set("review-note",new rn({stage:"comments",image:Se,needs:[wp],variables:{TG_TOKEN:qe.TG_TOKEN,GIT_STRATEGY:"none"},rules:[new on({if:Ke,when:"always"})],script:[He("REVIEW_NOTE",`set -uo pipefail
|
|
876
|
-
terragucci review post --forge gitlab --dir ${Mc}`)]}))}e.resume&&Vt.set("resume",new rn({stage:"resume",image:Se,variables:{TG_TOKEN:qe.TG_TOKEN},rules:[new on({if:`$CI_PIPELINE_SOURCE == "schedule" && $${ci} == "resume"`})],resource_group:"terragucci-resume",script:[He("RESUME","terragucci resume --forge gitlab")]})),y&&(Vt.set("ephemeral",new rn({stage:"apply",image:Se,variables:{...qe,TG_PR:"$CI_MERGE_REQUEST_IID",GIT_DEPTH:"0"},rules:[new on({if:'$CI_PIPELINE_SOURCE == "merge_request_event" && $CI_MERGE_REQUEST_SOURCE_PROJECT_PATH == $CI_PROJECT_PATH'})],resource_group:"terragucci-ephemeral-$CI_MERGE_REQUEST_IID",...tn("apply"),script:ze(He("EPHEMERAL",Ghe(a,n,w)))})),Vt.set("ephemeral-sweep",new rn({stage:"apply",image:Se,variables:{...qe,GIT_DEPTH:"0"},rules:[new on({if:`$CI_PIPELINE_SOURCE == "schedule" && $${ci} == "ephemeral"`})],resource_group:"terragucci-ephemeral-sweep",...tn("apply"),script:ze(He("SWEEP",$q(t,a,n,w)))}))),vq(Vt,e.runner,t,Oi);let sY=Iq(
|
|
876
|
+
terragucci review post --forge gitlab --dir ${Mc}`)]}))}e.resume&&Vt.set("resume",new rn({stage:"resume",image:Se,variables:{TG_TOKEN:qe.TG_TOKEN},rules:[new on({if:`$CI_PIPELINE_SOURCE == "schedule" && $${ci} == "resume"`})],resource_group:"terragucci-resume",script:[He("RESUME","terragucci resume --forge gitlab")]})),y&&(Vt.set("ephemeral",new rn({stage:"apply",image:Se,variables:{...qe,TG_PR:"$CI_MERGE_REQUEST_IID",GIT_DEPTH:"0"},rules:[new on({if:'$CI_PIPELINE_SOURCE == "merge_request_event" && $CI_MERGE_REQUEST_SOURCE_PROJECT_PATH == $CI_PROJECT_PATH'})],resource_group:"terragucci-ephemeral-$CI_MERGE_REQUEST_IID",...tn("apply"),script:ze(He("EPHEMERAL",Ghe(a,n,w)))})),Vt.set("ephemeral-sweep",new rn({stage:"apply",image:Se,variables:{...qe,GIT_DEPTH:"0"},rules:[new on({if:`$CI_PIPELINE_SOURCE == "schedule" && $${ci} == "ephemeral"`})],resource_group:"terragucci-ephemeral-sweep",...tn("apply"),script:ze(He("SWEEP",$q(t,a,n,w)))}))),vq(Vt,e.runner,t,Oi);let sY=Iq(I1.serialize(Vt)).replace(/^stages:\n((?: {2}- .*\n)+)/,(Ke,Rr)=>{let iY=Rr.trimEnd().split(`
|
|
877
877
|
`).map(aY=>aY.replace(/^ {2}- /,""));return`${or("stages",[...lh.before,...iY,...lh.after])}
|
|
878
878
|
`});return{path:Xr.gitlab,content:Tp(r,e.imageFromConfig)+sY+jq(e.ownJobs,[...Vt.keys()],t),applyJobs:Ge}}let Gs="github.event.pull_request.head.repo.full_name == github.repository",$y="github.event.pull_request.head.repo.full_name != github.repository",Po=q=>q&&t==="forgejo"?{"enable-openid-connect":!0}:{},ky=new Gu({name:"terragucci",on:{push:{branches:["**"]},pull_request:{},issue_comment:{types:["created"]},...b?{pull_request_review:{}}:{},...qn||y?{pull_request_target:{types:["opened","reopened","synchronize","closed"]}}:{},...E?{schedule:[{cron:E}]}:{},workflow_dispatch:{inputs:bye}},env:A,permissions:{contents:"read"},...t==="forgejo"?{concurrency:{group:`terragucci-\${{ github.event_name == 'issue_comment' && format('comment-{0}', github.event.issue.number) || ${vye} || ${qn||y?"github.event_name == 'pull_request_target' && format('lock-{0}', github.event.pull_request.number) || ":""}${rt?"":"github.event_name == 'push' && github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && format('{0}-{1}', github.ref, github.sha) || "}github.ref }}`,"cancel-in-progress":!1}}:{}}),as=(q,Se=!1,ze=!1,He,qe=!1,tn)=>[new se({uses:"actions/checkout@v4",...ze?{with:{"fetch-depth":0}}:{}}),...M?[new se({name:te,run:M})]:[],...qe&&he?[new se({name:`Install Infracost ${sv}`,run:he})]:[],...He?[new se({name:`Install the AWS CLI ${B$.version} unless the job has it`,shell:"bash",run:He})]:[],...Se&&l?[new se({name:"Cache Terragrunt sources and providers",...zc(t)})]:[],...tn?[tn]:[],q],D=t==="forgejo"?"actions/upload-artifact@v3":"actions/upload-artifact@v4",pe=t==="forgejo"?"actions/download-artifact@v3":"actions/download-artifact@v4",ge=(q,Se,ze)=>new se({...ze?{if:ze}:{},uses:"actions/checkout@v4",with:{...Se?{ref:Se}:{},...q?{"fetch-depth":0}:{},"persist-credentials":!1}}),Ye=(q,Se,ze,He=!1)=>[...M?[new se({...ze?{if:ze}:{},name:te,run:M})]:[],...Se&&he?[new se({...ze?{if:ze}:{},name:`Install Infracost ${sv}`,run:he})]:[],...q&&l?[He?new se({...ze?{if:ze}:{},name:"Restore the Terragrunt sources and providers cache",...bq()}):new se({...ze?{if:ze}:{},name:"Cache Terragrunt sources and providers",...zc(t)})]:[]],Gt=new pt({"runs-on":"ubuntu-latest",container:{image:r},if:`github.event_name == 'push' || (github.event_name == 'pull_request' && ${$y})`,env:{TG_BRANCH:"${{ github.event.repository.default_branch }}"},steps:[ge(!!e.policy),...Ye(!1,!1),new se({name:`Format check and validate, every ${Ze}`,run:`${U$("sh")}
|
|
879
879
|
${ie}`}),new se({name:"Keep the check report",if:"always()",uses:D,with:{name:Rp,path:`${Rp}/`,"if-no-files-found":"ignore"}})]}),ht=E&&t==="github"?{actions:"read"}:{},_n=new pt({"runs-on":"ubuntu-latest",container:{image:r},if:`github.event_name == 'pull_request' && ${Gs}`,permissions:{contents:"read",statuses:"write",...$?{"id-token":"write"}:{}},...Po($),env:{...p,TG_SHA:"${{ github.event.pull_request.head.sha }}",TG_PR:"${{ github.event.pull_request.number }}",...O,...N,...ne,...z$(t,e.reports)},steps:[new se({name:"Say on the head that the plan started",shell:"bash",env:{TG_TOKEN:"${{ github.token }}"},run:[yr(t),'tg status terragucci/plan pending "planning"'].join(`
|
|
@@ -885,7 +885,7 @@ ${H$(n,s,t,a,_,{replan:!0,traded:jn})}`}),new se({name:"Keep the plan report",if
|
|
|
885
885
|
|
|
886
886
|
${iJ(i.id)}`,{match:"anywhere",author:e.meName})}catch(N){return{...u,ran:I,replied:!1,fail:!0,reason:`${O}; and the reply failed (${N.message})`}}return{...u,ran:I,replied:!0,reason:O}},d=O=>({...u,ran:!1,replied:!1,fail:!0,reason:O}),p=O=>({...u,ran:!1,replied:!1,reason:O}),f=Cr(i.body,$s(e.env)),g=i.author?.username,m=i.author?.id;if(typeof g!="string"||!qo.test(g)||!Number.isInteger(m))return p("the note has no usable author");let h;try{h=(await t("GET",`/projects/${n}/members/all/${m}`))?.access_level}catch(O){return/ answered 404/.test(O.message)?p(`${g} is not a member of the project, so the note is ignored`):d(`could not read ${g}'s access, so nothing runs (${O.message})`)}if(typeof h!="number"||h<twe)return p(`${g} is below Developer on the project, so the note is ignored`);if(f.kind==="refused")return l(f.reason);if(f.kind==="agent"&&!e.agent)return l("the agent command is off in this project; `agent.comment` in terragucci.yml turns it on");if((f.kind==="lock"||f.kind==="unlock")&&!a)return l(`\`/terragucci ${f.kind}\` does not run here: this project applies after merge, so merge requests take no locks`);let y=s.source_project_id!==void 0&&s.target_project_id!==void 0&&s.source_project_id!==s.target_project_id;if(f.kind==="agent"){if(s.state!=="opened")return l(`!${c} is not open, so the agent does not run`);if(y)return l("a merge request from a fork gets no agent: its branch is not this project's to push to");if(s.source_branch===r)return l(`!${c}'s source branch is the default branch, and an agent never pushes there`);let O=s.sha;if(typeof O!="string"||!St.test(O))return d(`!${c}'s head is not a commit`);let I;try{I=await GO(e.env,e.fetch,{[gl]:String(c),[E$]:String(i.id),[T$]:O})}catch(L){return d(`could not start the agent's pipeline on ${r} for !${c} (${L.message})`)}let N=typeof I?.web_url=="string"&&/^https?:\/\//.test(I.web_url)?` ${I.web_url}`:"";return l(`started pipeline${N} on ${r} to run the agent on !${c}'s head ${Ts(O)} for ${g}; it reads !${c} and this note again before the agent runs, and pushes what the agent changes to \`${String(s.source_branch)}\``,!0)}if(a&&f.kind!=="plan"){if(s.state!=="opened")return f.kind==="apply"?l(s.state==="merged"?`!${c} is merged, and this project applies a merge request from its head before it merges, so there is nothing of it left to apply; the push after the merge ran \`confirm\``:`!${c} was closed without merging, so there is nothing of it to apply`):l(`!${c} is not open, so it holds no lock: the next merge request that reaches its roots takes them over`);if(y)return l("a merge request from a fork is never applied: its code would run with this project's apply credentials");if(s.target_branch!==r)return l(`!${c} targets ${String(s.target_branch)}, not the default branch ${r}, so it is not applied`);let O=s.sha;if(typeof O!="string"||!St.test(O))return d(`!${c}'s head is not a commit`);if(f.kind==="apply"){let M=await rP({api:t,id:n,mr:s,base:r,...await rl(process.cwd()),...e.env?{env:e.env}:{},...e.fetch?{fetch:e.fetch}:{},...e.requires?{requires:e.requires}:{},...e.wait?{wait:e.wait}:{}});if(M&&"fail"in M)return d(M.fail);if(M)return l(M.refuse)}if(!e.env.TG_MERGE_TOKEN)return d(`the comments job has no TG_MERGE_TOKEN, the token apply.merge_token_env names, so it cannot start a pipeline on ${r} for !${c}`);let I;try{I=await $o(e.env,e.env.TG_MERGE_TOKEN,e.fetch)("POST",`/projects/${n}/pipeline`,{ref:r,variables:[{key:wl,value:String(c),variable_type:"env_var"},{key:M$,value:String(i.id),variable_type:"env_var"},{key:L$,value:O,variable_type:"env_var"}]})}catch(M){return d(`could not start a pipeline on ${r} for !${c} (${M.message})`)}let N=typeof I?.web_url=="string"&&/^https?:\/\//.test(I.web_url)?` ${I.web_url}`:"",L=f.kind==="apply"?f.wave:void 0,U=L!==void 0?` through wave ${L}`:"",F=f.kind==="apply"?`apply !${c}'s head ${Ts(O)}${U}`:`${f.kind} the roots of !${c}`;return l(`started pipeline${N} on ${r} to ${F} for ${g}; its job reads !${c} again before it ${f.kind==="apply"?"applies":f.kind+"s"}`,!0)}if(f.kind==="plan"){if(f.root!==void 0&&!tA(f.root,o))return l(`${f.root} is not a root of this repository. The roots are ${o.flat().sort().map(L=>`\`${L}\``).join(", ")}.`);if(s.state!=="opened")return l(`!${c} is not open, so it is not planned`);if(y)return l("a merge request from a fork is not re-planned: its code never runs with this project's credentials");let O;try{O=await t("POST",`/projects/${n}/merge_requests/${c}/pipelines`)}catch(L){return d(`could not start a pipeline for !${c} (${L.message})`)}let I=typeof O?.web_url=="string"&&/^https?:\/\//.test(O.web_url)?` ${O.web_url}`:"",N=f.root!==void 0?`; GitLab re-plans every root the merge request reaches, \`${f.root}\` among them`:"";return l(`started pipeline${I} to re-plan !${c} for ${g}${N}`,!0)}if(f.kind!=="apply")return l(`\`/terragucci ${f.kind}\` does not run here`);if(f.wave!==void 0)return l(`GitLab runs the waves after a retried apply job by itself, so \`wave-${f.wave}\` cannot stop them; write \`/terragucci apply\`, and each wave still waits behind its own gate`);if(s.state==="opened")return l(`!${c} is not merged: \`/terragucci apply\` retries the apply of a merged merge request, from the default branch, and never applies a merge request's branch`);if(s.state!=="merged")return l(`!${c} was closed without merging, so there is nothing of it to apply`);if(y)return l("a merge request from a fork is not applied on a note: its apply runs when it reaches the default branch");if(s.target_branch!==r)return l(`!${c} was merged into ${String(s.target_branch)}, not the default branch ${r}, so nothing applies from it`);let w=[s.merge_commit_sha,s.squash_commit_sha,s.sha].find(O=>typeof O=="string"&&St.test(O));if(!w)return d(`!${c} has no merge commit`);let b,k;try{let O=await t("GET",`/projects/${n}/pipelines?sha=${w}&ref=${encodeURIComponent(r)}&source=push&order_by=id&sort=desc&per_page=1`);if(b=Array.isArray(O)?O[0]:void 0,!b)return l(`no pipeline ran on ${r} at the merge commit ${Ts(w)} of !${c}, so there is no apply job to retry; push to ${r} again`);k=await t("GET",`/projects/${n}/pipelines?ref=${encodeURIComponent(r)}&source=push&order_by=id&sort=desc&per_page=${nwe}`)}catch(O){return d(`could not read the pipelines of ${r} (${O.message})`)}let v=[...new Set((Array.isArray(k)?k:[]).filter(O=>Number(O?.id)>Number(b.id)&&O?.sha!==w).map(O=>O.sha))];for(let O of v){if(typeof O!="string"||!St.test(O))continue;let I;try{I=await t("GET",`/projects/${n}/repository/commits/${O}/statuses?name=${encodeURIComponent(sJ)}&ref=${encodeURIComponent(r)}`)}catch(L){return d(`could not read the statuses of ${Ts(O)} (${L.message})`)}let N=Array.isArray(I)?I.find(L=>L?.name===sJ):void 0;if(N){let L=typeof N.target_url=="string"&&/^https?:\/\//.test(N.target_url)?` (${N.target_url})`:"";return l(`a later apply already ran on ${r} at ${Ts(O)}${L}, so the merge commit ${Ts(w)} of !${c} is not applied: comment on the merge request that commit came from, or push again`)}}let $;try{$=await t("GET",`/projects/${n}/pipelines/${b.id}/jobs?per_page=100`)}catch(O){return d(`could not read the jobs of pipeline ${b.id} (${O.message})`)}let _=(Array.isArray($)?$:[]).map(O=>{let I=/^apply-wave-([1-9][0-9]*)(?:-share-([1-9][0-9]*))?$/.exec(String(O?.name));return{j:O,n:Number(I?.[1]),s:I?.[2]?Number(I[2]):0}}).filter(O=>Number.isInteger(O.n)).sort((O,I)=>O.n-I.n||O.s-I.s);if(_.length===0)return l(`pipeline ${b.id} at ${Ts(w)} has no apply jobs, so there is nothing to retry`);let E=_.find(O=>O.j.status!=="success");if(!E)return l(`every wave of !${c}'s merge commit ${Ts(w)} already applied`);let R=String(E.j.name);if(["created","pending","running","waiting_for_resource","preparing","scheduled"].includes(E.j.status))return l(`${R} at ${Ts(w)} is ${E.j.status} already, so it is not retried`);if(E.j.status!=="failed"&&E.j.status!=="canceled")return l(`${R} at ${Ts(w)} was ${String(E.j.status)}, and GitLab retries only a failed or canceled job; push to ${r} again`);let x;try{x=await t("POST",`/projects/${n}/jobs/${E.j.id}/retry`)}catch(O){return d(`could not retry ${R} of pipeline ${b.id} (${O.message})`)}let A=typeof x?.web_url=="string"&&/^https?:\/\//.test(x.web_url)?` (${x.web_url})`:"";return l(`retried ${R} of !${c}'s merge commit ${Ts(w)}${A} for ${g}; its gate decides again, and the waves after it follow, each behind its own gate`,!0)}import{readFileSync as iwe}from"node:fs";var cJ=["status","note","reply","stale"],uJ="<!-- terragucci:stale -->",awe=new Set(["pending","success","failure"]);async function dJ(e,t,n){let r=n.env??process.env;if(!cJ.includes(e))throw new S(`post is ${cJ.join(", ")}`);if(!r.TG_TOKEN)return`terragucci: no forge token, so no ${e}`;try{let o=jt(n.forge,r,n.fetch);e==="status"?await cwe(o,t,n,r):e==="note"?await o.upsertComment(lJ(r),yi,iwe(OP(t[0],"note <file>"),"utf-8")):e==="reply"?await o.addComment(lJ(r),`terragucci: ${OP(t[0],"reply <text>")}`):await dwe(o,OP(t[0],"stale <roots> <branch>").split(","),t[1]??"",n,r);return}catch(o){return`terragucci: ${e} failed: ${o.message}`}}function OP(e,t){if(e===void 0)throw new S(`post ${t}`);return e}function lJ(e){let t=Number((e.TG_PR??"").trim());if(!Number.isInteger(t)||t<1)throw new S("TG_PR names no pull request");return t}async function cwe(e,t,n,r){let[o,s,i=""]=t;if(!o||!s||!awe.has(s))throw new S("post status <context> pending|success|failure <description>");let a=(r.TG_SHA??"").trim();if(!a)throw new S("TG_SHA names no commit");let c=n.forge==="gitlab"&&a===r.CI_COMMIT_SHA&&/^[0-9]+$/.test(r.CI_PIPELINE_ID??"")?Number(r.CI_PIPELINE_ID):void 0;await e.setStatus(a,{context:o,state:s==="pending"?"running":s,description:i,...e.runUrl?{url:e.runUrl}:{},...c!==void 0?{pipelineId:c}:{}})}async function uwe(e,t,n){let r=t.fetch??globalThis.fetch,o=encodeURIComponent(e);if(t.forge==="gitlab"){let a=await $o(n,n.TG_TOKEN,r)("GET",`/projects/${encodeURIComponent(n.CI_PROJECT_ID??"")}/merge_requests?state=opened&target_branch=${o}&per_page=100`);return(Array.isArray(a)?a:[]).map(c=>c?.iid).filter(c=>Number.isInteger(c))}let s=pb(n,{kind:t.forge,tokenEnv:"TG_TOKEN"}),i=await kt(r,{forge:t.forge,origin:`https://${s.host}`,path:s.repo,token:s.token,api:s.apiBase},"GET",`/repos/${s.repo}/pulls?state=open&base=${o}&per_page=100`);return(Array.isArray(i)?i:[]).map(a=>a?.number).filter(a=>Number.isInteger(a))}function lwe(e,t,n){if(e.includes(uJ))return;let r=(/roots=(\S*) -->/.exec(e)?.[1]??"").split(",").filter(s=>t.includes(s));if(!r.length)return;let o=e.split(`
|
|
887
887
|
`);return o.splice(1,0,`> This plan is stale: ${n} moved under ${r.join(", ")}. Push to this pull request to plan again. ${uJ}`),o.join(`
|
|
888
|
-
`)}async function dwe(e,t,n,r,o){for(let s of await uwe(n,r,o)){let i=await e.findComment(s,yi),a=i?lwe(i.body,t,n):void 0;a&&await e.upsertComment(s,yi,a)}}import{spawnSync as fk}from"node:child_process";import{delimiter as gve,join as mve}from"node:path";import{spawn as mwe,spawnSync as hwe}from"node:child_process";import{delimiter as ywe,join as wwe}from"node:path";var Ri=e=>new Date(e).getTime();function pwe(e,t){let n=new Map;for(let r of e){if(t!==void 0&&r.gate!==t)continue;let o=`${r.gate}\0${r.environment??""}`,s=n.get(o);(!s||Ri(r.timestamp)>=Ri(s.timestamp))&&n.set(o,r)}return[...n.values()]}var fwe=(e,t)=>{if(t.approval)return;let n=e.filter(r=>t.environment===void 0||r.environment===t.environment);return qw(n,t.gate,t.timestamp,t.planDigest).resolution};function pJ(e,t,n,r){if(n===void 0)return;let o={gate:t,planDigest:n,timestamp:r.timestamp,...r.review?{review:{pr:r.review.pr,head:r.review.head}}:{}};if(wg(e,o))return e.find(s=>wg([s],o))}function mJ(e){let t=e.applied??[],n=e.finished??[],r=e.answering??fwe,o=[],s=pwe(e.pending,e.gate).filter(i=>!hg(i,e.now));for(let i of s){let a={source:"pending",op:i.op||e.op,gate:i.gate,...i.environment!==void 0?{environment:i.environment}:{},...i.planDigest!==void 0?{planDigest:i.planDigest}:{},...Pg(i.resume)?{run:i.resume}:{},pending:i};if(i.answer){let l=iH(i,e.questions?.get(i.answer.id));if(!l.resumable){o.push({...a,resumable:!1,reason:"not-approved",message:l.message});continue}let p=e.resolutions.filter(f=>f.gate===i.gate&&f.answer?.id===i.answer.id&&Ie(f.planDigest,i.planDigest)).map(f=>({r:f,by:pJ(t,i.gate,i.planDigest,f)})).find(f=>f.by);if(p?.by){o.push({...a,resumable:!1,reason:"spent",approvedBy:l.answeredBy,approvedAt:p.r.timestamp,message:gJ(p.by),spent:fJ(p.by)});continue}o.push({...a,resumable:!0,reason:"answered",approvedBy:l.answeredBy,...l.answeredOn?{approvedAt:l.answeredOn}:{},message:`question ${i.answer.id} has an answer the gate passes on`});continue}let c=r(e.resolutions,i,e.pending);if(!c){o.push({...a,resumable:!1,reason:"not-approved",message:i.approval&&!e.answering?"its gate has an approval block, which needs chant's tally (pass answering)":"no approval answers it yet"});continue}let u=pJ(t,i.gate,i.planDigest,c);if(u){o.push({...a,resumable:!1,reason:"spent",approvedBy:c.resolvedBy,approvedAt:c.timestamp,message:gJ(u),spent:fJ(u)});continue}o.push({...a,resumable:!0,reason:"approved",approvedBy:c.resolvedBy,approvedAt:c.timestamp,message:`approved by ${c.resolvedBy} at ${c.timestamp}`})}for(let i of gwe(t,n,e.pending,e.gate))o.push({source:"stopped",op:i.op||e.op,gate:i.gate,planDigest:i.planDigest,resumable:!0,reason:"stopped",approvedBy:i.approvedBy,approvedAt:i.approvedAt,run:i.resume,stopped:{appliedAt:i.timestamp,approvedAt:i.approvedAt,kind:i.kind,...i.wave?{wave:i.wave}:{}},message:`the apply of ${i.planDigest} under the approval by ${i.approvedBy||"?"} began at ${i.timestamp} and never recorded how it ended; it resumes once run ${i.resume.run} is gone`});return o}var fJ=e=>({appliedAt:e.timestamp,approvedAt:e.approvedAt,...e.wave?{wave:e.wave}:{}}),gJ=e=>`the approval that answers it is spent: ${e.wave?`wave ${e.wave.number} (${e.wave.name})`:"a wave"} applied under it at ${e.timestamp}`;function gwe(e,t,n,r){let o=new Map;for(let i of e){if(r!==void 0&&i.gate!==r)continue;let a=o.get(i.gate);(!a||Ri(i.timestamp)>=Ri(a.timestamp))&&o.set(i.gate,i)}let s=[];for(let[i,a]of o){let c=e.filter(l=>l.gate===i&&Ie(l.planDigest,a.planDigest)&&Ri(l.approvedAt)===Ri(a.approvedAt)&&Pg(l.resume)),u=c.reduce((l,d)=>!l||Ri(d.timestamp)>=Ri(l.timestamp)?d:l,void 0);u&&(c.some(l=>DM(t,l))||n.some(l=>l.gate===i&&Ri(l.timestamp)>Ri(u.timestamp))||s.push(u))}return s}function bwe(e,t,n,r){let o=(i,a)=>mJ({op:i,pending:a.pending,resolutions:a.resolutions,applied:a.applied??[],finished:a.finished??[],now:t,...r?{questions:r}:{}}).filter(c=>c.resumable),s=o(Yt,e).filter(i=>/^wave-\d+$/.test(i.gate)).map(i=>({wave:Number(i.gate.slice(5)),...hJ(i)}));return n&&s.push(...o(mr,n).filter(i=>i.source==="pending").map(i=>({wave:1,migration:i.gate,...hJ(i)}))),s.sort((i,a)=>i.wave-a.wave||+!!a.migration-+!!i.migration)}function hJ(e){let t=e.pending,n=t?yl(t):e.run?.run;return{digest:e.planDigest??"",by:e.approvedBy??"",...n?{runId:n}:{},...t?.commit?{commit:t.commit}:{},...e.source==="stopped"&&e.stopped?{stopped:{holder:e.run?.run??"",at:e.stopped.appliedAt}}:{}}}async function yJ(e){if(e.forge==="gitlab")return{kind:"none",why:"on GitLab chant run resume starts each job that waits, or whose apply stopped"};let t=e.liveness??ib(e.env),n=[];for(let o of bwe(e.ledger,e.now??new Date().toISOString(),e.migrations,e.questions))(!o.stopped||o.stopped.holder&&await t({run:o.stopped.holder,at:o.stopped.at})==="dead")&&n.push(o);if(n.length===0)return{kind:"none",why:"no wave waits with an approval that stands, and no approved apply stopped before it finished"};let r;try{r=(await xa(to(e.env,e.fetch),{...e.env,TG_PR:""},e.sha))?.number}catch{}return{kind:"apply",sha:e.sha,...r!==void 0?{pr:r}:{},waves:n}}function mh(e,t){let n=/^(?:([a-z+]+):\/\/)?(?:[^@/]+@)?([^/:]+)(:\d+)?[/:](.+?)(?:\.git)?\/?$/.exec(e.trim());if(!n)return;let r=n[1],o=n[2],s=r==="http"||r==="https"?`${r}://${o}${n[3]??""}`:`https://${o}`,i=o==="github.com"?"github":o==="gitlab.com"?"gitlab":t==="github"||t==="gitlab"||t==="forgejo"?t:void 0;return i?{forge:i,host:o,web:s,path:n[4]}:void 0}function vwe(e,t){if(e==="gitlab")return t.GITLAB_TOKEN||void 0;if(e==="forgejo")return t.FORGEJO_TOKEN||void 0;if(t.GH_TOKEN||t.GITHUB_TOKEN)return t.GH_TOKEN||t.GITHUB_TOKEN;let n=hwe("gh",["auth","token"],{encoding:"utf-8"});return n.status===0&&n.stdout.trim()?n.stdout.trim():void 0}var $we={github:"GH_TOKEN (or gh auth login)",forgejo:"FORGEJO_TOKEN",gitlab:"GITLAB_TOKEN"};async function PP(e){let t=e.env??process.env,n=e.fetch??fetch,{forge:r,host:o,web:s,path:i}=e.origin,a="the pipeline's resume job applies it on its next run when apply.resume is set; otherwise run its job again, or comment /terragucci apply",c=vwe(r,t);if(!c)return`not resumed from here: no token in ${$we[r]}; ${a}`;let u=async(l,d,p,f)=>{let g=await n(d,{method:l,headers:{"content-type":"application/json",...p},...f===void 0?{}:{body:JSON.stringify(f)}});if(!g.ok)throw new Error(`${l} ${d.replace(/^https?:\/\/[^/]+/,"")} answered ${g.status}`);return g.status===204?null:g.json().catch(()=>null)};try{if(r==="github"||r==="gitlab"){if(!e.wave.resume)return`not resumed from here: the pending fact names no CI job; ${a}`;let f=await
|
|
888
|
+
`)}async function dwe(e,t,n,r,o){for(let s of await uwe(n,r,o)){let i=await e.findComment(s,yi),a=i?lwe(i.body,t,n):void 0;a&&await e.upsertComment(s,yi,a)}}import{spawnSync as fk}from"node:child_process";import{delimiter as gve,join as mve}from"node:path";import{spawn as mwe,spawnSync as hwe}from"node:child_process";import{delimiter as ywe,join as wwe}from"node:path";var Ri=e=>new Date(e).getTime();function pwe(e,t){let n=new Map;for(let r of e){if(t!==void 0&&r.gate!==t)continue;let o=`${r.gate}\0${r.environment??""}`,s=n.get(o);(!s||Ri(r.timestamp)>=Ri(s.timestamp))&&n.set(o,r)}return[...n.values()]}var fwe=(e,t)=>{if(t.approval)return;let n=e.filter(r=>t.environment===void 0||r.environment===t.environment);return qw(n,t.gate,t.timestamp,t.planDigest).resolution};function pJ(e,t,n,r){if(n===void 0)return;let o={gate:t,planDigest:n,timestamp:r.timestamp,...r.review?{review:{pr:r.review.pr,head:r.review.head}}:{}};if(wg(e,o))return e.find(s=>wg([s],o))}function mJ(e){let t=e.applied??[],n=e.finished??[],r=e.answering??fwe,o=[],s=pwe(e.pending,e.gate).filter(i=>!hg(i,e.now));for(let i of s){let a={source:"pending",op:i.op||e.op,gate:i.gate,...i.environment!==void 0?{environment:i.environment}:{},...i.planDigest!==void 0?{planDigest:i.planDigest}:{},...Pg(i.resume)?{run:i.resume}:{},pending:i};if(i.answer){let l=iH(i,e.questions?.get(i.answer.id));if(!l.resumable){o.push({...a,resumable:!1,reason:"not-approved",message:l.message});continue}let p=e.resolutions.filter(f=>f.gate===i.gate&&f.answer?.id===i.answer.id&&Ie(f.planDigest,i.planDigest)).map(f=>({r:f,by:pJ(t,i.gate,i.planDigest,f)})).find(f=>f.by);if(p?.by){o.push({...a,resumable:!1,reason:"spent",approvedBy:l.answeredBy,approvedAt:p.r.timestamp,message:gJ(p.by),spent:fJ(p.by)});continue}o.push({...a,resumable:!0,reason:"answered",approvedBy:l.answeredBy,...l.answeredOn?{approvedAt:l.answeredOn}:{},message:`question ${i.answer.id} has an answer the gate passes on`});continue}let c=r(e.resolutions,i,e.pending);if(!c){o.push({...a,resumable:!1,reason:"not-approved",message:i.approval&&!e.answering?"its gate has an approval block, which needs chant's tally (pass answering)":"no approval answers it yet"});continue}let u=pJ(t,i.gate,i.planDigest,c);if(u){o.push({...a,resumable:!1,reason:"spent",approvedBy:c.resolvedBy,approvedAt:c.timestamp,message:gJ(u),spent:fJ(u)});continue}o.push({...a,resumable:!0,reason:"approved",approvedBy:c.resolvedBy,approvedAt:c.timestamp,message:`approved by ${c.resolvedBy} at ${c.timestamp}`})}for(let i of gwe(t,n,e.pending,e.gate))o.push({source:"stopped",op:i.op||e.op,gate:i.gate,planDigest:i.planDigest,resumable:!0,reason:"stopped",approvedBy:i.approvedBy,approvedAt:i.approvedAt,run:i.resume,stopped:{appliedAt:i.timestamp,approvedAt:i.approvedAt,kind:i.kind,...i.wave?{wave:i.wave}:{}},message:`the apply of ${i.planDigest} under the approval by ${i.approvedBy||"?"} began at ${i.timestamp} and never recorded how it ended; it resumes once run ${i.resume.run} is gone`});return o}var fJ=e=>({appliedAt:e.timestamp,approvedAt:e.approvedAt,...e.wave?{wave:e.wave}:{}}),gJ=e=>`the approval that answers it is spent: ${e.wave?`wave ${e.wave.number} (${e.wave.name})`:"a wave"} applied under it at ${e.timestamp}`;function gwe(e,t,n,r){let o=new Map;for(let i of e){if(r!==void 0&&i.gate!==r)continue;let a=o.get(i.gate);(!a||Ri(i.timestamp)>=Ri(a.timestamp))&&o.set(i.gate,i)}let s=[];for(let[i,a]of o){let c=e.filter(l=>l.gate===i&&Ie(l.planDigest,a.planDigest)&&Ri(l.approvedAt)===Ri(a.approvedAt)&&Pg(l.resume)),u=c.reduce((l,d)=>!l||Ri(d.timestamp)>=Ri(l.timestamp)?d:l,void 0);u&&(c.some(l=>DM(t,l))||n.some(l=>l.gate===i&&Ri(l.timestamp)>Ri(u.timestamp))||s.push(u))}return s}function bwe(e,t,n,r){let o=(i,a)=>mJ({op:i,pending:a.pending,resolutions:a.resolutions,applied:a.applied??[],finished:a.finished??[],now:t,...r?{questions:r}:{}}).filter(c=>c.resumable),s=o(Yt,e).filter(i=>/^wave-\d+$/.test(i.gate)).map(i=>({wave:Number(i.gate.slice(5)),...hJ(i)}));return n&&s.push(...o(mr,n).filter(i=>i.source==="pending").map(i=>({wave:1,migration:i.gate,...hJ(i)}))),s.sort((i,a)=>i.wave-a.wave||+!!a.migration-+!!i.migration)}function hJ(e){let t=e.pending,n=t?yl(t):e.run?.run;return{digest:e.planDigest??"",by:e.approvedBy??"",...n?{runId:n}:{},...t?.commit?{commit:t.commit}:{},...e.source==="stopped"&&e.stopped?{stopped:{holder:e.run?.run??"",at:e.stopped.appliedAt}}:{}}}async function yJ(e){if(e.forge==="gitlab")return{kind:"none",why:"on GitLab chant run resume starts each job that waits, or whose apply stopped"};let t=e.liveness??ib(e.env),n=[];for(let o of bwe(e.ledger,e.now??new Date().toISOString(),e.migrations,e.questions))(!o.stopped||o.stopped.holder&&await t({run:o.stopped.holder,at:o.stopped.at})==="dead")&&n.push(o);if(n.length===0)return{kind:"none",why:"no wave waits with an approval that stands, and no approved apply stopped before it finished"};let r;try{r=(await xa(to(e.env,e.fetch),{...e.env,TG_PR:""},e.sha))?.number}catch{}return{kind:"apply",sha:e.sha,...r!==void 0?{pr:r}:{},waves:n}}function mh(e,t){let n=/^(?:([a-z+]+):\/\/)?(?:[^@/]+@)?([^/:]+)(:\d+)?[/:](.+?)(?:\.git)?\/?$/.exec(e.trim());if(!n)return;let r=n[1],o=n[2],s=r==="http"||r==="https"?`${r}://${o}${n[3]??""}`:`https://${o}`,i=o==="github.com"?"github":o==="gitlab.com"?"gitlab":t==="github"||t==="gitlab"||t==="forgejo"?t:void 0;return i?{forge:i,host:o,web:s,path:n[4]}:void 0}function vwe(e,t){if(e==="gitlab")return t.GITLAB_TOKEN||void 0;if(e==="forgejo")return t.FORGEJO_TOKEN||void 0;if(t.GH_TOKEN||t.GITHUB_TOKEN)return t.GH_TOKEN||t.GITHUB_TOKEN;let n=hwe("gh",["auth","token"],{encoding:"utf-8"});return n.status===0&&n.stdout.trim()?n.stdout.trim():void 0}var $we={github:"GH_TOKEN (or gh auth login)",forgejo:"FORGEJO_TOKEN",gitlab:"GITLAB_TOKEN"};async function PP(e){let t=e.env??process.env,n=e.fetch??fetch,{forge:r,host:o,web:s,path:i}=e.origin,a="the pipeline's resume job applies it on its next run when apply.resume is set; otherwise run its job again, or comment /terragucci apply",c=vwe(r,t);if(!c)return`not resumed from here: no token in ${$we[r]}; ${a}`;let u=async(l,d,p,f)=>{let g=await n(d,{method:l,headers:{"content-type":"application/json",...p},...f===void 0?{}:{body:JSON.stringify(f)}});if(!g.ok)throw new Error(`${l} ${d.replace(/^https?:\/\/[^/]+/,"")} answered ${g.status}`);return g.status===204?null:g.json().catch(()=>null)};try{if(r==="github"||r==="gitlab"){if(!e.wave.resume)return`not resumed from here: the pending fact names no CI job; ${a}`;let f=await eL(e.wave.resume,{token:c,...e.fetch?{fetch:e.fetch}:{}});return f.status!=="resumed"?`not resumed from here: ${"reason"in f?f.reason:f.how}; ${a}`:`resumed: ${f.how}, so wave ${e.wave.wave} runs again and its gate decides`}if(!e.wave.commit)return`not resumed from here: the pending fact names no commit; ${a}`;let l=`${s}/api/v1/repos/${i}`,d={authorization:`token ${c}`},p=await u("GET",`${l}/commits/${e.wave.commit}/pull`,d).catch(()=>null);return Number.isInteger(p?.number)?(await Ag({apiBase:`${s}/api/v1`,repo:i,token:c,host:o,fetch:n}).addComment(p.number,"/terragucci apply"),`resumed: commented /terragucci apply on pull request ${p.number}, so its waves run again and each gate decides`):`not resumed from here: no merged pull request made ${e.wave.commit.slice(0,8)}; ${a}`}catch(l){return`not resumed from here (${l.message}); ${a}`}}var kwe=["tf-apply",mr];async function wJ(e,t,n={}){let r=n.log??(i=>console.log(i)),o=t.CHANT_FORGE_TOKEN||t.TG_TOKEN||t.GITLAB_TOKEN,s=0;for(let i of kwe){r(`terragucci resume: chant run resume --op ${i}`);let a=await _we(e,["run","resume","--op",i],{...t,...o?{CHANT_FORGE_TOKEN:o}:{}},n.chant,"inherit");a.error?(r(`terragucci resume: chant run resume did not start (${a.error.message}); install terragucci again, which installs chant with it`),s=1):a.status!==0&&(s=a.status??1)}return s}function _we(e,t,n,r,o){return new Promise(s=>{let i=mwe(vi(e,r?{chant:r}:{}),t,{cwd:e,stdio:["ignore",o,o],env:{...n,PATH:[wwe(e,"node_modules",".bin"),n.PATH??""].join(ywe)}}),a="",c="";i.stdout?.on("data",u=>a+=u),i.stderr?.on("data",u=>c+=u),i.on("error",u=>s({status:null,stdout:a,stderr:c,error:u})),i.on("close",u=>s({status:u,stdout:a,stderr:c}))})}import{spawnSync as kJ}from"node:child_process";import{createHash as Swe}from"node:crypto";import{existsSync as CP,mkdtempSync as _J,realpathSync as bJ,rmSync as Rwe,writeFileSync as xwe}from"node:fs";import{tmpdir as SJ}from"node:os";import{basename as Ewe,dirname as jP,isAbsolute as Twe,join as El,relative as Awe,resolve as Owe}from"node:path";var Hc="tf-state-export",ek=`_gates/${Hc}.jsonl`,IP=`_gates/${Hc}/done.jsonl`,Pwe=48,vJ=(e,t,n=!1)=>`terragucci approve export ${e} --plan ${t}${n?" --sign":""}`,RJ=e=>`sha256:${Swe("sha256").update(e).digest("hex")}`,Pa=e=>Date.parse(e)||0;function jwe(e){return RJ(JSON.stringify({op:Hc,root:e.root,location:e.location,version_id:e.version_id,by:e.by,at:e.at}))}function $J(e,t,n){let r=kJ("git",t,{cwd:e,encoding:"utf-8",env:n});return r.status===0?r.stdout.trim():""}function Iwe(e){let t=new Map;for(let n of e.split(`
|
|
889
889
|
`).map(r=>r.trim()).filter(Boolean))try{let r=JSON.parse(n);r.version===1&&r.kind==="state-export"&&typeof r.planDigest=="string"&&t.set(r.planDigest,r)}catch{}return t}function Cwe(e,t,n,r){let o=`${n.replace(/[^\w.-]+/g,"_")}.${r.replace(/[^\w.-]+/g,"_")}.tfstate`;if(!t)return El(_J(El(SJ(),"terragucci-export-")),o);let s=Owe(t);if(!CP(jP(s)))throw new S(`--out ${t}: ${jP(s)} is not a directory`);if(CP(s))throw new S(`--out ${t} is there already; name a new file`);let i=Awe(bJ(e),El(bJ(jP(s)),Ewe(s)));if(!i.startsWith("..")&&!Twe(i))throw new S(`--out ${t} is inside the repo, where a state could be committed; write it outside the repo`);return s}async function xJ(e,t){let n=t.log??(F=>console.log(F)),r=t.env??process.env,o=t.exec??li,s=t.now??new Date().toISOString(),i=t.root.replace(/\/+$/,"");if(!i)throw new S("state export takes the root whose state to export: terragucci state export <root> [--version <id>]");let a=t.config??fe(e),c=a?ue(await Re(a)):void 0,u=et(e,c??ue({}));await u.prepareRoots({roots:[i],...t.terragrunt?.path?{terragrunt:t.terragrunt.path}:{},...t.terragrunt?.exec?{exec:t.terragrunt.exec}:{}});let l=OA(e,i);if(l)throw new S(`state export: ${l}`);let d=CP(El(e,i,"terragrunt.hcl")),p=t.actor||$J(e,["config","user.name"],r)||$J(e,["config","user.email"],r);if(!p)throw new S("state export names who asks: set git's user.name, or pass --actor <name>");let f=t.binary??c?.binary??u.binary([i]).value,g=_J(El(SJ(),"terragucci-export-data-")),m;try{if(d){let F=await Dm(e,i,f,r,g,t.terragrunt);m=Hr(F.dir,F.env)}else{let F={...r,TF_DATA_DIR:g},M=await o(f,["init","-input=false","-no-color"],El(e,i),u.rootInit(i,F)?.init??F);if(M.code!==0)throw new S(`init in ${i} failed: ${M.out.trim().split(`
|
|
890
890
|
`).slice(-4).join(" ").slice(0,400)}`);m=Hr(El(e,i),u.rootEnv(i,F))}}finally{Rwe(g,{recursive:!0,force:!0})}if("unsupported"in m)throw new S(`state export: ${i}: ${m.unsupported.replace(Wf,"exports state from s3, gcs and azurerm backends and GitLab-managed state")}`);let h,y=t.version,w;if("gitlab"in m){let F=m.gitlab,M=t.fetch??globalThis.fetch;h=m.location;let te=async ie=>{try{return await ie()}catch(Q){throw new S(`state export: ${Q.message}`)}};if(y)if(/^\d+$/.test(y)){if(!await te(()=>eN(F,y,M)))throw new S(`state export: ${h} has no version ${y}; GitLab keeps no version with that serial`)}else throw new S(`state export: ${i}'s state is GitLab-managed, whose versions are serials; --version ${y} is not one`);else{let ie=await te(()=>Yy(F,M));if(!ie)throw new S(`state export: ${h} holds no state`);y=String(ie.serial)}w=ie=>te(()=>tN(F,ie,M))}else{if(!ar(m))throw new S(`state export: ${i}'s state is a local file, ${m.path}, which keeps no versions; export reads a version of an s3, gcs or azurerm state by its id`);let F=await Ws(m,t.fetch);if(h=F.location,y){if(!await F.hasVersion(y))throw new S(`state export: ${h} has no version ${y}; a lifecycle rule may have expired it`)}else{let M=await F.version(!0);if(!M.exists)throw new S(`state export: ${h} holds no state`);if(!M.versionId)throw new S(`state export: ${h} keeps no versions, so there is no version id to export: ${M.off??"the store gave none"}`);y=M.versionId}w=M=>F.readVersion(M)}let b=ft(e,ek),k=kJ("git",["show",`refs/remotes/origin/chant/lifecycle:${IP}`],{cwd:e,encoding:"utf-8"}),v=Iwe(k.status===0?k.stdout:""),$=b.pending.filter(F=>F.op===Hc&&F.gate===i&&F.request&&F.request.by===p&&F.request.version_id===y&&F.request.location===h).filter(F=>Pa(F.expiresAt)>Pa(s)&&F.planDigest!==void 0&&!v.has(F.planDigest)).sort((F,M)=>Pa(M.timestamp)-Pa(F.timestamp))[0],_=await Yr(e,{at:"HEAD",...a?{config:a}:{}}),E=_.mode==="sealed";if(!$){let F={root:i,location:h,version_id:y,by:p,at:s},M=jwe(F),te={version:1,kind:"pending",op:Hc,gate:i,timestamp:s,expiresAt:new Date(Pa(s)+Pwe*3600*1e3).toISOString(),planDigest:M,description:`state export: ${i} version ${y}, for ${p}`,request:F,neverOverMcp:!0};ha(e,te,{},ek);let ie=vJ(i,M,E);return n(`state export: ${p} asks for ${i}'s state, ${h} version ${y}; request ${M}`),n("someone other than you approves it with:"),n(` ${ie}`),n("then run this command again to download it."),{code:3,digest:M,command:ie}}let R=$.planDigest,x=vJ(i,R,E),I=b.resolutions.filter(F=>F.gate===i&&Ie(F.planDigest,R)&&Pa(F.timestamp)>=Pa($.timestamp)).filter(F=>{if(F.resolvedBy===p)return n(`state export: an approval by ${p} does not count: the person who asks for an export does not approve it`),!1;let M=E?ws(_.signers,F):null;return M&&n(`state export: an approval does not count: ${M}`),M===null}).sort((F,M)=>Pa(M.timestamp)-Pa(F.timestamp))[0];if(!I)return n(`state export: request ${R} for ${i} version ${y} waits for an approval by someone other than ${p}:`),n(` ${x}`),{code:3,digest:R,command:x};let N=Cwe(e,t.out,i,y),L=await w(y);if(L===void 0)throw new S(`state export: ${h} has no version ${y}; it was deleted since the request`);let U={version:1,kind:"state-export",op:Hc,gate:i,planDigest:R,root:i,location:h,version_id:y,exportedBy:p,approvedBy:I.resolvedBy,approvedAt:I.timestamp,timestamp:new Date().toISOString(),content_digest:RJ(L)};return hr(e,IP,[JSON.stringify(U)],{},`State exported: ${i} version ${y} for ${p}`),xwe(N,L,{mode:384,flag:"wx"}),n(`state export: wrote ${N}: ${i}'s state, ${h} version ${y}, approved by ${I.resolvedBy}`),n(`state export: recorded on chant/lifecycle in ${IP}; delete the file when you are done with it`),{code:0,digest:R,file:N}}import{spawnSync as R2}from"node:child_process";import{existsSync as ove,statSync as sve}from"node:fs";import{resolve as v2}from"node:path";import{execFileSync as Ybe}from"node:child_process";import{mkdirSync as Xbe,mkdtempSync as Qbe,rmSync as eve,writeFileSync as tve}from"node:fs";import{tmpdir as nve}from"node:os";import{dirname as rve,join as lk}from"node:path";import{execFileSync as mbe}from"node:child_process";import{mkdtempSync as hbe,rmSync as ybe,unlinkSync as wbe,writeFileSync as bbe}from"node:fs";import{tmpdir as vbe}from"node:os";import{join as WJ,relative as $be}from"node:path";import{execFileSync as FJ}from"node:child_process";import{existsSync as So,mkdirSync as sbe,readFileSync as Ro,unlinkSync as ibe,writeFileSync as abe}from"node:fs";import{basename as cbe,dirname as ube,join as _o,relative as qc}from"node:path";import{gunzipSync as Nwe}from"node:zlib";var EJ=JSON.parse(Nwe(Buffer.from("H4sIAAAAAAACA+1dC3PjNpL+KyhedkfOybIelsbW3uRqJjPJpi7ZmbInm7qKXSqYhERmKIIBQGt8Xu9vv2qApEg9KEoiJVFCtmoTiyQeja8f6G40ng2PCmL0jRviWYQRCz08IW4yxxf8gsnfzi3M7QeKmcUbgqMho2PEmXkx/fnC8SzytSF4A72nyKMCEcsRf0Ms8NCrPwIukOm8ahh1Y+i4hBv9358NHwvb6BsjhofYw8m2uEvPBWEMjwLTdM6x77tP5zwwTcJ54w9OPaNuyH/1nw3seVRg4VCPw5+uw4XR//3+pW5YRE3CgReN6+u/IPpIGGpfWX10+/NPdUQYoww9BNaICIQ9Cz0EzEMMC8LRkDIkbIKWDQPd/vwRpgOzxA8uMfqCBQSmx03s/i/B7FZgJn6hHkyyWYdp+vZnSl3h+Ea/VTdcx/sChLivGz72iKuIIp58WApGJ0bdMKnrYp8Ty+gPsctJ3RCOgL6Mjw9/EFM4j8SAlh3rE5Wzt2XLE6PfvqwbX2W/T0a/+VI3HEs+inu6f6nHnXGBhaFeaU+7uP35p5hgxiw1b23MCKJDNKLUQuSReIKrt4FoLuYCPmugHxkhHsICUYbwA30k8rlci2gGjbkpdOUUeokZtGA5scCcBswk8Fo4dJ/RMRE2CbhRNwKYgfH5x09/b366+fjLh89///DrrfFSNwRmIyIUgbdohpHhT/DoLaz7V58ZfaOFzhF3aZ+7zkDCqc8AiwMAUfvKeuYufXNnLAPRnVFPAGzgMwo0efPvO+Ob8L/vjBejbrhkRDzrB8rGWKiVMV7u6wb1Y9ib1KXsF2rBZB6w+WXEaOBZRgi78IFHPcALI1Zgko+Jj7FrAnEMWLd/UPGPwHWN+5cXQDNxre+pN3RG8KJFhjhwhfwo8BwYi0+YSTwh/wKQmM4YA8A6dUPYjHCbupZ8f6zGgB84dQMB4+CC+GpNHrEbEKPvBa5bV1MBFiCWASANHzYb19fThyPAlfEiBwmwY45FFKwX47qTh3WSuOsdNu4AczEHKcytxNoCJMVNZOEphM3ekJRziS+nS/whKdgZGWPHc7zRciEGMql9ZaUVQq31F3imZNuZVGlPRCDuE0800Dvi0glqyk9jKiKHo7HDObGyhVqrffjokrQYKFr0YxqWI9CmS7SJWMOM4F2BsVW4WJs+oQx7I5KWee3u5jKvO2WIdwHzzkEnIewSBhacwxZxhGScd3LN4ZP4bWkMOdLoqSPsPSHhELYC41cHjvFgXHv784ebz7fPcpYeHpM3d7MUAECvhrwik8ACWlCkvTNezsDoeSSmoKzWPJtHfbgGh6LJwzanWG9ujfUQsVNAt2b4IBvLwhkTTphDeIjoXk7r9LNNEGwniIVcx5NiOYfZeSXRC7I5tjt72u7MtDsrYSLWDTPggo4l3eMOb8WTSxK9KrwYL+sh8nUOoyMDZaEh0NOGQLYhcLA6uwyQrdr7v4u8FOCnkaoHTRzPkl+t9AW0LkNnwNUSZ8AcxK8X9dyy0QXqjlHNxyNyttiOQFIkTb0CIYRQS9rQFo/N7pBnyFdsCvcpFusNNGuODB1GOJo4wkacPBLmiCcEA0AT23EJeqDCDknBEZj2yuPQ6jQuL+vwjmknuw67dTzUttp2Ln3Q6h4ypy5SBi27CE5FF6jZaLbmGbZlS8YoYC7vVs+lOy51Lt3xJlKHB8Oh87X/NSVy2nVj7HjlmFASzbNmVJk6rtVcJAF6IAE6zWqIgG6jlykAulYjl6KuHv/3yuX/3i75v9MsVwB0mpWQAADmnfJ/a6EFYKEL1LZRTTjmFyL2KgDUELJFQLtxlSkCWpOOlcsGaHcqZwNY5doA1g5lQLtceda2qyABAMs7lQDtRRKgAxKgVx0J0Gxcd+CfTDFwOWnntAWqJwc65cqBzi7lwA7smsOXAzGiCxcG93Ix4EMQ8GMYm2mTMf4nYVwy9yWIBDySzuYpmeE9lxr3dUOQse9iAa6caVZGDIc/A8KejLoBTneFDlgQWAb8QFyjb3yKf9kCTqqXvmp0IGnGa9t4TV8WQUrJvaHjOaHYK7+7eGnadYNTJqSnZxy4wonSUBzPdAOLvHXd6Bfsuv9UqDEa/2kkoP37vQQMCH2gMOT2yGjA5Pw1eNYFVX8Z4Ts+SFqmOAL+/j8IG/SNB0YnnDAj6aH/2F+aPhMvW1aqj1E3JoR8kYk0Rt+AESgOM/rGnWKE3DlEFnOG4tykjBFTEGvjLKLmtllEMwPReUTb5BE1TzaPaAZGoO10+lB2bEhnD62VPTSHMJ0/tGb+UFMnEOWIG24hyXTekM4bqlDe0AKk68yhamQONU83c6hQS7MKRqHOF6qq3tdpQoeWJnSqQcIKJgptI/qlH/0QQ4Nbz0AHBPOnBJ14QLB6PN8pg+f3HgbcegY6+KeDfwsdoDGQCoz5reilkqG+mTktC/bNv1ZYuM93sXcOY9440NfdNtAXD0GH+LYJ8XVPNsQXA0iXCVjLpdPVgb61An0JnOkQ33ohvq6O8OXw9G0ryHSYT4f5KhTmS8FdB/iqEeDrnm6Ar3g7sxImoQ7zVVr561ifLgmgSwIUVRJgPS5Vx2a6B1gOoKB56FIAuhTASZQCKIhf9l8GoKCJ6BIAugTAqZQAKErn7zvHp6B56EwfffT/pI7+F6Uy953vU6ANo7N+Tj7rJxkNLfm4f56uKpn/E09sWeZP8oXNc34SDZo2BMfOGXl0yGTTnJ/fbCwQwaaN/MB1ESN/BoSLVxzBcBGDJ4TXZeoPaA6TEcj9qSNGfBebhMsnFuGC0ScOSVw+5cQK9R/kIu0vCehTYkK8iEQgNYm5TKAbSgVHajUs5BOWIuWcmaBen9iUE0Vk9SVHXDK7443q4NEM+UHSN9leXd1Eo/KIBOFCNQIheWzmdN8dqiYf468w9Vo497qc+CCc+BmqQRhvANAaALRqCenBgKiDcA2eucAjiD2KoeS65dmwv38zGMgQ8/0ZCB7H4wJ7IsKpCj5HsBpGSlPBYA29mWl8JjCVyMT5kdHA56vBFL4HaICZQlzatZDjCZoJopzunuPDCRBpMJJEqxJKVom675UIySPkrl/ivKB8gY/uXDcy4GJKBM7i8VOkACKh5nihdpHqJPDq04+XgTAdqGkecLaEBKGazBmqjfHXDNCFBFkPdY4nCHvErgLejPH+/Ky6fnnZyIi3wbLKmTeTyDT4XlkAcysf/j5rAtgESQaphxqLjWZV1hpCakHm69UhJ9OsklPFIqYe8tWbO0OZaTOia4Xs2n+q41qQTKQb/OpbCyEZ/r4Mko08Wa4aX4vwFUjKHjW+rhIGfrjXmTfl1YOxTO/fCGWRpaVhtghm4SbzqHGWyCt5H26j53D2fsn+OifGrjTGlmPMIi45HlG2qUPzkF2ZicWWGzsWeANOTOpZPOfhxLwtVMLxOJ1MH6kNGVIuwJiwy3yERTke5RnGTR2O7+FjYiHpsUn4KOrIphPkUm8k5ZrsA9mYIy4otaYeSPXdtKjp/vyLciaF+BVnt9ztmU4ici3xJSZ8gYpscAH1EPLk1d/ESjqD8m27K+L6yXT2yMkr1+Dzkj326+vWdbPZ4Us22OGbZe2wlzj/5JojPCJz6/33iEW445kktX1+xRH1iRciYILlgRYuQhzUHMHDRw7nAZHvEussl7/4+lDBIBf8DJ2jWu3Oq60PDUnFSBMshYgFBgEKPBeuQKdesoP1uzRdglm+Lu+8szuPsjuv/Ml9t+N5HYCz/XIugJM4k5+lsoZkIm1v7ClFhZ/QAxlSRvII2bSD/WAZy3Tx2B+MnUW4W2qJ5xC3Lese0eGQE4Fa1hlwbuHtn53VUXOn+NI290nZ3JHZN2drW+GDQmxsIs9PRkZ2aKeq2a9rcSvxJkOJkaktk8AcD7MnZVfH/aJHBVppVD6pgA28/P7jzVs0JoI5Jk++roaJTDr2A5kXMKZW4BLkOx5H2GSUc0gVoCpPQDi+NPlZ4BaUGPCcdNJRBjltFoHEpJAF4D1YKwbsYgvh8/7FhRj5drPheI/YdawLyRI3Hz59vPl8eyE/b9hi7IIEMSX9LGpK7IS9G0bMzfd1A/P3jPoWnXjT/YEUy+9c7H2ZgfA/MePxa18I8T9LoMY+hQ1yHNSKlp/ekIDPkl1IHOmL7dFUfgKb7k0a6EMy7iPXEgmKwEZliWU8qmSGHOkLQCgvTl/4d+zG+pcYKuGSESyUptXOtN6zMcbCtKXslHgxHp7+gWVqU9xgrK9e6vCfPmHCCT+WXygWrkdJfstZOTfzfvM8UNqnwfDkJc3Lc0z5ci//t3o3FqpxvvBYtRKidQndUHTGnoqEmGTEJZiTJQwC2/VgYXWMSmVlmDTwRNqJG5EnJE0dccIeHZMMwh8ymUJ9HL36fBDAXysr4z1xnUdlwpWalvGe+C59CqNQhCEwQObQ+tb3XYdYaIIfyfS1aSDBIxPAInFGtpCPIOtvka6X1atChS+BPk5ufP6Gvo3qC4QmTC4h3jwKfxNleGBNF2PgEzYAUmZuV/bhd8qoJPEzwRYK01TTCPqFWA5seqG8m8xBxGGWz6vI1yQ1fajIAWcIK9CtvzM+IkC4BFvySbZ3BGTjCGAD4SsqsFsiQIxNCzuE8YYhdtyAEXkuZg4mP2DHJVa49MruxynZM6SuSyfEAjJKs6aupBB23eirNQBz6HlAa+NF8dQgpPFAFTDbqQBJVpBYCymJnAnY2YAoYIQLynIJk+EUOE/T/YLyX09b2kSYHA84QiKscrTuQ26ssoZ+kZ6BqSMgj1XU7r3EWRIr96zJg/q/TN0Q8+f0wAgP/RQmdl047OB4c/nSsCfLdzL2dWXypGFOkY8mtsozUafeHfjOIRrgqdVvJYXP1M80v21z/FTIFHZfs6moI/yo3PmRpyrH+cjXh56oDHOZj+3UUfj7cgwAt2aHdLS7XbvbS3K3f5C7yEX+dhI9Kc/hHsJXe9y3PYpXSTe1dj8fqPtZu22121a7bbXbVrtttdtWu22121a7bbXbVrtttdtWu22121a7bbXbVrttT8Zt6zs+gUtNzm2CXWFveirxJvB4eNthWNIskBacJ7fc0iOm6pNE3BcX4VLPuI89vkcH6428yMgL7+riZZ9KBHJJytg0YPMXiEmSMHjHUYdk4LXwlEwosanjCSmq52hbbbdrJLUTjC1nuLDWIapND9okPwAwDcDG4QO5p3yebU2Kc/lf+ep/nqNSm4+O9Nhw9Ab2AOXOJjyKt5spLTbIZ5t/eUGpH3ftCerMXL7Ds2/14zk5FI65iXCHtxG3Hrrzu3bnHSLH1oHQIuADk1pQz+L289vPv94Ovv/4/sPgw83Nx5vSmTrXCHbK97lGVL5oWI8wZ+ER3ib6FmnVcHSqQR7Rvrjzanptj25t0XeouXvdn769LaqoPsZflRLdPKgZ7mdKD2rG+6Zuc84G8bvN6cZqTVNkm83CwUalbIcLOmJ4PPgzwJ5wXFJrNrr1WFW4aQGyoUiJSB55FgYPAVwfUYZ4KaurHKKmrK6XiZ3Sp3q2j63HxgHoKeNfd+cZ/7q7W8Y/+Hj0Qs6/1qyvWf8wWX+lA5Jw2V6eu1evXuIMhZXRwasZpyMEd2RXWV7HODsq9hm/4lEZ6pQwgZJzMu2WeNbyRL706K8r7XpUxDu2LUhUuvPoNiLRxHSsrsBY3dxqrX/n0SatFR3Dqy8jq4RZgqi34d97I6kcUEEEjdvaUUi0fWkXFRP9FMYqkYpVLgqOzoQzi4qSMsjdCwQ/l6l8G98VJfMAJ9gB9kZDyhD2IO+L0UfsSn0Kv0VlXFVBV5lPKpWyVLH1WO0OMVNGfpjSE6XhQ8lXGC4UuQ7EHkOqv6l5FhFKTZT7bic7SJDTWEJseU1UIuUFXudztKdDmbxrOaO892Yc9GmEReU86xJKmQkv8MIgpOeWtT3j3jYcBSdCuMTavtBnmdP+bp8zPoIq/ktOHv02FZCLkxgjCTqR6WqRQEzU9XVEmJmP+RdizXL7UtfHVfLikkrW7NVMflJMvq8TXr9FRkkh0ZCEFOgu6CPDZSGG5yptPu21kCTqT0/heFReCwnZ9PL1+jIbDK7lHMKRRPRATBxwkrgYcEwfIUt/KODIRvw+ZWF2R8UDKQt8H9Oj1wV6P+7iNSvH/bFu+8X4P9btdSMHyJZT21uyekbM4ybkNrn1Ucdg5AZroTGvmDxi0JjxsoMfVQ9zLODL8nhQtQw4Cqn8L0XiHbPqdsPYNqmipGEVy/IFkSjKrlKepPC2sFqYa1WuDDyro+S95uGM4NN4TuqP6P8Pc6hTsidGuiL0pL7fxZ0orxeV9wAJu6K2h3Q6hWHm0HcS21nHe0Y1rxGfeYPCwV91fBO6Mtc6h3iVw3a/XgS2tHNy4X08Y+w9hVeD+BEA05/Vo7o80hoPS+H0EUahW/aVvNhvxAjnuUKQh3tmbVpLZP6w4u5ONepYmT7XVta5tkj8SJs/MvbnwjjpeEthUZzA44XcCPKbTZjyDcg9CfeJqn3kMHmyv6/ys6DaA0QdpGiTR6uJAplawkfHIgzB+teRS80vKjQxvahPMGxKLSzloyqnpS/82PbCj1u1KiUWUrtNrvvC6lmCeCl0hB6m2QvfHQ+FLF/tI32C+l9qrWYdLdRgWTdWwXN1Qe0Kh2n+q2d3XEUtCYaoyks+TESFZDZGxaEfHdsSFnJDUi1cXC7ARaQSVoMienMOEfFRalU57hVXp6nXFiDX1URKHWHLAts/EzIh9aoFmG6qAujUYFhQ3GNMpBkiIDmTydhGVNlDfra+3KggGqLJZl53Hr4zADpWCw3JanWxvZgFhVSUC4VlDKn5RZV4kayzro3x+vi0CVBERoYPHw0rjVtwb+bz7HRX5ZBnFDlTmeLW9ETckkuWpQSKj6kgQemXfP6ZXmU8hiqbMgNd8oW5Aw8ZEHM8QdgjdldVMkPPz+EZht0fVvgMW9NcOOtcbljJ7CZYUno43han0dVAH+H2burJ8nuckLgaJ68ntuApM4lOPGUq/Q18iYH3iiNwFi26UHEetzDYudl2lwEXXFU0DbbPH3759DEXXLM+ToJ06LiCsHCfLR1Tn8MGgGZ/wg49clc9x/ZkI6xN3JBTf5MKddwZ6K9/VTR58+/UE9T4Vj0E+jWmvzeiUmLy/Sm00Yv0uYwBfF1YZlj25Oh42bcpyl7krxvep7iOV0RNSV6oCCvakFPhDfnzT+9fVl2quFt3DfkqCPOwi8LGynfcaKfviTl9lTCfd/Oq38ty7m54+0SFvbvaJapdotolql2i2iWqXaLaJapdotolql2i2iWqXaLaJapdouu5RLXD84AdnjptUnvQtvGgSYsPYOJ4owvsEmkdJ5xpjScsHcTyX/3fjf9APxKPwLVX8vaqBFeojVbiw7GLalMH3VkDfbAcMfOGvKDVUwpw2hQsKUid/0A/qmGiaGQoOd46GiXHAtdGyc+w74TY76OWFF408HnfqBsInUuW7sPG8TxBPLlfPueBaRLOz0PiyA8Q3NZlEdZHErs/fPz5/Yeb8EmkjfpIsh38BFeJhF3BP+cocKwVvbXs8+44/gIhueh9dPvzR7TsG3U3AXoIrBER6CFgHmrZF90xqvlhSZeoMQC85I8++j7xM/BrP/E3jFSK0z56m/oZwc2dWDiPBEznG3mcduYFJJe+j3rN5twTQfto9teprPgVaDMrEtIvj6lF3Pkep23MP0MI5E8fpcTP7CvB6q7RMoogBAaPXNU+d52BXIy+tGHgsjLSsp+5S98kdUZ6+e6Ml7kmQ4O5j0DEzI9EnWOWlsbiZXunl21m2d6tuWzd8R6W7ftsKg8GMObBoDjyLmkxJu7S50sGHLU7xrLG0jzBCVeS+Ju3UAa60epcXoLx9M27+M/EZx59jwW+hc14H338n8QT8pWYHxgLH8mbGRJPEwGd9NR5MB5j9rRCnDpcClFQMGAYp6TrmMoQEPZQq9O4vPwKOzU0hCtgay1bms3dMZo4nkUn/CzVd8I87yOwzlW7EnHKT/lAhR0ereNwCjO2wuXf3TEMDD/QRxIRribHIINP6gxUs9FsRSOFEBJqNq6vEX0A88Z5JGcN9Da8egIgLj9pX1npGTocjWBr4HiobbXtRmIS0qSaJalL+0tJmX6TPBLmiKc+8lXZtOkj2AIPFNX6qJVGjjzZGD9M6MY82rRnn3ea26vTnn3RaWp9ehD6tKf1aRX1aaepFeoOFGqz20vo02a3VxV12m30ksq0p7QfSN1CtGlvRptCwwl1CmSrdRu98nRp19q9Ju1ladKkXsy1MbXO2/b2G1Prom2jmgBfh9CqdM9bU0ur0gqq0ratNekONGn7KqlJ21dV0aTtxlVqW6rKaLXtgralVlqRtu2UHm1foVq7cVWeHm1NOqVoUqWRMnalVoYuTSjGPKq0Y533tlelHeuip1XpgajSjlalVVSlPa1Kd6FKm9cd+CepT8OfqqJUmw013qRm7ShV2CtIs3ZmNGsvrVkjGtaioZSnYy8n7b3o2E6Wjg015pKQrcWcoTg3KWPEFMQqPWg721++3fHMV3p/XL398cwSarVelR3yDhbu5BR7aou8nx3yrEg9/D3yoi3ynPbe7QZ5hoh72SLPatR8m+TVGlVvkw98m6w1akU3ylqjlrBVnt8p72+jvJlu3f9WOXOnXKCq3WSfXIyq3WKnDGfEz4Egpe+Rpz3lS2qO39cJzVVLaI6XTmvvqiQzl7pkJ6e3e6/bCZ3de93eh76eCtAqpDADyRZlMHdnFXR3txnMMRX3kL08VZk5M5ezdKbOWj7srGWtNKuXsay15pH5j9fVmfvNUwbf8VyacpEKc/005e3U5ZYpyoktZq4AbOYWU4deDzv0qvVl5YKuWl0Welo2dVb2sgrqcp+B1tblglzkIpXlJqHWVeqy3CDrVF/mC69m6UsdWD3wwKrWl5ULqWp9WWgw9bIH/3ST0dTwpyrozv0HUiP6Lck5LlKXbhJL3VaXro6iGverqmQtLDp/IHWy4nY2KpQVlX+dqZWVqBmniEfZCJi1tTKOrJh36LjRelkOh+p374lLFGKTwgu7Lp386vzqW3DRePpZIH/8KYxL36pCbuAviIbkz7AlrGAkyN9+/nv8uxos/4HR8Q+OS/itYIEpApYQpCkITJVFcrVlNBwWZl9Lvm5Ns2VRfEZMyqysOGJKOoJ2+9d5iodqNR6MZd3PdCFhWWAP1UwXj/3B2PFSdT+hruTAxK7LB4IK7D7PFgUFJTlUU7gz6ihdrDpwxZs7Y4gdV+YgoXNUXtt0OOQEAll11DxDlKES5xF4LpxP2cFkzuRUmuhbtJfF+/edoSr4W/8qbxUXdlL2ci7stJR1zZ7e2XSF98Sgeo3LX2MwcNX/pY3Ci0OQ0BoAuwRAIYf3VtsD6QyFEzYIOs1jsgg6zZM3Ccpb0MMQF3KJT94oOIlV1maBhsCuDYNUFuQJ2wUt+4jMgpZ96lZBact5GLICFvjkbYITWGNtEWgA7NYeaGt7QBK/fUz2QPvk7YH2cYuKtrYHTmKNtT2gAbBbe6Cn7QFJ/N4x2QO9k7cHesctKnraHjiJNdb2gAbAjuMFlrYHpHPGOqZ4gXXy8QLruF2JlrYHTmGNtT2gAbDjxEJtD6jsjWOyBzonbw90jltUdLQ9cBJrrO0BDYAd5w9czRsE+HE0gCPNAzgBXFun9OnsDV+/t6+s+zKmEp+GVgOam8IjMQVlcLr6uozuJS0G6qx1n5ExduD879woWugc1TIov5qA6EJeQVXUHLBLmAgP3L+To38XMG+lRThTEmCd+sVzE/ouKk367dzMEEr/hT1ro3HkQWb+cezmRum81U13dCvbVgVsf8OPBCkxx5Hsl1iN7cvatjqXlwvr2hZ5Q1u6rm3ZHNPLyTFQmbA8fllYj3PTYRzYBew74pdtilduzC4rSlo2u70FNS2LZJZkTcvSlYuVD6NQl6w8VmnbxY3i0C5YPvxbtDdXLAd1t3bZrNLJySpx7aHy+CWvgss9lEO6MbVCt+RuzDmHeHduvptqNy3FszqjXm7rZhhg6pNJOGJo4InETro2xl9nXgGaJt5IOAzkbAaMUsF/f33dum42O/z+7Ay9eYOaygUCH5yhc7R+q9zxTDLgqrpTsvUz9F9v0FXvstk8+71t97vj+4V+x+VTLGCGiY7nHWGzrjC0p1FtJZ+W3VRVRHbnaWCzty9s9g4Sm71DwObqTKPTwGbL2hM2px0fEjbTo9oTNjsamxKbnX1hs3OQ2OwcAjY3j8Dku4x9oxhMvunkj8KU039pYZh5KspATKu4aezeWbZgTglH1c68ZRsP4/CuW9/Zxb9buMzeQ4fQzbTPiSNsx0NQxfipAO/ZIudZgVcB7991thixKY/Vznxn243lsK5RrtTd2UXw0cFdrr3qIuvyqllnm9vrpjVZgezbi8zfwUMA8FySngPTg+wcl0A6Tq/ZrN3d3d01mmf/vSShqcjmc9RMLbK7ZUlMpUypkBzFuZFJwz9zYPkWLU87m6xOnnZzL8Nag1yeL5i1pdILspMF2UoJz9+/UUjJ4FOXupklKSsqdouqN3sIbL7e+uyLzzNrvx6b5K3kkuxc9rZsLXqzxVRW7b+KSt6CinoeApevtTr7YvKsApvHJnYruCA7F7ptLXRXSKj28Qnd9vEI3XYVeLx9QkK3gguyc6Hb00J3hYTqHZ/Q7R2P0O1Vgcd7JyR0K7ggu3cvWFrortiLW8fnXrCOx71gVWE3a52Qe6F6C7L7eJoWuqtCT8cndDvHI3Q7VeDxzgkJ3QouyO59uoWWI4pHsGka/Kop5E+A7xbddWm570miqfJDhYx996WHUhNJl/vp7qrs0GZj2IzGB1JuKB7YLkoNfXKxx9HQ8RxuRwm5rSYaO14gCC+g6FDvdXthzaHubD5ut8o1h2ZhGhf66e6s3tAmQyifT8qsM7Quo2xTY6gAPllRbQiOgcwVGyqSS/ZdbGgWoHGJn+6ujk5tMoIiWGSP56XW5ZEtTkoVoUoyD0q1LhdUGSqSQ/Z/VGoWoemyPt1dnZLaeBi74JYSj0atyy3bHooqgGVWnIm67ME/3WX1hYrknvljUVPKLjsIFXLY5/hFeUzso7vSdRgf9K+lcV7b+uC/Jc+zh64B6iVbWr9t0yWYzbSdwcOUZbBwUVP7bgezgl5UoYDWKumwnO9Xs++d8fyMvlHNNsLxo5cXZGOO5FARdS3CFLOG4+F3xnKG/ElEHzqcB0TiWDGLLH7QQN+r04vyJ/UisC3CyA9cFzHyZ0C4qIOPSlYpk++Z1CIIc+RAq40s9EOVsd+wIxY5IjZkgDqa4EeSuajwwmCius3LDXGzG3bHiRAusUpijUIm/d2u5ztlmnaZTAPjQQm+kX+/gAMTLecmoBSx0BCA7QG2GX3ErlTMaf5q82zuUmzxB31APnM8aBNYZEqusG3gmvEYNBt0CSrYckaEi0z2eQtt/yDLws+xz0nc0XZMd7ZGydG5WWHGEbGaERSQ3acZ5M8jBJhAjQ/snNjUsmnAloMdbLcY7K84cukottQEc/CIIEa4Tz2u1Ix8IG2rlSrihgwDfjgYZ2o45YA80Xj5KE90VhLMF01nTZxvZCdhJfbzIH2COQqH2ZeS15e7lDF9JBbCQ0FYLP+zTCnA/2RaNdVCHoX9zaiBbgi20AT2FbLNOiDfi+U+sIFHJnnkvdws3Arq+wuYYaNSYPIJC+LI33NiXaW5B4GwThOUd6ysO3uwcF9FZioLPKmfPar+G7RlVKFrlaHLTZtYgUsQVySUZisM9kk1pSTeAxnCftcRyKSBayFGfMrkuhj3L/cv/w97bwLGV/0BAA==","base64")).toString("utf8"));var tk={dir:"TGPH0DIR",prometheus:"TGPH0PROMETHEUS",tempo:"TGPH0TEMPO",folder:"TGPH0FOLDER",path:"TGPH0PATH",drift_age:"7919001s",wave_wait:"7919002s",schedule:"7919003s"},TJ="https://tgph0.invalid/TGPH0REPORTS",NP=["drift_age","wave_wait","schedule"],DP=["prometheus","tempo","folder","path"];function AJ(e){if(e==="0")return 0;let t=/^(?:(\d+)y)?(?:(\d+)w)?(?:(\d+)d)?(?:(\d+)h)?(?:(\d+)m)?(?:(\d+)s)?(?:(\d+)ms)?$/.exec(e);if(!e||!t)return 0;let n=[365*864e5,7*864e5,864e5,36e5,6e4,1e3,1],r=0;for(let o=0;o<n.length;o++)t[o+1]!==void 0&&(r+=Number(t[o+1])*n[o]);return Math.round(r/1e3)}var Dwe=["y","Y","yes","Yes","YES","on","On","ON","n","N","no","No","NO","off","Off","OFF"],Mwe=/^[-+]?[0-9_]+(?::[0-9_]+)+(?:\.[0-9_]*)?$/,Lwe=/^(?:[-+]?(?:[0-9]+)(?:\.[0-9]*)?(?:[eE][-+]?[0-9]+)?|\.[0-9]+(?:[eE][-+]?[0-9]+)?|[-+]?\.(?:inf|Inf|INF)|\.(?:nan|NaN|NAN))$/,Gwe=/^[0-9]{4}-[0-9]{2}-[0-9]{2}$/,Fwe=/^[0-9]{4}-[0-9][0-9]?-[0-9][0-9]?(?:[Tt]|[ \t]+)[0-9][0-9]?:[0-9]{2}:[0-9]{2}(?:\.[0-9]*)?(?:[ \t]*(Z|[-+][0-9][0-9]?(?::[0-9]{2})?))?$/,zwe=/^[-+]?(?:0b[01]+|0x[0-9a-fA-F]+|0o[0-7]+|[0-9]+)$/,Uwe={0:"\\0",7:"\\a",8:"\\b",9:"\\t",10:"\\n",11:"\\v",12:"\\f",13:"\\r",27:"\\e",34:'\\"',92:"\\\\",133:"\\N",160:"\\_",8232:"\\L",8233:"\\P"},nk=e=>e>=32&&e<=126||e>=161&&e<=55295&&e!==8232&&e!==8233||e>=57344&&e<=65533&&e!==65279||e>=65536&&e<=1114111,rk=e=>e===32||e===9,MP=e=>nk(e)&&e!==65279&&e!==13&&e!==10,Bwe=(e,t)=>{let n=MP(e)&&!rk(e);return MP(e)&&e!==35&&!(t===58&&!n)||t!==void 0&&MP(t)&&!rk(t)&&e===35||t===58&&n},Wwe=e=>nk(e)&&e!==65279&&!rk(e)&&!"-?:,[]{}#&*!|=>'\"%@`".includes(String.fromCodePoint(e)),Hwe=e=>{if(!zwe.test(e))return!1;let t=e.replace(/^[-+]/,""),n={b:2,o:8,x:16}[t[1]]??10;return Number.isFinite(Number.parseInt(n===10?t:t.slice(2),n))},qwe=e=>["~","null","Null","NULL","true","True","TRUE","false","False","FALSE","<<"].includes(e)||Hwe(e)||Lwe.test(e)&&(Number.isFinite(Number.parseFloat(e))||/\.(inf|Inf|INF|nan|NaN|NAN)$/.test(e))||Gwe.test(e)||Fwe.test(e);function Jwe(e){if(e==="")return'""';if(Dwe.includes(e)||Mwe.test(e))return`"${e}"`;let t=[...e].map(o=>o.codePointAt(0)),n=Wwe(t[0])&&!rk(t[t.length-1])&&t[t.length-1]!==58,r=!1;return t.forEach((o,s)=>{(o===10||!nk(o))&&(r=!0),n=n&&Bwe(o,s?t[s-1]:void 0)}),!r&&n&&!qwe(e)?e:`"${t.map(o=>Uwe[o]??(nk(o)?String.fromCodePoint(o):`\\${o<=255?"x":o<=65535?"u":"U"}${o.toString(16).toUpperCase().padStart(o<=255?2:o<=65535?4:8,"0")}`)).join("")}"`}var Kwe=e=>e.replace(/[.*+?^${}()|[\]\\/]/g,"\\$&");function OJ(e,t,n={}){for(let m of NP)if(!/^[0-9a-z]+$/.test(t[m]))throw new Error(`dashboards.${m} is ${JSON.stringify(t[m])}; use a duration such as 4h or 1d`);for(let m of DP)if(/[\r\n]/.test(t[m]))throw new Error(`dashboards.${m} must be one line`);let r=new Map;for(let m of NP)r.set(tk[m],t[m]);for(let m of NP)r.set(String(AJ(tk[m])),String(AJ(t[m])));for(let m of DP)r.set(tk[m],t[m]);r.set(TJ,n.reports);let o=new Set([...DP.map(m=>tk[m]),TJ]),s=[...r.keys()].map(Kwe).join("|"),i=new RegExp(s,"g"),a=new RegExp(s),c=m=>{let h=r.get(m);if(h===void 0)throw new Error(`the dashboards template has ${m} and nothing to fill it with`);return h},u=m=>m.replace(i,c),l=m=>typeof m=="string"?u(m):Array.isArray(m)?m.map(l):m&&typeof m=="object"?Object.fromEntries(Object.entries(m).map(([h,y])=>[u(h),l(y)])):m,d=m=>{let h=/^(.*(?:: |- ))(\S+)$/.exec(m);return h&&o.has(h[2])&&!a.test(h[1])?`${h[1]}${Jwe(c(h[2]))}`:m.replace(i,y=>{if(o.has(y))throw new Error(`the dashboards template has ${y} inside a YAML scalar: ${m}`);return c(y)})},p=t.dir.replace(/\/+$/,""),f=n.reports!==void 0,g=[];for(let m of e.files){if(m.links!==void 0&&m.links!==f)continue;let h="json"in m?JSON.stringify(l(m.json),null,2)+m.suffix:m.yaml.map(d).join(`
|
|
891
891
|
`);g.push({path:`${p}/${m.path}`,content:h})}return g.sort((m,h)=>m.path<h.path?-1:1)}function PJ(e,t={}){return OJ(EJ,e,t)}var Vwe={dir:"observability/terragucci",prometheus:"prometheus",tempo:"tempo",folder:"terragucci",path:"/var/lib/grafana/dashboards/terragucci",drift_age:"1d",wave_wait:"4h",schedule:"2d"};function jJ(e){if(e)return{...Vwe,...e===!0?{}:e}}var Zwe="# Generated by terragucci from terragucci.yml (dashboards). Edit terragucci.yml, then run terragucci init.",Ywe="terragucci";function IJ(e,t){if(!e.endsWith(".json"))return t.startsWith(Zwe);try{let n=JSON.parse(t).tags;return Array.isArray(n)&&n.includes(Ywe)}catch{return!1}}var Op=".gitlab-ci.yml",ok=`- local: ${Xr.gitlab}`,Xwe=["# terragucci's jobs are in .gitlab/terragucci.yml, which terragucci init writes","# and this file includes. Add your own jobs here; init keeps them.","include:",` ${ok}`,""].join(`
|
|
@@ -893,7 +893,7 @@ ${iJ(i.id)}`,{match:"anywhere",author:e.meName})}catch(N){return{...u,ran:I,repl
|
|
|
893
893
|
`);if(!("include"in t)){let i=0;for(;i<n.length&&(n[i].trim()===""||n[i].trim().startsWith("#")||n[i].trim()==="---");)i++;return[...n.slice(0,i),"include:",` ${ok}`,"",...n.slice(i)].join(`
|
|
894
894
|
`)}let r=n.findIndex(i=>/^include:\s*(#.*)?$/.test(i));if(r<0||!Array.isArray(t.include))return;let o=n.slice(r+1).find(i=>i.trim()!==""&&!i.trim().startsWith("#")),s=o&&/^(\s*)- /.exec(o)?.[1];if(s!==void 0)return[...n.slice(0,r+1),`${s}${ok}`,...n.slice(r+1)].join(`
|
|
895
895
|
`)}function CJ(e,t){if(e===void 0||e.startsWith(pi)||e.trim()==="")return Xwe;let n=GP(e),r=GP(t),s=Object.keys(r).filter(u=>u!=="stages").filter(u=>u in n);if(s.length)throw new S(`${Op} has jobs named ${s.join(", ")}, as ${Xr.gitlab} does, and GitLab would merge the two; rename yours`);if(Array.isArray(n.stages)){let u=[...lh.before,...lh.after],l=r.stages.filter(p=>!u.includes(p)),d=l.map(p=>n.stages.indexOf(p));if(d.some((p,f)=>p<0||f>0&&p<d[f-1]))throw new S(`${Op} lists its own stages, which GitLab reads in place of the included file's; add ${l.join(", ")} to them, in that order`)}if(LP(n.include))return e;let i=Qwe(e,n),a=i===void 0?void 0:GP(i),c=(u,l)=>{let{include:d,...p}=u,{include:f,...g}=l,m=Array.isArray(f)?f.filter(h=>!LP(h)):[];return JSON.stringify(p)===JSON.stringify(g)&&JSON.stringify(d??[])===JSON.stringify(m)};if(i===void 0||!a||!LP(a.include)||!c(n,a))throw new S(`${Op} has an include init cannot add a line to; write its include as a list and add \`${ok}\` to it`);return i}var zP="terragucci.components.json",ebe="terragucci.components/v1",FP="terragucci",tbe="applied by terragucci's pipeline; a failed wave leaves what applied, and a re-run finishes it";function nbe(e,t){let n=new Set,r=[];for(;n.size<e.length;){let o=e.filter(s=>!n.has(s)&&[...t.get(s)??[]].every(i=>n.has(i))).sort();if(o.length===0)throw new Error(`these roots depend on each other in a cycle: ${e.filter(s=>!n.has(s)).join(", ")}`);o.forEach(s=>n.add(s)),r.push(o)}return r}var rbe=(e,t)=>JSON.stringify(e.map(n=>[...n].sort()))===JSON.stringify(t.map(n=>[...n].sort()));function obe(e,t){let n=e.flat(),r=new Set(n),o=new Map(n.map(s=>[s,new Set([...t.get(s)??[]].filter(i=>r.has(i)&&i!==s))]));return rbe(nbe(n,o),e)||e.forEach((s,i)=>{if(i>0)for(let a of s)for(let c of e[i-1])o.get(a).add(c)}),o}function NJ(e){let t=e.waves.map(s=>[...s].sort()),n=obe(t,e.order),r=e.dirOf??(s=>s),o=t.flat();return{schema:ebe,generated_by:`${FP} ${Pr}`,roots:Object.fromEntries(o.map(s=>[s,{dir:r(s)}])),components:o.map(s=>({name:s,dependsOn:[...n.get(s)].sort(),deploy:[{phase:"Apply",steps:[{kind:"terraform-apply",root:s,noRollback:tbe}]}]})),pipeline:{path:e.pipeline.path,forge:e.pipeline.forge,jobs:e.pipeline.applyJobs}}}var DJ=e=>`${JSON.stringify(e,null,2)}
|
|
896
|
-
`;function MJ(e){try{let t=JSON.parse(e);return typeof t.generated_by=="string"&&(t.generated_by===FP||t.generated_by.startsWith(`${FP} `))}catch{return!1}}function As(e,t){return So(e)?{path:e,status:Ro(e,"utf-8")===t?"unchanged":"updated",content:t}:{path:e,status:"created",content:t}}function lbe(e,t){if(typeof t!="string")return t;let n=_o(e,t);if(!So(n))throw new S(`own_jobs names ${t}, which the repo does not have`);let r;try{r=Yl(Ro(n,"utf-8"))}catch(s){throw new S(`own_jobs: ${t} is not YAML (${s.message})`)}let o=typeof r=="string"?[`own_jobs (${t}) must be a map of job name to job`]:cR(r,`own_jobs (${t})`);if(o.length>0)throw new S(o.join("; "));return r}async function ik(e,t={}){let n=t.settings?void 0:fe(e),r=t.settings??ue(n?await Re(n):{}),o=[],s=et(e,r);
|
|
896
|
+
`;function MJ(e){try{let t=JSON.parse(e);return typeof t.generated_by=="string"&&(t.generated_by===FP||t.generated_by.startsWith(`${FP} `))}catch{return!1}}function As(e,t){return So(e)?{path:e,status:Ro(e,"utf-8")===t?"unchanged":"updated",content:t}:{path:e,status:"created",content:t}}function lbe(e,t){if(typeof t!="string")return t;let n=_o(e,t);if(!So(n))throw new S(`own_jobs names ${t}, which the repo does not have`);let r;try{r=Yl(Ro(n,"utf-8"))}catch(s){throw new S(`own_jobs: ${t} is not YAML (${s.message})`)}let o=typeof r=="string"?[`own_jobs (${t}) must be a map of job name to job`]:cR(r,`own_jobs (${t})`);if(o.length>0)throw new S(o.join("; "));return r}async function ik(e,t={}){let n=t.settings?void 0:fe(e),r=t.settings??ue(n?await Re(n):{}),o=[],s=et(e,r);o1(s);let i=s.kind==="terragrunt",a=Ks(e,[]),c=r.binary?{value:r.binary,reason:"terragucci.yml"}:t.binary?{value:t.binary,reason:"--binary"}:a,u=await s.discover({binary:c.value,...t.terragrunt?{terragrunt:t.terragrunt}:{},...t.atmos?{atmos:t.atmos}:{},...t.terramate?{terramate:t.terramate}:{}});o.push(...u.notes);let l=u.roots,d=u.layers,p,f,g;if(s.kind==="atmos")f={reason:s.reason,version:r.atmos?.version??yw};else if(s.kind==="terramate")g={reason:s.reason,version:rb,skipped:u.skipped??[]};else if(s.terragrunt){let J=r.terragrunt??{};s.terragrunt.stacks.length>0&&o.push(`explicit stacks: ${s.terragrunt.stacks.join(", ")}; terragrunt stack generate wrote their units, and every job generates them again before discovery`);let j=BN(e);p={reason:s.reason,version:J.version?{value:J.version,reason:"terragucci.yml"}:j?{value:j,reason:"terragrunt_version_constraint"}:{value:ai.terragrunt,reason:"the image"},parallelism:UN(e,J),source:u.source??"terragrunt.hcl files",stacks:s.terragrunt.stacks}}let m=l.map(J=>J.root);!i&&!r.binary&&!t.binary&&Object.assign(c,s.binary(m));let h=i||!ea[c.value].releaseInRequiredVersion?void 0:iD(e,m),y=i?void 0:om(c.value),w=y?_o(e,rm[y]):void 0,b=w&&So(w)?iT(Ro(w,"utf-8")):void 0,k=typeof r.version=="string"?{value:r.version,reason:"terragucci.yml"}:b&&y?{value:b,reason:rm[y]}:h?{value:h,reason:"required_version"}:{value:ai[c.value]??"",reason:"the image"},v=[];for(let J of m){let j=cT(e,J,c.value,r.version);j&&j.version!==k.value&&v.push({root:J,...j});let z=i?iv(e,J):void 0;z&&z!==p?.version.value&&v.push({root:J,tool:"terragrunt",version:z,source:"terragrunt_version_constraint"})}let $=Kr(e),_=r.forge?{value:r.forge,reason:t.settings?"the project":"terragucci.yml"}:t.forge?{value:t.forge,reason:"--forge"}:$;if(!_)throw new S("cannot tell which forge this repo is on; pass --forge github, gitlab or forgejo");if(_.value==="gitlab"){let J=Nf(r,"config");if(J.length)throw new S(J.join("; "))}let E=_o(e,"chant.workspace.json"),x=Xg(So(E)?Ro(E,"utf-8"):void 0)>0?"sealed":"ledger",A=Bs(r.approval),O=A?{value:A,explicit:!0}:t.approval?{value:t.approval,explicit:!0}:{value:x,explicit:!1},I,N;p?(I=c.value==="choudoufu"?HT("choudoufu"):hz(),N={version:p.version.value,parallelism:p.parallelism.value,exclude:r.terragrunt?.exclude??[],...s.terragrunt&&s.terragrunt.stacks.length>0?{stacks:!0}:{},...r.generate?{generate:!0}:{},...r.sources?{sources:!0}:{},...r.terragrunt?.credentials?{credentials:r.terragrunt.credentials}:{},installs:hq(c.value,k.value,p.version.value,c.value==="choudoufu"?{choudoufu:ai.choudoufu}:{tofu:ai.tofu,terragrunt:ai.terragrunt})}):I=HT(c.value);let L=N?void 0:sc(r,d.flat());if(L&&_.value==="forgejo"){let J=Object.entries(r.environments??{}).filter(([,j])=>j.forge_environment!==void 0).map(([j])=>j);if(J.length)throw new S(J.map(j=>`environments.${j}.forge_environment: ${vR}`).join("; "))}let U=ai[c.value],F=r.rollouts&&Kn(r,"rollout")!=="off"?r.rollouts:void 0,M=qq({forge:_.value,binary:c.value,version:k.value,image:r.image??qT(I),...r.image?{imageFromConfig:!0}:{},install:!N&&k.value!==U?{binary:c.value,version:k.value}:void 0,...v.length>0?{rootPins:!0}:{},...r.generate&&!N?{generate:!0}:{},...r.sources&&!N?{sources:!0}:{},...N?{terragrunt:N}:{},layers:d,env:r.env,oidc:Zs(r.oidc,r.environments),...L?{applyGroups:L}:{},tokenEnv:r.token_env,...r.decide?.token_env?{decideTokenEnv:r.decide.token_env}:{},...r.telemetry?.headers_secret?{headersSecret:r.telemetry.headers_secret}:{},...r.modules?.publish||r.modules?.registry?{publish:!0,...r.modules.attest?{attest:!0}:{},...r.modules.registry?.bucket?{publishBucket:r.modules.registry.bucket}:{}}:{},...r.reports?{reports:r.reports}:{},...r.drift?{drift:r.drift}:{},...F?{rollouts:F}:{},...s.prepare?{synth:s.prepare}:{},...f?{atmos:{version:f.version}}:{},...g?{terramate:{version:g.version}}:{},...r.notify?{notify:r.notify}:{},...r.cost?{cost:{keySecret:r.cost!==!0&&r.cost.key_secret||If,install:r.cost===!0||!r.cost.command,...r.cost!==!0&&r.cost.approve_above!==void 0?{approveAbove:!0}:{}}}:{},...r.comments?{comments:r.comments}:{},...r.gitlab?.token?{gitlabToken:r.gitlab.token}:{},...!N&&!f&&!g&&r.waves?.canary?.length?{canary:r.waves.canary}:{},...r.waves?.jobs&&r.waves.jobs>1?{waveJobs:r.waves.jobs}:{},gate:r.gate,...QR(r).length?{environmentGates:QR(r)}:{},...Bs(t.settings?.approval)?{approval:Bs(t.settings?.approval)}:{},...O.value==="pr-review"?{prReview:!0}:{},...r.agent?.login?{agentLogin:r.agent.login}:{},...r.respond?{respond:r.respond}:{},...r.policy?{policy:!0}:{},...Nv(r)?{agentComment:Nv(r)}:{},...aA(r)?{agentDrift:aA(r)}:{},...r.atlantis_comments?{atlantisComments:!0}:{},...NO(r)?{review:NO(r)}:{},...r.apply?.resume?{resume:r.apply.resume}:{},...Vv(e).length>0?{migrations:!0}:{},...r.apply?.when==="pull-request"?{applyWhen:"pull-request",...r.apply.merge?{applyMerge:r.apply.merge}:{},...r.apply.merge_token_env?{applyMergeTokenEnv:r.apply.merge_token_env}:{},...r.apply.requires?{applyRequires:r.apply.requires}:{}}:{},...r.locks==="plan"?{locksPlan:!0}:{},...r.apply?.branches&&Object.keys(r.apply.branches).length>0?{applyBranches:r.apply.branches}:{},...r.own_jobs!==void 0?{ownJobs:lbe(e,r.own_jobs)}:{},...r.ephemeral?{ephemeral:{sweep:r.ephemeral.sweep??KC}}:{},...r.runner!==void 0?{runner:r.runner}:{},...r.pass!==void 0?{pass:r.pass}:{}}),te=_o(e,M.path);if(So(te)&&!t.force&&!Ro(te,"utf-8").startsWith(pi))throw new S(`${M.path} exists and terragucci did not write it; move it aside or pass --force`);let ie=_.value==="gitlab"?void 0:gP[_.value],Q=_.value==="gitlab"?void 0:Dc[_.value],ne=_.value==="gitlab"?void 0:mP[_.value],ae=[As(te,M.content)];for(let J of M.extra??[]){let j=_o(e,J.path);if((J.path===ie||J.path===Q||J.path===ne)&&So(j)&&!t.force&&!Ro(j,"utf-8").startsWith(pi))throw new S(`${J.path} exists and terragucci did not write it; move it aside or pass --force`);ae.push(As(j,J.content))}let he=_o(e,zP);if(So(he)&&!t.force&&!MJ(Ro(he,"utf-8")))throw new S(`${zP} exists and terragucci did not write it; move it aside or pass --force`);for(let J of[ie,Q,ne]){if(!J||(M.extra??[]).some(z=>z.path===J)||t.settings)continue;let j=_o(e,J);So(j)&&Ro(j,"utf-8").startsWith(pi)&&ae.push({path:j,status:"removed",content:""})}if(_.value==="gitlab"){let J=_o(e,Op);ae.push(As(J,CJ(So(J)?Ro(J,"utf-8"):void 0,M.content)))}let $e=jJ(r.dashboards);if($e)for(let J of PJ($e,{...Zu(r.reports)?{reports:Zu(r.reports)}:{}})){let j=_o(e,J.path);if(So(j)&&!t.force&&!IJ(J.path,Ro(j,"utf-8")))throw new S(`${J.path} exists and terragucci did not write it; move it aside, set dashboards.dir, or pass --force`);ae.push(As(j,J.content))}let ye=pbe(e,i||f||g?d.length:an(d,r.waves?.canary).length,O.value==="sealed"?"seal":O.explicit?"unseal":"leave",t.name);if(ye&&ae.push(ye),O.value==="sealed"){let J=_o(e,Rg);So(J)?t.signer&&o.push(`${Rg} exists and init does not edit it; add ${t.signer} to it by hand`):t.signer?(ae.push(As(J,fbe(e,t.signer))),o.push(`${Rg} lists ${t.signer}; merge it in a reviewed pull request before the first wave waits`)):o.push(`approval: sealed counts only approvals sealed by a key ${Rg} lists, and there is none yet; terragucci init --signer <your principal> writes it from git config user.signingkey`)}if(t.settings){let J=await dbe(e,t.settings);J&&ae.push(J)}t.settings||(t.forge&&r.forge&&t.forge!==r.forge&&o.push(`--forge ${t.forge} is ignored: ${qc(e,n??"terragucci.yml")} sets forge: ${r.forge}`),t.binary&&r.binary&&t.binary!==r.binary&&o.push(`--binary ${t.binary} is ignored: ${qc(e,n??"terragucci.yml")} sets binary: ${r.binary}`),t.approval&&A&&t.approval!==A&&o.push(`--approval ${t.approval} is ignored: ${qc(e,n??"terragucci.yml")} sets approval: ${A}`));let Z=n?`using ${qc(e,n)}`:"no terragucci.yml needed (defaults fit)";if(!t.settings){let J={};if(t.forge&&!r.forge&&t.forge!==$?.value&&(J.forge=t.forge),t.binary&&!r.binary&&t.binary!==a.value&&(J.binary=t.binary),t.approval&&!A&&t.approval!==x&&(J.approval=t.approval),Object.keys(J).length){if(n)throw new S(`${qc(e,n)} exists and init does not edit it; add ${Object.entries(J).map(([z,X])=>`${z}: ${X}`).join(", ")} to it`);let j=_o(e,"terragucci.yml");ae.push(As(j,`${xr(J,0).trim()}
|
|
897
897
|
`)),Z=`terragucci.yml records ${Object.keys(J).join(" and ")}, which detection cannot find`}}if(r.drift&&_.value==="gitlab"&&o.push(`drift is set: add a pipeline schedule with the cron ${r.drift} under CI/CD > Schedules, and give ${r.token_env??"GITLAB_TOKEN"} the api scope so the drift issue can be kept`),F&&_.value==="gitlab"&&o.push(`rollouts is set: add a pipeline schedule with the cron ${F} and the variable TERRAGUCCI_SCHEDULE set to rollouts under CI/CD > Schedules, and give ${r.token_env??"GITLAB_TOKEN"} the api and write_repository scopes so the rollout job can push a wave's branch and open its merge request`),r.rollouts&&!F&&o.push("rollouts is set and respond.rollout is off, so no rollout job is written"),r.comments&&_.value==="gitlab"&&o.push(`comments is set: add a pipeline schedule with the cron ${r.comments} and the variable TERRAGUCCI_SCHEDULE set to comments under CI/CD > Schedules, and give ${r.token_env??"GITLAB_TOKEN"} the api scope and the Developer role so the comments job can answer notes and start pipelines`),r.gitlab?.token==="protected"&&_.value==="gitlab"){let J=r.token_env??"GITLAB_TOKEN";o.push(`gitlab.token is protected: under Settings > CI/CD > Variables, edit ${J} and tick Protect variable and Mask variable, and keep the default branch protected; merge request and branch pipelines then never see the token, the plan job stops if it does, the comments job posts the plan notes, and no fmt job commits formatting`)}r.waves?.canary?.length&&g&&o.push("waves.canary is set; the canary stacks' layers apply first, then the layers of the rest, each wave behind its gate"),r.waves?.canary?.length&&f&&o.push("waves.canary is set; the canary instances' layers apply first, then the layers of the rest, each wave behind its gate"),r.waves?.canary?.length&&p&&o.push("waves.canary is set; the canary units' layers apply first, then the layers of the rest, each wave behind its gate");let me=NJ({waves:i||f||g?d:an(d,r.waves?.canary),order:u.order,...f?{dirOf:J=>s.sourceOf(J)}:{},pipeline:{path:M.path,forge:_.value,applyJobs:M.applyJobs}});if(ae.push(As(he,DJ(me))),!t.dryRun){for(let J of ae)if(J.status!=="unchanged"){if(J.status==="removed"){ibe(J.path);continue}sbe(ube(J.path),{recursive:!0}),abe(J.path,J.content)}}return{roots:m,rootReasons:l,layers:d,...p?{terragrunt:p}:{},...f?{atmos:f}:{},...g?{terramate:g}:{},binary:c,image:r.image??qT(I),version:k,pins:v,forge:_,files:ae,notes:o,configNote:Z}}var LJ="# terragucci reconcile writes this file from the control repo's terragucci.yml; edit that file, not this one.";function zJ(e,t){let n=r=>r!==null&&typeof r=="object"&&!Array.isArray(r)?Object.fromEntries(Object.keys(r).sort().map(o=>[o,n(r[o])])):r;return JSON.stringify(n(e??null))===JSON.stringify(n(t??null))}function sk(e,t){let n=e[t];return n===void 0||zJ(n,Ky[t])?void 0:n}async function dbe(e,t){let n=fe(e),r=_o(e,"terragucci.yml"),o=n===r&&Ro(r,"utf-8").startsWith(LJ),s=VS.filter(a=>sk(t,a)!==void 0);if(n&&!o){let a=ue(await Re(n)),c=VS.filter(l=>!zJ(sk(a,l),sk(t,l)));if(c.length===0)return;let u=c.map(l=>sk(t,l)!==void 0?`set its ${l} key to the control repo's (${JSON.stringify(t[l])})`:`remove its ${l} key`).join(" and ");throw new S(`${qc(e,n)} exists and terragucci did not write it, and the jobs read ${c.join(" and ")} from it; ${u}, or remove the file so the control repo writes it`)}if(s.length===0&&!o)return;let i=s.length?xr(Object.fromEntries(s.map(a=>[a,t[a]])),0).trim():"{}";return As(r,`${LJ}
|
|
898
898
|
${i}
|
|
899
899
|
`)}var GJ="0.102.0";function pbe(e,t,n,r){let o=_o(e,"chant.workspace.json"),s,i=So(o)?Ro(o,"utf-8"):void 0;if(n!=="seal"){if(n==="leave"||i===void 0)return;try{s=JSON.parse(i)}catch{throw new S("chant.workspace.json is not valid JSON, so init cannot drop the tf-apply gates from it")}let p=s.identity,f=Object.keys(p?.gates??{}).filter(g=>/^wave-\d+$/.test(g));if(f.length===0)return As(o,i);for(let g of f)delete p.gates[g];return Object.keys(p.gates).length===0&&delete p.gates,Object.keys(p).length===0&&delete s.identity,As(o,`${JSON.stringify(s,null,2)}
|
|
@@ -982,7 +982,7 @@ ${[OK("<b>Estate</b>",e.estate),...e.projects.map(r=>OK(t(r.project),r))].join(`
|
|
|
982
982
|
<div class="scroll"><table id="dora-weeks"><tr><th>Week of</th><th>Deployments</th><th>Applies</th><th>Failed changes</th><th>Lead time</th><th>Time to restore</th></tr>
|
|
983
983
|
${n.join(`
|
|
984
984
|
`)}
|
|
985
|
-
</table></div>`}var Rk={deployments:sn.doraDeployments,leadTime:sn.doraLeadTime,failureRate:sn.doraFailureRate,restore:sn.doraRestore};function CK(e){let t=[],n=(r,o)=>{let s={project:r};t.push({name:Rk.deployments,unit:"",description:`Applied waves per week over the newest ${e.weeks} weeks.`,value:o.per_week,attributes:s});let i=o.lead_time;for(let[a,c]of[["total",i.median_seconds],["before_gate",i.before_gate_seconds],["at_gate",i.at_gate_seconds],["after_gate",i.after_gate_seconds]])c!==null&&t.push({name:Rk.leadTime,unit:"s",description:"Median lead time from a change's first plan to its wave applied, and its split.",value:c,attributes:{...s,segment:a}});o.change_failure.rate!==null&&t.push({name:Rk.failureRate,unit:"",description:"Failed applies and applied waves followed by drift, over all applies.",value:o.change_failure.rate,attributes:s}),o.restore.median_seconds!==null&&t.push({name:Rk.restore,unit:"s",description:"Median time from a failed apply or drift found to restored.",value:o.restore.median_seconds,attributes:s})};n("*",e.estate);for(let r of e.projects)n(r.project,r);return t}var eke="terragucci.estate/v1",tke=20,Zc=e=>Date.parse(e)||0,v0=(e,t)=>Math.max(0,Math.round((t.getTime()-Zc(e))/1e3));function xk(e,t){return{project:e.project,commit:e.commit,stage:e.stage,...e.wave!==void 0?{wave:e.wave}:{},...e.share!==void 0?{share:e.share}:{},finished:e.finished,roots:e.roots,...e.changed!==void 0?{changed:e.changed}:{},...e.failed!==void 0?{failed:e.failed}:{},refused:e.refused,totals:e.totals,destroys:e.destroys_total??e.destroys.length,...e.approval?{approval:e.approval}:{},...e.waiting_since?{waiting_since:e.waiting_since}:{},...e.applied?{applied:e.applied}:{},...e.overridden?{overridden:e.overridden}:{},...t!==void 0?{report:`${t}${e.path}/report.html`}:{},...e.pull_request?{pull_request:e.pull_request}:{},...e.pull_request_url?{pull_request_url:e.pull_request_url}:{},...e.job_url?{job_url:e.job_url}:{}}}var DK=e=>e===void 0||e===""||e.endsWith("/")?e:`${e}/`,b0=e=>e.reduce((t,n)=>!t||Zc(n.finished)>Zc(t.finished)?n:t,void 0);function nke(e,t){let n=e.roots.map(o=>({root:o.root,commit:o.commit,finished:o.finished,...o.wave!==void 0?{wave:o.wave}:{},...t!==void 0?{report:`${t}${o.path}/report.html`}:{},...o.environment?{environment:o.environment}:{},resources:o.resources.map(s=>({address:s.address,type:s.type,provider:s.provider}))})),r=n.flatMap(o=>o.resources);return{resources:r.length,types:
|
|
985
|
+
</table></div>`}var Rk={deployments:sn.doraDeployments,leadTime:sn.doraLeadTime,failureRate:sn.doraFailureRate,restore:sn.doraRestore};function CK(e){let t=[],n=(r,o)=>{let s={project:r};t.push({name:Rk.deployments,unit:"",description:`Applied waves per week over the newest ${e.weeks} weeks.`,value:o.per_week,attributes:s});let i=o.lead_time;for(let[a,c]of[["total",i.median_seconds],["before_gate",i.before_gate_seconds],["at_gate",i.at_gate_seconds],["after_gate",i.after_gate_seconds]])c!==null&&t.push({name:Rk.leadTime,unit:"s",description:"Median lead time from a change's first plan to its wave applied, and its split.",value:c,attributes:{...s,segment:a}});o.change_failure.rate!==null&&t.push({name:Rk.failureRate,unit:"",description:"Failed applies and applied waves followed by drift, over all applies.",value:o.change_failure.rate,attributes:s}),o.restore.median_seconds!==null&&t.push({name:Rk.restore,unit:"s",description:"Median time from a failed apply or drift found to restored.",value:o.restore.median_seconds,attributes:s})};n("*",e.estate);for(let r of e.projects)n(r.project,r);return t}var eke="terragucci.estate/v1",tke=20,Zc=e=>Date.parse(e)||0,v0=(e,t)=>Math.max(0,Math.round((t.getTime()-Zc(e))/1e3));function xk(e,t){return{project:e.project,commit:e.commit,stage:e.stage,...e.wave!==void 0?{wave:e.wave}:{},...e.share!==void 0?{share:e.share}:{},finished:e.finished,roots:e.roots,...e.changed!==void 0?{changed:e.changed}:{},...e.failed!==void 0?{failed:e.failed}:{},refused:e.refused,totals:e.totals,destroys:e.destroys_total??e.destroys.length,...e.approval?{approval:e.approval}:{},...e.waiting_since?{waiting_since:e.waiting_since}:{},...e.applied?{applied:e.applied}:{},...e.overridden?{overridden:e.overridden}:{},...t!==void 0?{report:`${t}${e.path}/report.html`}:{},...e.pull_request?{pull_request:e.pull_request}:{},...e.pull_request_url?{pull_request_url:e.pull_request_url}:{},...e.job_url?{job_url:e.job_url}:{}}}var DK=e=>e===void 0||e===""||e.endsWith("/")?e:`${e}/`,b0=e=>e.reduce((t,n)=>!t||Zc(n.finished)>Zc(t.finished)?n:t,void 0);function nke(e,t){let n=e.roots.map(o=>({root:o.root,commit:o.commit,finished:o.finished,...o.wave!==void 0?{wave:o.wave}:{},...t!==void 0?{report:`${t}${o.path}/report.html`}:{},...o.environment?{environment:o.environment}:{},resources:o.resources.map(s=>({address:s.address,type:s.type,provider:s.provider}))})),r=n.flatMap(o=>o.resources);return{resources:r.length,types:ML(r),roots:n}}function rke(e,t){return e.roots.map(n=>({root:n.root,backend:n.backend,...n.location!==void 0?{location:n.location}:{},versioning:n.versioning,...n.note!==void 0?{note:n.note}:{},checked:n.checked,versions:n.versions.map(r=>({version_id:r.version_id,commit:r.commit,finished:r.finished,...r.wave!==void 0?{wave:r.wave}:{},...t!==void 0?{report:`${t}${r.path}/report.html`}:{}}))}))}function oke(e,t){let n=r=>{if(!r)return;let{path:o,...s}=r;return{...s,...t!==void 0?{report:`${t}${o}/report.html`}:{}}};return VE(e).map(r=>{let o=n(r.consumer_planned),s=n(r.producer_applied);return{consumer:r.consumer,producer:r.producer,via:r.via,...o?{consumer_planned:o}:{},...s?{producer_applied:s}:{},status:r.status}})}function ske(e,t){let n=DK(e.base),r=n!==void 0?{index:`${n}index.html`}:{};if(e.error!==void 0)return{project:e.project,status:"error",error:e.error,...r,waiting:[],drifted:0,failed:0};let o=e.ephemeral&&e.ephemeral.project===e.project&&e.ephemeral.environments.length>0?{ephemeral:e.ephemeral.environments}:{};if(!e.reports)return{project:e.project,status:"no-index",waiting:[],drifted:0,failed:0,...o};let s=e.reports.filter(g=>g.project===e.project),i=b0(s.filter(g=>g.stage==="tf-plan")),a=b0(s.filter(g=>g.stage==="tf-drift")),c=s.filter(g=>g.stage==="tf-apply"),u=b0(c),l,d=[];if(u){let g=new Map;for(let h of c.filter(y=>y.commit===u.commit)){let y=`${h.wave??0}/${h.share??0}`,w=g.get(y);(!w||Zc(h.finished)>Zc(w.finished))&&g.set(y,h)}let m=[...g.values()].sort((h,y)=>(h.wave??0)-(y.wave??0)||(h.share??0)-(y.share??0));l={commit:u.commit,waves:m.map(h=>xk(h,n))};for(let h of m){if(h.approval!=="waiting")continue;let y=h.waiting_since??h.finished;d.push({project:e.project,wave:h.wave??0,commit:h.commit,since:y,age_seconds:v0(y,t),...n!==void 0?{report:`${n}${h.path}/report.html`}:{}})}}let p=g=>g?.failed??0,f=l?l.waves.reduce((g,m)=>g+(m.overridden??0),0):0;return{project:e.project,status:"ok",...r,...i?{plan:xk(i,n)}:{},...a?{drift:xk(a,n)}:{},...l?{apply:l}:{},waiting:d,drifted:a?.changed??0,failed:p(i)+p(a)+(l?l.waves.reduce((g,m)=>g+(m.failed??0),0):0),...f>0?{overridden:f}:{},...e.inventory?{inventory:nke(e.inventory,n)}:{},...e.states?{states:rke(e.states,n)}:{},...e.run?{run_view:{commit:e.run.commit,updated:e.run.updated,...n!==void 0?{page:`${n}runs/${e.run.commit}/run.html`}:{}}}:{},...ike(e.run),...e.edges&&VE(e.edges).length>0?{edges:oke(e.edges,n)}:{},...o}}function ike(e){let t=(e?.waves??[]).filter(n=>n.state==="applying"&&n.progress).map(n=>{let r=Ug(n.progress);return{wave:n.number,read:n.progress.read,total:n.progress.resources.length,done:r.done,in_flight:r["in-flight"],waiting:r.waiting}});return t.length?{applying:t}:{}}var ake=(e,t)=>e.key===t.key&&(!e.bucket||!t.bucket||e.bucket===t.bucket);function cke(e){let t=e.filter(s=>s.reports&&s.error===void 0&&s.run&&s.run.project===s.project).map(s=>s.run);if(t.length===0)return;let n=t.flatMap(s=>s.roots.map(i=>({project:s.project,root:i.root,wave:i.wave}))),r=[];for(let s of t)for(let i of s.roots)for(let a of i.reads)r.push({from:{project:s.project,root:a},to:{project:s.project,root:i.root}});let o=t.flatMap(s=>s.roots.filter(i=>i.state).map(i=>({project:s.project,root:i.root,state:i.state})));for(let s of t)for(let i of s.roots)for(let a of i.external??[])for(let c of o)c.project===s.project||!ake(c.state,a)||r.some(u=>u.from.project===c.project&&u.from.root===c.root&&u.to.project===s.project&&u.to.root===i.root)||r.push({from:{project:c.project,root:c.root},to:{project:s.project,root:i.root}});return{nodes:n,edges:r}}function MK(e,t=new Date){let n=e.map(a=>ske(a,t)),r=n.reduce((a,c)=>a+(c.overridden??0),0),o=n.filter(a=>a.inventory),s=n.reduce((a,c)=>a+(c.ephemeral?.length??0),0),i=e.flatMap(a=>(a.reports??[]).filter(c=>c.project===a.project).map(c=>({r:c,base:DK(a.base)}))).sort((a,c)=>Zc(c.r.finished)-Zc(a.r.finished)||(a.r.path<c.r.path?-1:1)).slice(0,tke).map(({r:a,base:c})=>xk(a,c));return{schema:eke,generated:t.toISOString(),totals:{projects:n.length,waiting:n.reduce((a,c)=>a+c.waiting.length,0),drifted_projects:n.filter(a=>a.drifted>0).length,drifted_roots:n.reduce((a,c)=>a+c.drifted,0),failed_roots:n.reduce((a,c)=>a+c.failed,0),unreadable:n.filter(a=>a.status==="error").length,...r>0?{overridden_roots:r}:{},...o.length>0?{resources:o.reduce((a,c)=>a+c.inventory.resources,0)}:{},...s>0?{ephemeral:s}:{}},projects:n,recent:i,...(()=>{let a=cke(e);return a?{graph:a}:{}})()}}function Lp(e){let t=Math.floor(e/86400),n=Math.floor(e%86400/3600),r=Math.floor(e%3600/60);return t>0?`${t}d ${n}h`:n>0?`${n}h ${r}m`:`${r}m`}var LK=e=>{let t=e.totals;return`+${t.create} ~${t.update} -/+${t.replace} -${t.delete}`},On=(e,t)=>e?`<a href="${T(e)}">${t}</a>`:t,Gp=(e,t)=>`<time datetime="${T(e)}">${T(Lp(v0(e,t)))} ago</time>`,GK=(e,t)=>`<time datetime="${T(e)}" data-for>${T(Lp(v0(e,t)))}</time>`,ja=e=>`<code>${T(e.slice(0,12))}</code>`;function NK(e,t,n=!1){if(!e)return'<td class="none">none yet</td>';let r=[n?`${e.changed??0} of ${e.roots} roots drifted`:`${e.roots} roots, ${LK(e)}`];return e.failed&&r.push(`<span class="bad">${e.failed} failed</span>`),e.refused&&r.push(`${e.refused} refused`),`<td${n&&(e.changed??0)>0?' class="warn"':""}>${On(e.report,r.join(", "))}<br><small>${ja(e.commit)} ${Gp(e.finished,t)}</small></td>`}function uke(e,t){if(!e.apply)return'<td class="none">none yet</td>';let n=e.apply.waves.map(o=>{let s=o.approval==="waiting"?`<span class="warn">waiting ${GK(o.waiting_since??o.finished,t)}</span>`:o.failed?`<span class="bad">${o.failed} failed</span>`:o.applied?"applied":o.approval??"ran";return`<li>${On(o.report,`wave ${o.wave??0}${o.share!==void 0?`, share ${o.share}`:""}`)}: ${s}${o.overridden?`, <span class="warn">${o.overridden} by policy override</span>`:""}</li>`}),r=e.run_view?.page&&e.run_view.commit===e.apply.commit?` ${On(e.run_view.page,"run view")}`:"";return`<td><ul>${n.join("")}</ul>${lke(e)}<small>${ja(e.apply.commit)}${r}</small></td>`}function lke(e){return(e.applying??[]).map(t=>{let n=(o,s)=>o>0?`<span class="${s}" style="flex:${o}"></span>`:"",r=`<span class="pbar">${n(t.done,"p-done")}${n(t.in_flight,"p-in-flight")}${n(t.waiting,"p-waiting")}</span>`;return`<div class="applying" data-wave="${t.wave}" data-done="${t.done}" data-in-flight="${t.in_flight}" data-waiting="${t.waiting}">${On(e.run_view?.page,`wave ${t.wave}`)} applying: ${t.done} of ${t.total} done, ${t.in_flight} in flight, ${t.waiting} waiting${r}</div>`}).join("")}var dke=e=>e.replace(/^registry\.(terraform\.io|opentofu\.org)\//,"");function pke(e,t){if(e.error!==void 0)return`<small class="versions" data-versions="error" title="${T(e.error)}">versions not listed</small>`;if(!e.kept)return'<small class="versions" data-versions="none">no past versions kept</small>';let n=e.count??0;return`<small class="versions" data-versions="${n}">${On(t,`${n} ${n===1?"version":"versions"}`)}</small>`}function fke(e,t){let n=e.projects.filter(o=>o.inventory);return n.length===0?'<p class="none">No apply has recorded its resources yet.</p>':`<p><input type="search" id="resources-filter" placeholder="Filter by root, address, type or provider" aria-label="Filter resources" hidden></p>
|
|
986
986
|
${n.map(o=>{let s=o.inventory,i=s.types.map(c=>`<code>${T(c.type)}</code> ${c.count}`).join(", "),a=s.roots.map(c=>{let u=`<tr class="head"><th colspan="3"><code>${T(c.root)}</code>${c.environment?` <span class="env" data-environment="${T(c.environment)}">${T(c.environment)}</span>`:""}: ${c.resources.length} ${c.resources.length===1?"resource":"resources"}, ${On(c.report,`${c.wave!==void 0?`wave ${c.wave}`:"applied"}`)} ${ja(c.commit)} ${Gp(c.finished,t)}</th></tr>`,l=c.resources.map(d=>`<tr data-r="${T(`${c.root}${c.environment?` ${c.environment}`:""} ${d.address} ${d.type} ${d.provider}`.toLowerCase())}"><td>${On(d.history,`<code>${T(d.address)}</code>`)}${d.record_versions?` ${pke(d.record_versions,d.history)}`:""}</td><td><code>${T(d.type)}</code></td><td>${T(dke(d.provider))}</td></tr>`);return`<tbody class="inv" data-root="${T(c.root)}">${u}${l.join("")}</tbody>`});return`<h3>${On(o.index,T(o.project))}: ${s.resources} ${s.resources===1?"resource":"resources"} in ${s.roots.length} ${s.roots.length===1?"root":"roots"}</h3>
|
|
987
987
|
<p class="types">${i}</p>
|
|
988
988
|
<div class="scroll"><table class="resources"><thead><tr><th>Address</th><th>Type</th><th>Provider</th></tr></thead>${a.join(`
|
|
@@ -1080,7 +1080,7 @@ ${s.join(`
|
|
|
1080
1080
|
`),s=n.get(o);return s||(s=dr(r,e,t),n.set(o,s)),s}}async function Kke(e,t,n,r){if(!t?.bucket)return{project:e,error:"the project names no reports bucket"};let o=Zu(t),s=P0(t,n)?`${e}/`:o?`${o}/${e}/`:void 0,i=Up(t.prefix??"",e,"index.json"),a;try{let g=await r(t).get(i);a=g===void 0?void 0:Dl(g,i)}catch(g){if(!(g instanceof Y)&&!(g instanceof TypeError))throw g;return{project:e,error:g.message,...s!==void 0?{base:s}:{}}}let c=a?await N0(r(t),Jg(e,t.prefix??"")):void 0,u=a?await I0(r(t),Zg(e,t.prefix??"")):void 0,l=a?await Yke(r(t),Kg(e,t.prefix??"")):void 0,d=a?await j0(r(t),Vg(e,t.prefix??"")):void 0,p=a?await Vke(r(t),e,a,t.prefix??""):void 0,f=await Zke(r(t),eO(e,t.prefix??""));return{project:e,...a?{reports:a}:{},...f?{ephemeral:f}:{},...c?{inventory:c}:{},...u?{changes:u}:{},...l?{states:l}:{},...d?{edges:d}:{},...p?{run:p}:{},...s!==void 0?{base:s}:{}}}async function Vke(e,t,n,r){let o=n.filter(i=>i.project===t&&i.stage==="tf-apply");if(o.length===0)return;let s=o.reduce((i,a)=>(Date.parse(a.finished)||0)>(Date.parse(i.finished)||0)?a:i);try{let i=await e.get(`${Yg(t,s.commit,r)}/run.json`);if(i===void 0)return;let a=JSON.parse(i);return a.schema===rv&&Array.isArray(a.roots)&&Array.isArray(a.waves)?a:void 0}catch(i){if(i instanceof Y||i instanceof TypeError||i instanceof SyntaxError)return;throw i}}async function Zke(e,t){try{let n=await e.get(t);if(n===void 0)return;let r=JSON.parse(n);return r.schema===Qv&&Array.isArray(r.environments)?r:void 0}catch(n){if(n instanceof Y||n instanceof TypeError||n instanceof SyntaxError)return;throw n}}async function Yke(e,t){try{let n=await e.get(t);if(n===void 0)return;let r=Hg(n);return r.roots.length>0?r:void 0}catch(n){if(n instanceof Y||n instanceof TypeError)return;throw n}}async function j0(e,t){try{let n=await e.get(t);if(n===void 0)return;let r=KE(n);return r.roots.length>0?r:void 0}catch(n){if(n instanceof Y||n instanceof TypeError)return;throw n}}async function I0(e,t){try{let n=await e.get(t);if(n===void 0)return;let r=JSON.parse(n);return r.schema===Ig&&Array.isArray(r.changes)?r.changes:void 0}catch(n){if(n instanceof Y||n instanceof TypeError||n instanceof SyntaxError)return;throw n}}async function C0(e,t){try{let n=await e.get(Up(t,Yn.record));return n===void 0?void 0:Yc(n).entries}catch(n){if(n instanceof Y||n instanceof TypeError)return;throw n}}async function N0(e,t){try{let n=await e.get(t);if(n===void 0)return;let r=JSON.parse(n);return r.schema===Cg&&Array.isArray(r.roots)?r:void 0}catch(n){if(n instanceof Y||n instanceof TypeError||n instanceof SyntaxError)return;throw n}}async function D0(e,t,n,r="estate"){if(e.projects&&Object.keys(e.projects).length>0){if(t.reports)throw new S(`--bucket names one bucket; a control repo reads each project's reports and ${r==="estate"?"writes the page":"reads the audit trail"} under defaults.reports`);let c=e.defaults?.reports;return{projects:Object.keys(e.projects).map(u=>({project:u,reports:ot(e,u).reports})),...c?{out:c}:{}}}let o=t.reports??ue(e).reports;if(!o?.bucket)throw new S(`${r} reads the reports bucket: set reports.bucket in terragucci.yml, or pass --bucket with s3://<bucket>, gs://<bucket> or az://<account>/<container>`);let s=Up(o.prefix??"","index.json"),i=await n(o).get(s),a=i===void 0?[]:Dl(i,s);return{projects:[...new Set(a.map(c=>c.project))].sort().map(c=>({project:c,reports:o})),out:o}}async function Xke(e,t){try{let n=await e.get(Up(t,"audit.json"));if(n===void 0)return;let r=JSON.parse(n);return r.schema!=="terragucci.audit-summary/v1"||typeof r.entries!="number"||typeof r.generated!="string"?void 0:{page:"audit.html",entries:r.entries,generated:r.generated}}catch(n){if(n instanceof Y||n instanceof TypeError||n instanceof SyntaxError)return;throw n}}var Ak={json:"history.json",page:"history.html"};function Qke(e,t){let n=new Map(t.resources.map(r=>[r.id,r]));e.history={page:Ak.page,resources:t.resources.length,generated:t.generated};for(let r of e.projects)for(let o of r.inventory?.roots??[])for(let s of o.resources){let i=TE(r.project,o.root,s.address),a=n.get(i);if(!a)continue;s.history=`${Ak.page}#${i}`;let c=a.record_versions;c&&(s.record_versions=c.error!==void 0?{kept:!1,error:c.error}:c.kept?{kept:!0,count:c.versions?.length??0}:{kept:!1})}}async function eV(e,t,n={}){let r=n.env??process.env,o=n.now??new Date,s=n.linkSeconds??O0;if(!Number.isInteger(s)||s<1||s>Jn)throw new S(`the link lives 1 second to 7 days (${Jn} seconds); ${s} is out of range`);let i=Ml(r,n.fetch),{projects:a,out:c}=await D0(t,n,i),u=[];for(let b of a)u.push(await Kke(b.project,b.reports,c,i));let l=MK(u,o);if(c?.bucket){let b=await Xke(i(c),c.prefix??"");b&&(l.audit=b)}let d=c?.bucket?await C0(i(c),c.prefix??""):void 0,p=jK(u.map(b=>({project:b.project,rows:b.reports??[]})),d,o);l.dora={file:Th,generated:p.generated,deployments:p.estate.deployments};let f,g=u.filter(b=>b.changes&&b.changes.length>0);g.length>0&&(f=Pb(g.map(b=>({project:b.project,changes:b.changes,...b.base!==void 0?{base:b.base}:{}})),d,o),Qke(l,f));let m={"estate.json":JSON.stringify(l,null,2)+`
|
|
1081
1081
|
`,"estate.html":FK(l,p),[Th]:JSON.stringify(p,null,2)+`
|
|
1082
1082
|
`};f&&Object.assign(m,{[Ak.json]:JSON.stringify(f,null,2)+`
|
|
1083
|
-
`,[Ak.page]:
|
|
1083
|
+
`,[Ak.page]:IL(f)});let h=Jke(e,n.out??"terragucci-estate");Hke(h,{recursive:!0});for(let[b,k]of Object.entries(m))qke(XK(h,b),k);let y={estate:l,files:Object.keys(m).map(b=>XK(h,b)),unreadable:u.filter(b=>b.error!==void 0).map(b=>b.project),dora:p},w=ol(r);if(w?.metrics){let b=CK(p),k=await Am(w.metrics,zv(b,{...w.resource,"service.version":Pr},Pr),n.otlpFetch);y.metrics=k?{problem:k}:{sent:b.length}}if(c?.bucket){let b=i(c),k=[];for(let[$,_]of Object.entries(m)){let E=Up(c.prefix??"",$);await b.put(E,_,$.endsWith(".html")?"text/html; charset=utf-8":"application/json"),k.push(E)}y.uploaded={bucket:c.bucket,keys:k};let v=await b.presign(Up(c.prefix??"","estate.html"),s,o);y.link={url:v.url,expires:v.expires.toISOString()}}return y}var zp=(e,t,n)=>`${e} ${e===1?t:n}`,QK=e=>e.length<3?e.join(" and "):`${e.slice(0,-1).join(", ")} and ${e[e.length-1]}`;function tV(e,t){let n=e.estate.totals,r=[`estate: ${zp(n.projects,"project","projects")}, ${zp(n.waiting,"wave","waves")} waiting, ${zp(n.drifted_projects,"project","projects")} drifted, ${zp(n.failed_roots,"root","roots")} failed`];for(let i of e.estate.projects){for(let a of i.waiting)r.push(` ${i.project}: wave ${a.wave} waits, for ${Lp(a.age_seconds)}`);i.drifted>0&&r.push(` ${i.project}: ${zp(i.drifted,"root","roots")} drifted`),i.status==="error"&&r.push(` ${i.project}: the index could not be read: ${i.error}`)}let o=e.dora,s=o.estate.change_failure.rate;return r.push(`dora: ${o.estate.per_week} deployments per week, lead time ${Cs(o.estate.lead_time.median_seconds)}, change failure rate ${s===null?"none":`${Math.round(s*1e3)/10}%`}, time to restore ${Cs(o.estate.restore.median_seconds)}`),e.metrics&&r.push("sent"in e.metrics?`sent ${zp(e.metrics.sent,"DORA gauge","DORA gauges")}`:`the DORA gauges were not sent: ${e.metrics.problem}`),r.push(`wrote ${QK(e.files.map(i=>i.startsWith(t+"/")?i.slice(t.length+1):i))}`),e.uploaded&&r.push(`copied to ${Bp(e.uploaded.bucket)}/${QK(e.uploaded.keys)}`),e.link&&r.push(`link, until ${e.link.expires}:`,e.link.url),r.join(`
|
|
1084
1084
|
`)}import{spawnSync as e_e}from"node:child_process";import{mkdirSync as t_e,mkdtempSync as n_e,rmSync as Ok,writeFileSync as r_e}from"node:fs";import{tmpdir as o_e}from"node:os";import{join as L0,resolve as s_e}from"node:path";var i_e=e=>e.replace(/^\/+|\/+$/g,""),Ch=(...e)=>e.map(i_e).filter(Boolean).join("/");function nV(e,t,n){let r,o;try{let a=po(e);r=a.path,o=a.host}catch{return()=>{}}let s=n??ir(o),i=t&&/^https?:\/\//.test(t)?t.replace(/\/+$/,""):`https://${o}`;return s?a=>`${i}/${r}/${s==="gitlab"?"-/":""}commit/${a}`:()=>{}}function a_e(e,t,n,r){if(t.projects&&Object.keys(t.projects).length>0){if(n.reports)throw new S("--bucket names one bucket; a control repo reads each project's reports and writes the audit trail under defaults.reports");let a=Object.keys(t.projects).map(u=>{let l=ot(t,u),d=l.forge??(()=>{try{return ir(po(u).host)}catch{return}})(),{cloneUrl:p,origin:f}=yh(u,l.url),g=l.token_env??(d?Gn[d]:void 0),m=g?r[g]:void 0;return{project:u,...l.reports?{reports:l.reports}:{},ledger:{kind:"remote",url:p,...m?{token:m}:{}},commitUrl:nV(u,f,d)}}),c=t.defaults?.reports;return{projects:a,...c?{out:c}:{}}}let o=ue(t),s=n.reports??o.reports;if(!s?.bucket)throw new S("audit writes to the reports bucket: set reports.bucket in terragucci.yml, or pass --bucket with s3://<bucket>, gs://<bucket> or az://<account>/<container>");let i=zn(e,r,o.forge).project;return{projects:[{project:i,reports:s,ledger:{kind:"checkout",dir:e},commitUrl:nV(i,o.url,o.forge)}],out:s}}function Wp(e,t,n){let r=e_e("git",["-C",e,...t],{encoding:"utf-8",maxBuffer:268435456}),o=s=>n?s.split(n).join("***"):s;return{ok:r.status===0,out:r.stdout??"",err:o((r.stderr??"").trim()||(r.error?.message??""))}}function c_e(e){let t,n,r,o;if(e.ledger.kind==="checkout"){t=e.ledger.dir,n=`refs/remotes/origin/${kn}`;let s=Wp(t,["ls-remote","--heads","origin",kn]);if(!s.ok)throw new Error(`cannot read ${kn} from origin: ${s.err}`);if(!s.out.trim())return"none";let i=Wp(t,["fetch","-q","origin",`+refs/heads/${kn}:${n}`]);if(!i.ok)throw new Error(`cannot fetch ${kn}: ${i.err}`)}else{r=n_e(L0(o_e(),"terragucci-audit-")),t=r,n=`refs/heads/${kn}`,o=e.ledger.token;let s=wh(e.ledger.url,o);Wp(t,["init","-q","--bare"]);let i=Wp(t,["ls-remote","--heads",s,kn],o);if(!i.ok)throw Ok(r,{recursive:!0,force:!0}),new Error(`cannot read ${kn} from ${e.ledger.url}: ${i.err}`);if(!i.out.trim())return Ok(r,{recursive:!0,force:!0}),"none";let a=Wp(t,["fetch","-q",s,`+refs/heads/${kn}:${n}`],o);if(!a.ok)throw Ok(r,{recursive:!0,force:!0}),new Error(`cannot fetch ${kn} from ${e.ledger.url}: ${a.err}`)}try{let s=[];for(let i of[BK,S0,WK,Ah,HK,Oh,qK,Ph,JK,jh]){let a=Wp(t,["-c","core.quotepath=off","log","--reverse","--no-color","--no-ext-diff","--no-renames","--format=%x1e%H%x1f%an%x1f%aI","-p","--unified=0",n,"--",i]);if(!a.ok)throw new Error(`cannot read the history of ${i} on ${kn}: ${a.err}`);s.push(...KK(e.project,i,UK(a.out),e.commitUrl))}return s}finally{r&&Ok(r,{recursive:!0,force:!0})}}async function u_e(e,t,n,r,o){let s=e.reports;if(!s?.bucket)throw new Y("the project names no reports bucket");let i=n(s),a=Ch(s.prefix??"",e.project,"index.json"),c=await i.get(a);if(c===void 0)return{entries:[],read:0};let u=Dl(c,a).filter(g=>g.project===e.project&&g.stage==="tf-apply"),l=Zu(s),d=P0(s,t)?`${e.project}/`:l?`${l}/${e.project}/`:void 0,p=[],f=0;for(let g of u){if(r.has(R0(e.project,g.path,g.finished)))continue;let m=Ch(s.prefix??"",e.project,g.path,"report.json"),h=await i.get(m);if(h===void 0)continue;let y;try{y=JSON.parse(h)}catch{continue}f++;let w=VK(y,g.path,{source:"report",bucket:Bp(s.bucket),key:m,...d!==void 0?{url:`${d}${g.path}/report.html`}:{}},o);w&&p.push(w)}return{entries:p,read:f}}async function l_e(e,t,n){for(let r=1;;r++){let o=await e.read(t),s=Yc(o.body),i=x0(s,n);if(i.length===0&&o.body!==void 0)return{record:s,added:i};let a=o.etag?{ifMatch:o.etag}:o.body===void 0?{ifNoneMatch:"*"}:void 0;try{let c=E0(s,i);return await e.put(t,c,"application/x-ndjson",a),{record:Yc(c),added:i}}catch(c){if(!(c instanceof Er))throw c;if(r>=zd)throw new Y(`${t} changed under this run ${zd} times in a row; nothing was appended`);await new Promise(u=>setTimeout(u,100*r))}}}async function oV(e,t,n={}){let r=n.env??process.env,o=n.now??new Date,s=n.linkSeconds??O0;if(!Number.isInteger(s)||s<1||s>Jn)throw new S(`the link lives 1 second to 7 days (${Jn} seconds); ${s} is out of range`);let i=Ml(r,n.fetch),{projects:a,out:c}=a_e(e,t,n,r);if(!c?.bucket)throw new S("audit writes to the reports bucket: set defaults.reports.bucket in the control repo's terragucci.yml");let u=i(c),l=Ch(c.prefix??"",Yn.record),d=Yc((await u.read(l)).body),p=[],f=[];for(let $ of a){let _={project:$.project,ledger:"read",reports:0},E=[];try{let x=c_e($);x==="none"?_.ledger="none":(E=x,f.push(...x))}catch(x){_.ledger="error",_.error=x.message}let R=new Set(d.ids);try{let x=await u_e($,c,i,R,[...d.entries,...E]);f.push(...x.entries),_.reports=x.read}catch(x){if(!(x instanceof Y)&&!(x instanceof TypeError))throw x;_.error=_.error?`${_.error}; ${x.message}`:`the index could not be read: ${x.message}`}p.push(_)}let g=p.filter($=>$.error!==void 0).map($=>$.project);if(n.check){let $=x0(d,f);return{summary:T0(d.entries,0,p,o),added:$,files:[],unreadable:g}}let{record:m,added:h}=await l_e(u,l,f),y=T0(m.entries,h.length,p,o),w={[Yn.record]:E0(m,[]),[Yn.page]:YK(y,m.entries),[Yn.summary]:JSON.stringify(y,null,2)+`
|
|
1085
1085
|
`},b=s_e(e,n.out??"terragucci-audit");t_e(b,{recursive:!0});for(let[$,_]of Object.entries(w))r_e(L0(b,$),_);let k=[l];for(let $ of[Yn.page,Yn.summary]){let _=Ch(c.prefix??"",$);await u.put(_,w[$],$.endsWith(".html")?"text/html; charset=utf-8":"application/json"),k.push(_)}let v=await u.presign(Ch(c.prefix??"",Yn.page),s,o);return{summary:y,added:h,files:Object.keys(w).map($=>L0(b,$)),uploaded:{bucket:c.bucket,keys:k},link:{url:v.url,expires:v.expires.toISOString()},unreadable:g}}var M0=(e,t,n)=>`${e} ${e===1?t:n}`,rV=e=>` ${e.at} ${e.project} ${e.kind} ${e.what}${e.who?` by ${e.who}`:""}${e.digest?` ${e.digest}`:""}: ${e.result}`;function sV(e,t,n=!1){let r=[];if(n){r.push(e.added.length===0?`audit: the record holds every entry the sources hold (${M0(e.summary.entries,"entry","entries")})`:`audit: the record lacks ${M0(e.added.length,"entry","entries")} the sources hold:`);for(let o of e.added)r.push(rV(o))}else{r.push(`audit: ${M0(e.summary.entries,"entry","entries")}, ${e.added.length} added`);for(let o of e.added)r.push(rV(o))}for(let o of e.summary.projects)o.ledger==="error"?r.push(` ${o.project}: the ledger could not be read: ${o.error}`):o.error?r.push(` ${o.project}: ${o.error}`):o.ledger==="none"&&r.push(` ${o.project}: no ${kn} yet`);return e.files.length&&r.push(`wrote ${e.files.map(o=>o.startsWith(t+"/")?o.slice(t.length+1):o).join(", ")}`),e.uploaded&&r.push(`copied to ${Bp(e.uploaded.bucket)}/${e.uploaded.keys.join(", ")}`),e.link&&r.push(`link, until ${e.link.expires}:`,e.link.url),r.join(`
|
|
1086
1086
|
`)}import{existsSync as d_e,readFileSync as aV,unlinkSync as p_e,writeFileSync as iV}from"node:fs";var Nh=e=>`'${e.replace(/'/g,"'\\''")}'`;function f_e(e){let t=e.AWS_ENDPOINT_URL_STS||e.AWS_ENDPOINT_URL;if(t)return t.replace(/\/+$/,"");let n=e.AWS_REGION||e.AWS_DEFAULT_REGION||"us-east-1";return`https://sts.${n}.amazonaws.com${n.startsWith("cn-")?".cn":""}`}function g_e(e,t){let n=[];try{n=JSON.parse(e[Au]||"[]")}catch{throw new Y(`${Au} is not JSON`)}let r=[e.AWS_ROLE_ARN,...jd(e).map(([,o])=>o),...n.map(([,o])=>o),t];return[...new Set(r.filter(o=>!!o))]}function cV(e){try{let t=JSON.parse(aV(e,"utf-8")),n=/\/serviceAccounts\/([^/:]+):generateAccessToken$/.exec(t.service_account_impersonation_url??"");return n?decodeURIComponent(n[1]):void 0}catch{return}}function m_e(e){try{return JSON.parse(aV(e,"utf-8")).credential_source?.file}catch{return}}async function h_e(e,t={}){let n=t.fetchFn??fetch;if(e.ARM_OIDC_TOKEN_FILE_PATH)throw new Y("oidc-trade does not trade for oidc.azure: the azurerm provider and backend take only the OIDC token itself. A re-plan with oidc.azure plans with the job's token and has no trade step");let r={aws:{},gcp:{}},o=g_e(e,t.reportsRole);if(o.length>0){let c=e.AWS_WEB_IDENTITY_TOKEN_FILE;if(!c)throw new Y(`the re-plan takes ${o.join(", ")}, and AWS_WEB_IDENTITY_TOKEN_FILE is not set: the job has no OIDC token to trade`);let u=f_e(e),l=e.AWS_ROLE_SESSION_NAME||"terragucci-plan";for(let d of o){let p=await Bf({roleArn:d,tokenFile:c,sessionName:l,endpoint:u},n);r.aws[d]={accessKeyId:p.accessKeyId,secretAccessKey:p.secretAccessKey,...p.sessionToken?{sessionToken:p.sessionToken}:{},...p.expiration?{expiration:p.expiration.toISOString()}:{}}}}let s=e.GOOGLE_APPLICATION_CREDENTIALS,i=s?cV(s):void 0,a=[...new Set([i,...jd(e,na).map(([,c])=>c)].filter(c=>!!c))];for(let c of a){let u=hE(e,c);u!==s&&t.written?.push(u),r.gcp[c]=await new Za({bucket:"",endpoint:"",credentialsFile:u},n).identityToken()}return r}function y_e(e){let t=e.GOOGLE_APPLICATION_CREDENTIALS;return[e.AWS_WEB_IDENTITY_TOKEN_FILE,e.TERRAGUCCI_GCP_TOKEN_FILE,t,t?m_e(t):void 0].filter(n=>!!n)}function w_e(e,t,n){let r=`${e}.json`;iV(r,JSON.stringify(t)+`
|
|
@@ -1298,9 +1298,9 @@ Exit codes: 0 done; 1 one or more projects or roots failed; 2 a usage or config
|
|
|
1298
1298
|
|
|
1299
1299
|
init, reconcile, plan, stage, rollout, respond, config check, config identities, forge check and query take --json: one envelope on stdout.
|
|
1300
1300
|
|
|
1301
|
-
Docs: https://intentius.io/terragucci/`;async function X9(e){try{let t=fe(e);if(!t)return{};let n=et(e,ue(await Re(t)));return{...n.after?{after:n.after}:{},...n.kind==="synth"?{synth:!0}:{}}}catch{return{}}}function Q9(e){if(e!==void 0&&!Do.includes(e))throw new S(`--approval must be one of ${Do.join(", ")}`);return e}function eY(e,t){let n=e===void 0?void 0:e==="none"?[]:e.split(",");if(n?.some(r=>!Cn.includes(r)))throw new S(`${t}'s --requires is a comma-separated list of ${Cn.join(", ")}, or none`);return n}function tY(e){let t=Number(e);if(!Number.isInteger(t)||t<1)throw new S(`--parallelism is ${JSON.stringify(e)}; use a whole number of 1 or more`);return t}function BI(e,t){let n=Number(t);if(!Number.isInteger(n)||n<1)throw new S(`--${e} is ${JSON.stringify(t)}; use a whole number of 1 or more`);return n}function FOe(e){let[t="",...n]=e,r={},o=[];for(let s=0;s<n.length;s++){let i=n[s];if(i.startsWith("--")){let[a,c]=i.slice(2).split("=",2);c!==void 0?r[a]=c:n[s+1]!==void 0&&!n[s+1].startsWith("--")&&!["force","dry-run","json","terragrunt","rest","again","poll","plan-notes","no-cost","check","approval-status","review","stand-down","publish","no-open","units","self-hosted","no-roots","require-clouds"].includes(a)?r[a]=n[++s]:r[a]=!0}else o.push(i)}return{cmd:t,flags:r,args:o}}function zOe(e){if(e!=="wait"&&e!=="refuse")throw new S("--on-held must be wait or refuse");return e}function P(e,t){let n=e[t];if(n===!0)throw new S(`--${t} needs a value`);return n}function UOe(e,t){let n=new Set(["aws"]),r=e.projects?Object.entries(e.projects).map(([o,s])=>({host:po(o).host,s:{...e.defaults,...s}})):[{host:void 0,s:e}];for(let{host:o,s}of r){let i=s.forge??(o?ir(o):t?Kr(t)?.value:void 0);i&&n.add(i)}return[...n]}async function nY(e){let{cmd:t,flags:n,args:r}=FOe(e),o=process.cwd(),s=n.json===!0,i=a=>(console.log(JSON.stringify(a,null,2)),a.exit);try{if(s&&!N2.includes(t))throw new S(`--json is not available on ${t||"help"}`);switch(t){case"init":{let a=P(n,"forge"),c=P(n,"binary");if(a&&!Wr.includes(a))throw new S(`--forge must be one of ${Wr.join(", ")}`);if(c&&!od.includes(c))throw new S(`--binary must be one of ${od.join(", ")}`);let u=Q9(P(n,"approval")),l=P(n,"signer"),d=await ik(o,{forge:a,binary:c,...u?{approval:u}:{},...l?{signer:l}:{},force:n.force===!0,dryRun:n["dry-run"]===!0});return s?i(Xo("init",0,BJ(o,d,n["dry-run"]===!0))):(console.log(UJ(o,d,n["dry-run"]===!0)),n["dry-run"]===!0&&console.log("dry run: nothing was written"),0)}case"import":{let[a,c,u]=r;if(gK.includes(a)){if(c!==void 0)throw new S(`import ${a} reads the platform's API, not a file: give --hostname${a==="scalr"?"":" and --organization"}`);let f=P(n,"forge");if(f&&!Wr.includes(f))throw new S(`--forge must be one of ${Wr.join(", ")}`);let g=P(n,"hostname")??(a==="hcp"?"app.terraform.io":void 0);if(!g||!/^[a-z0-9]([a-z0-9.-]*[a-z0-9])?(:\d+)?$/i.test(g))throw new S(`import ${a} needs --hostname, the host name of ${a==="scalr"?"the Scalr account, such as acme.scalr.io":"the OTF server"}`);let m=P(n,"organization");if(a!=="scalr"&&!m)throw new S(`import ${a} needs --organization, the organization whose workspaces it reads`);if(a==="scalr"&&m)throw new S("import scalr reads the account the host names; --environment narrows it to one environment");let h=P(n,"environment");if(a!=="scalr"&&h)throw new S(`--environment is Scalr's; import ${a} reads one --organization`);let y=P(n,"repo");if(y!==void 0&&!/^[^/\s]+(\/[^/\s]+)+$/.test(y))throw new S("--repo must be the repo as the VCS connection names it, owner/name");let w=await mK(o,()=>a==="scalr"?lK(g,h,process.env):cK(a,g,m,process.env),{...y?{repo:y}:{},...f?{forge:f}:{},force:n.force===!0,dryRun:n["dry-run"]===!0});return console.log(hK(w)),0}if(!p0.includes(a))throw new S(`import reads ${p0.join(", ")}: \`terragucci import atlantis [atlantis.yaml]\`, \`terragucci import digger [digger.yml]\`, \`terragucci import terragrunt-scale [.gruntwork]\`, \`terragucci import spacelift [.spacelift/config.yml]\` or \`terragucci import env0 [env0-discovery.yml]\``);if(u!==void 0)throw new S("import reads one file");let l=P(n,"forge");if(l&&!Wr.includes(l))throw new S(`--forge must be one of ${Wr.join(", ")}`);let d=P(n,"apply-when");if(d&&!id.includes(d))throw new S(`--apply-when must be one of ${id.join(", ")}`);let p=iK(o,a,{...c?{file:c}:{},...l?{forge:l}:{},...d?{applyWhen:d}:{},force:n.force===!0,dryRun:n["dry-run"]===!0});return console.log(aK(p)),0}case"reconcile":{let a=P(n,"config")??fe(o);if(!a)throw new S("reconcile needs --config <file>");let c=cd(P(n,"mode")??"dry-run"),u=await HJ(await Re(Oe(a)),{mode:c,project:P(n,"project")}),l=u.some(d=>d.status==="failed")?1:0;return s?i(Xo("reconcile",l,{mode:c,projects:u})):(console.log(qJ(u,c)),l)}case"generate":{let a=P(n,"config")??fe(o),c=ue(a?await Re(Oe(a)):{});if(n.check===!0){let d=EB(o,c);return xp(o,d),d.ok?0:1}let u=qm(o,c);if(u.foreign.length)throw new S(u.foreign.join("; "));let l=n["dry-run"]===!0;if(!l)for(let d of u.files)d.status!=="unchanged"&&(d.status==="removed"?LOe(d.path):V9(d.path,d.content));return console.log(TB(o,u,l)),0}case"estate":{let a=P(n,"config")??fe(o),c=P(n,"link-hours"),u=c===void 0?void 0:Math.round(Number(c)*3600);if(c!==void 0&&!(Number(c)>0))throw new S(`--link-hours is ${JSON.stringify(c)}; use a number of hours, up to 168`);let l=P(n,"bucket"),d=await eV(o,a?await Re(Oe(a)):{},{...P(n,"out")?{out:P(n,"out")}:{},...u!==void 0?{linkSeconds:u}:{},...l?{reports:{bucket:l,...P(n,"bucket-endpoint")?{endpoint:P(n,"bucket-endpoint")}:{},...P(n,"bucket-prefix")?{prefix:P(n,"bucket-prefix")}:{}}}:{}});return console.log(tV(d,o)),d.unreadable.length?1:0}case"audit":{let a=P(n,"config")??fe(o),c=P(n,"link-hours"),u=c===void 0?void 0:Math.round(Number(c)*3600);if(c!==void 0&&!(Number(c)>0))throw new S(`--link-hours is ${JSON.stringify(c)}; use a number of hours, up to 168`);let l=P(n,"bucket"),d=n.check===!0,p=await oV(o,a?await Re(Oe(a)):{},{...d?{check:d}:{},...P(n,"out")?{out:P(n,"out")}:{},...u!==void 0?{linkSeconds:u}:{},...l?{reports:{bucket:l,...P(n,"bucket-endpoint")?{endpoint:P(n,"bucket-endpoint")}:{},...P(n,"bucket-prefix")?{prefix:P(n,"bucket-prefix")}:{}}}:{}});return console.log(sV(p,o,d)),p.unreadable.length||d&&p.added.length?1:0}case"plan":{let a=await bK(o,{root:P(n,"root"),project:P(n,"project"),config:P(n,"config")},s?()=>{}:console.log),c=a.every(u=>u.ok)?0:1;return s?i(Xo("plan",c,{roots:a})):c}case"stage":{if(r[0]==="tf-apply"){if(s)throw new S("--json is not available on stage tf-apply");let l=P(n,"layers");if(!l||!P(n,"wave"))throw new S("stage tf-apply needs --wave <n> and --layers <a,b;c>");return await LH(o,{wave:Number(P(n,"wave")),layers:Ic(l),canary:(P(n,"canary")??"").split(",").filter(Boolean),binary:P(n,"binary")??"tofu",gate:P(n,"gate")??"on-destructive",...P(n,"approval")?{approval:Q9(P(n,"approval"))}:{},...P(n,"config")?{config:P(n,"config")}:{},...P(n,"parallelism")?{parallelism:tY(P(n,"parallelism"))}:{},...n.terragrunt===!0?{terragrunt:!0}:{},...n.rest===!0?{rest:!0}:{},...P(n,"base")?{base:P(n,"base")}:{},...P(n,"shares")?{shares:BI("shares",P(n,"shares"))}:{},...P(n,"share")?{share:BI("share",P(n,"share"))}:{},...P(n,"decided")?{decided:P(n,"decided")}:{},...P(n,"branches")?{branches:jH(P(n,"branches"))}:{},...P(n,"branch")?{branch:P(n,"branch")}:{},...P(n,"groups")?{groups:IH(P(n,"groups"))}:{},...P(n,"group")?{group:P(n,"group")}:{},...P(n,"on-held")?{onHeld:zOe(P(n,"on-held"))}:{},...n["stand-down"]===!0?{standDown:!0}:{}})}let a=await ZB(r[0]??"",o,{root:P(n,"root"),project:P(n,"project"),config:P(n,"config"),out:P(n,"out"),reportUrl:P(n,"report-url"),...P(n,"layers")?{layers:Ic(P(n,"layers"))}:{},...P(n,"binary")?{binary:P(n,"binary")}:{},...P(n,"forge")?{forge:P(n,"forge")}:{},...P(n,"canary")?{canary:P(n,"canary").split(",").filter(Boolean)}:{},...n.terragrunt===!0?{terragrunt:!0}:{},...n["no-cost"]===!0?{noCost:!0}:{},...P(n,"base")?{base:P(n,"base")}:{},...P(n,"parallelism")?{parallelism:tY(P(n,"parallelism"))}:{},...P(n,"bucket")?{reports:{bucket:P(n,"bucket"),...P(n,"bucket-endpoint")?{endpoint:P(n,"bucket-endpoint")}:{},...P(n,"bucket-prefix")?{prefix:P(n,"bucket-prefix")}:{},...P(n,"bucket-url")?{url:P(n,"bucket-url")}:{}}}:{}},s?()=>{}:console.error),c=a.failed?1:0;r[0]==="tf-plan"&&(await Zv(o,{binary:P(n,"binary")??a.report.run.binary??"tofu",planOnly:!0,log:s?console.error:console.log})).code!==0&&(c=1);let u={html:`${a.dir}/report.html`,json:`${a.dir}/report.json`,note:`${a.dir}/note.md`};return s?i(Xo("stage",c,{stage:r[0],change_set:a.report.change_set,files:u,uploaded:a.uploaded??null,...a.issue?{issue:a.issue}:{}})):(console.log(Ub(a.report)),console.log(`report: ${eS(o,u.html)||u.html}`),a.uploaded&&console.log(`copied to the bucket under ${a.uploaded.prefix}; index rewritten at ${a.uploaded.indexes.join(" and ")}`),a.report.run.report_url&&console.log(`served at ${a.report.run.report_url}`),a.report.run.trace_id&&console.log(`trace: ${a.report.run.trace_url??a.report.run.trace_id}`),c)}case"check-root":{let a=r[0];if(!a)throw new S("usage: terragucci check-root <dir> [--binary <b>] [--config <file>] [--base <ref>]");let c=P(n,"config")??fe(o),u=ue(c?await Re(Oe(c)):{}),l=await Zd(o,u.modules,P(n,"base")??ah(process.env),c?{config:Oe(c)}:{}),d=await lq(P(n,"binary")??"tofu",a,o,l?{pins:l}:{});return xp(o,d),d.ok?0:1}case"check-sources":{let a=n.units===!0,c=r[0];if(a===!!c)throw new S("usage: terragucci check-sources <dir>|--units [--config <file>] [--base <ref>]");let u=P(n,"config")??fe(o),l=ue(u?await Re(Oe(u)):{}),d=await kc(o,l.sources,P(n,"base")??ah(process.env),u?{config:Oe(u)}:{},{afterInit:!1});if(!d)return 0;let p=a?Dn(o,l.terragrunt?.exclude).map(m=>m.path):[c],f=[];for(let m of p)f.push(dq(m,await d.before(m)));let g={ok:f.every(m=>m.ok),log:f.flatMap(m=>m.log),report:f.flatMap(m=>m.report)};return xp(o,g),g.ok?0:1}case"check-job":{let a=P(n,"forge"),c=P(n,"environment"),u=n.branches===void 0?void 0:P(n,"branches");if(!a||!Wr.includes(a)||!c||u===void 0)throw new S("usage: terragucci check-job --forge github|gitlab|forgejo --environment <name> --branches <b>[,<b>...] [--forge-environment <name>] [--runner-tags <t>[,<t>...]] [--self-hosted]");let l=f=>(f??"").split(",").map(g=>g.trim()).filter(g=>g!==""),d={forge:a,environment:c,branches:l(u),...P(n,"forge-environment")?{forgeEnvironment:P(n,"forge-environment")}:{},...P(n,"runner-tags")?{runnerTags:l(P(n,"runner-tags"))}:{},...n["self-hosted"]===!0?{selfHosted:!0}:{}},p=mM(d,fM(d,await gM(d)));for(let f of p.out)console.log(f);for(let f of p.err)console.error(f);return p.ok?0:1}case"check-pins":{let a=P(n,"config")??fe(o),c=ue(a?await Re(Oe(a)):{}),u=await Zd(o,c.modules,P(n,"base")??ah(process.env),a?{config:Oe(a)}:{});if(!u)return 0;let l=await uq(Dn(o,c.terragrunt?.exclude).map(d=>d.path),u);return xp(o,l),l.ok?0:1}case"check-policy":{let a=await fq(o,{...P(n,"config")?{config:P(n,"config")}:{},...P(n,"base")?{base:P(n,"base")}:{}});return xp(o,a),a.ok?0:1}case"atmos":{if(r[0]!=="write")throw new S("usage: terragucci atmos write");for(let a of await tD(o))console.log(a);return 0}case"terramate":{if(r[0]!=="generate")throw new S("usage: terragucci terramate generate");for(let a of await tL(o))console.log(a);return 0}case"oidc-trade":{let a=P(n,"out");if(!a)throw new S("usage: terragucci oidc-trade --out <file> [--reports-role <arn>]");for(let c of await uV(a,process.env,{reportsRole:P(n,"reports-role")}))console.error(`terragucci: ${c}`);return 0}case"auth-provider":return console.log(JSON.stringify(HN(o,process.env))),0;case"binary":{let a=r[0];if(!a)throw new S("usage: terragucci binary <root> [--binary <b>] [--config <file>]");let c=P(n,"config")??fe(o),u=ue(c?await Re(Oe(c)):{}),d=await new jr(o,P(n,"binary")??u.binary??"tofu",u.version).resolve(a);return console.error(`${a}: ${Vr(d)}`),console.log(d.path),0}case"install":{let[a,c]=r;if(!a||!c||!["tofu","terraform","terragrunt","choudoufu","infracost","cosign","atmos","terramate"].includes(a))throw new S("usage: terragucci install tofu|terraform|terragrunt|choudoufu|infracost|cosign|atmos|terramate <version>");return Bd(),console.log(await Wd(a,c)),0}case"publish":{let a=P(n,"config")??fe(o),c=ue(a?await Re(Oe(a)):{}),u=await SK(o,c,{dryRun:n["dry-run"]===!0});return console.log(RK(u)),u.some(l=>l.status==="refused")?1:0}case"verify-release":{let[a,c]=r;if(!a||!c)throw new S("usage: terragucci verify-release <module> <version> [--config <file>]");let u=P(n,"config")??fe(o),l=await xK(o,ue(u?await Re(Oe(u)):{}),a,c);return console.log(EK(l)),l.every(d=>d.verified)?0:1}case"respond":{let a=r[0]??"",c=P(n,"config")??fe(o);if(a==="rollout"&&(r.length>1||n.provider!==void 0)){let p=c?await Re(Oe(c)):{};if(Kn(p.projects?ue(p.defaults??{}):ue(p),"rollout")!=="off")return nY(["rollout",...e.slice(e.indexOf("rollout")+1)])}let u=p=>P(n,p),l=u("log"),d=await l4(a,o,{...Object.fromEntries(["config","project","base","out","report","approved","current","root","binary","branch","module","version","title","description","since","attributions"].map(p=>[p,u(p)])),mode:u("mode")??"dry-run",...u("wave")?{wave:Number(u("wave"))}:{},...l?{log:X_(l==="-"?0:Oe(o,l),"utf-8")}:{},...u("platform")?{platforms:u("platform").split(",")}:{},imports:e.flatMap((p,f)=>p==="--import"?[e[f+1]??""]:p.startsWith("--import=")?[p.slice(9)]:[]).map(xz)});return s?i(Xo("respond",d.exit??0,d)):(console.log(d.text),d.exit??0)}case"comment":{let a=P(n,"layers"),c=P(n,"out"),u=P(n,"forge")??"github",l=P(n,"agent")??"off",d=P(n,"policy-dir")??"policy";if(!["off","on","run","push"].includes(l))throw new S("comment's --agent is off, on, run or push");if(u==="gitlab"&&l==="run"&&n.poll!==!0){let g=P(n,"out"),m=P(n,"prompt");if(!g||!m)throw new S("comment --forge gitlab --agent run needs --out <file> and --prompt <file>");let h=await FO($o(process.env,process.env.TG_TOKEN,fetch),process.env);return Rm(Oe(o,g),h),h.go&&h.ask&&pA(Oe(o,m),{ask:h.ask,pr:h.pr,head:h.head,user:h.user,policyDir:d,what:`merge request !${h.pr}`}),h.fail?(console.error(`terragucci comment: failed, no agent: ${h.reason}`),1):(console.log(`terragucci comment: ${h.go?"":"no agent: "}${h.reason}`),0)}if(u==="gitlab"&&l==="push"&&n.poll!==!0){let g=P(n,"change");if(!g)throw new S("comment --agent push needs --change <dir>");let m=await UW({change:Oe(o,g),policyDir:d});return console.log(`terragucci comment: ${m.pushed?"":"nothing pushed: "}${m.reason}`),m.fail?1:0}if(n.poll===!0||u==="gitlab"){if(u!=="gitlab"||n.poll!==!0)throw new S("comment --poll is GitLab's: run it as comment --forge gitlab --poll --layers <a,b;c>");if(!a)throw new S("comment --forge gitlab --poll needs --layers <a,b;c>");let g=P(n,"when")??"merge";if(g!=="merge"&&g!=="pull-request")throw new S("comment's --when is merge or pull-request");let m=eY(P(n,"requires"),"comment");if(l!=="off"&&l!=="on")throw new S("comment --forge gitlab --poll's --agent is off or on");let h=await aJ({layers:Ic(a),when:g,...m?{requires:m}:{},...n["plan-notes"]===!0?{planNotes:!0}:{},...l==="on"?{agent:!0}:{},...n.review===!0?{review:!0}:{}});for(let y of h.plans??[])console.log(`terragucci comment: !${y.mr} plan: ${y.reason}`);for(let y of h.reviews??[])console.log(`terragucci comment: !${y.mr} review: ${y.reason}`);for(let y of h.outcomes)console.log(`terragucci comment: !${y.mr} note ${y.note}: ${y.ran?"":"nothing run: "}${y.reason}`);return h.outcomes.length===0&&!h.fail&&console.log("terragucci comment: no new /terragucci notes"),h.fail&&console.error(`terragucci comment: failed: ${h.fail}`),h.fail||h.outcomes.some(y=>y.fail)||(h.plans??[]).some(y=>y.fail)||(h.reviews??[]).some(y=>y.fail)?1:0}if(l==="push"){let g=P(n,"change");if(!g)throw new S("comment --agent push needs --change <dir>");let m=await Fz({change:Oe(o,g),policyDir:d});return console.log(`terragucci comment: ${m.pushed?"":"nothing pushed: "}${m.reason}`),m.fail?1:0}let p=P(n,"prompt");if(l==="run"?!c||!p:!a||!c)throw new S(l==="run"?"comment --agent run needs --out <file> and --prompt <file>":"comment needs --layers <a,b;c> and --out <file>");if(u!=="github"&&u!=="forgejo")throw new S("comment's --forge is github or forgejo");let f=await jz({layers:a?Ic(a):[],forge:u,agent:l});return Rm(Oe(o,c),f),f.go&&f.ask&&p&&pA(Oe(o,p),{ask:f.ask,pr:f.pr,head:f.head,user:f.user,policyDir:d}),f.fail?(console.error(`terragucci comment: failed, no re-plan: ${f.reason}`),1):(console.log(`terragucci comment: ${f.go?"":l==="run"?"no agent: ":"no re-plan: "}${f.reason}`),0)}case"comment-apply":{let a=P(n,"layers"),c=P(n,"out"),u=P(n,"forge")??"github",l=P(n,"canary"),d=P(n,"when")??"merge";if(!a||!c)throw new S("comment-apply needs --layers <a,b;c> and --out <file>");if(u!=="github"&&u!=="forgejo"&&u!=="gitlab")throw new S("comment-apply's --forge is github, forgejo or gitlab");if(d!=="merge"&&d!=="pull-request")throw new S("comment-apply's --when is merge or pull-request");let p=eY(P(n,"requires"),"comment-apply"),{after:f,synth:g}=n.terragrunt===!0?{}:await X9(o);if(u==="gitlab"){if(d!=="pull-request")throw new S("comment-apply --forge gitlab is apply before merge's: pass --when pull-request");let h=await ZH({layers:Ic(a),...l?{canary:l.split(",")}:{},...f?{after:f}:{},...p?{requires:p}:{},...g?{synth:g}:{},...n.terragrunt===!0?{terragrunt:!0}:{}});return Rm(Oe(o,c),h),h.fail?(console.error(`terragucci comment-apply: failed, nothing applied: ${h.reason}`),1):(console.log(`terragucci comment-apply: ${h.go?"":"nothing applied: "}${h.reason}`),0)}let m=await uW({layers:Ic(a),forge:u,when:d,...l?{canary:l.split(",")}:{},...f?{after:f}:{},...p?{requires:p}:{},...g?{synth:g}:{},...n.terragrunt===!0?{terragrunt:!0}:{},...n.again===!0?{again:!0}:{}});return Rm(Oe(o,c),m),m.fail?(console.error(`terragucci comment-apply: failed, nothing applied: ${m.reason}`),1):(console.log(`terragucci comment-apply: ${m.go?"":"nothing applied: "}${m.reason}`),0)}case"pr-lock":{let a=P(n,"layers"),c=P(n,"forge")??"github",u=P(n,"when")??"merge";if(!a)throw new S("pr-lock needs --layers <a,b;c>");if(c!=="github"&&c!=="forgejo")throw new S("pr-lock's --forge is github or forgejo");if(u!=="merge"&&u!=="pull-request")throw new S("pr-lock's --when is merge or pull-request");let{synth:l}=n.terragrunt===!0?{}:await X9(o),d=await pW({layers:Ic(a),forge:c,when:u,...l?{synth:l}:{},...n.terragrunt===!0?{terragrunt:!0}:{}});return d.fail?(console.error(`terragucci pr-lock: failed: ${d.reason}`),1):(console.log(`terragucci pr-lock: ${d.reason}`),0)}case"pr-merge":{let a=Number(P(n,"pr")),c=P(n,"sha"),u=P(n,"forge")??"github";if(!Number.isInteger(a)||a<1||!c)throw new S("pr-merge needs --pr <n> and --sha <sha>");if(u!=="github"&&u!=="forgejo"&&u!=="gitlab")throw new S("pr-merge's --forge is github, forgejo or gitlab");try{return console.log(`terragucci pr-merge: ${u==="gitlab"?await YH({pr:a,sha:c}):await fW({pr:a,sha:c,forge:u})}`),0}catch(l){if(l instanceof S)throw l;return console.error(`terragucci pr-merge: not merged: ${l.message}`),1}}case"approve":{let a=n.sign===!0?!0:P(n,"sign"),c=n.plan===!0?"":P(n,"plan");if(I2(r)){if(r.length>2)throw new S(`approve ${r[0]} takes one ${r[0]==="ephemeral"?"pull request":"root"}, not ${r.slice(1).join(" ")}`);return(await C2(o,{kind:r[0],target:r[1],...c!==void 0?{plan:c}:{},...a!==void 0?{sign:a}:{},...P(n,"actor")?{actor:P(n,"actor")}:{},dryRun:n["dry-run"]===!0})).code}return(await A2(o,{...n["no-resume"]===!0?{resume:!1}:{},...r[0]?{wave:r[0]}:{},...c!==void 0?{plan:c}:{},...a!==void 0?{sign:a}:{},...P(n,"actor")?{actor:P(n,"actor")}:{},dryRun:n["dry-run"]===!0})).code}case"override":{let a=n.sign===!0?!0:P(n,"sign"),c=e.flatMap((l,d)=>l==="--rule"?[e[d+1]??""]:l.startsWith("--rule=")?[l.slice(7)]:[]).flatMap(l=>l.split(","));if(c.some(l=>l===""||l.startsWith("--")))throw new S("--rule needs a rule id, such as main.deny_public_bucket");return(await P2(o,{root:r[0]??"",rules:c,reason:P(n,"reason")??"",...a!==void 0?{sign:a}:{},...P(n,"actor")?{actor:P(n,"actor")}:{},dryRun:n["dry-run"]===!0})).code}case"state":{if(r[0]!=="export"||!r[1])throw new S("state takes: state export <root> [--version <id>] [--out <file>] [--actor <name>]");return(await xJ(o,{root:r[1],...P(n,"version")?{version:P(n,"version")}:{},...P(n,"out")?{out:P(n,"out")}:{},...P(n,"actor")?{actor:P(n,"actor")}:{},...P(n,"binary")?{binary:P(n,"binary")}:{},...P(n,"config")?{config:P(n,"config")}:{}})).code}case"migrate":{if(r[0]!=="revert"||!r[1])throw new S("migrate takes: migrate revert <migration>");let a=PU(o,r[1]);return console.log(`wrote ${a}: it puts back each state ${r[1]} wrote, to the version it recorded before`),console.log(`revert the code of ${r[1]} in the same change; the plan job proves the revert and wave 1 waits for its approval`),0}case"unlock-state":{if(!r[0]||r.length>1)throw new S("unlock-state takes one root: unlock-state <root>");return(await T2(o,r[0],{...P(n,"binary")?{binary:P(n,"binary")}:{},...P(n,"config")?{config:P(n,"config")}:{},...P(n,"actor")?{actor:P(n,"actor")}:{},log:c=>console.log(`terragucci unlock-state: ${c}`)})).code}case"ephemeral":{let a=r[0],c={...P(n,"binary")?{binary:P(n,"binary")}:{},...P(n,"config")?{config:P(n,"config")}:{},...P(n,"base")?{base:P(n,"base")}:{},...P(n,"actor")?{actor:P(n,"actor")}:{},log:d=>console.log(`terragucci ephemeral: ${d}`)};if(a==="sweep")return await wB(o,c);if(a!=="up"&&a!=="down")throw new S("ephemeral takes up, down or sweep");let u=BI("pr",P(n,"pr")??"");if(a==="up")return await mB(o,{...c,pr:u,...P(n,"head")?{head:P(n,"head")}:{}});let l=P(n,"reason");if(l!=="closed"&&l!=="expired")throw new S("ephemeral down takes --reason closed or --reason expired");return await hB(o,{...c,pr:u,reason:l})}case"resume":{let a=P(n,"forge")??(process.env.GITLAB_CI==="true"?"gitlab":process.env.GITEA_ACTIONS==="true"||process.env.FORGEJO_ACTIONS==="true"?"forgejo":"github");if(a!=="github"&&a!=="forgejo"&&a!=="gitlab")throw new S("resume's --forge is github, forgejo or gitlab");let c=P(n,"out"),u=process.env.TG_SHA||MOe("git",["rev-parse","HEAD"],{cwd:o,encoding:"utf-8"}).stdout.trim();if(a==="gitlab")return await wJ(o,process.env);let l=ft(o),d;try{d=await pH(o,l)}catch(f){console.log(`terragucci resume: the decision point questions could not be read, so no wave waiting on one is resumed: ${f.message}`)}let p=await yJ({ledger:l,migrations:ft(o,ga),forge:a,sha:u,env:process.env,...d?{questions:d}:{}});if(c&&V9(Oe(o,c),p.kind==="apply"?`TG_SHA=${p.sha}
|
|
1301
|
+
Docs: https://intentius.io/terragucci/`;async function X9(e){try{let t=fe(e);if(!t)return{};let n=et(e,ue(await Re(t)));return{...n.after?{after:n.after}:{},...n.kind==="synth"?{synth:!0}:{}}}catch{return{}}}function Q9(e){if(e!==void 0&&!Do.includes(e))throw new S(`--approval must be one of ${Do.join(", ")}`);return e}function eY(e,t){let n=e===void 0?void 0:e==="none"?[]:e.split(",");if(n?.some(r=>!Cn.includes(r)))throw new S(`${t}'s --requires is a comma-separated list of ${Cn.join(", ")}, or none`);return n}function tY(e){let t=Number(e);if(!Number.isInteger(t)||t<1)throw new S(`--parallelism is ${JSON.stringify(e)}; use a whole number of 1 or more`);return t}function BI(e,t){let n=Number(t);if(!Number.isInteger(n)||n<1)throw new S(`--${e} is ${JSON.stringify(t)}; use a whole number of 1 or more`);return n}function FOe(e){let[t="",...n]=e,r={},o=[];for(let s=0;s<n.length;s++){let i=n[s];if(i.startsWith("--")){let[a,c]=i.slice(2).split("=",2);c!==void 0?r[a]=c:n[s+1]!==void 0&&!n[s+1].startsWith("--")&&!["force","dry-run","json","terragrunt","rest","again","poll","plan-notes","no-cost","check","approval-status","review","stand-down","publish","no-open","units","self-hosted","no-roots","require-clouds"].includes(a)?r[a]=n[++s]:r[a]=!0}else o.push(i)}return{cmd:t,flags:r,args:o}}function zOe(e){if(e!=="wait"&&e!=="refuse")throw new S("--on-held must be wait or refuse");return e}function P(e,t){let n=e[t];if(n===!0)throw new S(`--${t} needs a value`);return n}function UOe(e,t){let n=new Set(["aws"]),r=e.projects?Object.entries(e.projects).map(([o,s])=>({host:po(o).host,s:{...e.defaults,...s}})):[{host:void 0,s:e}];for(let{host:o,s}of r){let i=s.forge??(o?ir(o):t?Kr(t)?.value:void 0);i&&n.add(i)}return[...n]}async function nY(e){let{cmd:t,flags:n,args:r}=FOe(e),o=process.cwd(),s=n.json===!0,i=a=>(console.log(JSON.stringify(a,null,2)),a.exit);try{if(s&&!N2.includes(t))throw new S(`--json is not available on ${t||"help"}`);switch(t){case"init":{let a=P(n,"forge"),c=P(n,"binary");if(a&&!Wr.includes(a))throw new S(`--forge must be one of ${Wr.join(", ")}`);if(c&&!od.includes(c))throw new S(`--binary must be one of ${od.join(", ")}`);let u=Q9(P(n,"approval")),l=P(n,"signer"),d=await ik(o,{forge:a,binary:c,...u?{approval:u}:{},...l?{signer:l}:{},force:n.force===!0,dryRun:n["dry-run"]===!0});return s?i(Xo("init",0,BJ(o,d,n["dry-run"]===!0))):(console.log(UJ(o,d,n["dry-run"]===!0)),n["dry-run"]===!0&&console.log("dry run: nothing was written"),0)}case"import":{let[a,c,u]=r;if(gK.includes(a)){if(c!==void 0)throw new S(`import ${a} reads the platform's API, not a file: give --hostname${a==="scalr"?"":" and --organization"}`);let f=P(n,"forge");if(f&&!Wr.includes(f))throw new S(`--forge must be one of ${Wr.join(", ")}`);let g=P(n,"hostname")??(a==="hcp"?"app.terraform.io":void 0);if(!g||!/^[a-z0-9]([a-z0-9.-]*[a-z0-9])?(:\d+)?$/i.test(g))throw new S(`import ${a} needs --hostname, the host name of ${a==="scalr"?"the Scalr account, such as acme.scalr.io":"the OTF server"}`);let m=P(n,"organization");if(a!=="scalr"&&!m)throw new S(`import ${a} needs --organization, the organization whose workspaces it reads`);if(a==="scalr"&&m)throw new S("import scalr reads the account the host names; --environment narrows it to one environment");let h=P(n,"environment");if(a!=="scalr"&&h)throw new S(`--environment is Scalr's; import ${a} reads one --organization`);let y=P(n,"repo");if(y!==void 0&&!/^[^/\s]+(\/[^/\s]+)+$/.test(y))throw new S("--repo must be the repo as the VCS connection names it, owner/name");let w=await mK(o,()=>a==="scalr"?lK(g,h,process.env):cK(a,g,m,process.env),{...y?{repo:y}:{},...f?{forge:f}:{},force:n.force===!0,dryRun:n["dry-run"]===!0});return console.log(hK(w)),0}if(!p0.includes(a))throw new S(`import reads ${p0.join(", ")}: \`terragucci import atlantis [atlantis.yaml]\`, \`terragucci import digger [digger.yml]\`, \`terragucci import terragrunt-scale [.gruntwork]\`, \`terragucci import spacelift [.spacelift/config.yml]\` or \`terragucci import env0 [env0-discovery.yml]\``);if(u!==void 0)throw new S("import reads one file");let l=P(n,"forge");if(l&&!Wr.includes(l))throw new S(`--forge must be one of ${Wr.join(", ")}`);let d=P(n,"apply-when");if(d&&!id.includes(d))throw new S(`--apply-when must be one of ${id.join(", ")}`);let p=iK(o,a,{...c?{file:c}:{},...l?{forge:l}:{},...d?{applyWhen:d}:{},force:n.force===!0,dryRun:n["dry-run"]===!0});return console.log(aK(p)),0}case"reconcile":{let a=P(n,"config")??fe(o);if(!a)throw new S("reconcile needs --config <file>");let c=cd(P(n,"mode")??"dry-run"),u=await HJ(await Re(Oe(a)),{mode:c,project:P(n,"project")}),l=u.some(d=>d.status==="failed")?1:0;return s?i(Xo("reconcile",l,{mode:c,projects:u})):(console.log(qJ(u,c)),l)}case"generate":{let a=P(n,"config")??fe(o),c=ue(a?await Re(Oe(a)):{});if(n.check===!0){let d=EB(o,c);return xp(o,d),d.ok?0:1}let u=qm(o,c);if(u.foreign.length)throw new S(u.foreign.join("; "));let l=n["dry-run"]===!0;if(!l)for(let d of u.files)d.status!=="unchanged"&&(d.status==="removed"?LOe(d.path):V9(d.path,d.content));return console.log(TB(o,u,l)),0}case"estate":{let a=P(n,"config")??fe(o),c=P(n,"link-hours"),u=c===void 0?void 0:Math.round(Number(c)*3600);if(c!==void 0&&!(Number(c)>0))throw new S(`--link-hours is ${JSON.stringify(c)}; use a number of hours, up to 168`);let l=P(n,"bucket"),d=await eV(o,a?await Re(Oe(a)):{},{...P(n,"out")?{out:P(n,"out")}:{},...u!==void 0?{linkSeconds:u}:{},...l?{reports:{bucket:l,...P(n,"bucket-endpoint")?{endpoint:P(n,"bucket-endpoint")}:{},...P(n,"bucket-prefix")?{prefix:P(n,"bucket-prefix")}:{}}}:{}});return console.log(tV(d,o)),d.unreadable.length?1:0}case"audit":{let a=P(n,"config")??fe(o),c=P(n,"link-hours"),u=c===void 0?void 0:Math.round(Number(c)*3600);if(c!==void 0&&!(Number(c)>0))throw new S(`--link-hours is ${JSON.stringify(c)}; use a number of hours, up to 168`);let l=P(n,"bucket"),d=n.check===!0,p=await oV(o,a?await Re(Oe(a)):{},{...d?{check:d}:{},...P(n,"out")?{out:P(n,"out")}:{},...u!==void 0?{linkSeconds:u}:{},...l?{reports:{bucket:l,...P(n,"bucket-endpoint")?{endpoint:P(n,"bucket-endpoint")}:{},...P(n,"bucket-prefix")?{prefix:P(n,"bucket-prefix")}:{}}}:{}});return console.log(sV(p,o,d)),p.unreadable.length||d&&p.added.length?1:0}case"plan":{let a=await bK(o,{root:P(n,"root"),project:P(n,"project"),config:P(n,"config")},s?()=>{}:console.log),c=a.every(u=>u.ok)?0:1;return s?i(Xo("plan",c,{roots:a})):c}case"stage":{if(r[0]==="tf-apply"){if(s)throw new S("--json is not available on stage tf-apply");let l=P(n,"layers");if(!l||!P(n,"wave"))throw new S("stage tf-apply needs --wave <n> and --layers <a,b;c>");return await LH(o,{wave:Number(P(n,"wave")),layers:Ic(l),canary:(P(n,"canary")??"").split(",").filter(Boolean),binary:P(n,"binary")??"tofu",gate:P(n,"gate")??"on-destructive",...P(n,"approval")?{approval:Q9(P(n,"approval"))}:{},...P(n,"config")?{config:P(n,"config")}:{},...P(n,"parallelism")?{parallelism:tY(P(n,"parallelism"))}:{},...n.terragrunt===!0?{terragrunt:!0}:{},...n.rest===!0?{rest:!0}:{},...P(n,"base")?{base:P(n,"base")}:{},...P(n,"shares")?{shares:BI("shares",P(n,"shares"))}:{},...P(n,"share")?{share:BI("share",P(n,"share"))}:{},...P(n,"decided")?{decided:P(n,"decided")}:{},...P(n,"branches")?{branches:jH(P(n,"branches"))}:{},...P(n,"branch")?{branch:P(n,"branch")}:{},...P(n,"groups")?{groups:IH(P(n,"groups"))}:{},...P(n,"group")?{group:P(n,"group")}:{},...P(n,"on-held")?{onHeld:zOe(P(n,"on-held"))}:{},...n["stand-down"]===!0?{standDown:!0}:{}})}let a=await ZB(r[0]??"",o,{root:P(n,"root"),project:P(n,"project"),config:P(n,"config"),out:P(n,"out"),reportUrl:P(n,"report-url"),...P(n,"layers")?{layers:Ic(P(n,"layers"))}:{},...P(n,"binary")?{binary:P(n,"binary")}:{},...P(n,"forge")?{forge:P(n,"forge")}:{},...P(n,"canary")?{canary:P(n,"canary").split(",").filter(Boolean)}:{},...n.terragrunt===!0?{terragrunt:!0}:{},...n["no-cost"]===!0?{noCost:!0}:{},...P(n,"base")?{base:P(n,"base")}:{},...P(n,"parallelism")?{parallelism:tY(P(n,"parallelism"))}:{},...P(n,"bucket")?{reports:{bucket:P(n,"bucket"),...P(n,"bucket-endpoint")?{endpoint:P(n,"bucket-endpoint")}:{},...P(n,"bucket-prefix")?{prefix:P(n,"bucket-prefix")}:{},...P(n,"bucket-url")?{url:P(n,"bucket-url")}:{}}}:{}},s?()=>{}:console.error),c=a.failed?1:0;r[0]==="tf-plan"&&(await Zv(o,{binary:P(n,"binary")??a.report.run.binary??"tofu",planOnly:!0,log:s?console.error:console.log})).code!==0&&(c=1);let u={html:`${a.dir}/report.html`,json:`${a.dir}/report.json`,note:`${a.dir}/note.md`};return s?i(Xo("stage",c,{stage:r[0],change_set:a.report.change_set,files:u,uploaded:a.uploaded??null,...a.issue?{issue:a.issue}:{}})):(console.log(Ub(a.report)),console.log(`report: ${eS(o,u.html)||u.html}`),a.uploaded&&console.log(`copied to the bucket under ${a.uploaded.prefix}; index rewritten at ${a.uploaded.indexes.join(" and ")}`),a.report.run.report_url&&console.log(`served at ${a.report.run.report_url}`),a.report.run.trace_id&&console.log(`trace: ${a.report.run.trace_url??a.report.run.trace_id}`),c)}case"check-root":{let a=r[0];if(!a)throw new S("usage: terragucci check-root <dir> [--binary <b>] [--config <file>] [--base <ref>]");let c=P(n,"config")??fe(o),u=ue(c?await Re(Oe(c)):{}),l=await Zd(o,u.modules,P(n,"base")??ah(process.env),c?{config:Oe(c)}:{}),d=await lq(P(n,"binary")??"tofu",a,o,l?{pins:l}:{});return xp(o,d),d.ok?0:1}case"check-sources":{let a=n.units===!0,c=r[0];if(a===!!c)throw new S("usage: terragucci check-sources <dir>|--units [--config <file>] [--base <ref>]");let u=P(n,"config")??fe(o),l=ue(u?await Re(Oe(u)):{}),d=await kc(o,l.sources,P(n,"base")??ah(process.env),u?{config:Oe(u)}:{},{afterInit:!1});if(!d)return 0;let p=a?Dn(o,l.terragrunt?.exclude).map(m=>m.path):[c],f=[];for(let m of p)f.push(dq(m,await d.before(m)));let g={ok:f.every(m=>m.ok),log:f.flatMap(m=>m.log),report:f.flatMap(m=>m.report)};return xp(o,g),g.ok?0:1}case"check-job":{let a=P(n,"forge"),c=P(n,"environment"),u=n.branches===void 0?void 0:P(n,"branches");if(!a||!Wr.includes(a)||!c||u===void 0)throw new S("usage: terragucci check-job --forge github|gitlab|forgejo --environment <name> --branches <b>[,<b>...] [--forge-environment <name>] [--runner-tags <t>[,<t>...]] [--self-hosted]");let l=f=>(f??"").split(",").map(g=>g.trim()).filter(g=>g!==""),d={forge:a,environment:c,branches:l(u),...P(n,"forge-environment")?{forgeEnvironment:P(n,"forge-environment")}:{},...P(n,"runner-tags")?{runnerTags:l(P(n,"runner-tags"))}:{},...n["self-hosted"]===!0?{selfHosted:!0}:{}},p=mM(d,fM(d,await gM(d)));for(let f of p.out)console.log(f);for(let f of p.err)console.error(f);return p.ok?0:1}case"check-pins":{let a=P(n,"config")??fe(o),c=ue(a?await Re(Oe(a)):{}),u=await Zd(o,c.modules,P(n,"base")??ah(process.env),a?{config:Oe(a)}:{});if(!u)return 0;let l=await uq(Dn(o,c.terragrunt?.exclude).map(d=>d.path),u);return xp(o,l),l.ok?0:1}case"check-policy":{let a=await fq(o,{...P(n,"config")?{config:P(n,"config")}:{},...P(n,"base")?{base:P(n,"base")}:{}});return xp(o,a),a.ok?0:1}case"atmos":{if(r[0]!=="write")throw new S("usage: terragucci atmos write");for(let a of await tD(o))console.log(a);return 0}case"terramate":{if(r[0]!=="generate")throw new S("usage: terragucci terramate generate");for(let a of await t1(o))console.log(a);return 0}case"oidc-trade":{let a=P(n,"out");if(!a)throw new S("usage: terragucci oidc-trade --out <file> [--reports-role <arn>]");for(let c of await uV(a,process.env,{reportsRole:P(n,"reports-role")}))console.error(`terragucci: ${c}`);return 0}case"auth-provider":return console.log(JSON.stringify(HN(o,process.env))),0;case"binary":{let a=r[0];if(!a)throw new S("usage: terragucci binary <root> [--binary <b>] [--config <file>]");let c=P(n,"config")??fe(o),u=ue(c?await Re(Oe(c)):{}),d=await new jr(o,P(n,"binary")??u.binary??"tofu",u.version).resolve(a);return console.error(`${a}: ${Vr(d)}`),console.log(d.path),0}case"install":{let[a,c]=r;if(!a||!c||!["tofu","terraform","terragrunt","choudoufu","infracost","cosign","atmos","terramate"].includes(a))throw new S("usage: terragucci install tofu|terraform|terragrunt|choudoufu|infracost|cosign|atmos|terramate <version>");return Bd(),console.log(await Wd(a,c)),0}case"publish":{let a=P(n,"config")??fe(o),c=ue(a?await Re(Oe(a)):{}),u=await SK(o,c,{dryRun:n["dry-run"]===!0});return console.log(RK(u)),u.some(l=>l.status==="refused")?1:0}case"verify-release":{let[a,c]=r;if(!a||!c)throw new S("usage: terragucci verify-release <module> <version> [--config <file>]");let u=P(n,"config")??fe(o),l=await xK(o,ue(u?await Re(Oe(u)):{}),a,c);return console.log(EK(l)),l.every(d=>d.verified)?0:1}case"respond":{let a=r[0]??"",c=P(n,"config")??fe(o);if(a==="rollout"&&(r.length>1||n.provider!==void 0)){let p=c?await Re(Oe(c)):{};if(Kn(p.projects?ue(p.defaults??{}):ue(p),"rollout")!=="off")return nY(["rollout",...e.slice(e.indexOf("rollout")+1)])}let u=p=>P(n,p),l=u("log"),d=await l4(a,o,{...Object.fromEntries(["config","project","base","out","report","approved","current","root","binary","branch","module","version","title","description","since","attributions"].map(p=>[p,u(p)])),mode:u("mode")??"dry-run",...u("wave")?{wave:Number(u("wave"))}:{},...l?{log:X_(l==="-"?0:Oe(o,l),"utf-8")}:{},...u("platform")?{platforms:u("platform").split(",")}:{},imports:e.flatMap((p,f)=>p==="--import"?[e[f+1]??""]:p.startsWith("--import=")?[p.slice(9)]:[]).map(xz)});return s?i(Xo("respond",d.exit??0,d)):(console.log(d.text),d.exit??0)}case"comment":{let a=P(n,"layers"),c=P(n,"out"),u=P(n,"forge")??"github",l=P(n,"agent")??"off",d=P(n,"policy-dir")??"policy";if(!["off","on","run","push"].includes(l))throw new S("comment's --agent is off, on, run or push");if(u==="gitlab"&&l==="run"&&n.poll!==!0){let g=P(n,"out"),m=P(n,"prompt");if(!g||!m)throw new S("comment --forge gitlab --agent run needs --out <file> and --prompt <file>");let h=await FO($o(process.env,process.env.TG_TOKEN,fetch),process.env);return Rm(Oe(o,g),h),h.go&&h.ask&&pA(Oe(o,m),{ask:h.ask,pr:h.pr,head:h.head,user:h.user,policyDir:d,what:`merge request !${h.pr}`}),h.fail?(console.error(`terragucci comment: failed, no agent: ${h.reason}`),1):(console.log(`terragucci comment: ${h.go?"":"no agent: "}${h.reason}`),0)}if(u==="gitlab"&&l==="push"&&n.poll!==!0){let g=P(n,"change");if(!g)throw new S("comment --agent push needs --change <dir>");let m=await UW({change:Oe(o,g),policyDir:d});return console.log(`terragucci comment: ${m.pushed?"":"nothing pushed: "}${m.reason}`),m.fail?1:0}if(n.poll===!0||u==="gitlab"){if(u!=="gitlab"||n.poll!==!0)throw new S("comment --poll is GitLab's: run it as comment --forge gitlab --poll --layers <a,b;c>");if(!a)throw new S("comment --forge gitlab --poll needs --layers <a,b;c>");let g=P(n,"when")??"merge";if(g!=="merge"&&g!=="pull-request")throw new S("comment's --when is merge or pull-request");let m=eY(P(n,"requires"),"comment");if(l!=="off"&&l!=="on")throw new S("comment --forge gitlab --poll's --agent is off or on");let h=await aJ({layers:Ic(a),when:g,...m?{requires:m}:{},...n["plan-notes"]===!0?{planNotes:!0}:{},...l==="on"?{agent:!0}:{},...n.review===!0?{review:!0}:{}});for(let y of h.plans??[])console.log(`terragucci comment: !${y.mr} plan: ${y.reason}`);for(let y of h.reviews??[])console.log(`terragucci comment: !${y.mr} review: ${y.reason}`);for(let y of h.outcomes)console.log(`terragucci comment: !${y.mr} note ${y.note}: ${y.ran?"":"nothing run: "}${y.reason}`);return h.outcomes.length===0&&!h.fail&&console.log("terragucci comment: no new /terragucci notes"),h.fail&&console.error(`terragucci comment: failed: ${h.fail}`),h.fail||h.outcomes.some(y=>y.fail)||(h.plans??[]).some(y=>y.fail)||(h.reviews??[]).some(y=>y.fail)?1:0}if(l==="push"){let g=P(n,"change");if(!g)throw new S("comment --agent push needs --change <dir>");let m=await Fz({change:Oe(o,g),policyDir:d});return console.log(`terragucci comment: ${m.pushed?"":"nothing pushed: "}${m.reason}`),m.fail?1:0}let p=P(n,"prompt");if(l==="run"?!c||!p:!a||!c)throw new S(l==="run"?"comment --agent run needs --out <file> and --prompt <file>":"comment needs --layers <a,b;c> and --out <file>");if(u!=="github"&&u!=="forgejo")throw new S("comment's --forge is github or forgejo");let f=await jz({layers:a?Ic(a):[],forge:u,agent:l});return Rm(Oe(o,c),f),f.go&&f.ask&&p&&pA(Oe(o,p),{ask:f.ask,pr:f.pr,head:f.head,user:f.user,policyDir:d}),f.fail?(console.error(`terragucci comment: failed, no re-plan: ${f.reason}`),1):(console.log(`terragucci comment: ${f.go?"":l==="run"?"no agent: ":"no re-plan: "}${f.reason}`),0)}case"comment-apply":{let a=P(n,"layers"),c=P(n,"out"),u=P(n,"forge")??"github",l=P(n,"canary"),d=P(n,"when")??"merge";if(!a||!c)throw new S("comment-apply needs --layers <a,b;c> and --out <file>");if(u!=="github"&&u!=="forgejo"&&u!=="gitlab")throw new S("comment-apply's --forge is github, forgejo or gitlab");if(d!=="merge"&&d!=="pull-request")throw new S("comment-apply's --when is merge or pull-request");let p=eY(P(n,"requires"),"comment-apply"),{after:f,synth:g}=n.terragrunt===!0?{}:await X9(o);if(u==="gitlab"){if(d!=="pull-request")throw new S("comment-apply --forge gitlab is apply before merge's: pass --when pull-request");let h=await ZH({layers:Ic(a),...l?{canary:l.split(",")}:{},...f?{after:f}:{},...p?{requires:p}:{},...g?{synth:g}:{},...n.terragrunt===!0?{terragrunt:!0}:{}});return Rm(Oe(o,c),h),h.fail?(console.error(`terragucci comment-apply: failed, nothing applied: ${h.reason}`),1):(console.log(`terragucci comment-apply: ${h.go?"":"nothing applied: "}${h.reason}`),0)}let m=await uW({layers:Ic(a),forge:u,when:d,...l?{canary:l.split(",")}:{},...f?{after:f}:{},...p?{requires:p}:{},...g?{synth:g}:{},...n.terragrunt===!0?{terragrunt:!0}:{},...n.again===!0?{again:!0}:{}});return Rm(Oe(o,c),m),m.fail?(console.error(`terragucci comment-apply: failed, nothing applied: ${m.reason}`),1):(console.log(`terragucci comment-apply: ${m.go?"":"nothing applied: "}${m.reason}`),0)}case"pr-lock":{let a=P(n,"layers"),c=P(n,"forge")??"github",u=P(n,"when")??"merge";if(!a)throw new S("pr-lock needs --layers <a,b;c>");if(c!=="github"&&c!=="forgejo")throw new S("pr-lock's --forge is github or forgejo");if(u!=="merge"&&u!=="pull-request")throw new S("pr-lock's --when is merge or pull-request");let{synth:l}=n.terragrunt===!0?{}:await X9(o),d=await pW({layers:Ic(a),forge:c,when:u,...l?{synth:l}:{},...n.terragrunt===!0?{terragrunt:!0}:{}});return d.fail?(console.error(`terragucci pr-lock: failed: ${d.reason}`),1):(console.log(`terragucci pr-lock: ${d.reason}`),0)}case"pr-merge":{let a=Number(P(n,"pr")),c=P(n,"sha"),u=P(n,"forge")??"github";if(!Number.isInteger(a)||a<1||!c)throw new S("pr-merge needs --pr <n> and --sha <sha>");if(u!=="github"&&u!=="forgejo"&&u!=="gitlab")throw new S("pr-merge's --forge is github, forgejo or gitlab");try{return console.log(`terragucci pr-merge: ${u==="gitlab"?await YH({pr:a,sha:c}):await fW({pr:a,sha:c,forge:u})}`),0}catch(l){if(l instanceof S)throw l;return console.error(`terragucci pr-merge: not merged: ${l.message}`),1}}case"approve":{let a=n.sign===!0?!0:P(n,"sign"),c=n.plan===!0?"":P(n,"plan");if(I2(r)){if(r.length>2)throw new S(`approve ${r[0]} takes one ${r[0]==="ephemeral"?"pull request":"root"}, not ${r.slice(1).join(" ")}`);return(await C2(o,{kind:r[0],target:r[1],...c!==void 0?{plan:c}:{},...a!==void 0?{sign:a}:{},...P(n,"actor")?{actor:P(n,"actor")}:{},dryRun:n["dry-run"]===!0})).code}return(await A2(o,{...n["no-resume"]===!0?{resume:!1}:{},...r[0]?{wave:r[0]}:{},...c!==void 0?{plan:c}:{},...a!==void 0?{sign:a}:{},...P(n,"actor")?{actor:P(n,"actor")}:{},dryRun:n["dry-run"]===!0})).code}case"override":{let a=n.sign===!0?!0:P(n,"sign"),c=e.flatMap((l,d)=>l==="--rule"?[e[d+1]??""]:l.startsWith("--rule=")?[l.slice(7)]:[]).flatMap(l=>l.split(","));if(c.some(l=>l===""||l.startsWith("--")))throw new S("--rule needs a rule id, such as main.deny_public_bucket");return(await P2(o,{root:r[0]??"",rules:c,reason:P(n,"reason")??"",...a!==void 0?{sign:a}:{},...P(n,"actor")?{actor:P(n,"actor")}:{},dryRun:n["dry-run"]===!0})).code}case"state":{if(r[0]!=="export"||!r[1])throw new S("state takes: state export <root> [--version <id>] [--out <file>] [--actor <name>]");return(await xJ(o,{root:r[1],...P(n,"version")?{version:P(n,"version")}:{},...P(n,"out")?{out:P(n,"out")}:{},...P(n,"actor")?{actor:P(n,"actor")}:{},...P(n,"binary")?{binary:P(n,"binary")}:{},...P(n,"config")?{config:P(n,"config")}:{}})).code}case"migrate":{if(r[0]!=="revert"||!r[1])throw new S("migrate takes: migrate revert <migration>");let a=PU(o,r[1]);return console.log(`wrote ${a}: it puts back each state ${r[1]} wrote, to the version it recorded before`),console.log(`revert the code of ${r[1]} in the same change; the plan job proves the revert and wave 1 waits for its approval`),0}case"unlock-state":{if(!r[0]||r.length>1)throw new S("unlock-state takes one root: unlock-state <root>");return(await T2(o,r[0],{...P(n,"binary")?{binary:P(n,"binary")}:{},...P(n,"config")?{config:P(n,"config")}:{},...P(n,"actor")?{actor:P(n,"actor")}:{},log:c=>console.log(`terragucci unlock-state: ${c}`)})).code}case"ephemeral":{let a=r[0],c={...P(n,"binary")?{binary:P(n,"binary")}:{},...P(n,"config")?{config:P(n,"config")}:{},...P(n,"base")?{base:P(n,"base")}:{},...P(n,"actor")?{actor:P(n,"actor")}:{},log:d=>console.log(`terragucci ephemeral: ${d}`)};if(a==="sweep")return await wB(o,c);if(a!=="up"&&a!=="down")throw new S("ephemeral takes up, down or sweep");let u=BI("pr",P(n,"pr")??"");if(a==="up")return await mB(o,{...c,pr:u,...P(n,"head")?{head:P(n,"head")}:{}});let l=P(n,"reason");if(l!=="closed"&&l!=="expired")throw new S("ephemeral down takes --reason closed or --reason expired");return await hB(o,{...c,pr:u,reason:l})}case"resume":{let a=P(n,"forge")??(process.env.GITLAB_CI==="true"?"gitlab":process.env.GITEA_ACTIONS==="true"||process.env.FORGEJO_ACTIONS==="true"?"forgejo":"github");if(a!=="github"&&a!=="forgejo"&&a!=="gitlab")throw new S("resume's --forge is github, forgejo or gitlab");let c=P(n,"out"),u=process.env.TG_SHA||MOe("git",["rev-parse","HEAD"],{cwd:o,encoding:"utf-8"}).stdout.trim();if(a==="gitlab")return await wJ(o,process.env);let l=ft(o),d;try{d=await pH(o,l)}catch(f){console.log(`terragucci resume: the decision point questions could not be read, so no wave waiting on one is resumed: ${f.message}`)}let p=await yJ({ledger:l,migrations:ft(o,ga),forge:a,sha:u,env:process.env,...d?{questions:d}:{}});if(c&&V9(Oe(o,c),p.kind==="apply"?`TG_SHA=${p.sha}
|
|
1302
1302
|
TG_PR=${p.pr??""}
|
|
1303
|
-
`:""),p.kind==="none")console.log(`terragucci resume: nothing to resume: ${p.why}`);else{for(let f of p.waves)console.log(f.migration?`terragucci resume: migration ${f.migration}, which wave 1 runs, was approved by ${f.by} for ${f.digest}`:f.stopped?`terragucci resume: the apply of wave ${f.wave}, approved by ${f.by} for ${f.digest}, stopped before it finished; the wave applies the rest`:`terragucci resume: wave ${f.wave} was approved by ${f.by} for ${f.digest}`);console.log(`terragucci resume: applying the waves again at ${p.sha.slice(0,8)}; each gate decides`)}return 0}case"notify":{if(r[0]==="drift"){for(let g of await E4(x4(Oe(o,P(n,"report")??"terragucci-report"))))console.log(`terragucci notify: ${g}`);return 0}let a=r[0];if(!aj.includes(a))throw new S(`terragucci notify takes one of ${[...aj,"drift"].join(", ")}`);let c=Number(P(n,"wave"));if(!Number.isInteger(c)||c<1)throw new S("terragucci notify needs --wave <n>");let u=P(n,"outcome"),l=u&&J9(Oe(o,u))?X_(Oe(o,u),"utf-8"):void 0,d=P(n,"outcome-json"),p=d?b4(Oe(o,d)):void 0,f=P(n,"report");for(let g of await R4(v4(a,c,{...l?{outcome:l}:{},...p?{result:p}:{},reportDir:Oe(o,f??"terragucci-report")})))console.log(`terragucci notify: ${g}`);return 0}case"drift-agent":{let a=r[0],c=P(n,"policy-dir")??"policy";if(a==="prompt"){let u=P(n,"report"),l=P(n,"out");if(!u||!l)throw new S("drift-agent prompt needs --report <dir> and --out <file>");let d=Hz({report:Oe(o,u),out:Oe(o,l),policyDir:c});return console.log(`terragucci drift-agent: wrote the prompt for drift issue #${d.issue}, ${d.roots} root${d.roots===1?"":"s"} drifted`),0}if(a==="push"){let u=P(n,"change");if(!u)throw new S("drift-agent push needs --change <dir>");let l=P(n,"forge")??"github";if(l!=="github"&&l!=="forgejo")throw new S("drift-agent push's --forge is github or forgejo");let d=await qz({change:Oe(o,u),policyDir:c,forge:l});return console.log(`terragucci drift-agent: ${d.reason}`),d.fail?1:0}throw new S("drift-agent is drift-agent prompt or drift-agent push")}case"query":{let[a,c]=r;if(a===void 0||c!==void 0)throw new S('query takes one statement, quoted: terragucci query "SELECT * FROM inventory"');let u=P(n,"config")??fe(o),l=P(n,"bucket"),d=await M9(u?await Re(Oe(u)):{},a,{...l?{reports:{bucket:l,...P(n,"bucket-endpoint")?{endpoint:P(n,"bucket-endpoint")}:{},...P(n,"bucket-prefix")?{prefix:P(n,"bucket-prefix")}:{}}}:{}});return s?i(Xo("query",0,d)):(console.log(L9(d)),0)}case"mcp":{let a=P(n,"config")??fe(o),c=P(n,"bucket");return await I9({cwd:o,config:a?await Re(Oe(a)):{},...c?{reports:{bucket:c,...P(n,"bucket-endpoint")?{endpoint:P(n,"bucket-endpoint")}:{},...P(n,"bucket-prefix")?{prefix:P(n,"bucket-prefix")}:{}}}:{}}),0}case"view":{if(r.length)throw new S("view takes no arguments: terragucci view [--export <dir> [--publish]] [--reports <dir|s3://<bucket>/<prefix>|https://...>] [--project <host/path>] [--port <n>] [--no-open]");let a=P(n,"export");if(n.publish===!0&&a===void 0)throw new S("--publish uploads the exported view; give --export <dir> too");let c=P(n,"port")===void 0?void 0:Number(P(n,"port"));if(c!==void 0&&(!Number.isInteger(c)||c<1||c>65535))throw new S("terragucci view --port takes a port number");if(c!==void 0&&a!==void 0)throw new S("--port is for serving; --export writes files and serves nothing");let u=W9(o),l=P(n,"reports"),d=P(n,"project")??H9(u,process.env),p=l===void 0?P(n,"config")??fe(o)??(u!==o?fe(u):void 0):void 0,f=z9(l===void 0?F9(p?await Re(Oe(p)):{},d):void 0,l,o);return n.publish===!0&&Y_(f),await B9({root:u,source:f,project:d,...c!==void 0?{port:c}:{},open:n["no-open"]!==!0,...a!==void 0?{export:Oe(o,a),publish:n.publish===!0}:{}},q9())}case"relay":{let a=Number(P(n,"port")??process.env.PORT??8080);if(!Number.isInteger(a)||a<1||a>65535)throw new S("terragucci relay --port takes a port number");return await N4({port:a}),await new Promise(()=>{})}case"post":{let a=P(n,"forge")??process.env.TG_FORGE??"github";if(a!=="github"&&a!=="gitlab"&&a!=="forgejo")throw new S("post's --forge is github, gitlab or forgejo");let c=await dJ(r[0]??"",r.slice(1),{forge:a});return c&&console.log(c),0}case"plan-note":{let a=P(n,"forge")??"github";if(a!=="github"&&a!=="forgejo")throw new S("plan-note's --forge is github or forgejo");let c=P(n,"report")??"terragucci-report",u=P(n,"root"),l=await xW({forge:a,report:Oe(o,c),planResult:P(n,"plan-result")??"",...u?{root:u}:{},...n["approval-status"]===!0?{approval:!0}:{},...P(n,"agent-login")?{agentLogin:P(n,"agent-login")}:{}});for(let d of l)console.log(d);return 0}case"review":{let a=r[0],c=P(n,"forge");if(c!==void 0&&!["github","forgejo","gitlab"].includes(c))throw new S("review's --forge is github, forgejo or gitlab");if(a==="prompt"&&c==="gitlab"){let u=Oe(o,P(n,"report")??"terragucci-report"),{written:l,report:d}=await WW({report:u,instructions:P(n,"instructions")??Xm});return console.log(`terragucci review: ${d}`),console.log(`terragucci review: wrote the prompt for merge request !${l.pr} at ${l.head.slice(0,8)}; instructions: ${l.instructions==="default"?"the default branch's":"none on the default branch"}${l.changed?", which this merge request changes":""}`),0}if(a==="post"&&c==="gitlab"){let u=P(n,"dir");if(!u)throw new S("review post needs --dir <dir>");let l=Oe(o,u),d=await HW({dir:l,read:p=>J9(Z9(l,p))?X_(Z9(l,p),"utf-8"):""});return console.log(`terragucci review: ${d.reason}`),d.posted?0:1}if(a==="prompt"){let u=Oe(o,P(n,"report")??"terragucci-report"),l;try{l=JSON.parse(X_(process.env.GITHUB_EVENT_PATH??"","utf-8"))}catch(m){throw new S(`review prompt reads the pull request from the event file, and could not (${m.message})`)}let d=to(process.env),{subject:p,run:f}=await MW(l,d);console.log(`terragucci review: ${await LW(d,x$(process.env),p,f,u)}`);let g=_$({report:u,instructions:P(n,"instructions")??Xm,pull:p});return console.log(`terragucci review: wrote the prompt for pull request ${g.pr} at ${g.head.slice(0,8)}; instructions: ${g.instructions==="default"?"the default branch's":"none on the default branch"}${g.changed?", which this pull request changes":""}`),0}if(a==="post"){let u=P(n,"dir");if(!u)throw new S("review post needs --dir <dir>");let l=await NW({dir:Oe(o,u)});return console.log(`terragucci review: ${l.reason}`),0}throw new S("review is review prompt --report <dir> [--instructions <path>] or review post --dir <dir>")}case"approval-status":{let a=P(n,"forge")??"github";if(a!=="github"&&a!=="forgejo")throw new S("approval-status's --forge is github or forgejo");let c=P(n,"report"),u=P(n,"agent-login"),l=await h$({forge:a,...c?{report:Oe(o,c)}:{},...u?{agentLogin:u}:{},log:d=>console.log(`terragucci approval-status: ${d}`)});return console.log(`terragucci approval-status: ${l.state}: ${l.description}`),0}case"rollout":{let a=await ZP(o,b2(r,n)),c=l2(a);return s?i(Xo("rollout",c,a)):(console.log(XP(a)),c)}case"profiles":{let a=P(n,"config")??fe(o);return console.log(UOe(a?await Re(Oe(a)):{},a?void 0:o).join(" ")),0}case"config":{if(r[0]==="identities"){let g=P(n,"config")??fe(o);if(!g)throw new S("no terragucci config here; pass --config <file>");let m=await Mh(Oe(g),eS(o,Oe(g))||g,{...P(n,"project")?{project:P(n,"project")}:{},...P(n,"forge")?{forge:P(n,"forge")}:{},...P(n,"repo")?{repo:P(n,"repo")}:{},...P(n,"default-branch")?{defaultBranch:P(n,"default-branch")}:{},...P(n,"owner-id")?{ownerId:P(n,"owner-id")}:{},...P(n,"repo-id")?{repoId:P(n,"repo-id")}:{},noRoots:n["no-roots"]===!0});return s?i(Xo("config identities",0,m)):(console.log(fV(m)),0)}if(r[0]!=="check")throw new S("usage: terragucci config check|identities [--config <file>] [--json]");let a=P(n,"config")??fe(o);if(!a)throw new S("no terragucci config here; pass --config <file>");let c=[],u=[],l=[],d,p;try{let g=await Re(Oe(a),"check"),m=oJ(g);if(m.length)throw new S(`${a} does not match terragucci.schema.json`,m.map(v=>`${v} (terragucci.schema.json)`));let h=Q_(Oe(a)),y=g.projects?void 0:et(h,ue(g));y&&c.push(...y.problems("config"));for(let v of Object.keys(g.projects??{}))try{ot(g,v)}catch($){if(!($ instanceof S))throw $;c.push(...$.problems??[$.message])}if(y&&c.length===0&&g.environments){let v=y.kind==="terragrunt"?Dn(h,g.terragrunt?.exclude).map($=>$.path):y.kind==="atmos"?Hi(await Wi(h)).map($=>$.path):wt(h,g.roots);c.push(...wD(v,g))}let w=Zs(g.oidc,g.environments),b=w?["aws","gcp","azure"].filter(v=>v==="aws"?w.roles:w[v]?.roles):[];if(y&&c.length===0&&w&&b.length>0&&(y.kind==="atmos"||y.engine==="per-root")){let v=y.kind==="atmos"?Hi(await Wi(h)):void 0,$=v?v.map(R=>R.path):wt(h,g.roots),_=v?{...rg(v),via:"!terraform.state"}:void 0,E=b.map(R=>
|
|
1303
|
+
`:""),p.kind==="none")console.log(`terragucci resume: nothing to resume: ${p.why}`);else{for(let f of p.waves)console.log(f.migration?`terragucci resume: migration ${f.migration}, which wave 1 runs, was approved by ${f.by} for ${f.digest}`:f.stopped?`terragucci resume: the apply of wave ${f.wave}, approved by ${f.by} for ${f.digest}, stopped before it finished; the wave applies the rest`:`terragucci resume: wave ${f.wave} was approved by ${f.by} for ${f.digest}`);console.log(`terragucci resume: applying the waves again at ${p.sha.slice(0,8)}; each gate decides`)}return 0}case"notify":{if(r[0]==="drift"){for(let g of await E4(x4(Oe(o,P(n,"report")??"terragucci-report"))))console.log(`terragucci notify: ${g}`);return 0}let a=r[0];if(!aj.includes(a))throw new S(`terragucci notify takes one of ${[...aj,"drift"].join(", ")}`);let c=Number(P(n,"wave"));if(!Number.isInteger(c)||c<1)throw new S("terragucci notify needs --wave <n>");let u=P(n,"outcome"),l=u&&J9(Oe(o,u))?X_(Oe(o,u),"utf-8"):void 0,d=P(n,"outcome-json"),p=d?b4(Oe(o,d)):void 0,f=P(n,"report");for(let g of await R4(v4(a,c,{...l?{outcome:l}:{},...p?{result:p}:{},reportDir:Oe(o,f??"terragucci-report")})))console.log(`terragucci notify: ${g}`);return 0}case"drift-agent":{let a=r[0],c=P(n,"policy-dir")??"policy";if(a==="prompt"){let u=P(n,"report"),l=P(n,"out");if(!u||!l)throw new S("drift-agent prompt needs --report <dir> and --out <file>");let d=Hz({report:Oe(o,u),out:Oe(o,l),policyDir:c});return console.log(`terragucci drift-agent: wrote the prompt for drift issue #${d.issue}, ${d.roots} root${d.roots===1?"":"s"} drifted`),0}if(a==="push"){let u=P(n,"change");if(!u)throw new S("drift-agent push needs --change <dir>");let l=P(n,"forge")??"github";if(l!=="github"&&l!=="forgejo")throw new S("drift-agent push's --forge is github or forgejo");let d=await qz({change:Oe(o,u),policyDir:c,forge:l});return console.log(`terragucci drift-agent: ${d.reason}`),d.fail?1:0}throw new S("drift-agent is drift-agent prompt or drift-agent push")}case"query":{let[a,c]=r;if(a===void 0||c!==void 0)throw new S('query takes one statement, quoted: terragucci query "SELECT * FROM inventory"');let u=P(n,"config")??fe(o),l=P(n,"bucket"),d=await M9(u?await Re(Oe(u)):{},a,{...l?{reports:{bucket:l,...P(n,"bucket-endpoint")?{endpoint:P(n,"bucket-endpoint")}:{},...P(n,"bucket-prefix")?{prefix:P(n,"bucket-prefix")}:{}}}:{}});return s?i(Xo("query",0,d)):(console.log(L9(d)),0)}case"mcp":{let a=P(n,"config")??fe(o),c=P(n,"bucket");return await I9({cwd:o,config:a?await Re(Oe(a)):{},...c?{reports:{bucket:c,...P(n,"bucket-endpoint")?{endpoint:P(n,"bucket-endpoint")}:{},...P(n,"bucket-prefix")?{prefix:P(n,"bucket-prefix")}:{}}}:{}}),0}case"view":{if(r.length)throw new S("view takes no arguments: terragucci view [--export <dir> [--publish]] [--reports <dir|s3://<bucket>/<prefix>|https://...>] [--project <host/path>] [--port <n>] [--no-open]");let a=P(n,"export");if(n.publish===!0&&a===void 0)throw new S("--publish uploads the exported view; give --export <dir> too");let c=P(n,"port")===void 0?void 0:Number(P(n,"port"));if(c!==void 0&&(!Number.isInteger(c)||c<1||c>65535))throw new S("terragucci view --port takes a port number");if(c!==void 0&&a!==void 0)throw new S("--port is for serving; --export writes files and serves nothing");let u=W9(o),l=P(n,"reports"),d=P(n,"project")??H9(u,process.env),p=l===void 0?P(n,"config")??fe(o)??(u!==o?fe(u):void 0):void 0,f=z9(l===void 0?F9(p?await Re(Oe(p)):{},d):void 0,l,o);return n.publish===!0&&Y_(f),await B9({root:u,source:f,project:d,...c!==void 0?{port:c}:{},open:n["no-open"]!==!0,...a!==void 0?{export:Oe(o,a),publish:n.publish===!0}:{}},q9())}case"relay":{let a=Number(P(n,"port")??process.env.PORT??8080);if(!Number.isInteger(a)||a<1||a>65535)throw new S("terragucci relay --port takes a port number");return await N4({port:a}),await new Promise(()=>{})}case"post":{let a=P(n,"forge")??process.env.TG_FORGE??"github";if(a!=="github"&&a!=="gitlab"&&a!=="forgejo")throw new S("post's --forge is github, gitlab or forgejo");let c=await dJ(r[0]??"",r.slice(1),{forge:a});return c&&console.log(c),0}case"plan-note":{let a=P(n,"forge")??"github";if(a!=="github"&&a!=="forgejo")throw new S("plan-note's --forge is github or forgejo");let c=P(n,"report")??"terragucci-report",u=P(n,"root"),l=await xW({forge:a,report:Oe(o,c),planResult:P(n,"plan-result")??"",...u?{root:u}:{},...n["approval-status"]===!0?{approval:!0}:{},...P(n,"agent-login")?{agentLogin:P(n,"agent-login")}:{}});for(let d of l)console.log(d);return 0}case"review":{let a=r[0],c=P(n,"forge");if(c!==void 0&&!["github","forgejo","gitlab"].includes(c))throw new S("review's --forge is github, forgejo or gitlab");if(a==="prompt"&&c==="gitlab"){let u=Oe(o,P(n,"report")??"terragucci-report"),{written:l,report:d}=await WW({report:u,instructions:P(n,"instructions")??Xm});return console.log(`terragucci review: ${d}`),console.log(`terragucci review: wrote the prompt for merge request !${l.pr} at ${l.head.slice(0,8)}; instructions: ${l.instructions==="default"?"the default branch's":"none on the default branch"}${l.changed?", which this merge request changes":""}`),0}if(a==="post"&&c==="gitlab"){let u=P(n,"dir");if(!u)throw new S("review post needs --dir <dir>");let l=Oe(o,u),d=await HW({dir:l,read:p=>J9(Z9(l,p))?X_(Z9(l,p),"utf-8"):""});return console.log(`terragucci review: ${d.reason}`),d.posted?0:1}if(a==="prompt"){let u=Oe(o,P(n,"report")??"terragucci-report"),l;try{l=JSON.parse(X_(process.env.GITHUB_EVENT_PATH??"","utf-8"))}catch(m){throw new S(`review prompt reads the pull request from the event file, and could not (${m.message})`)}let d=to(process.env),{subject:p,run:f}=await MW(l,d);console.log(`terragucci review: ${await LW(d,x$(process.env),p,f,u)}`);let g=_$({report:u,instructions:P(n,"instructions")??Xm,pull:p});return console.log(`terragucci review: wrote the prompt for pull request ${g.pr} at ${g.head.slice(0,8)}; instructions: ${g.instructions==="default"?"the default branch's":"none on the default branch"}${g.changed?", which this pull request changes":""}`),0}if(a==="post"){let u=P(n,"dir");if(!u)throw new S("review post needs --dir <dir>");let l=await NW({dir:Oe(o,u)});return console.log(`terragucci review: ${l.reason}`),0}throw new S("review is review prompt --report <dir> [--instructions <path>] or review post --dir <dir>")}case"approval-status":{let a=P(n,"forge")??"github";if(a!=="github"&&a!=="forgejo")throw new S("approval-status's --forge is github or forgejo");let c=P(n,"report"),u=P(n,"agent-login"),l=await h$({forge:a,...c?{report:Oe(o,c)}:{},...u?{agentLogin:u}:{},log:d=>console.log(`terragucci approval-status: ${d}`)});return console.log(`terragucci approval-status: ${l.state}: ${l.description}`),0}case"rollout":{let a=await ZP(o,b2(r,n)),c=l2(a);return s?i(Xo("rollout",c,a)):(console.log(XP(a)),c)}case"profiles":{let a=P(n,"config")??fe(o);return console.log(UOe(a?await Re(Oe(a)):{},a?void 0:o).join(" ")),0}case"config":{if(r[0]==="identities"){let g=P(n,"config")??fe(o);if(!g)throw new S("no terragucci config here; pass --config <file>");let m=await Mh(Oe(g),eS(o,Oe(g))||g,{...P(n,"project")?{project:P(n,"project")}:{},...P(n,"forge")?{forge:P(n,"forge")}:{},...P(n,"repo")?{repo:P(n,"repo")}:{},...P(n,"default-branch")?{defaultBranch:P(n,"default-branch")}:{},...P(n,"owner-id")?{ownerId:P(n,"owner-id")}:{},...P(n,"repo-id")?{repoId:P(n,"repo-id")}:{},noRoots:n["no-roots"]===!0});return s?i(Xo("config identities",0,m)):(console.log(fV(m)),0)}if(r[0]!=="check")throw new S("usage: terragucci config check|identities [--config <file>] [--json]");let a=P(n,"config")??fe(o);if(!a)throw new S("no terragucci config here; pass --config <file>");let c=[],u=[],l=[],d,p;try{let g=await Re(Oe(a),"check"),m=oJ(g);if(m.length)throw new S(`${a} does not match terragucci.schema.json`,m.map(v=>`${v} (terragucci.schema.json)`));let h=Q_(Oe(a)),y=g.projects?void 0:et(h,ue(g));y&&c.push(...y.problems("config"));for(let v of Object.keys(g.projects??{}))try{ot(g,v)}catch($){if(!($ instanceof S))throw $;c.push(...$.problems??[$.message])}if(y&&c.length===0&&g.environments){let v=y.kind==="terragrunt"?Dn(h,g.terragrunt?.exclude).map($=>$.path):y.kind==="atmos"?Hi(await Wi(h)).map($=>$.path):wt(h,g.roots);c.push(...wD(v,g))}let w=Zs(g.oidc,g.environments),b=w?["aws","gcp","azure"].filter(v=>v==="aws"?w.roles:w[v]?.roles):[];if(y&&c.length===0&&w&&b.length>0&&(y.kind==="atmos"||y.engine==="per-root")){let v=y.kind==="atmos"?Hi(await Wi(h)):void 0,$=v?v.map(R=>R.path):wt(h,g.roots),_=v?{...rg(v),via:"!terraform.state"}:void 0,E=b.map(R=>X1(h,$,w,_,R));d={roles:E.flatMap(R=>R.roles),warnings:E.flatMap(R=>R.warnings)},u=d.warnings}y&&c.length===0&&y.engine==="per-root"&&y.kind!=="atmos"&&u.push(...uD(h,wt(h,g.roots))),!g.projects&&g.terragrunt?.credentials&&u.push(...Q1(g.terragrunt.credentials));let k=!!(g.environments||g.oidc?.roles||g.oidc?.gcp?.roles||g.oidc?.azure?.roles);if(y&&c.length===0&&y.kind!=="terragrunt"&&k)try{let v=y.kind==="atmos"?Hi(await Wi(h)).map(R=>R.path):wt(h,g.roots),$=g.forge??Kr(h)?.value,_=sc(g,v),E=dM(_,$);E&&l.push({rule:$x,message:E}),l.push(...Vq(_,g,$))}catch{}for(let v of[g.agent,...Object.values(g.projects??{}).map($=>$.agent)]){let $=Vy(v);$&&!u.includes($)&&u.push($)}p=Yu(Q_(Oe(a)),g),c.push(...EA(Q_(Oe(a)))),!g.forge&&(g.apply?.when==="pull-request"||g.agent?.comment||g.review?.agent)&&Kr(Q_(Oe(a)))?.value==="gitlab"&&c.push(...Nf(g,"config"))}catch(g){if(!(g instanceof S))throw g;c=g.problems??[g.message]}let f=eS(o,Oe(a))||a;if(s)return i(Xo("config check",c.length?2:0,{file:f,ok:c.length===0,problems:c,...u.length?{warnings:u}:{},...l.length&&c.length===0?{tips:l}:{},...d&&c.length===0?{state_access:d.roles}:{},...p&&c.length===0?{approval:p}:{}}));if(c.length===0){if(console.log(`${f}: ok`),p&&console.log(`approval: ${p.mode} (${p.source})${p.note?`
|
|
1304
1304
|
note: ${p.note}`:""}`),d){console.log("state access:");for(let g of d.roles)console.log(` ${g.role} (${g.cloud?`${g.cloud}, `:""}${g.stage}, ${g.environment}): ${g.states.length?g.states.join(", "):"no state named in code"}${g.reads.length?`; reads ${g.reads.join(", ")}`:""}`)}for(let g of l)console.log(`tip (${g.rule}): ${g.message}`)}else console.error(`${f}: ${c.length} problem(s)
|
|
1305
1305
|
${c.join(`
|
|
1306
1306
|
`)}`);return u.length&&console.error(`${f}: ${u.length} warning(s)
|