From 577cbd62ec96b4435b972020811a6393014e5e4b Mon Sep 17 00:00:00 2001 From: Eric Anderson Date: Mon, 15 Jul 2024 09:29:04 -0500 Subject: [PATCH] MRL enforces tilde not caret for internal deps (#464) * MRL enforces tilde not caret for internal deps * Cleanup --- .monorepolint.config.mjs | 59 ++++++++++++++++++++++++++++++++ package.json | 8 ++--- packages/client.api/package.json | 2 +- pnpm-lock.yaml | 54 ++++++++++++++--------------- 4 files changed, 91 insertions(+), 32 deletions(-) diff --git a/.monorepolint.config.mjs b/.monorepolint.config.mjs index 173dba971..eb9dc76c2 100644 --- a/.monorepolint.config.mjs +++ b/.monorepolint.config.mjs @@ -19,6 +19,7 @@ import { alphabeticalDependencies, alphabeticalScripts, + createRuleFactory, fileContents, packageEntry, packageOrder, @@ -75,6 +76,61 @@ const esmOnlyPackages = [ // "@osdk/examples.*", but they have their own config cause they are nonstandard ]; +/** + * We don't want to allow `workspace:^` in our dependencies because our current release branch + * strategy only allows for patch changes in the release branch and minors elsewhere. + * + * If we were to allow `workspace:^`, then the follow scenario causes issues: + * - Suppose we have a Foo and a Bar package and Bar depends on Foo. + * - at T0 we cut a release/1.1.x branch and ship Foo@1.1.0, Bar@1.1.0 + * - at T1 we cut a release 1.2.x branch and ship Foo@1.2.0 + * + * If we have `workspace:^` in our deps, a user that already has `Bar@1.1.0` in their package.json + * could update their dependencies without updating Bar (say via pnpm update) and Bar's dependency + * on Foo @ `^1.1.0` would be satisfied by the shipped `Foo@1.2.0`. + * + * Using `workspace:~` prevents this as `~` can only resolve patch changes. + */ +const disallowWorkspaceCaret = createRuleFactory({ + name: "disallowWorkspaceCaret", + check: async (context) => { + const packageJson = context.getPackageJson(); + const packageJsonPath = context.getPackageJsonPath(); + + for (const d of ["dependencies", "devDependencies", "peerDependencies"]) { + const deps = packageJson[d] ?? {}; + + for (const [dep, version] of Object.entries(deps)) { + if (version === "workspace:^") { + const message = `'workspace:^' not allowed (${d}['${dep}']).`; + context.addError({ + message, + longMessage: `${message} Did you mean 'workspace:~'?`, + file: context.getPackageJsonPath(), + fixer: () => { + // always refetch in fixer since another fixer may have already changed the file + let packageJson = context.getPackageJson(); + if (packageJson[d]?.[dep] === "workspace:^") { + packageJson[d] = Object.assign( + {}, + packageJson[d], + { [dep]: "workspace:~" }, + ); + + context.host.writeJson( + context.getPackageJsonPath(), + packageJson, + ); + } + }, + }); + } + } + } + }, + validateOptions: () => {}, // no options right now +}); + const cache = new Map(); /** @@ -157,6 +213,7 @@ function getTsconfigOptions(baseTsconfigPath, opts) { * skipTsconfigReferences?: boolean * singlePackageName?: string * }} options + * @returns {import("@monorepolint/config").RuleModule[]} */ function standardPackageRules(shared, options) { if (options.esmOnly && options.legacy) { @@ -171,6 +228,8 @@ function standardPackageRules(shared, options) { .slice(0, -1); // drop trailing slash return [ + disallowWorkspaceCaret({ ...shared }), + standardTsconfig({ ...shared, diff --git a/package.json b/package.json index 29d348ca6..79710c624 100644 --- a/package.json +++ b/package.json @@ -21,10 +21,10 @@ "@babel/preset-typescript": "^7.24.1", "@changesets/changelog-git": "^0.2.0", "@changesets/cli": "^2.27.3", - "@monorepolint/cli": "0.5.0-alpha.137", - "@monorepolint/config": "0.5.0-alpha.137", - "@monorepolint/core": "0.5.0-alpha.137", - "@monorepolint/rules": "0.5.0-alpha.137", + "@monorepolint/cli": "0.5.0-beta.10", + "@monorepolint/config": "0.5.0-beta.10", + "@monorepolint/core": "0.5.0-beta.10", + "@monorepolint/rules": "0.5.0-beta.10", "@types/lint-staged": "^13.3.0", "@typescript-eslint/parser": "^7.16.0", "babel-plugin-dev-expression": "^0.2.3", diff --git a/packages/client.api/package.json b/packages/client.api/package.json index 180b721a5..822723ac1 100644 --- a/packages/client.api/package.json +++ b/packages/client.api/package.json @@ -35,7 +35,7 @@ "devDependencies": { "@microsoft/api-documenter": "^7.25.3", "@microsoft/api-extractor": "^7.47.0", - "@osdk/api": "workspace:^", + "@osdk/api": "workspace:~", "@osdk/api-extractor": "workspace:~", "@osdk/internal.foundry": "workspace:~", "@types/geojson": "^7946.0.14", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index cfcd749d9..bc3f7ddd0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -27,17 +27,17 @@ importers: specifier: ^2.27.3 version: 2.27.3 '@monorepolint/cli': - specifier: 0.5.0-alpha.137 - version: 0.5.0-alpha.137 + specifier: 0.5.0-beta.10 + version: 0.5.0-beta.10 '@monorepolint/config': - specifier: 0.5.0-alpha.137 - version: 0.5.0-alpha.137 + specifier: 0.5.0-beta.10 + version: 0.5.0-beta.10 '@monorepolint/core': - specifier: 0.5.0-alpha.137 - version: 0.5.0-alpha.137 + specifier: 0.5.0-beta.10 + version: 0.5.0-beta.10 '@monorepolint/rules': - specifier: 0.5.0-alpha.137 - version: 0.5.0-alpha.137 + specifier: 0.5.0-beta.10 + version: 0.5.0-beta.10 '@types/lint-staged': specifier: ^13.3.0 version: 13.3.0 @@ -3234,45 +3234,45 @@ packages: resolution: {integrity: sha512-HZpPoABogPvjeJOdzCOSJsXeL/SMCBgBZMVC3X3d7YYp2gf31MfxhUoYUNwf1ERPJOnQc0wkFn9trqI6ZEdZuA==} dev: true - /@monorepolint/cli@0.5.0-alpha.137: - resolution: {integrity: sha512-Y368Lgf96MlXDYvnuPmI4K42rFKP0SbEHkdxhp1T/Xk4w5LlG+DN+F3sGsuq8nC+WC+bEp73Hjg1gmdyL2ev7g==} + /@monorepolint/cli@0.5.0-beta.10: + resolution: {integrity: sha512-zeQn1OHpuG5f4gn9MhfyAiMVv8w43bCm7EMo8VVOG6aARMuNU+9VUaIL4a/4kfGIMISjmay1lLdh+V6E6qD7ZA==} engines: {node: '>=18'} hasBin: true dependencies: - '@monorepolint/config': 0.5.0-alpha.137 - '@monorepolint/core': 0.5.0-alpha.137 - '@monorepolint/utils': 0.5.0-alpha.137 + '@monorepolint/config': 0.5.0-beta.10 + '@monorepolint/core': 0.5.0-beta.10 + '@monorepolint/utils': 0.5.0-beta.10 chalk: 5.3.0 tslib: 2.6.2 yargs: 17.7.2 dev: true - /@monorepolint/config@0.5.0-alpha.137: - resolution: {integrity: sha512-263OymAmzI2tLwTNVEC5qqqcWQpuPTuCr16v+Hbvgp6VgdUVHqFDqtnr3VnkpiA92Bom3UptZodwx5NCKee4eQ==} + /@monorepolint/config@0.5.0-beta.10: + resolution: {integrity: sha512-ho0IVagHGsWeTE9Sr+S00M8zWMUYMenqxM1PW3+c49Q+rdO3Jyipap4H82zQKk2+PkYSK2e5WWrYoSs5GaKJig==} engines: {node: '>=18'} dependencies: - '@monorepolint/utils': 0.5.0-alpha.137 + '@monorepolint/utils': 0.5.0-beta.10 chalk: 5.3.0 tslib: 2.6.2 dev: true - /@monorepolint/core@0.5.0-alpha.137: - resolution: {integrity: sha512-gbN+CX/7RJ5BPL2SoX1XL0+/nQu28OGoyOuN2G3smzDwjDWTxKUIa7akxEc1pc+6ABFYDNVuJAOtZMZrDo+7eA==} + /@monorepolint/core@0.5.0-beta.10: + resolution: {integrity: sha512-CxsA3vrdqGHkFrGwVQiwzIPnPYVzuPW1B06rhT8pocCjvETF3kihyKLt0AZjgB7CcWOCCx7MJlxnL7Rp+5/X/Q==} engines: {node: '>=18'} dependencies: - '@monorepolint/config': 0.5.0-alpha.137 - '@monorepolint/utils': 0.5.0-alpha.137 + '@monorepolint/config': 0.5.0-beta.10 + '@monorepolint/utils': 0.5.0-beta.10 chalk: 5.3.0 tslib: 2.6.2 dev: true - /@monorepolint/rules@0.5.0-alpha.137: - resolution: {integrity: sha512-92O16iWaaOMNVjAzwZ/h8vvFSVIKSKeIYa3HeDjV4VRcwTH84JopnfJOpd5y/6x3OxaDvhkuZmcpnOnr/Nmsig==} + /@monorepolint/rules@0.5.0-beta.10: + resolution: {integrity: sha512-bqFKq8Qf1IQBM6AuKPrx8fwSq/CZSYVtd5VKG7cMqAz/OGopGd+2NxhAGUll4xqLGUf9Eue1jWKaOaz4mp/0Sw==} engines: {node: '>=18'} dependencies: - '@monorepolint/config': 0.5.0-alpha.137 - '@monorepolint/core': 0.5.0-alpha.137 - '@monorepolint/utils': 0.5.0-alpha.137 + '@monorepolint/config': 0.5.0-beta.10 + '@monorepolint/core': 0.5.0-beta.10 + '@monorepolint/utils': 0.5.0-beta.10 globby: 14.0.1 jest-diff: 29.7.0 resolve-package-path: 4.0.3 @@ -3281,8 +3281,8 @@ packages: tslib: 2.6.2 dev: true - /@monorepolint/utils@0.5.0-alpha.137: - resolution: {integrity: sha512-16Cap06ObJt75HmpKA6LBmy+4yKZYiqMrciWF5W5JFpwWuAQ/y4KAUFr6QfSujLv8j1c/KyuA2BbDJKTR42cKA==} + /@monorepolint/utils@0.5.0-beta.10: + resolution: {integrity: sha512-168whB3isRAweutUEPUbaZ3Bu8vN8k8y83Vufk8ouEZlWRvtzRvn+cyOuY/Ltm8jti7FVxKZeTnYGsfe5idHwA==} engines: {node: '>=18'} dependencies: find-packages: 10.0.4