Frontend Development 10 min read

Building a GitHub‑Based Static CMS with Editor‑Publish‑Developer Separation

This article explains how to construct a static‑site CMS using GitHub repositories for content, code, and a Travis‑CI‑driven builder, detailing the architecture, deployment pipeline, and the Grunt‑based static generation process for front‑end developers.

Architect
Architect
Architect
Building a GitHub‑Based Static CMS with Editor‑Publish‑Developer Separation

System Architecture

The author outlines a CMS architecture that relies on three GitHub repositories: Content (stores editor‑generated JSON files), Code (holds themes, static generators, and assets), and Builder (runs on Travis CI to clone the other repos, execute build scripts, and push the generated static site to GitHub Pages).

Repository Roles

Content : JSON files produced by the editor, consumed by front‑end frameworks such as Backbone, Angular, or React to render a SPA.

Code : Contains the static‑site generator, themes, and any supporting resources.

Builder : Holds Travis‑CI scripts that clone the Content and Code repos, run the generator, and commit the output.

Optional Services

Additional services can be added when needed, for example an Extend Service for search (e.g., Python whoosh with Flask) and an Editor service that could be built with Electron + Node.js and the GitHub API V3 to upload JSON content.

Pipeline Overview

Editor publishes content, which is uploaded to the Content repo via the GitHub API.

A webhook detects changes in the Content repo and triggers the Builder on Travis CI.

The Builder configures Git, clones Content and Code, runs the build commands, and generates the new static files.

The Builder commits the generated files and pushes them to the gh-pages branch.

Builder: Deployment Script

Travis CI can encrypt a deployment key:

travis encrypt-file ssh_key --add

The encrypted key is stored in .travis.yml . The deployment script is executed in the after_success phase:

after_success:
  - test $TRAVIS_PULL_REQUEST == "false" && test $TRAVIS_BRANCH == "master" && bash deploy.sh

The deploy.sh script performs the following steps (shown in a simplified form):

#!/bin/bash
set -o errexit -o nounset
rev=$(git rev-parse --short HEAD)
cd stage/

git init
git config user.name "Robot"
git config user.email "[email protected]"

git remote add upstream "https://[email protected]/phodal-archive/echeveria-deploy.git"
git fetch upstream
git reset upstream/gh-pages

git clone https://github.com/phodal-archive/echeveria-deploy code
git clone https://github.com/phodal-archive/echeveria-content content

cp -a content/contents code/content

cd code
npm install
npm install grunt-cli -g
grunt
mv dest/* ../
cd ../
rm -rf code content

touch .
if [ ! -f CNAME ]; then
  echo "deploy.baimizhou.net" > CNAME
fi

git add -A .
git commit -m "rebuild pages at ${rev}"
git push -q upstream HEAD:gh-pages

The core of the build is the grunt task, which runs grunt.registerTask('default', ['clean', 'assemble', 'copy']) to generate the static site.

Code: Static Site Generation

The static site is generated using Assemble , a Node.js/Grunt based generator. The configuration in Gruntfile.js defines two targets:

assemble: {
  options: {
    flatten: true,
    partials: ['templates/includes/*.hbs'],
    layoutdir: 'templates/layouts',
    data: 'content/blogs.json',
    layout: 'default.hbs'
  },
  site: {
    files: {'dest/': ['templates/*.hbs']}
  },
  blogs: {
    options: {
      flatten: true,
      layoutdir: 'templates/layouts',
      data: 'content/*.json',
      partials: ['templates/includes/*.hbs'],
      pages: pages
    },
    files: [{ dest: './dest/blog/', src: '!*' }]
  }
}

The site target builds general site pages, while the blogs target creates HTML files from each JSON blog entry, placing them under dest/blog/ . The resulting directory structure includes HTML files, CSS, JavaScript, and assets ready for deployment.

Editor‑Publish‑Developer Separation

By separating editing, publishing, and development, the workflow eliminates the need for a dynamic server; all content is static and can be hosted on any static‑file service (e.g., CloudFront, S3, Nginx). This reduces runtime errors, simplifies debugging, and allows easy rollback by reverting the code repository.

Testing the build tools and generators is essential, and because the output is static, troubleshooting is straightforward.

Source: SegmentFault article

CI/CDNode.jsCMSGitHubGruntstatic site
Architect
Written by

Architect

Professional architect sharing high‑quality architecture insights. Topics include high‑availability, high‑performance, high‑stability architectures, big data, machine learning, Java, system and distributed architecture, AI, and practical large‑scale architecture case studies. Open to ideas‑driven architects who enjoy sharing and learning.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.