@intentius/chant-lexicon-forgejo 0.57.0 → 0.59.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.
- package/dist/components/generate-op-pipeline.d.ts +33 -2
- package/dist/components/generate-op-pipeline.d.ts.map +1 -1
- package/dist/integrity.json +2 -2
- package/dist/manifest.json +1 -1
- package/package.json +3 -3
- package/src/components/generate-op-pipeline.test.ts +129 -1
- package/src/components/generate-op-pipeline.ts +44 -2
- package/src/serializer.test.ts +4 -0
|
@@ -16,13 +16,44 @@
|
|
|
16
16
|
* needs still rides the trigger step's `env:` (`GH_TOKEN`/`GITHUB_TOKEN`,
|
|
17
17
|
* built by github's generator); actual write access on Forgejo is a property
|
|
18
18
|
* of the runner/token configuration, not the workflow YAML.
|
|
19
|
+
*
|
|
20
|
+
* That covers a spec's additive `permissions` too (#2242): an `id-token:
|
|
21
|
+
* write` added for OIDC is dropped here along with the mode's own scopes,
|
|
22
|
+
* because the Forgejo runner reads none of them and issues no OIDC token off
|
|
23
|
+
* a workflow permission. A Forgejo job authenticates to a cloud provider
|
|
24
|
+
* through whatever the runner already holds. A spec's `setup` steps do cross
|
|
25
|
+
* over unchanged: Forgejo runs `uses:` steps, so they are emitted in the
|
|
26
|
+
* position github puts them (after the checkout, ahead of the `beforeScript`
|
|
27
|
+
* lines) with the dialect's own action-ref rewrite applied — an action with
|
|
28
|
+
* no mapping in ../actions.ts passes through verbatim and resolves only if
|
|
29
|
+
* the runner can fetch it.
|
|
30
|
+
*
|
|
31
|
+
* One finding-mode does not cross over: `comment` (#2231) posts onto the
|
|
32
|
+
* triggering pull request by shelling to `gh` against the GitHub API and
|
|
33
|
+
* reading the GitHub Actions event payload. Forgejo's API is
|
|
34
|
+
* GitHub-compatible in shape, but chant has no Forgejo client and no host
|
|
35
|
+
* configuration to point `gh` at a Forgejo instance, so this refuses the mode
|
|
36
|
+
* by name rather than generating a job whose finding step fails on every run.
|
|
37
|
+
*
|
|
38
|
+
* The gated-apply notice job (#2243) does not cross over either, for the same
|
|
39
|
+
* reason and by the same mechanism as `permissions:`: it shells to `gh`, which
|
|
40
|
+
* a Forgejo `act_runner` neither ships nor can point at its own instance, and
|
|
41
|
+
* it runs outside the Op's container image, where a hosted GitHub runner's
|
|
42
|
+
* preinstalled tools would be. So the doc rebuilt below simply does not carry
|
|
43
|
+
* `gatedNoticeDoc`, and the job is dropped. What does cross over is the half
|
|
44
|
+
* that needs no forge API: a `push` job still runs with `--gated-exit 0`, so a
|
|
45
|
+
* Forgejo apply that stops at its gate is a green run rather than a red one,
|
|
46
|
+
* and `chant run` still writes the gate and the approve command to
|
|
47
|
+
* `GITHUB_STEP_SUMMARY`, which Forgejo Actions sets like GitHub does.
|
|
19
48
|
*/
|
|
20
49
|
import type { ComponentPipelineOptions, OpPipelineResult, ScheduledOpSpec } from "@intentius/chant/lexicon";
|
|
21
50
|
import { type ForgejoDialectOptions } from "../dialect.js";
|
|
22
51
|
/**
|
|
23
52
|
* Synthesize one `.forgejo/workflows/*.yml` per scheduled Op. Reuses github's
|
|
24
|
-
* trigger/job structure ({@link buildGithubOpPipelineDocs})
|
|
25
|
-
*
|
|
53
|
+
* trigger/job structure ({@link buildGithubOpPipelineDocs}) — its `setup`-step
|
|
54
|
+
* and additive-permission validation included, so an unpinned action ref is
|
|
55
|
+
* refused here on the same terms — then applies the Forgejo dialect and drops
|
|
56
|
+
* `permissions:` (ignored by the Forgejo runner).
|
|
26
57
|
* Wired into core's Op generate mode via the forgejo lexicon plugin's
|
|
27
58
|
* `generateOpPipeline` (../plugin.ts).
|
|
28
59
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generate-op-pipeline.d.ts","sourceRoot":"","sources":["../../src/components/generate-op-pipeline.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"generate-op-pipeline.d.ts","sourceRoot":"","sources":["../../src/components/generate-op-pipeline.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AAOH,OAAO,KAAK,EACV,wBAAwB,EACxB,gBAAgB,EAChB,eAAe,EAChB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAA2B,KAAK,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAOjF;;;;;;;;GAQG;AACH,wBAAgB,yBAAyB,CACvC,GAAG,EAAE,eAAe,EAAE,EACtB,OAAO,GAAE,wBAA6B,EACtC,cAAc,GAAE,qBAA0B,GACzC,gBAAgB,CA2BlB"}
|
package/dist/integrity.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"algorithm": "sha256",
|
|
3
3
|
"artifacts": {
|
|
4
|
-
"manifest.json": "
|
|
4
|
+
"manifest.json": "e73b98e495fadacf1d8eb846417c22f36d149b2f6a5fbaa822e3aa97cd0f987b",
|
|
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": "
|
|
12
|
+
"composite": "0081cd5b78e54825816485bdff8d43a75f9dff6f63a59c8e32c7358c88dcdab2"
|
|
13
13
|
}
|
package/dist/manifest.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@intentius/chant-lexicon-forgejo",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.59.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.
|
|
58
|
-
"@intentius/chant-lexicon-github": "^0.
|
|
57
|
+
"@intentius/chant": "^0.59.0",
|
|
58
|
+
"@intentius/chant-lexicon-github": "^0.59.0"
|
|
59
59
|
},
|
|
60
60
|
"scripts": {
|
|
61
61
|
"generate": "tsx src/codegen/generate-cli.ts",
|
|
@@ -14,7 +14,8 @@ import type { ScheduledOpSpec } from "@intentius/chant/lexicon";
|
|
|
14
14
|
|
|
15
15
|
interface ParsedJob {
|
|
16
16
|
"runs-on"?: string;
|
|
17
|
-
|
|
17
|
+
outputs?: Record<string, string>;
|
|
18
|
+
steps: Array<{ id?: string; uses?: string; run?: string }>;
|
|
18
19
|
}
|
|
19
20
|
interface ParsedDoc {
|
|
20
21
|
on?: Record<string, unknown>;
|
|
@@ -70,3 +71,130 @@ describe("generateForgejoOpPipeline: dialect applied", () => {
|
|
|
70
71
|
expect(ghYaml).toMatch(/^permissions:/m);
|
|
71
72
|
});
|
|
72
73
|
});
|
|
74
|
+
|
|
75
|
+
describe("generateForgejoOpPipeline: non-cron trigger survives the dialect transform (#2084)", () => {
|
|
76
|
+
test("a pull_request trigger round-trips through the Forgejo dialect, with permissions: still dropped", () => {
|
|
77
|
+
const specs: ScheduledOpSpec[] = [
|
|
78
|
+
{ name: "tf-plan", trigger: { kind: "pull_request", branches: ["main"] }, findingMode: "issue" },
|
|
79
|
+
];
|
|
80
|
+
const fj = generateForgejoOpPipeline(specs);
|
|
81
|
+
const gh = generateGithubOpPipeline(specs);
|
|
82
|
+
|
|
83
|
+
const fjDoc = parseFile(fj.files[0].yaml);
|
|
84
|
+
const ghDoc = parseFile(gh.files[0].yaml);
|
|
85
|
+
|
|
86
|
+
expect(fjDoc.on).toEqual({ pull_request: { branches: ["main"] } });
|
|
87
|
+
expect(fjDoc.on).toEqual(ghDoc.on);
|
|
88
|
+
expect(fj.jobs).toEqual(gh.jobs);
|
|
89
|
+
expect(fj.jobs[0].trigger).toEqual({ kind: "pull_request", branches: ["main"] });
|
|
90
|
+
|
|
91
|
+
expect(fj.files[0].yaml).not.toMatch(/^permissions:/m);
|
|
92
|
+
expect(gh.files[0].yaml).toMatch(/^permissions:/m);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
test("a push trigger round-trips through the Forgejo dialect, with permissions: still dropped", () => {
|
|
96
|
+
const specs: ScheduledOpSpec[] = [{ name: "tf-apply", trigger: { kind: "push" } }];
|
|
97
|
+
const fj = generateForgejoOpPipeline(specs);
|
|
98
|
+
const ghDoc = parseFile(generateGithubOpPipeline(specs).files[0].yaml);
|
|
99
|
+
const fjDoc = parseFile(fj.files[0].yaml);
|
|
100
|
+
|
|
101
|
+
expect(fjDoc.on).toEqual({ push: { branches: ["main"] } });
|
|
102
|
+
expect(fjDoc.on).toEqual(ghDoc.on);
|
|
103
|
+
expect(fj.files[0].yaml).not.toMatch(/^permissions:/m);
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
describe("generateForgejoOpPipeline: no comment finding mode (#2231)", () => {
|
|
108
|
+
test("findingMode comment is refused by name, on the pull_request trigger it would otherwise fit", () => {
|
|
109
|
+
// Forgejo Actions runs the same workflow shape and Forgejo's API is
|
|
110
|
+
// GitHub-compatible, but the activity behind the mode shells to `gh`
|
|
111
|
+
// against github.com and reads the GitHub Actions event payload. Nothing
|
|
112
|
+
// in chant points either at a Forgejo instance, so the mode is refused
|
|
113
|
+
// here rather than generating a job that fails at its Report step.
|
|
114
|
+
const specs: ScheduledOpSpec[] = [
|
|
115
|
+
{ name: "app-plan", trigger: { kind: "pull_request", branches: ["main"] }, findingMode: "comment" },
|
|
116
|
+
];
|
|
117
|
+
expect(() => generateForgejoOpPipeline(specs)).toThrow(
|
|
118
|
+
/Scheduled Op "app-plan".*findingMode "comment".*no Forgejo API client/s,
|
|
119
|
+
);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
test("github generates the same spec, so the refusal is forgejo's and not the shared builder's", () => {
|
|
123
|
+
const specs: ScheduledOpSpec[] = [
|
|
124
|
+
{ name: "app-plan", trigger: { kind: "pull_request", branches: ["main"] }, findingMode: "comment" },
|
|
125
|
+
];
|
|
126
|
+
const gh = parseFile(generateGithubOpPipeline(specs).files[0].yaml);
|
|
127
|
+
expect(gh.permissions).toEqual({ contents: "read", "pull-requests": "write" });
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* #2242 crosses the dialect asymmetrically: Forgejo runs `uses:` steps, so a
|
|
133
|
+
* spec's `setup` list is emitted; it ignores `permissions:`, so an additive
|
|
134
|
+
* scope is dropped with the rest of the section rather than emitted as a
|
|
135
|
+
* control the runner never reads.
|
|
136
|
+
*/
|
|
137
|
+
describe("generateForgejoOpPipeline: setup steps and additive permissions (#2242)", () => {
|
|
138
|
+
const OIDC_SPEC: ScheduledOpSpec = {
|
|
139
|
+
name: "app-apply",
|
|
140
|
+
trigger: { kind: "push", branches: ["main"] },
|
|
141
|
+
setup: [
|
|
142
|
+
{
|
|
143
|
+
uses: "aws-actions/configure-aws-credentials@v6",
|
|
144
|
+
with: { "role-to-assume": "${{ vars.AWS_ROLE_ARN }}", "aws-region": "eu-west-1" },
|
|
145
|
+
},
|
|
146
|
+
],
|
|
147
|
+
permissions: { "id-token": "write" },
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
test("emits the setup step after the checkout, with its `with:` intact", () => {
|
|
151
|
+
const doc = parseFile(generateForgejoOpPipeline([OIDC_SPEC]).files[0].yaml);
|
|
152
|
+
const steps = doc.jobs!["app-apply"].steps;
|
|
153
|
+
// The checkout is rewritten to the Forgejo mirror; an action with no
|
|
154
|
+
// mapping in ../actions.ts passes through verbatim.
|
|
155
|
+
expect(steps[0].uses).toContain("actions/checkout@v4");
|
|
156
|
+
expect(steps[1].uses).toBe("aws-actions/configure-aws-credentials@v6");
|
|
157
|
+
expect((steps[1] as { with?: Record<string, string> }).with).toEqual({
|
|
158
|
+
"role-to-assume": "${{ vars.AWS_ROLE_ARN }}",
|
|
159
|
+
"aws-region": "eu-west-1",
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
test("drops the additive permission along with the mode's own scopes", () => {
|
|
164
|
+
const yaml = generateForgejoOpPipeline([OIDC_SPEC]).files[0].yaml;
|
|
165
|
+
expect(yaml).not.toContain("permissions:");
|
|
166
|
+
expect(yaml).not.toContain("id-token");
|
|
167
|
+
// The same spec on github does carry it — this is a dialect drop, not a
|
|
168
|
+
// generator that never computed the scope.
|
|
169
|
+
expect(generateGithubOpPipeline([OIDC_SPEC]).files[0].yaml).toContain("id-token: write");
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
test("refuses an unpinned action ref on the same terms as github", () => {
|
|
173
|
+
expect(() =>
|
|
174
|
+
generateForgejoOpPipeline([{ ...OIDC_SPEC, setup: [{ uses: "aws-actions/configure-aws-credentials@main" }] }]),
|
|
175
|
+
).toThrow(/the action repository's own default branch/);
|
|
176
|
+
});
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
describe("generateForgejoOpPipeline: the gated apply on push (#2243)", () => {
|
|
180
|
+
const pushSpec: ScheduledOpSpec = { name: "app-apply", trigger: { kind: "push", branches: ["main"] } };
|
|
181
|
+
|
|
182
|
+
test("the exit mapping crosses over: a gated apply is a green Forgejo run too", () => {
|
|
183
|
+
// `--gated-exit 0` is `chant run`'s own, so it needs nothing from the
|
|
184
|
+
// runner. `GITHUB_STEP_SUMMARY`, which the gate block goes to, is set by
|
|
185
|
+
// Forgejo's act_runner the same way GitHub sets it.
|
|
186
|
+
const doc = parseFile(generateForgejoOpPipeline([pushSpec]).files[0].yaml);
|
|
187
|
+
const step = doc.jobs!["app-apply"].steps.find((s) => s.id === "chant-run");
|
|
188
|
+
expect(step?.run).toContain("chant run app-apply --gated-exit 0 --json");
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
test("the notice job does not: it shells to `gh`, which no Forgejo runner points at its own instance", () => {
|
|
192
|
+
const fj = parseFile(generateForgejoOpPipeline([pushSpec]).files[0].yaml);
|
|
193
|
+
const gh = parseFile(generateGithubOpPipeline([pushSpec]).files[0].yaml);
|
|
194
|
+
// Dropped the same way `permissions:` is dropped — by not being carried
|
|
195
|
+
// onto the rebuilt doc — and github still has it, so the omission is
|
|
196
|
+
// forgejo's rather than the shared builder's.
|
|
197
|
+
expect(Object.keys(fj.jobs ?? {})).toEqual(["app-apply"]);
|
|
198
|
+
expect(gh.jobs).toHaveProperty("app-apply-gate-notice");
|
|
199
|
+
});
|
|
200
|
+
});
|
|
@@ -16,6 +16,35 @@
|
|
|
16
16
|
* needs still rides the trigger step's `env:` (`GH_TOKEN`/`GITHUB_TOKEN`,
|
|
17
17
|
* built by github's generator); actual write access on Forgejo is a property
|
|
18
18
|
* of the runner/token configuration, not the workflow YAML.
|
|
19
|
+
*
|
|
20
|
+
* That covers a spec's additive `permissions` too (#2242): an `id-token:
|
|
21
|
+
* write` added for OIDC is dropped here along with the mode's own scopes,
|
|
22
|
+
* because the Forgejo runner reads none of them and issues no OIDC token off
|
|
23
|
+
* a workflow permission. A Forgejo job authenticates to a cloud provider
|
|
24
|
+
* through whatever the runner already holds. A spec's `setup` steps do cross
|
|
25
|
+
* over unchanged: Forgejo runs `uses:` steps, so they are emitted in the
|
|
26
|
+
* position github puts them (after the checkout, ahead of the `beforeScript`
|
|
27
|
+
* lines) with the dialect's own action-ref rewrite applied — an action with
|
|
28
|
+
* no mapping in ../actions.ts passes through verbatim and resolves only if
|
|
29
|
+
* the runner can fetch it.
|
|
30
|
+
*
|
|
31
|
+
* One finding-mode does not cross over: `comment` (#2231) posts onto the
|
|
32
|
+
* triggering pull request by shelling to `gh` against the GitHub API and
|
|
33
|
+
* reading the GitHub Actions event payload. Forgejo's API is
|
|
34
|
+
* GitHub-compatible in shape, but chant has no Forgejo client and no host
|
|
35
|
+
* configuration to point `gh` at a Forgejo instance, so this refuses the mode
|
|
36
|
+
* by name rather than generating a job whose finding step fails on every run.
|
|
37
|
+
*
|
|
38
|
+
* The gated-apply notice job (#2243) does not cross over either, for the same
|
|
39
|
+
* reason and by the same mechanism as `permissions:`: it shells to `gh`, which
|
|
40
|
+
* a Forgejo `act_runner` neither ships nor can point at its own instance, and
|
|
41
|
+
* it runs outside the Op's container image, where a hosted GitHub runner's
|
|
42
|
+
* preinstalled tools would be. So the doc rebuilt below simply does not carry
|
|
43
|
+
* `gatedNoticeDoc`, and the job is dropped. What does cross over is the half
|
|
44
|
+
* that needs no forge API: a `push` job still runs with `--gated-exit 0`, so a
|
|
45
|
+
* Forgejo apply that stops at its gate is a green run rather than a red one,
|
|
46
|
+
* and `chant run` still writes the gate and the approve command to
|
|
47
|
+
* `GITHUB_STEP_SUMMARY`, which Forgejo Actions sets like GitHub does.
|
|
19
48
|
*/
|
|
20
49
|
|
|
21
50
|
import {
|
|
@@ -37,8 +66,10 @@ function forgejoize(value: Record<string, unknown>, dialect: ForgejoDialectOptio
|
|
|
37
66
|
|
|
38
67
|
/**
|
|
39
68
|
* Synthesize one `.forgejo/workflows/*.yml` per scheduled Op. Reuses github's
|
|
40
|
-
* trigger/job structure ({@link buildGithubOpPipelineDocs})
|
|
41
|
-
*
|
|
69
|
+
* trigger/job structure ({@link buildGithubOpPipelineDocs}) — its `setup`-step
|
|
70
|
+
* and additive-permission validation included, so an unpinned action ref is
|
|
71
|
+
* refused here on the same terms — then applies the Forgejo dialect and drops
|
|
72
|
+
* `permissions:` (ignored by the Forgejo runner).
|
|
42
73
|
* Wired into core's Op generate mode via the forgejo lexicon plugin's
|
|
43
74
|
* `generateOpPipeline` (../plugin.ts).
|
|
44
75
|
*/
|
|
@@ -47,6 +78,17 @@ export function generateForgejoOpPipeline(
|
|
|
47
78
|
options: ComponentPipelineOptions = {},
|
|
48
79
|
dialectOptions: ForgejoDialectOptions = {},
|
|
49
80
|
): OpPipelineResult {
|
|
81
|
+
for (const spec of ops) {
|
|
82
|
+
if (spec.findingMode === "comment") {
|
|
83
|
+
throw new Error(
|
|
84
|
+
`Scheduled Op "${spec.name}" has findingMode "comment", which posts its finding on the pull request ` +
|
|
85
|
+
`that triggered the run. That activity shells to \`gh\` against the GitHub API and reads the ` +
|
|
86
|
+
`GitHub Actions event payload; chant carries no Forgejo API client to post the equivalent comment ` +
|
|
87
|
+
`(#2231). Use findingMode "issue" here, or generate this Op for github.`,
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
50
92
|
const { files, jobs } = buildGithubOpPipelineDocs(ops, options);
|
|
51
93
|
|
|
52
94
|
return {
|
package/src/serializer.test.ts
CHANGED
|
@@ -151,5 +151,9 @@ describe("forgejoSerializer — inherits PrPlanReport from github (#1983)", () =
|
|
|
151
151
|
// The sticky-comment mechanism is a raw script, nothing to remap or drop.
|
|
152
152
|
expect(result.primary).toContain("Post or update PR comment");
|
|
153
153
|
expect(result.primary).toContain("gh api");
|
|
154
|
+
// Including the `-F` that reads the body from plan.md — forgejo renders
|
|
155
|
+
// the same script, so the #2236 regression would show up here as well.
|
|
156
|
+
expect(result.primary).toContain("-F body=@plan.md");
|
|
157
|
+
expect(result.primary).not.toContain("-f body=@");
|
|
154
158
|
});
|
|
155
159
|
});
|