Marcel Kapfer

Publishing my Emacs Configuration using Gitea Actions


714 words, ~5min reading time


About a year ago I already wrote a few blog posts about publishing my Emacs configuration, lastly using a GitLab pipeline. This worked quite find since back then and I had zero problems or issues with the pipeline. Although I'm using the GitLab CI feature for this I don't use GitLab for hosting the repository. My dot-emacs-repository over there is just a mirror, the main one is in my personal Gitea instance.

So, a few days ago, Gitea 1.90.0 was released with experimental feature called "Gitea Actions". This is pipeline implementation like GitLab Pipelines oder GitHub Actions. And since I didn't have anything better to do yesterday I decided to give this thing a try and publish my Emacs configuration using it.

The runner for Gitea Actions is and adjusted fork of nektos/act which is a tool for running GitHub Actions locally. This means that the Gitea Runner is largely compatible with the GitHub Actions Workflows format. If I understand it correctly, most GitHub Action definitions should "just" work without any adjustments.

I followed to Guide from the Gitea Blog for enabling the feature in the Gitea configuration and installing the Gitea Act runner. Afterwards, I started migrating the pipeline script from the GitLab CI format to the GitHub/Gitea format. Since I never used GitHub Actions before I run into a few problems and misunderstandings before I had a successful configuration of the runner (as it turned out: the defaults work just fine, my adjustments didn't) as well as the workflow action configuration.

First of all it is necessary to activate the Gitea Actions for the dot-emacs repository.

Then I needed to declare some secrets for the publish job to deploy the changes to my server using SSH. At the moment I keep using the gitlab-ci user I already created and configured. So I copied the four secretes SSH_PRIVATE_KEY, SSH_KNOWN_HOSTS, SSH_PORT and SSH_USER from GitLab to Gitea. If you're following, along save the variables somewhere else (e.g. a password store) since contrary to GitLab you are not able to view or edit the Gitea Secrets after saving them.

Now I can add and push my new Gitea workflow configuration which I placed in the repository at .gitea/workflows/publish.yaml.

name: Publish

      - main

    runs-on: ubuntu-latest
    container: silex/emacs:28.1-alpine-ci
      - name: Install packages
        run: apk add --no-cache rsync nodejs

      - name: Add SSH key
        run: |
          mkdir ~/.ssh
          chmod 700 ~/.ssh
          echo "$SSH_PRIVATE_KEY" | tr -d '\r' > ~/.ssh/id_ed25519
          chmod 600 ~/.ssh/id_ed25519
          echo "$SSH_KNOWN_HOSTS" | tr -d '\r' >> ~/.ssh/known_hosts
          chmod 644 ~/.ssh/known_hosts
          SSH_PRIVATE_KEY: ${{secrets.SSH_PRIVATE_KEY}}
          SSH_KNOWN_HOSTS: ${{secrets.SSH_KNOWN_HOSTS}}

      - name: Check out
        uses: actions/checkout@v3

      - name: Build publish script
        run: emacs -Q --script publish/publish.el

      - name: Deploy build
        run: |
          rsync \
            --archive \
            --verbose \
            -e"ssh -p "$SSH_PORT""\
          SSH_USER: ${{secrets.SSH_USER}}
          SSH_PORT: ${{secrets.SSH_PORT}}

Essentially, not much changed compared to the GitLab CI version. As a base image I decided to go with the silex/emacs using Emacs 28.1 on top of Alpine Linux. I additionally restricted the job to only run when pushed to the main branch. While I didn't work with any other branches until now, this is a possibility I'd like to keep open.

The workflow itself is still quite the same. First we install necessary packages. We need rsync for uploading the resulting website to my server and nodejs for the actions/checkout@v3. Then I add the private key to the build job and this works a bit easier since a running ssh-agent is not needed (apparently for GitLab there was no way around this). After checking out the repository code I execute my publish.el Emacs Lisp script that generated a nice HTML page from the org-mode-based Emacs configuration. The last thing to do now is just trigger the upload of the resulting files using rsync.

Although the Gitea Action file is more verbose and longer than its GitLab equivalent I prefer it slightly due to the option to name the individual build steps. This is something I come to enjoy quite a bit from writing and using Ansible playbooks.

Since the configuration is done and tested in a private repository with a modified upload path I removed the .gitlab-ci.yml file and push the changes to the Gitea repository. We can now see the running pipeline in the "Actions" tab.

And with a click on the job title we can see the detailed execution and finally some nice green checkmarks.

Interestingly, the whole run takes only 11s on Gitea compared to about 33s on I don't know if the reason for this is the platform itself or the restricted public runners on

After running into a few problems initially due to my missing knowledge regarding GitHub Actions I enjoyed writing and optimizing the pipeline so well that I will not only keep this process but perhaps also migrate my other CI and CD jobs over.

I would like to hear what you think about this post. Feel free to write me a mail!

Reply by mail