Posted:

Using Woodpecker CI for my static sites

6 minute read

Recently, due to all the discussions around GitHub and Copilot and Software Freedom, I moved my repositories to Codeberg. I was an avid user of Github Pages, which allowed me to have a fully automated way of updating my static websites that I build with Jekyll. Like this very blog :)

The last night was spent bringing that luxury over to Codeberg with their woodpecker based CI. As it is now all working, let us go through the setup I use right now. I am by no means an expert in CI or Jekyll, so I am sure there is a lot to enhance and simplify. That’s why I throw it out here :)

A little warning. The Codeberg CI is not publicly available to all users. I was granted access and promised to not be too much of a burden on their resources. It’s a non-profit, not a startup. So don’t run them over to also demand access, kthxbai ;)

The TL;DR of my setup. I have a source repository called jwildeboersource with the content of this blog and all its components that goes into a jekyll build stage. The output of the build stage goes to the pages branch at the target repository at jwildeboer which in turn gets picked up by Codeberg Pages and through some DNS magic this lands on your browser as my blog. Codeberg pages does the letsencrypt certificate stuff etc.

As a prerequisite, I expect you to have a working repository that you can feed to jekyll build hence I will not cover that part. We’ll take my working .woodpecker.yml file and go through it line by line, so you can hopefully understand what’s happening and maybe even adapt it to your own needs.

# .woodpecker.yml
pipeline:
  build:
    image: jekyll/jekyll
    secrets: [ cbtoken, cbmail ]
    commands:
      - chmod -R a+w .
      - git config --global --add safe.directory /woodpecker/src/codeberg.org/jwildeboer/jwildeboersource/_site
      - git config --global user.email "$CBMAIL"
      - git config --global user.name "CI Builder"
      - git config --global init.defaultBranch pages
      - git clone -b pages https://codeberg.org/jwildeboer/jwildeboer.git
      - mv jwildeboer _site
      - chmod -R a+w _site
      - cd _site
      - git remote set-url origin https://$CBTOKEN@codeberg.org/jwildeboer/jwildeboer.git
      - cd ..
      - bundle install
      - bundle exec jekyll build
      - cp domains _site/.domains
      - cd _site
      - git add --all
      - git commit -m "Woodpecker CI Jekyll Build"
      - git push

That’s the complete pipeline. Simple, robust and partly brutal :)

Let’s analyse. It starts simple enough:

# .woodpecker.yml
pipeline:
  build:
    image: jekyll/jekyll
    secrets: [ cbtoken, cbmail ]

We are building something. And we are using the more or less official jekyll build container from the default registry. As we will be pushing the static files that make up this blog to a different repository, we need a codeberg token that we store as a secret in woodpecker, to keep it out of direct sight :) I’ll also throw in the email address for the git user this way.

    commands:
      - chmod -R a+w .

Now this is where I ran into my first problem. When I tried the CI pipeline for the first time, I got a lot of permission denied errors. I fixed it the brutal way. chmod 777 everything and move on. Later I found a slightly less brutal way - chmod -R a+w, which also works. I still hope I can find a better way - so if anyone out there knows, please share as comment or issue or maybe even as pull request on my repo.

      - git config --global --add safe.directory /woodpecker/src/codeberg.org/jwildeboer/jwildeboersource/_site
      - git config --global user.email "$CBMAIL"
      - git config --global user.name "CI Builder"
      - git config --global init.defaultBranch pages
      - git clone -b pages https://codeberg.org/jwildeboer/jwildeboer.git

We will clone the pages branch of the repository where the generated files go. As this is all ephemeral stuff, we need to take some precautions, like declaring the output directory a safe directory in git lingo to avoid git bailing out with an error. As Codeberg only serves static pages from the pages branch, let’s also set that. OK.

      - mv jwildeboer _site
      - cd _site
      - git remote set-url origin https://$CBTOKEN@codeberg.org/jwildeboer/jwildeboer.git
      - chmod -R a+w _site
      - cd ..

The clone landed in the directory jwildeboer, obviously, but Jekyll by default throws everything in the _site directory. Again, let’s be simple, rename the repo dir, move inside, set the branch to pages and add the git remote with the secret token, so we can reliably push at the end of the pipeline.

Now this again threw some errors when I first set it up, so just like before - chmod -R a+weverything and move on.

      - cd ..
      - bundle install
      - bundle exec jekyll build
      - cp domains _site/.domains

We are prepared. Move up one directory level and let Jekyll do it’s job. As this is an ephemeral setup, we have to do the full dance of bundle install and the build part. As this blog is served at https://jan.wildeboer.net we have to add a .domains file to the output or else codeberg pages won’t find it.

Not much left to do :)

      - cd _site
      - git add --all
      - git commit -m "Woodpecker CI Jekyll Build"
      - git push

We move into the _site directory, throw everything in a commit and git push it up to the target repository. Once that is done, codeberg pages will immediately notice and my blog is updated.

So what can I do now? Well, I have my source repo on my laptop so I can edit, correct, enhance this blog post - even without an internet connection using a local bundle exec jekyll serve. When I am satisfied and (hopefully) fixed all typos and stuff, I commit everything locally, push it up to codeberg, the pipeline kicks in, the output lands on codeberg pages and done!

As always, you can comment via mastodon or contact me on Twitter if you have questions. I hope you’ve enjoyed this little story :) Time for a saturday beer!

COMMENTS

You can use your Mastodon account to comment on this article by replying to the associated Mastodon post.