@intentius/chant 0.50.0 → 0.52.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.
- package/dist/cli/build-options.d.ts +68 -0
- package/dist/cli/build-options.d.ts.map +1 -0
- package/dist/cli/commands/build.d.ts.map +1 -1
- package/dist/cli/handlers/lifecycle.d.ts.map +1 -1
- package/dist/cli/handlers/op-progress.d.ts +57 -0
- package/dist/cli/handlers/op-progress.d.ts.map +1 -0
- package/dist/cli/handlers/run-client.d.ts +21 -1
- package/dist/cli/handlers/run-client.d.ts.map +1 -1
- package/dist/cli/handlers/run-report.d.ts.map +1 -1
- package/dist/cli/handlers/run.d.ts +0 -21
- package/dist/cli/handlers/run.d.ts.map +1 -1
- package/dist/cli/handlers/search.d.ts +22 -0
- package/dist/cli/handlers/search.d.ts.map +1 -1
- package/dist/cli/main.d.ts.map +1 -1
- package/dist/cli/mcp/op-tools.d.ts.map +1 -1
- package/dist/cli/mcp/resource-handlers.d.ts.map +1 -1
- package/dist/cli/registry.d.ts +33 -1
- package/dist/cli/registry.d.ts.map +1 -1
- package/dist/components/run-progress.d.ts +7 -5
- package/dist/components/run-progress.d.ts.map +1 -1
- package/dist/lexicon.d.ts +41 -0
- package/dist/lexicon.d.ts.map +1 -1
- package/dist/lifecycle/assert-live.d.ts +77 -0
- package/dist/lifecycle/assert-live.d.ts.map +1 -0
- package/dist/lifecycle/change-set.d.ts +40 -0
- package/dist/lifecycle/change-set.d.ts.map +1 -1
- package/dist/lifecycle/disruption.d.ts +96 -0
- package/dist/lifecycle/disruption.d.ts.map +1 -0
- package/dist/lifecycle/index.d.ts +2 -0
- package/dist/lifecycle/index.d.ts.map +1 -1
- package/dist/lifecycle/replay.d.ts +2 -0
- package/dist/lifecycle/replay.d.ts.map +1 -1
- package/dist/lint/policy.d.ts +16 -0
- package/dist/lint/policy.d.ts.map +1 -1
- package/dist/op/local-executor.d.ts +22 -2
- package/dist/op/local-executor.d.ts.map +1 -1
- package/dist/testing.d.ts +23 -2
- package/dist/testing.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/cli/build-options.test.ts +101 -0
- package/src/cli/build-options.ts +109 -0
- package/src/cli/commands/build.ts +24 -53
- package/src/cli/handlers/lifecycle.test.ts +109 -0
- package/src/cli/handlers/lifecycle.ts +32 -8
- package/src/cli/handlers/op-progress.test.ts +202 -0
- package/src/cli/handlers/op-progress.ts +192 -0
- package/src/cli/handlers/run-client.test.ts +82 -0
- package/src/cli/handlers/run-client.ts +85 -2
- package/src/cli/handlers/run-report.test.ts +62 -0
- package/src/cli/handlers/run-report.ts +20 -58
- package/src/cli/handlers/run.test.ts +240 -0
- package/src/cli/handlers/run.ts +76 -18
- package/src/cli/handlers/search-drift.test.ts +263 -0
- package/src/cli/handlers/search.ts +150 -1
- package/src/cli/main.ts +11 -0
- package/src/cli/mcp/op-tools.ts +17 -6
- package/src/cli/mcp/resource-handlers.ts +13 -5
- package/src/cli/registry.ts +33 -1
- package/src/components/run-progress.ts +9 -5
- package/src/lexicon.ts +51 -0
- package/src/lifecycle/assert-live.test.ts +125 -0
- package/src/lifecycle/assert-live.ts +154 -0
- package/src/lifecycle/change-set.test.ts +144 -1
- package/src/lifecycle/change-set.ts +165 -11
- package/src/lifecycle/disruption.test.ts +186 -0
- package/src/lifecycle/disruption.ts +224 -0
- package/src/lifecycle/index.ts +2 -0
- package/src/lifecycle/replay.test.ts +25 -0
- package/src/lifecycle/replay.ts +11 -3
- package/src/lint/policy-build-parity.test.ts +232 -0
- package/src/lint/policy.ts +51 -6
- package/src/op/local-executor.ts +35 -1
- package/src/op/local-output.ts +1 -1
- package/src/testing.test.ts +89 -2
- package/src/testing.ts +63 -3
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* assertLive (#1857) — the read half of the test harness (#1224):
|
|
3
|
+
* observation-backed assertions against a live deploy, for exactly one
|
|
4
|
+
* declared entity at a time. Same primitive teardown.ts's fallback path
|
|
5
|
+
* uses — `describeResources` — turned into a pass/throw instead of a
|
|
6
|
+
* would-delete set.
|
|
7
|
+
*
|
|
8
|
+
* The observation contract (#1089) draws a hard line between OBSERVED-ABSENT
|
|
9
|
+
* and NOT-OBSERVED: a declared entity the read could not cover is never the
|
|
10
|
+
* same as one confirmed missing. `assertLiveEntity` preserves that line by
|
|
11
|
+
* construction — NOT-OBSERVED throws {@link UnobservedAssertionError}, a type
|
|
12
|
+
* distinct from the {@link LiveAssertionError} an observed-absent, foreign,
|
|
13
|
+
* or status-mismatched verdict throws, so a caller can tell "could not tell"
|
|
14
|
+
* from "confirmed wrong" without parsing a message.
|
|
15
|
+
*
|
|
16
|
+
* Marker verification is best-effort by the same logic {@link
|
|
17
|
+
* ResourceMetadata.marker}'s own contract states: a lexicon with no marker
|
|
18
|
+
* channel on this read path (aws's thin `describeResources`, `ownership:
|
|
19
|
+
* "unknown"`) reports no marker at all, which is not the same claim as
|
|
20
|
+
* "foreign". Enforcing a match whenever the channel exists — a present
|
|
21
|
+
* mismatch, or `ownership: "foreign"` with no marker to show — catches the
|
|
22
|
+
* case the harness cares about (a same-named leftover from another env);
|
|
23
|
+
* an absent channel is passed through unverified rather than making every
|
|
24
|
+
* lexicon without one unusable.
|
|
25
|
+
*/
|
|
26
|
+
import { type UnobservedReason } from "../observation.js";
|
|
27
|
+
import type { ObservationLexicon, ResourceMetadata } from "../lexicon.js";
|
|
28
|
+
import type { OwnershipMarker } from "../ownership.js";
|
|
29
|
+
/**
|
|
30
|
+
* Thrown by {@link assertLiveEntity} for a confirmed failure: observed
|
|
31
|
+
* absent, a marker that names another stack/env, a resource confirmed
|
|
32
|
+
* foreign, or a status mismatch. Never thrown for NOT-OBSERVED — see {@link
|
|
33
|
+
* UnobservedAssertionError}.
|
|
34
|
+
*/
|
|
35
|
+
export declare class LiveAssertionError extends Error {
|
|
36
|
+
constructor(message: string);
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Thrown when the entity is NOT-OBSERVED (#1089) rather than confirmed
|
|
40
|
+
* present or absent. Kept as its own type, not a flag on {@link
|
|
41
|
+
* LiveAssertionError}: a suite (or a CI policy) that wants to fail loudly on
|
|
42
|
+
* "could not tell" but treat it differently from a confirmed miss can catch
|
|
43
|
+
* this one specifically.
|
|
44
|
+
*/
|
|
45
|
+
export declare class UnobservedAssertionError extends Error {
|
|
46
|
+
readonly entity: string;
|
|
47
|
+
readonly reason: UnobservedReason;
|
|
48
|
+
readonly detail?: string | undefined;
|
|
49
|
+
constructor(entity: string, reason: UnobservedReason, detail?: string | undefined);
|
|
50
|
+
}
|
|
51
|
+
export interface AssertLiveOptions {
|
|
52
|
+
/** Expected `ResourceMetadata.status`, where the lexicon reports one. Skipped when omitted. */
|
|
53
|
+
status?: string;
|
|
54
|
+
}
|
|
55
|
+
export interface AssertLiveEntityOptions extends AssertLiveOptions {
|
|
56
|
+
plugin: ObservationLexicon;
|
|
57
|
+
/** chant entity name — the key to assert on. */
|
|
58
|
+
name: string;
|
|
59
|
+
entityType: string;
|
|
60
|
+
props: Record<string, unknown>;
|
|
61
|
+
/** This lexicon's own built output for the deploy, or `""` when none was built. */
|
|
62
|
+
buildOutput: string;
|
|
63
|
+
environment: string;
|
|
64
|
+
/** This deploy's identity — the marker an observed resource is checked against. */
|
|
65
|
+
marker: OwnershipMarker;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Assert one declared entity is live: observed present in `environment`, not
|
|
69
|
+
* a confirmed-foreign resource, and — when `status` is given — reporting
|
|
70
|
+
* that status. Resolves to the entity's {@link ResourceMetadata} on success.
|
|
71
|
+
*
|
|
72
|
+
* Throws {@link UnobservedAssertionError} for NOT-OBSERVED. Throws {@link
|
|
73
|
+
* LiveAssertionError} for observed-absent, a confirmed-foreign identity, or a
|
|
74
|
+
* status mismatch.
|
|
75
|
+
*/
|
|
76
|
+
export declare function assertLiveEntity(opts: AssertLiveEntityOptions): Promise<ResourceMetadata>;
|
|
77
|
+
//# sourceMappingURL=assert-live.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"assert-live.d.ts","sourceRoot":"","sources":["../../src/lifecycle/assert-live.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,EAA6D,KAAK,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAClH,OAAO,KAAK,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AACvE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAEpD;;;;;GAKG;AACH,qBAAa,kBAAmB,SAAQ,KAAK;gBAC/B,OAAO,EAAE,MAAM;CAI5B;AAED;;;;;;GAMG;AACH,qBAAa,wBAAyB,SAAQ,KAAK;aAE/B,MAAM,EAAE,MAAM;aACd,MAAM,EAAE,gBAAgB;aACxB,MAAM,CAAC,EAAE,MAAM;gBAFf,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,gBAAgB,EACxB,MAAM,CAAC,EAAE,MAAM,YAAA;CAQlC;AAED,MAAM,WAAW,iBAAiB;IAChC,+FAA+F;IAC/F,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,uBAAwB,SAAQ,iBAAiB;IAChE,MAAM,EAAE,kBAAkB,CAAC;IAC3B,gDAAgD;IAChD,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,mFAAmF;IACnF,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,mFAAmF;IACnF,MAAM,EAAE,eAAe,CAAC;CACzB;AAQD;;;;;;;;GAQG;AACH,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,uBAAuB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAwD/F"}
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
*/
|
|
15
15
|
import { type AttributeChange, type DiffLiveInput } from "./live-diff.js";
|
|
16
16
|
import { type UnobservedReason } from "../observation.js";
|
|
17
|
+
import { type Disruption } from "./disruption.js";
|
|
17
18
|
/**
|
|
18
19
|
* What the projection proposes for a single resource.
|
|
19
20
|
*
|
|
@@ -125,6 +126,22 @@ export interface ChangeSetEntry {
|
|
|
125
126
|
effectReason?: EffectFireReason;
|
|
126
127
|
/** Human-readable backing for `effectReason` (the digests that differ, the unresolved path). */
|
|
127
128
|
effectDetail?: string;
|
|
129
|
+
/**
|
|
130
|
+
* How much applying this change hurts (#1665) — in-place / rolling / replace
|
|
131
|
+
* / destroy / unknown. Set on `update` entries only: every other action
|
|
132
|
+
* carries its blast radius in the action itself.
|
|
133
|
+
*
|
|
134
|
+
* The verdict comes from the lexicon that owns the spec
|
|
135
|
+
* ({@link LexiconPlugin.classifyDisruption}), never from core, which has no
|
|
136
|
+
* per-provider replacement rules and must not grow any. `unknown` is the
|
|
137
|
+
* default and the only fallback — read it as "nobody could say", never as
|
|
138
|
+
* "probably in place".
|
|
139
|
+
*/
|
|
140
|
+
disruption?: Disruption;
|
|
141
|
+
/** The attribute paths that forced `disruption` (#1665). */
|
|
142
|
+
disruptionBecause?: string[];
|
|
143
|
+
/** Human-readable backing for `disruption` — the spec knowledge behind the call, or why there is none. */
|
|
144
|
+
disruptionDetail?: string;
|
|
128
145
|
}
|
|
129
146
|
export interface ChangeSet {
|
|
130
147
|
env: string;
|
|
@@ -170,6 +187,29 @@ export interface GitlabMrReport {
|
|
|
170
187
|
}
|
|
171
188
|
/** Project a change set onto the GitLab MR plan widget shape. Pure. */
|
|
172
189
|
export declare function gitlabMrReport(cs: ChangeSet): GitlabMrReport;
|
|
190
|
+
/**
|
|
191
|
+
* Warning a plan should print when it carries a hole — a declared entity the
|
|
192
|
+
* lexicon could not observe (#1089). The `--json` and `--report gitlab-mr`
|
|
193
|
+
* shapes have no column for `unobserved`, so both the CLI (on stderr) and the
|
|
194
|
+
* `markdown` report (in the body, since a reviewer never sees a job's stderr)
|
|
195
|
+
* read this same wording rather than drifting apart. Empty when the plan has
|
|
196
|
+
* no hole. Same discipline as {@link disruptionNotices} in `./disruption`.
|
|
197
|
+
*/
|
|
198
|
+
export declare function unobservedPlanNotice(cs: ChangeSet): string[];
|
|
173
199
|
/** Human-readable render of a change set. Pure — returns a string. */
|
|
174
200
|
export declare function renderChangeSet(cs: ChangeSet): string;
|
|
201
|
+
/**
|
|
202
|
+
* Markdown projection of a change set (#1983) — sized for a PR/MR comment
|
|
203
|
+
* rather than a terminal. A scannable counts header, entries grouped by
|
|
204
|
+
* action and attributed to their lexicon, deltas in a fenced block, and any
|
|
205
|
+
* group past {@link MARKDOWN_FOLD_THRESHOLD} folded into a `<details>` so a
|
|
206
|
+
* large plan doesn't bury the comment. Pure — no ANSI, no I/O — and
|
|
207
|
+
* deterministic: same entry ordering as {@link renderChangeSet}.
|
|
208
|
+
*
|
|
209
|
+
* Both a hole (#1089) and an expensive verdict (#1665) are surfaced IN the
|
|
210
|
+
* body, not left to stderr the way `--json` and `--report gitlab-mr` leave
|
|
211
|
+
* them: a reviewer reading a comment never sees the job's log, so a plan
|
|
212
|
+
* carrying either must not read as clean.
|
|
213
|
+
*/
|
|
214
|
+
export declare function renderChangeSetMarkdown(cs: ChangeSet): string;
|
|
175
215
|
//# sourceMappingURL=change-set.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"change-set.d.ts","sourceRoot":"","sources":["../../src/lifecycle/change-set.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,EAAY,KAAK,eAAe,EAAE,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AACjF,OAAO,EAAwB,KAAK,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"change-set.d.ts","sourceRoot":"","sources":["../../src/lifecycle/change-set.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-change disruption classification (#1665).
|
|
3
|
+
*
|
|
4
|
+
* The change set says WHAT a pending change is (`create`/`update`/`delete`/…).
|
|
5
|
+
* It says nothing about what applying it costs. An `update` that flips a tag
|
|
6
|
+
* and an `update` that rebuilds a database read identically, and the second one
|
|
7
|
+
* is the one that wakes somebody up.
|
|
8
|
+
*
|
|
9
|
+
* The knowledge that separates them is spec knowledge. CloudFormation's
|
|
10
|
+
* registry schema declares `createOnlyProperties` per type; Kubernetes' SSA
|
|
11
|
+
* schema knows which field changes roll a workload. Core owns neither, and
|
|
12
|
+
* hardcoding either here would put per-provider replacement rules in the tool
|
|
13
|
+
* — the same mistake `postSynthChecks` exists to avoid. So core defines the
|
|
14
|
+
* contract and the reporting, and the lexicon that compiled the spec supplies
|
|
15
|
+
* the answer, via {@link LexiconPlugin.classifyDisruption}.
|
|
16
|
+
*
|
|
17
|
+
* The invariant that makes the field trustworthy is that `unknown` is the
|
|
18
|
+
* default and the only fallback. No classifier, a classifier that says nothing
|
|
19
|
+
* about an entry, a classifier that throws, a classifier that returns a level
|
|
20
|
+
* outside the vocabulary — all of them land on `unknown`, never on `in-place`.
|
|
21
|
+
* A confident "this mutates in place" is only ever a lexicon's own claim.
|
|
22
|
+
*/
|
|
23
|
+
import type { AttributeChange } from "./live-diff.js";
|
|
24
|
+
import type { ChangeSet, ChangeSetEntry } from "./change-set.js";
|
|
25
|
+
/**
|
|
26
|
+
* How much applying one pending change hurts.
|
|
27
|
+
*
|
|
28
|
+
* - `in-place` — the provider mutates the existing resource. No new identity,
|
|
29
|
+
* no window where it is absent.
|
|
30
|
+
* - `rolling` — the resource survives, but its workload is replaced
|
|
31
|
+
* incrementally (a Deployment's pod template changing). Disruptive to what
|
|
32
|
+
* is running, not to the resource.
|
|
33
|
+
* - `replace` — a new resource is created and the old one removed. The
|
|
34
|
+
* physical id changes; anything holding the old one has to be updated.
|
|
35
|
+
* - `destroy` — replacement that removes the old resource FIRST. There is a
|
|
36
|
+
* window with nothing there, and whatever the old one held is gone.
|
|
37
|
+
* - `unknown` — nobody could say. The honest value, and the default: it is
|
|
38
|
+
* what a change gets when no lexicon classifies it, and it must never be
|
|
39
|
+
* read as "probably fine".
|
|
40
|
+
*/
|
|
41
|
+
export type Disruption = "in-place" | "rolling" | "replace" | "destroy" | "unknown";
|
|
42
|
+
/** Every level, most disruptive last — also the guard core validates a lexicon's answer against. */
|
|
43
|
+
export declare const DISRUPTION_LEVELS: readonly Disruption[];
|
|
44
|
+
/** One pending change put to a lexicon for classification. */
|
|
45
|
+
export interface DisruptionQuery {
|
|
46
|
+
/** The change set entry's `name` — the key a verdict comes back under. */
|
|
47
|
+
name: string;
|
|
48
|
+
/** Resource type, when the observation reported one. */
|
|
49
|
+
type?: string;
|
|
50
|
+
/** The attribute-level changes the entry carries. */
|
|
51
|
+
deltas: AttributeChange[];
|
|
52
|
+
}
|
|
53
|
+
/** A lexicon's answer for one query. */
|
|
54
|
+
export interface DisruptionVerdict {
|
|
55
|
+
disruption: Disruption;
|
|
56
|
+
/** The attribute paths that forced the verdict — empty or absent when none did. */
|
|
57
|
+
because?: string[];
|
|
58
|
+
/** One line of human-readable backing, naming the spec knowledge behind the call. */
|
|
59
|
+
detail?: string;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* The shape of {@link LexiconPlugin.classifyDisruption}. Keyed by query `name`;
|
|
63
|
+
* a name the lexicon says nothing about degrades to `unknown`, so a partial
|
|
64
|
+
* answer is a valid answer.
|
|
65
|
+
*/
|
|
66
|
+
export type DisruptionClassifier = (options: {
|
|
67
|
+
environment: string;
|
|
68
|
+
changes: DisruptionQuery[];
|
|
69
|
+
}) => Record<string, DisruptionVerdict> | Promise<Record<string, DisruptionVerdict>>;
|
|
70
|
+
/** The verdict every fallback path produces. */
|
|
71
|
+
export declare function unknownDisruption(detail: string): DisruptionVerdict;
|
|
72
|
+
/**
|
|
73
|
+
* Annotate one lexicon's change set with a disruption verdict per `update`.
|
|
74
|
+
*
|
|
75
|
+
* Only `update` entries are asked about: every other action already carries its
|
|
76
|
+
* blast radius in the action itself. Called once per lexicon, before the plan
|
|
77
|
+
* merges the change sets, so `classify` is always the lexicon that produced the
|
|
78
|
+
* entries — the only party that can map its own observation's attribute paths
|
|
79
|
+
* back onto spec properties.
|
|
80
|
+
*
|
|
81
|
+
* Returns a new change set; the input is not mutated.
|
|
82
|
+
*/
|
|
83
|
+
export declare function annotateDisruption(cs: ChangeSet, environment: string, classify?: DisruptionClassifier): Promise<ChangeSet>;
|
|
84
|
+
/** Count `update` entries per level. Entries with no verdict at all are not counted. */
|
|
85
|
+
export declare function summarizeDisruption(cs: ChangeSet): Record<Disruption, number>;
|
|
86
|
+
/** The most disruptive verdict in the set, or undefined when nothing was classified. */
|
|
87
|
+
export declare function worstDisruption(cs: ChangeSet): Disruption | undefined;
|
|
88
|
+
/**
|
|
89
|
+
* Warnings a plan should print on stderr — so a `--json` or `--report gitlab-mr`
|
|
90
|
+
* consumer, whose shape has no column for disruption, still hears about the
|
|
91
|
+
* expensive rows. Same discipline as the unobserved warning (#1089).
|
|
92
|
+
*/
|
|
93
|
+
export declare function disruptionNotices(cs: ChangeSet): string[];
|
|
94
|
+
/** Render one entry's verdict for the human plan, or "" when there is none. */
|
|
95
|
+
export declare function renderDisruption(entry: ChangeSetEntry): string;
|
|
96
|
+
//# sourceMappingURL=disruption.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"disruption.d.ts","sourceRoot":"","sources":["../../src/lifecycle/disruption.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,KAAK,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAE9D;;;;;;;;;;;;;;;GAeG;AACH,MAAM,MAAM,UAAU,GAAG,UAAU,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,CAAC;AAEpF,oGAAoG;AACpG,eAAO,MAAM,iBAAiB,EAAE,SAAS,UAAU,EAMlD,CAAC;AAWF,8DAA8D;AAC9D,MAAM,WAAW,eAAe;IAC9B,0EAA0E;IAC1E,IAAI,EAAE,MAAM,CAAC;IACb,wDAAwD;IACxD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,qDAAqD;IACrD,MAAM,EAAE,eAAe,EAAE,CAAC;CAC3B;AAED,wCAAwC;AACxC,MAAM,WAAW,iBAAiB;IAChC,UAAU,EAAE,UAAU,CAAC;IACvB,mFAAmF;IACnF,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,qFAAqF;IACrF,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;GAIG;AACH,MAAM,MAAM,oBAAoB,GAAG,CAAC,OAAO,EAAE;IAC3C,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,eAAe,EAAE,CAAC;CAC5B,KAAK,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC,CAAC;AAErF,gDAAgD;AAChD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,iBAAiB,CAEnE;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,kBAAkB,CACtC,EAAE,EAAE,SAAS,EACb,WAAW,EAAE,MAAM,EACnB,QAAQ,CAAC,EAAE,oBAAoB,GAC9B,OAAO,CAAC,SAAS,CAAC,CA0CpB;AAiBD,wFAAwF;AACxF,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,SAAS,GAAG,MAAM,CAAC,UAAU,EAAE,MAAM,CAAC,CAY7E;AAED,wFAAwF;AACxF,wBAAgB,eAAe,CAAC,EAAE,EAAE,SAAS,GAAG,UAAU,GAAG,SAAS,CAOrE;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,SAAS,GAAG,MAAM,EAAE,CAkBzD;AAED,+EAA+E;AAC/E,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,cAAc,GAAG,MAAM,CAG9D"}
|
|
@@ -7,6 +7,7 @@ export * from "./deep-diff.js";
|
|
|
7
7
|
export * from "./deep-observe.js";
|
|
8
8
|
export * from "./observation-baseline.js";
|
|
9
9
|
export * from "./change-set.js";
|
|
10
|
+
export * from "./disruption.js";
|
|
10
11
|
export * from "./unobserved-gate.js";
|
|
11
12
|
export * from "./receipt-plan.js";
|
|
12
13
|
export * from "./affected.js";
|
|
@@ -16,6 +17,7 @@ export * from "./build-ledger-store.js";
|
|
|
16
17
|
export * from "./oras-referrer-lookup.js";
|
|
17
18
|
export * from "./status.js";
|
|
18
19
|
export * from "./teardown.js";
|
|
20
|
+
export * from "./assert-live.js";
|
|
19
21
|
export * from "./symptoms.js";
|
|
20
22
|
export * from "./converge-ledger.js";
|
|
21
23
|
export * from "./scenario.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/lifecycle/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC;AACxB,cAAc,OAAO,CAAC;AACtB,cAAc,UAAU,CAAC;AACzB,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,wBAAwB,CAAC;AACvC,cAAc,cAAc,CAAC;AAC7B,cAAc,mBAAmB,CAAC;AAClC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,YAAY,CAAC;AAC3B,cAAc,kBAAkB,CAAC;AACjC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,sBAAsB,CAAC;AACrC,cAAc,wBAAwB,CAAC;AACvC,cAAc,UAAU,CAAC;AACzB,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC;AAC3B,cAAc,mBAAmB,CAAC;AAClC,cAAc,YAAY,CAAC;AAC3B,cAAc,iBAAiB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/lifecycle/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC;AACxB,cAAc,OAAO,CAAC;AACtB,cAAc,UAAU,CAAC;AACzB,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,wBAAwB,CAAC;AACvC,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC;AAC7B,cAAc,mBAAmB,CAAC;AAClC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,YAAY,CAAC;AAC3B,cAAc,kBAAkB,CAAC;AACjC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,sBAAsB,CAAC;AACrC,cAAc,wBAAwB,CAAC;AACvC,cAAc,UAAU,CAAC;AACzB,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,cAAc,YAAY,CAAC;AAC3B,cAAc,mBAAmB,CAAC;AAClC,cAAc,YAAY,CAAC;AAC3B,cAAc,iBAAiB,CAAC"}
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
* answer most questions but never all of them.
|
|
15
15
|
*/
|
|
16
16
|
import type { LiveObservation } from "../graph-ir.js";
|
|
17
|
+
import type { ObservationDepth } from "./types.js";
|
|
17
18
|
/**
|
|
18
19
|
* Rebuild observations from a recorded snapshot (#1266).
|
|
19
20
|
*
|
|
@@ -40,6 +41,7 @@ export declare function replaySnapshots(environment: string, ref: string, scoped
|
|
|
40
41
|
observations: LiveObservation[];
|
|
41
42
|
commit: string;
|
|
42
43
|
timestamp: string;
|
|
44
|
+
depth: ObservationDepth;
|
|
43
45
|
} | {
|
|
44
46
|
error: string;
|
|
45
47
|
hint?: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"replay.d.ts","sourceRoot":"","sources":["../../src/lifecycle/replay.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAIH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"replay.d.ts","sourceRoot":"","sources":["../../src/lifecycle/replay.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAIH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,KAAK,EAAqB,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAEnE;;;;;;;;;;;;GAYG;AACH;;;;;;;GAOG;AACH,wBAAsB,WAAW,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAMvE;AAED,wBAAsB,eAAe,CACnC,WAAW,EAAE,MAAM,EACnB,GAAG,EAAE,MAAM,EACX,YAAY,EAAE,GAAG,CAAC,MAAM,CAAC,GACxB,OAAO,CACN;IAAE,YAAY,EAAE,eAAe,EAAE,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,gBAAgB,CAAA;CAAE,GAC/F;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CACnC,CA8IA"}
|
package/dist/lint/policy.d.ts
CHANGED
|
@@ -25,12 +25,28 @@ export interface PolicyEvaluation {
|
|
|
25
25
|
* of the finding just vanishing.
|
|
26
26
|
*/
|
|
27
27
|
suppressed: PostSynthDiagnostic[];
|
|
28
|
+
/**
|
|
29
|
+
* chant #2002 — divergences between this evaluation and what `chant build`
|
|
30
|
+
* would do, named rather than left silent. Today that is a project whose
|
|
31
|
+
* resolved `build.sandbox` is `true`: the gate builds unsandboxed, because
|
|
32
|
+
* running it inside the boundary needs the per-execution arming decision
|
|
33
|
+
* tracked on #1157. Also written to stderr — the gate's caller is an
|
|
34
|
+
* activity wrapper that surfaces output, not a structured result.
|
|
35
|
+
*/
|
|
36
|
+
warnings: string[];
|
|
28
37
|
}
|
|
29
38
|
/**
|
|
30
39
|
* Build a project and run its `lint.policies` over the resolved resources,
|
|
31
40
|
* standalone — used by the `policyGate` Op step to gate an apply on the same
|
|
32
41
|
* organizational policy `chant build` enforces. Loads the project's lexicons,
|
|
33
42
|
* builds, then runs the policy pack with `env` (explicit, else `ownership.env`).
|
|
43
|
+
*
|
|
44
|
+
* chant #2002 — "the same organizational policy `chant build` enforces" only
|
|
45
|
+
* holds if it is the same build. The build options are assembled by
|
|
46
|
+
* `../cli/build-options.ts`'s `resolveProjectBuildOptions`, the single function
|
|
47
|
+
* `chant build` also calls, rather than by a second list here; a build option
|
|
48
|
+
* added for one caller and not the other is what let a gate pass a build
|
|
49
|
+
* `chant build` fails.
|
|
34
50
|
*/
|
|
35
51
|
export declare function evaluateProjectPolicies(opts: {
|
|
36
52
|
path: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"policy.d.ts","sourceRoot":"","sources":["../../src/lint/policy.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"policy.d.ts","sourceRoot":"","sources":["../../src/lint/policy.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAIxE;;;;;;;;;;;GAWG;AACH,wBAAsB,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC,CAuBpG;AAED,MAAM,WAAW,gBAAgB;IAC/B,yFAAyF;IACzF,WAAW,EAAE,mBAAmB,EAAE,CAAC;IACnC,2FAA2F;IAC3F,UAAU,EAAE,mBAAmB,EAAE,CAAC;IAClC,gEAAgE;IAChE,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;;;OAIG;IACH,UAAU,EAAE,mBAAmB,EAAE,CAAC;IAClC;;;;;;;OAOG;IACH,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,uBAAuB,CAAC,IAAI,EAAE;IAClD,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;CACd,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAwE5B"}
|
|
@@ -10,12 +10,18 @@
|
|
|
10
10
|
* implementations and profiles are passed in (loaded dynamically by the CLI),
|
|
11
11
|
* so core never statically depends on `@intentius/chant-lexicon-temporal`.
|
|
12
12
|
*/
|
|
13
|
-
import type { OpConfig, GateStep } from "./types.js";
|
|
13
|
+
import type { OpConfig, ActivityStep, GateStep } from "./types.js";
|
|
14
14
|
import { type ActivityFn, type ActivityProfile } from "./activity-registry.js";
|
|
15
15
|
export interface StepRecord {
|
|
16
16
|
phase: string;
|
|
17
17
|
fn: string;
|
|
18
|
-
|
|
18
|
+
/**
|
|
19
|
+
* Present for a local-executor record. Absent for one reconstructed from
|
|
20
|
+
* Temporal workflow history (op-progress.ts) — an activity's scheduled
|
|
21
|
+
* input isn't decoded there, so the field is simply omitted rather than
|
|
22
|
+
* populated with a guess.
|
|
23
|
+
*/
|
|
24
|
+
args?: Record<string, unknown>;
|
|
19
25
|
status: "ok" | "fail" | "skipped";
|
|
20
26
|
durationMs: number;
|
|
21
27
|
outcome?: {
|
|
@@ -45,6 +51,20 @@ export declare function parseDuration(s: string): number;
|
|
|
45
51
|
/** Find the first gate step anywhere in the Op (phases + onFailure, including
|
|
46
52
|
* gates nested inside effect steps), if any. */
|
|
47
53
|
export declare function findGate(config: OpConfig): GateStep | undefined;
|
|
54
|
+
/**
|
|
55
|
+
* Find the first `policyGate` step anywhere in the Op (phases + `onFailure`,
|
|
56
|
+
* including steps nested inside effect steps), if any.
|
|
57
|
+
*
|
|
58
|
+
* chant #2003 — `--sandbox` is a GLOBAL flag (`../cli/registry.ts`), and
|
|
59
|
+
* `../cli/main.ts` arms the process-wide policy latch off it for every command,
|
|
60
|
+
* `chant run` included. A `policyGate` step then reaches `loadPolicyChecks`,
|
|
61
|
+
* which refuses while armed with a message written for a chant maintainer —
|
|
62
|
+
* shown to a user who passed a documented flag. The combination cannot be
|
|
63
|
+
* honoured until the gate can build and load policies inside the boundary
|
|
64
|
+
* (#1157), so both `chant run` paths pre-flight it with this and refuse before
|
|
65
|
+
* any phase executes, the same shape as {@link findGate}'s pre-flight.
|
|
66
|
+
*/
|
|
67
|
+
export declare function findPolicyGateStep(config: OpConfig): ActivityStep | undefined;
|
|
48
68
|
/**
|
|
49
69
|
* Execute an Op locally. Resolves with the run result on success; rejects with
|
|
50
70
|
* `OpRunFailure` (carrying the partial result) on terminal failure, after
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"local-executor.d.ts","sourceRoot":"","sources":["../../src/op/local-executor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,QAAQ,
|
|
1
|
+
{"version":3,"file":"local-executor.d.ts","sourceRoot":"","sources":["../../src/op/local-executor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAmB,YAAY,EAAE,QAAQ,EAA8B,MAAM,SAAS,CAAC;AAC7G,OAAO,EAAmB,KAAK,UAAU,EAAE,KAAK,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAM7F,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,EAAE,EAAE,MAAM,CAAC;IACX;;;;;OAKG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,MAAM,EAAE,IAAI,GAAG,MAAM,GAAG,SAAS,CAAC;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,OAAO,CAAA;KAAE,CAAC;IAC3C,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,EAAE,EAAE,OAAO,CAAC;CACb;AAID,kFAAkF;AAClF,qBAAa,yBAA0B,SAAQ,KAAK;aACtB,UAAU,EAAE,MAAM;gBAAlB,UAAU,EAAE,MAAM;CAO/C;AAED,mFAAmF;AACnF,qBAAa,YAAa,SAAQ,KAAK;aACT,MAAM,EAAE,WAAW;gBAAnB,MAAM,EAAE,WAAW;CAIhD;AAmBD,8EAA8E;AAC9E,wBAAgB,aAAa,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAU/C;AA8BD;gDACgD;AAChD,wBAAgB,QAAQ,CAAC,MAAM,EAAE,QAAQ,GAAG,QAAQ,GAAG,SAAS,CAY/D;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,QAAQ,GAAG,YAAY,GAAG,SAAS,CAa7E;AAiSD;;;;;GAKG;AACH,wBAAsB,YAAY,CAChC,MAAM,EAAE,QAAQ,EAChB,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,EACnC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,EACzC,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,WAAW,CAAC,CAgDtB"}
|
package/dist/testing.d.ts
CHANGED
|
@@ -15,6 +15,12 @@
|
|
|
15
15
|
* in-process for exactly this suite's environment. Stateless by design:
|
|
16
16
|
* a crashed suite's environment is recovered by calling destroy again (or
|
|
17
17
|
* `chant lifecycle teardown <env> --yes`).
|
|
18
|
+
* - **assertLive** (#1857) — `describeResources()` against exactly one
|
|
19
|
+
* declared entity, turned into a pass or a thrown failure. Preserves
|
|
20
|
+
* #1089's tri-state rather than collapsing it: NOT-OBSERVED throws {@link
|
|
21
|
+
* UnobservedAssertionError}, never a silent pass and never an ordinary
|
|
22
|
+
* failure, because chant could not read the entity is not the same claim
|
|
23
|
+
* as chant read it and it is gone.
|
|
18
24
|
*
|
|
19
25
|
* ## Isolation
|
|
20
26
|
*
|
|
@@ -39,12 +45,14 @@
|
|
|
39
45
|
* exported, and a CI job pointing the identical suite at a real account just
|
|
40
46
|
* leaves both unset.
|
|
41
47
|
*/
|
|
42
|
-
import type
|
|
48
|
+
import { type Declarable } from "./declarable.js";
|
|
43
49
|
import type { SerializerResult } from "./serializer.js";
|
|
44
50
|
import { type BuildParamValue } from "./build-params.js";
|
|
45
|
-
import type { LexiconPlugin } from "./lexicon.js";
|
|
51
|
+
import type { LexiconPlugin, ResourceMetadata } from "./lexicon.js";
|
|
46
52
|
import { type ActivityFn, type ActivityProfile } from "./op/activity-registry.js";
|
|
47
53
|
import { type TeardownReport } from "./lifecycle/teardown.js";
|
|
54
|
+
import { LiveAssertionError, UnobservedAssertionError, type AssertLiveOptions } from "./lifecycle/assert-live.js";
|
|
55
|
+
export { LiveAssertionError, UnobservedAssertionError, type AssertLiveOptions };
|
|
48
56
|
export interface DeployStackOptions {
|
|
49
57
|
/** The chant project directory (where the infra source lives). */
|
|
50
58
|
dir: string;
|
|
@@ -86,6 +94,19 @@ export interface DeployedStack {
|
|
|
86
94
|
entities: Map<string, Declarable>;
|
|
87
95
|
/** The environment this deploy targeted — the teardown key. */
|
|
88
96
|
env: string;
|
|
97
|
+
/**
|
|
98
|
+
* Assert that a declared entity is live: observed present in this
|
|
99
|
+
* deploy's environment, not confirmed as another stack/env's resource,
|
|
100
|
+
* and — when `status` is given — reporting that status.
|
|
101
|
+
*
|
|
102
|
+
* Rides the observation contract (#1089): an entity `describeResources`
|
|
103
|
+
* could not cover throws {@link UnobservedAssertionError}, never a pass
|
|
104
|
+
* and never a plain failure — NOT-OBSERVED is not the same claim as
|
|
105
|
+
* absent. Observed-absent, a confirmed-foreign identity, or a status
|
|
106
|
+
* mismatch throws {@link LiveAssertionError}. Resolves to the entity's
|
|
107
|
+
* `ResourceMetadata` on success.
|
|
108
|
+
*/
|
|
109
|
+
assertLive(name: string, options?: AssertLiveOptions): Promise<ResourceMetadata>;
|
|
89
110
|
/**
|
|
90
111
|
* Tear down everything carrying this suite's marker `{ stack, env }`.
|
|
91
112
|
* Throws {@link TeardownIncompleteError} when any candidate failed to
|
package/dist/testing.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"testing.d.ts","sourceRoot":"","sources":["../src/testing.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"testing.d.ts","sourceRoot":"","sources":["../src/testing.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8CG;AAMH,OAAO,EAAwB,KAAK,UAAU,EAAE,MAAM,cAAc,CAAC;AACrE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAMrD,OAAO,EAAsB,KAAK,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAI1E,OAAO,KAAK,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAGjE,OAAO,EAAgC,KAAK,UAAU,EAAE,KAAK,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAE7G,OAAO,EAAmB,KAAK,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC5E,OAAO,EAEL,kBAAkB,EAClB,wBAAwB,EACxB,KAAK,iBAAiB,EACvB,MAAM,yBAAyB,CAAC;AAEjC,OAAO,EAAE,kBAAkB,EAAE,wBAAwB,EAAE,KAAK,iBAAiB,EAAE,CAAC;AAgBhF,MAAM,WAAW,kBAAkB;IACjC,kEAAkE;IAClE,GAAG,EAAE,MAAM,CAAC;IACZ;;;;;OAKG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,iFAAiF;IACjF,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;IACzC;;;OAGG;IACH,OAAO,CAAC,EAAE,aAAa,EAAE,CAAC;IAC1B,6EAA6E;IAC7E,UAAU,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IACrC,oFAAoF;IACpF,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;IAC3C;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACvC;AAED,mEAAmE;AACnE,MAAM,WAAW,aAAa;IAC5B,+DAA+D;IAC/D,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,gBAAgB,CAAC,CAAC;IAChD,8CAA8C;IAC9C,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IAClC,+DAA+D;IAC/D,GAAG,EAAE,MAAM,CAAC;IACZ;;;;;;;;;;;OAWG;IACH,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC;IACjF;;;;;;;OAOG;IACH,OAAO,IAAI,OAAO,CAAC,cAAc,CAAC,CAAC;CACpC;AAED;;;;;;GAMG;AACH,qBAAa,uBAAwB,SAAQ,KAAK;aACpB,MAAM,EAAE,cAAc;gBAAtB,MAAM,EAAE,cAAc;CAkBnD;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAUjD;AA8BD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAsB,WAAW,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,aAAa,CAAC,CAmJrF"}
|
package/package.json
CHANGED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* chant #2002 — the shared build-option assembler. `chant build` and the
|
|
3
|
+
* `policyGate` step both call this; `../lint/policy-build-parity.test.ts`
|
|
4
|
+
* proves they stay in step, this file pins what the function itself resolves.
|
|
5
|
+
*/
|
|
6
|
+
import { describe, test, expect } from "vitest";
|
|
7
|
+
import { createMockPlugin } from "@intentius/chant-test-utils";
|
|
8
|
+
import type { LexiconPlugin } from "../lexicon";
|
|
9
|
+
import type { ChantConfig } from "../config";
|
|
10
|
+
import { resolveBuildModes, resolveProjectBuildOptions } from "./build-options";
|
|
11
|
+
|
|
12
|
+
const config = (over: Record<string, unknown> = {}): ChantConfig => over as ChantConfig;
|
|
13
|
+
|
|
14
|
+
describe("resolveBuildModes", () => {
|
|
15
|
+
test("fold defaults on, sandbox defaults off (#1134 / #1045)", () => {
|
|
16
|
+
expect(resolveBuildModes(config())).toEqual({ fold: true, sandbox: false });
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
test("the project config decides when no flag is given", () => {
|
|
20
|
+
expect(resolveBuildModes(config({ build: { fold: false, sandbox: true } })))
|
|
21
|
+
.toEqual({ fold: false, sandbox: true });
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
test("an explicit flag wins over the config, in both directions", () => {
|
|
25
|
+
expect(resolveBuildModes(config({ build: { fold: false } }), { fold: true }).fold).toBe(true);
|
|
26
|
+
expect(resolveBuildModes(config({ build: { fold: true } }), { fold: false }).fold).toBe(false);
|
|
27
|
+
expect(resolveBuildModes(config(), { sandbox: true }).sandbox).toBe(true);
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
describe("resolveProjectBuildOptions", () => {
|
|
32
|
+
const modes = { fold: true, sandbox: false };
|
|
33
|
+
|
|
34
|
+
test("threads the project config through to the serializers", () => {
|
|
35
|
+
const projectConfig = config({ forgejo: { runnerLabels: { "ubuntu-latest": "docker-arm" } } });
|
|
36
|
+
const options = resolveProjectBuildOptions({ config: projectConfig, configDir: "/p", modes });
|
|
37
|
+
expect(options.config).toEqual(projectConfig);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
test("collects intrinsics and lexicon names from the loaded plugins", () => {
|
|
41
|
+
const intrinsic = { tag: "Sub", lexicon: "aws" } as never;
|
|
42
|
+
const plugins: LexiconPlugin[] = [
|
|
43
|
+
{ ...createMockPlugin({ name: "aws" }), intrinsics: () => [intrinsic] },
|
|
44
|
+
createMockPlugin({ name: "k8s" }),
|
|
45
|
+
];
|
|
46
|
+
const options = resolveProjectBuildOptions({ config: config(), configDir: "/p", plugins, modes });
|
|
47
|
+
expect(options.lexicons).toEqual(["aws", "k8s"]);
|
|
48
|
+
expect(options.intrinsics).toEqual([intrinsic]);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
test("binds each plugin's buildRoots hook to the CONFIG dir, not the build path", async () => {
|
|
52
|
+
let seenRoot: string | undefined;
|
|
53
|
+
const plugins: LexiconPlugin[] = [
|
|
54
|
+
{
|
|
55
|
+
...createMockPlugin({ name: "cedar" }),
|
|
56
|
+
buildRoots: async (ctx) => {
|
|
57
|
+
seenRoot = ctx.projectRoot;
|
|
58
|
+
return { entities: new Map() };
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
];
|
|
62
|
+
const options = resolveProjectBuildOptions({ config: config(), configDir: "/project", plugins, modes });
|
|
63
|
+
expect(options.buildRoots).toHaveLength(1);
|
|
64
|
+
await options.buildRoots });
|
|
65
|
+
expect(seenRoot).toBe("/project");
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
test("a plugin without a buildRoots hook contributes nothing", () => {
|
|
69
|
+
const options = resolveProjectBuildOptions({
|
|
70
|
+
config: config(),
|
|
71
|
+
configDir: "/p",
|
|
72
|
+
plugins: [createMockPlugin({ name: "k8s" })],
|
|
73
|
+
modes,
|
|
74
|
+
});
|
|
75
|
+
expect(options.buildRoots).toEqual([]);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
test("no plugins at all is empty, never undefined-by-accident", () => {
|
|
79
|
+
const options = resolveProjectBuildOptions({ config: config(), configDir: "/p", modes });
|
|
80
|
+
expect(options.lexicons).toEqual([]);
|
|
81
|
+
expect(options.intrinsics).toEqual([]);
|
|
82
|
+
expect(options.buildRoots).toEqual([]);
|
|
83
|
+
expect(options.lexiconVersions).toEqual({});
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
test("carries the resolved modes, ownership and build parameters verbatim", () => {
|
|
87
|
+
const ownership = { stack: "s", env: "prod" } as never;
|
|
88
|
+
const buildParams = [{ name: "env", value: "prod", source: "default" }] as never;
|
|
89
|
+
const options = resolveProjectBuildOptions({
|
|
90
|
+
config: config(),
|
|
91
|
+
configDir: "/p",
|
|
92
|
+
modes: { fold: false, sandbox: true },
|
|
93
|
+
ownership,
|
|
94
|
+
buildParams,
|
|
95
|
+
});
|
|
96
|
+
expect(options.fold).toBe(false);
|
|
97
|
+
expect(options.sandbox).toBe(true);
|
|
98
|
+
expect(options.ownership).toBe(ownership);
|
|
99
|
+
expect(options.buildParams).toBe(buildParams);
|
|
100
|
+
});
|
|
101
|
+
});
|