/** * Generate mode — scheduled Op → Forgejo Actions workflow YAML (#927). * * The forgejo counterpart to the github Op generator * (`@intentius/chant-lexicon-github/components/generate-op-pipeline`, #927), * exactly like `./generate-pipeline.ts` is the forgejo counterpart to * github's component generator (#969). Reuses github's * `buildGithubOpPipelineDocs` to build the same cron-trigger/concurrency/job * structure, then applies the Forgejo dialect ({@link transformWorkflowObject}) * before emitting. * * One difference from the component generator: Forgejo Actions ignores * `permissions:` entirely (the dialect drops it wherever it appears, see * ../dialect.ts's `DROPPED_KEYS`), so this omits the section outright rather * than emitting a control the runner can't act on. The token a finding-mode * needs still rides the trigger step's `env:` (`GH_TOKEN`/`GITHUB_TOKEN`, * built by github's generator); actual write access on Forgejo is a property * of the runner/token configuration, not the workflow YAML. * * That covers a spec's additive `permissions` too (#2242): an `id-token: * write` added for OIDC is dropped here along with the mode's own scopes, * because the Forgejo runner reads none of them and issues no OIDC token off * a workflow permission. A Forgejo job authenticates to a cloud provider * through whatever the runner already holds. A spec's `setup` steps do cross * over unchanged: Forgejo runs `uses:` steps, so they are emitted in the * position github puts them (after the checkout, ahead of the `beforeScript` * lines) with the dialect's own action-ref rewrite applied — an action with * no mapping in ../actions.ts passes through verbatim and resolves only if * the runner can fetch it. * * `comment` (#2231) crosses over, since chant #2291: it posts onto the * triggering pull request by shelling to `gh` against `${GITHUB_API_URL}`, * and a Forgejo Actions job already sets that (and `github.token`) the same * way a GitHub Actions job does. This generator used to refuse the mode by * name here, on the premise that chant had no way to point `gh` at a Forgejo * instance; that premise was checked against a real Forgejo * 12.0.4+gitea-1.22.0 instance during INTENTIUS/choudoufu#1027 and found * false — the failure was `gh api` resolving a *relative* path against * `/api/v3`, which Forgejo does not serve, not an unreachable forge. See * `reconcilePr`'s `postOrUpdateComment` (`packages/core/src/op/activities/reconcile.ts`) * for the fix. * * `issue` (`gh issue create`/`postOrUpdateGithubIssue`) does not cross over, * and chant #2315 is why: it remained un-refused-but-unverified after #2304 * lifted `comment`'s refusal, and settling that gap the same way — against a * real instance rather than a mock — found a real failure, so the refusal * below is reinstated rather than lifted. Tested against the same * 12.0.4+gitea-1.22.0 image (`codeberg.org/forgejo/forgejo:12`) with a repo, * an issue, and a pull request created on it: the *read* half of * `postOrUpdateGithubIssue` checks out fine on Forgejo — plain paginated * `GET .../issues?state=open` (no `/search/issues`, confirmed absent from a * live Forgejo's own OpenAPI spec) returns pull requests interleaved with * issues exactly as GitHub's endpoint does, and the `.pull_request == null` * filter and the marker `startswith` match both behave identically to * GitHub. The *write* half did not: `postOrUpdateGithubIssue`'s POST and * PATCH calls carried only `GH_TOKEN` (in fact, on the `issue` path, not even * that — `reconcilePr` handed it `execAsync` with no `env` override at all, * chant #2320), and `gh`'s own documented environment variables (`gh help * environment`) scope `GH_TOKEN`/`GITHUB_TOKEN` to "github.com or a subdomain * of ghe.com" — never a self-hosted Forgejo. A `GH_DEBUG=api` POST against * the live instance, with `GH_TOKEN` and `GITHUB_API_URL` set exactly as the * generated workflow sets them, sent no `Authorization` header at all and * Forgejo answered `{"message":"token is required"}` (HTTP 401); adding * `GH_HOST` alongside `GH_TOKEN` made no difference. The same shape was what * `postOrUpdateComment`'s writes carried too, which is why #2304's "verified * against a real Forgejo instance" could not have gone through the generated * workflow's own credential path — either that session had a `gh auth login` * already stored for the test instance, which a real Actions job's fresh * checkout never has, or a different `gh` build was in play. * * Chant #2333 fixed that credential for both modes: `ghCredentialEnv` now * forwards the resolved token as `GH_ENTERPRISE_TOKEN` beside `GH_TOKEN`, * which is the variable `gh` reads for a host in neither of the two classes * above. #2333's own probe against the same image also narrowed the claim * made here: `GH_ENTERPRISE_TOKEN` authenticated the identical call *without* * a `GH_HOST` beside it, because the full URL #2291 built already names the * host. So the premise this refusal was reinstated on no longer holds, and * both halves of `postOrUpdateGithubIssue` — the paginated read and the * POST/PATCH write — were driven green end to end against a live * 12.0.4+gitea-1.22.0 instance under #2333, from a shell with no stored * `gh auth login`. * * The refusal below is nonetheless left standing here, because lifting it is * a generator behavior change with its own YAML surface to settle and #2333 * was scoped to the credential. Lifting it is filed separately; this comment * is corrected rather than acted on so the stated reason does not outlive the * fact it rested on. * * A spec's `environment` (#2257) is dropped on the same terms as * `permissions:`, and for a stronger reason: Forgejo Actions has no * environments at all — no protection rules, no required reviewers, no * per-environment secrets — so the key names an object that does not exist on * the instance. Dropping it silently would be the worst outcome available, * because the whole point of the option is a human holding an apply, so this * also writes a comment into the generated file's header saying which * environment was asked for and that nothing on Forgejo enforces it. What * still holds the apply here is chant's own gate (#2119), which is a fact on * the `chant/lifecycle` branch and needs nothing from the forge. * * The gated-apply notice job (#2243) does not cross over either, for the same * reason and by the same mechanism as `permissions:`: it shells to `gh`, which * a Forgejo `act_runner` neither ships nor can point at its own instance, and * it runs outside the Op's container image, where a hosted GitHub runner's * preinstalled tools would be. So the doc rebuilt below simply does not carry * `gatedNoticeDoc`, and the job is dropped. What does cross over is the half * that needs no forge API: a `push` job still runs with `--gated-exit 0`, so a * Forgejo apply that stops at its gate is a green run rather than a red one, * and `chant run` still writes the gate and the approve command to * `GITHUB_STEP_SUMMARY`, which Forgejo Actions sets like GitHub does. * * The gate-notice job's own reason for being — `outputs: { gated, op, gate, * approve }` on the Op's job, and the `node -e` step that writes them to * `$GITHUB_OUTPUT` — does not survive it either (#2294). Both existed only for * that job to read via `needs..outputs`; with the job gone, so is every * reader, and `buildGithubOpPipelineDocs` is asked for neither * (`emitGatedOutputs: false` below) rather than emitting them here and * stripping them back out. Should the Forgejo posting work in #2291 grow a * notice job of its own, this is the one flag that brings both back. * * A spec's own `variables` (#2290) crosses over unchanged: forgejo reuses the * same job-level `env:` github's builder emits, and the dialect transform * touches only `permissions:`/`environment:`/action refs, never a job's `env:` * mapping. That is what makes a per-Op credential expressible here at all — * `ComponentPipelineOptions.variables` is workflow-scoped and, on Forgejo, * Actions mints no OIDC token to put in `setup` instead, so a job-level * `variables` entry is the only way one Op's job can hold a credential no * sibling Op's job receives. */ import type { ComponentPipelineOptions, OpPipelineResult, ScheduledOpSpec } from "@intentius/chant/lexicon"; import { type ForgejoDialectOptions } from "../dialect.js"; /** * Synthesize one `.forgejo/workflows/*.yml` per scheduled Op. Reuses github's * trigger/job structure ({@link buildGithubOpPipelineDocs}) — its `setup`-step * and additive-permission validation included, so an unpinned action ref is * refused here on the same terms — then applies the Forgejo dialect and drops * `permissions:` (ignored by the Forgejo runner). * Wired into core's Op generate mode via the forgejo lexicon plugin's * `generateOpPipeline` (../plugin.ts). */ export declare function generateForgejoOpPipeline(ops: ScheduledOpSpec[], options?: ComponentPipelineOptions, dialectOptions?: ForgejoDialectOptions): OpPipelineResult; //# sourceMappingURL=generate-op-pipeline.d.ts.map