Pause a workflow for a mobile (or comment) approval before continuing. Forge-native, fail-closed deploy gate Forgejo lacks. No github.com.
  • JavaScript 79.5%
  • Shell 15.7%
  • Makefile 4.8%
Find a file
Stephen Way 65482563fd
All checks were successful
test / unit (push) Successful in 31s
Initial release: fjord-approval-gate-action
Pause a workflow until an authorized human approves, from the Fjord
mobile app (via the relay) or a /approve comment on the approval thread.
Fail-closed: a timeout or /deny stops the deploy. The deploy-approval
gate Forgejo has no native equivalent for; unlike GitHub's it's
self-hostable.

Approval state lives in the forge (issue + comments), so a killed runner
re-runs with the same issue-number and finds the decision rather than
stranding a half-deploy. node20 action with a pure command/auth core.
Offline suite covers approve, deny, unauthorized-ignored,
self-approval-blocked, and fail-closed timeout.
2026-06-01 18:42:14 -07:00
.forgejo/workflows Initial release: fjord-approval-gate-action 2026-06-01 18:42:14 -07:00
src Initial release: fjord-approval-gate-action 2026-06-01 18:42:14 -07:00
tests Initial release: fjord-approval-gate-action 2026-06-01 18:42:14 -07:00
.gitignore Initial release: fjord-approval-gate-action 2026-06-01 18:42:14 -07:00
action.yml Initial release: fjord-approval-gate-action 2026-06-01 18:42:14 -07:00
CHANGELOG.md Initial release: fjord-approval-gate-action 2026-06-01 18:42:14 -07:00
LICENSE Initial release: fjord-approval-gate-action 2026-06-01 18:42:14 -07:00
Makefile Initial release: fjord-approval-gate-action 2026-06-01 18:42:14 -07:00
README.md Initial release: fjord-approval-gate-action 2026-06-01 18:42:14 -07:00

Fjord Approval Gate

The deploy approval gate Forgejo doesn't have. Pause a workflow until an authorized human approves, from the Fjord mobile app (tap to approve) or by commenting /approve on the approval thread. Fail-closed: a timeout or /deny stops the deploy. No github.com.

Part of the Fjord Actions bundle. Uses fjord-notify's relay to reach phones.

Why

Forgejo has no equivalent of GitHub's environment protection rules, so prod deploys ship the instant CI is green, with nobody in the loop. Teams hand-roll a "gate" job; when one such gate was cancelled mid-deploy it caused a ~1.5h outage. This action is the missing gate, and unlike a hosted feature you can actually self-host it.

The approval state lives in the forge (an issue + its comments). If the runner dies while waiting, re-run with the same issue-number and it finds the decision instead of stranding a half-applied deploy.

Usage

jobs:
  deploy:
    runs-on: [self-hosted, Linux]
    # Queue concurrent deploys instead of cancelling a waiting one.
    concurrency:
      group: deploy-prod
      cancel-in-progress: false
    permissions:
      issues: write
    steps:
      - uses: https://rasterhub.com/rasterstate/fjord-approval-gate-action@v1
        with:
          environment: production
          approvers: stephen, ops-oncall
          timeout-minutes: 30
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          FJORD_RELAY_URL: ${{ vars.FJORD_RELAY_URL }}        # optional: push to phones
          FJORD_RELAY_TOKEN: ${{ secrets.FJORD_RELAY_TOKEN }}

      - run: ./deploy.sh    # only runs after approval

Approve from the Fjord app, or comment /approve (or /deny) on the issue the action opens. /lgtm and also approve; /reject denies.

Inputs

Input Default Description
environment production Name shown in the prompt.
approvers Logins allowed to approve. Empty = anyone except the run actor.
allow-self-approval false Let the run actor approve their own deploy.
timeout-minutes 60 Wait before timing out.
poll-interval-seconds 15 How often to check.
fail-on-timeout true Block the deploy if no approval arrives.
issue-number new issue Reuse an approval thread (for re-runs).
issue-title / instructions generated Customize the prompt.
relay-url / relay-token FJORD_RELAY_* env Push the request to phones.
decisions-url Relay endpoint polled for an app decision (fast path).
token / api_url / repository derived Forgejo auth + target.

Outputs

Output Description
approved true, or false on timeout when fail-on-timeout is off.
approver Login of the approver.
issue-number The approval thread (reuse on a re-run).

How approval arrives

  1. Mobile (fast path): with relay-url set, the action pushes an approval-request to the Fjord relay; the app shows Approve/Deny. If you also set decisions-url, the action polls it for the app's decision ({ "decision": "approve" | "deny", "approver": "login" }).
  2. Comment (always works): the action opens an issue and polls its comments for /approve or /deny from an authorized approver. This needs no relay and no app, just Forgejo.

Pair with concurrency: { cancel-in-progress: false } so a second deploy queues behind a waiting one instead of cancelling it (the "hold, don't cancel" property that avoids the half-deploy failure mode).

License

MIT, see LICENSE.