Skip to content
Latchkey

CI workflow (brianc/node-postgres)

The CI workflow from brianc/node-postgres, explained and optimized by Latchkey.

A

CI health: A - excellent

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

Grade your own workflow free or run it on Latchkey →
Source: brianc/node-postgres.github/workflows/ci.ymlLicense MITView source

What it does

This is the CI workflow from the brianc/node-postgres 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)
name: CI

on: [push, pull_request]

permissions:
  contents: read

jobs:
  lint:
    timeout-minutes: 5
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          persist-credentials: false
      - name: Setup node
        uses: actions/setup-node@v4
        with:
          node-version: 18
          cache: yarn
      - run: yarn install --frozen-lockfile
      - run: yarn lint
  build:
    timeout-minutes: 15
    needs: lint
    services:
      postgres:
        image: ghcr.io/railwayapp-templates/postgres-ssl:${{ matrix.postgres }}
        env:
          POSTGRES_USER: postgres
          POSTGRES_PASSWORD: postgres
          POSTGRES_HOST_AUTH_METHOD: 'md5'
          POSTGRES_DB: ci_db_test
          # PostgreSQL 18's official image defaults PGDATA to a versioned
          # subdirectory (/var/lib/postgresql/18/docker), but the
          # railwayapp-templates/postgres-ssl entrypoint requires PGDATA to
          # start with /var/lib/postgresql/data so we pin it explicitly. This is
          # also the default for the older images, so it is a no-op there.
          PGDATA: /var/lib/postgresql/data
        ports:
          - 5432:5432
        options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
    strategy:
      fail-fast: false
      matrix:
        include:
          # Historical Node.js versions tested against a single PostgreSQL version
          - { node: '16', postgres: &stable_postgres '18' }
          - { node: '18', postgres: *stable_postgres }
          - { node: '20', postgres: *stable_postgres }
          - { node: '22', postgres: *stable_postgres }
          - { node: '24', postgres: *stable_postgres }
          # Latest Node.js version tested against multiple PostgreSQL versions
          - { node: &latest_node '26', postgres: '13' }
          - { node: *latest_node, postgres: '14' }
          - { node: *latest_node, postgres: '15' }
          - { node: *latest_node, postgres: '16' }
          - { node: *latest_node, postgres: '17' }
          - { node: *latest_node, postgres: '18' }
    name: Node.js ${{ matrix.node }} x PostgreSQL ${{ matrix.postgres }}
    runs-on: ubuntu-latest
    env:
      PGUSER: postgres
      PGPASSWORD: postgres
      PGHOST: localhost
      PGDATABASE: ci_db_test
      PGTESTNOSSL: 'true'
      SCRAM_TEST_PGUSER: scram_test
      SCRAM_TEST_PGPASSWORD: test4scram
      SCRAM_TEST_PGUSER_UNICODE: scram_unicode_test
      # Raw form of a password whose NFKC normalization differs from itself.
      # U+2168 (ROMAN NUMERAL IX) decomposes to ASCII "IX" under NFKC; the
      # server stores the verifier from the SASLprep-normalized form, so the
      # client must apply SASLprep too. This is the regression check for the
      # RFC 4013 fix in packages/pg/lib/crypto/sasl.js.
      SCRAM_TEST_PGPASSWORD_UNICODE: "IX-\u2168"
    steps:
      - name: Show OS
        run: |
          uname -a
      - run: |
          psql \
            -c "SET password_encryption = 'scram-sha-256'" \
            -c "CREATE ROLE scram_test LOGIN PASSWORD 'test4scram'" \
            -c "CREATE ROLE scram_unicode_test LOGIN PASSWORD U&'IX-\2168'"
      - uses: actions/checkout@v4
        with:
          persist-credentials: false
      - name: Setup node
        uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node }}
          cache: yarn
      - run: yarn install --frozen-lockfile
      - run: yarn test

The same workflow, on Latchkey

Removes redundant runs and caps runaway jobs. Added and changed lines are highlighted.

name: CI
 
on: [push, pull_request]
 
permissions:
  contents: read
 
concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true
 
jobs:
  lint:
    timeout-minutes: 5
    runs-on: latchkey-small
    steps:
      - uses: actions/checkout@v4
        with:
          persist-credentials: false
      - name: Setup node
        uses: actions/setup-node@v4
        with:
          node-version: 18
          cache: yarn
      - run: yarn install --frozen-lockfile
      - run: yarn lint
  build:
    timeout-minutes: 15
    needs: lint
    services:
      postgres:
        image: ghcr.io/railwayapp-templates/postgres-ssl:${{ matrix.postgres }}
        env:
          POSTGRES_USER: postgres
          POSTGRES_PASSWORD: postgres
          POSTGRES_HOST_AUTH_METHOD: 'md5'
          POSTGRES_DB: ci_db_test
          # PostgreSQL 18's official image defaults PGDATA to a versioned
          # subdirectory (/var/lib/postgresql/18/docker), but the
          # railwayapp-templates/postgres-ssl entrypoint requires PGDATA to
          # start with /var/lib/postgresql/data so we pin it explicitly. This is
          # also the default for the older images, so it is a no-op there.
          PGDATA: /var/lib/postgresql/data
        ports:
          - 5432:5432
        options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
    strategy:
      fail-fast: false
      matrix:
        include:
          # Historical Node.js versions tested against a single PostgreSQL version
          - { node: '16', postgres: &stable_postgres '18' }
          - { node: '18', postgres: *stable_postgres }
          - { node: '20', postgres: *stable_postgres }
          - { node: '22', postgres: *stable_postgres }
          - { node: '24', postgres: *stable_postgres }
          # Latest Node.js version tested against multiple PostgreSQL versions
          - { node: &latest_node '26', postgres: '13' }
          - { node: *latest_node, postgres: '14' }
          - { node: *latest_node, postgres: '15' }
          - { node: *latest_node, postgres: '16' }
          - { node: *latest_node, postgres: '17' }
          - { node: *latest_node, postgres: '18' }
    name: Node.js ${{ matrix.node }} x PostgreSQL ${{ matrix.postgres }}
    runs-on: latchkey-small
    env:
      PGUSER: postgres
      PGPASSWORD: postgres
      PGHOST: localhost
      PGDATABASE: ci_db_test
      PGTESTNOSSL: 'true'
      SCRAM_TEST_PGUSER: scram_test
      SCRAM_TEST_PGPASSWORD: test4scram
      SCRAM_TEST_PGUSER_UNICODE: scram_unicode_test
      # Raw form of a password whose NFKC normalization differs from itself.
      # U+2168 (ROMAN NUMERAL IX) decomposes to ASCII "IX" under NFKC; the
      # server stores the verifier from the SASLprep-normalized form, so the
      # client must apply SASLprep too. This is the regression check for the
      # RFC 4013 fix in packages/pg/lib/crypto/sasl.js.
      SCRAM_TEST_PGPASSWORD_UNICODE: "IX-\u2168"
    steps:
      - name: Show OS
        run: |
          uname -a
      - run: |
          psql \
            -c "SET password_encryption = 'scram-sha-256'" \
            -c "CREATE ROLE scram_test LOGIN PASSWORD 'test4scram'" \
            -c "CREATE ROLE scram_unicode_test LOGIN PASSWORD U&'IX-\2168'"
      - uses: actions/checkout@v4
        with:
          persist-credentials: false
      - name: Setup node
        uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node }}
          cache: yarn
      - run: yarn install --frozen-lockfile
      - run: yarn test
 

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 2 jobs per trigger. On Latchkey the same minutes cost up to 58% less than GitHub-hosted, with zero queue time.

Actions used in this workflow