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.

  • Dedicated branch and repository
    On GitHub Pages can you publish a site on a dedicated gh-pages branch, making it available under <user>.github.io, or by publishing your page on the default branch on a repository named <user>.github.io.
    Codeberg Pages offers a similar solution, with the difference being, that the name of the branch and repository are both pages. But similar to GitHub Pages would the pages branch be used for sub-pages under <user>.codeberg.page while the pages repository would be used for the <user>.codeberg.page domain itself.
  • Custom (sub)domains
    Both GitHub Pages and Codeberg Pages offer you the option to set a custom (sub)domain to use for your site.
    On GitHub would such a domain be set inside a CNAME file, while on Codeberg, the file would be named .domains. This file has some benefits, but also drawbacks, which are all explained in the later sections.

Beneficial features

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

  • Multiple custom (sub)domains
    Unlike GitHub Pages does Codeberg Pages allow you to define multiple (sub)domains for one site to use. In such a case would the domain at the very top be the default one used, with every additional entry below it being redirected to it by Codeberg Pages.
  • Redirects
    Using a _redirects file, you can create static or even dynamic redirects.
    As an example, adding the line /blog/* /blog/post/:splat would create a redirect for any /blog/ sub-page redirecting to /blog/post/ while preserving any additional subpages (i.e. /blog/01-01-hello redirects to /blog/post/01-01-hello).
  • Special sub-domains and sub-pages
    Codeberg Pages offers specific subdomains and sub-page patterns that can be used for various things. Namely...

    • raw.codeberg.page/<user>/<repository>/ can be used to access files in their raw format. Proper MIME types (Except for HTML) will be applied and are CORS accessible.
    • Any domain - either custom or codeberg.page subdomain - suffixed with a @<branch> will show the content of the defined <branch>. This is extremely useful for features like a deploy-preview. Keep in mind that / in branch names need to be replaced with ~ to work
  • Let (sub)domains point to specific branches of a repository
    Using DNS TXT entries, one can set custom (sub)domains that would display content of a specific repository and/or branch.
    As an example setting a TXT entry with key example and value example.repo.example.codeberg.page would result in a domain like example.example.com displaying the content of codeberg.org/example/repo/src/branch/example/

drawbacks

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

  • Custom domains break <user>.codeberg.page/<repo> pages
    Once you've set up a custom domain for your <user>.codeberg.page domain (A user/org-level domain) will any <user>.codeberg.page/<repo> domain stop working.
    This means that while <user>.codeberg.page/repo works, example.com/repo will not.
    This is a bug and has been reported already.
  • .domains file issue with site-builders
    MkDocs by default ignores files that start with a dot, making it ignore and effectively not include the .domains file itself.
    A workaround for this issue is shown later in this post.
  • No specific folder for a site
    On GitHub can you define a docs folder to be used as the source of your page content.
    Codeberg Pages unfortunetly does not offer such a solution. The static site content has to either be on the pages branch of a repository, or the default branch of the pages repository to be displayed.
    A small workaround exists, where you either use the @<branch> sub-page pattern or make your (sub)domain point to a specific branch through DNS. This still doesn't solve the issue of the content not being stored in a docs folder or alike. You have to put it in the root.

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.

The user Tobias Bölz created a blog post covering how you can publish your site using Forgejo Actions while also using the pages branch aproach. It can be found here.

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.
Below is the entire file. You can click the for further details on the different parts:

publish_docs.yml
when:
  - event: push # (1)
    branch: ${CI_REPO_DEFAULT_BRANCH}
    path:
      include: ["docs/**", "mkdocs.yml", "requirements.txt"]

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

steps:
  cloneTargetRepo:
    image: alpine:3.18.4 # (3)
    secrets: [cbemail]
    commands:
      - chmod -R a+w .
      - <<: *setup_git # (4)
      - 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 # (5)
      - chmod -R a+w pages
  buildDocs:
    image: woodpeckerci/plugin-mkdocs:1.2.0 # (6)
    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 # (7)
      - git add --all
      - git commit -m "Update Docs ($( env TZ=Europe/Berlin date +"%d.%m.%Y %Z" )) [SKIP CI]"
      - git push
  1. This configures our pipeline to only run when a push is made towards the repository's default branch (${CI_REPO_DEFAULT_BRANCH}) and if any file in the docs directory, the mkdocs.yml file, or the requirements.txt file are affected.
  2. This is a reusable (Also known as YAML anchor) that we insert into the different steps later on using - <<: *setup_git. The $CBEMAIL is a secret that we will provide in the later steps.
  3. I use alpine here as it is a fairly lightweight system to use that offers necessary commands (cd, mv, etc.). You can use any image available on Docker as long as they offer necessary features used here.
  4. This injects the setup_git anchor we defined earlier in the file, adding git to the system and configuring all necessary things such as username and e-mail.
  5. We clone our target repository to then build or site into. This particular command would result in the repository being cloned into a pages folder.
  6. We use the official MkDocs Woodpecker-CI plugin7 to build our docs with. This plugin uses Material for MkDocs as its base, so certain dependencies are already included.
    We also set the site_dir option to point to pages, so that our final site is being created in there.
    Also, make sure to check if you're using the latest version available. You can use latest as version to always use the latest dev build from Docker.
  7. We set the URL of the origin repository to point to our pages repository, while using our Codeberg PAT for authorization. This is important as it otherwise would not allow us to push the changes to the repository.

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: 06. May 2024 ()

Comments

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