Workflows and Actions for GitHub Actions-based building of static sites for OMSF projects.
This repository contains several reusable GitHub Actions workflows and actions that facilitate deployment of static sites. This is aimed to support the workflows used by projects at the Open Molecular Software Foundation (OMSF). In particular, our projects had the following needs:
- Deploy static sites build with Hugo or Jekyll
- Provide preview builds of pull requests, including from forks
- Deploy to Cloudflare Pages
We decided on deploying to Cloudflare Pages because (1) it is unlikely that our needs will go beyond the free tier, (2) it made the preview builds very easy, and (3) it has excellent and easy-to-use user management.
Note that the requirement for preview builds of pull requests from forks is one of the things that makes the tooling here a little more complicated. We need access to secrets in order to deploy the preview site, and PRs from forks don't have access to secrets. But as an open and transparent organization, we wanted to encourage contributions from outsiders whenever possible.
Contents
- Overview: A brief explanation of how this all works.
- Preparing to Install: Setting up Cloudflare and GitHub, before adding workflows to your website repository.
- Installing for Hugo: How to install the workflows and actions for a Hugo-based website.
- Installing for Jekyll: How to install the workflows and actions for a Jekyll-based website.
The basic idea here is that there are three kinds of workflows that we need, which are triggered by different GitHub events:
- Deploying a PR preview: This happens when a pull request is opened or updated. We build the site according to the PR, and deploy it as a preview on Cloudflare. We create or update a comment on the PR that provides a link to the preview site.
- Deploying a main branch build: This happens when a commit is pushed to the main brain (including after merging a PR). Again, we build the site and deploy, although this time we create a deployment on the main site.
- Cleaning up after a PR: This happens when a PR is closed (including merged). We delete all the deployments from that branch except for the final one (left so that links from the comment will still be valid; potentially useful for tracking history.)
We also recommend a nightly build of the main branch to test with.
flowchart LR
A([PR opened/updated]) -- pull_request event --> B[build-pr.yaml]
B -- upload_artifact --> C[site.tar.gz]
C -- download_artifact --> D[stage-cloudflare.yaml]
B -- workflow_run event --> D
style D color:#f00,stroke:#f00,fill:#fee
Handling this event is split into 2 GitHub Actions Workflows. The basic idea is
that the site is built in the first workflow, which is triggered by a
pull_request
event. The resulting HTML is stored in an artifact. When the
build-pr
workflow has succeeded, the stage-cloudflare
workflow is triggered
by a workflow_run
event. The artifact is used to communicate between the
build-pr
and stage-cloudflare
workflows. The workflow_run
event has
access to repository secrets, and is marked in red to emphasize that. It can
therefore deploy to Cloudflare. But because it operates independently of the PR
(and never even does a checkout), the only thing a malicious PR could do is
deploy the preview.
flowchart LR
A([main branch commit]) -- push event --> B[build-push.yaml]
B -- upload_artifact --> C[site.tar.gz]
C -- download_artifact --> D[prod-cloudflare.yaml]
B -- workflow_run event --> D
style D color:#f00,stroke:#f00,fill:#fee
The deploying to the main branch uses nearly the same pattern as for PRs, although the deployment to production doesn't require interaction with a PR (via comments, etc) that the staging does.
flowchart LR
A([PR closed]) -- pull_request_target event --> B[cleanup-cloudflare.yaml]
style B color:#f00,stroke:#f00,fill:#fee
When a PR is closed (including by merge) we trigger the cleanup-cloudflare
workflow using the pull_request_target
event. This workflow deletes older
deployments from the branch when the PR is closed.
We are able to setup the required Cloudflare Pages project and required GitHub Secrets and Variables using OpenTofu/Terraform.
To do so, you would create an OpenTofu project and use the two modules in the module folder which would look like this:
locals {
cloudflare_account_id = "your-cloudflare-account-id"
cloudflare_project_name = "your-pages-project-name"
}
module "cloudflare_pages" {
source = "github.com/omsf/static-site-tools//modules/cloudflare_pages"
cloudflare_token_name = "my-pages-token"
cloudflare_project_name = local.cloudflare_project_name
cloudflare_account_id = local.cloudflare_account_id
# Optional: specify a custom compatibility date
cf_compat_date = "2024-01-01"
}
module "github_vars" {
source = "github.com/omsf/static-site-tools//modules/github_vars"
github_repository = "owner/repository-name"
cloudflare_account_id = local.cloudflare_account_id
cloudflare_token = "$(module.cloudflare_pages.cloudflare_token)"
cloudflare_project_name = local.cloudflare_project_name
# Optional: customize variable names
cloudflare_account_id_var_name = "CLOUDFLARE_ACCOUNT_ID"
cloudflare_token_var_name = "CLOUDFLARE_API_TOKEN"
cloudflare_project_name_var_name = "CLOUDFLARE_PROJECT_NAME"
}
Please note that we have a separate page documenting how to set up Cloudflare accounts and how to set up a Cloudflare Pages project.
You'll need to get several pieces of information from Cloudflare, which you will then add to GitHub as secrets/variables:
- Your Cloudflare account ID: Find this by going to the Cloudflare account, clicking on "Workers and Pages" in the left sidebar. The account ID can be found on the right. Cloudflare docs
- Your Cloudflare API token: To create an API token, click on "Manage Account" in the left sidebar, then "Account API Tokens". Click on "Create Token", and use the "Edit Cloudflare Workers" template. On the next page, you'll need to include "All zones from an account" and select the account (under "Zone resources"). Then click "Continue to summary" and then "Create Token". Cloudflare docs
- Your Cloudflare Pages project name: This is the name of the project you created in Cloudflare Pages. You'll see this on the dashboard as one of you applications, or on the Workers & Pages page.
Now that we've obtained the information from Cloudflare, we'll store it as repository level secrets/variables in GitHub.
To set secrets, go to the repository settings, and then click on "Secrets and Variables" in the left sidebar, then click on "Actions". Ensure that the "Secrets" tab is selected, and click on "New repository secret". GitHub docs
You'll need to add two secrets:
CLOUDFLARE_ACCOUNT_ID
: The Cloudflare account ID you obtained above.CLOUDFLARE_API_TOKEN
: The Cloudflare API token you obtained above.
Once you've added those two secrets, add the variables that will be used. Select the "Variables" tab, and click "New repository variable" to create a new variable. GitHub docs You'll need to add the following variables:
CLOUDFLARE_PROJECT_NAME
: The name of the Cloudflare Pages project you obtained above.