ci workflow (sverweij/dependency-cruiser)
The ci workflow from sverweij/dependency-cruiser, explained and optimized by Latchkey.
CI health: D - needs work
Run this on Latchkey for self-healing, caching, and up to 58% lower cost.
Grade your own workflow free or run it on Latchkey →What it does
This is the ci workflow from the sverweij/dependency-cruiser repository, a real project running GitHub Actions. It is shown here with attribution under its MIT license.
Below, Latchkey shows a faster, safer version produced by its optimization engine.
The workflow
name: ci
on:
push:
branches: [main]
pull_request:
branches: [main]
workflow_dispatch:
env:
NODE_LATEST: 26.x
PLATFORM: ubuntu-latest
CI: true
# the LANG (used by the Intl API, which we use for some of the number
# formatting) is set in the run scripts. This works everywhere, except on
# windows. So, especially for windows ...
LANG: en_US.UTF-8
# Same for NODE_OPTIONS, just for windows:
NODE_OPTIONS: --no-warnings
defaults:
run:
shell: bash
jobs:
check-linux:
permissions:
contents: read
pull-requests: read
actions: read
strategy:
fail-fast: false
matrix:
node-version: [22.x, 26.x]
platform: [ubuntu-latest]
runs-on: ${{matrix.platform}}
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 2
if: github.event_name == 'pull_request' && matrix.node-version == env.NODE_LATEST
- uses: actions/checkout@v6
with:
fetch-depth: 1
if: github.event_name != 'pull_request' || matrix.node-version != env.NODE_LATEST
- uses: actions/cache@v5
with:
path: node_modules
key: ${{matrix.node-version}}@${{matrix.platform}}-build-${{hashFiles('package-lock.json')}}
restore-keys: |
${{matrix.node-version}}@${{matrix.platform}}-build-
- uses: actions/setup-node@v6
with:
node-version: ${{matrix.node-version}}
- name: install & build
run: |
sudo apt-get update
sudo apt-get install graphviz
# legacy-peer-deps because otherwise typescript-eslint hard-complains
# as it can't find a suitable typescript compiler (we're on 6 now,
# ts-eslint isn't yet)
# ignore-scripts because no need for them
npm install --legacy-peer-deps --ignore-scripts
# engine-strict disable
# better to have no stuff altered going forward
git restore .npmrc
# finally what we're here for ...
npm run build
- name: lint (run on node ${{env.NODE_LATEST}} only)
if: matrix.node-version == env.NODE_LATEST
run: npm run lint
- name: forbidden dependency check
run: npm run depcruise
- name: test coverage (run on node ${{env.NODE_LATEST}} only)
if: matrix.node-version == env.NODE_LATEST
run: npm run test:cover
- name: emit coverage results & depcruise result to step summary
if: always() && matrix.node-version == env.NODE_LATEST
run: |
echo '## Code coverage' >> $GITHUB_STEP_SUMMARY
node tools/istanbul-json-summary-to-markdown.mjs < coverage/coverage-summary.json >> $GITHUB_STEP_SUMMARY
yarn --silent depcruise --output-type markdown >> $GITHUB_STEP_SUMMARY
- name: on pushes to the default branch emit graph to the step summary
if: always() && matrix.node-version == env.NODE_LATEST && github.event_name == 'push' && github.ref_name == github.event.repository.default_branch
run: |
echo '## Visual overview' >> $GITHUB_STEP_SUMMARY
echo '```mermaid' >> $GITHUB_STEP_SUMMARY
yarn --silent depcruise:graph:mermaid >> $GITHUB_STEP_SUMMARY
echo '' >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY
- name: on pull requests emit depcruise graph to step summary with changed modules highlighted
if: always() && matrix.node-version == env.NODE_LATEST && github.event_name == 'pull_request' && github.ref_name != github.event.repository.default_branch
run: |
echo '## Visual diff' >> $GITHUB_STEP_SUMMARY
echo Modules changed in this PR have a fluorescent green color. All other modules in the graph are those directly or indirectly affected by changes in the green modules. >> $GITHUB_STEP_SUMMARY
echo '```mermaid' >> $GITHUB_STEP_SUMMARY
SHA=${{github.event.pull_request.base.sha}} yarn --silent depcruise:graph:mermaid:diff >> $GITHUB_STEP_SUMMARY
echo '' >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY
- name: regular test (run on node != ${{env.NODE_LATEST}} only)
if: matrix.node-version != env.NODE_LATEST
run: npm test
check-windows:
env:
PLATFORM: windows-latest
NO_COLOR: 1
permissions:
contents: read
pull-requests: read
actions: read
runs-on: windows-latest
steps:
- uses: actions/checkout@v6
- uses: actions/cache@v5
with:
path: node_modules
key: ${{env.NODE_LATEST}}@${{env.PLATFORM}}-build-${{hashFiles('package.json')}}
restore-keys: |
${{env.NODE_LATEST}}@${{env.PLATFORM}}-build-
- uses: actions/setup-node@v6
with:
node-version: ${{env.NODE_LATEST}}
- name: install & build
run: |
choco install graphviz
# legacy peer deps because otherwise typescript-eslint hard-complains
# as it can't find a suitable typescript compiler (we're on 6 now,
# ts-eslint isn't yet)
npm install --legacy-peer-deps --ignore-scripts
npm run build
- run: npm run depcruise
- run: npx mocha --invert --fgrep "#do-not-run-on-windows"
The same workflow, on Latchkey
Estimated ~20% faster on cache hits, plus fewer wasted runs and a safer supply chain. Added and changed lines are highlighted.
name: ci on: push: branches: [main] pull_request: branches: [main] workflow_dispatch: env: NODE_LATEST: 26.x PLATFORM: ubuntu-latest CI: true # the LANG (used by the Intl API, which we use for some of the number # formatting) is set in the run scripts. This works everywhere, except on # windows. So, especially for windows ... LANG: en_US.UTF-8 # Same for NODE_OPTIONS, just for windows: NODE_OPTIONS: --no-warnings defaults: run: shell: bash concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true jobs: check-linux: timeout-minutes: 30 permissions: contents: read pull-requests: read actions: read strategy: fail-fast: false matrix: node-version: [22.x, 26.x] platform: [ubuntu-latest] runs-on: ${{matrix.platform}} steps: - uses: actions/checkout@v6 with: fetch-depth: 2 if: github.event_name == 'pull_request' && matrix.node-version == env.NODE_LATEST - uses: actions/checkout@v6 with: fetch-depth: 1 if: github.event_name != 'pull_request' || matrix.node-version != env.NODE_LATEST - uses: actions/cache@v5 with: path: node_modules key: ${{matrix.node-version}}@${{matrix.platform}}-build-${{hashFiles('package-lock.json')}} restore-keys: | ${{matrix.node-version}}@${{matrix.platform}}-build- - uses: actions/setup-node@v6 with: cache: 'npm' node-version: ${{matrix.node-version}} - name: install & build run: | sudo apt-get update sudo apt-get install graphviz # legacy-peer-deps because otherwise typescript-eslint hard-complains # as it can't find a suitable typescript compiler (we're on 6 now, # ts-eslint isn't yet) # ignore-scripts because no need for them npm install --legacy-peer-deps --ignore-scripts # engine-strict disable # better to have no stuff altered going forward git restore .npmrc # finally what we're here for ... npm run build - name: lint (run on node ${{env.NODE_LATEST}} only) if: matrix.node-version == env.NODE_LATEST run: npm run lint - name: forbidden dependency check run: npm run depcruise - name: test coverage (run on node ${{env.NODE_LATEST}} only) if: matrix.node-version == env.NODE_LATEST run: npm run test:cover - name: emit coverage results & depcruise result to step summary if: always() && matrix.node-version == env.NODE_LATEST run: | echo '## Code coverage' >> $GITHUB_STEP_SUMMARY node tools/istanbul-json-summary-to-markdown.mjs < coverage/coverage-summary.json >> $GITHUB_STEP_SUMMARY yarn --silent depcruise --output-type markdown >> $GITHUB_STEP_SUMMARY - name: on pushes to the default branch emit graph to the step summary if: always() && matrix.node-version == env.NODE_LATEST && github.event_name == 'push' && github.ref_name == github.event.repository.default_branch run: | echo '## Visual overview' >> $GITHUB_STEP_SUMMARY echo '```mermaid' >> $GITHUB_STEP_SUMMARY yarn --silent depcruise:graph:mermaid >> $GITHUB_STEP_SUMMARY echo '' >> $GITHUB_STEP_SUMMARY echo '```' >> $GITHUB_STEP_SUMMARY - name: on pull requests emit depcruise graph to step summary with changed modules highlighted if: always() && matrix.node-version == env.NODE_LATEST && github.event_name == 'pull_request' && github.ref_name != github.event.repository.default_branch run: | echo '## Visual diff' >> $GITHUB_STEP_SUMMARY echo Modules changed in this PR have a fluorescent green color. All other modules in the graph are those directly or indirectly affected by changes in the green modules. >> $GITHUB_STEP_SUMMARY echo '```mermaid' >> $GITHUB_STEP_SUMMARY SHA=${{github.event.pull_request.base.sha}} yarn --silent depcruise:graph:mermaid:diff >> $GITHUB_STEP_SUMMARY echo '' >> $GITHUB_STEP_SUMMARY echo '```' >> $GITHUB_STEP_SUMMARY - name: regular test (run on node != ${{env.NODE_LATEST}} only) if: matrix.node-version != env.NODE_LATEST run: npm test check-windows: timeout-minutes: 30 env: PLATFORM: windows-latest NO_COLOR: 1 permissions: contents: read pull-requests: read actions: read runs-on: windows-latest steps: - uses: actions/checkout@v6 - uses: actions/cache@v5 with: path: node_modules key: ${{env.NODE_LATEST}}@${{env.PLATFORM}}-build-${{hashFiles('package.json')}} restore-keys: | ${{env.NODE_LATEST}}@${{env.PLATFORM}}-build- - uses: actions/setup-node@v6 with: cache: 'npm' node-version: ${{env.NODE_LATEST}} - name: install & build run: | choco install graphviz # legacy peer deps because otherwise typescript-eslint hard-complains # as it can't find a suitable typescript compiler (we're on 6 now, # ts-eslint isn't yet) npm install --legacy-peer-deps --ignore-scripts npm run build - run: npm run depcruise - run: npx mocha --invert --fgrep "#do-not-run-on-windows"
What changed
- Cancel superseded runs when a branch or PR gets a newer push.
- Cache dependency installs on the setup step so they are served from cache.
- Add a job timeout so a hung step cannot burn hours of runner time.
What Latchkey heals here
This workflow has steps that commonly fail on transient issues (network, registries, flaky browsers). On Latchkey managed runners they are detected, retried, and self-healed instead of failing your build:
- Dependency installs
This workflow runs 2 jobs (3 with the matrix expanded) per trigger. On Latchkey the same minutes cost up to 58% less than GitHub-hosted, with zero queue time.