If you've written a Deno module recently, you most likely will want to ensure that you can lint, test and build it in an automated process!
If you are using a public GitHub repository to store your code then you are in luck! You can use GitHub Actions to automate all of your CI/CD needs for your GitHub project 🎉
Getting started
In your project add the following directory structure and files:
.
├── .github/
│ └── workflows/
│ └── test.yml
│
... // other folders and files
A .github
directory in the root of your project containing a workflows
directory, and finally a test.yml
files in the workflows
directory.
This test.yml
file will contain all of the instructions for our GitHub Action for testing Deno.
The test file
Add the following to your test.yml
file:
name: Test Deno Module
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
deno-version: [1.2.2, 1.3.0]
steps:
- name: Git Checkout Deno Module
uses: actions/checkout@v2
- name: Use Deno Version ${{ matrix.deno-version }}
uses: denolib/setup-deno@v2
with:
deno-version: ${{ matrix.deno-version }}
- name: Lint Deno Module
run: deno fmt --check
- name: Build Deno Module
run: deno run --reload mod.ts
- name: Test Deno Module
run: deno test --allow-none
There's a lot going on here! So let's break it down 😄
name: Test Deno Module
First we add a name
property to the test.yml
file. You can change this to something relevant to your module. GitHub displays this as the name of your workflow on your repository's actions page.
on:
push:
branches: [main]
pull_request:
branches: [main]
Next we set the on
property to define what triggers we would like to automatically start our action's workflow. For this example we have opted to trigger this workflow whenever a git push
is made to the main
branch, and also whenever there is a Pull Request that wishes to merge changes into the main
branch.
If instead you wanted the action to trigger on every push no matter what branch is used, you could do something like:
on: push
Or if you wanted it to trigger on every push and pull request no matter what branch is used, you could do:
on: [push, pull_request]
You can also specify multiple branches, or a different branch to the main
branch.
Finally we define the main part of our GitHub Action workflow, the jobs
section:
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
deno-version: [1.2.2, 1.3.0]
// ... remainder of workflow file
Here we define the a jobs
property and add a single job called test
. This job is what we will be using to run our lint, test and build.
In the test
object we define a runs-on
property which let's GitHub know what machine OS we want to use, in this case ubuntu-latest
, but there is also windows-latest
, macos-latest
and others to choose from.
Next we define a strategy
object which allows us to define a list of Deno versions that we want this workflow to run against. Each of the subsequent steps are executed for each value in the provided array, in this example we have chosen to test against some of the version 1.x.x releases of Deno.
steps:
- name: Git Checkout Deno Module
uses: actions/checkout@v2
- name: Use Deno Version ${{ matrix.deno-version }}
uses: denolib/setup-deno@v2
with:
deno-version: ${{ matrix.deno-version }}
- name: Lint Deno Module
run: deno fmt --check
- name: Build Deno Module
run: deno run --reload mod.ts
- name: Test Deno Module
run: deno test --allow-none
Lastly we define our job's steps
.
Here we define a list of steps, each with an optional name
to make it clear what the step does, and then some other keys to provide instructions on what to run in that step:
First we use the official
actions/checkout@v2
action for performing agit checkout
of our git repository onto the GitHub Action build server so we are able to test our module. This is defined using theuses
parameter.-
Next we use the
denolib/setup-deno@v2
action to install and configure Deno on the GitHub Action build server.Here we also use the additional
with
property to specify the variabledeno-version
, which thesetup-deno
action requires in order to know what version of Deno to install.Notice here we are using a special syntax
${{ matrix.deno-version }}
. This is called an expression and it will evaluate to the value inside the double curly braces, in this case, thedeno-version
which is taken from thematrix
list we provided earlier. -
Finally we define three more steps: one to lint, one to build and one to test our Deno module. Here we use the
run
property which allows us to run any command we like using the build server's shell.For linting we use the
deno fmt --check
command and flag which will check if our code is properly formatted, and exit with an error if not. If this errors, you can fix your Deno module's code formatting by running thedeno fmt
command (without any flags).For building our module we run
deno run --reload mod.ts
, wheremod.ts
is the main entypoint / starting point of our code (change the file depending on your repository setup). Here we are using the--reload
flag to ensure that we fully rebuild the module and all of it's third party modules. This is to make sure that we don't accidentally rely on a cached version of the module or any third party dependencies, as this would give us a false impression of whether the module will build properly on a new module user's computer.For testing we use the
deno test --allow-none
command which will run all tests available in your repository. The--allow-none
flag means that the command won't fail if your repository doesn't have any tests. If you want the test to fail if it can't find any tests, just remove the flag.
If any of the above steps fail the GitHub Action workflow will report the failure with the logs messages.
And that's it! 🎉 🎉
This workflow will work with any Deno project straight away so long as it uses the main
branch as it's main branch. All you need to do is copy the test.yml
file to the correct place in your repository and git push
the code to GitHub.
You can then check your actions using the Actions
page on your GitHub repo, and every Pull Request into the main
branch and every commit to main
will get a status check tick or cross depending on success or failure.
If you want to find out more about GitHub Actions, check out their Workflow Syntax Docs.
That's it peeps! Would love to hear your thoughts and how you're getting on with Deno and if you've done anything cool with GitHub Actions - drop your comments and questions below!
Top comments (1)
Thanks for the detailed writeup! I didn't know about the version matrix option, might use it in the future.
With two years passed since this post, some parts here are oudated:
actions/checkout@v2
, now has a new v3 versiondenolib/setup-deno@v2
has moved to denoland/setup-deno@v1Deno also has official docs now for setting up a Github worfklow.
I also believe the
deno run --reload
step is both unnecessary and outdated. Unnecessary, because by default Github Action runners don't cache dependencies. And outdated, because if you would want to cache deps across runner sections, you should useactions/cache
as described in the Deno docs. Also,deno cache --reload
seems preferable here over thedeno run --reload
command. By usingrun
, you run your actual code in preparation of running tests, which might not be desirable (in my case, my code runs a server).With those changes, I got the following single-version workflow for my project, without cache reloading: