Combell is kind enough to offer me this A++ hosting for free and was my employer for many years, so the least I could do in return is spread the word about their hosting solutions. Today we zoom in on this little less known feature of their Linux hosting offering. It enables me to manage this little blog with minimal effort.

I should also note that I am the main implementor of the first versions of this nifty feature. From the looks of it, it didn’t change much since I left.

Combell is mainly targeted at the hugely popular PHP community but it is obviously also possible to host plain static content. And moreover, the Autogit automation allows you to use a static file generator such as Hugo to generate HTML, CSS, javascript, merely based on some Markdown formatted text files, the only thing your really need to manage. And static assets such as images of course.

Combell has some nice examples hosted on Github that illustrate the features of Autogit and how you can manage your favourite PHP project using Autogit: https://github.com/combell

SSH jail

At the base of Autogit is a so called ‘SSH jail’. SSH really should be the way how you interact with your hosting, at any hosting company for that matter. It allows you to securely transfer your files using scp or sftp to your website storage. FTP is to be avoided, as should be obvious. The jail aspect means that you are shielded from other hosting accounts using container technologies. A long time ago, I was the main implementor of the first SSH jail versions. It has hugely evolved since, mainly thanks to the uprise of container technologies since Docker became a thing.

It also enables a lot of extra cool features mainly targeted to the PHP community. Autogit is the one we leverage here. There is a so called empty bare git repository provisioned (when enabled in the control panel) that you can securely access using the SSH protocol. I should also note here that the Combell control panel allows you to setup SSH Keys enabling secure access to your jail.

Using a simple YAML config file you can automate stuff when you push new content to the Autogit git repository using so called git hooks.

Hugo

Hugo (https://gohugo.io/ ) is a popular way to create very fast modern looking static websites. When I want to create a project I usually google something like ‘best themes hugo’ and start from the sample website the great theme creators usually provide. At the time of writing I picked https://github.com/adityatelange/hugo-PaperMod/tree/exampleSite

You usually only need to run the hugo binary without extra parameters to generate your static content, after adding/updating some Markdown (.md) formatted text files.

Before you publish, also open the config file and customize it to your liking. Look for config.toml or config.yml in the project root.

Setup

Extra binaries

Combell does not provide the hugo binary (yet). Also there is no Go support as far as I know. I noticed Hugo needs (or needed?) Go for some features, so I also install it. And also to show you that it is possible, even if the host did not provide it for you.

Luckily it is pretty easy to install dependencies.

Just download the latest ‘Linux-64bit’ of Hugo from https://github.com/gohugoio/hugo/releases and drop it in the home of your jail.

I installed version 0.98.0 using this command:

$ wget https://github.com/gohugoio/hugo/releases/download/v0.98.0/hugo_extended_0.98.0_Linux-64bit.tar.gz

Note that we chose the ’extended’ version. Some themes require functionality not available in the non-extended version.

The tar needs to be extracted:

$ tar xvzf hugo_extended_0.98.0_Linux-64bit.tar.gz
$ rm hugo_extended_0.98.0_Linux-64bit.tar.gz

For Go, just head to https://go.dev/dl/ and download the latest linux tar and extract it, again in your home. You can remove folders we don’t need such as ‘src’ and ’test’.

A fair warning here, although Combell goes to great lengths to accomodate your needs, custom binaries are a bit in the grey zone of what is supported in a shared hosting product. I don’t know their exact policy nowadays, but if you can’t seem to get your binary working, it might have triggered some “defense” mechanism. In that case just contact their very responsive support and you probably will be back on your way in no time.

Autogit config

Let’s study the Autogit config file, .autogit.yml:

shared_folders: [ maarten.gent/themes ]

hooks:
# SETUP: Create folder structure for newest release
  setup_before: |
    # Download themes when not done yet
    if [ -z "$(ls -A /site/checkout/master/shared/maarten.gent/themes)" ]; then 
      git clone /autogit/
      cd autogit/
      git submodule update --init --recursive

      mv maarten.gent/themes/* /site/checkout/master/shared/maarten.gent/themes/
      rm -rf /site/checkout/master/shared/maarten.gent/themes/*/.git

      cd ..
      rm -rf autogit/
    fi    
  setup_after: |
        exit 0

# INSTALL: Put code in release folder
  install_before: |
        exit 0
  install_after: |
        exit 0


# SHAREDSYMLINK: Create symlink to shared files and folders
#                present at every release (config, logs, ...)
  sharedsymlink_before: |
        rm -rf maarten.gent/themes/
  sharedsymlink_after: | 
    export GOPATH="~/go"
    export PATH="~:$GOPATH/bin:~:$PATH"
    cd maarten.gent/
    hugo

In the config you can specify script snippets specifying what you want to do before or after different phases of the deploy process. We hook in on the setup_before, sharedsymlink_before and sharedsymlink_after opportunities.

We also specified some ‘shared folders’.

shared_folders feature

This allows us to specify some folders that are common to all deployments and that we typically not want to store in git. Autogit will symlink this folders automatically at every deploy.

Hugo themes are not managed in this git project. We use git submodules to refer to the author’s repository, as explained in next section.

Shared folders usually are set up manually once. As alternative we choose to use the setup_before hook to check if the initialization must be done (empty folder) and download the theme(s) only if needed. This way part of this initialization is cleanly stored in git and automatically applied when needed.

If at one point in time we want to update our theme, i.e. we updated the submodule with another commit id, we can delete the contents of the folder /site/checkout/master/shared/maarten.gent/themes and our hook implementation will download the updated theme at the next git push.

For more information about this feature I refer you again to the excellent Autogit documentation on Github: https://github.com/combell/autogit-reference#how-to-manage-shared-resources

setup_before hook

This is the first possible hook in the process and we leverage it do initialize stuff, if needed.

The bash snippet is a little necessary ‘hack’ because we didn’t commit the Hugo themes to git but make use of so called git submodules. This means that we keep a reference to the git repository and a specific commit (usually HEAD in master at setup time). Since Autogit (feature request) does not process git submodules, we need to do it ourself.

This is a time and bandwidth consuming action, so we make use of the ‘shared folders’ feature, as explained, and we check if the shared folder is empty. If not, we assume the theme is already downloaded and give control immediately back to Autogit.

But when we need to initialize our theme, we perform a new checkout of the Autogit repository, after which we can checkout the submodules (git submodule update). You should know (or learn here) that the bare git repository is located at the folder /autogit in the SSH jail.

Finally we move the downloaded theme(s) to the shared_folder location so Autogit can correctly link it to our release later. We don’t need the .git folder so we also get rid of it.

In this phase we make sure the shared_folderssymlinks can be setup correctly: the (mostly empty) themes/ folder is removed.

This is where the real magic happens. In this phase we have all the files and themes where Hugo expects them.

We setup the environment variables $GOPATH and $PATH so Go can be executed as if it is installed system wide. The home folder (~) is also added to $PATH, also enabling us to execute hugo without specifying its path.

Next we navigate to our project folder maarten.gent/ and simply execute hugo. This will generate all the static assets needed to render this little blog. Hugo puts these in the public/ folder of the Hugo project.

Git structure

The only missing piece of the puzzle to do this yourself is the git project structure.

Usually you start with checking out the empty repository after enabling the Autogit feature in your control panel. Autogit needs at least two things to function, committed to git: the already mentioned .autogit.yml config file and a www/ folder or symlink.

The www folder (or symlink) should contain your index.html. In our case this should point to the automatically generated public folder, that we have triggered in our sharedsymlink_after hook by running hugo.

We generate the www symlink as follows in the root of our git repository:

$ ln -sf /site/checkout/master/current/maarten.gent/public www

Next to this your git project should obviously contain your Hugo project. We opted to store this in the folder maarten.gent/ but this could have been the git root just as well.

Autogit magic at work

Autogit generates some output that allows you to follow what happens when you push something to your hosting. As you can see in the output, Hugo generates content very fast and together with the efficient Autogit deploy, this leads to near instant deployments.

Please note that it looks a bit more complicated than explained above because I have an extra little Hugo project to host my CV. The setup_before hook generates no output and is only an if test statement always resulting to false because the Hugo themes are already initialized before.

**************************************************************
*                                                            *
*                                                            *
*               Unauthorized access to this                  *
*               system/network is prohibited.                *
*                                                            *
*                                                            *
**************************************************************
Enumerating objects: 13, done.
Counting objects: 100% (13/13), done.
Delta compression using up to 8 threads
Compressing objects: 100% (7/7), done.
Writing objects: 100% (7/7), 1.60 KiB | 1.60 MiB/s, done.
Total 7 (delta 3), reused 0 (delta 0), pack-reused 0
remote: production branch 'master' ref received.
remote: Parsing config '.autogit/config':
remote: domain: maarten.gent
remote: production: master
remote: Updated 1 path from adc5bdb
------> Executing BEFORE SETUP hook..
        Checking out in /data/sites/web/maartengent1807/checkout/master/48ec229ce5b16d4b381d357ad39d3f6b752c222b...
------> Executing AFTER SETUP hook..
------> Executing BEFORE INSTALL hook..
remote: HEAD is now at 48ec229 autogit v2
------> Executing AFTER INSTALL hook..
------> Executing BEFORE SHAREDSYMLINK hook..
        Creating shared folder link maarten.gent/themes...
        Creating shared folder link cv/themes...
------> Executing AFTER SHAREDSYMLINK hook..
remote: Start building sites …
remote: hugo v0.98.0-165d299cde259c8b801abadc6d3405a229e449f6+extended linux/amd64 BuildDate=2022-04-28T10:23:30Z VendorInfo=gohugoio
remote:
remote:                    | EN
remote: -------------------+-----
remote:   Pages            | 35
remote:   Paginator pages  |  0
remote:   Non-page files   |  0
remote:   Static files     |  0
remote:   Processed images |  0
remote:   Aliases          | 11
remote:   Sitemaps         |  1
remote:   Cleaned          |  0
remote:
remote: Total in 322 ms
remote: Start building sites …
remote: hugo v0.98.0-165d299cde259c8b801abadc6d3405a229e449f6+extended linux/amd64 BuildDate=2022-04-28T10:23:30Z VendorInfo=gohugoio
remote:
remote:                    | EN
remote: -------------------+-----
remote:   Pages            |  4
remote:   Paginator pages  |  0
remote:   Non-page files   |  0
remote:   Static files     | 98
remote:   Processed images |  0
remote:   Aliases          |  0
remote:   Sitemaps         |  1
remote:   Cleaned          |  0
remote:
remote: Total in 335 ms
------> Executing BEFORE SYMLINK hook..
        Reloading PHP... "OK"
------> Executing AFTER SYMLINK hook..
------> All done. Please visit your fresh deploy: http://maarten.gent
------> Executing BEFORE CLEANUP hook..
        Cleaning up release fdff1feb74b689bfe4126b2fc70f2a4e993d4db6...
------> Executing AFTER CLEANUP hook..
To ssh057.webhosting.be:auto.git
   9a1d08f..48ec229  master -> master