Introduction

In my previous posts, I’ve covered how to set up Drone, Gitea and deploy a build to an FTP server. In this post, I’ll be covering how to configure a full pipeline to include:

  • A continuous testing build on each commit to the Develop branch.
  • An automated build to a staging environment on commit to the Main branch.
  • A manual deployment to a production environment using the Promote function in Drone.

Gitea Drone Setup

Prerequisites

Before getting started, make sure you have the following prerequisites in place:

  • A working Hugo website:
    • Set up a Hugo website locally.
  • A Git repository:
    • Host your Hugo website’s code in a Gitea repository.
  • A working Drone instance:
    • Your Drone instance should be working correctly with your Gitea repository.
  • An FTP server
    • An FTP server configured with username and password, a valid user home directory and web content loaded from that location.

What is a Drone Pipeline?

In Drone, a pipeline refers to a sequence of steps and actions that are executed in a predefined order to build, test, and deploy software. It provides a structured and automated workflow for managing the different stages of the development process.

A pipeline in Drone is typically defined using a configuration file named ‘.drone.yml’. This file specifies the steps to be executed, the order in which they should run, and any required configurations or dependencies.

Here are the key components and concepts associated with a pipeline in Drone:

  • Stages:
    A pipeline is divided into stages, which represent different phases of the software development lifecycle. Common stages include building, testing, and deploying. Each stage contains one or more steps that define the actions to be performed.

  • Steps:
    Steps are the individual units of work within a stage. They represent specific actions to be executed, such as running a command, running tests, or deploying the application. Steps can be executed sequentially or in parallel, depending on the requirements.

  • Dependencies:
    A step in a pipeline may have dependencies on previous steps. This allows for the establishment of a logical flow, where certain steps must be completed successfully before proceeding to the next ones. Dependencies ensure that the pipeline runs in a consistent and controlled manner.

  • Conditions:
    Conditions are used to control the execution of steps based on specific criteria. For example, a step may only run if a certain condition is met, such as the branch name, the outcome of a previous step, or the presence of a specific environment variable. Conditions provide flexibility and allow for conditional execution based on the project’s needs.

  • Artifacts:
    Artifacts are files or directories generated during the pipeline execution that can be passed between steps or stored for later use. They allow for sharing data, test reports, build artifacts, or any other relevant files between different stages of the pipeline.

  • Triggers:
    Triggers determine when the pipeline should run. They can be defined to automatically start the pipeline on specific events, such as code pushes, pull requests, or scheduled intervals. Triggers ensure that the pipeline is executed whenever a relevant change occurs, maintaining an up-to-date and automated development process.

Development Build

In my current setup, every commit to the Develop branch is picked up by Drone and creates a build. This build pulls the develop branch from the Gitea repository, installs the Hugo modules and builds the Hugo website in development mode.

I have also set this to fail on any warnings, so that I can quickly see if there are any problems with my code.

This step is where I would run any tests on the output code and fail the build should any fail. More to come on that in future posts.

The specific steps for this in the drone.yaml file are:

---
kind: pipeline
type: docker
name: "Test Environment"

trigger:
  branch: 
    - develop 
  event:
    include:
      - push

steps:   
  - name: Clone Git Submodules
    image: alpine/git 
    commands:
      - git submodule init
      - git submodule update --recursive --remote

  - name: Build with Hugo
    image: binaryronin/drone-hugo:latest
    pull: always 
    commands:
      - echo "Checking Hugo version."
      - hugo version
      - cd /drone/src/hugo/
      # Disable panicOnWarning due to bug in Hugo.
      - hugo --environment development --verbose --debug # --panicOnWarning
      - ls -al /drone/src/hugo/public

As you can see, every push to the ‘develop’ branch will trigger the following steps. I have this configured to also show debug and more verbose information to help with investigating any problems. You’ll see I also specify the development environment in Hugo, this is to prevent items such as Google Analytics from running in a test setup.

Staging Build

My staging build is run when a commit is made to the main branch. A nice approach would be to lock down the main branch so that no commits can be made directly to it. Any merges from the develop branch must come via a pull request. A tad overkill for a home setup, but it’s good practice.

This build will do the same as the Test build, but also includes a step to deploy to my staging web server.

---
kind: pipeline
type: docker
name: "Staging Environment"

trigger:
  branch:
    - main
  event:
    include: 
      - push

steps:
  - name: Clone Git Submodules
    image: alpine/git
    commands:
      - git submodule init
      - git submodule update --recursive --remote

  - name: Build with Hugo
    image: binaryronin/drone-hugo:latest
    pull: always 
    commands:
      - echo "Checking Hugo version."
      - hugo version
      - cd /drone/src/hugo/
      - hugo --environment develop --verbose
      - ls -al /drone/src/hugo/public

  - name: Deploy to Staging FTP
    image: cschlosser/drone-ftps
    environment:
        FTP_USERNAME:
          from_secret: staging_ftp_username
        FTP_PASSWORD:
          from_secret: staging_ftp_password 
        PLUGIN_SECURE: true
        PLUGIN_VERIFY: true
        PLUGIN_HOSTNAME:
          from_secret: staging_ftp_hostname
        PLUGIN_SRC_DIR: /hugo/public/
        PLUGIN_DEST_DIR: 
          from_secret: staging_ftp_directory
        PLUGIN_AUTO_CONFIRM: true
        PLUGIN_DEBUG: true 
        PLUGIN_ONLY_NEWER: true

You can see that I specify the environment for Hugo, as development. And this time, take advantage of Drone secrets for the Staging server details and credentials.

Production Build

Now the production build is slightly different, in that it can only be run through Drone by promoting an existing successful Staging build.

---
kind: pipeline
type: docker
name: "Production Environment"

trigger:
  target: 
    - production

steps:
  - name: Clone Git Submodules
    image: alpine/git
    commands:
      - git submodule init
      - git submodule update --recursive --remote

  - name: Build with Hugo
    image: binaryronin/drone-hugo:latest
    pull: always 
    commands:
      - echo "Checking Hugo version."
      - hugo version
      - cd /drone/src/hugo/
      - hugo --environment production --verbose
      - ls -al /drone/src/hugo/public

  - name: Deploy to Production FTP
    image: cschlosser/drone-ftps
    environment:
        FTP_USERNAME:
          from_secret: production_ftp_username
        FTP_PASSWORD:
          from_secret: production_ftp_password 
        PLUGIN_SECURE: true
        PLUGIN_VERIFY: true
        PLUGIN_HOSTNAME:
          from_secret: production_ftp_hostname
        PLUGIN_SRC_DIR: /hugo/public/
        PLUGIN_DEST_DIR: 
          from_secret: production_ftp_directory
        PLUGIN_AUTO_CONFIRM: true
        PLUGIN_DEBUG: true 
        PLUGIN_ONLY_NEWER: true

Here you’ll see that we change the environment for Hugo to be production and use Drone secrets again to specify the Production details and credentials.

Complete Drone Configuration

Putting all of these steps together, the complete config.yml file looks like this:

---
kind: pipeline
type: docker
name: "Test Environment"

trigger:
  branch: 
    - develop 
  event:
    include:
      - push

steps:   
  - name: Clone Git Submodules
    image: alpine/git 
    commands:
      - git submodule init
      - git submodule update --recursive --remote

  - name: Build with Hugo
    image: binaryronin/drone-hugo:latest
    pull: always 
    commands:
      - echo "Checking Hugo version."
      - hugo version
      - cd /drone/src/hugo/
      # Disable panicOnWarning due to bug in Hugo.
      - hugo --environment development --verbose --debug # --panicOnWarning
      - ls -al /drone/src/hugo/public

---
kind: pipeline
type: docker
name: "Staging Environment"

trigger:
  branch:
    - main
  event:
    include: 
      - push

steps:
  - name: Clone Git Submodules
    image: alpine/git
    commands:
      - git submodule init
      - git submodule update --recursive --remote

  - name: Build with Hugo
    image: binaryronin/drone-hugo:latest
    pull: always 
    commands:
      - echo "Checking Hugo version."
      - hugo version
      - cd /drone/src/hugo/
      - hugo --environment development --verbose
      - ls -al /drone/src/hugo/public

  - name: Deploy to Staging FTP
    image: cschlosser/drone-ftps
    environment:
        FTP_USERNAME:
          from_secret: staging_ftp_username
        FTP_PASSWORD:
          from_secret: staging_ftp_password 
        PLUGIN_SECURE: true
        PLUGIN_VERIFY: true
        PLUGIN_HOSTNAME:
          from_secret: staging_ftp_hostname
        PLUGIN_SRC_DIR: /hugo/public/
        PLUGIN_DEST_DIR: 
          from_secret: staging_ftp_directory
        PLUGIN_AUTO_CONFIRM: true
        PLUGIN_DEBUG: true 
        PLUGIN_ONLY_NEWER: true

---
kind: pipeline
type: docker
name: "Production Environment"

trigger:
  target: 
    - production

steps:
  - name: Clone Git Submodules
    image: alpine/git
    commands:
      - git submodule init
      - git submodule update --recursive --remote

  - name: Build with Hugo
    image: binaryronin/drone-hugo:latest
    pull: always 
    commands:
      - echo "Checking Hugo version."
      - hugo version
      - cd /drone/src/hugo/
      - hugo --environment production --verbose
      - ls -al /drone/src/hugo/public

  - name: Deploy to Production FTP
    image: cschlosser/drone-ftps
    environment:
        FTP_USERNAME:
          from_secret: production_ftp_username
        FTP_PASSWORD:
          from_secret: production_ftp_password 
        PLUGIN_SECURE: true
        PLUGIN_VERIFY: true
        PLUGIN_HOSTNAME:
          from_secret: production_ftp_hostname
        PLUGIN_SRC_DIR: /hugo/public/
        PLUGIN_DEST_DIR: 
          from_secret: production_ftp_directory
        PLUGIN_AUTO_CONFIRM: true
        PLUGIN_DEBUG: true 
        PLUGIN_ONLY_NEWER: true

Summary

With the above setup, you can see how easily a full work flow can be configured in Drone that will enable automatic and continuous builds and deployments. Hopefully saving you some time in the future and catching any problems well before they reach the production environment.

I’ll continue documenting my journey with Gitea, Drone and Hugo and share the interesting things I find along the way.

If you want to look further in to any of the above tools, do take a look at my previous posts and read the official documentation from the following sites:

Gitea Drone Hugo

Related Posts