@intentius/chant-lexicon-forgejo 0.124.31 → 0.124.33

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 (37) hide show
  1. package/dist/actions.js +96 -0
  2. package/dist/codegen/docs-cli.js +6 -0
  3. package/dist/codegen/docs.js +99 -0
  4. package/dist/codegen/generate-cli.js +7 -0
  5. package/dist/codegen/generate.js +31 -0
  6. package/dist/codegen/package-cli.js +12 -0
  7. package/dist/codegen/package.js +42 -0
  8. package/dist/components/ci-jobs.js +71 -0
  9. package/dist/components/generate-op-pipeline.js +116 -0
  10. package/dist/components/generate-op-waves-pipeline.js +66 -0
  11. package/dist/components/generate-pipeline.js +51 -0
  12. package/dist/config.js +36 -0
  13. package/dist/dialect.js +198 -0
  14. package/dist/index.js +24 -0
  15. package/dist/integrity.json +2 -2
  16. package/dist/lint/audit-catalog.js +9 -0
  17. package/dist/lint/post-synth/index.js +6 -0
  18. package/dist/lint/post-synth/wfj010.js +34 -0
  19. package/dist/lint/post-synth/wfj011.js +36 -0
  20. package/dist/lint/rules/delegate-to-github.js +30 -0
  21. package/dist/lint/rules/index.js +5 -0
  22. package/dist/lsp/completions.js +13 -0
  23. package/dist/lsp/hover.js +11 -0
  24. package/dist/manifest.json +1 -1
  25. package/dist/mcp/context-tools.js +243 -0
  26. package/dist/migrate/from-github/index.js +63 -0
  27. package/dist/migrate/from-github/security.js +140 -0
  28. package/dist/plugin.js +206 -0
  29. package/dist/serializer.js +50 -0
  30. package/package.json +7 -7
  31. package/src/codegen/package.ts +2 -2
  32. package/src/components/__fixtures__/pr-loop-build.forgejo.golden.yml +6 -2
  33. package/src/components/__fixtures__/pr-loop-record.forgejo.golden.yml +64 -0
  34. package/src/components/__fixtures__/pr-loop-waves.forgejo.golden.yml +117 -0
  35. package/src/components/__fixtures__/pr-loop.forgejo.golden.yml +6 -2
  36. package/src/components/generate-pr-pipeline.test.ts +41 -7
  37. package/src/components/generate-pr-waves-pipeline.test.ts +42 -0
@@ -0,0 +1,63 @@
1
+ /**
2
+ * github → forgejo migration entry point.
3
+ *
4
+ * Thin by design: Forgejo Actions YAML *is* GitHub Actions YAML, so the
5
+ * migration applies the same dialect as a `chant build` (drop ignored keys,
6
+ * resolve `uses:` refs, map runner labels) and emits the result. No stage
7
+ * inference, job→script rewrite, or `!reference` intrinsic like the gitlab
8
+ * migration needs. The differentiated value is the security-fate **compare**
9
+ * (see ./security).
10
+ */
11
+ import { parseYAML, emitYAML } from "@intentius/chant/yaml";
12
+ import { transformWorkflowObject } from "../../dialect.js";
13
+ import { analyzeForgejoSecurity, provenanceToDiagnostics, renderSecurityPosture, } from "./security.js";
14
+ /** Emit a parsed/transformed workflow object back to YAML. */
15
+ function emitForgejoYaml(value) {
16
+ const body = emitYAML(value, 0);
17
+ return (body.startsWith("\n") ? body.slice(1) : body) + "\n";
18
+ }
19
+ /** Emit a chant TypeScript pipeline (authored github-style, imported from forgejo). */
20
+ async function emitTs(content, opts) {
21
+ const { GitHubActionsParser } = await import("@intentius/chant-lexicon-github/import/parser");
22
+ const { GitHubActionsGenerator } = await import("@intentius/chant-lexicon-github/import/generator");
23
+ const ir = new GitHubActionsParser().parse(content);
24
+ const files = new GitHubActionsGenerator().generate(ir);
25
+ const banner = `// Migrated from ${opts.sourceFile ?? "(stdin)"} by chant migrate (github → forgejo).\n` +
26
+ `// Authored github-style; the forgejo lexicon applies its dialect on build.\n\n`;
27
+ return (banner +
28
+ files
29
+ .map((f) => f.content.replace(/@intentius\/chant-lexicon-github/g, "@intentius/chant-lexicon-forgejo"))
30
+ .join("\n"));
31
+ }
32
+ /**
33
+ * Migrate a GitHub Actions workflow into a Forgejo workflow.
34
+ *
35
+ * @param content raw .github/workflows/*.yml content
36
+ * @param opts migration options
37
+ */
38
+ export async function transform(content, opts = {}) {
39
+ const source = parseYAML(content);
40
+ let output;
41
+ if (opts.emit === "ts") {
42
+ output = await emitTs(content, opts);
43
+ }
44
+ else {
45
+ const { value } = transformWorkflowObject(source);
46
+ output = emitForgejoYaml(value);
47
+ }
48
+ // The compare is computed against the *source* — that's where the dropped
49
+ // keys still exist — so it reports what the move costs.
50
+ const provenance = analyzeForgejoSecurity(source, { sourceFile: opts.sourceFile });
51
+ const diagnostics = provenanceToDiagnostics(provenance, { strict: opts.strict });
52
+ const securityPosture = renderSecurityPosture(provenance);
53
+ return { output, provenance, diagnostics, securityPosture };
54
+ }
55
+ /**
56
+ * Lightweight detector: does this content look like a GitHub Actions workflow?
57
+ * Used by the plugin's `migrationSource("github")`.
58
+ */
59
+ export function detectGitHubWorkflow(content) {
60
+ if (!/^\s*jobs\s*:/m.test(content))
61
+ return false;
62
+ return /^\s*on\s*:/m.test(content) || /^\s*runs-on\s*:/m.test(content);
63
+ }
@@ -0,0 +1,140 @@
1
+ /**
2
+ * Security-fate model for the github → forgejo migration edge.
3
+ *
4
+ * github → forgejo YAML is near-identical, so the migration itself is thin. The
5
+ * differentiated value is the **compare**: classifying what survives the move
6
+ * and what Forgejo silently drops. Most properties translate verbatim; the
7
+ * useful findings are the keys the Forgejo runner ignores (`permissions` →
8
+ * `needs-review`, `continue-on-error` → `lost`) plus refs/labels that need attention
9
+ * (unresolved `uses:`, unmapped runner labels → `needs-review`).
10
+ *
11
+ * Mirrors the gitlab lexicon's fate model (translated / approximated /
12
+ * needs-review / lost) but for the Forgejo edge — kept local so the forgejo
13
+ * lexicon does not depend on the gitlab one.
14
+ */
15
+ import { DEFAULT_RUNNER_LABELS } from "../../dialect.js";
16
+ import { resolveActionRef } from "../../actions.js";
17
+ function toKebabCase(name) {
18
+ return name.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
19
+ }
20
+ /**
21
+ * Classify the fate of security-relevant properties in a parsed GitHub Actions
22
+ * workflow as it migrates to Forgejo. Walks the source object so occurrence
23
+ * counts (and paths) are precise.
24
+ */
25
+ export function analyzeForgejoSecurity(workflow, opts = {}) {
26
+ const records = [];
27
+ const file = opts.sourceFile;
28
+ function visit(value, path) {
29
+ if (value === null || typeof value !== "object")
30
+ return;
31
+ if (Array.isArray(value)) {
32
+ value.forEach((v, i) => visit(v, `${path}[${i}]`));
33
+ return;
34
+ }
35
+ for (const [key, v] of Object.entries(value)) {
36
+ const kebab = toKebabCase(key);
37
+ const childPath = path ? `${path}.${key}` : key;
38
+ if (kebab === "permissions") {
39
+ records.push({
40
+ sourceKey: childPath,
41
+ sourceFile: file,
42
+ category: "needs-review",
43
+ rule: "MIG-FJ-PERMISSIONS",
44
+ note: `permissions: is dropped, except id-token, which a job needs to request an OIDC token. The Forgejo runner does not enforce the scopes, so confirm the token scope in your Forgejo/runner and repository settings.`,
45
+ security: { property: "Least-privilege permissions", fate: "needs-review", severity: "warning", reestablish: "Forgejo runner/token settings" },
46
+ });
47
+ continue;
48
+ }
49
+ if (kebab === "continue-on-error") {
50
+ records.push({
51
+ sourceKey: childPath,
52
+ sourceFile: file,
53
+ category: "skipped",
54
+ rule: "MIG-FJ-CONTINUE-ON-ERROR",
55
+ note: `continue-on-error is ignored by the Forgejo runner — the failure-tolerance control is lost. A failing step/job will fail the run.`,
56
+ security: { property: "continue-on-error tolerance", fate: "lost", severity: "warning" },
57
+ });
58
+ continue;
59
+ }
60
+ if (kebab === "uses" && typeof v === "string") {
61
+ const { warning } = resolveActionRef(v);
62
+ if (warning) {
63
+ records.push({
64
+ sourceKey: childPath,
65
+ sourceFile: file,
66
+ category: "needs-review",
67
+ rule: "MIG-FJ-ACTION-UNRESOLVED",
68
+ note: `Action ref '${v}' has no built-in Forgejo mapping and won't resolve from a GitHub Marketplace. Use a full repository URL or mirror it under forgejo.actionsRoot.`,
69
+ security: { property: "Action reference", fate: "needs-review", severity: "warning" },
70
+ });
71
+ }
72
+ continue;
73
+ }
74
+ if (kebab === "runs-on") {
75
+ for (const label of runnerLabels(v)) {
76
+ if (DEFAULT_RUNNER_LABELS[label] === undefined) {
77
+ records.push({
78
+ sourceKey: childPath,
79
+ sourceFile: file,
80
+ category: "needs-review",
81
+ rule: "MIG-FJ-RUNNER-LABEL",
82
+ note: `Runner label '${label}' has no built-in Forgejo mapping. Confirm a Forgejo runner advertises it, or map it via forgejo.runnerLabels.`,
83
+ security: { property: "Runner label", fate: "needs-review", severity: "warning" },
84
+ });
85
+ }
86
+ }
87
+ continue;
88
+ }
89
+ visit(v, childPath);
90
+ }
91
+ }
92
+ visit(workflow, "");
93
+ return records;
94
+ }
95
+ function runnerLabels(value) {
96
+ if (typeof value === "string")
97
+ return [value];
98
+ if (Array.isArray(value))
99
+ return value.filter((v) => typeof v === "string");
100
+ return [];
101
+ }
102
+ /**
103
+ * Convert security records into SARIF-shaped diagnostics. Security findings
104
+ * always emit a diagnostic at their severity, escalated to `error` under
105
+ * `--strict` when the property was lost or needs review.
106
+ */
107
+ export function provenanceToDiagnostics(records, opts = {}) {
108
+ return records.map((r) => {
109
+ const escalate = opts.strict && (r.security.fate === "lost" || r.security.fate === "needs-review");
110
+ return {
111
+ file: r.sourceFile ?? "<input>",
112
+ line: 1,
113
+ column: 1,
114
+ ruleId: r.rule,
115
+ severity: escalate ? "error" : r.security.severity,
116
+ message: r.note ?? r.rule,
117
+ };
118
+ });
119
+ }
120
+ /** Render a "Security posture" Markdown section from the security records. */
121
+ export function renderSecurityPosture(records) {
122
+ if (records.length === 0) {
123
+ return "## Security posture\n\nNo security-relevant properties weaken or drop at the Forgejo migration edge.\n";
124
+ }
125
+ const byRule = new Map();
126
+ for (const r of records) {
127
+ const existing = byRule.get(r.rule);
128
+ if (existing)
129
+ existing.count += 1;
130
+ else
131
+ byRule.set(r.rule, { property: r.security.property, fate: r.security.fate, rule: r.rule, count: 1 });
132
+ }
133
+ let out = "## Security posture\n\n";
134
+ out += "| Property | Fate | Rule | Count |\n|---|---|---|---|\n";
135
+ for (const line of byRule.values()) {
136
+ out += `| ${line.property} | ${line.fate} | ${line.rule} | ${line.count} |\n`;
137
+ }
138
+ out += "\nFates: **translated** (carried as-is) · **approximated** · **needs-review** (confirm/adjust on Forgejo) · **lost** (the Forgejo runner ignores the property).\n";
139
+ return out;
140
+ }
package/dist/plugin.js ADDED
@@ -0,0 +1,206 @@
1
+ /**
2
+ * Forgejo Actions lexicon plugin.
3
+ *
4
+ * Forgejo runs GitHub-Actions-compatible workflows, so this lexicon is a thin
5
+ * dialect of the github lexicon: it reuses github's generated entities and
6
+ * composites wholesale (re-exported from ./index) and overrides only the
7
+ * serializer. There is no own spec, so the codegen lifecycle methods are
8
+ * intentionally no-ops — they delegate the real work to the github lexicon.
9
+ */
10
+ import { postSynthChecks as postSynthCheckList } from "./lint/post-synth/index.js";
11
+ import { forgejoAuditCatalog } from "./lint/audit-catalog.js";
12
+ import { createSkillsLoader, createDiffTool } from "@intentius/chant/lexicon-plugin-helpers";
13
+ import { forgejoSerializer } from "./serializer.js";
14
+ import { forgejoContextTools } from "./mcp/context-tools.js";
15
+ import { generateForgejoPipeline } from "./components/generate-pipeline.js";
16
+ import { generateForgejoOpPipeline } from "./components/generate-op-pipeline.js";
17
+ import { generateForgejoOpWavesPipeline } from "./components/generate-op-waves-pipeline.js";
18
+ import { forgejoLintRules } from "./lint/rules/delegate-to-github.js";
19
+ import { forgejoCompletions } from "./lsp/completions.js";
20
+ import { forgejoHover } from "./lsp/hover.js";
21
+ import { forgejoConfigSchema } from "./config.js";
22
+ import { githubGraphEdges } from "@intentius/chant-lexicon-github";
23
+ const reuseNote = "forgejo reuses the github lexicon's entities — run `chant generate` in the github lexicon instead.";
24
+ export const forgejoPlugin = {
25
+ name: "forgejo",
26
+ configSchema: forgejoConfigSchema,
27
+ auditCatalog: () => forgejoAuditCatalog,
28
+ serializer: forgejoSerializer,
29
+ // Forgejo workflows are github entities, so github resolves their `needs`,
30
+ // `uses:` and `workflow_run` edges (#3910); `.forgejo/workflows/` paths too.
31
+ graphEdges: (entities) => githubGraphEdges(entities),
32
+ // Generate mode (#969): component graph → Forgejo Actions workflow. Reuses
33
+ // github's pipeline structure and applies the Forgejo dialect (runner labels,
34
+ // `uses:` resolution) — see ./components/generate-pipeline.
35
+ generateComponentPipeline: (components, options) => generateForgejoPipeline(components, options),
36
+ // Generate mode, Op counterpart (#927): scheduled Op → Forgejo Actions workflow.
37
+ generateOpPipeline: (ops, options) => generateForgejoOpPipeline(ops, options),
38
+ // Ordered waves of Op runs, each behind one gate (#3679).
39
+ generateOpWavesPipeline: (spec, options) => generateForgejoOpWavesPipeline(spec, options),
40
+ initTemplates(template) {
41
+ if (template === "docker-build") {
42
+ return {
43
+ src: {
44
+ "pipeline.ts": `import { Workflow, Job, Step, Checkout } from "@intentius/chant-lexicon-forgejo";
45
+
46
+ export const workflow = new Workflow({
47
+ name: "Docker Build",
48
+ on: { push: { branches: ["main"] } },
49
+ });
50
+
51
+ export const build = new Job({
52
+ "runs-on": "ubuntu-latest",
53
+ steps: [
54
+ Checkout({}).step,
55
+ new Step({ name: "Build", run: "docker build -t myapp ." }),
56
+ ],
57
+ });
58
+ `,
59
+ },
60
+ };
61
+ }
62
+ // Default template: a Node CI workflow. Reuses github entities; the forgejo
63
+ // serializer maps "ubuntu-latest" to a Forgejo runner label on build.
64
+ return {
65
+ src: {
66
+ "pipeline.ts": `import { Workflow, Job, Step, Checkout, SetupNode } from "@intentius/chant-lexicon-forgejo";
67
+
68
+ export const workflow = new Workflow({
69
+ name: "CI",
70
+ on: {
71
+ push: { branches: ["main"] },
72
+ pull_request: { branches: ["main"] },
73
+ },
74
+ });
75
+
76
+ export const build = new Job({
77
+ "runs-on": "ubuntu-latest",
78
+ steps: [
79
+ Checkout({}).step,
80
+ SetupNode({ nodeVersion: "22", cache: "npm" }).step,
81
+ new Step({ name: "Install", run: "npm ci" }),
82
+ new Step({ name: "Build", run: "npm run build" }),
83
+ new Step({ name: "Test", run: "npm test" }),
84
+ ],
85
+ });
86
+ `,
87
+ },
88
+ };
89
+ },
90
+ detectTemplate(data) {
91
+ if (typeof data !== "object" || data === null)
92
+ return false;
93
+ const obj = data;
94
+ // Forgejo Actions workflows are GitHub-Actions-shaped: `on:` + `jobs:`.
95
+ if (obj.on !== undefined && obj.jobs !== undefined)
96
+ return true;
97
+ for (const value of Object.values(obj)) {
98
+ if (typeof value === "object" && value !== null) {
99
+ const entry = value;
100
+ if (entry["runs-on"] !== undefined || entry.steps !== undefined) {
101
+ return true;
102
+ }
103
+ }
104
+ }
105
+ return false;
106
+ },
107
+ migrationSource(from) {
108
+ if (from !== "github")
109
+ return undefined;
110
+ return {
111
+ detect(content) {
112
+ // Inline heuristic — keeps the migrate code out of the import graph
113
+ // until a transform actually runs.
114
+ if (!/^\s*jobs\s*:/m.test(content))
115
+ return false;
116
+ return /^\s*on\s*:/m.test(content) || /^\s*runs-on\s*:/m.test(content);
117
+ },
118
+ async transform(content, opts) {
119
+ const { transform } = await import("./migrate/from-github/index.js");
120
+ const result = await transform(content, {
121
+ emit: opts.emit,
122
+ sourceFile: opts.sourceFile,
123
+ strict: opts.strict,
124
+ security: opts.security,
125
+ });
126
+ return {
127
+ output: result.output,
128
+ provenance: result.provenance,
129
+ diagnostics: result.diagnostics,
130
+ securityPosture: result.securityPosture,
131
+ };
132
+ },
133
+ };
134
+ },
135
+ postSynthChecks() {
136
+ return postSynthCheckList;
137
+ },
138
+ // Lint rules and LSP support are delegated to github, not forked — see
139
+ // ./lint/rules/delegate-to-github.ts and ./lsp/{completions,hover}.ts.
140
+ lintRules() {
141
+ return forgejoLintRules;
142
+ },
143
+ completionProvider(ctx) {
144
+ return forgejoCompletions(ctx);
145
+ },
146
+ hoverProvider(ctx) {
147
+ return forgejoHover(ctx);
148
+ },
149
+ mcpTools() {
150
+ return [
151
+ createDiffTool(forgejoSerializer, "Compare current build output against previous output for Forgejo Actions", "forgejo"),
152
+ ...forgejoContextTools(),
153
+ ];
154
+ },
155
+ skills: createSkillsLoader(import.meta.url, [
156
+ {
157
+ file: "chant-forgejo.md",
158
+ name: "chant-forgejo",
159
+ description: "Forgejo / Codeberg / Gitea Actions with chant — build, the dialect (dropped keys, runner labels, uses: resolution), and github→forgejo migration",
160
+ triggers: [
161
+ { type: "file-pattern", value: "**/.forgejo/workflows/*.yml" },
162
+ { type: "file-pattern", value: "**/.gitea/workflows/*.yml" },
163
+ { type: "context", value: "forgejo" },
164
+ { type: "context", value: "codeberg" },
165
+ { type: "context", value: "gitea" },
166
+ ],
167
+ parameters: [],
168
+ examples: [
169
+ {
170
+ title: "Build a Forgejo workflow",
171
+ description: "Author github-style; the forgejo dialect applies on build",
172
+ input: "Create a CI workflow for Codeberg",
173
+ output: `chant build src -o .forgejo/workflows/ci.yml`,
174
+ },
175
+ ],
176
+ },
177
+ ]),
178
+ // ── Codegen lifecycle — delegated to github (no own spec) ──────────
179
+ async generate() {
180
+ console.error(reuseNote);
181
+ },
182
+ async validate() {
183
+ console.error("All checks passed.");
184
+ },
185
+ async coverage() {
186
+ console.error(reuseNote);
187
+ },
188
+ // Packaging is real (unlike generate/validate/coverage above): forgejo
189
+ // ships its own dist/manifest.json — an empty resource catalog plus its
190
+ // own post-synth checks and skill — even though the catalog it packages
191
+ // is empty. See ./codegen/{generate,package}.ts.
192
+ async package(options) {
193
+ const { packageLexicon } = await import("./codegen/package.js");
194
+ const { writeBundleSpec } = await import("@intentius/chant/codegen/package");
195
+ const { join: pathJoin, dirname: pathDirname } = await import("path");
196
+ const { fileURLToPath: toPath } = await import("url");
197
+ const { spec, stats } = await packageLexicon(options);
198
+ const pkgDir = pathDirname(pathDirname(toPath(import.meta.url)));
199
+ writeBundleSpec(spec, pathJoin(pkgDir, "dist"));
200
+ console.error(`Packaged ${stats.resources} resources, ${stats.ruleCount} rules, ${stats.skillCount} skills`);
201
+ },
202
+ async docs(options) {
203
+ const { generateDocs } = await import("./codegen/docs.js");
204
+ return generateDocs(options);
205
+ },
206
+ };
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Forgejo Actions YAML serializer.
3
+ *
4
+ * A thin dialect of the github serializer: it applies the Forgejo dialect
5
+ * (see ./dialect) to the entity graph, then delegates emission to the github
6
+ * serializer, which already produces GitHub-Actions-compatible YAML — exactly
7
+ * what Forgejo / Codeberg / Gitea runners execute.
8
+ *
9
+ * The serializer's `name` is "github" on purpose: github-lexicon entities are
10
+ * tagged `lexicon: "github"`, and the build pipeline partitions and looks up
11
+ * serializers by that tag. A forgejo project loads only this serializer, so it
12
+ * claims that partition. The distinct `rulePrefix` ("WFJ") keeps Forgejo lint
13
+ * diagnostics namespaced apart from github's "GHA".
14
+ */
15
+ import { githubSerializer } from "@intentius/chant-lexicon-github";
16
+ import { applyForgejoDialect } from "./dialect.js";
17
+ /** Extract forgejo dialect options from the resolved project config, if any. */
18
+ function readForgejoOptions(config) {
19
+ const forgejo = config?.forgejo;
20
+ if (!forgejo || typeof forgejo !== "object")
21
+ return {};
22
+ const obj = forgejo;
23
+ const options = {};
24
+ if (obj.runnerLabels && typeof obj.runnerLabels === "object") {
25
+ options.runnerLabels = obj.runnerLabels;
26
+ }
27
+ if (typeof obj.actionsRoot === "string") {
28
+ options.actionsRoot = obj.actionsRoot;
29
+ }
30
+ return options;
31
+ }
32
+ /**
33
+ * Forgejo Actions YAML serializer implementation.
34
+ */
35
+ export const forgejoSerializer = {
36
+ name: "github",
37
+ rulePrefix: "WFJ",
38
+ serialize(entities, outputs, context) {
39
+ const options = readForgejoOptions(context?.config);
40
+ const { entities: transformed, warnings } = applyForgejoDialect(entities, options);
41
+ const result = githubSerializer.serialize(transformed, outputs, context);
42
+ if (typeof result === "string") {
43
+ return warnings.length > 0 ? { primary: result, warnings } : result;
44
+ }
45
+ return {
46
+ ...result,
47
+ warnings: [...(result.warnings ?? []), ...warnings],
48
+ };
49
+ },
50
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@intentius/chant-lexicon-forgejo",
3
- "version": "0.124.31",
3
+ "version": "0.124.33",
4
4
  "description": "Forgejo / Codeberg / Gitea Actions lexicon for chant — a thin GitHub Actions dialect",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://intentius.io/chant",
@@ -34,17 +34,17 @@
34
34
  ".": {
35
35
  "development": "./src/index.ts",
36
36
  "types": "./dist/index.d.ts",
37
- "default": "./src/index.ts"
37
+ "default": "./dist/index.js"
38
38
  },
39
39
  "./*": {
40
40
  "development": "./src/*.ts",
41
41
  "types": "./dist/*.d.ts",
42
- "default": "./src/*.ts"
42
+ "default": "./dist/*.js"
43
43
  },
44
44
  "./lint/post-synth": {
45
45
  "development": "./src/lint/post-synth/index.ts",
46
46
  "types": "./dist/lint/post-synth/index.d.ts",
47
- "default": "./src/lint/post-synth/index.ts"
47
+ "default": "./dist/lint/post-synth/index.js"
48
48
  }
49
49
  },
50
50
  "devDependencies": {
@@ -54,14 +54,14 @@
54
54
  },
55
55
  "peerDependencies": {
56
56
  "zod": "^4.3.6",
57
- "@intentius/chant": "^0.124.31",
58
- "@intentius/chant-lexicon-github": "^0.124.31"
57
+ "@intentius/chant": "^0.124.33",
58
+ "@intentius/chant-lexicon-github": "^0.124.33"
59
59
  },
60
60
  "scripts": {
61
61
  "generate": "tsx src/codegen/generate-cli.ts",
62
62
  "bundle": "tsx src/codegen/package-cli.ts",
63
63
  "docs": "tsx src/codegen/docs-cli.ts",
64
64
  "prepack": "npm run generate && npm run bundle && npm run build",
65
- "build": "../../scripts/tsc7.sh -p tsconfig.build.json && tsc-alias -p tsconfig.build.json && find dist -type f \\( -name \"*.js\" -o -name \"*.js.map\" \\) -delete"
65
+ "build": "../../scripts/tsc7.sh -p tsconfig.build.json --sourceMap false && tsc-alias -p tsconfig.build.json"
66
66
  }
67
67
  }
@@ -10,7 +10,7 @@
10
10
  import { readFileSync } from "fs";
11
11
  import { forgejoPlugin } from "../plugin";
12
12
  import { join, dirname } from "path";
13
- import { fileURLToPath } from "url";
13
+ import { moduleSourceDir } from "@intentius/chant/module-source";
14
14
  import {
15
15
  packagePipeline,
16
16
  collectSkills,
@@ -23,7 +23,7 @@ export type { PackageOptions, PackageResult };
23
23
 
24
24
  // package.ts is at src/codegen/package.ts — 2 dirname calls reach src/
25
25
  // then join(pkgDir, "..") is the package root where package.json lives
26
- const pkgDir = dirname(dirname(fileURLToPath(import.meta.url)));
26
+ const pkgDir = dirname(moduleSourceDir(import.meta.url));
27
27
 
28
28
  /**
29
29
  * Package the Forgejo lexicon into a distributable BundleSpec.
@@ -30,7 +30,8 @@ jobs:
30
30
  BASE_SHA: '${{ github.event.pull_request.base.sha }}'
31
31
  PR_NUMBER: '${{ github.event.pull_request.number }}'
32
32
  GITHUB_TOKEN: '${{ github.token }}'
33
- run: chant components pr-plan --base "$BASE_SHA" --pr "$PR_NUMBER" --env prod --gate pr-apply --output .chant/pr --forge forgejo
33
+ CHANT_RUNNER_KEY: '${{ secrets.CHANT_RUNNER_KEY }}'
34
+ run: chant ci run plan --in-place --no-push --base "$BASE_SHA" --pr "$PR_NUMBER" --env prod --gate pr-apply --forge forgejo
34
35
  - name: Keep the plan report
35
36
  if: always()
36
37
  uses: https://code.forgejo.org/actions/upload-artifact@v3
@@ -64,7 +65,10 @@ jobs:
64
65
  env:
65
66
  BASE_SHA: '${{ github.event.before }}'
66
67
  GITHUB_TOKEN: '${{ github.token }}'
67
- run: chant components pr-apply --base "$BASE_SHA" --env prod --gate pr-apply --output .chant/pr --forge forgejo --require-review --resume .chant/pr-resume/pr-apply.json
68
+ CHANT_RUNNER_KEY: '${{ secrets.CHANT_RUNNER_KEY }}'
69
+ run: |
70
+ if [ -z "$BASE_SHA" ] || [ "$BASE_SHA" = "0000000000000000000000000000000000000000" ]; then git fetch origin "main" && BASE_SHA="$(git merge-base FETCH_HEAD "$GITHUB_SHA")"; if [ "$BASE_SHA" = "$GITHUB_SHA" ]; then BASE_SHA="$(git rev-parse --verify --quiet "$GITHUB_SHA^" || echo 4b825dc642cb6eb9a060e54bf8d69288fbee4904)"; fi; fi; export BASE_SHA
71
+ chant ci run apply --in-place --base "$BASE_SHA" --env prod --gate pr-apply --forge forgejo
68
72
  - name: Keep the record for a re-run
69
73
  if: always()
70
74
  uses: https://code.forgejo.org/actions/cache/save@v4
@@ -0,0 +1,64 @@
1
+ name: chant-pr-prod
2
+
3
+ on:
4
+ pull_request:
5
+ branches:
6
+ - main
7
+ push:
8
+ branches:
9
+ - main
10
+
11
+ env:
12
+ CHANT_ENV: prod
13
+
14
+ jobs:
15
+ plan:
16
+ if: github.event_name == 'pull_request'
17
+ runs-on: docker
18
+ container: node:22
19
+ steps:
20
+ - uses: https://code.forgejo.org/actions/checkout@v7
21
+ with:
22
+ fetch-depth: 0
23
+ - run: command -v tofu >/dev/null 2>&1 || (curl -fsSL https://get.opentofu.org/install-opentofu.sh -o /tmp/install-opentofu.sh && sh /tmp/install-opentofu.sh --install-method standalone)
24
+ - name: Plan the members this pull request reaches
25
+ env:
26
+ BASE_SHA: '${{ github.event.pull_request.base.sha }}'
27
+ PR_NUMBER: '${{ github.event.pull_request.number }}'
28
+ GITHUB_TOKEN: '${{ github.token }}'
29
+ CHANT_RUNNER_KEY: '${{ secrets.CHANT_RUNNER_KEY }}'
30
+ run: chant ci run plan --in-place --no-push --base "$BASE_SHA" --pr "$PR_NUMBER" --env prod --gate pr-apply --forge forgejo
31
+ - name: Keep the plan report
32
+ if: always()
33
+ uses: https://code.forgejo.org/actions/upload-artifact@v3
34
+ with:
35
+ name: chant-pr-plan
36
+ path: .chant/pr
37
+ if-no-files-found: ignore
38
+ record:
39
+ if: github.event_name == 'push'
40
+ runs-on: docker
41
+ container: node:22
42
+ concurrency:
43
+ group: chant-record-prod
44
+ cancel-in-progress: false
45
+ steps:
46
+ - uses: https://code.forgejo.org/actions/checkout@v7
47
+ with:
48
+ fetch-depth: 0
49
+ - run: command -v tofu >/dev/null 2>&1 || (curl -fsSL https://get.opentofu.org/install-opentofu.sh -o /tmp/install-opentofu.sh && sh /tmp/install-opentofu.sh --install-method standalone)
50
+ - name: Record the plan on the merged commit; nothing applies
51
+ env:
52
+ BASE_SHA: '${{ github.event.before }}'
53
+ GITHUB_TOKEN: '${{ github.token }}'
54
+ CHANT_RUNNER_KEY: '${{ secrets.CHANT_RUNNER_KEY }}'
55
+ run: |
56
+ if [ -z "$BASE_SHA" ] || [ "$BASE_SHA" = "0000000000000000000000000000000000000000" ]; then git fetch origin "main" && BASE_SHA="$(git merge-base FETCH_HEAD "$GITHUB_SHA")"; if [ "$BASE_SHA" = "$GITHUB_SHA" ]; then BASE_SHA="$(git rev-parse --verify --quiet "$GITHUB_SHA^" || echo 4b825dc642cb6eb9a060e54bf8d69288fbee4904)"; fi; fi; export BASE_SHA
57
+ chant ci run record --in-place --base "$BASE_SHA" --env prod --gate pr-apply --forge forgejo
58
+ - name: Keep the record report
59
+ if: always()
60
+ uses: https://code.forgejo.org/actions/upload-artifact@v3
61
+ with:
62
+ name: chant-pr-record
63
+ path: .chant/pr
64
+ if-no-files-found: ignore