🗙 Gitea Actions - Jekyll Workflow

6 Minute(n) Lesezeit

Nachdem ich in diesem Projekt meinen eigenen, kleinen Server aufgesetzt und mit den entsprechenden Programmen zum Hosten meines Blogs ausgestattet habe, möchte ich ihn nun in die Lage versetzen, die Website automatisch zu bauen.

Gewünschter Ablauf

Ich möchte folgendes erreichen: Sobald Gitea eine push-Aktion auf dem Blog-Repository verzeichnet, soll ein Action Runner in einem eigenen Docker-Container loslaufen und Ruby, Bundler, Jekyll sowie sämtliche Abhängigkeiten nachladen. Dann soll der Runner meine Quelldateien für den Blog kopieren und dem bundle exec jekyll build Prozess zur Verfügung stellen, welcher dann die Website baut und schließlich im _site-Ordner ablegt. Anschließend soll der Runner sich selbst beenden und alle in Anspruch genommenen Ressourcen wieder freigeben.

An dieser Stelle möchte ich nachkontrollieren können, ob die Website fehlerfrei gebaut wurde und in Zukunft eventuell noch ein paar weitere Prüfungen darüberlaufen lassen - z.B. kaputte Links erkennen, nicht angezeigte Bilder markieren, Rechtschreibprüfung durchführen.

Die Freigabe sowie die Veröffentlichung soll dann ein anderer Prozess übernehmen, um den ich mich später kümmere.

Ausgangspunkt: Github Actions

Wie üblich wildere ich erst einmal bei anderen Menschen, die ein ähnliches Problem für sich bereits gelöst haben. Ich wurde im Github Actions starter-workflows Repository von Github selbst fündig und habe die Workflowdatei für Jekyll mit leichten Modifikationen direkt mal mit meiner Gitea-Instanz ausprobiert:

# jekyll-build-pages.yml
# Sample workflow for building and deploying a Jekyll site
name: Deploy Jekyll site
run-name: $ builds Jekyll site
on: [push]

jobs:
  # Build job
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v3
      - name: Setup Ruby
        uses: ruby/setup-ruby@55283cc23133118229fd3f97f9336ee23a179fcf # v1.146.0
        with:
          ruby-version: '3.1' # Not needed with a .ruby-version file
          bundler-cache: true # runs 'bundle install' and caches installed gems automatically
          cache-version: 0 # Increment this number if you need to re-download cached gems
#[...]

Nachdem ich diesen Workflow in mein Blogverzeichnis unter workflows/jekyll.yml hochgeladen hatte, lief der Runner tatsächlich direkt los:

Image: Gitea action runner returns error on ruby install

Probleme mit der Ruby-Installation

Schon mal prima. Allerdings wenig erbaulich, dass bereits bei der Installation von Ruby etwas nicht passt. Werfen wir einen Blick in die Logs:

::error::The current runner (debian-11-x64) was detected as self-hosted because the platform does not match a GitHub-hosted runner image 
(or that image is deprecated and no longer supported).
In such a case, you should install Ruby in the $RUNNER_TOOL_CACHE yourself, for example using https://github.com/rbenv/ruby-build
You can take inspiration from this workflow for more details: 
https://github.com/ruby/ruby-builder/blob/master/.github/workflows/build.yml

Na, das ist doch mal eine hilfreiche Fehlermeldung!

Der Runner läuft um Ressourcen zu sparen standardmäßig im Docker mit einem abgespeckten Image basierend auf Debian namens node:16-bullseye (Referenz). Scheinbar ist dieser nicht kompatibel mit dem unter Ubuntu22 betriebenen Github Actions Runner. Ich habe also ein paar Lösungsmöglichkeiten zur Verfügung:

Möglichkeiten zur Fehlerbehebung

  • Den Runner über seine “Labels” auf Ubuntu22.04 umsatteln.
  • Ren Runner nicht im Docker, sondern nativ auf dem Server laufen lassen.
  • Wie in der Fehlermeldung vorgeschlagen, Ruby im Cache des Runners vorinstallieren.
  • Ein Jekyll-Dockerimage verwenden, welches Jekyll bereits im Gepäck hat und nicht erst die Installation benötigt.

Den Runner mit Ubuntu22 statt Node16 betreiben

Das entsprechende Dockerimage existiert zwar. Aber es kostet aber einen Haufen Speicherplatz auf meinem Server, benötigt durch seinen größeren Funktionsumfang mehr RAM und braucht vor allem länger, bis es hochgefahren ist.

Und Zeit ist bei Runnern wertvoll, schließlich will ich ja möglichst schnell wissen, ob das Bauen geklappt hat.

Den Runner direkt auf der Hostmaschine (Ubuntu22) laufen lassen

Ist sicherheitstechnisch nicht so sehr zu empfehlen (Bei einem ungeschützten Branch würde der Runner von Dritten per git push --force hochgeschobenen Schadcode unbesehen auf meiner Hostmaschine ausführen) und zudem ginge die Portabilität flöten, weil ich das Zielsystem des Runners dann fest verdrahte.

Gitea act_runner direkt auf dem Host laufen lassen

Für den zweiten Ansatz habe ich mir per Anleitung eine Konfiguration für den runner angelegt und darin das bestehende label-mapping so geändert, dass nicht mehr in Richtung Docker, sondern auf dem Ubuntu des Servers selbst gebaut wird. Dafür genügt es in meinem Fall, das Label ubuntu-latest:host statt ubuntu-latest in die Konfiguration zu schreiben. Leider erhielt ich hier Fehlermeldungen bei den Zugriffsrechten des Runners, welcher aus Docker heraus auf dem Hostsystem laufen müsste.

Nicht so schlimm, ich wollte diese Lösung wegen der oben beschriebenen möglichen Probleme sowieso nicht dauerhaft verwenden. Also investiere ich hier keine weitere Zeit.

Ruby im Cache vorinstallieren

Wie von der Fehlermeldung empfohlen habe ich dieser Anleitung folgend in der docker-compose.yml von Gitea ein Volume angelegt für den Tool Cache des runners und das Zielverzeichnis in der Umgebungsvariable RUNNER_TOOL_CACHE abgelegt.

# gitea/docker-compose.yml
# [...]
runner:
    image: gitea/act_runner:nightly
    environment:
      - GITEA_INSTANCE_URL=<redacted>
      - GITEA_RUNNER_NAME=<redacted>
      - RUNNER_TOOL_CACHE=/opt/hostedtoolcache
      - GITEA_RUNNER_REGISTRATION_TOKEN= <redacted>
    volumes:
      - ./runner/data:/data
      - /opt/hostedtoolcache:/opt/hostedtoolcache
      - /var/run/docker.sock:/var/run/docker.sock

Hiermit wird dem in Docker laufenden Runner der Ordner /opt/hostedtoolcache im Dateisystem des Hosts unter dem Namen /opt/hostedtoolcache zur Verfügung gestellt. Nun probiere ich mal, Ruby direkt dort hinein zu installieren.

Also lade ich das ruby-build repository herunter (git clone) und lasse das Installerscript mit ./ruby-build/install.sh durchlaufen. Danach führe ich den Build mit dem in der Fehlermeldung angegebenen Pfad aus: ruby-build 3.1.4 /opt/hostedtoolcache/Ruby/3.1.4/x64

==> Downloading openssl-3.1.4.tar.gz...
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 14.8M  100 14.8M    0     0  81.3M      0 --:--:-- --:--:-- --:--:-- 81.1M
==> Installing openssl-3.1.4...

BUILD FAILED (Ubuntu 22.04 on x86_64 using ruby-build 20231114)

Probleme mit prebuilt-Ruby

Das Log zeigt das Problem:

[...] No C compiler found, please specify one with the environment variable CC [...]

Also tippe ich apt install build-essential und stelle dann mit which gcc fest, dass er nun unter /usr/bin/gcc installiert ist. Auf ein Neues!

crypto/comp/c_zlib.c:36:11: fatal error: zlib.h: No such file or directory

Dann schnell apt install libz-dev eingeben und erneut probieren. Dieses mal läuft er recht lange, dann:

*** Following extensions are not compiled:
openssl:
        Could not be configured. It will not be installed.
        /tmp/ruby-build.20231205114312.9174.0gmhNf/ruby-3.1.4/ext/openssl/extconf.rb:100: OpenSSL library could not be found. You might Check ext/openssl/mkmf.log for more details.
readline:
        Could not be configured. It will not be installed.
        /tmp/ruby-build.20231205114312.9174.0gmhNf/ruby-3.1.4/ext/readline/extconf.rb:62: Neither readline nor libedit was found
        Check ext/readline/mkmf.log for more details.
*** Fix the problems, then remove these directories and try again if you want.
[...]

OK, nun ist es openssl. Fix nachinstallieren mit apt install libssl-dev sowie apt install libreadline-dev und endlich 🎉:

Installed ruby-3.1.4 to /opt/hostedtoolcache/Ruby/3.1.4/x64

Noch immer Fehler beim Bauen mit dem Github-Acitons script

Merkwürdig, das Log spuckt mir immer noch denselben Fehler aus:

💬  ::debug::isExplicit: 3.1.4

gitea-runner-1  | [Deploy Jekyll site/build]   💬  ::debug::checking cache: /opt/hostedtoolcache/Ruby/3.1.4/x64
gitea-runner-1  | [Deploy Jekyll site/build]   | ::debug::checking cache: /opt/hostedtoolcache/Ruby/3.1.4/x64
gitea-runner-1  | [Deploy Jekyll site/build]   💬  ::debug::not found
gitea-runner-1  | [Deploy Jekyll site/build]   | ::debug::not found
gitea-runner-1  | [Deploy Jekyll site/build]   ❗  ::error::The current runner (debian-11-x64) was detected as self-hosted

Das geht auf folgende Abfage in der ausführenden Action zurück:

//  setup-ruby/ruby-builder.js
if (common.shouldUseToolCache(engine, version)) {
    inToolCache = common.toolCacheFind(engine, version)
    if (inToolCache) {
      rubyPrefix = inToolCache
    } else {
      const toolCacheRubyPrefix = common.getToolCacheRubyPrefix(platform, engine, version)
      if (common.isSelfHostedRunner()) {
        const rubyBuildDefinition = engine === 'ruby' ? version : `${engine}-${version}`
        core.error( [...] )

Update Jan-2024

Es es gibt zwei weitere Möglichkeiten dafür, dass der toolcache nicht gefunden wird:

  1. Ich habe das Volume nicht der Action, sondern dem Runner zur Verfügung gestellt. Ich bin mir nicht sicher, dass die Daten auch in der Action verfügbar sind.
  2. Der toolcache-Pfad ist nicht in der Konfigurationsdatei des runners unter dem Eintrag valid_volumes enthalten. Eine pull request Kommunikation lässt mich jedoch vermuten, dass dies automatisch passiert. Dennoch bin ich mir nicht 100% sicher, ob die Information noch dem Stand der Dinge entspricht.

Docker volume überprüfen

Mist. Prüfen wir schnell mal, ob Ruby in dem Volume auch wirklich verfügbar ist. Dafür suchen wir mit docker ps die Container ID des runners heraus und geben sie bei folgendem Kommando ein:

docker exec -it <containerID> bash

Dann navigiere ich in den gesuchten Ordner cd /opt/hostedtoolcache, liste dort den Inhalt mit ls und siehe da: Ruby. Es liegt also alles vor, nur wird es nicht gefunden.

Misserfolg anerkennen

So komme ich hier also nicht weiter 😖. Daher öffne ich mein Problem für die Community unter dem Titel act_runner cannot find hostedtoolcache und probiere

einen anderen Ansatz.

Problemursache

Der Support der Action setup-ruby für selbsgehostete CI-Pipelines wie meinen act_runner wurde wohl eingestellt. Die Dokumentation mit den Hinweisen zur Fehlerbehebung bei Verwendung selbstgehosteter Runner allerdings zum Zeitpunkt der Verwendung durch meine Wenigkeit war aber noch nicht auf Stand gebracht worden.