Gitea Actions with Self-Hosted Gitea Runner

Deploying a Self-Hosted Gitea Runner & Automating Deployments Link to heading

ci-cd

I have self-hosted Gitea in Docker for a long time and use it almost exclusively as my git configuration tool of choice. Beginning with Version 1.19 released in March 2023 (enabled by default in 1.21, Nov 23), Gitea implemented Actions. Actions are an integrated Continuous Integration/Continuous Delivery (CI/CD) solution that automates the build/test/deploy pipeline in a simple workflow built into a git repository. There is a ton of value in leveraging a git repository system for version control of congiruation files, IaC, docker compose, and any scripts/code projects that you inevietably build.

Pets vs. Cattle Link to heading

pets-cattle

My recent goal has been to transform my homelab into immutable infrastructure wherein my server hardware becomes disposable & the important components remain running within an application, referencing a defined configuration baseline in a known good state. This harkins back to the classic example of “Pets and Cattle” analogy. “I believe the pets vs cattle meme has been as successful as it has because it highlights the biggest difference: servers as a disposable, commodity item. No identity and low or no cost. Treating servers like electrical amps, data packets, cellular minutes, oil, gas, or anything else that is a true commodity.” Randy Bias. Obviously a homelab setup makes it difficult to fully replicate this ideal (I always plan on having a few pets), but removing the ‘uniqueness’ from my server hardware and deploying elastic applications is what I am working toward bit by bit. Improving my GitOps process is one of those small steps.

Gitea Act Runners Link to heading

Gitea Actions is extremely simple to setup, but requires a runner, which is an isolated environment where jobs are…run – think “on-demand & disposable test environment.” An “Act runner will automatically read Gitea Actions from .gitea/workflows/.

If you are already hosting Gitea in Docker, a runner is very easy to deploy following the documentation. I use Docker Compose and simply spun up a new container on my docker server based on the below .yml file. The “Registration Token” is created in Gitea either per repository or globally for the instance, depending on the level of granularity your use case requires.

version: "3.8"
services:
  runner:
    image: gitea/act_runner:nightly
    environment:
      #CONFIG_FILE: /config.yaml #optional configuration file
      GITEA_INSTANCE_URL: "${INSTANCE_URL}"
      GITEA_RUNNER_REGISTRATION_TOKEN: "${REGISTRATION_TOKEN}"
      GITEA_RUNNER_NAME: "${RUNNER_NAME}"
      GITEA_RUNNER_LABELS: "ubuntu-latest:docker://node:16-bullseye,ubuntu-22.04:docker://node:16-bullseye"
    volumes:
      - ./config.yaml:/config.yaml
      - ./data:/data
      - /var/run/docker.sock:/var/run/docker.sock

Gitea Actions Link to heading

As stated earlier, Gitea Actions are now included by default in the most recent versions of Gitea – no configuration changes required. However, if end up initially confused like me, actions are still globally disabled by default and must be enabled per repository within the repo’s settings menu. Fortunately, this is clearly laid out within Gitea’s quick start guide.

Once turned on, you will see a new “Actions” tab at the top of your repoository. This is where you can watch jobs run in real-time or review historical jobs.

Deploying Static Hugo Website with Actions Link to heading

The whole point of this setup was to create a Gitea Actions workflow that would automatically push my Hugo Static Site public HTML files to my Nginx web server whenever I commit to the main branch of my repo. I have been doing this with a simple Rsync shell script based on the hugo documentation which essentially consisted of:

#!/bin/bash
USER=username
HOST=10.x.x.x
DIR=/var/www/my-website #Where the HTML files are stored on the VPS
now=${date}

git add .
git commit -m  "`date`"
git push origin main

hugo && rsync -avz -e 'ssh -i ~/.ssh/my_private_key' --delete public/ ${USER}@${HOST}:${DIR} #Replace everything on the server with the local build

exit 0

This script would commit to my Gitea repo, build the site locally and then rsync the files to my VPS over SSH. No error checking & 100% manual manipulation! It got the job done, but my focus on Git-based configurations meant this would be a great use reason for me to setup Actions.

Since there is now an Act Runner deployed and registered in Gitea & Actions is enabled in the repo, the last step is to create a deploy workflow in my-hugo-repo/.gitea/workflows/deploy.yml

I leveraged Gitea Secrets to store my SSH Private Key used for deployment, and created a simple deployment script that automates the hugo build and rsync process. Thanks to Christian Engel for the walkthrough! This workflow uses Actions Checkout to checkout the repo, Github Actions for Hugo to build the site, and Rsync Deployments to deploy the static HTML to the VPS.

Place the following in your ‘.gitea/workflows/deploy.yml’ script Link to heading

# Github Action to generate a Hugo blog and deploy to webserver

name: Hugo Gitea Actions Deployment
run-name: ${{ gitea.actor }} is building, testing and deploying the static page

# Controls when the workflow will run
on:
  # Triggers the workflow on push events but only for the main branch
  push:
    branches: [main]

  # Allows you to run this workflow manually from the Actions tab
  workflow_dispatch:

# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
  # This workflow contains a single job called "build"
  build:
    # The type of runner that the job will run on
    runs-on: ubuntu-latest

    # Steps represent a sequence of tasks that will be executed as part of the job
    steps:
      # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
      - uses: actions/checkout@v2

      # Runs the Hugo command to build public directory
      - name: Setup Hugo
        uses: peaceiris/actions-hugo@v2
        with:
          hugo-version: "0.90.1"
          extended: true

      # Clone the theme as a submodule
      - name: Clone theme
        run: git submodule update --init --recursive

      # Build the Hugo web site
      - name: Hugo build
        run: |
          cd $GITHUB_WORKSPACE
          hugo -v                    

      # Copy build folder to remote using rsync
      - name: Rsync Deployments Action
        uses: Burnett01/rsync-deployments@5.2
        with:
          switches: -avzr --delete
          path: public/
          remote_path: ${{ secrets.REMOTE_PATH }}
          remote_host: ${{ secrets.REMOTE_HOST }}
          remote_user: ${{ secrets.REMOTE_USER }}
          remote_key: ${{ secrets.SSH_PRIV_KEY }}

Once I inevietably caused and then resolved a multitude of permissions issues related to rsync with my remote VPS directories, this action has run flawlessly. Now that my Actions setup is in place, I plan to further build on this simple test case and expand this pipeline to other areas of my homelab.