Skip to content

Update dependencies and workflows #444

Update dependencies and workflows

Update dependencies and workflows #444

Workflow file for this run

name: Tests
on: [push, pull_request]
jobs:
resolve-node-versions:
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.set-matrix.outputs.matrix }}
steps:
- id: set-matrix
run: |
node <<'EOF' >> $GITHUB_OUTPUT
const https = require('https');
const fs = require('fs');
function compareSemver(a, b) {
const pa = a.split('.').map(Number);
const pb = b.split('.').map(Number);
for (let i = 0; i < 3; i++) {
if ((pa[i] ?? 0) !== (pb[i] ?? 0)) return (pb[i] ?? 0) - (pa[i] ?? 0);
}
return 0;
}
function parseEnginesVersion(enginesNode) {
if (!enginesNode) return null;
const match = enginesNode.match(/>=\s*(\d+)(?:\.(\d+)(?:\.(\d+))?)?/);
if (!match) return null;
return {
major: parseInt(match[1], 10),
minor: match[2] !== undefined ? parseInt(match[2], 10) : null,
patch: match[3] !== undefined ? parseInt(match[3], 10) : null,
};
}
let enginesNode = null;
try {
const pkg = JSON.parse(fs.readFileSync('package.json', 'utf8'));
enginesNode = pkg?.engines?.node ?? null;
} catch {}
const minVersion = parseEnginesVersion(enginesNode);
https.get('https://nodejs.org/dist/index.json', res => {
let data = '';
res.on('data', chunk => data += chunk);
res.on('end', () => {
const releases = JSON.parse(data);
// All LTS versions, stripped of 'v' prefix
const lts = releases
.filter(r => r.lts)
.map(r => r.version.replace(/^v/, ''));
// Group by major, pick highest semver per major, sort descending, take top 4
const byMajor = {};
for (const v of lts) {
const major = parseInt(v.split('.')[0], 10);
if (!byMajor[major]) byMajor[major] = [];
byMajor[major].push(v);
}
const top4 = Object.entries(byMajor)
.map(([major, versions]) => {
versions.sort(compareSemver);
return versions[0]; // latest patch for this major
})
.sort(compareSemver)
.slice(0, 4);
// Find the latest LTS version satisfying the engines constraint:
// - major must match
// - if minor specified, minor must match
// - if patch specified, patch must match
let minMatch = null;
if (minVersion !== null) {
const candidates = lts.filter(v => {
const [maj, min, pat] = v.split('.').map(Number);
if (maj !== minVersion.major) return false;
if (minVersion.minor !== null && min !== minVersion.minor) return false;
if (minVersion.patch !== null && pat !== minVersion.patch) return false;
return true;
});
candidates.sort(compareSemver);
minMatch = candidates[0] ?? null; // highest version matching the constraint
}
// Add minMatch only if it isn't already in top4
const versions = [...top4];
if (minMatch !== null && !versions.includes(minMatch)) {
versions.push(minMatch);
}
// Sort ascending (oldest to newest) for natural CI output
versions.sort((a, b) => compareSemver(b, a));
const matrix = { 'node': versions };
console.log(`matrix=${JSON.stringify(matrix)}`);
});
});
EOF
test:
needs: resolve-node-versions
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix: ${{ fromJson(needs.resolve-node-versions.outputs.matrix) }}
name: Node ${{ matrix.node }}
steps:
- uses: actions/checkout@v4
- name: Use Node.js ${{ matrix.node }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
- name: Enable Corepack
run: corepack enable
- name: Detect Yarn version
id: yarn-version
run: |
YARN_VERSION=$(node -p "
try {
const pm = require('./package.json').packageManager ?? '';
const match = pm.match(/^yarn@(\d+)/);
match ? match[1] : '';
} catch { '' }
")
if [ -z "$YARN_VERSION" ]; then
YARN_VERSION=$(yarn --version | cut -d. -f1)
fi
echo "major=$YARN_VERSION" >> "$GITHUB_OUTPUT"
echo "Detected Yarn major version: $YARN_VERSION"
- name: Install dependencies
run: |
if [ "${{ steps.yarn-version.outputs.major }}" = "1" ]; then
yarn install --frozen-lockfile --non-interactive --prefer-offline
else
yarn install --immutable
fi
- name: Lint
run: yarn lint
- name: Test
run: yarn test