How to build a Hugo static site automatically using Drone CI and Codeberg.org
In this guide I’ll show the necessary steps to set up a Codeberg Git repository in Drone CI and run a simple build pipeline to generate and upload your static Hugo website. Anoxinon e.V. from Germany is using a very similar setup to make its blog authors’ life easier.
All the steps are shown in an LXD based environment on Debian 10 Buster. Of course it will work very similarly on other Linux systems.
Preparing your Host
As Drone CI heavily depends on a Docker Environment, install the Docker Daemon on your Server (in fact, using Drone CI’s Docker image is the only official way to install it for now):
https://docs.docker.com/install/linux/docker-ce/debian/#install-docker-ce
Preparing credentials
The first credential you’ll need for your Drone setup is a random shared secret that is generated e.g. via:
openssl rand -hex 16
Note down the random string - you will need it later. The secret is used to connect the Drone main instance with a “worker” / “runner” / “agent” instance. The agent accepts build jobs and runs them in its own Docker environment. There can be more than just one agent, but a single one is sufficient for now.
Next, log in to your Codeberg.org account and enter the settings page. Create a new OAuth2 application in the “Applications” tab. You can give any title to your new Application, such as “Drone”. The “Redirect URI” must point to the Login page of your future Drone web interface. In case you’re planning to make the web interface available at https://drone.myserver.tld
, the settings look like this:
Hit “Create Application”. You will receive
- A client ID
- A client secret
You will need both to connect Drone to your Codeberg repositories. Note them down carefully. As soon as you click on “Save”, the secret will never be displayed again.
Setting up startup files for Drone CI and Drone agent
Now let’s get Drone configured!
Drone is configured mainly via environment variables. There’s a lot of them, and we need to define them in the docker run
command. That is why you should create two different startup Bash scripts. The first one is for the Drone main instance - the second one is for the build agent.
For the Drone main instance: create_drone_main.sh
#!/bin/bash
export DRONE_GITEA_SERVER="https://codeberg.org"
export DRONE_GITEA_CLIENT_ID=""
export DRONE_GITEA_CLIENT_SECRET=""
export DRONE_RPC_SECRET=""
export DRONE_SERVER_HOST="drone.myserver.tld"
export DRONE_SERVER_PROTO="https"
export DRONE_ALLOWED_USERS="myuser@codeberg.org, mygroup"
docker run \
--volume=/var/lib/drone:/data \
--env=DRONE_AGENTS_ENABLED=true \
--env=DRONE_GITEA_SERVER=${DRONE_GITEA_SERVER} \
--env=DRONE_GITEA_CLIENT_ID=${DRONE_GITEA_CLIENT_ID} \
--env=DRONE_GITEA_CLIENT_SECRET=${DRONE_GITEA_CLIENT_SECRET} \
--env=DRONE_RPC_SECRET=${DRONE_RPC_SECRET} \
--env=DRONE_SERVER_HOST=${DRONE_SERVER_HOST} \
--env=DRONE_SERVER_PROTO=${DRONE_SERVER_PROTO} \
--env=DRONE_TLS_AUTOCERT=true \
--env=DRONE_USER_FILTER="${DRONE_ALLOWED_USERS}" \
--publish=80:80 \
--publish=443:443 \
--restart=always \
--detach=true \
--name=drone \
drone/drone:latest
You need to adapt all the export
ed variables to your environment and your own credentials. RPC_SECRET
is the “shared secret” that you created earlier with the openssl
command.
DRONE_ALLOWED_USERS="myuser@codeberg.org, mygroup"
defines all (Codeberg) users and groups that are allowed to register / use your Drone.ci instance.
For the Drone Docker runner: create_drone_agent.sh
#/bin/bash
export DRONE_RUNNER_NAME="local-docker"
export DRONE_RPC_PROTO="https"
export DRONE_RPC_HOST="drone.myserver.tld"
export DRONE_RPC_SECRET=""
docker run -d \
-v /var/run/docker.sock:/var/run/docker.sock \
-e DRONE_RPC_PROTO=${DRONE_RPC_PROTO} \
-e DRONE_RPC_HOST=${DRONE_RPC_HOST} \
-e DRONE_RPC_SECRET=${DRONE_RPC_SECRET} \
-e DRONE_RUNNER_CAPACITY=2 \
-e DRONE_RUNNER_NAME=${DRONE_RUNNER_NAME} \
-p 3000:3000 \
--restart always \
--name ${DRONE_RUNNER_NAME} \
drone/drone-runner-docker:latest
RUNNER_NAME
: Can be any name. You can choose your own :-)RPC_SECRET
: The “shared secret” again.
Make both files executable:
chmod u+x create_drone_main.sh
chmod u+x create_drone_agent.sh
And now let the magic happen: Start your main instance, first. Then, start your agent:
./create_drone_main
./create_drone_agent
docker run
commands have a --restart always
flag to auto-start after a reboot. There’s no need to run the Bash scripts ever again - except you’ve removed one of the containers e.g. because of re-configuration.It might take a minute until both containers are initialized and ready. You should then be able to open https://drone.myserver.tld in your web browser.
Authenticating on Codeberg and activating Drone for your repos
Drone will ask you to log in to your Codeberg account or accept an access request. Allow the OAuth2 request, so Drone can access your repositories.
Back on the Drone CI main page, you should be able to see a “Sync” button at the top of the page. Click it. Your Git repositories should now appear as a list below.
Hit “Activate” on every repo which Drone should be enabled for. After confirming your request on the next page, you will will be shown the CICD settings for that repository.
Creating a Drone “build and publish” pipeline for Hugo
You can leave most of the settings at their default values. We only need to add a “Secret”.
A secret in Drone terms can be any string. It can be a password, a PIN, or even an SSH key… and that’s what we are going to use it for: We will create a “secret” to securely store the SSH key that we are going to use for the website upload.
Create a user and SSH key for upload
To safely upload the generated HTML files, create a new Linux user on your webserver and generate a new SSH key for it:
adduser drone
su - drone
ssh-keygen -t ed25519
exit
Do not set a password for the key!
Then set the permissions on your webserver’s directory, so the drone
user has full access:
chown -R drone:nginx /var/www/mysite.tld
Copy the SSH private key and paste it into the “Secret Value” field in your Drone repository settings:
cat /home/drone/.ssh/id_ed25519
The “Secret Name” field muss be set to drone_ssh_key
. Then hit “Add a secret”.
Create pipeline in Hugo source repository
All build pipelines are controlled via a .drone.yml
file (mind the dot prefix!) in their corresponding source repo. To enable the Hugo build pipeline, create a .drone.yml
file in your source Repo and put the following contents inside:
---
kind: pipeline
type: docker
name: default
trigger:
branch:
- master
steps:
- name: Version check
image: jguyomard/hugo-builder
commands:
- echo "Checking Hugo version."
- hugo version
- name: Build
image: jguyomard/hugo-builder
commands:
- hugo --destination /drone/src/build
- minify -r -o /drone/src/build /drone/src/build
- name: Upload
image: jguyomard/hugo-builder
commands:
- eval `ssh-agent -s`
- echo "$SSH_KEY" | ssh-add -
- mkdir -p ~/.ssh
- echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config
- rsync -rv -e "ssh -p 22" /drone/src/build/ drone@mywebserver.tld:/var/www/mysite.tld/ --checksum
environment:
SSH_KEY:
from_secret: drone_ssh_key
If you have a basic understanding of YAML and how build pipelines work in general, you might already have an idea what’s happening here.
The build process is separated in three steps. For each step a name
, an image
and a set of commands
is defined. The image
defines a Docker image as a build environment. All the commands
will be run inside that Docker container. In this example, all the commands will be run in jguyomard’s “hugo-builder” container, which includes all the needed build dependencies, such as the hugo
binary, a minify
binary and some SSH tools for the upload. The environment
setting at the end makes sure that the SSH key secret is available as an environment variable.
drone@mywebserver.tld:/var/www/mysite.tld/
according to your web server’s configurationPush the new file to your Git repo, and watch Drone CI build and upload your site … :-)
The build and upload will be triggered on every push to the master
branch. You change that behavior in the trigger
section of the .drone.yml
config file.
Happy droning!