@intentius/terragucci 0.4.21 → 0.4.22
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 +90 -90
- package/dist/terragucci.mjs.map +1 -1
- package/package.json +1 -1
package/dist/terragucci.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire as __terragucciRequire } from 'node:module';
|
|
3
3
|
const require = __terragucciRequire(import.meta.url);
|
|
4
|
-
var cY=Object.defineProperty;var WI=(e,t)=>{for(var n in t)cY(e,n,{get:t[n],enumerable:!0})};import{spawnSync as MOe}from"node:child_process";import{existsSync as J9,readFileSync as X_,realpathSync as K9,unlinkSync as LOe,writeFileSync as V9}from"node:fs";import{dirname as Q_,join as Z9,relative as eS,resolve as Oe}from"node:path";import{fileURLToPath as GOe}from"node:url";import{execFileSync as sD}from"node:child_process";import{existsSync as Jr,readFileSync as Mn,readdirSync as Ew,statSync as JR}from"node:fs";import{dirname as _w,isAbsolute as ZX,join as Pt,relative as xw,resolve as Sw}from"node:path";import{createHash as uY}from"node:crypto";function mf(e){return`sha256:${uY("sha256").update(e,"utf8").digest("hex")}`}var gu=Symbol.for("chant.declarable");function Xe(e){return"props"in e}var lY=".declarable";function HI(e){return typeof e!="object"||e===null?!1:e[gu]===!0?!0:dY(e)}function dY(e){for(let t of Object.getOwnPropertySymbols(e)){let n=Object.getOwnPropertyDescriptor(e,t);if(!(n===void 0||n.enumerable||n.value!==!0)&&t.description?.endsWith(lY)===!0)return!0}return!1}function Wl(e){return e.kind==="property"}var Fs=Symbol.for("chant.intrinsic");function Ey(e){let t=tS(e,"$",new Set);if(t===void 0)throw new Error("canonicalJson: value at $ is not representable in JSON");return t}function tS(e,t,n){if(e!==null&&(typeof e=="object"||typeof e=="function")){let o=e.toJSON;typeof o=="function"&&(e=o.call(e))}if(e===void 0)return;if(e===null)return"null";switch(typeof e){case"boolean":return e?"true":"false";case"number":if(!Number.isFinite(e))throw new Error(`canonicalJson: non-finite number at ${t}`);return String(e);case"string":return JSON.stringify(e);case"bigint":throw new Error(`canonicalJson: bigint at ${t} is not representable in JSON`);case"function":case"symbol":throw new Error(`canonicalJson: ${typeof e} at ${t} is not representable in JSON`)}let r=e;if(n.has(r))throw new Error(`canonicalJson: circular structure at ${t}`);n.add(r);try{if(Array.isArray(r))return`[${r.map((a,c)=>tS(a,`${t}[${c}]`,n)??"null").join(",")}]`;let o=Object.keys(r).sort(),s=[];for(let i of o){let a=tS(r[i],`${t}.${i}`,n);a!==void 0&&s.push(`${JSON.stringify(i)}:${a}`)}return`{${s.join(",")}}`}finally{n.delete(r)}}var Ty="jcs1-sha256:",pY="sha256:",qI=/^(?:jcs1-)?sha256:([0-9a-f]{64})$/;function Rn(e,t){return Ty+mf(Ey({kind:e,subject:t})).slice(pY.length)}function Ie(e,t){if(e===t)return!0;if(e===void 0||t===void 0)return!1;let n=qI.exec(e),r=qI.exec(t);return n!==null&&r!==null&&n[1]===r[1]}var fY="https://intentius.io/chant/schemas/workspace/change-set/v1/change-set.schema.json",gY=1,mY=["create","update","replace","delete","read","no-op","forget"];var Pi=(e,t)=>e<t?-1:e>t?1:0;function Ay(e){return`${e.member}\0${e.address}\0${e.deposed??""}\0${e.action}`}var KI=(e,t)=>Pi(e.member,t.member)||Pi(e.address,t.address)||Pi(e.trigger??"",t.trigger??"");function Wa(e){let t=e.filter(n=>n.provisional!==!0).map(n=>({member:n.member,planDigest:n.planDigest})).sort((n,r)=>Pi(n.member,r.member));for(let n=1;n<t.length;n++)if(t[n-1].member===t[n].member)throw new Error(`the change set names member ${t[n].member} twice`);return Rn("change-set",t)}function hY(e){let t=new Set(e.members.filter(s=>s.provisional===!0).map(s=>s.member)),n=e.members.filter(s=>!t.has(s.member)).map(s=>({member:s.member,status:s.status,holes:s.holes})).sort((s,i)=>Pi(s.member,i.member)),r=e.entries.filter(s=>!t.has(s.member)).sort((s,i)=>Pi(Ay(s),Ay(i))),o=(e.sideEffects??[]).filter(s=>!t.has(s.member)).sort(KI);return Rn("change-set-document",{set:Wa(e.members),members:n,entries:r,sideEffects:o})}function yY(e,t){let n=Object.fromEntries(mY.map(c=>[c,0])),r={},o={},s=[],i=[],a=(c,u,l)=>{let d=c[u]??={};d[l]=(d[l]??0)+1};for(let c of t){n[c.action]++,a(r,c.type,c.action),a(o,c.member,c.action);let u={member:c.member,address:c.address,type:c.type,...c.deposed!==void 0?{deposed:c.deposed}:{},...c.disruption!==void 0?{disruption:c.disruption}:{}};c.action==="delete"&&s.push(u),c.action==="replace"&&i.push(u)}return{members:e.length,entries:t.length,actions:n,types:JI(r),byMember:JI(o),deletes:s,replacements:i,failed:e.filter(c=>c.status==="failed").map(c=>c.member),holes:e.reduce((c,u)=>c+u.holes.length,0)}}function JI(e){return Object.fromEntries(Object.entries(e).sort(([t],[n])=>Pi(t,n)))}function nS(e,t={}){let n=e.map(i=>i.member).sort((i,a)=>Pi(i.member,a.member)),r=[];for(let i of e)for(let a of i.entries){if(a.member!==i.member.member)throw new Error(`an entry at ${a.address} names member ${a.member}, inside member ${i.member.member}`);r.push(a)}r.sort((i,a)=>Pi(Ay(i),Ay(a)));let o=e.flatMap(i=>(i.sideEffects??[]).map(a=>({member:i.member.member,...a}))).sort(KI),s=hY({members:n,entries:r,sideEffects:o});return{$schema:fY,contract:gY,...t.chant!==void 0?{chant:t.chant}:{},digest:s,members:n,entries:r,...o.length>0?{sideEffects:o}:{},summary:yY(n,r)}}var rS=class extends Error{constructor(n,r){super(`canary names "${n}", which is not in this set (known: ${r.join(", ")||"none"})`);this.canary=n;this.name="UnknownCanaryError"}canary},oS=class extends Error{constructor(n){super(`dependency cycle among: ${n.join(", ")}`);this.members=n;this.name="WaveCycleError"}members};function VI(e,t={}){let n=new Set(e.map(c=>c.name)),r=new Set([...n,...t.known??[]]);for(let c of t.canary??[])if(!r.has(c))throw new rS(c,[...r].sort());let o=[...new Set((t.canary??[]).filter(c=>n.has(c)))].sort(),s=o.length>0?[o]:[],i=new Set([...n].filter(c=>!o.includes(c))),a=new Map(e.map(c=>[c.name,(c.dependsOn??[]).filter(u=>i.has(u))]));for(;i.size>0;){let c=[...i].filter(u=>a.get(u).every(l=>!i.has(l))).sort();if(c.length===0)throw new oS([...i].sort());for(let u of c)i.delete(u);s.push(c)}return s}var sS=class extends Error{constructor(n){super(`a wave cannot be gated on a provisional plan: ${n.join(", ")} planned before what it reads applied. Plan it again in its own wave, after its upstream applied`);this.members=n;this.name="ProvisionalWaveMemberError"}members};function mu(e){let t=e.filter(n=>n.provisional===!0).map(n=>n.member);if(t.length>0)throw new sS([...new Set(t)].sort());return Wa(e)}var ZI="1.1.0",hu=["catalog/**","**/.terragrunt-cache/**"];function wY(e){let t=/v?(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?/.exec(e);if(t)return{major:Number(t[1]),minor:Number(t[2]),patch:Number(t[3]),...t[4]?{pre:t[4]}:{},raw:t[0]}}function QI(e){let t=wY(e);if(!t)return`could not read a version from \`terragrunt --version\` (got ${JSON.stringify(e.trim().slice(0,80))})`;let[n,r]=ZI.split(".").map(Number);if(!(t.major>n||t.major===n&&t.minor>=r))return`Terragrunt ${t.raw} is older than ${ZI}, the oldest release chant runs units with (run --all, find --dag and --filter as 1.1 ships them)`}function eC(e){return`{./${e}}`}function hf(e){return`!./${e.replace(/^\.\//,"")}`}function tC(e={}){return["find","--json","--dag","--dependencies","--include","--reading","--no-color",...[...hu,...e.exclude??[]].flatMap(n=>["--filter",hf(n)])]}var YI=e=>e!==null&&typeof e=="object"&&!Array.isArray(e),XI=e=>Array.isArray(e)?e.filter(t=>typeof t=="string"):[],Hl=e=>e.replace(/\\/g,"/").replace(/^\.\//,"").replace(/\/+$/,"");function Py(e){let t=typeof e=="string"?JSON.parse(e.trim()===""?"[]":e):e;if(!Array.isArray(t))throw new Error("terragrunt find --json did not print an array");let n=[];for(let r of t){if(!YI(r)||r.type!=="unit"||typeof r.path!="string")continue;let o=YI(r.include)?Object.fromEntries(Object.entries(r.include).filter(i=>typeof i[1]=="string").map(([i,a])=>[i,Hl(a)])):void 0,s=Array.isArray(r.reading)?XI(r.reading).map(Hl):void 0;n.push({path:Hl(r.path),dependencies:[...new Set(XI(r.dependencies).map(Hl))].sort(),...o&&Object.keys(o).length>0?{include:o}:{},...s?{reading:s}:{}})}return n}function jy(e){let t=e.lastIndexOf("/");return t<=0?".":e.slice(0,t)}function ql(e,t){let n=Hl(t),r="";for(let o=0;o<n.length;o++){let s=n[o];s==="*"&&n[o+1]==="*"?n[o+2]==="/"?(r+="(?:.*/)?",o+=2):(r+=".*",o+=1):s==="*"?r+="[^/]*":s==="?"?r+="[^/]":r+=s.replace(/[.+^${}()|[\]\\]/g,"\\$&")}return new RegExp(`^${r}$`).test(e)}function bY(e){return e.map(t=>({name:t.path,dependsOn:t.dependencies}))}var Oy=class extends Error{constructor(t){super(t),this.name="TerragruntWaveError"}};function iS(e,t={}){let n=new Set((t.canary??[]).flatMap(i=>e.filter(a=>ql(a.path,i)).map(a=>a.path))),r=new Set(e.map(i=>i.path));for(let i of e){if(!n.has(i.path))continue;let a=i.dependencies.filter(c=>r.has(c)&&!n.has(c));if(a.length>0)throw new Oy(`canary ${i.path} depends on ${a.join(", ")}, which is not a canary, so the canary wave would apply it first`)}let o=bY(e),s=i=>{try{return VI(i)}catch(a){throw new Oy(a instanceof Error?a.message:String(a))}};return[...s(o.filter(i=>n.has(i.name))),...s(o.filter(i=>!n.has(i.name)))]}function aS(e,t,n=r=>r.dependencies){let r=new Set(t.map(Hl)),o=new Set(r),s=!0;for(;s;){s=!1;for(let i of e)o.has(i.path)||n(i).some(a=>o.has(a))&&(o.add(i.path),s=!0)}return[...o].filter(i=>!r.has(i)).sort()}var Vl=class extends Error{reason;mark;constructor(t,n){super(t),this.name="YAMLException",this.reason=t,this.mark=n}},yu={isObject:e=>typeof e=="object"&&e!==null,repeat:(e,t)=>e.repeat(Math.max(0,t))};var lS={tag:"tag:yaml.org,2002:null",kind:"scalar",resolve:e=>e===null||e==="~"||e==="null"||e==="Null"||e==="NULL",construct:()=>null},vY=e=>e>=48&&e<=57||e>=65&&e<=70||e>=97&&e<=102,$Y=e=>e>=48&&e<=55,kY=e=>e>=48&&e<=57;function _Y(e){if(e===null)return!1;let t=e.length,n=0,r=!1;if(!t)return!1;let o=e[n];if((o==="-"||o==="+")&&(o=e[++n]),o==="0"){if(n+1===t)return!0;o=e[++n];let s={b:i=>i===48||i===49,x:vY,o:$Y}[o];if(s){for(n++;n<t;n++){if(!s(e.charCodeAt(n)))return!1;r=!0}return r&&isFinite(uS(e))}}for(;n<t;n++){if(!kY(e.charCodeAt(n)))return!1;r=!0}return r?isFinite(uS(e)):!1}function uS(e){let t=e,n=1,r=t[0];if((r==="-"||r==="+")&&(r==="-"&&(n=-1),t=t.slice(1),r=t[0]),t==="0")return 0;if(r==="0"){if(t[1]==="b")return n*parseInt(t.slice(2),2);if(t[1]==="x")return n*parseInt(t.slice(2),16);if(t[1]==="o")return n*parseInt(t.slice(2),8)}return n*parseInt(t,10)}var dS={tag:"tag:yaml.org,2002:int",kind:"scalar",resolve:_Y,construct:uS},SY=new RegExp("^(?:[-+]?(?:[0-9]+)(?:\\.[0-9]*)?(?:[eE][-+]?[0-9]+)?|\\.[0-9]+(?:[eE][-+]?[0-9]+)?|[-+]?\\.(?:inf|Inf|INF)|\\.(?:nan|NaN|NAN))$"),RY=new RegExp("^(?:[-+]?\\.(?:inf|Inf|INF)|\\.(?:nan|NaN|NAN))$");function xY(e){return e===null||!SY.test(e)?!1:isFinite(parseFloat(e,10))?!0:RY.test(e)}var pS={tag:"tag:yaml.org,2002:float",kind:"scalar",resolve:xY,construct:e=>{let t=e.toLowerCase(),n=t[0]==="-"?-1:1;return"+-".indexOf(t[0])>=0&&(t=t.slice(1)),t===".inf"?n===1?Number.POSITIVE_INFINITY:Number.NEGATIVE_INFINITY:t===".nan"?NaN:n*parseFloat(t,10)}},fS={tag:"tag:yaml.org,2002:str",kind:"scalar",construct:e=>e!==null?e:""},gS={tag:"tag:yaml.org,2002:seq",kind:"sequence",construct:e=>e!==null?e:[]},mS={tag:"tag:yaml.org,2002:map",kind:"mapping",construct:e=>e!==null?e:{}};for(let e of[lS,dS,pS,fS,gS,mS])e.resolve??=()=>!0;function EY(e,t){let n={scalar:{},sequence:{},mapping:{},fallback:{},multi:{scalar:[],sequence:[],mapping:[],fallback:[]}};for(let r of[...e,...t])r.resolve??=()=>!0,r.construct??=o=>o,r.multi?(n.multi[r.kind].push(r),n.multi.fallback.push(r)):n[r.kind][r.tag]=n.fallback[r.tag]=r;return n}var Ii=Object.prototype.hasOwnProperty,Iy=1,lC=2,dC=3,Cy=4,cS=1,TY=2,nC=3,AY=/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F-\x84\x86-\x9F\uFFFE\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]/,OY=/[,\[\]{}]/,pC=/^(?:!|!!|![0-9A-Za-z-]+!)$/,fC=/^(?:!|[^,\[\]{}])(?:%[0-9a-f]{2}|[0-9a-z\-#;/?:@&=+$,_.!~*'()\[\]])*$/i;function rC(e){return Object.prototype.toString.call(e)}function zs(e){return e===10||e===13}function ji(e){return e===9||e===32}function Br(e){return e===9||e===32||e===10||e===13}function Jl(e){return e===44||e===91||e===93||e===123||e===125}function PY(e){if(e>=48&&e<=57)return e-48;let t=e|32;return t>=97&&t<=102?t-97+10:-1}function jY(e){return e===120?2:e===117?4:e===85?8:0}function IY(e){return e>=48&&e<=57?e-48:-1}function oC(e){switch(e){case 48:return"\0";case 97:return"\x07";case 98:return"\b";case 116:return" ";case 9:return" ";case 110:return`
|
|
4
|
+
var cY=Object.defineProperty;var WI=(e,t)=>{for(var n in t)cY(e,n,{get:t[n],enumerable:!0})};import{spawnSync as MOe}from"node:child_process";import{existsSync as J9,readFileSync as X_,realpathSync as K9,unlinkSync as LOe,writeFileSync as V9}from"node:fs";import{dirname as Q_,join as Z9,relative as eS,resolve as Oe}from"node:path";import{fileURLToPath as GOe}from"node:url";import{execFileSync as sD}from"node:child_process";import{existsSync as Jr,readFileSync as Mn,readdirSync as Ew,statSync as JR}from"node:fs";import{dirname as _w,isAbsolute as Z7,join as Pt,relative as xw,resolve as Sw}from"node:path";import{createHash as uY}from"node:crypto";function mf(e){return`sha256:${uY("sha256").update(e,"utf8").digest("hex")}`}var gu=Symbol.for("chant.declarable");function Xe(e){return"props"in e}var lY=".declarable";function HI(e){return typeof e!="object"||e===null?!1:e[gu]===!0?!0:dY(e)}function dY(e){for(let t of Object.getOwnPropertySymbols(e)){let n=Object.getOwnPropertyDescriptor(e,t);if(!(n===void 0||n.enumerable||n.value!==!0)&&t.description?.endsWith(lY)===!0)return!0}return!1}function Wl(e){return e.kind==="property"}var Fs=Symbol.for("chant.intrinsic");function Ey(e){let t=tS(e,"$",new Set);if(t===void 0)throw new Error("canonicalJson: value at $ is not representable in JSON");return t}function tS(e,t,n){if(e!==null&&(typeof e=="object"||typeof e=="function")){let o=e.toJSON;typeof o=="function"&&(e=o.call(e))}if(e===void 0)return;if(e===null)return"null";switch(typeof e){case"boolean":return e?"true":"false";case"number":if(!Number.isFinite(e))throw new Error(`canonicalJson: non-finite number at ${t}`);return String(e);case"string":return JSON.stringify(e);case"bigint":throw new Error(`canonicalJson: bigint at ${t} is not representable in JSON`);case"function":case"symbol":throw new Error(`canonicalJson: ${typeof e} at ${t} is not representable in JSON`)}let r=e;if(n.has(r))throw new Error(`canonicalJson: circular structure at ${t}`);n.add(r);try{if(Array.isArray(r))return`[${r.map((a,c)=>tS(a,`${t}[${c}]`,n)??"null").join(",")}]`;let o=Object.keys(r).sort(),s=[];for(let i of o){let a=tS(r[i],`${t}.${i}`,n);a!==void 0&&s.push(`${JSON.stringify(i)}:${a}`)}return`{${s.join(",")}}`}finally{n.delete(r)}}var Ty="jcs1-sha256:",pY="sha256:",qI=/^(?:jcs1-)?sha256:([0-9a-f]{64})$/;function Rn(e,t){return Ty+mf(Ey({kind:e,subject:t})).slice(pY.length)}function Ie(e,t){if(e===t)return!0;if(e===void 0||t===void 0)return!1;let n=qI.exec(e),r=qI.exec(t);return n!==null&&r!==null&&n[1]===r[1]}var fY="https://intentius.io/chant/schemas/workspace/change-set/v1/change-set.schema.json",gY=1,mY=["create","update","replace","delete","read","no-op","forget"];var Pi=(e,t)=>e<t?-1:e>t?1:0;function Ay(e){return`${e.member}\0${e.address}\0${e.deposed??""}\0${e.action}`}var KI=(e,t)=>Pi(e.member,t.member)||Pi(e.address,t.address)||Pi(e.trigger??"",t.trigger??"");function Wa(e){let t=e.filter(n=>n.provisional!==!0).map(n=>({member:n.member,planDigest:n.planDigest})).sort((n,r)=>Pi(n.member,r.member));for(let n=1;n<t.length;n++)if(t[n-1].member===t[n].member)throw new Error(`the change set names member ${t[n].member} twice`);return Rn("change-set",t)}function hY(e){let t=new Set(e.members.filter(s=>s.provisional===!0).map(s=>s.member)),n=e.members.filter(s=>!t.has(s.member)).map(s=>({member:s.member,status:s.status,holes:s.holes})).sort((s,i)=>Pi(s.member,i.member)),r=e.entries.filter(s=>!t.has(s.member)).sort((s,i)=>Pi(Ay(s),Ay(i))),o=(e.sideEffects??[]).filter(s=>!t.has(s.member)).sort(KI);return Rn("change-set-document",{set:Wa(e.members),members:n,entries:r,sideEffects:o})}function yY(e,t){let n=Object.fromEntries(mY.map(c=>[c,0])),r={},o={},s=[],i=[],a=(c,u,l)=>{let d=c[u]??={};d[l]=(d[l]??0)+1};for(let c of t){n[c.action]++,a(r,c.type,c.action),a(o,c.member,c.action);let u={member:c.member,address:c.address,type:c.type,...c.deposed!==void 0?{deposed:c.deposed}:{},...c.disruption!==void 0?{disruption:c.disruption}:{}};c.action==="delete"&&s.push(u),c.action==="replace"&&i.push(u)}return{members:e.length,entries:t.length,actions:n,types:JI(r),byMember:JI(o),deletes:s,replacements:i,failed:e.filter(c=>c.status==="failed").map(c=>c.member),holes:e.reduce((c,u)=>c+u.holes.length,0)}}function JI(e){return Object.fromEntries(Object.entries(e).sort(([t],[n])=>Pi(t,n)))}function nS(e,t={}){let n=e.map(i=>i.member).sort((i,a)=>Pi(i.member,a.member)),r=[];for(let i of e)for(let a of i.entries){if(a.member!==i.member.member)throw new Error(`an entry at ${a.address} names member ${a.member}, inside member ${i.member.member}`);r.push(a)}r.sort((i,a)=>Pi(Ay(i),Ay(a)));let o=e.flatMap(i=>(i.sideEffects??[]).map(a=>({member:i.member.member,...a}))).sort(KI),s=hY({members:n,entries:r,sideEffects:o});return{$schema:fY,contract:gY,...t.chant!==void 0?{chant:t.chant}:{},digest:s,members:n,entries:r,...o.length>0?{sideEffects:o}:{},summary:yY(n,r)}}var rS=class extends Error{constructor(n,r){super(`canary names "${n}", which is not in this set (known: ${r.join(", ")||"none"})`);this.canary=n;this.name="UnknownCanaryError"}canary},oS=class extends Error{constructor(n){super(`dependency cycle among: ${n.join(", ")}`);this.members=n;this.name="WaveCycleError"}members};function VI(e,t={}){let n=new Set(e.map(c=>c.name)),r=new Set([...n,...t.known??[]]);for(let c of t.canary??[])if(!r.has(c))throw new rS(c,[...r].sort());let o=[...new Set((t.canary??[]).filter(c=>n.has(c)))].sort(),s=o.length>0?[o]:[],i=new Set([...n].filter(c=>!o.includes(c))),a=new Map(e.map(c=>[c.name,(c.dependsOn??[]).filter(u=>i.has(u))]));for(;i.size>0;){let c=[...i].filter(u=>a.get(u).every(l=>!i.has(l))).sort();if(c.length===0)throw new oS([...i].sort());for(let u of c)i.delete(u);s.push(c)}return s}var sS=class extends Error{constructor(n){super(`a wave cannot be gated on a provisional plan: ${n.join(", ")} planned before what it reads applied. Plan it again in its own wave, after its upstream applied`);this.members=n;this.name="ProvisionalWaveMemberError"}members};function mu(e){let t=e.filter(n=>n.provisional===!0).map(n=>n.member);if(t.length>0)throw new sS([...new Set(t)].sort());return Wa(e)}var ZI="1.1.0",hu=["catalog/**","**/.terragrunt-cache/**"];function wY(e){let t=/v?(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?/.exec(e);if(t)return{major:Number(t[1]),minor:Number(t[2]),patch:Number(t[3]),...t[4]?{pre:t[4]}:{},raw:t[0]}}function QI(e){let t=wY(e);if(!t)return`could not read a version from \`terragrunt --version\` (got ${JSON.stringify(e.trim().slice(0,80))})`;let[n,r]=ZI.split(".").map(Number);if(!(t.major>n||t.major===n&&t.minor>=r))return`Terragrunt ${t.raw} is older than ${ZI}, the oldest release chant runs units with (run --all, find --dag and --filter as 1.1 ships them)`}function eC(e){return`{./${e}}`}function hf(e){return`!./${e.replace(/^\.\//,"")}`}function tC(e={}){return["find","--json","--dag","--dependencies","--include","--reading","--no-color",...[...hu,...e.exclude??[]].flatMap(n=>["--filter",hf(n)])]}var YI=e=>e!==null&&typeof e=="object"&&!Array.isArray(e),XI=e=>Array.isArray(e)?e.filter(t=>typeof t=="string"):[],Hl=e=>e.replace(/\\/g,"/").replace(/^\.\//,"").replace(/\/+$/,"");function Py(e){let t=typeof e=="string"?JSON.parse(e.trim()===""?"[]":e):e;if(!Array.isArray(t))throw new Error("terragrunt find --json did not print an array");let n=[];for(let r of t){if(!YI(r)||r.type!=="unit"||typeof r.path!="string")continue;let o=YI(r.include)?Object.fromEntries(Object.entries(r.include).filter(i=>typeof i[1]=="string").map(([i,a])=>[i,Hl(a)])):void 0,s=Array.isArray(r.reading)?XI(r.reading).map(Hl):void 0;n.push({path:Hl(r.path),dependencies:[...new Set(XI(r.dependencies).map(Hl))].sort(),...o&&Object.keys(o).length>0?{include:o}:{},...s?{reading:s}:{}})}return n}function jy(e){let t=e.lastIndexOf("/");return t<=0?".":e.slice(0,t)}function ql(e,t){let n=Hl(t),r="";for(let o=0;o<n.length;o++){let s=n[o];s==="*"&&n[o+1]==="*"?n[o+2]==="/"?(r+="(?:.*/)?",o+=2):(r+=".*",o+=1):s==="*"?r+="[^/]*":s==="?"?r+="[^/]":r+=s.replace(/[.+^${}()|[\]\\]/g,"\\$&")}return new RegExp(`^${r}$`).test(e)}function bY(e){return e.map(t=>({name:t.path,dependsOn:t.dependencies}))}var Oy=class extends Error{constructor(t){super(t),this.name="TerragruntWaveError"}};function iS(e,t={}){let n=new Set((t.canary??[]).flatMap(i=>e.filter(a=>ql(a.path,i)).map(a=>a.path))),r=new Set(e.map(i=>i.path));for(let i of e){if(!n.has(i.path))continue;let a=i.dependencies.filter(c=>r.has(c)&&!n.has(c));if(a.length>0)throw new Oy(`canary ${i.path} depends on ${a.join(", ")}, which is not a canary, so the canary wave would apply it first`)}let o=bY(e),s=i=>{try{return VI(i)}catch(a){throw new Oy(a instanceof Error?a.message:String(a))}};return[...s(o.filter(i=>n.has(i.name))),...s(o.filter(i=>!n.has(i.name)))]}function aS(e,t,n=r=>r.dependencies){let r=new Set(t.map(Hl)),o=new Set(r),s=!0;for(;s;){s=!1;for(let i of e)o.has(i.path)||n(i).some(a=>o.has(a))&&(o.add(i.path),s=!0)}return[...o].filter(i=>!r.has(i)).sort()}var Vl=class extends Error{reason;mark;constructor(t,n){super(t),this.name="YAMLException",this.reason=t,this.mark=n}},yu={isObject:e=>typeof e=="object"&&e!==null,repeat:(e,t)=>e.repeat(Math.max(0,t))};var lS={tag:"tag:yaml.org,2002:null",kind:"scalar",resolve:e=>e===null||e==="~"||e==="null"||e==="Null"||e==="NULL",construct:()=>null},vY=e=>e>=48&&e<=57||e>=65&&e<=70||e>=97&&e<=102,$Y=e=>e>=48&&e<=55,kY=e=>e>=48&&e<=57;function _Y(e){if(e===null)return!1;let t=e.length,n=0,r=!1;if(!t)return!1;let o=e[n];if((o==="-"||o==="+")&&(o=e[++n]),o==="0"){if(n+1===t)return!0;o=e[++n];let s={b:i=>i===48||i===49,x:vY,o:$Y}[o];if(s){for(n++;n<t;n++){if(!s(e.charCodeAt(n)))return!1;r=!0}return r&&isFinite(uS(e))}}for(;n<t;n++){if(!kY(e.charCodeAt(n)))return!1;r=!0}return r?isFinite(uS(e)):!1}function uS(e){let t=e,n=1,r=t[0];if((r==="-"||r==="+")&&(r==="-"&&(n=-1),t=t.slice(1),r=t[0]),t==="0")return 0;if(r==="0"){if(t[1]==="b")return n*parseInt(t.slice(2),2);if(t[1]==="x")return n*parseInt(t.slice(2),16);if(t[1]==="o")return n*parseInt(t.slice(2),8)}return n*parseInt(t,10)}var dS={tag:"tag:yaml.org,2002:int",kind:"scalar",resolve:_Y,construct:uS},SY=new RegExp("^(?:[-+]?(?:[0-9]+)(?:\\.[0-9]*)?(?:[eE][-+]?[0-9]+)?|\\.[0-9]+(?:[eE][-+]?[0-9]+)?|[-+]?\\.(?:inf|Inf|INF)|\\.(?:nan|NaN|NAN))$"),RY=new RegExp("^(?:[-+]?\\.(?:inf|Inf|INF)|\\.(?:nan|NaN|NAN))$");function xY(e){return e===null||!SY.test(e)?!1:isFinite(parseFloat(e,10))?!0:RY.test(e)}var pS={tag:"tag:yaml.org,2002:float",kind:"scalar",resolve:xY,construct:e=>{let t=e.toLowerCase(),n=t[0]==="-"?-1:1;return"+-".indexOf(t[0])>=0&&(t=t.slice(1)),t===".inf"?n===1?Number.POSITIVE_INFINITY:Number.NEGATIVE_INFINITY:t===".nan"?NaN:n*parseFloat(t,10)}},fS={tag:"tag:yaml.org,2002:str",kind:"scalar",construct:e=>e!==null?e:""},gS={tag:"tag:yaml.org,2002:seq",kind:"sequence",construct:e=>e!==null?e:[]},mS={tag:"tag:yaml.org,2002:map",kind:"mapping",construct:e=>e!==null?e:{}};for(let e of[lS,dS,pS,fS,gS,mS])e.resolve??=()=>!0;function EY(e,t){let n={scalar:{},sequence:{},mapping:{},fallback:{},multi:{scalar:[],sequence:[],mapping:[],fallback:[]}};for(let r of[...e,...t])r.resolve??=()=>!0,r.construct??=o=>o,r.multi?(n.multi[r.kind].push(r),n.multi.fallback.push(r)):n[r.kind][r.tag]=n.fallback[r.tag]=r;return n}var Ii=Object.prototype.hasOwnProperty,Iy=1,lC=2,dC=3,Cy=4,cS=1,TY=2,nC=3,AY=/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F-\x84\x86-\x9F\uFFFE\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]/,OY=/[,\[\]{}]/,pC=/^(?:!|!!|![0-9A-Za-z-]+!)$/,fC=/^(?:!|[^,\[\]{}])(?:%[0-9a-f]{2}|[0-9a-z\-#;/?:@&=+$,_.!~*'()\[\]])*$/i;function rC(e){return Object.prototype.toString.call(e)}function zs(e){return e===10||e===13}function ji(e){return e===9||e===32}function Br(e){return e===9||e===32||e===10||e===13}function Jl(e){return e===44||e===91||e===93||e===123||e===125}function PY(e){if(e>=48&&e<=57)return e-48;let t=e|32;return t>=97&&t<=102?t-97+10:-1}function jY(e){return e===120?2:e===117?4:e===85?8:0}function IY(e){return e>=48&&e<=57?e-48:-1}function oC(e){switch(e){case 48:return"\0";case 97:return"\x07";case 98:return"\b";case 116:return" ";case 9:return" ";case 110:return`
|
|
5
5
|
`;case 118:return"\v";case 102:return"\f";case 114:return"\r";case 101:return"\x1B";case 32:return" ";case 34:return'"';case 47:return"/";case 92:return"\\";case 78:return"\x85";case 95:return"\xA0";case 76:return"\u2028";case 80:return"\u2029";default:return""}}function CY(e){return e<=65535?String.fromCharCode(e):String.fromCharCode((e-65536>>10)+55296,(e-65536&1023)+56320)}function gC(e,t,n){t==="__proto__"?Object.defineProperty(e,t,{configurable:!0,enumerable:!0,writable:!0,value:n}):e[t]=n}var mC=new Array(256),hC=new Array(256);for(let e=0;e<256;e++)mC[e]=oC(e)?1:0,hC[e]=oC(e);function NY(e,t){this.input=e,this.implicitTypes=t.implicit,this.typeMap=EY(t.implicit,t.explicit),this.maxDepth=100,this.maxTotalMergeKeys=1e4,this.length=e.length,this.position=0,this.line=0,this.lineStart=0,this.lineIndent=0,this.depth=0,this.totalMergeKeys=0,this.firstTabInLine=-1,this.documents=[],this.anchorMapTransactions=[]}function DY(e,t){let n={name:null,buffer:e.input.slice(0,-1),position:e.position,line:e.line,column:e.position-e.lineStart};return new Vl(t,n)}function we(e,t){throw DY(e,t)}function wu(e,t,n){let r=e.anchorMapTransactions;if(r.length!==0){let o=r[r.length-1];Ii.call(o,t)||(o[t]={existed:Ii.call(e.anchorMap,t),value:e.anchorMap[t]})}e.anchorMap[t]=n}function MY(e){e.anchorMapTransactions.push(Object.create(null))}function LY(e){let t=e.anchorMapTransactions.pop(),n=e.anchorMapTransactions;if(n.length===0)return;let r=n[n.length-1],o=Object.keys(t);for(let s=0,i=o.length;s<i;s+=1){let a=o[s];Ii.call(r,a)||(r[a]=t[a])}}function GY(e){let t=e.anchorMapTransactions.pop(),n=Object.keys(t);for(let r=n.length-1;r>=0;r-=1){let o=t[n[r]];o.existed?e.anchorMap[n[r]]=o.value:delete e.anchorMap[n[r]]}}function yC(e){return{position:e.position,line:e.line,lineStart:e.lineStart,lineIndent:e.lineIndent,firstTabInLine:e.firstTabInLine,tag:e.tag,anchor:e.anchor,kind:e.kind,result:e.result}}function sC(e,t){e.position=t.position,e.line=t.line,e.lineStart=t.lineStart,e.lineIndent=t.lineIndent,e.firstTabInLine=t.firstTabInLine,e.tag=t.tag,e.anchor=t.anchor,e.kind=t.kind,e.result=t.result}var iC={YAML:function(t,n,r){t.version!==null&&we(t,"duplication of %YAML directive"),r.length!==1&&we(t,"YAML directive accepts exactly one argument");let o=/^([0-9]+)\.([0-9]+)$/.exec(r[0]);o===null&&we(t,"ill-formed argument of the YAML directive"),parseInt(o[1],10)!==1&&we(t,"unacceptable YAML version of the document"),t.version=r[0]},TAG:function(t,n,r){let o;r.length!==2&&we(t,"TAG directive accepts exactly two arguments");let s=r[0];o=r[1],pC.test(s)||we(t,"ill-formed tag handle (first argument) of the TAG directive"),Ii.call(t.tagMap,s)&&we(t,'there is a previously declared suffix for "'+s+'" tag handle'),fC.test(o)||we(t,"ill-formed tag prefix (second argument) of the TAG directive");try{o=decodeURIComponent(o)}catch{we(t,"tag prefix is malformed: "+o)}t.tagMap[s]=o}};function Ha(e,t,n,r){if(t<n){let o=e.input.slice(t,n);if(r)for(let s=0,i=o.length;s<i;s+=1){let a=o.charCodeAt(s);a===9||a>=32&&a<=1114111||we(e,"expected valid JSON character")}else AY.test(o)&&we(e,"the stream contains non-printable characters");e.result+=o}}function aC(e){e.totalMergeKeys++,e.maxTotalMergeKeys!==-1&&e.totalMergeKeys>e.maxTotalMergeKeys&&we(e,"merge keys exceeded maxTotalMergeKeys ("+e.maxTotalMergeKeys+")")}function cC(e,t,n,r){yu.isObject(n)||we(e,"cannot merge mappings; the provided source object is unacceptable"),aC(e);let o=Object.keys(n);for(let s=0,i=o.length;s<i;s+=1){let a=o[s];aC(e),Ii.call(t,a)||(gC(t,a,n[a]),r[a]=!0)}}function Kl(e,t,n,r,o,s){if(Array.isArray(o)){o=Array.prototype.slice.call(o);for(let i=0,a=o.length;i<a;i+=1)Array.isArray(o[i])&&we(e,"nested arrays are not supported inside keys"),typeof o=="object"&&rC(o[i])==="[object Object]"&&(o[i]="[object Object]")}if(typeof o=="object"&&rC(o)==="[object Object]"&&(o="[object Object]"),o=String(o),t===null&&(t={}),r==="tag:yaml.org,2002:merge")if(Array.isArray(s)){s.length>100&&we(e,"abnormal merge sequence size");for(let i=0,a=s.length;i<a;i+=1)cC(e,t,s[i],n)}else cC(e,t,s,n);else gC(t,o,s),delete n[o];return t}function hS(e){let t=e.input.charCodeAt(e.position);t===10?e.position++:t===13?(e.position++,e.input.charCodeAt(e.position)===10&&e.position++):we(e,"a line break is expected"),e.line+=1,e.lineStart=e.position,e.firstTabInLine=-1}function mn(e,t,n){let r=0,o=e.input.charCodeAt(e.position);for(;o!==0;){for(;ji(o);)o===9&&e.firstTabInLine===-1&&(e.firstTabInLine=e.position),o=e.input.charCodeAt(++e.position);if(t&&o===35)do o=e.input.charCodeAt(++e.position);while(o!==10&&o!==13&&o!==0);if(zs(o))for(hS(e),o=e.input.charCodeAt(e.position),r++,e.lineIndent=0;o===32;)e.lineIndent++,o=e.input.charCodeAt(++e.position);else break}return r}function Ny(e){let t=e.position,n=e.input.charCodeAt(t);return!!((n===45||n===46)&&n===e.input.charCodeAt(t+1)&&n===e.input.charCodeAt(t+2)&&(t+=3,n=e.input.charCodeAt(t),n===0||Br(n)))}function yS(e,t){t===1?e.result+=" ":t>1&&(e.result+=yu.repeat(`
|
|
6
6
|
`,t-1))}function FY(e,t,n){let r,o,s,i,a,c,u=e.kind,l=e.result,d=e.input.charCodeAt(e.position);if(Br(d)||Jl(d)||d===35||d===38||d===42||d===33||d===124||d===62||d===39||d===34||d===37||d===64||d===96)return!1;if(d===63||d===45){let p=e.input.charCodeAt(e.position+1);if(Br(p)||n&&Jl(p))return!1}for(e.kind="scalar",e.result="",r=o=e.position,s=!1;d!==0;){if(d===58){let p=e.input.charCodeAt(e.position+1);if(Br(p)||n&&Jl(p))break}else if(d===35){let p=e.input.charCodeAt(e.position-1);if(Br(p))break}else{if(e.position===e.lineStart&&Ny(e)||n&&Jl(d))break;if(zs(d))if(i=e.line,a=e.lineStart,c=e.lineIndent,mn(e,!1,-1),e.lineIndent>=t){s=!0,d=e.input.charCodeAt(e.position);continue}else{e.position=o,e.line=i,e.lineStart=a,e.lineIndent=c;break}}s&&(Ha(e,r,o,!1),yS(e,e.line-i),r=o=e.position,s=!1),ji(d)||(o=e.position+1),d=e.input.charCodeAt(++e.position)}return Ha(e,r,o,!1),e.result?!0:(e.kind=u,e.result=l,!1)}function zY(e,t){let n,r,o=e.input.charCodeAt(e.position);if(o!==39)return!1;for(e.kind="scalar",e.result="",e.position++,n=r=e.position;(o=e.input.charCodeAt(e.position))!==0;)if(o===39)if(Ha(e,n,e.position,!0),o=e.input.charCodeAt(++e.position),o===39)n=e.position,e.position++,r=e.position;else return!0;else zs(o)?(Ha(e,n,r,!0),yS(e,mn(e,!1,t)),n=r=e.position):e.position===e.lineStart&&Ny(e)?we(e,"unexpected end of the document within a single quoted scalar"):(e.position++,ji(o)||(r=e.position));we(e,"unexpected end of the stream within a single quoted scalar")}function UY(e,t){let n,r,o,s=e.input.charCodeAt(e.position);if(s!==34)return!1;for(e.kind="scalar",e.result="",e.position++,n=r=e.position;(s=e.input.charCodeAt(e.position))!==0;){if(s===34)return Ha(e,n,e.position,!0),e.position++,!0;if(s===92){if(Ha(e,n,e.position,!0),s=e.input.charCodeAt(++e.position),zs(s))mn(e,!1,t);else if(s<256&&mC[s])e.result+=hC[s],e.position++;else if((o=jY(s))>0){let i=o,a=0;for(;i>0;i--)s=e.input.charCodeAt(++e.position),(o=PY(s))>=0?a=(a<<4)+o:we(e,"expected hexadecimal character");e.result+=CY(a),e.position++}else we(e,"unknown escape sequence");n=r=e.position}else zs(s)?(Ha(e,n,r,!0),yS(e,mn(e,!1,t)),n=r=e.position):e.position===e.lineStart&&Ny(e)?we(e,"unexpected end of the document within a double quoted scalar"):(e.position++,ji(s)||(r=e.position))}we(e,"unexpected end of the stream within a double quoted scalar")}function BY(e,t){let n=!0,r,o=e.tag,s,i=e.anchor,a,c,u,l,d=Object.create(null),p,f,g,m=e.input.charCodeAt(e.position);if(m===91)a=93,l=!1,s=[];else if(m===123)a=125,l=!0,s={};else return!1;for(e.anchor!==null&&wu(e,e.anchor,s),m=e.input.charCodeAt(++e.position);m!==0;){if(mn(e,!0,t),m=e.input.charCodeAt(e.position),m===a)return e.position++,e.tag=o,e.anchor=i,e.kind=l?"mapping":"sequence",e.result=s,!0;if(n?m===44&&we(e,"expected the node content, but found ','"):we(e,"missed comma between flow collection entries"),f=p=g=null,c=u=!1,m===63){let h=e.input.charCodeAt(e.position+1);Br(h)&&(c=u=!0,e.position++,mn(e,!0,t))}r=e.line,Zl(e,t,Iy,!1,!0),f=e.tag,p=e.result,mn(e,!0,t),m=e.input.charCodeAt(e.position),(u||e.line===r)&&m===58&&(c=!0,m=e.input.charCodeAt(++e.position),mn(e,!0,t),Zl(e,t,Iy,!1,!0),g=e.result),l?Kl(e,s,d,f,p,g):c?s.push(Kl(e,null,d,f,p,g)):s.push(p),mn(e,!0,t),m=e.input.charCodeAt(e.position),m===44?(n=!0,m=e.input.charCodeAt(++e.position)):n=!1}we(e,"unexpected end of the stream within a flow collection")}function WY(e,t){let n,r=cS,o=!1,s=!1,i=t,a=0,c=!1,u,l=e.input.charCodeAt(e.position);if(l===124)n=!1;else if(l===62)n=!0;else return!1;for(e.kind="scalar",e.result="";l!==0;)if(l=e.input.charCodeAt(++e.position),l===43||l===45)cS===r?r=l===43?nC:TY:we(e,"repeat of a chomping mode identifier");else if((u=IY(l))>=0)u===0?we(e,"bad explicit indentation width of a block scalar; it cannot be less than one"):s?we(e,"repeat of an indentation width identifier"):(i=t+u-1,s=!0);else break;if(ji(l)){do l=e.input.charCodeAt(++e.position);while(ji(l));if(l===35)do l=e.input.charCodeAt(++e.position);while(!zs(l)&&l!==0)}for(;l!==0;){for(hS(e),e.lineIndent=0,l=e.input.charCodeAt(e.position);(!s||e.lineIndent<i)&&l===32;)e.lineIndent++,l=e.input.charCodeAt(++e.position);if(!s&&e.lineIndent>i&&(i=e.lineIndent),zs(l)){a++;continue}if(!s&&i===0&&we(e,"missing indentation for block scalar"),e.lineIndent<i){r===nC?e.result+=yu.repeat(`
|
|
7
7
|
`,o?1+a:a):r===cS&&o&&(e.result+=`
|
|
@@ -31,43 +31,43 @@ var cY=Object.defineProperty;var WI=(e,t)=>{for(var n in t)cY(e,n,{get:t[n],enum
|
|
|
31
31
|
${n.join(`
|
|
32
32
|
`)}`,n);let i=JSON.parse(JSON.stringify(e)),a=[i,i.defaults,...oe(i.projects)?Object.values(i.projects):[]];for(let c of a)if(oe(c)&&c.gate!==void 0&&(c.gate=lo(c.gate)),oe(c)&&oe(c.environments))for(let u of Object.values(c.environments))oe(u)&&u.gate!==void 0&&(u.gate=lo(u.gate));return i}function V5(e){let t=new Map;return(r=>{for(let o of $5(r)){if(o==="node_modules"||o.startsWith("."))continue;let s=TS(r,o);k5(s).isDirectory()||o.endsWith(".ts")&&!o.endsWith(".d.ts")&&!o.endsWith(".test.ts")&&t.set(HC(e,s).split("\\").join("/"),WC(s,"utf-8"))}})(e),t}var Z5="npm i -D @intentius/tsad-reference";async function Y5(e){let t;try{t=await import("@intentius/tsad-reference")}catch{throw new S(`${e} is TypeScript, which needs the TypeScript folder: ${Z5}`)}let{foldProject:n,EMPTY_HOST:r}=t,o=_5(AS(e)),s=HC(o,AS(e)).split("\\").join("/"),i=n(V5(o),{...r,profile:"data-host"}).verdicts.get(s);if(!i)throw new S(`${e}: not found`);if(i.kind==="run")throw new S(`${e} is not data (${i.rule}): ${i.reason}`);let a=Object.fromEntries(i.exports);if(!("default"in a))throw new S(`${e} must export the config as \`export default\``);return a.default}async function zC(e){let t=await import(S5(AS(e)).href);if(!("default"in t))throw new S(`${e} must export the config as \`export default\``);return t.default}var UC=e=>{let t=n=>n===null||typeof n!="object"?n:Array.isArray(n)?n.map(t):Object.fromEntries(Object.keys(n).sort().map(r=>[r,t(n[r])]));return JSON.stringify(t(JSON.parse(JSON.stringify(e??{}))))};async function Re(e,t="fold"){if(!BC(e))throw new S(`${e} does not exist`);if(e.endsWith(".ts")){if(t==="run")return Ni(await zC(e),e);let o=await Y5(e);if(t==="check"){let s=await zC(e);if(UC(o)!==UC(s))throw new S(`${e}: folding and running the config disagree, so it is not data`)}return Ni(o,e)}let n=WC(e,"utf-8"),r;try{r=e.endsWith(".json")?JSON.parse(n):n.trim()===""?{}:Yl(n)}catch(o){throw new S(`${e}: ${o.message}`)}return Ni(r,e)}function po(e){let n=e.replace(/^https?:\/\//,"").replace(/\.git$/,"").replace(/\/+$/,"").split("/");if(n.length<3||n.some(s=>s===""))throw new S(`project key "${e}" must be <host>/<owner>/<name>, such as github.com/acme/infra`);let[r,...o]=n;return{key:e,host:r,path:o.join("/"),owner:o.slice(0,-1).join("/"),name:o[o.length-1]}}function ir(e){let t=e.toLowerCase().replace(/:\d+$/,"");if(t==="github.com"||t.startsWith("github."))return"github";if(t==="gitlab.com"||t.startsWith("gitlab.")||t.includes(".gitlab."))return"gitlab";if(t==="codeberg.org"||t.startsWith("forgejo.")||t.startsWith("gitea.")||t.includes("forgejo"))return"forgejo"}function ue(e){if(e.projects)throw new S("this config lists projects, so it belongs to a control repo; run terragucci reconcile instead");return PS(Ky,e)}function ot(e,t){let n=e.projects?.[t];if(n===void 0)throw new S(`no project "${t}" in the config`);let r=PS(PS(Ky,e.defaults??{}),n??{}),o=[];if(r.environments&&(VC(r.environments,`projects["${t}"].environments`,o,r),o.push(...ZC(r,`projects["${t}"]`))),o.length)throw new S(`projects["${t}"] has ${o.length} problem(s) once the defaults are merged in:
|
|
33
33
|
${o.join(`
|
|
34
|
-
`)}`,o);return r}function X5(e,t){if(!e||!t)return t??e;let n={...e};for(let[r,o]of Object.entries(t))n[r]={...e[r],...o};return n}function PS(e,t){let{defaults:n,projects:r,...o}=t,s={...e,...o,gate:lo(o.gate)??e.gate,env:{...e.env,...o.env??{}}};return o.oidc&&(s.oidc={...e.oidc,...o.oidc}),(e.terragrunt||o.terragrunt)&&(s.terragrunt={...e.terragrunt,...o.terragrunt}),(e.respond||o.respond)&&(s.respond={...e.respond,...o.respond}),(e.policy||o.policy)&&(s.policy={...e.policy,...o.policy}),(e.waves||o.waves)&&(s.waves={...e.waves,...o.waves}),(e.apply||o.apply)&&(s.apply={...e.apply,...o.apply}),(e.generate||o.generate)&&(s.generate=_S(e.generate,o.generate)),(e.environments||o.environments)&&(s.environments=X5(e.environments,o.environments)),s}import{spawn as
|
|
34
|
+
`)}`,o);return r}function X5(e,t){if(!e||!t)return t??e;let n={...e};for(let[r,o]of Object.entries(t))n[r]={...e[r],...o};return n}function PS(e,t){let{defaults:n,projects:r,...o}=t,s={...e,...o,gate:lo(o.gate)??e.gate,env:{...e.env,...o.env??{}}};return o.oidc&&(s.oidc={...e.oidc,...o.oidc}),(e.terragrunt||o.terragrunt)&&(s.terragrunt={...e.terragrunt,...o.terragrunt}),(e.respond||o.respond)&&(s.respond={...e.respond,...o.respond}),(e.policy||o.policy)&&(s.policy={...e.policy,...o.policy}),(e.waves||o.waves)&&(s.waves={...e.waves,...o.waves}),(e.apply||o.apply)&&(s.apply={...e.apply,...o.apply}),(e.generate||o.generate)&&(s.generate=_S(e.generate,o.generate)),(e.environments||o.environments)&&(s.environments=X5(e.environments,o.environments)),s}import{spawn as C7}from"node:child_process";import{cpSync as N7,existsSync as tg,mkdirSync as D7,readdirSync as BR,readFileSync as KN,rmSync as M7,statSync as VN,writeFileSync as WR}from"node:fs";import{join as qr,relative as qN,resolve as ZN,sep as L7}from"node:path";import{existsSync as xR,readFileSync as ER}from"node:fs";import{isAbsolute as bX,join as md}from"node:path";import{createHash as vX}from"node:crypto";import{randomUUID as Q5}from"node:crypto";var XC=/\/api\/v4\/projects\/[^/]+\/terraform\/state\/([^/]+)\/?$/,YC=e=>typeof e=="string"&&e!==""?e:void 0;function Mf(e){if(!e)return!1;try{return XC.test(new URL(e).pathname)}catch{return!1}}function xn(e){try{let t=new URL(e);return`${t.protocol}//${t.host}${t.pathname}`}catch{return e}}var Su=(e,t,n)=>YC(e[n])??YC(t[`TF_HTTP_${n.toUpperCase()}`]);function QC(e,t=process.env){let n=Su(e,t,"address");if(!n||!Mf(n))return;let r=decodeURIComponent(new URL(n).pathname.match(XC)[1]),o=Su(e,t,"username"),s=Su(e,t,"password"),i=Su(e,t,"lock_address"),a=Su(e,t,"unlock_address");return{address:n,name:r,...i?{lockAddress:i}:{},...a?{unlockAddress:a}:{},lockMethod:Su(e,t,"lock_method")??"LOCK",unlockMethod:Su(e,t,"unlock_method")??"UNLOCK",...o||s?{auth:`Basic ${Buffer.from(`${o??""}:${s??""}`).toString("base64")}`}:{}}}var Lf=(e,t=!1)=>({...e.auth?{authorization:e.auth}:{},...t?{"content-type":"application/json"}:{}}),eX=e=>e.replace(/\/+$/,"");function Gf(e,t,n,r){let o=n===401?": the backend's username and password (TF_HTTP_USERNAME, TF_HTTP_PASSWORD) are not a token GitLab accepts":n===403?": the token may not read this project's state (reads need Developer)":"";return new Error(`GitLab answered ${n} to ${e} ${xn(t)}${o}${r&&!o?`: ${r.trim().slice(0,200)}`:""}`)}async function Yy(e,t){let n=await t(e.address,{method:"GET",headers:Lf(e)});if(n.status===404||n.status===204)return;let r=await n.text();if(!n.ok)throw Gf("a read of",e.address,n.status,r);let o;try{o=JSON.parse(r)}catch{throw new Error(`GitLab answered ${xn(e.address)} with no state`)}if(typeof o.serial!="number")throw new Error(`the state at ${xn(e.address)} has no serial`);return{serial:o.serial,...typeof o.lineage=="string"?{lineage:o.lineage}:{}}}var Zy=(e,t)=>`${eX(e.address)}/versions/${encodeURIComponent(t)}`;async function eN(e,t,n){let r=await n(Zy(e,t),{method:"HEAD",headers:Lf(e)});if(r.status===404)return!1;if(!r.ok)throw Gf("a check of",Zy(e,t),r.status,"");return!0}async function tN(e,t,n){let r=await n(Zy(e,t),{method:"GET",headers:Lf(e)});if(r.status===404)return;let o=await r.text();if(!r.ok)throw Gf("a read of",Zy(e,t),r.status,o);return o}async function $R(e,t){if(!e.lockAddress||!e.unlockAddress)throw new Error(`the http backend of ${xn(e.address)} names no ${e.lockAddress?"unlock_address":"lock_address"}`);let n=`terragucci-probe-${Q5()}`,r=JSON.stringify({ID:n,Operation:"OperationTypeInvalid",Info:"terragucci reads who holds the lock",Who:"terragucci",Version:"",Created:new Date().toISOString(),Path:""}),o=await t(e.lockAddress,{method:e.lockMethod,headers:Lf(e,!0),body:r}),s=await o.text();if(o.status===409||o.status===423)return tX(s);if(!o.ok)throw Gf("a lock of",e.lockAddress,o.status,s);await kR(e,n,t)}function tX(e){try{let t=JSON.parse(e);return JSON.stringify(Object.fromEntries(["ID","Who","Created"].filter(n=>typeof t[n]=="string").map(n=>[n,t[n]])))}catch{return e}}async function kR(e,t,n){if(!e.unlockAddress)throw new Error(`the http backend of ${xn(e.address)} names no unlock_address`);let r=await n(e.unlockAddress,{method:e.unlockMethod,headers:Lf(e,!0),body:JSON.stringify({ID:t})}),o=await r.text();if(r.status===409||r.status===423)throw new Error(`GitLab did not release lock ${t} on ${xn(e.unlockAddress)}: another lock holds the state now`);if(!r.ok)throw Gf("a release of",e.unlockAddress,r.status,o)}function Xy(e,t){let n;try{n=new URL(e)}catch{return}let r=n.pathname.match(/^(.*\/api\/v4\/projects\/[^/]+\/terraform\/state\/)([^/]+?)(\/lock)?\/?$/);if(r)return n.pathname=`${r[1]}${r[2]}-${t}${r[3]??""}`,n.toString()}import{createHmac as nX}from"node:crypto";import{readFileSync as rX}from"node:fs";var _R="2021-08-06",Qy="https://login.microsoftonline.com",oX="https://storage.azure.com/.default";function nN(e,t=process.env){let n={account:e.account,container:e.container,endpoint:(e.endpoint??`https://${e.account}.blob.core.windows.net`).replace(/\/+$/,"")};if(t.AZURE_STORAGE_KEY)return{...n,accountKey:t.AZURE_STORAGE_KEY};let r=t.ARM_TENANT_ID,o=t.ARM_CLIENT_ID,s=t.ARM_OIDC_TOKEN_FILE_PATH;if(r&&o&&s)return{...n,oidc:{tenantId:r,clientId:o,tokenFile:s,authority:(t.AZURE_AUTHORITY_HOST||Qy).replace(/\/+$/,"")}};throw new Y(`reports.bucket is az://${e.account}/${e.container}, but the job has no credentials for it: give the job oidc.azure (it sets ARM_TENANT_ID, ARM_CLIENT_ID and ARM_OIDC_TOKEN_FILE_PATH), or set AZURE_STORAGE_KEY`)}var rN=(e,t)=>nX("sha256",Buffer.from(e,"base64")).update(t,"utf8").digest("base64"),sX=["content-encoding","content-language","content-length","content-md5","content-type","date","if-modified-since","if-match","if-none-match","if-unmodified-since","range"];function iX(e,t,n,r,o){let s=new URL(r),i=Object.fromEntries(Object.entries(o).map(([p,f])=>[p.toLowerCase(),f])),a=sX.map(p=>p==="content-length"&&i[p]==="0"?"":i[p]??""),c=Object.keys(i).filter(p=>p.startsWith("x-ms-")).sort().map(p=>`${p}:${i[p].trim()}
|
|
35
35
|
`).join(""),u=new Map;for(let[p,f]of s.searchParams)u.set(p.toLowerCase(),[...u.get(p.toLowerCase())??[],f]);let l=[...u.keys()].sort().map(p=>`
|
|
36
36
|
${p}:${u.get(p).sort().join(",")}`).join(""),d=[n,...a].join(`
|
|
37
37
|
`)+`
|
|
38
|
-
`+c+`/${e}${s.pathname}`+l;return`SharedKey ${e}:${rN(t,d)}`}function
|
|
38
|
+
`+c+`/${e}${s.pathname}`+l;return`SharedKey ${e}:${rN(t,d)}`}function aX(e,t,n,r){let o=Gy(n),s=e.endpoint.startsWith("https:")?"https":"",i=`/blob/${e.account}/${e.container}/${t}`,a=[s,_R,"b","","","","","","",""],c,u;if("key"in r){let p=r.key;c=["r","",o,i,p.oid,p.tid,p.start,p.expiry,p.service,p.version,"","","","",...a].join(`
|
|
39
39
|
`),u=[["skoid",p.oid],["sktid",p.tid],["skt",p.start],["ske",p.expiry],["sks",p.service],["skv",p.version]]}else c=["r","",o,i,"","",...a].join(`
|
|
40
40
|
`),u=[];let l=rN("key"in r?r.key.value:r.accountKey,c);return new URLSearchParams([["sv",_R],["sr","b"],["sp","r"],["se",o],...s?[["spr",s]]:[],...u,["sig",l]]).toString()}var Mi=e=>{try{let t=JSON.parse(e);if(t.error)return[t.error,t.error_description?.split(`\r
|
|
41
41
|
`)[0]].filter(Boolean).join(": ")}catch{}return[hn(e,"Code"),hn(e,"Message")?.split(`
|
|
42
|
-
`)[0]].filter(Boolean).join(": ")||e.slice(0,300)},ud=class{constructor(t,n=fetch,r=o=>
|
|
43
|
-
`),f=["GOOG4-RSA-SHA256",c,u,
|
|
44
|
-
`);return`${a.origin}${a.pathname}?${d}&X-Goog-Signature=${(await i(f)).toString("hex")}`}var Va=e=>{try{let t=JSON.parse(e);if(typeof t.error=="object"&&t.error?.message)return t.error.message;if(typeof t.error=="string")return[t.error,t.error_description].filter(Boolean).join(": ")}catch{}return e.slice(0,300)},Za=class{constructor(t,n=fetch,r=o=>l7(o,"utf-8")){this.target=t;this.fetchFn=n;this.readFile=r}target;fetchFn;readFile;creds;token;get location(){return`gs://${this.target.bucket}`}get source(){return this.target.credentialsFile??"the backend's credentials"}credentials(){if(this.creds)return this.creds;let t=this.source,n;try{n=JSON.parse(this.target.credentialsJson??this.readFile(this.target.credentialsFile??""))}catch(r){throw new Y(`cannot read the credentials in ${t}: ${r.message}`)}if(n.type==="external_account"&&n.token_url&&n.audience&&n.credential_source&&(n.credential_source.file||n.credential_source.url))return this.creds=n;if(n.type==="service_account"&&n.client_email&&n.private_key)return this.creds=n;throw new Y(`${t} is not an external_account file with a credential_source file or url, nor a service_account key`)}async post(t,n,r,o){let s=await this.fetchFn(t,{method:"POST",headers:n,body:r}),i=await s.text();if(!s.ok)throw new Y(`${o}: ${s.status} ${Va(i)}`);try{return JSON.parse(i)}catch{throw new Y(`${o}: the answer is not JSON`)}}async subjectToken(t){let n=t.credential_source,r;if(n.file)try{r=this.readFile(n.file)}catch(i){throw new Y(`cannot read the OIDC token in ${n.file}: ${i.message}`)}else{let i=await this.fetchFn(n.url,{method:"GET",headers:n.headers??{}});if(r=await i.text(),!i.ok)throw new Y(`the OIDC token at ${n.url.split("?")[0]}: ${i.status} ${Va(r)}`)}let o=n.format?.type==="json"?n.format.subject_token_field_name:void 0,s=r.trim();if(o)try{s=String(JSON.parse(r)[o]??"")}catch{throw new Y(`the job's OIDC token is not JSON with ${o}`)}if(!s)throw new Y("the job's OIDC token is empty");return s}async exchange(t){let n=await this.post(t.token_url,{"content-type":"application/x-www-form-urlencoded"},new URLSearchParams({grant_type:"urn:ietf:params:oauth:grant-type:token-exchange",audience:t.audience,scope:SR,requested_token_type:"urn:ietf:params:oauth:token-type:access_token",subject_token:await this.subjectToken(t),subject_token_type:t.subject_token_type}).toString(),`Google STS refused the OIDC token for ${t.audience}`);if(typeof n.access_token!="string")throw new Y("Google STS answered without a token");let r={token:n.access_token,expires:Date.now()+Number(n.expires_in??3600)*1e3};if(!t.service_account_impersonation_url)return r;let o=await this.post(t.service_account_impersonation_url,{"content-type":"application/json",authorization:`Bearer ${r.token}`},JSON.stringify({scope:[SR],lifetime:"3600s"}),`impersonating ${iN(t.service_account_impersonation_url)??"the service account"}`);if(typeof o.accessToken!="string")throw new Y("IAM answered the impersonation without a token");let s=typeof o.expireTime=="string"?Date.parse(o.expireTime):NaN;return{token:o.accessToken,expires:Number.isNaN(s)?Date.now()+36e5:s}}async own(){if(this.target.accessToken)return{token:this.target.accessToken,expires:Date.now()+36e5};let t=this.credentials();return t.type==="service_account"?{token:p7(t,new Date),expires:Date.now()+36e5}:this.exchange(t)}async impersonating(t){let n=await this.own(),r=await this.post(`${(this.target.iamEndpoint??oN).replace(/\/+$/,"")}/v1/projects/-/serviceAccounts/${encodeURIComponent(t)}:generateAccessToken`,{"content-type":"application/json",authorization:`Bearer ${n.token}`},JSON.stringify({scope:[SR],lifetime:"3600s"}),`impersonating ${t}, which needs Service Account Token Creator for the identity`);if(typeof r.accessToken!="string")throw new Y(`IAM answered the impersonation of ${t} without a token`);let o=typeof r.expireTime=="string"?Date.parse(r.expireTime):NaN;return{token:r.accessToken,expires:Number.isNaN(o)?Date.now()+36e5:o}}async bearer(){let t=this.target.impersonate,n=()=>t?this.impersonating(t):this.own(),r=()=>this.token=n().catch(s=>{throw this.token=void 0,s}),o=await(this.token??r());return o.expires-Date.now()<6e4&&(o=await r()),o.token}async identityToken(){return this.target.impersonate?this.impersonating(this.target.impersonate):this.own()}async put(t,n,r,o){let s=this.target,i=o?"ifMatch"in o?o.ifMatch:"0":"",a=`${s.endpoint}/upload/storage/v1/b/${encodeURIComponent(s.bucket)}/o?uploadType=media&name=${Zt(t)}${i?`&ifGenerationMatch=${Zt(i)}`:""}`,c=await this.fetchFn(a,{method:"POST",headers:{"content-type":r,authorization:`Bearer ${await this.bearer()}`},body:n});if(c.status===412)throw new Er(`PUT ${this.location}/${t}: 412, it changed since it was read`);let u=await c.text();if(!c.ok)throw new Y(`PUT ${this.location}/${t}: ${c.status} ${Va(u)}`);let l;try{l=JSON.parse(u).generation}catch{}return l!==void 0?{etag:String(l)}:{}}async read(t){let n=this.target,r=`${n.endpoint}/storage/v1/b/${encodeURIComponent(n.bucket)}/o/${Zt(t)}?alt=media`,o=await this.fetchFn(r,{method:"GET",headers:{authorization:`Bearer ${await this.bearer()}`}});if(o.status===404)return{};if(!o.ok)throw new Y(`GET ${this.location}/${t}: ${o.status} ${Va(await o.text())}`);let s=o.headers?.get("x-goog-generation")??void 0;return{body:await o.text(),...s?{etag:s}:{}}}async get(t){return(await this.read(t)).body}objectUrl(t){return`${this.target.endpoint}/storage/v1/b/${encodeURIComponent(this.target.bucket)}/o/${Zt(t)}`}async head(t,n){let r=`${this.objectUrl(t)}${n?`?generation=${Zt(n)}`:""}`,o=await this.fetchFn(r,{method:"GET",headers:{authorization:`Bearer ${await this.bearer()}`}}),s=await o.text();if(o.status===404||n&&o.status===400)return{exists:!1};if(!o.ok)throw new Y(`GET ${this.location}/${t} metadata: ${o.status} ${Va(s)}`);let i;try{i=JSON.parse(s).generation}catch{}return{exists:!0,...i!=null&&i!==""?{generation:String(i)}:{}}}async readGeneration(t,n){let r=`${this.objectUrl(t)}?alt=media&generation=${Zt(n)}`,o=await this.fetchFn(r,{method:"GET",headers:{authorization:`Bearer ${await this.bearer()}`}});if(!(o.status===404||o.status===400)){if(!o.ok)throw new Y(`GET ${this.location}/${t}#${n}: ${o.status} ${Va(await o.text())}`);return o.text()}}async versioning(){let t=`${this.target.endpoint}/storage/v1/b/${encodeURIComponent(this.target.bucket)}?fields=versioning`,n=await this.fetchFn(t,{method:"GET",headers:{authorization:`Bearer ${await this.bearer()}`}}),r=await n.text();if(!(n.status===403||n.status===401)){if(!n.ok)throw new Y(`GET ${this.location} settings: ${n.status} ${Va(r)}`);try{return JSON.parse(r).versioning?.enabled===!0}catch{return}}}async putIfAbsent(t,n,r){try{return await this.put(t,n,r,{ifNoneMatch:"*"}),!0}catch(o){if(o instanceof Er)return!1;throw o}}async remove(t){let n=await this.fetchFn(this.objectUrl(t),{method:"DELETE",headers:{authorization:`Bearer ${await this.bearer()}`}});if(!n.ok&&n.status!==404)throw new Y(`DELETE ${this.location}/${t}: ${n.status} ${Va(await n.text())}`)}async presign(t,n,r=new Date){if(!Number.isInteger(n)||n<1||n>Jn)throw new Y(`a signed URL lives 1 to ${Jn} seconds, not ${n}`);let o,s,i=this.target.accessToken&&this.target.signAs?void 0:this.credentials();if(i)if(i.type==="service_account")o=i.client_email,s=async c=>aN("RSA-SHA256").update(c).sign(i.private_key);else{let c=i.service_account_impersonation_url?iN(i.service_account_impersonation_url):void 0;if(!c)throw new Y(`a signed URL needs a service account to sign it, and ${this.source} impersonates none: set oidc.gcp's service accounts`);o=c;let u=new URL(i.service_account_impersonation_url).origin;s=async l=>{let d=await this.post(`${u}/v1/projects/-/serviceAccounts/${encodeURIComponent(c)}:signBlob`,{"content-type":"application/json",authorization:`Bearer ${await this.bearer()}`},JSON.stringify({payload:Buffer.from(l).toString("base64")}),`signBlob as ${c}, which needs Service Account Token Creator on itself`);if(typeof d.signedBlob!="string")throw new Y(`signBlob as ${c} answered without a signature`);return Buffer.from(d.signedBlob,"base64")}}else{let c=this.target.signAs;o=c,s=async u=>{let l=await this.post(`${(this.target.iamEndpoint??oN).replace(/\/+$/,"")}/v1/projects/-/serviceAccounts/${encodeURIComponent(c)}:signBlob`,{"content-type":"application/json",authorization:`Bearer ${await this.bearer()}`},JSON.stringify({payload:Buffer.from(u).toString("base64")}),`signBlob as ${c}, which needs Service Account Token Creator on itself`);if(typeof l.signedBlob!="string")throw new Y(`signBlob as ${c} answered without a signature`);return Buffer.from(l.signedBlob,"base64")}}return{url:await f7(this.target.endpoint,this.target.bucket,t,o,n,r,s),expires:new Date(r.getTime()+n*1e3)}}};import{createHash as g7,createHmac as RR}from"node:crypto";import{readFileSync as m7}from"node:fs";function h7(e){let t=/^(?:s3:\/\/)?([^/]+)\/?$/.exec(e.trim());if(!t)throw new Y(`reports.bucket must be s3://<bucket>, not ${e}`);return t[1]}function dd(e,t=process.env){let n=e.endpoint??t.AWS_ENDPOINT_URL_S3??t.AWS_ENDPOINT_URL,r=t.AWS_REGION||t.AWS_DEFAULT_REGION||"us-east-1",o={bucket:h7(e.bucket),...n?{endpoint:n.replace(/\/+$/,"")}:{},region:r},s=t.AWS_WEB_IDENTITY_TOKEN_FILE,i=l=>({...o,webIdentity:{roleArn:l,tokenFile:s,sessionName:t.AWS_ROLE_SESSION_NAME||"terragucci-report",endpoint:(t.AWS_ENDPOINT_URL_STS||t.AWS_ENDPOINT_URL||`https://sts.${r}.amazonaws.com`).replace(/\/+$/,"")}}),a=Li(t);if(e.role&&a&&!s){let l=Ru(a,e.role);return{...o,accessKeyId:l.accessKeyId,secretAccessKey:l.secretAccessKey,...l.sessionToken?{sessionToken:l.sessionToken}:{}}}if(e.role){if(!s)throw new Y(`reports.role is set, but AWS_WEB_IDENTITY_TOKEN_FILE is not: the job needs an OIDC token to assume ${e.role}, which a job with oidc gets`);return i(e.role)}let c=t.AWS_ACCESS_KEY_ID,u=t.AWS_SECRET_ACCESS_KEY;if(c&&u)return{...o,accessKeyId:c,secretAccessKey:u,...t.AWS_SESSION_TOKEN?{sessionToken:t.AWS_SESSION_TOKEN}:{}};if(t.AWS_ROLE_ARN&&s)return i(t.AWS_ROLE_ARN);throw new Y("reports.bucket is set, but the job has no credentials for it: set AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY, or AWS_ROLE_ARN and AWS_WEB_IDENTITY_TOKEN_FILE (a job with oidc sets both), or reports.role")}async function Bf(e,t,n=r=>m7(r,"utf-8")){let r;try{r=n(e.tokenFile).trim()}catch(d){throw new Y(`cannot read the OIDC token in ${e.tokenFile}: ${d.message}`)}if(!r)throw new Y(`the OIDC token file ${e.tokenFile} is empty`);let o=new URLSearchParams({Action:"AssumeRoleWithWebIdentity",Version:"2011-06-15",RoleArn:e.roleArn,RoleSessionName:e.sessionName,WebIdentityToken:r}).toString(),s=await t(`${e.endpoint}/`,{method:"POST",headers:{"content-type":"application/x-www-form-urlencoded; charset=utf-8",accept:"application/xml"},body:o}),i=await s.text();if(!s.ok){let d=[hn(i,"Code"),hn(i,"Message")].filter(Boolean).join(": ")||i.slice(0,300);throw new Y(`AssumeRoleWithWebIdentity for ${e.roleArn}: ${s.status} ${d}`)}let a=hn(i,"AccessKeyId"),c=hn(i,"SecretAccessKey"),u=hn(i,"SessionToken");if(!a||!c||!u)throw new Y(`AssumeRoleWithWebIdentity for ${e.roleArn} answered without credentials`);let l=hn(i,"Expiration");return{accessKeyId:a,secretAccessKey:c,sessionToken:u,...l&&!Number.isNaN(Date.parse(l))?{expiration:new Date(l)}:{}}}var Uf=e=>g7("sha256").update(e).digest("hex"),Ya=(e,t)=>RR("sha256",e).update(t).digest();function zf(e,t,n,r,o=new Date,s,i={}){let a=tw(e,n);return{url:a,headers:xu(e,t,a,{...s?{"content-type":s}:{},...i},Uf(r),o)}}function xu(e,t,n,r,o,s,i="s3"){let a=new URL(n),c=s.toISOString().replace(/[-:]/g,"").replace(/\.\d{3}/,""),u=c.slice(0,8),l={host:a.host,"x-amz-content-sha256":o,"x-amz-date":c,...Object.fromEntries(Object.entries(r).map(([k,v])=>[k.toLowerCase(),v])),...e.sessionToken?{"x-amz-security-token":e.sessionToken}:{}},d=Object.keys(l).sort(),p=[...a.searchParams].map(([k,v])=>`${Zt(k)}=${Zt(v)}`).sort().join("&"),f=[t,a.pathname,p,...d.map(k=>`${k}:${l[k].trim()}`),"",d.join(";"),o].join(`
|
|
42
|
+
`)[0]].filter(Boolean).join(": ")||e.slice(0,300)},ud=class{constructor(t,n=fetch,r=o=>rX(o,"utf-8")){this.target=t;this.fetchFn=n;this.readToken=r}target;fetchFn;readToken;token;keys=new Map;get location(){return`az://${this.target.account}/${this.target.container}`}blobUrl(t,n=""){let r=this.target,o=[n,"sas"in r?r.sas.replace(/^\?/,""):""].filter(Boolean).join("&");return`${r.endpoint}/${r.container}/${Xl(t)}${o?`?${o}`:""}`}async bearer(t){let n=async()=>{let s;try{s=this.readToken(t.tokenFile).trim()}catch(l){throw new Y(`cannot read the OIDC token in ${t.tokenFile}: ${l.message}`)}if(!s)throw new Y(`the OIDC token file ${t.tokenFile} is empty`);let i=new URLSearchParams({client_id:t.clientId,scope:oX,grant_type:"client_credentials",client_assertion_type:"urn:ietf:params:oauth:client-assertion-type:jwt-bearer",client_assertion:s}).toString(),a=await this.fetchFn(`${t.authority}/${t.tenantId}/oauth2/v2.0/token`,{method:"POST",headers:{"content-type":"application/x-www-form-urlencoded"},body:i}),c=await a.text();if(!a.ok)throw new Y(`Entra ID refused the OIDC token for client ${t.clientId}: ${a.status} ${Mi(c)}`);let u={};try{u=JSON.parse(c)}catch{}if(!u.access_token)throw new Y(`Entra ID answered client ${t.clientId} without a token`);return{token:u.access_token,expires:Date.now()+Number(u.expires_in??3600)*1e3}},r=()=>this.token=n().catch(s=>{throw this.token=void 0,s}),o=await(this.token??r());return o.expires-Date.now()<6e4&&(o=await r()),o.token}async headers(t,n,r){let o={"x-ms-date":new Date().toUTCString(),"x-ms-version":_R,...r},s=this.target;return"accountKey"in s?{...o,authorization:iX(s.account,s.accountKey,t,n,o)}:"sas"in s?o:{...o,authorization:`Bearer ${await this.bearer(s.oidc)}`}}async put(t,n,r,o){let s=this.blobUrl(t),i=o?"ifMatch"in o?{"if-match":o.ifMatch}:{"if-none-match":"*"}:{},a=typeof n=="string"?Buffer.byteLength(n):n.byteLength,c=await this.headers("PUT",s,{"x-ms-blob-type":"BlockBlob","content-type":r,"content-length":String(a),...i}),u=await this.fetchFn(s,{method:"PUT",headers:c,body:n});if(u.status===412||u.status===409&&o)throw new Er(`PUT ${this.location}/${t}: ${u.status}, it changed since it was read`);if(!u.ok)throw new Y(`PUT ${this.location}/${t}: ${u.status} ${Mi(await u.text())}`);let l=u.headers?.get("etag")??void 0;return l?{etag:l}:{}}async read(t){let n=this.blobUrl(t),r=await this.fetchFn(n,{method:"GET",headers:await this.headers("GET",n,{})});if(r.status===404)return{};if(!r.ok)throw new Y(`GET ${this.location}/${t}: ${r.status} ${Mi(await r.text())}`);let o=r.headers?.get("etag")??void 0;return{body:await r.text(),...o?{etag:o}:{}}}async get(t){return(await this.read(t)).body}async head(t,n){let r=this.blobUrl(t,n?new URLSearchParams(n).toString():""),o=await this.fetchFn(r,{method:"HEAD",headers:await this.headers("HEAD",r,{})});if(o.status===404||n&&o.status===400)return{exists:!1,metadata:{}};if(!o.ok)throw new Y(`HEAD ${this.location}/${t}: ${o.status}`);let s=d=>o.headers?.get(d)??void 0,i={};o.headers?.forEach?.((d,p)=>{p.toLowerCase().startsWith("x-ms-meta-")&&(i[p.toLowerCase().slice(10)]=d)});let c=s("etag"),u=s("x-ms-version-id"),l=s("x-ms-lease-state");return{exists:!0,...c?{etag:c}:{},...u?{versionId:u}:{},...l?{leaseState:l}:{},metadata:i}}async readAt(t,n){let r=this.blobUrl(t,new URLSearchParams(n).toString()),o=await this.fetchFn(r,{method:"GET",headers:await this.headers("GET",r,{})});if(!(o.status===404||o.status===400)){if(!o.ok)throw new Y(`GET ${this.location}/${t} ${JSON.stringify(n)}: ${o.status} ${Mi(await o.text())}`);return o.text()}}async snapshot(t){let n=this.blobUrl(t,"comp=snapshot"),r=await this.fetchFn(n,{method:"PUT",headers:await this.headers("PUT",n,{"content-length":"0"})});if(!r.ok)throw new Y(`snapshot of ${this.location}/${t}: ${r.status} ${Mi(await r.text())}`);let o=r.headers?.get("x-ms-snapshot");if(!o)throw new Y(`snapshot of ${this.location}/${t}: Azure answered without x-ms-snapshot`);return o}async putBlob(t,n,r,o={}){let s=this.blobUrl(t),i=Object.fromEntries(Object.entries(o.metadata??{}).map(([u,l])=>[`x-ms-meta-${u}`,l])),a={"x-ms-blob-type":"BlockBlob","content-type":r,"content-length":String(Buffer.byteLength(n)),...i,...o.leaseId?{"x-ms-lease-id":o.leaseId}:{},...o.ifNoneMatch?{"if-none-match":"*"}:{}},c=await this.fetchFn(s,{method:"PUT",headers:await this.headers("PUT",s,a),body:n});if(o.ifNoneMatch&&(c.status===409||c.status===412))return!1;if(!c.ok)throw new Y(`PUT ${this.location}/${t}: ${c.status} ${Mi(await c.text())}`);return!0}async lease(t,n,r){let o=this.blobUrl(t,"comp=lease"),s={"x-ms-lease-action":n,"content-length":"0",...n==="acquire"?{"x-ms-lease-duration":"-1","x-ms-proposed-lease-id":r}:{"x-ms-lease-id":r}},i=await this.fetchFn(o,{method:"PUT",headers:await this.headers("PUT",o,s)});if(n==="acquire"&&i.status===409)return!1;if(!i.ok)throw new Y(`${n} the lease of ${this.location}/${t}: ${i.status} ${Mi(await i.text())}`);return!0}async setMetadata(t,n,r){let o=this.blobUrl(t,"comp=metadata"),s={"content-length":"0",...Object.fromEntries(Object.entries(n).map(([a,c])=>[`x-ms-meta-${a}`,c])),...r?{"x-ms-lease-id":r}:{}},i=await this.fetchFn(o,{method:"PUT",headers:await this.headers("PUT",o,s)});if(!i.ok)throw new Y(`set the metadata of ${this.location}/${t}: ${i.status} ${Mi(await i.text())}`)}delegationKey(t,n){let r=Gy(n),o=this.keys.get(r);return o||(o=(async()=>{let s=`${this.target.endpoint}/?restype=service&comp=userdelegationkey`,i=`<?xml version="1.0" encoding="utf-8"?><KeyInfo><Start>${Gy(t)}</Start><Expiry>${r}</Expiry></KeyInfo>`,a=await this.fetchFn(s,{method:"POST",headers:await this.headers("POST",s,{"content-type":"application/xml"}),body:i}),c=await a.text();if(!a.ok)throw new Y(`Get User Delegation Key on ${this.target.account}: ${a.status} ${Mi(c)}`);let u=l=>{let d=hn(c,l);if(!d)throw new Y(`Get User Delegation Key on ${this.target.account} answered without ${l}`);return d};return{oid:u("SignedOid"),tid:u("SignedTid"),start:u("SignedStart"),expiry:u("SignedExpiry"),service:u("SignedService"),version:u("SignedVersion"),value:u("Value")}})(),o.catch(()=>this.keys.delete(r)),this.keys.set(r,o)),o}async presign(t,n,r=new Date){if(!Number.isInteger(n)||n<1||n>Jn)throw new Y(`a SAS link lives 1 to ${Jn} seconds, not ${n}`);let o=new Date(Math.floor(r.getTime()/1e3)*1e3+n*1e3),s=this.target,i=aX(s,t,o,"accountKey"in s?{accountKey:s.accountKey}:{key:await this.delegationKey(r,o)});return{url:`${this.blobUrl(t)}?${i}`,expires:o}}};import{createHash as uX,createSign as aN}from"node:crypto";import{readFileSync as lX}from"node:fs";import{readFileSync as cX}from"node:fs";var Ff="TERRAGUCCI_TRADED";function Li(e){let t=e[Ff];if(t)try{let n=JSON.parse(cX(t,"utf-8"));return{aws:n.aws??{},gcp:n.gcp??{}}}catch(n){throw new Y(`${Ff} names ${t}, which cannot be read: ${n.message}`)}}function Ru(e,t){let n=e.aws[t];if(!n)throw new Y(`the re-plan traded no credentials for ${t}`);return n}var ew="https://storage.googleapis.com",oN="https://iamcredentials.googleapis.com",SR="https://www.googleapis.com/auth/cloud-platform",dX="https://www.googleapis.com/auth/devstorage.read_write";function cN(e,t=process.env){let n=t.GOOGLE_APPLICATION_CREDENTIALS,r=!n&&t.GOOGLE_OAUTH_ACCESS_TOKEN?Li(t):void 0;if(r&&t.GOOGLE_OAUTH_ACCESS_TOKEN){let o=Object.entries(r.gcp).find(([,s])=>s.token===t.GOOGLE_OAUTH_ACCESS_TOKEN)?.[0];return{bucket:e.bucket,endpoint:(e.endpoint??ew).replace(/\/+$/,""),accessToken:t.GOOGLE_OAUTH_ACCESS_TOKEN,...o?{signAs:o}:{}}}if(!n)throw new Y(`reports.bucket is gs://${e.bucket}, but the job has no credentials for it: give the job oidc.gcp (it sets GOOGLE_APPLICATION_CREDENTIALS), or set GOOGLE_APPLICATION_CREDENTIALS to a credentials file`);return{bucket:e.bucket,endpoint:(e.endpoint??ew).replace(/\/+$/,""),credentialsFile:n}}var sN=e=>Buffer.from(e).toString("base64url");function pX(e,t){let n=Math.floor(t.getTime()/1e3),r=sN(JSON.stringify({alg:"RS256",typ:"JWT",...e.private_key_id?{kid:e.private_key_id}:{}})),o=sN(JSON.stringify({iss:e.client_email,sub:e.client_email,scope:dX,iat:n,exp:n+3600}));return`${r}.${o}.${aN("RSA-SHA256").update(`${r}.${o}`).sign(e.private_key).toString("base64url")}`}var iN=e=>{let t=/\/serviceAccounts\/([^/:]+):generateAccessToken$/.exec(e);return t?decodeURIComponent(t[1]):void 0};async function fX(e,t,n,r,o,s,i){let a=new URL(`${e}/${t}/${Xl(n)}`),c=s.toISOString().replace(/[-:]/g,"").replace(/\.\d{3}/,""),u=`${c.slice(0,8)}/auto/storage/goog4_request`,l={"X-Goog-Algorithm":"GOOG4-RSA-SHA256","X-Goog-Credential":`${r}/${u}`,"X-Goog-Date":c,"X-Goog-Expires":String(o),"X-Goog-SignedHeaders":"host"},d=Object.keys(l).sort().map(g=>`${Zt(g)}=${Zt(l[g])}`).join("&"),p=["GET",a.pathname,d,`host:${a.host}`,"","host","UNSIGNED-PAYLOAD"].join(`
|
|
43
|
+
`),f=["GOOG4-RSA-SHA256",c,u,uX("sha256").update(p).digest("hex")].join(`
|
|
44
|
+
`);return`${a.origin}${a.pathname}?${d}&X-Goog-Signature=${(await i(f)).toString("hex")}`}var Va=e=>{try{let t=JSON.parse(e);if(typeof t.error=="object"&&t.error?.message)return t.error.message;if(typeof t.error=="string")return[t.error,t.error_description].filter(Boolean).join(": ")}catch{}return e.slice(0,300)},Za=class{constructor(t,n=fetch,r=o=>lX(o,"utf-8")){this.target=t;this.fetchFn=n;this.readFile=r}target;fetchFn;readFile;creds;token;get location(){return`gs://${this.target.bucket}`}get source(){return this.target.credentialsFile??"the backend's credentials"}credentials(){if(this.creds)return this.creds;let t=this.source,n;try{n=JSON.parse(this.target.credentialsJson??this.readFile(this.target.credentialsFile??""))}catch(r){throw new Y(`cannot read the credentials in ${t}: ${r.message}`)}if(n.type==="external_account"&&n.token_url&&n.audience&&n.credential_source&&(n.credential_source.file||n.credential_source.url))return this.creds=n;if(n.type==="service_account"&&n.client_email&&n.private_key)return this.creds=n;throw new Y(`${t} is not an external_account file with a credential_source file or url, nor a service_account key`)}async post(t,n,r,o){let s=await this.fetchFn(t,{method:"POST",headers:n,body:r}),i=await s.text();if(!s.ok)throw new Y(`${o}: ${s.status} ${Va(i)}`);try{return JSON.parse(i)}catch{throw new Y(`${o}: the answer is not JSON`)}}async subjectToken(t){let n=t.credential_source,r;if(n.file)try{r=this.readFile(n.file)}catch(i){throw new Y(`cannot read the OIDC token in ${n.file}: ${i.message}`)}else{let i=await this.fetchFn(n.url,{method:"GET",headers:n.headers??{}});if(r=await i.text(),!i.ok)throw new Y(`the OIDC token at ${n.url.split("?")[0]}: ${i.status} ${Va(r)}`)}let o=n.format?.type==="json"?n.format.subject_token_field_name:void 0,s=r.trim();if(o)try{s=String(JSON.parse(r)[o]??"")}catch{throw new Y(`the job's OIDC token is not JSON with ${o}`)}if(!s)throw new Y("the job's OIDC token is empty");return s}async exchange(t){let n=await this.post(t.token_url,{"content-type":"application/x-www-form-urlencoded"},new URLSearchParams({grant_type:"urn:ietf:params:oauth:grant-type:token-exchange",audience:t.audience,scope:SR,requested_token_type:"urn:ietf:params:oauth:token-type:access_token",subject_token:await this.subjectToken(t),subject_token_type:t.subject_token_type}).toString(),`Google STS refused the OIDC token for ${t.audience}`);if(typeof n.access_token!="string")throw new Y("Google STS answered without a token");let r={token:n.access_token,expires:Date.now()+Number(n.expires_in??3600)*1e3};if(!t.service_account_impersonation_url)return r;let o=await this.post(t.service_account_impersonation_url,{"content-type":"application/json",authorization:`Bearer ${r.token}`},JSON.stringify({scope:[SR],lifetime:"3600s"}),`impersonating ${iN(t.service_account_impersonation_url)??"the service account"}`);if(typeof o.accessToken!="string")throw new Y("IAM answered the impersonation without a token");let s=typeof o.expireTime=="string"?Date.parse(o.expireTime):NaN;return{token:o.accessToken,expires:Number.isNaN(s)?Date.now()+36e5:s}}async own(){if(this.target.accessToken)return{token:this.target.accessToken,expires:Date.now()+36e5};let t=this.credentials();return t.type==="service_account"?{token:pX(t,new Date),expires:Date.now()+36e5}:this.exchange(t)}async impersonating(t){let n=await this.own(),r=await this.post(`${(this.target.iamEndpoint??oN).replace(/\/+$/,"")}/v1/projects/-/serviceAccounts/${encodeURIComponent(t)}:generateAccessToken`,{"content-type":"application/json",authorization:`Bearer ${n.token}`},JSON.stringify({scope:[SR],lifetime:"3600s"}),`impersonating ${t}, which needs Service Account Token Creator for the identity`);if(typeof r.accessToken!="string")throw new Y(`IAM answered the impersonation of ${t} without a token`);let o=typeof r.expireTime=="string"?Date.parse(r.expireTime):NaN;return{token:r.accessToken,expires:Number.isNaN(o)?Date.now()+36e5:o}}async bearer(){let t=this.target.impersonate,n=()=>t?this.impersonating(t):this.own(),r=()=>this.token=n().catch(s=>{throw this.token=void 0,s}),o=await(this.token??r());return o.expires-Date.now()<6e4&&(o=await r()),o.token}async identityToken(){return this.target.impersonate?this.impersonating(this.target.impersonate):this.own()}async put(t,n,r,o){let s=this.target,i=o?"ifMatch"in o?o.ifMatch:"0":"",a=`${s.endpoint}/upload/storage/v1/b/${encodeURIComponent(s.bucket)}/o?uploadType=media&name=${Zt(t)}${i?`&ifGenerationMatch=${Zt(i)}`:""}`,c=await this.fetchFn(a,{method:"POST",headers:{"content-type":r,authorization:`Bearer ${await this.bearer()}`},body:n});if(c.status===412)throw new Er(`PUT ${this.location}/${t}: 412, it changed since it was read`);let u=await c.text();if(!c.ok)throw new Y(`PUT ${this.location}/${t}: ${c.status} ${Va(u)}`);let l;try{l=JSON.parse(u).generation}catch{}return l!==void 0?{etag:String(l)}:{}}async read(t){let n=this.target,r=`${n.endpoint}/storage/v1/b/${encodeURIComponent(n.bucket)}/o/${Zt(t)}?alt=media`,o=await this.fetchFn(r,{method:"GET",headers:{authorization:`Bearer ${await this.bearer()}`}});if(o.status===404)return{};if(!o.ok)throw new Y(`GET ${this.location}/${t}: ${o.status} ${Va(await o.text())}`);let s=o.headers?.get("x-goog-generation")??void 0;return{body:await o.text(),...s?{etag:s}:{}}}async get(t){return(await this.read(t)).body}objectUrl(t){return`${this.target.endpoint}/storage/v1/b/${encodeURIComponent(this.target.bucket)}/o/${Zt(t)}`}async head(t,n){let r=`${this.objectUrl(t)}${n?`?generation=${Zt(n)}`:""}`,o=await this.fetchFn(r,{method:"GET",headers:{authorization:`Bearer ${await this.bearer()}`}}),s=await o.text();if(o.status===404||n&&o.status===400)return{exists:!1};if(!o.ok)throw new Y(`GET ${this.location}/${t} metadata: ${o.status} ${Va(s)}`);let i;try{i=JSON.parse(s).generation}catch{}return{exists:!0,...i!=null&&i!==""?{generation:String(i)}:{}}}async readGeneration(t,n){let r=`${this.objectUrl(t)}?alt=media&generation=${Zt(n)}`,o=await this.fetchFn(r,{method:"GET",headers:{authorization:`Bearer ${await this.bearer()}`}});if(!(o.status===404||o.status===400)){if(!o.ok)throw new Y(`GET ${this.location}/${t}#${n}: ${o.status} ${Va(await o.text())}`);return o.text()}}async versioning(){let t=`${this.target.endpoint}/storage/v1/b/${encodeURIComponent(this.target.bucket)}?fields=versioning`,n=await this.fetchFn(t,{method:"GET",headers:{authorization:`Bearer ${await this.bearer()}`}}),r=await n.text();if(!(n.status===403||n.status===401)){if(!n.ok)throw new Y(`GET ${this.location} settings: ${n.status} ${Va(r)}`);try{return JSON.parse(r).versioning?.enabled===!0}catch{return}}}async putIfAbsent(t,n,r){try{return await this.put(t,n,r,{ifNoneMatch:"*"}),!0}catch(o){if(o instanceof Er)return!1;throw o}}async remove(t){let n=await this.fetchFn(this.objectUrl(t),{method:"DELETE",headers:{authorization:`Bearer ${await this.bearer()}`}});if(!n.ok&&n.status!==404)throw new Y(`DELETE ${this.location}/${t}: ${n.status} ${Va(await n.text())}`)}async presign(t,n,r=new Date){if(!Number.isInteger(n)||n<1||n>Jn)throw new Y(`a signed URL lives 1 to ${Jn} seconds, not ${n}`);let o,s,i=this.target.accessToken&&this.target.signAs?void 0:this.credentials();if(i)if(i.type==="service_account")o=i.client_email,s=async c=>aN("RSA-SHA256").update(c).sign(i.private_key);else{let c=i.service_account_impersonation_url?iN(i.service_account_impersonation_url):void 0;if(!c)throw new Y(`a signed URL needs a service account to sign it, and ${this.source} impersonates none: set oidc.gcp's service accounts`);o=c;let u=new URL(i.service_account_impersonation_url).origin;s=async l=>{let d=await this.post(`${u}/v1/projects/-/serviceAccounts/${encodeURIComponent(c)}:signBlob`,{"content-type":"application/json",authorization:`Bearer ${await this.bearer()}`},JSON.stringify({payload:Buffer.from(l).toString("base64")}),`signBlob as ${c}, which needs Service Account Token Creator on itself`);if(typeof d.signedBlob!="string")throw new Y(`signBlob as ${c} answered without a signature`);return Buffer.from(d.signedBlob,"base64")}}else{let c=this.target.signAs;o=c,s=async u=>{let l=await this.post(`${(this.target.iamEndpoint??oN).replace(/\/+$/,"")}/v1/projects/-/serviceAccounts/${encodeURIComponent(c)}:signBlob`,{"content-type":"application/json",authorization:`Bearer ${await this.bearer()}`},JSON.stringify({payload:Buffer.from(u).toString("base64")}),`signBlob as ${c}, which needs Service Account Token Creator on itself`);if(typeof l.signedBlob!="string")throw new Y(`signBlob as ${c} answered without a signature`);return Buffer.from(l.signedBlob,"base64")}}return{url:await fX(this.target.endpoint,this.target.bucket,t,o,n,r,s),expires:new Date(r.getTime()+n*1e3)}}};import{createHash as gX,createHmac as RR}from"node:crypto";import{readFileSync as mX}from"node:fs";function hX(e){let t=/^(?:s3:\/\/)?([^/]+)\/?$/.exec(e.trim());if(!t)throw new Y(`reports.bucket must be s3://<bucket>, not ${e}`);return t[1]}function dd(e,t=process.env){let n=e.endpoint??t.AWS_ENDPOINT_URL_S3??t.AWS_ENDPOINT_URL,r=t.AWS_REGION||t.AWS_DEFAULT_REGION||"us-east-1",o={bucket:hX(e.bucket),...n?{endpoint:n.replace(/\/+$/,"")}:{},region:r},s=t.AWS_WEB_IDENTITY_TOKEN_FILE,i=l=>({...o,webIdentity:{roleArn:l,tokenFile:s,sessionName:t.AWS_ROLE_SESSION_NAME||"terragucci-report",endpoint:(t.AWS_ENDPOINT_URL_STS||t.AWS_ENDPOINT_URL||`https://sts.${r}.amazonaws.com`).replace(/\/+$/,"")}}),a=Li(t);if(e.role&&a&&!s){let l=Ru(a,e.role);return{...o,accessKeyId:l.accessKeyId,secretAccessKey:l.secretAccessKey,...l.sessionToken?{sessionToken:l.sessionToken}:{}}}if(e.role){if(!s)throw new Y(`reports.role is set, but AWS_WEB_IDENTITY_TOKEN_FILE is not: the job needs an OIDC token to assume ${e.role}, which a job with oidc gets`);return i(e.role)}let c=t.AWS_ACCESS_KEY_ID,u=t.AWS_SECRET_ACCESS_KEY;if(c&&u)return{...o,accessKeyId:c,secretAccessKey:u,...t.AWS_SESSION_TOKEN?{sessionToken:t.AWS_SESSION_TOKEN}:{}};if(t.AWS_ROLE_ARN&&s)return i(t.AWS_ROLE_ARN);throw new Y("reports.bucket is set, but the job has no credentials for it: set AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY, or AWS_ROLE_ARN and AWS_WEB_IDENTITY_TOKEN_FILE (a job with oidc sets both), or reports.role")}async function Bf(e,t,n=r=>mX(r,"utf-8")){let r;try{r=n(e.tokenFile).trim()}catch(d){throw new Y(`cannot read the OIDC token in ${e.tokenFile}: ${d.message}`)}if(!r)throw new Y(`the OIDC token file ${e.tokenFile} is empty`);let o=new URLSearchParams({Action:"AssumeRoleWithWebIdentity",Version:"2011-06-15",RoleArn:e.roleArn,RoleSessionName:e.sessionName,WebIdentityToken:r}).toString(),s=await t(`${e.endpoint}/`,{method:"POST",headers:{"content-type":"application/x-www-form-urlencoded; charset=utf-8",accept:"application/xml"},body:o}),i=await s.text();if(!s.ok){let d=[hn(i,"Code"),hn(i,"Message")].filter(Boolean).join(": ")||i.slice(0,300);throw new Y(`AssumeRoleWithWebIdentity for ${e.roleArn}: ${s.status} ${d}`)}let a=hn(i,"AccessKeyId"),c=hn(i,"SecretAccessKey"),u=hn(i,"SessionToken");if(!a||!c||!u)throw new Y(`AssumeRoleWithWebIdentity for ${e.roleArn} answered without credentials`);let l=hn(i,"Expiration");return{accessKeyId:a,secretAccessKey:c,sessionToken:u,...l&&!Number.isNaN(Date.parse(l))?{expiration:new Date(l)}:{}}}var Uf=e=>gX("sha256").update(e).digest("hex"),Ya=(e,t)=>RR("sha256",e).update(t).digest();function zf(e,t,n,r,o=new Date,s,i={}){let a=tw(e,n);return{url:a,headers:xu(e,t,a,{...s?{"content-type":s}:{},...i},Uf(r),o)}}function xu(e,t,n,r,o,s,i="s3"){let a=new URL(n),c=s.toISOString().replace(/[-:]/g,"").replace(/\.\d{3}/,""),u=c.slice(0,8),l={host:a.host,"x-amz-content-sha256":o,"x-amz-date":c,...Object.fromEntries(Object.entries(r).map(([k,v])=>[k.toLowerCase(),v])),...e.sessionToken?{"x-amz-security-token":e.sessionToken}:{}},d=Object.keys(l).sort(),p=[...a.searchParams].map(([k,v])=>`${Zt(k)}=${Zt(v)}`).sort().join("&"),f=[t,a.pathname,p,...d.map(k=>`${k}:${l[k].trim()}`),"",d.join(";"),o].join(`
|
|
45
45
|
`),g=`${u}/${e.region}/${i}/aws4_request`,m=["AWS4-HMAC-SHA256",c,g,Uf(f)].join(`
|
|
46
|
-
`),h=Ya(Ya(Ya(Ya(`AWS4${e.secretAccessKey}`,u),e.region),i),"aws4_request"),y=RR("sha256",h).update(m).digest("hex"),{host:w,...b}=l;return{...b,authorization:`AWS4-HMAC-SHA256 Credential=${e.accessKeyId}/${g}, SignedHeaders=${d.join(";")}, Signature=${y}`}}var tw=(e,t)=>`${e.endpoint?`${e.endpoint}/${e.bucket}`:`https://${e.bucket}.s3.${e.region}.amazonaws.com`}/${Xl(t)}`;function
|
|
46
|
+
`),h=Ya(Ya(Ya(Ya(`AWS4${e.secretAccessKey}`,u),e.region),i),"aws4_request"),y=RR("sha256",h).update(m).digest("hex"),{host:w,...b}=l;return{...b,authorization:`AWS4-HMAC-SHA256 Credential=${e.accessKeyId}/${g}, SignedHeaders=${d.join(";")}, Signature=${y}`}}var tw=(e,t)=>`${e.endpoint?`${e.endpoint}/${e.bucket}`:`https://${e.bucket}.s3.${e.region}.amazonaws.com`}/${Xl(t)}`;function yX(e,t,n,r){if(!Number.isInteger(n)||n<1||n>Jn)throw new Y(`a presigned link lives 1 to ${Jn} seconds, not ${n}`);let o=new URL(t),s=r.toISOString().replace(/[-:]/g,"").replace(/\.\d{3}/,""),i=`${s.slice(0,8)}/${e.region}/s3/aws4_request`,a={"X-Amz-Algorithm":"AWS4-HMAC-SHA256","X-Amz-Credential":`${e.accessKeyId}/${i}`,"X-Amz-Date":s,"X-Amz-Expires":String(n),...e.sessionToken?{"X-Amz-Security-Token":e.sessionToken}:{},"X-Amz-SignedHeaders":"host"},c=Object.keys(a).sort().map(p=>`${Zt(p)}=${Zt(a[p])}`).join("&"),u=["GET",o.pathname,c,`host:${o.host}`,"","host","UNSIGNED-PAYLOAD"].join(`
|
|
47
47
|
`),l=["AWS4-HMAC-SHA256",s,i,Uf(u)].join(`
|
|
48
|
-
`),d=Ya(Ya(Ya(Ya(`AWS4${e.secretAccessKey}`,s.slice(0,8)),e.region),"s3"),"aws4_request");return`${o.origin}${o.pathname}?${c}&X-Amz-Signature=${RR("sha256",d).update(l).digest("hex")}`}var ld=class{constructor(t,n=fetch){this.target=t;this.fetchFn=n}target;fetchFn;creds;conditional=!0;get location(){return`s3://${this.target.bucket}`}async signer(){let t=this.target;if(!("webIdentity"in t))return t;let n=()=>this.creds=Bf(t.webIdentity,this.fetchFn).catch(a=>{throw this.creds=void 0,a}),r=await(this.creds??n());r.expiration&&r.expiration.getTime()-Date.now()<6e4&&(r=await n());let{accessKeyId:o,secretAccessKey:s,sessionToken:i}=r;return{bucket:t.bucket,...t.endpoint?{endpoint:t.endpoint}:{},region:t.region,accessKeyId:o,secretAccessKey:s,...i?{sessionToken:i}:{}}}async put(t,n,r,o){let s=!o||!this.conditional?{}:"ifMatch"in o?{"if-match":o.ifMatch}:{"if-none-match":"*"},{url:i,headers:a}=zf(await this.signer(),"PUT",t,n,new Date,r,s),c=await this.fetchFn(i,{method:"PUT",headers:a,body:n});if(c.status===501&&Object.keys(s).length)return this.conditional=!1,this.put(t,n,r);if(c.status===412||c.status===409&&Object.keys(s).length)throw new Er(`PUT s3://${this.target.bucket}/${t}: ${c.status}, it changed since it was read`);if(!c.ok)throw new Y(`PUT s3://${this.target.bucket}/${t}: ${c.status} ${(await c.text()).slice(0,300)}`);let u=c.headers?.get("etag")??void 0;return u?{etag:u}:{}}async read(t){let{url:n,headers:r}=zf(await this.signer(),"GET",t,""),o=await this.fetchFn(n,{method:"GET",headers:r});if(o.status===404)return{};if(!o.ok)throw new Y(`GET s3://${this.target.bucket}/${t}: ${o.status} ${(await o.text()).slice(0,300)}`);let s=o.headers?.get("etag")??void 0;return{body:await o.text(),...s?{etag:s}:{}}}async presign(t,n,r=new Date){let o=await this.signer(),s=y7(o,tw(o,t),n,r),i=new Date(r.getTime()+n*1e3),a=this.creds?await this.creds:void 0;return a?.expiration&&a.expiration<i&&(i=a.expiration),{url:s,expires:i}}async get(t){return(await this.read(t)).body}async head(t){let{url:n,headers:r}=zf(await this.signer(),"HEAD",t,""),o=await this.fetchFn(n,{method:"HEAD",headers:r});if(o.status===404)return{exists:!1};if(!o.ok)throw new Y(`HEAD s3://${this.target.bucket}/${t}: ${o.status}`);let s=o.headers?.get("etag")??void 0,i=o.headers?.get("x-amz-version-id")??void 0;return{exists:!0,...s?{etag:s}:{},...i&&i!=="null"?{versionId:i}:{}}}async putIfAbsent(t,n,r){let{url:o,headers:s}=zf(await this.signer(),"PUT",t,n,new Date,r,{"if-none-match":"*"}),i=await this.fetchFn(o,{method:"PUT",headers:s,body:n});if(i.status===412||i.status===409)return!1;if(i.status===501)throw new Y(`PUT s3://${this.target.bucket}/${t}: the store does not take a conditional write (501)`);if(!i.ok)throw new Y(`PUT s3://${this.target.bucket}/${t}: ${i.status} ${(await i.text()).slice(0,300)}`);return!0}async readVersion(t,n){let r=await this.signer(),o=`${tw(r,t)}?versionId=${Zt(n)}`,s=await this.fetchFn(o,{method:"GET",headers:xu(r,"GET",o,{},Uf(""),new Date)});if(s.status!==404){if(!s.ok)throw new Y(`GET s3://${this.target.bucket}/${t}?versionId=${n}: ${s.status} ${(await s.text()).slice(0,300)}`);return s.text()}}async hasVersion(t,n){let r=await this.signer(),o=`${tw(r,t)}?versionId=${Zt(n)}`,s=await this.fetchFn(o,{method:"HEAD",headers:xu(r,"HEAD",o,{},Uf(""),new Date)});if(s.status===404||s.status===400)return!1;if(!s.ok)throw new Y(`HEAD s3://${this.target.bucket}/${t}?versionId=${n}: ${s.status}`);return!0}async remove(t){let{url:n,headers:r}=zf(await this.signer(),"DELETE",t,""),o=await this.fetchFn(n,{method:"DELETE",headers:r});if(!o.ok&&o.status!==404)throw new Y(`DELETE s3://${this.target.bucket}/${t}: ${o.status} ${(await o.text()).slice(0,300)}`)}};import{posix as pd,sep as uN}from"node:path";var fd=e=>({unresolved:e});function Gi(e){if(e!==void 0)return typeof e=="string"&&!/\$\{|%\{/.test(e)?e:null}function lN(e,t){try{let n=new URL(e.includes("://")?e:`http://${e}`),r=n.pathname.replace(/\/+$/,"");return`${t?`${n.protocol}//`:""}${n.host.toLowerCase()}${r}`}catch{return}}function w7(e){if(/^postgres(ql)?:\/\//i.test(e))try{let o=new URL(e),s=o.pathname.replace(/^\/+/,"")||o.searchParams.get("dbname"),i=o.host.toLowerCase()||o.searchParams.get("host");return i&&s?`${i}/${s}`:void 0}catch{return}let t=new Map([...e.matchAll(/(\w+)\s*=\s*('(?:[^'\\]|\\.)*'|\S+)/g)].map(o=>[o[1],o[2].replace(/^'|'$/g,"")])),n=t.get("host")?.toLowerCase(),r=t.get("dbname");if(!(!n||!r))return`${n}${t.get("port")?`:${t.get("port")}`:""}/${r}`}function gd(e,t,n){let r=(c,u)=>fd(t(c)===null?`its ${e} ${c} is an expression, not a plain string`:`its ${e} backend names no ${c} in the code (${u?`${u} or `:""}a -backend-config file supplies it)`);switch(e){case"local":{let c=t("path");if(c===null)return r("path");let u=(c??"terraform.tfstate").split(uN).join(pd.sep);return{key:`local:${pd.isAbsolute(u)?pd.normalize(u):pd.normalize(pd.join(n.split(uN).join(pd.sep)||".",u))}`}}case"pg":{let c=t("conn_str");if(!c)return r("conn_str","PG_CONN_STR");let u=w7(c);if(!u)return fd("its pg conn_str names no host and database");let l=t("schema_name"),d=t("table_name");return l===null?r("schema_name"):d===null?r("table_name"):{key:`pg:${u}/${l??"terraform_remote_state"}.${d??"states"}`}}case"http":{let c=t("address");if(!c)return r("address","TF_HTTP_ADDRESS");let u=lN(c,!0);return u?{key:`http:${u}`}:fd("its http address is not a URL")}case"consul":{let c=t("address"),u=t("path");if(!u)return r("path");if(!c)return r("address","CONSUL_HTTP_ADDR");let l=lN(c,!1);return l?{key:`consul:${l}/${u.replace(/^\/+/,"")}`}:fd("its consul address is not a host")}case"kubernetes":{let c=t("secret_suffix"),u=t("namespace");return c?u?{key:`kubernetes:${u}/${c}`}:r("namespace","KUBE_NAMESPACE"):r("secret_suffix")}case"remote":case"cloud":{let c=t("hostname"),u=t("organization"),l=t("name");return c===null?r("hostname"):u?l===null?fd(`its ${e} workspace name is an expression, not a plain string`):l===void 0?fd(`its ${e} workspaces block names no workspace by name (one picked by tags, prefix or TF_WORKSPACE cannot be read from the code)`):{key:`remote:${(c??"app.terraform.io").toLowerCase()}/${u}/${l}`}:r("organization",e==="cloud"?"TF_CLOUD_ORGANIZATION":void 0)}}let o=t("key"),s=t("prefix"),i=o||s;if(!i)return r(o===null?"key":s===null||e==="gcs"?"prefix":"key");let a=t("bucket");return a===null?r("bucket"):{...a?{bucket:a}:{},key:i}}var ar=e=>!("unsupported"in e)&&(e.backend==="s3"||e.backend==="gcs"||e.backend==="azurerm"),Wf="reads state versions from s3, gcs, azurerm, local and GitLab-managed http backends",ut=e=>typeof e=="string"&&e!==""?e:void 0,fN={pg:"a pg backend keeps one row per workspace, which each write replaces",kubernetes:"a kubernetes backend keeps one Secret per workspace, which each write replaces",consul:"a consul backend keeps one key per workspace, which each write replaces",http:"an http backend keeps what its server holds at the address and names no versions"};function $7(e,t={}){if(e.type in fN&&!(e.type==="http"&&Mf(ut(e.config.address)??t.TF_HTTP_ADDRESS)))return!1}function k7(e){let t=Array.isArray(e.config.workspaces)?e.config.workspaces[0]:e.config.workspaces,n=o=>Gi(e.config[o]??(t&&typeof t=="object"?t[o]:void 0)),r=gd(e.type,n,"");return"key"in r?r.key:void 0}function rw(e,t=process.env){let n=t.TF_DATA_DIR;return n?b7(n)?n:md(e,n):md(e,".terraform")}function _7(e,t=process.env){let n=md(rw(e,t),"terraform.tfstate");if(xR(n))try{let r=JSON.parse(ER(n,"utf-8")),o=ut(r.backend?.type);if(!o)return;let s=r.backend?.config&&typeof r.backend.config=="object"?r.backend.config:{};return{type:o,config:s}}catch{return}}var ow=".terragucci-workspace";function hd(e){let t=md(e,ow);return xR(t)&&ER(t,"utf-8").trim()||void 0}function ls(e,t){let n=hd(t);return n?{...e,TF_WORKSPACE:n}:e}function Fi(e,t){let n=hd(t);if(!n)return;let{TF_WORKSPACE:r,...o}=e;return{init:{...e,TF_WORKSPACE:"default"},select:["workspace","select","-or-create=true",n],selectEnv:o}}function S7(e,t=process.env){if(t.TF_WORKSPACE)return t.TF_WORKSPACE;let n=md(rw(e,t),"environment");if(xR(n)){let r=ER(n,"utf-8").trim();if(r)return r}return"default"}function R7(e,t,n="env:"){return t==="default"?e:`${n}/${t}/${e}`}function Hr(e,t=process.env,n=_7(e,t)){let r=S7(e,t);if(!n||n.type==="local"){let g=n?.config??{};return{backend:"local",path:r==="default"?ut(g.path)??"terraform.tfstate":md(ut(g.workspace_dir)??"terraform.tfstate.d",r,"terraform.tfstate")}}if(n.type==="http"){let g=QC(n.config,t);if(g)return{backend:"http",gitlab:g,location:xn(g.address)}}if(n.type==="gcs")return E7(n.config,r,t);if(n.type==="azurerm")return A7(n.config,r,t);if(n.type!=="s3"){let g=k7(n),m=g?{location:g}:{},h=`terragucci ${Wf}, and this root's backend is ${n.type==="http"?"an http backend whose address is not a GitLab project's state API":n.type}`;return $7(n,t)===!1?{backend:n.type,unsupported:h,...m,versions:{versioning:"off",note:fN[n.type]}}:{backend:n.type,unsupported:h,...m,versions:{versioning:"unknown",note:`a ${n.type} backend can keep versions of the state, which terragucci does not read`}}}let o=n.config,s=ut(o.bucket),i=ut(o.key);if(!s||!i)return{backend:"s3",unsupported:"the s3 backend's bucket or key is not in the configuration init recorded"};let a=o.endpoints&&typeof o.endpoints=="object"?o.endpoints:{},c=ut(a.s3)??ut(o.endpoint)??t.AWS_ENDPOINT_URL_S3??t.AWS_ENDPOINT_URL,u=ut(o.region)??t.AWS_REGION??t.AWS_DEFAULT_REGION??"us-east-1",l=ut(o.access_key),d=ut(o.secret_key),p={...t,AWS_REGION:u,...l&&d?{AWS_ACCESS_KEY_ID:l,AWS_SECRET_ACCESS_KEY:d,AWS_SESSION_TOKEN:ut(o.token)??""}:{}},f;try{f=dd({bucket:s,...c?{endpoint:c}:{}},p)}catch(g){return{backend:"s3",unsupported:`the job has no credentials to read s3://${s}: ${g.message}`}}return{backend:"s3",bucket:s,key:R7(i,r,ut(o.workspace_key_prefix)),region:u,...c?{endpoint:c.replace(/\/+$/,"")}:{},lockfile:o.use_lockfile===!0,dynamodb:ut(o.dynamodb_table)!==void 0,target:f}}var x7=e=>e.replace(/\/+$/,"").replace(/\/storage\/v1$/,"");function E7(e,t,n){let r=ut(e.bucket);if(!r)return{backend:"gcs",unsupported:"the gcs backend's bucket is not in the configuration init recorded"};let o=ut(e.prefix)??"",s=o===""||o.endsWith("/")?o:`${o}/`,i=`${s}${t}.tfstate`,a=x7(ut(e.storage_custom_endpoint)??n.GOOGLE_BACKEND_STORAGE_CUSTOM_ENDPOINT??n.GOOGLE_STORAGE_CUSTOM_ENDPOINT??ew),c=ut(e.impersonate_service_account)??n.GOOGLE_BACKEND_IMPERSONATE_SERVICE_ACCOUNT??n.GOOGLE_IMPERSONATE_SERVICE_ACCOUNT,u={bucket:r,endpoint:a,...c?{impersonate:c}:{}},l=ut(e.access_token)??n.GOOGLE_OAUTH_ACCESS_TOKEN,d=ut(e.credentials)??n.GOOGLE_BACKEND_CREDENTIALS??n.GOOGLE_CREDENTIALS,p;if(l)p={...u,accessToken:l};else if(d)p=d.trim().startsWith("{")?{...u,credentialsJson:d}:{...u,credentialsFile:d};else if(n.GOOGLE_APPLICATION_CREDENTIALS)p={...u,credentialsFile:n.GOOGLE_APPLICATION_CREDENTIALS};else return{backend:"gcs",unsupported:`the job has no credentials to read gs://${r}: give the job oidc.gcp (it sets GOOGLE_APPLICATION_CREDENTIALS), or set GOOGLE_CREDENTIALS or GOOGLE_OAUTH_ACCESS_TOKEN`};return{backend:"gcs",bucket:r,key:i,lock:`${s}${t}.tflock`,target:p}}var T7={public:{storage:"core.windows.net",authority:Qy},china:{storage:"core.chinacloudapi.cn",authority:"https://login.chinacloudapi.cn"},usgovernment:{storage:"core.usgovcloudapi.net",authority:"https://login.microsoftonline.us"}};function A7(e,t,n){let r=ut(e.storage_account_name),o=ut(e.container_name),s=ut(e.key);if(!r||!o||!s)return{backend:"azurerm",unsupported:"the azurerm backend's storage_account_name, container_name or key is not in the configuration init recorded"};let i=t==="default"?s:`${s}env:${t}`,a=T7[ut(e.environment)??n.ARM_ENVIRONMENT??"public"],c=ut(e.metadata_host)??n.ARM_METADATA_HOSTNAME??n.ARM_METADATA_HOST;if(!a&&!c)return{backend:"azurerm",unsupported:`the azurerm backend's environment ${ut(e.environment)??n.ARM_ENVIRONMENT} is not public, china or usgovernment, and it names no metadata_host`};let u=ut(e.access_key)??n.ARM_ACCESS_KEY,l=ut(e.sas_token)??n.ARM_SAS_TOKEN,d=ut(e.tenant_id)??n.ARM_TENANT_ID,p=ut(e.client_id)??n.ARM_CLIENT_ID,f=ut(e.oidc_token_file_path)??n.ARM_OIDC_TOKEN_FILE_PATH,g;if(u)g={accountKey:u};else if(l)g={sas:l};else if(d&&p&&f)g={oidc:{tenantId:d,clientId:p,tokenFile:f,authority:(n.AZURE_AUTHORITY_HOST||a?.authority||Qy).replace(/\/+$/,"")}};else return{backend:"azurerm",unsupported:`the job has no credentials to read the container ${o} of ${r}: give the job oidc.azure (it sets ARM_TENANT_ID, ARM_CLIENT_ID and ARM_OIDC_TOKEN_FILE_PATH), or set ARM_ACCESS_KEY`};return{backend:"azurerm",account:r,container:o,key:i,snapshot:e.snapshot===!0||n.ARM_SNAPSHOT==="true",...c?{metadataHost:c}:{endpoint:`https://${r}.blob.${a.storage}`},auth:g}}function sw(e){if("unsupported"in e)return;if("gitlab"in e)return e.location;if(e.backend==="local")return e.path;let t=e;return t.backend==="gcs"?`gs://${t.bucket}/${t.key}`:t.backend==="azurerm"?`az://${t.account}/${t.container}/${t.key}`:`s3://${t.bucket}/${t.key}`}function iw(e,t){return new ld(e.target,t)}var dN=e=>v7("sha256").update(e).digest("hex");function O7(e,t){let n=iw(e,t),r=`${e.key}.tflock`,o=async()=>{let s=await n.head(e.key);return s.exists?s.versionId?{exists:!0,versionId:s.versionId}:{exists:!0,off:`bucket versioning is off for ${e.bucket}, so it keeps only the latest state`}:{exists:!1}};return{backend:"s3",location:`s3://${e.bucket}/${e.key}`,lockLocation:`s3://${e.bucket}/${r}`,version:o,hasVersion:s=>n.hasVersion(e.key,s),readVersion:s=>n.readVersion(e.key,s),isAt:async s=>(await o()).versionId===s,heldLock:async()=>{let s=await n.get(r);if(s!==void 0)return{id:nw(s),info:s}},lock:s=>n.putIfAbsent(r,s,"application/json"),unlock:()=>n.remove(r),readBeside:s=>n.get(`${e.key}${s}`),writeBeside:async(s,i)=>{await n.put(`${e.key}${s}`,i,"application/json")},adopt:()=>{}}}function nw(e){try{let t=JSON.parse(e);return typeof t.ID=="string"?t.ID:""}catch{return""}}function P7(e,t){let n=new Za(e.target,t),r,o=async()=>{let s=await n.head(e.key);return s.exists?(r??=n.versioning(),await r===!1?{exists:!0,off:`object versioning is off for ${e.bucket}, so it keeps only the latest state`}:{exists:!0,...s.generation?{versionId:s.generation}:{}}):{exists:!1}};return{backend:"gcs",location:`gs://${e.bucket}/${e.key}`,lockLocation:`gs://${e.bucket}/${e.lock}`,version:o,hasVersion:async s=>(await n.head(e.key,s)).exists,readVersion:s=>n.readGeneration(e.key,s),isAt:async s=>(await n.head(e.key)).generation===s,heldLock:async()=>{let s=await n.head(e.lock),i=s.exists?await n.get(e.lock):void 0;if(!(!s.exists||i===void 0))return{id:s.generation??nw(i),info:i}},lock:s=>n.putIfAbsent(e.lock,s,"application/json"),unlock:()=>n.remove(e.lock),readBeside:s=>n.get(`${e.key}${s}`),writeBeside:async(s,i)=>{await n.put(`${e.key}${s}`,i,"application/json")},adopt:()=>{}}}var pN="terraformlockid";async function j7(e,t){if(e.endpoint)return e.endpoint;let n=`https://${e.metadataHost}/metadata/endpoints?api-version=2023-11-01`,r=await t(n,{method:"GET",headers:{accept:"application/json"}}),o=await r.text();if(!r.ok)throw new Y(`the Azure metadata at ${e.metadataHost}: ${r.status}`);let s;try{let i=JSON.parse(o);s=(Array.isArray(i)?i[0]:i)?.suffixes?.storage}catch{}if(typeof s!="string"||s==="")throw new Y(`the Azure metadata at ${e.metadataHost} names no storage suffix`);return`https://${e.account}.blob.${s.replace(/^\.+/,"")}`}async function I7(e,t=fetch){let n=await j7(e,t),r=new ud({account:e.account,container:e.container,endpoint:n,...e.auth},t),o,s=async a=>(await r.head(e.key)).versionId?{versionid:a}:{snapshot:a},i=async(a=!1)=>{let c=await r.head(e.key);return c.exists?c.versionId?{exists:!0,versionId:c.versionId}:e.snapshot?a?{exists:!0,versionId:await r.snapshot(e.key)}:{exists:!0}:{exists:!0,off:`blob versioning is off for ${e.account}, and the backend takes no snapshots (snapshot = true), so it keeps only the latest state`}:{exists:!1}};return{backend:"azurerm",location:`az://${e.account}/${e.container}/${e.key}`,lockLocation:`az://${e.account}/${e.container}/${e.key}`,version:i,hasVersion:async a=>(await r.head(e.key,await s(a))).exists,readVersion:async a=>r.readAt(e.key,await s(a)),isAt:async a=>{let c=await r.head(e.key);if(c.versionId)return c.versionId===a;let[u,l]=await Promise.all([r.readAt(e.key,{snapshot:a}),r.get(e.key)]);return u!==void 0&&l!==void 0&&dN(u)===dN(l)},heldLock:async()=>{let a=await r.head(e.key);if(!a.exists||a.leaseState!=="leased")return;let c=a.metadata[pN],u=c?Buffer.from(c,"base64").toString("utf-8"):"";return{id:nw(u),info:u}},lock:async a=>{let c=nw(a);return(await r.head(e.key)).exists||await r.putBlob(e.key,"","application/json",{ifNoneMatch:!0}),await r.lease(e.key,"acquire",c)?(o=c,await r.setMetadata(e.key,{[pN]:Buffer.from(a).toString("base64")},c),!0):!1},unlock:async()=>{if(!o)return;let a=o;await r.setMetadata(e.key,{},a),await r.lease(e.key,"release",a),o=void 0},write:async a=>{if(!o)throw new Y(`${e.key} is written under its lease, and none is held`);e.snapshot&&(await r.head(e.key)).exists&&await r.snapshot(e.key);let c=(await r.head(e.key)).metadata;await r.putBlob(e.key,a,"application/json",{leaseId:o,metadata:c})},readBeside:a=>r.get(`${e.key}${a}`),writeBeside:async(a,c)=>{await r.put(`${e.key}${a}`,c,"application/json")},adopt:a=>{o=a.id}}}async function Ws(e,t){return e.backend==="s3"?O7(e,t):e.backend==="gcs"?P7(e,t):I7(e,t)}async function Hf(e,t=process.env,n,r){let o=r?Hr(e,t,r):Hr(e,t);if("unsupported"in o)return{backend:o.backend,...o.location?{location:o.location}:{},versioning:o.versions?.versioning??"unknown",note:o.versions?.note??o.unsupported};if(o.backend==="local")return{backend:"local",location:o.path,versioning:"off",note:"a local backend keeps only the latest state"};if("gitlab"in o)try{let i=await Yy(o.gitlab,n??globalThis.fetch);return i?{backend:"http",location:o.location,version_id:String(i.serial),versioning:"on"}:{backend:"http",location:o.location,versioning:"unknown",note:"GitLab holds no state at this address"}}catch(i){return{backend:"http",location:o.location,versioning:"unknown",note:i.message}}if(!ar(o))return{backend:o.backend,versioning:"unknown",note:"terragucci reads no versions of this state"};let s=sw(o);try{let i=await(await Ws(o,n)).version(!0);return i.exists?i.versionId?{backend:o.backend,location:s,version_id:i.versionId,versioning:"on"}:i.off?{backend:o.backend,location:s,versioning:"off",note:i.off}:{backend:o.backend,location:s,versioning:"unknown",note:"the store gave no version id"}:{backend:o.backend,location:s,versioning:"unknown",note:`the state object is not in the ${o.backend==="azurerm"?"container":"bucket"}`}}catch(i){return{backend:o.backend,location:s,versioning:"unknown",note:i.message}}}import{execFile as C7}from"node:child_process";var N7=["TG_TOKEN","TG_MERGE_TOKEN","GITHUB_TOKEN","GH_TOKEN","GITLAB_TOKEN","CI_JOB_TOKEN","FORGEJO_TOKEN","GITEA_TOKEN","ACTIONS_RUNTIME_TOKEN"];function Ve(e){let t=new Set([e.TG_TOKEN,e.TG_MERGE_TOKEN].filter(r=>typeof r=="string"&&r.length>0)),n={};for(let[r,o]of Object.entries(e))N7.includes(r)||o!==void 0&&t.has(o)&&!r.startsWith("TF_")||(n[r]=o);return n}var Nn=(e,t,n)=>new Promise(r=>{C7(e,[...t],{cwd:n.cwd,env:Ve({...process.env,...n.env}),maxBuffer:256*1024*1024},(o,s,i)=>{let a=o?typeof o.code=="number"?o.code:null:0;r({code:a,stdout:String(s),stderr:o&&a===null?`${String(i)}${o.message}`:String(i)})})});import{existsSync as FR,readdirSync as FN,readFileSync as Qf,statSync as AX}from"node:fs";import{dirname as OX,join as eg,relative as fw,resolve as GR}from"node:path";import{execFile as q7}from"node:child_process";import{existsSync as yd,mkdirSync as EN,readFileSync as uw,realpathSync as J7,rmSync as TN,writeFileSync as K7}from"node:fs";import{dirname as V7,join as ds,resolve as Kf}from"node:path";function TR(e){let t=e,r=(Array.isArray(t?.resource_changes)?t.resource_changes:[]).map(s=>{let i=s??{},a=i.change??{};return{address:i.address,mode:i.mode,type:i.type,name:i.name,index:i.index,provider_name:i.provider_name,deposed:i.deposed,actions:a.actions,before:a.before,after:a.after,after_unknown:a.after_unknown,replace_paths:a.replace_paths,importing:a.importing}});r.sort((s,i)=>`${String(s.address)}\0${String(s.deposed??"")}`.localeCompare(`${String(i.address)}\0${String(i.deposed??"")}`));let o=Array.isArray(t?.action_invocations)?[...t.action_invocations].sort((s,i)=>JSON.stringify(s).localeCompare(JSON.stringify(i))):[];return{resourceChanges:r,outputChanges:t?.output_changes??{},...o.length>0?{actionInvocations:o}:{}}}function gN(e){return Rn("terraform-plan",TR(e))}var Tr=e=>e!==null&&typeof e=="object"&&!Array.isArray(e);function qf(e){return e===!0?!0:Array.isArray(e)?e.some(qf):Tr(e)?Object.values(e).some(qf):!1}function AR(e,t){if(e===t)return!0;if(Array.isArray(e)&&Array.isArray(t))return e.length===t.length&&e.every((n,r)=>AR(n,t[r]));if(Tr(e)&&Tr(t)){let n=Object.keys(e);return n.length===Object.keys(t).length&&n.every(r=>r in t&&AR(e[r],t[r]))}return!1}function nn(e){let t=(e??"terraform").split(/[\\/]/).pop()??"";return t.startsWith("choudoufu")?"choudoufu":t.startsWith("tofu")?"tofu":"terraform"}function D7(e){let t=Array.isArray(e)?e.map(String):[];if(t.length===2&&t.includes("delete")&&t.includes("create"))return"replace";if(t.length===1)switch(t[0]){case"create":case"update":case"delete":case"read":case"no-op":case"forget":return t[0]}throw new Error(`a plan entry has actions ${JSON.stringify(e)}, which the change set has no action for`)}function M7(e,t){switch(t){case"update":return"in-place";case"delete":return"destroy";case"replace":return e[0]==="create"?"replace":"destroy";default:return}}function L7(e){let n=/^((?:module\.[^.[\]]+(?:\[(?:"(?:[^"\\]|\\.)*"|\d+)\])?\.)*)(?:data\.)?([^.[\]]+)\.([^.[\]]+)(?:\[("(?:[^"\\]|\\.)*"|\d+)\])?$/.exec(e);if(!n)return{type:"unknown",name:e};let r=n[1]?n[1].slice(0,-1):void 0,o=n[4]===void 0?void 0:n[4].startsWith('"')?JSON.parse(n[4]):Number(n[4]);return{...r?{module:r}:{},type:n[2],name:n[3],...o!==void 0?{index:o}:{}}}function G7(e,t){if(t==="delete"||t==="no-op"||t==="forget"||t==="read")return[];let n=Tr(e.before)?e.before:{},r=Tr(e.after)?e.after:{},o=Tr(e.after_unknown)?e.after_unknown:{},s=Tr(e.before_sensitive)?e.before_sensitive:{},i=Tr(e.after_sensitive)?e.after_sensitive:{},a=new Set((Array.isArray(e.replace_paths)?e.replace_paths:[]).map(l=>Array.isArray(l)?l[0]:void 0).filter(l=>typeof l=="string")),c=[...new Set([...Object.keys(n),...Object.keys(r),...Object.keys(o)])].sort(),u=[];for(let l of c){let d=qf(o[l]),p=t!=="create"&&n[l]!==void 0&&n[l]!==null,f=r[l]!==void 0&&r[l]!==null;if(t==="create"?!f&&!d:!d&&AR(n[l]??null,r[l]??null))continue;let g=qf(s[l])||qf(i[l]);u.push({path:l,...g?{sensitive:!0}:{...p?{before:n[l]}:{},...f&&!d?{after:r[l]}:{}},...d?{unknown:!0}:{},...a.has(l)?{forcesReplacement:!0}:{}})}return u}function F7(e){for(let t of[e.after,e.before])if(Tr(t)&&typeof t.region=="string"&&t.region!=="")return t.region}function fo(e){let t=Tr(e.plan)?e.plan:{},n=e.planner??"terraform",o=(Array.isArray(t.resource_changes)?t.resource_changes:[]).map(a=>{let c=Tr(a)?a:{},u=Tr(c.change)?c.change:{},l=String(c.address??""),d=L7(l),p=D7(u.actions),f=M7(u.actions,p),g=F7(u),m=c.index!==void 0&&c.index!==null?c.index:d.index,h=typeof c.module_address=="string"?c.module_address:d.module;return{member:e.member,lexicon:"terraform",planner:n,address:l,type:typeof c.type=="string"?c.type:d.type,name:typeof c.name=="string"?c.name:d.name,...m!==void 0?{index:m}:{},...h?{module:h}:{},...typeof c.deposed=="string"?{deposed:c.deposed}:{},action:p,...Tr(u.importing)?{importing:!0}:{},...f?{disruption:f}:{},...g?{region:g}:{},...e.scope?{scope:e.scope}:{},attributes:G7(u,p)}}),s=(Array.isArray(t.action_invocations)?t.action_invocations:[]).map(z7),i=t.errored===!0;return{member:{member:e.member,lexicon:"terraform",planner:n,...e.scope?{scope:e.scope}:{},status:i?"failed":"planned",...i?{error:`${n} marked the plan errored`}:{},planDigest:gN(t),...e.nativeDigest?{nativeDigest:e.nativeDigest}:{},holes:[]},entries:o,...s.length>0?{sideEffects:s}:{}}}function z7(e){let t=Tr(e)?e:{},n=typeof t.type=="string"?t.type:"unknown",r=Tr(t.lifecycle_action_trigger)?t.lifecycle_action_trigger:{};return{address:typeof t.address=="string"?t.address:n,type:n,...typeof r.triggering_resource_address=="string"?{trigger:r.triggering_resource_address}:{},...typeof r.action_trigger_event=="string"?{event:r.action_trigger_event}:{}}}var Xa=e=>e!==null&&typeof e=="object"&&!Array.isArray(e);function OR(e){let t=e.replace(/\\/g,"/"),n=[];for(let o of t.split("/"))o===""||o==="."||(o===".."&&n.length>0&&n[n.length-1]!==".."?n.pop():n.push(o));let r=n.join("/");return t.startsWith("/")?`/${r}`:r||"."}var U7=e=>e.startsWith("/")||/^[A-Za-z]:[\\/]/.test(e);function PR(e,t,n){let r=t.replace(/\\/g,"/").replace(/(^|\/)terragrunt\.hcl$/,"");if(!U7(r))return OR(`${e}/${r}`);let o=OR(r);for(let s of typeof n=="string"?[n]:n){let i=OR(s);if(o===i)return".";if(o.startsWith(`${i}/`))return o.slice(i.length+1)}return o}function mN(e,t,n){let r=typeof t=="string"?JSON.parse(t):t;if(!Xa(r))throw new Error("terragrunt render --json did not print an object");let o=Xa(r.dependency)?r.dependency:{},s=[];for(let[i,a]of Object.entries(o)){if(!Xa(a)||typeof a.config_path!="string")continue;let c=Array.isArray(a.mock_outputs_allowed_terraform_commands)?a.mock_outputs_allowed_terraform_commands.filter(l=>typeof l=="string"):void 0,u=typeof a.mock_outputs_merge_strategy_with_state=="string"?a.mock_outputs_merge_strategy_with_state:a.mock_outputs_merge_with_state===!0?"shallow":void 0;s.push({name:typeof a.name=="string"&&a.name?a.name:i,upstream:PR(e,a.config_path,n),...Xa(a.mock_outputs)?{mockOutputs:a.mock_outputs}:{},...c?{mockAllowed:c}:{},...u&&u!=="no_merge"?{mergeStrategy:u}:{},...a.skip===!0||a.skip_outputs===!0?{skipOutputs:!0}:{},...a.enabled===!1?{disabled:!0}:{}})}return s.sort((i,a)=>i.name<a.name?-1:i.name>a.name?1:0)}function hN(e){let t=e.trim(),n=t.indexOf("{");if(n<0)return;let r;try{r=JSON.parse(t.slice(n))}catch{return}if(Xa(r))return Object.fromEntries(Object.entries(r).map(([o,s])=>[o,Xa(s)&&"value"in s?s.value:s]))}function yN(e){return["run","--working-dir",e,"--non-interactive","--no-color","--","output","-json"]}function aw(e){return["render","--json","--non-interactive","--no-color","--working-dir",e]}function wN(e,t,n,r=""){let o=[];for(let[s,i]of Object.entries(e)){let a=r?`${r}.${s}`:s;s in t?n&&Xa(i)&&Xa(t[s])&&o.push(...wN(i,t[s],n,a)):o.push(a)}return o.sort()}var B7=e=>!e.mockAllowed||e.mockAllowed.length===0||e.mockAllowed.includes("plan");function bN(e){let t=[];for(let[n,r]of e.dependencies)for(let o of r){let s={unit:n,dependency:o.name,upstream:o.upstream};if(o.disabled||o.skipOutputs){o.mockOutputs&&t.push({...s,reason:o.disabled?"disabled":"skip-outputs"});continue}let i=e.outputs.get(o.upstream)??{};if(Object.keys(i).length===0){t.push({...s,reason:"no-outputs"});continue}if(o.mockOutputs&&o.mergeStrategy&&B7(o)){let a=wN(o.mockOutputs,i,o.mergeStrategy!=="shallow");a.length>0&&t.push({...s,reason:"partial",keys:a})}}return t.sort((n,r)=>n.unit<r.unit?-1:n.unit>r.unit?1:n.dependency<r.dependency?-1:n.dependency>r.dependency?1:0)}function vN(e){let t=new Set;for(let n of e.values())for(let r of n)!r.disabled&&!r.skipOutputs&&t.add(r.upstream);return[...t].sort()}function W7(e){let t=`${e.unit} (dependency "${e.dependency}" on ${e.upstream})`;switch(e.reason){case"no-outputs":return`${t}: ${e.upstream} has no outputs yet; apply ${e.upstream} first`;case"partial":return`${t}: ${e.upstream}'s outputs lack ${e.keys.join(", ")}, which the merge strategy fills from mock_outputs; apply ${e.upstream} with those outputs first, or drop the merge strategy`;case"skip-outputs":return`${t}: skip_outputs = true with mock_outputs set always reads the mock; read the real outputs or drop mock_outputs`;case"disabled":return`${t}: enabled = false with mock_outputs set always reads the mock; enable the dependency or drop mock_outputs`}}function $N(e){let t=[...new Set(e.filter(r=>r.reason==="no-outputs"||r.reason==="partial").map(r=>r.upstream))].sort();return["this wave was not planned and is not gated: its plans would read mock_outputs, so nobody could approve them as real. "+(t.length>0?`Apply ${t.join(", ")} first, then plan the wave again.`:"Fix the dependency blocks below, then plan the wave again."),...e.map(r=>` ${W7(r)}`)].join(`
|
|
49
|
-
`)}var
|
|
50
|
-
`);let c=IR({units:e.units,command:"plan",outDir:o,jsonOutDir:s,reportFile:i,...e.destroy?{destroy:!0}:{},...e.parallelism!==void 0?{parallelism:e.parallelism}:{}}),u=await t(n,c,{cwd:e.dir,env:zi(e.binary)}),l=yd(i)?Jf(uw(i,"utf8")):new Map,d=m=>{let h=ds(s,m,"tfplan.json");return yd(h)?JSON.parse(uw(h,"utf8")):void 0},p=RN({units:e.units,report:l,planFor:d,planner:
|
|
51
|
-
${u.stdout}`,AN(e.dir)).filter(m=>f.has(m.unit));return{parts:e.provisional?SN(p):_N(p,g),provisional:e.provisional===!0,results:cw(e.units,l),code:u.code,log:u.stderr}}async function ON(e){let t=e.exec??Eu,n=e.terragrunt??"terragrunt",r=Kf(e.dir,e.workDir),o=ds(r,"plans");if(e.provisional||yd(ds(r,jR)))throw new cr(`the plans under ${r} are provisional: planned before their upstream applied, possibly on mock_outputs. They are never applied; plan the wave again once its upstream applied`);let s=[...new Set(e.units)].sort().filter(l=>!yd(ds(o,l,"tfplan.tfplan")));if(s.length>0)throw new cr(`no saved plan for ${s.join(", ")} under ${o}; plan the wave before applying it, so apply runs the plan that was reviewed`);let i=ds(r,"apply-report.json");TN(i,{force:!0}),EN(
|
|
48
|
+
`),d=Ya(Ya(Ya(Ya(`AWS4${e.secretAccessKey}`,s.slice(0,8)),e.region),"s3"),"aws4_request");return`${o.origin}${o.pathname}?${c}&X-Amz-Signature=${RR("sha256",d).update(l).digest("hex")}`}var ld=class{constructor(t,n=fetch){this.target=t;this.fetchFn=n}target;fetchFn;creds;conditional=!0;get location(){return`s3://${this.target.bucket}`}async signer(){let t=this.target;if(!("webIdentity"in t))return t;let n=()=>this.creds=Bf(t.webIdentity,this.fetchFn).catch(a=>{throw this.creds=void 0,a}),r=await(this.creds??n());r.expiration&&r.expiration.getTime()-Date.now()<6e4&&(r=await n());let{accessKeyId:o,secretAccessKey:s,sessionToken:i}=r;return{bucket:t.bucket,...t.endpoint?{endpoint:t.endpoint}:{},region:t.region,accessKeyId:o,secretAccessKey:s,...i?{sessionToken:i}:{}}}async put(t,n,r,o){let s=!o||!this.conditional?{}:"ifMatch"in o?{"if-match":o.ifMatch}:{"if-none-match":"*"},{url:i,headers:a}=zf(await this.signer(),"PUT",t,n,new Date,r,s),c=await this.fetchFn(i,{method:"PUT",headers:a,body:n});if(c.status===501&&Object.keys(s).length)return this.conditional=!1,this.put(t,n,r);if(c.status===412||c.status===409&&Object.keys(s).length)throw new Er(`PUT s3://${this.target.bucket}/${t}: ${c.status}, it changed since it was read`);if(!c.ok)throw new Y(`PUT s3://${this.target.bucket}/${t}: ${c.status} ${(await c.text()).slice(0,300)}`);let u=c.headers?.get("etag")??void 0;return u?{etag:u}:{}}async read(t){let{url:n,headers:r}=zf(await this.signer(),"GET",t,""),o=await this.fetchFn(n,{method:"GET",headers:r});if(o.status===404)return{};if(!o.ok)throw new Y(`GET s3://${this.target.bucket}/${t}: ${o.status} ${(await o.text()).slice(0,300)}`);let s=o.headers?.get("etag")??void 0;return{body:await o.text(),...s?{etag:s}:{}}}async presign(t,n,r=new Date){let o=await this.signer(),s=yX(o,tw(o,t),n,r),i=new Date(r.getTime()+n*1e3),a=this.creds?await this.creds:void 0;return a?.expiration&&a.expiration<i&&(i=a.expiration),{url:s,expires:i}}async get(t){return(await this.read(t)).body}async head(t){let{url:n,headers:r}=zf(await this.signer(),"HEAD",t,""),o=await this.fetchFn(n,{method:"HEAD",headers:r});if(o.status===404)return{exists:!1};if(!o.ok)throw new Y(`HEAD s3://${this.target.bucket}/${t}: ${o.status}`);let s=o.headers?.get("etag")??void 0,i=o.headers?.get("x-amz-version-id")??void 0;return{exists:!0,...s?{etag:s}:{},...i&&i!=="null"?{versionId:i}:{}}}async putIfAbsent(t,n,r){let{url:o,headers:s}=zf(await this.signer(),"PUT",t,n,new Date,r,{"if-none-match":"*"}),i=await this.fetchFn(o,{method:"PUT",headers:s,body:n});if(i.status===412||i.status===409)return!1;if(i.status===501)throw new Y(`PUT s3://${this.target.bucket}/${t}: the store does not take a conditional write (501)`);if(!i.ok)throw new Y(`PUT s3://${this.target.bucket}/${t}: ${i.status} ${(await i.text()).slice(0,300)}`);return!0}async readVersion(t,n){let r=await this.signer(),o=`${tw(r,t)}?versionId=${Zt(n)}`,s=await this.fetchFn(o,{method:"GET",headers:xu(r,"GET",o,{},Uf(""),new Date)});if(s.status!==404){if(!s.ok)throw new Y(`GET s3://${this.target.bucket}/${t}?versionId=${n}: ${s.status} ${(await s.text()).slice(0,300)}`);return s.text()}}async hasVersion(t,n){let r=await this.signer(),o=`${tw(r,t)}?versionId=${Zt(n)}`,s=await this.fetchFn(o,{method:"HEAD",headers:xu(r,"HEAD",o,{},Uf(""),new Date)});if(s.status===404||s.status===400)return!1;if(!s.ok)throw new Y(`HEAD s3://${this.target.bucket}/${t}?versionId=${n}: ${s.status}`);return!0}async remove(t){let{url:n,headers:r}=zf(await this.signer(),"DELETE",t,""),o=await this.fetchFn(n,{method:"DELETE",headers:r});if(!o.ok&&o.status!==404)throw new Y(`DELETE s3://${this.target.bucket}/${t}: ${o.status} ${(await o.text()).slice(0,300)}`)}};import{posix as pd,sep as uN}from"node:path";var fd=e=>({unresolved:e});function Gi(e){if(e!==void 0)return typeof e=="string"&&!/\$\{|%\{/.test(e)?e:null}function lN(e,t){try{let n=new URL(e.includes("://")?e:`http://${e}`),r=n.pathname.replace(/\/+$/,"");return`${t?`${n.protocol}//`:""}${n.host.toLowerCase()}${r}`}catch{return}}function wX(e){if(/^postgres(ql)?:\/\//i.test(e))try{let o=new URL(e),s=o.pathname.replace(/^\/+/,"")||o.searchParams.get("dbname"),i=o.host.toLowerCase()||o.searchParams.get("host");return i&&s?`${i}/${s}`:void 0}catch{return}let t=new Map([...e.matchAll(/(\w+)\s*=\s*('(?:[^'\\]|\\.)*'|\S+)/g)].map(o=>[o[1],o[2].replace(/^'|'$/g,"")])),n=t.get("host")?.toLowerCase(),r=t.get("dbname");if(!(!n||!r))return`${n}${t.get("port")?`:${t.get("port")}`:""}/${r}`}function gd(e,t,n){let r=(c,u)=>fd(t(c)===null?`its ${e} ${c} is an expression, not a plain string`:`its ${e} backend names no ${c} in the code (${u?`${u} or `:""}a -backend-config file supplies it)`);switch(e){case"local":{let c=t("path");if(c===null)return r("path");let u=(c??"terraform.tfstate").split(uN).join(pd.sep);return{key:`local:${pd.isAbsolute(u)?pd.normalize(u):pd.normalize(pd.join(n.split(uN).join(pd.sep)||".",u))}`}}case"pg":{let c=t("conn_str");if(!c)return r("conn_str","PG_CONN_STR");let u=wX(c);if(!u)return fd("its pg conn_str names no host and database");let l=t("schema_name"),d=t("table_name");return l===null?r("schema_name"):d===null?r("table_name"):{key:`pg:${u}/${l??"terraform_remote_state"}.${d??"states"}`}}case"http":{let c=t("address");if(!c)return r("address","TF_HTTP_ADDRESS");let u=lN(c,!0);return u?{key:`http:${u}`}:fd("its http address is not a URL")}case"consul":{let c=t("address"),u=t("path");if(!u)return r("path");if(!c)return r("address","CONSUL_HTTP_ADDR");let l=lN(c,!1);return l?{key:`consul:${l}/${u.replace(/^\/+/,"")}`}:fd("its consul address is not a host")}case"kubernetes":{let c=t("secret_suffix"),u=t("namespace");return c?u?{key:`kubernetes:${u}/${c}`}:r("namespace","KUBE_NAMESPACE"):r("secret_suffix")}case"remote":case"cloud":{let c=t("hostname"),u=t("organization"),l=t("name");return c===null?r("hostname"):u?l===null?fd(`its ${e} workspace name is an expression, not a plain string`):l===void 0?fd(`its ${e} workspaces block names no workspace by name (one picked by tags, prefix or TF_WORKSPACE cannot be read from the code)`):{key:`remote:${(c??"app.terraform.io").toLowerCase()}/${u}/${l}`}:r("organization",e==="cloud"?"TF_CLOUD_ORGANIZATION":void 0)}}let o=t("key"),s=t("prefix"),i=o||s;if(!i)return r(o===null?"key":s===null||e==="gcs"?"prefix":"key");let a=t("bucket");return a===null?r("bucket"):{...a?{bucket:a}:{},key:i}}var ar=e=>!("unsupported"in e)&&(e.backend==="s3"||e.backend==="gcs"||e.backend==="azurerm"),Wf="reads state versions from s3, gcs, azurerm, local and GitLab-managed http backends",ut=e=>typeof e=="string"&&e!==""?e:void 0,fN={pg:"a pg backend keeps one row per workspace, which each write replaces",kubernetes:"a kubernetes backend keeps one Secret per workspace, which each write replaces",consul:"a consul backend keeps one key per workspace, which each write replaces",http:"an http backend keeps what its server holds at the address and names no versions"};function $X(e,t={}){if(e.type in fN&&!(e.type==="http"&&Mf(ut(e.config.address)??t.TF_HTTP_ADDRESS)))return!1}function kX(e){let t=Array.isArray(e.config.workspaces)?e.config.workspaces[0]:e.config.workspaces,n=o=>Gi(e.config[o]??(t&&typeof t=="object"?t[o]:void 0)),r=gd(e.type,n,"");return"key"in r?r.key:void 0}function rw(e,t=process.env){let n=t.TF_DATA_DIR;return n?bX(n)?n:md(e,n):md(e,".terraform")}function _X(e,t=process.env){let n=md(rw(e,t),"terraform.tfstate");if(xR(n))try{let r=JSON.parse(ER(n,"utf-8")),o=ut(r.backend?.type);if(!o)return;let s=r.backend?.config&&typeof r.backend.config=="object"?r.backend.config:{};return{type:o,config:s}}catch{return}}var ow=".terragucci-workspace";function hd(e){let t=md(e,ow);return xR(t)&&ER(t,"utf-8").trim()||void 0}function ls(e,t){let n=hd(t);return n?{...e,TF_WORKSPACE:n}:e}function Fi(e,t){let n=hd(t);if(!n)return;let{TF_WORKSPACE:r,...o}=e;return{init:{...e,TF_WORKSPACE:"default"},select:["workspace","select","-or-create=true",n],selectEnv:o}}function SX(e,t=process.env){if(t.TF_WORKSPACE)return t.TF_WORKSPACE;let n=md(rw(e,t),"environment");if(xR(n)){let r=ER(n,"utf-8").trim();if(r)return r}return"default"}function RX(e,t,n="env:"){return t==="default"?e:`${n}/${t}/${e}`}function Hr(e,t=process.env,n=_X(e,t)){let r=SX(e,t);if(!n||n.type==="local"){let g=n?.config??{};return{backend:"local",path:r==="default"?ut(g.path)??"terraform.tfstate":md(ut(g.workspace_dir)??"terraform.tfstate.d",r,"terraform.tfstate")}}if(n.type==="http"){let g=QC(n.config,t);if(g)return{backend:"http",gitlab:g,location:xn(g.address)}}if(n.type==="gcs")return EX(n.config,r,t);if(n.type==="azurerm")return AX(n.config,r,t);if(n.type!=="s3"){let g=kX(n),m=g?{location:g}:{},h=`terragucci ${Wf}, and this root's backend is ${n.type==="http"?"an http backend whose address is not a GitLab project's state API":n.type}`;return $X(n,t)===!1?{backend:n.type,unsupported:h,...m,versions:{versioning:"off",note:fN[n.type]}}:{backend:n.type,unsupported:h,...m,versions:{versioning:"unknown",note:`a ${n.type} backend can keep versions of the state, which terragucci does not read`}}}let o=n.config,s=ut(o.bucket),i=ut(o.key);if(!s||!i)return{backend:"s3",unsupported:"the s3 backend's bucket or key is not in the configuration init recorded"};let a=o.endpoints&&typeof o.endpoints=="object"?o.endpoints:{},c=ut(a.s3)??ut(o.endpoint)??t.AWS_ENDPOINT_URL_S3??t.AWS_ENDPOINT_URL,u=ut(o.region)??t.AWS_REGION??t.AWS_DEFAULT_REGION??"us-east-1",l=ut(o.access_key),d=ut(o.secret_key),p={...t,AWS_REGION:u,...l&&d?{AWS_ACCESS_KEY_ID:l,AWS_SECRET_ACCESS_KEY:d,AWS_SESSION_TOKEN:ut(o.token)??""}:{}},f;try{f=dd({bucket:s,...c?{endpoint:c}:{}},p)}catch(g){return{backend:"s3",unsupported:`the job has no credentials to read s3://${s}: ${g.message}`}}return{backend:"s3",bucket:s,key:RX(i,r,ut(o.workspace_key_prefix)),region:u,...c?{endpoint:c.replace(/\/+$/,"")}:{},lockfile:o.use_lockfile===!0,dynamodb:ut(o.dynamodb_table)!==void 0,target:f}}var xX=e=>e.replace(/\/+$/,"").replace(/\/storage\/v1$/,"");function EX(e,t,n){let r=ut(e.bucket);if(!r)return{backend:"gcs",unsupported:"the gcs backend's bucket is not in the configuration init recorded"};let o=ut(e.prefix)??"",s=o===""||o.endsWith("/")?o:`${o}/`,i=`${s}${t}.tfstate`,a=xX(ut(e.storage_custom_endpoint)??n.GOOGLE_BACKEND_STORAGE_CUSTOM_ENDPOINT??n.GOOGLE_STORAGE_CUSTOM_ENDPOINT??ew),c=ut(e.impersonate_service_account)??n.GOOGLE_BACKEND_IMPERSONATE_SERVICE_ACCOUNT??n.GOOGLE_IMPERSONATE_SERVICE_ACCOUNT,u={bucket:r,endpoint:a,...c?{impersonate:c}:{}},l=ut(e.access_token)??n.GOOGLE_OAUTH_ACCESS_TOKEN,d=ut(e.credentials)??n.GOOGLE_BACKEND_CREDENTIALS??n.GOOGLE_CREDENTIALS,p;if(l)p={...u,accessToken:l};else if(d)p=d.trim().startsWith("{")?{...u,credentialsJson:d}:{...u,credentialsFile:d};else if(n.GOOGLE_APPLICATION_CREDENTIALS)p={...u,credentialsFile:n.GOOGLE_APPLICATION_CREDENTIALS};else return{backend:"gcs",unsupported:`the job has no credentials to read gs://${r}: give the job oidc.gcp (it sets GOOGLE_APPLICATION_CREDENTIALS), or set GOOGLE_CREDENTIALS or GOOGLE_OAUTH_ACCESS_TOKEN`};return{backend:"gcs",bucket:r,key:i,lock:`${s}${t}.tflock`,target:p}}var TX={public:{storage:"core.windows.net",authority:Qy},china:{storage:"core.chinacloudapi.cn",authority:"https://login.chinacloudapi.cn"},usgovernment:{storage:"core.usgovcloudapi.net",authority:"https://login.microsoftonline.us"}};function AX(e,t,n){let r=ut(e.storage_account_name),o=ut(e.container_name),s=ut(e.key);if(!r||!o||!s)return{backend:"azurerm",unsupported:"the azurerm backend's storage_account_name, container_name or key is not in the configuration init recorded"};let i=t==="default"?s:`${s}env:${t}`,a=TX[ut(e.environment)??n.ARM_ENVIRONMENT??"public"],c=ut(e.metadata_host)??n.ARM_METADATA_HOSTNAME??n.ARM_METADATA_HOST;if(!a&&!c)return{backend:"azurerm",unsupported:`the azurerm backend's environment ${ut(e.environment)??n.ARM_ENVIRONMENT} is not public, china or usgovernment, and it names no metadata_host`};let u=ut(e.access_key)??n.ARM_ACCESS_KEY,l=ut(e.sas_token)??n.ARM_SAS_TOKEN,d=ut(e.tenant_id)??n.ARM_TENANT_ID,p=ut(e.client_id)??n.ARM_CLIENT_ID,f=ut(e.oidc_token_file_path)??n.ARM_OIDC_TOKEN_FILE_PATH,g;if(u)g={accountKey:u};else if(l)g={sas:l};else if(d&&p&&f)g={oidc:{tenantId:d,clientId:p,tokenFile:f,authority:(n.AZURE_AUTHORITY_HOST||a?.authority||Qy).replace(/\/+$/,"")}};else return{backend:"azurerm",unsupported:`the job has no credentials to read the container ${o} of ${r}: give the job oidc.azure (it sets ARM_TENANT_ID, ARM_CLIENT_ID and ARM_OIDC_TOKEN_FILE_PATH), or set ARM_ACCESS_KEY`};return{backend:"azurerm",account:r,container:o,key:i,snapshot:e.snapshot===!0||n.ARM_SNAPSHOT==="true",...c?{metadataHost:c}:{endpoint:`https://${r}.blob.${a.storage}`},auth:g}}function sw(e){if("unsupported"in e)return;if("gitlab"in e)return e.location;if(e.backend==="local")return e.path;let t=e;return t.backend==="gcs"?`gs://${t.bucket}/${t.key}`:t.backend==="azurerm"?`az://${t.account}/${t.container}/${t.key}`:`s3://${t.bucket}/${t.key}`}function iw(e,t){return new ld(e.target,t)}var dN=e=>vX("sha256").update(e).digest("hex");function OX(e,t){let n=iw(e,t),r=`${e.key}.tflock`,o=async()=>{let s=await n.head(e.key);return s.exists?s.versionId?{exists:!0,versionId:s.versionId}:{exists:!0,off:`bucket versioning is off for ${e.bucket}, so it keeps only the latest state`}:{exists:!1}};return{backend:"s3",location:`s3://${e.bucket}/${e.key}`,lockLocation:`s3://${e.bucket}/${r}`,version:o,hasVersion:s=>n.hasVersion(e.key,s),readVersion:s=>n.readVersion(e.key,s),isAt:async s=>(await o()).versionId===s,heldLock:async()=>{let s=await n.get(r);if(s!==void 0)return{id:nw(s),info:s}},lock:s=>n.putIfAbsent(r,s,"application/json"),unlock:()=>n.remove(r),readBeside:s=>n.get(`${e.key}${s}`),writeBeside:async(s,i)=>{await n.put(`${e.key}${s}`,i,"application/json")},adopt:()=>{}}}function nw(e){try{let t=JSON.parse(e);return typeof t.ID=="string"?t.ID:""}catch{return""}}function PX(e,t){let n=new Za(e.target,t),r,o=async()=>{let s=await n.head(e.key);return s.exists?(r??=n.versioning(),await r===!1?{exists:!0,off:`object versioning is off for ${e.bucket}, so it keeps only the latest state`}:{exists:!0,...s.generation?{versionId:s.generation}:{}}):{exists:!1}};return{backend:"gcs",location:`gs://${e.bucket}/${e.key}`,lockLocation:`gs://${e.bucket}/${e.lock}`,version:o,hasVersion:async s=>(await n.head(e.key,s)).exists,readVersion:s=>n.readGeneration(e.key,s),isAt:async s=>(await n.head(e.key)).generation===s,heldLock:async()=>{let s=await n.head(e.lock),i=s.exists?await n.get(e.lock):void 0;if(!(!s.exists||i===void 0))return{id:s.generation??nw(i),info:i}},lock:s=>n.putIfAbsent(e.lock,s,"application/json"),unlock:()=>n.remove(e.lock),readBeside:s=>n.get(`${e.key}${s}`),writeBeside:async(s,i)=>{await n.put(`${e.key}${s}`,i,"application/json")},adopt:()=>{}}}var pN="terraformlockid";async function jX(e,t){if(e.endpoint)return e.endpoint;let n=`https://${e.metadataHost}/metadata/endpoints?api-version=2023-11-01`,r=await t(n,{method:"GET",headers:{accept:"application/json"}}),o=await r.text();if(!r.ok)throw new Y(`the Azure metadata at ${e.metadataHost}: ${r.status}`);let s;try{let i=JSON.parse(o);s=(Array.isArray(i)?i[0]:i)?.suffixes?.storage}catch{}if(typeof s!="string"||s==="")throw new Y(`the Azure metadata at ${e.metadataHost} names no storage suffix`);return`https://${e.account}.blob.${s.replace(/^\.+/,"")}`}async function IX(e,t=fetch){let n=await jX(e,t),r=new ud({account:e.account,container:e.container,endpoint:n,...e.auth},t),o,s=async a=>(await r.head(e.key)).versionId?{versionid:a}:{snapshot:a},i=async(a=!1)=>{let c=await r.head(e.key);return c.exists?c.versionId?{exists:!0,versionId:c.versionId}:e.snapshot?a?{exists:!0,versionId:await r.snapshot(e.key)}:{exists:!0}:{exists:!0,off:`blob versioning is off for ${e.account}, and the backend takes no snapshots (snapshot = true), so it keeps only the latest state`}:{exists:!1}};return{backend:"azurerm",location:`az://${e.account}/${e.container}/${e.key}`,lockLocation:`az://${e.account}/${e.container}/${e.key}`,version:i,hasVersion:async a=>(await r.head(e.key,await s(a))).exists,readVersion:async a=>r.readAt(e.key,await s(a)),isAt:async a=>{let c=await r.head(e.key);if(c.versionId)return c.versionId===a;let[u,l]=await Promise.all([r.readAt(e.key,{snapshot:a}),r.get(e.key)]);return u!==void 0&&l!==void 0&&dN(u)===dN(l)},heldLock:async()=>{let a=await r.head(e.key);if(!a.exists||a.leaseState!=="leased")return;let c=a.metadata[pN],u=c?Buffer.from(c,"base64").toString("utf-8"):"";return{id:nw(u),info:u}},lock:async a=>{let c=nw(a);return(await r.head(e.key)).exists||await r.putBlob(e.key,"","application/json",{ifNoneMatch:!0}),await r.lease(e.key,"acquire",c)?(o=c,await r.setMetadata(e.key,{[pN]:Buffer.from(a).toString("base64")},c),!0):!1},unlock:async()=>{if(!o)return;let a=o;await r.setMetadata(e.key,{},a),await r.lease(e.key,"release",a),o=void 0},write:async a=>{if(!o)throw new Y(`${e.key} is written under its lease, and none is held`);e.snapshot&&(await r.head(e.key)).exists&&await r.snapshot(e.key);let c=(await r.head(e.key)).metadata;await r.putBlob(e.key,a,"application/json",{leaseId:o,metadata:c})},readBeside:a=>r.get(`${e.key}${a}`),writeBeside:async(a,c)=>{await r.put(`${e.key}${a}`,c,"application/json")},adopt:a=>{o=a.id}}}async function Ws(e,t){return e.backend==="s3"?OX(e,t):e.backend==="gcs"?PX(e,t):IX(e,t)}async function Hf(e,t=process.env,n,r){let o=r?Hr(e,t,r):Hr(e,t);if("unsupported"in o)return{backend:o.backend,...o.location?{location:o.location}:{},versioning:o.versions?.versioning??"unknown",note:o.versions?.note??o.unsupported};if(o.backend==="local")return{backend:"local",location:o.path,versioning:"off",note:"a local backend keeps only the latest state"};if("gitlab"in o)try{let i=await Yy(o.gitlab,n??globalThis.fetch);return i?{backend:"http",location:o.location,version_id:String(i.serial),versioning:"on"}:{backend:"http",location:o.location,versioning:"unknown",note:"GitLab holds no state at this address"}}catch(i){return{backend:"http",location:o.location,versioning:"unknown",note:i.message}}if(!ar(o))return{backend:o.backend,versioning:"unknown",note:"terragucci reads no versions of this state"};let s=sw(o);try{let i=await(await Ws(o,n)).version(!0);return i.exists?i.versionId?{backend:o.backend,location:s,version_id:i.versionId,versioning:"on"}:i.off?{backend:o.backend,location:s,versioning:"off",note:i.off}:{backend:o.backend,location:s,versioning:"unknown",note:"the store gave no version id"}:{backend:o.backend,location:s,versioning:"unknown",note:`the state object is not in the ${o.backend==="azurerm"?"container":"bucket"}`}}catch(i){return{backend:o.backend,location:s,versioning:"unknown",note:i.message}}}import{execFile as CX}from"node:child_process";var NX=["TG_TOKEN","TG_MERGE_TOKEN","GITHUB_TOKEN","GH_TOKEN","GITLAB_TOKEN","CI_JOB_TOKEN","FORGEJO_TOKEN","GITEA_TOKEN","ACTIONS_RUNTIME_TOKEN"];function Ve(e){let t=new Set([e.TG_TOKEN,e.TG_MERGE_TOKEN].filter(r=>typeof r=="string"&&r.length>0)),n={};for(let[r,o]of Object.entries(e))NX.includes(r)||o!==void 0&&t.has(o)&&!r.startsWith("TF_")||(n[r]=o);return n}var Nn=(e,t,n)=>new Promise(r=>{CX(e,[...t],{cwd:n.cwd,env:Ve({...process.env,...n.env}),maxBuffer:256*1024*1024},(o,s,i)=>{let a=o?typeof o.code=="number"?o.code:null:0;r({code:a,stdout:String(s),stderr:o&&a===null?`${String(i)}${o.message}`:String(i)})})});import{existsSync as FR,readdirSync as FN,readFileSync as Qf,statSync as A7}from"node:fs";import{dirname as O7,join as eg,relative as fw,resolve as GR}from"node:path";import{execFile as qX}from"node:child_process";import{existsSync as yd,mkdirSync as EN,readFileSync as uw,realpathSync as JX,rmSync as TN,writeFileSync as KX}from"node:fs";import{dirname as VX,join as ds,resolve as Kf}from"node:path";function TR(e){let t=e,r=(Array.isArray(t?.resource_changes)?t.resource_changes:[]).map(s=>{let i=s??{},a=i.change??{};return{address:i.address,mode:i.mode,type:i.type,name:i.name,index:i.index,provider_name:i.provider_name,deposed:i.deposed,actions:a.actions,before:a.before,after:a.after,after_unknown:a.after_unknown,replace_paths:a.replace_paths,importing:a.importing}});r.sort((s,i)=>`${String(s.address)}\0${String(s.deposed??"")}`.localeCompare(`${String(i.address)}\0${String(i.deposed??"")}`));let o=Array.isArray(t?.action_invocations)?[...t.action_invocations].sort((s,i)=>JSON.stringify(s).localeCompare(JSON.stringify(i))):[];return{resourceChanges:r,outputChanges:t?.output_changes??{},...o.length>0?{actionInvocations:o}:{}}}function gN(e){return Rn("terraform-plan",TR(e))}var Tr=e=>e!==null&&typeof e=="object"&&!Array.isArray(e);function qf(e){return e===!0?!0:Array.isArray(e)?e.some(qf):Tr(e)?Object.values(e).some(qf):!1}function AR(e,t){if(e===t)return!0;if(Array.isArray(e)&&Array.isArray(t))return e.length===t.length&&e.every((n,r)=>AR(n,t[r]));if(Tr(e)&&Tr(t)){let n=Object.keys(e);return n.length===Object.keys(t).length&&n.every(r=>r in t&&AR(e[r],t[r]))}return!1}function nn(e){let t=(e??"terraform").split(/[\\/]/).pop()??"";return t.startsWith("choudoufu")?"choudoufu":t.startsWith("tofu")?"tofu":"terraform"}function DX(e){let t=Array.isArray(e)?e.map(String):[];if(t.length===2&&t.includes("delete")&&t.includes("create"))return"replace";if(t.length===1)switch(t[0]){case"create":case"update":case"delete":case"read":case"no-op":case"forget":return t[0]}throw new Error(`a plan entry has actions ${JSON.stringify(e)}, which the change set has no action for`)}function MX(e,t){switch(t){case"update":return"in-place";case"delete":return"destroy";case"replace":return e[0]==="create"?"replace":"destroy";default:return}}function LX(e){let n=/^((?:module\.[^.[\]]+(?:\[(?:"(?:[^"\\]|\\.)*"|\d+)\])?\.)*)(?:data\.)?([^.[\]]+)\.([^.[\]]+)(?:\[("(?:[^"\\]|\\.)*"|\d+)\])?$/.exec(e);if(!n)return{type:"unknown",name:e};let r=n[1]?n[1].slice(0,-1):void 0,o=n[4]===void 0?void 0:n[4].startsWith('"')?JSON.parse(n[4]):Number(n[4]);return{...r?{module:r}:{},type:n[2],name:n[3],...o!==void 0?{index:o}:{}}}function GX(e,t){if(t==="delete"||t==="no-op"||t==="forget"||t==="read")return[];let n=Tr(e.before)?e.before:{},r=Tr(e.after)?e.after:{},o=Tr(e.after_unknown)?e.after_unknown:{},s=Tr(e.before_sensitive)?e.before_sensitive:{},i=Tr(e.after_sensitive)?e.after_sensitive:{},a=new Set((Array.isArray(e.replace_paths)?e.replace_paths:[]).map(l=>Array.isArray(l)?l[0]:void 0).filter(l=>typeof l=="string")),c=[...new Set([...Object.keys(n),...Object.keys(r),...Object.keys(o)])].sort(),u=[];for(let l of c){let d=qf(o[l]),p=t!=="create"&&n[l]!==void 0&&n[l]!==null,f=r[l]!==void 0&&r[l]!==null;if(t==="create"?!f&&!d:!d&&AR(n[l]??null,r[l]??null))continue;let g=qf(s[l])||qf(i[l]);u.push({path:l,...g?{sensitive:!0}:{...p?{before:n[l]}:{},...f&&!d?{after:r[l]}:{}},...d?{unknown:!0}:{},...a.has(l)?{forcesReplacement:!0}:{}})}return u}function FX(e){for(let t of[e.after,e.before])if(Tr(t)&&typeof t.region=="string"&&t.region!=="")return t.region}function fo(e){let t=Tr(e.plan)?e.plan:{},n=e.planner??"terraform",o=(Array.isArray(t.resource_changes)?t.resource_changes:[]).map(a=>{let c=Tr(a)?a:{},u=Tr(c.change)?c.change:{},l=String(c.address??""),d=LX(l),p=DX(u.actions),f=MX(u.actions,p),g=FX(u),m=c.index!==void 0&&c.index!==null?c.index:d.index,h=typeof c.module_address=="string"?c.module_address:d.module;return{member:e.member,lexicon:"terraform",planner:n,address:l,type:typeof c.type=="string"?c.type:d.type,name:typeof c.name=="string"?c.name:d.name,...m!==void 0?{index:m}:{},...h?{module:h}:{},...typeof c.deposed=="string"?{deposed:c.deposed}:{},action:p,...Tr(u.importing)?{importing:!0}:{},...f?{disruption:f}:{},...g?{region:g}:{},...e.scope?{scope:e.scope}:{},attributes:GX(u,p)}}),s=(Array.isArray(t.action_invocations)?t.action_invocations:[]).map(zX),i=t.errored===!0;return{member:{member:e.member,lexicon:"terraform",planner:n,...e.scope?{scope:e.scope}:{},status:i?"failed":"planned",...i?{error:`${n} marked the plan errored`}:{},planDigest:gN(t),...e.nativeDigest?{nativeDigest:e.nativeDigest}:{},holes:[]},entries:o,...s.length>0?{sideEffects:s}:{}}}function zX(e){let t=Tr(e)?e:{},n=typeof t.type=="string"?t.type:"unknown",r=Tr(t.lifecycle_action_trigger)?t.lifecycle_action_trigger:{};return{address:typeof t.address=="string"?t.address:n,type:n,...typeof r.triggering_resource_address=="string"?{trigger:r.triggering_resource_address}:{},...typeof r.action_trigger_event=="string"?{event:r.action_trigger_event}:{}}}var Xa=e=>e!==null&&typeof e=="object"&&!Array.isArray(e);function OR(e){let t=e.replace(/\\/g,"/"),n=[];for(let o of t.split("/"))o===""||o==="."||(o===".."&&n.length>0&&n[n.length-1]!==".."?n.pop():n.push(o));let r=n.join("/");return t.startsWith("/")?`/${r}`:r||"."}var UX=e=>e.startsWith("/")||/^[A-Za-z]:[\\/]/.test(e);function PR(e,t,n){let r=t.replace(/\\/g,"/").replace(/(^|\/)terragrunt\.hcl$/,"");if(!UX(r))return OR(`${e}/${r}`);let o=OR(r);for(let s of typeof n=="string"?[n]:n){let i=OR(s);if(o===i)return".";if(o.startsWith(`${i}/`))return o.slice(i.length+1)}return o}function mN(e,t,n){let r=typeof t=="string"?JSON.parse(t):t;if(!Xa(r))throw new Error("terragrunt render --json did not print an object");let o=Xa(r.dependency)?r.dependency:{},s=[];for(let[i,a]of Object.entries(o)){if(!Xa(a)||typeof a.config_path!="string")continue;let c=Array.isArray(a.mock_outputs_allowed_terraform_commands)?a.mock_outputs_allowed_terraform_commands.filter(l=>typeof l=="string"):void 0,u=typeof a.mock_outputs_merge_strategy_with_state=="string"?a.mock_outputs_merge_strategy_with_state:a.mock_outputs_merge_with_state===!0?"shallow":void 0;s.push({name:typeof a.name=="string"&&a.name?a.name:i,upstream:PR(e,a.config_path,n),...Xa(a.mock_outputs)?{mockOutputs:a.mock_outputs}:{},...c?{mockAllowed:c}:{},...u&&u!=="no_merge"?{mergeStrategy:u}:{},...a.skip===!0||a.skip_outputs===!0?{skipOutputs:!0}:{},...a.enabled===!1?{disabled:!0}:{}})}return s.sort((i,a)=>i.name<a.name?-1:i.name>a.name?1:0)}function hN(e){let t=e.trim(),n=t.indexOf("{");if(n<0)return;let r;try{r=JSON.parse(t.slice(n))}catch{return}if(Xa(r))return Object.fromEntries(Object.entries(r).map(([o,s])=>[o,Xa(s)&&"value"in s?s.value:s]))}function yN(e){return["run","--working-dir",e,"--non-interactive","--no-color","--","output","-json"]}function aw(e){return["render","--json","--non-interactive","--no-color","--working-dir",e]}function wN(e,t,n,r=""){let o=[];for(let[s,i]of Object.entries(e)){let a=r?`${r}.${s}`:s;s in t?n&&Xa(i)&&Xa(t[s])&&o.push(...wN(i,t[s],n,a)):o.push(a)}return o.sort()}var BX=e=>!e.mockAllowed||e.mockAllowed.length===0||e.mockAllowed.includes("plan");function bN(e){let t=[];for(let[n,r]of e.dependencies)for(let o of r){let s={unit:n,dependency:o.name,upstream:o.upstream};if(o.disabled||o.skipOutputs){o.mockOutputs&&t.push({...s,reason:o.disabled?"disabled":"skip-outputs"});continue}let i=e.outputs.get(o.upstream)??{};if(Object.keys(i).length===0){t.push({...s,reason:"no-outputs"});continue}if(o.mockOutputs&&o.mergeStrategy&&BX(o)){let a=wN(o.mockOutputs,i,o.mergeStrategy!=="shallow");a.length>0&&t.push({...s,reason:"partial",keys:a})}}return t.sort((n,r)=>n.unit<r.unit?-1:n.unit>r.unit?1:n.dependency<r.dependency?-1:n.dependency>r.dependency?1:0)}function vN(e){let t=new Set;for(let n of e.values())for(let r of n)!r.disabled&&!r.skipOutputs&&t.add(r.upstream);return[...t].sort()}function WX(e){let t=`${e.unit} (dependency "${e.dependency}" on ${e.upstream})`;switch(e.reason){case"no-outputs":return`${t}: ${e.upstream} has no outputs yet; apply ${e.upstream} first`;case"partial":return`${t}: ${e.upstream}'s outputs lack ${e.keys.join(", ")}, which the merge strategy fills from mock_outputs; apply ${e.upstream} with those outputs first, or drop the merge strategy`;case"skip-outputs":return`${t}: skip_outputs = true with mock_outputs set always reads the mock; read the real outputs or drop mock_outputs`;case"disabled":return`${t}: enabled = false with mock_outputs set always reads the mock; enable the dependency or drop mock_outputs`}}function $N(e){let t=[...new Set(e.filter(r=>r.reason==="no-outputs"||r.reason==="partial").map(r=>r.upstream))].sort();return["this wave was not planned and is not gated: its plans would read mock_outputs, so nobody could approve them as real. "+(t.length>0?`Apply ${t.join(", ")} first, then plan the wave again.`:"Fix the dependency blocks below, then plan the wave again."),...e.map(r=>` ${WX(r)}`)].join(`
|
|
49
|
+
`)}var HX=/Config (\S+?) is a dependency of (\S+?) that has no outputs, but mock outputs provided/g;function kN(e,t){let n=new Set,r=[];for(let o of e.matchAll(HX)){let s=PR(".",o[2],t),i=PR(".",o[1],t),a=`${s}\0${i}`;n.has(a)||(n.add(a),r.push({unit:s,upstream:i}))}return r.sort((o,s)=>o.unit<s.unit?-1:o.unit>s.unit?1:o.upstream<s.upstream?-1:1)}function _N(e,t){let n=new Map;for(let r of t)n.set(r.unit,[...n.get(r.unit)??[],r.upstream]);return e.map(r=>{let o=n.get(r.member.member);return o?{member:{...r.member,status:"failed",error:`planned on mock_outputs: ${o.join(", ")} had no outputs when Terragrunt planned it; apply ${o.join(", ")} first`,planDigest:null},entries:[]}:r})}function SN(e){return e.map(t=>({...t,member:{...t.member,provisional:!0}}))}var jR="PROVISIONAL";function IR(e){if(e.units.length===0)throw new Error("a Terragrunt wave needs at least one unit; with no path filter run --all would run every unit");if(e.parallelism!==void 0&&(!Number.isInteger(e.parallelism)||e.parallelism<1))throw new Error(`parallelism must be a whole number of 1 or more, got ${e.parallelism}`);return["run","--all","--non-interactive","--no-color","--no-filters-file",...[...new Set(e.units)].sort().flatMap(r=>["--filter",eC(r)]),...e.destroy?["--filter-allow-destroy"]:[],"--out-dir",e.outDir,...e.command==="plan"&&e.jsonOutDir?["--json-out-dir",e.jsonOutDir]:[],"--report-file",e.reportFile,"--report-format","json",...e.parallelism!==void 0?["--parallelism",String(e.parallelism)]:[],"--",e.command,...e.command==="plan"&&e.destroy?["-destroy"]:[],...e.extra??[]]}function zi(e){return e?{TG_TF_PATH:e}:{}}function Jf(e){let t=typeof e=="string"?JSON.parse(e.trim()===""?"[]":e):e,n=Array.isArray(t)?t:[],r=new Map;for(let o of n){if(o===null||typeof o!="object")continue;let s=o;if(typeof s.Name!="string"||typeof s.Result!="string")continue;let i=s.Name.replace(/\\/g,"/").replace(/^\.\//,"");r.set(i,{name:i,result:s.Result,...typeof s.Reason=="string"&&s.Reason?{reason:s.Reason}:{},...typeof s.Cause=="string"&&s.Cause?{cause:s.Cause.trim()}:{},...typeof s.Cmd=="string"?{cmd:s.Cmd}:{},...typeof s.Started=="string"?{started:s.Started}:{},...typeof s.Ended=="string"?{ended:s.Ended}:{}})}return r}function cw(e,t){return[...new Set(e)].sort().map(n=>{let r=t.get(n);if(!r)return{unit:n,status:"failed",result:"not run",error:"not in Terragrunt's run report, so it did not run"};if(r.result==="succeeded")return{unit:n,status:"succeeded",result:r.result};let o=[r.reason,r.cause].filter(Boolean).join(": ");return{unit:n,status:"failed",result:r.result,error:`${r.result}${o?` (${o})`:""}`}})}function RN(e){let t=e.scopeFor??jy;return cw(e.units,e.report).map(n=>{let r=t(n.unit),o=n.status==="succeeded"?e.planFor(n.unit):void 0;return n.status==="succeeded"&&o!==void 0?fo({member:n.unit,plan:o,planner:e.planner,...r?{scope:r}:{}}):{member:{member:n.unit,lexicon:"terraform",planner:e.planner,...r?{scope:r}:{},status:"failed",error:n.error??"Terragrunt reported the unit succeeded but wrote no plan JSON for it",planDigest:null,holes:[]},entries:[]}})}var Eu=(e,t,n)=>new Promise(r=>{qX(e,[...t],{cwd:n.cwd,env:{...process.env,...n.env},maxBuffer:256*1024*1024},(o,s,i)=>{let a=o?typeof o.code=="number"?o.code:null:0;r({code:a,stdout:String(s),stderr:o&&a===null?`${String(i)}${o.message}`:String(i)})})}),cr=class extends Error{constructor(t){super(t),this.name="TerragruntError"}};async function lw(e){let t=e.exec??Eu,n=e.terragrunt??"terragrunt",r=await t(n,["--version"],{cwd:e.dir,env:{}});if(r.code!==0)throw new cr(`\`${n} --version\` failed (exit ${r.code}): ${(r.stderr||r.stdout).trim()}`);let o=QI(r.stdout);if(o)throw new cr(o);return r.stdout.trim()}async function dw(e){let t=e.exec??Eu,n=e.terragrunt??"terragrunt",r=tC(e),o=await t(n,r,{cwd:e.dir,env:zi(e.binary)});if(o.code!==0)throw new cr(`\`${n} ${r.join(" ")}\` failed (exit ${o.code}): ${o.stderr.trim()}`);let s;try{s=Py(o.stdout)}catch(i){throw new cr(`could not read \`terragrunt find\` output: ${i instanceof Error?i.message:String(i)}`)}return{units:s,warnings:e.binary?YX(e.dir,s,e.binary):[]}}var ZX=/^\s*terraform_binary\s*=\s*(.+?)\s*$/m;function YX(e,t,n){let r=[];for(let o of t){let s=[ds(o.path,"terragrunt.hcl"),...Object.values(o.include??{})];for(let i of s){let a;try{a=uw(Kf(e,i),"utf8")}catch{continue}let c=ZX.exec(a);if(!c)continue;let u=/^"([^"$]*)"$/.exec(c[1]);if(u&&u[1]===n)break;r.push(`${o.path}: ${i} sets terraform_binary = ${c[1]}, but the project's binary is ${n}; TG_TF_PATH=${n} overrides it, so the unit runs ${n}`);break}}return r}function XX(e){return nn(e??"tofu")}var Hs=class extends cr{constructor(n){super($N(n));this.reads=n;this.name="TerragruntMockRefusal"}reads};async function xN(e,t,n){let r=new Array(e.length),o=0,s=async()=>{for(;o<e.length;){let i=o++;r[i]=await n(e[i])}};return await Promise.all(Array.from({length:Math.max(1,Math.min(t,e.length))},s)),r}function AN(e){let t=Kf(e);try{let n=JX(t);return n===t?[t]:[t,n]}catch{return[t]}}async function QX(e){let t=e.exec??Eu,n=e.terragrunt??"terragrunt",r=zi(e.binary),o=e.parallelism??8,s=AN(e.dir),i=[...new Set(e.units)].sort().filter(d=>yd(ds(Kf(e.dir),d,"terragrunt.hcl"))),a=await xN(i,o,async d=>{let p=aw(d),f=await t(n,p,{cwd:e.dir,env:r});if(f.code!==0)throw new cr(`\`${n} ${p.join(" ")}\` failed (exit ${f.code}), so the wave's dependencies are unknown: ${f.stderr.trim()}`);try{return[d,mN(d,f.stdout,s)]}catch(g){throw new cr(`could not read \`terragrunt render --json\` for ${d}: ${g instanceof Error?g.message:String(g)}`)}}),c=new Map(a),u=vN(c),l=await xN(u,o,async d=>{let p=yN(d),f=await t(n,p,{cwd:e.dir,env:r}),g=f.code===0?hN(f.stdout):void 0;if(!g)throw new cr(`could not read the outputs of ${d}, which the wave reads (\`${n} ${p.join(" ")}\` exit ${f.code}): ${(f.stderr||f.stdout).trim().slice(-2e3)}`);return[d,g]});return bN({dependencies:c,outputs:new Map(l)})}async function pw(e){let t=e.exec??Eu,n=e.terragrunt??"terragrunt",r=Kf(e.dir,e.workDir),o=ds(r,"plans"),s=ds(r,"json"),i=ds(r,"plan-report.json"),a=ds(r,jR);for(let m of[o,s,i,a])TN(m,{recursive:!0,force:!0});if(!e.provisional){let m=await QX({...e,exec:t});if(m.length>0)throw new Hs(m)}EN(r,{recursive:!0}),e.provisional&&KX(a,`planned before its upstream applied; never applied
|
|
50
|
+
`);let c=IR({units:e.units,command:"plan",outDir:o,jsonOutDir:s,reportFile:i,...e.destroy?{destroy:!0}:{},...e.parallelism!==void 0?{parallelism:e.parallelism}:{}}),u=await t(n,c,{cwd:e.dir,env:zi(e.binary)}),l=yd(i)?Jf(uw(i,"utf8")):new Map,d=m=>{let h=ds(s,m,"tfplan.json");return yd(h)?JSON.parse(uw(h,"utf8")):void 0},p=RN({units:e.units,report:l,planFor:d,planner:XX(e.binary)}),f=new Set(e.units),g=kN(`${u.stderr}
|
|
51
|
+
${u.stdout}`,AN(e.dir)).filter(m=>f.has(m.unit));return{parts:e.provisional?SN(p):_N(p,g),provisional:e.provisional===!0,results:cw(e.units,l),code:u.code,log:u.stderr}}async function ON(e){let t=e.exec??Eu,n=e.terragrunt??"terragrunt",r=Kf(e.dir,e.workDir),o=ds(r,"plans");if(e.provisional||yd(ds(r,jR)))throw new cr(`the plans under ${r} are provisional: planned before their upstream applied, possibly on mock_outputs. They are never applied; plan the wave again once its upstream applied`);let s=[...new Set(e.units)].sort().filter(l=>!yd(ds(o,l,"tfplan.tfplan")));if(s.length>0)throw new cr(`no saved plan for ${s.join(", ")} under ${o}; plan the wave before applying it, so apply runs the plan that was reviewed`);let i=ds(r,"apply-report.json");TN(i,{force:!0}),EN(VX(i),{recursive:!0});let a=IR({units:e.units,command:"apply",outDir:o,reportFile:i,...e.destroy?{destroy:!0}:{},...e.parallelism!==void 0?{parallelism:e.parallelism}:{}}),c=await t(n,a,{cwd:e.dir,env:zi(e.binary)}),u=yd(i)?Jf(uw(i,"utf8")):new Map;return{results:cw(e.units,u),code:c.code,log:c.stderr}}import{readFileSync as R7}from"node:fs";import{join as x7}from"node:path";import{existsSync as d7,readdirSync as p7,readFileSync as f7}from"node:fs";import{join as DR,posix as Zf,relative as g7,resolve as m7,sep as h7}from"node:path";import{existsSync as e7,readdirSync as t7,readFileSync as n7,statSync as r7}from"node:fs";import{join as o7,posix as jN,relative as s7,resolve as CR,sep as i7}from"node:path";var a7=/\bsource\s*=\s*"(\.{1,2}\/[^"]*)"/g,c7=e=>e.split(i7).join("/");function PN(e,t){let n=c7(s7(e,t));return n===""?".":jN.normalize(n).replace(/\/+$/,"")}function u7(e){let t;try{t=t7(e)}catch{return[]}let n=[];for(let r of t){if(!r.endsWith(".tf")&&!r.endsWith(".tofu"))continue;let o;try{o=n7(o7(e,r),"utf8")}catch{continue}for(let s of o.matchAll(a7)){let i=CR(e,s[1].replace("//","/"));e7(i)&&r7(i).isDirectory()&&n.push(i)}}return n}function NR(e,t){let n=CR(e,t.dir),r=new Set,o=[n];for(;o.length>0;){let i=o.shift();r.has(i)||(r.add(i),o.push(...u7(i)))}let s=[...r].map(i=>PN(e,i));for(let i of t.varFiles??[])s.push(PN(e,CR(n,i)));return[...new Set(s)].sort()}function l7(e,t){return e==="."?!t.startsWith("../"):t===e||t.startsWith(`${e}/`)}function Vf(e,t,n){let r=n.map(s=>jN.normalize(s)),o=[];for(let[s,i]of Object.entries(t)){let a=NR(e,i);r.some(c=>a.some(u=>l7(u,c)))&&o.push(s)}return o.sort()}var IN=["terragrunt","removed","module-file","module-call","stack-template"],y7=/\.(tf|tofu)(\.json)?$/,NN=e=>e.split(h7).join("/"),Yf=e=>Zf.normalize(NN(e)).replace(/^\.\//,"").replace(/\/+$/,""),Tu=(e,t)=>e==="."||t===e||t.startsWith(`${e}/`);function w7(e){let t=[...hu,...e.exclude??[]];return["find","--json","--no-color","--no-filters-file","--filter",`[${e.base}...${e.head??"HEAD"}]`,...t.flatMap(n=>["--filter",hf(n)])]}function b7(e){return["diff","--name-only","--no-renames","--relative",`${e.base}...${e.head??"HEAD"}`]}function v7(e){let t=new Set;for(let n of e.reading??[])!y7.test(n)||Tu(e.path,n)||n.includes(".terragrunt-cache/")||t.add(Zf.dirname(n));return[...t].sort()}function MR(e,t,n){let r=Zf.dirname(Yf(t)),o=[],s=/\b(unit|stack)\s+"([^"]+)"\s*\{/g;for(let i=s.exec(n);i;i=s.exec(n)){let a=$7(n,s.lastIndex),c=CN(a,"source"),u=CN(a,"path");if(u===void 0)continue;let d=/^\s*no_dot_terragrunt_stack\s*=\s*true\b/m.test(a)?r:Zf.join(r,".terragrunt-stack"),p=Yf(Zf.join(d,u)),f;if(c!==void 0){let g=c.replace(/^\$\{get_terragrunt_dir\(\)\}\//,"./");/^\.{1,2}\//.test(g)&&(f=Yf(NN(g7(e,m7(e,r,g.replace("//","/"))))))}o.push({type:i[1],name:i[2],generated:p,...f!==void 0?{template:f}:{}})}return o}function $7(e,t){let n=1;for(let r=t;r<e.length;r++){let o=e[r];if(o==='"')for(r++;r<e.length&&e[r]!=='"';r++)e[r]==="\\"&&r++;else if(o==="#"||o==="/"&&e[r+1]==="/")for(;r<e.length&&e[r]!==`
|
|
52
52
|
`;)r++;else if(o==="{")n++;else if(o==="}"&&--n===0)return e.slice(t,r)}return e.slice(t)}function CN(e,t){let n=new RegExp(`^\\s*${t}\\s*=\\s*"([^"]*)"`),r=0;for(let o of e.split(`
|
|
53
|
-
`)){if(r===0){let i=n.exec(o);if(i)return i[1]}let s=o.replace(/"(?:[^"\\]|\\.)*"/g,'""');r+=(s.match(/[{[]/g)??[]).length-(s.match(/[}\]]/g)??[]).length}}var
|
|
54
|
-
`).map(l=>l.trim()).filter(l=>l!=="")})}var wd=".terragrunt-stack",
|
|
53
|
+
`)){if(r===0){let i=n.exec(o);if(i)return i[1]}let s=o.replace(/"(?:[^"\\]|\\.)*"/g,'""');r+=(s.match(/[{[]/g)??[]).length-(s.match(/[}\]]/g)??[]).length}}var k7=new Set([".git",".terragrunt-cache","node_modules",".terraform"]);function LR(e){let t=[],n=r=>{let o;try{o=p7(DR(e,r),{withFileTypes:!0})}catch{return}for(let s of o){let i=r?`${r}/${s.name}`:s.name;s.isDirectory()&&!k7.has(s.name)?n(i):s.isFile()&&s.name==="terragrunt.stack.hcl"&&t.push(i)}};return n(""),t.sort()}function _7(e){let t=e.lastIndexOf("/.terragrunt-stack/");return t<0?void 0:`${e.slice(0,t)}/terragrunt.stack.hcl`}function S7(e){let t=[...new Set(e.changedFiles.map(Yf))].sort(),n=new Map(e.units.map(a=>[a.path,a])),r=new Map,o=[],s=(a,c)=>{let u=r.get(a)??[];u.push(c),r.set(a,u)};for(let a of new Set(e.terragruntSelected.map(Yf))){let c=n.get(a);if(!c){d7(DR(e.dir,a,"terragrunt.hcl"))?o.push(`${a}: Terragrunt's git range selected it, but discovery left it out, so it is not affected here`):s(a,{kind:"removed",files:t.filter(l=>Tu(a,l))});continue}let u=new Set([...c.reading??[],...Object.values(c.include??{}),_7(c.path)].filter(l=>!!l));s(a,{kind:"terragrunt",files:t.filter(l=>Tu(c.path,l)||u.has(l))})}for(let a of e.units){let c=new Set(a.reading??[]),u=v7(a),l=new Map;for(let f of u)for(let g of NR(e.dir,{dir:f}))!u.includes(g)&&!l.has(g)&&l.set(g,f);let d=new Map,p=new Map;for(let f of t){if(c.has(f))continue;let g=[...l.keys()].filter(h=>Tu(h,f)).sort((h,y)=>y.length-h.length)[0];if(g!==void 0){p.set(g,[...p.get(g)??[],f]);continue}let m=u.filter(h=>Tu(h,f)).sort((h,y)=>y.length-h.length)[0];m!==void 0&&d.set(m,[...d.get(m)??[],f])}for(let[f,g]of d)s(a.path,{kind:"module-file",files:g,via:f});for(let[f,g]of p)s(a.path,{kind:"module-call",files:g,via:f})}for(let a of LR(e.dir)){let c;try{c=f7(DR(e.dir,a),"utf8")}catch{continue}for(let u of MR(e.dir,a,c)){if(u.template===void 0)continue;let l=t.filter(p=>Tu(u.template,p));if(l.length===0)continue;let d=u.type==="unit"?n.has(u.generated)?[u.generated]:[]:e.units.filter(p=>Tu(u.generated,p.path)).map(p=>p.path);d.length===0&&o.push(`${u.generated}: generated by ${u.type} "${u.name}" in ${a} from the changed template ${u.template}, but discovery did not list it; run \`terragrunt stack generate\` before discovery`);for(let p of d)s(p,{kind:"stack-template",files:l,via:u.template})}}return{units:[...r.entries()].map(([a,c])=>({path:a,reasons:c.sort((u,l)=>IN.indexOf(u.kind)-IN.indexOf(l.kind)||(u.via??"").localeCompare(l.via??""))})).sort((a,c)=>a.path<c.path?-1:a.path>c.path?1:0),notes:[...new Set(o)].sort()}}function DN(e){let t=e.files.join(", ");switch(e.kind){case"terragrunt":return t?`Terragrunt's git range selected it (${t})`:"Terragrunt's git range selected it";case"removed":return"removed in the range; Terragrunt plans its destroy";case"module-file":return`${t} changed under its module ${e.via}, which Terragrunt does not track`;case"module-call":return`${t} changed under ${e.via}, a module its module calls`;case"stack-template":return`${t} changed under ${e.via}, the stack template it is generated from`}}async function MN(e){let t=e.exec??Eu,n=e.terragrunt??"terragrunt",r=e.git??"git",o=b7(e),s=await t(r,o,{cwd:e.dir,env:{}});if(s.code!==0)throw new cr(`\`${r} ${o.join(" ")}\` failed (exit ${s.code}): ${s.stderr.trim()}`);let i=w7(e),a=await t(n,i,{cwd:e.dir,env:zi(e.binary)});if(a.code!==0)throw new cr(`\`${n} ${i.join(" ")}\` failed (exit ${a.code}): ${a.stderr.trim()}`);let c;try{c=Py(a.stdout).map(l=>l.path)}catch(l){throw new cr(`could not read \`terragrunt find\` output: ${l instanceof Error?l.message:String(l)}`)}let u=e.units??(await dw(e)).units;return S7({dir:e.dir,units:u,terragruntSelected:c,changedFiles:s.stdout.split(`
|
|
54
|
+
`).map(l=>l.trim()).filter(l=>l!=="")})}var wd=".terragrunt-stack",E7=e=>e.split("\\").join("/");function T7(e){return LR(e).filter(t=>!t.split("/").includes(wd)).map(t=>t.includes("/")?t.slice(0,t.lastIndexOf("/")):".").sort()}function Qa(e){let t=E7(e);if(t.startsWith(`${wd}/`))return".";let n=t.lastIndexOf(`/${wd}/`);return n<0?void 0:t.slice(0,n)}function bd(e){return e==="."?"terragrunt.stack.hcl":`${e}/terragrunt.stack.hcl`}function Ui(e){let t=Qa(e);return t===void 0?{stack:jy(e)}:{stack:t,stack_file:bd(t)}}async function Bi(e,t={}){let n=T7(e);if(n.length===0)return[];let r=t.terragrunt??process.env.TERRAGUCCI_TERRAGRUNT??"terragrunt",o=t.exec??Nn,s={dir:e,terragrunt:r,...t.binary?{binary:t.binary}:{},exec:o};try{await lw(s)}catch(a){throw new S(`${n.map(bd).join(", ")}: an explicit stack's units are generated by terragrunt stack generate, and Terragrunt did not run (${a.message.split(`
|
|
55
55
|
`)[0]}); put Terragrunt 1.1 or later on the path, or set TERRAGUCCI_TERRAGRUNT`)}let i=await o(r,["stack","generate","--non-interactive","--no-color"],{cwd:e,env:{TG_NON_INTERACTIVE:"true",...t.binary?{TG_TF_PATH:t.binary}:{}}});if(i.code!==0)throw new S(`terragrunt stack generate failed (exit ${i.code}):
|
|
56
56
|
${(i.stderr||i.stdout).trim().split(`
|
|
57
57
|
`).slice(-20).join(`
|
|
58
|
-
`)}`);return n}function LN(e,t){let n=[];for(let r of t){let o=bd(r),s=
|
|
59
|
-
`)[0]}), so a dependency path built with a function is not followed; the apply jobs take the waves from terragrunt find`]}}function Js(e,t=[]){return iS(e,{canary:t}).filter(n=>n.length>0)}function mw(e,t){let n=new Map(t.map(s=>[s.path,s.dependencies])),r=new Map;e.forEach((s,i)=>s.forEach(a=>r.set(a,i)));let o=[];return e.forEach((s,i)=>{let a=new Set(s);for(let u of s){let l=(n.get(u)??[]).filter(d=>(r.get(d)??-1)>i);if(l.length>0)throw new S(`${u} reads ${l.join(", ")}, which the pipeline applies in a later wave; run terragucci init to cut the waves again`)}let c=s.map(u=>({path:u,dependencies:(n.get(u)??[]).filter(l=>a.has(l))}));o.push(...iS(c).filter(u=>u.length>0))}),o}function UR(e){return new Map(e.map(t=>[t.path,new Set(t.dependencies)]))}function
|
|
58
|
+
`)}`);return n}function LN(e,t){let n=[];for(let r of t){let o=bd(r),s=R7(x7(e,o),"utf-8");for(let i of MR(e,o,s)){if(i.template===void 0){let a=new RegExp(`\\b${i.type}\\s+"${i.name.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}"\\s*\\{`).exec(s),c=a?s.slice(a.index+a[0].length):"",u=c.search(/^\s*(unit|stack)\s+"/m),l=a?/^\s*source\s*=\s*"\$\{get_repo_root\(\)\}\/([^"$]*)"/m.exec(u<0?c:c.slice(0,u)):null;l&&(i.template=l[1].replace("//","/").replace(/\/+$/,""))}if(i.type==="stack")throw new S(`${o}: stack "${i.name}" nests a stack, and ${PC}`);n.push({unit:i.generated,file:o,name:i.name,...i.template!==void 0?{template:i.template}:{}})}}return n}var GN=["root.hcl","terragrunt.hcl","terragrunt.stack.hcl"],P7=new Set([".git",".terraform",".terragrunt-cache","node_modules",".terragucci","terragucci-report"]),gw=e=>e.split("\\").join("/");function zN(e,t,n=()=>!1){let r=o=>{let s;try{s=FN(o)}catch{return}t(o,s);for(let i of s){if(P7.has(i)||i.startsWith(".")&&!n(i))continue;let a=eg(o,i);A7(a).isDirectory()&&r(a)}};r(e)}function vd(e){let t=new Map,n=[];zN(e,(o,s)=>{let i=gw(fw(e,o))||".";for(let a of GN)s.includes(a)&&!t.has(a)&&t.set(a,i==="."?a:`${i}/${a}`);s.includes("terragrunt.stack.hcl")&&n.push(i)});let r=GN.find(o=>t.has(o));return r?{reason:t.get(r),stacks:n.sort()}:void 0}function Xf(e,t){let n=[],r=new RegExp(`^\\s*${t}\\b[^{\\n]*\\{`,"gm");for(let o=r.exec(e);o;o=r.exec(e)){let s=1,i=o.index+o[0].length,a=i;for(let c=!1;i<e.length&&s>0;i++){let u=e[i];c?u==="\\"?i++:u==='"'&&(c=!1):u==='"'?c=!0:u==="{"?s++:u==="}"&&s--}n.push(e.slice(a,i-1)),r.lastIndex=i}return n}function zR(e){let t=e.replace(/(^|[^:"$])(#|\/\/).*$/gm,"$1"),n=/^"([^"$]*)"$/,r=[];for(let o of Xf(t,"dependency")){let s=/^\s*config_path\s*=\s*("[^"\n]*")\s*$/m.exec(o),i=s&&n.exec(s[1]);i&&r.push(i[1])}for(let o of Xf(t,"dependencies")){let s=/\bpaths\s*=\s*\[([^\]]*)\]/.exec(o);for(let i of s?s[1].split(",").map(a=>a.trim()).filter(Boolean):[]){let a=n.exec(i);a&&r.push(a[1])}}return r}function Dn(e,t=[]){let n=[...hu,...t],r=[];zN(e,(s,i)=>{let a=gw(fw(e,s));if(!a||!i.includes("terragrunt.hcl")||n.some(l=>ql(a,l)))return;let c="";try{c=Qf(eg(s,"terragrunt.hcl"),"utf-8")}catch{}let u=zR(c).map(l=>gw(fw(e,GR(s,l))));r.push({path:a,dependencies:[...new Set(u)].sort()})},s=>s===wd);let o=new Set(r.map(s=>s.path));for(let s of r)s.dependencies=s.dependencies.filter(i=>o.has(i));return r.sort((s,i)=>s.path<i.path?-1:1)}async function qs(e,t={}){let n=t.terragrunt??process.env.TERRAGUCCI_TERRAGRUNT??"terragrunt",r={dir:e,terragrunt:n,...t.binary?{binary:t.binary}:{},exec:t.exec??Nn};await Bi(e,r);let o;try{await lw(r);let s=await dw({...r,...t.exclude?{exclude:t.exclude}:{}});return{units:s.units,source:"terragrunt find",notes:s.warnings}}catch(s){o=s.message}return{units:Dn(e,t.exclude),source:"terragrunt.hcl files",notes:[`units and their edges come from terragrunt.hcl files: Terragrunt discovery did not run (${o.split(`
|
|
59
|
+
`)[0]}), so a dependency path built with a function is not followed; the apply jobs take the waves from terragrunt find`]}}function Js(e,t=[]){return iS(e,{canary:t}).filter(n=>n.length>0)}function mw(e,t){let n=new Map(t.map(s=>[s.path,s.dependencies])),r=new Map;e.forEach((s,i)=>s.forEach(a=>r.set(a,i)));let o=[];return e.forEach((s,i)=>{let a=new Set(s);for(let u of s){let l=(n.get(u)??[]).filter(d=>(r.get(d)??-1)>i);if(l.length>0)throw new S(`${u} reads ${l.join(", ")}, which the pipeline applies in a later wave; run terragucci init to cut the waves again`)}let c=s.map(u=>({path:u,dependencies:(n.get(u)??[]).filter(l=>a.has(l))}));o.push(...iS(c).filter(u=>u.length>0))}),o}function UR(e){return new Map(e.map(t=>[t.path,new Set(t.dependencies)]))}function j7(e){let t=eg(e,"root.hcl");if(!FR(t))return;let n=Qf(t,"utf-8").replace(/(^|[^:"])(#|\/\/).*$/gm,"$1"),r=/\bremote_state\s*\{[\s\S]*?\bbackend\s*=\s*"([^"]+)"/.exec(n);if(r)return{backend:r[1],gitlab:r[1]==="http"&&/\/api\/v4\/projects\/[^"]*\/terraform\/state\//.test(n)}}function UN(e,t={}){if(t.parallelism!==void 0)return{value:t.parallelism,reason:"terragucci.yml"};let n=j7(e);return n?.gitlab?{value:3,reason:"GitLab-managed state rate-limits concurrent inits"}:{value:16,reason:n?`the ${n.backend} backend`:"the default"}}function BN(e){let t=eg(e,"root.hcl");if(FR(t))return/terragrunt_version_constraint\s*=\s*"\s*=?\s*(\d+\.\d+\.\d+)\s*"/.exec(Qf(t,"utf-8"))?.[1]}var Au="TERRAGUCCI_TG_ROLES";function WN(e,t){return Object.entries(e).map(([n,r])=>[n,r[t]])}function I7(e,t){let n=s=>{try{return/^\s*iam_role\s*=/m.test(Qf(s,"utf-8"))}catch{return!1}},r=GR(e),o=GR(t);for(let s=o;s.startsWith(r);s=O7(s)){if((s===o?["terragrunt.hcl"]:FN(s).filter(a=>/^[^.].*\.hcl$/.test(a)&&a!=="terragrunt.hcl")).some(a=>n(eg(s,a))))return!0;if(s===r)break}return!1}function HN(e,t){let n=t.TERRAGUCCI_REPO??process.cwd(),r=gw(fw(n,e)),o;try{o=JSON.parse(t[Au]??"[]")}catch{throw new S(`${Au} is not JSON`)}if(I7(n,e))return{};let s=o.find(([c])=>ql(r,c));if(!s)return{};let i=Li(t);if(i){let c=Ru(i,s[1]);return{awsCredentials:{ACCESS_KEY_ID:c.accessKeyId,SECRET_ACCESS_KEY:c.secretAccessKey,...c.sessionToken?{SESSION_TOKEN:c.sessionToken}:{}}}}let a=t.AWS_WEB_IDENTITY_TOKEN_FILE;if(!a||!FR(a))throw new S(`${r} needs ${s[1]}, but the job has no OIDC token file`);return{awsRole:{roleARN:s[1],roleSessionName:`terragucci-${t.TERRAGUCCI_PHASE??"run"}`,webIdentityToken:Qf(a,"utf-8").trim()}}}var yw="1.230.1",ww="atmos.yaml",bw="terragucci atmos write";function vw(e){return tg(qr(e,ww))?ww:void 0}var YN=".terragucci-atmos.json",HR=".terragucci-terramate.json",G7="terragucci-atmos-reads.auto.tfvars.json",F7=["terraform.state","terraform.output","store","store.get"],JN=/^!(terraform\.state|terraform\.output|store\.get|store)(\s|$)/;function z7(e){let t=[];for(let n of e.matchAll(/"((?:[^"\\]|\\.)*)"|'([^']*)'|(\S+)/g))t.push(n[1]??n[2]??n[3]);return t}function U7(e,t,n,r){if(typeof r!="string"){let c=l=>typeof l=="string"?JN.test(l)?l:void 0:l&&typeof l=="object"?Object.values(l).map(c).find(d=>d):void 0,u=c(r);if(u)throw new S(`${e} sets ${n} with ${u.split(/\s/)[0]} inside a map or a list; terragucci reads a whole var only, so give the function a var of its own`);return}let o=r.match(JN);if(!o)return;let s=o[1];if(s==="store"||s==="store.get")throw new S(`${e} sets ${n} with !${s}, which terragucci does not read in the job; output the value from an instance and read it with !terraform.state`);let i=z7(r.slice(o[0].length));if(i.length!==2&&i.length!==3)throw new S(`${e} sets ${n} with "${r}"; terragucci reads !${s} <component> [<stack>] <output>`);let a=i[i.length-1];if(!/^\.?[A-Za-z_][\w-]*(\.[A-Za-z_][\w-]*)*$/.test(a))throw new S(`${e} sets ${n} with "${r}", whose expression ${a} terragucci does not evaluate; read an output by name or by a path of keys, such as .subnets.private`);return{var:n,component:i[0],stack:i.length===3?i[1]:t,output:a.replace(/^\./,"").split("."),function:r}}var yn=e=>e&&typeof e=="object"&&!Array.isArray(e)?e:{},ps=e=>typeof e=="string"&&e!==""?e:void 0,XN=e=>e.split(L7).join("/"),B7={ATMOS_TELEMETRY_ENABLED:"false",ATMOS_LOGS_LEVEL:"Warning"};async function Wi(e,t={}){let n=t.atmos??process.env.TERRAGUCCI_ATMOS??"atmos",r=t.exec??Nn,o;try{o=await r(n,["describe","stacks","--format","json","--component-types","terraform",...F7.flatMap(s=>["--skip",s])],{cwd:e,env:{...B7}})}catch(s){throw new S(`atmos describe stacks did not run (${s.message}); put Atmos ${yw} on the path, or set TERRAGUCCI_ATMOS`)}if(o.code!==0){let s=(o.stderr||o.stdout).trim().split(`
|
|
60
60
|
`).filter(i=>i.trim()).slice(0,12).join(`
|
|
61
61
|
`);throw new S(o.code===null||/ENOENT|not found/.test(s)?`atmos describe stacks did not run (${s.split(`
|
|
62
62
|
`)[0]}); put Atmos ${yw} on the path, or set TERRAGUCCI_ATMOS`:`atmos describe stacks failed (exit ${o.code}):
|
|
63
63
|
${s}`)}try{return yn(JSON.parse(o.stdout))}catch{throw new S(`atmos describe stacks printed no JSON:
|
|
64
|
-
${o.stdout.slice(0,400)}`)}}var hw=["namespace","tenant","environment","stage"];function
|
|
64
|
+
${o.stdout.slice(0,400)}`)}}var hw=["namespace","tenant","environment","stage"];function W7(e){let t=[],n=o=>{let s=ps(o.component);if(!s)return;let i={};for(let a of hw)ps(o[a])&&(i[a]=o[a]);t.push({component:s,...ps(o.stack)?{stack:o.stack}:{},context:i})},r=yn(e.dependencies).components;if(Array.isArray(r))for(let o of r)n(yn(o));for(let o of Object.values(yn(yn(e.settings).depends_on)))n(yn(o));return t}function Hi(e){let t=[],n=new Set;for(let[o,s]of Object.entries(e))for(let[i,a]of Object.entries(yn(yn(yn(s).components).terraform))){let c=yn(a),u=yn(c.metadata);if(u.type==="abstract")continue;let l=`${o}/${i}`;if(u.enabled===!1){n.add(l);continue}for(let f of["env","generate"])if(Object.keys(yn(c[f])).length>0)throw new S(`${l} sets ${f}, which terragucci does not carry into the jobs yet; move it into the component's Terraform, or leave the instance out with metadata.enabled: false`);let d=Object.entries(yn(c.vars)).map(([f,g])=>U7(l,o,f,g)).filter(f=>f!==void 0),p=ps(yn(c.component_info).component_path)??`components/terraform/${ps(u.component)??ps(c.component)??i}`;t.push({path:l,stack:o,component:i,componentPath:XN(p),workspace:ps(c.workspace)??o,vars:yn(c.vars),backendType:ps(c.backend_type)??"",backend:yn(c.backend),providers:yn(c.providers),dependencies:[],reads:[],raw:W7(c),ctx:yn(c.vars),rawReads:d})}let r=new Map(t.map(o=>[o.path,o]));for(let o of t){let s=new Set;for(let i of o.raw){let a=i.stack;if(!a&&Object.keys(i.context).length>0){let u=Object.fromEntries(hw.map(d=>[d,i.context[d]??o.ctx[d]])),l=t.filter(d=>d.component===i.component&&hw.every(p=>u[p]===void 0||d.ctx[p]===u[p]));if(a=l.length===1?l[0].stack:void 0,!a){let d=hw.filter(p=>u[p]!==void 0).map(p=>`${p} ${String(u[p])}`).join(", ");throw new S(`${o.path} depends on ${i.component} in ${d}, which ${l.length===0?"no stack has":`${l.length} stacks have`}`)}}let c=`${a??o.stack}/${i.component}`;if(c!==o.path&&!n.has(c)){if(!r.has(c))throw new S(`${o.path} depends on ${c}, which no stack deploys`);s.add(c)}}for(let i of o.rawReads){let a=`${i.stack}/${i.component}`,c=`${o.path} reads ${i.var} from ${a} (${i.function})`;if(a===o.path)throw new S(`${c}, its own state`);if(n.has(a))throw new S(`${c}, which is disabled, so nothing ever applies it`);if(!r.has(a))throw new S(`${c}, which no stack deploys`);s.add(a),o.reads.push({var:i.var,upstream:a,output:i.output,function:i.function})}o.dependencies=[...s].sort()}return t.map(({raw:o,ctx:s,rawReads:i,...a})=>a).sort((o,s)=>o.path<s.path?-1:o.path>s.path?1:0)}function QN(e,t=[]){try{return Js(e.map(n=>({path:n.path,dependencies:n.dependencies})),t)}catch(n){throw new S(n.message)}}function H7(e){return e===".terraform"||e==="terraform.tfstate.d"||/\.tfstate(\.backup)?$/.test(e)||/\.planfile$/.test(e)||e==="backend.tf.json"||e==="providers_override.tf.json"||e.endsWith(".terraform.tfvars.json")}function q7(e,t,n,r){return e.replace(/(\bsource\s*=\s*")(\.\.?\/[^"]*)(")/g,(o,s,i,a)=>{let c=ZN(t,i);return qN(r,c).startsWith("..")?`${s}${XN(qN(n,c))}${a}`:o})}function eD(e,t,n){D7(t,{recursive:!0});for(let r of BR(e)){if(H7(r))continue;let o=qr(e,r),s=qr(t,r);VN(o).isDirectory()?eD(o,s,n):/\.(tf|tofu)$/.test(r)?WR(s,q7(KN(o,"utf-8"),e,t,n)):N7(o,s)}}function J7(e){let t=s=>`${JSON.stringify(s,null,2)}
|
|
65
65
|
`,n=new Set(e.reads.map(s=>s.var)),r=Object.fromEntries(Object.entries(e.vars).filter(([s])=>!n.has(s))),o={"terragucci-atmos.auto.tfvars.json":t(r),[ow]:`${e.workspace}
|
|
66
|
-
`,[YN]:t({component:e.componentPath,dependencies:e.dependencies,reads:e.reads})};return e.backendType==="cloud"?o["backend.tf.json"]=t({terraform:{cloud:e.backend}}):e.backendType&&(o["backend.tf.json"]=t({terraform:{backend:{[e.backendType]:e.backend}}})),Object.keys(e.providers).length>0&&(o["providers_override.tf.json"]=t({provider:e.providers})),o}function
|
|
66
|
+
`,[YN]:t({component:e.componentPath,dependencies:e.dependencies,reads:e.reads})};return e.backendType==="cloud"?o["backend.tf.json"]=t({terraform:{cloud:e.backend}}):e.backendType&&(o["backend.tf.json"]=t({terraform:{backend:{[e.backendType]:e.backend}}})),Object.keys(e.providers).length>0&&(o["providers_override.tf.json"]=t({provider:e.providers})),o}function K7(e,t){for(let n of t){let r=qr(e,n.componentPath);if(!tg(r)||!VN(r).isDirectory())throw new S(`${n.path} runs ${n.componentPath}, which the repo does not have`);let o=qr(e,n.path);if(tg(o)){if(!tg(qr(o,ow))&&BR(o).length>0)throw new S(`${n.path} is a directory of the repo, and the Atmos instance ${n.component} in stack ${n.stack} is written there; rename one of them`);for(let s of BR(o))s!==".terraform"&&M7(qr(o,s),{recursive:!0,force:!0})}eD(r,o,ZN(r));for(let[s,i]of Object.entries(J7(n)))WR(qr(o,s),i)}}async function tD(e,t={}){let n=Hi(await Wi(e,t));if(n.length===0)throw new S("atmos describe stacks lists no Terraform instance that is neither abstract nor disabled");return K7(e,n),n.map(r=>`${r.path}: ${r.componentPath} in workspace ${r.workspace}`)}function ec(e){let t=[qr(e,YN),qr(e,HR)].find(n=>tg(n));if(t)try{let n=JSON.parse(KN(t,"utf-8")),r=Array.isArray(n.dependencies)?n.dependencies.filter(s=>typeof s=="string"):[],o=(Array.isArray(n.reads)?n.reads:[]).filter(s=>!!s&&typeof s=="object"&&typeof s.var=="string"&&typeof s.upstream=="string"&&Array.isArray(s.output));return{...typeof n.component=="string"?{component:n.component}:{},dependencies:r,reads:o}}catch{return}}function nD(e,t){let n=new Map;for(let r of t){let o=ec(qr(e,r))?.reads??[];o.length>0&&n.set(r,new Set(o.map(s=>s.upstream)))}return n}function ng(e,t){let n=new Map;for(let r of t){let o=ec(qr(e,r))?.dependencies??[];o.length>0&&n.set(r,new Set(o))}return n}async function $w(e,t,n,r=o=>o()){let o=(u,l)=>new Promise(d=>{let p=C7(e,[`-chdir=${t}`,...u],{env:Ve(l),stdio:["ignore","pipe","pipe"]}),f=[],g=[];p.stdout.on("data",m=>f.push(m)),p.stderr.on("data",m=>g.push(m)),p.on("error",m=>g.push(Buffer.from(m.message))),p.on("close",m=>d({code:m,out:Buffer.concat(f).toString("utf-8"),err:Buffer.concat(g).toString("utf-8")}))}),s=u=>u.trim().split(`
|
|
67
67
|
`).slice(-6).join(`
|
|
68
|
-
`),i=Fi(n,t),a=await r(()=>o(["init","-input=false","-no-color"],i?.init??n));if(a.code!==0)return{error:`init failed: ${s(a.err||a.out)}`};if(i){let u=await o(i.select,i.selectEnv);if(u.code!==0)return{error:`workspace select failed: ${s(u.err||u.out)}`}}let c=await o(["output","-json","-no-color"],n);if(c.code!==0)return{error:`output -json failed: ${s(c.err||c.out)}`};try{let u=yn(JSON.parse(c.out||"{}"));return{outputs:Object.fromEntries(Object.entries(u).map(([l,d])=>[l,yn(d).value]))}}catch{return{error:`output -json printed no JSON: ${s(c.out)}`}}}function
|
|
69
|
-
`),o.filled=Object.keys(s).sort()),o}function rg(e){let t=new Map;for(let n of e){let r=ps(n.backend.key)??"terraform.tfstate";n.backendType==="s3"?t.set(n.path,{...ps(n.backend.bucket)?{bucket:n.backend.bucket}:{},key:`${ps(n.backend.workspace_key_prefix)??n.component}/${n.workspace}/${r}`}):t.set(n.path,n.backendType?{key:`${n.backendType}:${n.workspace}`}:void 0)}return{states:t,reads:new Map(e.map(n=>[n.path,new Set(n.reads.map(r=>r.upstream))]))}}var
|
|
70
|
-
`?"":r+s}}return!1}function Ar(e){return e.replace(/\/\*[\s\S]*?\*\//g,"").replace(/(^|[^:"])(#|\/\/).*$/gm,"$1")}function Pe(e,t){return ql(Ji(t).replace(/\/+$/,""),e)}function wt(e,t){return KR(e,t).map(n=>n.root)}function KR(e,t){let n=[];for(let r of XX(e)){let o=Ji(xw(e,r))||".";if(t){let s=t.find(i=>Pe(i,o));s!==void 0&&gs(r).length>0&&n.push({root:o,reason:`matches roots glob ${s}`})}else{let s=QX(r);s&&n.push({root:o,reason:s})}}return n.sort((r,o)=>r.root<o.root?-1:r.root>o.root?1:0)}function rD(e){try{return sD(process.platform==="win32"?"where":"which",[e],{stdio:"ignore"}),!0}catch{return!1}}function Ks(e,t){return Jr(Pt(e,".opentofu-version"))?{value:"tofu",reason:".opentofu-version"}:Jr(Pt(e,".terraform-version"))?{value:"terraform",reason:".terraform-version"}:t.some(n=>Jr(Pt(e,n))&&Ew(Pt(e,n)).some(r=>r.endsWith(".tofu")))?{value:"tofu",reason:".tofu files"}:rD("tofu")?{value:"tofu",reason:"tofu on the path"}:rD("terraform")?{value:"terraform",reason:"terraform on the path"}:{value:"tofu",reason:"the default"}}function iD(e,t){let n=new Set;for(let r of t)for(let o of VR(Pt(e,r)))n.add(o);return n.size===1?[...n][0]:void 0}function VR(e){let t=[];for(let n of gs(e)){let r=Ar(Mn(n,"utf-8")).match(/required_version\s*=\s*"\s*=?\s*(\d+\.\d+\.\d+)\s*"/);r&&!t.includes(r[1])&&t.push(r[1])}return t}function Kr(e){if(Jr(Pt(e,".forgejo","workflows")))return{value:"forgejo",reason:".forgejo/workflows"};if(Jr(Pt(e,".gitea","workflows")))return{value:"forgejo",reason:".gitea/workflows"};if(Jr(Pt(e,".gitlab-ci.yml")))return{value:"gitlab",reason:".gitlab-ci.yml"};let t="";try{t=sD("git",["-C",e,"remote","get-url","origin"],{encoding:"utf-8",stdio:["ignore","pipe","ignore"]}).trim()}catch{}let n=sg(t),r=n?ir(n):void 0;if(r)return{value:r,reason:`the origin remote (${n})`};if(Jr(Pt(e,".github","workflows")))return{value:"github",reason:".github/workflows"}}function sg(e){if(!e)return;let t=e.match(/^[a-z+]+:\/\/(?:[^@/]+@)?([^/]+)/i);if(t)return t[1];let n=e.match(/^(?:[^@]+@)?([^:/]+):/);return n?n[1]:void 0}function $t(e,t){let n=0;for(let r=e.indexOf("{",t);r<e.length;r++)if(e[r]==="{")n++;else if(e[r]==="}"&&--n===0)return e.slice(t,r+1);return e.slice(t)}function og(e,t){return e.match(new RegExp(`\\b${t}\\s*=\\s*"([^"]*)"`))?.[1]}var $d=(e,t)=>n=>{let r=e.match(new RegExp(`(?<![\\w.-])${n}\\s*=(?!=)\\s*(.)`));if(!r)return;if(r[1]!=='"')return null;let o=r.index+r[0].length,s="",i=0;for(let a=o;a<e.length;a++){if(e[a]==="\\"){s+=e[++a]??"";continue}if(t&&e[a]==="$"&&e[a+1]==="{"?i++:t&&i>0&&e[a]==="}"&&i--,e[a]==='"'&&i===0)return Gi(t?t(s):s)??null;s+=e[a]}return null},nQ=(e,t)=>n=>{for(let r=t.length-1;r>=0;r--){let o=t[r](n);if(o!==void 0)return o}return e(n)};function rQ(e){let t=[...e.matchAll(/"((?:[^"\\]|\\.)*)"|'([^']*)'|(\S+)/g)].map(r=>r[1]?.replace(/\\(.)/g,"$1")??r[2]??r[3]),n=[];for(let r=0;r<t.length;r++){let o=/^--?backend-config(?:=(.*))?$/.exec(t[r]);if(!o)continue;let s=o[1]??t[++r];s!==void 0&&n.push(s.replace(/^"(.*)"$/,"$1").replace(/^'(.*)'$/,"$1"))}return n}function Tw(e){let t;try{let r=fe(e);if(!r||r.endsWith(".ts"))return[];let o=Mn(r,"utf-8"),s=r.endsWith(".json")?JSON.parse(o):o.trim()===""?{}:Yl(o);t=Mo(s)?.env}catch{return[]}let n=Mo(t)??{};return[n.TF_CLI_ARGS,n.TF_CLI_ARGS_init].flatMap(r=>typeof r=="string"?rQ(r):[])}function oQ(e){try{return Ew(e).filter(t=>t==="backend.hcl"||t==="backend.tfvars"||t.endsWith(".tfbackend")).sort()}catch{return[]}}function sQ(e,t,n){let r={layers:[],files:[],missing:[]},o=Pt(e,t);for(let s of n){let i=s.indexOf("=");if(i>=0){let u=s.slice(0,i).trim(),l=s.slice(i+1);r.layers.push(d=>d===u?Gi(l)??null:void 0);continue}let a=ZX(s)?s:Sw(o,s),c=Ji(xw(e,a))||".";if(!Jr(a)||!JR(a).isFile()){r.missing.push(c);continue}r.files.push(c),r.layers.push($d(Ar(Mn(a,"utf-8"))))}return r}var aD="terragrunt.hcl";function iQ(e,t,n){let r=/^"?\$?\{?\s*find_in_parent_folders\(\s*(?:"([^"]+)")?\s*\)\s*\}?"?$/.exec(n);if(r){let i=r[1]??aD,a=Sw(e);for(let c=_w(Sw(t));;c=_w(c)){if(Jr(Pt(c,i)))return Pt(c,i);if(c===a||_w(c)===c)return}}let o=/^"\$\{\s*get_repo_root\(\s*\)\s*\}\/([^"$]+)"$/.exec(n);if(o)return Pt(e,o[1]);let s=/^"([^"$]+)"$/.exec(n);return s?Sw(t,s[1]):void 0}function cD(e,t){let n=Pt(e,t),r=Pt(n,aD);if(!Jr(r))return;let o=Ar(Mn(r,"utf-8")),s=[];for(let a of o.matchAll(/^\s*include(?:\s+"([^"]*)")?\s*\{/gm)){let c=/(?:^|[{\s])path\s*=\s*(.+?)\s*$/m.exec($t(o,a.index).replace(/^[^{]*\{/,"").replace(/\}$/,""))?.[1],u=c?iQ(e,n,c):void 0;u&&Jr(u)&&s.push({name:a[1]??"",file:u})}let i=[{file:r,text:o,include:s[0]},...s.map(a=>({file:a.file,text:Ar(Mn(a.file,"utf-8")),include:a}))];for(let a of i){let c=/^\s*remote_state\s*=?\s*\{/m.exec(a.text);if(!c)continue;let u=Ji(xw(e,a.file)),l=$t(a.text,c.index),d=h=>{let y=h===void 0?a.include:s.find(w=>w.name===h);return y?Ji(xw(_w(y.file),n))||".":h===void 0?".":void 0},p=h=>h.replace(/\$\{\s*path_relative_to_include\(\s*(?:"([^"]*)")?\s*\)\s*\}/g,(y,w)=>d(w)??y),f=$d(l)("backend");if(f==null)return{why:`the Terragrunt remote_state block in ${u} names its backend with an expression, not a plain string`};let g=/\bconfig\s*=\s*\{/.exec(l);if(!g)return{why:`the Terragrunt remote_state block in ${u} takes its config from an expression, not an object`};let m=qi(f,$d($t(l,g.index).replace(/^[^{]*\{/,""),p),t);return m.ref?{ref:m.ref}:{why:`the Terragrunt remote_state block in ${u}: ${m.why}`}}}var Mo=e=>e&&typeof e=="object"&&!Array.isArray(e)?e:void 0,Rw=e=>Mo(Array.isArray(e)?e[0]:e),qR=e=>t=>{let n=e?.[t]??Rw(e?.workspaces)?.[t];return Gi(n)};function qi(e,t,n){if(e===null)return{why:"its backend is an expression, not a plain string"};let r=gd(e??"local",t,n);return"unresolved"in r?{why:r.unresolved}:{ref:r}}function aQ(e,t,n=(r,o)=>qi(r,o,t)){let r,o={reads:[],unresolved:[],declared:!1};try{r=JSON.parse(e)}catch{return o}let s=Mo(r);for(let i of Array.isArray(s?.terraform)?s.terraform:[s?.terraform]){let a=Object.entries(Mo(Mo(i)?.backend)??{});Mo(i)?.cloud!==void 0&&a.push(["cloud",Mo(i).cloud]);for(let[c,u]of a){let l=c==="cloud"?qi(c,qR(Rw(u)),t):n(c,qR(Rw(u)));o.declared=!0,l.ref?(o.own=l.ref,delete o.ownUnresolved):o.ownUnresolved=l.why}}for(let i of Array.isArray(s?.data)?s.data:[s?.data]){let a=Mo(Mo(i)?.terraform_remote_state)??{};for(let[c,u]of Object.entries(a)){let l=Rw(u),d=Mo(l?.config),p=d===void 0&&l?.config!==void 0?{why:"its config is an expression, not an object"}:qi(Gi(l?.backend),qR(d),t);p.ref?o.reads.push({name:c,...p.ref,repeated:l?.count!==void 0||l?.for_each!==void 0}):o.unresolved.push({name:c,why:p.why})}}return o}function Lo(e,t,n=Tw(e)){let r={reads:[],unresolved:[]},o=!1,s=u=>{o=!0,u.ref?(r.own=u.ref,delete r.ownUnresolved):r.ownUnresolved=u.why},i=n.length?sQ(e,t,n):void 0,a=(u,l)=>{let d=qi(u,i?nQ(l,i.layers):l,t);if(d.ref)return d;let p=(y,w,b)=>y.length===1?w:b,f=i?.files??[],g=i?.missing??[],m=oQ(Pt(e,t)).map(y=>Ji(t==="."?y:`${t}/${y}`)).filter(y=>!f.includes(y)),h=[...f.length?[`the -backend-config ${p(f,"file","files")} ${f.join(", ")} from terragucci.yml's env ${p(f,"does","do")} not supply it either`]:[],...g.length?[`terragucci.yml's env names -backend-config ${g.join(", ")}, which ${p(g,"is","are")} not in the repo`]:[],...m.length?[`${m.join(", ")} ${p(m,"is","are")} beside it, but no -backend-config in terragucci.yml's env names ${p(m,"it","them")}`]:[]];return h.length?{why:`${d.why}; ${h.join("; ")}`}:d};for(let u of gs(Pt(e,t))){if(fs(u)){let d=aQ(Mn(u,"utf-8"),t,a);d.declared&&s(d.own?{ref:d.own}:{why:d.ownUnresolved}),r.reads.push(...d.reads),r.unresolved.push(...d.unresolved);continue}let l=Ar(Mn(u,"utf-8"));for(let d of l.matchAll(/\bbackend\s+"([^"]+)"\s*\{/g))s(a(d[1],$d($t(l,d.index))));for(let d of l.matchAll(/^\s*cloud\s*\{/gm))s(qi("cloud",$d($t(l,d.index)),t));for(let d of l.matchAll(/\bdata\s+"terraform_remote_state"\s+"([^"]+)"\s*\{/g)){let p=$t(l,d.index),f=$d(p),g=qi(f("backend"),f,t);g.ref?r.reads.push({name:d[1],...g.ref,repeated:/^\s*(count|for_each)\s*=/m.test(p)}):r.unresolved.push({name:d[1],why:g.why})}}o||s(qi("local",()=>{},t));let c=cD(e,t);return c?.ref?s(c):c?.why&&(delete r.own,r.ownUnresolved=c.why),r}function ig(e,t){let n=Tw(e),r=new Map(t.map(s=>[s,Lo(e,s,n)])),o=new Map(t.map(s=>[s,[]]));for(let[s,{reads:i}]of r)for(let a of i)for(let[c,{own:u}]of r)c===s||!u||u.key===a.key&&(!u.bucket||!a.bucket||u.bucket===a.bucket)&&o.get(s).push({name:a.name,upstream:c,repeated:a.repeated});return o}function ZR(e,t){let n=Tw(e),r=t.map(s=>[s,Lo(e,s,n)]),o=r.some(([,s])=>s.reads.length>0||s.unresolved.length>0);return r.map(([s,i])=>({root:s,...o&&i.ownUnresolved?{own:i.ownUnresolved}:{},reads:i.unresolved})).filter(s=>s.own!==void 0||s.reads.length>0)}function uD(e,t){return ZR(e,t).flatMap(n=>[...n.own?[`state: ${n.root} keeps its state where the code does not say (${n.own}), so a root that reads it through terraform_remote_state is not ordered after it`]:[],...n.reads.map(r=>`state: ${n.root} reads state through terraform_remote_state "${r.name}" where the code does not say (${r.why}), so it is not ordered after the root that writes it`)])}var oD=e=>`tofu-records/${e}`;function cQ(e,t,n){let r=Pt(e,t),{estate:o}=Go(r),s=[...n.reads];for(let c of gs(r)){if(fs(c))continue;let u=Ar(Mn(c,"utf-8"));for(let l of u.matchAll(/\bdata\s+"terraform_estate_outputs"\s+"([^"]+)"\s*\{/g)){let d=og($t(u,l.index),"estate");d&&s.push({name:l[1],key:oD(d),repeated:!1})}}let i=qi("local",()=>{},t).ref;return{own:o&&(!n.own||i&&pD(n.own,i))?{key:oD(o)}:n.own,reads:s}}function Aw(e,t){let n=Tw(e),r=new Map(t.map(i=>[i,cQ(e,i,Lo(e,i,n))])),o=[...r.values()].flatMap(i=>i.own?[i.own]:[]),s=new Map;for(let[i,{own:a,reads:c}]of r){let u=c.filter(l=>!o.some(d=>d!==a&&pD(d,l))).map(l=>({data:l.name,...l.bucket!==void 0?{bucket:l.bucket}:{},key:l.key}));s.set(i,{...a?{state:lD(a)}:{},external:u})}return s}var lD=e=>({...e.bucket!==void 0?{bucket:e.bucket}:{},key:e.key});function dD(e,t){return new Map(t.map(n=>{let r=cD(e,n)?.ref;return[n,{...r?{state:lD(r)}:{},external:[]}]}))}function Ow(e){for(let t of gs(e)){let n=Mn(t,"utf-8");if(fs(t)){let s;try{s=JSON.parse(n)}catch{continue}let i=s?.terraform;for(let a of Array.isArray(i)?i:[i]){if(!a||typeof a!="object")continue;if(a.cloud!==void 0)return{cloud:!0};let c=a.backend&&typeof a.backend=="object"?a.backend:{},u=Object.keys(c)[0];if(!u)continue;let l=Array.isArray(c[u])?c[u][0]:c[u],d=Object.fromEntries(Object.entries(l??{}).filter(([,p])=>typeof p=="string"));return{type:u,attrs:d}}continue}let r=Ar(n),o=r.match(/\bbackend\s+"([^"]+)"\s*\{/);if(o){let s=$t(r,o.index),i={};for(let a of s.matchAll(/^\s*([A-Za-z_][A-Za-z0-9_]*)\s*=\s*"([^"]*)"/gm))i[a[1]]=a[2];return{type:o[1],attrs:i}}if(/^\s*cloud\s*\{/m.test(r))return{cloud:!0}}}var pD=(e,t)=>e.key===t.key&&(!e.bucket||!t.bucket||e.bucket===t.bucket);function Go(e){let t,n=[],r=Pt(e,"estate.chdf.hcl");Jr(r)&&(t=og(Ar(Mn(r,"utf-8")),"estate"));for(let o of gs(e)){if(fs(o))continue;let s=Ar(Mn(o,"utf-8"));for(let i of s.matchAll(/^\s*terraform\s*\{/gm)){let a=$t(s,i.index),c=a.match(/\blive\s*\{/);c&&!t&&(t=og($t(a,c.index),"estate"))}for(let i of s.matchAll(/\bdata\s+"terraform_estate_outputs"\s+"[^"]+"\s*\{/g)){let a=og($t(s,i.index),"estate");a&&n.push(a)}}return{estate:t,reads:n}}function fD(e){let t=Pt(e,"estate.chdf.hcl");if(Jr(t))return Ar(Mn(t,"utf-8"));for(let n of gs(e)){if(fs(n))continue;let r=Ar(Mn(n,"utf-8"));for(let o of r.matchAll(/^\s*terraform\s*\{/gm)){let s=$t(r,o.index),i=s.match(/\blive\s*\{/);if(i)return $t(s,i.index)}}}function Pw(e){let t=[];for(let n of gs(e)){if(fs(n))continue;let r=Ar(Mn(n,"utf-8"));for(let o of r.matchAll(/\bdata\s+"terraform_estate_outputs"\s+"([^"]+)"\s*\{/g)){let s=og($t(r,o.index),"estate");s&&t.push({name:o[1],estate:s})}}return t}function gD(e){let t=new Map;for(let n of gs(e)){let r=Mn(n,"utf-8");if(fs(n)){try{let s=JSON.parse(r);for(let i of Array.isArray(s.locals)?s.locals:[s.locals])for(let[a,c]of Object.entries(Mo(i)??{}))t.set(a,JSON.stringify(c))}catch{}continue}let o=Ar(r);for(let s of o.matchAll(/^\s*locals\s*\{/gm)){let i=$t(o,s.index),a=i.slice(i.indexOf("{")+1,-1),c=0,u,l=[];for(let d of a.split(`
|
|
68
|
+
`),i=Fi(n,t),a=await r(()=>o(["init","-input=false","-no-color"],i?.init??n));if(a.code!==0)return{error:`init failed: ${s(a.err||a.out)}`};if(i){let u=await o(i.select,i.selectEnv);if(u.code!==0)return{error:`workspace select failed: ${s(u.err||u.out)}`}}let c=await o(["output","-json","-no-color"],n);if(c.code!==0)return{error:`output -json failed: ${s(c.err||c.out)}`};try{let u=yn(JSON.parse(c.out||"{}"));return{outputs:Object.fromEntries(Object.entries(u).map(([l,d])=>[l,yn(d).value]))}}catch{return{error:`output -json printed no JSON: ${s(c.out)}`}}}function V7(e,t){let[n,...r]=t;if(!(n in e))return;let o=e[n];for(let s of r){if(!o||typeof o!="object"||Array.isArray(o)||!(s in o))return;o=o[s]}return{value:o}}async function kw(e,t,n){let r=ec(qr(e,t)),o={waiting:[],errors:[],filled:[]};if(!r||r.reads.length===0)return o;let s={};for(let i of r.reads){let a=await n(i.upstream);if("error"in a){o.errors.push(`${t} reads ${i.var} from ${i.upstream}, whose state could not be read: ${a.error}`);continue}let c=V7(a.outputs,i.output);if(!c){let u=Object.keys(a.outputs).length===0?"has no state yet":`has no output ${i.output.join(".")} yet`;o.waiting.push({read:i,why:u});continue}s[i.var]=c.value}return o.waiting.length===0&&o.errors.length===0&&(WR(qr(e,t,G7),`${JSON.stringify(s,null,2)}
|
|
69
|
+
`),o.filled=Object.keys(s).sort()),o}function rg(e){let t=new Map;for(let n of e){let r=ps(n.backend.key)??"terraform.tfstate";n.backendType==="s3"?t.set(n.path,{...ps(n.backend.bucket)?{bucket:n.backend.bucket}:{},key:`${ps(n.backend.workspace_key_prefix)??n.component}/${n.workspace}/${r}`}):t.set(n.path,n.backendType?{key:`${n.backendType}:${n.workspace}`}:void 0)}return{states:t,reads:new Map(e.map(n=>[n.path,new Set(n.reads.map(r=>r.upstream))]))}}var Y7=new Set([".git",".terraform",".terragrunt-cache","node_modules",".terragucci"]);function gs(e){try{return Ew(e).filter(t=>t.endsWith(".tf")||t.endsWith(".tofu")||fs(t)).map(t=>Pt(e,t)).filter(t=>JR(t).isFile())}catch{return[]}}var fs=e=>e.endsWith(".tf.json")||e.endsWith(".tofu.json");function X7(e){let t=[],n=r=>{t.push(r);for(let o of Ew(r)){if(Y7.has(o)||o.startsWith("."))continue;let s=Pt(r,o);JR(s).isDirectory()&&n(s)}};return n(e),t}var Ji=e=>e.split("\\").join("/");function Q7(e){let t=gs(e);for(let r of t.filter(fs)){let o=eQ(Mn(r,"utf-8"));if(o)return o}let n=t.filter(r=>!fs(r)).map(r=>Ar(Mn(r,"utf-8")));for(let r of n){let o=r.match(/\bbackend\s+"([^"]+)"\s*\{/);if(o)return`backend ${o[1]}`}if(n.some(r=>/^\s*cloud\s*\{/m.test(r)))return"cloud block";if(n.some(tQ))return"choudoufu live block";for(let r of n){let o=r.match(/^\s*provider\s+"([^"]+)"\s*\{/m);if(o)return`provider ${o[1]}`}}function eQ(e){let t;try{t=JSON.parse(e)}catch{return}let n=i=>i&&typeof i=="object"&&!Array.isArray(i)?i:void 0,r=n(t)?.terraform,o=(Array.isArray(r)?r:[r]).map(n).filter(i=>i!==void 0);for(let i of o){let a=Object.keys(n(i.backend)??{})[0];if(a)return`backend ${a}`}if(o.some(i=>i.cloud!==void 0))return"cloud block";let s=Object.keys(n(n(t)?.provider)??{})[0];return s?`provider ${s}`:void 0}function tQ(e){for(let t of e.matchAll(/^\s*terraform\s*\{/gm)){let n=1,r="";for(let o=(t.index??0)+t[0].length;o<e.length&&n>0;o++){let s=e[o];if(s==="{"){if(n===1&&/^\s*live\s*$/.test(r))return!0;n++}else s==="}"&&n--;r=s===`
|
|
70
|
+
`?"":r+s}}return!1}function Ar(e){return e.replace(/\/\*[\s\S]*?\*\//g,"").replace(/(^|[^:"])(#|\/\/).*$/gm,"$1")}function Pe(e,t){return ql(Ji(t).replace(/\/+$/,""),e)}function wt(e,t){return KR(e,t).map(n=>n.root)}function KR(e,t){let n=[];for(let r of X7(e)){let o=Ji(xw(e,r))||".";if(t){let s=t.find(i=>Pe(i,o));s!==void 0&&gs(r).length>0&&n.push({root:o,reason:`matches roots glob ${s}`})}else{let s=Q7(r);s&&n.push({root:o,reason:s})}}return n.sort((r,o)=>r.root<o.root?-1:r.root>o.root?1:0)}function rD(e){try{return sD(process.platform==="win32"?"where":"which",[e],{stdio:"ignore"}),!0}catch{return!1}}function Ks(e,t){return Jr(Pt(e,".opentofu-version"))?{value:"tofu",reason:".opentofu-version"}:Jr(Pt(e,".terraform-version"))?{value:"terraform",reason:".terraform-version"}:t.some(n=>Jr(Pt(e,n))&&Ew(Pt(e,n)).some(r=>r.endsWith(".tofu")))?{value:"tofu",reason:".tofu files"}:rD("tofu")?{value:"tofu",reason:"tofu on the path"}:rD("terraform")?{value:"terraform",reason:"terraform on the path"}:{value:"tofu",reason:"the default"}}function iD(e,t){let n=new Set;for(let r of t)for(let o of VR(Pt(e,r)))n.add(o);return n.size===1?[...n][0]:void 0}function VR(e){let t=[];for(let n of gs(e)){let r=Ar(Mn(n,"utf-8")).match(/required_version\s*=\s*"\s*=?\s*(\d+\.\d+\.\d+)\s*"/);r&&!t.includes(r[1])&&t.push(r[1])}return t}function Kr(e){if(Jr(Pt(e,".forgejo","workflows")))return{value:"forgejo",reason:".forgejo/workflows"};if(Jr(Pt(e,".gitea","workflows")))return{value:"forgejo",reason:".gitea/workflows"};if(Jr(Pt(e,".gitlab-ci.yml")))return{value:"gitlab",reason:".gitlab-ci.yml"};let t="";try{t=sD("git",["-C",e,"remote","get-url","origin"],{encoding:"utf-8",stdio:["ignore","pipe","ignore"]}).trim()}catch{}let n=sg(t),r=n?ir(n):void 0;if(r)return{value:r,reason:`the origin remote (${n})`};if(Jr(Pt(e,".github","workflows")))return{value:"github",reason:".github/workflows"}}function sg(e){if(!e)return;let t=e.match(/^[a-z+]+:\/\/(?:[^@/]+@)?([^/]+)/i);if(t)return t[1];let n=e.match(/^(?:[^@]+@)?([^:/]+):/);return n?n[1]:void 0}function $t(e,t){let n=0;for(let r=e.indexOf("{",t);r<e.length;r++)if(e[r]==="{")n++;else if(e[r]==="}"&&--n===0)return e.slice(t,r+1);return e.slice(t)}function og(e,t){return e.match(new RegExp(`\\b${t}\\s*=\\s*"([^"]*)"`))?.[1]}var $d=(e,t)=>n=>{let r=e.match(new RegExp(`(?<![\\w.-])${n}\\s*=(?!=)\\s*(.)`));if(!r)return;if(r[1]!=='"')return null;let o=r.index+r[0].length,s="",i=0;for(let a=o;a<e.length;a++){if(e[a]==="\\"){s+=e[++a]??"";continue}if(t&&e[a]==="$"&&e[a+1]==="{"?i++:t&&i>0&&e[a]==="}"&&i--,e[a]==='"'&&i===0)return Gi(t?t(s):s)??null;s+=e[a]}return null},nQ=(e,t)=>n=>{for(let r=t.length-1;r>=0;r--){let o=t[r](n);if(o!==void 0)return o}return e(n)};function rQ(e){let t=[...e.matchAll(/"((?:[^"\\]|\\.)*)"|'([^']*)'|(\S+)/g)].map(r=>r[1]?.replace(/\\(.)/g,"$1")??r[2]??r[3]),n=[];for(let r=0;r<t.length;r++){let o=/^--?backend-config(?:=(.*))?$/.exec(t[r]);if(!o)continue;let s=o[1]??t[++r];s!==void 0&&n.push(s.replace(/^"(.*)"$/,"$1").replace(/^'(.*)'$/,"$1"))}return n}function Tw(e){let t;try{let r=fe(e);if(!r||r.endsWith(".ts"))return[];let o=Mn(r,"utf-8"),s=r.endsWith(".json")?JSON.parse(o):o.trim()===""?{}:Yl(o);t=Mo(s)?.env}catch{return[]}let n=Mo(t)??{};return[n.TF_CLI_ARGS,n.TF_CLI_ARGS_init].flatMap(r=>typeof r=="string"?rQ(r):[])}function oQ(e){try{return Ew(e).filter(t=>t==="backend.hcl"||t==="backend.tfvars"||t.endsWith(".tfbackend")).sort()}catch{return[]}}function sQ(e,t,n){let r={layers:[],files:[],missing:[]},o=Pt(e,t);for(let s of n){let i=s.indexOf("=");if(i>=0){let u=s.slice(0,i).trim(),l=s.slice(i+1);r.layers.push(d=>d===u?Gi(l)??null:void 0);continue}let a=Z7(s)?s:Sw(o,s),c=Ji(xw(e,a))||".";if(!Jr(a)||!JR(a).isFile()){r.missing.push(c);continue}r.files.push(c),r.layers.push($d(Ar(Mn(a,"utf-8"))))}return r}var aD="terragrunt.hcl";function iQ(e,t,n){let r=/^"?\$?\{?\s*find_in_parent_folders\(\s*(?:"([^"]+)")?\s*\)\s*\}?"?$/.exec(n);if(r){let i=r[1]??aD,a=Sw(e);for(let c=_w(Sw(t));;c=_w(c)){if(Jr(Pt(c,i)))return Pt(c,i);if(c===a||_w(c)===c)return}}let o=/^"\$\{\s*get_repo_root\(\s*\)\s*\}\/([^"$]+)"$/.exec(n);if(o)return Pt(e,o[1]);let s=/^"([^"$]+)"$/.exec(n);return s?Sw(t,s[1]):void 0}function cD(e,t){let n=Pt(e,t),r=Pt(n,aD);if(!Jr(r))return;let o=Ar(Mn(r,"utf-8")),s=[];for(let a of o.matchAll(/^\s*include(?:\s+"([^"]*)")?\s*\{/gm)){let c=/(?:^|[{\s])path\s*=\s*(.+?)\s*$/m.exec($t(o,a.index).replace(/^[^{]*\{/,"").replace(/\}$/,""))?.[1],u=c?iQ(e,n,c):void 0;u&&Jr(u)&&s.push({name:a[1]??"",file:u})}let i=[{file:r,text:o,include:s[0]},...s.map(a=>({file:a.file,text:Ar(Mn(a.file,"utf-8")),include:a}))];for(let a of i){let c=/^\s*remote_state\s*=?\s*\{/m.exec(a.text);if(!c)continue;let u=Ji(xw(e,a.file)),l=$t(a.text,c.index),d=h=>{let y=h===void 0?a.include:s.find(w=>w.name===h);return y?Ji(xw(_w(y.file),n))||".":h===void 0?".":void 0},p=h=>h.replace(/\$\{\s*path_relative_to_include\(\s*(?:"([^"]*)")?\s*\)\s*\}/g,(y,w)=>d(w)??y),f=$d(l)("backend");if(f==null)return{why:`the Terragrunt remote_state block in ${u} names its backend with an expression, not a plain string`};let g=/\bconfig\s*=\s*\{/.exec(l);if(!g)return{why:`the Terragrunt remote_state block in ${u} takes its config from an expression, not an object`};let m=qi(f,$d($t(l,g.index).replace(/^[^{]*\{/,""),p),t);return m.ref?{ref:m.ref}:{why:`the Terragrunt remote_state block in ${u}: ${m.why}`}}}var Mo=e=>e&&typeof e=="object"&&!Array.isArray(e)?e:void 0,Rw=e=>Mo(Array.isArray(e)?e[0]:e),qR=e=>t=>{let n=e?.[t]??Rw(e?.workspaces)?.[t];return Gi(n)};function qi(e,t,n){if(e===null)return{why:"its backend is an expression, not a plain string"};let r=gd(e??"local",t,n);return"unresolved"in r?{why:r.unresolved}:{ref:r}}function aQ(e,t,n=(r,o)=>qi(r,o,t)){let r,o={reads:[],unresolved:[],declared:!1};try{r=JSON.parse(e)}catch{return o}let s=Mo(r);for(let i of Array.isArray(s?.terraform)?s.terraform:[s?.terraform]){let a=Object.entries(Mo(Mo(i)?.backend)??{});Mo(i)?.cloud!==void 0&&a.push(["cloud",Mo(i).cloud]);for(let[c,u]of a){let l=c==="cloud"?qi(c,qR(Rw(u)),t):n(c,qR(Rw(u)));o.declared=!0,l.ref?(o.own=l.ref,delete o.ownUnresolved):o.ownUnresolved=l.why}}for(let i of Array.isArray(s?.data)?s.data:[s?.data]){let a=Mo(Mo(i)?.terraform_remote_state)??{};for(let[c,u]of Object.entries(a)){let l=Rw(u),d=Mo(l?.config),p=d===void 0&&l?.config!==void 0?{why:"its config is an expression, not an object"}:qi(Gi(l?.backend),qR(d),t);p.ref?o.reads.push({name:c,...p.ref,repeated:l?.count!==void 0||l?.for_each!==void 0}):o.unresolved.push({name:c,why:p.why})}}return o}function Lo(e,t,n=Tw(e)){let r={reads:[],unresolved:[]},o=!1,s=u=>{o=!0,u.ref?(r.own=u.ref,delete r.ownUnresolved):r.ownUnresolved=u.why},i=n.length?sQ(e,t,n):void 0,a=(u,l)=>{let d=qi(u,i?nQ(l,i.layers):l,t);if(d.ref)return d;let p=(y,w,b)=>y.length===1?w:b,f=i?.files??[],g=i?.missing??[],m=oQ(Pt(e,t)).map(y=>Ji(t==="."?y:`${t}/${y}`)).filter(y=>!f.includes(y)),h=[...f.length?[`the -backend-config ${p(f,"file","files")} ${f.join(", ")} from terragucci.yml's env ${p(f,"does","do")} not supply it either`]:[],...g.length?[`terragucci.yml's env names -backend-config ${g.join(", ")}, which ${p(g,"is","are")} not in the repo`]:[],...m.length?[`${m.join(", ")} ${p(m,"is","are")} beside it, but no -backend-config in terragucci.yml's env names ${p(m,"it","them")}`]:[]];return h.length?{why:`${d.why}; ${h.join("; ")}`}:d};for(let u of gs(Pt(e,t))){if(fs(u)){let d=aQ(Mn(u,"utf-8"),t,a);d.declared&&s(d.own?{ref:d.own}:{why:d.ownUnresolved}),r.reads.push(...d.reads),r.unresolved.push(...d.unresolved);continue}let l=Ar(Mn(u,"utf-8"));for(let d of l.matchAll(/\bbackend\s+"([^"]+)"\s*\{/g))s(a(d[1],$d($t(l,d.index))));for(let d of l.matchAll(/^\s*cloud\s*\{/gm))s(qi("cloud",$d($t(l,d.index)),t));for(let d of l.matchAll(/\bdata\s+"terraform_remote_state"\s+"([^"]+)"\s*\{/g)){let p=$t(l,d.index),f=$d(p),g=qi(f("backend"),f,t);g.ref?r.reads.push({name:d[1],...g.ref,repeated:/^\s*(count|for_each)\s*=/m.test(p)}):r.unresolved.push({name:d[1],why:g.why})}}o||s(qi("local",()=>{},t));let c=cD(e,t);return c?.ref?s(c):c?.why&&(delete r.own,r.ownUnresolved=c.why),r}function ig(e,t){let n=Tw(e),r=new Map(t.map(s=>[s,Lo(e,s,n)])),o=new Map(t.map(s=>[s,[]]));for(let[s,{reads:i}]of r)for(let a of i)for(let[c,{own:u}]of r)c===s||!u||u.key===a.key&&(!u.bucket||!a.bucket||u.bucket===a.bucket)&&o.get(s).push({name:a.name,upstream:c,repeated:a.repeated});return o}function ZR(e,t){let n=Tw(e),r=t.map(s=>[s,Lo(e,s,n)]),o=r.some(([,s])=>s.reads.length>0||s.unresolved.length>0);return r.map(([s,i])=>({root:s,...o&&i.ownUnresolved?{own:i.ownUnresolved}:{},reads:i.unresolved})).filter(s=>s.own!==void 0||s.reads.length>0)}function uD(e,t){return ZR(e,t).flatMap(n=>[...n.own?[`state: ${n.root} keeps its state where the code does not say (${n.own}), so a root that reads it through terraform_remote_state is not ordered after it`]:[],...n.reads.map(r=>`state: ${n.root} reads state through terraform_remote_state "${r.name}" where the code does not say (${r.why}), so it is not ordered after the root that writes it`)])}var oD=e=>`tofu-records/${e}`;function cQ(e,t,n){let r=Pt(e,t),{estate:o}=Go(r),s=[...n.reads];for(let c of gs(r)){if(fs(c))continue;let u=Ar(Mn(c,"utf-8"));for(let l of u.matchAll(/\bdata\s+"terraform_estate_outputs"\s+"([^"]+)"\s*\{/g)){let d=og($t(u,l.index),"estate");d&&s.push({name:l[1],key:oD(d),repeated:!1})}}let i=qi("local",()=>{},t).ref;return{own:o&&(!n.own||i&&pD(n.own,i))?{key:oD(o)}:n.own,reads:s}}function Aw(e,t){let n=Tw(e),r=new Map(t.map(i=>[i,cQ(e,i,Lo(e,i,n))])),o=[...r.values()].flatMap(i=>i.own?[i.own]:[]),s=new Map;for(let[i,{own:a,reads:c}]of r){let u=c.filter(l=>!o.some(d=>d!==a&&pD(d,l))).map(l=>({data:l.name,...l.bucket!==void 0?{bucket:l.bucket}:{},key:l.key}));s.set(i,{...a?{state:lD(a)}:{},external:u})}return s}var lD=e=>({...e.bucket!==void 0?{bucket:e.bucket}:{},key:e.key});function dD(e,t){return new Map(t.map(n=>{let r=cD(e,n)?.ref;return[n,{...r?{state:lD(r)}:{},external:[]}]}))}function Ow(e){for(let t of gs(e)){let n=Mn(t,"utf-8");if(fs(t)){let s;try{s=JSON.parse(n)}catch{continue}let i=s?.terraform;for(let a of Array.isArray(i)?i:[i]){if(!a||typeof a!="object")continue;if(a.cloud!==void 0)return{cloud:!0};let c=a.backend&&typeof a.backend=="object"?a.backend:{},u=Object.keys(c)[0];if(!u)continue;let l=Array.isArray(c[u])?c[u][0]:c[u],d=Object.fromEntries(Object.entries(l??{}).filter(([,p])=>typeof p=="string"));return{type:u,attrs:d}}continue}let r=Ar(n),o=r.match(/\bbackend\s+"([^"]+)"\s*\{/);if(o){let s=$t(r,o.index),i={};for(let a of s.matchAll(/^\s*([A-Za-z_][A-Za-z0-9_]*)\s*=\s*"([^"]*)"/gm))i[a[1]]=a[2];return{type:o[1],attrs:i}}if(/^\s*cloud\s*\{/m.test(r))return{cloud:!0}}}var pD=(e,t)=>e.key===t.key&&(!e.bucket||!t.bucket||e.bucket===t.bucket);function Go(e){let t,n=[],r=Pt(e,"estate.chdf.hcl");Jr(r)&&(t=og(Ar(Mn(r,"utf-8")),"estate"));for(let o of gs(e)){if(fs(o))continue;let s=Ar(Mn(o,"utf-8"));for(let i of s.matchAll(/^\s*terraform\s*\{/gm)){let a=$t(s,i.index),c=a.match(/\blive\s*\{/);c&&!t&&(t=og($t(a,c.index),"estate"))}for(let i of s.matchAll(/\bdata\s+"terraform_estate_outputs"\s+"[^"]+"\s*\{/g)){let a=og($t(s,i.index),"estate");a&&n.push(a)}}return{estate:t,reads:n}}function fD(e){let t=Pt(e,"estate.chdf.hcl");if(Jr(t))return Ar(Mn(t,"utf-8"));for(let n of gs(e)){if(fs(n))continue;let r=Ar(Mn(n,"utf-8"));for(let o of r.matchAll(/^\s*terraform\s*\{/gm)){let s=$t(r,o.index),i=s.match(/\blive\s*\{/);if(i)return $t(s,i.index)}}}function Pw(e){let t=[];for(let n of gs(e)){if(fs(n))continue;let r=Ar(Mn(n,"utf-8"));for(let o of r.matchAll(/\bdata\s+"terraform_estate_outputs"\s+"([^"]+)"\s*\{/g)){let s=og($t(r,o.index),"estate");s&&t.push({name:o[1],estate:s})}}return t}function gD(e){let t=new Map;for(let n of gs(e)){let r=Mn(n,"utf-8");if(fs(n)){try{let s=JSON.parse(r);for(let i of Array.isArray(s.locals)?s.locals:[s.locals])for(let[a,c]of Object.entries(Mo(i)??{}))t.set(a,JSON.stringify(c))}catch{}continue}let o=Ar(r);for(let s of o.matchAll(/^\s*locals\s*\{/gm)){let i=$t(o,s.index),a=i.slice(i.indexOf("{")+1,-1),c=0,u,l=[];for(let d of a.split(`
|
|
71
71
|
`)){let p=c===0?/^\s*([A-Za-z_][\w-]*)\s*=(?!=)/.exec(d):null;p?(u&&t.set(u,l.join(`
|
|
72
72
|
`)),u=p[1],l=[d.slice(p[0].length)]):l.push(d);for(let f of d.replace(/"(?:[^"\\]|\\.)*"/g,'""'))f==="{"||f==="["||f==="("?c++:(f==="}"||f==="]"||f===")")&&c--}u&&t.set(u,l.join(`
|
|
73
73
|
`))}}return t}function ms(e,t){return t.filter(n=>{let r=Pt(e,n);return Jr(Pt(r,"estate.chdf.hcl"))?!0:gs(r).some(o=>{if(fs(o))return!1;let s=Ar(Mn(o,"utf-8"));return[...s.matchAll(/^\s*terraform\s*\{/gm)].some(i=>/\blive\s*\{/.test($t(s,i.index)))})})}function wn(e,t,{estates:n=!0}={}){let r=ig(e,t),o=nD(e,t),s=new Map(t.map(a=>[a,n?Go(Pt(e,a)):{reads:[]}])),i=new Map;for(let[a,c]of s)"estate"in c&&c.estate&&i.set(c.estate,a);return new Map(t.map(a=>{let c=new Set([...r.get(a).map(u=>u.upstream),...o.get(a)??[]]);for(let u of s.get(a).reads){let l=i.get(u);l&&l!==a&&c.add(l)}return[a,c]}))}function Fo(e,t){let n=new Map;if(!e)return n;let r=i=>t.filter(a=>a===Ji(i).replace(/\/+$/,"")||Pe(i,a)),o=[],s=[];for(let[i,a]of Object.entries(e)){let c=r(i);c.length===0&&o.push(i);for(let u of a){let l=r(u);if(l.length===0){o.includes(u)||o.push(u);continue}for(let d of c)for(let p of l){if(p===d){Ji(i)===d&&Ji(u)===d&&!s.includes(d)&&s.push(d);continue}n.has(d)||n.set(d,new Set),n.get(d).add(p)}}}if(o.length)throw new S(`waves.after names ${o.map(i=>JSON.stringify(i)).join(", ")}, which ${o.length===1?"matches":"match"} no root; the roots are ${[...t].sort().join(", ")}`);if(s.length)throw new S(`waves.after puts ${s.join(", ")} after ${s.length===1?"itself":"themselves"}`);return n}function Vs(e,t,n){let r=wn(e,t);for(let[o,s]of Fo(n,t))for(let i of s)r.get(o).add(i);return r}function tc(e,t,n){let r=Fo(n,t),o=Vs(e,t,n),s=[],i=new Set;for(;i.size<t.length;){let a=t.filter(c=>!i.has(c)&&[...o.get(c)].every(u=>i.has(u)));if(a.length===0){let c=t.filter(l=>!i.has(l)),u=uQ(o,c);throw u.some((l,d)=>r.get(l)?.has(u[(d+1)%u.length]))?new S(`waves.after puts these roots in a cycle: ${[...u,u[0]].join(" after ")}`):new Error(`these roots read each other's state in a cycle: ${c.join(", ")}`)}a.forEach(c=>i.add(c)),s.push(a)}return s}function uQ(e,t){let n=new Set(t),r=[],o=t[0];for(;!r.includes(o);)r.push(o),o=[...e.get(o)].find(s=>n.has(s));return r.slice(r.indexOf(o))}function hs(e,t){for(let[n,r]of Object.entries(t.environments??{}))if((r.roots??[]).some(o=>Pe(o,e)))return n}var hD="on-destructive";function XR(e,t){let n=hs(e,t),r={gate:lo(t.gate)??hD,...t.oidc?{oidc:t.oidc}:{},...t.runner!==void 0?{runner:t.runner}:{},...t.parallelism!==void 0?{parallelism:t.parallelism}:{}};if(n===void 0)return r;let o=t.environments[n],s=lQ(t.oidc,o);return{...r,environment:n,...o.gate!==void 0?{gate:lo(o.gate)??r.gate}:{},...s?{oidc:s}:{},...o.runner!==void 0?{runner:o.runner}:{},...o.parallelism!==void 0?{parallelism:o.parallelism}:{},forge_environment:o.forge_environment??n}}function lQ(e,t){let n=t.oidc;if(!n)return e;let r={...e??{}};return n.plan_role&&n.apply_role&&Object.assign(r,{plan_role:n.plan_role,apply_role:n.apply_role}),n.gcp&&e?.gcp&&(r.gcp={...e.gcp,...n.gcp}),n.azure&&e?.azure&&(r.azure={...e.azure,...n.azure}),r}var mD={never:0,"on-destructive":1,always:2};function yD(e,t=hD){let n;for(let r of e)(n===void 0||mD[r]>mD[n])&&(n=r);return n??t}function jw(e,t,n){return!t.environments||e.length===0?n:yD(e.map(r=>{let o=hs(r,t),s=o===void 0?void 0:t.environments[o].gate;return(s!==void 0?lo(s):void 0)??n}),n)}function Iw(e,t){let n=Object.values(e.environments??{}).map(r=>r.gate!==void 0?lo(r.gate):void 0).filter(r=>r!==void 0);return yD([t,...n],t)}function QR(e){return Object.values(e.environments??{}).map(t=>t.gate!==void 0?lo(t.gate):void 0).filter(t=>t!==void 0)}function YR(e,t,n){let r={...e??{}};for(let o of t)o in r||(r[o]=n);return r}function Zs(e,t){let n=Object.values(t??{}).filter(o=>o.oidc&&(o.oidc.plan_role||o.oidc.gcp||o.oidc.azure));if(n.length===0)return e;let r={...e??{}};for(let o of n){let s=o.oidc,i=o.roots??[];s.plan_role&&s.apply_role&&(r.roles=YR(r.roles,i,{plan:s.plan_role,apply:s.apply_role})),s.gcp&&r.gcp&&(r.gcp={...r.gcp,roles:YR(r.gcp.roles,i,{plan:s.gcp.plan_service_account,apply:s.gcp.apply_service_account})}),s.azure&&r.azure&&(r.azure={...r.azure,roles:YR(r.azure.roles,i,{plan:s.azure.plan_client_id,apply:s.azure.apply_client_id})})}return r}function wD(e,t,n="config"){let r=Object.entries(t.environments??{});if(r.length===0)return[];let o=[],s=[["oidc.roles","plan_role",t.oidc?.roles],["oidc.gcp.roles","gcp",t.oidc?.gcp?.roles],["oidc.azure.roles","azure",t.oidc?.azure?.roles]];for(let i of e){let a=r.filter(([,l])=>(l.roots??[]).some(d=>Pe(d,i))).map(([l])=>l);a.length>1&&o.push(`${n}.environments: ${i} is a root of ${a.join(" and ")}; a root belongs to one environment, so narrow their roots globs`);let c=a[0];if(c===void 0)continue;let u=t.environments[c].oidc;if(u)for(let[l,d,p]of s){if(u[d]===void 0||!p)continue;let f=Object.keys(p).find(g=>Pe(g,i));f!==void 0&&o.push(`${n}.environments.${c}.oidc: ${i} takes identities from both environments.${c}.oidc and ${l} (${f}); set them in one place`)}}return o}import{existsSync as rM,readdirSync as oM,readFileSync as sM}from"node:fs";import{join as oc,posix as eM}from"node:path";var dQ=/^v?\d+(\.\d+){0,2}(-[0-9A-Za-z.-]+)?(\+[0-9A-Za-z.-]+)?$/,pQ=/^[a-z0-9]+(?:[.+_-][a-z0-9]+)*:[0-9a-fA-F]{32,}$/;function ag(e){let t=e.trim().replace(/^=\s*/,"");return dQ.test(t)?t:null}function bD(e){let t=e.indexOf("?");if(t<0)return{base:e,params:[]};let n=e.slice(t+1).split("&").filter(r=>r.length>0).map(r=>{let o=r.indexOf("=");return o<0?{key:r,value:void 0,raw:r}:{key:r.slice(0,o),value:fQ(r.slice(o+1)),raw:r}});return{base:e.slice(0,t),params:n}}function fQ(e){try{return decodeURIComponent(e)}catch{return e}}function ex(e,t){return t.length===0?e:`${e}?${t.join("&")}`}function gQ(e){return e.startsWith("./")||e.startsWith("../")}function mQ(e){return e.startsWith("oci://")}function hQ(e){return e.startsWith("git::")||e.startsWith("git@")||/^(github\.com|bitbucket\.org)\//.test(e)||/\.git(\/\/|\?|$)/.test(e)}function Ki(e,t){let n=yQ(e,t),r=n.at==="version"?t:n.pin;return r!=null&&/[$%]\{/.test(r)?{...n,pin:null,unpinned:`the pin "${r}" is an expression, so there is no literal version to move`}:n}function yQ(e,t){if(t!==void 0){let c=ag(t);return c?{module:e,at:"version",pin:c}:{module:e,at:"version",pin:null,unpinned:`version "${t}" is a constraint, not a pin: there is no one version to move`}}let{base:n,params:r}=bD(e),o=c=>ex(n,r.filter(u=>u.key!==c).map(u=>u.raw)),s=c=>r.find(u=>u.key===c);if(mQ(e)){let c=s("tag"),u=s("digest");if(c&&u)return{module:ex(n,r.filter(d=>d.key!=="tag"&&d.key!=="digest").map(d=>d.raw)),at:"source",pin:null,unpinned:"the oci source names both a tag and a digest"};let l=u??c;return!l||!l.value?{module:e,at:"source",pin:null,unpinned:"the oci source names no tag or digest, so it resolves whatever the registry calls latest"}:{module:o(l.key),at:"source",param:l.key,pin:l.value}}let i=s("ref");if(i?.value)return{module:o("ref"),at:"source",param:"ref",pin:i.value};let a=s("version");if(a?.value){let c=ag(a.value);return c?{module:o("version"),at:"source",param:"version",pin:c}:{module:o("version"),at:"source",param:"version",pin:null,unpinned:`version "${a.value}" is a constraint, not a pin: there is no one version to move`}}return gQ(e)?{module:e,at:"source",pin:null,unpinned:"a local path has no version to pin"}:hQ(e)?{module:e,at:"source",pin:null,unpinned:"the git source names no ref, so it follows the default branch"}:{module:e,at:"source",pin:null,unpinned:"the source has no version argument and no pin in its address, so it resolves the newest release"}}function wQ(e,t,n,r){let{base:o,params:s}=bD(e);return ex(o,s.map(i=>i.key===t?`${n}=${r}`:i.raw))}function tx(e,t,n){let r=Ki(e,t);if(r.module!==n.module)return null;if(r.pin===null)return{outcome:"refused",reason:r.unpinned??"no pin"};if(r.pin===n.to)return{outcome:"already",pin:r.pin};if(r.pin!==n.from)return{outcome:"elsewhere",pin:r.pin};if(r.at==="version"){let s=t.lastIndexOf(r.pin);return{outcome:"moved",from:n.from,to:n.to,source:e,version:t.slice(0,s)+n.to+t.slice(s+r.pin.length)}}let o=r.param==="tag"||r.param==="digest"?pQ.test(n.to)?"digest":"tag":r.param;return{outcome:"moved",from:n.from,to:n.to,source:wQ(e,r.param,o,n.to)}}var bQ=/[A-Za-z_]/,vQ=/[A-Za-z0-9_-]/,nc=class extends Error{constructor(t,n){super(`${t}: ${n}`),this.name="HclScanError"}};function $D(e,t,n){let r=t+1,o=!1;for(;r<e.length;){let s=e[r];if(s==="\\"){r+=2;continue}if(s==='"')return{end:r+1,template:o};if(s===`
|
|
@@ -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 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(`
|
|
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(`
|
|
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 eL(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 tL(e,t={}){let n=await Hte(e,t);if(n)throw new S(n);let r=Vx(await eL(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 nL(e,t,n,r={}){let o=await eL(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 rL(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 nL(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 oL(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}`,aL="_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 cL(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:${iL()}:${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]!==iL())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}:${aL}`]);return{parent:r,file:nne(o.status===0?o.stdout:"")}}function uL(e,t,n,r){let o=`${JSON.stringify(n,null,2)}
|
|
129
|
+
`,s=Xs(e,["hash-object","-w","--stdin"],o).stdout.trim(),i=Vte(sL(Yte(),"terragucci-rows-")),a={...process.env,...Qte,GIT_INDEX_FILE:sL(i,"index")};try{t&&Xs(e,["read-tree",t],void 0,a),Xs(e,["update-index","--add","--cacheinfo",`100644,${s},${aL}`],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 lL(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 dL=e=>new Promise(t=>setTimeout(t,e));async function pL(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(uL(e,i,l,`Hold ${o.length} row${o.length===1?"":"s"} for run ${n.run}${p}`))return{ok:!0,tookOver:d};await dL(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 fL(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],uL(e,o,a,`Release ${i.length} row${i.length===1?"":"s"} of run ${n}`))return!0;await dL(200+Math.floor(Math.random()*800))}}catch{}return!1}function gL(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 hL=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)||!hL.has(e.entityType))return;let t=e.props?.comment;return typeof t=="string"&&t!==""?t:void 0}function yL(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 wL(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=hL.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(yL(n.props),t),o=r&&typeof r=="object"?rE(r):void 0;return wL(n,o),o}return rE(mo(n))}function bL(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 vL(e,t){if(e.length===0)return;let n={};for(let[r,o]of e){let s=Ad(yL(Xe(o)?o.props:void 0),t);if(!s)continue;let i=rE(s);wL(o,i),n[zu(r)]=i}return Object.keys(n).length>0?n:void 0}var $L=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,$L.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||$L.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=kL(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=bL(a,o)}return i||(i=vL(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=kL(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=bL(p,o)??(a===0?vL(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 kL(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}: ${mL(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}: ${mL(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)||_L(t).some(n=>!!n&&typeof n=="object"&&Fu.has(n))))}var _L=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=_L(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 mL(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 RL(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:SL(n)};let u=`${c[0]}/${c[1]}`,l=c.slice(2).join("/"),d=o[u];if(!d)return{rewritten:e,warning:SL(n)};let p=d.kind==="mirror"?`${r}/${d.repo}`:d.url,f=l?`${p}/${l}`:p;return{rewritten:a?`${f}@${a}`:f}}function SL(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"]),xL="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[xL];return t===void 0?void 0:{[xL]: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 EL(e,t){return t.labels[e]??e}function Ene(e,t){return typeof e=="string"?EL(e,t):Array.isArray(e)&&e.every(n=>typeof n=="string")?e.map(n=>EL(n,t)):e}function cE(e,t){if(e===null||typeof e!="object")return e;if(HI(e))return TL(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]=RL(o,{actionsRoot:t.actionsRoot}).rewritten;continue}n[r]=cE(o,t)}return n}function TL(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 AL(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,TL(i,a))}return{entities:o,warnings:r}}var rn=Mu("GitLab::CI::Job","gitlab",{});var OL=Lu("GitLab::CI::Image","gitlab");var on=Lu("GitLab::CI::Rule","gitlab");function PL(e){return e.split(`
|
|
136
136
|
`).map(t=>`#${t.trimEnd()?` ${t.trimEnd()}`:""}`).join(`
|
|
137
|
-
`)}function
|
|
137
|
+
`)}function jL(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 IL={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}=jL(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(PL(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}=jL(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?`${PL(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 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}
|
|
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}
|
|
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=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(`
|
|
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 LL(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 JL(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 KL(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(`${KL(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 JL(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:KL(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 ZL,readdirSync as Une,readFileSync as YL,writeFileSync as Bne}from"node:fs";import{join as VL}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(YL(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 ZL(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=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(`
|
|
147
147
|
`)+`
|
|
148
|
-
`}import{existsSync as
|
|
148
|
+
`}import{existsSync as k1,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!k1(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(k1(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 $1(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=$1(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($1(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 _1(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 S1}from"node:fs";import{dirname as Cre,join as R1}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 x1(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=R1(r,`cost-${g}.json`);S1(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:x1(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=R1(e,x1(n));Ire(Cre(o),{recursive:!0}),S1(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 E1(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 T1(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 A1="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="${A1}">`,Uo=`<img class="taco" src="${A1}" width="51" height="31" alt="">`,Bo="h1.brand{display:flex;align-items:center;gap:10px}.taco{image-rendering:pixelated;flex:none}",O1="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 P1(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 j1(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 I1(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 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,`
|
|
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,`
|
|
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 q1(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 J1="policy-override";var vs=J1,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 V1(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 K1=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(K1).includes(K1(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 Z1(e){let t=sa(e.rules),n=V1(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 Y1(e,t){return{mode:t.mode,overriders:t.overriders,...t.mode==="sealed"?{signers:tb(e,t.base)}:{}}}var X1=e=>({pending:e.pending,resolutions:e.resolutions});function Q1(e,t,n,r,o,s){let i=Z1({ledger:X1(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(X1(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 v1(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="${O1}" width="26" height="16" alt=""> Posted by [terragucci](https://intentius.io/terragucci/)</sub>`,qoe=1e6,Joe={github:kE,gitlab:l1,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=q1(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=N1(n,c),f;p.length>0&&(f=Jg(a,s),await aa(e,f,b=>D1(b,p),"its resources",o));let g=P1(n,c),m;g.length>0&&(m=Zg(a,s),await aa(e,m,b=>j1(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.22";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.21-tofu1.13.1":"sha256:8247108a133bb70f78e8e992adf015b74a40a6c33a8ef577b04c17b66c2e6be2","ghcr.io/intentius/terragucci-terraform:0.4.21-tf1.14.9":"sha256:c386fbd9d187c9bb21d3c0bf8134f27be01f01346892989114710b48c430cc7e","ghcr.io/intentius/terragucci-terragrunt:0.4.21-tg1.1.6-tofu1.13.1":"sha256:53461368a47eeb570f38ec5cdb6c9e084d5ac47f591218d41af40bd64d15e269","ghcr.io/intentius/terragucci-choudoufu:0.4.21-choudoufu0.28.0":"sha256:89ec4b705551dd7e17ba899be3dcc2fd8651da6fa4e9c312db72f287c33d7875"};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.22-tofu1.13.1":"sha256:24c43f1c6ef58813bb8a6cfd3417be64beac6c200803828d8290b54b11a67d17","ghcr.io/intentius/terragucci-terraform:0.4.22-tf1.14.9":"sha256:50ade495669d859eb193fc5eea0ba93af721494ecaee62bea407b01c90bb2fcb","ghcr.io/intentius/terragucci-terragrunt:0.4.22-tg1.1.6-tofu1.13.1":"sha256:33340a3b96ab10af6cd37e42a86f4c827842c2fc9ce096e59c41ebd0cf273b10","ghcr.io/intentius/terragucci-choudoufu:0.4.22-choudoufu0.28.0":"sha256:969acd0016430d803e90c4d71392e08bacd80f4be0c6e28fddea0a53ca4c7e1c"};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=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(`
|
|
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(`
|
|
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&&rL(_),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=Y1(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=Q1(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=>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??=
|
|
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??=F1(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(AL(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 OL({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(IL.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(`
|
|
@@ -881,29 +881,29 @@ ${ie}`}),new se({name:"Keep the check report",if:"always()",uses:D,with:{name:Rp
|
|
|
881
881
|
${H$(n,s,t,a,_)}`}),new se({name:"Keep the plan report",if:"always()",uses:D,with:{name:be,path:`${be}/`,"if-no-files-found":"ignore"}})]}),Kt=(q,Se,ze,He,qe)=>new pt({"runs-on":"ubuntu-latest",container:{image:r},needs:q,if:`always() && (needs.${q}.result == 'success' || needs.${q}.result == 'failure') && ${Se}`,permissions:{statuses:"write","pull-requests":"write",...ht},env:{TG_TOKEN:"${{ github.token }}",TG_PLAN_RESULT:`\${{ needs.${q}.result }}`,...He},steps:[new se({name:"Fetch the plan report",uses:pe,"continue-on-error":!0,with:{name:ze,path:be}}),new se({name:"Post the plan note and terragucci/plan",if:"always()",shell:"bash",run:yye(t,{replan:qe,prReview:b,...k?{agentLogin:k}:{}})})]}),zr=Kt("plan",`github.event_name == 'pull_request' && ${Gs}`,be,{TG_SHA:"${{ github.event.pull_request.head.sha }}",TG_PR:"${{ github.event.pull_request.number }}"},!1),Sn=q=>`startsWith(github.event.comment.body, '${q}')`,jo=e.atlantisComments===!0,au=jo?`${Sn("/terragucci apply")} || ${Sn("atlantis apply")}`:Sn("/terragucci apply"),cu=jo?`(${Sn("/terragucci plan")} || ${Sn("atlantis plan")})`:Sn("/terragucci plan"),uu=V?`(${au} || ${Sn("/terragucci lock")} || ${Sn("/terragucci unlock")})`:jo?`(${au})`:au,lu="(startsWith(github.event.comment.body, '/terragucci lock') || startsWith(github.event.comment.body, '/terragucci unlock'))",du=qn&&!V?` && !${lu}`:"",cs="steps.decide.outputs.go == '1'",pu=!!a?.azure,jn=$&&!pu,ff={...p,...O,...N,...ne,...z$(t,e.reports)},_y=new pt({"runs-on":"ubuntu-latest",container:{image:r},if:`(github.event_name == 'issue_comment' && ${jo?`(${Sn("/terragucci")} || ${Sn("atlantis plan")})`:Sn("/terragucci")} && !${uu}${du}${e.agentComment?` && !${oP}`:""}) || ${Hq}`,permissions:{contents:"read",statuses:"write","pull-requests":"write",...$?{"id-token":"write"}:{}},...Po($),concurrency:{group:"terragucci-replan-${{ github.repository }}-${{ github.event.issue.number || github.event.inputs.pr }}","cancel-in-progress":!1},...Object.keys(ff).length?{env:ff}:{},outputs:{go:"${{ steps.decide.outputs.go }}",pr:"${{ steps.decide.outputs.pr }}",sha:"${{ steps.decide.outputs.sha }}",root:"${{ steps.decide.outputs.root }}"},steps:[ge(!1),new se({id:"decide",name:"Read the comment and decide whether it re-plans",shell:"bash",env:{TG_TOKEN:"${{ github.token }}"},run:hye(s,t,e.agentComment!==void 0)}),ge(!0,"refs/pull/${{ steps.decide.outputs.pr }}/head",cs),...Ye(!0,!0,cs,!0),...jn?[new se({if:cs,name:"Trade the job's OIDC token for the plan identities",shell:"bash",run:pye(t,a,g,e.reports?.role)})]:[],new se({if:cs,name:"Re-plan the pull request on request and write the plan report",shell:"bash",env:{TG_PR:"${{ steps.decide.outputs.pr }}",TG_SHA:"${{ steps.decide.outputs.sha }}",TG_ROOT:"${{ steps.decide.outputs.root }}",TG_BASE:"origin/${{ steps.decide.outputs.base }}"},run:pu?`${U$()}
|
|
882
882
|
${fye}
|
|
883
883
|
${H$(n,s,t,a,_,{replan:!0,sweep:!1})}`:`${U$("bash",!0)}
|
|
884
|
-
${H$(n,s,t,a,_,{replan:!0,traded:jn})}`}),new se({name:"Keep the plan report",if:`always() && ${cs}`,uses:D,with:{name:`${be}-replan`,path:`${be}/`,"if-no-files-found":"ignore"}})]}),Sy=Kt("replan","needs.replan.outputs.go == '1'",`${be}-replan`,{TG_PR:"${{ needs.replan.outputs.pr }}",TG_SHA:"${{ needs.replan.outputs.sha }}",TG_ROOT:"${{ needs.replan.outputs.root }}"},!0),Ft=new Map([["workflow",ky],["check",Gt],["plan",_n],["plan-note",zr],["replan",_y],["replan-note",Sy]]);if(vt&&Ft.set("fmt",new pt({"runs-on":"ubuntu-latest",container:{image:r},needs:"check",if:`always() && needs.check.result == 'failure' && github.event_name == 'push' && github.ref != format('refs/heads/{0}', github.event.repository.default_branch)${h.map(q=>` && github.ref != 'refs/heads/${q}'`).join("")}`,permissions:{contents:"write",...qn?{statuses:"write","pull-requests":"write"}:{}},env:{TG_TOKEN:"${{ github.token }}"},steps:[new se({uses:"actions/checkout@v4"}),...M?[new se({name:te,run:M})]:[],new se({name:"Commit the formatting",shell:"bash",run:_q(n,t,c)}),...qn?[new se({name:"Lock the roots the pull request's new head reaches",shell:"bash",run:Eq(s,t,V,!!l)})]:[]]})),ee?Ft.set("apply-comment",new pt({"runs-on":"ubuntu-latest",container:{image:r},if:`github.event_name == 'issue_comment' && ${uu}`,permissions:{contents:"read","pull-requests":"write"},env:{TG_TOKEN:"${{ github.token }}"},steps:as(new se({name:"Answer that a comment applies nothing here",shell:"bash",run:oye(s,t,e.canary)}),!0,!0,void 0,!0)})):Ft.set("apply-comment",new pt({"runs-on":"ubuntu-latest",container:{image:r},if:`github.event_name == 'issue_comment' && ${uu}`,permissions:{contents:kr||V||st||Ot==="resource"?"write":"read",statuses:"write","pull-requests":"write",...V&&t==="github"?{checks:"read"}:{},...v,...$?{"id-token":"write"}:{}},...Po($),...rt?{concurrency:dP(t)}:{},env:{...p,TG_TOKEN:"${{ github.token }}",...O,...ye,...ne},...K?{outputs:{merge:"${{ steps.apply.outputs.merge }}",sha:"${{ steps.apply.outputs.sha }}",waves:"${{ steps.apply.outputs.waves }}"}}:{},steps:[...as(new se({...K?{id:"apply"}:{},name:V?"Apply a pull request on request, from its head before merge or its merge commit after":"Apply a merged pull request on request, from its merge commit",shell:"bash",run:rye(n,s,t,a,mt)}),!0,!0,void 0,!0),new se({name:"Keep the apply report",if:"always()",uses:t==="forgejo"?"actions/upload-artifact@v3":"actions/upload-artifact@v4",with:{name:`${be}-apply-comment`,path:`${be}/`,"if-no-files-found":"ignore"}})]})),K&&Ft.set("pr-merge",new pt({"runs-on":"ubuntu-latest",container:{image:r},needs:"apply-comment",if:"needs.apply-comment.outputs.merge == '1'",permissions:{contents:"write","pull-requests":"write"},env:{TG_TOKEN:"${{ github.token }}",...e.applyMergeTokenEnv?{TG_MERGE_TOKEN:`\${{ secrets.${e.applyMergeTokenEnv} }}`}:{},TG_PR:"${{ github.event.issue.number }}",TG_SHA:"${{ needs.apply-comment.outputs.sha }}",TG_WAVES:"${{ needs.apply-comment.outputs.waves }}"},steps:[new se({uses:"actions/checkout@v4"}),new se({name:"Merge the pull request whose every wave applied",shell:"bash",run:Qhe(t)})]})),b&&Ft.set("approval",new pt({"runs-on":"ubuntu-latest",container:{image:r},if:`github.event_name == 'pull_request_review' && ${Gs}`,permissions:{contents:"read",statuses:"write","pull-requests":"read"},env:{TG_TOKEN:"${{ github.token }}",TG_SHA:"${{ github.event.pull_request.head.sha }}",TG_PR:"${{ github.event.pull_request.number }}"},steps:[new se({name:"Say on the head whether its waiting waves are approved",shell:"bash",run:`terragucci approval-status --forge ${t}${k?` --agent-login ${k}`:""}`})]})),e.agentComment)for(let[q,Se]of tq(t,r,e.agentComment))Ft.set(q,Se);if(qn){let q=V?cu:`(${cu} || ${lu})`;Ft.set("pr-lock",new pt({"runs-on":"ubuntu-latest",container:{image:r},if:`github.event_name == 'pull_request_target' || (github.event_name == 'issue_comment' && ${q})`,permissions:{contents:"write",statuses:"write","pull-requests":"write"},concurrency:{group:"terragucci-lock-${{ github.repository }}-${{ github.event.pull_request.number || github.event.issue.number }}","cancel-in-progress":!1},env:{TG_TOKEN:"${{ github.token }}"},steps:[new se({uses:"actions/checkout@v4",with:{"fetch-depth":0}}),new se({name:"Lock the roots the pull request reaches, or release them",shell:"bash",run:Eq(s,t,V,!!l)})]}))}y&&Ft.set("ephemeral",new pt({"runs-on":"ubuntu-latest",container:{image:r},if:`github.event_name == 'pull_request_target' && ${Gs}`,permissions:{contents:"write","pull-requests":"read",...$?{"id-token":"write"}:{}},...Po($),concurrency:{group:"terragucci-ephemeral-${{ github.repository }}-${{ github.event.pull_request.number }}","cancel-in-progress":!1},env:{...p,TG_TOKEN:"${{ github.token }}",TG_PR:"${{ github.event.pull_request.number }}",TG_SHA:"${{ github.event.pull_request.head.sha }}",TG_ACTION:"${{ github.event.action }}",...O},steps:as(new se({name:"Apply the pull request's copy of the ephemeral roots, or destroy it once it closes",shell:"bash",run:Lhe(t,a,n,w)}),!1,!0)}));let ct=["github.ref == format('refs/heads/{0}', github.event.repository.default_branch)",...h.map(q=>`github.ref == 'refs/heads/${q}'`)],nr=`${E?"github.event_name == 'push' && ":""}${ct.length>1?`(${ct.join(" || ")})`:ct[0]}`;for(let q of $r){if(q.done){Ft.set(q.name,new pt({"runs-on":"ubuntu-latest",container:{image:r},needs:q.needs,if:nr,permissions:{contents:"read",statuses:"write"},env:{TG_TOKEN:"${{ github.token }}",TG_SHA:"${{ github.sha }}"},steps:[new se({name:q.step,shell:"bash",run:q.body})]}));continue}Ft.set(q.name,new pt({"runs-on":"ubuntu-latest",container:{image:r},needs:q.needs.length===0?"check":q.needs.length===1?q.needs[0]:q.needs,if:nr,permissions:{contents:kr||st||Ot==="resource"?"write":"read",statuses:"write","pull-requests":"write",...v,...$?{"id-token":"write"}:{}},...Po($),...q.environment!==void 0?{environment:q.environment}:{},...q.share===void 0&&q.group===void 0&&rt?{concurrency:dP(t)}:{},env:{...p,TG_TOKEN:"${{ github.token }}",TG_SHA:"${{ github.sha }}",TG_BEFORE:"${{ github.event.before }}",TG_BRANCH:"${{ github.event.repository.default_branch }}",...O,...ye,...ne},steps:[...as(new se({name:q.step,shell:"bash",run:q.body}),!0,!1,void 0,!0,q.share!==void 0||q.group!==void 0?new se({name:"Fetch the wave's decision",uses:pe,with:{name:`${hl}-${q.wave}`,path:hl}}):void 0),...q.decides?[new se({name:C?"Hand the decision to the wave's groups":"Hand the decision to the wave's shares",uses:D,with:{name:`${hl}-${q.wave}`,path:`${hl}/`,"if-no-files-found":"error"}})]:[],new se({name:"Keep the apply report",if:"always()",uses:t==="forgejo"?"actions/upload-artifact@v3":"actions/upload-artifact@v4",with:{name:`${be}-${q.name}`,path:`${be}/`,"if-no-files-found":"ignore"}}),...Ce?[new se({name:"Keep the agent input",if:"failure()",uses:t==="forgejo"?"actions/upload-artifact@v3":"actions/upload-artifact@v4",with:{name:`${vl}-${q.name}`,path:`${vl}/`,"if-no-files-found":"ignore"}})]:[]]}))}if(V&&Ft.set("confirm",new pt({"runs-on":"ubuntu-latest",container:{image:r},needs:"check",if:`${E?"github.event_name == 'push' && ":""}github.ref == format('refs/heads/{0}', github.event.repository.default_branch)`,permissions:{contents:"read",statuses:"write",...$?{"id-token":"write"}:{}},...Po($),env:{...p,TG_TOKEN:"${{ github.token }}",TG_SHA:"${{ github.sha }}",...O},steps:[...as(new se({name:`Plan every ${Ze} to confirm the merge applied`,shell:"bash",run:Pq(n,s,t,a,_)}),!0),new se({name:"Keep the plan report",if:"always()",uses:t==="forgejo"?"actions/upload-artifact@v3":"actions/upload-artifact@v4",with:{name:`${be}-confirm`,path:`${be}/`,"if-no-files-found":"ignore"}})]})),is&&Ft.set("tips",new pt({"runs-on":"ubuntu-latest",container:{image:r},needs:de,if:`${E?"github.event_name == 'push' && ":""}github.ref == format('refs/heads/{0}', github.event.repository.default_branch)`,permissions:{contents:"write","pull-requests":"write"},concurrency:{group:"terragucci-tips-${{ github.repository }}","cancel-in-progress":!1},env:{TG_TOKEN:"${{ github.token }}"},steps:[new se({uses:"actions/checkout@v4",with:{"fetch-depth":0}}),...M?[new se({name:te,run:M})]:[],new se({name:"Open a pull request for each tip",shell:"bash",run:Sq(n,t,c,e.synth)})]})),U&&Ft.set("version-bump",new pt({"runs-on":"ubuntu-latest",container:{image:r},needs:de,if:`${E?"github.event_name == 'push' && ":""}github.ref == format('refs/heads/{0}', github.event.repository.default_branch)`,permissions:{contents:"write","pull-requests":"write"},concurrency:{group:"terragucci-version-bump-${{ github.repository }}","cancel-in-progress":!1},env:{TG_TOKEN:"${{ github.token }}",...I},steps:[new se({uses:"actions/checkout@v4",with:{"fetch-depth":0}}),new se({name:"Suggest the next version of each module that changed",shell:"bash",run:Rq(t,c)})]})),e.publish&&Ft.set("publish",new pt({"runs-on":"ubuntu-latest",container:{image:r},needs:de,if:"github.ref == format('refs/heads/{0}', github.event.repository.default_branch)",permissions:{contents:"write"},concurrency:{group:"terragucci-publish-${{ github.repository }}","cancel-in-progress":!1},env:{TERRAGUCCI_REGISTRY_USER:"${{ secrets.TERRAGUCCI_REGISTRY_USER }}",TERRAGUCCI_REGISTRY_PASSWORD:"${{ secrets.TERRAGUCCI_REGISTRY_PASSWORD }}",TERRAGUCCI_REGISTRY_INSECURE:"${{ secrets.TERRAGUCCI_REGISTRY_INSECURE }}",...e.attest?{COSIGN_PRIVATE_KEY:"${{ secrets.COSIGN_PRIVATE_KEY }}",COSIGN_PASSWORD:"${{ secrets.COSIGN_PASSWORD }}"}:{},...e.publishBucket?z$(t,{bucket:e.publishBucket}):{}},steps:[new se({uses:"actions/checkout@v4",with:{"fetch-depth":0}}),...M&&o?[new se({name:`Install ${o.binary} ${o.version}`,run:M})]:[],...e.attest?[new se({name:`Install cosign ${Ud}`,run:F$("cosign",Ud,t)})]:[],new se({name:"Publish the modules that changed",shell:"bash",run:Aq(t)})]})),E&&Ft.set("drift",new pt({"runs-on":"ubuntu-latest",container:{image:r},if:"github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && github.event.inputs.pr == '')",permissions:{contents:_r?"write":"read",issues:"write",..._r?{"pull-requests":"write"}:{},...a?{"id-token":"write"}:{}},...Po(!!a),...e.agentDrift?{outputs:{agent:"${{ steps.drift.outputs.agent }}",issue:"${{ steps.drift.outputs.issue }}"}}:{},env:{...p,TG_TOKEN:"${{ github.token }}",TG_SHA:"${{ github.sha }}",...O,...L,...z$(t,e.reports),...Z},steps:[...as(new se({...e.agentDrift?{id:"drift"}:{},name:`Plan every ${Ze} against what exists, and keep the drift issue`,shell:"bash",run:Oq(n,s,t,a,_,_r,me,e.agentDrift!==void 0)}),!0,!1,Bl),new se({name:"Keep the drift report",if:"always()",uses:t==="forgejo"?"actions/upload-artifact@v3":"actions/upload-artifact@v4",with:{name:`${be}-drift`,path:`${be}/`,"if-no-files-found":"ignore"}}),...Ul?[new se({name:"Keep the agent input",if:"always()",uses:t==="forgejo"?"actions/upload-artifact@v3":"actions/upload-artifact@v4",with:{name:`${vl}-drift`,path:`${vl}/`,"if-no-files-found":"ignore"}})]:[]]})),E&&e.agentDrift)for(let[q,Se]of nq(t,r,e.agentDrift,`${be}-drift`))Ft.set(q,Se);t==="forgejo"&&Dhe(Ft);let rr=q=>(vq(q,e.runner,t,Oi),Iq((t==="forgejo"?Ohe:iE).serialize(q))),Io=[];if(e.resume){let q=new Map([["workflow",new Gu({name:"terragucci resume",on:{schedule:[{cron:kq(e.resume)}],workflow_dispatch:{}},env:A,permissions:{contents:"read"}})],["resume",new pt({"runs-on":"ubuntu-latest",container:{image:r},permissions:{contents:"write",statuses:"write","pull-requests":"write",...v,...$?{"id-token":"write"}:{}},...Po($),...rt?{concurrency:dP(t)}:{},env:{...p,TG_TOKEN:"${{ github.token }}",...O,...ye,...ne},steps:[...as(new se({name:"Apply a waiting wave once its approval stands",shell:"bash",run:aye(n,s,t,a,mt)}),!0,!0,void 0,!0),new se({name:"Keep the apply report",if:"always()",uses:t==="forgejo"?"actions/upload-artifact@v3":"actions/upload-artifact@v4",with:{name:`${be}-resume`,path:`${be}/`,"if-no-files-found":"ignore"}})]})]]);Io.push({path:Mhe[t],content:Tp(r,e.imageFromConfig)+rr(q)})}if(y){let q=new Map([["workflow",new Gu({name:"terragucci ephemeral",on:{schedule:[{cron:kq(y.sweep)}],workflow_dispatch:{}},env:A,permissions:{contents:"read"}})],["sweep",new pt({"runs-on":"ubuntu-latest",container:{image:r},permissions:{contents:"write","pull-requests":"read",...$?{"id-token":"write"}:{}},...Po($),concurrency:{group:"terragucci-ephemeral-sweep-${{ github.repository }}","cancel-in-progress":!1},env:{...p,TG_TOKEN:"${{ github.token }}",...O},steps:as(new se({name:"Destroy the ephemeral copies whose TTL passed or whose pull request closed",shell:"bash",run:$q(t,a,n)}),!1,!0)})]]);Io.push({path:mP[t],content:Tp(r,e.imageFromConfig)+rr(q)})}return e.review&&Io.push({path:Dc[t],content:Tp(r,e.imageFromConfig)+rr(sq(t,r,e.review,{pipelineName:"terragucci",env:A}))}),R&&Io.push({path:gP[t],content:Tp(r,e.imageFromConfig)+rr(Eye(t,r,R,A,c,M?{name:te,run:M}:void 0))}),{path:Xr[t],content:Tp(r,e.imageFromConfig)+rr(Ft)+jq(e.ownJobs,[...Ft.keys()].filter(q=>q!=="workflow"),t),applyJobs:Ge,...Io.length?{extra:Io}:{}}}function Eye(e,t,n,r,o,s){let i=o?`\${{ secrets.${o} }}`:"${{ github.token }}",a=new Gu({name:"terragucci-rollout",on:{schedule:[{cron:n}],workflow_dispatch:{}},env:r,permissions:{contents:"read"},concurrency:{group:"terragucci-rollout-${{ github.repository }}","cancel-in-progress":!1}}),c=new pt({"runs-on":"ubuntu-latest",container:{image:t},permissions:{contents:"write","pull-requests":"write",statuses:"read",...e==="github"?{checks:"read"}:{}},env:{TG_TOKEN:i},steps:[new se({uses:"actions/checkout@v4",with:{"fetch-depth":0,...o?{token:i}:{}}}),...s?[new se(s)]:[],new se({name:"Open the next wave of each rollout whose last wave merged and applied",shell:"bash",run:Cq(e,o)})]});return new Map([["workflow",a],["rollout",c]])}var Z$=e=>e.length<2?e.join(""):`${e.slice(0,-1).join(", ")} and ${e[e.length-1]}`;function Jq(e,t){if(e===void 0)return;let n=typeof e=="string"?{labels:[e]}:Array.isArray(e)?{labels:[...e]}:{group:e.group,labels:[...e.labels??[]]};if(!((t==="github"||t===void 0)&&n.group===void 0&&!n.labels.includes("self-hosted")))return n}function Kq(e,t){if(e.group!==void 0&&t.group!==void 0&&e.group!==t.group)return!1;let n=(r,o)=>r.every(s=>o.includes(s));return n(e.labels,t.labels)||n(t.labels,e.labels)}var V$=e=>[...e.group!==void 0?[`group ${e.group}`]:[],...e.labels.length?[`labels ${e.labels.join(", ")}`]:[]].join(" and ");function Tye(e,t,n){let r=(e??[]).filter(a=>a.environment!==void 0);if(r.length===0)return[];let o=Jq(kl(t,"plan"),n),s=r.map(a=>({name:a.name,at:Jq(a.runner??kl(t,"apply"),n)})),i=[];for(let[a,c]of s.entries())if(c.at){o&&Kq(c.at,o)&&i.push({rule:kx,message:`the apply jobs of environment ${c.name} (${V$(c.at)}) and the plan jobs (${V$(o)}) can run on the same runner, so a pull request's code planned there can leave files or processes for ${c.name}'s apply; give ${c.name}'s apply runners a label no plan job uses, and use runners that start clean for each job`});for(let u of s.slice(a+1))!u.at||!Kq(c.at,u.at)||i.push({rule:kx,message:`the apply jobs of environments ${c.name} (${V$(c.at)}) and ${u.name} (${V$(u.at)}) can run on the same runner, so one environment's apply can leave files or processes for the other's; give each environment's apply runners a label of its own`})}return i}function Aye(e,t){let n=e.oidc;return!n||!t?[]:[...n.apply_role!==void 0&&n.apply_role===t.apply_role?[{what:"oidc.apply_role",id:n.apply_role}]:[],...n.gcp?.apply_service_account!==void 0&&n.gcp.apply_service_account===t.gcp?.apply_service_account?[{what:"oidc.gcp.apply_service_account",id:n.gcp.apply_service_account}]:[],...n.azure?.apply_client_id!==void 0&&n.azure.apply_client_id===t.azure?.apply_client_id?[{what:"oidc.azure.apply_client_id",id:n.azure.apply_client_id}]:[]]}function Oye(e,t){if(!e||e.length<2)return[];let n=new Map;for(let o of e)for(let{what:s,id:i}of Aye(o,t)){let a=n.get(i)??{what:s,names:[],envs:[]};a.names.push(o.name),o.environment!==void 0&&a.envs.push(o.name),n.set(i,a)}let r=[];for(let[o,{what:s,names:i,envs:a}]of n){if(i.length<2||a.length===0)continue;let c=s==="oidc.apply_role"?"apply_role":s==="oidc.gcp.apply_service_account"?"gcp.apply_service_account":"azure.apply_client_id";r.push({rule:iM,message:`the apply jobs of ${i.length===a.length?"environments":"the groups"} ${Z$(i)} all apply as ${o} (${s}), so its trust must accept each of their jobs and no condition keeps one environment's job from another's role; set environments.<name>.oidc.${c} for ${Z$(a)}`})}return r}var Pye=e=>!!(e&&(e.apply_role||e.roles&&Object.keys(e.roles).length>0));function jye(e,t,n){if(n!=="gitlab"||!e||!e.some(s=>s.environment!==void 0))return[];let r=new Map;for(let s of e){if(!Pye(s.oidc))continue;let i=s.audience??Es(t,"apply");r.set(i,[...r.get(i)??[],s])}let o=[];for(let[s,i]of r){let a=i.filter(u=>u.environment!==void 0&&u.audience===void 0).map(u=>u.name);if(i.length<2||a.length===0)continue;let c=i.map(u=>u.name);o.push({rule:aM,message:`on GitLab the apply jobs of ${c.every(u=>u!==Ka)&&i.every(u=>u.environment!==void 0)?"environments":"the groups"} ${Z$(c)} carry the same AWS token audience ${s}, and AWS cannot read GitLab's environment claims, so an apply role's trust cannot tell their jobs apart; set environments.<name>.oidc.audience for ${Z$(a)} and have each apply role trust its environment's audience alone`})}return o}function Vq(e,t,n){return[...Tye(e,t.runner,n),...Oye(e,t.oidc),...jye(e,t.oidc,n)]}var Iye="https://intentius.io/terragucci/terragucci.schema.json",ko={type:"string"},Rl={type:"boolean"},xl={type:"string",pattern:"\\S"},EP={type:"string",pattern:"^[^\\r\\n]*\\S[^\\r\\n]*$"},$n=e=>({type:"string",pattern:e.source}),_i=$n(rd),Y$={type:"array",items:ko},Zq={anyOf:[ko,{const:!1}]},Et=e=>({enum:[...e]}),Si=(e,t)=>({type:"integer",minimum:e,...t===void 0?{}:{maximum:t}}),Wc=e=>({$ref:`#/$defs/${e}`}),Sl=e=>({anyOf:[{type:"null"},e]}),TP={type:"string",pattern:"^[^\\s,;=']+$"},nJ={type:"string",minLength:1,pattern:"^(?!/)(?!(?:.*/)?\\.\\.(?:/|$))"};function Qe(e,t,n={}){let r=e.filter(s=>!(s in t)),o=Object.keys(t).filter(s=>!e.includes(s));if(r.length||o.length)throw new Error(`config schema: keys ${[...r,...o].join(", ")} disagree with config.ts`);return{type:"object",properties:t,additionalProperties:!1,...n}}var AP=(e,t)=>Object.fromEntries(e.map(n=>[n,t(n)])),Cye=e=>[...e].map(t=>/[a-z]/i.test(t)?`[${t.toUpperCase()}${t.toLowerCase()}]`:t).join(""),Yq={type:"string",pattern:`^(?!(?:${ku.map(Cye).join("|")}))(?!(?:${nd.join("|")})$)[A-Za-z_][A-Za-z0-9_]*$`},xP=Qe(["plan","apply"],{plan:{type:"string",minLength:1},apply:{type:"string",minLength:1}},{required:["plan","apply"]}),X$={type:"string",pattern:"^[^@\\s]+@[^@\\s]+$"},Nye=Qe(["plan","apply"],{plan:X$,apply:X$},{required:["plan","apply"]}),Q$={anyOf:[$n(jf),{type:"array",minItems:1,uniqueItems:!0,items:$n(jf)},Qe(["group","labels"],{group:xl,labels:{type:"array",minItems:1,uniqueItems:!0,items:$n(jf)}},{required:["group"]})]},Xq={anyOf:[Rl,Qe(bf,{command:EP,key_secret:_i,max_turns:Si(1),timeout:Si(1)})]},Dye={type:"object",propertyNames:$n(qa),additionalProperties:Sl(Wc("hcl_value"))};function Qq(e){let t={backend:Sl({type:"object",minProperties:1,maxProperties:1,propertyNames:$n(qa),additionalProperties:Dye}),providers:{type:"object",propertyNames:$n($S),additionalProperties:Sl({type:"object",properties:{source:{type:["string","null"]},version:{type:["string","null"]}},propertyNames:$n(qa),additionalProperties:Sl(Wc("hcl_value")),not:{required:["alias"]}})},required_version:Sl(xl),disable_init:{type:["boolean","null"]}};if(!e)return Qe(bu,t);let n={type:"object",additionalProperties:Sl(Wc("generate_level"))};return Qe(yf,{...t,dirs:n,roots:n})}var Mye=Qe(xf,{when:Et(id),merge:Et(LS),merge_token_env:_i,requires:{type:"array",uniqueItems:!0,items:Et(Cn)},resume:Si(5,60),branches:{type:"object",minProperties:1,propertyNames:$n(lR),additionalProperties:{type:"array",minItems:1,items:TP}},point:$n(uR)}),Lye=Qe(Uy,{name:xl,run:xl,before:Et(td),after:Et(td),roots:Y$,on_failure:Et(FS)},{required:["run"],oneOf:[{required:["before"]},{required:["after"]}],if:{properties:{on_failure:{const:"approve"}},required:["on_failure"]},then:{properties:{before:Et(["init","plan"]),after:Et(["init","plan"])}}}),Gye=Qe(Jy,{bucket:ko,dir:nJ,endpoint:ko,prefix:ko,url:{type:"string",pattern:"^https://[^/\\s?#]+/?$"},namespace:$n(_u),namespaces:{type:"object",propertyNames:{pattern:"^[^/]"},additionalProperties:$n(_u)},system:$n(rR),download:Et(["tarball","git-tags","oci"])},{required:["url","namespace"],oneOf:[{required:["bucket"]},{required:["dir"]}],dependentRequired:{endpoint:["bucket"]}}),eJ={type:"string",pattern:"^(?:git-tags$|oci://[^/]+/.)"},Fye=Qe([...qy],{path:ko,publish:{anyOf:[eJ,{type:"array",items:eJ}]},attest:{anyOf:[Rl,Qe(["key"],{key:{type:"string",minLength:1}})]},require:{const:"attested"},trusted:{type:"array",items:Qe(["source","key","ledger"],{source:{type:"string",pattern:"^(oci://[^/]+/.+|(git::)?(https?|ssh)://.+)$"},key:{type:"string",minLength:1},ledger:{type:"string",pattern:"^(https?|ssh|file)://."}},{required:["source","key","ledger"]})},test:Rl,registry:Gye}),zye=Qe(kf,{version:$n(Ja),exclude:Y$,parallelism:Si(1),dependents:Et(CS),credentials:{type:"object",additionalProperties:xP}}),Uye=Qe(Wy,{engine:Et(NS),path:nJ,source:{type:"string",pattern:"^git\\+(https?|file)://.*@"},namespace:{type:"string",pattern:"^[A-Za-z_][A-Za-z0-9_.]*$"},input:Et(DS),override:{type:"array",minItems:1,items:EP}}),tJ={type:"array",minItems:1,items:{type:"string",pattern:YS}},Bye=Qe(_f,{modules:tJ,providers:tJ},{minProperties:1}),Wye=Qe(Hy,{plan_role:{type:"string",minLength:1},apply_role:{type:"string",minLength:1},audience:{anyOf:[ko,Qe(Sf,{plan:{type:"string",minLength:1},apply:{type:"string",minLength:1},drift:{type:"string",minLength:1}})]},roles:{type:"object",minProperties:1,additionalProperties:xP},gcp:Qe(["workload_identity_provider","plan_service_account","apply_service_account","token_url","roles"],{workload_identity_provider:$n(gR),plan_service_account:{type:"string",pattern:"^[^@\\s]+@[^@\\s]+$"},apply_service_account:{type:"string",pattern:"^[^@\\s]+@[^@\\s]+$"},token_url:{type:"string",pattern:"^https://[^\\s/]+/\\S*$"},roles:{type:"object",minProperties:1,additionalProperties:Nye}},{required:["workload_identity_provider","plan_service_account","apply_service_account"]}),azure:Qe(["tenant_id","subscription_id","plan_client_id","apply_client_id","audience","roles"],{tenant_id:{type:"string",minLength:1},subscription_id:{type:"string",minLength:1},plan_client_id:{type:"string",minLength:1},apply_client_id:{type:"string",minLength:1},audience:{type:"string",minLength:1},roles:{type:"object",minProperties:1,additionalProperties:xP}},{required:["tenant_id","subscription_id","plan_client_id","apply_client_id"]})},{minProperties:1,dependentRequired:{plan_role:["apply_role"],apply_role:["plan_role"]},if:{required:["audience"],not:{required:["roles"]}},then:{required:["plan_role","apply_role"]}}),Hye=Qe(Ef,{backend:Et(Af),url:{type:"string",pattern:"^https?://[^/\\s]+"},model:{type:"string",minLength:1,pattern:"^(?![\\s\\S]*(?:^|[-_.])(?:latest|preview)$)"},token_env:_i,thresholds:Qe($u,AP($u,()=>({type:"number",exclusiveMinimum:0,maximum:1})))},{required:["backend"],allOf:[{if:{properties:{backend:{const:"jev"}}},then:{required:["token_env"]},else:{required:["url"]}},{if:{properties:{backend:{const:"laya"}}},else:{required:["model"]}}]}),qye={anyOf:[Rl,Qe(vu,AP(vu,e=>US.includes(e)?$n(fR):e==="dir"?{type:"string",pattern:"^(?!/)(?!(?:.*/)?\\.\\.(?:/|$))[^\\r\\n]+$"}:{type:"string",pattern:"^[^\\r\\n]+$"}))]},Jye=Qe(Rf,{roots:{type:"array",minItems:1,items:TP},ttl:{type:"string",pattern:"^[1-9]\\d*[mhd]$"},sweep:Si(5,60)},{required:["roots"]}),Kye=Qe(["plan_role","apply_role","audience","gcp","azure"],{plan_role:{type:"string",minLength:1},apply_role:{type:"string",minLength:1},audience:{type:"string",minLength:1,pattern:"\\S"},gcp:Qe(["plan_service_account","apply_service_account"],{plan_service_account:X$,apply_service_account:X$},{required:["plan_service_account","apply_service_account"]}),azure:Qe(["plan_client_id","apply_client_id"],{plan_client_id:{type:"string",minLength:1},apply_client_id:{type:"string",minLength:1}},{required:["plan_client_id","apply_client_id"]})},{minProperties:1,dependentRequired:{plan_role:["apply_role"],apply_role:["plan_role"]}});function Vye(e){return Qe(["roots","gate","oidc","runner","parallelism","forge_environment"],{roots:{type:"array",minItems:1,items:TP},gate:Et(jS),oidc:Kye,runner:{anyOf:[Q$,Qe(["apply"],{apply:Q$},{required:["apply"]})]},parallelism:Si(1),forge_environment:$n(hR)},e?{required:["roots"]}:{})}function rJ(e){return{roots:{...Y$,description:"Globs of root directories. Detected when absent."},binary:{...Et(od),description:"The binary the pipeline runs. Detected when absent."},version:e?{anyOf:[ko,{type:"object",additionalProperties:$n(Ja)}],description:"The binary's release, or a map of root glob to release."}:{...ko,description:"The binary's release."},generate:{...Wc("generate"),description:"Backend, provider and version files terragucci generate writes for each plain root."},forge:{...Et(Wr),description:"The forge, for a host terragucci cannot name."},url:{...ko,description:"Where the project lives, for a forge not on https or the default port."},gate:{...Et(jS),description:"When a wave waits for an approval. on-destroy is an older name for on-destructive."},approval:{anyOf:[Et(Do),{type:"object",additionalProperties:!1,properties:{mode:{...Et(Do),description:"What counts as a waiting wave's approval."},require_signed:{type:"boolean",description:"Refuse an unsigned approval on the ledger, so every ledger approval is tied to the principal who signed it."}}}],description:"What counts as a waiting wave's approval: the mode, or a map with the mode and require_signed."},apply:{...Mye,description:"When a change applies."},locks:{...Et(MS),description:"When a pull request takes its root locks."},waves:{type:"object",properties:{canary:Y$,jobs:Si(1),after:{type:"object",additionalProperties:{type:"array",minItems:1,items:{type:"string",minLength:1}}},compose:{type:"boolean"},compose_max:Si(2)},description:"The canary wave, the jobs one wave spreads across, extra apply order, and whether a change's roots apply as one composed run."},drift:{...Zq,description:"A cron schedule for tf-drift, or false."},synth:{...xl,description:"The command that writes the roots before any job reads them."},steps:{type:"array",items:Lye,description:"Commands run before or after a root's init, plan, apply and drift."},image:{type:"string",pattern:"^[^\\s]+$",description:"The image every job runs in, built FROM terragucci's."},notify:{...Qe(ZS,{slack:_i,teams:_i,webhook:_i,webhook_key:_i,relay:{type:"string",pattern:"^[A-Za-z0-9][A-Za-z0-9 ._-]{0,63}$"}},{minProperties:1,dependentRequired:{webhook:["webhook_key"],webhook_key:["webhook"]},if:{required:["relay"]},then:{anyOf:[{required:["slack"]},{required:["teams"]}]}}),description:"The secrets holding Slack, Teams or webhook addresses, and the relay's name."},cost:{anyOf:[{const:!0},Qe(eR,{key_secret:_i,command:xl,approve_above:{type:"number",minimum:0}})],description:"Cost estimates per root in the plan note."},comments:{...Zq,description:"GitLab only: the cron of the comments schedule, or false."},rollouts:e?{anyOf:[xl,{const:!1}],description:"A cron schedule for the job that opens each rollout's next wave, or false."}:{const:!1,description:"A single repo's key; a control repo runs terragucci respond rollout itself."},gitlab:{...Qe(QS,{token:Et(GS)}),description:"GitLab only: how the project keeps its forge token."},runtime:{...Et(IS),description:"Where the stages run."},reports:{type:"object",properties:{bucket:ko,endpoint:{},prefix:{},url:{type:"string",pattern:"^https?://[^\\s?#]+$"},role:{type:"string",pattern:"^arn:aws[\\w-]*:iam::\\d{12}:role/\\S+$"}},required:["bucket"],description:"A bucket for plan reports: s3://, gs:// or az://."},token_env:{...ko,description:"The environment variable holding the forge token."},env:{type:"object",additionalProperties:ko,description:"Environment variables every job gets. Values only, never secrets."},runner:{anyOf:[Q$,{type:"object",minProperties:1,not:{required:["group"]},propertyNames:Et($f),additionalProperties:Q$}],description:"The runner each job runs on: a label, a list of labels, a GitHub runner group, or one per stage."},pass:{...Qe(tR,{secrets:{type:"array",minItems:1,uniqueItems:!0,items:Yq},vars:{type:"array",minItems:1,uniqueItems:!0,items:Yq}},{minProperties:1}),description:"Names of CI secrets and variables the plan, apply and drift jobs get."},telemetry:{...Qe(XS,{headers_secret:_i,trace_url:{type:"string",pattern:"^https?://\\S*\\{trace_id\\}\\S*$"}},{minProperties:1}),description:"The secret holding OTLP headers, and a trace link with {trace_id}."},tips:{...Rl,description:"Tips in the plan note."},modules:{...Fye,description:"Module publishing, attestation and the module registry."},oidc:{...Wye,description:"Cloud identities the jobs take over OIDC: a read-only one for plan, a write one for apply."},parallelism:{...Si(1),description:"How many roots of one dependency layer plan at once."},terragrunt:{...zye,description:"Terragrunt settings."},atmos:{...Qe(Tf,{version:{type:"string",pattern:"^\\d+\\.\\d+\\.\\d+(-[0-9A-Za-z.]+)?$"}}),description:"Atmos settings."},policy:{...Uye,description:"Policy checks over each plan."},sources:{...Bye,description:"Globs of the module sources and provider addresses a root may use, checked before init."},atlantis_comments:{...Rl,description:"Read atlantis plan and atlantis apply comments as terragucci's."},respond:{...Qe(Object.keys(Us),AP(Object.keys(Us),t=>Et(Us[t]))),description:"The response to each pipeline event."},agent:{...Qe(nR,{via:Et(zS),token_env:{type:"string",minLength:1},login:{type:"string",pattern:Cf.source,description:"The forge login agent.token_env's token acts as. No approval by it counts."},comment:Xq,drift:Xq},{required:["via","token_env"]}),description:"The coding agent behind /terragucci agent and the drift agent."},review:{...Qe(vf,{agent:Rl,command:EP,key_secret:_i,instructions:{type:"string",pattern:"^(?:\\./)?(?!/)[A-Za-z0-9_./-]+$"},timeout:Si(1)},{anyOf:[{maxProperties:0},{required:["agent"]}]}),description:"A model's review of each pull request's intent against its plan."},decide:{...Hye,description:"The typed-decision service."},audit_region:{type:"string",pattern:"^[a-z]{2}(-[a-z]+)+-\\d+$",description:"The AWS region whose CloudTrail drift attribution reads."},dashboards:{...qye,description:"Dashboards and alert rules written next to the pipeline."},own_jobs:{anyOf:[{type:"string",pattern:"^(?!/)(?!(?:.*/)?\\.\\.(?:/|$)).*\\.ya?ml$"},{type:"object",minProperties:1,propertyNames:$n(aR),additionalProperties:{type:"object",minProperties:1}}],description:"Jobs of your own, in the forge's syntax, or the path of a YAML file holding them."},ephemeral:{...Jye,description:"Roots each pull request gets a copy of."},environments:{type:"object",minProperties:1,propertyNames:{...$n(WS),not:{anyOf:[{enum:["default","decide"]},{pattern:"^share-[0-9]+$"}]}},additionalProperties:Vye(e),description:"Named environments: each one's root globs, and its gate, identities, apply runner, parallelism and forge environment."}}}function Zye(){return{type:"object",properties:rJ(!1),additionalProperties:!1}}function Yye(){let e=rJ(!0);return{$schema:"https://json-schema.org/draft/2020-12/schema",$id:Iye,title:"terragucci.yml",description:"One repo's settings, or a control repo's defaults and projects. https://intentius.io/terragucci/reference/config/",type:["object","null"],properties:{...e,defaults:{...Wc("project"),not:{required:["url"]},description:"Settings every project takes, under its own."},projects:{type:"object",propertyNames:{pattern:"^(?:https?://)?[^/]+(?:/[^/]+){2,}/*$"},additionalProperties:Sl(Wc("project")),description:"<host>/<owner>/<name> to that project's settings."}},additionalProperties:!1,dependentRequired:{defaults:["projects"]},if:{required:["projects"]},then:{propertyNames:Et(["defaults","projects"])},$defs:{project:Zye(),generate:Qq(!0),generate_level:Qq(!1),hcl_value:{anyOf:[{type:["string","number","boolean"]},{type:"array",items:Wc("hcl_value")},{type:"object",additionalProperties:Wc("hcl_value")}]}}}}var Bc=e=>e!==null&&typeof e=="object"&&!Array.isArray(e);function Xye(e){return e===null?"null":Array.isArray(e)?"array":typeof e}function Qye(e,t){return t==="integer"?Number.isInteger(e):Xye(e)===t}var gh=e=>JSON.stringify(e,(t,n)=>Bc(n)?Object.fromEntries(Object.keys(n).sort().map(r=>[r,n[r]])):n);function _l(e,t,n=e,r="config"){let o=[],s=e;if(typeof s.$ref=="string"){let a=n.$defs[s.$ref.replace("#/$defs/","")];if(!a)throw new Error(`config schema: no ${s.$ref}`);o.push(..._l(a,t,n,r))}if(s.type!==void 0){let a=Array.isArray(s.type)?s.type:[s.type];if(!a.some(c=>Qye(t,c)))return[...o,`${r}: must be ${a.join(" or ")}`]}if("const"in s&&gh(t)!==gh(s.const)&&o.push(`${r}: must be ${JSON.stringify(s.const)}`),Array.isArray(s.enum)&&!s.enum.some(a=>gh(a)===gh(t))&&o.push(`${r}: must be one of ${s.enum.join(", ")}`),typeof t=="string"&&(typeof s.minLength=="number"&&[...t].length<s.minLength&&o.push(`${r}: must not be empty`),typeof s.pattern=="string"&&!new RegExp(s.pattern,"u").test(t)&&o.push(`${r}: does not match ${s.pattern}`)),typeof t=="number"&&(typeof s.minimum=="number"&&t<s.minimum&&o.push(`${r}: must be ${s.minimum} or more`),typeof s.maximum=="number"&&t>s.maximum&&o.push(`${r}: must be ${s.maximum} or less`),typeof s.exclusiveMinimum=="number"&&t<=s.exclusiveMinimum&&o.push(`${r}: must be more than ${s.exclusiveMinimum}`)),Array.isArray(t)&&(typeof s.minItems=="number"&&t.length<s.minItems&&o.push(`${r}: must list at least ${s.minItems}`),s.uniqueItems===!0&&new Set(t.map(gh)).size!==t.length&&o.push(`${r}: lists an item twice`),Bc(s.items)&&t.forEach((a,c)=>o.push(..._l(s.items,a,n,`${r}[${c}]`)))),Bc(t)){let a=Object.keys(t),c=s.properties??{};typeof s.minProperties=="number"&&a.length<s.minProperties&&o.push(`${r}: must set at least ${s.minProperties} key(s)`),typeof s.maxProperties=="number"&&a.length>s.maxProperties&&o.push(`${r}: must set at most ${s.maxProperties} key(s)`);for(let u of s.required??[])u in t||o.push(`${r}.${u}: is required`);for(let[u,l]of Object.entries(s.dependentRequired??{}))if(u in t)for(let d of l)d in t||o.push(`${r}.${d}: is required with ${u}`);for(let u of a){let l=`${r}.${u}`;if(Bc(s.propertyNames))for(let d of _l(s.propertyNames,u,n,l))o.push(`${l}: is not a key here (${d.slice(l.length+2)})`);u in c?o.push(..._l(c[u],t[u],n,l)):s.additionalProperties===!1?o.push(`${l}: is not a setting`):Bc(s.additionalProperties)&&o.push(..._l(s.additionalProperties,t[u],n,l))}}let i=a=>_l(a,t,n,r);for(let a of s.allOf??[])o.push(...i(a));if(Array.isArray(s.anyOf)){let a=s.anyOf.map(i);a.some(c=>c.length===0)||o.push(...a.reduce((c,u)=>u.length<c.length?u:c))}if(Array.isArray(s.oneOf)&&s.oneOf.filter(c=>i(c).length===0).length!==1&&o.push(`${r}: must match exactly one of ${s.oneOf.map(c=>JSON.stringify(c)).join(", ")}`),Bc(s.not)&&i(s.not).length===0&&o.push(`${r}: must not match ${JSON.stringify(s.not)}`),Bc(s.if)){let a=i(s.if).length===0?s.then:s.else;Bc(a)&&o.push(...i(a))}return o}function oJ(e){return _l(Yye(),e)}var ewe=1440,twe=30,nwe=50,sJ="terragucci/apply",iJ=e=>`<!-- terragucci:note=${e} -->`,rwe=/<!-- terragucci:note=(\d+) -->/g,Ts=e=>e.slice(0,8);function owe(e){let t=e.CI_API_V4_URL,n=e.CI_PROJECT_ID,r=e.TG_TOKEN,o=e.CI_DEFAULT_BRANCH;if(!t||!n||!r||!o)throw new S("comment --forge gitlab --poll needs CI_API_V4_URL, CI_PROJECT_ID, CI_DEFAULT_BRANCH and TG_TOKEN in the environment");return{t:{forge:"gitlab",origin:e.CI_SERVER_URL??t,path:n,token:r,api:t},base:o}}async function aJ(e){let t=e.env??process.env,n=e.fetch??fetch,r=e.now??new Date,{t:o,base:s}=owe(t),i=encodeURIComponent(o.path),a=(y,w,b)=>kt(n,o,y,w,b),c=new Date(r.getTime()-ewe*6e4).toISOString(),u,l,d;try{let y=await a("GET","/user");u=Number(y?.id),l=String(y?.username??""),d=await a("GET",`/projects/${i}/merge_requests?state=all&updated_after=${encodeURIComponent(c)}&order_by=updated_at&sort=desc&per_page=100`)}catch(y){return{outcomes:[],fail:`could not list the merge requests (${y.message})`}}if(!Array.isArray(d))return{outcomes:[],fail:"GitLab answered the merge request list with something other than a list"};let p=[],f=[],g=[],m=jt("gitlab",t,n),h={api:a,id:i,me:u,meName:l,forge:m,apiUrl:o.api,token:o.token,fetch:n,reportDir:e.reportDir??"terragucci-report"};for(let y of d){let w=y?.iid;if(!Number.isInteger(w)||w<1)continue;let b;try{b=await a("GET",`/projects/${i}/merge_requests/${w}/notes?order_by=created_at&sort=desc&per_page=100`)}catch(_){return{outcomes:p,plans:f,reviews:g,fail:`could not read the notes of !${w} (${_.message})`}}if(!Array.isArray(b))continue;let k=e.planNotes?await _W(h,y,b):void 0;k&&f.push(k);let v=new Set;for(let _ of b)if(!(_?.author?.id!==u||typeof _?.body!="string"))for(let E of _.body.matchAll(rwe))v.add(Number(E[1]));let $=b.filter(_=>Number.isInteger(_?.id)&&_.system!==!0&&!v.has(_.id)).filter(_=>typeof _.created_at=="string"&&_.created_at>=c).filter(_=>Cr(_.body,$s(t))!==void 0).sort((_,E)=>_.created_at<E.created_at?-1:_.created_at>E.created_at?1:_.id-E.id);for(let _ of $)p.push(await swe({api:a,forge:m,meName:l,id:i,base:s,layers:e.layers,mr:y,note:_,env:t,fetch:n,...e.when?{when:e.when}:{},...e.requires?{requires:e.requires}:{},...e.wait?{wait:e.wait}:{},...e.agent?{agent:!0}:{}}));if(e.review){let _=await qW({api:a,id:i,me:u,meName:l,base:s,env:t,fetch:n},y,b);_&&g.push(_)}}return{outcomes:p,plans:f,reviews:g}}async function swe(e){let{api:t,id:n,base:r,layers:o,mr:s,note:i}=e,a=e.when==="pull-request",c=s.iid,u={mr:c,note:i.id},l=async(O,I=!1)=>{try{await e.forge.upsertComment(c,iJ(i.id),`terragucci: ${O}
|
|
884
|
+
${H$(n,s,t,a,_,{replan:!0,traded:jn})}`}),new se({name:"Keep the plan report",if:`always() && ${cs}`,uses:D,with:{name:`${be}-replan`,path:`${be}/`,"if-no-files-found":"ignore"}})]}),Sy=Kt("replan","needs.replan.outputs.go == '1'",`${be}-replan`,{TG_PR:"${{ needs.replan.outputs.pr }}",TG_SHA:"${{ needs.replan.outputs.sha }}",TG_ROOT:"${{ needs.replan.outputs.root }}"},!0),Ft=new Map([["workflow",ky],["check",Gt],["plan",_n],["plan-note",zr],["replan",_y],["replan-note",Sy]]);if(vt&&Ft.set("fmt",new pt({"runs-on":"ubuntu-latest",container:{image:r},needs:"check",if:`always() && needs.check.result == 'failure' && github.event_name == 'push' && github.ref != format('refs/heads/{0}', github.event.repository.default_branch)${h.map(q=>` && github.ref != 'refs/heads/${q}'`).join("")}`,permissions:{contents:"write",...qn?{statuses:"write","pull-requests":"write"}:{}},env:{TG_TOKEN:"${{ github.token }}"},steps:[new se({uses:"actions/checkout@v4"}),...M?[new se({name:te,run:M})]:[],new se({name:"Commit the formatting",shell:"bash",run:_q(n,t,c)}),...qn?[new se({name:"Lock the roots the pull request's new head reaches",shell:"bash",run:Eq(s,t,V,!!l)})]:[]]})),ee?Ft.set("apply-comment",new pt({"runs-on":"ubuntu-latest",container:{image:r},if:`github.event_name == 'issue_comment' && ${uu}`,permissions:{contents:"read","pull-requests":"write"},env:{TG_TOKEN:"${{ github.token }}"},steps:as(new se({name:"Answer that a comment applies nothing here",shell:"bash",run:oye(s,t,e.canary)}),!0,!0,void 0,!0)})):Ft.set("apply-comment",new pt({"runs-on":"ubuntu-latest",container:{image:r},if:`github.event_name == 'issue_comment' && ${uu}`,permissions:{contents:kr||V||st||Ot==="resource"?"write":"read",statuses:"write","pull-requests":"write",...V&&t==="github"?{checks:"read"}:{},...v,...$?{"id-token":"write"}:{}},...Po($),...rt?{concurrency:dP(t)}:{},env:{...p,TG_TOKEN:"${{ github.token }}",...O,...ye,...ne},...K?{outputs:{merge:"${{ steps.apply.outputs.merge }}",sha:"${{ steps.apply.outputs.sha }}",waves:"${{ steps.apply.outputs.waves }}"}}:{},steps:[...as(new se({...K?{id:"apply"}:{},name:V?"Apply a pull request on request, from its head before merge or its merge commit after":"Apply a merged pull request on request, from its merge commit",shell:"bash",run:rye(n,s,t,a,mt)}),!0,!0,void 0,!0),new se({name:"Keep the apply report",if:"always()",uses:t==="forgejo"?"actions/upload-artifact@v3":"actions/upload-artifact@v4",with:{name:`${be}-apply-comment`,path:`${be}/`,"if-no-files-found":"ignore"}})]})),K&&Ft.set("pr-merge",new pt({"runs-on":"ubuntu-latest",container:{image:r},needs:"apply-comment",if:"needs.apply-comment.outputs.merge == '1'",permissions:{contents:"write","pull-requests":"write"},env:{TG_TOKEN:"${{ github.token }}",...e.applyMergeTokenEnv?{TG_MERGE_TOKEN:`\${{ secrets.${e.applyMergeTokenEnv} }}`}:{},TG_PR:"${{ github.event.issue.number }}",TG_SHA:"${{ needs.apply-comment.outputs.sha }}",TG_WAVES:"${{ needs.apply-comment.outputs.waves }}"},steps:[new se({uses:"actions/checkout@v4"}),new se({name:"Merge the pull request whose every wave applied",shell:"bash",run:Qhe(t)})]})),b&&Ft.set("approval",new pt({"runs-on":"ubuntu-latest",container:{image:r},if:`github.event_name == 'pull_request_review' && ${Gs}`,permissions:{contents:"read",statuses:"write","pull-requests":"read"},env:{TG_TOKEN:"${{ github.token }}",TG_SHA:"${{ github.event.pull_request.head.sha }}",TG_PR:"${{ github.event.pull_request.number }}"},steps:[new se({name:"Say on the head whether its waiting waves are approved",shell:"bash",run:`terragucci approval-status --forge ${t}${k?` --agent-login ${k}`:""}`})]})),e.agentComment)for(let[q,Se]of tq(t,r,e.agentComment))Ft.set(q,Se);if(qn){let q=V?cu:`(${cu} || ${lu})`;Ft.set("pr-lock",new pt({"runs-on":"ubuntu-latest",container:{image:r},if:`github.event_name == 'pull_request_target' || (github.event_name == 'issue_comment' && ${q})`,permissions:{contents:"write",statuses:"write","pull-requests":"write"},concurrency:{group:"terragucci-lock-${{ github.repository }}-${{ github.event.pull_request.number || github.event.issue.number }}","cancel-in-progress":!1},env:{TG_TOKEN:"${{ github.token }}"},steps:[new se({uses:"actions/checkout@v4",with:{"fetch-depth":0}}),new se({name:"Lock the roots the pull request reaches, or release them",shell:"bash",run:Eq(s,t,V,!!l)})]}))}y&&Ft.set("ephemeral",new pt({"runs-on":"ubuntu-latest",container:{image:r},if:`github.event_name == 'pull_request_target' && ${Gs}`,permissions:{contents:"write","pull-requests":"read",...$?{"id-token":"write"}:{}},...Po($),concurrency:{group:"terragucci-ephemeral-${{ github.repository }}-${{ github.event.pull_request.number }}","cancel-in-progress":!1},env:{...p,TG_TOKEN:"${{ github.token }}",TG_PR:"${{ github.event.pull_request.number }}",TG_SHA:"${{ github.event.pull_request.head.sha }}",TG_ACTION:"${{ github.event.action }}",...O},steps:as(new se({name:"Apply the pull request's copy of the ephemeral roots, or destroy it once it closes",shell:"bash",run:Lhe(t,a,n,w)}),!1,!0)}));let ct=["github.ref == format('refs/heads/{0}', github.event.repository.default_branch)",...h.map(q=>`github.ref == 'refs/heads/${q}'`)],nr=`${E?"github.event_name == 'push' && ":""}${ct.length>1?`(${ct.join(" || ")})`:ct[0]}`;for(let q of $r){if(q.done){Ft.set(q.name,new pt({"runs-on":"ubuntu-latest",container:{image:r},needs:q.needs,if:nr,permissions:{contents:"read",statuses:"write"},env:{TG_TOKEN:"${{ github.token }}",TG_SHA:"${{ github.sha }}"},steps:[new se({name:q.step,shell:"bash",run:q.body})]}));continue}Ft.set(q.name,new pt({"runs-on":"ubuntu-latest",container:{image:r},needs:q.needs.length===0?"check":q.needs.length===1?q.needs[0]:q.needs,if:nr,permissions:{contents:kr||st||Ot==="resource"?"write":"read",statuses:"write","pull-requests":"write",...v,...$?{"id-token":"write"}:{}},...Po($),...q.environment!==void 0?{environment:q.environment}:{},...q.share===void 0&&q.group===void 0&&rt?{concurrency:dP(t)}:{},env:{...p,TG_TOKEN:"${{ github.token }}",TG_SHA:"${{ github.sha }}",TG_BEFORE:"${{ github.event.before }}",TG_BRANCH:"${{ github.event.repository.default_branch }}",...O,...ye,...ne},steps:[...as(new se({name:q.step,shell:"bash",run:q.body}),!0,!1,void 0,!0,q.share!==void 0||q.group!==void 0?new se({name:"Fetch the wave's decision",uses:pe,with:{name:`${hl}-${q.wave}`,path:hl}}):void 0),...q.decides?[new se({name:C?"Hand the decision to the wave's groups":"Hand the decision to the wave's shares",uses:D,with:{name:`${hl}-${q.wave}`,path:`${hl}/`,"if-no-files-found":"error"}})]:[],new se({name:"Keep the apply report",if:"always()",uses:t==="forgejo"?"actions/upload-artifact@v3":"actions/upload-artifact@v4",with:{name:`${be}-${q.name}`,path:`${be}/`,"if-no-files-found":"ignore"}}),...Ce?[new se({name:"Keep the agent input",if:"failure()",uses:t==="forgejo"?"actions/upload-artifact@v3":"actions/upload-artifact@v4",with:{name:`${vl}-${q.name}`,path:`${vl}/`,"if-no-files-found":"ignore"}})]:[]]}))}if(V&&Ft.set("confirm",new pt({"runs-on":"ubuntu-latest",container:{image:r},needs:"check",if:`${E?"github.event_name == 'push' && ":""}github.ref == format('refs/heads/{0}', github.event.repository.default_branch)`,permissions:{contents:"read",statuses:"write",...$?{"id-token":"write"}:{}},...Po($),env:{...p,TG_TOKEN:"${{ github.token }}",TG_SHA:"${{ github.sha }}",...O},steps:[...as(new se({name:`Plan every ${Ze} to confirm the merge applied`,shell:"bash",run:Pq(n,s,t,a,_)}),!0),new se({name:"Keep the plan report",if:"always()",uses:t==="forgejo"?"actions/upload-artifact@v3":"actions/upload-artifact@v4",with:{name:`${be}-confirm`,path:`${be}/`,"if-no-files-found":"ignore"}})]})),is&&Ft.set("tips",new pt({"runs-on":"ubuntu-latest",container:{image:r},needs:de,if:`${E?"github.event_name == 'push' && ":""}github.ref == format('refs/heads/{0}', github.event.repository.default_branch)`,permissions:{contents:"write","pull-requests":"write"},concurrency:{group:"terragucci-tips-${{ github.repository }}","cancel-in-progress":!1},env:{TG_TOKEN:"${{ github.token }}"},steps:[new se({uses:"actions/checkout@v4",with:{"fetch-depth":0}}),...M?[new se({name:te,run:M})]:[],new se({name:"Open a pull request for each tip",shell:"bash",run:Sq(n,t,c,e.synth)})]})),U&&Ft.set("version-bump",new pt({"runs-on":"ubuntu-latest",container:{image:r},needs:de,if:`${E?"github.event_name == 'push' && ":""}github.ref == format('refs/heads/{0}', github.event.repository.default_branch)`,permissions:{contents:"write","pull-requests":"write"},concurrency:{group:"terragucci-version-bump-${{ github.repository }}","cancel-in-progress":!1},env:{TG_TOKEN:"${{ github.token }}",...I},steps:[new se({uses:"actions/checkout@v4",with:{"fetch-depth":0}}),new se({name:"Suggest the next version of each module that changed",shell:"bash",run:Rq(t,c)})]})),e.publish&&Ft.set("publish",new pt({"runs-on":"ubuntu-latest",container:{image:r},needs:de,if:"github.ref == format('refs/heads/{0}', github.event.repository.default_branch)",permissions:{contents:"write"},concurrency:{group:"terragucci-publish-${{ github.repository }}","cancel-in-progress":!1},env:{TERRAGUCCI_REGISTRY_USER:"${{ secrets.TERRAGUCCI_REGISTRY_USER }}",TERRAGUCCI_REGISTRY_PASSWORD:"${{ secrets.TERRAGUCCI_REGISTRY_PASSWORD }}",TERRAGUCCI_REGISTRY_INSECURE:"${{ secrets.TERRAGUCCI_REGISTRY_INSECURE }}",...e.attest?{COSIGN_PRIVATE_KEY:"${{ secrets.COSIGN_PRIVATE_KEY }}",COSIGN_PASSWORD:"${{ secrets.COSIGN_PASSWORD }}"}:{},...e.publishBucket?z$(t,{bucket:e.publishBucket}):{}},steps:[new se({uses:"actions/checkout@v4",with:{"fetch-depth":0}}),...M&&o?[new se({name:`Install ${o.binary} ${o.version}`,run:M})]:[],...e.attest?[new se({name:`Install cosign ${Ud}`,run:F$("cosign",Ud,t)})]:[],new se({name:"Publish the modules that changed",shell:"bash",run:Aq(t)})]})),E&&Ft.set("drift",new pt({"runs-on":"ubuntu-latest",container:{image:r},if:"github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && github.event.inputs.pr == '')",permissions:{contents:_r?"write":"read",issues:"write",..._r?{"pull-requests":"write"}:{},...a?{"id-token":"write"}:{}},...Po(!!a),...e.agentDrift?{outputs:{agent:"${{ steps.drift.outputs.agent }}",issue:"${{ steps.drift.outputs.issue }}"}}:{},env:{...p,TG_TOKEN:"${{ github.token }}",TG_SHA:"${{ github.sha }}",...O,...L,...z$(t,e.reports),...Z},steps:[...as(new se({...e.agentDrift?{id:"drift"}:{},name:`Plan every ${Ze} against what exists, and keep the drift issue`,shell:"bash",run:Oq(n,s,t,a,_,_r,me,e.agentDrift!==void 0)}),!0,!1,Bl),new se({name:"Keep the drift report",if:"always()",uses:t==="forgejo"?"actions/upload-artifact@v3":"actions/upload-artifact@v4",with:{name:`${be}-drift`,path:`${be}/`,"if-no-files-found":"ignore"}}),...Ul?[new se({name:"Keep the agent input",if:"always()",uses:t==="forgejo"?"actions/upload-artifact@v3":"actions/upload-artifact@v4",with:{name:`${vl}-drift`,path:`${vl}/`,"if-no-files-found":"ignore"}})]:[]]})),E&&e.agentDrift)for(let[q,Se]of nq(t,r,e.agentDrift,`${be}-drift`))Ft.set(q,Se);t==="forgejo"&&Dhe(Ft);let rr=q=>(vq(q,e.runner,t,Oi),Iq((t==="forgejo"?Ohe:iE).serialize(q))),Io=[];if(e.resume){let q=new Map([["workflow",new Gu({name:"terragucci resume",on:{schedule:[{cron:kq(e.resume)}],workflow_dispatch:{}},env:A,permissions:{contents:"read"}})],["resume",new pt({"runs-on":"ubuntu-latest",container:{image:r},permissions:{contents:"write",statuses:"write","pull-requests":"write",...v,...$?{"id-token":"write"}:{}},...Po($),...rt?{concurrency:dP(t)}:{},env:{...p,TG_TOKEN:"${{ github.token }}",...O,...ye,...ne},steps:[...as(new se({name:"Apply a waiting wave once its approval stands",shell:"bash",run:aye(n,s,t,a,mt)}),!0,!0,void 0,!0),new se({name:"Keep the apply report",if:"always()",uses:t==="forgejo"?"actions/upload-artifact@v3":"actions/upload-artifact@v4",with:{name:`${be}-resume`,path:`${be}/`,"if-no-files-found":"ignore"}})]})]]);Io.push({path:Mhe[t],content:Tp(r,e.imageFromConfig)+rr(q)})}if(y){let q=new Map([["workflow",new Gu({name:"terragucci ephemeral",on:{schedule:[{cron:kq(y.sweep)}],workflow_dispatch:{}},env:A,permissions:{contents:"read"}})],["sweep",new pt({"runs-on":"ubuntu-latest",container:{image:r},permissions:{contents:"write","pull-requests":"read",...$?{"id-token":"write"}:{}},...Po($),concurrency:{group:"terragucci-ephemeral-sweep-${{ github.repository }}","cancel-in-progress":!1},env:{...p,TG_TOKEN:"${{ github.token }}",...O},steps:as(new se({name:"Destroy the ephemeral copies whose TTL passed or whose pull request closed",shell:"bash",run:$q(t,a,n)}),!1,!0)})]]);Io.push({path:mP[t],content:Tp(r,e.imageFromConfig)+rr(q)})}return e.review&&Io.push({path:Dc[t],content:Tp(r,e.imageFromConfig)+rr(sq(t,r,e.review,{pipelineName:"terragucci",env:A}))}),R&&Io.push({path:gP[t],content:Tp(r,e.imageFromConfig)+rr(Eye(t,r,R,A,c,M?{name:te,run:M}:void 0))}),{path:Xr[t],content:Tp(r,e.imageFromConfig)+rr(Ft)+jq(e.ownJobs,[...Ft.keys()].filter(q=>q!=="workflow"),t),applyJobs:Ge,...Io.length?{extra:Io}:{}}}function Eye(e,t,n,r,o,s){let i=o?`\${{ secrets.${o} }}`:"${{ github.token }}",a=new Gu({name:"terragucci-rollout",on:{schedule:[{cron:n}],workflow_dispatch:{}},env:r,permissions:{contents:"read"},concurrency:{group:"terragucci-rollout-${{ github.repository }}","cancel-in-progress":!1}}),c=new pt({"runs-on":"ubuntu-latest",container:{image:t},permissions:{contents:"write","pull-requests":"write",statuses:"read",...e==="github"?{checks:"read"}:{}},env:{TG_TOKEN:i},steps:[new se({uses:"actions/checkout@v4",with:{"fetch-depth":0,...o?{token:i}:{}}}),...s?[new se(s)]:[],new se({name:"Open the next wave of each rollout whose last wave merged and applied",shell:"bash",run:Cq(e,o)})]});return new Map([["workflow",a],["rollout",c]])}var Z$=e=>e.length<2?e.join(""):`${e.slice(0,-1).join(", ")} and ${e[e.length-1]}`;function Jq(e,t){if(e===void 0)return;let n=typeof e=="string"?{labels:[e]}:Array.isArray(e)?{labels:[...e]}:{group:e.group,labels:[...e.labels??[]]};if(!((t==="github"||t===void 0)&&n.group===void 0&&!n.labels.includes("self-hosted")))return n}function Kq(e,t){if(e.group!==void 0&&t.group!==void 0&&e.group!==t.group)return!1;let n=(r,o)=>r.every(s=>o.includes(s));return n(e.labels,t.labels)||n(t.labels,e.labels)}var V$=e=>[...e.group!==void 0?[`group ${e.group}`]:[],...e.labels.length?[`labels ${e.labels.join(", ")}`]:[]].join(" and ");function Tye(e,t,n){let r=(e??[]).filter(a=>a.environment!==void 0);if(r.length===0)return[];let o=Jq(kl(t,"plan"),n),s=r.map(a=>({name:a.name,at:Jq(a.runner??kl(t,"apply"),n)})),i=[];for(let[a,c]of s.entries())if(c.at){o&&Kq(c.at,o)&&i.push({rule:kx,message:`the apply jobs of environment ${c.name} (${V$(c.at)}) and the plan jobs (${V$(o)}) can run on the same runner, so a pull request's code planned there can leave files or processes for ${c.name}'s apply; give ${c.name}'s apply runners a label no plan job uses, and use runners that start clean for each job`});for(let u of s.slice(a+1))!u.at||!Kq(c.at,u.at)||i.push({rule:kx,message:`the apply jobs of environments ${c.name} (${V$(c.at)}) and ${u.name} (${V$(u.at)}) can run on the same runner, so one environment's apply can leave files or processes for the other's; give each environment's apply runners a label of its own`})}return i}function Aye(e,t){let n=e.oidc;return!n||!t?[]:[...n.apply_role!==void 0&&n.apply_role===t.apply_role?[{what:"oidc.apply_role",id:n.apply_role}]:[],...n.gcp?.apply_service_account!==void 0&&n.gcp.apply_service_account===t.gcp?.apply_service_account?[{what:"oidc.gcp.apply_service_account",id:n.gcp.apply_service_account}]:[],...n.azure?.apply_client_id!==void 0&&n.azure.apply_client_id===t.azure?.apply_client_id?[{what:"oidc.azure.apply_client_id",id:n.azure.apply_client_id}]:[]]}function Oye(e,t){if(!e||e.length<2)return[];let n=new Map;for(let o of e)for(let{what:s,id:i}of Aye(o,t)){let a=n.get(i)??{what:s,names:[],envs:[]};a.names.push(o.name),o.environment!==void 0&&a.envs.push(o.name),n.set(i,a)}let r=[];for(let[o,{what:s,names:i,envs:a}]of n){if(i.length<2||a.length===0)continue;let c=s==="oidc.apply_role"?"apply_role":s==="oidc.gcp.apply_service_account"?"gcp.apply_service_account":"azure.apply_client_id";r.push({rule:iM,message:`the apply jobs of ${i.length===a.length?"environments":"the groups"} ${Z$(i)} all apply as ${o} (${s}), so its trust must accept each of their jobs and no condition keeps one environment's job from another's role; set environments.<name>.oidc.${c} for ${Z$(a)}`})}return r}var Pye=e=>!!(e&&(e.apply_role||e.roles&&Object.keys(e.roles).length>0));function jye(e,t,n){if(n!=="gitlab"||!e||!e.some(s=>s.environment!==void 0))return[];let r=new Map;for(let s of e){if(!Pye(s.oidc))continue;let i=s.audience??Es(t,"apply");r.set(i,[...r.get(i)??[],s])}let o=[];for(let[s,i]of r){let a=i.filter(u=>u.environment!==void 0&&u.audience===void 0).map(u=>u.name);if(i.length<2||a.length===0)continue;let c=i.map(u=>u.name);o.push({rule:aM,message:`on GitLab the apply jobs of ${c.every(u=>u!==Ka)&&i.every(u=>u.environment!==void 0)?"environments":"the groups"} ${Z$(c)} carry the same AWS token audience ${s}, and AWS cannot read GitLab's environment claims, so an apply role's trust cannot tell their jobs apart; set environments.<name>.oidc.audience for ${Z$(a)} and have each apply role trust its environment's audience alone`})}return o}function Vq(e,t,n){return[...Tye(e,t.runner,n),...Oye(e,t.oidc),...jye(e,t.oidc,n)]}var Iye="https://intentius.io/terragucci/terragucci.schema.json",ko={type:"string"},Rl={type:"boolean"},xl={type:"string",pattern:"\\S"},EP={type:"string",pattern:"^[^\\r\\n]*\\S[^\\r\\n]*$"},$n=e=>({type:"string",pattern:e.source}),_i=$n(rd),Y$={type:"array",items:ko},Zq={anyOf:[ko,{const:!1}]},Et=e=>({enum:[...e]}),Si=(e,t)=>({type:"integer",minimum:e,...t===void 0?{}:{maximum:t}}),Wc=e=>({$ref:`#/$defs/${e}`}),Sl=e=>({anyOf:[{type:"null"},e]}),TP={type:"string",pattern:"^[^\\s,;=']+$"},n2={type:"string",minLength:1,pattern:"^(?!/)(?!(?:.*/)?\\.\\.(?:/|$))"};function Qe(e,t,n={}){let r=e.filter(s=>!(s in t)),o=Object.keys(t).filter(s=>!e.includes(s));if(r.length||o.length)throw new Error(`config schema: keys ${[...r,...o].join(", ")} disagree with config.ts`);return{type:"object",properties:t,additionalProperties:!1,...n}}var AP=(e,t)=>Object.fromEntries(e.map(n=>[n,t(n)])),Cye=e=>[...e].map(t=>/[a-z]/i.test(t)?`[${t.toUpperCase()}${t.toLowerCase()}]`:t).join(""),Yq={type:"string",pattern:`^(?!(?:${ku.map(Cye).join("|")}))(?!(?:${nd.join("|")})$)[A-Za-z_][A-Za-z0-9_]*$`},xP=Qe(["plan","apply"],{plan:{type:"string",minLength:1},apply:{type:"string",minLength:1}},{required:["plan","apply"]}),X$={type:"string",pattern:"^[^@\\s]+@[^@\\s]+$"},Nye=Qe(["plan","apply"],{plan:X$,apply:X$},{required:["plan","apply"]}),Q$={anyOf:[$n(jf),{type:"array",minItems:1,uniqueItems:!0,items:$n(jf)},Qe(["group","labels"],{group:xl,labels:{type:"array",minItems:1,uniqueItems:!0,items:$n(jf)}},{required:["group"]})]},Xq={anyOf:[Rl,Qe(bf,{command:EP,key_secret:_i,max_turns:Si(1),timeout:Si(1)})]},Dye={type:"object",propertyNames:$n(qa),additionalProperties:Sl(Wc("hcl_value"))};function Qq(e){let t={backend:Sl({type:"object",minProperties:1,maxProperties:1,propertyNames:$n(qa),additionalProperties:Dye}),providers:{type:"object",propertyNames:$n($S),additionalProperties:Sl({type:"object",properties:{source:{type:["string","null"]},version:{type:["string","null"]}},propertyNames:$n(qa),additionalProperties:Sl(Wc("hcl_value")),not:{required:["alias"]}})},required_version:Sl(xl),disable_init:{type:["boolean","null"]}};if(!e)return Qe(bu,t);let n={type:"object",additionalProperties:Sl(Wc("generate_level"))};return Qe(yf,{...t,dirs:n,roots:n})}var Mye=Qe(xf,{when:Et(id),merge:Et(LS),merge_token_env:_i,requires:{type:"array",uniqueItems:!0,items:Et(Cn)},resume:Si(5,60),branches:{type:"object",minProperties:1,propertyNames:$n(lR),additionalProperties:{type:"array",minItems:1,items:TP}},point:$n(uR)}),Lye=Qe(Uy,{name:xl,run:xl,before:Et(td),after:Et(td),roots:Y$,on_failure:Et(FS)},{required:["run"],oneOf:[{required:["before"]},{required:["after"]}],if:{properties:{on_failure:{const:"approve"}},required:["on_failure"]},then:{properties:{before:Et(["init","plan"]),after:Et(["init","plan"])}}}),Gye=Qe(Jy,{bucket:ko,dir:n2,endpoint:ko,prefix:ko,url:{type:"string",pattern:"^https://[^/\\s?#]+/?$"},namespace:$n(_u),namespaces:{type:"object",propertyNames:{pattern:"^[^/]"},additionalProperties:$n(_u)},system:$n(rR),download:Et(["tarball","git-tags","oci"])},{required:["url","namespace"],oneOf:[{required:["bucket"]},{required:["dir"]}],dependentRequired:{endpoint:["bucket"]}}),e2={type:"string",pattern:"^(?:git-tags$|oci://[^/]+/.)"},Fye=Qe([...qy],{path:ko,publish:{anyOf:[e2,{type:"array",items:e2}]},attest:{anyOf:[Rl,Qe(["key"],{key:{type:"string",minLength:1}})]},require:{const:"attested"},trusted:{type:"array",items:Qe(["source","key","ledger"],{source:{type:"string",pattern:"^(oci://[^/]+/.+|(git::)?(https?|ssh)://.+)$"},key:{type:"string",minLength:1},ledger:{type:"string",pattern:"^(https?|ssh|file)://."}},{required:["source","key","ledger"]})},test:Rl,registry:Gye}),zye=Qe(kf,{version:$n(Ja),exclude:Y$,parallelism:Si(1),dependents:Et(CS),credentials:{type:"object",additionalProperties:xP}}),Uye=Qe(Wy,{engine:Et(NS),path:n2,source:{type:"string",pattern:"^git\\+(https?|file)://.*@"},namespace:{type:"string",pattern:"^[A-Za-z_][A-Za-z0-9_.]*$"},input:Et(DS),override:{type:"array",minItems:1,items:EP}}),t2={type:"array",minItems:1,items:{type:"string",pattern:YS}},Bye=Qe(_f,{modules:t2,providers:t2},{minProperties:1}),Wye=Qe(Hy,{plan_role:{type:"string",minLength:1},apply_role:{type:"string",minLength:1},audience:{anyOf:[ko,Qe(Sf,{plan:{type:"string",minLength:1},apply:{type:"string",minLength:1},drift:{type:"string",minLength:1}})]},roles:{type:"object",minProperties:1,additionalProperties:xP},gcp:Qe(["workload_identity_provider","plan_service_account","apply_service_account","token_url","roles"],{workload_identity_provider:$n(gR),plan_service_account:{type:"string",pattern:"^[^@\\s]+@[^@\\s]+$"},apply_service_account:{type:"string",pattern:"^[^@\\s]+@[^@\\s]+$"},token_url:{type:"string",pattern:"^https://[^\\s/]+/\\S*$"},roles:{type:"object",minProperties:1,additionalProperties:Nye}},{required:["workload_identity_provider","plan_service_account","apply_service_account"]}),azure:Qe(["tenant_id","subscription_id","plan_client_id","apply_client_id","audience","roles"],{tenant_id:{type:"string",minLength:1},subscription_id:{type:"string",minLength:1},plan_client_id:{type:"string",minLength:1},apply_client_id:{type:"string",minLength:1},audience:{type:"string",minLength:1},roles:{type:"object",minProperties:1,additionalProperties:xP}},{required:["tenant_id","subscription_id","plan_client_id","apply_client_id"]})},{minProperties:1,dependentRequired:{plan_role:["apply_role"],apply_role:["plan_role"]},if:{required:["audience"],not:{required:["roles"]}},then:{required:["plan_role","apply_role"]}}),Hye=Qe(Ef,{backend:Et(Af),url:{type:"string",pattern:"^https?://[^/\\s]+"},model:{type:"string",minLength:1,pattern:"^(?![\\s\\S]*(?:^|[-_.])(?:latest|preview)$)"},token_env:_i,thresholds:Qe($u,AP($u,()=>({type:"number",exclusiveMinimum:0,maximum:1})))},{required:["backend"],allOf:[{if:{properties:{backend:{const:"jev"}}},then:{required:["token_env"]},else:{required:["url"]}},{if:{properties:{backend:{const:"laya"}}},else:{required:["model"]}}]}),qye={anyOf:[Rl,Qe(vu,AP(vu,e=>US.includes(e)?$n(fR):e==="dir"?{type:"string",pattern:"^(?!/)(?!(?:.*/)?\\.\\.(?:/|$))[^\\r\\n]+$"}:{type:"string",pattern:"^[^\\r\\n]+$"}))]},Jye=Qe(Rf,{roots:{type:"array",minItems:1,items:TP},ttl:{type:"string",pattern:"^[1-9]\\d*[mhd]$"},sweep:Si(5,60)},{required:["roots"]}),Kye=Qe(["plan_role","apply_role","audience","gcp","azure"],{plan_role:{type:"string",minLength:1},apply_role:{type:"string",minLength:1},audience:{type:"string",minLength:1,pattern:"\\S"},gcp:Qe(["plan_service_account","apply_service_account"],{plan_service_account:X$,apply_service_account:X$},{required:["plan_service_account","apply_service_account"]}),azure:Qe(["plan_client_id","apply_client_id"],{plan_client_id:{type:"string",minLength:1},apply_client_id:{type:"string",minLength:1}},{required:["plan_client_id","apply_client_id"]})},{minProperties:1,dependentRequired:{plan_role:["apply_role"],apply_role:["plan_role"]}});function Vye(e){return Qe(["roots","gate","oidc","runner","parallelism","forge_environment"],{roots:{type:"array",minItems:1,items:TP},gate:Et(jS),oidc:Kye,runner:{anyOf:[Q$,Qe(["apply"],{apply:Q$},{required:["apply"]})]},parallelism:Si(1),forge_environment:$n(hR)},e?{required:["roots"]}:{})}function r2(e){return{roots:{...Y$,description:"Globs of root directories. Detected when absent."},binary:{...Et(od),description:"The binary the pipeline runs. Detected when absent."},version:e?{anyOf:[ko,{type:"object",additionalProperties:$n(Ja)}],description:"The binary's release, or a map of root glob to release."}:{...ko,description:"The binary's release."},generate:{...Wc("generate"),description:"Backend, provider and version files terragucci generate writes for each plain root."},forge:{...Et(Wr),description:"The forge, for a host terragucci cannot name."},url:{...ko,description:"Where the project lives, for a forge not on https or the default port."},gate:{...Et(jS),description:"When a wave waits for an approval. on-destroy is an older name for on-destructive."},approval:{anyOf:[Et(Do),{type:"object",additionalProperties:!1,properties:{mode:{...Et(Do),description:"What counts as a waiting wave's approval."},require_signed:{type:"boolean",description:"Refuse an unsigned approval on the ledger, so every ledger approval is tied to the principal who signed it."}}}],description:"What counts as a waiting wave's approval: the mode, or a map with the mode and require_signed."},apply:{...Mye,description:"When a change applies."},locks:{...Et(MS),description:"When a pull request takes its root locks."},waves:{type:"object",properties:{canary:Y$,jobs:Si(1),after:{type:"object",additionalProperties:{type:"array",minItems:1,items:{type:"string",minLength:1}}},compose:{type:"boolean"},compose_max:Si(2)},description:"The canary wave, the jobs one wave spreads across, extra apply order, and whether a change's roots apply as one composed run."},drift:{...Zq,description:"A cron schedule for tf-drift, or false."},synth:{...xl,description:"The command that writes the roots before any job reads them."},steps:{type:"array",items:Lye,description:"Commands run before or after a root's init, plan, apply and drift."},image:{type:"string",pattern:"^[^\\s]+$",description:"The image every job runs in, built FROM terragucci's."},notify:{...Qe(ZS,{slack:_i,teams:_i,webhook:_i,webhook_key:_i,relay:{type:"string",pattern:"^[A-Za-z0-9][A-Za-z0-9 ._-]{0,63}$"}},{minProperties:1,dependentRequired:{webhook:["webhook_key"],webhook_key:["webhook"]},if:{required:["relay"]},then:{anyOf:[{required:["slack"]},{required:["teams"]}]}}),description:"The secrets holding Slack, Teams or webhook addresses, and the relay's name."},cost:{anyOf:[{const:!0},Qe(eR,{key_secret:_i,command:xl,approve_above:{type:"number",minimum:0}})],description:"Cost estimates per root in the plan note."},comments:{...Zq,description:"GitLab only: the cron of the comments schedule, or false."},rollouts:e?{anyOf:[xl,{const:!1}],description:"A cron schedule for the job that opens each rollout's next wave, or false."}:{const:!1,description:"A single repo's key; a control repo runs terragucci respond rollout itself."},gitlab:{...Qe(QS,{token:Et(GS)}),description:"GitLab only: how the project keeps its forge token."},runtime:{...Et(IS),description:"Where the stages run."},reports:{type:"object",properties:{bucket:ko,endpoint:{},prefix:{},url:{type:"string",pattern:"^https?://[^\\s?#]+$"},role:{type:"string",pattern:"^arn:aws[\\w-]*:iam::\\d{12}:role/\\S+$"}},required:["bucket"],description:"A bucket for plan reports: s3://, gs:// or az://."},token_env:{...ko,description:"The environment variable holding the forge token."},env:{type:"object",additionalProperties:ko,description:"Environment variables every job gets. Values only, never secrets."},runner:{anyOf:[Q$,{type:"object",minProperties:1,not:{required:["group"]},propertyNames:Et($f),additionalProperties:Q$}],description:"The runner each job runs on: a label, a list of labels, a GitHub runner group, or one per stage."},pass:{...Qe(tR,{secrets:{type:"array",minItems:1,uniqueItems:!0,items:Yq},vars:{type:"array",minItems:1,uniqueItems:!0,items:Yq}},{minProperties:1}),description:"Names of CI secrets and variables the plan, apply and drift jobs get."},telemetry:{...Qe(XS,{headers_secret:_i,trace_url:{type:"string",pattern:"^https?://\\S*\\{trace_id\\}\\S*$"}},{minProperties:1}),description:"The secret holding OTLP headers, and a trace link with {trace_id}."},tips:{...Rl,description:"Tips in the plan note."},modules:{...Fye,description:"Module publishing, attestation and the module registry."},oidc:{...Wye,description:"Cloud identities the jobs take over OIDC: a read-only one for plan, a write one for apply."},parallelism:{...Si(1),description:"How many roots of one dependency layer plan at once."},terragrunt:{...zye,description:"Terragrunt settings."},atmos:{...Qe(Tf,{version:{type:"string",pattern:"^\\d+\\.\\d+\\.\\d+(-[0-9A-Za-z.]+)?$"}}),description:"Atmos settings."},policy:{...Uye,description:"Policy checks over each plan."},sources:{...Bye,description:"Globs of the module sources and provider addresses a root may use, checked before init."},atlantis_comments:{...Rl,description:"Read atlantis plan and atlantis apply comments as terragucci's."},respond:{...Qe(Object.keys(Us),AP(Object.keys(Us),t=>Et(Us[t]))),description:"The response to each pipeline event."},agent:{...Qe(nR,{via:Et(zS),token_env:{type:"string",minLength:1},login:{type:"string",pattern:Cf.source,description:"The forge login agent.token_env's token acts as. No approval by it counts."},comment:Xq,drift:Xq},{required:["via","token_env"]}),description:"The coding agent behind /terragucci agent and the drift agent."},review:{...Qe(vf,{agent:Rl,command:EP,key_secret:_i,instructions:{type:"string",pattern:"^(?:\\./)?(?!/)[A-Za-z0-9_./-]+$"},timeout:Si(1)},{anyOf:[{maxProperties:0},{required:["agent"]}]}),description:"A model's review of each pull request's intent against its plan."},decide:{...Hye,description:"The typed-decision service."},audit_region:{type:"string",pattern:"^[a-z]{2}(-[a-z]+)+-\\d+$",description:"The AWS region whose CloudTrail drift attribution reads."},dashboards:{...qye,description:"Dashboards and alert rules written next to the pipeline."},own_jobs:{anyOf:[{type:"string",pattern:"^(?!/)(?!(?:.*/)?\\.\\.(?:/|$)).*\\.ya?ml$"},{type:"object",minProperties:1,propertyNames:$n(aR),additionalProperties:{type:"object",minProperties:1}}],description:"Jobs of your own, in the forge's syntax, or the path of a YAML file holding them."},ephemeral:{...Jye,description:"Roots each pull request gets a copy of."},environments:{type:"object",minProperties:1,propertyNames:{...$n(WS),not:{anyOf:[{enum:["default","decide"]},{pattern:"^share-[0-9]+$"}]}},additionalProperties:Vye(e),description:"Named environments: each one's root globs, and its gate, identities, apply runner, parallelism and forge environment."}}}function Zye(){return{type:"object",properties:r2(!1),additionalProperties:!1}}function Yye(){let e=r2(!0);return{$schema:"https://json-schema.org/draft/2020-12/schema",$id:Iye,title:"terragucci.yml",description:"One repo's settings, or a control repo's defaults and projects. https://intentius.io/terragucci/reference/config/",type:["object","null"],properties:{...e,defaults:{...Wc("project"),not:{required:["url"]},description:"Settings every project takes, under its own."},projects:{type:"object",propertyNames:{pattern:"^(?:https?://)?[^/]+(?:/[^/]+){2,}/*$"},additionalProperties:Sl(Wc("project")),description:"<host>/<owner>/<name> to that project's settings."}},additionalProperties:!1,dependentRequired:{defaults:["projects"]},if:{required:["projects"]},then:{propertyNames:Et(["defaults","projects"])},$defs:{project:Zye(),generate:Qq(!0),generate_level:Qq(!1),hcl_value:{anyOf:[{type:["string","number","boolean"]},{type:"array",items:Wc("hcl_value")},{type:"object",additionalProperties:Wc("hcl_value")}]}}}}var Bc=e=>e!==null&&typeof e=="object"&&!Array.isArray(e);function Xye(e){return e===null?"null":Array.isArray(e)?"array":typeof e}function Qye(e,t){return t==="integer"?Number.isInteger(e):Xye(e)===t}var gh=e=>JSON.stringify(e,(t,n)=>Bc(n)?Object.fromEntries(Object.keys(n).sort().map(r=>[r,n[r]])):n);function _l(e,t,n=e,r="config"){let o=[],s=e;if(typeof s.$ref=="string"){let a=n.$defs[s.$ref.replace("#/$defs/","")];if(!a)throw new Error(`config schema: no ${s.$ref}`);o.push(..._l(a,t,n,r))}if(s.type!==void 0){let a=Array.isArray(s.type)?s.type:[s.type];if(!a.some(c=>Qye(t,c)))return[...o,`${r}: must be ${a.join(" or ")}`]}if("const"in s&&gh(t)!==gh(s.const)&&o.push(`${r}: must be ${JSON.stringify(s.const)}`),Array.isArray(s.enum)&&!s.enum.some(a=>gh(a)===gh(t))&&o.push(`${r}: must be one of ${s.enum.join(", ")}`),typeof t=="string"&&(typeof s.minLength=="number"&&[...t].length<s.minLength&&o.push(`${r}: must not be empty`),typeof s.pattern=="string"&&!new RegExp(s.pattern,"u").test(t)&&o.push(`${r}: does not match ${s.pattern}`)),typeof t=="number"&&(typeof s.minimum=="number"&&t<s.minimum&&o.push(`${r}: must be ${s.minimum} or more`),typeof s.maximum=="number"&&t>s.maximum&&o.push(`${r}: must be ${s.maximum} or less`),typeof s.exclusiveMinimum=="number"&&t<=s.exclusiveMinimum&&o.push(`${r}: must be more than ${s.exclusiveMinimum}`)),Array.isArray(t)&&(typeof s.minItems=="number"&&t.length<s.minItems&&o.push(`${r}: must list at least ${s.minItems}`),s.uniqueItems===!0&&new Set(t.map(gh)).size!==t.length&&o.push(`${r}: lists an item twice`),Bc(s.items)&&t.forEach((a,c)=>o.push(..._l(s.items,a,n,`${r}[${c}]`)))),Bc(t)){let a=Object.keys(t),c=s.properties??{};typeof s.minProperties=="number"&&a.length<s.minProperties&&o.push(`${r}: must set at least ${s.minProperties} key(s)`),typeof s.maxProperties=="number"&&a.length>s.maxProperties&&o.push(`${r}: must set at most ${s.maxProperties} key(s)`);for(let u of s.required??[])u in t||o.push(`${r}.${u}: is required`);for(let[u,l]of Object.entries(s.dependentRequired??{}))if(u in t)for(let d of l)d in t||o.push(`${r}.${d}: is required with ${u}`);for(let u of a){let l=`${r}.${u}`;if(Bc(s.propertyNames))for(let d of _l(s.propertyNames,u,n,l))o.push(`${l}: is not a key here (${d.slice(l.length+2)})`);u in c?o.push(..._l(c[u],t[u],n,l)):s.additionalProperties===!1?o.push(`${l}: is not a setting`):Bc(s.additionalProperties)&&o.push(..._l(s.additionalProperties,t[u],n,l))}}let i=a=>_l(a,t,n,r);for(let a of s.allOf??[])o.push(...i(a));if(Array.isArray(s.anyOf)){let a=s.anyOf.map(i);a.some(c=>c.length===0)||o.push(...a.reduce((c,u)=>u.length<c.length?u:c))}if(Array.isArray(s.oneOf)&&s.oneOf.filter(c=>i(c).length===0).length!==1&&o.push(`${r}: must match exactly one of ${s.oneOf.map(c=>JSON.stringify(c)).join(", ")}`),Bc(s.not)&&i(s.not).length===0&&o.push(`${r}: must not match ${JSON.stringify(s.not)}`),Bc(s.if)){let a=i(s.if).length===0?s.then:s.else;Bc(a)&&o.push(...i(a))}return o}function o2(e){return _l(Yye(),e)}var ewe=1440,twe=30,nwe=50,s2="terragucci/apply",i2=e=>`<!-- terragucci:note=${e} -->`,rwe=/<!-- terragucci:note=(\d+) -->/g,Ts=e=>e.slice(0,8);function owe(e){let t=e.CI_API_V4_URL,n=e.CI_PROJECT_ID,r=e.TG_TOKEN,o=e.CI_DEFAULT_BRANCH;if(!t||!n||!r||!o)throw new S("comment --forge gitlab --poll needs CI_API_V4_URL, CI_PROJECT_ID, CI_DEFAULT_BRANCH and TG_TOKEN in the environment");return{t:{forge:"gitlab",origin:e.CI_SERVER_URL??t,path:n,token:r,api:t},base:o}}async function a2(e){let t=e.env??process.env,n=e.fetch??fetch,r=e.now??new Date,{t:o,base:s}=owe(t),i=encodeURIComponent(o.path),a=(y,w,b)=>kt(n,o,y,w,b),c=new Date(r.getTime()-ewe*6e4).toISOString(),u,l,d;try{let y=await a("GET","/user");u=Number(y?.id),l=String(y?.username??""),d=await a("GET",`/projects/${i}/merge_requests?state=all&updated_after=${encodeURIComponent(c)}&order_by=updated_at&sort=desc&per_page=100`)}catch(y){return{outcomes:[],fail:`could not list the merge requests (${y.message})`}}if(!Array.isArray(d))return{outcomes:[],fail:"GitLab answered the merge request list with something other than a list"};let p=[],f=[],g=[],m=jt("gitlab",t,n),h={api:a,id:i,me:u,meName:l,forge:m,apiUrl:o.api,token:o.token,fetch:n,reportDir:e.reportDir??"terragucci-report"};for(let y of d){let w=y?.iid;if(!Number.isInteger(w)||w<1)continue;let b;try{b=await a("GET",`/projects/${i}/merge_requests/${w}/notes?order_by=created_at&sort=desc&per_page=100`)}catch(_){return{outcomes:p,plans:f,reviews:g,fail:`could not read the notes of !${w} (${_.message})`}}if(!Array.isArray(b))continue;let k=e.planNotes?await _W(h,y,b):void 0;k&&f.push(k);let v=new Set;for(let _ of b)if(!(_?.author?.id!==u||typeof _?.body!="string"))for(let E of _.body.matchAll(rwe))v.add(Number(E[1]));let $=b.filter(_=>Number.isInteger(_?.id)&&_.system!==!0&&!v.has(_.id)).filter(_=>typeof _.created_at=="string"&&_.created_at>=c).filter(_=>Cr(_.body,$s(t))!==void 0).sort((_,E)=>_.created_at<E.created_at?-1:_.created_at>E.created_at?1:_.id-E.id);for(let _ of $)p.push(await swe({api:a,forge:m,meName:l,id:i,base:s,layers:e.layers,mr:y,note:_,env:t,fetch:n,...e.when?{when:e.when}:{},...e.requires?{requires:e.requires}:{},...e.wait?{wait:e.wait}:{},...e.agent?{agent:!0}:{}}));if(e.review){let _=await qW({api:a,id:i,me:u,meName:l,base:s,env:t,fetch:n},y,b);_&&g.push(_)}}return{outcomes:p,plans:f,reviews:g}}async function swe(e){let{api:t,id:n,base:r,layers:o,mr:s,note:i}=e,a=e.when==="pull-request",c=s.iid,u={mr:c,note:i.id},l=async(O,I=!1)=>{try{await e.forge.upsertComment(c,i2(i.id),`terragucci: ${O}
|
|
885
885
|
|
|
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
|
-
`);return o.splice(1,0,`> This plan is stale: ${n} moved under ${r.join(", ")}. Push to this pull request to plan again. ${
|
|
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
|
|
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(
|
|
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
|
-
`);g.push({path:`${p}/${m.path}`,content:h})}return g.sort((m,h)=>m.path<h.path?-1:1)}function
|
|
886
|
+
${i2(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(s2)}&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===s2):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 c2=["status","note","reply","stale"],u2="<!-- terragucci:stale -->",awe=new Set(["pending","success","failure"]);async function d2(e,t,n){let r=n.env??process.env;if(!c2.includes(e))throw new S(`post is ${c2.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(l2(r),yi,iwe(OP(t[0],"note <file>"),"utf-8")):e==="reply"?await o.addComment(l2(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 l2(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(u2))return;let r=(/roots=(\S*) -->/.exec(e)?.[1]??"").split(",").filter(s=>t.includes(s));if(!r.length)return;let o=e.split(`
|
|
887
|
+
`);return o.splice(1,0,`> This plan is stale: ${n} moved under ${r.join(", ")}. Push to this pull request to plan again. ${u2}`),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 p2(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 m2(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:p2(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:g2(p.by),spent:f2(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=p2(t,i.gate,i.planDigest,c);if(u){o.push({...a,resumable:!1,reason:"spent",approvedBy:c.resolvedBy,approvedAt:c.timestamp,message:g2(u),spent:f2(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 f2=e=>({appliedAt:e.timestamp,approvedAt:e.approvedAt,...e.wave?{wave:e.wave}:{}}),g2=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)=>m2({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)),...h2(i)}));return n&&s.push(...o(mr,n).filter(i=>i.source==="pending").map(i=>({wave:1,migration:i.gate,...h2(i)}))),s.sort((i,a)=>i.wave-a.wave||+!!a.migration-+!!i.migration)}function h2(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 y2(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 e1(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 w2(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 k2}from"node:child_process";import{createHash as Swe}from"node:crypto";import{existsSync as CP,mkdtempSync as _2,realpathSync as b2,rmSync as Rwe,writeFileSync as xwe}from"node:fs";import{tmpdir as S2}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,v2=(e,t,n=!1)=>`terragucci approve export ${e} --plan ${t}${n?" --sign":""}`,R2=e=>`sha256:${Swe("sha256").update(e).digest("hex")}`,Pa=e=>Date.parse(e)||0;function jwe(e){return R2(JSON.stringify({op:Hc,root:e.root,location:e.location,version_id:e.version_id,by:e.by,at:e.at}))}function $2(e,t,n){let r=k2("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
|
+
`).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(_2(El(S2(),"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(b2(e),El(b2(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 x2(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||$2(e,["config","user.name"],r)||$2(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=_2(El(S2(),"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
|
+
`).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=k2("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=v2(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=v2(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:R2(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 RJ}from"node:child_process";import{existsSync as ove,statSync as sve}from"node:fs";import{resolve as vJ}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 W2,relative as $be}from"node:path";import{execFileSync as F2}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 E2=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"},T2="https://tgph0.invalid/TGPH0REPORTS",NP=["drift_age","wave_wait","schedule"],DP=["prometheus","tempo","folder","path"];function A2(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 O2(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(A2(tk[m])),String(A2(t[m])));for(let m of DP)r.set(tk[m],t[m]);r.set(T2,n.reports);let o=new Set([...DP.map(m=>tk[m]),T2]),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
|
+
`);g.push({path:`${p}/${m.path}`,content:h})}return g.sort((m,h)=>m.path<h.path?-1:1)}function P2(e,t={}){return O2(E2,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 j2(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 I2(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(`
|
|
892
892
|
`);function LP(e){let t=n=>{let r=typeof n=="string"?n:n&&typeof n=="object"?n.local:void 0;return typeof r=="string"&&r.replace(/^\.?\//,"")===Xr.gitlab};return Array.isArray(e)?e.some(t):t(e)}function GP(e){try{return uo(e)}catch(t){throw new S(`${Op} is not YAML init can read, so it cannot add the include of ${Xr.gitlab} (${t.message})`)}}function Qwe(e,t){let n=e.split(`
|
|
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
|
-
`)}function
|
|
896
|
-
`;function
|
|
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=
|
|
895
|
+
`)}function C2(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 N2(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 D2=e=>`${JSON.stringify(e,null,2)}
|
|
896
|
+
`;function M2(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);oL(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&&!M2(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,C2(So(J)?Ro(J,"utf-8"):void 0,M.content)))}let $e=j2(r.dashboards);if($e)for(let J of P2($e,{...Zu(r.reports)?{reports:Zu(r.reports)}:{}})){let j=_o(e,J.path);if(So(j)&&!t.force&&!I2(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
|
+
`)),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=N2({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,D2(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 L2="# terragucci reconcile writes this file from the control repo's terragucci.yml; edit that file, not this one.";function z2(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||z2(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(L2),s=VS.filter(a=>sk(t,a)!==void 0);if(n&&!o){let a=ue(await Re(n)),c=VS.filter(l=>!z2(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,`${L2}
|
|
898
898
|
${i}
|
|
899
|
-
`)}var
|
|
900
|
-
`)}if(i!==void 0)try{s=JSON.parse(i)}catch{throw new S("chant.workspace.json is not valid JSON, so init cannot add the tf-apply gates to it")}else s={name:gbe(e,r),schema:1,minReader:
|
|
901
|
-
`:i)}function fbe(e,t){if(!/^[^\s,"*?!]+$/.test(t))throw new S(`--signer ${JSON.stringify(t)} is not a principal; use a name with no spaces, commas, quotes or patterns, such as github:alice`);let n="";try{n=
|
|
902
|
-
`}function gbe(e,t){let n=t;if(!n)try{n=
|
|
903
|
-
`)}function
|
|
904
|
-
`)});s.push({key:i,status:"pull-request",changes:b,pullRequest:R.url})}catch(c){s.push({key:i,status:"failed",changes:[],error:c.message})}finally{ybe(a,{recursive:!0,force:!0})}}return s}function
|
|
905
|
-
`)}import{execFileSync as Cbe}from"node:child_process";import{existsSync as VP,mkdtempSync as
|
|
906
|
-
`).filter(Boolean).map(s=>s.split(" ").pop().replace(/^refs\/tags\//,""));if(n[0]==="ls-remote"||o.length>0)return o}catch{}return[]}async function
|
|
899
|
+
`)}var G2="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)}
|
|
900
|
+
`)}if(i!==void 0)try{s=JSON.parse(i)}catch{throw new S("chant.workspace.json is not valid JSON, so init cannot add the tf-apply gates to it")}else s={name:gbe(e,r),schema:1,minReader:G2,members:[]};let a=i===void 0,[c=0,u=0]=String(s.minReader).split(".").map(Number);(typeof s.minReader!="string"||c===0&&u<102)&&(s.minReader=G2,a=!0);let l=s.identity??={},d=l.gates??={};for(let p=1;p<=t;p++)d[Ct(p)]===void 0&&(d[Ct(p)]={},a=!0);return As(o,a?`${JSON.stringify(s,null,2)}
|
|
901
|
+
`:i)}function fbe(e,t){if(!/^[^\s,"*?!]+$/.test(t))throw new S(`--signer ${JSON.stringify(t)} is not a principal; use a name with no spaces, commas, quotes or patterns, such as github:alice`);let n="";try{n=F2("git",["-C",e,"config","user.signingkey"],{encoding:"utf-8",stdio:["ignore","pipe","ignore"]}).trim()}catch{}if(!n)throw new S("--signer reads git config user.signingkey, which is not set; set it to your ssh key (git config --global user.signingkey ~/.ssh/id_ed25519.pub)");let r=n.replace(/^key::/,"");if(!/^(ssh-|sk-|ecdsa-)/.test(r)){let i=r.replace(/^~(?=\/)/,process.env.HOME??"~"),a=i.endsWith(".pub")?i:`${i}.pub`;if(!So(a))throw new S(`--signer found user.signingkey ${n}, and no public key at ${a}`);r=Ro(a,"utf-8").trim()}let[o,s]=r.split(/\s+/);if(!o||!s||!/^(ssh-(ed25519|rsa)|sk-ssh-ed25519@openssh\.com)$/.test(o))throw new S(`--signer needs an ed25519 or RSA ssh key; user.signingkey gives ${o??"nothing"}`);return`${t} ${o} ${s}
|
|
902
|
+
`}function gbe(e,t){let n=t;if(!n)try{n=F2("git",["-C",e,"remote","get-url","origin"],{encoding:"utf-8",stdio:["ignore","pipe","ignore"]}).trim().replace(/\.git$/,"").split(/[/:]/).pop()}catch{}return(n||cbe(e)).toLowerCase().replace(/[^a-z0-9-]+/g,"-").replace(/^-+/,"").slice(0,40).replace(/-+$/,"")||"infra"}function U2(e,t,n=!1){let r=a=>a==="unchanged"?"unchanged":n?a==="created"?"would write":a==="removed"?"would remove":"would update":a==="created"?"wrote":a==="removed"?"removed":"updated",o=(a,c)=>`${a} ${c}${a===1?"":"s"}`,s=t.terragrunt;return[t.terramate?`found Terramate (${t.terramate.reason}): ${o(t.roots.length,"stack")} in ${o(t.layers.length,"wave")} from terramate list and the stacks' order, terramate ${t.terramate.version}, ${t.binary.value} ${t.version.value} (${t.binary.reason}), forge ${t.forge.value} (${t.forge.reason})`:t.atmos?`found Atmos (${t.atmos.reason}): ${o(t.roots.length,"instance")} in ${o(t.layers.length,"wave")} from atmos describe stacks, atmos ${t.atmos.version} calling ${t.binary.value} ${t.version.value} (${t.binary.reason}), forge ${t.forge.value} (${t.forge.reason})`:s?`found Terragrunt (${s.reason}): ${o(t.roots.length,"unit")} in ${o(t.layers.length,"wave")} from ${s.source}, terragrunt ${s.version.value} (${s.version.reason}) calling ${t.binary.value} ${t.version.value} (${t.binary.reason}), parallelism ${s.parallelism.value} (${s.parallelism.reason}), forge ${t.forge.value} (${t.forge.reason})`:`found ${o(t.roots.length,"root")} in ${o(t.layers.length,"layer")}, ${t.binary.value} ${t.version.value} (${t.binary.reason}), forge ${t.forge.value} (${t.forge.reason})`,...t.pins.length>0?[`${o(t.pins.length,"root")} ${t.pins.length===1?"pins its":"pin their"} own version: ${t.pins.map(a=>`${a.root} ${a.tool??t.binary.value} ${a.version} (${a.source})`).join(", ")}`]:[],...t.files.map(a=>`${r(a.status)} ${qc(e,a.path)}`),t.configNote,...t.notes.map(a=>`note: ${a}`)].join(`
|
|
903
|
+
`)}function B2(e,t,n){return{dryRun:n,roots:t.rootReasons.map(({root:r,reason:o})=>({path:r,reason:o})),layers:t.layers,...t.terragrunt?{terragrunt:t.terragrunt}:{},...t.atmos?{atmos:t.atmos}:{},...t.terramate?{terramate:t.terramate}:{},binary:t.binary,version:t.version,...t.pins.length>0?{pins:t.pins}:{},forge:t.forge,image:t.image,files:t.files.map(r=>({path:qc(e,r.path),status:r.status,content:r.content})),notes:t.notes,configNote:t.configNote}}var UP="terragucci/pipeline",Tl=["-c","user.name=terragucci","-c","user.email=terragucci@users.noreply.intentius.io","-c","commit.gpgsign=false"];function yh(e,t){let n=po(e);if(!t)return{cloneUrl:`https://${n.host}/${n.path}.git`,origin:`https://${n.host}`};if(t.startsWith("/")||t.startsWith("file://"))return{cloneUrl:t,origin:`https://${n.host}`};let r=new URL(t);return{cloneUrl:t.endsWith(".git")?t:`${t}.git`,origin:`${r.protocol}//${r.host}`}}function wh(e,t){return!t||!/^https?:\/\//.test(e)?e:e.replace(/^(https?:\/\/)/,`$1oauth2:${encodeURIComponent(t)}@`)}function hh(e,t,n){try{return mbe("git",["-C",e,...t],{encoding:"utf-8",stdio:["ignore","pipe","pipe"]})}catch(r){let o=String(r.stderr??r.message);throw new Error(n?o.split(n).join("***"):o)}}async function kbe(e,t){try{let n=await yo().catch(()=>{});return await Lw(e,wt(e,t.roots),{settings:t,...n?{parser:n}:{}})}catch{return[]}}function _be(e,t){let n=qm(e,t);if(n.foreign.length)throw new S(n.foreign.join("; "));for(let r of n.files)r.status==="removed"?wbe(r.path):r.status!=="unchanged"&&bbe(r.path,r.content);return n.files}async function H2(e,t){if(!e.projects||Object.keys(e.projects).length===0)throw new S("reconcile needs a control repo config with projects; in a single repo, run terragucci init");let n=t.project?[t.project]:Object.keys(e.projects),r=t.env??process.env,o=t.fetch??globalThis.fetch,s=[];for(let i of n){let a=hbe(W2(vbe(),"terragucci-reconcile-"));try{let c=ot(e,i),u=po(i),l=c.forge??ir(u.host);if(!l)throw new S(`cannot tell which forge ${u.host} is; set forge for this project`);let d=c.token_env??Gn[l],p=r[d];if(t.mode==="apply"&&!p)throw new S(`${d} is not set; it holds the token for ${u.host}`);let{cloneUrl:f,origin:g}=yh(i,c.url),m=W2(a,"repo");hh(a,["clone","-q","--depth","1",wh(f,p),m],p);let h={...c,forge:l,...c.generate?{generate:AB(c)}:{}},y=c.generate?_be(m,h):[],w=await ik(m,{settings:h,name:u.name,dryRun:t.mode==="dry-run"}),b=[...y,...w.files].map(x=>({...x,path:$be(m,x.path)})),k=b.filter(x=>x.status!=="unchanged"),v=t.mode==="dry-run"&&c.tips?await kbe(m,c):void 0,$=v?{tips:v}:{};if(k.length===0){s.push({key:i,status:"unchanged",changes:b,...$});continue}if(t.mode==="dry-run"){s.push({key:i,status:"would-change",changes:b,...$});continue}let _={forge:l,origin:g,path:u.path,token:p},E=await lc(o,_);hh(m,["checkout","-q","-B",UP]),hh(m,["add","-A"]),hh(m,[...Tl,"commit","-q","-m","terragucci: update the pipeline from the control repo"]),hh(m,["push","-q","--force","origin",`HEAD:refs/heads/${UP}`],p);let R=await dc(o,_,{head:UP,base:E,title:"terragucci: update the pipeline",body:["The control repo's terragucci config changed what this repo's pipeline should be.","",...k.map(x=>`- ${x.status==="created"?"adds":"updates"} \`${x.path}\``),"","Review and merge it here; terragucci never writes this repo's default branch."].join(`
|
|
904
|
+
`)});s.push({key:i,status:"pull-request",changes:b,pullRequest:R.url})}catch(c){s.push({key:i,status:"failed",changes:[],error:c.message})}finally{ybe(a,{recursive:!0,force:!0})}}return s}function q2(e,t){let n=[];for(let o of e){let s=o.changes.filter(i=>i.status!=="unchanged").map(i=>`${i.status==="created"?"new":"changed"} ${i.path}`);o.status==="unchanged"?n.push(`${o.key}: unchanged`):o.status==="would-change"?n.push(`${o.key}: would write ${s.join(", ")}`):o.status==="pull-request"?n.push(`${o.key}: ${s.join(", ")} -> ${o.pullRequest}`):n.push(`${o.key}: FAILED ${o.error}`);for(let i of Gw(o.tips??[]))n.push(` ${i}`)}let r=e.filter(o=>o.status==="failed").length;return n.push(t==="dry-run"?`dry run: ${e.filter(o=>o.status==="would-change").length} of ${e.length} would change; nothing was written`:`${e.filter(o=>o.status==="pull-request").length} pull request(s) opened or updated`),r&&n.push(`${r} project(s) failed`),n.join(`
|
|
905
|
+
`)}import{execFileSync as Cbe}from"node:child_process";import{existsSync as VP,mkdtempSync as sJ,readdirSync as iJ,readFileSync as ck,rmSync as aJ,statSync as Nbe,writeFileSync as Dbe}from"node:fs";import{tmpdir as cJ}from"node:os";import{join as Ps,posix as uJ}from"node:path";import{execFileSync as Sbe}from"node:child_process";import{posix as Rbe}from"node:path";function xbe(e,t){for(let n of[["ls-remote","--tags","--refs","origin",`refs/tags/${t}/v*`],["tag","--list",`${t}/v*`]])try{let o=Sbe("git",["-C",e,...n],{encoding:"utf-8",stdio:["ignore","pipe","ignore"]}).split(`
|
|
906
|
+
`).filter(Boolean).map(s=>s.split(" ").pop().replace(/^refs\/tags\//,""));if(n[0]==="ls-remote"||o.length>0)return o}catch{}return[]}async function J2(e){let t,n=(i,a)=>{let c=pr(i);c&&(!t||wo(c,t.v)>0)&&(t={v:c,from:a})},r=e.module.replace(/^\.\//,"").replace(/\/+$/,"");if(!/^[a-z]+:\/\/|::/.test(r))for(let i of e.repos)for(let a of xbe(i,r))n(a.slice(r.length+1),`tag ${a}`);let o=e.env??process.env,s=/^(1|true|yes)$/i.test(o.TERRAGUCCI_REGISTRY_INSECURE??"");for(let i of new Set(e.oci)){let{host:a,repo:c}=wc(i),u=new ii(a,{fetch:e.fetch,scheme:s?"http":"https",user:o.TERRAGUCCI_REGISTRY_USER,password:o.TERRAGUCCI_REGISTRY_PASSWORD});for(let l of await u.tags(c))n(l,`${i}:${l}`)}for(let i of new Set(e.registries??[]))for(let a of await JF(i,e.fetch))n(a,`${i} ${a}`);return t?{version:Je(t.v),from:t.from}:void 0}function K2(e,t){return`${e.replace(/\/+$/,"")}/${Rbe.basename(t.replace(/\/+$/,""))}`}function V2(e,t){let n=e.replace(/^\.\//,"").replace(/\/+$/,"");if(/^[a-z]+:\/\/|::/.test(n))return[];if(vc(n))return[n.split("?")[0].toLowerCase()];let r=new Set;for(let o of t){let s=o.modules?.registry;if(s)try{r.add(bv(s,n).source.toLowerCase())}catch{}}return[...r]}var Z2=500,X2={success:"success",neutral:"success",skipped:"success",failure:"failure",failed:"failure",error:"failure",cancelled:"failure",canceled:"failure",timed_out:"failure",action_required:"failure",startup_failure:"failure",stale:"failure",warning:"failure"},Y2=e=>X2[e??""]??"pending";function BP(e,t){let n=new Map;for(let r of e){let o=t(r),s=n.get(o);(!s||(r.id??0)>(s.id??0))&&n.set(o,r)}return[...n.values()]}function WP(e,t){let n=o=>kt(e,t,"GET",o),r=encodeURIComponent(t.path);return{async defaultBranch(){return t.forge==="gitlab"?(await n(`/projects/${r}`)).default_branch??"main":(await n(`/repos/${t.path}`)).default_branch??"main"},async findPullRequest(o){if(t.forge==="gitlab"){let l=(await n(`/projects/${r}/merge_requests?state=all&source_branch=${encodeURIComponent(o)}&order_by=created_at&sort=desc`))[0];if(!l)return null;let d=l.state==="merged"?"merged":l.state==="opened"?"open":"closed",p=l.merge_commit_sha??l.squash_commit_sha??l.sha;return{url:l.web_url,state:d,body:l.description??"",...d==="merged"&&p?{mergeCommit:p}:{}}}let s=[];if(t.forge==="github"){let u=t.path.split("/")[0];s=await n(`/repos/${t.path}/pulls?state=all&head=${encodeURIComponent(`${u}:${o}`)}&per_page=10`)}else for(let u=1;u<=20;u++){let l;try{l=await n(`/repos/${t.path}/pulls?state=all&limit=50&page=${u}`)}catch(d){if(!(d instanceof ei&&d.status===404&&(await n(`/repos/${t.path}`)).empty===!0))throw d;l=[]}if(s.push(...l.filter(d=>d.head?.ref===o)),l.length<50)break}let i=s.sort((u,l)=>l.number-u.number)[0];if(!i)return null;let a=i.merged===!0||!!i.merged_at,c=a?"merged":i.state==="open"?"open":"closed";return{url:i.html_url,state:c,body:i.body??"",...a&&i.merge_commit_sha?{mergeCommit:i.merge_commit_sha}:{}}},async listPullRequests(o){let s=[];if(t.forge==="gitlab"){for(let a=1;a<=Z2/100;a++){let c=await n(`/projects/${r}/merge_requests?state=all&order_by=updated_at&sort=desc&per_page=100&page=${a}`);for(let u of c)u.source_branch?.startsWith(o)&&s.push({branch:u.source_branch,url:u.web_url,title:u.title??"",state:u.state==="merged"?"merged":u.state==="opened"?"open":"closed",body:u.description??""});if(c.length<100)break}return s}let i=t.forge==="github"?100:50;for(let a=1;a<=Z2/i;a++){let c;try{c=await n(t.forge==="github"?`/repos/${t.path}/pulls?state=all&sort=updated&direction=desc&per_page=100&page=${a}`:`/repos/${t.path}/pulls?state=all&sort=recentupdate&limit=50&page=${a}`)}catch(u){if(!(t.forge==="forgejo"&&u instanceof ei&&u.status===404&&(await n(`/repos/${t.path}`)).empty===!0))throw u;c=[]}for(let u of c){let l=u.head?.ref;if(!l?.startsWith(o))continue;let d=u.merged===!0||!!u.merged_at;s.push({branch:l,url:u.html_url,title:u.title??"",state:d?"merged":u.state==="open"?"open":"closed",body:u.body??""})}if(c.length<i)break}return s},async createPullRequest(o){return(await dc(e,t,o)).url},async commitChecks(o){if(t.forge==="gitlab"){let a=await n(`/projects/${r}/repository/commits/${o}/statuses?per_page=100`);return BP(a,c=>c.name).map(c=>({name:c.name,state:Y2(c.status)}))}let s=[];if(t.forge==="github"){let a=await n(`/repos/${t.path}/commits/${o}/check-runs?per_page=100`);for(let c of BP(a.check_runs??[],u=>u.name))s.push({name:c.name,state:c.status==="completed"?X2[c.conclusion??""]??"failure":"pending"})}let i=await n(`/repos/${t.path}/commits/${o}/statuses?${t.forge==="github"?"per_page":"limit"}=100`);for(let a of BP(i,c=>c.context))s.push({name:a.context,state:Y2(a.status??a.state)});return s}}}function HP(e,t){let n=e.find(s=>s.name===`apply/${t}`);if(n)return n.state;let r=e.find(s=>s.name==="terragucci/apply");if(r)return r.state;let o=e.filter(s=>/(^|\/ )apply(-wave-\d+)?( \(|$)/.test(s.name));return o.some(s=>s.state==="failure")?"failure":o.length===0||o.some(s=>s.state==="pending")?"pending":"success"}import{spawnSync as Ebe}from"node:child_process";import{existsSync as Tbe,readFileSync as bh,readdirSync as Abe,rmSync as Q2,writeFileSync as qP}from"node:fs";import{join as Os,posix as Obe}from"node:path";var pn=".terraform.lock.hcl",JP="terragucci_rollout_override.tf";function Al(e){let t=new Map;for(let n of e.matchAll(/provider\s+"([^"]+)"\s*\{([^}]*)\}/g)){let r=/\bversion\s*=\s*"([^"]+)"/.exec(n[2]);r&&t.set(n[1],r[1])}return t}function KP(e,t){let n=t.toLowerCase(),r=e.toLowerCase();return r===n||r.endsWith(`/${n}`)}function ak(e,t){for(let[n,r]of e)if(KP(n,t))return{address:n,version:r}}function eJ(e){return Tbe(e)?Abe(e).filter(t=>t.endsWith(".tf")&&t!==JP).sort():[]}function Pbe(e,t){let n=t.split("/").slice(-2).join("/").toLowerCase();for(let r of eJ(e))for(let o of bh(Os(e,r),"utf-8").matchAll(/([A-Za-z_][\w-]*)\s*=\s*\{([^{}]*)\}/g)){let s=/\bsource\s*=\s*"([^"]+)"/.exec(o[2])?.[1]?.toLowerCase();if(s&&(s===n||s.endsWith(`/${n}`)))return o[1]}return t.split("/").pop()}function jbe(e,t,n,r){let o=t.split("/").slice(-2).join("/").toLowerCase();return e.replace(/(\{[^{}]*\})/g,s=>{let i=/\bsource\s*=\s*"([^"]+)"/.exec(s)?.[1]?.toLowerCase();return!i||!(i===o||i.endsWith(`/${o}`))?s:s.replace(/(\bversion\s*=\s*")(=?\s*)([^"]+)(")/,(a,c,u,l,d)=>l.trim()===n?`${c}${u}${r}${d}`:a)})}var Ibe=(e,t,n)=>{let r=Ebe(e,t,{cwd:n,encoding:"utf-8",env:Ve({...process.env,TF_IN_AUTOMATION:"1",TF_INPUT:"0"})});return{status:r.status,output:`${r.stdout??""}${r.stderr??""}${r.error?r.error.message:""}`}};function tJ(e,t=Ibe){return(n,r,o)=>{let s=Pbe(n,r);qP(Os(n,JP),`terraform {
|
|
907
907
|
required_providers {
|
|
908
908
|
${s} = {
|
|
909
909
|
source = "${r}"
|
|
@@ -912,21 +912,21 @@ ${i}
|
|
|
912
912
|
}
|
|
913
913
|
}
|
|
914
914
|
`);try{for(let i of[["get","-no-color"],["providers","lock","-no-color",r]]){let a=t(e,i,n);if(a.status!==0)throw new Error(`${e} ${i.join(" ")} failed: ${a.output.trim().split(`
|
|
915
|
-
`).slice(-3).join(" ")}`)}}finally{
|
|
916
|
-
`));try{s(i,u.address,o)}catch(m){return{refused:m.message}}let f=bh(Os(i,pn),"utf-8"),g=Al(f);if(g.get(u.address)!==o)return{refused:`${a(pn)} has ${u.address} at ${g.get(u.address)??"nothing"} after locking, not ${o}`};for(let[m,h]of c)if(m!==u.address&&g.get(m)!==h)return{refused:`locking ${u.address} also moved ${m} from ${h} to ${g.get(m)??"nothing"}, so nothing was written`};return l.set(a(pn),f),{edits:l}}function
|
|
917
|
-
`)}async function Kbe(e,t,n,r){let o=r.kind==="module"||n.some($=>
|
|
915
|
+
`).slice(-3).join(" ")}`)}}finally{Q2(Os(n,JP),{force:!0}),Q2(Os(n,".terraform"),{recursive:!0,force:!0})}}}function nJ(e,t,n,r,o,s){let i=Os(e,t),a=m=>t==="."?m:Obe.join(t,m),c=Al(bh(Os(i,pn),"utf-8")),u=ak(c,n);if(!u)return{refused:`${a(pn)} has no ${n}`};let l=new Map;for(let m of eJ(i)){let h=bh(Os(i,m),"utf-8"),y=jbe(h,u.address,r,o);y!==h&&(qP(Os(i,m),y),l.set(a(m),y))}let d=bh(Os(i,pn),"utf-8"),p=u.address.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");qP(Os(i,pn),d.replace(new RegExp(`\\n?provider\\s+"${p}"\\s*\\{[^}]*\\}\\n?`),`
|
|
916
|
+
`));try{s(i,u.address,o)}catch(m){return{refused:m.message}}let f=bh(Os(i,pn),"utf-8"),g=Al(f);if(g.get(u.address)!==o)return{refused:`${a(pn)} has ${u.address} at ${g.get(u.address)??"nothing"} after locking, not ${o}`};for(let[m,h]of c)if(m!==u.address&&g.get(m)!==h)return{refused:`locking ${u.address} also moved ${m} from ${h} to ${g.get(m)??"nothing"}, so nothing was written`};return l.set(a(pn),f),{edits:l}}function rJ(e){let t=[],n=[];for(let o of e){let s=o.roots.filter(a=>o.canary.some(c=>Pe(c,a))),i=Nw(o.roots.map(a=>({root:a,dependsOn:[...o.dependsOn.get(a)??[]]})),s);s.length>0&&t.push({project:o.key,roots:i[0].roots}),n.push({project:o.key,layers:i.filter(a=>!a.canary).map(a=>a.roots)})}let r=[];t.length>0&&r.push({wave:1,canary:!0,parts:t});for(let{project:o,layers:s}of n)for(let i of s)r.push({wave:r.length+1,canary:!1,parts:[{project:o,roots:i}]});return r}function lJ(e){return e.status==="stopped"?1:e.status==="waiting"?3:0}function xi(e,t,n){try{return Cbe("git",["-C",e,...t],{encoding:"utf-8",stdio:["ignore","pipe","pipe"]})}catch(r){let o=String(r.stderr||r.message);throw new Error(n?o.split(n).join("***"):o)}}function dJ(e,t){return(n,r,o)=>{xi(e,["add","--",...r]),xi(e,[...Tl,"commit","-q","--no-verify","-m",o]),xi(e,["push","-q","--force","origin",`HEAD:refs/heads/${n}`],t)}}function uk(e){let t=sg(e);if(!t)return;let n=/^(https?):\/\/(?:[^@/]+@)?[^/]+\/(.+?)(?:\.git)?\/?$/.exec(e);if(n)return{origin:`${n[1]}://${t}`,path:n[2],host:t};let r=/^(?:[a-z+]+:\/\/)?(?:[^@]+@)?[^:/]+[:/](.+?)(?:\.git)?\/?$/.exec(e);return r?{origin:`https://${t.replace(/:\d+$/,"")}`,path:r[1].replace(/^\d+\//,""),host:t}:void 0}function pJ(e,t,n){let r=n.env??process.env,o;try{o=xi(e,["remote","get-url","origin"]).trim()}catch{throw new S("this repository has no origin remote, so there is nowhere to open a pull request")}let s=t.forge??Kr(e)?.value,i=t.url?uk(t.url):uk(o),a=s?r[t.token_env??Gn[s]]??"":"",c=s&&i?{forge:s,origin:i.origin,path:i.path,token:a}:void 0,u=i?`${i.host}/${i.path}`:".",l;if(n.forge)l=n.forge({key:u,target:c});else if(c)l=WP(n.fetch??globalThis.fetch,c);else throw new S("cannot tell which forge this repository's origin is on; set forge (and url) in terragucci.yml");if(n.mode==="apply"&&!n.forge&&!a)throw new S(`${t.token_env??Gn[s]} is not set; it holds the forge token`);return{key:u,forge:l,forgeName:s,token:a}}function fJ(e,t,n){let r=n.env??process.env,o=ot(e,t),s=po(t),i=o.forge??ir(s.host);if(!i)throw new S(`cannot tell which forge ${s.host} is; set forge for this project`);let a=o.token_env??Gn[i],c=r[a]??"";if(n.mode==="apply"&&!n.forge&&!c)throw new S(`${a} is not set; it holds the token for ${s.host}`);let{cloneUrl:u,origin:l}=yh(t,o.url),d={forge:i,origin:l,path:s.path,token:c},p=n.forge?n.forge({key:t,target:d}):WP(n.fetch??globalThis.fetch,d);return{key:t,forge:p,forgeName:i,token:c,settings:o,cloneUrl:u}}async function Mbe(e,t,n){let{key:r,forge:o}=pJ(e,t,n);xi(e,["fetch","-q","origin"]);let s=await o.defaultBranch(),i=sJ(Ps(cJ(),"terragucci-rollout-"));return xi(e,["worktree","add","-q","--detach",i,`refs/remotes/origin/${s}`]),{key:r,settings:t,dir:i,base:s,forge:o,push:dJ(i),cleanup:()=>{try{xi(e,["worktree","remove","--force",i])}catch{aJ(i,{recursive:!0,force:!0})}}}}async function Lbe(e,t,n){let{settings:r,forge:o,token:s,cloneUrl:i}=fJ(e,t,n),a=await o.defaultBranch(),c=sJ(Ps(cJ(),"terragucci-rollout-")),u=Ps(c,"repo");return xi(c,["clone","-q","--depth","1","--branch",a,wh(i,s||void 0),u],s||void 0),{key:t,settings:r,dir:u,base:a,forge:o,push:dJ(u,s||void 0),cleanup:()=>aJ(c,{recursive:!0,force:!0})}}var Gbe=new Set(["node_modules",".terraform",".terragrunt-cache"]);function gJ(e){let t=[],n=r=>{let o=Ps(e,r);for(let s of iJ(o)){if(s.startsWith(".")||Gbe.has(s))continue;let i=r?uJ.join(r,s):s;Nbe(Ps(o,s)).isDirectory()?n(i):s==="terragrunt.hcl"&&r&&t.push(r)}};return n(""),t}function Fbe(e){return[...new Set([...wt(e.dir,e.settings.roots),...gJ(e.dir)])].sort()}function zbe(e,t,n,r,o){let s={project:e,root:t};if(n.length===0)return{status:{...s,state:"absent"}};let i=n.filter(u=>u.pin.pin===null);if(i.length>0)return{status:{...s,state:"refused",reason:i.map(u=>`${u.file} ${u.call}: ${u.pin.unpinned}`).join("; ")},calls:n};let a=(u,l)=>u.version===l||u.pin.pin===l;if(n.every(u=>a(u,o)))return{status:{...s,state:"to",version:o},calls:n};if(r!==void 0&&n.every(u=>a(u,r)||a(u,o)))return{status:{...s,state:"from",version:r},calls:n};let c=[...new Set(n.filter(u=>!(r!==void 0&&a(u,r))&&!a(u,o)).map(u=>u.version))];return r!==void 0&&n.some(u=>a(u,r))?{status:{...s,state:"refused",reason:`its calls are pinned at more than one version (${[r,...c].join(", ")})`},calls:n}:{status:{...s,state:"elsewhere",version:c.join(", "),reason:`pinned at ${c.join(", ")}`},calls:n}}function Ube(e){return e.split("/").slice(-2).join("/").toLowerCase()}function Bbe(e,t,n,r,o,s){let i={project:t,root:n},a=Ps(e,n,pn);if(!VP(a)){let u=Ube(r);return iJ(Ps(e,n)).filter(d=>d.endsWith(".tf")).some(d=>ck(Ps(e,n,d),"utf-8").toLowerCase().includes(`"${u}"`))?{...i,state:"refused",reason:`it has no ${pn}, so init picks the ${u} version and the diff cannot show it`}:{...i,state:"absent"}}let c=ak(Al(ck(a,"utf-8")),r);return c?c.version===s?{...i,state:"to",version:s}:c.version===o?{...i,state:"from",version:o}:{...i,state:"elsewhere",version:c.version,reason:`locked at ${c.version}`}:{...i,state:"absent"}}function Wbe(e,t){if(e.state!=="refused"||!e.reason||e.reason===ed||e.reason===wf)return;let n={project:e.project,root:e.root};return/is a constraint/.test(e.reason)?{...n,rule:"rollout-floating-pin",message:`${e.root}: ${e.reason}. Pin one version, such as version = "${t}", so a rollout can move it and the diff shows which roots it moves.`}:/is an expression/.test(e.reason)?{...n,rule:"rollout-literal-pin",message:`${e.root}: ${e.reason}. Write the pin as a literal: a pin set from a variable or local cannot be read from the diff, so a rollout cannot tell which roots it moves.`}:/no \.terraform\.lock\.hcl/.test(e.reason)?{...n,rule:"rollout-lock-file",message:`${e.root}: ${e.reason}. Commit ${pn} so a provider bump is a reviewed change.`}:/more than one version/.test(e.reason)?{...n,rule:"rollout-one-pin",message:`${e.root}: ${e.reason}. Pin every call of the module at one version.`}:{...n,rule:"rollout-pin",message:`${e.root}: ${e.reason}. Pin it to one version (a ?ref=, ?tag= or version) so a rollout can move it.`}}var mJ="terragucci-rollout";function oJ(e){return e.replace(/^[a-z]+:\/\//,"").replace(/[^A-Za-z0-9._-]+/g,"-").replace(/^-+|-+$/g,"").slice(0,60)}function Hbe(e,t,n){return`terragucci/rollout/${oJ(e)}-${oJ(t)}/wave-${n}`}function hJ(e){let t=new RegExp(`<!-- ${mJ} (\\{.*?\\}) -->`).exec(e);try{return t?JSON.parse(t[1]):void 0}catch{return}}function qbe(e){return hJ(e)?.roots}async function ZP(e,t){let n=t.mode??"dry-run",r;try{r=xi(e,["rev-parse","--show-toplevel"]).trim()}catch{throw new S("terragucci rollout runs in a git repository")}let o=t.config??fe(r),s=o?await Re(o):{};if(t.kind==="provider"&&!t.to)throw new S("a provider rollout needs the version to move to");let i=t.parser??(t.kind==="module"?await yo():void 0),a=[];try{if(s.projects)for(let c of Object.keys(s.projects))a.push(await Lbe(s,c,{...t,mode:n}));else{let c=ue(s),u=et(r,c).refuses("rollouts");if(u)throw new S(`terragucci rollout: ${u}`);a.push(await Mbe(r,c,{...t,mode:n}))}return await Kbe(r,s,a,{...t,mode:n,parser:i})}finally{for(let c of a)c.cleanup()}}var Jbe="terragucci/rollout/";function YP(e){return e.rollouts.some(t=>t.action==="failed")?1:0}async function yJ(e,t={}){let n=t.mode??"dry-run",r;try{r=xi(e,["rev-parse","--show-toplevel"]).trim()}catch{throw new S("terragucci respond rollout runs in a git repository")}let o=t.config??fe(r),s=o?await Re(o):{},i=s.projects?void 0:et(r,ue(s)).refuses("rollouts");if(i)throw new S(`terragucci respond rollout: ${i}`);let a=s.projects?Object.keys(s.projects).map(l=>fJ(s,l,{...t,mode:n})):[pJ(r,ue(s),{...t,mode:n})],c=new Map;for(let l of a){let d=new Set;for(let p of await l.forge.listPullRequests(Jbe)){if(d.has(p.branch))continue;d.add(p.branch);let f=hJ(p.body);if(!f||f.kind!=="module"&&f.kind!=="provider"||!f.name||!f.to||!Number.isInteger(f.wave))continue;let g=/, wave \d+ of (\d+)$/.exec(p.title),m={...f,...f.waves===void 0&&g?{waves:Number(g[1])}:{}},h=JSON.stringify([f.kind,f.name,f.to]);c.set(h,[...c.get(h)??[],{marker:m,state:p.state,url:p.url}])}}let u=[];for(let l of c.values()){let d=Math.max(...l.map(w=>w.marker.wave)),p=l.filter(w=>w.marker.wave===d),f=p[0].marker,g=p.map(w=>w.marker.waves).find(w=>w!==void 0),m={kind:f.kind,name:f.name,...f.from!==void 0?{from:f.from}:{},to:f.to,wave:d,...g!==void 0?{waves:g}:{},action:"ran",pullRequests:p.map(w=>w.url)},h=p.filter(w=>w.state==="open"),y=p.filter(w=>w.state==="closed");if(h.length>0)u.push({...m,action:"waiting",reason:`wave ${d} is open: ${h.map(w=>w.url).join(", ")}`});else if(y.length>0)u.push({...m,action:"stopped",reason:`wave ${d} was closed without merging: ${y.map(w=>w.url).join(", ")}`});else if(g!==void 0&&d>=g)u.push({...m,action:"done",reason:`wave ${d} of ${g}, the last, merged`});else try{let w=await ZP(r,{...t,mode:n,kind:f.kind,name:f.name,to:f.to,...f.from!==void 0?{from:f.from}:{}});u.push({...m,result:w})}catch(w){u.push({...m,action:"failed",reason:w.message})}}return{mode:n,rollouts:u}}function wJ(e){return e.rollouts.length===0?"no rollout in flight":e.rollouts.map(t=>t.result?XP(t.result):`${t.kind==="module"?t.name:`provider ${t.name}`} ${t.from??"?"} -> ${t.to}: ${t.action} (${t.reason})`).join(`
|
|
917
|
+
`)}async function Kbe(e,t,n,r){let o=r.kind==="module"||n.some($=>gJ($.dir).length>0),s=r.parser??(o?await yo():void 0),i=new Map,a=new Map;r.kind==="module"&&(r={...r,aliases:V2(r.name,[...n.map($=>$.settings),t.defaults??{}])});let c=new Map(n.map($=>[$.key,et($.dir,$.settings).refuses("rollouts")]));for(let $ of n)if(i.set($.key,c.get($.key)?[]:Fbe($)),r.kind==="module")for(let _ of i.get($.key))a.set(`${$.key}\0${_}`,await pT($.dir,_,r.name,s,r.aliases));let u=r.to,l;if(!u){let $=new Set;for(let R of[...n.map(x=>x.settings),t.defaults??{}]){let x=R.modules?.publish;for(let A of Array.isArray(x)?x:x?[x]:[])A.startsWith("oci://")&&$.add(K2(A,r.name))}for(let R of a.values())for(let x of R)x.pin.module.startsWith("oci://")&&$.add(x.pin.module.split("?")[0]);let _=new Set(r.aliases??[]);for(let R of a.values())for(let x of R)x.pin.at==="version"&&vc(x.pin.module)&&_.add(x.pin.module);let E=await J2({module:r.name,repos:[e,...n.map(R=>R.dir)],oci:[...$],registries:[..._],env:r.env,fetch:r.registryFetch});if(!E)throw new S(`found no published version of ${r.name}; name the version to roll out`);u=E.version,l=E.from}let d=r.kind==="module"?hc(u):u,p=r.from!==void 0?hc(r.from):void 0;if(p===void 0){let $=new Set;if(r.kind==="module")for(let _ of a.values())for(let E of _)E.version!==null&&E.version!==d&&$.add(E.version);else for(let _ of n)for(let E of i.get(_.key)){let R=Ps(_.dir,E,pn),x=VP(R)?ak(Al(ck(R,"utf-8")),r.name):void 0;x&&x.version!==d&&$.add(x.version)}if($.size>1)throw new S(`${r.name} is at ${[...$].sort().join(", ")} across the roots; pass --from to say which version moves`);p=[...$][0]}let f=[],g=new Map(n.map($=>[$.key,$])),m=new Map;for(let $ of n){let _=[],E=c.get($.key);E&&f.push({project:$.key,root:".",state:"refused",reason:E});for(let R of i.get($.key)){let x=r.kind==="module"?zbe($.key,R,a.get(`${$.key}\0${R}`),p,d).status:Bbe($.dir,$.key,R,r.name,p,d);x.state!=="absent"&&f.push(x),(x.state==="from"||x.state==="to")&&_.push(R)}m.set($.key,_)}let h=($,_)=>f.find(E=>E.project===$&&E.root===_)?.state,y=[],w=[];for(let $ of n){let _=m.get($.key);if(_.length===0)continue;let E=wn($.dir,i.get($.key));for(let R of _){let x=Ps($.dir,R,"terragrunt.hcl");if(VP(x))for(let A of await Dw(R,ck(x,"utf-8"),s))E.get(R)?.add(A)??E.set(R,new Set([A]))}w.push({key:$.key,roots:_,dependsOn:E,canary:$.settings.waves?.canary??[]})}try{y.push(...rJ(w))}catch($){throw new S($.message)}let b={kind:r.kind,name:r.name,...p!==void 0?{from:p}:{},to:d,...l?{discovered:l}:{},mode:r.mode,status:"complete",waves:y.map($=>({wave:$.wave,canary:$.canary,parts:$.parts.map(_=>({..._,branch:Hbe(r.name,d,$.wave),state:"not-reached"}))})),roots:f,tips:[]},k=$=>(r.locker??tJ)($.settings.binary??Ks($.dir,i.get($.key)).value);for(let $ of b.waves){let _=[],E=!1;for(let R of $.parts){let x=g.get(R.project),A=await x.forge.findPullRequest(R.branch);if(A&&(R.pullRequest=A.url),A?.state==="open")R.state="open",E=!0;else if(A?.state==="closed")R.state="closed",b.stop??=`wave ${$.wave}: ${A.url} was closed without merging`;else if(A?.state==="merged"){let O=qbe(A.body)??R.roots,I=A.mergeCommit?await x.forge.commitChecks(A.mergeCommit):[];R.failed=O.filter(N=>HP(I,N)==="failure"),R.pending=O.filter(N=>HP(I,N)==="pending"),R.failed.length>0?(R.state="failed",b.stop??=`wave ${$.wave}: ${R.failed.join(", ")} in ${R.project} failed to apply after ${A.url} merged`):R.pending.length>0?(R.state="waiting-apply",E=!0):R.state="applied"}else R.roots.some(O=>h(R.project,O)==="from")?_.push(R):R.state="nothing-to-move"}if(b.stop){b.status="stopped";break}if(_.length>0){for(let R of _)await Vbe(g.get(R.project),R,$,b,r,k,s);b.stop?b.status="stopped":b.status=r.mode==="apply"?"opened":"would-open";break}if(E){b.status="waiting";break}}let v=$=>g.get($)?.settings.tips!==!1;return b.tips=f.filter($=>v($.project)).map($=>Wbe($,d)).filter($=>$!==void 0),b}async function Vbe(e,t,n,r,o,s,i){let a=t.roots.filter(g=>r.roots.find(m=>m.project===t.project&&m.root===g)?.state==="from");if(o.mode==="dry-run"&&o.kind==="provider"){t.files=a.map(g=>g==="."?pn:uJ.join(g,pn)).sort(),t.state="would-open";return}let c=new Map;for(let g of a){let m=o.kind==="module"?await wF(e.dir,await pT(e.dir,g,o.name,i,o.aliases),r.from,r.to,i):nJ(e.dir,g,o.name,r.from,r.to,s(e));if("refused"in m){t.state="failed",t.reason=m.refused,r.stop??=`wave ${n.wave}: ${g} in ${t.project} cannot move: ${m.refused}`;return}for(let[h,y]of m.edits)c.set(h,y)}if(t.files=[...c.keys()].sort(),o.mode==="dry-run"){t.state="would-open";return}for(let[g,m]of c)Dbe(Ps(e.dir,g),m);let u=r.waves.length,l=o.kind==="module"?`${o.name} ${r.from} -> ${r.to}`:`provider ${o.name} ${r.from} -> ${r.to}`,d=`terragucci rollout: ${l}, wave ${n.wave} of ${u}`,p=r.roots.filter(g=>g.project===t.project&&(g.state==="refused"||g.state==="elsewhere")),f=[`<!-- ${mJ} ${JSON.stringify({kind:o.kind,name:o.name,from:r.from,to:r.to,wave:n.wave,waves:u,roots:a})} -->`,`Wave ${n.wave} of ${u}${n.canary?" (canaries)":""}: ${l}.`,"",`This pull request moves ${o.kind==="module"?"the pin":`\`${pn}\``} for these roots and changes no other file:`,"",...a.map(g=>`- \`${g}\``),...p.length>0?["","Not moved by this rollout:","",...p.map(g=>`- \`${g.root}\`: ${g.reason}`)]:[],"",n.wave<u?`Wave ${n.wave+1} opens on a later run, once every pull request of this wave has merged and its roots have applied.`:"This is the last wave."].join(`
|
|
918
918
|
`);e.push(t.branch,t.files,`${d}
|
|
919
919
|
|
|
920
920
|
Roots: ${a.join(", ")}
|
|
921
921
|
`),t.pullRequest=await e.forge.createPullRequest({base:e.base,head:t.branch,title:d,body:f}),t.state="opened"}var Zbe={applied:"merged and applied","nothing-to-move":"nothing to move",opened:"pull request opened","would-open":"next; would open its pull request",open:"pull request open, waiting for merge","waiting-apply":"merged, waiting for apply",failed:"failed",closed:"pull request closed without merging","not-reached":"not opened"};function XP(e){let n=[`${e.kind==="module"?e.name:`provider ${e.name}`} ${e.from??"?"} -> ${e.to}${e.discovered?` (newest published: ${e.discovered})`:""}: ${e.status}${e.stop?` (${e.stop})`:""}`],r=new Set(e.waves.flatMap(o=>o.parts.map(s=>s.project))).size>1;for(let o of e.waves){n.push(` wave ${o.wave}${o.canary?" (canaries)":""}`);for(let s of o.parts)n.push(` ${r?`${s.project}: `:""}${Zbe[s.state]}${s.pullRequest?` ${s.pullRequest}`:""}${s.reason?` (${s.reason})`:""}`),n.push(` roots: ${s.roots.join(", ")}`),s.pending?.length&&n.push(` apply pending: ${s.pending.join(", ")}`),s.failed?.length&&n.push(` apply failed: ${s.failed.join(", ")}`),s.files?.length&&n.push(` files: ${s.files.join(", ")}`)}for(let o of e.roots.filter(s=>s.state==="refused"||s.state==="elsewhere"))n.push(` not in the rollout: ${r?`${o.project} `:""}${o.root}: ${o.reason}`);for(let o of e.tips)n.push(` tip (${o.rule}): ${o.message}`);return e.mode==="dry-run"&&(e.status==="would-open"||e.status==="complete")&&n.push("dry run: nothing was opened"),n.join(`
|
|
922
|
-
`)}function
|
|
923
|
-
`).filter(Boolean);if(p.length===0){c.push({branch:u.branch,title:u.title,files:p,state:"nothing-to-change"});continue}no(d.dir,[...Tl,"commit","-q","--no-verify","-m",u.title]),no(d.dir,["push","-q","--force","origin",`HEAD:refs/heads/${u.branch}`]);let f=await dc(i,s,{head:u.branch,base:l,title:u.title,body:u.body});c.push({branch:u.branch,title:u.title,files:p,state:f.existing?"open":"opened",pullRequest:f.url})}finally{d.done()}}return c}var Pp="tf-unlock",pk=`_gates/${Pp}.jsonl`,$
|
|
922
|
+
`)}function bJ(e,t){let n=i=>{let a=t[i];if(a===!0)throw new S(`--${i} needs a value`);return a},r=cd(n("mode")??"dry-run"),o=n("provider"),s="usage: terragucci rollout <module> [<version>] | terragucci rollout --provider <address> <version>";if(o){if(e.length!==1)throw new S(s);return{kind:"provider",name:o,to:e[0],from:n("from"),mode:r,config:n("config")}}if(e.length<1||e.length>2)throw new S(s);return{kind:"module",name:e[0],to:e[1],from:n("from"),mode:r,config:n("config")}}function no(e,t){try{return Ybe("git",["-C",e,...t],{encoding:"utf-8",stdio:["ignore","pipe","pipe"]}).trim()}catch(n){throw new S(`git ${t[0]}: ${String(n.stderr||n.message).trim()}`)}}function vh(e,t,n){let r="";try{r=no(e,["remote","get-url","origin"])}catch{throw new S("this repository has no origin remote, so there is nowhere to open a pull request")}let o=t.forge??Kr(e)?.value,s=uk(t.url??r);if(!o||!s)throw new S("cannot tell which forge this repository's origin is on; set forge (and url) in terragucci.yml");return{forge:o,origin:s.origin,path:s.path,token:n[t.token_env??Gn[o]]??""}}function QP(e,t){let n=lk(Qbe(lk(nve(),"terragucci-respond-")),"tree");return no(e,["worktree","add","-q","--detach",n,t]),{dir:n,done:()=>{try{no(e,["worktree","remove","--force",n])}catch{eve(n,{recursive:!0,force:!0})}}}}async function dk(e,t,n,r){if(r.mode==="dry-run")return n.map(u=>({branch:u.branch,title:u.title,files:[...u.files.keys(),...u.expect??[]].sort(),state:"would-open"}));let o=r.env??process.env,s=vh(e,t,o);if(!s.token)throw new S(`${t.token_env??Gn[s.forge]} is not set; it holds the forge token`);let i=r.fetch??globalThis.fetch,a,c=[];for(let u of n){let l=u.base??(a??=await lc(i,s));no(e,["fetch","-q","origin",l]);let d=QP(e,"FETCH_HEAD");try{for(let[g,m]of u.files)Xbe(rve(lk(d.dir,g)),{recursive:!0}),tve(lk(d.dir,g),m);u.run?.(d.dir),no(d.dir,["add","-A"]);let p=no(d.dir,["diff","--cached","--name-only"]).split(`
|
|
923
|
+
`).filter(Boolean);if(p.length===0){c.push({branch:u.branch,title:u.title,files:p,state:"nothing-to-change"});continue}no(d.dir,[...Tl,"commit","-q","--no-verify","-m",u.title]),no(d.dir,["push","-q","--force","origin",`HEAD:refs/heads/${u.branch}`]);let f=await dc(i,s,{head:u.branch,base:l,title:u.title,body:u.body});c.push({branch:u.branch,title:u.title,files:p,state:f.existing?"open":"opened",pullRequest:f.url})}finally{d.done()}}return c}var Pp="tf-unlock",pk=`_gates/${Pp}.jsonl`,$J=`_gates/${Pp}/done.jsonl`,Ol={released:0,refused:1,waiting:3,other:4},ive=120*1e3;function xJ(e){try{let t=JSON.parse(e);if(!t||typeof t!="object"||typeof t.ID!="string"||t.ID==="")return;let n=r=>typeof t[r]=="string"?t[r]:void 0;return{ID:t.ID,Operation:n("Operation"),Info:n("Info"),Who:n("Who"),Version:n("Version"),Created:n("Created"),Path:n("Path")}}catch{return}}var ave=(e,t,n)=>Rn("terragucci-state-lock",{root:e,location:t,id:n}),cve=(e,t,n=!1)=>`terragucci approve unlock ${e} --plan ${t}${n?" --sign":""}`,kh=e=>e?Date.parse(e):Number.NaN;async function uve(e,t){let n=new Map;if(t.forge==="gitlab"){for(let s of["running","pending","preparing","waiting_for_resource"]){let i=await kt(e,t,"GET",`/projects/${encodeURIComponent(t.path)}/pipelines?status=${s}&per_page=100`);for(let a of Array.isArray(i)?i:[])n.set(String(a.id),{id:String(a.id),status:String(a.status??s),started:a.started_at??a.created_at,url:a.web_url})}return[...n.values()]}let r=t.forge==="github"?["in_progress","queued","waiting","pending","requested"]:["running","waiting","blocked"],o=t.forge==="github"?"per_page=100":"limit=50";for(let s of r){let i=await kt(e,t,"GET",`/repos/${t.path}/actions/runs?status=${s}&${o}`);for(let a of i.workflow_runs??[]){let c=t.forge==="github"?a.run_started_at??a.created_at:a.started??a.created,u=c&&kh(c)>0?c:a.created??a.created_at;n.set(String(a.id),{id:String(a.id),status:String(a.status??s),started:u,url:a.html_url})}}return[...n.values()]}function lve(e,t,n=ive){let r=kh(t);return e.filter(o=>Number.isNaN(r)||Number.isNaN(kh(o.started))||kh(o.started)<=r+n)}var dve=(e,t,n,r)=>{let o=RJ(e,[`-chdir=${n}`,...t],{encoding:"utf-8",env:Ve(r),maxBuffer:67108864});return{status:o.status??1,out:`${o.stdout??""}${o.stderr??""}${o.error?o.error.message:""}`}},EJ=e=>e.trim().split(`
|
|
924
924
|
`).slice(-10).join(`
|
|
925
|
-
`);function
|
|
926
|
-
${
|
|
927
|
-
${E2(f.out)}`);let g=pve(i,Hr(a,d.rootEnv(i,o))),m=await fve(i,g,p,a,o,s,n),h=m.location,y=await m.read();if(y===void 0)return r(`${i}: no lock is held on ${h}; nothing to release`),{code:Ol.released,location:h};let w=x2(y.text);if(!w)throw new S(`${i}: ${h} holds no lock ID the binary wrote; read it, and remove it by hand if it is not a lock`);let b={...w,...y.id?{ID:y.id}:{}},k=ave(i,h,b.ID);r(`${i}: ${h} holds ${$h(b)}`);let v=vh(e,l,o),$=l.token_env??Gn[v.forge];if(!v.token)throw new S(`${$} is not set; unlock-state reads the forge's runs with it, to know no run holding the lock is alive`);let _=n.fetch??globalThis.fetch,E=0,R=async()=>{let Q;try{Q=await uve(_,v)}catch(ne){throw new S(`${i}: the forge's runs could not be read (${ne.message}), so nothing says the run holding the lock is gone`)}return E=Q.length,lve(Q,b.Created)},x=await R();if(x.length>0){r(`${i}: ${x.length===1?"a run that began before the lock is":`${x.length} runs that began before the lock are`} still alive, so the lock may be theirs and is not released:`);for(let Q of x)r(` ${_2(Q)}`);return r("Run this again once they have finished, or stop them on the forge first."),{code:Ol.refused,lock:b,location:h,digest:k,alive:x}}r(`${i}: no run that began before the lock is alive`);let A=n.now??new Date().toISOString(),O=await Yr(e,{at:"HEAD",...c?{config:c}:{}}),I=O.mode==="sealed",N=ft(e,pk);I&&(N={...N,resolutions:N.resolutions.filter(Q=>{if(Q.gate!==i)return!0;let ne=ws(O.signers,Q);return ne!==null&&Ie(Q.planDigest,k)&&r(`${i}: an approval does not count: ${ne}`),ne===null})});let L=ma(N,i,k,A),U=cve(i,k,I),F=k2(e,["rev-parse","HEAD"]);if(L.status!=="approved"){if(!L.standing){let Q={version:1,kind:"pending",op:Pp,gate:i,timestamp:A,expiresAt:new Date(kh(A)+1728e5).toISOString(),planDigest:k,description:`release ${$h(b)} on ${h}`,...F?{commit:F}:{},neverOverMcp:!0};ha(e,Q,{},pk)}return L.status==="refused"?(r(`${i}: ${L.by} approved ${L.approved??"another digest"}, the release of another lock; this one is not released`),r(`${i}: to release ${$h(b)}, approve it with:`),r(` ${U}`),{code:Ol.other,lock:b,location:h,digest:k,command:U}):(r(`${i}: releasing ${$h(b)} waits for an approval of digest ${k}. Approve it with:`),r(` ${U}`),r("Then run this again."),{code:Ol.waiting,lock:b,location:h,digest:k,command:U})}r(`${i}: approved by ${L.by} for this lock`);let M=await R();if(M.length>0){r(`${i}: a run that began before the lock is alive again, so the lock is not released:`);for(let Q of M)r(` ${_2(Q)}`);return{code:Ol.refused,lock:b,location:h,digest:k,alive:M}}await m.release(b.ID);let te=n.actor||k2(e,["config","user.name"])||o.USER||o.USERNAME||"unknown",ie={version:1,kind:"unlock",op:Pp,gate:i,root:i,location:h,lock:b,planDigest:k,approvedBy:L.by,approvedAt:L.at,releasedBy:te,timestamp:new Date().toISOString(),liveRuns:E,...F?{commit:F}:{}};return hr(e,$2,[JSON.stringify(ie)],{},`Released the state lock of ${i}: ${b.ID}`),r(`${i}: released ${$h(b)}, approved by ${L.by}; recorded in ${$2} on chant/lifecycle`),{code:Ol.released,lock:b,location:h,digest:k}}var ro=e=>new Date(e).getTime();function _h(e){let t=new Map;for(let r of e.pending){let o=t.get(r.gate);/^wave-\d+$/.test(r.gate)&&r.planDigest&&(!o||ro(r.timestamp)>=ro(o.timestamp))&&t.set(r.gate,r)}let n=[];for(let[r,o]of t)e.resolutions.some(i=>i.gate===r&&i.planDigest===o.planDigest&&ro(i.timestamp)>=ro(o.timestamp))||n.push({wave:Number(r.slice(5)),digest:o.planDigest,since:o.timestamp,expiresAt:o.expiresAt,...o.description?{description:o.description}:{},...yl(o)?{runId:yl(o)}:{},...o.commit?{commit:o.commit}:{},...o.resume?{resume:o.resume}:{},...o.answer?.id?{question:o.answer.id}:{}});return n.sort((r,o)=>r.wave-o.wave)}function hve(e){if(!e)return[" (the wave kept no report of its plans; read them in its job's log or report)"];try{let t=JSON.parse(e),n=[` roots: ${(t.roots??[]).map(o=>o.path).join(", ")||"none"}`],r=(t.named??[]).filter(o=>o.action==="delete"||o.action==="replace");for(let o of r)n.push(` ${o.action==="delete"?"destroys":"replaces"} ${o.root}${o.address?`: ${o.address}`:""}`);return r.length===0&&n.push(" destroys and replaces nothing"),n}catch{return[" (the wave's kept report could not be read)"]}}async function A2(e,t={}){let n=t.log??(g=>console.log(g)),r=_h(ft(e)),o=yve(ft(e,ga)),s=t.wave!==void 0&&!/^(?:wave-)?\d+$/.test(t.wave)?t.wave:void 0;if(s!==void 0){let g=o.find(m=>m.migration===s);if(!g)throw new S(`no migration ${s} waits for an approval${o.length?`; waiting: ${o.map(m=>m.migration).join(", ")}`:""}`);return t.plan!==void 0&&!Ie(g.digest,t.plan.trim())?(n(`not approved: migration ${s} waits for ${g.digest}, not ${t.plan.trim()}. The states moved since that digest; read the new proof, then approve its digest`),{code:1,command:""}):e0(e,g,t,n)}if(t.plan!==void 0&&t.wave===void 0){let g=o.find(m=>Ie(m.digest,t.plan.trim()));if(g)return e0(e,g,t,n)}if(t.plan===void 0&&t.wave===void 0&&r.length+o.length===1&&o.length===1)return e0(e,o[0],t,n);if(t.plan===void 0&&t.wave===void 0&&r.length+o.length>1&&o.length>0)throw new S(`${r.length+o.length} gates wait (${[...r.map(g=>Ct(g.wave)),...o.map(g=>`migration ${g.migration}`)].join(", ")}); name one: terragucci approve wave-<k> or terragucci approve <migration>`);let i;if(t.plan!==void 0){let g=t.plan.trim();if(!/^\S+$/.test(g))throw new S("--plan takes the digest to approve, such as jcs1-sha256:...");let m=t.wave===void 0?void 0:Number(/^(?:wave-)?(\d+)$/.exec(t.wave)?.[1]);if(m!==void 0&&(!Number.isInteger(m)||m<1))throw new S(`approve takes a wave as wave-<k> or <k>, not ${JSON.stringify(t.wave)}`);let h=m===void 0?r:r.filter(y=>y.wave===m);if(i=h.find(y=>Ie(y.digest,g)),!i){let y=m===void 0?"no wave":Ct(m),w=h.length>0?`; waiting: ${h.map(b=>`${Ct(b.wave)} for ${b.digest}`).join(", ")}`:m===void 0?": no wave waits for an approval":" is not waiting";return n(`not approved: ${y} waits for ${g}${w}. The plans moved since that digest, or were approved and applied; read the waiting plans, then approve their digest`),{code:1,command:""}}}else if(t.wave!==void 0){let g=Number(/^(?:wave-)?(\d+)$/.exec(t.wave)?.[1]);if(!Number.isInteger(g)||g<1)throw new S(`approve takes a wave as wave-<k> or <k>, not ${JSON.stringify(t.wave)}`);if(i=r.find(m=>m.wave===g),!i)throw new S(`${Ct(g)} is not waiting${r.length?`; waiting: ${r.map(m=>Ct(m.wave)).join(", ")}`:": no wave waits for an approval"}`)}else if(r.length===1)i=r[0];else throw r.length===0?new S("no wave waits for an approval on chant/lifecycle"):new S(`${r.length} waves wait (${r.map(g=>Ct(g.wave)).join(", ")}); name one: terragucci approve wave-<k>`);let a=i,c=fe(e),u=Yu(e,c?await Re(c):{})?.mode??"ledger";n(`${Ct(a.wave)} waits for an approval of ${a.digest}${a.description?` (${a.description})`:""}, since ${a.since}`);for(let g of hve(QO(e,a.wave,a.digest)))n(g);ro(a.expiresAt)<Date.now()&&n(` its pending fact expired at ${a.expiresAt}; the approval still counts if the next run plans the same digest`);let l=t.sign??(u==="sealed"?!0:void 0),d=["approve","tf-apply",Ct(a.wave),"--plan",a.digest,...t.actor?["--actor",t.actor]:[],...l===void 0?[]:l===!0?["--sign"]:["--sign",l]],p=`chant ${d.join(" ")}`;if(t.dryRun)return n(`would run: ${p}`),{code:0,command:p,wave:a};let f=gk(e,d,p,t,n);if(f===0){n(`approved ${Ct(a.wave)}`);let g=c?await Re(c):{},m=fk("git",["remote","get-url","origin"],{cwd:e,encoding:"utf-8"}).stdout?.trim()??"",h=mh(m,typeof g.forge=="string"?g.forge:void 0);t.resume===!1?n("not resumed (--no-resume): run its job again, comment /terragucci apply, or let the resume job apply it"):n(h?await PP({origin:h,wave:a,...t.env?{env:t.env}:{},...t.fetch?{fetch:t.fetch}:{}}):"not resumed from here: the origin is not on github.com or gitlab.com and terragucci.yml names no forge; run its job again, or comment /terragucci apply")}return{code:f,command:p,wave:a}}function yve(e){let t=new Map;for(let r of e.pending){let o=t.get(r.gate);r.planDigest&&(!o||ro(r.timestamp)>=ro(o.timestamp))&&t.set(r.gate,r)}let n=[];for(let[r,o]of t)e.resolutions.some(s=>s.gate===r&&Ie(s.planDigest,o.planDigest)&&ro(s.timestamp)>=ro(o.timestamp))||n.push({migration:r,digest:o.planDigest,since:o.timestamp,expiresAt:o.expiresAt,...o.description?{description:o.description}:{},...yl(o)?{runId:yl(o)}:{},...o.commit?{commit:o.commit}:{},...o.resume?{resume:o.resume}:{}});return n.sort((r,o)=>r.migration<o.migration?-1:1)}function wve(e){if(!e)return[" (the migration kept no plan record; read its proof in wave 1's log)"];try{let t=JSON.parse(e);if(t.change==="retag")return[...(t.retags??[]).map(r=>` retags ${r.address} (${r.live_id}) from estate ${r.from_estate} to estate ${r.to_estate}`)," every change each root planned is one the retag removes"];if(t.change==="adopt")return[...(t.roots??[]).map(r=>` adopts ${r.source?.location??"the state"}${r.source?.version_id?` at version ${r.source.version_id}`:""} into ${r.location??r.root}, ${(t.stamps??[]).filter(o=>o.root===r.root).length} resource instances`)," every resource verified against the live system"];let n=(t.moves??[]).map(r=>` moves ${r.addresses.join(", ")} from ${r.from} to ${r.to}`);for(let r of t.roots??[])n.push(` ${r.root}: ${r.location??"state"}${r.before?.version_id?` at version ${r.before.version_id}`:""}${r.before?.digest?"":", no state yet"}`);return n.push(" every root planned with no change against its new state"),n}catch{return[" (the migration's plan record could not be read)"]}}async function e0(e,t,n,r){let o=fe(e),s=o?await Re(o):{},i=Yu(e,s)?.mode??"ledger";r(`migration ${t.migration} waits for an approval of ${t.digest}${t.description?` (${t.description})`:""}, since ${t.since}`);let a=fk("git",["show",`refs/remotes/origin/chant/lifecycle:${Cm(t.migration,t.digest)}`],{cwd:e,encoding:"utf-8"});for(let p of wve(a.status===0?a.stdout:void 0))r(p);ro(t.expiresAt)<Date.now()&&r(` its pending fact expired at ${t.expiresAt}; the approval still counts if wave 1 proves the same digest`);let c=n.sign??(i==="sealed"?!0:void 0),u=["approve",mr,t.migration,"--plan",t.digest,...n.actor?["--actor",n.actor]:[],...c===void 0?[]:c===!0?["--sign"]:["--sign",c]],l=`chant ${u.join(" ")}`;if(n.dryRun)return r(`would run: ${l}`),{code:0,command:l,migration:t};let d=gk(e,u,l,n,r);if(d===0){r(`approved migration ${t.migration}`);let p=fk("git",["remote","get-url","origin"],{cwd:e,encoding:"utf-8"}).stdout?.trim()??"",f=mh(p,typeof s.forge=="string"?s.forge:void 0);n.resume===!1?r("not resumed (--no-resume): run wave 1 again, comment /terragucci apply, or let the resume job run it"):r(f?await PP({origin:f,wave:{wave:1,...t.runId?{runId:t.runId}:{},...t.commit?{commit:t.commit}:{},...t.resume?{resume:t.resume}:{}},...n.env?{env:n.env}:{},...n.fetch?{fetch:n.fetch}:{}}):"not resumed from here: the origin is not on github.com or gitlab.com and terragucci.yml names no forge; run wave 1 again, or comment /terragucci apply")}return{code:d,command:l,migration:t}}function gk(e,t,n,r,o){o(`running: ${n}`);let s=r.env??process.env,i=t[1]===vs?{CHANT_OVERRIDERS_FROM:"policy.override in terragucci.yml"}:{},a=fk(vi(e,r),t,{cwd:e,stdio:"inherit",env:{...s,...i,PATH:[mve(e,"node_modules",".bin"),s.PATH??""].join(gve)}});if(a.error)throw new S(`could not record the approval (${a.error.message}): the @intentius/chant package that @intentius/terragucci installs with itself is missing; install terragucci again with npm i -D @intentius/terragucci`);return a.status??1}var O2=e=>/^[\w@%+=:,./-]+$/.test(e)?e:`'${e.replace(/'/g,"'\\''")}'`;async function P2(e,t){let n=t.log??(g=>console.log(g));if(!t.root)throw new S("override takes the root the policy denied: terragucci override <root> --rule <id> --reason <text>");let r=sa(t.rules);if(r.length===0)throw new S("override names each rule it overrides with --rule <id>");if(!t.reason.trim())throw new S("override needs --reason <text>: why this plan goes out although the policy denies it");let o=eG(ft(e,Dg)),s=o.find(g=>g.gate===t.root);if(!s)throw new S(`no denial of ${t.root} is recorded on chant/lifecycle${o.length?`; denied: ${o.map(g=>g.gate).join(", ")}`:""}. A tf-apply wave records one when the policy denies the root and policy.override in the config at base names who may override it`);let i=sa(s.rules??[]);if(i.join(`
|
|
925
|
+
`);function kJ(e,t){let n=RJ("git",t,{cwd:e,encoding:"utf-8"});return n.status===0?n.stdout.trim():""}var $h=e=>`lock ${e.ID}${e.Operation?`, ${e.Operation}`:""}${e.Who?` by ${e.Who}`:""}${e.Created?` at ${e.Created}`:""}`,_J=e=>`run ${e.id} (${e.status}${e.started?`, began ${e.started}`:""})${e.url?` ${e.url}`:""}`,SJ="unlock-state releases the lock of an s3 backend's lock file, a gcs or azurerm backend, and a GitLab-managed state";function pve(e,t){if("unsupported"in t)throw new S(`${e}: ${t.unsupported}; ${SJ}`);if("gitlab"in t){if(!t.gitlab.lockAddress)throw new S(`${e}: its http backend names no lock_address (TF_HTTP_LOCK_ADDRESS), so it takes no lock`);if(!t.gitlab.unlockAddress)throw new S(`${e}: its http backend names no unlock_address (TF_HTTP_UNLOCK_ADDRESS), so no lock it takes can be released`);return t}if(!ar(t))throw new S(`${e} keeps its state in a local file, whose lock goes with the process that took it; ${SJ}`);if(t.backend==="s3"&&!t.lockfile)throw new S(`${e}: its s3 backend takes no lock file (use_lockfile = true)${t.dynamodb?", only a DynamoDB lock,":""} and unlock-state releases the lock file`);return t}async function fve(e,t,n,r,o,s,i){if("gitlab"in t){let u=t.gitlab,l=i.s3Fetch??globalThis.fetch,d=xn(u.lockAddress),p=async f=>{try{return await f()}catch(g){throw new S(`${e}: ${g.message}`)}};return{location:d,read:async()=>{let f=await p(()=>$R(u,l));return f===void 0?void 0:{text:f}},release:async f=>{await p(()=>kR(u,f,l));let g=await p(()=>$R(u,l));if(g&&xJ(g)?.ID===f)throw new S(`${e}: GitLab released lock ${f}, and ${d} still holds it`)}}}let a=await Ws(t,i.s3Fetch),c=a.lockLocation;return{location:c,read:async()=>{let u=await a.heldLock();return u===void 0?void 0:{text:u.info,...u.id?{id:u.id}:{}}},release:async u=>{let l=s(n,["force-unlock","-force","-no-color",u],r,o);if(l.status!==0)throw new S(`${e}: ${n} force-unlock ${u} failed:
|
|
926
|
+
${EJ(l.out)}`);if(await a.heldLock())throw new S(`${e}: ${n} force-unlock ran, and ${c} is still locked`)}}}async function TJ(e,t,n={}){let r=n.log??(Q=>console.log(Q)),o=n.env??process.env,s=n.exec??dve,i=t.replace(/\/+$/,""),a=vJ(e,i);if(!i||i.split("/").includes("..")||!ove(a)||!sve(a).isDirectory())throw new S(`${t} is not a root of this repository`);let c=n.config??fe(e),u=c?await Re(vJ(c)):{};if(u.projects)throw new S("unlock-state runs in a project's own checkout, not a control repo");let l=ue(u),d=et(e,l),p=n.binary??l.binary??d.binary([i]).value;if(p==="choudoufu"&&ms(e,[i]).length>0)return r(`${i}: choudoufu keeps its resources under live resource markers, with no state file and no state lock; nothing to release`),{code:Ol.released,live:!0};let f=s(p,["init","-input=false","-no-color"],a,d.rootInit(i,o)?.init??o);if(f.status!==0)throw new S(`${i}: ${p} init failed:
|
|
927
|
+
${EJ(f.out)}`);let g=pve(i,Hr(a,d.rootEnv(i,o))),m=await fve(i,g,p,a,o,s,n),h=m.location,y=await m.read();if(y===void 0)return r(`${i}: no lock is held on ${h}; nothing to release`),{code:Ol.released,location:h};let w=xJ(y.text);if(!w)throw new S(`${i}: ${h} holds no lock ID the binary wrote; read it, and remove it by hand if it is not a lock`);let b={...w,...y.id?{ID:y.id}:{}},k=ave(i,h,b.ID);r(`${i}: ${h} holds ${$h(b)}`);let v=vh(e,l,o),$=l.token_env??Gn[v.forge];if(!v.token)throw new S(`${$} is not set; unlock-state reads the forge's runs with it, to know no run holding the lock is alive`);let _=n.fetch??globalThis.fetch,E=0,R=async()=>{let Q;try{Q=await uve(_,v)}catch(ne){throw new S(`${i}: the forge's runs could not be read (${ne.message}), so nothing says the run holding the lock is gone`)}return E=Q.length,lve(Q,b.Created)},x=await R();if(x.length>0){r(`${i}: ${x.length===1?"a run that began before the lock is":`${x.length} runs that began before the lock are`} still alive, so the lock may be theirs and is not released:`);for(let Q of x)r(` ${_J(Q)}`);return r("Run this again once they have finished, or stop them on the forge first."),{code:Ol.refused,lock:b,location:h,digest:k,alive:x}}r(`${i}: no run that began before the lock is alive`);let A=n.now??new Date().toISOString(),O=await Yr(e,{at:"HEAD",...c?{config:c}:{}}),I=O.mode==="sealed",N=ft(e,pk);I&&(N={...N,resolutions:N.resolutions.filter(Q=>{if(Q.gate!==i)return!0;let ne=ws(O.signers,Q);return ne!==null&&Ie(Q.planDigest,k)&&r(`${i}: an approval does not count: ${ne}`),ne===null})});let L=ma(N,i,k,A),U=cve(i,k,I),F=kJ(e,["rev-parse","HEAD"]);if(L.status!=="approved"){if(!L.standing){let Q={version:1,kind:"pending",op:Pp,gate:i,timestamp:A,expiresAt:new Date(kh(A)+1728e5).toISOString(),planDigest:k,description:`release ${$h(b)} on ${h}`,...F?{commit:F}:{},neverOverMcp:!0};ha(e,Q,{},pk)}return L.status==="refused"?(r(`${i}: ${L.by} approved ${L.approved??"another digest"}, the release of another lock; this one is not released`),r(`${i}: to release ${$h(b)}, approve it with:`),r(` ${U}`),{code:Ol.other,lock:b,location:h,digest:k,command:U}):(r(`${i}: releasing ${$h(b)} waits for an approval of digest ${k}. Approve it with:`),r(` ${U}`),r("Then run this again."),{code:Ol.waiting,lock:b,location:h,digest:k,command:U})}r(`${i}: approved by ${L.by} for this lock`);let M=await R();if(M.length>0){r(`${i}: a run that began before the lock is alive again, so the lock is not released:`);for(let Q of M)r(` ${_J(Q)}`);return{code:Ol.refused,lock:b,location:h,digest:k,alive:M}}await m.release(b.ID);let te=n.actor||kJ(e,["config","user.name"])||o.USER||o.USERNAME||"unknown",ie={version:1,kind:"unlock",op:Pp,gate:i,root:i,location:h,lock:b,planDigest:k,approvedBy:L.by,approvedAt:L.at,releasedBy:te,timestamp:new Date().toISOString(),liveRuns:E,...F?{commit:F}:{}};return hr(e,$J,[JSON.stringify(ie)],{},`Released the state lock of ${i}: ${b.ID}`),r(`${i}: released ${$h(b)}, approved by ${L.by}; recorded in ${$J} on chant/lifecycle`),{code:Ol.released,lock:b,location:h,digest:k}}var ro=e=>new Date(e).getTime();function _h(e){let t=new Map;for(let r of e.pending){let o=t.get(r.gate);/^wave-\d+$/.test(r.gate)&&r.planDigest&&(!o||ro(r.timestamp)>=ro(o.timestamp))&&t.set(r.gate,r)}let n=[];for(let[r,o]of t)e.resolutions.some(i=>i.gate===r&&i.planDigest===o.planDigest&&ro(i.timestamp)>=ro(o.timestamp))||n.push({wave:Number(r.slice(5)),digest:o.planDigest,since:o.timestamp,expiresAt:o.expiresAt,...o.description?{description:o.description}:{},...yl(o)?{runId:yl(o)}:{},...o.commit?{commit:o.commit}:{},...o.resume?{resume:o.resume}:{},...o.answer?.id?{question:o.answer.id}:{}});return n.sort((r,o)=>r.wave-o.wave)}function hve(e){if(!e)return[" (the wave kept no report of its plans; read them in its job's log or report)"];try{let t=JSON.parse(e),n=[` roots: ${(t.roots??[]).map(o=>o.path).join(", ")||"none"}`],r=(t.named??[]).filter(o=>o.action==="delete"||o.action==="replace");for(let o of r)n.push(` ${o.action==="delete"?"destroys":"replaces"} ${o.root}${o.address?`: ${o.address}`:""}`);return r.length===0&&n.push(" destroys and replaces nothing"),n}catch{return[" (the wave's kept report could not be read)"]}}async function AJ(e,t={}){let n=t.log??(g=>console.log(g)),r=_h(ft(e)),o=yve(ft(e,ga)),s=t.wave!==void 0&&!/^(?:wave-)?\d+$/.test(t.wave)?t.wave:void 0;if(s!==void 0){let g=o.find(m=>m.migration===s);if(!g)throw new S(`no migration ${s} waits for an approval${o.length?`; waiting: ${o.map(m=>m.migration).join(", ")}`:""}`);return t.plan!==void 0&&!Ie(g.digest,t.plan.trim())?(n(`not approved: migration ${s} waits for ${g.digest}, not ${t.plan.trim()}. The states moved since that digest; read the new proof, then approve its digest`),{code:1,command:""}):e0(e,g,t,n)}if(t.plan!==void 0&&t.wave===void 0){let g=o.find(m=>Ie(m.digest,t.plan.trim()));if(g)return e0(e,g,t,n)}if(t.plan===void 0&&t.wave===void 0&&r.length+o.length===1&&o.length===1)return e0(e,o[0],t,n);if(t.plan===void 0&&t.wave===void 0&&r.length+o.length>1&&o.length>0)throw new S(`${r.length+o.length} gates wait (${[...r.map(g=>Ct(g.wave)),...o.map(g=>`migration ${g.migration}`)].join(", ")}); name one: terragucci approve wave-<k> or terragucci approve <migration>`);let i;if(t.plan!==void 0){let g=t.plan.trim();if(!/^\S+$/.test(g))throw new S("--plan takes the digest to approve, such as jcs1-sha256:...");let m=t.wave===void 0?void 0:Number(/^(?:wave-)?(\d+)$/.exec(t.wave)?.[1]);if(m!==void 0&&(!Number.isInteger(m)||m<1))throw new S(`approve takes a wave as wave-<k> or <k>, not ${JSON.stringify(t.wave)}`);let h=m===void 0?r:r.filter(y=>y.wave===m);if(i=h.find(y=>Ie(y.digest,g)),!i){let y=m===void 0?"no wave":Ct(m),w=h.length>0?`; waiting: ${h.map(b=>`${Ct(b.wave)} for ${b.digest}`).join(", ")}`:m===void 0?": no wave waits for an approval":" is not waiting";return n(`not approved: ${y} waits for ${g}${w}. The plans moved since that digest, or were approved and applied; read the waiting plans, then approve their digest`),{code:1,command:""}}}else if(t.wave!==void 0){let g=Number(/^(?:wave-)?(\d+)$/.exec(t.wave)?.[1]);if(!Number.isInteger(g)||g<1)throw new S(`approve takes a wave as wave-<k> or <k>, not ${JSON.stringify(t.wave)}`);if(i=r.find(m=>m.wave===g),!i)throw new S(`${Ct(g)} is not waiting${r.length?`; waiting: ${r.map(m=>Ct(m.wave)).join(", ")}`:": no wave waits for an approval"}`)}else if(r.length===1)i=r[0];else throw r.length===0?new S("no wave waits for an approval on chant/lifecycle"):new S(`${r.length} waves wait (${r.map(g=>Ct(g.wave)).join(", ")}); name one: terragucci approve wave-<k>`);let a=i,c=fe(e),u=Yu(e,c?await Re(c):{})?.mode??"ledger";n(`${Ct(a.wave)} waits for an approval of ${a.digest}${a.description?` (${a.description})`:""}, since ${a.since}`);for(let g of hve(QO(e,a.wave,a.digest)))n(g);ro(a.expiresAt)<Date.now()&&n(` its pending fact expired at ${a.expiresAt}; the approval still counts if the next run plans the same digest`);let l=t.sign??(u==="sealed"?!0:void 0),d=["approve","tf-apply",Ct(a.wave),"--plan",a.digest,...t.actor?["--actor",t.actor]:[],...l===void 0?[]:l===!0?["--sign"]:["--sign",l]],p=`chant ${d.join(" ")}`;if(t.dryRun)return n(`would run: ${p}`),{code:0,command:p,wave:a};let f=gk(e,d,p,t,n);if(f===0){n(`approved ${Ct(a.wave)}`);let g=c?await Re(c):{},m=fk("git",["remote","get-url","origin"],{cwd:e,encoding:"utf-8"}).stdout?.trim()??"",h=mh(m,typeof g.forge=="string"?g.forge:void 0);t.resume===!1?n("not resumed (--no-resume): run its job again, comment /terragucci apply, or let the resume job apply it"):n(h?await PP({origin:h,wave:a,...t.env?{env:t.env}:{},...t.fetch?{fetch:t.fetch}:{}}):"not resumed from here: the origin is not on github.com or gitlab.com and terragucci.yml names no forge; run its job again, or comment /terragucci apply")}return{code:f,command:p,wave:a}}function yve(e){let t=new Map;for(let r of e.pending){let o=t.get(r.gate);r.planDigest&&(!o||ro(r.timestamp)>=ro(o.timestamp))&&t.set(r.gate,r)}let n=[];for(let[r,o]of t)e.resolutions.some(s=>s.gate===r&&Ie(s.planDigest,o.planDigest)&&ro(s.timestamp)>=ro(o.timestamp))||n.push({migration:r,digest:o.planDigest,since:o.timestamp,expiresAt:o.expiresAt,...o.description?{description:o.description}:{},...yl(o)?{runId:yl(o)}:{},...o.commit?{commit:o.commit}:{},...o.resume?{resume:o.resume}:{}});return n.sort((r,o)=>r.migration<o.migration?-1:1)}function wve(e){if(!e)return[" (the migration kept no plan record; read its proof in wave 1's log)"];try{let t=JSON.parse(e);if(t.change==="retag")return[...(t.retags??[]).map(r=>` retags ${r.address} (${r.live_id}) from estate ${r.from_estate} to estate ${r.to_estate}`)," every change each root planned is one the retag removes"];if(t.change==="adopt")return[...(t.roots??[]).map(r=>` adopts ${r.source?.location??"the state"}${r.source?.version_id?` at version ${r.source.version_id}`:""} into ${r.location??r.root}, ${(t.stamps??[]).filter(o=>o.root===r.root).length} resource instances`)," every resource verified against the live system"];let n=(t.moves??[]).map(r=>` moves ${r.addresses.join(", ")} from ${r.from} to ${r.to}`);for(let r of t.roots??[])n.push(` ${r.root}: ${r.location??"state"}${r.before?.version_id?` at version ${r.before.version_id}`:""}${r.before?.digest?"":", no state yet"}`);return n.push(" every root planned with no change against its new state"),n}catch{return[" (the migration's plan record could not be read)"]}}async function e0(e,t,n,r){let o=fe(e),s=o?await Re(o):{},i=Yu(e,s)?.mode??"ledger";r(`migration ${t.migration} waits for an approval of ${t.digest}${t.description?` (${t.description})`:""}, since ${t.since}`);let a=fk("git",["show",`refs/remotes/origin/chant/lifecycle:${Cm(t.migration,t.digest)}`],{cwd:e,encoding:"utf-8"});for(let p of wve(a.status===0?a.stdout:void 0))r(p);ro(t.expiresAt)<Date.now()&&r(` its pending fact expired at ${t.expiresAt}; the approval still counts if wave 1 proves the same digest`);let c=n.sign??(i==="sealed"?!0:void 0),u=["approve",mr,t.migration,"--plan",t.digest,...n.actor?["--actor",n.actor]:[],...c===void 0?[]:c===!0?["--sign"]:["--sign",c]],l=`chant ${u.join(" ")}`;if(n.dryRun)return r(`would run: ${l}`),{code:0,command:l,migration:t};let d=gk(e,u,l,n,r);if(d===0){r(`approved migration ${t.migration}`);let p=fk("git",["remote","get-url","origin"],{cwd:e,encoding:"utf-8"}).stdout?.trim()??"",f=mh(p,typeof s.forge=="string"?s.forge:void 0);n.resume===!1?r("not resumed (--no-resume): run wave 1 again, comment /terragucci apply, or let the resume job run it"):r(f?await PP({origin:f,wave:{wave:1,...t.runId?{runId:t.runId}:{},...t.commit?{commit:t.commit}:{},...t.resume?{resume:t.resume}:{}},...n.env?{env:n.env}:{},...n.fetch?{fetch:n.fetch}:{}}):"not resumed from here: the origin is not on github.com or gitlab.com and terragucci.yml names no forge; run wave 1 again, or comment /terragucci apply")}return{code:d,command:l,migration:t}}function gk(e,t,n,r,o){o(`running: ${n}`);let s=r.env??process.env,i=t[1]===vs?{CHANT_OVERRIDERS_FROM:"policy.override in terragucci.yml"}:{},a=fk(vi(e,r),t,{cwd:e,stdio:"inherit",env:{...s,...i,PATH:[mve(e,"node_modules",".bin"),s.PATH??""].join(gve)}});if(a.error)throw new S(`could not record the approval (${a.error.message}): the @intentius/chant package that @intentius/terragucci installs with itself is missing; install terragucci again with npm i -D @intentius/terragucci`);return a.status??1}var OJ=e=>/^[\w@%+=:,./-]+$/.test(e)?e:`'${e.replace(/'/g,"'\\''")}'`;async function PJ(e,t){let n=t.log??(g=>console.log(g));if(!t.root)throw new S("override takes the root the policy denied: terragucci override <root> --rule <id> --reason <text>");let r=sa(t.rules);if(r.length===0)throw new S("override names each rule it overrides with --rule <id>");if(!t.reason.trim())throw new S("override needs --reason <text>: why this plan goes out although the policy denies it");let o=eG(ft(e,Dg)),s=o.find(g=>g.gate===t.root);if(!s)throw new S(`no denial of ${t.root} is recorded on chant/lifecycle${o.length?`; denied: ${o.map(g=>g.gate).join(", ")}`:""}. A tf-apply wave records one when the policy denies the root and policy.override in the config at base names who may override it`);let i=sa(s.rules??[]);if(i.join(`
|
|
928
928
|
`)!==r.join(`
|
|
929
|
-
`))throw new S(`${t.root} was denied by ${i.join(", ")||"no rule it named"}; an override names exactly those rules, and --rule gave ${r.join(", ")}`);let a=fe(e),c=Yu(e,a?await Re(a):{})?.mode??"ledger",u=s.plan||s.members?.[0]?.planDigest;n(`${t.root}: its plan${u?` ${u}`:""} was denied by ${i.join(", ")}, since ${s.timestamp}`),n(` the override binds the root, that plan and those rules: ${s.planDigest}`),ro(s.expiresAt)<Date.now()&&n(` the denial's pending fact expired at ${s.expiresAt}; the override still counts if the next run plans the same digest`);let l=t.sign??(c==="sealed"?!0:void 0),d=["approve",vs,t.root,...r.flatMap(g=>["--rule",g]),"--reason",t.reason.trim(),...t.actor?["--actor",t.actor]:[],...l===void 0?[]:l===!0?["--sign"]:["--sign",l]],p=`chant ${d.map(
|
|
929
|
+
`))throw new S(`${t.root} was denied by ${i.join(", ")||"no rule it named"}; an override names exactly those rules, and --rule gave ${r.join(", ")}`);let a=fe(e),c=Yu(e,a?await Re(a):{})?.mode??"ledger",u=s.plan||s.members?.[0]?.planDigest;n(`${t.root}: its plan${u?` ${u}`:""} was denied by ${i.join(", ")}, since ${s.timestamp}`),n(` the override binds the root, that plan and those rules: ${s.planDigest}`),ro(s.expiresAt)<Date.now()&&n(` the denial's pending fact expired at ${s.expiresAt}; the override still counts if the next run plans the same digest`);let l=t.sign??(c==="sealed"?!0:void 0),d=["approve",vs,t.root,...r.flatMap(g=>["--rule",g]),"--reason",t.reason.trim(),...t.actor?["--actor",t.actor]:[],...l===void 0?[]:l===!0?["--sign"]:["--sign",l]],p=`chant ${d.map(OJ).join(" ")}`;if(t.dryRun)return n(`would run: ${p}`),{code:0,command:p};let f=gk(e,d,p,t,n);return f===0&&n(`overrode the denial of ${t.root}; run its wave again and it applies this plan, if policy.override at base lists you`),{code:f,command:p}}var jJ={export:{op:Hc,ledger:ek,what:"the state export of",next:e=>`the person who asked runs terragucci state export ${e} again to download it`},unlock:{op:Pp,ledger:pk,what:"releasing the state lock of",next:e=>`run terragucci unlock-state ${e} again to release it`},ephemeral:{op:ka,ledger:ap,what:"the ephemeral copy of",next:()=>"run the pull request's ephemeral job again, or push to it"}},IJ=e=>e.length>=2&&Object.hasOwn(jJ,e[0])&&e[1]!=="";async function CJ(e,t){let n=t.log??(h=>console.log(h)),r=jJ[t.kind],o=t.kind==="ephemeral"?"terragucci approve ephemeral <pr> --plan <digest>":`terragucci approve ${t.kind} <root> --plan <digest>`,s=t.target;if(t.kind==="ephemeral"){let h=Number(/^(?:pr-)?(\d+)$/.exec(t.target)?.[1]);if(!Number.isInteger(h)||h<1)throw new S(`approve ephemeral takes the pull request's number, not ${JSON.stringify(t.target)}: ${o}`);s=t$(h)}let i=t.plan?.trim();if(!i||!/^\S+$/.test(i))throw new S(`approve ${t.kind} needs --plan <digest>, the digest its request printed: ${o}`);let c=ft(e,r.ledger).pending.filter(h=>h.gate===s&&h.planDigest).sort((h,y)=>ro(y.timestamp)-ro(h.timestamp)),u=(t.kind==="export"?c.find(h=>Ie(h.planDigest,i)):void 0)??c[0];if(!u)return n(`not approved: nothing waits for an approval of ${r.what} ${s} on chant/lifecycle`),{code:1,command:""};if(!Ie(u.planDigest,i))return n(`not approved: ${r.what} ${s} waits for ${u.planDigest}, not ${i}. What it asks moved since that digest; read the new request, then approve its digest`),{code:1,command:""};let l=fe(e),d=Yu(e,l?await Re(l):{})?.mode??"ledger";n(`${r.what} ${s} waits for an approval of ${u.planDigest}${u.description?` (${u.description})`:""}, since ${u.timestamp}`),ro(u.expiresAt)<Date.now()&&n(` its pending fact expired at ${u.expiresAt}`);let p=t.sign??(d==="sealed"?!0:void 0),f=["approve",r.op,s,"--plan",u.planDigest,...t.actor?["--actor",t.actor]:[],...p===void 0?[]:p===!0?["--sign"]:["--sign",p]],g=`chant ${f.map(OJ).join(" ")}`;if(t.dryRun)return n(`would run: ${g}`),{code:0,command:g};let m=gk(e,f,g,t,n);return m===0&&n(`approved ${r.what} ${s}; ${r.next(t.kind==="ephemeral"?s:t.target)}`),{code:m,command:g}}var NJ=["init","reconcile","plan","stage","config","forge","rollout","respond","query"],bve={0:"ok",1:"failed",2:"usage",3:"waiting"};function Xo(e,t,n,r){let o={schema:1,command:e,exit:t,status:bve[t],results:n};return r!==void 0&&(o.error=r),o}import{existsSync as XJ,readFileSync as Xve,writeFileSync as Qve}from"node:fs";import{join as eK,relative as Dp,resolve as QJ}from"node:path";var Ei=class{constructor(t=_m){this.url=t}url;list=[];add(t,n,r,o,s){this.list.some(i=>i.key===n&&i.row===r)||this.list.push({key:n,kind:t,row:r,text:o,...s?{detail:s}:{}})}mapped(t,n,r){this.add("mapped",t,n,Pv(n),r)}covered(t,n,r){this.add("default",t,n,Pv(n),r)}unmapped(t,n,r){this.add("unmapped",t,n,Pv(n),r)}leftOut(t,n,r){let o=Sm(n);this.add("left-out",t,n,`${o.rule}. Instead: ${o.instead}`,r)}unknown(t,n){this.add("unmapped",t,"",`the guide has no row for it, so nothing was written; see ${this.url}`,n)}cell(t,n,r,o,s){this.add(t,n,r,o,s)}scale(t,n,r,o){this.add(t,n,r,Oz(r),o)}own(t,n,r,o){this.add(n,t,"",r,o)}};function Me(e){return e!==null&&typeof e=="object"&&!Array.isArray(e)}function oo(e){return Array.isArray(e)?e:e==null?[]:[e]}function xo(e){if(typeof e!="string")return;let t=e.trim().split("/").filter(n=>n!==""&&n!==".");if(!(e.trim().startsWith("/")||t.includes("..")))return t.length?t.join("/"):"."}function jp(e,t=new Map){let n=(u,l)=>{let d=new Set,p=f=>[...t.get(f)??[]].some(g=>g===l||!d.has(g)&&(d.add(g),p(g)));return p(u)},r=new Set,o={};for(let[u,l]of e)if(u!==l){if(n(u,l)){r.add(`${u}\0${l}`);continue}(o[u]??[]).includes(l)||(o[u]=[...o[u]??[],l].sort())}let s=u=>[...new Set([...o[u]??[],...t.get(u)??[]])],i=new Map,a=[],c=u=>{if(i.get(u)!==2){if(i.get(u)===1)return[...a.slice(a.indexOf(u)),u];i.set(u,1),a.push(u);for(let l of s(u)){let d=c(l);if(d)return d}a.pop(),i.set(u,2)}};for(let u of Object.keys(o).sort()){let l=c(u);if(l)return{after:{},fromReads:r,cycle:l}}return{after:Object.fromEntries(Object.entries(o).sort(([u],[l])=>u.localeCompare(l))),fromReads:r}}import{existsSync as LJ,readdirSync as Ove,readFileSync as Pve,statSync as jve}from"node:fs";import{dirname as n0,join as wk,relative as Ive}from"node:path";import{readdirSync as vve,readFileSync as $ve,statSync as kve}from"node:fs";import{join as _ve,relative as Sve}from"node:path";var Rve=new Set(["node_modules",".git",".terraform",".terragrunt-cache",".terragucci"]),t0=class{constructor(t){this.s=t}s;i=0;ws(t=!1){let n=this.s;for(;this.i<n.length;){let r=n[this.i];if(r===" "||r===" "||r==="\r"||!t&&r===`
|
|
930
930
|
`)this.i++;else if(r==="#"||r==="/"&&n[this.i+1]==="/")for(;this.i<n.length&&n[this.i]!==`
|
|
931
931
|
`;)this.i++;else if(r==="/"&&n[this.i+1]==="*"){let o=n.indexOf("*/",this.i+2);this.i=o<0?n.length:o+2}else break}}ident(){let t=/^[A-Za-z_][\w-]*/.exec(this.s.slice(this.i,this.i+200));return t?(this.i+=t[0].length,t[0]):""}str(){let t=this.s,n="";for(this.i++;this.i<t.length&&t[this.i]!=='"';)if(t[this.i]==="\\"){let r=t[this.i+1];n+=r==="n"?`
|
|
932
932
|
`:r==="t"?" ":r??"",this.i+=2}else if(t[this.i]==="$"&&t[this.i+1]==="{"){let r=this.i;this.skipBalanced("{","}",this.i+1),n+=t.slice(r,this.i)}else n+=t[this.i++];return this.i++,n}skipBalanced(t,n,r){let o=0;for(this.i=r;this.i<this.s.length;){let s=this.s[this.i];if(s==='"'){this.str();continue}if(s===t)o++;else if(s===n&&--o===0){this.i++;return}this.i++}}heredoc(){let t=/^<<(-?)([A-Za-z_]\w*)[^\n]*\n/.exec(this.s.slice(this.i));if(!t)return this.raw();this.i+=t[0].length;let n=[];for(;this.i<this.s.length;){let r=this.s.indexOf(`
|
|
@@ -937,15 +937,15 @@ ${E2(f.out)}`);let g=pve(i,Hr(a,d.rootEnv(i,o))),m=await fve(i,g,p,a,o,s,n),h=m.
|
|
|
937
937
|
`}raw(){let t=this.s,n=this.i,r=0;for(;this.i<t.length;){let o=t[this.i];if(o==='"'){this.str();continue}if(o==="("||o==="["||o==="{")r++;else if(o===")"||o==="]"||o==="}"){if(r===0)break;r--}else if(r===0&&(o===`
|
|
938
938
|
`||o===","))break;this.i++}return t.slice(n,this.i).trim()}value(){this.ws(!0);let t=this.s[this.i];if(t==='"'){let o=this.i,s=this.str();this.ws(!0);let i=this.s[this.i];return i!==void 0&&!`
|
|
939
939
|
,]})#/`.includes(i)?(this.i=o,{expr:this.raw()}):s}if(t==="["){this.i++;let o=[];for(;;){if(this.ws(),this.s[this.i]==="]"||this.i>=this.s.length)return this.i++,o;o.push(this.value()),this.ws(),this.s[this.i]===","&&this.i++}}if(t==="{"){let o=this.i;return this.skipBalanced("{","}",this.i),{expr:this.s.slice(o,this.i)}}if(t==="<"&&this.s[this.i+1]==="<")return this.heredoc();let n=this.raw();if(n==="true"||n==="false")return n==="true";if(/^-?\d+(\.\d+)?$/.test(n))return Number(n);if(/^[A-Za-z_][\w-]*(\.[\w-]+)+$/.test(n))return{ref:n};let r=/^file\(\s*"([^"]*)"\s*\)$/.exec(n);return r?{file:r[1]}:{expr:n}}body(){let t={attrs:{},blocks:[]};for(;;){if(this.ws(),this.i>=this.s.length)return t;if(this.s[this.i]==="}")return this.i++,t;let n=this.ident();if(!n){for(;this.i<this.s.length&&this.s[this.i]!==`
|
|
940
|
-
`;)this.i++;continue}if(this.ws(!0),this.s[this.i]==="="&&this.s[this.i+1]!=="="){this.i++,t.attrs[n]=this.value();continue}let r=[];for(;this.s[this.i]==='"'||/[A-Za-z_]/.test(this.s[this.i]??"");)r.push(this.s[this.i]==='"'?this.str():this.ident()),this.ws(!0);this.s[this.i]==="{"&&(this.i++,t.blocks.push({type:n,labels:r,body:this.body()}))}}};function xve(e){return new t0(e).body()}function Eve(e){let t=[],n=r=>{let o;try{o=vve(r).sort()}catch{return}for(let s of o){if(Rve.has(s)||s.startsWith("."))continue;let i=_ve(r,s),a;try{a=kve(i)}catch{continue}a.isDirectory()?n(i):(s.endsWith(".tf")||s.endsWith(".tofu"))&&t.push(i)}};return n(e),t}function mk(e,t){let n=[];for(let r of Eve(e)){let o=$ve(r,"utf-8");if(o.includes(`"${t}`))for(let s of xve(o).blocks)s.type==="resource"&&s.labels.length===2&&s.labels[0].startsWith(t)&&n.push({type:s.labels[0],name:s.labels[1],file:Sve(e,r),body:s.body})}return n}function Tt(e){return typeof e=="string"&&!e.includes("${")?e:void 0}function Ip(e){if(!Array.isArray(e))return;let t=e.map(Tt);return t.every(n=>n!==void 0)?t:void 0}function js(e){if(!e||typeof e!="object"||!("ref"in e))return;let t=e.ref.split(".");return t.length>=2?`${t[0]}.${t[1]}`:void 0}function so(e){return e===void 0?"":typeof e=="string"?JSON.stringify(e):typeof e!="object"?String(e):Array.isArray(e)?`[${e.map(so).join(", ")}]`:"ref"in e?e.ref:"file"in e?`file("${e.file}")`:e.expr}var hk="https://intentius.io/terragucci/guides/coming-from-spacelift-or-env-zero/",Tve=[["Root","stack","environment","a [root](/terragucci/concepts/glossary/#root): one directory, one state; an import writes `roots` from the stacks' project roots or the environments' template paths"],["Shared code","several stacks on one project root","several environments from one template","a directory per stack or environment, each with its backend key and `terraform.tfvars`, calling a shared module"],["Workspace","`terraform_workspace`","`workspaceName`, `workspace`","none: each root is a directory with one state"],["Managed state","managed state, `manage_state`","env zero's remote backend, `isRemoteBackend`","a key in your S3 bucket, moved by a [backend move](#state-move); an import names each stack or environment whose state moves"],["Own backend","your own backend","your own S3 backend","the same backend; nothing moves"],["Variables","context and stack environment variables","variables","[`env`](/terragucci/reference/config/#keys) for values every root gets; a secret's name under [`pass`](#variables), whose value you create as a CI secret; a root's own values in its `terraform.tfvars`"],["Files","context and stack mounted files","","files committed in the root, such as `terraform.tfvars`"],["Cloud credentials","cloud integration","credentials","[`oidc`](/terragucci/reference/environment/#cloud-roles-over-oidc): a plan role and an apply role per cloud"],["Version","`terraform_version`, `opentofu_version`, `terraform_workflow_tool`","a template's `type`, `terraform_version`, `opentofu_version`","`binary`, and [`version`](/terragucci/reference/config/#a-version-per-root) for every root or per root glob, or the root's `.terraform-version`"],["Hooks","hooks: `before_init`, `after_plan`, `before_apply` and the rest","custom flows (`env0.yml`)","[`steps`](/terragucci/guides/run-steps/) before or after `init`, `plan`, `apply` and `drift`, for the roots the hook ran in; nothing runs at a destroy or a task"],["Runner image","`runner_image`","","`image`: one image for every job, built FROM the terragucci image for the binary"],["Plan policy","plan policy","approval policy (OPA)","[`policy`](/terragucci/reference/policy/) over each plan, with [cost](/terragucci/guides/estimate-cost/#cost-in-the-policy) in `input.cost`"],["Approval","approval policy, `autodeploy` off","approval policy, `requiresApproval`","[`gate`](/terragucci/reference/stages/#gate-policy) and [approval modes](/terragucci/guides/approve-a-wave/#approval-modes); [`cost.approve_above`](/terragucci/guides/estimate-cost/#hold-a-wave-over-an-amount); an import writes `gate: always` when every stack or environment waits for an approval"],["Triggers","push and trigger policies, project globs","triggers, `continuousDeployment`, `pullRequestPlanDeployments`","the [generated pipeline](/terragucci/getting-started/): a pull request plans the roots its files reach; merge applies them"],["Order","stack dependencies, output references","workflows","[waves](/terragucci/concepts/waves-and-approvals/#linked-roots): a root that reads another through `terraform_remote_state` applies in a later wave; an import writes `waves.after` for a stack dependency the reads do not give"],["Access","login policy, spaces","RBAC","your forge's permissions and cloud IAM ([access and identity](/terragucci/standards/access-and-identity/))"],["Notifications","notification policy","notifications","[`notify`](/terragucci/guides/notify-a-chat-channel/): Slack, Teams or a signed webhook"],["Drift","drift detection","drift detection, `driftDetectionCron`","[`drift`](/terragucci/guides/turn-on-drift-checks/), one cron schedule over every root, and a pull request for drift on a literal; it never reconciles"],["TTL","","environment TTL","[ephemeral environments](/terragucci/guides/ephemeral-environments/) per pull request, with a TTL; an import writes `ephemeral` for the environments that expire"],["Cost","","cost monitoring","[`cost`](/terragucci/guides/estimate-cost/): an estimate per plan, not billed spend"],["Workers","private workers, `worker_pool_id`","self-hosted agents","your CI's runners, your own ones by label with [`runner`](/terragucci/guides/add-to-a-repo/#self-hosted-runners)"],["Module registry","module registry","module registry","[a module registry](/terragucci/guides/publish-modules/#serve-a-module-registry) in your bucket"],["Run history","run history","deployment log","the [report](/terragucci/guides/keep-reports-in-a-bucket/) per run and [`chant/lifecycle`](/terragucci/concepts/glossary/#chantlifecycle)"]];function Ave(e){return bo(Tve.find(t=>t[0]===e)[3])}function
|
|
941
|
-
`)}};function yk(e,t,n){let{notes:r,s:o}=e,s=new Map;for(let p of e.units)p.root!==void 0&&s.set(p.root,[...s.get(p.root)??[],p]);let i=e.units.filter(p=>p.root===void 0);s.size&&!i.length?(o.roots=[...s.keys()].sort(),e.note("mapped",n,"Root",`roots: ${o.roots.join(", ")}`)):s.size&&e.note("unmapped",n,"Root",`no roots written, so terragucci detects them: ${i.map(p=>p.id).join(", ")} ${i.length===1?"has":"have"} no directory the files name`);for(let[p,f]of s)f.length>1&&e.note("unmapped",f.map(g=>g.key).join(", "),"Shared code",`${f.map(g=>g.id).join(", ")} all run ${p}, which is one root with one state; give each a directory`);let a=[...e.binaries.keys()];a.length===1?(o.binary=a[0],e.note("mapped",e.binaries.get(a[0])[0],"Version",`binary: ${a[0]}`)):a.length>1&&e.note("unmapped",[...e.binaries.values()].flat().join(", "),"Version",`the ${e.platform} config runs ${a.join(" and ")}, and the pipeline runs one binary`);let c=new Set([...e.versions.values()].map(p=>p.version));if(c.size===1){let[p]=c;o.version=p;for(let f of e.versions.values())e.note("mapped",f.key,"Version",`version: ${p}`)}else if(c.size>1){o.version=Object.fromEntries([...e.versions].sort(([p],[f])=>p.localeCompare(f)).map(([p,f])=>[p,f.version]));for(let[p,f]of e.versions)e.note("mapped",f.key,"Version",`version: ${p}: ${f.version}`)}if(e.edges.length){let{after:p,fromReads:f,cycle:g}=jp(e.edges.map(m=>[m.d,m.u]),t.repo?.reads);Object.keys(p).length&&!g&&(o.waves={...o.waves,after:p});for(let m of e.edges){let h=`${m.d} after ${m.u}`;m.d===m.u?e.note("unmapped",m.key,"Order",`both stacks run ${m.d}`):f.has(`${m.d}\0${m.u}`)?e.note("default",m.key,"Order",`the terraform_remote_state reads already order ${h}`):g?e.note("unmapped",m.key,"Order",`${h} is not kept: with the terraform_remote_state reads it makes a cycle, ${g.join(" after ")}`):e.note("mapped",m.key,"Order",`waves.after: ${h}`)}}let u=`${e.platform} applies the branch a ${n==="stacks"?"stack":"environment"} tracks after a push, terragucci the default branch after a merge`;if(t.applyWhen==="pull-request"&&t.forge==="gitlab"?r.own("when it applies","unmapped",u,`left at apply.when: merge: ${us.comments}; and ${us.token}`):t.applyWhen==="pull-request"?(o.apply={when:"pull-request"},r.own("when it applies","mapped",u,"apply.when: pull-request, from --apply-when")):r.own("when it applies","default",u,"apply.when: merge, terragucci's default"),e.approvals.length){let p=e.approvals.filter(f=>f.waits);if(p.length===e.approvals.length){o.gate="always";for(let f of p)e.note("mapped",f.key,"Approval","gate: always")}else if(p.length)for(let f of p)e.note("unmapped",f.key,"Approval",`gate is one setting for every wave, and ${e.approvals.length-p.length} of ${e.approvals.length} ${n} apply without an approval; gate: always holds them all`);else for(let f of e.approvals)e.note("default",f.key,"Approval","applies without an approval, as gate's default does for a change that destroys nothing")}let l=[...new Set(e.drift.map(p=>p.cron))];if(l.length===1){o.drift=l[0];let p=new Set(e.drift.map(f=>f.unit)).size>=e.units.length;for(let f of e.drift)e.note("mapped",f.key,"Drift",`drift: "${l[0]}"${p?"":`, over every root, not only ${[...new Set(e.drift.map(g=>g.unit))].join(", ")}`}`)}else if(l.length>1)for(let p of e.drift)e.note("unmapped",p.key,"Drift",`drift is one schedule, and the ${n} give ${l.join(", ")}`);if(e.ephemeral.length){let p=[...new Set(e.ephemeral.map(f=>f.root))].sort();o.ephemeral={roots:p,...e.ttl?{ttl:e.ttl.value}:{}};for(let f of e.ephemeral)e.note("mapped",f.key,"TTL",`ephemeral.roots: ${f.root}${e.ttl?`, ttl: ${e.ttl.value}`:", ttl: 24h, terragucci's default"}`);e.ttl&&e.note("mapped",e.ttl.key,"TTL",`ephemeral.ttl: ${e.ttl.value}`)}let d=[...e.env].filter(([p])=>!e.envConflicts.has(p)&&!e.secrets.has(p));for(let[p,f]of e.env)e.secrets.has(p)&&e.note("unmapped",f.key,"Variables",`${p} is a secret elsewhere, so it goes under pass`);if(d.length){o.env=Object.fromEntries(d.map(([p,f])=>[p,f.value]));for(let[p,f]of d)e.note("mapped",f.key,"Variables",`env: ${p}`)}if(e.secrets.size){o.pass={secrets:[...e.secrets.keys()].sort()};for(let[p,f]of e.secrets){let g=f.length>1?`; set in ${f.length} places, and a CI secret holds one value, so give a root's own value a name of its own`:"";e.note("mapped",f.join(", "),"Variables",`pass.secrets: ${p}; create the CI secret ${p}, whose value ${e.platform} does not give back${g}`)}}e.steps.length&&(o.steps=e.steps)}var bk=["env0-discovery.yml","env0-discovery.yaml"],Cve=new Set(["env0.yml","env0.yaml"]),Nve=new Set(["name","templateId","templateName","projectId","projectName","workspaceName","revision","variableFiles","requiresApproval","isRemoteBackend","continuousDeployment","autoDeployOnPathChangesOnly","autoDeployByCustomGlob","pullRequestPlanDeployments","vcsPrCommentsEnabled","vcsCommandsAlias","driftDetectionCron"]),Dve={setupVariables:{after:{before:"init"}},terraformInit:{before:{before:"init"},after:{after:"init"}},opentofuInit:{before:{before:"init"},after:{after:"init"}},terraformPlan:{before:{before:"plan"},after:{after:"plan"}},opentofuPlan:{before:{before:"plan"},after:{after:"plan"}},terraformApply:{before:{before:"apply"},after:{after:"apply"}},opentofuApply:{before:{before:"apply"},after:{after:"apply"}},storeState:{before:{after:"apply"},after:{after:"apply"}},terraformOutput:{before:{after:"apply"},after:{after:"apply"}},opentofuOutput:{before:{after:"apply"},after:{after:"apply"}}},Mve=/^(pulumi|cf|k8s|helm|ansible)/;function G2(e){let t=/^([1-9]\d*)-([hdwM])$/.exec(e.trim());if(!t)return;let n=Number(t[1]),r=t[2]==="h"?`${n}h`:t[2]==="d"?`${n}d`:t[2]==="w"?`${n*7}d`:`${n*30}d`;return ad(r)?r:void 0}var wr=e=>`${e.type}.${e.name}`;function Lve(e){let t=[],n=r=>{let o;try{o=Ove(r).sort()}catch{return}for(let s of o){if(s.startsWith(".")||s==="node_modules")continue;let i=wk(r,s);try{jve(i).isDirectory()?n(i):Cve.has(s)&&t.push(Ive(e,i))}catch{continue}}};return n(e),t}function F2(e,t){let n;try{n=uo(Pve(wk(e,t),"utf-8"))}catch(r){throw new S(`${t} is not YAML: ${r.message}`)}if(n==null)return{};if(!Me(n))throw new S(`${t} is not a map of settings`);return n}function B2(e,t={}){let n=t.file??bk.find(a=>L2(wk(e,a)));if(t.file!==void 0&&!L2(wk(e,t.file)))throw new S(`import env0 reads ${t.file}, and there is none in ${e}`);let r=n!==void 0?F2(e,n):void 0,o=Lve(e).map(a=>({file:a,doc:F2(e,a)})),s=mk(e,"env0_");if(r===void 0&&!o.length&&!s.length)throw new S(`import env0 reads ${bk[0]}, env0.yml custom flows or env0_* resources, and there are none in ${e}`);let i=[...n?[n]:[],...o.map(a=>a.file)];for(let a of s)i.includes(a.file)||i.push(a.file);return{...r0({discovery:r,flows:o,resources:s},t),read:i}}function Gve(e,t){return e.filter(n=>n==="."||t===n||t.startsWith(`${n}/`)).sort((n,r)=>r.length-n.length)[0]}function r0(e,t={}){let n=new Jc("env zero"),{notes:r}=n,{resources:o}=e,s=g=>o.filter(m=>m.type===g),i=t.repo?.roots,a=s("env0_template"),c=new Map;for(let g of a){let m=wr(g),h=g.body.attrs.path,y=h===void 0?".":Tt(h),w=y===void 0?void 0:xo(y.replace(/^\/+/,"")||".");c.set(m,w),w===void 0&&n.note("unmapped",`${m}.path`,"Root",`${so(h)} is not a directory inside the repo`)}let u=[];for(let g of s("env0_environment")){let h={id:Tt(g.body.attrs.name)??g.name,key:wr(g),res:g,template:js(g.body.attrs.template_id),project:js(g.body.attrs.project_id)},y=g.body.blocks.find(w=>w.type==="without_template_settings");if(y){let w=y.body.attrs.path===void 0?".":Tt(y.body.attrs.path);h.root=w===void 0?void 0:xo(w.replace(/^\/+/,"")||"."),z2(n,`${h.key}.without_template_settings`,y.body,[h])}else h.template&&c.has(h.template)&&(h.root=c.get(h.template));u.push(h)}let l=e.discovery??{};for(let g of Object.keys(l))g!=="environments"&&Xt(r,g);let d=Me(l.environments)?l.environments:{};l.environments!==void 0&&!Me(l.environments)&&Xt(r,"environments");for(let[g,m]of Object.entries(d)){let h=Me(m)?m:{},y=`environments.${g}`,w=typeof h.name=="string"?h.name:g,b=u.find(v=>v.id===w),k=b??{id:w,key:y};k.conf=h,k.confKey=y,b||u.push(k);for(let v of Object.keys(h))Nve.has(v)||Xt(r,`${y}.${v}`);if(k.root===void 0&&typeof h.templateName=="string"){let v=a.find($=>Tt($.body.attrs.name)===h.templateName);v&&(k.template=wr(v),k.root=c.get(wr(v)))}if(k.root===void 0&&i){let v=oo(h.variableFiles).find(_=>Me(_)&&typeof _.path=="string"),$=v?Gve(i,n0(v.path.replace(/^\.?\//,""))):void 0;$&&(k.root=$,n.note("mapped",`${y}.variableFiles`,"Root",`${$}, the root ${v.path} is in`))}if(k.root===void 0){let v=h.templateName??h.templateId;n.note("unmapped",y,"Root",`its template ${v===void 0?"":`${JSON.stringify(v)} `}is not an env0_template in the repo and its variable files sit in no root, so the import cannot tell its directory`)}}n.units.push(...u);for(let g of a){let m=u.filter(h=>h.template===wr(g));z2(n,wr(g),g.body,m),m.length||r.own(wr(g),"default","no environment the import read deploys this template, so it is not a root of its own")}let p=new Map;for(let g of s("env0_project_policy")){let m=js(g.body.attrs.project_id);m&&p.set(m,g);let h=wr(g);for(let[y,w]of Object.entries(g.body.attrs)){let b=`${h}.${y}`;if(y!=="project_id")if(y==="default_ttl"){let k=typeof w=="string"?G2(w):void 0;k&&(!n.ttl||n.ttl.value===k)?n.ttl={value:k,key:b}:k?n.note("unmapped",b,"TTL",`ephemeral.ttl is one setting, and ${n.ttl.key} gives ${n.ttl.value}`):n.note("default",b,"TTL",`${so(w)} is no fixed TTL, so nothing expires`)}else if(y==="max_ttl")n.note("default",b,"TTL","a copy lives until its pull request closes or its TTL passes");else if(y==="drift_detection_cron"){let k=Tt(w);k&&n.drift.push({cron:k,key:b,unit:m??h})}else y==="include_cost_estimation"?w===!0?(n.s.cost=!0,n.note("mapped",b,"Cost","cost: true")):n.note("default",b,"Cost","off, as terragucci's default"):y==="requires_approval_default"||y==="continuous_deployment_default"||y==="run_pull_request_plan_default"||y==="vcs_pr_comments_enabled_default"?Qo(r,b,"env zero","a default for new environments; each environment's own setting is read"):y==="auto_drift_remediation"?w!=="DISABLED"&&n.note("unmapped",b,"Drift","a drift run never applies; respond.drift opens a pull request for it"):y==="force_remote_backend"?w===!0&&n.note("unmapped",b,"Managed state","the project's environments keep their state in env zero"):Qo(r,b,"env zero")}}for(let g of u){let m=g.conf,h=g.res,y=(N,L)=>{if(m&&m[N]!==void 0)return{v:m[N],key:`${g.confKey}.${N}`};if(h&&h.body.attrs[L]!==void 0)return{v:h.body.attrs[L],key:`${g.key}.${L}`}},w=y("workspaceName","workspace");w&&n.note("unmapped",w.key,"Workspace",`workspace ${so(w.v)} is not planned`);let b=y("revision","revision");b&&Qo(r,b.key,"env zero","terragucci applies the default branch, and apply.branches applies roots from another");let k=y("requiresApproval","approve_plan_automatically"),v=k?k.key.endsWith("requiresApproval")?k.v===!0:k.v===!1:void 0;k&&v!==void 0&&n.approvals.push({key:k.key,waits:v});let $=y("isRemoteBackend","is_remote_backend"),_=g.project?p.get(g.project):void 0;$?.v===!0||!$&&_?.body.attrs.force_remote_backend===!0?n.note("unmapped",$?.key??`${g.key}: ${g.id}`,"Managed state",`${g.id}'s state is in env zero's remote backend: move it to ${g.root??"its root"}'s backend before the root's backend block merges`):$&&n.note("default",$.key,"Own backend");let E=y("continuousDeployment","deploy_on_push");E?.v===!1?n.note("unmapped",E.key,"Triggers","merge applies what changed; gate: always holds each wave for an approval first"):E&&n.note("default",E.key,"Triggers");for(let[N,L]of[["autoDeployOnPathChangesOnly","auto_deploy_on_path_changes_only"],["autoDeployByCustomGlob","auto_deploy_by_custom_glob"],["pullRequestPlanDeployments","run_plan_on_pull_requests"],["vcsPrCommentsEnabled","vcs_pr_comments_enabled"]]){let U=y(N,L);U&&n.note("default",U.key,"Triggers",N==="vcsPrCommentsEnabled"?"a comment of /terragucci plan plans again":void 0)}let R=y("vcsCommandsAlias","vcs_commands_alias");R&&n.note("unmapped",R.key,"Triggers","comments name a root by its path: /terragucci plan <root>");let x=y("driftDetectionCron","drift_detection_cron");x&&typeof x.v=="string"&&n.drift.push({cron:x.v,key:x.key,unit:g.id});let A=y("projectName","project_id")??y("projectId","project_id");if(A&&Qo(r,A.key.replace(/\.[^.]+$/,".project"),"env zero","a project's environments are this repo's roots"),m){for(let N of["name","templateName","templateId"])m[N]!==void 0&&n.note("default",`environments.*.${N}`,"Root","a root goes by its path from the repo root");oo(m.variableFiles).forEach((N,L)=>{let U=`${g.confKey}.variableFiles[${L}]`,F=Me(N)&&typeof N.path=="string"?N.path.replace(/^\.?\//,""):void 0;if(F===void 0)return Xt(r,U);let M=F.split("/").pop(),te=M==="terraform.tfvars"||M.endsWith(".auto.tfvars");g.root!==void 0&&n0(F)===g.root&&te?n.note("default",U,"Variables",`the binary reads ${F} in ${g.root} itself`):n.note("unmapped",U,"Variables",`copy ${F} to ${g.root??"the root"}/terraform.tfvars (or a *.auto.tfvars there), which the binary reads`)})}let O=h?.body.attrs.ttl,I=_&&typeof _.body.attrs.default_ttl=="string"?G2(_.body.attrs.default_ttl):void 0;if(O!==void 0||I){let N=O!==void 0?`${g.key}.ttl`:`${g.key}: ${g.id}`;g.root===void 0?n.note("unmapped",N,"TTL",`${g.id} has no root`):n.ephemeral.push({root:g.root,key:N})}if(h){let N=new Set(["name","template_id","project_id","workspace","revision","approve_plan_automatically","is_remote_backend","deploy_on_push","auto_deploy_on_path_changes_only","auto_deploy_by_custom_glob","run_plan_on_pull_requests","vcs_pr_comments_enabled","vcs_commands_alias","drift_detection_cron","ttl"]);for(let L of Object.keys(h.body.attrs))N.has(L)||(L==="variable_sets"?n.note("unmapped",`${g.key}.variable_sets`,"Variables","set the variable sets' values by hand: env for values, pass for secrets"):L==="terragrunt_working_directory"?n.note("default",`${g.key}.${L}`,"Root","Terragrunt is detected, and each unit is a root"):L==="k8s_namespace"||L==="sub_environment_configuration"?n.note("unmapped",`${g.key}.${L}`,"Root","terragucci runs Terraform, OpenTofu and Terragrunt"):Qo(r,`env0_environment.*.${L}`,"env zero"));for(let L of h.body.blocks)if(L.type==="configuration")U2(n,`${g.key}.configuration: ${Tt(L.body.attrs.name)??"?"}`,L.body,!1,g);else{if(L.type==="without_template_settings")continue;L.type==="sub_environment_configuration"?n.note("unmapped",`${g.key}.sub_environment_configuration`,"Order","a workflow's sub environments are roots, ordered by their terraform_remote_state reads"):Qo(r,`env0_environment.*.${L.type}`,"env zero")}}}for(let g of s("env0_configuration_variable")){let m=wr(g),h=js(g.body.attrs.environment_id),y=js(g.body.attrs.template_id),w=h?u.find(v=>v.res&&wr(v.res)===h):void 0,b=y?u.filter(v=>v.template===y):void 0,k=!h&&(!y||b!==void 0&&b.length===u.length);U2(n,m,g.body,k,w??(b?.length===1?b[0]:void 0))}for(let g of s("env0_environment_drift_detection")){let m=js(g.body.attrs.environment_id),h=Tt(g.body.attrs.cron);h?n.drift.push({cron:h,key:`${wr(g)}.cron`,unit:u.find(w=>w.res&&wr(w.res)===m)?.id??wr(g)}):n.note("unmapped",`${wr(g)}.cron`,"Drift","it is not a plain cron schedule");let y=g.body.attrs.auto_drift_remediation;y!==void 0&&y!=="DISABLED"&&n.note("unmapped",`${wr(g)}.auto_drift_remediation`,"Drift","a drift run never applies; respond.drift opens a pull request for it")}for(let g of e.flows)Fve(n,g.file,g.doc,u);let f=new Set(["env0_template","env0_environment","env0_project_policy","env0_configuration_variable","env0_environment_drift_detection"]);for(let g of o){if(f.has(g.type))continue;let m=wr(g);g.type==="env0_project"||g.type==="env0_template_project_assignment"?Qo(r,m,"env zero","a project's environments are this repo's roots"):g.type.includes("credentials")?n.note("unmapped",m,g.type.startsWith("env0_cost_")?"Cost":"Cloud credentials",g.type.startsWith("env0_cost_")?"Infracost prices on your INFRACOST_API_KEY":"name a read-only plan role and an apply role under oidc"):g.type.startsWith("env0_variable_set")?n.note("unmapped",m,"Variables","set its values by hand: env for values, pass for secrets"):g.type.startsWith("env0_agent")?n.note("unmapped",m,"Workers"):g.type.startsWith("env0_notification")?n.note("unmapped",m,"Notifications"):g.type.startsWith("env0_approval_policy")?n.note("unmapped",m,"Plan policy","copy its Rego into policy; a rule that asked for an approval becomes cost.approve_above or gate: always"):/^env0_(user|team|custom_role|.*role_assignment|organization)/.test(g.type)?n.note("unmapped",m,"Access"):g.type==="env0_module"||g.type.startsWith("env0_module_")?n.note("unmapped",m,"Module registry"):g.type.includes("trigger")?n.note("unmapped",m,"Order","a root that reads another's state through terraform_remote_state applies after it"):Xt(r,m)}return yk(n,t,"environments"),{settings:n.s,notes:r.list}}function z2(e,t,n,r){let o=Tt(n.attrs.type)??"terraform",s=!1;if(o==="opentofu")s=!0,e.binary("tofu",`${t}.type`);else if(o==="terraform")n.attrs.type!==void 0&&e.binary("terraform",`${t}.type`);else if(o==="terragrunt")e.note("default",`${t}.type`,"Root","Terragrunt is detected from the repo's root.hcl or terragrunt.hcl, with terragrunt.version for its release"),s=(Tt(n.attrs.terragrunt_tf_binary)??"opentofu")==="opentofu",e.binary(s?"tofu":"terraform",`${t}.terragrunt_tf_binary`);else{e.note("unmapped",`${t}.type`,"Root",`terragucci runs Terraform, OpenTofu and Terragrunt, not ${o}`);return}let i=s?n.attrs.opentofu_version:n.attrs.terraform_version,a=`${t}.${s?"opentofu_version":"terraform_version"}`;if(i!==void 0){let u=[...new Set(r.map(l=>l.root).filter(l=>l!==void 0))];u.length||e.version(void 0,i,a);for(let l of u)e.version(l,i,a)}(s?n.attrs.terraform_version:n.attrs.opentofu_version)!==void 0&&e.note("default",`${t}.${s?"terraform_version":"opentofu_version"}`,"Version",`the template runs ${s?"OpenTofu":"Terraform"}`)}function U2(e,t,n,r,o){let s=Tt(n.attrs.name);if(!s)return e.note("unmapped",t,"Variables","its name is not a plain string");let i=Tt(n.attrs.type)==="terraform"?`TF_VAR_${s}`:s;if(n.attrs.is_sensitive===!0)return e.takeSecret(t,i);let a=Tt(n.attrs.value);if(a===void 0)return e.note("unmapped",t,"Variables","its value is not a plain string in the code; set it by hand");if(r)return e.takeEnv(t,i,a);let c=o?.root;e.note("unmapped",t,"Variables",i.startsWith("TF_VAR_")?`it is ${o?.id??"one environment"}'s alone: set ${s} in ${c??"its root"}/terraform.tfvars`:`it is ${o?.id??"one environment"}'s alone, and env is every root's`)}function Fve(e,t,n,r){let{notes:o}=e,s=xo(n0(t))??".",i=[...new Set(r.map(p=>p.root).filter(p=>p!==void 0))],a=r.length>0&&r.every(p=>p.root===s),c=s==="."&&!i.includes(".");if(!c&&!i.includes(s)&&r.length){o.own(t,"unmapped",`no environment the import read runs ${s}`,"a custom flow runs in its template's directory");return}let u=n.shell==="bash";for(let p of Object.keys(n))p==="version"?o.own(`${t}: version`,"default","the file's format version; terragucci.yml has none"):p==="shell"?o.own(`${t}: shell`,u?"mapped":"default",u?"each step runs its commands under bash":"a step runs under sh"):p==="destroy"?e.note("unmapped",`${t}: destroy`,"Hooks","a destroy is a reviewed change that removes the code, or an ephemeral copy's sweep"):p==="task"?e.note("unmapped",`${t}: task`,"Hooks","a task has no counterpart: state changes come from a reviewed commit"):p!=="deploy"&&Xt(o,`${t}: ${p}`);let l=Me(n.deploy)?n.deploy:{},d=(p,f,g)=>{let m=[];if(oo(g).forEach((y,w)=>{let b=typeof y=="string"?y:Me(y)&&typeof y.run=="string"?y.run.trimEnd():void 0;if(b===void 0)return Xt(o,`${p}[${w}]`);let k=/^echo\s+["']?([A-Za-z_][A-Za-z0-9_]*)=([^"'$`]*)["']?\s*>>\s*"?\$\{?ENV0_ENV\}?"?\s*$/.exec(b.trim());if(k){a||c?e.takeEnv(`${p}[${w}]`,k[1],k[2]):e.note("unmapped",`${p}[${w}]`,"Variables",`it is ${s}'s alone, and env is every root's`);return}b.includes("ENV0_ENV")&&e.note("unmapped",`${p}[${w}]`,"Hooks","it sets a variable for later steps, and a step cannot set the binary's environment"),m.push(b)}),!m.length)return;let h=Jc.script(m);u&&(h=`bash -c '${h.replace(/'/g,"'\\''")}'`),c&&(h=`cd "$TG_REPO" && ${h}`),e.steps.push({name:p,run:h,...f,...c||a?{}:{roots:[s]}}),e.note("mapped",p,"Hooks",`steps: ${f.before?"before":"after"}: ${f.before??f.after}${c||a?" for every root":` for ${s}`}${c?", from the repo's root":""}`)};for(let[p,f]of Object.entries(l)){let g=`${t}: deploy.${p}`;if(p==="onSuccess")d(g,{after:"apply"},f);else if(p==="onFailure"||p==="onCompletion")e.note("unmapped",g,"Hooks","a step after a stage runs when the stage succeeded");else if(p==="steps"&&Me(f))for(let[m,h]of Object.entries(f)){let y=`${t}: deploy.steps.${m}`,w=Dve[m];if(!w){Mve.test(m)?e.note("unmapped",y,"Root","terragucci runs Terraform, OpenTofu and Terragrunt"):Xt(o,y);continue}if(!Me(h)){Xt(o,y);continue}for(let[b,k]of Object.entries(h)){let v=b==="before"?w.before:b==="after"?w.after:void 0;v?d(`${y}.${b}`,v,k):Xt(o,`${y}.${b}`)}}else Xt(o,g)}}import{existsSync as W2,readFileSync as zve}from"node:fs";import{join as o0}from"node:path";var $k=[".spacelift/config.yml",".spacelift/config.yaml"],vk={before_init:{before:"init"},after_init:{after:"init"},before_plan:{before:"plan"},after_plan:{after:"plan"},before_apply:{before:"apply"},after_apply:{after:"apply"},before_perform:void 0,after_perform:void 0,before_destroy:void 0,after_destroy:void 0,after_run:void 0},H2=new Set([...Object.keys(vk),"environment","project_root","runner_image","terraform_version","opentofu_version","terraform_workflow_tool","git_sparse_checkout_paths","terragrunt"]),q2=new Set(["name","slug","repository","branch","description","space_id","protect_from_deletion","enable_local_preview","autoretry","allow_run_promotion","github_action_deploy","enable_well_known_secret_masking","terraform_smart_sanitization","terraform_external_state_access","prevent_changes_when_locked","git_sparse_checkout_paths","enable_sensitive_outputs_upload","import_state","import_state_file","labels","github_enterprise","gitlab","azure_devops","bitbucket_cloud","bitbucket_datacenter","raw_git","origin","spacelift_repo","showcase"]);function Uve(e){return e.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"")}function J2(e,t){return t in e.body.attrs||e.body.blocks.some(n=>n.type===t)}var Zn=e=>`${e.type}.${e.name}`;function K2(e,t={}){let n=t.file??$k.find(i=>W2(o0(e,i))),r={},o=[];if(n!==void 0){if(!W2(o0(e,n)))throw new S(`import spacelift reads ${n}, and there is none in ${e}`);let i;try{i=uo(zve(o0(e,n),"utf-8"))}catch(a){throw new S(`${n} is not YAML: ${a.message}`)}if(i!=null&&!Me(i))throw new S(`${n} is not a map of settings`);r=i??{},o.push(n)}let s=mk(e,"spacelift_");for(let i of s)o.includes(i.file)||o.push(i.file);if(n===void 0&&!s.length)throw new S(`import spacelift reads ${$k[0]} or spacelift_stack resources, and there are neither in ${e}`);return{...s0(r,s,t),read:o}}function s0(e,t,n={}){let r=new Jc("Spacelift"),{notes:o}=r,s=new Map(t.map(w=>[Zn(w),w])),i=w=>t.filter(b=>b.type===w);for(let w of Object.keys(e))w==="version"?o.own("version","default","the file's format version; terragucci.yml has none"):w==="module_version"||w==="tests"?r.note("unmapped",w,"Module registry","a module's version and tests are the module's own release"):w!=="stack_defaults"&&w!=="stacks"&&Xt(o,w);let a=Me(e.stack_defaults)?e.stack_defaults:{};e.stack_defaults!==void 0&&!Me(e.stack_defaults)&&Xt(o,"stack_defaults");let c=Me(e.stacks)?e.stacks:{};e.stacks!==void 0&&!Me(e.stacks)&&Xt(o,"stacks");let u=[],l=new Map;for(let w of i("spacelift_stack")){let b=Tt(w.body.attrs.name),k=Tt(w.body.attrs.slug)??(b?Uve(b):w.name),v={id:k,key:Zn(w),res:w,labels:Ip(w.body.attrs.labels)??[]};u.push(v),l.set(k,v)}for(let[w,b]of Object.entries(c)){let k=l.get(w)??{id:w,key:`stacks.${w}`,labels:[]};l.has(w)||(u.push(k),l.set(w,k)),k.conf=Me(b)?b:{},k.confKey=`stacks.${w}`,!Me(b)&&b!==null&&Xt(o,`stacks.${w}`);for(let v of Object.keys(k.conf))H2.has(v)||Xt(o,`stacks.${w}.${v}`)}for(let w of Object.keys(a))H2.has(w)||Xt(o,`stack_defaults.${w}`);!u.length&&Me(e.stack_defaults)&&u.push({id:"the repo's stack",key:"stack_defaults",labels:[]});let d=(w,b)=>{if(w.conf&&w.conf[b]!==void 0)return{v:w.conf[b],key:`${w.confKey}.${b}`};if(a[b]!==void 0)return{v:a[b],key:`stack_defaults.${b}`};let k=w.res?.body.attrs[b];if(k!==void 0)return{v:k,key:`${w.key}.${b}`}};for(let w of u){let b=d(w,"project_root"),k=b===void 0?".":typeof b.v=="string"?Tt(b.v):void 0;w.root=k===void 0?void 0:xo(k===""?".":k),b&&w.root===void 0&&r.note("unmapped",b.key,"Root",`${so(b.v)} is not a directory inside the repo`),r.units.push(w)}for(let w of u){let b=d(w,"terraform_workflow_tool"),k=w.res&&w.res.body.blocks.some(E=>E.type==="opentofu"),v=!1;b?b.v==="OPEN_TOFU"?(v=!0,r.binary("tofu",b.key)):b.v==="TERRAFORM_FOSS"?r.binary("terraform",b.key):r.note("unmapped",b.key,"Version",`${so(b.v)} is not Terraform or OpenTofu; set binary to the one the stack runs`):k&&(v=!0,r.binary("tofu",`${w.key}.opentofu`));let $=v?d(w,"opentofu_version")??d(w,"terraform_version"):d(w,"terraform_version");$&&r.version(w.root,$.v,$.key);let _=v?d(w,"terraform_version"):d(w,"opentofu_version");_&&_!==$&&r.note("default",_.key,"Version",`the stack runs ${v?"OpenTofu":"Terraform"}`)}let p=i("spacelift_context"),f=new Map(p.map(w=>[Zn(w),new Set])),g=w=>{let b=js(w);if(b)return u.find(v=>v.res&&Zn(v.res)===b);let k=Tt(w);return k?l.get(k):void 0};for(let w of i("spacelift_context_attachment")){let b=js(w.body.attrs.context_id),k=g(w.body.attrs.stack_id);b&&f.has(b)&&k?(f.get(b).add(k),Qo(o,Zn(w),"Spacelift",`attaches ${b} to ${k.id}`)):r.note("unmapped",Zn(w),"Variables","the import cannot tell which context and stack it joins")}for(let w of p){for(let b of Ip(w.body.attrs.labels)??[]){let k=/^autoattach:(.+)$/.exec(b);if(k)for(let v of u)(k[1]==="*"||v.labels.includes(k[1]))&&f.get(Zn(w)).add(v)}for(let b of Object.keys(w.body.attrs))!(b in vk)&&b!=="name"&&b!=="labels"&&b!=="description"&&b!=="space_id"&&Xt(o,`${Zn(w)}.${b}`)}let m=w=>u.every(b=>w.has(b)),h=new Map;for(let[w,b]of Object.entries(vk))for(let k of u){let v=d(k,w),$=v?Array.isArray(v.v)?Ip(v.v):void 0:[];if(v&&$===void 0){r.note("unmapped",v.key,"Hooks","it is not a list of commands");continue}let _=p.filter(I=>f.get(Zn(I)).has(k)&&I.body.attrs[w]!==void 0),E=_.flatMap(I=>Ip(I.body.attrs[w])??[]),R=w.startsWith("before")?[...E,...$]:[...$,...E],x=[...v?[v.key]:[],..._.map(I=>`${Zn(I)}.${w}`)];if(!R.length)continue;if(!b){for(let I of x)r.note("unmapped",I,"Hooks",w.includes("destroy")?"a destroy is a reviewed change that removes the code":w.includes("perform")?"a task has no counterpart: state changes come from a reviewed commit":"after_run runs whatever the run's outcome; a step after a stage runs when the stage succeeded");continue}if(k.root===void 0){for(let I of x)r.note("unmapped",I,"Hooks",`${k.id} has no root`);continue}let A=Jc.script(R),O=h.get(`${w}\0${A}`)??{stage:b,hook:w,script:A,roots:new Set,keys:new Set,stacks:new Set};O.roots.add(k.root),O.stacks.add(k),x.forEach(I=>O.keys.add(I)),h.set(`${w}\0${A}`,O)}for(let w of h.values()){let b=m(w.stacks),k=[...w.roots].sort();r.steps.push({name:w.hook,run:w.script,...w.stage,...b?{}:{roots:k}});let v=`${w.stage.before?"before":"after"}: ${w.stage.before??w.stage.after}`;for(let $ of w.keys)r.note("mapped",$,"Hooks",`steps: ${v}${b?" for every root":` for ${k.join(", ")}`}`)}if(Me(a.environment))for(let[w,b]of Object.entries(a.environment))typeof b=="string"||typeof b=="number"||typeof b=="boolean"?r.takeEnv(`stack_defaults.environment.${w}`,w,String(b)):r.note("unmapped",`stack_defaults.environment.${w}`,"Variables","its value is not a string");for(let w of u)if(!(!w.conf||!Me(w.conf.environment)))for(let b of Object.keys(w.conf.environment))r.note("unmapped",`${w.confKey}.environment.${b}`,"Variables",`it is ${w.id}'s alone, and env is every root's; a TF_VAR_ value goes in ${w.root??"its root"}'s terraform.tfvars`);for(let w of i("spacelift_environment_variable")){let b=Zn(w),k=Tt(w.body.attrs.name);if(!k){r.note("unmapped",b,"Variables","its name is not a plain string");continue}let v=js(w.body.attrs.context_id),$=w.body.attrs.stack_id!==void 0?g(w.body.attrs.stack_id):void 0;if(w.body.attrs.module_id!==void 0){r.note("unmapped",b,"Module registry","a module's test variable");continue}if(w.body.attrs.write_only!==!1){r.takeSecret(b,k);continue}let _=Tt(w.body.attrs.value),E=v&&f.has(v)?f.get(v):$?new Set([$]):void 0;_===void 0?r.note("unmapped",b,"Variables","its value is not a plain string in the code; set it by hand"):E&&m(E)?r.takeEnv(b,k,_):r.note("unmapped",b,"Variables",`it is for ${E?[...E].map(R=>R.id).join(", ")||"no stack":"a stack the import cannot tell"}, and env is every root's; a TF_VAR_ value goes in the root's terraform.tfvars`)}for(let w of i("spacelift_mounted_file")){let b=Tt(w.body.attrs.relative_path)??so(w.body.attrs.relative_path);r.note("unmapped",Zn(w),"Files",w.body.attrs.write_only===!1?`commit ${b} in the root`:`${b} is secret: keep it in a CI secret and write it in a step before init`)}for(let w of i("spacelift_stack_dependency")){let b=Zn(w),k=g(w.body.attrs.stack_id),v=g(w.body.attrs.depends_on_stack_id);!k||!v?r.note("unmapped",b,"Order",`${k?so(w.body.attrs.depends_on_stack_id):so(w.body.attrs.stack_id)} names no stack the import read`):k.root===void 0||v.root===void 0?r.note("unmapped",b,"Order",`${k.root===void 0?k.id:v.id} has no root`):r.edges.push({d:k.root,u:v.root,key:b}),w.body.attrs.trigger_always!==void 0&&r.note("default",`${b}.trigger_always`,"Triggers","a root that reads a changed root's state plans with it")}for(let w of i("spacelift_stack_dependency_reference")){let b=Tt(w.body.attrs.input_name)??so(w.body.attrs.input_name),k=Tt(w.body.attrs.output_name)??so(w.body.attrs.output_name);r.note("unmapped",Zn(w),"Order",`read ${k} through a terraform_remote_state block of the upstream root in place of the input ${b}`)}for(let w of i("spacelift_drift_detection")){let b=Zn(w),k=g(w.body.attrs.stack_id),v=Ip(w.body.attrs.schedule);if(!v?.length)r.note("unmapped",`${b}.schedule`,"Drift","it is not a list of cron schedules");else for(let _ of v)r.drift.push({cron:_,key:v.length>1?`${b}.schedule: ${_}`:`${b}.schedule`,unit:k?.id??b});w.body.attrs.reconcile===!0&&r.note("unmapped",`${b}.reconcile`,"Drift","a drift run never applies; respond.drift opens a pull request for it");let $=Tt(w.body.attrs.timezone);$&&$!=="UTC"&&r.note("unmapped",`${b}.timezone`,"Drift",`the cron runs in UTC, not ${$}`)}for(let w of u){let b=w.res;if(!b)continue;let k=w.key;r.approvals.push({key:`${k}.autodeploy`,waits:b.body.attrs.autodeploy!==!0}),b.body.attrs.manage_state!==!1?r.note("unmapped",`${k}.manage_state`,"Managed state",`${w.id}'s state is in Spacelift: pull it and move it to ${w.root??"its root"}'s backend before the root's backend block merges`):r.note("default",`${k}.manage_state`,"Own backend"),b.body.attrs.terraform_workspace!==void 0&&r.note("unmapped",`${k}.terraform_workspace`,"Workspace",`workspace ${so(b.body.attrs.terraform_workspace)} is not planned`),b.body.attrs.worker_pool_id!==void 0&&r.note("unmapped",`${k}.worker_pool_id`,"Workers","name the runner label of your own runners under runner"),b.body.attrs.additional_project_globs!==void 0&&r.note("default",`${k}.additional_project_globs`,"Triggers","a root plans when a file in it, a local module it calls or its var files changed"),b.body.attrs.administrative!==void 0&&o.own(`${k}.administrative`,"unmapped","an administrative stack manages Spacelift itself","once every stack it declares has moved, stop applying it");for(let v of["pulumi","kubernetes","cloudformation","ansible"])J2(b,v)&&r.note("unmapped",`${k}.${v}`,"Root",`terragucci runs Terraform, OpenTofu and Terragrunt, not ${v}`);J2(b,"terragrunt")&&r.note("default",`${k}.terragrunt`,"Root","Terragrunt is detected from the repo's root.hcl or terragrunt.hcl");for(let v of Object.keys(b.body.attrs))q2.has(v)?Qo(o,`spacelift_stack.*.${v}`,"Spacelift"):!["project_root","terraform_version","terraform_workflow_tool","autodeploy","manage_state","terraform_workspace","worker_pool_id","additional_project_globs","administrative","runner_image"].includes(v)&&!(v in vk)&&Xt(o,`${k}.${v}`);for(let v of b.body.blocks)!q2.has(v.type)&&!["opentofu","terragrunt","pulumi","kubernetes","cloudformation","ansible"].includes(v.type)&&Xt(o,`${k}.${v.type}`)}for(let w of u){let b=d(w,"runner_image");b&&r.note("unmapped",b.key,"Runner image","build an image FROM terragucci's with what the hooks need, and set image")}(Me(a.terragrunt)||u.some(w=>w.conf&&w.conf.terragrunt!==void 0))&&r.note("default","terragrunt","Root","Terragrunt is detected from the repo's root.hcl or terragrunt.hcl, with terragrunt.version for its release");for(let w of i("spacelift_policy")){let b=Zn(w),k=Tt(w.body.attrs.type)??"",v=w.body.attrs.body,$=v&&typeof v=="object"&&"file"in v?` in ${v.file.replace(/^\$\{path\.module\}\//,"")}`:"";k==="PLAN"?r.note("unmapped",b,"Plan policy",`copy the Rego${$} into policy with input: plan, and change input.terraform to input; input.spacelift has no counterpart`):k==="APPROVAL"?r.note("unmapped",b,"Approval","gate decides which waves wait, and how many reviewers a change needs is the forge's branch protection"):k==="GIT_PUSH"||k==="TRIGGER"?r.note("default",b,"Triggers"):k==="LOGIN"||k==="ACCESS"||k==="STACK_ACCESS"?r.note("unmapped",b,"Access"):k==="NOTIFICATION"?r.note("unmapped",b,"Notifications","set notify for the channels it routed to"):o.own(b,"unmapped",`a ${k||"policy"} policy has no counterpart`,"a state change comes from an import, removed or moved block, or a migration, in a reviewed commit")}let y=new Set(["spacelift_stack","spacelift_context","spacelift_context_attachment","spacelift_environment_variable","spacelift_mounted_file","spacelift_stack_dependency","spacelift_stack_dependency_reference","spacelift_drift_detection","spacelift_policy"]);for(let w of t){if(y.has(w.type))continue;let b=Zn(w);w.type==="spacelift_policy_attachment"?Qo(o,b,"Spacelift","the policy's own note says where it goes"):/_(integration|integration_attachment|aws_role)$/.test(w.type)||w.type.startsWith("spacelift_aws_")||w.type.startsWith("spacelift_gcp_")||w.type.startsWith("spacelift_azure_")?r.note("unmapped",b,"Cloud credentials","name a read-only plan role and an apply role under oidc"):w.type==="spacelift_worker_pool"?r.note("unmapped",b,"Workers"):w.type==="spacelift_module"||w.type.startsWith("spacelift_module_")?r.note("unmapped",b,"Module registry"):/^spacelift_(space|role|role_attachment|user|idp_group_mapping|api_key|team|saml_saml2|audit_trail_webhook)/.test(w.type)?r.note("unmapped",b,"Access"):w.type==="spacelift_webhook"||w.type==="spacelift_named_webhook"?r.note("unmapped",b,"Notifications","notify.webhook posts a signed body to an address"):Xt(o,b)}return yk(r,n,"stacks"),{settings:r.s,notes:o.list}}import{existsSync as Sh,readdirSync as Bve,readFileSync as i0,statSync as V2}from"node:fs";import{join as Kc,relative as Wve,resolve as Hve}from"node:path";var qve=/[A-Za-z0-9_-]/;function a0(e){let t=0,n=p=>{let f=e.slice(0,t).split(`
|
|
940
|
+
`;)this.i++;continue}if(this.ws(!0),this.s[this.i]==="="&&this.s[this.i+1]!=="="){this.i++,t.attrs[n]=this.value();continue}let r=[];for(;this.s[this.i]==='"'||/[A-Za-z_]/.test(this.s[this.i]??"");)r.push(this.s[this.i]==='"'?this.str():this.ident()),this.ws(!0);this.s[this.i]==="{"&&(this.i++,t.blocks.push({type:n,labels:r,body:this.body()}))}}};function xve(e){return new t0(e).body()}function Eve(e){let t=[],n=r=>{let o;try{o=vve(r).sort()}catch{return}for(let s of o){if(Rve.has(s)||s.startsWith("."))continue;let i=_ve(r,s),a;try{a=kve(i)}catch{continue}a.isDirectory()?n(i):(s.endsWith(".tf")||s.endsWith(".tofu"))&&t.push(i)}};return n(e),t}function mk(e,t){let n=[];for(let r of Eve(e)){let o=$ve(r,"utf-8");if(o.includes(`"${t}`))for(let s of xve(o).blocks)s.type==="resource"&&s.labels.length===2&&s.labels[0].startsWith(t)&&n.push({type:s.labels[0],name:s.labels[1],file:Sve(e,r),body:s.body})}return n}function Tt(e){return typeof e=="string"&&!e.includes("${")?e:void 0}function Ip(e){if(!Array.isArray(e))return;let t=e.map(Tt);return t.every(n=>n!==void 0)?t:void 0}function js(e){if(!e||typeof e!="object"||!("ref"in e))return;let t=e.ref.split(".");return t.length>=2?`${t[0]}.${t[1]}`:void 0}function so(e){return e===void 0?"":typeof e=="string"?JSON.stringify(e):typeof e!="object"?String(e):Array.isArray(e)?`[${e.map(so).join(", ")}]`:"ref"in e?e.ref:"file"in e?`file("${e.file}")`:e.expr}var hk="https://intentius.io/terragucci/guides/coming-from-spacelift-or-env-zero/",Tve=[["Root","stack","environment","a [root](/terragucci/concepts/glossary/#root): one directory, one state; an import writes `roots` from the stacks' project roots or the environments' template paths"],["Shared code","several stacks on one project root","several environments from one template","a directory per stack or environment, each with its backend key and `terraform.tfvars`, calling a shared module"],["Workspace","`terraform_workspace`","`workspaceName`, `workspace`","none: each root is a directory with one state"],["Managed state","managed state, `manage_state`","env zero's remote backend, `isRemoteBackend`","a key in your S3 bucket, moved by a [backend move](#state-move); an import names each stack or environment whose state moves"],["Own backend","your own backend","your own S3 backend","the same backend; nothing moves"],["Variables","context and stack environment variables","variables","[`env`](/terragucci/reference/config/#keys) for values every root gets; a secret's name under [`pass`](#variables), whose value you create as a CI secret; a root's own values in its `terraform.tfvars`"],["Files","context and stack mounted files","","files committed in the root, such as `terraform.tfvars`"],["Cloud credentials","cloud integration","credentials","[`oidc`](/terragucci/reference/environment/#cloud-roles-over-oidc): a plan role and an apply role per cloud"],["Version","`terraform_version`, `opentofu_version`, `terraform_workflow_tool`","a template's `type`, `terraform_version`, `opentofu_version`","`binary`, and [`version`](/terragucci/reference/config/#a-version-per-root) for every root or per root glob, or the root's `.terraform-version`"],["Hooks","hooks: `before_init`, `after_plan`, `before_apply` and the rest","custom flows (`env0.yml`)","[`steps`](/terragucci/guides/run-steps/) before or after `init`, `plan`, `apply` and `drift`, for the roots the hook ran in; nothing runs at a destroy or a task"],["Runner image","`runner_image`","","`image`: one image for every job, built FROM the terragucci image for the binary"],["Plan policy","plan policy","approval policy (OPA)","[`policy`](/terragucci/reference/policy/) over each plan, with [cost](/terragucci/guides/estimate-cost/#cost-in-the-policy) in `input.cost`"],["Approval","approval policy, `autodeploy` off","approval policy, `requiresApproval`","[`gate`](/terragucci/reference/stages/#gate-policy) and [approval modes](/terragucci/guides/approve-a-wave/#approval-modes); [`cost.approve_above`](/terragucci/guides/estimate-cost/#hold-a-wave-over-an-amount); an import writes `gate: always` when every stack or environment waits for an approval"],["Triggers","push and trigger policies, project globs","triggers, `continuousDeployment`, `pullRequestPlanDeployments`","the [generated pipeline](/terragucci/getting-started/): a pull request plans the roots its files reach; merge applies them"],["Order","stack dependencies, output references","workflows","[waves](/terragucci/concepts/waves-and-approvals/#linked-roots): a root that reads another through `terraform_remote_state` applies in a later wave; an import writes `waves.after` for a stack dependency the reads do not give"],["Access","login policy, spaces","RBAC","your forge's permissions and cloud IAM ([access and identity](/terragucci/standards/access-and-identity/))"],["Notifications","notification policy","notifications","[`notify`](/terragucci/guides/notify-a-chat-channel/): Slack, Teams or a signed webhook"],["Drift","drift detection","drift detection, `driftDetectionCron`","[`drift`](/terragucci/guides/turn-on-drift-checks/), one cron schedule over every root, and a pull request for drift on a literal; it never reconciles"],["TTL","","environment TTL","[ephemeral environments](/terragucci/guides/ephemeral-environments/) per pull request, with a TTL; an import writes `ephemeral` for the environments that expire"],["Cost","","cost monitoring","[`cost`](/terragucci/guides/estimate-cost/): an estimate per plan, not billed spend"],["Workers","private workers, `worker_pool_id`","self-hosted agents","your CI's runners, your own ones by label with [`runner`](/terragucci/guides/add-to-a-repo/#self-hosted-runners)"],["Module registry","module registry","module registry","[a module registry](/terragucci/guides/publish-modules/#serve-a-module-registry) in your bucket"],["Run history","run history","deployment log","the [report](/terragucci/guides/keep-reports-in-a-bucket/) per run and [`chant/lifecycle`](/terragucci/concepts/glossary/#chantlifecycle)"]];function Ave(e){return bo(Tve.find(t=>t[0]===e)[3])}function DJ(e,t,n,r,o){e.cell(t,n,r,Ave(r),o)}function Xt(e,t,n){e.own(t,"unmapped",`the guide has no row for it, so nothing was written; see ${hk}`,n)}function Qo(e,t,n,r){e.own(t,"default",`${n}'s own setting, with nothing to carry over`,r)}var MJ=/^[A-Za-z_][A-Za-z0-9_]*$/,Jc=class{constructor(t){this.platform=t}platform;notes=new Ei;s={};units=[];steps=[];versions=new Map;binaries=new Map;env=new Map;envConflicts=new Set;secrets=new Map;approvals=[];drift=[];edges=[];ephemeral=[];ttl;note(t,n,r,o){DJ(this.notes,t,n,r,o)}version(t,n,r){if(n===void 0)return;let o=String(n).trim().replace(/^v/,"");if(!Ja.test(o))return this.note("default",r,"Version",`${JSON.stringify(n)} is not one release, so the root's required_version pin decides`);if(t===void 0)return this.note("unmapped",r,"Version","the import cannot tell which root it is for");let s=this.versions.get(t);if(s&&s.version!==o)return this.note("unmapped",r,"Version",`${t} runs ${s.version} by ${s.key}`);s||this.versions.set(t,{version:o,key:r})}binary(t,n){this.binaries.set(t,[...this.binaries.get(t)??[],n])}takeEnv(t,n,r){if(!MJ.test(n))return this.note("unmapped",t,"Variables",`${JSON.stringify(n)} is not a variable name`);let o=this.env.get(n);if(o&&o.value!==r)return this.envConflicts.add(n),this.note("unmapped",t,"Variables",`${n} is set to two values (in ${o.key} too), and \`env\` holds one`);o||this.env.set(n,{value:r,key:t})}takeSecret(t,n){if(!MJ.test(n))return this.note("unmapped",t,"Variables",`${JSON.stringify(n)} is not a variable name`);if(ku.some(r=>n.toUpperCase().startsWith(r))||nd.includes(n))return this.note("unmapped",t,"Variables",`${n} is a name the forge refuses for a secret or terragucci sets itself; pass it under another name`);this.secrets.set(n,[...this.secrets.get(n)??[],t])}static script(t){return t.length===1?t[0]:["set -e",...t].join(`
|
|
941
|
+
`)}};function yk(e,t,n){let{notes:r,s:o}=e,s=new Map;for(let p of e.units)p.root!==void 0&&s.set(p.root,[...s.get(p.root)??[],p]);let i=e.units.filter(p=>p.root===void 0);s.size&&!i.length?(o.roots=[...s.keys()].sort(),e.note("mapped",n,"Root",`roots: ${o.roots.join(", ")}`)):s.size&&e.note("unmapped",n,"Root",`no roots written, so terragucci detects them: ${i.map(p=>p.id).join(", ")} ${i.length===1?"has":"have"} no directory the files name`);for(let[p,f]of s)f.length>1&&e.note("unmapped",f.map(g=>g.key).join(", "),"Shared code",`${f.map(g=>g.id).join(", ")} all run ${p}, which is one root with one state; give each a directory`);let a=[...e.binaries.keys()];a.length===1?(o.binary=a[0],e.note("mapped",e.binaries.get(a[0])[0],"Version",`binary: ${a[0]}`)):a.length>1&&e.note("unmapped",[...e.binaries.values()].flat().join(", "),"Version",`the ${e.platform} config runs ${a.join(" and ")}, and the pipeline runs one binary`);let c=new Set([...e.versions.values()].map(p=>p.version));if(c.size===1){let[p]=c;o.version=p;for(let f of e.versions.values())e.note("mapped",f.key,"Version",`version: ${p}`)}else if(c.size>1){o.version=Object.fromEntries([...e.versions].sort(([p],[f])=>p.localeCompare(f)).map(([p,f])=>[p,f.version]));for(let[p,f]of e.versions)e.note("mapped",f.key,"Version",`version: ${p}: ${f.version}`)}if(e.edges.length){let{after:p,fromReads:f,cycle:g}=jp(e.edges.map(m=>[m.d,m.u]),t.repo?.reads);Object.keys(p).length&&!g&&(o.waves={...o.waves,after:p});for(let m of e.edges){let h=`${m.d} after ${m.u}`;m.d===m.u?e.note("unmapped",m.key,"Order",`both stacks run ${m.d}`):f.has(`${m.d}\0${m.u}`)?e.note("default",m.key,"Order",`the terraform_remote_state reads already order ${h}`):g?e.note("unmapped",m.key,"Order",`${h} is not kept: with the terraform_remote_state reads it makes a cycle, ${g.join(" after ")}`):e.note("mapped",m.key,"Order",`waves.after: ${h}`)}}let u=`${e.platform} applies the branch a ${n==="stacks"?"stack":"environment"} tracks after a push, terragucci the default branch after a merge`;if(t.applyWhen==="pull-request"&&t.forge==="gitlab"?r.own("when it applies","unmapped",u,`left at apply.when: merge: ${us.comments}; and ${us.token}`):t.applyWhen==="pull-request"?(o.apply={when:"pull-request"},r.own("when it applies","mapped",u,"apply.when: pull-request, from --apply-when")):r.own("when it applies","default",u,"apply.when: merge, terragucci's default"),e.approvals.length){let p=e.approvals.filter(f=>f.waits);if(p.length===e.approvals.length){o.gate="always";for(let f of p)e.note("mapped",f.key,"Approval","gate: always")}else if(p.length)for(let f of p)e.note("unmapped",f.key,"Approval",`gate is one setting for every wave, and ${e.approvals.length-p.length} of ${e.approvals.length} ${n} apply without an approval; gate: always holds them all`);else for(let f of e.approvals)e.note("default",f.key,"Approval","applies without an approval, as gate's default does for a change that destroys nothing")}let l=[...new Set(e.drift.map(p=>p.cron))];if(l.length===1){o.drift=l[0];let p=new Set(e.drift.map(f=>f.unit)).size>=e.units.length;for(let f of e.drift)e.note("mapped",f.key,"Drift",`drift: "${l[0]}"${p?"":`, over every root, not only ${[...new Set(e.drift.map(g=>g.unit))].join(", ")}`}`)}else if(l.length>1)for(let p of e.drift)e.note("unmapped",p.key,"Drift",`drift is one schedule, and the ${n} give ${l.join(", ")}`);if(e.ephemeral.length){let p=[...new Set(e.ephemeral.map(f=>f.root))].sort();o.ephemeral={roots:p,...e.ttl?{ttl:e.ttl.value}:{}};for(let f of e.ephemeral)e.note("mapped",f.key,"TTL",`ephemeral.roots: ${f.root}${e.ttl?`, ttl: ${e.ttl.value}`:", ttl: 24h, terragucci's default"}`);e.ttl&&e.note("mapped",e.ttl.key,"TTL",`ephemeral.ttl: ${e.ttl.value}`)}let d=[...e.env].filter(([p])=>!e.envConflicts.has(p)&&!e.secrets.has(p));for(let[p,f]of e.env)e.secrets.has(p)&&e.note("unmapped",f.key,"Variables",`${p} is a secret elsewhere, so it goes under pass`);if(d.length){o.env=Object.fromEntries(d.map(([p,f])=>[p,f.value]));for(let[p,f]of d)e.note("mapped",f.key,"Variables",`env: ${p}`)}if(e.secrets.size){o.pass={secrets:[...e.secrets.keys()].sort()};for(let[p,f]of e.secrets){let g=f.length>1?`; set in ${f.length} places, and a CI secret holds one value, so give a root's own value a name of its own`:"";e.note("mapped",f.join(", "),"Variables",`pass.secrets: ${p}; create the CI secret ${p}, whose value ${e.platform} does not give back${g}`)}}e.steps.length&&(o.steps=e.steps)}var bk=["env0-discovery.yml","env0-discovery.yaml"],Cve=new Set(["env0.yml","env0.yaml"]),Nve=new Set(["name","templateId","templateName","projectId","projectName","workspaceName","revision","variableFiles","requiresApproval","isRemoteBackend","continuousDeployment","autoDeployOnPathChangesOnly","autoDeployByCustomGlob","pullRequestPlanDeployments","vcsPrCommentsEnabled","vcsCommandsAlias","driftDetectionCron"]),Dve={setupVariables:{after:{before:"init"}},terraformInit:{before:{before:"init"},after:{after:"init"}},opentofuInit:{before:{before:"init"},after:{after:"init"}},terraformPlan:{before:{before:"plan"},after:{after:"plan"}},opentofuPlan:{before:{before:"plan"},after:{after:"plan"}},terraformApply:{before:{before:"apply"},after:{after:"apply"}},opentofuApply:{before:{before:"apply"},after:{after:"apply"}},storeState:{before:{after:"apply"},after:{after:"apply"}},terraformOutput:{before:{after:"apply"},after:{after:"apply"}},opentofuOutput:{before:{after:"apply"},after:{after:"apply"}}},Mve=/^(pulumi|cf|k8s|helm|ansible)/;function GJ(e){let t=/^([1-9]\d*)-([hdwM])$/.exec(e.trim());if(!t)return;let n=Number(t[1]),r=t[2]==="h"?`${n}h`:t[2]==="d"?`${n}d`:t[2]==="w"?`${n*7}d`:`${n*30}d`;return ad(r)?r:void 0}var wr=e=>`${e.type}.${e.name}`;function Lve(e){let t=[],n=r=>{let o;try{o=Ove(r).sort()}catch{return}for(let s of o){if(s.startsWith(".")||s==="node_modules")continue;let i=wk(r,s);try{jve(i).isDirectory()?n(i):Cve.has(s)&&t.push(Ive(e,i))}catch{continue}}};return n(e),t}function FJ(e,t){let n;try{n=uo(Pve(wk(e,t),"utf-8"))}catch(r){throw new S(`${t} is not YAML: ${r.message}`)}if(n==null)return{};if(!Me(n))throw new S(`${t} is not a map of settings`);return n}function BJ(e,t={}){let n=t.file??bk.find(a=>LJ(wk(e,a)));if(t.file!==void 0&&!LJ(wk(e,t.file)))throw new S(`import env0 reads ${t.file}, and there is none in ${e}`);let r=n!==void 0?FJ(e,n):void 0,o=Lve(e).map(a=>({file:a,doc:FJ(e,a)})),s=mk(e,"env0_");if(r===void 0&&!o.length&&!s.length)throw new S(`import env0 reads ${bk[0]}, env0.yml custom flows or env0_* resources, and there are none in ${e}`);let i=[...n?[n]:[],...o.map(a=>a.file)];for(let a of s)i.includes(a.file)||i.push(a.file);return{...r0({discovery:r,flows:o,resources:s},t),read:i}}function Gve(e,t){return e.filter(n=>n==="."||t===n||t.startsWith(`${n}/`)).sort((n,r)=>r.length-n.length)[0]}function r0(e,t={}){let n=new Jc("env zero"),{notes:r}=n,{resources:o}=e,s=g=>o.filter(m=>m.type===g),i=t.repo?.roots,a=s("env0_template"),c=new Map;for(let g of a){let m=wr(g),h=g.body.attrs.path,y=h===void 0?".":Tt(h),w=y===void 0?void 0:xo(y.replace(/^\/+/,"")||".");c.set(m,w),w===void 0&&n.note("unmapped",`${m}.path`,"Root",`${so(h)} is not a directory inside the repo`)}let u=[];for(let g of s("env0_environment")){let h={id:Tt(g.body.attrs.name)??g.name,key:wr(g),res:g,template:js(g.body.attrs.template_id),project:js(g.body.attrs.project_id)},y=g.body.blocks.find(w=>w.type==="without_template_settings");if(y){let w=y.body.attrs.path===void 0?".":Tt(y.body.attrs.path);h.root=w===void 0?void 0:xo(w.replace(/^\/+/,"")||"."),zJ(n,`${h.key}.without_template_settings`,y.body,[h])}else h.template&&c.has(h.template)&&(h.root=c.get(h.template));u.push(h)}let l=e.discovery??{};for(let g of Object.keys(l))g!=="environments"&&Xt(r,g);let d=Me(l.environments)?l.environments:{};l.environments!==void 0&&!Me(l.environments)&&Xt(r,"environments");for(let[g,m]of Object.entries(d)){let h=Me(m)?m:{},y=`environments.${g}`,w=typeof h.name=="string"?h.name:g,b=u.find(v=>v.id===w),k=b??{id:w,key:y};k.conf=h,k.confKey=y,b||u.push(k);for(let v of Object.keys(h))Nve.has(v)||Xt(r,`${y}.${v}`);if(k.root===void 0&&typeof h.templateName=="string"){let v=a.find($=>Tt($.body.attrs.name)===h.templateName);v&&(k.template=wr(v),k.root=c.get(wr(v)))}if(k.root===void 0&&i){let v=oo(h.variableFiles).find(_=>Me(_)&&typeof _.path=="string"),$=v?Gve(i,n0(v.path.replace(/^\.?\//,""))):void 0;$&&(k.root=$,n.note("mapped",`${y}.variableFiles`,"Root",`${$}, the root ${v.path} is in`))}if(k.root===void 0){let v=h.templateName??h.templateId;n.note("unmapped",y,"Root",`its template ${v===void 0?"":`${JSON.stringify(v)} `}is not an env0_template in the repo and its variable files sit in no root, so the import cannot tell its directory`)}}n.units.push(...u);for(let g of a){let m=u.filter(h=>h.template===wr(g));zJ(n,wr(g),g.body,m),m.length||r.own(wr(g),"default","no environment the import read deploys this template, so it is not a root of its own")}let p=new Map;for(let g of s("env0_project_policy")){let m=js(g.body.attrs.project_id);m&&p.set(m,g);let h=wr(g);for(let[y,w]of Object.entries(g.body.attrs)){let b=`${h}.${y}`;if(y!=="project_id")if(y==="default_ttl"){let k=typeof w=="string"?GJ(w):void 0;k&&(!n.ttl||n.ttl.value===k)?n.ttl={value:k,key:b}:k?n.note("unmapped",b,"TTL",`ephemeral.ttl is one setting, and ${n.ttl.key} gives ${n.ttl.value}`):n.note("default",b,"TTL",`${so(w)} is no fixed TTL, so nothing expires`)}else if(y==="max_ttl")n.note("default",b,"TTL","a copy lives until its pull request closes or its TTL passes");else if(y==="drift_detection_cron"){let k=Tt(w);k&&n.drift.push({cron:k,key:b,unit:m??h})}else y==="include_cost_estimation"?w===!0?(n.s.cost=!0,n.note("mapped",b,"Cost","cost: true")):n.note("default",b,"Cost","off, as terragucci's default"):y==="requires_approval_default"||y==="continuous_deployment_default"||y==="run_pull_request_plan_default"||y==="vcs_pr_comments_enabled_default"?Qo(r,b,"env zero","a default for new environments; each environment's own setting is read"):y==="auto_drift_remediation"?w!=="DISABLED"&&n.note("unmapped",b,"Drift","a drift run never applies; respond.drift opens a pull request for it"):y==="force_remote_backend"?w===!0&&n.note("unmapped",b,"Managed state","the project's environments keep their state in env zero"):Qo(r,b,"env zero")}}for(let g of u){let m=g.conf,h=g.res,y=(N,L)=>{if(m&&m[N]!==void 0)return{v:m[N],key:`${g.confKey}.${N}`};if(h&&h.body.attrs[L]!==void 0)return{v:h.body.attrs[L],key:`${g.key}.${L}`}},w=y("workspaceName","workspace");w&&n.note("unmapped",w.key,"Workspace",`workspace ${so(w.v)} is not planned`);let b=y("revision","revision");b&&Qo(r,b.key,"env zero","terragucci applies the default branch, and apply.branches applies roots from another");let k=y("requiresApproval","approve_plan_automatically"),v=k?k.key.endsWith("requiresApproval")?k.v===!0:k.v===!1:void 0;k&&v!==void 0&&n.approvals.push({key:k.key,waits:v});let $=y("isRemoteBackend","is_remote_backend"),_=g.project?p.get(g.project):void 0;$?.v===!0||!$&&_?.body.attrs.force_remote_backend===!0?n.note("unmapped",$?.key??`${g.key}: ${g.id}`,"Managed state",`${g.id}'s state is in env zero's remote backend: move it to ${g.root??"its root"}'s backend before the root's backend block merges`):$&&n.note("default",$.key,"Own backend");let E=y("continuousDeployment","deploy_on_push");E?.v===!1?n.note("unmapped",E.key,"Triggers","merge applies what changed; gate: always holds each wave for an approval first"):E&&n.note("default",E.key,"Triggers");for(let[N,L]of[["autoDeployOnPathChangesOnly","auto_deploy_on_path_changes_only"],["autoDeployByCustomGlob","auto_deploy_by_custom_glob"],["pullRequestPlanDeployments","run_plan_on_pull_requests"],["vcsPrCommentsEnabled","vcs_pr_comments_enabled"]]){let U=y(N,L);U&&n.note("default",U.key,"Triggers",N==="vcsPrCommentsEnabled"?"a comment of /terragucci plan plans again":void 0)}let R=y("vcsCommandsAlias","vcs_commands_alias");R&&n.note("unmapped",R.key,"Triggers","comments name a root by its path: /terragucci plan <root>");let x=y("driftDetectionCron","drift_detection_cron");x&&typeof x.v=="string"&&n.drift.push({cron:x.v,key:x.key,unit:g.id});let A=y("projectName","project_id")??y("projectId","project_id");if(A&&Qo(r,A.key.replace(/\.[^.]+$/,".project"),"env zero","a project's environments are this repo's roots"),m){for(let N of["name","templateName","templateId"])m[N]!==void 0&&n.note("default",`environments.*.${N}`,"Root","a root goes by its path from the repo root");oo(m.variableFiles).forEach((N,L)=>{let U=`${g.confKey}.variableFiles[${L}]`,F=Me(N)&&typeof N.path=="string"?N.path.replace(/^\.?\//,""):void 0;if(F===void 0)return Xt(r,U);let M=F.split("/").pop(),te=M==="terraform.tfvars"||M.endsWith(".auto.tfvars");g.root!==void 0&&n0(F)===g.root&&te?n.note("default",U,"Variables",`the binary reads ${F} in ${g.root} itself`):n.note("unmapped",U,"Variables",`copy ${F} to ${g.root??"the root"}/terraform.tfvars (or a *.auto.tfvars there), which the binary reads`)})}let O=h?.body.attrs.ttl,I=_&&typeof _.body.attrs.default_ttl=="string"?GJ(_.body.attrs.default_ttl):void 0;if(O!==void 0||I){let N=O!==void 0?`${g.key}.ttl`:`${g.key}: ${g.id}`;g.root===void 0?n.note("unmapped",N,"TTL",`${g.id} has no root`):n.ephemeral.push({root:g.root,key:N})}if(h){let N=new Set(["name","template_id","project_id","workspace","revision","approve_plan_automatically","is_remote_backend","deploy_on_push","auto_deploy_on_path_changes_only","auto_deploy_by_custom_glob","run_plan_on_pull_requests","vcs_pr_comments_enabled","vcs_commands_alias","drift_detection_cron","ttl"]);for(let L of Object.keys(h.body.attrs))N.has(L)||(L==="variable_sets"?n.note("unmapped",`${g.key}.variable_sets`,"Variables","set the variable sets' values by hand: env for values, pass for secrets"):L==="terragrunt_working_directory"?n.note("default",`${g.key}.${L}`,"Root","Terragrunt is detected, and each unit is a root"):L==="k8s_namespace"||L==="sub_environment_configuration"?n.note("unmapped",`${g.key}.${L}`,"Root","terragucci runs Terraform, OpenTofu and Terragrunt"):Qo(r,`env0_environment.*.${L}`,"env zero"));for(let L of h.body.blocks)if(L.type==="configuration")UJ(n,`${g.key}.configuration: ${Tt(L.body.attrs.name)??"?"}`,L.body,!1,g);else{if(L.type==="without_template_settings")continue;L.type==="sub_environment_configuration"?n.note("unmapped",`${g.key}.sub_environment_configuration`,"Order","a workflow's sub environments are roots, ordered by their terraform_remote_state reads"):Qo(r,`env0_environment.*.${L.type}`,"env zero")}}}for(let g of s("env0_configuration_variable")){let m=wr(g),h=js(g.body.attrs.environment_id),y=js(g.body.attrs.template_id),w=h?u.find(v=>v.res&&wr(v.res)===h):void 0,b=y?u.filter(v=>v.template===y):void 0,k=!h&&(!y||b!==void 0&&b.length===u.length);UJ(n,m,g.body,k,w??(b?.length===1?b[0]:void 0))}for(let g of s("env0_environment_drift_detection")){let m=js(g.body.attrs.environment_id),h=Tt(g.body.attrs.cron);h?n.drift.push({cron:h,key:`${wr(g)}.cron`,unit:u.find(w=>w.res&&wr(w.res)===m)?.id??wr(g)}):n.note("unmapped",`${wr(g)}.cron`,"Drift","it is not a plain cron schedule");let y=g.body.attrs.auto_drift_remediation;y!==void 0&&y!=="DISABLED"&&n.note("unmapped",`${wr(g)}.auto_drift_remediation`,"Drift","a drift run never applies; respond.drift opens a pull request for it")}for(let g of e.flows)Fve(n,g.file,g.doc,u);let f=new Set(["env0_template","env0_environment","env0_project_policy","env0_configuration_variable","env0_environment_drift_detection"]);for(let g of o){if(f.has(g.type))continue;let m=wr(g);g.type==="env0_project"||g.type==="env0_template_project_assignment"?Qo(r,m,"env zero","a project's environments are this repo's roots"):g.type.includes("credentials")?n.note("unmapped",m,g.type.startsWith("env0_cost_")?"Cost":"Cloud credentials",g.type.startsWith("env0_cost_")?"Infracost prices on your INFRACOST_API_KEY":"name a read-only plan role and an apply role under oidc"):g.type.startsWith("env0_variable_set")?n.note("unmapped",m,"Variables","set its values by hand: env for values, pass for secrets"):g.type.startsWith("env0_agent")?n.note("unmapped",m,"Workers"):g.type.startsWith("env0_notification")?n.note("unmapped",m,"Notifications"):g.type.startsWith("env0_approval_policy")?n.note("unmapped",m,"Plan policy","copy its Rego into policy; a rule that asked for an approval becomes cost.approve_above or gate: always"):/^env0_(user|team|custom_role|.*role_assignment|organization)/.test(g.type)?n.note("unmapped",m,"Access"):g.type==="env0_module"||g.type.startsWith("env0_module_")?n.note("unmapped",m,"Module registry"):g.type.includes("trigger")?n.note("unmapped",m,"Order","a root that reads another's state through terraform_remote_state applies after it"):Xt(r,m)}return yk(n,t,"environments"),{settings:n.s,notes:r.list}}function zJ(e,t,n,r){let o=Tt(n.attrs.type)??"terraform",s=!1;if(o==="opentofu")s=!0,e.binary("tofu",`${t}.type`);else if(o==="terraform")n.attrs.type!==void 0&&e.binary("terraform",`${t}.type`);else if(o==="terragrunt")e.note("default",`${t}.type`,"Root","Terragrunt is detected from the repo's root.hcl or terragrunt.hcl, with terragrunt.version for its release"),s=(Tt(n.attrs.terragrunt_tf_binary)??"opentofu")==="opentofu",e.binary(s?"tofu":"terraform",`${t}.terragrunt_tf_binary`);else{e.note("unmapped",`${t}.type`,"Root",`terragucci runs Terraform, OpenTofu and Terragrunt, not ${o}`);return}let i=s?n.attrs.opentofu_version:n.attrs.terraform_version,a=`${t}.${s?"opentofu_version":"terraform_version"}`;if(i!==void 0){let u=[...new Set(r.map(l=>l.root).filter(l=>l!==void 0))];u.length||e.version(void 0,i,a);for(let l of u)e.version(l,i,a)}(s?n.attrs.terraform_version:n.attrs.opentofu_version)!==void 0&&e.note("default",`${t}.${s?"terraform_version":"opentofu_version"}`,"Version",`the template runs ${s?"OpenTofu":"Terraform"}`)}function UJ(e,t,n,r,o){let s=Tt(n.attrs.name);if(!s)return e.note("unmapped",t,"Variables","its name is not a plain string");let i=Tt(n.attrs.type)==="terraform"?`TF_VAR_${s}`:s;if(n.attrs.is_sensitive===!0)return e.takeSecret(t,i);let a=Tt(n.attrs.value);if(a===void 0)return e.note("unmapped",t,"Variables","its value is not a plain string in the code; set it by hand");if(r)return e.takeEnv(t,i,a);let c=o?.root;e.note("unmapped",t,"Variables",i.startsWith("TF_VAR_")?`it is ${o?.id??"one environment"}'s alone: set ${s} in ${c??"its root"}/terraform.tfvars`:`it is ${o?.id??"one environment"}'s alone, and env is every root's`)}function Fve(e,t,n,r){let{notes:o}=e,s=xo(n0(t))??".",i=[...new Set(r.map(p=>p.root).filter(p=>p!==void 0))],a=r.length>0&&r.every(p=>p.root===s),c=s==="."&&!i.includes(".");if(!c&&!i.includes(s)&&r.length){o.own(t,"unmapped",`no environment the import read runs ${s}`,"a custom flow runs in its template's directory");return}let u=n.shell==="bash";for(let p of Object.keys(n))p==="version"?o.own(`${t}: version`,"default","the file's format version; terragucci.yml has none"):p==="shell"?o.own(`${t}: shell`,u?"mapped":"default",u?"each step runs its commands under bash":"a step runs under sh"):p==="destroy"?e.note("unmapped",`${t}: destroy`,"Hooks","a destroy is a reviewed change that removes the code, or an ephemeral copy's sweep"):p==="task"?e.note("unmapped",`${t}: task`,"Hooks","a task has no counterpart: state changes come from a reviewed commit"):p!=="deploy"&&Xt(o,`${t}: ${p}`);let l=Me(n.deploy)?n.deploy:{},d=(p,f,g)=>{let m=[];if(oo(g).forEach((y,w)=>{let b=typeof y=="string"?y:Me(y)&&typeof y.run=="string"?y.run.trimEnd():void 0;if(b===void 0)return Xt(o,`${p}[${w}]`);let k=/^echo\s+["']?([A-Za-z_][A-Za-z0-9_]*)=([^"'$`]*)["']?\s*>>\s*"?\$\{?ENV0_ENV\}?"?\s*$/.exec(b.trim());if(k){a||c?e.takeEnv(`${p}[${w}]`,k[1],k[2]):e.note("unmapped",`${p}[${w}]`,"Variables",`it is ${s}'s alone, and env is every root's`);return}b.includes("ENV0_ENV")&&e.note("unmapped",`${p}[${w}]`,"Hooks","it sets a variable for later steps, and a step cannot set the binary's environment"),m.push(b)}),!m.length)return;let h=Jc.script(m);u&&(h=`bash -c '${h.replace(/'/g,"'\\''")}'`),c&&(h=`cd "$TG_REPO" && ${h}`),e.steps.push({name:p,run:h,...f,...c||a?{}:{roots:[s]}}),e.note("mapped",p,"Hooks",`steps: ${f.before?"before":"after"}: ${f.before??f.after}${c||a?" for every root":` for ${s}`}${c?", from the repo's root":""}`)};for(let[p,f]of Object.entries(l)){let g=`${t}: deploy.${p}`;if(p==="onSuccess")d(g,{after:"apply"},f);else if(p==="onFailure"||p==="onCompletion")e.note("unmapped",g,"Hooks","a step after a stage runs when the stage succeeded");else if(p==="steps"&&Me(f))for(let[m,h]of Object.entries(f)){let y=`${t}: deploy.steps.${m}`,w=Dve[m];if(!w){Mve.test(m)?e.note("unmapped",y,"Root","terragucci runs Terraform, OpenTofu and Terragrunt"):Xt(o,y);continue}if(!Me(h)){Xt(o,y);continue}for(let[b,k]of Object.entries(h)){let v=b==="before"?w.before:b==="after"?w.after:void 0;v?d(`${y}.${b}`,v,k):Xt(o,`${y}.${b}`)}}else Xt(o,g)}}import{existsSync as WJ,readFileSync as zve}from"node:fs";import{join as o0}from"node:path";var $k=[".spacelift/config.yml",".spacelift/config.yaml"],vk={before_init:{before:"init"},after_init:{after:"init"},before_plan:{before:"plan"},after_plan:{after:"plan"},before_apply:{before:"apply"},after_apply:{after:"apply"},before_perform:void 0,after_perform:void 0,before_destroy:void 0,after_destroy:void 0,after_run:void 0},HJ=new Set([...Object.keys(vk),"environment","project_root","runner_image","terraform_version","opentofu_version","terraform_workflow_tool","git_sparse_checkout_paths","terragrunt"]),qJ=new Set(["name","slug","repository","branch","description","space_id","protect_from_deletion","enable_local_preview","autoretry","allow_run_promotion","github_action_deploy","enable_well_known_secret_masking","terraform_smart_sanitization","terraform_external_state_access","prevent_changes_when_locked","git_sparse_checkout_paths","enable_sensitive_outputs_upload","import_state","import_state_file","labels","github_enterprise","gitlab","azure_devops","bitbucket_cloud","bitbucket_datacenter","raw_git","origin","spacelift_repo","showcase"]);function Uve(e){return e.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"")}function JJ(e,t){return t in e.body.attrs||e.body.blocks.some(n=>n.type===t)}var Zn=e=>`${e.type}.${e.name}`;function KJ(e,t={}){let n=t.file??$k.find(i=>WJ(o0(e,i))),r={},o=[];if(n!==void 0){if(!WJ(o0(e,n)))throw new S(`import spacelift reads ${n}, and there is none in ${e}`);let i;try{i=uo(zve(o0(e,n),"utf-8"))}catch(a){throw new S(`${n} is not YAML: ${a.message}`)}if(i!=null&&!Me(i))throw new S(`${n} is not a map of settings`);r=i??{},o.push(n)}let s=mk(e,"spacelift_");for(let i of s)o.includes(i.file)||o.push(i.file);if(n===void 0&&!s.length)throw new S(`import spacelift reads ${$k[0]} or spacelift_stack resources, and there are neither in ${e}`);return{...s0(r,s,t),read:o}}function s0(e,t,n={}){let r=new Jc("Spacelift"),{notes:o}=r,s=new Map(t.map(w=>[Zn(w),w])),i=w=>t.filter(b=>b.type===w);for(let w of Object.keys(e))w==="version"?o.own("version","default","the file's format version; terragucci.yml has none"):w==="module_version"||w==="tests"?r.note("unmapped",w,"Module registry","a module's version and tests are the module's own release"):w!=="stack_defaults"&&w!=="stacks"&&Xt(o,w);let a=Me(e.stack_defaults)?e.stack_defaults:{};e.stack_defaults!==void 0&&!Me(e.stack_defaults)&&Xt(o,"stack_defaults");let c=Me(e.stacks)?e.stacks:{};e.stacks!==void 0&&!Me(e.stacks)&&Xt(o,"stacks");let u=[],l=new Map;for(let w of i("spacelift_stack")){let b=Tt(w.body.attrs.name),k=Tt(w.body.attrs.slug)??(b?Uve(b):w.name),v={id:k,key:Zn(w),res:w,labels:Ip(w.body.attrs.labels)??[]};u.push(v),l.set(k,v)}for(let[w,b]of Object.entries(c)){let k=l.get(w)??{id:w,key:`stacks.${w}`,labels:[]};l.has(w)||(u.push(k),l.set(w,k)),k.conf=Me(b)?b:{},k.confKey=`stacks.${w}`,!Me(b)&&b!==null&&Xt(o,`stacks.${w}`);for(let v of Object.keys(k.conf))HJ.has(v)||Xt(o,`stacks.${w}.${v}`)}for(let w of Object.keys(a))HJ.has(w)||Xt(o,`stack_defaults.${w}`);!u.length&&Me(e.stack_defaults)&&u.push({id:"the repo's stack",key:"stack_defaults",labels:[]});let d=(w,b)=>{if(w.conf&&w.conf[b]!==void 0)return{v:w.conf[b],key:`${w.confKey}.${b}`};if(a[b]!==void 0)return{v:a[b],key:`stack_defaults.${b}`};let k=w.res?.body.attrs[b];if(k!==void 0)return{v:k,key:`${w.key}.${b}`}};for(let w of u){let b=d(w,"project_root"),k=b===void 0?".":typeof b.v=="string"?Tt(b.v):void 0;w.root=k===void 0?void 0:xo(k===""?".":k),b&&w.root===void 0&&r.note("unmapped",b.key,"Root",`${so(b.v)} is not a directory inside the repo`),r.units.push(w)}for(let w of u){let b=d(w,"terraform_workflow_tool"),k=w.res&&w.res.body.blocks.some(E=>E.type==="opentofu"),v=!1;b?b.v==="OPEN_TOFU"?(v=!0,r.binary("tofu",b.key)):b.v==="TERRAFORM_FOSS"?r.binary("terraform",b.key):r.note("unmapped",b.key,"Version",`${so(b.v)} is not Terraform or OpenTofu; set binary to the one the stack runs`):k&&(v=!0,r.binary("tofu",`${w.key}.opentofu`));let $=v?d(w,"opentofu_version")??d(w,"terraform_version"):d(w,"terraform_version");$&&r.version(w.root,$.v,$.key);let _=v?d(w,"terraform_version"):d(w,"opentofu_version");_&&_!==$&&r.note("default",_.key,"Version",`the stack runs ${v?"OpenTofu":"Terraform"}`)}let p=i("spacelift_context"),f=new Map(p.map(w=>[Zn(w),new Set])),g=w=>{let b=js(w);if(b)return u.find(v=>v.res&&Zn(v.res)===b);let k=Tt(w);return k?l.get(k):void 0};for(let w of i("spacelift_context_attachment")){let b=js(w.body.attrs.context_id),k=g(w.body.attrs.stack_id);b&&f.has(b)&&k?(f.get(b).add(k),Qo(o,Zn(w),"Spacelift",`attaches ${b} to ${k.id}`)):r.note("unmapped",Zn(w),"Variables","the import cannot tell which context and stack it joins")}for(let w of p){for(let b of Ip(w.body.attrs.labels)??[]){let k=/^autoattach:(.+)$/.exec(b);if(k)for(let v of u)(k[1]==="*"||v.labels.includes(k[1]))&&f.get(Zn(w)).add(v)}for(let b of Object.keys(w.body.attrs))!(b in vk)&&b!=="name"&&b!=="labels"&&b!=="description"&&b!=="space_id"&&Xt(o,`${Zn(w)}.${b}`)}let m=w=>u.every(b=>w.has(b)),h=new Map;for(let[w,b]of Object.entries(vk))for(let k of u){let v=d(k,w),$=v?Array.isArray(v.v)?Ip(v.v):void 0:[];if(v&&$===void 0){r.note("unmapped",v.key,"Hooks","it is not a list of commands");continue}let _=p.filter(I=>f.get(Zn(I)).has(k)&&I.body.attrs[w]!==void 0),E=_.flatMap(I=>Ip(I.body.attrs[w])??[]),R=w.startsWith("before")?[...E,...$]:[...$,...E],x=[...v?[v.key]:[],..._.map(I=>`${Zn(I)}.${w}`)];if(!R.length)continue;if(!b){for(let I of x)r.note("unmapped",I,"Hooks",w.includes("destroy")?"a destroy is a reviewed change that removes the code":w.includes("perform")?"a task has no counterpart: state changes come from a reviewed commit":"after_run runs whatever the run's outcome; a step after a stage runs when the stage succeeded");continue}if(k.root===void 0){for(let I of x)r.note("unmapped",I,"Hooks",`${k.id} has no root`);continue}let A=Jc.script(R),O=h.get(`${w}\0${A}`)??{stage:b,hook:w,script:A,roots:new Set,keys:new Set,stacks:new Set};O.roots.add(k.root),O.stacks.add(k),x.forEach(I=>O.keys.add(I)),h.set(`${w}\0${A}`,O)}for(let w of h.values()){let b=m(w.stacks),k=[...w.roots].sort();r.steps.push({name:w.hook,run:w.script,...w.stage,...b?{}:{roots:k}});let v=`${w.stage.before?"before":"after"}: ${w.stage.before??w.stage.after}`;for(let $ of w.keys)r.note("mapped",$,"Hooks",`steps: ${v}${b?" for every root":` for ${k.join(", ")}`}`)}if(Me(a.environment))for(let[w,b]of Object.entries(a.environment))typeof b=="string"||typeof b=="number"||typeof b=="boolean"?r.takeEnv(`stack_defaults.environment.${w}`,w,String(b)):r.note("unmapped",`stack_defaults.environment.${w}`,"Variables","its value is not a string");for(let w of u)if(!(!w.conf||!Me(w.conf.environment)))for(let b of Object.keys(w.conf.environment))r.note("unmapped",`${w.confKey}.environment.${b}`,"Variables",`it is ${w.id}'s alone, and env is every root's; a TF_VAR_ value goes in ${w.root??"its root"}'s terraform.tfvars`);for(let w of i("spacelift_environment_variable")){let b=Zn(w),k=Tt(w.body.attrs.name);if(!k){r.note("unmapped",b,"Variables","its name is not a plain string");continue}let v=js(w.body.attrs.context_id),$=w.body.attrs.stack_id!==void 0?g(w.body.attrs.stack_id):void 0;if(w.body.attrs.module_id!==void 0){r.note("unmapped",b,"Module registry","a module's test variable");continue}if(w.body.attrs.write_only!==!1){r.takeSecret(b,k);continue}let _=Tt(w.body.attrs.value),E=v&&f.has(v)?f.get(v):$?new Set([$]):void 0;_===void 0?r.note("unmapped",b,"Variables","its value is not a plain string in the code; set it by hand"):E&&m(E)?r.takeEnv(b,k,_):r.note("unmapped",b,"Variables",`it is for ${E?[...E].map(R=>R.id).join(", ")||"no stack":"a stack the import cannot tell"}, and env is every root's; a TF_VAR_ value goes in the root's terraform.tfvars`)}for(let w of i("spacelift_mounted_file")){let b=Tt(w.body.attrs.relative_path)??so(w.body.attrs.relative_path);r.note("unmapped",Zn(w),"Files",w.body.attrs.write_only===!1?`commit ${b} in the root`:`${b} is secret: keep it in a CI secret and write it in a step before init`)}for(let w of i("spacelift_stack_dependency")){let b=Zn(w),k=g(w.body.attrs.stack_id),v=g(w.body.attrs.depends_on_stack_id);!k||!v?r.note("unmapped",b,"Order",`${k?so(w.body.attrs.depends_on_stack_id):so(w.body.attrs.stack_id)} names no stack the import read`):k.root===void 0||v.root===void 0?r.note("unmapped",b,"Order",`${k.root===void 0?k.id:v.id} has no root`):r.edges.push({d:k.root,u:v.root,key:b}),w.body.attrs.trigger_always!==void 0&&r.note("default",`${b}.trigger_always`,"Triggers","a root that reads a changed root's state plans with it")}for(let w of i("spacelift_stack_dependency_reference")){let b=Tt(w.body.attrs.input_name)??so(w.body.attrs.input_name),k=Tt(w.body.attrs.output_name)??so(w.body.attrs.output_name);r.note("unmapped",Zn(w),"Order",`read ${k} through a terraform_remote_state block of the upstream root in place of the input ${b}`)}for(let w of i("spacelift_drift_detection")){let b=Zn(w),k=g(w.body.attrs.stack_id),v=Ip(w.body.attrs.schedule);if(!v?.length)r.note("unmapped",`${b}.schedule`,"Drift","it is not a list of cron schedules");else for(let _ of v)r.drift.push({cron:_,key:v.length>1?`${b}.schedule: ${_}`:`${b}.schedule`,unit:k?.id??b});w.body.attrs.reconcile===!0&&r.note("unmapped",`${b}.reconcile`,"Drift","a drift run never applies; respond.drift opens a pull request for it");let $=Tt(w.body.attrs.timezone);$&&$!=="UTC"&&r.note("unmapped",`${b}.timezone`,"Drift",`the cron runs in UTC, not ${$}`)}for(let w of u){let b=w.res;if(!b)continue;let k=w.key;r.approvals.push({key:`${k}.autodeploy`,waits:b.body.attrs.autodeploy!==!0}),b.body.attrs.manage_state!==!1?r.note("unmapped",`${k}.manage_state`,"Managed state",`${w.id}'s state is in Spacelift: pull it and move it to ${w.root??"its root"}'s backend before the root's backend block merges`):r.note("default",`${k}.manage_state`,"Own backend"),b.body.attrs.terraform_workspace!==void 0&&r.note("unmapped",`${k}.terraform_workspace`,"Workspace",`workspace ${so(b.body.attrs.terraform_workspace)} is not planned`),b.body.attrs.worker_pool_id!==void 0&&r.note("unmapped",`${k}.worker_pool_id`,"Workers","name the runner label of your own runners under runner"),b.body.attrs.additional_project_globs!==void 0&&r.note("default",`${k}.additional_project_globs`,"Triggers","a root plans when a file in it, a local module it calls or its var files changed"),b.body.attrs.administrative!==void 0&&o.own(`${k}.administrative`,"unmapped","an administrative stack manages Spacelift itself","once every stack it declares has moved, stop applying it");for(let v of["pulumi","kubernetes","cloudformation","ansible"])JJ(b,v)&&r.note("unmapped",`${k}.${v}`,"Root",`terragucci runs Terraform, OpenTofu and Terragrunt, not ${v}`);JJ(b,"terragrunt")&&r.note("default",`${k}.terragrunt`,"Root","Terragrunt is detected from the repo's root.hcl or terragrunt.hcl");for(let v of Object.keys(b.body.attrs))qJ.has(v)?Qo(o,`spacelift_stack.*.${v}`,"Spacelift"):!["project_root","terraform_version","terraform_workflow_tool","autodeploy","manage_state","terraform_workspace","worker_pool_id","additional_project_globs","administrative","runner_image"].includes(v)&&!(v in vk)&&Xt(o,`${k}.${v}`);for(let v of b.body.blocks)!qJ.has(v.type)&&!["opentofu","terragrunt","pulumi","kubernetes","cloudformation","ansible"].includes(v.type)&&Xt(o,`${k}.${v.type}`)}for(let w of u){let b=d(w,"runner_image");b&&r.note("unmapped",b.key,"Runner image","build an image FROM terragucci's with what the hooks need, and set image")}(Me(a.terragrunt)||u.some(w=>w.conf&&w.conf.terragrunt!==void 0))&&r.note("default","terragrunt","Root","Terragrunt is detected from the repo's root.hcl or terragrunt.hcl, with terragrunt.version for its release");for(let w of i("spacelift_policy")){let b=Zn(w),k=Tt(w.body.attrs.type)??"",v=w.body.attrs.body,$=v&&typeof v=="object"&&"file"in v?` in ${v.file.replace(/^\$\{path\.module\}\//,"")}`:"";k==="PLAN"?r.note("unmapped",b,"Plan policy",`copy the Rego${$} into policy with input: plan, and change input.terraform to input; input.spacelift has no counterpart`):k==="APPROVAL"?r.note("unmapped",b,"Approval","gate decides which waves wait, and how many reviewers a change needs is the forge's branch protection"):k==="GIT_PUSH"||k==="TRIGGER"?r.note("default",b,"Triggers"):k==="LOGIN"||k==="ACCESS"||k==="STACK_ACCESS"?r.note("unmapped",b,"Access"):k==="NOTIFICATION"?r.note("unmapped",b,"Notifications","set notify for the channels it routed to"):o.own(b,"unmapped",`a ${k||"policy"} policy has no counterpart`,"a state change comes from an import, removed or moved block, or a migration, in a reviewed commit")}let y=new Set(["spacelift_stack","spacelift_context","spacelift_context_attachment","spacelift_environment_variable","spacelift_mounted_file","spacelift_stack_dependency","spacelift_stack_dependency_reference","spacelift_drift_detection","spacelift_policy"]);for(let w of t){if(y.has(w.type))continue;let b=Zn(w);w.type==="spacelift_policy_attachment"?Qo(o,b,"Spacelift","the policy's own note says where it goes"):/_(integration|integration_attachment|aws_role)$/.test(w.type)||w.type.startsWith("spacelift_aws_")||w.type.startsWith("spacelift_gcp_")||w.type.startsWith("spacelift_azure_")?r.note("unmapped",b,"Cloud credentials","name a read-only plan role and an apply role under oidc"):w.type==="spacelift_worker_pool"?r.note("unmapped",b,"Workers"):w.type==="spacelift_module"||w.type.startsWith("spacelift_module_")?r.note("unmapped",b,"Module registry"):/^spacelift_(space|role|role_attachment|user|idp_group_mapping|api_key|team|saml_saml2|audit_trail_webhook)/.test(w.type)?r.note("unmapped",b,"Access"):w.type==="spacelift_webhook"||w.type==="spacelift_named_webhook"?r.note("unmapped",b,"Notifications","notify.webhook posts a signed body to an address"):Xt(o,b)}return yk(r,n,"stacks"),{settings:r.s,notes:o.list}}import{existsSync as Sh,readdirSync as Bve,readFileSync as i0,statSync as VJ}from"node:fs";import{join as Kc,relative as Wve,resolve as Hve}from"node:path";var qve=/[A-Za-z0-9_-]/;function a0(e){let t=0,n=p=>{let f=e.slice(0,t).split(`
|
|
942
942
|
`).length;throw new Error(`line ${f}: ${p}`)},r=p=>{for(;t<e.length;){let f=e[t];if(f===" "||f===" "||f==="\r"||p&&f===`
|
|
943
943
|
`)t++;else if(f==="#"||f==="/"&&e[t+1]==="/")for(;t<e.length&&e[t]!==`
|
|
944
944
|
`;)t++;else if(f==="/"&&e[t+1]==="*"){let g=e.indexOf("*/",t+2);t=g<0?e.length:g+2}else break}},o=()=>{let p=t;for(;t<e.length&&qve.test(e[t]);)t++;return t===p&&n(`expected a name, found ${JSON.stringify(e[t]??"the end")}`),e.slice(p,t)},s=()=>{let p=t,f=0;for(let g=!1;t<e.length;t++){let m=e[t];if(g)m==="\\"?t++:m==='"'&&(g=!1);else if(m==='"')g=!0;else if("([{".includes(m))f++;else if(")]}".includes(m)&&--f===0){t++;break}}return e.slice(p,t)},i=()=>{t++;let p=[],f="";for(;t<e.length&&e[t]!=='"';)if(e[t]==="\\"){let g=e[t+1];f+=g==="n"?`
|
|
945
945
|
`:g==="t"?" ":g,t+=2}else if(e[t]==="$"&&e[t+1]==="{"){f&&p.push(f),f="",t++;let g=s();p.push(l(g.slice(1,-1)))}else f+=e[t++];return e[t]!=='"'&&n("a string has no closing quote"),t++,(f||!p.length)&&p.push(f),{t:"str",parts:p}},a=()=>{r(!1);let p=e[t];if(p==='"')return i();if(p==="["){t++;let f=[];for(;;){if(r(!0),e[t]==="]")return t++,{t:"list",items:f};f.push(u()),r(!0),e[t]===","?t++:e[t]!=="]"&&n("expected , or ] in a list")}}if(p==="{"){t++;let f=[];for(;;){if(r(!0),e[t]==="}")return t++,{t:"obj",entries:f};let g=e[t]==='"'?Cp(i())??n("a map key is not a plain string"):o();r(!1),e[t]!=="="&&e[t]!==":"&&n("expected = in a map"),t++,f.push([g,u()]),r(!1),e[t]===","&&t++}}if(p!==void 0&&/[0-9]/.test(p)){let f=/^[0-9]+(\.[0-9]+)?/.exec(e.slice(t));return t+=f[0].length,{t:"lit",v:Number(f[0])}}if(p!==void 0&&/[A-Za-z_]/.test(p)){let f=t,g=o();if(g==="true"||g==="false")return{t:"lit",v:g==="true"};if(g==="null")return{t:"lit",v:null};if(e[t]==="(")return s(),{t:"raw",text:e.slice(f,t)};let m=[g];for(;;)if(e[t]==="."&&/[A-Za-z_]/.test(e[t+1]??""))t++,m.push(o());else if(e[t]==="["&&e[t+1]==='"'){t++;let h=Cp(i());if(h===void 0||e[t]!=="]")return{t:"raw",text:e.slice(f,t=c(f))};t++,m.push(h)}else break;return{t:"ref",path:m}}return n(`expected a value, found ${JSON.stringify(p??"the end")}`)},c=p=>{let f=p,g=0;for(let m=!1;f<e.length;f++){let h=e[f];if(m)h==="\\"?f++:h==='"'&&(m=!1);else if(h==='"')m=!0;else if("([{".includes(h))g++;else if(")]}".includes(h)){if(g===0)break;g--}else if((h===`
|
|
946
946
|
`||h===",")&&g===0)break}return f},u=()=>{r(!1);let p=t,f=a();r(!1);let g=e[t];return g===void 0||g===`
|
|
947
947
|
`||g===","||g==="]"||g==="}"||g===")"?f:(t=c(p),{t:"raw",text:e.slice(p,t).trim()})};function l(p){try{return a0(`x = ${p}
|
|
948
|
-
`).attrs.get("x")??{t:"raw",text:p}}catch{return{t:"raw",text:p}}}let d=p=>{let f={type:"",labels:[],attrs:new Map,blocks:[]};for(;;){if(r(!0),t>=e.length)return p||n("a block has no closing }"),f;if(e[t]==="}")return p&&n("a } closes no block"),t++,f;let g=o();if(r(!1),e[t]==="="){t++,f.attrs.set(g,u());continue}let m=[];for(;e[t]!=="{";)e[t]==='"'?m.push(Cp(i())??n("a block label is not a plain string")):m.push(o()),r(!1);t++;let h=d(!1);f.blocks.push({...h,type:g,labels:m})}};return d(!0)}function Cp(e){return e.t==="str"&&e.parts.every(t=>typeof t=="string")?e.parts.join(""):void 0}function Np(e){switch(e.t){case"str":return`"${e.parts.map(t=>typeof t=="string"?t:`\${${Np(t)}}`).join("")}"`;case"lit":return String(e.v);case"list":return`[${e.items.map(Np).join(", ")}]`;case"obj":return`{ ${e.entries.map(([t,n])=>`${t} = ${Np(n)}`).join(", ")} }`;case"ref":return e.path.join(".");case"raw":return e.text}}var Z2=".gruntwork";function Jve(e){let t=e.trim().split("/").filter(o=>o!==""&&o!==".");if(e.trim().startsWith("/")||t.includes(".."))return;if(!t.length)return["**"];let n=t[t.length-1],r=t.join("/");return n==="**"?[r]:n==="*"?[`${r}*`]:[r,`${r}/**`]}var Kve=new Set(["environment","aws","repository","annotation"]),Vve=new Set(["account_id","plan_iam_role_arn","apply_iam_role_arn","region","session_duration"]),Zve={opentofu:"tofu",terraform:"terraform"};function Yve(e){let t=new Ei(Iv),n=e.dir??Z2,r=[];for(let p of e.global)try{r.push({file:p.path,body:a0(p.text)})}catch(f){throw new S(`${p.path} is not HCL this import reads: ${f.message}`)}let o=new Map;for(let{file:p,body:f}of r)for(let g of f.blocks.filter(m=>m.type==="aws"))for(let m of g.blocks){let h=`${p}: aws.accounts.${m.labels[0]??""}`;if(m.type!=="accounts"||m.labels.length!==1){t.unknown(`${p}: aws.${m.type}`);continue}let y=m.attrs.get("path"),w=y&&Cp(y);if(w===void 0){t.scale("unmapped",`${h}.path`,"Accounts","its path is not a plain string, so the accounts it names are not read");continue}let k=[Kc(".",w),Kc(n,w)].map($=>$.split("\\").join("/")).map($=>({x:$,text:$.startsWith("..")?void 0:e.read($)})).find($=>$.text!==void 0);if(!k){t.scale("unmapped",`${h}.path`,"Accounts",`${w} is not in the repo, so the roles that name its accounts are not read`);continue}let v;try{v=uo(k.text)}catch($){throw new S(`${k.x} is not YAML: ${$.message}`)}if(!Me(v))throw new S(`${k.x} is not a map of accounts`);o.set(m.labels[0],v),t.scale("default",h,"Accounts",`read ${k.x}`)}let s=p=>{if(p){if(p.t==="str"){let f=p.parts.map(g=>typeof g=="string"?g:s(g));return f.every(g=>g!==void 0)?f.join(""):void 0}if(p.t==="ref"&&p.path.length===5&&p.path[0]==="aws"&&p.path[1]==="accounts"){let f=o.get(p.path[2])?.[p.path[3]],g=Me(f)?f[p.path[4]]:void 0;return typeof g=="string"||typeof g=="number"?String(g):void 0}}},i=(p,f)=>{if(!p){t.scale("unmapped",f,"Roles","it has no authentication block");return}for(let m of p.attrs.keys())t.unknown(`${f}.authentication.${m}`);if(!p.blocks.length)return t.scale("default",`${f}.authentication`,"No authentication"),"none";let g;for(let m of p.blocks){let h=`${f}.authentication.${m.type}`;if(m.type==="azure_oidc"||m.type==="gcp_oidc"||m.type==="custom"){t.scale("unmapped",h,"Other clouds");continue}if(m.type!=="aws_oidc"){t.unknown(h);continue}for(let k of m.attrs.keys())Vve.has(k)||t.unknown(`${h}.${k}`);let y=s(m.attrs.get("plan_iam_role_arn")),w=s(m.attrs.get("apply_iam_role_arn")),b=["plan_iam_role_arn","apply_iam_role_arn"].filter(k=>s(m.attrs.get(k))===void 0);if(b.length){let k=b.map(v=>`${v} = ${m.attrs.has(v)?Np(m.attrs.get(v)):"(unset)"}`).join("; ");t.scale("unmapped",h,"Roles",`${k}: only Pipelines can evaluate it, so set this pair in terragrunt.credentials by hand`);continue}if(y===w){t.scale("unmapped",h,"Roles",`plan and apply are both ${y}; plan runs pull-request code, so give it a read-only role of its own and add the pair by hand`);continue}g={plan:y,apply:w}}return g},a=[],c=(p,f,g)=>{let m=[];for(let h of p){let y=a.find(([w])=>w===h);if(y&&(y[1].plan!==f.plan||y[1].apply!==f.apply)){t.scale("unmapped",`${g}: ${h}`,"Roles",`${h} already takes ${y[1].plan} and ${y[1].apply}`);continue}y||a.push([h,f]),m.push(h)}m.length&&t.scale("mapped",g,"Roles",`terragrunt.credentials ${m.map(h=>`"${h}"`).join(", ")}: plan ${f.plan}, apply ${f.apply}`)};for(let{unit:p,file:f}of[...e.units].sort((g,m)=>g.unit.localeCompare(m.unit))){let g;try{g=a0(f.text)}catch(m){throw new S(`${f.path} is not HCL this import reads: ${m.message}`)}for(let m of g.attrs.keys())t.unknown(`${f.path}: ${m}`);for(let m of g.blocks){if(m.type!=="unit"){t.unknown(`${f.path}: ${m.type}`);continue}let h=`${f.path}: unit`,y=m.blocks.find(b=>b.type==="authentication");for(let b of m.blocks)b.type!=="authentication"&&t.unknown(`${h}.${b.type}`);if(y&&!y.blocks.length&&!y.attrs.size){t.scale("unmapped",`${h}.authentication`,"No authentication",`${p} still takes the roles of the environment it is in, if any`);continue}let w=i(y,h);w&&w!=="none"&&c([p],w,`${h}.authentication.aws_oidc`)}}let u;for(let{file:p,body:f}of r){for(let g of f.attrs.keys())t.unknown(`${p}: ${g}`);for(let g of f.blocks){if(!Kve.has(g.type)){t.unknown(`${p}: ${g.type}`);continue}if(g.type==="environment"){let m=`${p}: environment.${g.labels[0]??""}`,y=g.blocks.find(k=>k.type==="filter")?.attrs.get("paths"),w=[];if(y?.t==="list")for(let k of y.items){let v=Cp(k),$=v===void 0?void 0:Jve(v);$===void 0?t.scale("unmapped",`${m}.filter.paths`,"Roles",`${Np(k)} is not a path inside the repo`):w.push(...$)}else t.scale("unmapped",`${m}.filter`,"Roles","it has no filter.paths list");let b=i(g.blocks.find(k=>k.type==="authentication"),m);b&&b!=="none"&&w.length&&c(w,b,`${m}.authentication.aws_oidc`)}else if(g.type==="repository"){for(let[m,h]of g.attrs){let y=`${p}: repository.${m}`,w=m==="tf_binary"?Zve[Cp(h)??""]:void 0;w?(u=w,t.scale("mapped",y,"Binary",`binary: ${w}`)):m==="tf_binary"?t.scale("unmapped",y,"Binary",`${Np(h)} is not opentofu or terraform`):t.scale("unmapped",y,"Other settings")}for(let m of g.blocks)t.scale("unmapped",`${p}: repository.${m.type}${m.labels.length?`.${m.labels[0]}`:""}`,"Other settings")}else g.type==="annotation"&&t.scale("unmapped",`${p}: annotation.${g.labels[0]??""}`,"Other settings")}}t.own("when it applies","default","Pipelines plans on the pull request and applies after merge","apply.when: merge, terragucci's default");let l={};u&&(l.binary=u),a.length&&(l.terragrunt={credentials:Object.fromEntries(a)}),Ni(l,"terragucci.yml");let d=e.unitPaths?a.map(([p])=>p).filter(p=>!e.unitPaths.some(f=>Pe(p,f))):[];return{settings:l,notes:t.list,missing:d}}function Y2(e,t=Z2){let n=Hve(e,t);if(!Sh(n)||!V2(n).isDirectory())throw new S(`import terragrunt-scale reads the ${t} directory, and there is none in ${e}`);let r=Wve(e,n).split("\\").join("/")||".",o=Bve(n).filter(c=>c.endsWith(".hcl")).sort();if(!o.length){let c=Sh(Kc(n,"config.yml"))||Sh(Kc(n,"config.yaml"));throw new S(c?`${r} holds the legacy config.yml, which names no roles; Pipelines' HCL configuration does (environment blocks), or set terragrunt.credentials by hand`:`${r} holds no .hcl files`)}if(!vd(e))throw new S(`${e} has no root.hcl, terragrunt.hcl or terragrunt.stack.hcl; import terragrunt-scale writes roles by unit, for a Terragrunt repo`);let s=Dn(e).map(c=>c.path),i=c=>{let u=Kc(e,c);return Sh(u)&&V2(u).isFile()?i0(u,"utf-8"):void 0};return{...Yve({dir:r,global:o.map(c=>({path:`${r}/${c}`,text:i0(Kc(n,c),"utf-8")})),units:s.filter(c=>Sh(Kc(e,c,"gruntwork.hcl"))).map(c=>({unit:c,file:{path:`${c}/gruntwork.hcl`,text:i0(Kc(e,c,"gruntwork.hcl"),"utf-8")}})),read:i,unitPaths:s}),from:r}}var p0=["atlantis","digger","terragrunt-scale","spacelift","env0"],c0={atlantis:["atlantis.yaml","atlantis.yml"],digger:["digger.yml","digger.yaml"],"terragrunt-scale":[".gruntwork"],spacelift:$k,env0:bk},e$e={atlantis:"Atlantis",digger:"OpenTaco","terragrunt-scale":"Terragrunt Scale",spacelift:"Spacelift",env0:"env zero"},tK={atlantis:_m,digger:_m,"terragrunt-scale":Iv,spacelift:hk,env0:hk},l0={approved:{row:"Required approval",to:["approved"]},mergeable:{row:"Checks green",to:["mergeable","checks"]},undiverged:{row:"Up to date",to:["undiverged"]}};function t$e(e){let[t,n]=Az;return`${e==="atlantis"?"Atlantis":"OpenTaco"} applies ${bo(t[e==="atlantis"?1:2])} by default, terragucci ${bo(t[3])}; the other way is ${bo(n[3])}`}function n$e(){return{roots:[],afterMerge:!1,requirements:new Map,projectsWithRequirements:0,projects:0,versions:new Map,tofu:[],env:new Map,envConflicts:new Set,named:new Map,dependsOn:[],terragrunt:!1}}function nK(e,t,n,r,o){if(typeof r!="string"||!/^[A-Za-z_][A-Za-z0-9_]*$/.test(r)||typeof o!="string"){t.unmapped(n,"Custom steps","it sets no fixed value, so it stays out of `env`");return}let s=e.env.get(r);if(s&&s.value!==o){e.envConflicts.add(r),t.unmapped(n,"Custom steps",`${r} is set to two values (in ${s.key} too), and \`env\` holds one`);return}s||e.env.set(r,{value:o,key:n})}function rK(e,t,n,r){oo(r).forEach((o,s)=>{let i=`${n}[${s}]`;if(typeof o=="string"){o==="policy_check"?t.unmapped(i,"Policy","set `policy:` and keep the Rego in the repo"):(o==="import"||o==="state_rm")&&t.leftOut(i,"`import` or `state rm` from a comment");return}if(!Me(o))return t.unknown(i);for(let[a,c]of Object.entries(o)){let u=`${i}.${a}`;a==="env"?Me(c)?nK(e,t,u,c.name,c.value):t.unmapped(u,"Custom steps"):a==="run"||a==="multienv"?t.unmapped(u,"Custom steps"):Me(c)&&c.extra_args!==void 0?t.leftOut(`${u}.extra_args`,"Flags at run time"):a==="policy_check"?t.unmapped(u,"Policy","set `policy:` and keep the Rego in the repo"):a==="import"||a==="state_rm"?t.leftOut(u,"`import` or `state rm` from a comment"):["init","plan","apply","show"].includes(a)||t.unknown(u)}})}function oK(e,t,n){typeof t.name=="string"&&n!==void 0&&!e.named.has(t.name)&&e.named.set(t.name,n)}function r$e(e,t,n,r){if(!e.dependsOn.length)return;if(e.terragrunt){for(let u of e.dependsOn)t.unmapped(u.at,"Order","in a Terragrunt repo the units' dependency blocks order them");return}let o=[];for(let u of e.dependsOn){let l=oo(u.names),d=l.filter(p=>typeof p!="string"||!e.named.has(p));d.length&&t.unmapped(`${u.at}: ${d.map(String).join(", ")}`,"Order",`${d.map(p=>JSON.stringify(p)).join(", ")} ${d.length===1?"names":"name"} no project`);for(let p of l)typeof p=="string"&&e.named.has(p)&&e.named.get(p)!==u.root&&o.push({at:u.at,d:u.root,u:e.named.get(p)})}let{after:s,fromReads:i,cycle:a}=jp(o.map(u=>[u.d,u.u]),r);Object.keys(s).length&&(n.waves={...n.waves,after:s});let c=new Map;for(let u of o)c.set(u.at,[...c.get(u.at)??[],u]);for(let[u,l]of c){let d=f=>f.map(g=>`${g.d} after ${g.u}`).join(", "),p=l.filter(f=>!i.has(`${f.d}\0${f.u}`));a&&p.length?t.unmapped(u,"Order",`${d(p)} ${p.length===1?"is":"are"} not kept: with the terraform_remote_state reads they make a cycle, ${a.join(" after ")}`):p.length?t.mapped(u,"Order",`waves.after: ${d(p)}`):t.covered(u,"Order",`the terraform_remote_state reads already order ${d(l)}`)}}function sK(e,t,n,r){let o=oo(r);o.length&&e.projectsWithRequirements++;for(let s of o)typeof s=="string"&&l0[s]?e.requirements.set(s,[...e.requirements.get(s)??[],n]):t.unknown(`${n}: ${String(s)}`)}var o$e=new Set(["name","dir","workspace","terraform_version","autoplan","apply_requirements","plan_requirements","import_requirements","execution_order_group","depends_on","workflow","repo_locks","custom_policy_check"]);function s$e(e,t,n){for(let o of Object.keys(e))["version","projects","workflows","automerge","parallel_plan","parallel_apply","autodiscover","abort_on_execution_order_fail","repo_locks"].includes(o)||n.unknown(o);e.autodiscover!==void 0&&n.covered("autodiscover","Which directories"),e.abort_on_execution_order_fail!==void 0&&n.unmapped("abort_on_execution_order_fail","Order"),e.automerge===!0&&(t.autoMerge="automerge"),e.repo_locks!==void 0&&d0(t,n,"repo_locks",e.repo_locks);let r=oo(e.projects);t.projects=r.length,r.forEach((o,s)=>{if(!Me(o))return n.unknown(`projects[${s}]`);let i=xo(o.dir??"."),a=`projects[${typeof o.name=="string"?o.name:i??s}]`;for(let c of Object.keys(o))o$e.has(c)||n.unknown(`${a}.${c}`);i===void 0?n.unmapped(`${a}.dir`,"Which directories",`${JSON.stringify(o.dir)} is not a directory inside the repo`):t.roots.includes(i)||t.roots.push(i),o.name!==void 0&&n.covered("projects[].name","Project name"),o.workspace!==void 0&&o.workspace!=="default"&&n.unmapped(`${a}.workspace`,"Workspace",`workspace ${String(o.workspace)} is not planned`),typeof o.terraform_version=="string"&&t.versions.set(o.terraform_version.replace(/^v/,""),[...t.versions.get(o.terraform_version.replace(/^v/,""))??[],a]),Me(o.autoplan)&&(o.autoplan.when_modified!==void 0&&n.covered("projects[].autoplan.when_modified","What triggers a plan"),o.autoplan.enabled===!1&&n.unmapped(`${a}.autoplan.enabled`,"What triggers a plan","no key turns the plan off for a root")),sK(t,n,`${a}.apply_requirements`,o.apply_requirements),o.plan_requirements!==void 0&&n.unmapped(`${a}.plan_requirements`,"Plan requirements"),o.import_requirements!==void 0&&n.leftOut(`${a}.import_requirements`,"`import` or `state rm` from a comment"),o.execution_order_group!==void 0&&n.unmapped(`${a}.execution_order_group`,"Order"),oK(t,o,i),o.depends_on!==void 0&&i!==void 0&&t.dependsOn.push({at:`${a}.depends_on`,root:i,names:o.depends_on}),o.repo_locks!==void 0&&d0(t,n,`${a}.repo_locks`,o.repo_locks),o.custom_policy_check!==void 0&&n.unmapped(`${a}.custom_policy_check`,"Policy")});for(let o of["parallel_plan","parallel_apply"])e[o]===!1?n.mapped(o,"Concurrency","parallelism: 1"):e[o]!==void 0&&n.covered(o,"Concurrency");if(Me(e.workflows)){for(let[o,s]of Object.entries(e.workflows))if(Me(s)){n.unmapped(`workflows.${o}`,"Custom steps");for(let[i,a]of Object.entries(s)){let c=`workflows.${o}.${i}`;i==="import"||i==="state_rm"?n.leftOut(c,"`import` or `state rm` from a comment"):i==="policy_check"?n.unmapped(c,"Policy","set `policy:` and keep the Rego in the repo"):i==="plan"||i==="apply"?rK(t,n,`${c}.steps`,Me(a)?a.steps:void 0):n.unknown(c)}}}}function d0(e,t,n,r){r===!0||Me(r)&&r.mode==="on_plan"?e.planLocks=n:t.covered(n,"Lock at plan time")}var i$e=new Set(["name","dir","workspace","terragrunt","opentofu","workflow","include_patterns","exclude_patterns","depends_on","aws_role_to_assume","apply_requirements"]);function a$e(e,t,n){for(let o of Object.keys(e))["projects","generate_projects","workflows","auto_merge","pr_locks"].includes(o)||n.unknown(o);e.auto_merge===!0&&(t.autoMerge="auto_merge"),e.pr_locks!==void 0&&d0(t,n,"pr_locks",e.pr_locks);let r=oo(e.projects);if(t.projects=r.length,r.forEach((o,s)=>{if(!Me(o))return n.unknown(`projects[${s}]`);let i=xo(o.dir??"."),a=`projects[${typeof o.name=="string"?o.name:i??s}]`;for(let c of Object.keys(o))i$e.has(c)||n.unknown(`${a}.${c}`);i===void 0?n.unmapped(`${a}.dir`,"Which directories",`${JSON.stringify(o.dir)} is not a directory inside the repo`):t.roots.includes(i)||t.roots.push(i),o.name!==void 0&&n.covered("projects[].name","Project name"),o.workspace!==void 0&&o.workspace!=="default"&&n.unmapped(`${a}.workspace`,"Workspace",`workspace ${String(o.workspace)} is not planned`),o.opentofu===!0&&t.tofu.push(a),o.terragrunt!==void 0&&n.covered("projects[].terragrunt","Terragrunt"),o.include_patterns!==void 0&&n.covered("projects[].include_patterns","What triggers a plan"),o.exclude_patterns!==void 0&&n.covered("projects[].exclude_patterns","What triggers a plan"),o.terragrunt===!0&&(t.terragrunt=!0),oK(t,o,i),o.depends_on!==void 0&&i!==void 0&&t.dependsOn.push({at:`${a}.depends_on`,root:i,names:o.depends_on}),o.aws_role_to_assume!==void 0&&n.unmapped(`${a}.aws_role_to_assume`,"Cloud credentials","name a read-only plan role and an apply role under `oidc`"),sK(t,n,`${a}.apply_requirements`,o.apply_requirements)}),Me(e.generate_projects)){let o=e.generate_projects,s=o.blocks!==void 0?oo(o.blocks).map((i,a)=>({b:i,at:`generate_projects.blocks[${a}]`})):[{b:o,at:"generate_projects"}];for(let{b:i,at:a}of s)if(Me(i)){for(let c of oo(i.include)){let u=xo(c);u===void 0?n.unmapped(`${a}.include`,"Which directories",`${JSON.stringify(c)} is not a glob inside the repo`):t.roots.includes(u)||(t.roots.push(u),n.mapped(`${a}.include`,"Which directories",`roots glob ${u}`))}i.exclude!==void 0&&n.unmapped(`${a}.exclude`,"Which directories","roots lists what to plan; narrow the globs instead"),(i.terragrunt_parsing!==void 0||i.terragrunt===!0)&&n.covered(`${a}.terragrunt_parsing`,"Terragrunt");for(let c of Object.keys(i))["include","exclude","terragrunt_parsing","terragrunt","blocks"].includes(c)||n.unknown(`${a}.${c}`)}}if(Me(e.workflows)){for(let[o,s]of Object.entries(e.workflows))if(Me(s))for(let[i,a]of Object.entries(s)){let c=`workflows.${o}.${i}`;if(i==="plan"||i==="apply")n.unmapped(c,"Custom steps"),rK(t,n,`${c}.steps`,Me(a)?a.steps:void 0);else if(i==="env_vars"&&Me(a))for(let[u,l]of Object.entries(a))oo(l).forEach((d,p)=>{let f=`${c}.${u}[${p}]`;Me(d)?nK(t,n,f,d.name,d.value):n.unknown(f)});else if(i==="workflow_configuration"&&Me(a))for(let[u,l]of Object.entries(a)){let d=`${c}.${u}`;u==="on_pull_request_pushed"?n.covered(d,"What triggers a plan"):u==="on_pull_request_closed"?n.covered(d,"Release locks on close"):u==="on_commit_to_default"?(f=>oo(l).some(g=>typeof g=="string"&&g.trim()===f))("digger apply")?(t.afterMerge=!0,n.covered(d,"Apply on merge")):n.covered(d,"Release locks on close"):n.unknown(d)}else n.unknown(c)}}}function c$e(e,t,n={}){if(!Me(t))throw new S(`the ${e$e[e]} config is not a map of settings`);if(e==="spacelift")return s0(t,[],n);if(e==="env0")return r0({discovery:t,flows:[],resources:[]},n);let r=new Ei,o=n$e();e==="atlantis"?s$e(t,o,r):a$e(t,o,r);let s={};o.roots.length&&(s.roots=[...o.roots].sort(),r.mapped("projects[].dir","Which directories",`roots: ${s.roots.join(", ")}`));let i=[...o.versions.keys()];i.length===1?(s.binary="terraform",s.version=i[0],r.mapped("projects[].terraform_version","Binary version",`binary: terraform, version: ${i[0]}`)):i.length>1&&r.unmapped("projects[].terraform_version","Binary version",`the projects pin ${i.join(", ")}, and the pipeline runs one; each root's required_version pin is read instead`),o.tofu.length&&(o.tofu.length===o.projects?(s.binary="tofu",r.mapped("projects[].opentofu","Binary version","binary: tofu")):r.unmapped("projects[].opentofu","Binary version",`only ${o.tofu.join(", ")} run OpenTofu, and the pipeline runs one binary`)),r$e(o,r,s,n.repo?.reads),e==="atlantis"&&(t.parallel_plan===!1||t.parallel_apply===!1)&&(s.parallelism=1);let a=n.applyWhen!==void 0,c=n.applyWhen??(o.afterMerge?"merge":"pull-request"),u=t$e(e);c==="pull-request"&&n.forge==="gitlab"?(c="merge",r.own("when it applies","unmapped",u,`left at apply.when: merge: ${us.comments}; and ${us.token}`)):c==="pull-request"?(s.apply={when:c},r.own("when it applies","mapped",u,a?"apply.when: pull-request, from --apply-when":"apply.when: pull-request, to keep applying before merge; --apply-when merge applies after merge instead")):r.own("when it applies","default",u,a?"apply.when: merge, from --apply-when":"apply.when: merge, terragucci's default");let l=Cn.filter(p=>[...o.requirements.keys()].some(f=>l0[f].to.includes(p)));for(let[p,f]of o.requirements){let{row:g,to:m}=l0[p],h=f.length===1?f[0]:`projects[].apply_requirements: ${p}`;c==="pull-request"?r.mapped(h,g,`apply.requires: ${m.join(", ")}`):r.unmapped(h,g,"applying after merge, a branch protection rule on the default branch does this job")}if(c==="pull-request"&&s.apply&&(l.length?(s.apply.requires=l,o.projectsWithRequirements<o.projects&&r.own("projects[].apply_requirements","mapped",`apply.requires covers every root: ${l.join(", ")}`,`${o.projectsWithRequirements} of ${o.projects} projects set apply_requirements, and terragucci keeps one list`)):r.own("projects[].apply_requirements","default",`no project sets apply_requirements, so apply.requires is left unset and every requirement applies: ${Cn.join(", ")}`)),o.autoMerge){let p=o.autoMerge;c!=="pull-request"||!s.apply?r.unmapped(p,"Merge after apply","apply.when is merge here, so a person merges"):n.forge==="forgejo"?r.unmapped(p,"Merge after apply","on Forgejo apply.merge: auto also needs apply.merge_token_env, the secret holding a token that may merge, which this command cannot name; add both"):s.apply.requires&&!s.apply.requires.includes("approved")?r.unmapped(p,"Merge after apply","apply.merge: auto merges only an approved head, and apply.requires leaves out approved"):(s.apply.merge="auto",r.mapped(p,"Merge after apply","apply.merge: auto"))}o.planLocks&&(n.forge==="gitlab"?r.unmapped(o.planLocks,"Lock at plan time",Df):(s.locks="plan",r.mapped(o.planLocks,"Lock at plan time","locks: plan")));let d=[...o.env].filter(([p])=>!o.envConflicts.has(p));if(d.length){s.env=Object.fromEntries(d.map(([p,f])=>[p,f.value]));for(let[p,f]of d)r.mapped(f.key,"Custom steps",`env: ${p}`)}return Ni(s,"terragucci.yml"),{settings:s,notes:r.list}}function u$e(e){let t=wt(e),n=wn(e,t);try{return{roots:t,layers:tc(e,t),reads:n}}catch{return{roots:t,layers:[t],reads:new Map}}}function iK(e,t,n={}){if(t==="terragrunt-scale"){if(n.applyWhen)throw new S("import terragrunt-scale takes no --apply-when: Pipelines applies after merge, terragucci's default");let p=Y2(e,n.file??c0[t][0]);return u0(e,t,p.from,p.settings,p.notes,p.missing,n)}let r=n.forge??Kr(e)?.value,o={...r?{forge:r}:{},...n.applyWhen?{applyWhen:n.applyWhen}:{},repo:u$e(e)};if(t==="spacelift"||t==="env0"){let p=n.file?Dp(e,Q2(e,n.file)):void 0,f=(t==="spacelift"?K2:B2)(e,{...o,...p!==void 0?{file:p}:{}}),g=f.missing??(f.settings.roots??[]).filter(m=>wt(e,[m]).length===0);return u0(e,t,f.read.join(", "),f.settings,f.notes,g,n)}let s=n.file?Q2(e,n.file):c0[t].map(p=>eK(e,p)).find(p=>X2(p));if(!s||!X2(s))throw new S(`import ${t} reads ${n.file??c0[t].join(" or ")}, and there is none in ${e}`);let i;try{i=uo(Xve(s,"utf-8"))}catch(p){throw new S(`${Dp(e,s)} is not YAML: ${p.message}`)}let{settings:a,notes:c,missing:u}=c$e(t,i,o),l=Dp(e,s)||s,d=u??(a.roots??[]).filter(p=>wt(e,[p]).length===0);return u0(e,t,l,a,c,d,n)}function u0(e,t,n,r,o,s,i){let a=`# Written by terragucci import ${t} from ${n}. What became of each setting: ${tK[t]}
|
|
948
|
+
`).attrs.get("x")??{t:"raw",text:p}}catch{return{t:"raw",text:p}}}let d=p=>{let f={type:"",labels:[],attrs:new Map,blocks:[]};for(;;){if(r(!0),t>=e.length)return p||n("a block has no closing }"),f;if(e[t]==="}")return p&&n("a } closes no block"),t++,f;let g=o();if(r(!1),e[t]==="="){t++,f.attrs.set(g,u());continue}let m=[];for(;e[t]!=="{";)e[t]==='"'?m.push(Cp(i())??n("a block label is not a plain string")):m.push(o()),r(!1);t++;let h=d(!1);f.blocks.push({...h,type:g,labels:m})}};return d(!0)}function Cp(e){return e.t==="str"&&e.parts.every(t=>typeof t=="string")?e.parts.join(""):void 0}function Np(e){switch(e.t){case"str":return`"${e.parts.map(t=>typeof t=="string"?t:`\${${Np(t)}}`).join("")}"`;case"lit":return String(e.v);case"list":return`[${e.items.map(Np).join(", ")}]`;case"obj":return`{ ${e.entries.map(([t,n])=>`${t} = ${Np(n)}`).join(", ")} }`;case"ref":return e.path.join(".");case"raw":return e.text}}var ZJ=".gruntwork";function Jve(e){let t=e.trim().split("/").filter(o=>o!==""&&o!==".");if(e.trim().startsWith("/")||t.includes(".."))return;if(!t.length)return["**"];let n=t[t.length-1],r=t.join("/");return n==="**"?[r]:n==="*"?[`${r}*`]:[r,`${r}/**`]}var Kve=new Set(["environment","aws","repository","annotation"]),Vve=new Set(["account_id","plan_iam_role_arn","apply_iam_role_arn","region","session_duration"]),Zve={opentofu:"tofu",terraform:"terraform"};function Yve(e){let t=new Ei(Iv),n=e.dir??ZJ,r=[];for(let p of e.global)try{r.push({file:p.path,body:a0(p.text)})}catch(f){throw new S(`${p.path} is not HCL this import reads: ${f.message}`)}let o=new Map;for(let{file:p,body:f}of r)for(let g of f.blocks.filter(m=>m.type==="aws"))for(let m of g.blocks){let h=`${p}: aws.accounts.${m.labels[0]??""}`;if(m.type!=="accounts"||m.labels.length!==1){t.unknown(`${p}: aws.${m.type}`);continue}let y=m.attrs.get("path"),w=y&&Cp(y);if(w===void 0){t.scale("unmapped",`${h}.path`,"Accounts","its path is not a plain string, so the accounts it names are not read");continue}let k=[Kc(".",w),Kc(n,w)].map($=>$.split("\\").join("/")).map($=>({x:$,text:$.startsWith("..")?void 0:e.read($)})).find($=>$.text!==void 0);if(!k){t.scale("unmapped",`${h}.path`,"Accounts",`${w} is not in the repo, so the roles that name its accounts are not read`);continue}let v;try{v=uo(k.text)}catch($){throw new S(`${k.x} is not YAML: ${$.message}`)}if(!Me(v))throw new S(`${k.x} is not a map of accounts`);o.set(m.labels[0],v),t.scale("default",h,"Accounts",`read ${k.x}`)}let s=p=>{if(p){if(p.t==="str"){let f=p.parts.map(g=>typeof g=="string"?g:s(g));return f.every(g=>g!==void 0)?f.join(""):void 0}if(p.t==="ref"&&p.path.length===5&&p.path[0]==="aws"&&p.path[1]==="accounts"){let f=o.get(p.path[2])?.[p.path[3]],g=Me(f)?f[p.path[4]]:void 0;return typeof g=="string"||typeof g=="number"?String(g):void 0}}},i=(p,f)=>{if(!p){t.scale("unmapped",f,"Roles","it has no authentication block");return}for(let m of p.attrs.keys())t.unknown(`${f}.authentication.${m}`);if(!p.blocks.length)return t.scale("default",`${f}.authentication`,"No authentication"),"none";let g;for(let m of p.blocks){let h=`${f}.authentication.${m.type}`;if(m.type==="azure_oidc"||m.type==="gcp_oidc"||m.type==="custom"){t.scale("unmapped",h,"Other clouds");continue}if(m.type!=="aws_oidc"){t.unknown(h);continue}for(let k of m.attrs.keys())Vve.has(k)||t.unknown(`${h}.${k}`);let y=s(m.attrs.get("plan_iam_role_arn")),w=s(m.attrs.get("apply_iam_role_arn")),b=["plan_iam_role_arn","apply_iam_role_arn"].filter(k=>s(m.attrs.get(k))===void 0);if(b.length){let k=b.map(v=>`${v} = ${m.attrs.has(v)?Np(m.attrs.get(v)):"(unset)"}`).join("; ");t.scale("unmapped",h,"Roles",`${k}: only Pipelines can evaluate it, so set this pair in terragrunt.credentials by hand`);continue}if(y===w){t.scale("unmapped",h,"Roles",`plan and apply are both ${y}; plan runs pull-request code, so give it a read-only role of its own and add the pair by hand`);continue}g={plan:y,apply:w}}return g},a=[],c=(p,f,g)=>{let m=[];for(let h of p){let y=a.find(([w])=>w===h);if(y&&(y[1].plan!==f.plan||y[1].apply!==f.apply)){t.scale("unmapped",`${g}: ${h}`,"Roles",`${h} already takes ${y[1].plan} and ${y[1].apply}`);continue}y||a.push([h,f]),m.push(h)}m.length&&t.scale("mapped",g,"Roles",`terragrunt.credentials ${m.map(h=>`"${h}"`).join(", ")}: plan ${f.plan}, apply ${f.apply}`)};for(let{unit:p,file:f}of[...e.units].sort((g,m)=>g.unit.localeCompare(m.unit))){let g;try{g=a0(f.text)}catch(m){throw new S(`${f.path} is not HCL this import reads: ${m.message}`)}for(let m of g.attrs.keys())t.unknown(`${f.path}: ${m}`);for(let m of g.blocks){if(m.type!=="unit"){t.unknown(`${f.path}: ${m.type}`);continue}let h=`${f.path}: unit`,y=m.blocks.find(b=>b.type==="authentication");for(let b of m.blocks)b.type!=="authentication"&&t.unknown(`${h}.${b.type}`);if(y&&!y.blocks.length&&!y.attrs.size){t.scale("unmapped",`${h}.authentication`,"No authentication",`${p} still takes the roles of the environment it is in, if any`);continue}let w=i(y,h);w&&w!=="none"&&c([p],w,`${h}.authentication.aws_oidc`)}}let u;for(let{file:p,body:f}of r){for(let g of f.attrs.keys())t.unknown(`${p}: ${g}`);for(let g of f.blocks){if(!Kve.has(g.type)){t.unknown(`${p}: ${g.type}`);continue}if(g.type==="environment"){let m=`${p}: environment.${g.labels[0]??""}`,y=g.blocks.find(k=>k.type==="filter")?.attrs.get("paths"),w=[];if(y?.t==="list")for(let k of y.items){let v=Cp(k),$=v===void 0?void 0:Jve(v);$===void 0?t.scale("unmapped",`${m}.filter.paths`,"Roles",`${Np(k)} is not a path inside the repo`):w.push(...$)}else t.scale("unmapped",`${m}.filter`,"Roles","it has no filter.paths list");let b=i(g.blocks.find(k=>k.type==="authentication"),m);b&&b!=="none"&&w.length&&c(w,b,`${m}.authentication.aws_oidc`)}else if(g.type==="repository"){for(let[m,h]of g.attrs){let y=`${p}: repository.${m}`,w=m==="tf_binary"?Zve[Cp(h)??""]:void 0;w?(u=w,t.scale("mapped",y,"Binary",`binary: ${w}`)):m==="tf_binary"?t.scale("unmapped",y,"Binary",`${Np(h)} is not opentofu or terraform`):t.scale("unmapped",y,"Other settings")}for(let m of g.blocks)t.scale("unmapped",`${p}: repository.${m.type}${m.labels.length?`.${m.labels[0]}`:""}`,"Other settings")}else g.type==="annotation"&&t.scale("unmapped",`${p}: annotation.${g.labels[0]??""}`,"Other settings")}}t.own("when it applies","default","Pipelines plans on the pull request and applies after merge","apply.when: merge, terragucci's default");let l={};u&&(l.binary=u),a.length&&(l.terragrunt={credentials:Object.fromEntries(a)}),Ni(l,"terragucci.yml");let d=e.unitPaths?a.map(([p])=>p).filter(p=>!e.unitPaths.some(f=>Pe(p,f))):[];return{settings:l,notes:t.list,missing:d}}function YJ(e,t=ZJ){let n=Hve(e,t);if(!Sh(n)||!VJ(n).isDirectory())throw new S(`import terragrunt-scale reads the ${t} directory, and there is none in ${e}`);let r=Wve(e,n).split("\\").join("/")||".",o=Bve(n).filter(c=>c.endsWith(".hcl")).sort();if(!o.length){let c=Sh(Kc(n,"config.yml"))||Sh(Kc(n,"config.yaml"));throw new S(c?`${r} holds the legacy config.yml, which names no roles; Pipelines' HCL configuration does (environment blocks), or set terragrunt.credentials by hand`:`${r} holds no .hcl files`)}if(!vd(e))throw new S(`${e} has no root.hcl, terragrunt.hcl or terragrunt.stack.hcl; import terragrunt-scale writes roles by unit, for a Terragrunt repo`);let s=Dn(e).map(c=>c.path),i=c=>{let u=Kc(e,c);return Sh(u)&&VJ(u).isFile()?i0(u,"utf-8"):void 0};return{...Yve({dir:r,global:o.map(c=>({path:`${r}/${c}`,text:i0(Kc(n,c),"utf-8")})),units:s.filter(c=>Sh(Kc(e,c,"gruntwork.hcl"))).map(c=>({unit:c,file:{path:`${c}/gruntwork.hcl`,text:i0(Kc(e,c,"gruntwork.hcl"),"utf-8")}})),read:i,unitPaths:s}),from:r}}var p0=["atlantis","digger","terragrunt-scale","spacelift","env0"],c0={atlantis:["atlantis.yaml","atlantis.yml"],digger:["digger.yml","digger.yaml"],"terragrunt-scale":[".gruntwork"],spacelift:$k,env0:bk},e$e={atlantis:"Atlantis",digger:"OpenTaco","terragrunt-scale":"Terragrunt Scale",spacelift:"Spacelift",env0:"env zero"},tK={atlantis:_m,digger:_m,"terragrunt-scale":Iv,spacelift:hk,env0:hk},l0={approved:{row:"Required approval",to:["approved"]},mergeable:{row:"Checks green",to:["mergeable","checks"]},undiverged:{row:"Up to date",to:["undiverged"]}};function t$e(e){let[t,n]=Az;return`${e==="atlantis"?"Atlantis":"OpenTaco"} applies ${bo(t[e==="atlantis"?1:2])} by default, terragucci ${bo(t[3])}; the other way is ${bo(n[3])}`}function n$e(){return{roots:[],afterMerge:!1,requirements:new Map,projectsWithRequirements:0,projects:0,versions:new Map,tofu:[],env:new Map,envConflicts:new Set,named:new Map,dependsOn:[],terragrunt:!1}}function nK(e,t,n,r,o){if(typeof r!="string"||!/^[A-Za-z_][A-Za-z0-9_]*$/.test(r)||typeof o!="string"){t.unmapped(n,"Custom steps","it sets no fixed value, so it stays out of `env`");return}let s=e.env.get(r);if(s&&s.value!==o){e.envConflicts.add(r),t.unmapped(n,"Custom steps",`${r} is set to two values (in ${s.key} too), and \`env\` holds one`);return}s||e.env.set(r,{value:o,key:n})}function rK(e,t,n,r){oo(r).forEach((o,s)=>{let i=`${n}[${s}]`;if(typeof o=="string"){o==="policy_check"?t.unmapped(i,"Policy","set `policy:` and keep the Rego in the repo"):(o==="import"||o==="state_rm")&&t.leftOut(i,"`import` or `state rm` from a comment");return}if(!Me(o))return t.unknown(i);for(let[a,c]of Object.entries(o)){let u=`${i}.${a}`;a==="env"?Me(c)?nK(e,t,u,c.name,c.value):t.unmapped(u,"Custom steps"):a==="run"||a==="multienv"?t.unmapped(u,"Custom steps"):Me(c)&&c.extra_args!==void 0?t.leftOut(`${u}.extra_args`,"Flags at run time"):a==="policy_check"?t.unmapped(u,"Policy","set `policy:` and keep the Rego in the repo"):a==="import"||a==="state_rm"?t.leftOut(u,"`import` or `state rm` from a comment"):["init","plan","apply","show"].includes(a)||t.unknown(u)}})}function oK(e,t,n){typeof t.name=="string"&&n!==void 0&&!e.named.has(t.name)&&e.named.set(t.name,n)}function r$e(e,t,n,r){if(!e.dependsOn.length)return;if(e.terragrunt){for(let u of e.dependsOn)t.unmapped(u.at,"Order","in a Terragrunt repo the units' dependency blocks order them");return}let o=[];for(let u of e.dependsOn){let l=oo(u.names),d=l.filter(p=>typeof p!="string"||!e.named.has(p));d.length&&t.unmapped(`${u.at}: ${d.map(String).join(", ")}`,"Order",`${d.map(p=>JSON.stringify(p)).join(", ")} ${d.length===1?"names":"name"} no project`);for(let p of l)typeof p=="string"&&e.named.has(p)&&e.named.get(p)!==u.root&&o.push({at:u.at,d:u.root,u:e.named.get(p)})}let{after:s,fromReads:i,cycle:a}=jp(o.map(u=>[u.d,u.u]),r);Object.keys(s).length&&(n.waves={...n.waves,after:s});let c=new Map;for(let u of o)c.set(u.at,[...c.get(u.at)??[],u]);for(let[u,l]of c){let d=f=>f.map(g=>`${g.d} after ${g.u}`).join(", "),p=l.filter(f=>!i.has(`${f.d}\0${f.u}`));a&&p.length?t.unmapped(u,"Order",`${d(p)} ${p.length===1?"is":"are"} not kept: with the terraform_remote_state reads they make a cycle, ${a.join(" after ")}`):p.length?t.mapped(u,"Order",`waves.after: ${d(p)}`):t.covered(u,"Order",`the terraform_remote_state reads already order ${d(l)}`)}}function sK(e,t,n,r){let o=oo(r);o.length&&e.projectsWithRequirements++;for(let s of o)typeof s=="string"&&l0[s]?e.requirements.set(s,[...e.requirements.get(s)??[],n]):t.unknown(`${n}: ${String(s)}`)}var o$e=new Set(["name","dir","workspace","terraform_version","autoplan","apply_requirements","plan_requirements","import_requirements","execution_order_group","depends_on","workflow","repo_locks","custom_policy_check"]);function s$e(e,t,n){for(let o of Object.keys(e))["version","projects","workflows","automerge","parallel_plan","parallel_apply","autodiscover","abort_on_execution_order_fail","repo_locks"].includes(o)||n.unknown(o);e.autodiscover!==void 0&&n.covered("autodiscover","Which directories"),e.abort_on_execution_order_fail!==void 0&&n.unmapped("abort_on_execution_order_fail","Order"),e.automerge===!0&&(t.autoMerge="automerge"),e.repo_locks!==void 0&&d0(t,n,"repo_locks",e.repo_locks);let r=oo(e.projects);t.projects=r.length,r.forEach((o,s)=>{if(!Me(o))return n.unknown(`projects[${s}]`);let i=xo(o.dir??"."),a=`projects[${typeof o.name=="string"?o.name:i??s}]`;for(let c of Object.keys(o))o$e.has(c)||n.unknown(`${a}.${c}`);i===void 0?n.unmapped(`${a}.dir`,"Which directories",`${JSON.stringify(o.dir)} is not a directory inside the repo`):t.roots.includes(i)||t.roots.push(i),o.name!==void 0&&n.covered("projects[].name","Project name"),o.workspace!==void 0&&o.workspace!=="default"&&n.unmapped(`${a}.workspace`,"Workspace",`workspace ${String(o.workspace)} is not planned`),typeof o.terraform_version=="string"&&t.versions.set(o.terraform_version.replace(/^v/,""),[...t.versions.get(o.terraform_version.replace(/^v/,""))??[],a]),Me(o.autoplan)&&(o.autoplan.when_modified!==void 0&&n.covered("projects[].autoplan.when_modified","What triggers a plan"),o.autoplan.enabled===!1&&n.unmapped(`${a}.autoplan.enabled`,"What triggers a plan","no key turns the plan off for a root")),sK(t,n,`${a}.apply_requirements`,o.apply_requirements),o.plan_requirements!==void 0&&n.unmapped(`${a}.plan_requirements`,"Plan requirements"),o.import_requirements!==void 0&&n.leftOut(`${a}.import_requirements`,"`import` or `state rm` from a comment"),o.execution_order_group!==void 0&&n.unmapped(`${a}.execution_order_group`,"Order"),oK(t,o,i),o.depends_on!==void 0&&i!==void 0&&t.dependsOn.push({at:`${a}.depends_on`,root:i,names:o.depends_on}),o.repo_locks!==void 0&&d0(t,n,`${a}.repo_locks`,o.repo_locks),o.custom_policy_check!==void 0&&n.unmapped(`${a}.custom_policy_check`,"Policy")});for(let o of["parallel_plan","parallel_apply"])e[o]===!1?n.mapped(o,"Concurrency","parallelism: 1"):e[o]!==void 0&&n.covered(o,"Concurrency");if(Me(e.workflows)){for(let[o,s]of Object.entries(e.workflows))if(Me(s)){n.unmapped(`workflows.${o}`,"Custom steps");for(let[i,a]of Object.entries(s)){let c=`workflows.${o}.${i}`;i==="import"||i==="state_rm"?n.leftOut(c,"`import` or `state rm` from a comment"):i==="policy_check"?n.unmapped(c,"Policy","set `policy:` and keep the Rego in the repo"):i==="plan"||i==="apply"?rK(t,n,`${c}.steps`,Me(a)?a.steps:void 0):n.unknown(c)}}}}function d0(e,t,n,r){r===!0||Me(r)&&r.mode==="on_plan"?e.planLocks=n:t.covered(n,"Lock at plan time")}var i$e=new Set(["name","dir","workspace","terragrunt","opentofu","workflow","include_patterns","exclude_patterns","depends_on","aws_role_to_assume","apply_requirements"]);function a$e(e,t,n){for(let o of Object.keys(e))["projects","generate_projects","workflows","auto_merge","pr_locks"].includes(o)||n.unknown(o);e.auto_merge===!0&&(t.autoMerge="auto_merge"),e.pr_locks!==void 0&&d0(t,n,"pr_locks",e.pr_locks);let r=oo(e.projects);if(t.projects=r.length,r.forEach((o,s)=>{if(!Me(o))return n.unknown(`projects[${s}]`);let i=xo(o.dir??"."),a=`projects[${typeof o.name=="string"?o.name:i??s}]`;for(let c of Object.keys(o))i$e.has(c)||n.unknown(`${a}.${c}`);i===void 0?n.unmapped(`${a}.dir`,"Which directories",`${JSON.stringify(o.dir)} is not a directory inside the repo`):t.roots.includes(i)||t.roots.push(i),o.name!==void 0&&n.covered("projects[].name","Project name"),o.workspace!==void 0&&o.workspace!=="default"&&n.unmapped(`${a}.workspace`,"Workspace",`workspace ${String(o.workspace)} is not planned`),o.opentofu===!0&&t.tofu.push(a),o.terragrunt!==void 0&&n.covered("projects[].terragrunt","Terragrunt"),o.include_patterns!==void 0&&n.covered("projects[].include_patterns","What triggers a plan"),o.exclude_patterns!==void 0&&n.covered("projects[].exclude_patterns","What triggers a plan"),o.terragrunt===!0&&(t.terragrunt=!0),oK(t,o,i),o.depends_on!==void 0&&i!==void 0&&t.dependsOn.push({at:`${a}.depends_on`,root:i,names:o.depends_on}),o.aws_role_to_assume!==void 0&&n.unmapped(`${a}.aws_role_to_assume`,"Cloud credentials","name a read-only plan role and an apply role under `oidc`"),sK(t,n,`${a}.apply_requirements`,o.apply_requirements)}),Me(e.generate_projects)){let o=e.generate_projects,s=o.blocks!==void 0?oo(o.blocks).map((i,a)=>({b:i,at:`generate_projects.blocks[${a}]`})):[{b:o,at:"generate_projects"}];for(let{b:i,at:a}of s)if(Me(i)){for(let c of oo(i.include)){let u=xo(c);u===void 0?n.unmapped(`${a}.include`,"Which directories",`${JSON.stringify(c)} is not a glob inside the repo`):t.roots.includes(u)||(t.roots.push(u),n.mapped(`${a}.include`,"Which directories",`roots glob ${u}`))}i.exclude!==void 0&&n.unmapped(`${a}.exclude`,"Which directories","roots lists what to plan; narrow the globs instead"),(i.terragrunt_parsing!==void 0||i.terragrunt===!0)&&n.covered(`${a}.terragrunt_parsing`,"Terragrunt");for(let c of Object.keys(i))["include","exclude","terragrunt_parsing","terragrunt","blocks"].includes(c)||n.unknown(`${a}.${c}`)}}if(Me(e.workflows)){for(let[o,s]of Object.entries(e.workflows))if(Me(s))for(let[i,a]of Object.entries(s)){let c=`workflows.${o}.${i}`;if(i==="plan"||i==="apply")n.unmapped(c,"Custom steps"),rK(t,n,`${c}.steps`,Me(a)?a.steps:void 0);else if(i==="env_vars"&&Me(a))for(let[u,l]of Object.entries(a))oo(l).forEach((d,p)=>{let f=`${c}.${u}[${p}]`;Me(d)?nK(t,n,f,d.name,d.value):n.unknown(f)});else if(i==="workflow_configuration"&&Me(a))for(let[u,l]of Object.entries(a)){let d=`${c}.${u}`;u==="on_pull_request_pushed"?n.covered(d,"What triggers a plan"):u==="on_pull_request_closed"?n.covered(d,"Release locks on close"):u==="on_commit_to_default"?(f=>oo(l).some(g=>typeof g=="string"&&g.trim()===f))("digger apply")?(t.afterMerge=!0,n.covered(d,"Apply on merge")):n.covered(d,"Release locks on close"):n.unknown(d)}else n.unknown(c)}}}function c$e(e,t,n={}){if(!Me(t))throw new S(`the ${e$e[e]} config is not a map of settings`);if(e==="spacelift")return s0(t,[],n);if(e==="env0")return r0({discovery:t,flows:[],resources:[]},n);let r=new Ei,o=n$e();e==="atlantis"?s$e(t,o,r):a$e(t,o,r);let s={};o.roots.length&&(s.roots=[...o.roots].sort(),r.mapped("projects[].dir","Which directories",`roots: ${s.roots.join(", ")}`));let i=[...o.versions.keys()];i.length===1?(s.binary="terraform",s.version=i[0],r.mapped("projects[].terraform_version","Binary version",`binary: terraform, version: ${i[0]}`)):i.length>1&&r.unmapped("projects[].terraform_version","Binary version",`the projects pin ${i.join(", ")}, and the pipeline runs one; each root's required_version pin is read instead`),o.tofu.length&&(o.tofu.length===o.projects?(s.binary="tofu",r.mapped("projects[].opentofu","Binary version","binary: tofu")):r.unmapped("projects[].opentofu","Binary version",`only ${o.tofu.join(", ")} run OpenTofu, and the pipeline runs one binary`)),r$e(o,r,s,n.repo?.reads),e==="atlantis"&&(t.parallel_plan===!1||t.parallel_apply===!1)&&(s.parallelism=1);let a=n.applyWhen!==void 0,c=n.applyWhen??(o.afterMerge?"merge":"pull-request"),u=t$e(e);c==="pull-request"&&n.forge==="gitlab"?(c="merge",r.own("when it applies","unmapped",u,`left at apply.when: merge: ${us.comments}; and ${us.token}`)):c==="pull-request"?(s.apply={when:c},r.own("when it applies","mapped",u,a?"apply.when: pull-request, from --apply-when":"apply.when: pull-request, to keep applying before merge; --apply-when merge applies after merge instead")):r.own("when it applies","default",u,a?"apply.when: merge, from --apply-when":"apply.when: merge, terragucci's default");let l=Cn.filter(p=>[...o.requirements.keys()].some(f=>l0[f].to.includes(p)));for(let[p,f]of o.requirements){let{row:g,to:m}=l0[p],h=f.length===1?f[0]:`projects[].apply_requirements: ${p}`;c==="pull-request"?r.mapped(h,g,`apply.requires: ${m.join(", ")}`):r.unmapped(h,g,"applying after merge, a branch protection rule on the default branch does this job")}if(c==="pull-request"&&s.apply&&(l.length?(s.apply.requires=l,o.projectsWithRequirements<o.projects&&r.own("projects[].apply_requirements","mapped",`apply.requires covers every root: ${l.join(", ")}`,`${o.projectsWithRequirements} of ${o.projects} projects set apply_requirements, and terragucci keeps one list`)):r.own("projects[].apply_requirements","default",`no project sets apply_requirements, so apply.requires is left unset and every requirement applies: ${Cn.join(", ")}`)),o.autoMerge){let p=o.autoMerge;c!=="pull-request"||!s.apply?r.unmapped(p,"Merge after apply","apply.when is merge here, so a person merges"):n.forge==="forgejo"?r.unmapped(p,"Merge after apply","on Forgejo apply.merge: auto also needs apply.merge_token_env, the secret holding a token that may merge, which this command cannot name; add both"):s.apply.requires&&!s.apply.requires.includes("approved")?r.unmapped(p,"Merge after apply","apply.merge: auto merges only an approved head, and apply.requires leaves out approved"):(s.apply.merge="auto",r.mapped(p,"Merge after apply","apply.merge: auto"))}o.planLocks&&(n.forge==="gitlab"?r.unmapped(o.planLocks,"Lock at plan time",Df):(s.locks="plan",r.mapped(o.planLocks,"Lock at plan time","locks: plan")));let d=[...o.env].filter(([p])=>!o.envConflicts.has(p));if(d.length){s.env=Object.fromEntries(d.map(([p,f])=>[p,f.value]));for(let[p,f]of d)r.mapped(f.key,"Custom steps",`env: ${p}`)}return Ni(s,"terragucci.yml"),{settings:s,notes:r.list}}function u$e(e){let t=wt(e),n=wn(e,t);try{return{roots:t,layers:tc(e,t),reads:n}}catch{return{roots:t,layers:[t],reads:new Map}}}function iK(e,t,n={}){if(t==="terragrunt-scale"){if(n.applyWhen)throw new S("import terragrunt-scale takes no --apply-when: Pipelines applies after merge, terragucci's default");let p=YJ(e,n.file??c0[t][0]);return u0(e,t,p.from,p.settings,p.notes,p.missing,n)}let r=n.forge??Kr(e)?.value,o={...r?{forge:r}:{},...n.applyWhen?{applyWhen:n.applyWhen}:{},repo:u$e(e)};if(t==="spacelift"||t==="env0"){let p=n.file?Dp(e,QJ(e,n.file)):void 0,f=(t==="spacelift"?KJ:BJ)(e,{...o,...p!==void 0?{file:p}:{}}),g=f.missing??(f.settings.roots??[]).filter(m=>wt(e,[m]).length===0);return u0(e,t,f.read.join(", "),f.settings,f.notes,g,n)}let s=n.file?QJ(e,n.file):c0[t].map(p=>eK(e,p)).find(p=>XJ(p));if(!s||!XJ(s))throw new S(`import ${t} reads ${n.file??c0[t].join(" or ")}, and there is none in ${e}`);let i;try{i=uo(Xve(s,"utf-8"))}catch(p){throw new S(`${Dp(e,s)} is not YAML: ${p.message}`)}let{settings:a,notes:c,missing:u}=c$e(t,i,o),l=Dp(e,s)||s,d=u??(a.roots??[]).filter(p=>wt(e,[p]).length===0);return u0(e,t,l,a,c,d,n)}function u0(e,t,n,r,o,s,i){let a=`# Written by terragucci import ${t} from ${n}. What became of each setting: ${tK[t]}
|
|
949
949
|
${Object.keys(r).length?xr(r,0).trim():"{}"}
|
|
950
950
|
`,c=fe(e),u;if(!i.dryRun){if(c&&!i.force)throw new S(`${Dp(e,c)} is already here; --force replaces it, or --dry-run prints what would be written`);let l=c??eK(e,"terragucci.yml");if(c&&!/\.ya?ml$/.test(c))throw new S(`${Dp(e,c)} is not YAML; move it aside first`);Qve(l,a),u=Dp(e,l)}return{source:t,from:n,settings:r,notes:o,yaml:a,missing:s,...u?{wrote:u}:{}}}var l$e={mapped:"Written to terragucci.yml",default:"Done by terragucci with no key",unmapped:"Not mapped","left-out":"Left out on purpose"};function aK(e){let t=[];t.push(e.wrote?`read ${e.from}, wrote ${e.wrote}:`:`read ${e.from}; dry run, nothing written. terragucci.yml would be:`),t.push("",...e.yaml.trimEnd().split(`
|
|
951
951
|
`).map(n=>` ${n}`));for(let n of["mapped","default","unmapped","left-out"]){let r=e.notes.filter(o=>o.kind===n);if(r.length){t.push("",`${l$e[n]}:`);for(let o of r){let s=o.detail?`${o.detail}. `:"",i=o.row?`The guide: ${o.text}`:o.text;t.push(` ${o.key}${o.row?` (${o.row})`:""}: ${s}${i}`)}}}if(e.missing.length){let n=e.source==="terragrunt-scale"?`No unit matches ${e.missing.join(", ")}; check those filter paths.`:e.source==="spacelift"?`No directory with Terraform files matches ${e.missing.join(", ")}; check those stacks' project_root.`:e.source==="env0"?`No directory with Terraform files matches ${e.missing.join(", ")}; check those environments' templates.`:`No directory with Terraform files matches ${e.missing.join(", ")}; check those projects' dir.`;t.push("",n)}return t.push("",`Next: \`npx terragucci init\` writes the pipeline for these settings. Every mapping: ${tK[e.source]}`),t.join(`
|
|
@@ -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:M1(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]:I1(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)+`
|
|
@@ -1160,7 +1160,7 @@ ${b.notes}`).join(`
|
|
|
1160
1160
|
`)),h={text:w||"no published module",data:y}}else if(r==="version-bump"){let{suggestions:y,proposals:w}=await r4(t,a,{module:n.module,since:n.since,env:o,fetch:n.decideFetch}),b=await dk(t,a,w,{mode:u,env:o,fetch:n.fetch});h={text:[...y.map(v=>`${v.module} ${v.last}${v.version?` -> ${v.version}`:""}: ${v.note}`),...b.map(Z0)].join(`
|
|
1161
1161
|
`)||"no module",data:y,proposals:b}}else if(r==="description"){let y=Xc(t,n.report??"terragucci-report");m(Da(vr(y,"report.json")),"--report, the directory terragucci stage tf-plan wrote");let w=n.title!==void 0||n.description!==void 0?{title:n.title??"",description:n.description??""}:eRe(o)??await tRe(o,n.fetch??globalThis.fetch);if(!w||!w.title&&!w.description)return{event:r,response:c,skipped:"no pull request title or description to read",text:"no pull request title or description to read; the note is unchanged"};let b=await JV({dir:y,...w,decide:a.decide,write:u==="apply",env:o,fetch:n.decideFetch}),k="";if(b.files.length>0&&a.reports?.bucket)try{let v=JSON.parse(Zp(vr(y,"report.json"),"utf-8")),$=await qG(dr(a.reports,o,n.s3Fetch),y,v,b.files,a.reports.prefix);k=`
|
|
1162
1162
|
copied ${b.files.join(", ")} to the bucket under ${$[0].slice(0,$[0].lastIndexOf("/"))}`}catch(v){k=`
|
|
1163
|
-
the bucket's copy was not updated: ${v.message}`}h={text:b.text+k,data:b.record}}else{let y=await
|
|
1163
|
+
the bucket's copy was not updated: ${v.message}`}h={text:b.text+k,data:b.record}}else{let y=await yJ(t,{mode:u,config:n.config,env:o,...n.fetch?{fetch:n.fetch}:{}});h={text:wJ(y),data:y,...YP(y)?{exit:YP(y)}:{}}}return{event:r,response:c,...h}}var Dk="terragucci_imports.tf",Vp="terragucci_generated.tf";function nRe(e,t){if(t&&typeof t!="string")return t;let n=Xc(e,t??vr("terragucci-report",vm));if(!Da(n)){if(t)throw new S(`--attributions ${t}: no such file`);return}return JSON.parse(Zp(n,"utf-8"))}async function rRe(e,t,n,r,o,s){if(t.length===0)throw new S("no root matches");if(r.length&&t.length!==1)throw new S(`--import needs --root to name one root; ${t.length} match`);let i={codified:[],left:[],files:new Map,imports:[],attributions:[],routed:[],notes:[]};for(let a of t){let c=vr(e,a),u=(...p)=>{let f=Y0(n,[`-chdir=${c}`,p[0],"-no-color",...p.slice(1)],{encoding:"utf-8",maxBuffer:536870912,env:Ve(o)});if(f.status!==0&&p[0]!=="query"&&!p.some(g=>g.startsWith("-generate")))throw new S(`${a}: ${n} ${p[0]} failed:
|
|
1164
1164
|
${Mk(f.stderr||f.stdout)}`);return f},l=vr(c,".terragucci-drift.tfplan");try{u("init","-input=false");let p=ob(n)&&ms(e,[a]).length>0;u("plan",...p?[]:["-refresh-only"],"-input=false","-lock=false",`-out=${l}`);let f=JSON.parse(u("show","-json",l).stdout),g=nl(p?Dv(f):f);if(s){let h=s.known?.[a]??await ep(a,g,s),y=VT(h.attributions);g=ZT(g,y.leave),i.attributions.push(...h.attributions),i.routed.push(...y.lines.map(w=>`${a}: ${w.slice(2)}`).map(w=>`- ${w}`)),i.notes.push(...h.notes.filter(w=>!i.notes.includes(w)))}let m=Av(a,c,g);i.codified.push(...m.codified),i.left.push(...m.left);for(let[h,y]of m.files)i.files.set(a4.join(a,h),y)}finally{i4(l,{force:!0})}let d=n==="terraform"&&Ez(c);if(!(!r.length&&!d)){for(let p of[Dk,Vp])if(Da(vr(c,p)))throw new S(`${a}/${p} exists; merge or remove it first`);try{r.length&&u4(vr(c,Dk),Rz(r));let p=d?u("query",`-generate-config-out=${Vp}`):u("plan","-input=false","-lock=false",`-generate-config-out=${Vp}`);if(!Da(vr(c,Vp)))throw new S(`${a}: ${n} generated no config:
|
|
1165
1165
|
${Mk(p.stderr||p.stdout)}`);for(let f of[Dk,Vp])Da(vr(c,f))&&i.files.set(a4.join(a,f),Zp(vr(c,f),"utf-8"));i.imports.push(...r.length?r.map(f=>`${a}: ${f.address} (${f.id})`):[`${a}: what terraform query lists`])}finally{for(let p of[Dk,Vp])i4(vr(c,p),{force:!0})}}}return i}async function oRe(e,t,n,r,o,s,i){if(n.imports?.length)throw new S("--import writes import blocks into a root's own files, and a Terragrunt unit's resources are in its module; import into a unit with an import block in its module, through a reviewed change");let a=MV(t,r,{...n.terragrunt?{terragrunt:n.terragrunt}:{},...n.terragruntExec?{exec:n.terragruntExec}:{}}),{units:c,from:u}=await LV(e,a,{...n.report?{report:n.report}:{},...n.root?{root:n.root}:{},...o.terragrunt?.exclude?{exclude:o.terragrunt.exclude}:{}});await s.prepareRoots({roots:c,binary:t,terragrunt:a.terragrunt,exec:a.exec});let l={codified:[],left:[],files:new Map,imports:[],attributions:[],routed:[],notes:[]};if(c.length===0)return l;l.notes.push(`${c.length} unit${c.length===1?"":"s"} from ${u}: ${c.join(", ")}`);for(let d of c){let p=await GV(e,d,a);if(p.length===0)continue;if(i){let g=i.known?.[d]??await ep(d,p,i),m=VT(g.attributions);p=ZT(p,m.leave),l.attributions.push(...g.attributions),l.routed.push(...m.lines.map(h=>`${d}: ${h.slice(2)}`).map(h=>`- ${h}`)),l.notes.push(...g.notes.filter(h=>!l.notes.includes(h)))}let f=await UV(e,d,p,a,l.files,s.sourceOf(d));l.codified.push(...f.codified),l.left.push(...f.left)}return l}async function sRe(e,t,n,r,o,s){let i=o.branch??s.GITHUB_HEAD_REF??s.CI_MERGE_REQUEST_SOURCE_BRANCH_NAME;if(!i)throw new S("respond fmt needs --branch, the pull request's branch");if(r==="apply"&&i===await lc(o.fetch??globalThis.fetch,vh(e,t,s)))throw new S(`${i} is the default branch; fmt commits only to a pull request's branch`);no(e,["fetch","-q","origin",i]);let a=QP(e,"FETCH_HEAD");try{let c=Y0(n,["fmt","-recursive","-list=true",...r==="apply"?[]:["-check"]],{cwd:a.dir,encoding:"utf-8",env:Ve(s)});if(c.error)throw new S(`respond fmt could not run ${n}: ${c.error.message}`);let u=c.stdout.split(`
|
|
1166
1166
|
`).filter(Boolean);if(c.status!==0&&u.length===0)throw new S(`${i}: ${n} fmt failed:
|
|
@@ -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 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}
|
|
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&&!NJ.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,B2(o,d,n["dry-run"]===!0))):(console.log(U2(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 H2(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(q2(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 a2({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(IJ(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 CJ(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 AJ(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 PJ(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 x2(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 TJ(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 w2(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 y2({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
|
|
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 d2(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,bJ(r,n)),c=lJ(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=o2(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=>XL(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(...QL(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)
|