@intentius/chant-lexicon-github 0.124.31 → 0.124.32

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.
Files changed (151) hide show
  1. package/dist/action-pins.js +53 -0
  2. package/dist/codegen/docs-cli.js +2 -0
  3. package/dist/codegen/docs.js +142 -0
  4. package/dist/codegen/generate-cli.js +26 -0
  5. package/dist/codegen/generate-lexicon.js +36 -0
  6. package/dist/codegen/generate-typescript.js +108 -0
  7. package/dist/codegen/generate.js +111 -0
  8. package/dist/codegen/naming.js +49 -0
  9. package/dist/codegen/package.js +40 -0
  10. package/dist/codegen/parse.js +595 -0
  11. package/dist/codegen/patches.js +36 -0
  12. package/dist/components/ci-jobs.js +84 -0
  13. package/dist/components/generate-op-pipeline.js +440 -0
  14. package/dist/components/generate-op-waves-pipeline.js +158 -0
  15. package/dist/components/generate-pipeline.d.ts.map +1 -1
  16. package/dist/components/generate-pipeline.js +476 -0
  17. package/dist/composites/cache.js +17 -0
  18. package/dist/composites/catalog.js +859 -0
  19. package/dist/composites/checkout.js +25 -0
  20. package/dist/composites/dependabot.js +43 -0
  21. package/dist/composites/deploy-environment.js +38 -0
  22. package/dist/composites/docker-build.js +70 -0
  23. package/dist/composites/download-artifact.js +18 -0
  24. package/dist/composites/go-ci.js +62 -0
  25. package/dist/composites/index.js +15 -0
  26. package/dist/composites/node-ci.js +44 -0
  27. package/dist/composites/node-pipeline.js +96 -0
  28. package/dist/composites/pr-plan-report.js +129 -0
  29. package/dist/composites/python-ci.js +60 -0
  30. package/dist/composites/setup-go.js +18 -0
  31. package/dist/composites/setup-node.js +21 -0
  32. package/dist/composites/setup-python.js +18 -0
  33. package/dist/composites/upload-artifact.js +19 -0
  34. package/dist/coverage.js +32 -0
  35. package/dist/expression.js +177 -0
  36. package/dist/generated/index.js +26 -0
  37. package/dist/generated/runtime.js +4 -0
  38. package/dist/graph-edges.js +174 -0
  39. package/dist/import/generator.js +108 -0
  40. package/dist/import/parser.js +69 -0
  41. package/dist/index.js +22 -0
  42. package/dist/integrity.json +2 -2
  43. package/dist/lint/audit-catalog.js +69 -0
  44. package/dist/lint/audit-lineage.js +168 -0
  45. package/dist/lint/post-synth/gha006.js +50 -0
  46. package/dist/lint/post-synth/gha009.js +35 -0
  47. package/dist/lint/post-synth/gha011.js +34 -0
  48. package/dist/lint/post-synth/gha013.js +40 -0
  49. package/dist/lint/post-synth/gha017.js +25 -0
  50. package/dist/lint/post-synth/gha018.js +33 -0
  51. package/dist/lint/post-synth/gha019.js +58 -0
  52. package/dist/lint/post-synth/gha021.js +42 -0
  53. package/dist/lint/post-synth/gha022.js +44 -0
  54. package/dist/lint/post-synth/gha023.js +37 -0
  55. package/dist/lint/post-synth/gha024.js +32 -0
  56. package/dist/lint/post-synth/gha025.js +33 -0
  57. package/dist/lint/post-synth/gha026.js +32 -0
  58. package/dist/lint/post-synth/gha027.js +46 -0
  59. package/dist/lint/post-synth/gha028.js +28 -0
  60. package/dist/lint/post-synth/gha029.js +56 -0
  61. package/dist/lint/post-synth/gha030.js +35 -0
  62. package/dist/lint/post-synth/gha031.js +75 -0
  63. package/dist/lint/post-synth/gha032.js +35 -0
  64. package/dist/lint/post-synth/gha033.js +39 -0
  65. package/dist/lint/post-synth/gha034.js +35 -0
  66. package/dist/lint/post-synth/gha035.js +50 -0
  67. package/dist/lint/post-synth/gha036.js +37 -0
  68. package/dist/lint/post-synth/gha037.js +41 -0
  69. package/dist/lint/post-synth/gha038.js +35 -0
  70. package/dist/lint/post-synth/gha039.js +34 -0
  71. package/dist/lint/post-synth/gha040.js +36 -0
  72. package/dist/lint/post-synth/gha041.js +32 -0
  73. package/dist/lint/post-synth/gha042.js +33 -0
  74. package/dist/lint/post-synth/gha043.js +35 -0
  75. package/dist/lint/post-synth/gha044.js +36 -0
  76. package/dist/lint/post-synth/gha045.js +33 -0
  77. package/dist/lint/post-synth/gha046.js +55 -0
  78. package/dist/lint/post-synth/gha047.js +44 -0
  79. package/dist/lint/post-synth/gha048.js +34 -0
  80. package/dist/lint/post-synth/gha049.js +36 -0
  81. package/dist/lint/post-synth/gha050.js +37 -0
  82. package/dist/lint/post-synth/gha051.js +38 -0
  83. package/dist/lint/post-synth/gha052.js +35 -0
  84. package/dist/lint/post-synth/gha053.js +34 -0
  85. package/dist/lint/post-synth/gha054.js +35 -0
  86. package/dist/lint/post-synth/gha055.js +44 -0
  87. package/dist/lint/post-synth/gha056.js +32 -0
  88. package/dist/lint/post-synth/gha057.js +35 -0
  89. package/dist/lint/post-synth/gha058.js +42 -0
  90. package/dist/lint/post-synth/gha059.js +119 -0
  91. package/dist/lint/post-synth/gha060.js +129 -0
  92. package/dist/lint/post-synth/gha061.js +78 -0
  93. package/dist/lint/post-synth/gha062.js +65 -0
  94. package/dist/lint/post-synth/gha063.js +56 -0
  95. package/dist/lint/post-synth/gha064.js +47 -0
  96. package/dist/lint/post-synth/gha065.js +125 -0
  97. package/dist/lint/post-synth/gha066.js +40 -0
  98. package/dist/lint/post-synth/gha067.js +75 -0
  99. package/dist/lint/post-synth/gha068.js +47 -0
  100. package/dist/lint/post-synth/gha069.js +99 -0
  101. package/dist/lint/post-synth/index.js +114 -0
  102. package/dist/lint/post-synth/yaml-helpers.js +465 -0
  103. package/dist/lint/rules/data/action-usage-policy.js +16 -0
  104. package/dist/lint/rules/data/advisory-feed.js +15 -0
  105. package/dist/lint/rules/data/deprecated-versions.js +14 -0
  106. package/dist/lint/rules/data/flagged-actions.js +18 -0
  107. package/dist/lint/rules/data/known-action-slugs.js +38 -0
  108. package/dist/lint/rules/data/known-actions.js +12 -0
  109. package/dist/lint/rules/data/preinstalled-tools.js +36 -0
  110. package/dist/lint/rules/data/recommended-inputs.js +9 -0
  111. package/dist/lint/rules/data/risky-features.js +22 -0
  112. package/dist/lint/rules/data/secret-patterns.js +30 -0
  113. package/dist/lint/rules/data/trusted-action-owners.js +12 -0
  114. package/dist/lint/rules/data/untrusted-contexts.js +44 -0
  115. package/dist/lint/rules/deprecated-action-version.js +44 -0
  116. package/dist/lint/rules/detect-secrets.js +45 -0
  117. package/dist/lint/rules/extract-inline-structs.js +55 -0
  118. package/dist/lint/rules/file-job-limit.js +43 -0
  119. package/dist/lint/rules/index.js +16 -0
  120. package/dist/lint/rules/job-timeout.js +53 -0
  121. package/dist/lint/rules/missing-recommended-inputs.js +51 -0
  122. package/dist/lint/rules/no-hardcoded-secrets.js +39 -0
  123. package/dist/lint/rules/no-raw-expressions.js +44 -0
  124. package/dist/lint/rules/suggest-cache.js +60 -0
  125. package/dist/lint/rules/use-condition-builders.js +37 -0
  126. package/dist/lint/rules/use-matrix-builder.js +36 -0
  127. package/dist/lint/rules/use-typed-actions.js +41 -0
  128. package/dist/lint/rules/validate-concurrency.js +59 -0
  129. package/dist/lsp/completions.d.ts.map +1 -1
  130. package/dist/lsp/completions.js +18 -0
  131. package/dist/lsp/hover.d.ts.map +1 -1
  132. package/dist/lsp/hover.js +33 -0
  133. package/dist/manifest.json +1 -1
  134. package/dist/mcp/context-tools.js +152 -0
  135. package/dist/package-cli.js +20 -0
  136. package/dist/plugin.js +344 -0
  137. package/dist/serializer.js +572 -0
  138. package/dist/spec/fetch.js +46 -0
  139. package/dist/validate-cli.js +16 -0
  140. package/dist/validate.js +38 -0
  141. package/dist/variables.js +41 -0
  142. package/package.json +6 -6
  143. package/src/codegen/package.ts +2 -2
  144. package/src/components/__fixtures__/pr-loop-build.github.golden.yml +6 -2
  145. package/src/components/__fixtures__/pr-loop-waves.github.golden.yml +178 -0
  146. package/src/components/__fixtures__/pr-loop.github.golden.yml +6 -2
  147. package/src/components/generate-pipeline.ts +66 -29
  148. package/src/components/generate-pr-pipeline.test.ts +44 -20
  149. package/src/components/generate-pr-waves-pipeline.test.ts +117 -0
  150. package/src/lsp/completions.ts +2 -1
  151. package/src/lsp/hover.ts +2 -1
@@ -0,0 +1,18 @@
1
+ import { Composite, mergeDefaults } from "@intentius/chant";
2
+ import { Step } from "../generated/index.js";
3
+ export const SetupPython = Composite((props) => {
4
+ const { defaults } = props;
5
+ const withObj = {};
6
+ if (props.pythonVersion !== undefined)
7
+ withObj["python-version"] = props.pythonVersion;
8
+ if (props.cache !== undefined)
9
+ withObj.cache = props.cache;
10
+ if (props.architecture !== undefined)
11
+ withObj.architecture = props.architecture;
12
+ const step = new Step(mergeDefaults({
13
+ name: "Setup Python",
14
+ uses: "actions/setup-python@v5",
15
+ ...(Object.keys(withObj).length > 0 ? { with: withObj } : {}),
16
+ }, defaults?.step));
17
+ return { step };
18
+ }, "SetupPython");
@@ -0,0 +1,19 @@
1
+ import { Composite, mergeDefaults } from "@intentius/chant";
2
+ import { Step } from "../generated/index.js";
3
+ export const UploadArtifact = Composite((props) => {
4
+ const { defaults } = props;
5
+ const withObj = {
6
+ name: props.name,
7
+ path: props.path,
8
+ };
9
+ if (props.retentionDays !== undefined)
10
+ withObj["retention-days"] = String(props.retentionDays);
11
+ if (props.compressionLevel !== undefined)
12
+ withObj["compression-level"] = String(props.compressionLevel);
13
+ const step = new Step(mergeDefaults({
14
+ name: "Upload Artifact",
15
+ uses: "actions/upload-artifact@v4",
16
+ with: withObj,
17
+ }, defaults?.step));
18
+ return { step };
19
+ }, "UploadArtifact");
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Coverage analysis for GitHub Actions lexicon.
3
+ */
4
+ import { readFileSync } from "fs";
5
+ import { join, dirname } from "path";
6
+ import { fileURLToPath } from "url";
7
+ import { computeCoverage, overallPct, formatSummary, formatVerbose, checkThresholds, } from "@intentius/chant/codegen/coverage";
8
+ export { computeCoverage, overallPct, formatSummary, formatVerbose, checkThresholds };
9
+ /**
10
+ * Run coverage analysis for the GitHub Actions lexicon.
11
+ */
12
+ export async function analyzeGitHubCoverage(opts) {
13
+ const basePath = opts?.basePath ?? dirname(dirname(fileURLToPath(import.meta.url)));
14
+ const lexiconPath = join(basePath, "src", "generated", "lexicon-github.json");
15
+ const content = readFileSync(lexiconPath, "utf-8");
16
+ const report = computeCoverage(content);
17
+ if (opts?.verbose) {
18
+ console.error(formatVerbose(report));
19
+ }
20
+ else {
21
+ console.error(formatSummary(report));
22
+ }
23
+ if (typeof opts?.minOverall === "number") {
24
+ const result = checkThresholds(report, { minOverallPct: opts.minOverall });
25
+ if (!result.ok) {
26
+ for (const v of result.violations)
27
+ console.error(` FAIL: ${v}`);
28
+ throw new Error("Coverage below threshold");
29
+ }
30
+ }
31
+ return report;
32
+ }
@@ -0,0 +1,177 @@
1
+ /**
2
+ * GitHub Actions expression system.
3
+ *
4
+ * Provides a typed Expression class and context accessors for building
5
+ * ${{ }} expressions in GitHub Actions workflows.
6
+ */
7
+ import { INTRINSIC_MARKER } from "@intentius/chant/intrinsic";
8
+ /**
9
+ * A GitHub Actions expression that serializes to `${{ raw }}`.
10
+ */
11
+ export class Expression {
12
+ constructor(raw) {
13
+ this[INTRINSIC_MARKER] = true;
14
+ this._raw = raw;
15
+ }
16
+ /** The raw expression string without ${{ }} wrapper. */
17
+ raw() {
18
+ return this._raw;
19
+ }
20
+ /** Returns the expression wrapped in ${{ }}. */
21
+ toString() {
22
+ return `\${{ ${this._raw} }}`;
23
+ }
24
+ toJSON() {
25
+ return this.toString();
26
+ }
27
+ /** YAML serialization — plain string with ${{ }}. */
28
+ toYAML() {
29
+ return this.toString();
30
+ }
31
+ /** Logical AND with another expression. */
32
+ and(other) {
33
+ return new Expression(`${this._raw} && ${other._raw}`);
34
+ }
35
+ /** Logical OR with another expression. */
36
+ or(other) {
37
+ return new Expression(`${this._raw} || ${other._raw}`);
38
+ }
39
+ /** Logical NOT of this expression. */
40
+ not() {
41
+ return new Expression(`!(${this._raw})`);
42
+ }
43
+ /** Equality comparison. */
44
+ eq(value) {
45
+ const rhs = value instanceof Expression ? value._raw : `'${value}'`;
46
+ return new Expression(`${this._raw} == ${rhs}`);
47
+ }
48
+ /** Inequality comparison. */
49
+ ne(value) {
50
+ const rhs = value instanceof Expression ? value._raw : `'${value}'`;
51
+ return new Expression(`${this._raw} != ${rhs}`);
52
+ }
53
+ }
54
+ // ── Context accessors ─────────────────────────────────────────────
55
+ /** GitHub context — github.* properties. */
56
+ export const github = {
57
+ get ref() { return new Expression("github.ref"); },
58
+ get sha() { return new Expression("github.sha"); },
59
+ get actor() { return new Expression("github.actor"); },
60
+ get repository() { return new Expression("github.repository"); },
61
+ get repositoryOwner() { return new Expression("github.repository_owner"); },
62
+ get event() { return new Expression("github.event"); },
63
+ get eventName() { return new Expression("github.event_name"); },
64
+ get runId() { return new Expression("github.run_id"); },
65
+ get runNumber() { return new Expression("github.run_number"); },
66
+ get workflow() { return new Expression("github.workflow"); },
67
+ get workspace() { return new Expression("github.workspace"); },
68
+ get token() { return new Expression("github.token"); },
69
+ get job() { return new Expression("github.job"); },
70
+ get refName() { return new Expression("github.ref_name"); },
71
+ get refType() { return new Expression("github.ref_type"); },
72
+ get headRef() { return new Expression("github.head_ref"); },
73
+ get baseRef() { return new Expression("github.base_ref"); },
74
+ get serverUrl() { return new Expression("github.server_url"); },
75
+ get apiUrl() { return new Expression("github.api_url"); },
76
+ get graphqlUrl() { return new Expression("github.graphql_url"); },
77
+ get action() { return new Expression("github.action"); },
78
+ get actionPath() { return new Expression("github.action_path"); },
79
+ get triggeringActor() { return new Expression("github.triggering_actor"); },
80
+ };
81
+ /** Runner context — runner.* properties. */
82
+ export const runner = {
83
+ get os() { return new Expression("runner.os"); },
84
+ get arch() { return new Expression("runner.arch"); },
85
+ get temp() { return new Expression("runner.temp"); },
86
+ get toolCache() { return new Expression("runner.tool_cache"); },
87
+ get name() { return new Expression("runner.name"); },
88
+ };
89
+ /** Access a secret by name. */
90
+ export function secrets(name) {
91
+ return new Expression(`secrets.${name}`);
92
+ }
93
+ /** Access a matrix value by key. */
94
+ export function matrix(key) {
95
+ return new Expression(`matrix.${key}`);
96
+ }
97
+ /** Access a step output. */
98
+ export function steps(id) {
99
+ return {
100
+ outputs(name) {
101
+ return new Expression(`steps.${id}.outputs.${name}`);
102
+ },
103
+ };
104
+ }
105
+ /** Access a job output from a needed job. */
106
+ export function needs(job) {
107
+ return {
108
+ outputs(name) {
109
+ return new Expression(`needs.${job}.outputs.${name}`);
110
+ },
111
+ };
112
+ }
113
+ /** Access a workflow input. */
114
+ export function inputs(name) {
115
+ return new Expression(`inputs.${name}`);
116
+ }
117
+ /** Access a configuration variable. */
118
+ export function vars(name) {
119
+ return new Expression(`vars.${name}`);
120
+ }
121
+ /** Access an environment variable. */
122
+ export function env(name) {
123
+ return new Expression(`env.${name}`);
124
+ }
125
+ // ── Condition helpers ─────────────────────────────────────────────
126
+ /** Always run, regardless of status. */
127
+ export function always() {
128
+ return new Expression("always()");
129
+ }
130
+ /** Run only if a previous step has failed. */
131
+ export function failure() {
132
+ return new Expression("failure()");
133
+ }
134
+ /** Run only if all previous steps succeeded (default). */
135
+ export function success() {
136
+ return new Expression("success()");
137
+ }
138
+ /** Run only if the workflow was cancelled. */
139
+ export function cancelled() {
140
+ return new Expression("cancelled()");
141
+ }
142
+ // ── Function helpers ──────────────────────────────────────────────
143
+ /** Check if a string contains a substring. */
144
+ export function contains(haystack, needle) {
145
+ const h = haystack instanceof Expression ? haystack.raw() : `'${haystack}'`;
146
+ const n = needle instanceof Expression ? needle.raw() : `'${needle}'`;
147
+ return new Expression(`contains(${h}, ${n})`);
148
+ }
149
+ /** Check if a string starts with a prefix. */
150
+ export function startsWith(value, prefix) {
151
+ const v = value instanceof Expression ? value.raw() : `'${value}'`;
152
+ const p = prefix instanceof Expression ? prefix.raw() : `'${prefix}'`;
153
+ return new Expression(`startsWith(${v}, ${p})`);
154
+ }
155
+ /** Convert a value to JSON. */
156
+ export function toJSON(value) {
157
+ return new Expression(`toJSON(${value.raw()})`);
158
+ }
159
+ /** Parse a JSON string. */
160
+ export function fromJSON(json) {
161
+ const j = json instanceof Expression ? json.raw() : `'${json}'`;
162
+ return new Expression(`fromJSON(${j})`);
163
+ }
164
+ /** Format a string with placeholders. */
165
+ export function format(template, ...args) {
166
+ const argStrs = args.map((a) => a.raw()).join(", ");
167
+ return new Expression(`format('${template}', ${argStrs})`);
168
+ }
169
+ // ── Convenience helpers ───────────────────────────────────────────
170
+ /** Check if the ref matches a branch name. */
171
+ export function branch(name) {
172
+ return github.ref.eq(`refs/heads/${name}`);
173
+ }
174
+ /** Check if the ref matches a tag prefix. */
175
+ export function tag(name) {
176
+ return startsWith(github.ref, `refs/tags/${name}`);
177
+ }
@@ -0,0 +1,26 @@
1
+ // Code generated by chant generate. DO NOT EDIT.
2
+ import { createResource, createProperty } from "./runtime.js";
3
+ export const Job = /* @__PURE__ */ createResource("GitHub::Actions::Job", "github", {});
4
+ export const ReusableWorkflowCallJob = /* @__PURE__ */ createResource("GitHub::Actions::ReusableWorkflowCallJob", "github", {});
5
+ export const Workflow = /* @__PURE__ */ createResource("GitHub::Actions::Workflow", "github", {});
6
+ export const Concurrency = /* @__PURE__ */ createProperty("GitHub::Actions::Concurrency", "github");
7
+ export const Container = /* @__PURE__ */ createProperty("GitHub::Actions::Container", "github");
8
+ export const Defaults = /* @__PURE__ */ createProperty("GitHub::Actions::Defaults", "github");
9
+ export const Environment = /* @__PURE__ */ createProperty("GitHub::Actions::Environment", "github");
10
+ export const Permissions = /* @__PURE__ */ createProperty("GitHub::Actions::Permissions", "github");
11
+ export const PullRequestTargetTrigger = /* @__PURE__ */ createProperty("GitHub::Actions::PullRequestTargetTrigger", "github");
12
+ export const PullRequestTrigger = /* @__PURE__ */ createProperty("GitHub::Actions::PullRequestTrigger", "github");
13
+ export const PushTrigger = /* @__PURE__ */ createProperty("GitHub::Actions::PushTrigger", "github");
14
+ export const RepositoryDispatchTrigger = /* @__PURE__ */ createProperty("GitHub::Actions::RepositoryDispatchTrigger", "github");
15
+ export const ScheduleTrigger = /* @__PURE__ */ createProperty("GitHub::Actions::ScheduleTrigger", "github");
16
+ export const Service = /* @__PURE__ */ createProperty("GitHub::Actions::Service", "github");
17
+ export const Step = /* @__PURE__ */ createProperty("GitHub::Actions::Step", "github");
18
+ export const Strategy = /* @__PURE__ */ createProperty("GitHub::Actions::Strategy", "github");
19
+ export const WorkflowCallTrigger = /* @__PURE__ */ createProperty("GitHub::Actions::WorkflowCallTrigger", "github");
20
+ export const WorkflowDispatchTrigger = /* @__PURE__ */ createProperty("GitHub::Actions::WorkflowDispatchTrigger", "github");
21
+ export const WorkflowInput = /* @__PURE__ */ createProperty("GitHub::Actions::WorkflowInput", "github");
22
+ export const WorkflowOutput = /* @__PURE__ */ createProperty("GitHub::Actions::WorkflowOutput", "github");
23
+ export const WorkflowRunTrigger = /* @__PURE__ */ createProperty("GitHub::Actions::WorkflowRunTrigger", "github");
24
+ export const WorkflowSecret = /* @__PURE__ */ createProperty("GitHub::Actions::WorkflowSecret", "github");
25
+ // Re-exports for convenience
26
+ export { GitHub, Runner } from "../variables.js";
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Runtime factory constructors — re-exported from core.
3
+ */
4
+ export { createResource, createProperty } from "@intentius/chant/runtime";
@@ -0,0 +1,174 @@
1
+ /**
2
+ * GitHub Actions dependencies as graph edges (#3910).
3
+ *
4
+ * `chant graph` draws an edge for every typed reference it can find, and a
5
+ * workflow has almost none: a job names the jobs it waits for, the reusable
6
+ * workflow it calls and the workflows that trigger it as strings. This module
7
+ * resolves those strings against the declared entities, the way the
8
+ * serializer would place them, and reports each one through the plugin's
9
+ * `graphEdges` hook:
10
+ *
11
+ * - `needs` job -> job in the same workflow
12
+ * - `calls` job -> the workflow a `uses: ./.github/workflows/<file>` names,
13
+ * and job -> a composite action a step uses by local path
14
+ * - `triggers` workflow -> each workflow its `on.workflow_run.workflows` names
15
+ *
16
+ * A string that names nothing declared (another repository's workflow, a
17
+ * typo'd job, a composite action, which chant does not declare) is reported
18
+ * as dangling, never matched to a near name.
19
+ *
20
+ * Forgejo reuses this module: its entities are github entities, and Forgejo
21
+ * reads reusable workflows from `.forgejo/workflows/` as well.
22
+ */
23
+ import { isPropertyDeclarable } from "@intentius/chant/declarable";
24
+ const WORKFLOW = "GitHub::Actions::Workflow";
25
+ const JOB_TYPES = new Set(["GitHub::Actions::Job", "GitHub::Actions::ReusableWorkflowCallJob"]);
26
+ function toKebabCase(name) {
27
+ return name.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
28
+ }
29
+ function entityType(value) {
30
+ if (!value || typeof value !== "object")
31
+ return undefined;
32
+ const t = value.entityType;
33
+ return typeof t === "string" ? t : undefined;
34
+ }
35
+ /** A Declarable's props bag, or the plain object itself (JSON-style jobs and steps). */
36
+ function propsOf(value) {
37
+ if (!value || typeof value !== "object" || Array.isArray(value))
38
+ return undefined;
39
+ const props = value.props;
40
+ if (entityType(value) !== undefined) {
41
+ return props && typeof props === "object" ? props : undefined;
42
+ }
43
+ return value;
44
+ }
45
+ /**
46
+ * Which jobs land in which workflow, mirroring ./serializer.ts: a workflow's
47
+ * own `jobs:` map when it has one, and otherwise the standalone Job exports,
48
+ * which go to the first workflow only.
49
+ */
50
+ function placeJobs(entities) {
51
+ const names = new Map();
52
+ const workflows = [];
53
+ const standalone = [];
54
+ for (const [name, entity] of entities) {
55
+ names.set(entity, name);
56
+ if (isPropertyDeclarable(entity))
57
+ continue;
58
+ const type = entityType(entity);
59
+ if (type === WORKFLOW)
60
+ workflows.push([name, entity]);
61
+ else if (type && JOB_TYPES.has(type))
62
+ standalone.push({ key: toKebabCase(name), node: name, props: propsOf(entity) ?? {} });
63
+ }
64
+ const placed = workflows.map(([name, wf], i) => {
65
+ const props = propsOf(wf) ?? {};
66
+ const inline = [];
67
+ const jobs = props.jobs;
68
+ if (jobs && typeof jobs === "object" && !Array.isArray(jobs)) {
69
+ for (const [key, value] of Object.entries(jobs)) {
70
+ const jobProps = propsOf(value);
71
+ if (!jobProps)
72
+ continue;
73
+ inline.push({ key: toKebabCase(key), node: names.get(value), props: jobProps });
74
+ }
75
+ }
76
+ return { name, props, jobs: inline.length > 0 ? inline : i === 0 ? standalone : [] };
77
+ });
78
+ return { workflows: placed, orphans: workflows.length === 0 ? standalone : [] };
79
+ }
80
+ /** The file a declared workflow is written to. Every workflow after the first
81
+ * is `<kebab-name>.yml` (./serializer.ts); the first is written to the
82
+ * `--output` path, which the examples and templates name the same way. */
83
+ function workflowFile(name) {
84
+ return `${toKebabCase(name)}.yml`;
85
+ }
86
+ const stripYamlExt = (file) => file.replace(/\.ya?ml$/, "");
87
+ /** `needs:` as a list of job keys. A Job object held directly names itself. */
88
+ function needsKeys(needs, names) {
89
+ const list = Array.isArray(needs) ? needs : needs === undefined ? [] : [needs];
90
+ const out = [];
91
+ for (const item of list) {
92
+ if (typeof item === "string")
93
+ out.push(item);
94
+ else if (names.has(item))
95
+ out.push(toKebabCase(names.get(item)));
96
+ }
97
+ return out;
98
+ }
99
+ /** `on.workflow_run.workflows`, read from the camelCase or snake_case key. */
100
+ function workflowRunNames(on) {
101
+ const triggers = propsOf(on);
102
+ if (!triggers)
103
+ return [];
104
+ const run = propsOf(triggers.workflow_run ?? triggers.workflowRun);
105
+ const workflows = run?.workflows;
106
+ if (typeof workflows === "string")
107
+ return [workflows];
108
+ return Array.isArray(workflows) ? workflows.filter((w) => typeof w === "string") : [];
109
+ }
110
+ /** The graph edges and pipeline membership a GitHub (or Forgejo) project declares. */
111
+ export function githubGraphEdges(entities) {
112
+ const { workflows, orphans } = placeJobs(entities);
113
+ if (workflows.length === 0 && orphans.length === 0)
114
+ return undefined;
115
+ const names = new Map();
116
+ for (const [name, entity] of entities)
117
+ names.set(entity, name);
118
+ const byFile = new Map();
119
+ const byDisplayName = new Map();
120
+ for (const wf of workflows) {
121
+ byFile.set(stripYamlExt(workflowFile(wf.name)), wf.name);
122
+ if (typeof wf.props.name === "string")
123
+ byDisplayName.set(wf.props.name, wf.name);
124
+ }
125
+ const references = [];
126
+ const pipelines = {};
127
+ const visitJobs = (jobs, workflow) => {
128
+ const byKey = new Map(jobs.map((j) => [j.key, j]));
129
+ for (const job of jobs) {
130
+ const from = job.node ?? workflow;
131
+ if (!from)
132
+ continue;
133
+ for (const key of needsKeys(job.props.needs, names)) {
134
+ const target = byKey.get(key);
135
+ const to = target ? (target.node ?? workflow) : undefined;
136
+ if (to)
137
+ references.push({ from, to, kind: "needs", viaAttr: "needs" });
138
+ else
139
+ references.push({ from, to: key, kind: "needs", viaAttr: "needs", dangling: true });
140
+ }
141
+ const uses = job.props.uses;
142
+ if (typeof uses === "string") {
143
+ const local = /^\.\/(?:.*\/)?workflows\/([^/]+\.ya?ml)$/.exec(uses);
144
+ const called = local ? byFile.get(stripYamlExt(local[1])) : undefined;
145
+ if (called)
146
+ references.push({ from, to: called, kind: "calls", viaAttr: "uses", crossPipeline: true });
147
+ else
148
+ references.push({ from, to: uses, kind: "calls", viaAttr: "uses", crossPipeline: true, dangling: true });
149
+ }
150
+ const steps = Array.isArray(job.props.steps) ? job.props.steps : [];
151
+ for (const step of steps) {
152
+ const stepUses = propsOf(step)?.uses;
153
+ // A local composite action. Remote actions (`owner/repo@ref`,
154
+ // `docker://`) are packages, not part of this project's graph.
155
+ if (typeof stepUses === "string" && stepUses.startsWith("./")) {
156
+ references.push({ from, to: stepUses, kind: "calls", viaAttr: "steps.uses", dangling: true });
157
+ }
158
+ }
159
+ }
160
+ };
161
+ for (const wf of workflows) {
162
+ visitJobs(wf.jobs, wf.name);
163
+ pipelines[wf.name] = [wf.name, ...wf.jobs.map((j) => j.node).filter((n) => n !== undefined)];
164
+ for (const trigger of workflowRunNames(wf.props.on)) {
165
+ const to = byDisplayName.get(trigger);
166
+ if (to)
167
+ references.push({ from: wf.name, to, kind: "triggers", viaAttr: "workflow_run", crossPipeline: true });
168
+ else
169
+ references.push({ from: wf.name, to: trigger, kind: "triggers", viaAttr: "workflow_run", crossPipeline: true, dangling: true });
170
+ }
171
+ }
172
+ visitJobs(orphans);
173
+ return { references, ...(Object.keys(pipelines).length > 0 ? { pipelines } : {}) };
174
+ }
@@ -0,0 +1,108 @@
1
+ /**
2
+ * TypeScript code generator for GitHub Actions import.
3
+ *
4
+ * Converts a TemplateIR into TypeScript source code using
5
+ * the @intentius/chant-lexicon-github constructors.
6
+ */
7
+ const TYPE_TO_CLASS = {
8
+ "GitHub::Actions::Workflow": "Workflow",
9
+ "GitHub::Actions::Job": "Job",
10
+ "GitHub::Actions::ReusableWorkflowCallJob": "ReusableWorkflowCallJob",
11
+ };
12
+ const PROPERTY_CONSTRUCTORS = {
13
+ strategy: "Strategy",
14
+ concurrency: "Concurrency",
15
+ container: "Container",
16
+ environment: "Environment",
17
+ permissions: "Permissions",
18
+ defaults: "Defaults",
19
+ };
20
+ /**
21
+ * Generate TypeScript source code from a GitHub Actions IR.
22
+ */
23
+ export class GitHubActionsGenerator {
24
+ generate(ir) {
25
+ const lines = [];
26
+ const usedConstructors = new Set();
27
+ for (const resource of ir.resources) {
28
+ const cls = TYPE_TO_CLASS[resource.type];
29
+ if (cls)
30
+ usedConstructors.add(cls);
31
+ this.collectNestedConstructors(resource.properties, usedConstructors);
32
+ }
33
+ const imports = [...usedConstructors].sort().join(", ");
34
+ lines.push(`import { ${imports} } from "@intentius/chant-lexicon-github";`);
35
+ lines.push("");
36
+ for (const resource of ir.resources) {
37
+ const cls = TYPE_TO_CLASS[resource.type];
38
+ if (!cls)
39
+ continue;
40
+ const varName = resource.logicalId;
41
+ const propsStr = this.emitProps(resource.properties, 1);
42
+ lines.push(`export const ${varName} = new ${cls}(${propsStr});`);
43
+ lines.push("");
44
+ }
45
+ return [{ path: "main.ts", content: lines.join("\n") }];
46
+ }
47
+ collectNestedConstructors(props, used) {
48
+ for (const [key, value] of Object.entries(props)) {
49
+ const constructor = PROPERTY_CONSTRUCTORS[key];
50
+ if (constructor && typeof value === "object" && value !== null && !Array.isArray(value)) {
51
+ used.add(constructor);
52
+ }
53
+ }
54
+ }
55
+ emitProps(props, depth) {
56
+ const indent = " ".repeat(depth);
57
+ const innerIndent = " ".repeat(depth + 1);
58
+ const entries = [];
59
+ for (const [key, value] of Object.entries(props)) {
60
+ if (value === undefined || value === null)
61
+ continue;
62
+ const emitted = this.emitValue(key, value, depth + 1);
63
+ entries.push(`${innerIndent}${JSON.stringify(key)}: ${emitted},`);
64
+ }
65
+ if (entries.length === 0)
66
+ return "{}";
67
+ return `{\n${entries.join("\n")}\n${indent}}`;
68
+ }
69
+ emitValue(key, value, depth) {
70
+ if (value === null || value === undefined)
71
+ return "undefined";
72
+ const constructor = PROPERTY_CONSTRUCTORS[key];
73
+ if (constructor && typeof value === "object" && !Array.isArray(value)) {
74
+ const propsStr = this.emitProps(value, depth);
75
+ return `new ${constructor}(${propsStr})`;
76
+ }
77
+ return this.emitLiteral(value, depth);
78
+ }
79
+ emitLiteral(value, depth) {
80
+ if (value === null || value === undefined)
81
+ return "undefined";
82
+ if (typeof value === "string")
83
+ return JSON.stringify(value);
84
+ if (typeof value === "number" || typeof value === "boolean")
85
+ return String(value);
86
+ if (Array.isArray(value)) {
87
+ if (value.length === 0)
88
+ return "[]";
89
+ const items = value.map((item) => this.emitLiteral(item, depth + 1));
90
+ const oneLine = `[${items.join(", ")}]`;
91
+ if (oneLine.length < 80)
92
+ return oneLine;
93
+ const indent = " ".repeat(depth);
94
+ const innerIndent = " ".repeat(depth + 1);
95
+ return `[\n${items.map((i) => `${innerIndent}${i},`).join("\n")}\n${indent}]`;
96
+ }
97
+ if (typeof value === "object") {
98
+ const entries = Object.entries(value);
99
+ if (entries.length === 0)
100
+ return "{}";
101
+ const indent = " ".repeat(depth);
102
+ const innerIndent = " ".repeat(depth + 1);
103
+ const items = entries.map(([k, v]) => `${innerIndent}${JSON.stringify(k)}: ${this.emitLiteral(v, depth + 1)},`);
104
+ return `{\n${items.join("\n")}\n${indent}}`;
105
+ }
106
+ return String(value);
107
+ }
108
+ }
@@ -0,0 +1,69 @@
1
+ /**
2
+ * GitHub Actions YAML parser for `chant import`.
3
+ *
4
+ * Parses existing .github/workflows/*.yml files into TemplateIR.
5
+ */
6
+ import { parseYAML } from "@intentius/chant/yaml";
7
+ function kebabToCamelCase(name) {
8
+ return name.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
9
+ }
10
+ function snakeToCamelCase(name) {
11
+ return name.replace(/_([a-z])/g, (_, c) => c.toUpperCase());
12
+ }
13
+ /**
14
+ * GitHub Actions YAML parser implementation.
15
+ */
16
+ export class GitHubActionsParser {
17
+ parse(content) {
18
+ const doc = parseYAML(content);
19
+ const resources = [];
20
+ // Extract workflow-level properties
21
+ const workflowProps = {};
22
+ if (doc.name)
23
+ workflowProps.name = doc.name;
24
+ if (doc["run-name"])
25
+ workflowProps["run-name"] = doc["run-name"];
26
+ if (doc.on)
27
+ workflowProps.on = doc.on;
28
+ if (doc.permissions)
29
+ workflowProps.permissions = doc.permissions;
30
+ if (doc.env)
31
+ workflowProps.env = doc.env;
32
+ if (doc.concurrency)
33
+ workflowProps.concurrency = doc.concurrency;
34
+ if (doc.defaults)
35
+ workflowProps.defaults = doc.defaults;
36
+ if (Object.keys(workflowProps).length > 0) {
37
+ resources.push({
38
+ logicalId: "workflow",
39
+ type: "GitHub::Actions::Workflow",
40
+ properties: workflowProps,
41
+ });
42
+ }
43
+ // Extract jobs
44
+ if (doc.jobs && typeof doc.jobs === "object") {
45
+ for (const [jobId, jobDef] of Object.entries(doc.jobs)) {
46
+ if (typeof jobDef !== "object" || jobDef === null)
47
+ continue;
48
+ const jobObj = jobDef;
49
+ const logicalId = kebabToCamelCase(jobId);
50
+ // Determine if it's a reusable workflow call job
51
+ const type = jobObj.uses
52
+ ? "GitHub::Actions::ReusableWorkflowCallJob"
53
+ : "GitHub::Actions::Job";
54
+ resources.push({
55
+ logicalId,
56
+ type,
57
+ properties: jobObj,
58
+ metadata: {
59
+ originalName: jobId,
60
+ },
61
+ });
62
+ }
63
+ }
64
+ return {
65
+ resources,
66
+ parameters: [],
67
+ };
68
+ }
69
+ }
package/dist/index.js ADDED
@@ -0,0 +1,22 @@
1
+ // Serializer
2
+ export { githubSerializer } from "./serializer.js";
3
+ export { githubGraphEdges } from "./graph-edges.js";
4
+ // Plugin
5
+ export { githubPlugin } from "./plugin.js";
6
+ // Expression system
7
+ 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";
8
+ // Context Variables
9
+ export { GitHub, Runner } from "./variables.js";
10
+ // Generated entities — export everything from generated index
11
+ // After running `chant generate`, this re-exports all entity classes
12
+ export * from "./generated/index.js";
13
+ // Action versions the composites emit, and their pinned commits
14
+ export { ACTION_PINS, actionRef, actionPin } from "./action-pins.js";
15
+ // Composites
16
+ export { Checkout, SetupNode, SetupGo, SetupPython, CacheAction, UploadArtifact, DownloadArtifact, NodeCI, NodePipeline, BunPipeline, PnpmPipeline, YarnPipeline, PythonCI, DockerBuild, DeployEnvironment, GoCI, Dependabot, DependabotConfig, PrPlanReport, } from "./composites/index.js";
17
+ // Spec utilities (for tooling)
18
+ export { fetchWorkflowSchema, fetchSchemas } from "./spec/fetch.js";
19
+ export { parseWorkflowSchema, githubShortName, githubServiceName } from "./codegen/parse.js";
20
+ // Code generation pipeline
21
+ export { generate, writeGeneratedFiles } from "./codegen/generate.js";
22
+ export { packageLexicon } from "./codegen/package.js";
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "algorithm": "sha256",
3
3
  "artifacts": {
4
- "manifest.json": "c023d92a6cad1bf225f73d5030d8e2a7ed5769383b94b62c4d6a15c3267f7373",
4
+ "manifest.json": "1ac5c53834d0d9e3f294436372ef80c67d915538899782e8188f48379b1c884d",
5
5
  "meta.json": "c50e0ef3845c370c3ad2210b8bba455980417c0f818aa6ed46e06d4fb8b1b9f9",
6
6
  "types/index.d.ts": "0ead95536e947ddfb98a5acf0ddb88547913b5a53a342df67711c59266f84b3e",
7
7
  "rules/deprecated-action-version.ts": "a9ba7cc924ff1c7055110416d8989273690415bfb5e8dcc32dc9431b9351f5ca",
@@ -78,5 +78,5 @@
78
78
  "skills/chant-github-patterns.md": "bb3abef289a8fdfcf07d6bb2d7289dcb2f38bc0cb0321ea320b78b45a6f548c0",
79
79
  "skills/chant-github-security.md": "aab111cb0871cad30281ce48d7da23663689619351029219e2be019a1a61e394"
80
80
  },
81
- "composite": "5c3bb6c14a961e31e874986fd49382290366cd82727d8d72488a75296ad236a9"
81
+ "composite": "fb2f379e45edbc503f525d77958b302d4f546273f1b09ba0f2d5e7d0cdde1e2f"
82
82
  }