Skip to content
Latchkey

Native and WSL workflow (import-js/eslint-plugin-import)

The Native and WSL workflow from import-js/eslint-plugin-import, explained and optimized by Latchkey.

B

CI health: B - good

Point runs-on at Latchkey and get job timeouts, SHA-pinned actions, self-healing for flaky steps, and up to 58% lower cost, applied automatically.

Grade your own workflow free or run it on Latchkey →
Source: import-js/eslint-plugin-import.github/workflows/native-wsl.ymlLicense MITView source

What it does

This is the Native and WSL workflow from the import-js/eslint-plugin-import 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: Native and WSL

on: [push, pull_request]

concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: ${{ github.event_name == 'pull_request' }}

permissions:
  contents: read

jobs:
  build:
    runs-on: ${{ matrix.os }}
    defaults:
        run:
          shell: ${{ matrix.configuration == 'wsl' && 'wsl-bash {0}' || 'pwsh' }}
    strategy:
      fail-fast: false
      matrix:
        os: [windows-2022]
        node-version: [18, 16, 14, 12, 10, 8, 6, 4]
        configuration: [wsl, native]

    steps:
    - uses: actions/checkout@v4
    - uses: Vampire/setup-wsl@v7
      if: matrix.configuration == 'wsl'
      with:
        distribution: Ubuntu-22.04
        wsl-version: 2
    - run: curl --version
    - name: 'WSL: do all npm install steps'
      if: matrix.configuration == 'wsl'
      env:
        ESLINT_VERSION: 7
        TRAVIS_NODE_VERSION: ${{ matrix.node-version }}
      run: |
        export ESLINT_IMPORT_WSL_TEST_TIMEOUT=60000
        curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/master/install.sh | bash
        export NVM_DIR="$HOME/.nvm"
        [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"  # This loads nvm
        nvm install --latest-npm ${{ matrix.node-version }}

        if [ ${{ matrix.node-version }} -ge 4 ] && [ ${{ matrix.node-version }} -lt 6 ]; then
          npm install eslint@4 --no-save --ignore-scripts
          npm install
          npm install eslint-import-resolver-typescript@1.0.2 --no-save
          npm uninstall @angular-eslint/template-parser @typescript-eslint/parser --no-save
        fi
        if [ ${{ matrix.node-version }} -ge 6 ] && [ ${{ matrix.node-version }} -lt 7 ]; then
          npm install eslint@5 --no-save --ignore-scripts
          npm install
          npm uninstall @angular-eslint/template-parser --no-save
          npm install eslint-import-resolver-typescript@1.0.2 @typescript-eslint/parser@3 --no-save
        fi
        if [ ${{ matrix.node-version }} -ge 7 ] && [ ${{ matrix.node-version }} -lt 8 ]; then
          npm install eslint@6 --no-save --ignore-scripts
          npm install
          npm install eslint-import-resolver-typescript@1.0.2 typescript-eslint-parser@20 --no-save
          npm uninstall @angular-eslint/template-parser --no-save
        fi
        if [ ${{ matrix.node-version }} -eq 8 ]; then
          npm install eslint@6 --no-save --ignore-scripts
          npm install
          npm uninstall @angular-eslint/template-parser --no-save
          npm install @typescript-eslint/parser@3 --no-save
        fi
        if [ ${{ matrix.node-version }} -gt 8 ] && [ ${{ matrix.node-version }} -lt 10 ]; then
          npm install eslint@7 --no-save --ignore-scripts
          npm install
          npm install @typescript-eslint/parser@3 --no-save
        fi
        if [ ${{ matrix.node-version }} -ge 10 ] && [ ${{ matrix.node-version }} -lt 12 ]; then
          npm install
          npm install @typescript-eslint/parser@4 --no-save
        fi
        if [ ${{ matrix.node-version }} -ge 12 ]; then
          npm install
        fi
        npm run copy-metafiles
        npm run pretest
        npm run mocha -- tests/src --timeout "$ESLINT_IMPORT_WSL_TEST_TIMEOUT"

    - name: install dependencies for node <= 10
      if: matrix.node-version <= '10' && matrix.configuration == 'native'
      run: |
        npm install --legacy-peer-deps
        npm install eslint@7 --no-save

    - name: Install dependencies for node > 10
      if: matrix.node-version > '10'  && matrix.configuration == 'native'
      run: npm install

    - name: install the latest version of nyc
      if: matrix.configuration == 'native'
      run: npm install nyc@latest --no-save

    - name: copy metafiles for node <= 8
      if: matrix.node-version <= 8 && matrix.configuration == 'native'
      env:
        ESLINT_VERSION: 6
        TRAVIS_NODE_VERSION: ${{ matrix.node-version }}
      run: |
        npm run copy-metafiles
        bash ./tests/dep-time-travel.sh 2>&1
    - name: copy metafiles for Node > 8
      if: matrix.node-version > 8 && matrix.configuration == 'native'
      env:
        ESLINT_VERSION: 7
        TRAVIS_NODE_VERSION: ${{ matrix.node-version }}
      run: |
        npm run copy-metafiles
        bash ./tests/dep-time-travel.sh 2>&1

    - name: install ./resolver dependencies in Native
      if: matrix.configuration == 'native'
      shell: pwsh
      run: |
        npm config set package-lock false
        $resolverDir = "./resolvers"
        Get-ChildItem -Directory $resolverDir |
          ForEach {
            Write-output $(Resolve-Path $(Join-Path $resolverDir  $_.Name))
            Push-Location $(Resolve-Path $(Join-Path $resolverDir  $_.Name))
              npm install
              npm ls nyc > $null;
              if ($?) {
                npm install nyc@latest --no-save
              }
              Pop-Location
            }

    - name: run tests in Native
      if: matrix.configuration == 'native'
      shell: pwsh
      run: |
        npm run pretest
        npm run tests-only
        $resolverDir = "./resolvers";
        $resolvers = @();
        Get-ChildItem -Directory $resolverDir |
          ForEach {
            $resolvers += "$(Resolve-Path $(Join-Path $resolverDir $_.Name))";
          }
        $env:RESOLVERS = [string]::Join(";", $resolvers);
        foreach ($resolver in $resolvers) {
            Set-Location -Path $resolver.Trim('"')
            npm run tests-only
            Set-Location -Path $PSScriptRoot
        }

    - name: codecov
      uses: codecov/codecov-action@v7.0.0

  windows:
    runs-on: ubuntu-latest
    needs: [build]
    steps:
      - run: true

The same workflow, on Latchkey

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

name: Native and WSL
 
on: [push, pull_request]
 
concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: ${{ github.event_name == 'pull_request' }}
 
permissions:
  contents: read
 
jobs:
  build:
    timeout-minutes: 30
    runs-on: ${{ matrix.os }}
    defaults:
        run:
          shell: ${{ matrix.configuration == 'wsl' && 'wsl-bash {0}' || 'pwsh' }}
    strategy:
      fail-fast: false
      matrix:
        os: [windows-2022]
        node-version: [18, 16, 14, 12, 10, 8, 6, 4]
        configuration: [wsl, native]
 
    steps:
    - uses: actions/checkout@v4
    - uses: Vampire/setup-wsl@v7
      if: matrix.configuration == 'wsl'
      with:
        distribution: Ubuntu-22.04
        wsl-version: 2
    - run: curl --version
    - name: 'WSL: do all npm install steps'
      if: matrix.configuration == 'wsl'
      env:
        ESLINT_VERSION: 7
        TRAVIS_NODE_VERSION: ${{ matrix.node-version }}
      run: |
        export ESLINT_IMPORT_WSL_TEST_TIMEOUT=60000
        curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/master/install.sh | bash
        export NVM_DIR="$HOME/.nvm"
        [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"  # This loads nvm
        nvm install --latest-npm ${{ matrix.node-version }}
 
        if [ ${{ matrix.node-version }} -ge 4 ] && [ ${{ matrix.node-version }} -lt 6 ]; then
          npm install eslint@4 --no-save --ignore-scripts
          npm install
          npm install eslint-import-resolver-typescript@1.0.2 --no-save
          npm uninstall @angular-eslint/template-parser @typescript-eslint/parser --no-save
        fi
        if [ ${{ matrix.node-version }} -ge 6 ] && [ ${{ matrix.node-version }} -lt 7 ]; then
          npm install eslint@5 --no-save --ignore-scripts
          npm install
          npm uninstall @angular-eslint/template-parser --no-save
          npm install eslint-import-resolver-typescript@1.0.2 @typescript-eslint/parser@3 --no-save
        fi
        if [ ${{ matrix.node-version }} -ge 7 ] && [ ${{ matrix.node-version }} -lt 8 ]; then
          npm install eslint@6 --no-save --ignore-scripts
          npm install
          npm install eslint-import-resolver-typescript@1.0.2 typescript-eslint-parser@20 --no-save
          npm uninstall @angular-eslint/template-parser --no-save
        fi
        if [ ${{ matrix.node-version }} -eq 8 ]; then
          npm install eslint@6 --no-save --ignore-scripts
          npm install
          npm uninstall @angular-eslint/template-parser --no-save
          npm install @typescript-eslint/parser@3 --no-save
        fi
        if [ ${{ matrix.node-version }} -gt 8 ] && [ ${{ matrix.node-version }} -lt 10 ]; then
          npm install eslint@7 --no-save --ignore-scripts
          npm install
          npm install @typescript-eslint/parser@3 --no-save
        fi
        if [ ${{ matrix.node-version }} -ge 10 ] && [ ${{ matrix.node-version }} -lt 12 ]; then
          npm install
          npm install @typescript-eslint/parser@4 --no-save
        fi
        if [ ${{ matrix.node-version }} -ge 12 ]; then
          npm install
        fi
        npm run copy-metafiles
        npm run pretest
        npm run mocha -- tests/src --timeout "$ESLINT_IMPORT_WSL_TEST_TIMEOUT"
 
    - name: install dependencies for node <= 10
      if: matrix.node-version <= '10' && matrix.configuration == 'native'
      run: |
        npm install --legacy-peer-deps
        npm install eslint@7 --no-save
 
    - name: Install dependencies for node > 10
      if: matrix.node-version > '10'  && matrix.configuration == 'native'
      run: npm install
 
    - name: install the latest version of nyc
      if: matrix.configuration == 'native'
      run: npm install nyc@latest --no-save
 
    - name: copy metafiles for node <= 8
      if: matrix.node-version <= 8 && matrix.configuration == 'native'
      env:
        ESLINT_VERSION: 6
        TRAVIS_NODE_VERSION: ${{ matrix.node-version }}
      run: |
        npm run copy-metafiles
        bash ./tests/dep-time-travel.sh 2>&1
    - name: copy metafiles for Node > 8
      if: matrix.node-version > 8 && matrix.configuration == 'native'
      env:
        ESLINT_VERSION: 7
        TRAVIS_NODE_VERSION: ${{ matrix.node-version }}
      run: |
        npm run copy-metafiles
        bash ./tests/dep-time-travel.sh 2>&1
 
    - name: install ./resolver dependencies in Native
      if: matrix.configuration == 'native'
      shell: pwsh
      run: |
        npm config set package-lock false
        $resolverDir = "./resolvers"
        Get-ChildItem -Directory $resolverDir |
          ForEach {
            Write-output $(Resolve-Path $(Join-Path $resolverDir  $_.Name))
            Push-Location $(Resolve-Path $(Join-Path $resolverDir  $_.Name))
              npm install
              npm ls nyc > $null;
              if ($?) {
                npm install nyc@latest --no-save
              }
              Pop-Location
            }
 
    - name: run tests in Native
      if: matrix.configuration == 'native'
      shell: pwsh
      run: |
        npm run pretest
        npm run tests-only
        $resolverDir = "./resolvers";
        $resolvers = @();
        Get-ChildItem -Directory $resolverDir |
          ForEach {
            $resolvers += "$(Resolve-Path $(Join-Path $resolverDir $_.Name))";
          }
        $env:RESOLVERS = [string]::Join(";", $resolvers);
        foreach ($resolver in $resolvers) {
            Set-Location -Path $resolver.Trim('"')
            npm run tests-only
            Set-Location -Path $PSScriptRoot
        }
 
    - name: codecov
      uses: codecov/codecov-action@v7.0.0
 
  windows:
    timeout-minutes: 30
    runs-on: latchkey-small
    needs: [build]
    steps:
      - run: true
 

What changed

2 third-party actions are referenced by a movable tag. Pin them to the commit SHA (Latchkey resolves and applies this automatically) so a repointed tag cannot change what runs.

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 (17 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