Cachet 3.x is under development. Learn more.

Tighten’s Jigsaw, GitHub Pages and GitHub Actions

This blog was previously powered by Tighten’s Jigsaw project. Even though it’s no longer used, if you’re thinking about starting your own blog, I can still highly recommend it.

Since Jigsaw generates a static website, I chose to host it with GitHub Pages as a "user site" as they allow for top-level domains. GitHub Pages only serve static sites, but as Jigsaw is a dynamic system we need to generate the site. I was able to automate this process with GitHub Actions. It took some trial and error (read: a lot of wip commits) but I was finally able to make it work and wanted to share this setup as I think it’s pretty sweet.

I’ll break this down into two sections, the first, using Jigsaw and GitHub Pages and the second, publishing with GitHub Actions.

Jigsaw & GitHub Pages

Jigsaw provides instructions on how to use GitHub Pages, but it’s written for project sites and not user sites. If you’re not sure why this is a problem, it’s because user sites require that the site contents are in the master branch, whereas repository sites default to gh-pages - though that can be changed.

To get this working, I setup Jigsaw under a source branch which contains all of the code, assets and post content. I then push the contents of the build_production directory to the master branch, which GitHub happily serves up for you to read.

It’s a small change, but it may not immediately be obvious, so it’s worth clarifying!

Jigsaw & GitHub Actions

Jigsaw is really, really good but I’m lazy and don’t want to manually be building my website, committing and publishing it after each post.

Admittedly, it took me a lot of time to figure these steps out, but now I’m able to write a new post and have GitHub automatically build and publish it for me!

I created a new workflow under .github/workflows/build-publish.yml with the following:

name: Build & Publish

      - source
    - cron: "0 2 * * 1-5"

    runs-on: ubuntu-latest
    - uses: actions/checkout@v2
    - name: Install Composer Dependencies
      run: composer install --no-ansi --no-interaction --no-scripts --no-suggest --no-progress --prefer-dist
    - name: Install NPM Dependencies
      run: npm install
    - name: Build Site
      run: npm run production
    - name: Create CNAME File
      run: echo "" >> build_production/CNAME
    - name: Stage Files
      run: git add -f build_production
    - name: Commit files
      run: |
        git config --local "[email protected]"
        git config --local "GitHub Actions"
        git commit -m "Build for deploy"
    - name: Publish
      run: |
        git subtree split --prefix build_production -b master
        git push -f origin master:master
        git branch -D master

It may look complicated, but we can break it down.

  1. We tell GitHub to only run the workflow on pushes to the source branch. I also opted to automatically run the workflow at 2PM Monday - Friday, just in case 🤷🏻‍♂️
  2. Next we’re installing the Composer and NPM dependencies.
  3. Once the dependencies have been installed, we build the site using the production environment configuration.
  4. Optional! I’m using a CNAME on GitHub Pages so that I can use my own domain. I create a new file and fill it with the domain name I want to use.
  5. I forcefully stage the build_production directory, this is because Jigsaw defaults to ignoring build_* directories from Git and I don’t want to change this and accidentally publish it into the source branch.
  6. Next we configure Git’s email and name, I chose to use GitHub Actions as the author so that I know it’s an automatic commit. We then commit the contents with a pre-defined commit message.
  7. Finally, we’re going to publish the site by pushing the changes from within the GitHub Action itself! This is a bit complicated if you’re not confident with Git, but let’s try to keep it simple. We create a subtree of the build_production directory and pushing it into a temporary master branch. Now we can force push the branch to GitHub and clean up by deleting the master branch1.

And that’s it, I’m now able to write new blog posts without my laptop (using just the GitHub website) and have them immediately published for me.

Let me know @jbrooksuk if you’re using this technique or have any suggestions on how to improve it.


  1. Because we’re force-pushing to master from a branch which contains only one commit, all history is lost for the published website. This is a trade-off that I’m happy with because the real history is in the source directory.