Skip to content

How to use Codeberg Pages for MkDocs

Codeberg1 is a Website providing Git Hosting and services for free and open source software, content and projects.
It's managed by a non-profit (Codeberg e.V.) and offers a lot of things, including their own static page hosting Codeberg Pages.

Since I moved my blog to Codeberg Pages a while ago and since I find its set of features quite good did I now decide to make this little tutorial on how you can automate the publishing and updating of your MkDocs Site for Codeberg Pages.

Comparison to GitHub Pages

Codeberg Pages has some similarities to GitHub Pages, while also offering additional features and having some smaller issues too.
This section will only cover a comparison to GitHub Pages. Other Page services (i.e. GitLab Pages) may offer similar features as Codeberg Pages.

Similarities to GitHub Pages

There are a few similarities to GitHub Pages.

The first similarity is the usage of a dedicated branch and repository for repo-sites and user/org sites respectively.
On GitHub this would be the gh-pages branch for a repository (<username>.github.io/repository) and <username>.github.io as repository name for a user/org site.
On Codeberg Pages, the equivalent to these are a branch named pages and a repository named pages.

Another feature available in both is defining a domain. In GitHub is this done using a CNAME file (Assuming you use their "deploy from branch" method) and in Codeberg page would this be a .domains file.
The .domains file has some benefits, but also drawbacks, which are explained in the next sections.

Beneficial features

Codeberg Pages has some features not available in GitHub Pages, which can be very useful to have.

The first feature is the .domains file, which allows to define more than one domain. The first domain would then be used as the primary and any other would redirect to it.

Additionally can you define a branch in the URL to see the content of a non-pages branch.
As an example knut.codeberg.page/@some-branch would display the content of the some-branch branch on the knut/pages repository.
This can be really useful for things such as deploy-previews of Pull requests, allowing you to see the changes before merging, or to host old versions of your documentation.
Additionally can you also configure your DNS to have a (sub)domain point to a specific branch of your repository.

drawbacks

There are some drawbacks/issues with Codeberg Pages not present in GitHub Pages itself.

One issue is, that once you create a custom domain for your user/org (Define a domain used for a pages repository), will sub-pages made from repositories no longer display content of a repository's pages branch, even if there is one.
This means while knut.codeberg.page/repository did display content of knut/repository's pages branch before would now example.com/repository return a 404 error. In addition will your codeberg.page sub-domain be automatically redirected to your custom domain (knut.codeberg.page/repository -> example.com/repository).

Another issue is the .domains file. By default does MkDocs ignore and not include files starting with a dot in its page build, meaning it would ignore and not include the .domains file. This is not an issue in GitHub Pages as the file used there is named CNAME which MkDocs just includes unchanged in its build page.
A workaround for this issue is explained later in this post.

As a final drawback can you not define a specific folder to be used for the page content. Only the root of a repository is used.

Preparations

Before your site can actually be published will you need to make sure, that you've prepared some stuff first, to ensure it will work.

First of all, you need to make sure you have a CI/CD system ready to use on your repository. I use Woodpecker-CI2, an open source CI software, in this guide, but any CI that can work with Forgejo3, a fork of Gitea4 used to manage the Git Hosting of Codeberg, should be fine to use here.
It's worth noting that Codeberg offers their own Woodpecker-CI Instance5 to use for free. Only requirement is that you have to apply for access.6

Next should you create an API token for Codeberg. This is required as otherwise, your CI won't be able to commit and push the docs to the repository.

Finally should you have prepared your repository. This means that you either set up a pages branch (Preferably orphaned) to push towards, or have created a repository named pages.
I will use the second option in this guide, but this also means that I'll need a second repository hosting the source (config files, markdown files, etc), as everything on the default branch of the pages repository would be served on your user.codeberg.page domain.

I suggest to have the following file structure prepared for your source repository:

.woodpecker/
└── publish_docs.yml
docs/
└── index.md
domains # Used for custom domains
mkdocs.yml
requirements.txt

The usage of a .woodpecker folder instead of a .woodpecker.yml file is recommended, as it would allow you to later add additional CI workflows for different things.

Preparing the woodpecker file

Info

The below example file will show some placeholder/example values which you should change to your own stuff. Namely...

  • knut will be used as user, which is Codeberg's example account and should be replaced with your username on Codeberg.
  • docs-source will be used as the source repository containing the raw MkDocs content and should be replaced with your source repo name.

The first thing you should do is add the source repository as a new repo in the Woodpecker-CI Dashboard. Next should you add the necessary secrets to it, to ensure that they will be present when using the file.
In my example below do I include two secrets:

  • cbemail which contains the e-mail to use for the Git user. This can be replaced with your e-mail in the file if you're not afraid of others knowing it.
  • cbtoken which contains a valid PAT (Personal Access Token) for your Codeberg account. This ensures that we can push the final docs to the target repository without requiring login data to be included in whatever way.
    DO NOT DIRECTLY INCLUDE YOUR PAT IN THE WOODPECKER FILE! This can result in your account being hijacked and abused. Always treat a PAT like a super important password nobody should know about.

In my example do I have .woodpecker/publish_docs.yml which does the publishing for us. Let's see how the file looks like and then go over all the necessary stuff:

publish_docs.yml
when:
  - event: push
    branch: master
    path:
      include: ["docs/**", "mkdocs.yml", "requirements.txt"]

variables:
  setup_git: &setup_git
    - apk add git
    - git config --global user.email "$CBEMAIL"
    - git config --global user.name "CI Docs Builder"

steps:
  cloneTargetRepo:
    image: alpine:3.18.4
    secrets: [cbemail]
    commands:
      - chmod -R a+w .
      - <<: *setup_git
      - git config --global --add safe.directory /woodpecker/src/codeberg.org/knut/docs-source
      - git config --global init.defaultBranch pages
      - git clone -b pages https://codeberg.org/knut/pages.git
      - chmod -R a+w pages
  buildDocs:
    image: woodpeckerci/plugin-mkdocs:1.2.0 
    settings:
      site_dir: pages
  commitAndPush:
    image: alpine:3.18.4
    secrets: [cbtoken, cbemail]
    commands:
      - <<: *setup_git
      - cd pages
      - git remote set-url origin https://$CBTOKEN@codeberg.org/knut/pages.git
      - git add --all
      - git commit -m "Update Docs ($( env TZ=Europe/Berlin date +"%d.%m.%Y %Z" )) [SKIP CI]"
      - git push

Lets get over the different parts to look at...

when:
  - event: push
    branch: master
    path:
      include: ["docs/**", "mkdocs.yml", "requirements.txt"]
This configures Woodpecker-CI to only run when a push is made towards the master branch and only files defined in include are modified.
Make sure to update master to whatever branch you use for your docs source and also update include to have the file paths necessary. The option supports glob patterns.


variables:
  setup_git: &setup_git
    - apk add git
    - git config --global user.email "$CBEMAIL"
    - git config --global user.name "CI Docs Builder"
This sets up a reusable list of commands using YAML's anchor feature to later use in the commands of Woodpecker-CI.
$CBEMAIL would be an enviroment variable that would be defined in a step using the secrets option.


steps:
  cloneTargetRepo:
    image: alpine:3.18.4
    secrets: [cbemail]
    commands:
      - chmod -R a+w .
      - <<: *setup_git
      - git config --global --add safe.directory /woodpecker/src/codeberg.org/knut/docs-source
      - git config --global init.defaultBranch pages
      - git clone -b pages https://codeberg.org/knut/pages.git
      - chmod -R a+w pages
This is the first step of the workflow which does multiple things:

  • It applies proper permissions using chmod to avoid access issues
  • It adds and setups git using the previously mentioned YAML anchor that has been created. <<: is another YAML feature that tells it to inject the provided anchor at this position.
  • It configures a safe directory in git to avoid security issues
  • It sets a default branch
  • It clones the target repository to where the built docs would be pushed. Make sure to update the URL here to whatever target repo yours would be.
    The -b pages defines the pages branch to be cloned from. Change the name to whatever branch name yours is.
  • It applies proper permission to the cloned repo folder.

Also, note that I use alpine here as image. You can use any docker image you like. Just make sure that essential commands such as chmod and mv are available, or else the workflow may not work for you.


steps:
  # ...
  buildDocs:
    image: woodpeckerci/plugin-mkdocs:1.2.0 
    settings:
      site_dir: pages
This step uses the official MkDocs plugin7 for Woodpecker-CI to build the documentation. The plugin itself uses Material for MkDocs as base alongside some other dependencies. Please see their documentation page for all settings.
We use the site_dir: setting to change the target directory for the built docs to our target repository (pages).


steps:
  # ...
  commitAndPush:
    image: alpine:3.18.4
    secrets: [cbtoken, cbemail]
    commands:
      - <<: *setup_git
      - cd pages
      - git remote set-url origin https://$CBTOKEN@codeberg.org/knut/pages.git
      - git add --all
      - git commit -m "Update Docs ($( env TZ=Europe/Berlin date +"%d.%m.%Y %Z" )) [SKIP CI]"
      - git push
This final step sets up git again like before, but also moves into the pages folder and sets the target URL to the target repository, while including the $CBTOKEN. This should be a valid Codeberg PAT to allow pushing the changes.
It then adds all the changed files, commits them (Note the [SKIP CI] which tells Woodpecker-CI to ignore this commit) and finally pushes it to the target repository to publish.

And that's all. Your site should now be available under your codeberg.page subdomain.

Adding custom domains

Similar to GitHub Pages does Codeberg pages allow you to define custom domains to use. The difference here is, that not only can you define multiple custom domains for one repository, but they also use a different file.

The file in question is called .domains which is already a smaller issue. With GitHub Pages you can have a CNAME file in the docs folder and MkDocs would carry it over into the build site. With the file from Codeberg Pages however can this not be done.
The main reason is MkDocs ignoring files starting with a dot by default.

You could now alter the configuration of MkDocs to change what files to ignore, or you can do the solution I'll show you below.

To make things work, all you have to do is make a domains file, fill it with the custom domain(s) you want to use and then add the following line to your workflow file:

publish_docs.yml
when:
  - event: push
    branch: master
    path:
      include: ["docs/**", "mkdocs.yml", "requirements.txt"]

variables:
  setup_git: &setup_git
    - apk add git
    - git config --global user.email "$CBEMAIL"
    - git config --global user.name "CI Docs Builder"

steps:
  cloneTargetRepo:
    image: alpine:3.18.4
    secrets: [cbemail]
    commands:
      - chmod -R a+w .
      - <<: *setup_git
      - git config --global --add safe.directory /woodpecker/src/codeberg.org/knut/docs-source
      - git config --global init.defaultBranch pages
      - git clone -b pages https://codeberg.org/knut/pages.git
      - chmod -R a+w pages
  buildDocs:
    image: woodpeckerci/plugin-mkdocs:1.2.0 
    settings:
      site_dir: pages
  commitAndPush:
    image: alpine:3.18.4
    secrets: [cbtoken, cbemail]
    commands:
      - <<: *setup_git
      - mv domains pages/.domains
      - cd pages
      - git remote set-url origin https://$CBTOKEN@codeberg.org/knut/pages.git
      - git add --all
      - git commit -m "Update Docs ($( env TZ=Europe/Berlin date +"%d.%m.%Y %Z" )) [SKIP CI]"
      - git push
This single line would now move the domains file into the pages folder and also rename it to .domains in order for Codeberg Pages to recognize it.

And that's already it. Your domain should now be available under your custom domain.

DNS settings

Remember to also configure your domain's DNS properly to have the domains work.
You can visit the Codeberg Pages documentation for how to setup and configure custom domains.


Footnotes


Last update: 13. March 2024 ()

Comments

Comment system powered by Mastodon.
Leave a comment using Mastodon or another Fediverse-compatible account.