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:
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.
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.