Actions and CI/CD
fremforge runs Forgejo Actions, a GitHub Actions-compatible CI/CD system. Workflow files use GitHub Actions YAML syntax and approximately 95% of GitHub Marketplace actions work without modification.
Workflow file location
Place workflow files in .forgejo/workflows/<name>.yaml inside your repository. Forgejo also reads .github/workflows/ for compatibility with repositories migrated from GitHub, but .forgejo/workflows/ is the canonical location on fremforge.
Trigger events
| Event | Trigger |
|---|---|
push | Any push to matched branches or tags |
pull_request | PR opened, synchronised, or reopened |
pull_request_target | PR against target repo (for forks) — see security note below |
schedule | Cron schedule (UTC) |
workflow_dispatch | Manual trigger from UI or API |
workflow_call | Reusable workflow called by another workflow |
release | Release published, created, or edited |
issues | Issue opened, edited, closed, or labeled |
issue_comment | Comment on issue or PR |
create | Branch or tag created |
delete | Branch or tag deleted |
registry_package | Package published or updated |
Context variables
fremforge uses github.* context names for compatibility with the GitHub Actions ecosystem. These refer to the fremforge/Forgejo instance, not GitHub.
| Variable | Value |
|---|---|
github.actor | Username of the user who triggered the run |
github.repository | <org>/<repo> |
github.ref | Full git ref, e.g. refs/heads/main |
github.sha | Commit SHA |
github.event_name | Event that triggered the workflow |
github.run_id | Unique run identifier |
github.server_url | https://frem.sh |
github.api_url | https://frem.sh/api/v1 |
Built-in secrets
| Secret | Description |
|---|---|
secrets.FORGEJO_TOKEN | Auto-generated per-job token; scoped to the repo; read-only by default |
secrets.GITHUB_TOKEN | Alias for FORGEJO_TOKEN (compatibility) |
Built-in tokens are short-lived and scoped to the current workflow run. They cannot be used outside the job that received them.
Permissions block
The default permission set is contents: read. All other permissions default to none unless explicitly declared.
permissions:
contents: read # git checkout
packages: write # publish to package registry
id-token: write # OIDC token federation
issues: write # create / update issues
pull-requests: write # comment on PRsDeclare permissions at the workflow level (applies to all jobs) or at the individual job level. Job-level declarations override workflow-level ones.
Minimal workflow example
name: CI
on:
push:
branches: [main]
pull_request:
jobs:
test:
runs-on: fremforge
steps:
- uses: actions/checkout@v4
- name: Run tests
run: npm ci && npm testReusable workflows
Extract common logic into a reusable workflow called by other workflows with workflow_call:
# .forgejo/workflows/reusable.yaml
on:
workflow_call:
inputs:
environment:
required: true
type: string
jobs:
deploy:
runs-on: fremforge
environment: ${{ inputs.environment }}
steps:
- run: echo "Deploying to ${{ inputs.environment }}"Call it from another workflow:
jobs:
deploy-staging:
uses: ./.forgejo/workflows/reusable.yaml@main
with:
environment: stagingCaching
Use actions/cache to persist directories between runs:
- uses: actions/cache@v3
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-Cache hits speed up jobs significantly for package managers, build tools, and compiled outputs. Cache is scoped per branch and falls back to the base branch on miss.
Environments and deployment gates
Environments add approval gates, required reviewers, and environment-scoped secrets to deploy jobs.
Create environments under Repo Settings → Environments. Configure required reviewers to require manual approval before the job runs.
Reference an environment in a job:
jobs:
deploy-prod:
runs-on: fremforge
environment: production
steps:
- run: ./deploy.shIf production has required reviewers configured, the job pauses for approval before executing. Secrets set on the production environment are injected only into this job.
Compatibility notes
- ~95% of GitHub Marketplace actions work without modification.
secrets.GITHUB_TOKENis an alias forsecrets.FORGEJO_TOKEN. Actions using it work as-is.- Actions that call the GitHub API directly (hardcoded
api.github.com) will fail. The fremforge API URL ishttps://frem.sh/api/v1. actions/upload-artifactandactions/download-artifactwork.- GitHub Packages (
ghcr.io,pkg.github.com) are not available. Use the fremforge package registry instead. - Actions that require GitHub-specific features (GitHub Copilot, GitHub Codespaces, GitHub Packages) have no fremforge equivalent.
See Marketplace compatibility for the full top-100 action compatibility matrix.
Fork-PR secret reach — pull_request_target policy
pull_request_target runs workflows in the target repo’s context when a PR comes from a fork — meaning the workflow has access to the target repo’s secrets even though the workflow file may have been modified by the fork author. This is the exact attack shape that produced the GitHub Actions Marketplace 2023 incident class.
fremforge policy (P2-THREAT-pull-request-target, 2026-05-19): workflows that use pull_request_target MUST NOT execute fork-supplied code with secrets present. Two acceptable patterns:
pull_requestinstead ofpull_request_targetwhen the workflow needs to run fork code. The fork-PR’s workflow runs in the fork’s context — no target-repo secrets reachable. This is the default and the safe shape.pull_request_targetONLY for read-only labeller/triage/comment workflows that explicitly check outgithub.sha(the merge-base, not the fork head) and don’t run any fork-supplied install/build/test step.
Workflows that mix pull_request_target with actions/checkout@v4 against github.event.pull_request.head.sha are a SEV-1 secret-exfiltration vector and will be flagged by the pre-merge review. A pre-receive Forgejo hook that statically rejects the dangerous combination is on the future-hardening list (P2-THREAT-pull-request-target-static-check); until it ships, code-review is the active gate.
For full background: Forgejo Actions security model is the same as GitHub’s. The pull_request_target semantics are inherited unchanged.
Image-source policy
uses: statements come in two shapes — both are accepted on fremforge but the egress posture differs:
uses: <owner>/<repo>@<sha>— resolved againstfrem.sh/mirrorsperDEFAULT_ACTIONS_URLso the action source is always a fremverk-mirrored copy that’s been through SBOM + SAST + Trivy gates. New top-level actions land via the dev-tools-mirror-sync CronJob (every ~1h). This is the recommended shape.uses: docker://<registry>/<image>:<tag>— Forgejo Actions resolves this by pulling the image directly from the registry URL the workflow author specified. There’s no automatic rewrite to a fremforge mirror, and no built-in allowlist on the registry portion today. Pin to a digest (docker://swr.eu-de.otc.t-systems.com/fremforge-prd/cache-…@sha256:…) and prefer the fremverk SWR mirror namespace (swr.eu-de.otc.t-systems.com/fremforge-prd/cache-<image>) over external registries. A pre-receive Forgejo hook or runner-side allowlist fordocker://is on the future-hardening list (P2-THREAT-docker-uri-allowlist) — until it ships, document the policy in your repo’sCONTRIBUTING.mdand treat unmirroreddocker://references as a code-review issue.
Cross-references
- CI runners, available runner labels, BYO runners, concurrency limits
- Secrets, secret scopes, rotation, OIDC as an alternative
- Marketplace compatibility, action compatibility matrix
- OIDC token federation, keyless cloud deployment from jobs
- Package registry, publishing packages from CI