favicon here hometagsblogmicrobio cvtech cvgpg keys

How To Deploy A Webpage to GitHub Pages

#webpages #ci #static-site-generators

uncomfyhalomacro | 2024-11-15 | reading time: ~8min

There are many existing documentation about deploying webpages to GitHub pages.

This guide is just a guide for those that are interested in a very manual setup.

There is already a full documentation for GitHub Pages if you want the full doc experience.

Also, readers are expected that they are competent enough to know basic Git commands.

Commands used here are for Bash shell.


Deploying a small HTML file§

If you are not yet familiar with how the CI works, do not worry as you will notice that writing CI workflows as basic as this is not that really difficult.

To start with, you have to do the following as prerequisites.

  1. Create a GitHub Account.
  2. Create a repository.
  3. Clone the new repository locally.

The full commands starting for step 3 are

git clone https://github.com/uncomfyhalomacro/simple-site
cd simple-site

Adjust the URL to your repository. If you prefer SSH, the full commands are

git clone git@github.com:uncomfyhalomacro/simple-site.git
cd simple-site

One can check that the remote for the repository is set properly by running the following command.

git remote -v

By default, it's named as origin. Your output could look like this

origin	git@github.com:uncomfyhalomacro/simple-site.git (fetch)
origin	git@github.com:uncomfyhalomacro/simple-site.git (push)

Now that everything is setup, let's create an index.html at the root directory of the project.

touch index.html

Open and edit it with your editor. I use kakoune.

Copy this simple HTML code below to your new file index.html.

<!DOCTYPE html>
<html lang="en">
<body>
Hello World!
</body>
</html>

This should suffice.

Setting Up Your PERSONAL ACCESS TOKEN§

To setup your Personal Access Token (PAT), you must be logged in to your GitHub account. Then head over to https://github.com/settings/apps.

You should see an option "Personal access tokens". Click that button.

You should get a drop down with some options. Click "Tokens (classic)".

In the upper-right, you can see the "Generate new token" button. Click that.

You should get a drop down with some options. Click "Generate new token (classic)".

You will be redirected to either enter your password or TOTP for 2FA. Please enter what was asked.

Name your TOKEN to whatever you want e.g. "simple-site". Feel free to set the expiration date of the TOKEN.

You will also see a lot of scopes. Just select the one with [ ] repo Full control of private repositories.

This will give full control for both private and public repositories that you have.

Now on the bottom-right, click the "Generate token" button. You will be redirected with a one time copy of the token. Copy or write it down somewhere safe.

⚠️ Be careful not to share your token once it's generated.

Setting Up GitHub Workflows§

Now, in your local repository, create a directory .github/workflows/.

mkdir -p .github/workflows/

Create a new file called pubish.yml at .github/workflows/.

touch .github/workflows/publish.yml

Copy the following code snippet to your publish.yml.

name: Publish
permissions: write-all
on: push

jobs:
  build:
    runs-on: ubuntu-latest
    env:
      TOKEN: ${{ secrets.TOKEN }}
    steps:
    - name: Checkout
      uses: actions/checkout@v4
    - name: Publish static site
      run: |
        mkdir -p public
        pushd public
        cp ../index.html index.html
        touch .nojekyll
        git init
        git branch -m gh-pages
        git config --global user.email "youremail"
        git config --global user.name "yourusername"
        git remote add origin "https://yourusername:$TOKEN@github.com/yourusername/simple-site.git"
        git add -A
        git commit -m "update site page"
        git push --force -u origin gh-pages

Ensure you have replaced the following

  • "youremail" should be your email address
  • "yourusername" should be your username e.g. username for GitHub account

Be careful with the indentations as YAML can be quite confusing.

Now before pushing to your remote repository, you have to set your TOKEN as a GitHub secret in your repository. You should head to the link like

https://github.com/yourusername/simple-site/settings/secrets/actions

Replace first the "yourusername" with your username on GitHub before copying it and pasting it on your browser.

You will see two options once you head to the webpage

  • Environment secrets
  • Repository secrets

Choose the one with Repository secrets and click "New repository secret". You will be redirected to a page where you will input your TOKEN. Set the name as "TOKEN". Then paste the value of your PERSONAL ACCESS TOKEN.

Once set, click "Add secret" button.

Lastly, add and commit your local changes and push it to your remote repository.

git add -A
git commit -m 'initial commit'
git push -u origin main

Your site will now be at https://yourgithubusername.github.io/simple-site where yourgithubusername is your GitHub username. You should see a "Hello World!" on your browser.

See the source repository for how we did it at https://github.com/uncomfyhalomacro/simple-site.

Convenient Static Site Generators§

At this point, you already know how to setup and deploy a webpage. However, writing a complex webpage with pure HTML can be tedious.

That's where static site generators come into the picture.

There are many popular static site generators to choose from such as

For this part of the tutorial, you are going to use zola, a simple static site generator written in Rust.

First create a new repository named "ssg-site".

Clone it locally and change directory into the ssg-site directory.

An example command will look like this. This uses SSH as the remote repository. Adjust to your configuration.

git clone git@github.com:yourusername/ssg-site.git

Replace "yourusername" with your GitHub username.

Then you must install zola. Follow installation instructions here.

Initialising Zola§

Run the following command at the root directory of the project.

zola init .

It will ask you the following questions.

For the first question

What is the URL of your site? (https://example.com):

You should input the following format https://yourusername.github.io/ssg-site where "yourusername" is your GitHub username.

The next question will be

Do you want to enable Sass compilation?

Type "Y" then press Enter.

The third question will be

Do you want to enable syntax highlighting?

Type "Y" then press Enter.

The last question will be

Do you want to build a search index of the content?

Feel free to type "Y" or "N". It does not matter yet, unless in the future, you want your site to have a search bar.

You have finally setup your Zola configuration. Now try running the command

zola serve

It should serve your webpage locally at http://127.0.0.1:1111. It might change if another has already parked that port.

But our site is still bland. Let's proceed adding a theme.

Setting Up Zola Theme§

Zola has many cool themes. You can check the theme collection at https://www.getzola.org/themes/.

Let's use anemone, one of my favourite themes in Zola.

At the root directory of your project, run the following command.

git submodule add "https://github.com/Speyll/anemone" themes/anemone

This should create a git submodule in your project.

Now open and edit your config.toml. It should look like the following.

# The URL the site will be built for
base_url = "https://yourusername.github.io/ssg-site"

# Whether to automatically compile all Sass files in the sass directory
compile_sass = true

# Whether to build a search index to be used later on by a JavaScript library
build_search_index = false

theme = "anemone"

[markdown]
# Whether to do syntax highlighting
# Theme can be customised by setting the `highlight_theme` variable to a theme supported by Zola
highlight_code = true

[extra]
# Put all your custom variables here
toc = true
display_author = true
list_pages = true
header_nav = [
  { url = "/", name_en = "/home/"}
]

Replace "yourusername" with your GitHub username. Save the file.

Your webpage will reload and it will be an empty page with a footer. The colors are new as well because of the theme.

Adding some content§

Now that our theme is up, let's add some content. At the root directory of your project, head to content directory. Create a new markdown file _index.md. Now copy and paste this Lorem ipsum to _index.md file.

+++
title = "Lorem Ipsum"
+++

# Lorem Ipsum

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat
non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
## Dolor sit amet

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat
non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

### Consectetur adipiscing elit

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat
non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

Then run the following commands.

cp _index.md 1.md

To understand what these files do, _index.md serves as the "home" page. It will list our posts such as 1.md.

Now try to edit _index.md such as an introduction about yourself and what you do.

Then edit 1.md with your new goals in life or tips and tricks.

Once satisfied, let's finally deploy your site to GitHub Pages.

Deploying to GitHub Pages§

At this point, you can either regenerate a new TOKEN or use the previous one you have created from simple-site. Let's just assume you already have a TOKEN.

First, run this command at the root directory of your project.

echo "public" >> .gitignore

This should ignore the generated public directory. You should always do this because otherwise, you will end up having duplicated files unless you have other plans. The public directory is only created when you run this command.

zola build

Next, add the new TOKEN as a Repository secret. You can repeat the steps you did on how to add a new TOKEN to your repository like in the simple-site.

Here is the fun part. Do the following commands.

mkdir -p .github/workflows/
touch .github/workflows/publish.yml

Then edit your publish.yml file which is located at .github/workflows/ with the following code.

name: Publish
permissions: write-all
on: push

jobs:
  build:
    runs-on: ubuntu-latest
    container: opensuse/tumbleweed:latest
    env:
      TOKEN: ${{ secrets.TOKEN }}
    steps:
    - name: Checkout
      uses: actions/checkout@v4
    - name: Install dependencies
      run: |
        zypper --non-interactive in zola git
    - name: Generate static site
      run: |
        rm -rf themes/
        mkdir -p themes/
        pushd themes/
        git clone --depth 1 https://github.com/Speyll/anemone
        popd
        zola build
    - name: Publish static site
      run: |
        pushd public
        touch .nojekyll
        echo "uncomfyhalomacro.pl" >> CNAME
        git init
        git branch -m gh-pages
        git config --global user.email "youremail"
        git config --global user.name "yourusername"
        git remote add origin "https://yourusername:$TOKEN@github.com/yourusername/ssg-site.git"
        git add -A
        git commit -m "update site page"
        git push --force -u origin gh-pages

Ensure you change "yourusername" to your GitHub username and "youremail" to the email you are using for your GitHub account.

Save the file. Now run the following command to add and commit your changes, then push.

git add -A
git commit -m 'my first blog'
git push -u origin main

Notice that in the YAML file, it uses a custom image. That's because that linux distribution image, Tumbleweed, already has an updated version of zola.

Once your site has been generated in the GitHub actions, you can head over to this link format yourusername.github.io/ssg-site where "yourusername" is your GitHub username.

Feel free to use this or learn and use other SSGs to prettify your blogs and web pages!

FAQ§

I noticed that you didn't use a GitHub action plugin shalzz/zola-deploy-action@master. Why?

If you know the process, then you can trust yourself more than some random plugin. Not saying they could not be trusted but you have to weigh your risks

  1. You already know how to deploy, then you are more sure that you can trust yourself.
  2. Trust is a spectrum.

Therefore, I don't need someone's tool if I already know how to do things. It's for the security side of things since GitHub actions can be a potential attack surface.


Okay that's a wrap. Feel free to reach out to me in my preferred communication channels 🤪

Articles from blogs I follow around the net

reqwest v0.13 - rustls by default

To end out the year, here comes a new major release of reqwest, the opinionated higher-level HTTP client for Rust. We don’t really need major breaking versions to keep providing value. Improvements keep coming all the time. But we did need one to make one…

via seanmonstarDecember 30, 2025

I hope generative AI does away with SEO

Have you ever searched for something on Google and found the first one, two, or three blog posts to be utter nonsense? That's because these blog posts have been optimized not for human consumption, but rather to entertain the search engine ranking algorith…

via pcloadletterDecember 30, 2025

Merry Christmas, Ya Filthy Animals (2025)

It’s my last day of writing for the year, so I’m going to try keep this one quick – it was knocked out over three hours, so I hope you can forgive me if it’s a bit clumsier than my usual writing. For some strange reason, one of the few clear memories I hav…

via LudicityDecember 27, 2025

ColdFusion++ Christmas Campaign: Catching a Coordinated Callback Calamity

UPDATE: Further analysis revealed the ColdFusion campaign represents a small fraction of a much larger operation. The two primary IPs (134.122.136.119, 134.122.136.96) generated over 2.5 million requests targeting 767 distinct CVEs across 47+ technology st…

via GreyNoise LabsDecember 26, 2025

Are people migrating away from GitHub?

I noticed some people migrating away from GitHub recently. I was curious to understand the rationale. Is it a blip or is it a sign of prolonged exodus?

via Rob O'Leary | BlogDecember 22, 2025

Status update, December 2025

Hi all! This month the new KMS plane color pipeline API has finally been merged! It took multiple years and continued work and review by engineers from multiple organizations, but at last we managed to push it over the finish line. This new API exposes to …

via emersionDecember 21, 2025

The Revolution Will Not Make the Hacker News Front Page

(with apologies to Gil Scott-Heron) If you get all of your important technology news from “content aggregators” like Hacker News, Lobste.rs, and most subreddits, you might be totally unaware of the important but boring infrastructure work happening largely…

via Dhole MomentsDecember 18, 2025

Yep, Passkeys Still Have Problems

It's now late into 2025, and just over a year since I wrote my last post on Passkeys. The prevailing dialogue that I see from thought leaders is "addressing common misconceptions" around Passkeys, the implication being that "you just don't understand it co…

via Firstyear's blog-a-logDecember 17, 2025

context—Odin's Most Misunderstood Feature

Even with the documentation on the topic, many people completely misunderstand what the context system is for, and what problem it actually solves. For those not familiar with Odin, in each scope, there is an implicit value named context. This context vari…

via Articles on gingerBillDecember 15, 2025

Announcing ic-dbms 0.1.0

What if I told you that this code: use candid::CandidType; use ic_dbms_api::prelude::{Text, Uint32}; use ic_dbms_canister::prelude::{DbmsCanister, Table}; use serde::Deserialize; #[derive(Debug, Table, CandidType, Deserialize, Clone, PartialEq, Eq…

via Christian Visintin BlogDecember 13, 2025

Theme selector

Two weeks ago I added dark mode to this website. It was late one night and I was revisiting an article and my eyes were tired, so that was that. It was based solely on system dark mode settings, and I started using some more nice, modern CSS features like …

via macwright.comDecember 09, 2025

OpenAI employees… are you okay?

You might have seen an article making the rounds this week, about a young man who ended his life after ChatGPT encouraged him to do so. The chat logs are really upsetting. Someone two degrees removed from me took their life a few weeks ago. A close friend …

via Drew DeVault's blogNovember 08, 2025

Comfort of Wabi Sabi

Comfort of accepting used things

via Ishan WritesOctober 25, 2025

i'm bored, so here's a useless 0day

i either want my US$2.5k professional-grade device backdoored or not at all

via maia blogAugust 20, 2025

Testing multiple versions of Python in parallel

Daniel Roy Greenfeld wrote about how to test your code for multiple versions of Python using `uv`. I follow up with a small improvement to the Makefile.

via Technically PersonalJuly 21, 2025

LLDB's TypeSystems Part 2: PDB

In my previous post, I described implementing PDB parsing as a can of worms. That might have been a bit of an understatement. PDB has been one "oh, it's gonna be twice as much work as I thought" after another. Implementing it has revealed many of the same …

via Cracking the ShellJuly 07, 2025

#Rx Writing Challenge 2025

This is a short reflection on my experience of the recent writing challenge I took part in. Over the past two weeks, I have participated in the #RxWritingChallenge 1—a daily, 30-minute writing group starting at 9 AM every morning. Surrounded by fellow doct…

via Ul-lingaApril 05, 2025

My coffee workflow

My coffee workflow by Clement Delafargue on April 1, 2025 Tagged as: coffee, espresso, flair58, v60. It is my first April cools’ and I guess I could start by talking about coffee. If you’ve seen me in person, it won’t be a surprise, I guess. This po…

via Clément Delafargue - RSS feedApril 01, 2025

Generated by openring-rs

favicon here hometagsblogmicrobio cvtech cvgpg keys