The pretty archaic Drone UI — but that’s all you need with Drone.

Introduction

We have been using Drone CI in our project for some months now. Recently I had to do a major refactoring regarding my team’s Drone pipelines so I thought that now that the Drone configurations are rather fresh in my memory I could write a blog post regarding my experiences with the Drone CI. So, here are my experiences using Drone CI as a developer — if you are looking for sysops stuff like how to install Drone CI, how to do authentication, etc. I’m not writing about that stuff and you need to read e.g. the official Drone documentation.

Drone CI Basics

I’m not going to give a lecture about Drone, you can read more about Drone in Drone CI home page but instead, I write about some important aspects about Drone CI regarding my experiences as a developer who maintains Drone pipelines and uses Drone to build and deploy applications as a daily basis.

local featureBranchPattern = "feature/*";
...
local buildApps() = {
name: 'build-apps',
user: user,
...
# Drone pipelines
[
{
kind: 'pipeline',
type:'kubernetes',
name: 'feature-branch',
trigger: {
branch: [featureBranchPattern],
event: ['push'],
},
steps: [
changeRights(),
cleanDirs(),
buildApps(),
startPostgresMinio(),
runTests(),
...
local user = 1000;
local buildBoxImage = 'circleci/clojure:openjdk-11-tools-deps-1.10.0.414-node';
local m2Repo = ' -Sdeps \'{:mvn/local-repo "/drone/src/.m2/repository"}\' ';
...
local buildApps() = {
name: 'build-apps',
user: user,
image: buildBoxImage,
commands: [
'echo "Starting step: buildApps *********************"',
'whoami',
'pwd',
'export ALL_BEGIN=$$(date +%s)',
# Start actual jar building
'echo "Building koodisto *********************"',
'export COMMAND_BEGIN=$$(date +%s)',
'cd koodisto',
'export NPM_CONFIG_PREFIX=/drone/src/koodisto/.npm-global',
'export PATH=$PATH:/drone/src/koodisto/.npm-global/bin',
'npm install shadow-cljs',
'npm install',
'echo "css phase..."',
'clj ' + m2Repo + ' -A:css -t target/shadow/prod/resources/public/css',
'echo "frontend phase..."',
'clj ' + m2Repo + ' -A:common:frontend -m shadow.cljs.devtools.cli release app',
'echo "uberjar phase..."',
'clj ' + m2Repo + ' -A:common:backend:uberjar',
'cd ..',
'export END=$$(date +%s)',
'export KOODISTO_DURATION=$$(($${END}-$${COMMAND_BEGIN}))',
'echo Koodisto build duration $$(($$KOODISTO_DURATION / 60)) minutes and $$(($$KOODISTO_DURATION % 60)) seconds',
...
# Convert .drone.jsonnet to .drone.yml...
drone jsonnet --source .drone.jsonnet --stream --target .drone.yml
# ... and run it.
DRONE_REPO_NAME="testing2" DRONE_BRANCH="testing2" DRONE_COMMIT_SHA="testing2aa2" drone exec --trusted --pipeline feature-branch --env-file=tmp/.env .drone.yml
[build-apps:0] + echo "Starting step: buildApps *********************"
[build-apps:1] Starting step: buildApps *********************
[build-apps:2] + whoami
[build-apps:3] node
[build-apps:4] + pwd
[build-apps:5] /drone/src
[build-apps:6] + export ALL_BEGIN=$(date +%s)
[build-apps:7] + echo "Building koodisto *********************"
[build-apps:8] Building koodisto *********************
[build-apps:9] + export COMMAND_BEGIN=$(date +%s)
[build-apps:10] + cd koodisto
[build-apps:11] + export NPM_CONFIG_PREFIX=/drone/src/koodisto/.npm-global
[build-apps:12] + export PATH=$PATH:/drone/src/koodisto/.npm-global/bin
[build-apps:13] + npm install shadow-cljs
[build-apps:14]
[build-apps:15] > puppeteer@1.20.0 install /drone/src/koodisto/node_modules/puppeteer
[build-apps:16] > node install.js
...
...
local dockerComposePluginImage = 'docker/compose:1.25.4';
...
local startPostgresMinio() = {
name: 'start-postgres-minio',
image: dockerComposePluginImage,
...
commands: [
'echo "Starting docker-compose: postgres and minio..."',
'docker-compose -p clojure-dc -f infra/docker-compose.yml up -d',
],
};
...

local koodistoTests() = {
name: 'koodisto-tests',
image: dockerComposePluginImage,
...
commands: [
...
'echo "Building koodisto unit test image..."',
'docker build -f koodisto/infra/unit-test/Dockerfile -t ' + koodistoUtImage + ' koodisto',
'echo "Starting testing koodisto..."',
'docker run -u 1000 --name koodisto-unit-test --network clojure-network --env-file infra/.env -v /drone/src:/drone/src ' + koodistoUtImage,
],
};
...
# Drone pipelines
...
steps: [
...
buildApps(),
startPostgresMinio(),
koodistoTests(),
...

Some Observations

In this chapter I list some special observations I noted when using Drone.

local user = 1000;    # => Use this user.
...
local buildBoxImage = 'circleci/clojure:openjdk-11-tools-deps-1.10.0.414-node';
...
local buildApps() = {
name: 'build-apps',
user: user, # => This image provides that user
image: buildBoxImage,
...
local changeRights() = {
name: 'change-rights',
image: 'alpine:3.11',
commands:
[
'adduser -D -H -u 1000 node node', # => This image doesn't provide that user... so, just create it.
'getent passwd | grep node',
'chown -R node /drone/src',
],
...
local changeRights() = {
name: 'change-rights',
image: 'alpine:3.11',
commands:
[
'sleep 100000', # => Stop the world!
'adduser -D -H -u 1000 node node',
[print-time-start-pipeline:3] + echo $MYBUF >> drone-build.log
[change-rights:0] + sleep 100000 # => It stopped here!
λ> whoami
kari
λ> docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
71ca3dd39a37 alpine:3.11 "/bin/sh /usr/drone/…" 16 minutes ago Up 16 minutes sun7x2doztrvwt72j058x9lcn9go3h81
c67686bcdcfb docker:dind "dockerd-entrypoint.…" 16 minutes ago Up 16 minutes 2375-2376/tcp 5hi9nwckg6dtufpg06znzf7l6qyqu1ir
λ> docker exec -it 71 /bin/sh
/drone/src # whoami # => We are in!
root
/drone/src # ls -lah
total 84K
drwxrwxr-x 10 1000 1000 4.0K Aug 26 13:33 .
drwxr-xr-x 3 root root 4.0K Aug 26 13:33 ..
-rw-rw-r-- 1 1000 1000 11.2K Aug 26 13:32 .drone.jsonnet
-rw-rw-r-- 1 1000 1000 10.0K Aug 26 13:32 .drone.yml
drwxrwxr-x 8 1000 1000 4.0K Aug 26 11:11 .git

Comparing Jenkins and Drone

I have mostly used Jenkins the previous years as my CI server. When comparing my Jenkins and Drone experiences I kind of like Drone more for the following reasons:

  • Containers. Containers provide a nice isolated place for your builds and they are also rather nice to debug once you understand the basic Drone container based architecture. Debugging Jenkins builds are of course rather easy, since you can always just get a ssh session to the Jenkins server and go to the Jenkins workplace directory and experiment there.
  • Local development. drone exec is a real nice tool to develop your CI pipelines. And if you follow some practices (e.g. the same user in all steps…) your local pipeline runs are exactly the same way as your Drone server runs them.
  • No UI hassle. If you want to use some specific plugins in Jenkins you can of course install them when you install Jenkins, but there are quite a lot of configurations in those plugins that you usually need to do using Jenkins UI. With Drone everything are just Docker containers, no special configurations.

Conclusions

Drone is a refreshing new CI tool which provides a pretty different architecture and model to create your build pipelines. Give it a try — I think you just might like it!

I’m a Software architect and developer. Currently implementing systems on AWS / GCP / Azure / Docker / Kubernetes using Java, Python, Go and Clojure.