automatic deploy & build of an SSG site (feat. forgejo & sourcehut builds)

| self-hosting |

so yesterday, i moved one of my personal sites, eunoia, from hand-written HTML/CSS to a static site generator, astro. it was a lot of fun! i figured out pages and content collections and stuff, all while preserving my site styling, layout, and content. it was pretty simple and a ton of fun.

i got bored after doing all of that though, so i thought: hm, wouldn't it be cool if i could have the site rebuild and deploy after i push a change to my forgejo instance?

at first i thought i'd need a forgejo runner to mimic github actions for this, which seems to be a huge undertaking that i COULD probably do but wasn't really keen on for security and lazy reasons. but then i remembered: i pay for a sourcehut account! i could use their builds service!

turns out it's a tiny bit more involved than i thought, due to the differing services. but it's not too hard!

basic setup and prerequisites

i'm assuming you have a self-hosted forgejo and/or gitea instance for this. alternatively, you can use the biggest forgejo instance out there, codeberg.

additionally, you'll also need a paid sourcehut account to use their builds service. i pay like $2/month for access to all their services so like it's well worth it honestly.

to bridge between forgejo and sourcehut, the wonderful dev simon ser created something that does just that: yojo! you can self host yojo, but again, for laziness reasons, i opted to use the provided public instance.

configuring forgejo

you have to create OAuth2 clients for both services. on your forgejo instance, navigate to your user settings, then the application section, and create an OAuth2 client token with the following redirect URL, replacing example.com with the URL of your instance (or the codeberg URL!):


https://example.com/authorize-gitea

once you've created the application, note down the client ID and client secret provided to you. you'll only be shown the secret once, so if you lose it, you need to delete and recreate the whole client.

next, you'll need to make an access token. this is only if you self host a forgejo instance. it's on the same page as OAuth2 applications, under an "access tokens" header.

create a new token with whatever name you want, choose the repository access level (either work), and for the permissions, give it the following:

these are all required by yojo.

once done, save the token value it provides you — like the client secret, you won't see it again, so don't lose it.

yojo setup

for sourcehut, it's essentially the same process, but this time, yojo can do it for us. navigate to yojo — either the public instance or your own — and click login with sr.ht. critically, check the box that says enable build secrets — we'll need this later!

after that, you can just give yojo the access it requests. if you're nervous about how much it needs, i recommend self-hosting a yojo instance for extra control. but i'm a lazy fuck so i just gave the public instance access. whatever!

once you've logged into sourcehut, you can navigate back to the yojo page and log in with either codeberg or your own forgejo instance. if you use codeberg, just click the big blue button for that. if not, you'll see a line that says use a custom forgejo/gitea instance — click that, and it'll show some options.

before doing anything, be sure to check the build secrets box again! you really need this!

for the instance URL, paste in your forgejo instance's URL (without any parameters or sub-paths). for the access token, well, give it the access token value you generated earlier!

and now you should be logged in :)

you can either enable yojo for all repositories, for some sicko reason, or you can enable it for the repositories you'll be automating. i chose the latter, so if you do the same, choose your repo from the list it provides, then once it redirects you to its page, click the blue button that says enable. there should be a header for build secrets below it if you have a build secret configured — if not, there's either nothing there or just a header that says secrets, i don't know. we're about to do that though.

that's it for yojo!

builds setup

over on sourcehut, you need to create a build secret. the secret will contain an SSH private key to give it access to build on your server. that sounds scary, so you might want to create a new user on your server with its own special SSH key for this, which is what i did.

generate an RSA SSH key for this, because when i tried the better algorithm (ed25519), it crapped out on me. you can do that with the following command:


ssh-keygen -t rsa

you can choose where the key will save to; it should probably be in ~/.ssh. you also might want to name it for its purpose, like srht or builds or something.

after that, cat the private key file and copy its output. do NOT share this anywhere else besides sourcehut's build secrets; if you do, the key is compromised.

back to sourcehut: navigate to the build secrets page, and where it says add new secret, give it a name, paste the output of the private key in the big secret box, and choose SSH Key as the type of secret. then add it!

once its added, you'll see a weird alphanumeric string with dashes. that's your secret! copy and note it down.

writing a build manifest

here's the slightly confusing part. the documentation for sourcehut build manifests is a little barebones, but i eventually figured it out. to build a statically generated site, you'll generally want to break it down into three tasks:

that's exactly what i did. as my site is built with astro, it required the following dependencies:

the universally required dependencies, no matter your SSG, are the following:

now we can write the manifest!

in the root of your site's git repo, create a new file named .build.yml. make sure it's not with a yaml extension — i did that at first and then wondered why my builds wouldn't get detected lol

my file looks like this:


image: alpine/latest
packages:
  - nodejs
  - npm
  - openssh
  - rsync
sources:
  - https://bytes.4-walls.net/kat/eunoia-astro
environment:
  deploy: kat@loop.sayitditto.net
secrets:
  - 43b388e6-3f50-4b13-9ac9-9b776c95b20c
tasks:
  - setup: |
      cd eunoia-astro
      npm install
  - build: |
      cd eunoia-astro
      npm run build
  - deploy: |
      cd eunoia-astro
      sshopts="ssh -p 22685 -i ~/.ssh/id_rsa -o StrictHostKeyChecking=no -o ServerAliveInterval=30"
      rsync -e "$sshopts" -avz ./dist "$deploy:/var/www/eunoia2/"

let's break it down:

for each of those steps, you need to cd into the repo, as the build will automatically git clone it for you.

in the setup step, install your dependencies. if you're using something like jekyll, you probably just have to do like, bundle install after including ruby and jekyll as dependencies or something. i'm not quite sure, but it's probably whatever you did to set up your site environment in the first place.

in the build step, simply run the command you use to compile the site. for me it's npm run build, but i know for something like hugo, it's just... well, hugo. consult your SSG's documentation on this if you're confused

lastly, for the deploy step, we first have to define a variable: sshopts. this is just what the rsync command will use to log into your server. if you use a different SSH port, configure that with the -p flag followed by the port number; if you use the default, you can safely remove that flag. keep the -i flag because i couldn't get it working without that. lastly, you'll need to set StrictHostKeyChecking=no for some reason, and i believe the ServerAliveInterval one should be unnecessary for most, but because of my SSH tunnel, i needed it.

in the rsync command, all you have to do is change the name of the dist folder to the folder name your SSG outputs as the final compiled site folder, and then change the site directory destination from /var/www/eunoia2 (this is mine) to wherever yours is located.

it should (hopefully) work now after a git push

now you can make a minor change to your site and test the build! maybe just edit the read me file or something. i did that several times over to debug things.

on your forgejo instance, where it shows the commit message, there should be a little yellow dot now — if you click it, it'll lead you to the page for the build job on sourcehut! it should show the build in progress; if it completes successfully or fails, it'll tell you, and if it fails, you can manually re-run the job with a tweaked build manifest to debug things.

if the job is successful, refresh your forgejo instance's page, and the dot will be replaced with a check mark. navigate to your site's domain, and if you made any changes to the site itself, they should show! yay for automation saving us manual steps!

conclusion

builds are really cool stuff and i hope to incorporate them in more of my projects with this exact method. i'd self-host a forgejo runner if i wasn't scared of the security implications and the idea of spinning up one-off VMs for random builds, so sourcehut's service is the next best thing for me. i hope this is helpful for anyone hoping to do something similar!


home | me