Skip to content

Update dependencies and workflows #441

Update dependencies and workflows

Update dependencies and workflows #441

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;
}
// Returns { major, minor, patch, minorSpecified, patchSpecified } or null
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,
minorSpecified: match[2] !== undefined,
patchSpecified: match[3] !== undefined,
};
}
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);
// Filter LTS only, strip 'v' prefix
const lts = releases
.filter(r => r.lts)
.map(r => r.version.replace(/^v/, ''));
// Group by major, pick highest semver per major
const byMajor = {};
for (const v of lts) {
const major = parseInt(v.split('.')[0], 10);
if (!byMajor[major]) byMajor[major] = [];
byMajor[major].push(v);
}
const latestPerMajor = Object.entries(byMajor)
.map(([major, versions]) => {
versions.sort(compareSemver);
return { major: parseInt(major, 10), version: versions[0] };
});
// Sort majors descending
latestPerMajor.sort((a, b) => b.major - a.major);
// Take latest 4 LTS majors
const top4 = latestPerMajor.slice(0, 4);
const includedVersions = new Set(top4.map(e => e.version));
const extra = [];
if (minVersion !== null) {
// Resolve the exact minimum version string to test:
// - patch specified (>= 18.18.2): use exactly 18.18.2
// - minor specified, no patch (>= 18.18): use latest 18.18.x
// - only major (>= 18): use latest 18.x.x (same as latestPerMajor entry)
let minExact = null;
if (minVersion.patchSpecified) {
// Use the exact version if it exists in LTS releases
const candidate = `${minVersion.major}.${minVersion.minor}.${minVersion.patch}`;
if (lts.includes(candidate)) minExact = candidate;
} else if (minVersion.minorSpecified) {
// Find the latest patch for this major.minor
const candidates = lts.filter(v => {
const [maj, min] = v.split('.').map(Number);
return maj === minVersion.major && min === minVersion.minor;
});
candidates.sort(compareSemver);
if (candidates.length > 0) minExact = candidates[0]; // highest patch
}
// If only major specified, minExact stays null — latestPerMajor already covers it
// Add the resolved minimum version if not already included
if (minExact && !includedVersions.has(minExact)) {
extra.push({ major: minVersion.major, version: minExact });
includedVersions.add(minExact);
}
// Always also add the latest patch of the minimum major if not already included
const latestOfMinMajor = latestPerMajor.find(e => e.major === minVersion.major);
if (latestOfMinMajor && !includedVersions.has(latestOfMinMajor.version)) {
extra.push(latestOfMinMajor);
includedVersions.add(latestOfMinMajor.version);
}
}
// Merge and sort ascending (oldest -> newest)
const all = [...top4, ...extra];
all.sort((a, b) => compareSemver(b.version, a.version));
const matrix = { "node-version": all.map(e => e.version) };
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-version }}
steps:
- uses: actions/checkout@v4
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- 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