Author: Marc Gorzala

This post explain how you can create your own (static) website. I creates the website from easy to learn and understand text files. The files are written in the asciidoctor markup language. From a collection of those files, a nice website wit a menu will be rendered and automatically published.

Problem Statement

I want to maintain documentation via some Markup language, instead of using some kind of CMS as Wordpress because:

  • I am faster in writing such a markup language, than using even a fancy UI from an CMS

  • comparing versions of documents, is easier (this applies maybe only for developer that are used to tools like diff and less for normal users)

  • I am less bound to the CMS. When eg. I could not use Wordpress anymore, it will be someway tricky to migrate the content.

  • I want easily export parts of my documentation isolated and in several formats (like PDF, ebub)

The chosen Markup language should be:

  • Easy to read in source code

  • Powerful enough to cover all normal use cases

  • Should also cover creating things like Sequence Diagramms and other small kind of illustration by coding them in readable text.

Why Asciidoctor

After comparing some of the existing Markup Languages, I have choosen to go for Asciidoctor for the following reasons:

  • Easy to read and well documented Markup Language

  • Powerful extensions that support enormous things to render just from code.

  • Good tooling support (eg. via an IntelliJ plugin)

Although I also could use Asciidoctor eg. in combination with Wordpress via a plugin, I decided (after trying it out) against it, because:

  • Asciidoctor is not a first class functionality of Wordpress. I would run into trouble if the plugin will not be mainted properly. Especially I would run into trouble, when the most recent version of Wordpress would not be supported via the plugin. I would need to get the most recent version of Wordpress for security updates and also as I am using a managed Wordpress installation, that receives automatic updates.

Why GibHub

To have some version control for the documentation, and also offering others the chance to contribute to documentation, I decided to have a public available Git service for maintaining the source of the site. I have chosen GitHub, because it is widely used and offered recently with GitHub-actions an easy way to run code when something changes in source code. I will use this feature to build the site from the ascii-doctor pages and upload them to me website.

Why JBake for building the site

What is left is a decision for a tool to create the static site from the asciidoctor content. From the list of available generator I have selected JBake for the following reasons:

  • Extremely easy to use

  • Well documented

  • Can render a static site from Asciidoctor/Markdown and HTML

  • As my dominant programming language is Java, I considered it as a plus that Jbake is implemented in that language in case I have to add or fix funcionality.

  • integrations for Maven and Gradle exist, to have a standard way of generating

The implementation

What you will need

  • About 30 minutes

  • Your favourite IDE (I would recommend IntelliJ, not only for the nice Asciidoctor-Plugin)

  • JDK 1.8 or later

  • Git installed and configured locally

  • Webspace that could serve static content

Install SDKMAN

For installing later Gradle and Jbake, I strongly recommend to use link: SDKMAN. With SDKMAN you can easily not only install the mentioned programms, you can also handle eg. different versions of JAVA.

If you do not want to install SDKMAN, than you will have to install Gradle and JBake by other means and can skip to XXXX when you are done with those installations.

You are still here?

So You want to install SDKMAN. This is easy:

curl -s "https://get.sdkman.io" | bash

Install Gradle

Assuming you have SDKMAN installation is pretty easy:

sdk install gradle

Install Jbake

Assuming you have SDKMAN installation is pretty easy:

sdk install jbake

Install Graphviz

Graphviz is needed by some diagramm types for PlanUML that is being used by asciidoctor.

Install on debian based linux distributions

sudo apt install graphviz

Install on Mac with brew:

brew cask install graphviz.

Install on windows with Graphviz

choco install graphviz

Setup your local GIT repo

# switch to home folder
cd ~
# create one folder for all your future websites
mkdir websites
cd websites
# create the folder that hold your site (I want to publish an repository for minecraft)
mkdir minecraft
cd minecraft
# initialise the folder as a git repo
git init
# initialise the folder as a gradle project
gradle init
# You will be asked what kind of project you are generation
# say you want to have a basic project with Groovy as the DSL
# accept the default for the rest

You have now a basic gradle project. You should commmit this:

cd ~/websites/minecraft
git add .
git commit -a -m "basic gradle project"

Now try out if your gradle project works (by using the gradle wrapper). We are using the gradle wrapper, because he is also used by GitHub to build the site later.

cd ~/websites/minecraft
./gradlew tasks

The first invocation will take some time, as the gradle wrapper will be downloaded. In the end you should see a list of available gradle tasks. You should also see that this "build" was successful.

With only have a pretty virgin gradle project. So the just created build.gradle is essentially empty.

Use your editor/IDE to let your build.gradle contain the following:

// get the dependency to draw uml and all the
// other fancy stuff
buildscript {
    dependencies {
        classpath 'org.asciidoctor:asciidoctorj-diagram:1.5.4.1'
    }
}

// get the plugin for gradle to build the site
plugins {
    id 'org.jbake.site' version '5.0.0'
}

// set default repositories to get dependencies
repositories {
    mavenCentral()
    jcenter()
}

jbake {
    version = '2.6.4'
    // where the source code will be stored
    srcDirName = 'src/site'
    // where the rendered page will be stored
    destDirName = 'docs/html5/site'
    // activate the diagramm extension for the fancy uml and other stuff
    configuration['asciidoctor.option.requires'] = "asciidoctor-diagram"
}

Now with this build.gradle in place you can run in the repo ./gradlew tasks again. Now you should see some more tasks available:

  • bake - Bake a jbake project

  • bakeInit - Setup a jbake project

  • bakePreview - Preview a jbake project

You can now try to bake your site by executing `./gradlew bake'. You will get an error. The build failed. This happens because you have no content that could be rendered.

If you have a look in your build.gradle you will see, that the content is expected in src/site in the repo. But this folder does not even exist.

Let’s create it:

cd ~/websites/minecraft
mkdir -p src/site

Still, running ./gradlew bake will not work. The now existing folder has to be initialised:

cd ~/websites/minecraft/src/site
# now initilise the jbake project (for this reason you have installed jbake)
jbake -i

Now you should be able to generate (bake) your site: ./gradlew bake. The rendered site will be stored in build/docs/html5/site.

Opening the index.html in this folder will display your first version of the page. It contains example blog posts. And also some links are not working.

To let the links work, run the build with this task ./gradlew bakePreview. This will start a small server on port 8080 on localhost.(make sure that another server is not running already on this port)

Now we are almost done.

I will now, just update in src/site/jbake.properties the entry for site.host and set the value to https://minecraft.frubumi.de as I want to publish my static site to a place under this address.

Last step and our (local) setup is done, is to check if we could also render 'plantuml' stuff.

Copy to the end of the following file src/site/content/blog/2013/fourth-post.adoc this snippet:

[ditaa]
....
                   +-------------+
                   | Asciidoctor |-------+
                   |   diagram   |       |
                   +-------------+       | PNG out
                       ^                 |
                       | ditaa in        |
                       |                 v
 +--------+   +--------+----+    /---------------\
 |        | --+ Asciidoctor +--> |               |
 |  Text  |   +-------------+    |   Beautiful   |
 |Document|   |   !magic!   |    |    Output     |
 |     {d}|   |             |    |               |
 +---+----+   +-------------+    \---------------/
     :                                   ^
     |          Lots of work             |
     +-----------------------------------+
....

If you now generate (./gradlew bake) the project again and view it with ./gradlew bakePreview (open the fourth blog page).

You should see now the rendered component diagram. Commit your changes and you are done for this section.

Pushing your local repository to GitHub

If you do not have a GitHub account, get it now.

If you want to use the ssh-protocol for cloning and pushing to the repository, you would have to give github your public ssh-key.

Then create a new repository. Let it be public of private. Just as you want.

As we already have a local repository, we now have to link our local one with the newly created one:

cd ~/websites/minecraft
# you have to use the url of your repository of course:
git remote add origin git@github.com:gorzala/minecraft.git
git push -u origin master

Making GitHub build your site on every push to the repository

Since 2019, GitHub is offering with GitHub-actions a service, that can run code, when something happens with your repository.

This something can be

  • pushing to the repository

  • creating a pull request

  • creating an issue

  • …​

To create such an GitHub-action just do the following

  1. click on the action-tab of your repository

  2. then click on "Skip this: Set up a workflow yourself"

  3. Github, created a small "hello world repository", accept that default by clicking on Start Commit

If you wait some seconds, you can click again on the actions tab. You should see now the created workflow (with name CI). You can click on it. Depending on wether the action has already been run, you can either see the action running, or see the result. If you click on the nodes in the view, you will be able to see the "hello world", that the action has action has executed.

But we do not want to have hello world printed, we want the website to be baked. The action configuration happened in the file .github/workflows/main.yml in the repository.

It is an ordinary file in the repository. You can edit it as any other file. I would recommend at least in the beginning, to edit this in GitHub itself, as GitHub offers you nice context help and code completion on this file. So, locate the file in GitHub and click on edit.

In this file replace echo "hello world with ./gradlew bake. Commit this change. Now the action will be triggered again. You can watch again the output and should see that the bake has been run.

Make GitHub publish your static site to your Webspace

As we are now building our site on every push, we only have to upload the build to our webspace.

I configured for my minecraft site and ftp resource, to feed the webserver:

Username, ftp-geheimer-user

Password, ftp-geheimes-password

Host, ftp-geheimer-host

We can now configure our action. Edit the workflow-file .github/workflows/main.yml now so, that it contain the following:

name: CI

on: [push]

jobs:
  build:

    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v1
    - name: install graphviz
      run: sudo apt-get install graphviz
    - name: Bake the site
      run: ./gradlew bake
    - name: ftp-action
      uses: sebastianpopp/ftp-action@v1.1.0
      with:
        # FTP host
        host: ${{ secrets.MINECRAFT_FTP_HOST }}
        # FTP userg
        user: ${{ secrets.MINECRAFT_FTP_USER }}
        # FTP password
        password:  ${{ secrets.MINECRAFT_FTP_PASS }}
        # Local directory
        localDir: build/docs/html5/site/

Compared with the previous version, I deleted the action with the multiline action and added two actions:

  • the ftp-action for uploading.

  • an action that installs Graphviz

You will notice, that I did not enter the password (and even not host and username) as clear text. Instead, I provided references to something that is called secret:

  • secrets.MINECRAFT_FTP_HOST

  • secrets.MINECRAFT_FTP_USER

  • secrets.MINECRAFT_FTP_PASS

Those secrets has to be configured. Just open the Settings-dialog from your repository. In the section for Secrets you can add those variables and values (use only the part after the "secrets.").

After adding secrets, you can not see that value again. You can only pass them around in the actions. By doing it in that way, you can have the repository public still not leaking your credentials. You should make sure, that only people can change the repository content (eg be enforcing PRs). Because everyone with write access to the workflow file, can also use the secrects to be handed into other actions, which could reveal the content.

Recap

What have we done?

  • We can now easily publish content from AsciiDoctor/Markdown content

  • We can also create Sequensdiagramm, Flowcharts and other stuff, based on text files

  • Publishing works automatically by pushing to the repository