Skip to main content
On this page

Run Deno in GitHub Actions

Deno ships its formatter, linter, and test runner in the deno binary, so a CI pipeline needs little more than installing Deno and running the corresponding subcommands.

A minimal workflow Jump to heading

The denoland/setup-deno action installs Deno on the runner. This workflow checks formatting, lints, and runs tests on every push and pull request:

.github/workflows/ci.yml
name: CI

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: denoland/setup-deno@v2
        with:
          deno-version: v2.x # Latest stable Deno
          cache: true # Cache dependencies between runs

      - run: deno ci # Install dependencies from the lockfile

      - run: deno fmt --check
      - run: deno lint
      - run: deno test --allow-all

deno-version accepts a semver range like v2.x, an exact version, or canary.

Installing with a frozen lockfile Jump to heading

deno ci mirrors npm ci: it is roughly equivalent to deleting node_modules and running deno install --frozen, but it errors out if deno.lock is missing, and fails if deno.json or package.json changed without the lockfile being refreshed. That catches "bumped a version but forgot to commit the lockfile" mistakes before they reach production.

Note

deno ci requires a committed deno.lock. If your repository doesn't have one yet, run deno install locally and commit the resulting lockfile.

How the cache works Jump to heading

With cache: true, the action saves and restores DENO_DIR, the directory where Deno stores downloaded dependencies. The cache key includes the job id, the runner OS and architecture, and a hash of all deno.lock files (${{ hashFiles('**/deno.lock') }}), so the cache is invalidated exactly when the lockfile changes. The first run downloads everything and saves the cache; subsequent runs restore it instead of re-downloading.

To key the cache on something else, set cache-hash (which implies cache: true):

- uses: denoland/setup-deno@v2
  with:
    cache-hash: ${{ hashFiles('**/deno.json') }}

Testing across Deno versions Jump to heading

To spot breaking changes early, run the same job against stable and canary with a matrix:

jobs:
  test:
    runs-on: ubuntu-latest
    continue-on-error: ${{ matrix.deno-version == 'canary' }}
    strategy:
      matrix:
        deno-version: [v2.x, canary]
    steps:
      - uses: actions/checkout@v4
      - uses: denoland/setup-deno@v2
        with:
          deno-version: ${{ matrix.deno-version }}
          cache: true
      - run: deno test --allow-all

continue-on-error keeps a canary failure from failing the whole workflow.

For cross-platform matrices, coverage uploads, and other CI providers, see Continuous integration.

Did you find what you needed?

Edit this page
Privacy policy