In the last few evenings i was able to rebuild my complete workflow to publish texts in my blog with Gitea Actions. My first version was rather simple.

How I’m publishing texts to this blog

For a long time i worked with a two stage model with a third separate version for testing purposes. Since i started with git and jekyll instead of mysql and s9y, i had one main branch that contains markdown files. This markdown files result in the html pages you are seeing right now via Jekyll. So, what you are seeing here is this production branch.

Having your blog in a versioning system had so far two killer features for me: Reverting and having a history of changes. And i used both quite frequently, even when there was only this mein version.

Then there were three

Not much later there were two additional versions: There is a staging version of this website. It’s meant for testing new software versions, new layouts, changes in the templates. Staging is is public, because sometimes you want to show someone a new idea. And at the end it’s the same content as in the public website. However it may be totally broken.

And then there is draft. Despite mentioned last, the next area was the second i started to use: Draft is not public, because it’s “content in progress”. I wanted to work on blog entries in a webserver environment with the normal rendering, without having to publish the blog entries too early. There are even a few blog entries in this draft area that never saw the light of the day, because there is one or the other reason why i didn’t want to publish them.1 Draft contains the rendering of all unpublished, drafted or future blog entries.

Branches

All three stages are today represented by a branch in my Gitea. In the past i had the same construct in Github. The production website is in the main branch, the draft area is in draft2 branch and staging is in staging3 branch.

Normally i just use draft and main. I’m writing my texts (and correct them) in the draft branch and then - when i deem the blog entries as ready for publication - I’m triggering a pull request into the main branch. This works reasonably well. In the past, i implemented this via some scripts running on my Mac Mini waddledee. It had the problem i described in the last blog entry about this topic, like it was a little bit difficult to publish something on a different system than this Mac Mini.

New workflow

However, with the new fileserver, i wanted to get rid of this kludge. To implement the workflow in Gitea (that is running on my NAS) i’m using this unified workflow yaml file. It’s the same for all branches. I could use different workflow files in each branch but then i would have to put back the matching one each time i delete the branch and create it anew.

Caution: Of course this script is heavily customized for my own implementation, it has no safeguards4 and you have to adapt it your sitation and use it on your own risk. I’m running it on a gitea system that is only used by myself. The branch names are hardcoded. You should only see it as a template for your own work. I wouldn’t use it on a public or semi-public Gitea server.

In principle it’s just an expanded version of the old script. This version needs the following secrets:

  • BLOG_SSHPRIVATEKEY: The private identity file for ssh. For example the content of id_rsa
  • BLOG_DRAFT_HTPASSWD: The complete content of .htpasswd file, created with the htpassword command

In addition you have to set the following variables:

  • BLOG_SSHKNOWNHOSTKEYS: The keys of the host you want to sync the files to. I’ve created this with ssh-keyscan examplewebspaceprovider.de
  • BLOG_REMOTEUSER: username for ssh on your target host
  • BLOG_REMOTEHOST: hostname of your target host
  • BLOG_HTPASSWD_DIRECTORY: absolute path where you htpassword file will be placed
  • BLOG_PRODUCTION_REMOTEDIR: document root for the production webserver
  • BLOG_STAGING_REMOTEDIR: document root for the staging webserver
  • BLOG_DRAFT_REMOTEDIR: document root for the draft webserver

This is the yaml for the workflow itself:

name: Building the blog
run-name: ${{ gitea.actor }} is building the blog
on: [push]

jobs:
  Buildblog:
    runs-on: ubuntu-latest
    container: 
      image: c0t0d0s0-jekyll
    steps:
      - name: Check out repository code
        uses: actions/checkout@v4
        with: 
          ref: ${{ gitea.ref_name }}
      - name: Run Jekyll. Do not Hyde!
        env: 
          GEM_HOME="/gems"
        run: |
          if [ "${{ gitea.ref_name }}" = "draft" ]; then
           echo "Triggered by draft branch"
           BLOG_BUILDOPTIONS="--future --unpublished --drafts --profile"
          else
           echo "Triggered by branch without special build options"
           BLOG_BUILDOPTIONS="--profile"
          fi
          bundle exec jekyll build --source ${{ gitea.workspace }} $BLOG_BUILDOPTIONS --destination /site
      - name: SSH stuff 
        env:
           BLOG_SSHPRIVATEKEY: ${{ secrets.BLOG_SSHPRIVATEKEY }}
           BLOG_SSHKNOWNHOSTKEYS: ${{ vars.BLOG_SSHKNOWNHOSTKEYS }}
        run: |
          echo "Placing identity file"
          mkdir ~/.ssh
          echo "$BLOG_SSHPRIVATEKEY" > ~/.ssh/id
          chmod 600 ~/.ssh/id
          echo "Placing known_hosts"
          echo "$BLOG_SSHKNOWNHOSTKEYS" >> ~/.ssh/known_hosts
      - name: preparing password protection for draft
        env:
          BLOG_REMOTEUSER: ${{ vars.BLOG_REMOTEUSER }}
          BLOG_REMOTEHOST: ${{ vars.BLOG_REMOTEHOST }}
          BLOG_DRAFT_HTPASSWD: ${{ secrets.BLOG_DRAFT_HTPASSWD }}
        run: |
          echo "Preparing password protection for draft"
          if [ "${{ gitea.ref_name }}" = "draft" ]; then
           echo "Creating htpasswd file"
           echo "$BLOG_DRAFT_HTPASSWD" > ~/draft.htpasswd 
           echo "Transfering htpasswd file into webspace"
           scp -i $HOME/.ssh/id -r ~/draft.htpasswd $BLOG_REMOTEUSER@$BLOG_REMOTEHOST:htpasswd/htpasswd
           echo "Enabling password protection of draft"
           echo "AuthUserFile ${{ vars.BLOG_HTPASSWD_DIRECTORY }}/draft.htpasswd" >> /site/.htaccess
           echo "AuthGroupFile /dev/null" >> /site/.htaccess
           echo "AuthName Draft" >> /site/.htaccess
           echo "AuthType Basic" >> /site/.htaccess
           echo "require valid-user" >> /site/.htaccess
          fi  
      - name: syncing to webspace
        env:
          BLOG_REMOTEUSER: ${{ vars.BLOG_REMOTEUSER }}
          BLOG_REMOTEHOST: ${{ vars.BLOG_REMOTEHOST }}
          BLOG_DRAFT_HTPASSWD: ${{ secrets.BLOG_DRAFT_HTPASSWD }}
        run: |
          echo ${{ gitea.ref_name }}
          if [ "${{ gitea.ref_name }}" = "main" ]; then
           echo "Triggered by main branch"
           BLOG_REMOTEDIR="${{ vars.BLOG_PRODUCTION_REMOTEDIR }}"
          elif [ "${{ gitea.ref_name }}" = "draft" ]; then
           echo "Triggered by draft branch"
           BLOG_REMOTEDIR="${{ vars.BLOG_DRAFT_REMOTEDIR }}"
          elif [ "${{ gitea.ref_name }}" = "staging" ]; then
           echo "Triggered by draft branch"
           BLOG_REMOTEDIR="${{ vars.BLOG_STAGING_REMOTEDIR }}"
          else
           echo "Triggered by unknown branch"
           exit 1
          fi
          echo Syncing as $BLOG_REMOTEUSER
          echo Syncing to $BLOG_REMOTEHOST
          echo Syncing in $BLOG_REMOTEDIR
          rsync -e "ssh -i $HOME/.ssh/id" -r /site/* $BLOG_REMOTEUSER@$BLOG_REMOTEHOST:$BLOG_REMOTEDIR
          echo "Transfering final .htaccess into webspace"
          scp -i $HOME/.ssh/id -r /site/.htaccess $BLOG_REMOTEUSER@$BLOG_REMOTEHOST:$BLOG_REMOTEDIR/.htaccess

  1. There is even a one sentence time-bombed blog entry in the repository, publishing between thirty and sixty days after i reset the timer. If i don’t reset the publication date, an scheduled automatic build run will put this online. More or less a morbid gag. Even when i really wanted to drop this blog, i winded up this egg timer. ;) 

  2. Big surprise! 

  3. Yet another big surprise! 

  4. For example it doesn’t check for spaces in the variables, doesn’t escape anything. As it said, it works in my sitation. 

Written by

Joerg Moellenkamp

Grey-haired, sometimes grey-bearded Windows dismissing Unix guy.