Skip to content
Latchkey

Testing workflow (nodegit/nodegit)

The Testing workflow from nodegit/nodegit, explained and optimized by Latchkey.

D

CI health: D - needs work

Point runs-on at Latchkey and get caching, run de-duplication, job timeouts, self-healing for flaky steps, and up to 58% lower cost, applied automatically.

Grade your own workflow free or run it on Latchkey →
Source: nodegit/nodegit.github/workflows/tests.ymlLicense MITView source

What it does

This is the Testing workflow from the nodegit/nodegit 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

workflow (.yml)
on:
  push:
    branches:
      - master
      - backport/*
    tags:
      - v*.*.*
  pull_request:

name: Testing

jobs:
  linux-tests:
    name: "Linux Tests"
    strategy:
      matrix:
        node: [20, 22, 24]
      fail-fast: false
    runs-on: ubuntu-22.04
    steps:
      - name: Install Dependencies for Ubuntu
        run: sudo apt-get update && sudo apt-get install -y software-properties-common git build-essential clang libssl-dev libkrb5-dev libc++-dev wget zlib1g-dev

      - uses: actions/checkout@v4

      - name: Setup Environment
        run: |
          set -e
          mkdir ~/.ssh_tests
          chmod 700 ~/.ssh_tests
          printf "%b" "Host *\n\tStrictHostKeyChecking no\n" > ~/.ssh_tests/config
          cat test/id_rsa.pub > ~/.ssh_tests/id_rsa.pub
          cat test/id_rsa.enc | base64 -d > ~/.ssh_tests/id_rsa
          chmod 600 ~/.ssh_tests/id_rsa*
          git config --global user.name "John Doe"
          git config --global user.email johndoe@example.com

      - uses: actions/setup-python@v5
        with:
          python-version: "3.11"

      - name: Use Node.js
        uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node }}
          check-latest: true

      - name: Install
        run: npm install

      - name: Test
        run: |
          set -e
          eval `ssh-agent -s`
          ssh-add ~/.ssh_tests/id_rsa
          node utils/retry npm test

      - name: Deploy
        if: startsWith(github.ref, 'refs/tags/v')
        env:
          node_pre_gyp_bucket: ${{ secrets.node_pre_gyp_bucket }}
          AWS_ACCESS_KEY_ID: ${{ secrets.node_pre_gyp_accessKeyId }}
          AWS_SECRET_ACCESS_KEY: ${{ secrets.node_pre_gyp_secretAccessKey }}
        run: |
          npm install -g @mapbox/node-pre-gyp aws-sdk
          node lifecycleScripts/clean
          node-pre-gyp package
          node-pre-gyp publish

  macos-tests:
    name: "macOS Tests"
    strategy:
      matrix:
        node: [20, 22, 24]
        arch: [x64, arm64]
      fail-fast: false
    runs-on: ${{ matrix.arch == 'x64' && 'macos-15-intel' || 'macos-15' }}
    steps:
      - uses: actions/checkout@v4

      - name: Setup Environment
        run: |
          mkdir ~/.ssh_tests
          chmod 700 ~/.ssh_tests
          printf "%b" "Host *\n\tStrictHostKeyChecking no\n" > ~/.ssh_tests/config
          cat test/id_rsa.pub > ~/.ssh_tests/id_rsa.pub
          cat test/id_rsa.enc | base64 -d > ~/.ssh_tests/id_rsa
          chmod 600 ~/.ssh_tests/id_rsa*
          git config --global user.name "John Doe"
          git config --global user.email johndoe@example.com

      - name: Use Node.js
        uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node }}
          check-latest: true

      - name: Install
        run: npm install

      - name: Test
        run: |
          set -e
          eval `ssh-agent -s`
          ssh-add ~/.ssh_tests/id_rsa
          node utils/retry npm test

      - name: Deploy
        if: startsWith(github.ref, 'refs/tags/v')
        env:
          node_pre_gyp_bucket: ${{ secrets.node_pre_gyp_bucket }}
          AWS_ACCESS_KEY_ID: ${{ secrets.node_pre_gyp_accessKeyId }}
          AWS_SECRET_ACCESS_KEY: ${{ secrets.node_pre_gyp_secretAccessKey }}
        run: |
          npm install -g @mapbox/node-pre-gyp aws-sdk
          node lifecycleScripts/clean
          node-pre-gyp package
          node-pre-gyp publish

  windows-tests:
    name: Windows Tests
    strategy:
      matrix:
        node: [20, 22, 24]
        arch: [x86, x64, arm64]
        exclude:
        - node: 24
          arch: x86
      fail-fast: false
    runs-on: windows-2022
    steps:
      - name: Setup Environment
        run: |
          git config --file C:\ProgramData\Git\config core.autocrlf input
          git config --system core.autocrlf input
          git config --global core.autocrlf input
          git config --global user.name "John Doe"
          git config --global user.email johndoe@example.com

      - uses: actions/checkout@v4

      - name: Use Node.js
        if: matrix.arch == 'x86'
        uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node }}
          check-latest: true
          architecture: x86

      - name: Use Node.js
        uses: actions/setup-node@v4
        if: matrix.arch != 'x86'
        with:
          node-version: ${{ matrix.node }}
          check-latest: true

      - name: Install
        env:
          npm_config_arch: ${{ matrix.arch == 'x86' && 'ia32' || matrix.arch }}
        run: npm install

      - name: Test
        # need arm64 runners or an emulator to run tests
        if: matrix.arch != 'arm64'
        env:
          GIT_SSH: ${{ github.workspace }}\vendor\plink.exe
        run: |
          $encodedKey = Get-Content -Path test\private.ppk.enc
          $finalPath = Join-Path -Path $HOME -ChildPath .ssh_tests\private.ppk
          mkdir ~\.ssh_tests
          Set-Content -Value $([System.Convert]::FromBase64String($encodedKey)) -Path $finalPath -AsByteStream
          powershell -command "Start-Process .\vendor\pageant\pageant_${{ matrix.arch }}.exe $finalPath"
          node utils/retry npm test

      # You're probably wondering why this isn't a single `run: |` step, it certainly is for *nix,
      # but it's not, because the CI runner for windows doesn't wait for each step as listed here
      # and it treats each additional step past the first as an orphaned process.
      - name: Deploy (Dependencies)
        if: startsWith(github.ref, 'refs/tags/v')
        run: npm install -g @mapbox/node-pre-gyp aws-sdk

      - name: Deploy (Clean)
        if: startsWith(github.ref, 'refs/tags/v')
        run: node lifecycleScripts\clean

      - name: Deploy (Package)
        if: startsWith(github.ref, 'refs/tags/v')
        run: node-pre-gyp package --target_arch=${{ matrix.arch }}

      - name: Deploy (Publish)
        if: startsWith(github.ref, 'refs/tags/v')
        env:
          node_pre_gyp_bucket: ${{ secrets.node_pre_gyp_bucket }}
          AWS_ACCESS_KEY_ID: ${{ secrets.node_pre_gyp_accessKeyId }}
          AWS_SECRET_ACCESS_KEY: ${{ secrets.node_pre_gyp_secretAccessKey }}
        run: node-pre-gyp publish --target_arch=${{ matrix.arch }}

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.

on:
  push:
    branches:
      - master
      - backport/*
    tags:
      - v*.*.*
  pull_request:
 
name: Testing
 
concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true
 
jobs:
  linux-tests:
    timeout-minutes: 30
    name: "Linux Tests"
    strategy:
      matrix:
        node: [20, 22, 24]
      fail-fast: false
    runs-on: latchkey-small
    steps:
      - name: Install Dependencies for Ubuntu
        run: sudo apt-get update && sudo apt-get install -y software-properties-common git build-essential clang libssl-dev libkrb5-dev libc++-dev wget zlib1g-dev
 
      - uses: actions/checkout@v4
 
      - name: Setup Environment
        run: |
          set -e
          mkdir ~/.ssh_tests
          chmod 700 ~/.ssh_tests
          printf "%b" "Host *\n\tStrictHostKeyChecking no\n" > ~/.ssh_tests/config
          cat test/id_rsa.pub > ~/.ssh_tests/id_rsa.pub
          cat test/id_rsa.enc | base64 -d > ~/.ssh_tests/id_rsa
          chmod 600 ~/.ssh_tests/id_rsa*
          git config --global user.name "John Doe"
          git config --global user.email johndoe@example.com
 
      - uses: actions/setup-python@v5
        with:
          python-version: "3.11"
 
      - name: Use Node.js
        uses: actions/setup-node@v4
        with:
          cache: 'npm'
          node-version: ${{ matrix.node }}
          check-latest: true
 
      - name: Install
        run: npm install
 
      - name: Test
        run: |
          set -e
          eval `ssh-agent -s`
          ssh-add ~/.ssh_tests/id_rsa
          node utils/retry npm test
 
      - name: Deploy
        if: startsWith(github.ref, 'refs/tags/v')
        env:
          node_pre_gyp_bucket: ${{ secrets.node_pre_gyp_bucket }}
          AWS_ACCESS_KEY_ID: ${{ secrets.node_pre_gyp_accessKeyId }}
          AWS_SECRET_ACCESS_KEY: ${{ secrets.node_pre_gyp_secretAccessKey }}
        run: |
          npm install -g @mapbox/node-pre-gyp aws-sdk
          node lifecycleScripts/clean
          node-pre-gyp package
          node-pre-gyp publish
 
  macos-tests:
    timeout-minutes: 30
    name: "macOS Tests"
    strategy:
      matrix:
        node: [20, 22, 24]
        arch: [x64, arm64]
      fail-fast: false
    runs-on: ${{ matrix.arch == 'x64' && 'macos-15-intel' || 'macos-15' }}
    steps:
      - uses: actions/checkout@v4
 
      - name: Setup Environment
        run: |
          mkdir ~/.ssh_tests
          chmod 700 ~/.ssh_tests
          printf "%b" "Host *\n\tStrictHostKeyChecking no\n" > ~/.ssh_tests/config
          cat test/id_rsa.pub > ~/.ssh_tests/id_rsa.pub
          cat test/id_rsa.enc | base64 -d > ~/.ssh_tests/id_rsa
          chmod 600 ~/.ssh_tests/id_rsa*
          git config --global user.name "John Doe"
          git config --global user.email johndoe@example.com
 
      - name: Use Node.js
        uses: actions/setup-node@v4
        with:
          cache: 'npm'
          node-version: ${{ matrix.node }}
          check-latest: true
 
      - name: Install
        run: npm install
 
      - name: Test
        run: |
          set -e
          eval `ssh-agent -s`
          ssh-add ~/.ssh_tests/id_rsa
          node utils/retry npm test
 
      - name: Deploy
        if: startsWith(github.ref, 'refs/tags/v')
        env:
          node_pre_gyp_bucket: ${{ secrets.node_pre_gyp_bucket }}
          AWS_ACCESS_KEY_ID: ${{ secrets.node_pre_gyp_accessKeyId }}
          AWS_SECRET_ACCESS_KEY: ${{ secrets.node_pre_gyp_secretAccessKey }}
        run: |
          npm install -g @mapbox/node-pre-gyp aws-sdk
          node lifecycleScripts/clean
          node-pre-gyp package
          node-pre-gyp publish
 
  windows-tests:
    timeout-minutes: 30
    name: Windows Tests
    strategy:
      matrix:
        node: [20, 22, 24]
        arch: [x86, x64, arm64]
        exclude:
        - node: 24
          arch: x86
      fail-fast: false
    runs-on: windows-2022
    steps:
      - name: Setup Environment
        run: |
          git config --file C:\ProgramData\Git\config core.autocrlf input
          git config --system core.autocrlf input
          git config --global core.autocrlf input
          git config --global user.name "John Doe"
          git config --global user.email johndoe@example.com
 
      - uses: actions/checkout@v4
 
      - name: Use Node.js
        if: matrix.arch == 'x86'
        uses: actions/setup-node@v4
        with:
          cache: 'npm'
          node-version: ${{ matrix.node }}
          check-latest: true
          architecture: x86
 
      - name: Use Node.js
        uses: actions/setup-node@v4
        if: matrix.arch != 'x86'
        with:
          cache: 'npm'
          node-version: ${{ matrix.node }}
          check-latest: true
 
      - name: Install
        env:
          npm_config_arch: ${{ matrix.arch == 'x86' && 'ia32' || matrix.arch }}
        run: npm install
 
      - name: Test
        # need arm64 runners or an emulator to run tests
        if: matrix.arch != 'arm64'
        env:
          GIT_SSH: ${{ github.workspace }}\vendor\plink.exe
        run: |
          $encodedKey = Get-Content -Path test\private.ppk.enc
          $finalPath = Join-Path -Path $HOME -ChildPath .ssh_tests\private.ppk
          mkdir ~\.ssh_tests
          Set-Content -Value $([System.Convert]::FromBase64String($encodedKey)) -Path $finalPath -AsByteStream
          powershell -command "Start-Process .\vendor\pageant\pageant_${{ matrix.arch }}.exe $finalPath"
          node utils/retry npm test
 
      # You're probably wondering why this isn't a single `run: |` step, it certainly is for *nix,
      # but it's not, because the CI runner for windows doesn't wait for each step as listed here
      # and it treats each additional step past the first as an orphaned process.
      - name: Deploy (Dependencies)
        if: startsWith(github.ref, 'refs/tags/v')
        run: npm install -g @mapbox/node-pre-gyp aws-sdk
 
      - name: Deploy (Clean)
        if: startsWith(github.ref, 'refs/tags/v')
        run: node lifecycleScripts\clean
 
      - name: Deploy (Package)
        if: startsWith(github.ref, 'refs/tags/v')
        run: node-pre-gyp package --target_arch=${{ matrix.arch }}
 
      - name: Deploy (Publish)
        if: startsWith(github.ref, 'refs/tags/v')
        env:
          node_pre_gyp_bucket: ${{ secrets.node_pre_gyp_bucket }}
          AWS_ACCESS_KEY_ID: ${{ secrets.node_pre_gyp_accessKeyId }}
          AWS_SECRET_ACCESS_KEY: ${{ secrets.node_pre_gyp_secretAccessKey }}
        run: node-pre-gyp publish --target_arch=${{ matrix.arch }}
 

What changed

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:

This workflow runs 3 jobs (18 with the matrix expanded) per trigger. On Latchkey the same minutes cost up to 58% less than GitHub-hosted, with zero queue time.

Actions used in this workflow