NUS Hackers Wiki
NUS Hackers Wiki
  • NUS Hackers Wiki
  • Hackerschool
    • Virtual Machines and Linux
    • Beginners' Guide to the Terminal
      • Introduction to the Terminal
      • Modern Shell Tools
      • Shell Scripting
      • Real World Scripting
      • Resources
    • Self-Hosting: Three Easy Pieces
      • 1. Setting up your server
      • 2. Running Services
      • 3. Monitoring your server
    • Vim
    • Introduction to Zig
      • Language Basics
      • Error Handling
      • Memory Management
      • Working with C
      • Exploring comptime
    • CI/CD with Github Actions
      • Background
      • Basics of Github Actions
        • Target workflow
        • Running unit tests
        • Linting code
        • Deploying to Github Pages
      • Advanced use cases
        • Pollers
        • Github script
        • Executing third-party scripts
        • Reusable workflows
      • Cookbook
    • Lightning Git
      • Git Concepts
      • Getting Started with Git
      • Making your first commit
      • Branching
      • Merge Conflicts
      • Integrating remote repositories
      • Collaborative Workflows
      • Commit Manipulation and Reflog
      • Interactive rebasing
      • filter-repo
  • Orbital
    • JavaScript
      • Browser Developer Tools
      • Getting Started
      • Datatypes
      • Operators and Operations
      • Loops and Conditions
      • Functions
      • Strings
      • Arrays
      • HTML
        • Getting Started
        • Tag Attributes
        • HTML Forms
        • Browser Inspector
      • CSS
        • Selectors
        • Colors in CSS
        • Measurements in CSS
        • The Box Model
        • Adding Styles - Part 1
        • Adding Styles - Part 2
      • Working with the DOM
        • Querying the DOM - Selectors
        • Querying the DOM - Element Attributes
        • Querying the DOM - Element Styles
        • Events with JS and HTML
        • Exercise: Click Counter
        • Editing the DOM
        • Fetch Requests
        • Exercise: The NUSMods API
    • React
      • Setup
      • State
    • React Native
      • Setup
      • Intro to JSX
      • Basic Syntax
      • Handling UI
      • Props
      • State Management
    • Git
      • Setup
      • Command Glossary
      • Fundamental Concepts
        • Getting Started
        • Integrating Remote Repositories
        • Branching
        • Merge Conflicts
      • Collaborative Workflows
        • Fork and PR Workflow
        • Branch and PR Workflow
      • Advanced Concepts
        • Ignoring Files
        • Commit Message Conventions
        • Github Collaborators
        • CI/CD with Github Actions
        • Advanced Git Commands
      • FAQ
    • Telegram Bot
      • Creating a TeleBot
      • API Calls
      • Telebot Basics
      • Integrating API's
    • Relational Database
      • Database Overview
      • Database Design
      • Entity Relationship Diagram
      • SQL Basics & PostgreSQL
    • TypeScript
      • Types and Interfaces
      • Utility Types
      • Typing Component Props, Events, and Hooks
      • Why You Should Avoid Using any (and What to Do Instead)
      • TypeScript Tricks You’ll Use All the Time in React
Powered by GitBook
On this page
  • Constructing the workflow
  • Designing the workflow
  • Breaking it down
  • Conditional workflow trigger
  • build job
  • Publishing artifacts
  • deploy job
  • Permissions & secrets
  • Environments
  • Deploying the artifact
  • Visualizing the workflow
  • Verifying the workflow
Edit on GitHub
Export as PDF
  1. Hackerschool
  2. CI/CD with Github Actions
  3. Basics of Github Actions

Deploying to Github Pages

PreviousLinting codeNextAdvanced use cases

Last updated 1 month ago

The final step of our Target workflow is automatically deploying our application to Github Pages when it is merged into main. This is the CD of CI/CD!

However, as Github Actions does not express "merging into main" as an event, we will instead be thinking in terms of deploying when changes are pushed to main instead.

Github Pages is a free static site hosting option that integrates directly with Github repositories. You can read more about them .

Constructing the workflow

We first go through the same questions as Linting code:

  1. "When will it run?" — when changes are pushed to main

  2. "What will it do?" — compile and build the React project and publish the generated build files to Github Pages

  3. "Is this going to be a separate workflow? A separate job in the same workflow? Or just another step in the existing job?" — this will be a separate workflow because (a) the events that trigger it are different from ci.yml, and (b) it is not logically a part of the ci workflow

If we look at our answer for (2), you may notice that we are essentially describing two separate tasks:

  1. Compiling and building the React project

  2. Publishing the generated build files to Github Pages

While we can represent them as a single job, we would like to explore what it's like to design jobs that are dependent on one another and passing around artifacts in Github Actions.

We also realize that (3) reveals that we are no longer treating ci.yml as the full CI/CD pipeline, so you are free to rename the workflow!

Designing the workflow

# .github/workflows/cd.yml
name: Deploying to Github Pages
on:
  push:
    branches: [main]
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Fetch repository
        uses: actions/checkout@v4
      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'yarn'
      - name: Install dependencies
        run: |
          yarn
      - name: Building
        run: |
          NODE_ENV=production yarn build
      - name: Uploading production artifacts
        uses: actions/upload-pages-artifact@v3
        with:
          name: github-pages
          path: dist

  deploy:
    needs: build
    runs-on: ubuntu-latest
    permissions:
      pages: write
      id-token: write
    environment:
      name: github-pages
      url: ${{ steps.deployment.outputs.page_url }}
    steps:
      - name: Publishing production artifact
        id: deployment
        uses: actions/deploy-pages@v4
        with:
          artifact_name: github-pages

Breaking it down

What a mouthful! That's quite a lot of new steps and concepts. Fret not, we will be explaining each step individually.

Conditional workflow trigger

on:
  push:
    branches: [main]

build job

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Fetch repository
        uses: actions/checkout@v4
      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'yarn'
      - name: Install dependencies
        run: |
          yarn
      - name: Building
        run: |
          NODE_ENV=production yarn build
      - name: Uploading production artifacts
        uses: actions/upload-pages-artifact@v3
        with:
          name: github-pages
          path: dist

Similar to the jobs written in Running unit tests and Linting code, we will declare a job to

  1. Fetch the repository

  2. Setup Node.js

  3. Install project dependencies

  4. Build the production distribution

Doing so, we should now have a dist/ folder in our virtual machine runner for the build job.

Let's talk more about the final step of the build job.

Publishing artifacts

      - name: Uploading production artifacts
        uses: actions/upload-pages-artifact@v3
        with:
          name: github-pages
          path: dist

Artifacts are files or collections of files produced during a workflow run. These artifacts are stored on Github. You may wish to use artifacts for things like storing build logs, test results, binary or compressed files, etc.

For our use case, we want to publish the generated dist/ folder from the previous step in build as an artifact, so that our next job, deploy has access to the files and can publish them accordingly.

Thankfully, there is an existing action actions/upload-pages-artifact@v3 that handles this process, as we specify the name of the artifact generated github-pages and the path to the directory containing the static assets, i.e. dist/.

deploy job

  deploy:
    needs: build
    runs-on: ubuntu-latest

We then declare a new job, deploy that needs the build job. This is how we construct the dependency graph between jobs, requiring one to complete before the other can execute.

Permissions & secrets

    permissions:
      pages: write
      id-token: write

In order to ensure that we can successfully publish to Github Pages, we also need to modify the default permissions of GITHUB_TOKEN.

We talk about how secrets are stored and used in the Github script section!

Environments

    environment:
      name: github-pages
      url: ${{ steps.deployment.outputs.page_url }}

An environment in Github refers to a general deployment target that can be configured with protection rules and secrets. Essentially, they allow you to handle different stages of your project, like development, production, and in our case, github-pages.

These environments are displayed on the repository page.

For our scenario, we want to set the url of the environment to point to the output (page_url) of one of the job steps with step id deployment.

Deploying the artifact

    steps:
      - name: Publishing production artifact
        id: deployment
        uses: actions/deploy-pages@v4
        with:
          artifact_name: github-pages

Finally, we can start deploying the artifact we published earlier. We use the action actions/deploy-pages@v4 , targeting the artifact named github-pages, which we named earlier.

Visualizing the workflow

We now have a workflow with sequential jobs, with build generating the production build as an artifact, and deploy consuming that artifact and publishing it to Github Pages.

We can also visualize the process of uploading an artifact as such:

Verifying the workflow

As per usual, add cd.yml, create a commit, and push to main.

git add .github/workflows/cd.yml
git commit -m "Add CD workflow"
git push

This should already trigger the workflow to run. However, if you navigate to Actions and select the "Deploying the Github Pages" workflow, you will notice that it fails:

If you select the specific workflow run, go to the deploy job, and select the "Publishing production artifact" step, you will see the following error:

The last message tells us what went wrong:

Error: Failed to create deployment (status: 404) with build version 6dbc2327e55394cd2690908b1b23d14eddb4a3cb. Request ID 1481:16FC0C:39A4ED5:737FC5E:67EA91C2 Ensure GitHub Pages has been enabled: https://github.com/woojiahao-git-mastery/cicd-calculator/settings/pages

So, visit the URL provided (it is different from the one above!) and select "Github Actions" instead:

Finally, to re-run the workflow, go back to Actions and return to the failed workflow run. At the top right corner, you should see a button to "Re-run worflows", choose to re-run all jobs:

This time, you should see the following:

You can select the URL in the deploy job component and you should be greeted with the following UI:

Next up: we will be exploring some unique workflows in Github Actions!

Recall in previously, we mentioned that the events may be dictionaries instead when there may be more properties/conditions. In our scenario, we only want the workflow to deploy to Github Pages when we push to main. So, we can express this using the push event, and specify that it should only run when one of the branches (which includes main) is pushed to.

They are also a way to share data between jobs in a workflow. Recall in where we mentioned that steps in the same job share the same filesystem as they belong to the same job's virtual machine runner, but steps in different jobs do not share the same filesystem as they have completely separate filesystems, belonging to separate virtual machine runners. Artifacts are the way to bridge this gap.

So, after build runs, we would have an artifact called github-pages uploaded to Github and accessible to subsequent jobs. You can read more about artifacts on the .

GITHUB_TOKEN is a that is automatically created as a secret in all workflows. It has access to the current repository, and it expires after the workflow completes. are a way of storing sensitive information in an organization, repository, or repository environment.

Essentially, GITHUB_TOKEN allows steps in the job to have some access to the current repository. So, in order for the job to publish to Github Actions, we want to grant the token write access to both pages and id-token . More information about the various permissions of GITHUB_TOKEN can be found .

If you are interested to learn more about Github environments, read this !

Notice that we also give an additional id to the step, deployment. This allows the step to be accessible via ${{ steps.deployment }} and allows the outputs of the action to be accessible to the environment (seen above). ${{ ... }} is a way of declaring expressions in a workflow file. More information about expressions in Github Actions can be found .

Congratulations! You have successfully setup a traditional CI/CD pipeline using Github Actions! Play around with the workflows we have created!

🎊
here
Github Actions documentation
special access token
Secrets
here
guide
here
Anatomy of Github Actions
Workflow triggers