ci-actions/README.md

5.9 KiB

CI Actions

Reusable Forgejo/Gitea actions for toph's infrastructure.

Available Actions

deploy-site

Deploy a static site to production via S3 and Nomad.

Usage:

name: Deploy
on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: self-hosted
    steps:
      - uses: actions/checkout@v4

      - name: Build site (if needed)
        run: npm run build

      - name: Deploy
        uses: https://git.toph.so/toph/ci-actions/deploy-site@main
        with:
          site-name: mysite
          traefik-rule: Host(`mysite.example.com`)
          source-dir: dist  # optional, defaults to current dir
        env:
          S3_ACCESS_KEY: ${{ secrets.S3_ACCESS_KEY }}
          S3_SECRET_KEY: ${{ secrets.S3_SECRET_KEY }}

Inputs:

  • site-name (required): Site identifier used as Nomad service name
  • traefik-rule (required): Traefik routing rule (see examples below)
  • source-dir (optional): Directory with built files, defaults to .
  • s3-endpoint (optional): S3 endpoint, defaults to https://s3.toph.so

Environment variables:

  • S3_ACCESS_KEY: S3 access key (set via Forgejo secrets)
  • S3_SECRET_KEY: S3 secret key (set via Forgejo secrets)

What it does:

  1. Packages the site directory as a tarball
  2. Uploads to S3 at s3://artifacts/<commit-sha>.tar.gz
  3. Sets public-read ACL on the artifact
  4. Dispatches Nomad job to deploy the site
  5. Site becomes available via the specified Traefik rule with Let's Encrypt SSL

Infrastructure handles:

  • Docker containers (static-web-server)
  • Resource limits (100 CPU, 64MB RAM)
  • Traefik routing & Let's Encrypt SSL
  • Automatic restarts

Traefik Rule Examples

Single domain:

traefik-rule: Host(`example.com`)

Multiple domains (with www):

traefik-rule: Host(`example.com`) || Host(`www.example.com`)

Subdomain:

traefik-rule: Host(`blog.example.com`)

toph.so domain:

traefik-rule: Host(`mysite.toph.so`)

Path-based routing:

traefik-rule: Host(`example.com`) && PathPrefix(`/docs`)

Setup

1. Deploy S3 service (SeaweedFS)

cd /srv/infra
bosun dispatch s3

2. Create artifacts bucket

# Configure AWS CLI
export AWS_ACCESS_KEY_ID=<your-access-key>
export AWS_SECRET_ACCESS_KEY=<your-secret-key>
export AWS_ENDPOINT_URL=https://s3.toph.so
export AWS_EC2_METADATA_DISABLED=true

# Create bucket
aws s3 mb s3://artifacts

# Set public-read policy for the bucket
aws s3api put-bucket-acl --bucket artifacts --acl public-read

3. Deploy static-site parameterized job

cd /srv/infra
bosun dispatch static-site-dispatch

4. Add Forgejo secrets

In your repository settings (or organization settings for global secrets):

  • S3_ACCESS_KEY: The access key from S3 credentials
  • S3_SECRET_KEY: The secret key from S3 credentials

Examples

Simple HTML site

name: Deploy
on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: self-hosted
    steps:
      - uses: actions/checkout@v4

      - uses: https://git.toph.so/toph/ci-actions/deploy-site@main
        with:
          site-name: mysite
          traefik-rule: Host(`mysite.toph.so`)
          source-dir: .  # HTML files in repo root
        env:
          S3_ACCESS_KEY: ${{ secrets.S3_ACCESS_KEY }}
          S3_SECRET_KEY: ${{ secrets.S3_SECRET_KEY }}

Node.js/Vite site with custom domain

name: Deploy
on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: self-hosted
    steps:
      - uses: actions/checkout@v4

      - name: Setup Node
        uses: actions/setup-node@v4
        with:
          node-version: 20

      - name: Install & build
        run: |
          npm ci
          npm run build

      - uses: https://git.toph.so/toph/ci-actions/deploy-site@main
        with:
          site-name: myapp
          traefik-rule: Host(`app.example.com`) || Host(`www.app.example.com`)
          source-dir: dist
        env:
          S3_ACCESS_KEY: ${{ secrets.S3_ACCESS_KEY }}
          S3_SECRET_KEY: ${{ secrets.S3_SECRET_KEY }}

Hugo site

name: Deploy
on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: self-hosted
    steps:
      - uses: actions/checkout@v4
        with:
          submodules: true  # For Hugo themes

      - name: Setup Hugo
        run: |
          wget https://github.com/gohugoio/hugo/releases/download/v0.121.0/hugo_extended_0.121.0_linux-amd64.tar.gz
          tar xzf hugo_extended_0.121.0_linux-amd64.tar.gz
          sudo mv hugo /usr/local/bin/

      - name: Build
        run: hugo --minify

      - uses: https://git.toph.so/toph/ci-actions/deploy-site@main
        with:
          site-name: myblog
          traefik-rule: Host(`blog.example.com`)
          source-dir: public
        env:
          S3_ACCESS_KEY: ${{ secrets.S3_ACCESS_KEY }}
          S3_SECRET_KEY: ${{ secrets.S3_SECRET_KEY }}

S3 Access

Troubleshooting

AWS CLI not found

The action automatically installs AWS CLI if not present on the runner.

Access denied during upload

Check that:

  1. S3_ACCESS_KEY and S3_SECRET_KEY are set in Forgejo secrets
  2. The credentials match those in the S3 Nomad job config
  3. S3 service is running: nomad job status s3

Nomad dispatch fails

Ensure the static-site parameterized job is running:

nomad job status static-site
# If not found:
bosun dispatch static-site-dispatch

Site not accessible

Check:

  1. Nomad allocation is running: nomad job status static-site
  2. Traefik has picked up the service: check Traefik dashboard
  3. DNS resolves: dig <site-name>.toph.so
  4. Certificate is valid: curl -v https://<site-name>.toph.so

Development

To add a new action:

mkdir -p new-action
cd new-action
vim action.yaml

Then commit and push to main.

License

MIT