From c4ee71409ee56202eb01712dfac6fd3e23eb8a3c Mon Sep 17 00:00:00 2001 From: Matiss Janis Aboltins Date: Tue, 17 Mar 2026 16:00:34 +0000 Subject: [PATCH] [AI] Add Yarn constraints to enforce consistent dependency versions (#7229) * [AI] Add yarn constraints to enforce consistent dependency versions Adds a `yarn.config.cjs` that uses Yarn 4's built-in constraints feature to detect when the same dependency is declared with different version ranges across workspaces. Workspace protocol references and peerDependencies are excluded from the check. Also adds a `yarn constraints` convenience script and the `@yarnpkg/types` dev dependency for type-checked constraint authoring. https://claude.ai/code/session_01B1xRjZXn6b18anZjo8cbqb * Add release notes for PR #7229 * Add constraints job to GitHub Actions workflow * Fix constraints --------- Co-authored-by: Claude Co-authored-by: github-actions[bot] --- .github/workflows/check.yml | 10 +++++++ package.json | 2 ++ packages/docs/package.json | 4 +-- upcoming-release-notes/7229.md | 6 ++++ yarn.config.cjs | 54 ++++++++++++++++++++++++++++++++++ yarn.lock | 18 +++++++++--- 6 files changed, 88 insertions(+), 6 deletions(-) create mode 100644 upcoming-release-notes/7229.md create mode 100644 yarn.config.cjs diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 9162014604..28765b5df0 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -12,6 +12,16 @@ concurrency: cancel-in-progress: ${{ github.ref != 'refs/heads/master' }} jobs: + constraints: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + - name: Set up environment + uses: ./.github/actions/setup + with: + download-translations: 'false' + - name: Check dependency version consistency + run: yarn constraints lint: runs-on: ubuntu-latest steps: diff --git a/package.json b/package.json index abc52cc1d4..0ade6b4146 100644 --- a/package.json +++ b/package.json @@ -57,6 +57,7 @@ "lint": "oxfmt --check . && oxlint --type-aware --quiet", "lint:fix": "oxfmt . && oxlint --fix --type-aware --quiet", "install:server": "yarn workspaces focus @actual-app/sync-server --production", + "constraints": "yarn constraints", "typecheck": "tsgo -p tsconfig.root.json --noEmit && lage typecheck", "jq": "./node_modules/node-jq/bin/jq", "prepare": "husky" @@ -66,6 +67,7 @@ "@types/node": "^22.19.10", "@types/prompts": "^2.4.9", "@typescript/native-preview": "^7.0.0-dev.20260309.1", + "@yarnpkg/types": "^4.0.1", "baseline-browser-mapping": "^2.9.19", "cross-env": "^10.1.0", "eslint": "^9.39.2", diff --git a/packages/docs/package.json b/packages/docs/package.json index d6382959c2..61389063a3 100644 --- a/packages/docs/package.json +++ b/packages/docs/package.json @@ -25,8 +25,8 @@ "@r74tech/docusaurus-plugin-panzoom": "^2.4.0", "clsx": "^2.1.1", "prism-react-renderer": "^2.4.1", - "react": "^19.2.4", - "react-dom": "^19.2.4" + "react": "19.2.4", + "react-dom": "19.2.4" }, "devDependencies": { "@docusaurus/module-type-aliases": "^3.9.2", diff --git a/upcoming-release-notes/7229.md b/upcoming-release-notes/7229.md new file mode 100644 index 0000000000..159b6e9b85 --- /dev/null +++ b/upcoming-release-notes/7229.md @@ -0,0 +1,6 @@ +--- +category: Maintenance +authors: [MatissJanis] +--- + +Add Yarn constraints to ensure consistent dependency versions across all workspace packages. diff --git a/yarn.config.cjs b/yarn.config.cjs new file mode 100644 index 0000000000..d16c6d795f --- /dev/null +++ b/yarn.config.cjs @@ -0,0 +1,54 @@ +// @ts-check + +/** @type {import('@yarnpkg/types')} */ +const { defineConfig } = require('@yarnpkg/types'); + +/** + * Enforce that all workspaces use the same version of any shared dependency. + * Workspace protocol references (workspace:*) and peerDependencies are + * excluded since they intentionally use different/wider ranges. + * + * @param {import('@yarnpkg/types').Yarn.Constraints.Context} context + */ +function enforceConsistentDependencies({ Yarn }) { + /** @type {Map>>} */ + const dependencyVersions = new Map(); + + for (const workspace of Yarn.workspaces()) { + for (const type of ['dependencies', 'devDependencies']) { + for (const dep of Yarn.dependencies({ workspace, type })) { + if (dep.range.startsWith('workspace:')) continue; + + if (!dependencyVersions.has(dep.ident)) { + dependencyVersions.set(dep.ident, new Map()); + } + const versions = dependencyVersions.get(dep.ident); + if (!versions.has(dep.range)) { + versions.set(dep.range, []); + } + versions.get(dep.range).push(workspace); + } + } + } + + for (const [ident, versions] of dependencyVersions) { + if (versions.size <= 1) continue; + + const rangeList = [...versions.keys()].join(', '); + + for (const [, workspaces] of versions) { + for (const workspace of workspaces) { + workspace.error( + `Package "${ident}" has inconsistent versions across workspaces (${rangeList}). ` + + `All workspaces must use the same version range.`, + ); + } + } + } +} + +module.exports = defineConfig({ + async constraints(ctx) { + enforceConsistentDependencies(ctx); + }, +}); diff --git a/yarn.lock b/yarn.lock index f645a7f1d8..789783f429 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11242,6 +11242,15 @@ __metadata: languageName: node linkType: hard +"@yarnpkg/types@npm:^4.0.1": + version: 4.0.1 + resolution: "@yarnpkg/types@npm:4.0.1" + dependencies: + tslib: "npm:^2.4.0" + checksum: 10/f391763cd955356e9aad551b29e8de7bbf68a6c8992af7cdc950ccf53f8aff6695ad81aa4c8a8e7c582786a840a4f30617732e2cb49f4109b971a9242c31c9fc + languageName: node + linkType: hard + "abbrev@npm:^3.0.0": version: 3.0.1 resolution: "abbrev@npm:3.0.1" @@ -11322,6 +11331,7 @@ __metadata: "@types/node": "npm:^22.19.10" "@types/prompts": "npm:^2.4.9" "@typescript/native-preview": "npm:^7.0.0-dev.20260309.1" + "@yarnpkg/types": "npm:^4.0.1" baseline-browser-mapping: "npm:^2.9.19" cross-env: "npm:^10.1.0" eslint: "npm:^9.39.2" @@ -15199,8 +15209,8 @@ __metadata: "@types/react": "npm:^19.2.14" clsx: "npm:^2.1.1" prism-react-renderer: "npm:^2.4.1" - react: "npm:^19.2.4" - react-dom: "npm:^19.2.4" + react: "npm:19.2.4" + react-dom: "npm:19.2.4" languageName: unknown linkType: soft @@ -24779,7 +24789,7 @@ __metadata: languageName: node linkType: hard -"react-dom@npm:19.2.4, react-dom@npm:^19.2.4": +"react-dom@npm:19.2.4": version: 19.2.4 resolution: "react-dom@npm:19.2.4" dependencies: @@ -25227,7 +25237,7 @@ __metadata: languageName: node linkType: hard -"react@npm:19.2.4, react@npm:^19.2.4": +"react@npm:19.2.4": version: 19.2.4 resolution: "react@npm:19.2.4" checksum: 10/18179fe217f67eb2d0bc61cd04e7ad3c282ea09a1dface7eacd71816f62609f4bbf566c447c704335284deb8397b00bca084e0cd60e6f437279a7498e2d0bfe0