My solution consists of the following two core parts:
- Blog source repository, which manages the configuration of the blog and all .md source files for articles, and uses GitHub Action for automated deployment. It automatically generates a static site and pushes it to the GitHub Pages blog publishing repository.
- GitHub Pages blog publishing repository, which uses GitHub Pages to deploy the website. It can be accessed using a custom domain name through CNAME resolution.
Building a Blog with Hugo#
Hugo is a blog tool implemented in Go. It uses Markdown for article editing, generates static site files, supports rich theme configuration, and can also embed plugins such as comment systems using JavaScript for highly customization. In addition to Hugo, there are other options such as Gatsby, Jekyll, Hexo, Ghost, etc., which have similar implementations and usage. You can choose according to your preferences.
Installing Hugo#
I am using macOS, so I will install the hugo program using the recommended homebrew method from the official website. For other systems, please refer to the official documentation.
brew install hugo
After installation, use the following command to verify (check the version):
hugo version
Creating a Hugo Site#
hugo new site blog-demo
Configuring the Theme#
After creating our site, we need to configure the theme. The Hugo community has a wide range of themes to choose from. You can select your favorite style from the Themes menu on the official website, view the preview effect, and then enter the theme project repository. There are usually detailed installation and configuration instructions. I will use the "smol" theme that I am currently using as an example to demonstrate the configuration process.
cd blog-demo
git clone [email protected]:colorchestra/smol.git themes/smol
cd themes/smol
rm -rf .git
After initializing the basic configuration of the theme, we can configure the site details in the config.toml file. Please refer to the theme documentation for specific configuration options.
Refer to the content of config.toml:
theme = "smol"
Publishing a New Article#
hugo new posts/blog-test.md
Local Debugging of the Site#
Perform real-time debugging and preview locally.
hugo server
After running the server, we can access our local preview webpage through the browser at http://localhost:1313.
GITHUB PAGES Repository#
After setting up the GitHub Pages repository, you can configure your registered custom domain name to point to the URL generated by GitHub Pages in the settings. In addition, you need to change the baseURL in the config.toml file of the blog site configuration to your custom domain name.
Publishing the Blog with GitHub Pages#
We can now access our GitHub Pages page using a custom domain name. Currently, because the project repository is empty, accessing it will result in a 404 page.
The static website generated by Hugo is hosted using GitHub Pages, so we need to upload the static web page files generated by Hugo to the GitHub Pages project repository.
Manual Publishing#
hugo
cd public
By default, Hugo will store the generated static web page files in the public/ directory. We can initialize the public/ directory as a git repository and associate it with our clin003/blog_html remote repository to push our web page static files.
git init
git remote add origin [email protected]:baicaime/meBlog
git add .
git commit -m "debug"
git push origin main
Push to the GitHub Pages repository, wait a few minutes, and you can access your blog site using your custom domain name, just like when running
hugo server
for local debugging.
Automatic Publishing#
Because our blog is based on GitHub and GitHub Pages, we can use the official GitHub Action for CI automatic publishing.
GitHub Action is a continuous integration and continuous delivery (CI/CD) platform that can be used to automatically execute build, test, and deployment pipelines with simple configuration.
The configuration is in the repository directory
.github/workflows
with the .yml extension. My GitHub Action configuration is
deploy.yml
The example configuration for automatic publishing is as follows:
name: Deploy Hugo site to Pages
on:
push:
branches: [ "main" ]
workflow_dispatch:
permissions:
contents: read
pages: write
id-token: write
defaults:
run:
shell: bash
env:
NAME: BLOG_push
# Target repository format: username/repository name
TARGET_REPOSITORY_NAME: baicaime/meBlog
# Synchronization temporary directory (optional)
CLONE_DIR: tmp_public
# Build temporary directory (optional)
BUILD_DIR: tmp_build
# Configure git username
GIT_USERNAME: baicaime
jobs:
# This workflow contains a single job called "build"
build:
runs-on: ubuntu-latest
env:
HUGO_VERSION: 0.117.0
steps:
- name: Install Hugo CLI
run: |
wget -O ${{ runner.temp }}/hugo.deb https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_extended_${HUGO_VERSION}_linux-amd64.deb \
&& sudo dpkg -i ${{ runner.temp }}/hugo.deb
- uses: actions/checkout@v3
- name: Build with Hugo
env:
# For maximum backward compatibility with Hugo modules
HUGO_ENVIRONMENT: production
HUGO_ENV: production
run: |
hugo \
--minify \
--baseURL "${{ steps.pages.outputs.base_url }}/" \
-d ${{ env.BUILD_DIR }}
- name: Git Config
run: |
git config --global user.email "[email protected]"
git config --global user.name "${{ env.GIT_USERNAME }}"
echo "Git configuration completed"
- name: Git clone
run: |
echo "Synchronizing target repository (start)"
git clone --depth 1 https://github.com/${{ env.TARGET_REPOSITORY_NAME }}.git ${{ env.CLONE_DIR }} &> /dev/null
echo "Synchronizing target repository (completed)"
- name: Git push
run: |
cp -rf ${{ env.BUILD_DIR }}/* ${{ env.CLONE_DIR }}/
cd ${{ env.CLONE_DIR }}
echo "${{ github.event.head_commit.message }} `date +%FT%T%z`" > _pub_time.html
git add .
git commit --message "Update ${{ env.NAME }} from ${{ env.TARGET_REPOSITORY_NAME }} ${{ github.event.head_commit.message }}"
git push -f -q https://oauth2:${{ secrets.GIT_TOKEN }}@github.com/${{ env.TARGET_REPOSITORY_NAME }}.git main
echo "git push ${{ env.TARGET_REPOSITORY_NAME }} (completed)"
The "on" field represents the triggering conditions for the GitHub Action. I have set it to push and workflow_dispatch:
- push: When there is a push action in this project repository, execute the GitHub Action.
- workflow_dispatch: Can be manually called in the Action toolbar of the GitHub project repository.
The "jobs" field represents the tasks in the GitHub Action. We have set up a "build" task,
"runs-on" represents the GitHub Action runtime environment, and we have chosen ubuntu-latest.
The "build" task includes six main steps: Install Hugo CLI, Checkout, Build with Hugo, Git Config, Git clone, and Git push,
where "run" is the command to be executed, and "uses" is a plugin in GitHub Action. We used the actions/checkout plugin.
In the Checkout step, you can configure the "submodules" value as true in "with" to synchronize the submodules of the blog source repository (such as theme modules). Since we did not install Hugo themes using submodules, we do not need this parameter.
You need to modify the following in deploy.yml:#
TARGET_REPOSITORY_NAME: Change it to your own GitHub Pages repository, such as baicaime/meBlog
GIT_USERNAME: Change it to the username of your GitHub Pages repository
Because we need to push from the blog repository to an external GitHub Pages repository, specific permissions are required. You need to create a Token under your GitHub account at Settings - Developer settings - Personal access tokens.
The required permissions are:
Contents: read/write
After configuration, copy the generated Token (note: it will only appear once), and then add it as an environment variable named "GIT_TOKEN" in the
Settings - secrets and variables - Actions
of the blog source repository. This way, GitHub Action can access the Token.
Push Test#
After completing the above configuration, push the code to the repository to trigger the GitHub Action, which will automatically generate the blog pages and push them to the GitHub Pages repository.
After the GitHub Pages repository is updated, the official page deployment CI will be automatically triggered to publish the website.
Now, whenever we finish editing the blog content using familiar Markdown syntax locally, we only need to push the code and wait a few minutes to access the updated website using our custom domain name.
The above is the blog automatic deployment system I implemented using Hugo and GitHub Action. My implementation repository is in the baicaime/meBlog repository.