Deno and GitHub Actions workflows

4 min read

927 words

Today I want to talk about Deno and GitHub Actions. For the last year or so, I have only built new projects with Deno. I really like the runtime, it's typescript out of the box config, formatting, linting etc. It is just a lot of fun to work with.

Since I still wanted to make sure that everything works when pushed to our Github repositories, I had to build some GitHub actions. Today I want to share a repository where I collect useful actions. At the time of writing, the repository contains 3 different workflows. You can find the repository at niklasmtj/deno-actions on GitHub.

CI

The CI workflow runs on every PR creation or commit. It checks the format, runs tests, and lints the whole project. Since these steps are usually really fast, I did not limit it to just the changed files.

name: "CI"

on:
  - pull_request

jobs:
  ci:
    runs-on: ubuntu-latest
    steps:
      - name: "Checkout"
        uses: actions/checkout@v4

      - name: "Setup Deno"
        uses: denoland/setup-deno@v1
        with:
          deno-version: "1.x"

      - name: "Run Tests"
        run: deno task test

      - name: "Run Lint"
        run: deno task lint

      - name: "Run Format"
        run: deno task format

To run this workflow you need to set up 3 tasks in the deno.json file. First set up test with the required permissions. Then lint and format. This is done in deno.json under the tasks key as you can see in the following block of code:

{
  "tasks": {
    "lint": "deno lint",
    "format": "deno fmt --check",
    "test": "deno test --allow-read --allow-write --allow-net"
  }
}

This example uses the `--allow-read --allow-write --allow-net' flags to run the tests. When setting up your workflow tasks, you can easily check the commands locally. Deno will help you figure out the permission flags you need.

Build Docker Container Images

The second action will build a container image using Docker. The workflow runs when new pushes are made to the `main' branch. This happens most often after merging a pull request.

Make sure that you have a Dockerfile describing your desired container image in the root directory of the repository. This is used by the build-and-push action as it's image recipe.

It is set up using buildx, which allows you to build multi-architecture images. For example, if you are building your containers locally on an M1 Mac, these are arm64 images. If your hosting provider does not support arm64 images, but rather uses Intel or AMD processors on their servers, you will need to build amd64 images. Using buildx and qemu we can make sure we're able to build both in the same build. This is why you see linux/amd64,linux/arm64 in the platforms fields in the following workflow file:

name: "Build Container Image"

on:
  push:
    branches:
      - main

jobs:
  ci:
    runs-on: ubuntu-latest
    steps:
      - name: "Checkout"
        uses: actions/checkout@v4

      - name: Set up QEMU
        uses: docker/setup-qemu-action@v3
        with:
          platforms: linux/amd64,linux/arm64

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3
        with:
          platforms: linux/amd64,linux/arm64

      # Login to Docker Hub or any other registry here
      - name: Login to Docker Hub
        uses: docker/login-action@v3
        with:
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_TOKEN }}

      - name: Build and push
        uses: docker/build-push-action@v5
        with:
          context: .
          push: true
          tags: niklasmtj/deno-image:latest
          cache-from: type=gha
          cache-to: type=gha,mode=max

The built image here will then be pushed to Docker Hub after login. Make sure your organization or repository already has the DOCKERHUB_USERNAME and DOCKERHUB_TOKEN secrets set. These are set in a repository under `Settings -> Secrets and Variables -> Actions -> Repository secrets'. How to obtain a Docker Hub token can be found in the Docker documentation.

Caching is also set up when the container image is built to ensure that the subsequent execution is as fast as possible.

Create a Test Coverage Website and Publish to GitHub Pages

Testing should be an integral part of application development. To make sure that every part of the application is covered with tests, Deno supports creating a coverage report right out of the box (🤝). The third action supports building just that. Create the coverage and push it to GitHub pages. To be able to publish, you need to make sure that GitHub Pages is enabled for the repository you want to build coverage for.

name: Code Coverage

on:
  push:
    branches: [main]
  workflow_dispatch:

# Make sure that you set these permissions
permissions:
  contents: read
  pages: write
  id-token: write

jobs:
  ci:
    runs-on: ubuntu-latest

    steps:
      - name: Setup repo
        uses: actions/checkout@v4

      - name: Setup Deno
        uses: denoland/setup-deno@v1

      - name: Create lcov file
        run: deno task coverage

      - name: Setup Pages
        uses: actions/configure-pages@v4

      - name: Upload artifact
        uses: actions/upload-pages-artifact@v3
        with:
          path: './coverage/html'

      - name: Deploy to GitHub Pages
        id: deployment
        uses: actions/deploy-pages@v4

One thing you need to make sure is that GitHub Actions has permissions to publish the created website. Make sure you set it under the permissions key in the yaml file.

Conclusion

This repository is not set in stone. I will try to keep the dependencies up to date (shout out to Dependabot) and will add or change actions in the future. So make sure you keep an eye on the repo (click the watch button in the top right corner).

Thanks for reading and have a great day!