@intentius/chant-lexicon-forgejo 0.114.1 → 0.116.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Op waves → Forgejo Actions workflow (#3679).
3
+ *
4
+ * The github workflow ({@link buildGithubOpWavesDoc}) under the Forgejo
5
+ * dialect: action refs rewritten, `permissions:` dropped (the runner reads
6
+ * none), and each wave's `environment:` dropped with a header line saying so,
7
+ * since Forgejo Actions has no environments. The wave's chant gate is what
8
+ * holds it there.
9
+ */
10
+ import type { OpWavesSpec } from "@intentius/chant/op/op-waves";
11
+ import type { OpWavesPipelineOptions, OpWavesPipelineResult } from "@intentius/chant/lexicon";
12
+ import { type ForgejoDialectOptions } from "../dialect.js";
13
+ /** Render an Op waves spec as one Forgejo Actions workflow, `<name>.yml`. */
14
+ export declare function generateForgejoOpWavesPipeline(spec: OpWavesSpec, options: OpWavesPipelineOptions, dialectOptions?: ForgejoDialectOptions): OpWavesPipelineResult;
15
+ //# sourceMappingURL=generate-op-waves-pipeline.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generate-op-waves-pipeline.d.ts","sourceRoot":"","sources":["../../src/components/generate-op-waves-pipeline.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAChE,OAAO,KAAK,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;AAC9F,OAAO,EAA2B,KAAK,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAMjF,6EAA6E;AAC7E,wBAAgB,8BAA8B,CAC5C,IAAI,EAAE,WAAW,EACjB,OAAO,EAAE,sBAAsB,EAC/B,cAAc,GAAE,qBAA0B,GACzC,qBAAqB,CA4BvB"}
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "algorithm": "sha256",
3
3
  "artifacts": {
4
- "manifest.json": "4dc0782d0f4666b22fd7f0bae8439b1c6e06d200fa5a52b0eb02e8b4de2cbc4b",
4
+ "manifest.json": "544a826c40f69b6ed2e907b9cb85ba4c025c450725a6bcdb51e79fa53d30e80f",
5
5
  "meta.json": "44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a",
6
6
  "types/index.d.ts": "66204549b2a864ab5489e3f02c1878e73ab4b3c9d938cfb480b66f71543a43de",
7
7
  "rules/delegate-to-github.ts": "1060cda40f4b73d6cca3ba3fa13b80b4886147d4034c88584c2d80c5510dbe5e",
@@ -9,5 +9,5 @@
9
9
  "rules/wfj011.ts": "fcb8bf6685d744af20ad804ce929488257995c74b9a3353b2eacae3768d1c83e",
10
10
  "skills/chant-forgejo.md": "f28d7fb69fb1cfd992496ef41fda0ea5ccd54a29b84ea1c501a44f6a3992c4d5"
11
11
  },
12
- "composite": "8fc4693a2e4a40a6489dac4e34480e28f89bacdbfc6575aa8c9540f652cef7c3"
12
+ "composite": "80de889783509ff7c8aed110c12ecb33062ceb672d1b29c981fbcff931212646"
13
13
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "forgejo",
3
- "version": "0.114.1",
3
+ "version": "0.116.0",
4
4
  "chantVersion": ">=0.1.0",
5
5
  "namespace": "Forgejo",
6
6
  "intrinsics": [],
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAoC,MAAM,0BAA0B,CAAC;AAmBhG,eAAO,MAAM,aAAa,EAAE,aAiM3B,CAAC"}
1
+ {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAoC,MAAM,0BAA0B,CAAC;AAoBhG,eAAO,MAAM,aAAa,EAAE,aAmM3B,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@intentius/chant-lexicon-forgejo",
3
- "version": "0.114.1",
3
+ "version": "0.116.0",
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",
@@ -54,8 +54,8 @@
54
54
  },
55
55
  "peerDependencies": {
56
56
  "zod": "^4.3.6",
57
- "@intentius/chant": "^0.114.1",
58
- "@intentius/chant-lexicon-github": "^0.114.1"
57
+ "@intentius/chant": "^0.116.0",
58
+ "@intentius/chant-lexicon-github": "^0.116.0"
59
59
  },
60
60
  "scripts": {
61
61
  "generate": "tsx src/codegen/generate-cli.ts",
@@ -0,0 +1,33 @@
1
+ import { describe, expect, test } from "vitest";
2
+ import type { OpWavesSpec } from "@intentius/chant/op/op-waves";
3
+ import { generateForgejoOpWavesPipeline } from "./generate-op-waves-pipeline";
4
+
5
+ const spec: OpWavesSpec = {
6
+ name: "migrations",
7
+ op: "migrate",
8
+ plan: ["./bin/plan", "{target}", "{plan}"],
9
+ waves: [
10
+ { name: "dev", runs: [{ target: "dev" }], gate: "never" },
11
+ { name: "staging", runs: [{ target: "staging" }] },
12
+ { name: "prod", runs: [{ target: "prod" }], environment: { name: "production" } },
13
+ ],
14
+ };
15
+
16
+ describe("generateForgejoOpWavesPipeline", () => {
17
+ test("the waves under the Forgejo dialect: chained by needs, no permissions, the environment named as dropped", () => {
18
+ const { files, jobs } = generateForgejoOpWavesPipeline(spec, { specFile: "waves.json" });
19
+ expect(jobs.map((j) => [j.jobName, j.needs])).toEqual([
20
+ ["wave-1-dev", []],
21
+ ["wave-2-staging", ["wave-1-dev"]],
22
+ ["wave-3-prod", ["wave-2-staging"]],
23
+ ]);
24
+ const yaml = files[0]!.yaml;
25
+ expect(files[0]!.name).toBe("migrations.yml");
26
+ expect(yaml).toContain("# chant dropped the environment of each of these waves: prod -> production.");
27
+ expect(yaml).not.toMatch(/^\s+environment:/m);
28
+ expect(yaml).not.toMatch(/^permissions:/m);
29
+ expect(yaml).toMatch(/wave-3-prod:\n\s+runs-on: docker\n\s+needs:\n\s+- wave-2-staging/);
30
+ expect(yaml).toContain("run: chant run wave --spec waves.json --wave 3\n");
31
+ expect(yaml).toContain("fetch-depth: 0");
32
+ });
33
+ });
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Op waves → Forgejo Actions workflow (#3679).
3
+ *
4
+ * The github workflow ({@link buildGithubOpWavesDoc}) under the Forgejo
5
+ * dialect: action refs rewritten, `permissions:` dropped (the runner reads
6
+ * none), and each wave's `environment:` dropped with a header line saying so,
7
+ * since Forgejo Actions has no environments. The wave's chant gate is what
8
+ * holds it there.
9
+ */
10
+
11
+ import { buildGithubOpWavesDoc } from "@intentius/chant-lexicon-github/components/generate-op-waves-pipeline";
12
+ import { emitOpPipelineYAML, type GithubOpPipelineDoc } from "@intentius/chant-lexicon-github/components/generate-op-pipeline";
13
+ import type { OpWavesSpec } from "@intentius/chant/op/op-waves";
14
+ import type { OpWavesPipelineOptions, OpWavesPipelineResult } from "@intentius/chant/lexicon";
15
+ import { transformWorkflowObject, type ForgejoDialectOptions } from "../dialect";
16
+
17
+ function forgejoize(value: Record<string, unknown>, dialect: ForgejoDialectOptions): Record<string, unknown> {
18
+ return transformWorkflowObject(value, dialect).value as Record<string, unknown>;
19
+ }
20
+
21
+ /** Render an Op waves spec as one Forgejo Actions workflow, `<name>.yml`. */
22
+ export function generateForgejoOpWavesPipeline(
23
+ spec: OpWavesSpec,
24
+ options: OpWavesPipelineOptions,
25
+ dialectOptions: ForgejoDialectOptions = {},
26
+ ): OpWavesPipelineResult {
27
+ const { doc, jobs } = buildGithubOpWavesDoc(spec, options);
28
+ const jobsDoc = Object.fromEntries(
29
+ Object.entries(doc.jobsDoc).map(([name, job]) => {
30
+ const { environment: _dropped, ...rest } = job as Record<string, unknown>;
31
+ return [name, rest];
32
+ }),
33
+ );
34
+ const dropped = spec.waves.filter((w) => w.environment).map((w) => `${w.name} -> ${w.environment!.name}`);
35
+ const forgejoDoc: GithubOpPipelineDoc = {
36
+ ...(dropped.length > 0
37
+ ? {
38
+ header: [
39
+ `# chant dropped the environment of each of these waves: ${dropped.join(", ")}.`,
40
+ "# Forgejo Actions has no environments, so nothing on the forge holds these jobs.",
41
+ "# Each wave's own chant gate does: it records a pending fact on chant/lifecycle",
42
+ "# and the wave exits 3 until `chant approve` approves its set digest.",
43
+ ],
44
+ }
45
+ : {}),
46
+ name: doc.name,
47
+ on: forgejoize(doc.on, dialectOptions),
48
+ ...(doc.env ? { env: forgejoize(doc.env, dialectOptions) } : {}),
49
+ concurrency: forgejoize(doc.concurrency, dialectOptions),
50
+ permissions: {},
51
+ jobsDoc: forgejoize(jobsDoc, dialectOptions),
52
+ };
53
+ return { files: [{ name: `${spec.name}.yml`, yaml: emitOpPipelineYAML(forgejoDoc) }], jobs };
54
+ }
package/src/plugin.ts CHANGED
@@ -19,6 +19,7 @@ import { forgejoSerializer } from "./serializer";
19
19
  import { forgejoContextTools } from "./mcp/context-tools";
20
20
  import { generateForgejoPipeline } from "./components/generate-pipeline";
21
21
  import { generateForgejoOpPipeline } from "./components/generate-op-pipeline";
22
+ import { generateForgejoOpWavesPipeline } from "./components/generate-op-waves-pipeline";
22
23
  import { forgejoLintRules } from "./lint/rules/delegate-to-github";
23
24
  import { forgejoCompletions } from "./lsp/completions";
24
25
  import { forgejoHover } from "./lsp/hover";
@@ -40,6 +41,8 @@ export const forgejoPlugin: LexiconPlugin = {
40
41
 
41
42
  // Generate mode, Op counterpart (#927): scheduled Op → Forgejo Actions workflow.
42
43
  generateOpPipeline: (ops, options) => generateForgejoOpPipeline(ops, options),
44
+ // Ordered waves of Op runs, each behind one gate (#3679).
45
+ generateOpWavesPipeline: (spec, options) => generateForgejoOpWavesPipeline(spec, options),
43
46
 
44
47
  initTemplates(template?: string): InitTemplateSet {
45
48
  if (template === "docker-build") {