@intentius/chant-lexicon-forgejo 0.80.0 → 0.82.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.
@@ -1 +1 @@
1
- {"version":3,"file":"generate-op-pipeline.d.ts","sourceRoot":"","sources":["../../src/components/generate-op-pipeline.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+HG;AAOH,OAAO,KAAK,EACV,wBAAwB,EACxB,gBAAgB,EAChB,eAAe,EAChB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAA2B,KAAK,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAmCjF;;;;;;;;GAQG;AACH,wBAAgB,yBAAyB,CACvC,GAAG,EAAE,eAAe,EAAE,EACtB,OAAO,GAAE,wBAA6B,EACtC,cAAc,GAAE,qBAA0B,GACzC,gBAAgB,CA4ClB"}
1
+ {"version":3,"file":"generate-op-pipeline.d.ts","sourceRoot":"","sources":["../../src/components/generate-op-pipeline.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+HG;AAOH,OAAO,KAAK,EACV,wBAAwB,EACxB,gBAAgB,EAChB,eAAe,EAChB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAA2B,KAAK,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAmCjF;;;;;;;;GAQG;AACH,wBAAgB,yBAAyB,CACvC,GAAG,EAAE,eAAe,EAAE,EACtB,OAAO,GAAE,wBAA6B,EACtC,cAAc,GAAE,qBAA0B,GACzC,gBAAgB,CA+ClB"}
@@ -1 +1 @@
1
- {"version":3,"file":"generate-pipeline.d.ts","sourceRoot":"","sources":["../../src/components/generate-pipeline.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAOH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oCAAoC,CAAC;AAC1E,OAAO,KAAK,EACV,wBAAwB,EACxB,uBAAuB,EACxB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAA2B,KAAK,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAOjF;;;;;GAKG;AACH,wBAAgB,uBAAuB,CACrC,UAAU,EAAE,eAAe,EAAE,EAC7B,OAAO,GAAE,wBAA6B,EACtC,cAAc,GAAE,qBAA0B,GACzC,uBAAuB,CAgBzB"}
1
+ {"version":3,"file":"generate-pipeline.d.ts","sourceRoot":"","sources":["../../src/components/generate-pipeline.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAOH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oCAAoC,CAAC;AAC1E,OAAO,KAAK,EACV,wBAAwB,EACxB,uBAAuB,EACxB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAA2B,KAAK,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAOjF;;;;;GAKG;AACH,wBAAgB,uBAAuB,CACrC,UAAU,EAAE,eAAe,EAAE,EAC7B,OAAO,GAAE,wBAA6B,EACtC,cAAc,GAAE,qBAA0B,GACzC,uBAAuB,CAkBzB"}
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "algorithm": "sha256",
3
3
  "artifacts": {
4
- "manifest.json": "07bace16712ce01b8d29bf2bff8bda5895d979e0a8f095ec3b352f0392795148",
4
+ "manifest.json": "bcfc082423806bcc5b376d996ee8d69f3267cdd27a3ccb749c548140c9673af1",
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": "a1a560429db736c187e0b34cf8dd9efc6ad771afefcbf6f802d160d3d5274257"
11
11
  },
12
- "composite": "48d71cec688480cade4f5524f0513445c14d8b2d2bd69e22510f6415f23af2d9"
12
+ "composite": "4147188ccde8f5175e53c3880aa64fdcd6dba2d5ebebb8f93173b33945b986be"
13
13
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "forgejo",
3
- "version": "0.80.0",
3
+ "version": "0.82.0",
4
4
  "chantVersion": ">=0.1.0",
5
5
  "namespace": "Forgejo",
6
6
  "intrinsics": [],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@intentius/chant-lexicon-forgejo",
3
- "version": "0.80.0",
3
+ "version": "0.82.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.80.0",
58
- "@intentius/chant-lexicon-github": "^0.80.0"
57
+ "@intentius/chant": "^0.82.0",
58
+ "@intentius/chant-lexicon-github": "^0.82.0"
59
59
  },
60
60
  "scripts": {
61
61
  "generate": "tsx src/codegen/generate-cli.ts",
@@ -298,9 +298,14 @@ describe("generateForgejoOpPipeline: a dropped deployment environment (#2257)",
298
298
  const APPLY: ScheduledOpSpec = { name: "app-apply", trigger: { kind: "push", branches: ["main"] } };
299
299
  const GATED: ScheduledOpSpec = { ...APPLY, environment: { name: "production" } };
300
300
 
301
- /** The document a spec with no `environment` emitted before the option existed. */
301
+ /**
302
+ * The document a spec with no `environment` emitted before the option
303
+ * existed. The `name:` line came later, from #2601, for every spec alike.
304
+ */
302
305
  const AUDIT_YAML_BEFORE_2257 =
303
306
  [
307
+ "name: actions-audit",
308
+ "",
304
309
  "on:",
305
310
  " schedule:",
306
311
  " - cron: '0 6 * * *'",
@@ -353,7 +358,16 @@ describe("generateForgejoOpPipeline: a dropped deployment environment (#2257)",
353
358
  ]).files;
354
359
  expect(files[0].yaml).toContain("# chant dropped `environment: production`");
355
360
  expect(files[1].yaml).not.toContain("# chant dropped");
356
- expect(files[1].yaml.startsWith("on:")).toBe(true);
361
+ expect(files[1].yaml.startsWith("name: app-watch\n\non:")).toBe(true);
362
+ });
363
+
364
+ test("the dropped-environment header comes first, then the workflow's name (#2601)", () => {
365
+ const yaml = generateForgejoOpPipeline([GATED]).files[0].yaml;
366
+ const lines = yaml.split("\n");
367
+ const nameAt = lines.indexOf("name: app-apply");
368
+ expect(nameAt).toBeGreaterThan(0);
369
+ expect(lines.slice(0, nameAt - 1).every((line) => line.startsWith("# "))).toBe(true);
370
+ expect(lines[nameAt + 2]).toBe("on:");
357
371
  });
358
372
 
359
373
  test("refuses the same malformed environment github refuses, through the shared builder", () => {
@@ -219,6 +219,9 @@ export function generateForgejoOpPipeline(
219
219
  const environment = ops[index].environment;
220
220
  const forgejoDoc: GithubOpPipelineDoc = {
221
221
  ...(environment ? { header: droppedEnvironmentHeader(environment.name) } : {}),
222
+ // Forgejo Actions reads `name:` as GitHub does (#2601), so the Op's
223
+ // name carries over and the Actions list shows it instead of the path.
224
+ ...(doc.name ? { name: doc.name } : {}),
222
225
  on: forgejoize(doc.on, dialectOptions),
223
226
  ...(doc.env ? { env: forgejoize(doc.env, dialectOptions) } : {}),
224
227
  concurrency: forgejoize(doc.concurrency, dialectOptions),
@@ -0,0 +1,32 @@
1
+ /**
2
+ * A workspace member's pipeline on Forgejo (#2542): the github shape, with
3
+ * the member's path filter and working directory carried across the dialect.
4
+ */
5
+
6
+ import { describe, test, expect } from "vitest";
7
+ import { parseYAML } from "@intentius/chant/yaml";
8
+ import type { DriverComponent } from "@intentius/chant/components/driver";
9
+ import { generateForgejoPipeline } from "./generate-pipeline";
10
+ import { generateForgejoOpPipeline } from "./generate-op-pipeline";
11
+
12
+ const components: DriverComponent[] = [{ name: "api", dependsOn: [], deploy: [] }];
13
+
14
+ describe("generateForgejoPipeline for a workspace member", () => {
15
+ test("working directory and name survive the dialect, and the triggers stay the plain pipeline's", () => {
16
+ const member = { name: "api", dir: "services/api", file: ".forgejo/workflows/chant-api-staging.yml" };
17
+ const doc = parseYAML(generateForgejoPipeline(components, { env: "staging", member }).yaml) as Record<string, unknown>;
18
+ expect(doc.name).toBe("chant-components-api-staging");
19
+ expect(Object.keys(doc.on as Record<string, unknown>)).toEqual(["workflow_dispatch"]);
20
+ expect(doc.defaults).toEqual({ run: { "working-directory": "services/api" } });
21
+ });
22
+
23
+ test("an Op's file and job carry the member", () => {
24
+ const result = generateForgejoOpPipeline([{ name: "plan", trigger: { kind: "pull_request" } }], {
25
+ member: { name: "api", dir: "services/api", fileDir: ".forgejo/workflows" },
26
+ });
27
+ expect(result.files[0].name).toBe("api-plan.yml");
28
+ const doc = parseYAML(result.files[0].yaml) as { on: Record<string, unknown>; jobs: Record<string, { defaults?: unknown }> };
29
+ expect(doc.on.pull_request).toEqual({ paths: ["services/api/**", ".forgejo/workflows/api-plan.yml"] });
30
+ expect(doc.jobs["plan"].defaults).toEqual({ run: { "working-directory": "services/api" } });
31
+ });
32
+ });
@@ -27,6 +27,7 @@ function pilotComponents(): DriverComponent[] {
27
27
 
28
28
  interface ParsedStep {
29
29
  name?: string;
30
+ id?: string;
30
31
  uses?: string;
31
32
  run?: string;
32
33
  with?: Record<string, unknown>;
@@ -35,6 +36,7 @@ interface ParsedJob {
35
36
  "runs-on"?: string;
36
37
  container?: string;
37
38
  needs?: string[];
39
+ outputs?: Record<string, string>;
38
40
  steps: ParsedStep[];
39
41
  }
40
42
  function parsedJobs(yaml: string): Record<string, ParsedJob> {
@@ -128,6 +130,23 @@ describe("generateForgejoPipeline: a promote job (#2575)", () => {
128
130
  const promote = jobs["promote-prod"];
129
131
  expect(promote.needs).toEqual(["api"]);
130
132
  expect(promote.steps.find((s) => s.name === "Download api build archive")?.with).toEqual({ name: "api-archive", path: "dist" });
131
- expect(promote.steps.some((s) => s.run === "chant components promote --from staging --to prod")).toBe(true);
133
+ expect(promote.steps.some((s) => s.run === 'chant components promote --from staging --to prod --digest "api=${{ needs.api.outputs.digest }}"')).toBe(true);
134
+ });
135
+
136
+ test("the digest the api job recorded reaches the promote command through a job output (#2602)", () => {
137
+ const components: DriverComponent[] = [
138
+ {
139
+ name: "api",
140
+ deploy: [
141
+ { phase: "Build", steps: [{ kind: "docker-build", context: ".", into: "dist/api.tar" }] },
142
+ { phase: "Publish", steps: [{ kind: "publish-image", from: "archive:dist/api.tar" }] },
143
+ ],
144
+ },
145
+ ];
146
+ const jobs = parsedJobs(generateForgejoPipeline(components, { env: "staging", promoteTo: "prod" }).yaml);
147
+ expect(jobs["api"].outputs).toEqual({ digest: "${{ steps.digest.outputs.digest }}" });
148
+ expect(jobs["api"].steps.some((s) => s.run?.includes("--digest-file api.digest"))).toBe(true);
149
+ expect(jobs["api"].steps.find((s) => s.id === "digest")?.run).toBe('echo "digest=$(cut -d= -f2- api.digest)" >> "$GITHUB_OUTPUT"');
150
+ expect(jobs["promote-prod"].steps.some((s) => s.run?.includes('--digest "api=${{ needs.api.outputs.digest }}"'))).toBe(true);
132
151
  });
133
152
  });
@@ -62,6 +62,8 @@ export function generateForgejoPipeline(
62
62
  environment: doc.environment,
63
63
  on: forgejoize(doc.on, dialectOptions),
64
64
  ...(doc.env ? { env: forgejoize(doc.env, dialectOptions) } : {}),
65
+ // A workspace member's working directory (#2542) reads the same on Forgejo.
66
+ ...(doc.defaults ? { defaults: doc.defaults } : {}),
65
67
  jobsDoc: forgejoize(doc.jobsDoc, dialectOptions),
66
68
  stages: doc.stages,
67
69
  jobs: doc.jobs,