GitHub Pages provides hosting for static files by serving a branch (e.g. gh-pages) of the respective repository. GitHub Actions can be used to automate deployments, avoiding the hassle of having to update that branch manually when the main branch (typically master) changes.
The idea of using other people’s actions made me slightly uncomfortable, due to mild security concerns (handing full repo access to some unknown party) and the complexity of excessive abstraction (who wants to read documentation when they can write code instead…. ). So I set out to automate this myself – thinking it should be straightforward:
#!/usr/bin/env bash
set -eu
repo_uri="https://x-access-token:${GITHUB_TOKEN}@github.com/${GITHUB_REPOSITORY}.git"
remote_name="origin"
main_branch="master"
target_branch="gh-pages"
build_dir="dist"
cd "$GITHUB_WORKSPACE"
git config user.name "$GITHUB_ACTOR"
git config user.email "${GITHUB_ACTOR}@bots.github.com"
git checkout "$target_branch"
git rebase "${remote_name}/${main_branch}"
./bin/build "$build_dir"
git add "$build_dir"
git commit -m "updated GitHub Pages"
if [ $? -ne 0 ]; then
echo "nothing to commit"
exit 0
fi
git remote set-url "$remote_name" "$repo_uri" # includes access token
git push --force-with-lease "$remote_name" "$target_branch"
Here we run ./bin/build
(a placeholder for make
, npm start
or similar) to
generate build artifacts in the dist
directory and then commit them to the
gh-pages branch. The script relies on various
environment variables.
We can make GitHub Actions execute that script (./bin/update-gh-pages
) by
creating a workflow description (e.g. .github/workflows/pages.yml
):
name: GitHub Pages
on:
push:
branches:
- master
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- run: ./bin/update-gh-pages
env:
GITHUB_TOKEN: ${{ secrets.github_token }}
Note that we pass in GITHUB_TOKEN
, which is used in the script’s repo URI to
provide read/write access.
However, turns out that repo updates using GITHUB_TOKEN
do not trigger GitHub Pages builds.
(Which was not at all obvious… )
So we need to
generate a personal access token, add it
to respective repo’s
secrets
(via Settings → Secrets; named DEPLOY_TOKEN
here) and use that instead of
GITHUB_TOKEN
:
env:
GITHUB_TOKEN: ${{ secrets.github_token }}
DEPLOY_TOKEN: ${{ secrets.DEPLOY_TOKEN }}
repo_uri="https://x-access-token:${DEPLOY_TOKEN}@github.com/${GITHUB_REPOSITORY}.git"
With those adjustments, our automated repo updates finally result in GitHub Pages being published as well.
PS: In my case, the repo in question is a Node-based
application, so the workflow file includes a few
additional steps
:
- uses: actions/setup-node@v1
with:
node-version: 12
- uses: actions/cache@v1
with:
path: ~/.npm
key: ${{ runner.OS }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-build-${{ env.cache-name }}-
${{ runner.os }}-build-
${{ runner.os }}-
- run: npm install-ci-test