@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,117 @@
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 --wave-gate release
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
+ apply-wave-1:
39
+ if: github.event_name == 'push'
40
+ runs-on: docker
41
+ container: node:22
42
+ concurrency:
43
+ group: chant-apply-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: Restore the newest record of this commit's apply
51
+ uses: https://code.forgejo.org/actions/cache/restore@v4
52
+ with:
53
+ path: .chant/pr-resume/pr-apply.json
54
+ key: chant-apply-prod-${{ github.sha }}-${{ github.run_id }}-${{ github.run_attempt }}-wave-1
55
+ restore-keys: chant-apply-prod-${{ github.sha }}-
56
+ - name: Apply wave 1, under the approval its plan has
57
+ env:
58
+ BASE_SHA: '${{ github.event.before }}'
59
+ GITHUB_TOKEN: '${{ github.token }}'
60
+ CHANT_RUNNER_KEY: '${{ secrets.CHANT_RUNNER_KEY }}'
61
+ run: |
62
+ 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
63
+ chant ci run apply --in-place --base "$BASE_SHA" --env prod --gate pr-apply --forge forgejo --wave-gate release --wave 1
64
+ - name: Keep the record for the next wave and a re-run
65
+ if: always()
66
+ uses: https://code.forgejo.org/actions/cache/save@v4
67
+ with:
68
+ path: .chant/pr-resume/pr-apply.json
69
+ key: chant-apply-prod-${{ github.sha }}-${{ github.run_id }}-${{ github.run_attempt }}-wave-1
70
+ - name: Keep the wave 1 apply report
71
+ if: always()
72
+ uses: https://code.forgejo.org/actions/upload-artifact@v3
73
+ with:
74
+ name: chant-pr-apply-wave-1
75
+ path: .chant/pr
76
+ if-no-files-found: ignore
77
+ apply-wave-2:
78
+ if: github.event_name == 'push'
79
+ runs-on: docker
80
+ needs:
81
+ - apply-wave-1
82
+ container: node:22
83
+ concurrency:
84
+ group: chant-apply-prod
85
+ cancel-in-progress: false
86
+ steps:
87
+ - uses: https://code.forgejo.org/actions/checkout@v7
88
+ with:
89
+ fetch-depth: 0
90
+ - 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)
91
+ - name: Restore the newest record of this commit's apply
92
+ uses: https://code.forgejo.org/actions/cache/restore@v4
93
+ with:
94
+ path: .chant/pr-resume/pr-apply.json
95
+ key: chant-apply-prod-${{ github.sha }}-${{ github.run_id }}-${{ github.run_attempt }}-wave-2
96
+ restore-keys: chant-apply-prod-${{ github.sha }}-
97
+ - name: Apply wave 2, under the approval its plan has
98
+ env:
99
+ BASE_SHA: '${{ github.event.before }}'
100
+ GITHUB_TOKEN: '${{ github.token }}'
101
+ CHANT_RUNNER_KEY: '${{ secrets.CHANT_RUNNER_KEY }}'
102
+ run: |
103
+ 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
104
+ chant ci run apply --in-place --base "$BASE_SHA" --env prod --gate pr-apply --forge forgejo --wave-gate release --wave 2
105
+ - name: Keep the record for the next wave and a re-run
106
+ if: always()
107
+ uses: https://code.forgejo.org/actions/cache/save@v4
108
+ with:
109
+ path: .chant/pr-resume/pr-apply.json
110
+ key: chant-apply-prod-${{ github.sha }}-${{ github.run_id }}-${{ github.run_attempt }}-wave-2
111
+ - name: Keep the wave 2 apply report
112
+ if: always()
113
+ uses: https://code.forgejo.org/actions/upload-artifact@v3
114
+ with:
115
+ name: chant-pr-apply-wave-2
116
+ path: .chant/pr
117
+ if-no-files-found: ignore
@@ -26,7 +26,8 @@ jobs:
26
26
  BASE_SHA: '${{ github.event.pull_request.base.sha }}'
27
27
  PR_NUMBER: '${{ github.event.pull_request.number }}'
28
28
  GITHUB_TOKEN: '${{ github.token }}'
29
- run: chant components pr-plan --base "$BASE_SHA" --pr "$PR_NUMBER" --env prod --gate pr-apply --output .chant/pr --forge forgejo
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
30
31
  - name: Keep the plan report
31
32
  if: always()
32
33
  uses: https://code.forgejo.org/actions/upload-artifact@v3
@@ -56,7 +57,10 @@ jobs:
56
57
  env:
57
58
  BASE_SHA: '${{ github.event.before }}'
58
59
  GITHUB_TOKEN: '${{ github.token }}'
59
- 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
60
+ CHANT_RUNNER_KEY: '${{ secrets.CHANT_RUNNER_KEY }}'
61
+ run: |
62
+ 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
63
+ chant ci run apply --in-place --base "$BASE_SHA" --env prod --gate pr-apply --forge forgejo
60
64
  - name: Keep the record for a re-run
61
65
  if: always()
62
66
  uses: https://code.forgejo.org/actions/cache/save@v4
@@ -14,6 +14,7 @@ import { describe, expect, test } from "vitest";
14
14
  import { parseYAML } from "@intentius/chant/yaml";
15
15
  import type { DriverComponent } from "@intentius/chant/components/driver";
16
16
  import { generateGithubPipeline } from "@intentius/chant-lexicon-github/components/generate-pipeline";
17
+ import { githubBaseFallback } from "@intentius/chant/components/pr-pipeline";
17
18
  import { generateForgejoPipeline } from "./generate-pipeline";
18
19
 
19
20
  const ESTATE: DriverComponent[] = [
@@ -33,13 +34,19 @@ function hasActionlint(): boolean {
33
34
  }
34
35
 
35
36
  describe("the Forgejo pull-request workflow", () => {
36
- const result = generateForgejoPipeline(ESTATE, { env: "prod", prLoop: {} });
37
+ const result = generateForgejoPipeline(ESTATE, { env: "prod", prLoop: { applyOnMerge: true } });
37
38
 
38
39
  test("matches its golden", () => {
39
40
  if (process.env.UPDATE_GOLDEN) writeFileSync(GOLDEN, result.yaml);
40
41
  expect(result.yaml).toBe(readFileSync(GOLDEN, "utf-8"));
41
42
  });
42
43
 
44
+ test("both jobs run chant ci run with the runner key secret, as on GitHub (#4156)", () => {
45
+ expect(result.yaml).toContain("chant ci run plan --in-place --no-push");
46
+ expect(result.yaml.match(/CHANT_RUNNER_KEY: '\$\{\{ secrets\.CHANT_RUNNER_KEY \}\}'/g)).toHaveLength(2);
47
+ expect(result.yaml).not.toContain("chant components pr-");
48
+ });
49
+
43
50
  test("its commands talk to Forgejo, on Forgejo's runner, with the job permissions dropped", () => {
44
51
  expect(result.yaml).toContain("--forge forgejo");
45
52
  expect(result.yaml).not.toContain("--forge github");
@@ -47,8 +54,17 @@ describe("the Forgejo pull-request workflow", () => {
47
54
  expect(result.yaml).not.toMatch(/^\s*permissions:/m);
48
55
  });
49
56
 
57
+ test("a first push's all-zero github.event.before is settled before pr-apply reads it, as on GitHub (#4004)", () => {
58
+ const doc = parseYAML(result.yaml) as { jobs: Record<string, { steps: Array<{ run?: string }> }> };
59
+ const apply = doc.jobs.apply!.steps.find((s) => s.run?.includes("ci run apply"))!.run!.trimEnd().split("\n");
60
+ expect(apply[0]).toBe(githubBaseFallback());
61
+ expect(apply[0]).toContain('git merge-base FETCH_HEAD "$GITHUB_SHA"');
62
+ expect(apply[1]).toMatch(/^chant ci run apply --in-place --base "\$BASE_SHA"/);
63
+ expect(doc.jobs.plan!.steps.find((s) => s.run?.includes("ci run plan"))!.run).not.toContain("0000000000000000000000000000000000000000");
64
+ });
65
+
50
66
  test("events and reportUrl reach Forgejo's jobs as they reach GitHub's (#4007)", () => {
51
- const yaml = generateForgejoPipeline(ESTATE, { env: "prod", prLoop: { events: true, reportUrl: "file:///srv/chant-reports" } }).yaml;
67
+ const yaml = generateForgejoPipeline(ESTATE, { env: "prod", prLoop: { applyOnMerge: true, events: true, reportUrl: "file:///srv/chant-reports" } }).yaml;
52
68
  expect(yaml.match(/--forge forgejo --events-json --report-url "file:\/\/\/srv\/chant-reports"/g)).toHaveLength(2);
53
69
  expect(yaml.match(/CHANT_RUN_EVENTS_DIR: \.chant\/pr\/run-events/g)).toHaveLength(2);
54
70
  });
@@ -56,7 +72,8 @@ describe("the Forgejo pull-request workflow", () => {
56
72
  test("the apply keeps its resume record in the runner's cache, through the Forgejo mirror of actions/cache (#3543)", () => {
57
73
  expect(result.yaml).toContain("uses: https://code.forgejo.org/actions/cache/restore@v4");
58
74
  expect(result.yaml).toContain("uses: https://code.forgejo.org/actions/cache/save@v4");
59
- expect(result.yaml).toContain("--forge forgejo --require-review --resume .chant/pr-resume/pr-apply.json");
75
+ // ci run's inner pr-apply resumes from it (#4156): the job runs in place, where the cache restored it.
76
+ expect(result.yaml).toContain("chant ci run apply --in-place --base");
60
77
  });
61
78
 
62
79
  test("keeps its reports with upload-artifact v3, the version a Forgejo runner runs, while GitHub keeps v4 (#4008)", () => {
@@ -66,7 +83,7 @@ describe("the Forgejo pull-request workflow", () => {
66
83
  // v3 uploads hidden files with no input asking for it, so .chant/pr is still kept.
67
84
  expect(result.yaml).not.toContain("include-hidden-files");
68
85
  expect(result.yaml).toContain("path: .chant/pr");
69
- const github = generateGithubPipeline(ESTATE, { env: "prod", prLoop: {} }).yaml;
86
+ const github = generateGithubPipeline(ESTATE, { env: "prod", prLoop: { applyOnMerge: true } }).yaml;
70
87
  expect(github.match(/uses: actions\/upload-artifact@v4$/gm)).toHaveLength(2);
71
88
  expect(github).toContain("include-hidden-files: true");
72
89
  });
@@ -87,7 +104,7 @@ describe("the Forgejo pull-request workflow", () => {
87
104
  });
88
105
 
89
106
  describe("the forgejo pull-request pipeline with build (#4088)", () => {
90
- const result = generateForgejoPipeline(ESTATE, { env: "prod", prLoop: { build: true } });
107
+ const result = generateForgejoPipeline(ESTATE, { env: "prod", prLoop: { applyOnMerge: true, build: true } });
91
108
  const doc = parseYAML(result.yaml) as Record<string, any>;
92
109
  const golden = join(import.meta.dirname, "__fixtures__", "pr-loop-build.forgejo.golden.yml");
93
110
 
@@ -105,14 +122,31 @@ describe("the forgejo pull-request pipeline with build (#4088)", () => {
105
122
  });
106
123
 
107
124
  test("buildCommand replaces the build line and turns the option on", () => {
108
- const yaml = generateForgejoPipeline(ESTATE, { env: "prod", prLoop: { buildCommand: "npm run build" } }).yaml;
125
+ const yaml = generateForgejoPipeline(ESTATE, { env: "prod", prLoop: { applyOnMerge: true, buildCommand: "npm run build" } }).yaml;
109
126
  expect(yaml).toContain("npm run build");
110
127
  expect(yaml).not.toContain("chant build --env");
111
128
  expect(yaml).toContain("chant lint");
112
129
  });
113
130
 
114
131
  test("the default output carries neither line", () => {
115
- const plain = generateForgejoPipeline(ESTATE, { env: "prod", prLoop: {} }).yaml;
132
+ const plain = generateForgejoPipeline(ESTATE, { env: "prod", prLoop: { applyOnMerge: true } }).yaml;
116
133
  expect(plain).not.toContain("chant lint");
117
134
  });
118
135
  });
136
+
137
+ describe("the Forgejo pull-request workflow records on merge by default (#4168)", () => {
138
+ const result = generateForgejoPipeline(ESTATE, { env: "prod", prLoop: {} });
139
+ const golden = join(import.meta.dirname, "__fixtures__", "pr-loop-record.forgejo.golden.yml");
140
+
141
+ test("matches its golden", () => {
142
+ if (process.env.UPDATE_GOLDEN) writeFileSync(golden, result.yaml);
143
+ expect(result.yaml).toBe(readFileSync(golden, "utf-8"));
144
+ });
145
+
146
+ test("a push runs ci run record against Forgejo, and nothing applies", () => {
147
+ expect(result.jobs.map((j) => j.jobName)).toEqual(["plan", "record"]);
148
+ expect(result.yaml).toContain('chant ci run record --in-place --base "$BASE_SHA" --env prod --gate pr-apply --forge forgejo');
149
+ expect(result.yaml).not.toContain("ci run apply");
150
+ expect(result.yaml).toContain("group: chant-record-prod");
151
+ });
152
+ });
@@ -0,0 +1,42 @@
1
+ /**
2
+ * The Forgejo pull-request workflow with a gate per wave on merge (#4003):
3
+ * github's wave jobs with the Forgejo dialect applied and the commands
4
+ * talking to Forgejo. The whole file has a golden.
5
+ */
6
+
7
+ import { readFileSync, writeFileSync } from "node:fs";
8
+ import { join } from "node:path";
9
+ import { describe, expect, test } from "vitest";
10
+ import { parseYAML } from "@intentius/chant/yaml";
11
+ import type { DriverComponent } from "@intentius/chant/components/driver";
12
+ import { generateForgejoPipeline } from "./generate-pipeline";
13
+
14
+ const root = (name: string, dependsOn: string[] = []): DriverComponent => ({
15
+ name,
16
+ dependsOn,
17
+ deploy: [{ phase: "Apply", steps: [{ kind: "terraform-apply", root: name }] }],
18
+ });
19
+ const ESTATE: DriverComponent[] = [root("net"), root("app", ["net"])];
20
+
21
+ const GOLDEN = join(import.meta.dirname, "__fixtures__", "pr-loop-waves.forgejo.golden.yml");
22
+
23
+ describe("the Forgejo pull-request workflow with a gate per wave (#4003)", () => {
24
+ const result = generateForgejoPipeline(ESTATE, { env: "prod", prLoop: { applyOnMerge: true }, gatedWaves: { gate: "release" } });
25
+ const doc = parseYAML(result.yaml) as { jobs: Record<string, { needs?: string[]; "runs-on": string; steps: Array<{ run?: string; uses?: string }> }> };
26
+
27
+ test("matches its golden", () => {
28
+ if (process.env.UPDATE_GOLDEN) writeFileSync(GOLDEN, result.yaml);
29
+ expect(result.yaml).toBe(readFileSync(GOLDEN, "utf-8"));
30
+ });
31
+
32
+ test("an apply job per wave on Forgejo's runner, each passing its wave and talking to Forgejo", () => {
33
+ expect(Object.keys(doc.jobs)).toEqual(["plan", "apply-wave-1", "apply-wave-2"]);
34
+ expect(doc.jobs["apply-wave-2"]!.needs).toEqual(["apply-wave-1"]);
35
+ for (const k of [1, 2]) {
36
+ const job = doc.jobs[`apply-wave-${k}`]!;
37
+ expect(job["runs-on"]).toBe("docker");
38
+ expect(job.steps.find((s) => s.run?.includes("ci run apply"))!.run).toContain(`--forge forgejo --wave-gate release --wave ${k}`);
39
+ expect(job.steps.some((s) => s.uses === "https://code.forgejo.org/actions/cache/restore@v4")).toBe(true);
40
+ }
41
+ });
42
+ });