@intentius/chant-lexicon-github 0.51.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.
@@ -26,4 +26,6 @@ export { GoCI } from "./go-ci.js";
26
26
  export type { GoCIProps } from "./go-ci.js";
27
27
  export { Dependabot, DependabotConfig } from "./dependabot.js";
28
28
  export type { DependabotProps, DependabotConfigProps, DependabotUpdate, DependabotEcosystem } from "./dependabot.js";
29
+ export { PrPlanReport } from "./pr-plan-report.js";
30
+ export type { PrPlanReportProps } from "./pr-plan-report.js";
29
31
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/composites/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,YAAY,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAChD,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACzC,YAAY,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AACrC,YAAY,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,YAAY,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AACvD,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AACtC,YAAY,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAChD,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACnD,YAAY,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAC7D,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AACvD,YAAY,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AACjE,OAAO,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AACnC,YAAY,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AACxF,YAAY,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACzD,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,YAAY,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,YAAY,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AACvD,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AACzD,YAAY,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AACnE,OAAO,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAC/B,YAAY,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACzC,OAAO,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAC5D,YAAY,EAAE,eAAe,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/composites/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,YAAY,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAChD,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACzC,YAAY,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AACrC,YAAY,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,YAAY,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AACvD,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AACtC,YAAY,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAChD,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACnD,YAAY,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAC7D,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AACvD,YAAY,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AACjE,OAAO,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AACnC,YAAY,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AACxF,YAAY,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACzD,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,YAAY,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,YAAY,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AACvD,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AACzD,YAAY,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AACnE,OAAO,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAC/B,YAAY,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACzC,OAAO,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAC5D,YAAY,EAAE,eAAe,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAClH,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAChD,YAAY,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC"}
@@ -0,0 +1,64 @@
1
+ import { Job } from "../generated/index.js";
2
+ export interface PrPlanReportProps {
3
+ /**
4
+ * Lifecycle environment to plan against — the `<environment>` argument of
5
+ * `chant lifecycle plan`.
6
+ */
7
+ environment: string;
8
+ /** Restrict the plan to one lexicon (the optional `[lexicon]` argument). */
9
+ lexicon?: string;
10
+ /** Plan against chant-owned resources only (passes `--owned`). */
11
+ ownedOnly?: boolean;
12
+ /** Runner label. Default: `"ubuntu-latest"` */
13
+ runsOn?: string;
14
+ /** Node.js version for chant's own toolchain. Default: `"22"` */
15
+ nodeVersion?: string;
16
+ /** Install command, run after checkout. Default: `"npm ci"` */
17
+ installCommand?: string;
18
+ /**
19
+ * Commands to run before the plan — typically cloud-credential setup, since
20
+ * the plan queries the live system to classify create/update/delete.
21
+ */
22
+ before?: string[];
23
+ /**
24
+ * Post the plan as a sticky PR comment. Default: `true` — the point of this
25
+ * composite is that the compiled diff reaches the reviewer without being
26
+ * asked for, so the manual step it replaces stops being skippable because
27
+ * it stops being manual. Set `false` to run the plan without posting —
28
+ * the explicit opt-out.
29
+ */
30
+ postComment?: boolean;
31
+ /**
32
+ * Hidden marker that makes the comment findable across pushes. Defaults to
33
+ * one keyed on `environment`, so a repo planning two environments gets two
34
+ * comments and each updates in place; override only to share one comment
35
+ * across environments.
36
+ */
37
+ marker?: string;
38
+ /** Per-member defaults for customizing the generated job. */
39
+ defaults?: {
40
+ job?: Partial<ConstructorParameters<typeof Job>[0]>;
41
+ };
42
+ }
43
+ /**
44
+ * A CI job that builds, plans, and posts `chant lifecycle plan --report
45
+ * markdown` as a sticky PR comment (#1983) — the compiled diff reaches the
46
+ * reviewer without being asked for.
47
+ *
48
+ * A sibling of the gitlab lexicon's `MrPlanReport`, but where GitLab renders
49
+ * a native merge-request widget from a counts artifact, GitHub has no such
50
+ * widget: the mechanism is the sticky-comment recipe `examples/github-pr-preview`
51
+ * proved (#1223), reused here rather than reinvented. The forgejo lexicon
52
+ * inherits this composite through its github re-export, since Forgejo Actions
53
+ * runs the same workflow shape and the Forgejo API accepts the same `gh api`
54
+ * calls against its GitHub-compatible surface.
55
+ *
56
+ * Only meaningful on a `pull_request`-triggered workflow — the job guards on
57
+ * `github.event_name` since the comment targets `github.event.number`, which
58
+ * a push-triggered run does not have. The plan reads the live system, so wire
59
+ * cloud credentials via `before` or CI variables.
60
+ */
61
+ export declare const PrPlanReport: import("@intentius/chant").CompositeDefinition<PrPlanReportProps, {
62
+ job: import("@intentius/chant").Declarable & Record<string, string>;
63
+ }>;
64
+ //# sourceMappingURL=pr-plan-report.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pr-plan-report.d.ts","sourceRoot":"","sources":["../../src/composites/pr-plan-report.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,GAAG,EAAqB,MAAM,oBAAoB,CAAC;AAI5D,MAAM,WAAW,iBAAiB;IAChC;;;OAGG;IACH,WAAW,EAAE,MAAM,CAAC;IACpB,4EAA4E;IAC5E,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,kEAAkE;IAClE,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,+CAA+C;IAC/C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,iEAAiE;IACjE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,+DAA+D;IAC/D,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB;;;;;OAKG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,6DAA6D;IAC7D,QAAQ,CAAC,EAAE;QACT,GAAG,CAAC,EAAE,OAAO,CAAC,qBAAqB,CAAC,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;KACrD,CAAC;CACH;AAoBD;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,YAAY;;EAkEP,CAAC"}
package/dist/index.d.ts CHANGED
@@ -3,8 +3,8 @@ export { githubPlugin } from "./plugin.js";
3
3
  export { Expression, github, runner, secrets, matrix, steps, needs, inputs, vars, env, always, failure, success, cancelled, contains, startsWith, toJSON, fromJSON, format, branch, tag, } from "./expression.js";
4
4
  export { GitHub, Runner } from "./variables.js";
5
5
  export * from "./generated/index.js";
6
- export { Checkout, SetupNode, SetupGo, SetupPython, CacheAction, UploadArtifact, DownloadArtifact, NodeCI, NodePipeline, BunPipeline, PnpmPipeline, YarnPipeline, PythonCI, DockerBuild, DeployEnvironment, GoCI, Dependabot, DependabotConfig, } from "./composites/index.js";
7
- export type { CheckoutProps, SetupNodeProps, SetupGoProps, SetupPythonProps, CacheActionProps, UploadArtifactProps, DownloadArtifactProps, NodeCIProps, NodePipelineProps, PythonCIProps, DockerBuildProps, DeployEnvironmentProps, GoCIProps, DependabotProps, DependabotConfigProps, DependabotUpdate, DependabotEcosystem, } from "./composites/index.js";
6
+ export { Checkout, SetupNode, SetupGo, SetupPython, CacheAction, UploadArtifact, DownloadArtifact, NodeCI, NodePipeline, BunPipeline, PnpmPipeline, YarnPipeline, PythonCI, DockerBuild, DeployEnvironment, GoCI, Dependabot, DependabotConfig, PrPlanReport, } from "./composites/index.js";
7
+ export type { CheckoutProps, SetupNodeProps, SetupGoProps, SetupPythonProps, CacheActionProps, UploadArtifactProps, DownloadArtifactProps, NodeCIProps, NodePipelineProps, PythonCIProps, DockerBuildProps, DeployEnvironmentProps, GoCIProps, DependabotProps, DependabotConfigProps, DependabotUpdate, DependabotEcosystem, PrPlanReportProps, } from "./composites/index.js";
8
8
  export { fetchWorkflowSchema, fetchSchemas } from "./spec/fetch.js";
9
9
  export { parseWorkflowSchema, githubShortName, githubServiceName } from "./codegen/parse.js";
10
10
  export type { GitHubParseResult, ParsedResource, ParsedProperty, ParsedPropertyType, ParsedEnum } from "./codegen/parse.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAGhD,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAGxC,OAAO,EACL,UAAU,EACV,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,EAChE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,SAAS,EACnC,QAAQ,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAC9C,MAAM,EAAE,GAAG,GACZ,MAAM,cAAc,CAAC;AAGtB,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAI7C,cAAc,mBAAmB,CAAC;AAGlC,OAAO,EACL,QAAQ,EAAE,SAAS,EAAE,OAAO,EAAE,WAAW,EACzC,WAAW,EAAE,cAAc,EAAE,gBAAgB,EAC7C,MAAM,EACN,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,YAAY,EACrD,QAAQ,EACR,WAAW,EACX,iBAAiB,EACjB,IAAI,EACJ,UAAU,EAAE,gBAAgB,GAC7B,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EACV,aAAa,EAAE,cAAc,EAAE,YAAY,EAAE,gBAAgB,EAC7D,gBAAgB,EAAE,mBAAmB,EAAE,qBAAqB,EAC5D,WAAW,EACX,iBAAiB,EACjB,aAAa,EACb,gBAAgB,EAChB,sBAAsB,EACtB,SAAS,EACT,eAAe,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,mBAAmB,GAC9E,MAAM,oBAAoB,CAAC;AAG5B,OAAO,EAAE,mBAAmB,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AACjE,OAAO,EAAE,mBAAmB,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAC1F,YAAY,EAAE,iBAAiB,EAAE,cAAc,EAAE,cAAc,EAAE,kBAAkB,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAGzH,OAAO,EAAE,QAAQ,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACnE,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACnD,YAAY,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAGhD,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAGxC,OAAO,EACL,UAAU,EACV,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,EAChE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,SAAS,EACnC,QAAQ,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAC9C,MAAM,EAAE,GAAG,GACZ,MAAM,cAAc,CAAC;AAGtB,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAI7C,cAAc,mBAAmB,CAAC;AAGlC,OAAO,EACL,QAAQ,EAAE,SAAS,EAAE,OAAO,EAAE,WAAW,EACzC,WAAW,EAAE,cAAc,EAAE,gBAAgB,EAC7C,MAAM,EACN,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,YAAY,EACrD,QAAQ,EACR,WAAW,EACX,iBAAiB,EACjB,IAAI,EACJ,UAAU,EAAE,gBAAgB,EAC5B,YAAY,GACb,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EACV,aAAa,EAAE,cAAc,EAAE,YAAY,EAAE,gBAAgB,EAC7D,gBAAgB,EAAE,mBAAmB,EAAE,qBAAqB,EAC5D,WAAW,EACX,iBAAiB,EACjB,aAAa,EACb,gBAAgB,EAChB,sBAAsB,EACtB,SAAS,EACT,eAAe,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,mBAAmB,EAC7E,iBAAiB,GAClB,MAAM,oBAAoB,CAAC;AAG5B,OAAO,EAAE,mBAAmB,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AACjE,OAAO,EAAE,mBAAmB,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAC1F,YAAY,EAAE,iBAAiB,EAAE,cAAc,EAAE,cAAc,EAAE,kBAAkB,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAGzH,OAAO,EAAE,QAAQ,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACnE,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACnD,YAAY,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC"}
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "algorithm": "sha256",
3
3
  "artifacts": {
4
- "manifest.json": "0176b6c24d0c74fb0a47f195e3bdff6c352773f0851059bd8d04d0d7ee02e86e",
4
+ "manifest.json": "f34639b85f5e99781f6774eb1855b1988b7da79dbe2f03650e2e1d7af7d64b2c",
5
5
  "meta.json": "e891b1214d530fd676172e3f7e0734995b9f839b7659eb2d53f43ffc97dcd21b",
6
6
  "types/index.d.ts": "7eab3c62c76bff65d68765609e9b6151844a1f581b0c648d0eda779b4252adaf",
7
7
  "rules/deprecated-action-version.ts": "d41e6e532ab7f623af1bee4ac5279fcb2baada7defa1c5d022a5bc71983e8797",
@@ -77,5 +77,5 @@
77
77
  "skills/chant-github-patterns.md": "bb3abef289a8fdfcf07d6bb2d7289dcb2f38bc0cb0321ea320b78b45a6f548c0",
78
78
  "skills/chant-github-security.md": "aab111cb0871cad30281ce48d7da23663689619351029219e2be019a1a61e394"
79
79
  },
80
- "composite": "9076b43c33423ff863fda81cd91ea7c61d17f99a59676c599fa425c5e4ffa2e9"
80
+ "composite": "d29354b7bf24d3c4017d583c09798d87ace0c5d51ad9cc63573cd8ca3e73958c"
81
81
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "github",
3
- "version": "0.51.0",
3
+ "version": "0.52.0",
4
4
  "chantVersion": ">=0.1.0",
5
5
  "namespace": "GitHub",
6
6
  "intrinsics": [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@intentius/chant-lexicon-github",
3
- "version": "0.51.0",
3
+ "version": "0.52.0",
4
4
  "description": "GitHub Actions lexicon for chant — declarative IaC in TypeScript",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://intentius.io/chant",
@@ -61,7 +61,7 @@
61
61
  "typescript": "^5.9.3"
62
62
  },
63
63
  "peerDependencies": {
64
- "@intentius/chant": "^0.51.0",
64
+ "@intentius/chant": "^0.52.0",
65
65
  "typescript": "^5.9.3"
66
66
  }
67
67
  }
@@ -25,7 +25,7 @@ npm install --save-dev @intentius/chant-lexicon-github
25
25
 
26
26
  {{file:docs-snippets/src/quickstart.ts}}
27
27
 
28
- The lexicon provides **3 resources** (Workflow, Job, Dependabot config), **14 composites** (Checkout, SetupNode, SetupGo, SetupPython, CacheAction, UploadArtifact, DownloadArtifact, NodeCI, NodePipeline, PythonCI, DockerBuild, DeployEnvironment, GoCI, Dependabot) + **3 presets** (BunPipeline, PnpmPipeline, YarnPipeline), a typed **Expression** system with 24 GitHub and 5 Runner context variables, and **13 lint rules** + **49 post-synth checks** (including a CI/CD supply-chain security pass, GHA029–062).
28
+ The lexicon provides **3 resources** (Workflow, Job, Dependabot config), **15 composites** (Checkout, SetupNode, SetupGo, SetupPython, CacheAction, UploadArtifact, DownloadArtifact, NodeCI, NodePipeline, PythonCI, DockerBuild, DeployEnvironment, GoCI, Dependabot, PrPlanReport) + **3 presets** (BunPipeline, PnpmPipeline, YarnPipeline), a typed **Expression** system with 24 GitHub and 5 Runner context variables, and **13 lint rules** + **49 post-synth checks** (including a CI/CD supply-chain security pass, GHA029–062).
29
29
  `;
30
30
 
31
31
  const outputFormat = `The GitHub Actions lexicon serializes resources into **\`.github/workflows/*.yml\`** YAML files.
@@ -26,3 +26,5 @@ export { GoCI } from "./go-ci";
26
26
  export type { GoCIProps } from "./go-ci";
27
27
  export { Dependabot, DependabotConfig } from "./dependabot";
28
28
  export type { DependabotProps, DependabotConfigProps, DependabotUpdate, DependabotEcosystem } from "./dependabot";
29
+ export { PrPlanReport } from "./pr-plan-report";
30
+ export type { PrPlanReportProps } from "./pr-plan-report";
@@ -0,0 +1,109 @@
1
+ import { describe, test, expect } from "vitest";
2
+ import type { PostSynthContext } from "@intentius/chant/lint/post-synth";
3
+ import { PrPlanReport } from "./pr-plan-report";
4
+ import { githubPlugin } from "../plugin";
5
+ import { githubSerializer } from "../serializer";
6
+ import { Workflow } from "../generated/index";
7
+ import type { SerializerResult } from "@intentius/chant/serializer";
8
+
9
+ /**
10
+ * `Job`/`Step` (`../generated/index`) come from core's `createResource`/
11
+ * `createProperty` (`packages/core/src/runtime.ts`), whose constructor type
12
+ * is `new (...) => Declarable & Record<string, string>` — every property,
13
+ * `.props` included, reads back as `string` regardless of what was actually
14
+ * passed in. That is a known, tracked gap (chant #1388's shrinking
15
+ * typecheck baseline; `composites.test.ts` in this same directory is
16
+ * grandfathered into it for exactly this reason). This file is new, so
17
+ * rather than add to that backlog, these two interfaces describe the real
18
+ * runtime shape for the fields this file asserts on, and `asJob` narrows the
19
+ * composite's output into it once instead of reading `.props` off the
20
+ * loosely-typed instance directly.
21
+ */
22
+ interface StepLike {
23
+ props: { name?: string; run?: string };
24
+ }
25
+ interface JobLike {
26
+ props: {
27
+ "runs-on": string;
28
+ if?: string;
29
+ permissions?: unknown;
30
+ env?: Record<string, string>;
31
+ steps?: StepLike[];
32
+ };
33
+ }
34
+
35
+ function asJob(job: unknown): JobLike {
36
+ return job as JobLike;
37
+ }
38
+
39
+ function steps(job: unknown): StepLike[] {
40
+ return asJob(job).props.steps ?? [];
41
+ }
42
+
43
+ describe("PrPlanReport composite (#1983)", () => {
44
+ test("defaults: guarded on pull_request, posts by default, marker keyed on environment", () => {
45
+ const { job } = PrPlanReport({ environment: "prod" });
46
+ expect(asJob(job).props["runs-on"]).toBe("ubuntu-latest");
47
+ expect(asJob(job).props.if).toBe("github.event_name == 'pull_request'");
48
+ expect(asJob(job).props.permissions).toMatchObject({ props: { contents: "read", "pull-requests": "write" } });
49
+ expect(asJob(job).props.env?.MARKER).toBe("<!-- chant-pr-plan-report:prod -->");
50
+ const names = steps(job).map((s) => s.props.name);
51
+ expect(names).toContain("Post or update PR comment");
52
+ });
53
+
54
+ test("two environments get two distinct markers", () => {
55
+ const prod = PrPlanReport({ environment: "prod" }).job;
56
+ const staging = PrPlanReport({ environment: "staging" }).job;
57
+ expect(asJob(prod).props.env?.MARKER).not.toBe(asJob(staging).props.env?.MARKER);
58
+ });
59
+
60
+ test("postComment: false is the explicit opt-out — no comment step, plan still runs", () => {
61
+ const { job } = PrPlanReport({ environment: "prod", postComment: false });
62
+ const names = steps(job).map((s) => s.props.name);
63
+ expect(names).not.toContain("Post or update PR comment");
64
+ expect(names.some((n) => n?.startsWith("Plan "))).toBe(true);
65
+ });
66
+
67
+ test("lexicon and --owned reach the plan command", () => {
68
+ const { job } = PrPlanReport({ environment: "prod", lexicon: "aws", ownedOnly: true });
69
+ const planStep = steps(job).find((s) => s.props.name === "Plan prod")!;
70
+ expect(planStep.props.run).toContain("lifecycle plan prod aws --owned --report markdown");
71
+ });
72
+
73
+ test("before commands become their own credential-setup steps, in order, ahead of the plan", () => {
74
+ const { job } = PrPlanReport({ environment: "prod", before: ["aws sts get-caller-identity", "echo ready"] });
75
+ const names = steps(job).map((s) => s.props.name);
76
+ const planIndex = names.indexOf("Plan prod");
77
+ const credIndexes = names.reduce<number[]>((acc, n, i) => (n === "Live credentials" ? [...acc, i] : acc), []);
78
+ expect(credIndexes).toHaveLength(2);
79
+ expect(Math.max(...credIndexes)).toBeLessThan(planIndex);
80
+ });
81
+
82
+ test("the emitted workflow passes the github lexicon's own lint — no errors, pinned actions included", () => {
83
+ const { job } = PrPlanReport({ environment: "prod", before: ["aws sts get-caller-identity"] });
84
+ const workflow = new Workflow({
85
+ name: "pr-plan",
86
+ on: { pull_request: { types: ["opened", "synchronize", "reopened"] } },
87
+ });
88
+ const result = githubSerializer.serialize(
89
+ new Map<string, unknown>([["workflow", workflow], ["plan", job]]) as never,
90
+ ) as SerializerResult;
91
+ const yaml = typeof result === "string" ? result : result.primary!;
92
+ expect(yaml).toContain("Post or update PR comment");
93
+
94
+ const ctx: PostSynthContext = {
95
+ outputs: new Map([["github", yaml]]),
96
+ entities: new Map(),
97
+ buildResult: {
98
+ outputs: new Map([["github", yaml]]),
99
+ entities: new Map(),
100
+ warnings: [],
101
+ errors: [],
102
+ sourceFileCount: 1,
103
+ },
104
+ };
105
+ const diagnostics = githubPlugin.postSynthChecks!().flatMap((check) => check.check(ctx));
106
+ const errors = diagnostics.filter((d) => d.severity === "error");
107
+ expect(errors).toEqual([]);
108
+ });
109
+ });
@@ -0,0 +1,150 @@
1
+ import { Composite, mergeDefaults } from "@intentius/chant";
2
+ import { Job, Step, Permissions } from "../generated/index";
3
+ import { Checkout } from "./checkout";
4
+ import { SetupNode } from "./setup-node";
5
+
6
+ export interface PrPlanReportProps {
7
+ /**
8
+ * Lifecycle environment to plan against — the `<environment>` argument of
9
+ * `chant lifecycle plan`.
10
+ */
11
+ environment: string;
12
+ /** Restrict the plan to one lexicon (the optional `[lexicon]` argument). */
13
+ lexicon?: string;
14
+ /** Plan against chant-owned resources only (passes `--owned`). */
15
+ ownedOnly?: boolean;
16
+ /** Runner label. Default: `"ubuntu-latest"` */
17
+ runsOn?: string;
18
+ /** Node.js version for chant's own toolchain. Default: `"22"` */
19
+ nodeVersion?: string;
20
+ /** Install command, run after checkout. Default: `"npm ci"` */
21
+ installCommand?: string;
22
+ /**
23
+ * Commands to run before the plan — typically cloud-credential setup, since
24
+ * the plan queries the live system to classify create/update/delete.
25
+ */
26
+ before?: string[];
27
+ /**
28
+ * Post the plan as a sticky PR comment. Default: `true` — the point of this
29
+ * composite is that the compiled diff reaches the reviewer without being
30
+ * asked for, so the manual step it replaces stops being skippable because
31
+ * it stops being manual. Set `false` to run the plan without posting —
32
+ * the explicit opt-out.
33
+ */
34
+ postComment?: boolean;
35
+ /**
36
+ * Hidden marker that makes the comment findable across pushes. Defaults to
37
+ * one keyed on `environment`, so a repo planning two environments gets two
38
+ * comments and each updates in place; override only to share one comment
39
+ * across environments.
40
+ */
41
+ marker?: string;
42
+ /** Per-member defaults for customizing the generated job. */
43
+ defaults?: {
44
+ job?: Partial<ConstructorParameters<typeof Job>[0]>;
45
+ };
46
+ }
47
+
48
+ /**
49
+ * Sticky-comment script (#1223's mechanism, reused as-is): find the comment
50
+ * whose body starts with `$MARKER`, PATCH it if found, POST otherwise. No
51
+ * marketplace action, nothing extra to pin — `gh` ships on GitHub's hosted
52
+ * runners. `-f body=@plan.md` reads the comment body from the file the plan
53
+ * step wrote, so a large or multi-line plan never has to survive shell
54
+ * quoting.
55
+ */
56
+ const stickyCommentScript = [
57
+ 'comment_id=$(gh api "repos/$REPO/issues/$PR_NUMBER/comments" --paginate ' +
58
+ '--jq "map(select(.body | startswith(\\"$MARKER\\"))) | .[0].id // empty")',
59
+ 'if [ -n "$comment_id" ]; then',
60
+ ' gh api -X PATCH "repos/$REPO/issues/comments/$comment_id" -f body=@plan.md > /dev/null',
61
+ "else",
62
+ ' gh api -X POST "repos/$REPO/issues/$PR_NUMBER/comments" -f body=@plan.md > /dev/null',
63
+ "fi",
64
+ ].join("\n");
65
+
66
+ /**
67
+ * A CI job that builds, plans, and posts `chant lifecycle plan --report
68
+ * markdown` as a sticky PR comment (#1983) — the compiled diff reaches the
69
+ * reviewer without being asked for.
70
+ *
71
+ * A sibling of the gitlab lexicon's `MrPlanReport`, but where GitLab renders
72
+ * a native merge-request widget from a counts artifact, GitHub has no such
73
+ * widget: the mechanism is the sticky-comment recipe `examples/github-pr-preview`
74
+ * proved (#1223), reused here rather than reinvented. The forgejo lexicon
75
+ * inherits this composite through its github re-export, since Forgejo Actions
76
+ * runs the same workflow shape and the Forgejo API accepts the same `gh api`
77
+ * calls against its GitHub-compatible surface.
78
+ *
79
+ * Only meaningful on a `pull_request`-triggered workflow — the job guards on
80
+ * `github.event_name` since the comment targets `github.event.number`, which
81
+ * a push-triggered run does not have. The plan reads the live system, so wire
82
+ * cloud credentials via `before` or CI variables.
83
+ */
84
+ export const PrPlanReport = Composite((props: PrPlanReportProps) => {
85
+ const {
86
+ environment,
87
+ lexicon,
88
+ ownedOnly = false,
89
+ runsOn = "ubuntu-latest",
90
+ nodeVersion = "22",
91
+ installCommand = "npm ci",
92
+ before,
93
+ postComment = true,
94
+ marker = `<!-- chant-pr-plan-report:${environment} -->`,
95
+ defaults: defs,
96
+ } = props;
97
+
98
+ const planArgs = [
99
+ "lifecycle",
100
+ "plan",
101
+ environment,
102
+ ...(lexicon ? [lexicon] : []),
103
+ ...(ownedOnly ? ["--owned"] : []),
104
+ "--report",
105
+ "markdown",
106
+ ].join(" ");
107
+
108
+ const steps = [
109
+ Checkout({}).step,
110
+ SetupNode({ nodeVersion, cache: "npm" }).step,
111
+ new Step({ name: "Install", run: installCommand }),
112
+ ...(before ?? []).map((cmd) => new Step({ name: "Live credentials", run: cmd })),
113
+ new Step({ name: "Build", run: "npx chant build" }),
114
+ new Step({
115
+ name: `Plan ${environment}`,
116
+ // The marker (and an "updated for" footer, same spirit as the preview
117
+ // tutorial's) is prepended to the rendered plan before anything is
118
+ // posted, so the comment body itself carries the identity the search
119
+ // step below keys on.
120
+ run: [
121
+ `npx chant ${planArgs} > plan-body.md`,
122
+ '{ printf \'%s\\n\\n\' "$MARKER"; printf \'_Updated for %s._\\n\\n\' "$HEAD_SHA"; cat plan-body.md; } > plan.md',
123
+ ].join("\n"),
124
+ }),
125
+ ...(postComment
126
+ ? [
127
+ new Step({
128
+ name: "Post or update PR comment",
129
+ env: { GH_TOKEN: "${{ secrets.GITHUB_TOKEN }}" },
130
+ run: stickyCommentScript,
131
+ }),
132
+ ]
133
+ : []),
134
+ ];
135
+
136
+ const job = new Job(mergeDefaults({
137
+ "runs-on": runsOn,
138
+ if: "github.event_name == 'pull_request'",
139
+ permissions: new Permissions({ contents: "read", "pull-requests": "write" }),
140
+ env: {
141
+ MARKER: marker,
142
+ HEAD_SHA: "${{ github.event.pull_request.head.sha }}",
143
+ PR_NUMBER: "${{ github.event.number }}",
144
+ REPO: "${{ github.repository }}",
145
+ },
146
+ steps,
147
+ }, defs?.job));
148
+
149
+ return { job };
150
+ }, "PrPlanReport");
package/src/index.ts CHANGED
@@ -31,6 +31,7 @@ export {
31
31
  DeployEnvironment,
32
32
  GoCI,
33
33
  Dependabot, DependabotConfig,
34
+ PrPlanReport,
34
35
  } from "./composites/index";
35
36
  export type {
36
37
  CheckoutProps, SetupNodeProps, SetupGoProps, SetupPythonProps,
@@ -42,6 +43,7 @@ export type {
42
43
  DeployEnvironmentProps,
43
44
  GoCIProps,
44
45
  DependabotProps, DependabotConfigProps, DependabotUpdate, DependabotEcosystem,
46
+ PrPlanReportProps,
45
47
  } from "./composites/index";
46
48
 
47
49
  // Spec utilities (for tooling)