We currently use docfx
to publish the NUnit docs, because it has a lovely capability of generating docs for our API reference in addition to all our articles.
However, one thing I often yearn for – that I get on this Jekyll blog and some others I work with – is the ability to have a live preview within a pull request of what the generated site will look like.
Because the source code for the API docs is in the nunit
repository, and the docs live in the docs
repo, it’ll be a little extra complicated.
Nevertheless: Let’s make it happen.
The Conceptual Approach
- We pull the NUnit DLL and create the docfx site
- We zip up the site
- We create a custom GitHub environment to be associated with our deployment environment
- We manually update the statuses of that deployment
- We deploy to Netlify
- We post the link as a comment in the PR
The Build Steps
Below are the steps in our docfx build process – I’ll break them down one at a time:
- uses: actions/checkout@v4
name: Check out the code
Self-explanatory; we need our code if we’re going to build it.
- name: Get latest NUnit Asset dir
uses: dsaltares/fetch-gh-release-asset@master
with:
repo: 'nunit/nunit'
version: 'tags/v${{ env.NUNIT_VERSION_FOR_API_DOCS }}'
file: 'NUnit.Framework-${{ env.NUNIT_VERSION_FOR_API_DOCS }}.zip'
token: ${{ secrets.GITHUB_TOKEN }}
Because the source code that contains the DLL is in another repository, we use this GitHub action to pull the file via pulling a specific tag (currently hard-coded. I’ll get around to fixing that. Probably.)
- name: Unzip NUnit Asset zip file into its own directory
run: unzip NUnit.Framework-${{ env.NUNIT_VERSION_FOR_API_DOCS }}.zip -d ./NUnit.Framework-${{ env.NUNIT_VERSION_FOR_API_DOCS }}
- name: Copy NUnit Asset dir
run: mkdir ./code-output && cp -r ./NUnit.Framework-${{ env.NUNIT_VERSION_FOR_API_DOCS }}/bin/net6.0/* ./code-output
We unzip the asset file and copy it to the right spot.
- uses: "nunit/[email protected]"
name: Build with Docfx
with:
args: docs/docfx.json --warningsAsErrors true
With that in place, we run docfx
to mash everything up into one deployable site.
- name: zip site contents
run: zip -r _site.zip docs/_site/
- name: Archive site artifacts
uses: actions/upload-artifact@v3
with:
name: siteArtifact
path: _site.zip
We zip up and archive the site contents. This is just for reference.
- name: Start deployment (PR only)
if: ${{ github.ref != 'refs/heads/master'}}
uses: bobheadxi/deployments@v1
id: deployment
with:
env: preview_${{github.event.number}}
step: start
token: ${{ secrets.SEAN_PAT_TO_MANAGE_ENVIRONMENTS }}
This creates a new deployment environment for the PR number and creates a deployment to it with a status of started.
Note that it requires a personal access token that has the authority to manage environments.
- name: Deploy to Netlify (PR only)
if: ${{ github.ref != 'refs/heads/master'}}
uses: South-Paw/action-netlify-cli@v2
id: netlify
with:
# note that the --json flag has been passed so we can parse outputs
args: deploy --json --dir './docs/_site' --message 'preview [${{ github.sha }}]'
env:
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }}
This uses the Netlify CLI to push all the site’s files. We can see it in Netlify because it’s a separate preview build with its own message.
Note that it requires a Netlify site to have been created and to produce an Auth token and a site ID, which I store in GitHub secrets for this action.
- name: Update Preview link comment
if: ${{ github.ref != 'refs/heads/master'}}
uses: marocchino/sticky-pull-request-comment@v2
with:
header: previewlink
message: |
Preview link: ${{ fromJson(steps.netlify.outputs.NETLIFY_OUTPUT).deploy_url }}
I was happy with this one. It uses a great GitHub action to post a sticky comment and uses the JSON output of the Netlify CLI to post the URL.
- name: Finish deployment
uses: bobheadxi/deployments@v1
if: ${{ github.ref != 'refs/heads/master'}}
with:
env: ${{ steps.deployment.outputs.env }}
step: finish
status: ${{ job.status }}
deployment_id: ${{ steps.deployment.outputs.deployment_id }}
env_url: ${{ fromJson(steps.netlify.outputs.NETLIFY_OUTPUT).deploy_url }}
token: ${{ secrets.SEAN_PAT_TO_MANAGE_ENVIRONMENTS }}
And here we go ahead and mark the deployment to the preview environment as “finished”.
A Quick Side Note: I love GitHub Actions!
Thanks to so many people who’ve worked to make others’ lives better, my experience was largely in googling around to discover that people had already done all the things that I needed to do! I look forward to being able to contribute more of that myself if I can ever find something that needs doing.
What To Do Once a PR is Merged?
We can’t leave those environments hanging around in our GitHub. So we delete them, using the same id format that we used when we created them.
on:
pull_request:
types: [ closed ]
jobs:
prune:
permissions:
deployments: write
runs-on: ubuntu-latest
steps:
- name: delete environment
uses: bobheadxi/deployments@v1
with:
step: delete-env
token: ${{ secrets.SEAN_PAT_TO_MANAGE_ENVIRONMENTS }}
env: preview_${{github.event.number}}
And…That’s It!
Once I got all the moving parts working together, it was an absolute delight to see it in action and I’ve used it so many times since. I hope this article helps someone else arrive at this place in slightly less time than it took me. Questions and feedback welcome in the comments!
Leave a comment