Getting started with Hugo and the plain-blog theme, on NearlyFreeSpeech.Net

Category: Computers

This guide will show you how to set up a Hugo website, using the plain-blog theme, on NearlyFreeSpeech.net.

It includes:

  • Installing Hugo on NearlyFreeSpeech.Net
  • Setting up a git repository on NearlyFreeSpeech.Net, which will automatically build your website after receiving updates.
  • Setting up a new hugo site on your local PC.
  • Sending changes from your local PC to NearlyFreeSpeech.Net, using git.
  • Troubleshooting if things don’t work.

I assume you’re proficient with SSH and a command line.

Let’s get started!

Install git.

See: Installing git.

Installing Hugo on NearlyFreeSpeech.Net

  1. SSH into your NearlyFreeSpeech.Net account.

  2. Download and install hugo.

    You can find the latest releases on GitHub.

    NearlyFreeSpeech.Net uses FreeBSD, so you’ll want hugo_x.yy.z_FreeBSD-64bit.zip.

    /home/public $ cd ~
    
    /home/private $ mkdir bin
    
    /home/private $ cd bin
    
    /home/private/bin $ fetch https://github.com/spf13/hugo/releases/download/v0.18.1/hugo_0.18.1_FreeBSD-64bit.zip
    hugo_0.18.1_FreeBSD-64bit.zip                 100% of 5256 kB 4516 kBps 00m01s
    
    /home/private/bin $ unzip hugo_0.18.1_FreeBSD-64bit.zip
    Archive:  hugo_0.18.1_FreeBSD-64bit.zip
     extracting: hugo_0.18.1_freebsd_amd64/hugo_0.18.1_freebsd_amd64
     extracting: hugo_0.18.1_freebsd_amd64/README.md
     extracting: hugo_0.18.1_freebsd_amd64/LICENSE.md
    
    /home/private/bin $ mv hugo_0.18.1_freebsd_amd64/hugo_0.18.1_freebsd_amd64 ./hugo
    
  3. Add ~/bin to your path by editing ~/.bashrc, inserting the following:

    export PATH=$PATH:$HOME/bin
    
  4. Refresh your shell environment by logging out and logging in again.

    /home/private/ $ exit
    exit
    
  5. When you log in again, you should be able to run hugo at a command line.

    You can test that by running hugo version:

    /home/public $ hugo version
    Hugo Static Site Generator v0.19-DEV BuildDate: 2016-12-30T17:06:12Z
    

Set up a git repository on NearlyFreeSpeech.Net

  1. SSH into your NearlyFreeSpeech.Net account.

  2. Create a new “bare” git repository:

    /home/public $ git init ~/mysite.git.bare --bare
    Initialized empty Git repository in /home/private/mysite.git.bare/
    

    Note the use of the --bare flag.

  3. Copy the contents from post-receive (below) into a new file called ~/mysite.git.bare/hooks/post-receive.

    • Note: spelling is important. “receive”, not “recieve”.
  4. Make post-receive file executable:

    /home/public $ chmod +x ~/mysite.git.bare/hooks/post-receive
    
  5. If you uploaded the post-receive script from Windows, use dos2unix to convert the line endings from Windows \r\n to Unix \n. (Actually, just do this anyway. It doesn’t hurt.)

    /home/public $ dos2unix ~/mysite.git.bare/hooks/post-receive
    
  6. Clone a working copy of the repository

    /home/public $ git clone ~/mysite.git.bare/ ~/mysite.git.checkout
    Cloning into '/home/private//mysite.git.checkout'...
    warning: You appear to have cloned an empty repository.
    done.
    
  • That concludes all the steps to be performed on the NearlyFreeSpeech server side. You can close your SSH session now.

On your computer

We’re done with the server side of things.

  1. Install Hugo on your local PC.

  2. Open a command-line terminal on your local PC.

    Note: I use Windows, so the following commands are all for Windows PowerShell.

    Adapt as required for your operating system.

  3. Make a new directory for your site:

    PS C:\Users\lws> cd G:
    PS G:\> mkdir mysite
    
    
        Directory: G:\
    
    
    Mode                LastWriteTime         Length Name
    ----                -------------         ------ ----
    d-----       2017-02-12     22:37                mysite
    
  4. Initialise a Git repository:

    PS G:\> cd mysite
    
    PS G:\mysite> git init .
    Initialized empty Git repository in G:/mysite/.git/
    
  5. Tell git where it should push the changes to.

    Note [email protected] should be replaced with your usual NearlyFreeSpeech.Net SSH login details.

    PS G:\mysite> git remote add deploy [email protected]:~/mysite.git.bare/
    
  6. Get a copy of the plain-blog theme:

    PS G:\mysite> git submodule add https://github.com/LiaungYip/plain-blog themes/plain-blog
    Cloning into 'G:/mysite/themes/plain-blog'...
    remote: Counting objects: 55, done.
    remote: Compressing objects: 100% (40/40), done.
    remote: Total 55 (delta 10), reused 54 (delta 9), pack-reused 0
    Unpacking objects: 100% (55/55), done.
    
  7. Copy the plain-blog example site:

    PS G:\mysite> copy -R .\themes\plain-blog\exampleSite\* .
    PS G:\mysite>
    
  8. Test that everything’s working:

    PS G:\mysite> hugo server
    Started building sites ...
    Built site for language en:
    0 draft content
    0 future content
    0 expired content
    4 regular pages created
    8 other pages created
    0 non-page files copied
    0 paginator pages created
    2 tags created
    2 categories created
    total in 18 ms
    Watching for changes in G:\mysite\{content,static,themes}
    Serving pages from memory
    Web Server is available at http://localhost:1313/ (bind address 127.0.0.1)
    Press Ctrl+C to stop
    

    Open a web browser to http://localhost:1313 and check that the website looks good.

  9. Once you’re happy, commit to git:

    PS G:\mysite> git add .
    PS G:\mysite> git commit -am "First deploy"
    [master (root-commit) ab3f26b] First deploy
     12 files changed, 152 insertions(+)
     create mode 100644 .gitmodules
     create mode 100644 README.md
     create mode 100644 config.toml
     create mode 160000 themes/plain-blog
     create mode 100644 content/_index.md
     create mode 100644 content/about.md
     create mode 100644 content/contact.md
     create mode 100644 content/post/beach.md
     create mode 100644 content/post/red_hammer.md
     create mode 100644 static/img/bettys_jetty_img_3119.jpg
     create mode 100644 static/img/penguin_island_img_3070.jpg
     create mode 100644 static/img/penguin_island_img_3095.jpg
    
  10. Then tell git to send the changes to the server:

    PS G:\mysite> git push deploy
    fatal: The current branch master has no upstream branch.
    To push the current branch and set the remote as upstream, use
    
    git push --set-upstream deploy master
    
  11. Oops, that didn’t work. Do what git told us to do:

    PS G:\mysite> git push --set-upstream deploy master
    Counting objects: 20, done.
    Delta compression using up to 8 threads.
    Compressing objects: 100% (18/18), done.
    Writing objects: 100% (20/20), 56.56 KiB | 0 bytes/s, done.
    Total 20 (delta 1), reused 0 (delta 0)
    remote: /home/private/mysite.git.checkout
    remote: /home/private/
    remote: pulling from bare repo into checkout repo
    remote: From /home/private//mysite.git.bare
    remote:  * [new branch]      master     -> origin/master
    remote: updating theme (git submodule update)
    remote: Submodule 'themes/plain-blog' (https://github.com/LiaungYip/plain-blog.git) registered for path 'themes/plain-blog'
    remote: Cloning into '/home/private/mysite.git.checkout/themes/plain-blog'...
    remote: Submodule path 'themes/plain-blog': checked out '037afc09e8fe895e47fd92f44232e4493f4e6657'
    remote: building site with hugo
    remote: Started building sites ...
    remote: Built site for language en:
    remote: 0 draft content
    remote: 0 future content
    remote: 0 expired content
    remote: 4 regular pages created
    remote: 8 other pages created
    remote: 0 non-page files copied
    remote: 0 paginator pages created
    remote: 2 tags created
    remote: 2 categories created
    remote: total in 79 ms
    remote: building site, with drafts
    remote: Started building sites ...
    remote: Built site for language en:
    remote: 0 draft content
    remote: 0 future content
    remote: 0 expired content
    remote: 4 regular pages created
    remote: 8 other pages created
    remote: 0 non-page files copied
    remote: 0 paginator pages created
    remote: 2 tags created
    remote: 2 categories created
    remote: total in 61 ms
    Branch master set up to track remote branch master from deploy.
    To ssh.phx.nearlyfreespeech.net:~/mysite.git.bare/
     * [new branch]      master -> master
    PS G:\mysite>    
    

We have pushed our changes up to our NearlyFreeSpeech.net server!

The post-recieve hook automatically took care of everything.

  • It built the site to a temporary directory.
  • It copied the temporary directory to the final destination, /home/public/mysite/.
  • It also built a copy of the site with drafts enabled, and put that in /home/public/mysite-draft/.

You should now be able to go to example.org/mysite and see your website!

Making more changes to your website

Once you have everything set up, it’s easy to make changes to your website:

  1. Change your files. (In this example, we’ve made some changes to /content/posts/red_hammer.md and made a new file /content/posts/yellow_lemon.md.)

  2. Check what git thinks has changed:

    PS C:\Users\lws> cd G:/mysite
    
    PS G:\mysite> git status
    On branch master
    Your branch is up-to-date with 'deploy/master'.
    Untracked files:
      (use "git add <file>..." to include in what will be committed)
    
            content/post/yellow_lemon.md
    
    nothing added to commit but untracked files present (use "git add" to track)
    PS G:\mysite> git status
    On branch master
    Your branch is up-to-date with 'deploy/master'.
    Changes not staged for commit:
      (use "git add <file>..." to update what will be committed)
      (use "git checkout -- <file>..." to discard changes in working directory)
    
            modified:   content/post/red_hammer.md
    
    Untracked files:
      (use "git add <file>..." to include in what will be committed)
    
            content/post/yellow_lemon.md
    
    no changes added to commit (use "git add" and/or "git commit -a")
    

    It’s telling us that /content/posts/yellow_lemon.md is “untracked”, which means it’s new.

    It’s also telling us that /content/posts/red_hammer.md has “changes not staged for commit”, i.e. we’ve modified it.

  3. Stage the changes for commit:

    PS G:\mysite> git add .
    PS G:\mysite> git status
    On branch master
    Your branch is up-to-date with 'deploy/master'.
    Changes to be committed:
      (use "git reset HEAD <file>..." to unstage)
    
            modified:   content/post/red_hammer.md
            new file:   content/post/yellow_lemon.md
    
  4. Commit the changes:

    PS G:\mysite> git commit -am "Second deploy"
    [master 7dac8d9] Second deploy
     2 files changed, 8 insertions(+)
     create mode 100644 content/post/yellow_lemon.md
    
  5. Send the changes to the server

    PS G:\mysite> git commit -am "Second deploy"
    [master 7dac8d9] Second deploy
     2 files changed, 8 insertions(+)
     create mode 100644 content/post/yellow_lemon.md
    
    PS G:\mysite> git push deploy
    Counting objects: 5, done.
    Delta compression using up to 8 threads.
    Compressing objects: 100% (5/5), done.
    Writing objects: 100% (5/5), 485 bytes | 0 bytes/s, done.
    Total 5 (delta 3), reused 0 (delta 0)
    remote: /home/private/mysite.git.checkout
    remote: /home/private/
    remote: pulling from bare repo into checkout repo
    remote: From /home/private//mysite.git.bare
    remote:    238794e..7dac8d9  master     -> origin/master
    remote: Updating 238794e..7dac8d9
    remote: Fast-forward
    remote:  content/post/red_hammer.md   | 1 +
    remote:  content/post/yellow_lemon.md | 7 +++++++
    remote:  2 files changed, 8 insertions(+)
    remote:  create mode 100644 content/post/yellow_lemon.md
    remote: updating theme (git submodule update)
    remote: building site with hugo
    remote: Started building sites ...
    remote: Built site for language en:
    remote: 0 draft content
    remote: 0 future content
    remote: 0 expired content
    remote: 5 regular pages created
    remote: 8 other pages created
    remote: 0 non-page files copied
    remote: 0 paginator pages created
    remote: 2 tags created
    remote: 2 categories created
    remote: total in 1082 ms
    remote: building site, with drafts
    remote: Started building sites ...
    remote: Built site for language en:
    remote: 0 draft content
    remote: 0 future content
    remote: 0 expired content
    remote: 5 regular pages created
    remote: 8 other pages created
    remote: 0 non-page files copied
    remote: 0 paginator pages created
    remote: 2 tags created
    remote: 2 categories created
    remote: total in 64 ms
    To ssh.phx.nearlyfreespeech.net:~/mysite.git.bare/
       238794e..7dac8d9  master -> master
    

Everything worked, and you can now go to example.com/mysite/ and see your changes!

Troubleshooting

My website looks terrible! The stylesheet seems to be missing!

Did you customise the example config.toml from plain-blog, following the instructions in the comments? Particularly the instructions for baseURL and canonifyURLs.

Did you customise the post-recieve script, replacing example.org/mysite with your actual web address?

Check these lines:

# Also configure the hugo -b flag for baseurl to match
PUBLIC_WWW=/home/public/mysite
                        ^^^^^^

# Also configure the hugo -b flag for baseurl to match
DRAFT_WWW=/home/public/mysite-draft
                       ^^^^^^^^^^^^

echo "building site with hugo"
/home/private/go/bin/hugo -s $CHECKED_OUT_GIT_REPO \
-d $TMP_BUILD_DESTINATION -b https://example.org/mysite
                             ^^^^^^^^^^^^^^^^^^^^^^^^^^
                             Note: http vs https!

echo "building site, with drafts"
/home/private/go/bin/hugo -D -s $CHECKED_OUT_GIT_REPO \
-d $TMP_BUILD_DESTINATION -b https://example.org/mysite-draft
                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
                             Note: http vs https!

git push didn’t work!

You might need to set up passwordless SSH login.

post-receive

You will need to customise this script to reflect your actual domain name, base URL, and folder locations.

#!/usr/bin/env bash
#BARE_GIT_REPO=/home/private/mysite.git.bare
CHECKED_OUT_GIT_REPO=/home/private/mysite.git.checkout
TMP_BUILD_DESTINATION=/home/private/tmp/mysite.public

# Also configure the hugo -b flag for baseurl to match
PUBLIC_WWW=/home/public/mysite

# Also configure the hugo -b flag for baseurl to match
DRAFT_WWW=/home/public/mysite-draft

echo $CHECKED_OUT_GIT_REPO
echo $HOME

echo "pulling from bare repo into checkout repo"
cd $CHECKED_OUT_GIT_REPO
# We're going into a working copy, from a hook in a bare copy. So GIT_DIR is set to ".".
# This causes the mysterious "fatal: Not a git repository: '.'    " error.
# http://stackoverflow.com/questions/4630704/receiving-fatal-not-a-git-repository-when-attempting-to-remote-add-a-git-repo
# +1 for mentioning GIT_DIR. within hooks in a bare repo, GIT_DIR is set to '.' instead of '.git'
export GIT_DIR=.git
git pull

echo "updating theme (git submodule update)"
git submodule update --init

echo "building site with hugo"
/home/private/go/bin/hugo -s $CHECKED_OUT_GIT_REPO -d $TMP_BUILD_DESTINATION -b https://example.org/mysite
# Delete old version of public files.
# Note - some other scripts just ask hugo to overwrite what's here, but hugo won't delete files that already exist.
# So old, obsolete files build up over time.
rm -Rf $PUBLIC_WWW
# Move newly built website to public location
mv $TMP_BUILD_DESTINATION $PUBLIC_WWW

echo "building site, with drafts"
/home/private/go/bin/hugo -D -s $CHECKED_OUT_GIT_REPO -d $TMP_BUILD_DESTINATION -b https://example.org/mysite-draft
rm -Rf $DRAFT_WWW
mv $TMP_BUILD_DESTINATION $DRAFT_WWW


exit