Skip to content

CI/CD Pipeline

KanjiIQ uses Forgejo Actions for continuous integration and deployment. Forgejo Actions is compatible with GitHub Actions YAML syntax, running on self-hosted runners.

Workflow Structure

.forgejo/workflows/
├── deploy-frontend.yml    # Build & deploy Flutter Web
├── deploy-backend.yml     # Build & deploy Dart Frog API
└── deploy-docs.yml        # Build & deploy documentation site

Each workflow follows the same pattern:

graph TD
    A[Push to main] --> B{Path filter<br/>matches?}
    B -->|Yes| C[Checkout code]
    B -->|No| X[Skip]
    C --> D[Login to Forgejo<br/>Container Registry]
    D --> E[Docker build<br/>--no-cache]
    E --> F[Push :latest +<br/>:COMMIT_SHA]
    F --> G[kubectl set image]
    G --> H[Wait for rollout<br/>timeout: 5m]
    H --> I[Cleanup: logout]

Frontend Workflow

name: Deploy Frontend to k3s

on:
  push:
    branches: [main]
    paths:
      - 'frontend/**'
      - 'Dockerfile.frontend'
      - 'nginx.frontend.conf'

jobs:
  build-and-deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Login to registry
        run: echo "$FORGEJO_TOKEN" | docker login $REGISTRY_URL -u $REGISTRY_USER --password-stdin

      - name: Build image
        run: |
          docker build --no-cache --pull \
            --build-arg CACHEBUST=$(date +%s) \
            -f Dockerfile.frontend \
            -t <registry>/jlpt-kanji-frontend:latest \
            -t <registry>/jlpt-kanji-frontend:$(git rev-parse --short HEAD) .

      - name: Push & deploy
        run: |
          docker push <registry>/jlpt-kanji-frontend:latest
          docker push <registry>/jlpt-kanji-frontend:$(git rev-parse --short HEAD)
          kubectl set image deployment/jlpt-kanji \
            frontend=<registry>/jlpt-kanji-frontend:$(git rev-parse --short HEAD) \
            -n jlpt-kanji
          kubectl rollout status deployment/jlpt-kanji -n jlpt-kanji --timeout=5m

Secrets

Workflows use a single secret:

Secret Purpose
FORGEJO_TOKEN Authentication for container registry push and Kubernetes access

The token is configured in Forgejo at Repository Settings > Secrets.

Image Registry

All images are stored in the Forgejo Container Registry:

<registry>/jlpt-kanji-frontend:latest
<registry>/jlpt-kanji-frontend:<commit-sha>
<registry>/jlpt-kanji-backend:latest
<registry>/jlpt-kanji-backend:<commit-sha>
<registry>/jlpt-kanji-docs:latest
<registry>/jlpt-kanji-docs:<commit-sha>

Rollout Verification

After kubectl set image, the workflow waits for the rollout to complete:

kubectl rollout status deployment/jlpt-kanji -n jlpt-kanji --timeout=5m

This blocks until:

  • All new pods pass readiness probes, or
  • The timeout expires (workflow fails, alerting the developer)

If a rollout fails, Kubernetes automatically stops rolling out and the previous version continues serving traffic.

Manual Rollback

To revert to a previous version:

# Rollback to previous revision
kubectl rollout undo deployment/jlpt-kanji -n jlpt-kanji

# Or roll back to a specific image
kubectl set image deployment/jlpt-kanji \
  frontend=<registry>/jlpt-kanji-frontend:<previous-sha> \
  -n jlpt-kanji