@intentius/terragucci 0.2.0

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.
@@ -0,0 +1,292 @@
1
+ export declare const BINARIES: readonly ["terraform", "tofu", "choudoufu", "cdktn"];
2
+ export declare const FORGES: readonly ["github", "gitlab", "forgejo"];
3
+ export declare const GATES: readonly ["always", "on-destroy", "never"];
4
+ export declare const RUNTIMES: readonly ["forge", "fountain"];
5
+ export declare const DEPENDENTS: readonly ["follow", "plan"];
6
+ export declare const POLICY_ENGINES: readonly ["conftest", "opa"];
7
+ export declare const POLICY_INPUTS: readonly ["plan", "hcp"];
8
+ export type Binary = (typeof BINARIES)[number];
9
+ export type ForgeName = (typeof FORGES)[number];
10
+ export type Gate = (typeof GATES)[number];
11
+ export type Runtime = (typeof RUNTIMES)[number];
12
+ export type Dependents = (typeof DEPENDENTS)[number];
13
+ export type PolicyEngine = (typeof POLICY_ENGINES)[number];
14
+ export type PolicyInput = (typeof POLICY_INPUTS)[number];
15
+ /**
16
+ * Policy as code, off unless set. `tf-plan` runs the engine over each planned
17
+ * root's plan JSON and fails the root on a denial. No response, agent or
18
+ * comment can waive it.
19
+ */
20
+ export interface PolicySettings {
21
+ /** The engine. Default `conftest`, which terragucci installs on demand when it is not on the path. */
22
+ engine?: PolicyEngine;
23
+ /** The directory of Rego policy, relative to the repo root. Default `policy`. */
24
+ path?: string;
25
+ /** The Rego package whose `deny`, `violation`, `deny_*` and `warn` rules count. conftest default: every namespace. opa default: `main`, or every package under `terraform.policies` with `input: hcp`. */
26
+ namespace?: string;
27
+ /** What `input` holds: `plan`, the bare plan JSON (default); `hcp`, `{plan, run}` as HCP Terraform's OPA policies read it. */
28
+ input?: PolicyInput;
29
+ }
30
+ /**
31
+ * The jobs' cloud identities over the forge's OIDC token. `plan_role` and
32
+ * `apply_role` are AWS roles; `gcp` and `azure` set those clouds, beside AWS
33
+ * or instead of it.
34
+ */
35
+ export interface OidcSettings {
36
+ plan_role?: string;
37
+ apply_role?: string;
38
+ /** The AWS token's audience. Default `sts.amazonaws.com`. */
39
+ audience?: string;
40
+ /** GCP Workload Identity Federation: the provider's resource name and a service account per stage. */
41
+ gcp?: {
42
+ workload_identity_provider: string;
43
+ plan_service_account: string;
44
+ apply_service_account: string;
45
+ };
46
+ /** An Entra app registration or managed identity per stage, with a federated credential for the forge. */
47
+ azure?: {
48
+ tenant_id: string;
49
+ subscription_id: string;
50
+ plan_client_id: string;
51
+ apply_client_id: string;
52
+ };
53
+ }
54
+ /** A plan role and an apply role, for the units under one path. */
55
+ export interface RolePair {
56
+ plan: string;
57
+ apply: string;
58
+ }
59
+ /**
60
+ * Terragrunt settings. Terragrunt mode is detected (`root.hcl`,
61
+ * `terragrunt.hcl` or `terragrunt.stack.hcl`); this block only tunes it.
62
+ */
63
+ export interface TerragruntSettings {
64
+ /** The Terragrunt release the pipeline installs. Default: the one terragucci's image carries. */
65
+ version?: string;
66
+ /** Unit globs discovery leaves out, beside `catalog/**` and the module cache. */
67
+ exclude?: string[];
68
+ /** How many units one `run --all` runs at once. Default: from the state backend. */
69
+ parallelism?: number;
70
+ /** Units that depend on a changed unit: `follow` plans them in later waves, `plan` also previews them at pull-request time. */
71
+ dependents?: Dependents;
72
+ /**
73
+ * Plan and apply roles by unit path glob, assumed over OIDC through a
74
+ * generated auth-provider-cmd. A unit that sets its own `iam_role` keeps it.
75
+ */
76
+ credentials?: Record<string, RolePair>;
77
+ }
78
+ /**
79
+ * Pipeline events and the responses each takes. The first mode is the
80
+ * default and needs no model; `agent` adds an agent's comment or proposal on
81
+ * top of the deterministic response, and is never the default. `drift:
82
+ * attribute` also names who changed each drifted attribute (a known-writes
83
+ * table, then the audit log, then a typed decision when `decide:` is set).
84
+ */
85
+ export declare const RESPONSES: {
86
+ readonly plan: readonly ["summary", "agent"];
87
+ readonly "wave-refused": readonly ["diff", "off"];
88
+ readonly "apply-failed": readonly ["triage", "agent", "off"];
89
+ readonly drift: readonly ["pull-request", "attribute", "agent", "off"];
90
+ readonly tips: readonly ["pull-request", "off"];
91
+ readonly fmt: readonly ["commit", "off"];
92
+ readonly publish: readonly ["notes", "agent", "off"];
93
+ readonly rollout: readonly ["next-wave", "off"];
94
+ readonly question: readonly ["off", "agent"];
95
+ readonly "version-bump": readonly ["off", "suggest"];
96
+ /** terragucci#30: a typed decision flags a pull request whose description leaves out what its plan destroys or replaces. Needs `decide:`. */
97
+ readonly description: readonly ["off", "check"];
98
+ };
99
+ export type RespondEvent = keyof typeof RESPONSES;
100
+ export declare const AGENT_VIA: readonly ["forge", "fountain"];
101
+ /** The services `decide:` can name; each speaks the Jev request and response shape. */
102
+ export declare const DECIDE_BACKENDS: readonly ["laya", "von", "decider", "jev"];
103
+ export type DecideBackend = (typeof DECIDE_BACKENDS)[number];
104
+ export declare const QUESTION_TYPES: readonly ["noul", "choice", "score"];
105
+ export type QuestionType = (typeof QUESTION_TYPES)[number];
106
+ /**
107
+ * `decide:`: the typed-decision service the opt-in uses ask (terragucci#28).
108
+ * With no `decide:`, every use runs its deterministic response.
109
+ */
110
+ export interface DecideSettings {
111
+ /** Which service answers: laya (the terragucci-decide image), von, decider or jev. */
112
+ backend: DecideBackend;
113
+ /** The service's base URL; `/v1/systemone` is appended. Required except for jev, which defaults to TypeSafe's. */
114
+ url?: string;
115
+ /** The pinned model version. Required except for laya, which defaults to the version terragucci-decide serves. */
116
+ model?: string;
117
+ /** The environment variable holding the service's bearer token. Required for jev. */
118
+ token_env?: string;
119
+ /** The probability an answer needs before a use acts on it, per question type, between 0 and 1. */
120
+ thresholds?: Partial<Record<QuestionType, number>>;
121
+ }
122
+ /** `dashboards:` in terragucci.yml, as a map. `true` takes every default. */
123
+ export interface DashboardSettings {
124
+ /** Where the files go, relative to the repo. Default `observability/terragucci`. */
125
+ dir?: string;
126
+ /** The uid of the Grafana datasource that reads the Prometheus holding the metrics. Default `prometheus`. */
127
+ prometheus?: string;
128
+ /** The uid of the Grafana datasource that reads Tempo. Default `tempo`. */
129
+ tempo?: string;
130
+ /** The Grafana folder the dashboards and Grafana-managed rules go in. Default `terragucci`. */
131
+ folder?: string;
132
+ /** Where Grafana's container finds the dashboard files. Default `/var/lib/grafana/dashboards/terragucci`. */
133
+ path?: string;
134
+ /** Alert when a project's drift is older than this. Default `1d`. */
135
+ drift_age?: string;
136
+ /** Alert when a wave has waited for its approval longer than this. Default `4h`. */
137
+ wave_wait?: string;
138
+ /** Alert when a project's drift run has not run for this long. Default `2d`. */
139
+ schedule?: string;
140
+ }
141
+ export declare const DASHBOARD_KEYS: readonly ["dir", "prometheus", "tempo", "folder", "path", "drift_age", "wave_wait", "schedule"];
142
+ export declare const DASHBOARD_DURATION_KEYS: readonly ["drift_age", "wave_wait", "schedule"];
143
+ /**
144
+ * `agent.comment`: the `/terragucci agent <ask>` pull request comment, off
145
+ * unless set. The comment starts a job that runs a coding agent on the pull
146
+ * request's head branch and pushes what it changes with `agent.token_env`'s
147
+ * token. The job gets no cloud credentials: no `oidc` role and not
148
+ * `agent.role`. `true` takes every default.
149
+ */
150
+ export interface AgentCommentSettings {
151
+ /** The agent's command line, run in the checkout with the prompt on stdin. Default: Claude Code in print mode with file tools only (AGENT_COMMAND in agent-comment.ts). */
152
+ command?: string;
153
+ /** The secret holding the model's API key, mapped into the agent's step alone. Default `ANTHROPIC_API_KEY`. */
154
+ key_secret?: string;
155
+ /** The most turns the agent takes, as `$TG_AGENT_MAX_TURNS`. Default 30. */
156
+ max_turns?: number;
157
+ /** Minutes before the agent's job is stopped. Default 30. */
158
+ timeout?: number;
159
+ }
160
+ export declare const AGENT_COMMENT_KEYS: readonly ["command", "key_secret", "max_turns", "timeout"];
161
+ /** The settings one project (or one repo) can carry. Every key is optional. */
162
+ export interface ProjectSettings {
163
+ /** Globs of root directories. Detected when absent. */
164
+ roots?: string[];
165
+ /** The binary the pipeline runs. Detected when absent. */
166
+ binary?: Binary;
167
+ /** The binary's version. Read from the roots' `required_version` when it pins one. */
168
+ version?: string;
169
+ /** The forge, for a host terragucci cannot name. */
170
+ forge?: ForgeName;
171
+ /** Where the project lives, for a forge not on https or the default port. */
172
+ url?: string;
173
+ /** When a wave waits for an approval. */
174
+ gate?: Gate;
175
+ waves?: {
176
+ canary?: string[];
177
+ };
178
+ /** A cron schedule for tf-drift, or false. */
179
+ drift?: string | false;
180
+ runtime?: Runtime;
181
+ /**
182
+ * A bucket for plan reports. `url` is the address that serves the bucket's
183
+ * objects to a browser (a static site, a CDN, the store's public endpoint);
184
+ * with it, the note, the index and the dashboards link the bucket's copy.
185
+ */
186
+ reports?: {
187
+ bucket: string;
188
+ endpoint?: string;
189
+ prefix?: string;
190
+ url?: string;
191
+ };
192
+ /** The environment variable holding the forge token. */
193
+ token_env?: string;
194
+ /** Environment variables every job gets. Values only, never secrets. */
195
+ env?: Record<string, string>;
196
+ /**
197
+ * The secret holding `OTEL_EXPORTER_OTLP_HEADERS`, such as a collector's API
198
+ * key, and `trace_url`: a link to a run's trace with `{trace_id}` in it
199
+ * (Grafana's Explore, Tempo, Jaeger), which the report links.
200
+ */
201
+ telemetry?: {
202
+ headers_secret?: string;
203
+ trace_url?: string;
204
+ };
205
+ tips?: boolean;
206
+ modules?: {
207
+ path?: string;
208
+ publish?: string | string[];
209
+ };
210
+ /**
211
+ * Cloud identities the pipeline takes over OIDC, so no long-lived keys sit in CI.
212
+ * Plan runs pull-request code and gets the read-only identity; apply gets the
213
+ * write one. The two must differ, on every cloud set.
214
+ */
215
+ oidc?: OidcSettings;
216
+ /** Whether removing the project from a control repo removes its generated files. */
217
+ owned?: boolean;
218
+ /** How many roots of one dependency layer plan at once. Default: from the state backend. */
219
+ parallelism?: number;
220
+ /** Terragrunt settings, for a repo terragucci finds Terragrunt in. */
221
+ terragrunt?: TerragruntSettings;
222
+ /** Opt-in policy checks over each plan; see PolicySettings. */
223
+ policy?: PolicySettings;
224
+ /** The response to each pipeline event; see RESPONSES. */
225
+ respond?: Partial<Record<RespondEvent, string>>;
226
+ /**
227
+ * Where an agent response runs, for any event set to `agent`. Its token can
228
+ * comment and open pull requests; its role, when named, is read-only.
229
+ */
230
+ agent?: {
231
+ via: (typeof AGENT_VIA)[number];
232
+ token_env: string;
233
+ role?: string;
234
+ comment?: boolean | AgentCommentSettings;
235
+ };
236
+ /** The typed-decision service; see DecideSettings. Off when absent. A project's `decide` replaces the defaults' whole. */
237
+ decide?: DecideSettings;
238
+ /** The AWS region whose CloudTrail drift attribution reads. Default: the region the aws CLI already uses. */
239
+ audit_region?: string;
240
+ /** Dashboards and alert rules written next to the pipeline. Off unless set. */
241
+ dashboards?: boolean | DashboardSettings;
242
+ }
243
+ /** The whole file: one repo's settings, or `defaults` and `projects` for many repos. */
244
+ export interface TerragucciConfig extends ProjectSettings {
245
+ defaults?: ProjectSettings;
246
+ projects?: Record<string, ProjectSettings>;
247
+ }
248
+ /** The response a project takes to an event: its setting, or the event's default. */
249
+ export declare function responseTo(settings: ProjectSettings, event: RespondEvent): string;
250
+ /** Settings with terragucci's defaults filled in. Detection fills `roots`, `binary` and `forge` later. */
251
+ export interface ResolvedSettings extends ProjectSettings {
252
+ gate: Gate;
253
+ drift: string | false;
254
+ runtime: Runtime;
255
+ tips: boolean;
256
+ owned: boolean;
257
+ env: Record<string, string>;
258
+ }
259
+ export declare const BUILT_IN: ResolvedSettings;
260
+ export declare class ConfigError extends Error {
261
+ /** Every problem found, when the error is a validation failure. */
262
+ readonly problems?: string[];
263
+ constructor(message: string, problems?: string[]);
264
+ }
265
+ /** A `--mode` value: dry-run or apply. */
266
+ export declare function checkMode(mode: string): "dry-run" | "apply";
267
+ export declare const CONFIG_NAMES: string[];
268
+ /** The config file in `dir`, or undefined. Two of them is an error. */
269
+ export declare function findConfig(dir: string): string | undefined;
270
+ /** Check a parsed config and return it typed, or throw with every problem listed. */
271
+ export declare function validateConfig(raw: unknown, where: string): TerragucciConfig;
272
+ export type ConfigMode = "fold" | "run" | "check";
273
+ /** The TypeScript folder ships separately; only a `.ts` config needs it. */
274
+ export declare const TSAD_INSTALL = "npm i -D @intentius/tsad-reference";
275
+ /** Load and validate a config file. A `.ts` file is folded unless `mode` says otherwise. */
276
+ export declare function loadConfig(path: string, mode?: ConfigMode): Promise<TerragucciConfig>;
277
+ export interface ProjectKey {
278
+ key: string;
279
+ host: string;
280
+ /** Everything after the host: owner/name, or a GitLab group path and name. */
281
+ path: string;
282
+ owner: string;
283
+ name: string;
284
+ }
285
+ /** Split `<host>/<path>`. The host is the first segment; the path needs an owner and a name. */
286
+ export declare function parseProjectKey(key: string): ProjectKey;
287
+ /** The forge a host belongs to, when the host says so. */
288
+ export declare function forgeFromHost(host: string): ForgeName | undefined;
289
+ /** One repo's settings: built-in defaults, then the file's own keys. */
290
+ export declare function resolveRepo(config: TerragucciConfig): ResolvedSettings;
291
+ /** A control repo's project: built-in defaults, then `defaults`, then the project's own keys. */
292
+ export declare function resolveProject(config: TerragucciConfig, key: string): ResolvedSettings;
package/package.json ADDED
@@ -0,0 +1,55 @@
1
+ {
2
+ "name": "@intentius/terragucci",
3
+ "version": "0.2.0",
4
+ "description": "The whole Terraform lifecycle, handled: one config file, generated pipelines for GitHub, GitLab and Forgejo.",
5
+ "license": "Apache-2.0",
6
+ "type": "module",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/INTENTIUS/terragucci.git",
10
+ "directory": "packages/terragucci"
11
+ },
12
+ "homepage": "https://intentius.io/terragucci/",
13
+ "bin": {
14
+ "terragucci": "dist/terragucci.mjs"
15
+ },
16
+ "exports": {
17
+ ".": {
18
+ "types": "./dist/types.d.ts"
19
+ },
20
+ "./report.schema.json": "./dist/report.schema.json"
21
+ },
22
+ "files": [
23
+ "dist",
24
+ "README.md",
25
+ "LICENSE"
26
+ ],
27
+ "engines": {
28
+ "node": ">=22"
29
+ },
30
+ "types": "./dist/types.d.ts",
31
+ "scripts": {
32
+ "build": "node ../../scripts/build-cli.mjs"
33
+ },
34
+ "peerDependencies": {
35
+ "@intentius/tsad-reference": "2.x",
36
+ "@cdktn/hcl2json": "0.x"
37
+ },
38
+ "peerDependenciesMeta": {
39
+ "@intentius/tsad-reference": {
40
+ "optional": true
41
+ },
42
+ "@cdktn/hcl2json": {
43
+ "optional": true
44
+ }
45
+ },
46
+ "devDependencies": {
47
+ "@cdktn/hcl2json": "0.24.0",
48
+ "@intentius/chant": "0.106.4",
49
+ "@intentius/chant-lexicon-forgejo": "0.106.4",
50
+ "@intentius/chant-lexicon-github": "0.106.4",
51
+ "@intentius/chant-lexicon-gitlab": "0.106.4",
52
+ "@intentius/chant-lexicon-terraform": "0.106.4",
53
+ "@intentius/tsad-reference": "2.1.0"
54
+ }
55
+ }