λ ryan. himmelwright. net

Creating a CI/CD 'Draft' Website with Jenkins (and Hugo)

test

American Tobacco Campus, Durham NC

The last few months I have been working more with the open source automation server, Jenkins. While digging into it, I have been thinking of ways to improve my home build environment. One idea, was to utilize Jenkins to automatically build and deploy a “draft” website, so I can stage new posts/website change on my home network, before publishing it to the “production” website. Here is how that idea was Instantiated…

My Website

Jenkins Logo

I have previously described how my website is currently generated, using the Hugo static website generator. To organize this system, I have two git repos: One that consists of all the hugo source files (where I write content), and one that contains the generated static website (that gets deployed to my web host).

When writing a post, I use hugo server -D -F to live view the page in my browser. However, I occasionally want to view the state of all the committed code in the repo, to see what the site would look like if I decided to publish a post. So, I created a “drafts” website, which shows the current state of my website’s source repo (including draft and future posts). If I want to check how a post looks on my phone, or any other device, I can just open up the draft website after pushing my changes.

Jenkins Logo

Jenkins

I had previously created a dedicated Jenkins server on my home network (Mr. Mime), using a CentOS 7 VM hosted on my home server. However, any Jenkins setup should work for this project (including a docker container). To get started, checkout the Jenkins Website, and be sure to take advantage of the the documentation for help.

Note: just make sure hugo is installed on the Jenkins server, as we need it to generate the website.

GitHub Integration

Jenkins Service

My website repo is hosted on GitHub, so we need to configure it to work with our Jenkins server. To do that, go to the project’s GitHub page, and navigate through Settings -> Integrations & services. Click the Add service drop-down and select Jenkins (Git Plugin). Next, add the Jenkins server url (assuming the server is accessible from the internet. If not, hosting the Jenkins server on something like Digital Ocean might be an easy solution). Lastly, make sure the Activate box is selected, and click the Add Service button.

SSH Keys

While on the project’s GitHub page, make sure that the Jenkins server’s ssh keys are added to the project. To add them, navigate to the Deploy Keys page (under the project’s Settings tab). Then select Add deploy key, and add the public key.

A Nginx Server

Default nginx page
The default nginx page.

With Jenkins ready, let’s quickly setup the web server before configuring the Jenkins project. Any web server will do (it just needs to serve the generated static website content). I used nginx in for setup. After installing, make sure it is running. To install and check the status of nginx on an Ubuntu System:

## Install
sudo apt install -y nginx

## Check it is running
sudo systemctl status nginx

## Optional: Ensure it is enabled to start up after reboots
sudo systemctl enable nginx

With the web server running, we need to know where the website files need to go. Nginx will by default serve content at /user/share/nginx/html/, so remember that location for later…

Note: Don’t forget to add the jenkins user’s ssh key from the jenkins server to the authorized_keys file of the nginx server. This will make file transfers easier when setting up the jenkins project.

Configuring a Project

Let’s configure our Jenkins project! Log in to the Jenkins server and click the New Item option on the left side bar. Enter a name for the project, select the Freestyle Project option, and hit OK.

The Project’s General Configuration Section
The Project’s General Configuration Section.

In the General section of the configuration screen, optionally write a description about the project. Next, select the “GitHub Project” check-box, and add the GitHub repo’s url into the Project url text box.

Source Control

Setting Credentials
Setting Credentials.

In the Source Code Management section of the configuration, select the Git option. Then, enter the repo’s url for the Repository URL box (I did the ssh url). For Credentials, select Add to configure a new credential. Select SSH Username with private key for Kind, use jenkins for the Username.

More source control options can be configured, but this should be the minimum setup required. Again, for this to work public keys for the jenkins user on the jenkins server must be generated, and added as a deployment key on GitHub.

Build Trigger

Under the Build Triggers section, select Poll SCM. Without adding any schedule parameters, it will trigger each time a new commit is detected. This is what we want.

Build Step

In the Build section, click Add build step, and select Execute shell. This is where we can add the shell commands to build the website with hugo. Add the following command to the box (don’t forget to change the url):

hugo -D -F -b "http://10.1.1.77" -d public

The -D flag tells hugo to include all draft posts, while the -F flag has it include all posts with a future date. The -b flag sets the url for the generated website. This should the be url or IP address of the nginx server setup previously. Lastly, the -d flag tells hugo to output the generated static website to the public directory. This will be useful to know when deploying the build.

Deploy to Webserver

For deployment, I used rsync to copy the build files to the nginx web server. This step will be another shell command, so I’ve actually added it as another “build” step. Add another Execute shell and paste the following command inside the text box (again, changing the url):

rsync -r "$WORKSPACE/public/" ryan@10.1.1.77:/usr/share/nginx/html/

I used the Jenkins $WORKSPACE variable to get the location of the build, and was able to append the public directory to that, since we defined it with the -d flag in the hugo build step above. This will copy the generated website, to the web server.

Hit Save, and test it out by clicking the Build Now link on the left. If the build is successful, check the nginx website to see if the website was deployed!

Note: If it doesn’t work, double check all permissions and credentials between accounts and servers.

Better Yet… Pipelines

What’s better than using Jenkins for automated “draft website” deployments? Using a Jenkins Pipeline. A Pipeline allows the jenkins project steps to be defined in a Jenkinsfile that, among other benefits, can be source controlled. In fact, by default a Jenkins pipeline searches for the Jenkinsfile right in the root directory of a project’s git repo.

While a pipeline and Jenkinsfile might be a bit more confusing to learn how to setup, it is well worth it. For example, the following Jenkinsfile can be used to do essentially what we setup in the previous steps:

pipeline {
    agent {
	label 'mr-mime'
    }
    stages {
	stage ('build') {
	    steps{
		sh 'hugo -D -F -b "http://10.1.1.77"'
	    }
	}
	stage ('deploy') {
	    steps{
		sh 'rsync -r "$WORKSPACE/public/" ryan@ponyta:/usr/share/nginx/html/'
	    }
	}

    }
}

I’m not going to cover pipelines in this post. However, I do encourage readers to check them out.

Conclusion

That’s it. While I currently host my website using GitHub pages, if I ever self-host it again, I will definitely automate publishing it using Jenkins as well. This has been a very basic example of what Jenkins can be used for, but I have found it rather useful when working on the content of this website. There is so much more it can do. Have fun!

Next Post:
Prev Post:

Monitor Selection: My New LG-UD4329b Nginx as a Reverse Proxy to Forward Sub-Domains