Caching, secrets and deployments — Examples

Example 1 — A cache that serves stale dependencies for ever

Every run reinstalls the whole dependency tree, so the cache step below was added. It helped for a week and then made an unrelated failure reproducible. Explain the key and correct the step.

- uses: cache
  with:
    path: ~/.npm
    key: node-deps

Solution

- uses: cache
  with:
    path: ~/.npm
    key: deps-${{ runner.os }}-${{ hashFiles('package-lock.json') }}

A cache keyed on a constant is a cache that can never miss, so the first set of dependencies is restored indefinitely, even after the lockfile changes. Keying on the lockfile’s hash means a dependency change produces a miss, while every other run hits and the install is skipped. This is the one setting that decides whether a fast pipeline is fast and correct or merely fast.

Example 2 — A passing check that is also a release

The workflow below runs on a pull request and ends in a deployment job that holds the production credential. A contributor’s branch can therefore reach production without a human decision. Restructure the workflow so checks and the release are separate.

on: pull_request
jobs:
  test:
    runs-on: ubuntu-latest
    steps: [{ run: ./run-tests.sh }]
  deploy:
    needs: test
    runs-on: ubuntu-latest
    steps: [{ run: ./deploy.sh }]

Solution

on:
  pull_request:
  push:
    branches: [main]
jobs:
  test:
    runs-on: ubuntu-latest
    steps: [{ run: ./run-tests.sh }]
  deploy:
    if: github.ref == 'refs/heads/main'
    environment: production
    runs-on: ubuntu-latest
    steps: [{ run: ./deploy.sh }]

A check reports whether a change is good; a deployment changes what users are running, so the deploy job now runs only for the main branch and waits on an environment that requires approval. The deployment job is also the only place the credential should live, because a workflow’s token is usually broader than the job needs.

Substitute your own action names, environment names and repository files where these sketches use placeholders.