From 938d05b3e7f30c2406558c4af85949e1a4ca5619 Mon Sep 17 00:00:00 2001 From: d3xter666 Date: Tue, 14 Apr 2026 11:55:29 +0300 Subject: [PATCH 01/14] feat: Implement a script to compare versions --- internal/engine-compat/cli.js | 101 ++++++++++++++++++++++++ internal/engine-compat/eslint.config.js | 10 +++ internal/engine-compat/package.json | 20 +++++ package-lock.json | 21 +++++ 4 files changed, 152 insertions(+) create mode 100755 internal/engine-compat/cli.js create mode 100644 internal/engine-compat/eslint.config.js create mode 100644 internal/engine-compat/package.json diff --git a/internal/engine-compat/cli.js b/internal/engine-compat/cli.js new file mode 100755 index 00000000000..0443d4df532 --- /dev/null +++ b/internal/engine-compat/cli.js @@ -0,0 +1,101 @@ +#!/usr/bin/env node + +/** + * Engine Compatibility Checker + * + * Compares the project's engines.node range against every installed dependency's + * engines.node range using semver.subset(). + * + * Outputs JSON to stdout: {projectRange, incompatible: [{name, version, enginesNode}], checked, skipped} + * Exits with code 1 if incompatible packages are found. + */ + +import fs from "node:fs"; +import {execSync} from "node:child_process"; +import semver from "semver"; + +// --- Read project engine range --- + +const rootDir = process.cwd(); +const rootPkg = JSON.parse(fs.readFileSync(`${rootDir}/package.json`, "utf8")); +const projectRange = rootPkg.engines?.node; + +if (!projectRange) { + console.error("No engines.node field found in package.json"); + process.exit(1); +} + +// --- Collect dependencies from npm ls --- + +let npmOutput; +try { + npmOutput = execSync("npm ls --json --all", { + cwd: rootDir, + encoding: "utf8", + maxBuffer: 50 * 1024 * 1024, // 50 MB — monorepos produce large output + stdio: ["pipe", "pipe", "pipe"] + }); +} catch (error) { + // npm ls exits non-zero on extraneous/missing deps but still outputs valid JSON + npmOutput = error.stdout; + if (!npmOutput) { + console.error(`npm ls failed: ${error.message}`); + process.exit(1); + } +} + +const tree = JSON.parse(npmOutput); +const seen = new Map(); + +function walkTree(dependencies) { + if (!dependencies) return; + for (const [name, info] of Object.entries(dependencies)) { + // Skip workspace packages — they share root engine config + if (info.resolved && info.resolved.startsWith("file:")) continue; + + const key = `${name}@${info.version}`; + if (!seen.has(key)) { + seen.set(key, {name, version: info.version}); + } + walkTree(info.dependencies); + } +} + +walkTree(tree.dependencies); + +// --- Check each dependency's engine range --- + +const incompatible = []; +let checked = 0; +let skipped = 0; + +for (const {name, version} of seen.values()) { + let depRange; + try { + const depPkg = JSON.parse( + fs.readFileSync(`${rootDir}/node_modules/${name}/package.json`, "utf8") + ); + depRange = depPkg.engines?.node; + } catch { + // Not hoisted to root node_modules — skip + } + + if (!depRange) { + skipped++; + continue; + } + + checked++; + if (!semver.subset(projectRange, depRange)) { + incompatible.push({name, version, enginesNode: depRange}); + } +} + +// --- Output results --- + +const results = {projectRange, incompatible, checked, skipped}; +console.log(JSON.stringify(results)); + +if (incompatible.length > 0) { + process.exit(1); +} diff --git a/internal/engine-compat/eslint.config.js b/internal/engine-compat/eslint.config.js new file mode 100644 index 00000000000..e72630b9687 --- /dev/null +++ b/internal/engine-compat/eslint.config.js @@ -0,0 +1,10 @@ +import commonConfig from "../../eslint.common.config.js"; + +export default [ + ...commonConfig, + { + rules: { + "no-console": "off" // Allow console output in CLI tools + } + } +]; diff --git a/internal/engine-compat/package.json b/internal/engine-compat/package.json new file mode 100644 index 00000000000..aeab4bda56a --- /dev/null +++ b/internal/engine-compat/package.json @@ -0,0 +1,20 @@ +{ + "name": "@ui5-internal/engine-compat", + "private": true, + "license": "Apache-2.0", + "type": "module", + "engines": { + "node": "^22.20.0 || >=24.0.0", + "npm": ">= 8" + }, + "scripts": { + "test": "npm run lint", + "lint": "eslint ." + }, + "dependencies": { + "semver": "^7.7.2" + }, + "devDependencies": { + "eslint": "^9.39.1" + } +} diff --git a/package-lock.json b/package-lock.json index e849d341f04..5a36c3bd415 100644 --- a/package-lock.json +++ b/package-lock.json @@ -74,6 +74,23 @@ "npm": ">= 8" } }, + "internal/engine-compat": { + "name": "@ui5-internal/engine-compat", + "license": "Apache-2.0", + "dependencies": { + "semver": "^7.7.2" + }, + "bin": { + "ui5-engine-compat": "cli.js" + }, + "devDependencies": { + "eslint": "^9.39.1" + }, + "engines": { + "node": "^22.20.0 || >=24.0.0", + "npm": ">= 8" + } + }, "internal/shrinkwrap-extractor": { "name": "@ui5/shrinkwrap-extractor", "version": "1.0.0", @@ -5736,6 +5753,10 @@ "resolved": "internal/benchmark", "link": true }, + "node_modules/@ui5-internal/engine-compat": { + "resolved": "internal/engine-compat", + "link": true + }, "node_modules/@ui5/builder": { "resolved": "packages/builder", "link": true From 1df07e3e450c78f40f98582a73c33aaeeb51910b Mon Sep 17 00:00:00 2001 From: d3xter666 Date: Tue, 14 Apr 2026 12:09:48 +0300 Subject: [PATCH 02/14] ci: Enable engine checks --- .github/workflows/engine-compat.yml | 94 +++++++++++++++++++++++++++++ .github/workflows/github-ci.yml | 5 ++ 2 files changed, 99 insertions(+) create mode 100644 .github/workflows/engine-compat.yml diff --git a/.github/workflows/engine-compat.yml b/.github/workflows/engine-compat.yml new file mode 100644 index 00000000000..34360accc44 --- /dev/null +++ b/.github/workflows/engine-compat.yml @@ -0,0 +1,94 @@ +name: Node Packages Engine Compatibility Check + +on: + workflow_call: + +permissions: + contents: read + pull-requests: write + +jobs: + engine-compat: + name: Engine compatibility + runs-on: ubuntu-24.04 + steps: + + - uses: actions/checkout@v6 + + - name: Setup Node.js + uses: actions/setup-node@v6 + with: + node-version: 24.x + + - name: Install dependencies + run: npm ci + + - name: Check engine compatibility + id: check + run: | + # Run the checker script; capture JSON output even on failure (exit 1 = incompatible found) + set +e + OUTPUT=$(node internal/engine-compat/cli.js) + EXIT_CODE=$? + set -e + + echo "json=$OUTPUT" >> "$GITHUB_OUTPUT" + + if [ $EXIT_CODE -eq 0 ]; then + echo "failed=false" >> "$GITHUB_OUTPUT" + else + echo "failed=true" >> "$GITHUB_OUTPUT" + fi + + - name: Build and post PR comment + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + PR="${{ github.event.pull_request.number }}" + MARKER="" + FAILED="${{ steps.check.outputs.failed }}" + + # Parse JSON output from the checker script + RESULT='${{ steps.check.outputs.json }}' + PROJECT_RANGE=$(echo "$RESULT" | jq -r '.projectRange') + CHECKED=$(echo "$RESULT" | jq -r '.checked') + SKIPPED=$(echo "$RESULT" | jq -r '.skipped') + + # Build the comment body + if [ "$FAILED" = "true" ]; then ICON="❌"; else ICON="✅"; fi + + { + echo "$MARKER" + echo "## $ICON Engine Compatibility" + echo "" + echo "**Project engines.node:** \`$PROJECT_RANGE\`" + echo "**Packages checked:** $CHECKED | **Skipped (no engines):** $SKIPPED" + echo "" + } > /tmp/comment.md + + if [ "$FAILED" = "true" ]; then + { + echo "| Package | Version | Required Node |" + echo "|---|---|---|" + echo "$RESULT" | jq -r '.incompatible[] | "| \(.name) | \(.version) | \(.enginesNode | gsub("\\|"; "\\\\|")) |"' + } >> /tmp/comment.md + else + echo "All dependencies are compatible with the project's engine range." >> /tmp/comment.md + fi + + # Update existing comment (matched by HTML marker) or create a new one + OLD=$(gh api "repos/${{ github.repository }}/issues/${PR}/comments" \ + --jq "[.[] | select(.body | contains(\"$MARKER\"))][0].id" 2>/dev/null || true) + if [ -n "$OLD" ] && [ "$OLD" != "null" ]; then + gh api --method PATCH "repos/${{ github.repository }}/issues/comments/$OLD" -F body=@/tmp/comment.md + else + gh pr comment "$PR" --body-file /tmp/comment.md + fi + + # Add "blocked" label on failure, remove it on success + if [ "$FAILED" = "true" ]; then + gh label create blocked --color D93F0B 2>/dev/null || true + gh pr edit "$PR" --add-label blocked + else + gh pr edit "$PR" --remove-label blocked 2>/dev/null || true + fi diff --git a/.github/workflows/github-ci.yml b/.github/workflows/github-ci.yml index 18cf8c99bd5..cb84c8616b7 100644 --- a/.github/workflows/github-ci.yml +++ b/.github/workflows/github-ci.yml @@ -10,8 +10,13 @@ on: permissions: contents: read + pull-requests: write jobs: + engine-compat: + if: github.event_name == 'pull_request' + uses: ./.github/workflows/engine-compat.yml + test: name: General checks and tests runs-on: ubuntu-24.04 From 9937e4bf4e37aa4880b3626ddbb6312d50eaf683 Mon Sep 17 00:00:00 2001 From: d3xter666 Date: Tue, 14 Apr 2026 12:27:38 +0300 Subject: [PATCH 03/14] feat: Add case with check-engine-light --- .github/workflows/engine-compat2.yml | 102 +++++++++++++++++++++++++++ .github/workflows/github-ci.yml | 4 ++ package-lock.json | 24 ++++++- package.json | 1 + 4 files changed, 128 insertions(+), 3 deletions(-) create mode 100644 .github/workflows/engine-compat2.yml diff --git a/.github/workflows/engine-compat2.yml b/.github/workflows/engine-compat2.yml new file mode 100644 index 00000000000..d16fc8884dd --- /dev/null +++ b/.github/workflows/engine-compat2.yml @@ -0,0 +1,102 @@ +name: Node Packages Engine Compatibility Check (check-engine-light) + +on: + workflow_call: + +permissions: + contents: read + pull-requests: write + +jobs: + engine-compat: + name: Engine compatibility (check-engine-light) + runs-on: ubuntu-24.04 + steps: + + - uses: actions/checkout@v6 + + - name: Setup Node.js + uses: actions/setup-node@v6 + with: + node-version: 24.x + + - name: Install dependencies + run: npm ci + + - name: Check engine compatibility + id: check + run: | + # Run check-engine-light with --dev to include devDependencies + # On success: exit 0, no output + # On failure: exit 1, stderr contains "Error: incompatible dependencies: pkg1, pkg2, ..." + set +e + OUTPUT=$(check-engine-light --dev 2>&1) + EXIT_CODE=$? + set -e + + { + echo "output<> "$GITHUB_OUTPUT" + + if [ $EXIT_CODE -eq 0 ]; then + echo "failed=false" >> "$GITHUB_OUTPUT" + else + echo "failed=true" >> "$GITHUB_OUTPUT" + fi + + - name: Build and post PR comment + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + PR="${{ github.event.pull_request.number }}" + MARKER="" + FAILED="${{ steps.check.outputs.failed }}" + + # Read project engine range from package.json + PROJECT_RANGE=$(jq -r '.engines.node' package.json) + + # Build the comment body + if [ "$FAILED" = "true" ]; then ICON="❌"; else ICON="✅"; fi + + { + echo "$MARKER" + echo "## $ICON Engine Compatibility (check-engine-light)" + echo "" + echo "**Project engines.node:** \`$PROJECT_RANGE\`" + echo "" + } > /tmp/comment.md + + if [ "$FAILED" = "true" ]; then + # Extract package names from "incompatible dependencies: pkg1, pkg2, ..." + OUTPUT='${{ steps.check.outputs.output }}' + PKGS=$(echo "$OUTPUT" | grep -oP 'incompatible dependencies: \K.*' | sed 's/, /\n/g') + + { + echo "| Incompatible Package |" + echo "|---|" + echo "$PKGS" | while IFS= read -r pkg; do + echo "| $pkg |" + done + } >> /tmp/comment.md + else + echo "All dependencies are compatible with the project's engine range." >> /tmp/comment.md + fi + + # Update existing comment (matched by HTML marker) or create a new one + OLD=$(gh api "repos/${{ github.repository }}/issues/${PR}/comments" \ + --jq "[.[] | select(.body | contains(\"$MARKER\"))][0].id" 2>/dev/null || true) + if [ -n "$OLD" ] && [ "$OLD" != "null" ]; then + gh api --method PATCH "repos/${{ github.repository }}/issues/comments/$OLD" -F body=@/tmp/comment.md + else + gh pr comment "$PR" --body-file /tmp/comment.md + fi + + # Add "blocked" label on failure, remove it on success + if [ "$FAILED" = "true" ]; then + gh label create blocked --color D93F0B 2>/dev/null || true + gh pr edit "$PR" --add-label blocked + else + gh pr edit "$PR" --remove-label blocked 2>/dev/null || true + fi diff --git a/.github/workflows/github-ci.yml b/.github/workflows/github-ci.yml index cb84c8616b7..f2d94e513f4 100644 --- a/.github/workflows/github-ci.yml +++ b/.github/workflows/github-ci.yml @@ -17,6 +17,10 @@ jobs: if: github.event_name == 'pull_request' uses: ./.github/workflows/engine-compat.yml + engine-compat-cel: + if: github.event_name == 'pull_request' + uses: ./.github/workflows/engine-compat2.yml + test: name: General checks and tests runs-on: ubuntu-24.04 diff --git a/package-lock.json b/package-lock.json index 5a36c3bd415..bbfc9533eaf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,6 +16,7 @@ "@commitlint/cli": "^20.1.0", "@commitlint/config-conventional": "^20.0.0", "@eslint/js": "^9.8.0", + "check-engine-light": "^0.4.0", "eslint": "^9.35.0", "eslint-config-google": "^0.14.0", "eslint-plugin-ava": "^15.1.0", @@ -80,9 +81,6 @@ "dependencies": { "semver": "^7.7.2" }, - "bin": { - "ui5-engine-compat": "cli.js" - }, "devDependencies": { "eslint": "^9.39.1" }, @@ -7241,6 +7239,26 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/check-engine-light": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/check-engine-light/-/check-engine-light-0.4.0.tgz", + "integrity": "sha512-hZzy5cbJg52nDGgyiNDVpjzrIo6V49lpFVJ7hJiGpbWs9in5mtpRMqdM1VPptab2QTkwYdac98PaXSBmQqh1Tg==", + "dev": true, + "license": "ISC", + "dependencies": { + "debug": "^4.4.3", + "semver": "^7.7.3" + }, + "bin": { + "check-engine-light": "lib/cli.js" + }, + "engines": { + "node": "^20.11 || >=21.2" + }, + "funding": { + "url": "https://ko-fi.com/textbook" + } + }, "node_modules/cheerio": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0.tgz", diff --git a/package.json b/package.json index eb4e2286596..e93c10caa11 100644 --- a/package.json +++ b/package.json @@ -41,6 +41,7 @@ "@commitlint/cli": "^20.1.0", "@commitlint/config-conventional": "^20.0.0", "@eslint/js": "^9.8.0", + "check-engine-light": "^0.4.0", "eslint": "^9.35.0", "eslint-config-google": "^0.14.0", "eslint-plugin-ava": "^15.1.0", From ee4f24677e39f83f41dec857321b7cd33d5a1c6c Mon Sep 17 00:00:00 2001 From: d3xter666 Date: Tue, 14 Apr 2026 12:29:37 +0300 Subject: [PATCH 04/14] fix: Ignore in knip --- knip.config.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/knip.config.js b/knip.config.js index a5eeea701f8..5de713203bc 100644 --- a/knip.config.js +++ b/knip.config.js @@ -30,7 +30,8 @@ const config = { * Used via nyc ava --node-arguments="--experimental-loader=@istanbuljs/esm-loader-hook" * which is not detected by knip as a usage of this package */ - "@istanbuljs/esm-loader-hook" + "@istanbuljs/esm-loader-hook", + "check-engine-light" ], workspaces: { From b8aac74a7ace6937540d61ad27e9281d79e2c2ae Mon Sep 17 00:00:00 2001 From: d3xter666 Date: Tue, 14 Apr 2026 12:38:00 +0300 Subject: [PATCH 05/14] fix: Execute check-engine-light via npx --- .github/workflows/engine-compat2.yml | 2 +- knip.config.js | 3 +-- package-lock.json | 21 --------------------- package.json | 1 - 4 files changed, 2 insertions(+), 25 deletions(-) diff --git a/.github/workflows/engine-compat2.yml b/.github/workflows/engine-compat2.yml index d16fc8884dd..b1ec6e28e90 100644 --- a/.github/workflows/engine-compat2.yml +++ b/.github/workflows/engine-compat2.yml @@ -30,7 +30,7 @@ jobs: # On success: exit 0, no output # On failure: exit 1, stderr contains "Error: incompatible dependencies: pkg1, pkg2, ..." set +e - OUTPUT=$(check-engine-light --dev 2>&1) + OUTPUT=$(npx -y check-engine-light --dev 2>&1) EXIT_CODE=$? set -e diff --git a/knip.config.js b/knip.config.js index 5de713203bc..a5eeea701f8 100644 --- a/knip.config.js +++ b/knip.config.js @@ -30,8 +30,7 @@ const config = { * Used via nyc ava --node-arguments="--experimental-loader=@istanbuljs/esm-loader-hook" * which is not detected by knip as a usage of this package */ - "@istanbuljs/esm-loader-hook", - "check-engine-light" + "@istanbuljs/esm-loader-hook" ], workspaces: { diff --git a/package-lock.json b/package-lock.json index bbfc9533eaf..780fdc7fedc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,7 +16,6 @@ "@commitlint/cli": "^20.1.0", "@commitlint/config-conventional": "^20.0.0", "@eslint/js": "^9.8.0", - "check-engine-light": "^0.4.0", "eslint": "^9.35.0", "eslint-config-google": "^0.14.0", "eslint-plugin-ava": "^15.1.0", @@ -7239,26 +7238,6 @@ "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/check-engine-light": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/check-engine-light/-/check-engine-light-0.4.0.tgz", - "integrity": "sha512-hZzy5cbJg52nDGgyiNDVpjzrIo6V49lpFVJ7hJiGpbWs9in5mtpRMqdM1VPptab2QTkwYdac98PaXSBmQqh1Tg==", - "dev": true, - "license": "ISC", - "dependencies": { - "debug": "^4.4.3", - "semver": "^7.7.3" - }, - "bin": { - "check-engine-light": "lib/cli.js" - }, - "engines": { - "node": "^20.11 || >=21.2" - }, - "funding": { - "url": "https://ko-fi.com/textbook" - } - }, "node_modules/cheerio": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0.tgz", diff --git a/package.json b/package.json index e93c10caa11..eb4e2286596 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,6 @@ "@commitlint/cli": "^20.1.0", "@commitlint/config-conventional": "^20.0.0", "@eslint/js": "^9.8.0", - "check-engine-light": "^0.4.0", "eslint": "^9.35.0", "eslint-config-google": "^0.14.0", "eslint-plugin-ava": "^15.1.0", From 8c5a82d941d174439ee17f9adb713bee3baf5b43 Mon Sep 17 00:00:00 2001 From: d3xter666 Date: Tue, 14 Apr 2026 14:03:02 +0300 Subject: [PATCH 06/14] fix: Bash script --- .github/workflows/engine-compat2.yml | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/.github/workflows/engine-compat2.yml b/.github/workflows/engine-compat2.yml index b1ec6e28e90..1f60fdae8df 100644 --- a/.github/workflows/engine-compat2.yml +++ b/.github/workflows/engine-compat2.yml @@ -49,6 +49,7 @@ jobs: - name: Build and post PR comment env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + CEL_OUTPUT: ${{ steps.check.outputs.output }} run: | PR="${{ github.event.pull_request.number }}" MARKER="" @@ -69,15 +70,16 @@ jobs: } > /tmp/comment.md if [ "$FAILED" = "true" ]; then - # Extract package names from "incompatible dependencies: pkg1, pkg2, ..." - OUTPUT='${{ steps.check.outputs.output }}' - PKGS=$(echo "$OUTPUT" | grep -oP 'incompatible dependencies: \K.*' | sed 's/, /\n/g') - + # Extract per-package detail lines: "pkg { node: 'range' } incompatible with ..." + # and the summary line: "Error: incompatible dependencies: pkg1, pkg2, ..." { - echo "| Incompatible Package |" - echo "|---|" - echo "$PKGS" | while IFS= read -r pkg; do - echo "| $pkg |" + echo "| Package | Required Node |" + echo "|---|---|" + echo "$CEL_OUTPUT" | grep ' incompatible with ' | while IFS= read -r line; do + PKG=$(echo "$line" | awk '{print $1}') + RANGE=$(echo "$line" | grep -oP '\{ node: .?\K[^}'"'"']+') + SAFE_RANGE="${RANGE//|/\\|}" + echo "| $PKG | $SAFE_RANGE |" done } >> /tmp/comment.md else From 8fb1af4d7d7682f6269f11185ace29d1df2c5e70 Mon Sep 17 00:00:00 2001 From: d3xter666 Date: Tue, 14 Apr 2026 14:21:03 +0300 Subject: [PATCH 07/14] fix: Update scripts' range grep --- .github/workflows/engine-compat2.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/engine-compat2.yml b/.github/workflows/engine-compat2.yml index 1f60fdae8df..c6890dd0ce8 100644 --- a/.github/workflows/engine-compat2.yml +++ b/.github/workflows/engine-compat2.yml @@ -77,7 +77,7 @@ jobs: echo "|---|---|" echo "$CEL_OUTPUT" | grep ' incompatible with ' | while IFS= read -r line; do PKG=$(echo "$line" | awk '{print $1}') - RANGE=$(echo "$line" | grep -oP '\{ node: .?\K[^}'"'"']+') + RANGE=$(echo "$line" | grep -oP '\{ node: .?\K[^}'"'"']+' | head -1) SAFE_RANGE="${RANGE//|/\\|}" echo "| $PKG | $SAFE_RANGE |" done From 59527d09788456cefb43c8068e81c305d02584b7 Mon Sep 17 00:00:00 2001 From: d3xter666 Date: Tue, 14 Apr 2026 14:29:50 +0300 Subject: [PATCH 08/14] refactor: Cleanup unnecessary scripts --- .github/workflows/engine-compat.yml | 36 +++++--- .github/workflows/engine-compat2.yml | 104 ------------------------ .github/workflows/github-ci.yml | 4 - internal/engine-compat/cli.js | 101 ----------------------- internal/engine-compat/eslint.config.js | 10 --- internal/engine-compat/package.json | 20 ----- package-lock.json | 18 ---- 7 files changed, 23 insertions(+), 270 deletions(-) delete mode 100644 .github/workflows/engine-compat2.yml delete mode 100755 internal/engine-compat/cli.js delete mode 100644 internal/engine-compat/eslint.config.js delete mode 100644 internal/engine-compat/package.json diff --git a/.github/workflows/engine-compat.yml b/.github/workflows/engine-compat.yml index 34360accc44..891621f67b6 100644 --- a/.github/workflows/engine-compat.yml +++ b/.github/workflows/engine-compat.yml @@ -26,13 +26,19 @@ jobs: - name: Check engine compatibility id: check run: | - # Run the checker script; capture JSON output even on failure (exit 1 = incompatible found) + # Run check-engine-light with --dev to include devDependencies + # On success: exit 0, no output + # On failure: exit 1, stderr contains "Error: incompatible dependencies: pkg1, pkg2, ..." set +e - OUTPUT=$(node internal/engine-compat/cli.js) + OUTPUT=$(npx -y check-engine-light --dev 2>&1) EXIT_CODE=$? set -e - echo "json=$OUTPUT" >> "$GITHUB_OUTPUT" + { + echo "output<> "$GITHUB_OUTPUT" if [ $EXIT_CODE -eq 0 ]; then echo "failed=false" >> "$GITHUB_OUTPUT" @@ -43,34 +49,38 @@ jobs: - name: Build and post PR comment env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + CEL_OUTPUT: ${{ steps.check.outputs.output }} run: | PR="${{ github.event.pull_request.number }}" MARKER="" FAILED="${{ steps.check.outputs.failed }}" - # Parse JSON output from the checker script - RESULT='${{ steps.check.outputs.json }}' - PROJECT_RANGE=$(echo "$RESULT" | jq -r '.projectRange') - CHECKED=$(echo "$RESULT" | jq -r '.checked') - SKIPPED=$(echo "$RESULT" | jq -r '.skipped') + # Read project engine range from package.json + PROJECT_RANGE=$(jq -r '.engines.node' package.json) # Build the comment body if [ "$FAILED" = "true" ]; then ICON="❌"; else ICON="✅"; fi { echo "$MARKER" - echo "## $ICON Engine Compatibility" + echo "## $ICON Engine Compatibility (check-engine-light)" echo "" echo "**Project engines.node:** \`$PROJECT_RANGE\`" - echo "**Packages checked:** $CHECKED | **Skipped (no engines):** $SKIPPED" echo "" } > /tmp/comment.md if [ "$FAILED" = "true" ]; then + # Extract per-package detail lines: "pkg { node: 'range' } incompatible with ..." + # and the summary line: "Error: incompatible dependencies: pkg1, pkg2, ..." { - echo "| Package | Version | Required Node |" - echo "|---|---|---|" - echo "$RESULT" | jq -r '.incompatible[] | "| \(.name) | \(.version) | \(.enginesNode | gsub("\\|"; "\\\\|")) |"' + echo "| Package | Required Node |" + echo "|---|---|" + echo "$CEL_OUTPUT" | grep ' incompatible with ' | while IFS= read -r line; do + PKG=$(echo "$line" | awk '{print $1}') + RANGE=$(echo "$line" | grep -oP '\{ node: .?\K[^}'"'"']+' | head -1) + SAFE_RANGE="${RANGE//|/\\|}" + echo "| $PKG | $SAFE_RANGE |" + done } >> /tmp/comment.md else echo "All dependencies are compatible with the project's engine range." >> /tmp/comment.md diff --git a/.github/workflows/engine-compat2.yml b/.github/workflows/engine-compat2.yml deleted file mode 100644 index c6890dd0ce8..00000000000 --- a/.github/workflows/engine-compat2.yml +++ /dev/null @@ -1,104 +0,0 @@ -name: Node Packages Engine Compatibility Check (check-engine-light) - -on: - workflow_call: - -permissions: - contents: read - pull-requests: write - -jobs: - engine-compat: - name: Engine compatibility (check-engine-light) - runs-on: ubuntu-24.04 - steps: - - - uses: actions/checkout@v6 - - - name: Setup Node.js - uses: actions/setup-node@v6 - with: - node-version: 24.x - - - name: Install dependencies - run: npm ci - - - name: Check engine compatibility - id: check - run: | - # Run check-engine-light with --dev to include devDependencies - # On success: exit 0, no output - # On failure: exit 1, stderr contains "Error: incompatible dependencies: pkg1, pkg2, ..." - set +e - OUTPUT=$(npx -y check-engine-light --dev 2>&1) - EXIT_CODE=$? - set -e - - { - echo "output<> "$GITHUB_OUTPUT" - - if [ $EXIT_CODE -eq 0 ]; then - echo "failed=false" >> "$GITHUB_OUTPUT" - else - echo "failed=true" >> "$GITHUB_OUTPUT" - fi - - - name: Build and post PR comment - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - CEL_OUTPUT: ${{ steps.check.outputs.output }} - run: | - PR="${{ github.event.pull_request.number }}" - MARKER="" - FAILED="${{ steps.check.outputs.failed }}" - - # Read project engine range from package.json - PROJECT_RANGE=$(jq -r '.engines.node' package.json) - - # Build the comment body - if [ "$FAILED" = "true" ]; then ICON="❌"; else ICON="✅"; fi - - { - echo "$MARKER" - echo "## $ICON Engine Compatibility (check-engine-light)" - echo "" - echo "**Project engines.node:** \`$PROJECT_RANGE\`" - echo "" - } > /tmp/comment.md - - if [ "$FAILED" = "true" ]; then - # Extract per-package detail lines: "pkg { node: 'range' } incompatible with ..." - # and the summary line: "Error: incompatible dependencies: pkg1, pkg2, ..." - { - echo "| Package | Required Node |" - echo "|---|---|" - echo "$CEL_OUTPUT" | grep ' incompatible with ' | while IFS= read -r line; do - PKG=$(echo "$line" | awk '{print $1}') - RANGE=$(echo "$line" | grep -oP '\{ node: .?\K[^}'"'"']+' | head -1) - SAFE_RANGE="${RANGE//|/\\|}" - echo "| $PKG | $SAFE_RANGE |" - done - } >> /tmp/comment.md - else - echo "All dependencies are compatible with the project's engine range." >> /tmp/comment.md - fi - - # Update existing comment (matched by HTML marker) or create a new one - OLD=$(gh api "repos/${{ github.repository }}/issues/${PR}/comments" \ - --jq "[.[] | select(.body | contains(\"$MARKER\"))][0].id" 2>/dev/null || true) - if [ -n "$OLD" ] && [ "$OLD" != "null" ]; then - gh api --method PATCH "repos/${{ github.repository }}/issues/comments/$OLD" -F body=@/tmp/comment.md - else - gh pr comment "$PR" --body-file /tmp/comment.md - fi - - # Add "blocked" label on failure, remove it on success - if [ "$FAILED" = "true" ]; then - gh label create blocked --color D93F0B 2>/dev/null || true - gh pr edit "$PR" --add-label blocked - else - gh pr edit "$PR" --remove-label blocked 2>/dev/null || true - fi diff --git a/.github/workflows/github-ci.yml b/.github/workflows/github-ci.yml index f2d94e513f4..cb84c8616b7 100644 --- a/.github/workflows/github-ci.yml +++ b/.github/workflows/github-ci.yml @@ -17,10 +17,6 @@ jobs: if: github.event_name == 'pull_request' uses: ./.github/workflows/engine-compat.yml - engine-compat-cel: - if: github.event_name == 'pull_request' - uses: ./.github/workflows/engine-compat2.yml - test: name: General checks and tests runs-on: ubuntu-24.04 diff --git a/internal/engine-compat/cli.js b/internal/engine-compat/cli.js deleted file mode 100755 index 0443d4df532..00000000000 --- a/internal/engine-compat/cli.js +++ /dev/null @@ -1,101 +0,0 @@ -#!/usr/bin/env node - -/** - * Engine Compatibility Checker - * - * Compares the project's engines.node range against every installed dependency's - * engines.node range using semver.subset(). - * - * Outputs JSON to stdout: {projectRange, incompatible: [{name, version, enginesNode}], checked, skipped} - * Exits with code 1 if incompatible packages are found. - */ - -import fs from "node:fs"; -import {execSync} from "node:child_process"; -import semver from "semver"; - -// --- Read project engine range --- - -const rootDir = process.cwd(); -const rootPkg = JSON.parse(fs.readFileSync(`${rootDir}/package.json`, "utf8")); -const projectRange = rootPkg.engines?.node; - -if (!projectRange) { - console.error("No engines.node field found in package.json"); - process.exit(1); -} - -// --- Collect dependencies from npm ls --- - -let npmOutput; -try { - npmOutput = execSync("npm ls --json --all", { - cwd: rootDir, - encoding: "utf8", - maxBuffer: 50 * 1024 * 1024, // 50 MB — monorepos produce large output - stdio: ["pipe", "pipe", "pipe"] - }); -} catch (error) { - // npm ls exits non-zero on extraneous/missing deps but still outputs valid JSON - npmOutput = error.stdout; - if (!npmOutput) { - console.error(`npm ls failed: ${error.message}`); - process.exit(1); - } -} - -const tree = JSON.parse(npmOutput); -const seen = new Map(); - -function walkTree(dependencies) { - if (!dependencies) return; - for (const [name, info] of Object.entries(dependencies)) { - // Skip workspace packages — they share root engine config - if (info.resolved && info.resolved.startsWith("file:")) continue; - - const key = `${name}@${info.version}`; - if (!seen.has(key)) { - seen.set(key, {name, version: info.version}); - } - walkTree(info.dependencies); - } -} - -walkTree(tree.dependencies); - -// --- Check each dependency's engine range --- - -const incompatible = []; -let checked = 0; -let skipped = 0; - -for (const {name, version} of seen.values()) { - let depRange; - try { - const depPkg = JSON.parse( - fs.readFileSync(`${rootDir}/node_modules/${name}/package.json`, "utf8") - ); - depRange = depPkg.engines?.node; - } catch { - // Not hoisted to root node_modules — skip - } - - if (!depRange) { - skipped++; - continue; - } - - checked++; - if (!semver.subset(projectRange, depRange)) { - incompatible.push({name, version, enginesNode: depRange}); - } -} - -// --- Output results --- - -const results = {projectRange, incompatible, checked, skipped}; -console.log(JSON.stringify(results)); - -if (incompatible.length > 0) { - process.exit(1); -} diff --git a/internal/engine-compat/eslint.config.js b/internal/engine-compat/eslint.config.js deleted file mode 100644 index e72630b9687..00000000000 --- a/internal/engine-compat/eslint.config.js +++ /dev/null @@ -1,10 +0,0 @@ -import commonConfig from "../../eslint.common.config.js"; - -export default [ - ...commonConfig, - { - rules: { - "no-console": "off" // Allow console output in CLI tools - } - } -]; diff --git a/internal/engine-compat/package.json b/internal/engine-compat/package.json deleted file mode 100644 index aeab4bda56a..00000000000 --- a/internal/engine-compat/package.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "name": "@ui5-internal/engine-compat", - "private": true, - "license": "Apache-2.0", - "type": "module", - "engines": { - "node": "^22.20.0 || >=24.0.0", - "npm": ">= 8" - }, - "scripts": { - "test": "npm run lint", - "lint": "eslint ." - }, - "dependencies": { - "semver": "^7.7.2" - }, - "devDependencies": { - "eslint": "^9.39.1" - } -} diff --git a/package-lock.json b/package-lock.json index 780fdc7fedc..e849d341f04 100644 --- a/package-lock.json +++ b/package-lock.json @@ -74,20 +74,6 @@ "npm": ">= 8" } }, - "internal/engine-compat": { - "name": "@ui5-internal/engine-compat", - "license": "Apache-2.0", - "dependencies": { - "semver": "^7.7.2" - }, - "devDependencies": { - "eslint": "^9.39.1" - }, - "engines": { - "node": "^22.20.0 || >=24.0.0", - "npm": ">= 8" - } - }, "internal/shrinkwrap-extractor": { "name": "@ui5/shrinkwrap-extractor", "version": "1.0.0", @@ -5750,10 +5736,6 @@ "resolved": "internal/benchmark", "link": true }, - "node_modules/@ui5-internal/engine-compat": { - "resolved": "internal/engine-compat", - "link": true - }, "node_modules/@ui5/builder": { "resolved": "packages/builder", "link": true From b30450995c35f333945b7118b54a868c89fb1643 Mon Sep 17 00:00:00 2001 From: d3xter666 Date: Tue, 14 Apr 2026 14:38:39 +0300 Subject: [PATCH 09/14] refactor: Skip dev dependencies --- .github/workflows/engine-compat.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/engine-compat.yml b/.github/workflows/engine-compat.yml index 891621f67b6..b2153653ad1 100644 --- a/.github/workflows/engine-compat.yml +++ b/.github/workflows/engine-compat.yml @@ -26,11 +26,11 @@ jobs: - name: Check engine compatibility id: check run: | - # Run check-engine-light with --dev to include devDependencies + # Run check-engine-light # On success: exit 0, no output # On failure: exit 1, stderr contains "Error: incompatible dependencies: pkg1, pkg2, ..." set +e - OUTPUT=$(npx -y check-engine-light --dev 2>&1) + OUTPUT=$(npx -y check-engine-light 2>&1) EXIT_CODE=$? set -e @@ -63,7 +63,7 @@ jobs: { echo "$MARKER" - echo "## $ICON Engine Compatibility (check-engine-light)" + echo "## $ICON Engine Compatibility" echo "" echo "**Project engines.node:** \`$PROJECT_RANGE\`" echo "" From 70770daa301c0c3f7d07f21c26825b9920a0ef4b Mon Sep 17 00:00:00 2001 From: d3xter666 Date: Tue, 14 Apr 2026 14:53:07 +0300 Subject: [PATCH 10/14] refactor: Remove --engine-strict from npm ci --- .github/workflows/github-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/github-ci.yml b/.github/workflows/github-ci.yml index cb84c8616b7..75fa9cc74c5 100644 --- a/.github/workflows/github-ci.yml +++ b/.github/workflows/github-ci.yml @@ -30,7 +30,7 @@ jobs: node-version: 22.20.0 - name: Install dependencies - run: npm ci --engine-strict # --engine-strict is used to fail-fast if deps require node versions unsupported by the repo + run: npm ci - name: Check package.json "engines" consistency run: | From a722aeeeaec7422fc04a4368b0a748ac20ae16cc Mon Sep 17 00:00:00 2001 From: d3xter666 Date: Thu, 16 Apr 2026 13:26:25 +0300 Subject: [PATCH 11/14] refactor: Choose the simple option to use only the tool --- .github/workflows/engine-compat.yml | 104 ---------------------------- .github/workflows/github-ci.yml | 7 +- 2 files changed, 4 insertions(+), 107 deletions(-) delete mode 100644 .github/workflows/engine-compat.yml diff --git a/.github/workflows/engine-compat.yml b/.github/workflows/engine-compat.yml deleted file mode 100644 index b2153653ad1..00000000000 --- a/.github/workflows/engine-compat.yml +++ /dev/null @@ -1,104 +0,0 @@ -name: Node Packages Engine Compatibility Check - -on: - workflow_call: - -permissions: - contents: read - pull-requests: write - -jobs: - engine-compat: - name: Engine compatibility - runs-on: ubuntu-24.04 - steps: - - - uses: actions/checkout@v6 - - - name: Setup Node.js - uses: actions/setup-node@v6 - with: - node-version: 24.x - - - name: Install dependencies - run: npm ci - - - name: Check engine compatibility - id: check - run: | - # Run check-engine-light - # On success: exit 0, no output - # On failure: exit 1, stderr contains "Error: incompatible dependencies: pkg1, pkg2, ..." - set +e - OUTPUT=$(npx -y check-engine-light 2>&1) - EXIT_CODE=$? - set -e - - { - echo "output<> "$GITHUB_OUTPUT" - - if [ $EXIT_CODE -eq 0 ]; then - echo "failed=false" >> "$GITHUB_OUTPUT" - else - echo "failed=true" >> "$GITHUB_OUTPUT" - fi - - - name: Build and post PR comment - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - CEL_OUTPUT: ${{ steps.check.outputs.output }} - run: | - PR="${{ github.event.pull_request.number }}" - MARKER="" - FAILED="${{ steps.check.outputs.failed }}" - - # Read project engine range from package.json - PROJECT_RANGE=$(jq -r '.engines.node' package.json) - - # Build the comment body - if [ "$FAILED" = "true" ]; then ICON="❌"; else ICON="✅"; fi - - { - echo "$MARKER" - echo "## $ICON Engine Compatibility" - echo "" - echo "**Project engines.node:** \`$PROJECT_RANGE\`" - echo "" - } > /tmp/comment.md - - if [ "$FAILED" = "true" ]; then - # Extract per-package detail lines: "pkg { node: 'range' } incompatible with ..." - # and the summary line: "Error: incompatible dependencies: pkg1, pkg2, ..." - { - echo "| Package | Required Node |" - echo "|---|---|" - echo "$CEL_OUTPUT" | grep ' incompatible with ' | while IFS= read -r line; do - PKG=$(echo "$line" | awk '{print $1}') - RANGE=$(echo "$line" | grep -oP '\{ node: .?\K[^}'"'"']+' | head -1) - SAFE_RANGE="${RANGE//|/\\|}" - echo "| $PKG | $SAFE_RANGE |" - done - } >> /tmp/comment.md - else - echo "All dependencies are compatible with the project's engine range." >> /tmp/comment.md - fi - - # Update existing comment (matched by HTML marker) or create a new one - OLD=$(gh api "repos/${{ github.repository }}/issues/${PR}/comments" \ - --jq "[.[] | select(.body | contains(\"$MARKER\"))][0].id" 2>/dev/null || true) - if [ -n "$OLD" ] && [ "$OLD" != "null" ]; then - gh api --method PATCH "repos/${{ github.repository }}/issues/comments/$OLD" -F body=@/tmp/comment.md - else - gh pr comment "$PR" --body-file /tmp/comment.md - fi - - # Add "blocked" label on failure, remove it on success - if [ "$FAILED" = "true" ]; then - gh label create blocked --color D93F0B 2>/dev/null || true - gh pr edit "$PR" --add-label blocked - else - gh pr edit "$PR" --remove-label blocked 2>/dev/null || true - fi diff --git a/.github/workflows/github-ci.yml b/.github/workflows/github-ci.yml index 75fa9cc74c5..8bfc9ad8e20 100644 --- a/.github/workflows/github-ci.yml +++ b/.github/workflows/github-ci.yml @@ -13,9 +13,6 @@ permissions: pull-requests: write jobs: - engine-compat: - if: github.event_name == 'pull_request' - uses: ./.github/workflows/engine-compat.yml test: name: General checks and tests @@ -29,6 +26,10 @@ jobs: with: node-version: 22.20.0 + # Check if "engines" field of dependencies is compatible with our Node.js version. + # This is a quick check to fail early if incompatible dependencies are introduced. + - name: Check Node.js engine compatibility + run: npx -y check-engine-light - name: Install dependencies run: npm ci From 401fb8a2ae6a60c13fccdd8612a4a7129ff3aa5f Mon Sep 17 00:00:00 2001 From: d3xter666 Date: Thu, 16 Apr 2026 14:36:50 +0300 Subject: [PATCH 12/14] ci: Add check-engine-light to the list of deps --- knip.config.js | 6 +++++- package-lock.json | 21 +++++++++++++++++++++ package.json | 3 ++- 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/knip.config.js b/knip.config.js index a5eeea701f8..a8706bc8ff4 100644 --- a/knip.config.js +++ b/knip.config.js @@ -39,7 +39,11 @@ const config = { /** * Invoked manually via "npx husky" to install git hooks */ - "husky" + "husky", + /** + * Invoked manually via "npx check-engine-light" to check for incompatible engines in productive deps + */ + "check-engine-light", ] }, "packages/cli": { diff --git a/package-lock.json b/package-lock.json index e849d341f04..e90b86a4503 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,6 +16,7 @@ "@commitlint/cli": "^20.1.0", "@commitlint/config-conventional": "^20.0.0", "@eslint/js": "^9.8.0", + "check-engine-light": "^0.4.0", "eslint": "^9.35.0", "eslint-config-google": "^0.14.0", "eslint-plugin-ava": "^15.1.0", @@ -7220,6 +7221,26 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/check-engine-light": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/check-engine-light/-/check-engine-light-0.4.0.tgz", + "integrity": "sha512-hZzy5cbJg52nDGgyiNDVpjzrIo6V49lpFVJ7hJiGpbWs9in5mtpRMqdM1VPptab2QTkwYdac98PaXSBmQqh1Tg==", + "dev": true, + "license": "ISC", + "dependencies": { + "debug": "^4.4.3", + "semver": "^7.7.3" + }, + "bin": { + "check-engine-light": "lib/cli.js" + }, + "engines": { + "node": "^20.11 || >=21.2" + }, + "funding": { + "url": "https://ko-fi.com/textbook" + } + }, "node_modules/cheerio": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0.tgz", diff --git a/package.json b/package.json index eb4e2286596..f095339aab8 100644 --- a/package.json +++ b/package.json @@ -48,7 +48,8 @@ "globals": "^17.3.0", "husky": "^9.1.7", "knip": "^5.83.0", - "licensee": "^11.1.1" + "licensee": "^11.1.1", + "check-engine-light": "^0.4.0" }, "overrides": { "pacote@<=20": { From 4629936dfb1e8b46f7051ae1a774d9e14c4ad228 Mon Sep 17 00:00:00 2001 From: Merlin Beutlberger Date: Thu, 16 Apr 2026 14:02:56 +0200 Subject: [PATCH 13/14] ci: Add npm script for engine check --- .github/workflows/github-ci.yml | 8 +++----- knip.config.js | 6 +----- package.json | 1 + 3 files changed, 5 insertions(+), 10 deletions(-) diff --git a/.github/workflows/github-ci.yml b/.github/workflows/github-ci.yml index 8bfc9ad8e20..c164066ca5e 100644 --- a/.github/workflows/github-ci.yml +++ b/.github/workflows/github-ci.yml @@ -10,7 +10,6 @@ on: permissions: contents: read - pull-requests: write jobs: @@ -26,13 +25,12 @@ jobs: with: node-version: 22.20.0 - # Check if "engines" field of dependencies is compatible with our Node.js version. - # This is a quick check to fail early if incompatible dependencies are introduced. - - name: Check Node.js engine compatibility - run: npx -y check-engine-light - name: Install dependencies run: npm ci + - name: Check Node.js engine compatibility + run: npm run check-engine + - name: Check package.json "engines" consistency run: | node << 'EOF' diff --git a/knip.config.js b/knip.config.js index a8706bc8ff4..a5eeea701f8 100644 --- a/knip.config.js +++ b/knip.config.js @@ -39,11 +39,7 @@ const config = { /** * Invoked manually via "npx husky" to install git hooks */ - "husky", - /** - * Invoked manually via "npx check-engine-light" to check for incompatible engines in productive deps - */ - "check-engine-light", + "husky" ] }, "packages/cli": { diff --git a/package.json b/package.json index f095339aab8..d1de7e40e2c 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,7 @@ "unit": "npm run unit --workspaces --if-present", "coverage": "npm run coverage --workspaces --if-present", "knip": "knip --config knip.config.js", + "check-engine": "check-engine-light .", "check-licenses": "licensee --errors-only", "schema-generate": "npm run schema-generate --workspace=@ui5/documentation", "generate-cli-doc": "npm run generate-cli-doc --workspace=@ui5/documentation" From 86c1e00eaff22a9b6165ba90c13227a954ad7a98 Mon Sep 17 00:00:00 2001 From: Merlin Beutlberger Date: Fri, 17 Apr 2026 10:07:01 +0200 Subject: [PATCH 14/14] ci: Pin check-engine-light dependency --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index e90b86a4503..0c94af61718 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,7 +16,7 @@ "@commitlint/cli": "^20.1.0", "@commitlint/config-conventional": "^20.0.0", "@eslint/js": "^9.8.0", - "check-engine-light": "^0.4.0", + "check-engine-light": "0.4.0", "eslint": "^9.35.0", "eslint-config-google": "^0.14.0", "eslint-plugin-ava": "^15.1.0", diff --git a/package.json b/package.json index d1de7e40e2c..064b1dd4754 100644 --- a/package.json +++ b/package.json @@ -50,7 +50,7 @@ "husky": "^9.1.7", "knip": "^5.83.0", "licensee": "^11.1.1", - "check-engine-light": "^0.4.0" + "check-engine-light": "0.4.0" }, "overrides": { "pacote@<=20": {