Files
web/content/blog/04-automatic-release.md
Jirka Stehlík 5d34c5f815
All checks were successful
Build and Publish Web / build (push) Successful in 4m0s
Added first workflow
2026-03-24 21:24:05 +01:00

5.6 KiB

date, draft, title, author, tags, categories, description
date draft title author tags categories description
2026-03-24T20:36:26+01:00 true Automatic web update release Jirka
self-host
gitea
automation
self-host
documentation
automation
Last step, how to relase changes to web automatically

Okay, this should be the last step of this article series. Automatic releases to web.

The idea is simple, push changes to master branch of repo holding this web and automatically release it.

We will split this problem into two: Building web with Hugo and second, updating files with webhook. Let's start.

Set up Gitea runner

Gitea does support actions like GitHub, but you have to provide it runner to execute you tasks. We are going to use the most secured way, runner in docker, which will start another docker container in another docker.

I know, it sounds silly, it has a reason. You do not want to have container with privileges to run docker containers. It is even more true, when you consider you can control this container via Gitea actions. So, we will start docker in container and then give Gitea runner rights to that. It is actually much simpler to do, then it may seem like.

First, let's get registration runner token. Where you can obtain one depends on scope, from which the runner should be accessible. I want the runner to be usable only by me, so I will get registration token from Gitea → Profile settings → Actions → Runners → Create new Runner.

I will store this token into .env file inside my gitea directory created in last article with RUNNER_TOKEN name.

Next we modify Gitea docker-compose.yml file to look as follows:

networks:
  jirka_builds_gitea_internal:
  jirkabuilds_proxy_network:
  jirkabuilds_gitea_runner_net: # For runner dind comunication
  jirkabuilds_gitea_net: # For runner gitea comunication, isolated from db
  

services:
  jirkabuilds_gitea:
    image: docker.gitea.com/gitea:latest
    container_name: jirkabuilds_gitea
    environment:
      - USER_UID=${APP_UID}
      - USER_GID=${APP_GID}
      - GITEA__database__DB_TYPE=postgres
      - GITEA__database__HOST=jirkabuilds_gitea_db:5432
      - GITEA__database__NAME=${DB_NAME}
      - GITEA__database__USER=${DB_USER}
      - GITEA__database__PASSWD=${DB_PASSWORD}
    restart: always
    networks:
      - jirka_builds_gitea_internal
      - jirkabuilds_proxy_network
      - jirkabuilds_gitea_net
    volumes:
      - ./gitea:/data
      - /etc/timezone:/etc/timezone:ro
      - /etc/localtime:/etc/localtime:ro
    depends_on:
      - jirkabuilds_gitea_db

  jirkabuilds_gitea_db:
    image: docker.io/library/postgres:14
    restart: always
    environment:
      - POSTGRES_USER=${DB_USER}
      - POSTGRES_PASSWORD=${DB_PASSWORD}
      - POSTGRES_DB=${DB_NAME}
    networks:
      - jirka_builds_gitea_internal
    volumes:
      - ./postgres:/var/lib/postgresql/data

  # New
  dind:
    image: docker:dind
    container_name: jirkabuilds-gitea-dind
    restart: always
    privileged: true
    environment:
      - DOCKER_TLS_CERTDIR=/certs
    volumes:
      - jirkabuilds_dind_certs:/certs
      - jirkabuilds_dind_data:/var/lib/docker
    networks:
      jirkabuilds_gitea_runner_net:
        aliases:
          - docker

  runner:
    image: gitea/act_runner:latest
    container_name: jirkabuilds-gitea-runner
    restart: always
    depends_on:
      - dind
      - jirkabuilds_gitea
    environment:
      - GITEA_INSTANCE_URL=https://git.jirkabuilds.dev
      - GITEA_RUNNER_REGISTRATION_TOKEN=${RUNNER_TOKEN}
      - GITEA_RUNNER_NAME=docker-runner-dind
      - DOCKER_HOST=tcp://docker:2376
      - DOCKER_CERT_PATH=/certs/client
      - DOCKER_TLS_VERIFY=1
    volumes:
      - jirkabuilds_runner_data:/data
      - jirkabuilds_dind_certs:/certs:ro
    networks:
      - jirkabuilds_gitea_net
      - jirkabuilds_gitea_runner_net

volumes:
  jirkabuilds_dind_certs:
  jirkabuilds_dind_data:
  jirkabuilds_runner_data:

And that is all, after docker compose up -d, runner should show up in the registry.

Set up workflow

Next step is to set up workflow inside repository. Before we can do that, we need token for workload, to add access for package creation.

To do so, in Gitea got to Settings → Applications → Generate New Token. Give it permission to create package and copy token.

Now go to desired repo Settings → Actions → Secrets → Add secret and save token to Value. Name it PACKAGE_TOKEN.

Now we can use the token in our workflow. Workflow will be defined in .gitea/workflows/deploy.yml inside repository. And should contain something like this:

name: Build and Publish Web
on:
  push:
    branches:
      - master

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Code checkout
        uses: actions/checkout@v3

      - name: Setup Node.js
        uses: actions/setup-node@v3
        with:
          node-version: '18'

      - name: Hugo Setup
        uses: peaceiris/actions-hugo@v2
        with:
          hugo-version: 'latest'
          extended: true

      - name: Web Build
        run: |
          cd themes/minimal-black
          npm install
          cd ../..
          hugo --minify

      - name: Packaging ZIP
        run: |
          cd public
          zip -r ../build.zip ./*

      - name: Uploading package back to gitea
        run: |
          curl --user "${{ github.actor }}:${{ secrets.PACKAGE_TOKEN }}" \
               --upload-file build.zip \
               "https://git.jirkabuilds.dev/api/packages/${{ github.repository_owner }}/generic/hugo-build/latest/build.zip"

      # TODO - Webhook

This will automatically create package on push to master.