Tutorial: Publishing your own Github blog (Part 1)

Part 1: how to easily to set up and manage your own blog

Hugo FerreiraHugo Ferreira

Introduction

This is the first part in a series of articles about creating and publishing technical content on publicly available hosts. The goal is to set-up a source to write and publish a static website to various target hosts. I want to make this as easy and painless as possible, so I have opted to use Markdown to write the contents, use code editing tools and version control to manage the content and use a static site generator to be able to cross-publish the full site or single posts to various destinations (hosts). The aim is to keep these hosts synchronized and facilitate updating when required. I will have one repository for the site's sources that will be kept private. I assume that all content that is published is public. I will use Java as the base technology to develop, maintain and publish the sites. In particular the tools used were selected to facilitate the documentation of Java and Scala 3 projects, including library APIs. This includes the use of the Mill build tool (Mill documentation) to develop and execute the required scripts to generate and publish the content.

Getting started

We start off by creating a Github repository with the sources for the websites. This is my private source where I keep my research and unfinished work. It also contains the final rendered pieces that can then me moved to the destination hosts.

Then we create the Github repository where the website is published.

  1. Go to your github account and press the create a new repository option;
  2. Create the repository name (in my case tech4rd) for your site using GitHub pages;
  3. Add a description (Articles on software development, programming stuff, embedded systems, sensors and electronics.);
  4. Make sure the site is public (don't keep their anything private);
  5. Select the option to initialize the repository with a README.md file;
  6. Select the option to add an initial .gitignore file (use the Scala template);
  7. Click the create a template button.

Github documentation states that: "GitHub Pages sites are publicly available on the internet, even if the repository for the site is private." Because these sites are public, don't store any personal, private or sensitive data there.

Now we can create the website source repository.

  1. Go to the repository you created to keep the sources of the site;
  2. Create a index.md or index.html file as the site's initial content:
    1. Don't place this file in the project's root of the source repository;
    2. Select add -> create a new file;
    3. Input both the path and the file: docs/index.md
    4. Place some content in this page;
  3. We then configure the publishing source:
    1. In the source site click on the Settings tab;
    2. Go to the Code and Automation section;
    3. Under this section click on Pages to open the Github pages configuration page:
    4. Select Source for a specific branch or leave as None. Select the main branch and select /docs as the site content folder.
    5. Don't forget to press the save button. When you do this the site's URL is provided for you;
    6. Note that the site will not be immediately available. It can take up to 10 minutes for each change to be reflected in the site;
    7. You can then choose a theme for the Jekyll static site generator if you use the gh-pages branch. We are going to use another static site generator, so leave it as is;
  4. Add the website link to you Github repository page
    1. Go to the project's Github repository site;
    2. Click on the configuration wheel of the About field;
    3. Click on the Website field and paste the previously configured URL into it;
    4. Fill in the Description and Topics as desired. For example:
      1. Description: Articles on software development and embedded systems.
      2. Topics: java scala programming electronics embedded-systems software-development sensors.

You can now view the initial content of your site. From the sites repository, click on the URL you have just saved. You should be able to see your contents.

Automatically generated sites

In the example above, we provided a Markdown file and the content was automatically made available as HTML. Several companies provide hosting platforms that can automatically render Markdown files as HTML. The site's content can be, for example, standard text files that can be viewed with the web interface of a code repository. It can also contain HTML content for a project or personal website. Examples of this include Github pages (used above) and Gitlab pages.

Generating a static website based only on Markdown, may not be enough for all but the simplest website. In these cases, one can use several Markdown files and use links among them. This allows one to set up simple static sites that include: manually generated static menus or indexes, use of links between content and adding any formatting that is supported by Markdown syntax. It is important to note that these hosts support the parsing of Markdown files that are not "standard". We have for example Github's and Gitlab's flavoured versions of Markdown. These versions of Markdown include for example encoding "for mentions", "emojis" and "issues" that can be placed in the source. These features facilitate interaction with the code repository and are parsed using the host's default parsers. In some cases, the online Web interface also allows for additional configurations and facilitates the editing of the content.

There is no standard for Markdown. The closest we have to a standard is John Gruber’s canonical description of Markdown’s syntax. A good source for a complete description of Markdown (including cheat sheets), is the Markdown Guide. Here you can also find a list of extended-syntax versions of the Markdown language that includes for example the Commonmark and R Markdown variants.

It is also possible to add themes, for example in GitHub pages, when using Markdown-only sites. Internally, Github pages uses Jekyll. In this case, the HTML files will automatically be created via this static site generator. No need to install or invoke the static site tools. However, to activate and use the theme, it is necessary for you to set up the YAML front matter that specifies the layout of each HTML output.

Alternatively, one can use a static site generator by incorporating it into the CI/CD workflow1 or use it locally to manually create and publish the site's contents to the hosts' website location. We will look at the details in the section on static site generation.

Static site generation

Static site generators process a set of source files (written, for example, in MarkDown, AsciiDoc (see AsciiDoc in Wikipedia) and ReStructuredText) and generate the HTML content. Usually each source file is converted into a corresponding HTML file. Files in HTML can also be used as sources, but they are usually not processed by the static site generator. In addition to this, these tools also make use of templates (such as Freemarker or ThymeLeaf) to define the layout and look of the website.

The templates let us configure the sites' menus, add headers and footers to the HTML pages, provide social media links, social share menus and 404 error handling for several hosts. They also allow one to place JavaScript in the HTML pages so that additional functionality can be made available. Examples include adding Google Analytics scripts, estimating read times for the posts, show maths expressions (MathJax, MathJS, KaTex), draw flowcharts (Mermaid-Js, Flowchart.js), render plots (ChartJS, Plotly, Chartist-Js, RawGraphs.io, d3js, c3js, nvd3) and use icons (FeatherIcons, IonIcons, FontAwesome, Tabler, css.gg).

There are quite a few static site generators out there. These include for example: Docusaurus.io (Docusaurus in Github), Jekyll (Jekyll in Github), sphinx-doc, Pamflet (Pamflet in Github), Nanoc, GitBook, Paradox (Paradox in Github), GoHugo and AsciiDoctor. Many of these tools are developed in Ruby, Go or Python. I have opted to use JVM based static site generators to make it easy to automate the process - I only depend on the JVM. Maven packages that can be installed via the Ivy package manager and Maven or Gradle plugins to run the necessary tools. No need to deal with operating system package installation once the Java system is installed. This also makes the use of CI/CD workflows easier to update a Blog or launch a new software release.

Some of these static site generators include: JBake (JBake in github), Laika (Laika Github), Pamflet and Paradox. Laika is a interesting case in that it is also capable of generating documents of other formats such as PDF. I also include in this category tools that can be used to generate Java or Scala API documentation. In the case of Java we have Javadoc. For Scala we will focus on ScalaDoc specifically for Scala3 (see the Scala3 ScalaDoc Guide and Scala3 ScalaDoc Usage webpages). Note that ScalaDoc is part of the Scala3 compiler, also known as Dotty. The Scala3 ScalaDoc tool is a static site generator in and of itself.

In addition to the HTML templates, the source files can also be preprocessed prior to conversion. In particular, I am interested in documenting software. The MDoc (MDoc in Github) can be used to compile code snippets embedded in the Markdown sources. Depending on the static site generator we also have build tool plugins for the Scala ecosystem. For example, the Sbt-site(Sbt-site Github) plugin can use several generators to create a static website. The Mill build tool includes plugins for Docusaurus, JBake and MDoc plugin.

I will first show you how to set-up and use a JBake static site. More concretely I will configure, populate and publish a blog. Later I will also describe how to use ScalaDoc and MDoc to document software and keep this documentation up to date with the software repository.

Workflow

Before getting into the nitty gritty, let's first take a look at what the workflow of publishing a site might look like. To generate a site we basically take a set of sources, for example Markdown files and drawings, and transform them into the website's artifacts. These artifacts are usually HTML files, but may include PDF files and images (for example in the PNG or SVG formats). Usually a single source file is transformed into a single HTML file, but in general this is not a strict requirement. A source file may go through several transformations before the final output is generated. For example a Markdown file may have its code fences compiled by the MDoc plugin resulting in a changed source. This file is then processed by JBake using Freemarker templates to produce the final HTML file.

The various transformations may be applied by several tools, each of which may need their own configuration. For example, we may want to generate HTML pages with a specific layout that may include pre-compiled content, specific menus, headers and footers. Freemarker for example allows the use of templates and a domain specific language (DSL) that defines the look and feel of the final HTML page. In this case, the static site generator takes as input a Freemarker template and a Markdown source file to generate the final output. Sometimes the source file itself may include configurations used to tweak the output. For example, the MDoc plugin allows us to tag each code fence to define the type of output we want or, a Markdown file may have a header. It is important to point out that anyone is free to include manually edited HTML pages to the site's contents. In addition to this, Markdown for example, allows one to embed HTML snippets that will be kept unaltered.

Compiling a site means taking a set of sources that are located in a given path, transforming these and placing them in the site's final directory. Some files may not be processed at all. For example images may be copied, as is, to the site's destination folder. Generally the location of the source files within the source directory determines how and where the content is made available. For example, blogs may have a directory specifically set aside to contain posts indexed by the publishing date. Usually the transformed files are placed in a local private directory where they can be served and checked locally. Once the content has been checked (or not), it can be published to the final public serving host.

The compilation of the website is usually done by executing one or more commands of a static site generator tool at the command line. This tool must be configured prior to execution. Such configurations may include the path to the site's sources and transformation workflow to be used. Usually the sources need to be made available in a specified path and with a required directory structure. As I have pointed out, we may also need to configure the use of other tools and their respective configurations.

The site generator command is usually invoked at the root of the site's sources. However, it may advantageous to invoke the static site generator via a build tool such as Mill. It allows us to customize our workflow so that we can use several tools including specific preprocessing transformers for our website. We are free to implement our own transformations tasks and embed them into the workflow of our Mill scripts that do source compilation and unit testing. We can also use Mill with CI/CD pipelines to automatically publish new content. Another advantage of using a build tool like Mill is that we only provide a Mill command line script with our sources and a Mill build script. Mill will then download and install itself and any of the required dependencies (such as the JBake tools) before executing the build script. When using CI/CD, we don't have to worry about downloading and installing Mill or any additional tools. In the next section we will use a Mill JBake plugin to generate a static site.

JBake

So the first question is, why JBake? JBake is a mature Java tool that has good documentation and large user community. So it should provide most of us with what we need or want (analytics, icons, menus, etc.). In particular, JBake has been extensively used to document open software projects and implement blogs and personal sites. Having said this, it does not seem to be as popular as Jekyll or GoHugo but, it is well supported and several templates are available. In particular it can use the Freemarker, Groovy Simple and Markup, Thymeleaf, Jade and Pebble template engines.

To set up a JBake static site consists of the following steps:

  1. Download and install the JBake tool;
  2. Create a directory for the website's source;
  3. Populate the directory with JBake's default configuration that uses Freemarker templates;
  4. Customize the templates if this is necessary;
  5. Add to or change content in the site's sources directory (assets, content, templates);
  6. Use JBake to generate the website's final output (HTML, XHTML);
  7. Preview the contents (includes a "watch" mode to automatically preview and update the content);
  8. Publish the contents.

To update the website we need only add or change the website's content and repeat the steps that generate the final output and publish the contents. JBake is used via the command line to perform all theses actions. It is also possible to use command line parameters, change configuration files and set environment variables to configure JBake's output. For full details see JBake's documentation. I strongly suggest you keep the site's sources under version control. Either place these sources in an existing repository that you want to document, or place this in a separate repository where you keep only the website's source code. In this example, we will work on a blog, so we use a separate repository that contains only the site's sources.

Installing Mill

In this article we will use Mill's JBake plugin. This allows us to download and execute JBake without installing this tool in the host operating system. This is advantageous when using a CI/CD pipeline in a virtual machine. All the static website generation setup and configuration is done as documented in JBake's site. The execution of JBake however, is done via a Mill build script and the corresponding Mill tasks encoded in that script. So let's start off by installing and writing up our Mill script:

First place yourself at the site's source repository:

$ cd ~/VSCodeProjects/blog

Next, download the latest release of Mill. Make sure you download the "starter" script (not the assembly). It is named with the Mill version and is less the 2 KBytes large. Here is an example of downloading the script:

$ wget https://github.com/com-lihaoyi/mill/releases/download/0.10.4/0.10.4

Next we rename the script to mill and make sure the script has the permission to execute:

$ mv 0.10.4 mill
$ chmod +x ./mill

For those of you in Windows, it may be advantageous for you to download and use millw. It is a drop-in replacement for mill that has a millw.bat version for windows. I also found in the past, that it was able to download and use snapshot versions of Mill, which the original script failed to do.

wget https://raw.githubusercontent.com/lefou/millw/main/millw
wget https://raw.githubusercontent.com/lefou/millw/main/millw.bat
chmod +x ./millw

A this point you should have the scripts ready to run:

$ ls -l

Output:

total 376
-rwxrwxr-x 1 user user   1714 mai  6 11:24 mill
-rwxrwxr-x 1 user user 187411 mai  7 17:25 millw
-rw-rw-r-- 1 user user 167228 mai  7 17:26 millw.bat
-rw-rw-r-- 1 user user     45 abr  7 18:43 README.md

In this article I am going to use a specific version of Mill. To ensure that you use the correct version, create the .mill-version file and place the Mill version inside this file:

$ touch .mill-version
$ echo 0.10.4 >> .mill-version 

We can now check the Mill version using the following command:

$ ./mill -i --version

Output:

Mill Build Tool version 0.10.4
Java version: 11.0.15, vendor: Private Build, runtime: /usr/lib/jvm/java-11-openjdk-amd64
Default locale: en_US, platform encoding: UTF-8
OS name: "Linux", version: 5.13.0-40-generic, arch: amd64

Note that when you run this command for the first time, the correct version of Mill will first be downloaded and then used. All subsequent calls to mill or millw will use the installed version. To see what version has already been downloaded, execute:

ls /home/use/.cache/mill/download/*

Set up the Mill JBake module

Mill scripts provides a set of modules, which are Scala 2 objects, that have several methods (referred to as tasks or targets) that allows one to: compile mixed Java and Scala projects, execute the code's main methods, and run unit tests. These modules can be "mixed" into a single compilation unit when we need to combine several functionalities into a single module. Many useful modules are provided out-of-the-box, but it is not possible to foresee or provide all required modules. Mill therefore allows anyone to distribute their modules as $3^{rd}$ party plugins. These plugins are provided as Maven artifacts that are downloaded via a Coursier backend using Ammonite's magic imports. Mill-JBake is one such plugin that can be automatically downloaded, installed and used via Mill.

We won't go into details here, but in the next steps I'll describe how to:

  1. Create a Mill script file;
  2. Write-up a simple Mill module that uses Mill-JBake to give us access to the JBake tool;
  3. Configure the JBake version to use;
  4. Execute JBake to initialize the repository.

First create the build script at the projects' root. In our case it is the blog code repository. Do as follows:

$ cd ~/VSCodeProjects/blog
$ touch build.sc

Then add the following code to the build.sc file:

import mill._
import $ivy.`de.tototec::de.tobiasroeser.mill.jbake::0.3.0`
import de.tobiasroeser.mill.jbake._

object site extends JBakeModule {

  def jbakeVersion = "2.6.7"

  // Default in 0.3.0
  override def jbakeProcessMode: JBakeModule.ProcessMode = JBakeModule.SubProcess

}

Let's take a look at what the code above does. The line shown below uses Ammonite's magic import to download and install the Mill-JBake plugin:

import $ivy.`de.tototec::de.tobiasroeser.mill.jbake::0.3.0`

These lines import the Mill default modules and the Mill-JBake module:

import mill._
import de.tobiasroeser.mill.jbake._

Next we set the version of the JBake tool that will be downloaded and used:

  def jbakeVersion = "2.6.7"

The following line ensures that JBake is launched in a separate JVM. I have found that for later versions of JBake, using Mill's JVM to execute the commands, results in errors.

  // Default in 0.3.0
  override def jbakeProcessMode: JBakeModule.ProcessMode = JBakeModule.SubProcess

The name of the module is set to site. Mill's convention is that a project may have several modules. The source of each module is placed in a directory with the same name located at the project's root. The project's root is where the build.sc file is found. So in our case we first create the module's source directory:

~/VSCodeProjects/blog$ mkdir site

The site module extends de.tobiasroeser.mill.jbake.JBakeModule. that provides us access to the JBake tool via several optional configuration values (such as jbakeVersion shown above) and the JBake commands (such as initializing, compiling ("baking") and serving content). All the commands we execute for a module is prefixed with the module's name. So in this case, all commands will explicitly start with site. To see what targets are available either of these commands can be used:

~/VSCodeProjects/blog./millw -i resolve site._
~/VSCodeProjects/blog./mill -i resolve site._

Output (note that the first time you run this, the output will include a list of the downloaded Mill-JBake Jars):

[1/1] resolve
site.jbake
site.jbakeClasspath
site.jbakeDistributionDir
site.jbakeDistributionZip
site.jbakeInit
site.jbakeRun
site.jbakeServe
site.jbakeVersion
site.jbakeWorker
site.sources

At this point we are ready to create the site's content. Let's start off by initializing the site with the default Freemarker template:

~/VSCodeProjects/blog$ ./millw -i site.jbakeInit

Output:

Compiling /home/user/VSCodeProjects/blog/build.sc
[7/7] site.jbakeInit 
JBake v2.6.7 (2021-05-14 21:54:29[GMT+01:00]) [http://jbake.org]

Base folder structure successfully created.

This command sets up an example site using the default FreeMarker template. It adds the required directories and example content. This includes assets such as images, icons, CSS formatting, JavaScript libraries, FreeMarker templates and some AsciiDoc content. We can now take a look at the site's source directory as follows:

~/VSCodeProjects/blog$ tree site/

Output:

site/
└── src
    ├── assets
    │   ├── css
    │   │   ├── asciidoctor.css
    │   │   ├── base.css
    │   │   ├── bootstrap.min.css
    │   │   └── prettify.css
    │   ├── favicon.ico
    │   ├── fonts
    │   │   ├── glyphicons-halflings-regular.eot
    │   │   ├── glyphicons-halflings-regular.svg
    │   │   ├── glyphicons-halflings-regular.ttf
    │   │   └── glyphicons-halflings-regular.woff
    │   ├── img
    │   │   └── beach.jpg
    │   └── js
    │       ├── bootstrap.min.js
    │       ├── html5shiv.min.js
    │       ├── jquery-1.11.1.min.js
    │       └── prettify.js
    ├── content
    │   ├── about
    │   │   └── jbake_logo.png
    │   ├── about.html
    │   └── blog
    │       └── 2013
    │           ├── fifth-post.adoc
    │           ├── first-post.html
    │           ├── fourth-post.adoc
    │           ├── second-post.md
    │           └── third-post.adoc
    ├── jbake.properties
    └── templates
        ├── archive.ftl
        ├── feed.ftl
        ├── footer.ftl
        ├── header.ftl
        ├── index.ftl
        ├── menu.ftl
        ├── page.ftl
        ├── post.ftl
        ├── sitemap.ftl
        └── tags.ftl

11 directories, 32 files

If you are working in a Git repository, don't forget to add the .gitignore file in the project's root with the following lines:

/.metals
/.vscode
/out

The next step is to compile or "bake" the site.

~/VSCodeProjects/blog$ ./mill -i site.jbake
Output of compilation
[7/7] site.jbake 
JBake v2.6.7 (2021-05-14 21:54:29[GMT+01:00]) [http://jbake.org]

10:16:50.269 INFO  org.jbake.app.Oven - Baking has started...
Warning: Nashorn engine is planned to be removed from a future JDK release
10:16:50.507 INFO  c.o.common.jna.ONative - Detected limit of amount of simultaneously open files is 1048576,  limit of open files for disk cache will be set to 523776
10:16:50.548 INFO  c.o.common.jna.ONative - 33429823488 B/31881 MB/31 GB of physical memory were detected on machine
10:16:50.549 INFO  c.o.common.jna.ONative - Soft memory limit for this process is set to -1 B/-1 MB/-1 GB
10:16:50.549 INFO  c.o.common.jna.ONative - Hard memory limit for this process is set to -1 B/-1 MB/-1 GB
10:16:50.549 INFO  c.o.common.jna.ONative - Path to 'memory' cgroup is '/user.slice/user-1000.slice/user@1000.service'
10:16:50.549 INFO  c.o.common.jna.ONative - Mounting path for memory cgroup controller is '/sys/fs/cgroup/memory'
10:16:50.550 INFO  c.o.common.jna.ONative - cgroup soft memory limit is 9223372036854771712 B/8796093022207 MB/8589934591 GB
10:16:50.550 INFO  c.o.common.jna.ONative - cgroup hard memory limit is 9223372036854771712 B/8796093022207 MB/8589934591 GB
10:16:50.550 INFO  c.o.common.jna.ONative - Detected memory limit for current process is 33429823488 B/31881 MB/31 GB
10:16:50.551 INFO  c.o.o.c.e.OMemoryAndLocalPaginatedEnginesInitializer - JVM can use maximum 7972MB of heap memory
10:16:50.551 INFO  c.o.o.c.e.OMemoryAndLocalPaginatedEnginesInitializer - Because OrientDB is running outside a container 12% of memory will be left unallocated according to the setting 'memory.leftToOS' not taking into account heap memory
10:16:50.551 INFO  com.orientechnologies - OrientDB auto-config DISKCACHE=20 083MB (heap=7 972MB os=31 881MB)
10:16:50.553 INFO  c.o.o.c.e.l.OEngineLocalPaginated - System is started under an effective user : `user`
10:16:50.553 INFO  c.o.o.c.e.l.OEngineLocalPaginated - Allocation of 305261 pages.
10:16:51.226 INFO  c.o.o.c.s.m.ODirectMemoryStorage - Storage 'memory:cache' is created under OrientDB distribution : 3.0.37 - Veloce (build 6a0e4724c10d51a0b19700fca46da8e41ae006f5, branch UNKNOWN)
10:16:52.199 INFO  org.jbake.template.ModelExtractors - register new extractors for document type: masterindex
10:16:52.200 INFO  org.jbake.template.ModelExtractors - register new extractors for document type: tagsindex
10:16:52.200 INFO  org.jbake.template.ModelExtractors - register new extractors for document type: sitemap
10:16:52.355 INFO  org.jbake.app.Crawler - Processing [/home/user/VSCodeProjects/blog/site/src/content/about.html]...  : new 
10:16:52.364 INFO  org.jbake.parser.AsciidoctorEngine - Initializing Asciidoctor engine...
10:16:54.177 INFO  org.jbake.parser.AsciidoctorEngine - Asciidoctor engine initialized.
10:16:54.325 INFO  org.jbake.app.Crawler - Processing [/home/user/VSCodeProjects/blog/site/src/content/blog/2013/fifth-post.adoc]...  : new 
10:16:54.329 INFO  org.jbake.app.Crawler - Processing [/home/user/VSCodeProjects/blog/site/src/content/blog/2013/first-post.html]...  : new 
10:16:54.392 INFO  org.jbake.app.Crawler - Processing [/home/user/VSCodeProjects/blog/site/src/content/blog/2013/fourth-post.adoc]...  : new 
10:16:54.496 INFO  org.jbake.app.Crawler - Processing [/home/user/VSCodeProjects/blog/site/src/content/blog/2013/second-post.md]...  : new 
10:16:54.525 INFO  org.jbake.app.Crawler - Processing [/home/user/VSCodeProjects/blog/site/src/content/blog/2013/third-post.adoc]...  : new 
10:16:54.525 INFO  org.jbake.app.Crawler - Content detected:
10:16:54.525 INFO  org.jbake.app.Crawler - Parsed 1 files of type: page
10:16:54.526 INFO  org.jbake.app.Crawler - Parsed 5 files of type: post
10:16:54.680 INFO  org.jbake.app.Renderer - Rendering archive [/home/user/VSCodeProjects/blog/out/site/jbake.dest/archive.html]... done!
10:16:54.687 INFO  org.jbake.app.Renderer - Rendering [/home/user/VSCodeProjects/blog/out/site/jbake.dest/about.html]... done!
10:16:54.698 INFO  org.jbake.app.Renderer - Rendering [/home/user/VSCodeProjects/blog/out/site/jbake.dest/blog/2013/fifth-post.html]... done!
10:16:54.701 INFO  org.jbake.app.Renderer - Rendering [/home/user/VSCodeProjects/blog/out/site/jbake.dest/blog/2013/fourth-post.html]... done!
10:16:54.704 INFO  org.jbake.app.Renderer - Rendering [/home/user/VSCodeProjects/blog/out/site/jbake.dest/blog/2013/third-post.html]... done!
10:16:54.706 INFO  org.jbake.app.Renderer - Rendering [/home/user/VSCodeProjects/blog/out/site/jbake.dest/blog/2013/second-post.html]... done!
10:16:54.709 INFO  org.jbake.app.Renderer - Rendering [/home/user/VSCodeProjects/blog/out/site/jbake.dest/blog/2013/first-post.html]... done!
10:16:54.733 INFO  org.jbake.app.Renderer - Rendering feed [/home/user/VSCodeProjects/blog/out/site/jbake.dest/feed.xml]... done!
10:16:54.740 INFO  org.jbake.app.Renderer - Rendering masterindex [/home/user/VSCodeProjects/blog/out/site/jbake.dest/index.html]... done!
10:16:54.751 INFO  org.jbake.app.Renderer - Rendering sitemap [/home/user/VSCodeProjects/blog/out/site/jbake.dest/sitemap.xml]... done!
10:16:54.756 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/css/asciidoctor.css]... done!
10:16:54.756 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/css/base.css]... done!
10:16:54.756 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/css/bootstrap.min.css]... done!
10:16:54.757 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/css/prettify.css]... done!
10:16:54.757 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/favicon.ico]... done!
10:16:54.757 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/fonts/glyphicons-halflings-regular.eot]... done!
10:16:54.758 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/fonts/glyphicons-halflings-regular.svg]... done!
10:16:54.758 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/fonts/glyphicons-halflings-regular.ttf]... done!
10:16:54.758 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/fonts/glyphicons-halflings-regular.woff]... done!
10:16:54.758 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/img/beach.jpg]... done!
10:16:54.759 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/js/bootstrap.min.js]... done!
10:16:54.759 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/js/html5shiv.min.js]... done!
10:16:54.759 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/js/jquery-1.11.1.min.js]... done!
10:16:54.759 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/js/prettify.js]... done!
10:16:54.760 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/content/about/jbake_logo.png]... done!
10:16:54.760 INFO  org.jbake.app.Oven - Baking finished!
10:16:54.760 INFO  org.jbake.app.Oven - Baked 10 items in 4491ms
10:16:54.761 INFO  c.o.orient.core.db.OrientDBEmbedded - - shutdown storage: cache...

And then serve the site at a local address:

~/VSCodeProjects/blog$ ./mill -i site.jbakeServe
Output of serving a JBake site
[8/8] site.jbakeServe 
JBake v2.6.7 (2021-05-14 21:54:29[GMT+01:00]) [http://jbake.org]

10:19:35.605 INFO  org.jbake.app.Oven - Baking has started...
Warning: Nashorn engine is planned to be removed from a future JDK release
10:19:35.824 INFO  c.o.common.jna.ONative - Detected limit of amount of simultaneously open files is 1048576,  limit of open files for disk cache will be set to 523776
10:19:35.870 INFO  c.o.common.jna.ONative - 33429823488 B/31881 MB/31 GB of physical memory were detected on machine
10:19:35.870 INFO  c.o.common.jna.ONative - Soft memory limit for this process is set to -1 B/-1 MB/-1 GB
10:19:35.870 INFO  c.o.common.jna.ONative - Hard memory limit for this process is set to -1 B/-1 MB/-1 GB
10:19:35.870 INFO  c.o.common.jna.ONative - Path to 'memory' cgroup is '/user.slice/user-1000.slice/user@1000.service'
10:19:35.871 INFO  c.o.common.jna.ONative - Mounting path for memory cgroup controller is '/sys/fs/cgroup/memory'
10:19:35.871 INFO  c.o.common.jna.ONative - cgroup soft memory limit is 9223372036854771712 B/8796093022207 MB/8589934591 GB
10:19:35.871 INFO  c.o.common.jna.ONative - cgroup hard memory limit is 9223372036854771712 B/8796093022207 MB/8589934591 GB
10:19:35.871 INFO  c.o.common.jna.ONative - Detected memory limit for current process is 33429823488 B/31881 MB/31 GB
10:19:35.872 INFO  c.o.o.c.e.OMemoryAndLocalPaginatedEnginesInitializer - JVM can use maximum 7972MB of heap memory
10:19:35.872 INFO  c.o.o.c.e.OMemoryAndLocalPaginatedEnginesInitializer - Because OrientDB is running outside a container 12% of memory will be left unallocated according to the setting 'memory.leftToOS' not taking into account heap memory
10:19:35.872 INFO  com.orientechnologies - OrientDB auto-config DISKCACHE=20 083MB (heap=7 972MB os=31 881MB)
10:19:35.874 INFO  c.o.o.c.e.l.OEngineLocalPaginated - System is started under an effective user : `user`
10:19:35.874 INFO  c.o.o.c.e.l.OEngineLocalPaginated - Allocation of 305261 pages.
10:19:36.562 INFO  c.o.o.c.s.m.ODirectMemoryStorage - Storage 'memory:cache' is created under OrientDB distribution : 3.0.37 - Veloce (build 6a0e4724c10d51a0b19700fca46da8e41ae006f5, branch UNKNOWN)
10:19:37.468 INFO  org.jbake.template.ModelExtractors - register new extractors for document type: masterindex
10:19:37.470 INFO  org.jbake.template.ModelExtractors - register new extractors for document type: tagsindex
10:19:37.470 INFO  org.jbake.template.ModelExtractors - register new extractors for document type: sitemap
10:19:37.635 INFO  org.jbake.app.Crawler - Processing [/home/user/VSCodeProjects/blog/site/src/content/about.html]...  : new 
10:19:37.644 INFO  org.jbake.parser.AsciidoctorEngine - Initializing Asciidoctor engine...
10:19:39.505 INFO  org.jbake.parser.AsciidoctorEngine - Asciidoctor engine initialized.
10:19:39.645 INFO  org.jbake.app.Crawler - Processing [/home/user/VSCodeProjects/blog/site/src/content/blog/2013/fifth-post.adoc]...  : new 
10:19:39.649 INFO  org.jbake.app.Crawler - Processing [/home/user/VSCodeProjects/blog/site/src/content/blog/2013/first-post.html]...  : new 
10:19:39.728 INFO  org.jbake.app.Crawler - Processing [/home/user/VSCodeProjects/blog/site/src/content/blog/2013/fourth-post.adoc]...  : new 
10:19:39.834 INFO  org.jbake.app.Crawler - Processing [/home/user/VSCodeProjects/blog/site/src/content/blog/2013/second-post.md]...  : new 
10:19:39.870 INFO  org.jbake.app.Crawler - Processing [/home/user/VSCodeProjects/blog/site/src/content/blog/2013/third-post.adoc]...  : new 
10:19:39.871 INFO  org.jbake.app.Crawler - Content detected:
10:19:39.871 INFO  org.jbake.app.Crawler - Parsed 1 files of type: page
10:19:39.871 INFO  org.jbake.app.Crawler - Parsed 5 files of type: post
10:19:39.973 INFO  org.jbake.app.Renderer - Rendering archive [/home/user/VSCodeProjects/blog/out/site/jbake.dest/archive.html]... done!
10:19:39.978 INFO  org.jbake.app.Renderer - Rendering [/home/user/VSCodeProjects/blog/out/site/jbake.dest/about.html]... done!
10:19:39.990 INFO  org.jbake.app.Renderer - Rendering [/home/user/VSCodeProjects/blog/out/site/jbake.dest/blog/2013/fifth-post.html]... done!
10:19:39.993 INFO  org.jbake.app.Renderer - Rendering [/home/user/VSCodeProjects/blog/out/site/jbake.dest/blog/2013/fourth-post.html]... done!
10:19:39.996 INFO  org.jbake.app.Renderer - Rendering [/home/user/VSCodeProjects/blog/out/site/jbake.dest/blog/2013/third-post.html]... done!
10:19:39.999 INFO  org.jbake.app.Renderer - Rendering [/home/user/VSCodeProjects/blog/out/site/jbake.dest/blog/2013/second-post.html]... done!
10:19:40.002 INFO  org.jbake.app.Renderer - Rendering [/home/user/VSCodeProjects/blog/out/site/jbake.dest/blog/2013/first-post.html]... done!
10:19:40.024 INFO  org.jbake.app.Renderer - Rendering feed [/home/user/VSCodeProjects/blog/out/site/jbake.dest/feed.xml]... done!
10:19:40.031 INFO  org.jbake.app.Renderer - Rendering masterindex [/home/user/VSCodeProjects/blog/out/site/jbake.dest/index.html]... done!
10:19:40.043 INFO  org.jbake.app.Renderer - Rendering sitemap [/home/user/VSCodeProjects/blog/out/site/jbake.dest/sitemap.xml]... done!
10:19:40.047 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/css/asciidoctor.css]... done!
10:19:40.047 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/css/base.css]... done!
10:19:40.047 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/css/bootstrap.min.css]... done!
10:19:40.048 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/css/prettify.css]... done!
10:19:40.048 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/favicon.ico]... done!
10:19:40.048 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/fonts/glyphicons-halflings-regular.eot]... done!
10:19:40.048 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/fonts/glyphicons-halflings-regular.svg]... done!
10:19:40.049 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/fonts/glyphicons-halflings-regular.ttf]... done!
10:19:40.049 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/fonts/glyphicons-halflings-regular.woff]... done!
10:19:40.049 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/img/beach.jpg]... done!
10:19:40.050 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/js/bootstrap.min.js]... done!
10:19:40.050 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/js/html5shiv.min.js]... done!
10:19:40.050 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/js/jquery-1.11.1.min.js]... done!
10:19:40.050 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/js/prettify.js]... done!
10:19:40.051 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/content/about/jbake_logo.png]... done!
10:19:40.051 INFO  org.jbake.app.Oven - Baking finished!
10:19:40.051 INFO  org.jbake.app.Oven - Baked 10 items in 4446ms
10:19:40.052 INFO  c.o.orient.core.db.OrientDBEmbedded - - shutdown storage: cache...
10:19:40.091 INFO  org.jbake.launcher.BakeWatcher - Watching for (content, template, asset) changes in [/home/user/VSCodeProjects/blog/site/src]
10:19:40.141 INFO  org.jbake.launcher.JettyServer - Serving out contents of: [/home/user/VSCodeProjects/blog/out/site/jbake.dest] on http://localhost:8820/
10:19:40.141 INFO  org.jbake.launcher.JettyServer - (To stop server hit CTRL-C)

You can then preview it in your browser at this URL. Here is an image of the initial example site (shown using Dark Reader):

Preview in Dark Mode

Note that JBake will monitor changes in the site's source and automatically update the site's content. To stop the automatic update and serving of the website simple press the keys Ctrl-C. The server will terminate as shown below:

^C
18:50:11.633 WARN  c.o.orient.core.OSignalHandler - Received signal: SIGINT
18:50:11.635 INFO  c.o.orient.core.Orient - Orient Engine is shutting down...
18:50:11.749 INFO  c.o.orient.core.Orient - Clearing byte buffer pool
18:50:11.803 INFO  c.o.orient.core.Orient - OrientDB Engine shutdown complete

Because we generated a static site, it is also possible for us to simply point our browser to the index.html file. From the output above we can see that the site's index file is located in the following directory:

/home/user/VSCodeProjects/blog/out/site/jbake.dest

If we open the index.html file, we will get a similar page as above. However, many of the assets will not appear correctly. For example, an image using an absolute link will not be rendered in the browser. This is because all links that absolute, will only resolve to the site's root, if served by an HTTP web server. So, for example, the images in the img directory and cascading style sheets in the css directory will not be found and used correctly, unless served by a web server. This is the reason why one should render the site using a web server.

You can now look a the the JBake documentation do learn how to configure this tool. It is possible to:

  • Configure the folders where the content, templates and assets are placed;
  • Flag files and folders to be ignored by JBake;
  • Define the default status value used in the metadata headers (draft, published, published-date)

Publishing

If we use the default publishing source, as we did in the getting started section, your content is automatically published. You can configure the sources of Github pages to be kept in a specific branch, and place the sources in that branch's root or /doc folder. If you publish to the /docs folder you can also specify the custom domain name of your site in the /docs/CNAME file. Every time you merge changes to the source branch, Github publishes those pages.

I have opted to place the source in one repository, and publish the content in another repository. I do this so that I can keep my sources private, which include drafts and notes. We have followed the instructions used for setting up Github pages using Jekyll. At this point, we have already created our source repository, generated a static site and tested it locally using the Mill JBake plugin. We also have our public site repository that will hold our static website that anyone can access via Github pages. However, because we are using Github free, we cannot publish our content automatically.

One way to do this is to simply:

  1. Create and clone your public website repository;
  2. Configure your public website repository to contain a Github pages URL;
  3. Create and clone your private source repository;
  4. Use the Mill JBake plugin to develop and test the local copy of your static website in your private repository;
  5. When you are ready to publish:
    1. Delete your public website repository's source folder from your local copy(such as /home/user/VSCodeProjects/tech4rd/docs/);
    2. Copy the compiled static website from your copy of the private site (for example /home/user/VSCodeProjects/blog/out/site/jbake.dest/) to your copy of the public site (for example /home/user/VSCodeProjects/tech4rd/docs/);
    3. Push and commit the changes of your copy of the public website repository;
  6. Github pages will detect the update and publish your site

Lets take a look at the build.sc Mill script code snippets defined in the site object extends JBakeModule. The set of methods, values and targets allow us to deploy the site as described above. The following line creates a path that point to the public website repository:

  def siteDestination: T[os.Path] = T { os.Path("/home/user/VSCodeProjects/tech4rd") }

The Mill script assumes that the siteDestination path contains a valid git repository. This is the default path where the compiled website is copied to. The next code snipped shows the jbakeCopy target that does the actual copying:

  def jbakeCopy(args: String*) = T.command {
    val outPath = jbake().path
    val destination = if (args.size > 0) os.Path(args(0)) else siteDestination()
    val dest = destination / "docs"
    val exists = os.exists(dest) && os.isDir(dest)
    if (exists) {
      os.remove.all(dest)
      T.ctx().log.info(s"Copying site from '$outPath' to '$dest'")
      os.copy(outPath, dest)
      Some(destination)
    }
    else {
      T.ctx().log.info(s"Warning: destination does not exist - $dest. Site not deployed.")
      None
    }
  }

The method above works as follows:

  1. It uses the jbake command to extract the destination folder where the compiled site exists. Note that if the website's sources have changed, jbake will automatically compile these sources;
  2. It then gets the destination path where the website's content will be copied to. If no path is passed into this target as a command line argument, then the siteDestination path will be used by default;
  3. The jbakeCopy target assumes the destination directory has a docs subfolder where the website's content must be copied to. This will allow us later to copy and publish additional elements, such as code examples or complete projects, that may be referred to in the website. If this subfolder does not exist, the process ends here;
  4. If the destination directory docs subfolder exists, then its contents are first deleted, and the new website's compiled content is copied into it;
  5. The method then returns the destination path if a copy was performed (Some(destination)) or None to indicate the copy failed.

Next we show how to execute this target on a new site. First place yourself at the private source repository's directory:

$ cd /home/user/VSCodeProjects/blog

Output:

~/VSCodeProjects/blog$ pwd
/home/user/VSCodeProjects/blog

Next we invoke the jbakeCopy target as follows:

~/VSCodeProjects/blog$ ./mill -i site.jbakeCopy
Output of `jbakeCopy` when website is compiled...
Compiling /home/user/VSCodeProjects/blog/build.sc
[7/9] site.jbake 
JBake v2.6.7 (2021-05-14 21:54:29[GMT+01:00]) [http://jbake.org]

18:32:25.715 INFO  org.jbake.app.Oven - Baking has started...
Warning: Nashorn engine is planned to be removed from a future JDK release
18:32:25.966 INFO  c.o.common.jna.ONative - Detected limit of amount of simultaneously open files is 1048576,  limit of open files for disk cache will be set to 523776
18:32:26.015 INFO  c.o.common.jna.ONative - 33429823488 B/31881 MB/31 GB of physical memory were detected on machine
18:32:26.016 INFO  c.o.common.jna.ONative - Soft memory limit for this process is set to -1 B/-1 MB/-1 GB
18:32:26.016 INFO  c.o.common.jna.ONative - Hard memory limit for this process is set to -1 B/-1 MB/-1 GB
18:32:26.016 INFO  c.o.common.jna.ONative - Path to 'memory' cgroup is '/user.slice/user-1000.slice/user@1000.service'
18:32:26.017 INFO  c.o.common.jna.ONative - Mounting path for memory cgroup controller is '/sys/fs/cgroup/memory'
18:32:26.017 INFO  c.o.common.jna.ONative - cgroup soft memory limit is 9223372036854771712 B/8796093022207 MB/8589934591 GB
18:32:26.017 INFO  c.o.common.jna.ONative - cgroup hard memory limit is 9223372036854771712 B/8796093022207 MB/8589934591 GB
18:32:26.017 INFO  c.o.common.jna.ONative - Detected memory limit for current process is 33429823488 B/31881 MB/31 GB
18:32:26.018 INFO  c.o.o.c.e.OMemoryAndLocalPaginatedEnginesInitializer - JVM can use maximum 7972MB of heap memory
18:32:26.018 INFO  c.o.o.c.e.OMemoryAndLocalPaginatedEnginesInitializer - Because OrientDB is running outside a container 12% of memory will be left unallocated according to the setting 'memory.leftToOS' not taking into account heap memory
18:32:26.018 INFO  com.orientechnologies - OrientDB auto-config DISKCACHE=20 083MB (heap=7 972MB os=31 881MB)
18:32:26.020 INFO  c.o.o.c.e.l.OEngineLocalPaginated - System is started under an effective user : `user`
18:32:26.020 INFO  c.o.o.c.e.l.OEngineLocalPaginated - Allocation of 305261 pages.
18:32:26.714 INFO  c.o.o.c.s.m.ODirectMemoryStorage - Storage 'memory:cache' is created under OrientDB distribution : 3.0.37 - Veloce (build 6a0e4724c10d51a0b19700fca46da8e41ae006f5, branch UNKNOWN)
18:32:27.614 INFO  org.jbake.template.ModelExtractors - register new extractors for document type: masterindex
18:32:27.615 INFO  org.jbake.template.ModelExtractors - register new extractors for document type: tagsindex
18:32:27.615 INFO  org.jbake.template.ModelExtractors - register new extractors for document type: sitemap
18:32:27.883 INFO  org.jbake.app.Crawler - Processing [/home/user/VSCodeProjects/blog/site/src/content/about.html]...  : new 
18:32:27.891 INFO  org.jbake.parser.AsciidoctorEngine - Initializing Asciidoctor engine...
18:32:29.672 INFO  org.jbake.parser.AsciidoctorEngine - Asciidoctor engine initialized.
18:32:29.811 INFO  org.jbake.app.Crawler - Processing [/home/user/VSCodeProjects/blog/site/src/content/blog/2013/fifth-post.adoc]...  : new 
18:32:29.816 INFO  org.jbake.app.Crawler - Processing [/home/user/VSCodeProjects/blog/site/src/content/blog/2013/first-post.html]...  : new 
18:32:29.896 INFO  org.jbake.app.Crawler - Processing [/home/user/VSCodeProjects/blog/site/src/content/blog/2013/fourth-post.adoc]...  : new 
18:32:29.995 INFO  org.jbake.app.Crawler - Processing [/home/user/VSCodeProjects/blog/site/src/content/blog/2013/second-post.md]...  : new 
18:32:30.029 INFO  org.jbake.app.Crawler - Processing [/home/user/VSCodeProjects/blog/site/src/content/blog/2013/third-post.adoc]...  : new 
18:32:30.030 INFO  org.jbake.app.Crawler - Content detected:
18:32:30.030 INFO  org.jbake.app.Crawler - Parsed 1 files of type: page
18:32:30.030 INFO  org.jbake.app.Crawler - Parsed 5 files of type: post
18:32:30.126 INFO  org.jbake.app.Renderer - Rendering archive [/home/user/VSCodeProjects/blog/out/site/jbake.dest/archive.html]... done!
18:32:30.132 INFO  org.jbake.app.Renderer - Rendering [/home/user/VSCodeProjects/blog/out/site/jbake.dest/about.html]... done!
18:32:30.143 INFO  org.jbake.app.Renderer - Rendering [/home/user/VSCodeProjects/blog/out/site/jbake.dest/blog/2013/fifth-post.html]... done!
18:32:30.146 INFO  org.jbake.app.Renderer - Rendering [/home/user/VSCodeProjects/blog/out/site/jbake.dest/blog/2013/fourth-post.html]... done!
18:32:30.149 INFO  org.jbake.app.Renderer - Rendering [/home/user/VSCodeProjects/blog/out/site/jbake.dest/blog/2013/third-post.html]... done!
18:32:30.152 INFO  org.jbake.app.Renderer - Rendering [/home/user/VSCodeProjects/blog/out/site/jbake.dest/blog/2013/second-post.html]... done!
18:32:30.156 INFO  org.jbake.app.Renderer - Rendering [/home/user/VSCodeProjects/blog/out/site/jbake.dest/blog/2013/first-post.html]... done!
18:32:30.182 INFO  org.jbake.app.Renderer - Rendering feed [/home/user/VSCodeProjects/blog/out/site/jbake.dest/feed.xml]... done!
18:32:30.189 INFO  org.jbake.app.Renderer - Rendering masterindex [/home/user/VSCodeProjects/blog/out/site/jbake.dest/index.html]... done!
18:32:30.199 INFO  org.jbake.app.Renderer - Rendering sitemap [/home/user/VSCodeProjects/blog/out/site/jbake.dest/sitemap.xml]... done!
18:32:30.205 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/css/asciidoctor.css]... done!
18:32:30.206 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/css/base.css]... done!
18:32:30.206 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/css/bootstrap.min.css]... done!
18:32:30.206 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/css/prettify.css]... done!
18:32:30.207 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/favicon.ico]... done!
18:32:30.207 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/fonts/glyphicons-halflings-regular.eot]... done!
18:32:30.208 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/fonts/glyphicons-halflings-regular.svg]... done!
18:32:30.208 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/fonts/glyphicons-halflings-regular.ttf]... done!
18:32:30.208 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/fonts/glyphicons-halflings-regular.woff]... done!
18:32:30.209 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/img/beach.jpg]... done!
18:32:30.209 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/js/bootstrap.min.js]... done!
18:32:30.209 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/js/html5shiv.min.js]... done!
18:32:30.209 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/js/jquery-1.11.1.min.js]... done!
18:32:30.210 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/js/prettify.js]... done!
18:32:30.210 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/content/about/jbake_logo.png]... done!
18:32:30.210 INFO  org.jbake.app.Oven - Baking finished!
18:32:30.210 INFO  org.jbake.app.Oven - Baked 10 items in 4496ms
[9/9] site.jbakeCopy 
Copying site from '/home/user/VSCodeProjects/blog/out/site/jbake.dest' to '/home/user/VSCodeProjects/tech4rd/docs'


We can also invoke the jbakeCopy target passing it the /tmp destination path:

~/VSCodeProjects/blog$ ./mill -i site.jbakeCopy /tmp

Output:

[9/9] site.jbakeCopy 
Copying site from '/home/user/VSCodeProjects/blog/out/site/jbake.dest' to '/tmp/docs'

To be able to deploy a site in Github pages, we need only commit and push the new content to the remote public website repository. The code snippet below shows the jbakeDeploy target that does this:

  def jbakeDeploy( args: String* ): Command[Unit] = T.command {

    val wasCopied = jbakeCopy(args:_*)() 

    if (wasCopied.isDefined){
      // Check if we are in a git repo
      val destination = wasCopied.get 
      val repo = destination / ".git"
      val exists = os.exists(repo) && os.isDir(repo)

      T.ctx().log.info(s"pushSite: check if Git repo")
      val isGitRepoCommand = List("git", "rev-parse", "--is-inside-work-tree")
      val isGitRepoRes = os.proc(isGitRepoCommand)
                           .call(
                                 cwd = destination, 
                                 mergeErrIntoOut = true
                               )
 
      // Is it a repo?
      val out = isGitRepoRes.out.string.trim
      val isRepo = out == "true"

      if (exists && isRepo){
        T.ctx().log.info(s"Git command at ${destination}")
        val addCommand = List("git", "add", ".")

        val message = if (args.length >=2) args(1) else ""
        val commitMessage = s"${LocalDateTime.now()}@$millSourcePath/build.sc/pushSite}: ${message}"
        T.ctx().log.info(s"pushSite: commit message = '$commitMessage'")
        val commitCommand = List("git", "commit", "-a", "-m", commitMessage)
        val pushCommand = List("git", "push")

        T.ctx().log.info(s"pushSite: add")
        val add = os.proc(addCommand)
                    .call( cwd = destination )

        T.ctx().log.info(s"pushSite: commit")
        val commit = os.proc(commitCommand)
                       .call( cwd = destination )

        T.ctx().log.info(s"pushSite: push")
        val push = os.proc(pushCommand)
                    .call( cwd = destination )
      } 
      else {
        T.ctx().log.info(s"Git not found at ${destination}. No git commands executed.")
      }
    }
  }

In the code snippet above, the jbakeCopy target is first invoked to ensure that the website's source is compiled and then copied to the public website repository. If the website's content was successfully generated and copied, then it checks to see if the destination folder is a valid Git repository by executing the Git command:

git rev-parse --is-inside-work-tree

If the destination folder is a valid Git repository, then it executes the Git commands add, commit and push. If the destination repository is correctly configured to use Github pages, then the website will be published and made available at the configured Github pages URL. Note that the website will take some time to be published. Here is an example of deploying the site via Github pages using the target above:

$ cd /home/user/VSCodeProjects/blog
$ ~/VSCodeProjects/blog$ ./mill -i site.jbakeDeploy 
Output of the jbakeCopy target command...
Compiling /home/user/VSCodeProjects/blog/build.sc
[7/10] site.jbake 
JBake v2.6.7 (2021-05-14 21:54:29[GMT+01:00]) [http://jbake.org]

18:31:23.772 INFO  org.jbake.app.Oven - Baking has started...
Warning: Nashorn engine is planned to be removed from a future JDK release
18:31:23.988 INFO  c.o.common.jna.ONative - Detected limit of amount of simultaneously open files is 1048576,  limit of open files for disk cache will be set to 523776
18:31:24.039 INFO  c.o.common.jna.ONative - 33429823488 B/31881 MB/31 GB of physical memory were detected on machine
18:31:24.039 INFO  c.o.common.jna.ONative - Soft memory limit for this process is set to -1 B/-1 MB/-1 GB
18:31:24.039 INFO  c.o.common.jna.ONative - Hard memory limit for this process is set to -1 B/-1 MB/-1 GB
18:31:24.039 INFO  c.o.common.jna.ONative - Path to 'memory' cgroup is '/user.slice/user-1000.slice/user@1000.service'
18:31:24.040 INFO  c.o.common.jna.ONative - Mounting path for memory cgroup controller is '/sys/fs/cgroup/memory'
18:31:24.041 INFO  c.o.common.jna.ONative - cgroup soft memory limit is 9223372036854771712 B/8796093022207 MB/8589934591 GB
18:31:24.041 INFO  c.o.common.jna.ONative - cgroup hard memory limit is 9223372036854771712 B/8796093022207 MB/8589934591 GB
18:31:24.041 INFO  c.o.common.jna.ONative - Detected memory limit for current process is 33429823488 B/31881 MB/31 GB
18:31:24.042 INFO  c.o.o.c.e.OMemoryAndLocalPaginatedEnginesInitializer - JVM can use maximum 7972MB of heap memory
18:31:24.042 INFO  c.o.o.c.e.OMemoryAndLocalPaginatedEnginesInitializer - Because OrientDB is running outside a container 12% of memory will be left unallocated according to the setting 'memory.leftToOS' not taking into account heap memory
18:31:24.043 INFO  com.orientechnologies - OrientDB auto-config DISKCACHE=20 083MB (heap=7 972MB os=31 881MB)
18:31:24.044 INFO  c.o.o.c.e.l.OEngineLocalPaginated - System is started under an effective user : `user`
18:31:24.045 INFO  c.o.o.c.e.l.OEngineLocalPaginated - Allocation of 305261 pages.
18:31:24.761 INFO  c.o.o.c.s.m.ODirectMemoryStorage - Storage 'memory:cache' is created under OrientDB distribution : 3.0.37 - Veloce (build 6a0e4724c10d51a0b19700fca46da8e41ae006f5, branch UNKNOWN)
18:31:25.770 INFO  org.jbake.template.ModelExtractors - register new extractors for document type: masterindex
18:31:25.771 INFO  org.jbake.template.ModelExtractors - register new extractors for document type: tagsindex
18:31:25.771 INFO  org.jbake.template.ModelExtractors - register new extractors for document type: sitemap
18:31:25.960 INFO  org.jbake.app.Crawler - Processing [/home/user/VSCodeProjects/blog/site/src/content/about.html]...  : new 
18:31:25.970 INFO  org.jbake.parser.AsciidoctorEngine - Initializing Asciidoctor engine...
18:31:27.877 INFO  org.jbake.parser.AsciidoctorEngine - Asciidoctor engine initialized.
18:31:28.036 INFO  org.jbake.app.Crawler - Processing [/home/user/VSCodeProjects/blog/site/src/content/blog/2013/fifth-post.adoc]...  : new 
18:31:28.040 INFO  org.jbake.app.Crawler - Processing [/home/user/VSCodeProjects/blog/site/src/content/blog/2013/first-post.html]...  : new 
18:31:28.116 INFO  org.jbake.app.Crawler - Processing [/home/user/VSCodeProjects/blog/site/src/content/blog/2013/fourth-post.adoc]...  : new 
18:31:28.224 INFO  org.jbake.app.Crawler - Processing [/home/user/VSCodeProjects/blog/site/src/content/blog/2013/second-post.md]...  : new 
18:31:28.258 INFO  org.jbake.app.Crawler - Processing [/home/user/VSCodeProjects/blog/site/src/content/blog/2013/third-post.adoc]...  : new 
18:31:28.258 INFO  org.jbake.app.Crawler - Content detected:
18:31:28.258 INFO  org.jbake.app.Crawler - Parsed 1 files of type: page
18:31:28.258 INFO  org.jbake.app.Crawler - Parsed 5 files of type: post
18:31:28.363 INFO  org.jbake.app.Renderer - Rendering archive [/home/user/VSCodeProjects/blog/out/site/jbake.dest/archive.html]... done!
18:31:28.369 INFO  org.jbake.app.Renderer - Rendering [/home/user/VSCodeProjects/blog/out/site/jbake.dest/about.html]... done!
18:31:28.385 INFO  org.jbake.app.Renderer - Rendering [/home/user/VSCodeProjects/blog/out/site/jbake.dest/blog/2013/fifth-post.html]... done!
18:31:28.388 INFO  org.jbake.app.Renderer - Rendering [/home/user/VSCodeProjects/blog/out/site/jbake.dest/blog/2013/fourth-post.html]... done!
18:31:28.391 INFO  org.jbake.app.Renderer - Rendering [/home/user/VSCodeProjects/blog/out/site/jbake.dest/blog/2013/third-post.html]... done!
18:31:28.395 INFO  org.jbake.app.Renderer - Rendering [/home/user/VSCodeProjects/blog/out/site/jbake.dest/blog/2013/second-post.html]... done!
18:31:28.399 INFO  org.jbake.app.Renderer - Rendering [/home/user/VSCodeProjects/blog/out/site/jbake.dest/blog/2013/first-post.html]... done!
18:31:28.430 INFO  org.jbake.app.Renderer - Rendering feed [/home/user/VSCodeProjects/blog/out/site/jbake.dest/feed.xml]... done!
18:31:28.436 INFO  org.jbake.app.Renderer - Rendering masterindex [/home/user/VSCodeProjects/blog/out/site/jbake.dest/index.html]... done!
18:31:28.447 INFO  org.jbake.app.Renderer - Rendering sitemap [/home/user/VSCodeProjects/blog/out/site/jbake.dest/sitemap.xml]... done!
18:31:28.451 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/css/asciidoctor.css]... done!
18:31:28.452 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/css/base.css]... done!
18:31:28.452 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/css/bootstrap.min.css]... done!
18:31:28.452 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/css/prettify.css]... done!
18:31:28.452 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/favicon.ico]... done!
18:31:28.452 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/fonts/glyphicons-halflings-regular.eot]... done!
18:31:28.453 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/fonts/glyphicons-halflings-regular.svg]... done!
18:31:28.453 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/fonts/glyphicons-halflings-regular.ttf]... done!
18:31:28.453 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/fonts/glyphicons-halflings-regular.woff]... done!
18:31:28.454 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/img/beach.jpg]... done!
18:31:28.454 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/js/bootstrap.min.js]... done!
18:31:28.454 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/js/html5shiv.min.js]... done!
18:31:28.455 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/js/jquery-1.11.1.min.js]... done!
18:31:28.455 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/js/prettify.js]... done!
18:31:28.456 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/content/about/jbake_logo.png]... done!
18:31:28.456 INFO  org.jbake.app.Oven - Baking finished!
18:31:28.456 INFO  org.jbake.app.Oven - Baked 10 items in 4685ms
[9/10] site.jbakeCopy 
Copying site from '/home/user/VSCodeProjects/blog/out/site/jbake.dest' to '/home/user/VSCodeProjects/tech4rd/docs'
[10/10] site.jbakeDeploy 
pushSite: check if Git repo
Git command at /home/user/VSCodeProjects/tech4rd
pushSite: commit message = '2022-05-18T18:31:28.698889@/home/user/VSCodeProjects/blog/site/build.sc/pushSite}: '
pushSite: add
pushSite: commit
pushSite: push
Enumerating objects: 7, done.
Counting objects: 100% (7/7), done.
Delta compression using up to 12 threads
Compressing objects: 100% (4/4), done.
Writing objects: 100% (4/4), 415 bytes | 415.00 KiB/s, done.
Total 4 (delta 3), reused 0 (delta 0)
remote: Resolving deltas: 100% (3/3), completed with 3 local objects.
To https://github.com/hmf/tech4rd.git
   1386894..fe6d585  main -> main


Just as in the case of the jbakeCopy target, we can also pass the destination path to the jbakeCopy target. Here is an example:

~/VSCodeProjects/blog$ ./mill -i site.jbakeDeploy /tmp

And the output is:

[9/10] site.jbakeCopy 
Copying site from '/home/user/VSCodeProjects/blog/out/site/jbake.dest' to '/tmp/docs'
[10/10] site.jbakeDeploy 
pushSite: check if Git repo
1 targets failed
site.jbakeDeploy os.SubprocessException: CommandResult 128
fatal: not a git repository (or any of the parent directories): .git

    os.proc.call(ProcessOps.scala:85)
    ammonite.$file.build$site$.$anonfun$jbakeDeploy$2(build.sc:94)
    scala.runtime.java8.JFunction0$mcV$sp.apply(JFunction0$mcV$sp.scala:18)

Note that because the destination folder is not a valid Git repository, the target will fail.

Using a different template

JBake provides a set of example projects. These are usually stored in the JBake installation directory. The Mill JBake plugin downloads and places these in the project's "distribution" output directory. Here is a list of those files for the version of JBake we selected:

~$ ls -l /~/VSCodeProjects/blog/out/site/jbakeDistributionDir.dest/unpacked/jbake-2.6.7-bin/
total 3,3M
drwxrwxr-x 2 user user 4,0K mai 18 18:33 bin
-rw-rw-r-- 1 user user 299K mai 18 18:33 example_project_freemarker.zip
-rw-rw-r-- 1 user user 2,1M mai 18 18:33 example_project_groovy-mte.zip
-rw-rw-r-- 1 user user 299K mai 18 18:33 example_project_groovy.zip
-rw-rw-r-- 1 user user 298K mai 18 18:33 example_project_jade.zip
-rw-rw-r-- 1 user user 300K mai 18 18:33 example_project_thymeleaf.zip
drwxrwxr-x 3 user user 4,0K mai 18 18:33 lib

If you want to change these examples so that you can initialize a site using your own custom structure and templates, you need to extract, change and repack the content of these archives so that the jbakeInit Mill target can use them. When using Mill, this is not the best solution because JBake is installed for each project module you set up. You would have to repeat the process of changing the template archives again. A better alternative is to create and keep your site "theme" in a separate code repository and then automatically download and unpack the contents into the docs folder simply pointing to the URL of that repository. Here is an example of Benjamin Marwell providing the JBake Author template, which is a port of the Wordpress Author theme. Their are other such as:

  1. JBake Future Imperfect template
  2. JBake Clean Blog theme
  3. JBake Phlat theme
  4. Beautiful-JBake template

Preparing one of these template or themes takes time and effort and the results may vary widely in terms of aesthetics and functionality. This is why I have opted to use an existing template. In particular, we will use the JBake Future Imperfect template because it has a pleasing aesthetic and is feature rich. It includes:

  1. Support for Discus comments
  2. Google analytics tracking
  3. Estimation of reading time
  4. Social links
  5. Customized Page not found - 404 URL
  6. Post featured images
  7. Feeds (JSON and RSS XML)

To initialize the JBake site with the contents of the Git URL, the following jbakeGitTemplate target can be used:

  def jbakeGitTemplate( args: String* ): Command[Unit] = T.command {
    val srcDir = sources().head.path
    val source: String = if (args.size > 0) 
                   args(0)
                 else 
                   gitTemplateURL()
    T.ctx().log.info(s"""jbakeGitTemplate: Git source at "${source}" """)
    val destination = build.millSourcePath
    T.ctx().log.info(s"jbakeGitTemplate: check if Git repo at $destination")
    val isGit = isGitRepo(destination)
    if (isGit) {
      if (os.exists(srcDir)){
        T.ctx().log.info(s"""Git command at "${millSourcePath}" remove "$srcDir"""")
        os.remove.all(srcDir)      
        os.remove(srcDir)      
      }
      val relative = srcDir.relativeTo(millSourcePath).toString 
      T.ctx().log.info(s"""Git clone command at "${millSourcePath}" with relative destination to "$relative"""")
      val cloneCommand = List("git", "clone", source, relative)
      val add = os.proc(cloneCommand)
                  .call( cwd = millSourcePath )
    }
    else {
      T.ctx().log.info(s"Git not found at ${destination}. No git commands executed.")
    }
  }

This target is equivalent to a jbakeInit. It will delete all of the website's sources, so be careful. It first obtains the public Git URL from the command line parameter. If no URL is provided it defaults to the gitTemplateURL target shown below. You can set the default URL in your Mill script by overriding this target.

  def gitTemplateURL: T[String] = T { "https://github.com/manikmagar/jbake-future-imperfect-template.git" }

To see the default URL that is set in the Mills script, execute the following command:

./mill -i show site.gitTemplateURL

The jbakeGitTemplate then checks if the destination target is a git repository. If it is, then executes a git clone command that copies the remote repository's content into the Mill module's destination folder. In this running example the JBake template sources are placed in the following folder:

home/user/VSCodeProjects/blog/site/src

At this point you are ready to contribute content to the template and then bake, serve and/or deploy the website's content as previously described. The following is an example of initializing a JBake template by explicitly providing a Git repository URL:

~/VSCodeProjects/blog$ ./mill -i site.jbakeGitTemplate https://github.com/SiddheshRane/jbake-clean-blog-template

And here is the output of the command (note the folders where the template's sources are placed):

[3/3] site.jbakeGitTemplate 
jbakeGitTemplate: Git source at "https://github.com/SiddheshRane/jbake-clean-blog-template"
jbakeGitTemplate: check if Git repo at /home/user/VSCodeProjects/blog
Git command at "/home/user/VSCodeProjects/blog/site" remove "/home/user/VSCodeProjects/blog/site/src"
Git clone command at "/home/user/VSCodeProjects/blog/site" with relative destination to "src"
Cloning into 'src'...
remote: Enumerating objects: 264, done.
remote: Total 264 (delta 0), reused 0 (delta 0), pack-reused 264
Receiving objects: 100% (264/264), 3.51 MiB | 3.75 MiB/s, done.
Resolving deltas: 100% (121/121), done.

One can now serve and test the website as follows:

~/VSCodeProjects/blog$ ./mill -i site.jbakeServe
Output of the site.jbakeServe target ...
[8/8] site.jbakeServe 
JBake v2.6.7 (2021-05-14 21:54:29[GMT+01:00]) [http://jbake.org]

11:23:06.454 INFO  org.jbake.app.Oven - Baking has started...
Warning: Nashorn engine is planned to be removed from a future JDK release
11:23:06.681 INFO  c.o.common.jna.ONative - Detected limit of amount of simultaneously open files is 1048576,  limit of open files for disk cache will be set to 523776
11:23:06.736 INFO  c.o.common.jna.ONative - 33429815296 B/31881 MB/31 GB of physical memory were detected on machine
11:23:06.736 INFO  c.o.common.jna.ONative - Soft memory limit for this process is set to -1 B/-1 MB/-1 GB
11:23:06.736 INFO  c.o.common.jna.ONative - Hard memory limit for this process is set to -1 B/-1 MB/-1 GB
11:23:06.736 INFO  c.o.common.jna.ONative - Path to 'memory' cgroup is '/user.slice/user-1000.slice/user@1000.service'
11:23:06.737 INFO  c.o.common.jna.ONative - Mounting path for memory cgroup controller is '/sys/fs/cgroup/memory'
11:23:06.737 INFO  c.o.common.jna.ONative - cgroup soft memory limit is 9223372036854771712 B/8796093022207 MB/8589934591 GB
11:23:06.737 INFO  c.o.common.jna.ONative - cgroup hard memory limit is 9223372036854771712 B/8796093022207 MB/8589934591 GB
11:23:06.738 INFO  c.o.common.jna.ONative - Detected memory limit for current process is 33429815296 B/31881 MB/31 GB
11:23:06.738 INFO  c.o.o.c.e.OMemoryAndLocalPaginatedEnginesInitializer - JVM can use maximum 7972MB of heap memory
11:23:06.738 INFO  c.o.o.c.e.OMemoryAndLocalPaginatedEnginesInitializer - Because OrientDB is running outside a container 12% of memory will be left unallocated according to the setting 'memory.leftToOS' not taking into account heap memory
11:23:06.739 INFO  com.orientechnologies - OrientDB auto-config DISKCACHE=20 083MB (heap=7 972MB os=31 881MB)
11:23:06.740 INFO  c.o.o.c.e.l.OEngineLocalPaginated - System is started under an effective user : `user`
11:23:06.742 INFO  c.o.o.c.e.l.OEngineLocalPaginated - Allocation of 305261 pages.
11:23:07.450 INFO  c.o.o.c.s.m.ODirectMemoryStorage - Storage 'memory:cache' is created under OrientDB distribution : 3.0.37 - Veloce (build 6a0e4724c10d51a0b19700fca46da8e41ae006f5, branch UNKNOWN)
11:23:08.446 INFO  org.jbake.template.ModelExtractors - register new extractors for document type: masterindex
11:23:08.448 INFO  org.jbake.template.ModelExtractors - register new extractors for document type: sitemap
11:23:08.448 INFO  org.jbake.template.ModelExtractors - register new extractors for document type: tagsindex
11:23:08.713 INFO  org.jbake.app.Crawler - Processing [/home/user/VSCodeProjects/blog/site/src/content/about.html]...  : new 
11:23:08.719 INFO  org.jbake.app.Crawler - Processing [/home/user/VSCodeProjects/blog/site/src/content/blog/2013/first-post.html]...  : new 
11:23:08.733 INFO  org.jbake.parser.AsciidoctorEngine - Initializing Asciidoctor engine...
11:23:10.469 INFO  org.jbake.parser.AsciidoctorEngine - Asciidoctor engine initialized.
11:23:10.668 INFO  org.jbake.app.Crawler - Processing [/home/user/VSCodeProjects/blog/site/src/content/blog/2013/fourth-post.adoc]...  : new 
11:23:10.804 INFO  org.jbake.app.Crawler - Processing [/home/user/VSCodeProjects/blog/site/src/content/blog/2013/second-post.md]...  : new 
11:23:10.851 INFO  org.jbake.app.Crawler - Processing [/home/user/VSCodeProjects/blog/site/src/content/blog/2013/third-post.adoc]...  : new 
11:23:10.851 INFO  org.jbake.app.Crawler - Content detected:
11:23:10.851 INFO  org.jbake.app.Crawler - Parsed 1 files of type: page
11:23:10.851 INFO  org.jbake.app.Crawler - Parsed 4 files of type: post
11:23:11.045 INFO  org.jbake.app.Renderer - Rendering archive [/home/user/VSCodeProjects/blog/out/site/jbake.dest/archive.html]... done!
11:23:11.064 INFO  org.jbake.app.Renderer - Rendering [/home/user/VSCodeProjects/blog/out/site/jbake.dest/about.html]... done!
11:23:11.097 INFO  org.jbake.app.Renderer - Rendering [/home/user/VSCodeProjects/blog/out/site/jbake.dest/blog/2013/fourth-post.html]... done!
11:23:11.105 INFO  org.jbake.app.Renderer - Rendering [/home/user/VSCodeProjects/blog/out/site/jbake.dest/blog/2013/third-post.html]... done!
11:23:11.112 INFO  org.jbake.app.Renderer - Rendering [/home/user/VSCodeProjects/blog/out/site/jbake.dest/blog/2013/second-post.html]... done!
11:23:11.120 INFO  org.jbake.app.Renderer - Rendering [/home/user/VSCodeProjects/blog/out/site/jbake.dest/blog/2013/first-post.html]... done!
11:23:11.151 INFO  org.jbake.app.Renderer - Rendering feed [/home/user/VSCodeProjects/blog/out/site/jbake.dest/feed.xml]... done!
11:23:11.171 INFO  org.jbake.app.Renderer - Rendering masterindex [/home/user/VSCodeProjects/blog/out/site/jbake.dest/index.html]... done!
11:23:11.183 INFO  org.jbake.app.Renderer - Rendering sitemap [/home/user/VSCodeProjects/blog/out/site/jbake.dest/sitemap.xml]... done!
11:23:11.210 INFO  org.jbake.app.Renderer - Rendering tag [/home/user/VSCodeProjects/blog/out/site/jbake.dest/tags/asciidoc.html]... done!
11:23:11.219 INFO  org.jbake.app.Renderer - Rendering tag [/home/user/VSCodeProjects/blog/out/site/jbake.dest/tags/blog.html]... done!
11:23:11.222 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/css/asciidoctor.css]... done!
11:23:11.222 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/css/clean-blog.css]... done!
11:23:11.223 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/css/clean-blog.min.css]... done!
11:23:11.223 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/css/coderay.css]... done!
11:23:11.223 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/favicon.png]... done!
11:23:11.223 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/img/archive-cover.jpg]... done!
11:23:11.225 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/img/clean-blog-share-card.png]... done!
11:23:11.225 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/img/grand-canyon.jpg]... done!
11:23:11.225 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/img/index-cover.jpg]... done!
11:23:11.226 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/img/jbake-logo.png]... done!
11:23:11.226 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/img/jekyll-logo.png]... done!
11:23:11.226 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/img/page-cover.jpg]... done!
11:23:11.226 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/img/tags-cover.jpg]... done!
11:23:11.227 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/js/clean-blog.js]... done!
11:23:11.227 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/js/clean-blog.min.js]... done!
11:23:11.227 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/vendor/bootstrap/css/bootstrap.css]... done!
11:23:11.228 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/vendor/bootstrap/css/bootstrap.min.css]... done!
11:23:11.228 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/vendor/bootstrap/fonts/glyphicons-halflings-regular.eot]... done!
11:23:11.229 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/vendor/bootstrap/fonts/glyphicons-halflings-regular.svg]... done!
11:23:11.229 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/vendor/bootstrap/fonts/glyphicons-halflings-regular.ttf]... done!
11:23:11.229 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/vendor/bootstrap/fonts/glyphicons-halflings-regular.woff]... done!
11:23:11.230 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/vendor/bootstrap/fonts/glyphicons-halflings-regular.woff2]... done!
11:23:11.230 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/vendor/bootstrap/js/bootstrap.js]... done!
11:23:11.230 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/vendor/bootstrap/js/bootstrap.min.js]... done!
11:23:11.231 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/vendor/font-awesome/css/font-awesome.css]... done!
11:23:11.231 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/vendor/font-awesome/css/font-awesome.min.css]... done!
11:23:11.232 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/vendor/font-awesome/fonts/FontAwesome.otf]... done!
11:23:11.232 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/vendor/font-awesome/fonts/fontawesome-webfont.eot]... done!
11:23:11.232 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/vendor/font-awesome/fonts/fontawesome-webfont.svg]... done!
11:23:11.233 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/vendor/font-awesome/fonts/fontawesome-webfont.ttf]... done!
11:23:11.233 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/vendor/font-awesome/fonts/fontawesome-webfont.woff]... done!
11:23:11.234 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/vendor/font-awesome/fonts/fontawesome-webfont.woff2]... done!
11:23:11.234 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/vendor/font-awesome/less/animated.less]... done!
11:23:11.235 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/vendor/font-awesome/less/bordered-pulled.less]... done!
11:23:11.235 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/vendor/font-awesome/less/core.less]... done!
11:23:11.235 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/vendor/font-awesome/less/fixed-width.less]... done!
11:23:11.235 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/vendor/font-awesome/less/font-awesome.less]... done!
11:23:11.235 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/vendor/font-awesome/less/icons.less]... done!
11:23:11.235 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/vendor/font-awesome/less/larger.less]... done!
11:23:11.236 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/vendor/font-awesome/less/list.less]... done!
11:23:11.236 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/vendor/font-awesome/less/mixins.less]... done!
11:23:11.236 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/vendor/font-awesome/less/path.less]... done!
11:23:11.236 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/vendor/font-awesome/less/rotated-flipped.less]... done!
11:23:11.236 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/vendor/font-awesome/less/screen-reader.less]... done!
11:23:11.237 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/vendor/font-awesome/less/stacked.less]... done!
11:23:11.237 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/vendor/font-awesome/less/variables.less]... done!
11:23:11.237 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/vendor/font-awesome/scss/_animated.scss]... done!
11:23:11.238 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/vendor/font-awesome/scss/_bordered-pulled.scss]... done!
11:23:11.238 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/vendor/font-awesome/scss/_core.scss]... done!
11:23:11.238 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/vendor/font-awesome/scss/_fixed-width.scss]... done!
11:23:11.238 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/vendor/font-awesome/scss/_icons.scss]... done!
11:23:11.238 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/vendor/font-awesome/scss/_larger.scss]... done!
11:23:11.239 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/vendor/font-awesome/scss/_list.scss]... done!
11:23:11.239 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/vendor/font-awesome/scss/_mixins.scss]... done!
11:23:11.239 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/vendor/font-awesome/scss/_path.scss]... done!
11:23:11.239 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/vendor/font-awesome/scss/_rotated-flipped.scss]... done!
11:23:11.239 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/vendor/font-awesome/scss/_screen-reader.scss]... done!
11:23:11.240 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/vendor/font-awesome/scss/_stacked.scss]... done!
11:23:11.240 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/vendor/font-awesome/scss/_variables.scss]... done!
11:23:11.240 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/vendor/font-awesome/scss/font-awesome.scss]... done!
11:23:11.241 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/vendor/jquery/jquery.js]... done!
11:23:11.241 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/vendor/jquery/jquery.min.js]... done!
11:23:11.242 INFO  org.jbake.app.Oven - Baking finished!
11:23:11.242 INFO  org.jbake.app.Oven - Baked 11 items in 4788ms
11:23:11.242 INFO  c.o.orient.core.db.OrientDBEmbedded - - shutdown storage: cache...
11:23:11.282 INFO  org.jbake.launcher.BakeWatcher - Watching for (content, template, asset) changes in [/home/user/VSCodeProjects/blog/site/src]
11:23:11.348 INFO  org.jbake.launcher.JettyServer - Serving out contents of: [/home/user/VSCodeProjects/blog/out/site/jbake.dest] on http://localhost:8820/
11:23:11.348 INFO  org.jbake.launcher.JettyServer - (To stop server hit CTRL-C)


If you point your browser to the local URL http://localhost:8820/, you should see the website's content similar to what is shown below:

Serving JBake Clean template - localhost

When you are ready to publish the website's content to the remote host, execute the following command:

~/VSCodeProjects/blog$ ./mill -i site.jbakeDeploy
Output of the site.jbakeDeploy target ...
[7/10] site.jbake 
JBake v2.6.7 (2021-05-14 21:54:29[GMT+01:00]) [http://jbake.org]

11:21:44.329 INFO  org.jbake.app.Oven - Baking has started...
Warning: Nashorn engine is planned to be removed from a future JDK release
11:21:44.564 INFO  c.o.common.jna.ONative - Detected limit of amount of simultaneously open files is 1048576,  limit of open files for disk cache will be set to 523776
11:21:44.614 INFO  c.o.common.jna.ONative - 33429815296 B/31881 MB/31 GB of physical memory were detected on machine
11:21:44.614 INFO  c.o.common.jna.ONative - Soft memory limit for this process is set to -1 B/-1 MB/-1 GB
11:21:44.614 INFO  c.o.common.jna.ONative - Hard memory limit for this process is set to -1 B/-1 MB/-1 GB
11:21:44.614 INFO  c.o.common.jna.ONative - Path to 'memory' cgroup is '/user.slice/user-1000.slice/user@1000.service'
11:21:44.615 INFO  c.o.common.jna.ONative - Mounting path for memory cgroup controller is '/sys/fs/cgroup/memory'
11:21:44.615 INFO  c.o.common.jna.ONative - cgroup soft memory limit is 9223372036854771712 B/8796093022207 MB/8589934591 GB
11:21:44.615 INFO  c.o.common.jna.ONative - cgroup hard memory limit is 9223372036854771712 B/8796093022207 MB/8589934591 GB
11:21:44.615 INFO  c.o.common.jna.ONative - Detected memory limit for current process is 33429815296 B/31881 MB/31 GB
11:21:44.616 INFO  c.o.o.c.e.OMemoryAndLocalPaginatedEnginesInitializer - JVM can use maximum 7972MB of heap memory
11:21:44.616 INFO  c.o.o.c.e.OMemoryAndLocalPaginatedEnginesInitializer - Because OrientDB is running outside a container 12% of memory will be left unallocated according to the setting 'memory.leftToOS' not taking into account heap memory
11:21:44.616 INFO  com.orientechnologies - OrientDB auto-config DISKCACHE=20 083MB (heap=7 972MB os=31 881MB)
11:21:44.617 INFO  c.o.o.c.e.l.OEngineLocalPaginated - System is started under an effective user : `user`
11:21:44.618 INFO  c.o.o.c.e.l.OEngineLocalPaginated - Allocation of 305261 pages.
11:21:45.325 INFO  c.o.o.c.s.m.ODirectMemoryStorage - Storage 'memory:cache' is created under OrientDB distribution : 3.0.37 - Veloce (build 6a0e4724c10d51a0b19700fca46da8e41ae006f5, branch UNKNOWN)
11:21:46.257 INFO  org.jbake.template.ModelExtractors - register new extractors for document type: masterindex
11:21:46.258 INFO  org.jbake.template.ModelExtractors - register new extractors for document type: sitemap
11:21:46.258 INFO  org.jbake.template.ModelExtractors - register new extractors for document type: tagsindex
11:21:46.477 INFO  org.jbake.app.Crawler - Processing [/home/user/VSCodeProjects/blog/site/src/content/about.html]...  : new 
11:21:46.484 INFO  org.jbake.app.Crawler - Processing [/home/user/VSCodeProjects/blog/site/src/content/blog/2013/first-post.html]...  : new 
11:21:46.500 INFO  org.jbake.parser.AsciidoctorEngine - Initializing Asciidoctor engine...
11:21:48.272 INFO  org.jbake.parser.AsciidoctorEngine - Asciidoctor engine initialized.
11:21:48.426 INFO  org.jbake.app.Crawler - Processing [/home/user/VSCodeProjects/blog/site/src/content/blog/2013/fourth-post.adoc]...  : new 
11:21:48.525 INFO  org.jbake.app.Crawler - Processing [/home/user/VSCodeProjects/blog/site/src/content/blog/2013/second-post.md]...  : new 
11:21:48.556 INFO  org.jbake.app.Crawler - Processing [/home/user/VSCodeProjects/blog/site/src/content/blog/2013/third-post.adoc]...  : new 
11:21:48.556 INFO  org.jbake.app.Crawler - Content detected:
11:21:48.556 INFO  org.jbake.app.Crawler - Parsed 1 files of type: page
11:21:48.556 INFO  org.jbake.app.Crawler - Parsed 4 files of type: post
11:21:48.748 INFO  org.jbake.app.Renderer - Rendering archive [/home/user/VSCodeProjects/blog/out/site/jbake.dest/archive.html]... done!
11:21:48.767 INFO  org.jbake.app.Renderer - Rendering [/home/user/VSCodeProjects/blog/out/site/jbake.dest/about.html]... done!
11:21:48.802 INFO  org.jbake.app.Renderer - Rendering [/home/user/VSCodeProjects/blog/out/site/jbake.dest/blog/2013/fourth-post.html]... done!
11:21:48.813 INFO  org.jbake.app.Renderer - Rendering [/home/user/VSCodeProjects/blog/out/site/jbake.dest/blog/2013/third-post.html]... done!
11:21:48.823 INFO  org.jbake.app.Renderer - Rendering [/home/user/VSCodeProjects/blog/out/site/jbake.dest/blog/2013/second-post.html]... done!
11:21:48.832 INFO  org.jbake.app.Renderer - Rendering [/home/user/VSCodeProjects/blog/out/site/jbake.dest/blog/2013/first-post.html]... done!
11:21:48.870 INFO  org.jbake.app.Renderer - Rendering feed [/home/user/VSCodeProjects/blog/out/site/jbake.dest/feed.xml]... done!
11:21:48.888 INFO  org.jbake.app.Renderer - Rendering masterindex [/home/user/VSCodeProjects/blog/out/site/jbake.dest/index.html]... done!
11:21:48.900 INFO  org.jbake.app.Renderer - Rendering sitemap [/home/user/VSCodeProjects/blog/out/site/jbake.dest/sitemap.xml]... done!
11:21:48.924 INFO  org.jbake.app.Renderer - Rendering tag [/home/user/VSCodeProjects/blog/out/site/jbake.dest/tags/asciidoc.html]... done!
11:21:48.932 INFO  org.jbake.app.Renderer - Rendering tag [/home/user/VSCodeProjects/blog/out/site/jbake.dest/tags/blog.html]... done!
11:21:48.936 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/css/asciidoctor.css]... done!
11:21:48.936 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/css/clean-blog.css]... done!
11:21:48.937 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/css/clean-blog.min.css]... done!
11:21:48.937 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/css/coderay.css]... done!
11:21:48.937 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/favicon.png]... done!
11:21:48.938 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/img/archive-cover.jpg]... done!
11:21:48.938 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/img/clean-blog-share-card.png]... done!
11:21:48.939 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/img/grand-canyon.jpg]... done!
11:21:48.939 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/img/index-cover.jpg]... done!
11:21:48.939 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/img/jbake-logo.png]... done!
11:21:48.939 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/img/jekyll-logo.png]... done!
11:21:48.940 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/img/page-cover.jpg]... done!
11:21:48.940 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/img/tags-cover.jpg]... done!
11:21:48.940 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/js/clean-blog.js]... done!
11:21:48.941 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/js/clean-blog.min.js]... done!
11:21:48.941 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/vendor/bootstrap/css/bootstrap.css]... done!
11:21:48.941 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/vendor/bootstrap/css/bootstrap.min.css]... done!
11:21:48.942 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/vendor/bootstrap/fonts/glyphicons-halflings-regular.eot]... done!
11:21:48.942 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/vendor/bootstrap/fonts/glyphicons-halflings-regular.svg]... done!
11:21:48.943 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/vendor/bootstrap/fonts/glyphicons-halflings-regular.ttf]... done!
11:21:48.943 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/vendor/bootstrap/fonts/glyphicons-halflings-regular.woff]... done!
11:21:48.943 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/vendor/bootstrap/fonts/glyphicons-halflings-regular.woff2]... done!
11:21:48.944 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/vendor/bootstrap/js/bootstrap.js]... done!
11:21:48.944 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/vendor/bootstrap/js/bootstrap.min.js]... done!
11:21:48.944 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/vendor/font-awesome/css/font-awesome.css]... done!
11:21:48.945 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/vendor/font-awesome/css/font-awesome.min.css]... done!
11:21:48.945 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/vendor/font-awesome/fonts/FontAwesome.otf]... done!
11:21:48.945 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/vendor/font-awesome/fonts/fontawesome-webfont.eot]... done!
11:21:48.946 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/vendor/font-awesome/fonts/fontawesome-webfont.svg]... done!
11:21:48.946 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/vendor/font-awesome/fonts/fontawesome-webfont.ttf]... done!
11:21:48.947 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/vendor/font-awesome/fonts/fontawesome-webfont.woff]... done!
11:21:48.947 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/vendor/font-awesome/fonts/fontawesome-webfont.woff2]... done!
11:21:48.947 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/vendor/font-awesome/less/animated.less]... done!
11:21:48.948 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/vendor/font-awesome/less/bordered-pulled.less]... done!
11:21:48.948 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/vendor/font-awesome/less/core.less]... done!
11:21:48.948 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/vendor/font-awesome/less/fixed-width.less]... done!
11:21:48.948 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/vendor/font-awesome/less/font-awesome.less]... done!
11:21:48.948 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/vendor/font-awesome/less/icons.less]... done!
11:21:48.949 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/vendor/font-awesome/less/larger.less]... done!
11:21:48.949 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/vendor/font-awesome/less/list.less]... done!
11:21:48.949 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/vendor/font-awesome/less/mixins.less]... done!
11:21:48.949 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/vendor/font-awesome/less/path.less]... done!
11:21:48.949 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/vendor/font-awesome/less/rotated-flipped.less]... done!
11:21:48.950 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/vendor/font-awesome/less/screen-reader.less]... done!
11:21:48.950 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/vendor/font-awesome/less/stacked.less]... done!
11:21:48.950 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/vendor/font-awesome/less/variables.less]... done!
11:21:48.951 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/vendor/font-awesome/scss/_animated.scss]... done!
11:21:48.951 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/vendor/font-awesome/scss/_bordered-pulled.scss]... done!
11:21:48.951 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/vendor/font-awesome/scss/_core.scss]... done!
11:21:48.951 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/vendor/font-awesome/scss/_fixed-width.scss]... done!
11:21:48.951 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/vendor/font-awesome/scss/_icons.scss]... done!
11:21:48.952 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/vendor/font-awesome/scss/_larger.scss]... done!
11:21:48.952 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/vendor/font-awesome/scss/_list.scss]... done!
11:21:48.952 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/vendor/font-awesome/scss/_mixins.scss]... done!
11:21:48.952 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/vendor/font-awesome/scss/_path.scss]... done!
11:21:48.952 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/vendor/font-awesome/scss/_rotated-flipped.scss]... done!
11:21:48.953 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/vendor/font-awesome/scss/_screen-reader.scss]... done!
11:21:48.953 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/vendor/font-awesome/scss/_stacked.scss]... done!
11:21:48.953 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/vendor/font-awesome/scss/_variables.scss]... done!
11:21:48.953 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/vendor/font-awesome/scss/font-awesome.scss]... done!
11:21:48.954 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/vendor/jquery/jquery.js]... done!
11:21:48.954 INFO  org.jbake.app.Asset - Copying [/home/user/VSCodeProjects/blog/site/src/assets/vendor/jquery/jquery.min.js]... done!
11:21:48.954 INFO  org.jbake.app.Oven - Baking finished!
11:21:48.954 INFO  org.jbake.app.Oven - Baked 11 items in 4626ms
[9/10] site.jbakeCopy 
Copying site from '/home/user/VSCodeProjects/blog/out/site/jbake.dest' to '/home/user/VSCodeProjects/tech4rd/docs'
[10/10] site.jbakeDeploy 
pushSite: check if Git repo
Git command at /home/user/VSCodeProjects/tech4rd
pushSite: commit message = '2022-05-21T11:21:49.241238@/home/user/VSCodeProjects/blog/site/build.sc/pushSite}: '
pushSite: add
pushSite: commit
pushSite: push
Enumerating objects: 111, done.
Counting objects: 100% (111/111), done.
Delta compression using up to 12 threads
Compressing objects: 100% (91/91), done.
Writing objects: 100% (92/92), 2.34 MiB | 4.51 MiB/s, done.
Total 92 (delta 14), reused 0 (delta 0)
remote: Resolving deltas: 100% (14/14), completed with 3 local objects.
To https://github.com/user/tech4rd.git
   82fbf46..cc57fd4  main -> main


If you point your browser to the Github pages URL, you should see the website's content similar to what is shown below:

Serving JBake Clean template - remote host

Conclusion

This concludes the first part of this series. You should now be able to publish a website using Github pages. In addition to this and with the help of the Mill script, you can now automate the generation of the website's content from your own private repository and publish it with a single command. In the next part we will set up the JBake Future Imperfect template and publish a first article.

References

  1. Markdown
  2. AsciiDoc site
  3. AsciiDoc Wikipedia
  4. ReStructuredText
  5. Freemarker
  6. ThymeLeaf
  7. Google Analytics
  8. MathJax
  9. MathJS
  10. KaTex
  11. Mermaid-Js
  12. Flowchart.js
  13. ChartJS
  14. Plotly
  15. Chartist-Js
  16. RawGraphs.io
  17. d3js
  18. c3js
  19. nvd3
  20. Mill Github
  21. Mill Github pages
  22. FeatherIcons
  23. IonIcons
  24. FontAwesome
  25. Tabler
  26. css.gg
  27. Docusaurus.io
  28. Docusaurus Github
  29. Jekyll
  30. Jekyll Github
  31. JBake github
  32. JBake
  33. Laika
  34. Laika Github
  35. Scala-lang docs
  36. Scala3 ScalaDoc
  37. Scala3 ScalaDoc Guide
  38. Scala3 ScalaDoc Usage
  39. Scala3/Dotty - ScalaDocs github
  40. Javadoc
  41. Sbt-site Github
  42. Sbt-site
  43. sphinx-doc
  44. Pamflet
  45. Pamflet Github
  46. Nanoc
  47. GitBook
  48. Paradox
  49. Github Paradox
  50. GoHugo
  51. AsciiDoctor
  52. Mill Build tool
  53. Mill Docusaurus plugin
  54. Mill JBake plugin
  55. Mill MDoc plugin
  56. MDoc
  57. MDoc in Github
  58. Mill JBake plugin in Github
  59. Github pages
  60. Gitlab pages
  61. Github Flavoured Markdown
  62. Gitlab Flavoured Markdown
  63. John Gruber’s canonical description of Markdown’s syntax
  64. Commonmark variant
  65. Markdown Guide
  66. Markdown guide extended-syntax
  67. R Markdown variant
  68. JBake documentation
  69. Ammonite
  70. Coursier
  71. JBake Author template

  1. See for example information provided for the Gitlab pages and the Github pages.