Deploying to GitHub Pages

Deploying to GitHub Pages

A guide to publishing static content

GitHub Pages is a way to freely host a static website. It supports HTTPS (so your site is always secure) and uses a content delivery network to ensure your site is always available globally and is responsive. It has a few restrictions but is ideal for a developer to host a personal website. It requires a GitHub Account but as developers we already have one of those, right?

In my Blogging with GitHub and Markdown article I discussed using Markdown to author content for your blog and in my Generating a Static Website Using Markdown article I showed how to use C#, Markdig, and RazorEngine.NetCore to convert the Markdown content to HTML.

In this follow up article, I will show how to publish the generated HTML content to GitHub Pages using Azure DevOps pipelines.

Basic Setup Process

The basic setup process for GitHub Pages is straight forwards. Follow the instructions below to get the initial minimal version of your site up and running:

  1. Create a repository called <your GitHub username>.github.io - this will be where content is published.
  2. Disable the default Jekyll site generation by adding an empty .nojekyll text file in the root of the repository. Jekyll is a great tool for building static websites, but I do not use it so disabling the default site generation makes publishing a little quicker. Feel free to skip this step if you want to use Jekyll.
  3. Create a basic index.html file in the root directory of the repository - this will be the front page to the site and provide links to other content. Also set up a 404 page by creating a 404.html file in the root directory to gracefully handle requests for missing content.
  4. [Optional] Set up a custom domain. I would recommend setting up both an Apex domain (i.e. sixsideddice.com) and a www subdomain (i.e. www.sixsideddice.com).

The Simplest Micro-frontend Architecture

My GitHub Pages repository contains all the content for this site. The home page and CSS theme are maintained directly in this repository but other content files, such as the Blog, are maintained in their own repositories (e.g. content for the Blog section is generated by the Blog repository). Each subcomponent has its own build pipeline that, when successful, publishes any generated content to the Pages repository.

To provide some consistency to the look and feel of the site, several simple Vanilla JavaScript Web Components are used to define sections such as the site header and footer. These components are also maintained in the Pages repository.

This is the simplest kind of Micro-frontend where each section of the site is largely independent and there is no composition of components (other that the simple header/footer) or communication between parts of the frontend. As such, it is easily extended and requiring that any new section of the site to be published to a new subfolder in the Pages repository.

How to Publish Content Using Azure DevOps Pipelines

We are going to use the git command line tools to integrate an Azure DevOps pipelines to publish content to our Pages repository. The pattern we will follow is described in the following steps:

  1. 'Build' the content using whatever build process is suitable for the component (MSBuild, npm, etc.)
  2. After the build step completes successfully, use git clone to create a copy of the Pages repository within the build servers file system.
  3. Copy the output HTML and any other static content to the appropriate folder in the local clone of the Pages repository.
  4. Add any new files using git add * and git commit.
  5. Push the changes to the Pages repository using git push.
  6. At this point, GitHub will detect that a change has occurred within the Pages repository and redeploy the new static content.

For these steps to work, we need to allow the pipeline to access the Pages repository. This is done by creating a personal access token that will be used by Azure DevOps to clone and commit changes to the Pages repository. The token should be given the public_repo permissions.

The best way to configure Azure DevOps to use the token is to create a Variable Group and Variable in the Azure DevOps Pipelines -> Library. This will allow the token to be securely consumed by any build pipelines. The variable should be marked as secure so that is cannot be read once it has been created. Make sure any pipelines that need to access the group are set up in the "Pipeline Permissions" tab. See How to use variables inside your Azure DevOps builds for more information on setting this up correctly.

Assuming the Variable Group created in the above step is called SharedVariables then a pipeline can access the variables using the variable group reference as shown in the YAML fragment below:

variables:
  - group: SharedVariables

Cloning the Pages repository can be achieved with the following PowerShell script which references the GitHubAccessToken and GitHubUsername variables created in the SharedVariables variable group:

- task: PowerShell@2
  displayName: Clone existing GitHub pages repository
  condition: succeeded()
  inputs:
    targetType: inline
    workingDirectory: '$(System.DefaultWorkingDirectory)'
    script: |
      write-host "current directory = $(get-location)"
      git clone https://$(GitHubUsername):$(GitHubAccessToken)@github.com/${GitHubUsername}/${GitHubUsername}.github.io.git --branch=main SiteSource --quiet
      if ($LastExitCode -gt 0)
      {
        write-host "Clone failed - check GitHubAccessToken is valid. Error code $LastExitCode"
      }      

After any output has been copied to the local copy of the repository the following PowerShell script can be used to push the changes and trigger a redeployment of the site:

- task: PowerShell@2
  displayName: Update GitHub pages repository
  condition: succeeded()
  inputs:
    targetType: inline
    workingDirectory: '$(System.DefaultWorkingDirectory)/SiteSource'
    script: |
      git config core.autocrlf false
      git config user.email $(GitHubUserEmailAddress)
      git config user.name $(GitHubUsername)
      git add *
      git commit --allow-empty -m "Update $(Build.BuildId)"      
      if ($LastEitCode -gt 0)
      {
        write-host "Commit failed. Error code $LastEitCode"
        [Environment]::Exit(1)
      }      

      git push --quiet
      if ($LastEitCode -gt 0)
      {
        write-host "Push failed. Error code $LastEitCode"
        [Environment]::Exit(1)
      }      

Conclusions

GitHub Pages is a great way to publish a static site and Azure DevOps provides build infrastructure to simplify generating content for the site.

Not only that but both these tools are free to use.