From 6ebc59dc4497e651782c0f603f524f64038534cf Mon Sep 17 00:00:00 2001 From: Elizabeth Craig Date: Wed, 21 Jan 2026 22:32:05 -0800 Subject: [PATCH 1/2] Add prepublish hook to unpin deps only for published version --- beachball.config.js | 20 +++++- ...-0eb584c1-05cc-470b-b7ca-28ec3778bb0f.json | 32 ++++++++++ package.json | 6 -- packages/cache/package.json | 2 +- packages/cli/package.json | 6 +- packages/rpc/package.json | 15 ++--- packages/target-graph/package.json | 4 +- renovate.json5 | 13 +--- syncpack.config.js | 39 ++++++++++++ yarn.lock | 63 ++++++++++--------- 10 files changed, 138 insertions(+), 62 deletions(-) create mode 100644 change/change-0eb584c1-05cc-470b-b7ca-28ec3778bb0f.json create mode 100644 syncpack.config.js diff --git a/beachball.config.js b/beachball.config.js index 79430c2ba..80bb3c24d 100644 --- a/beachball.config.js +++ b/beachball.config.js @@ -1,3 +1,5 @@ +const fs = require("fs"); + // @ts-check /** @type {import('beachball').BeachballConfig}*/ const config = { @@ -7,12 +9,28 @@ const config = { groups: [ { // roll up all changes to the lage changelog (packages still have individual changelogs too) - masterPackageName: "lage", + mainPackageName: "lage", include: ["packages/*"], changelogPath: "packages/lage", }, ], }, + hooks: { + prepublish: (packagePath, name, version, packageInfos) => { + const { packageJsonPath } = packageInfos[name]; + const packageJson = require(packageJsonPath); + if (!packageJson.dependencies) return; + + for (const [dep, version] of Object.entries(packageJson.dependencies)) { + // If the dep is a specific version, unpin before publishing. + // See the comment towards the end of renovate.json5 for why the deps are pinned to start out. + if (/\d/.test(version[0])) { + packageJson.dependencies[dep] = `^${version}`; + } + } + fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2) + "\n"); + }, + }, ignorePatterns: [".*ignore", "jest.config.js", "**/__*/**/*", "**/tests/**/*"], disallowedChangeTypes: ["major"], }; diff --git a/change/change-0eb584c1-05cc-470b-b7ca-28ec3778bb0f.json b/change/change-0eb584c1-05cc-470b-b7ca-28ec3778bb0f.json new file mode 100644 index 000000000..c2a053966 --- /dev/null +++ b/change/change-0eb584c1-05cc-470b-b7ca-28ec3778bb0f.json @@ -0,0 +1,32 @@ +{ + "changes": [ + { + "type": "patch", + "comment": "Bump and pin dependencies locally, but unpin them when the package is published", + "packageName": "@lage-run/cache", + "email": "elcraig@microsoft.com", + "dependentChangeType": "patch" + }, + { + "type": "patch", + "comment": "Bump and pin dependencies locally, but unpin them when the package is published", + "packageName": "@lage-run/cli", + "email": "elcraig@microsoft.com", + "dependentChangeType": "patch" + }, + { + "type": "patch", + "comment": "Bump and pin dependencies locally, but unpin them when the package is published", + "packageName": "@lage-run/rpc", + "email": "elcraig@microsoft.com", + "dependentChangeType": "patch" + }, + { + "type": "patch", + "comment": "Bump and pin dependencies locally, but unpin them when the package is published", + "packageName": "@lage-run/target-graph", + "email": "elcraig@microsoft.com", + "dependentChangeType": "patch" + } + ] +} \ No newline at end of file diff --git a/package.json b/package.json index 3ce0c111d..ab5b74b92 100644 --- a/package.json +++ b/package.json @@ -64,11 +64,5 @@ "!(*test).{ts,js}": "eslint --fix", "*.{ts,json,md}": "prettier --write" }, - "syncpack": { - "dependencyTypes": [ - "dev", - "prod" - ] - }, "packageManager": "yarn@4.12.0" } diff --git a/packages/cache/package.json b/packages/cache/package.json index 01493dbd5..9cf0c5436 100644 --- a/packages/cache/package.json +++ b/packages/cache/package.json @@ -18,7 +18,7 @@ }, "dependencies": { "@azure/core-auth": "1.10.1", - "@azure/identity": "^4.12.0", + "@azure/identity": "4.13.0", "@lage-run/config": "workspace:^", "@lage-run/logger": "workspace:^", "@lage-run/target-graph": "workspace:^", diff --git a/packages/cli/package.json b/packages/cli/package.json index ebf0dc73c..c631fddc2 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -38,9 +38,9 @@ "commander": "9.5.0", "execa": "5.1.1", "fast-glob": "3.3.3", - "is-interactive": "^1.0.0", - "proper-lockfile": "^4.1.2", - "shell-quote": "^1.8.1", + "is-interactive": "1.0.0", + "proper-lockfile": "4.1.2", + "shell-quote": "1.8.3", "workspace-tools": "0.40.4" }, "devDependencies": { diff --git a/packages/rpc/package.json b/packages/rpc/package.json index 7272f6b1b..97e8d2309 100644 --- a/packages/rpc/package.json +++ b/packages/rpc/package.json @@ -13,19 +13,20 @@ "scripts": { "lint": "buf lint", "generate": "buf generate", - "build": "tsc" + "build": "monorepo-scripts tsc" }, "dependencies": { - "@bufbuild/protobuf": "^1.10.0", - "@connectrpc/connect": "^1.4.0", - "@connectrpc/connect-fastify": "^1.4.0", - "@connectrpc/connect-node": "^1.4.0", - "fastify": "^4.28.1" + "@bufbuild/protobuf": "1.10.1", + "@connectrpc/connect": "1.7.0", + "@connectrpc/connect-fastify": "1.7.0", + "@connectrpc/connect-node": "1.7.0", + "fastify": "4.29.1" }, "devDependencies": { "@bufbuild/buf": "^1.39.0", "@bufbuild/protoc-gen-es": "^1.10.0", - "@connectrpc/protoc-gen-connect-es": "^1.4.0" + "@connectrpc/protoc-gen-connect-es": "1.7.0", + "@lage-run/monorepo-scripts": "workspace:^" }, "publishConfig": { "access": "public" diff --git a/packages/target-graph/package.json b/packages/target-graph/package.json index 3c7d948f8..53e1c103b 100644 --- a/packages/target-graph/package.json +++ b/packages/target-graph/package.json @@ -17,8 +17,8 @@ "lint": "monorepo-scripts lint" }, "dependencies": { - "mergician": "^2.0.2", - "p-limit": "^3.1.0", + "mergician": "2.0.2", + "p-limit": "3.1.0", "workspace-tools": "0.40.4" }, "devDependencies": { diff --git a/renovate.json5 b/renovate.json5 index 8a39a7dae..dba8f2407 100644 --- a/renovate.json5 +++ b/renovate.json5 @@ -45,20 +45,11 @@ "enabled": false }, { - // lage bundles its dependencies, so any updates should to dependencies should be explicit - // so that they trigger a new lage version (with proper documentation of included updates). - // The standard approach of using ^ dependencies and allowing implicit updates via the lock file - // (which with a published bundle, are guaranteed to affect consumers) makes it very hard to - // track when an issue was introduced if it's discovered in another repo. + // See syncpack.config.js for the rationale of pinning dependencies "rangeStrategy": "pin", "matchFileNames": ["packages/**"], // ignore this for docs, scripts, root "matchDepTypes": ["dependencies"], - "matchPackageNames": [ - // lage packages aren't an issue since they're within the repo and the latest version is always used. - "!@lage-run/*", - // glob-hasher is a runtime dependency of lage since it publishes binaries. - "!glob-hasher" - ] + "matchPackageNames": ["!@lage-run/*", "!glob-hasher"] // see syncpack config }, { "matchManagers": ["github-actions"], diff --git a/syncpack.config.js b/syncpack.config.js new file mode 100644 index 000000000..c2764b757 --- /dev/null +++ b/syncpack.config.js @@ -0,0 +1,39 @@ +// @ts-check + +// https://jamiemason.github.io/syncpack/ +/** @type {import('syncpack').RcFile} */ +const config = { + dependencyTypes: ["dev", "prod"], + versionGroups: [ + { + label: "lage deps use workspace protocol", + dependencies: ["@lage-run/**"], + packages: ["**"], + specifierTypes: ["workspace-protocol"], + }, + ], + semverGroups: [ + { + // See below for explanation (this must come first in order) + label: "non-pinned dependencies", + range: "^", + dependencyTypes: ["prod"], + // glob-hasher is a runtime dependency of lage since it publishes binaries. + dependencies: ["glob-hasher"], + }, + { + // lage bundles its dependencies, so any updates should to dependencies should be explicit + // so that they trigger a new lage version (with proper documentation of included updates). + // The standard approach of using ^ dependencies and allowing implicit updates via the lock file + // (which with a published bundle, are guaranteed to affect consumers) makes it very hard to + // track when an issue was introduced if it's discovered in another repo, so we pin deps locally. + // But since pinned deps are not great for anyone consuming the sub-packages directly, the + // beachball config includes a prepublish hook to unpin them. + label: "pin dependencies", + range: "", + dependencyTypes: ["prod"], + packages: ["!@lage-run/docs", "!@lage-run/monorepo-scripts"], + }, + ], +}; +module.exports = config; diff --git a/yarn.lock b/yarn.lock index 26ff67b37..4ac10cb1c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -189,7 +189,7 @@ __metadata: languageName: node linkType: hard -"@azure/identity@npm:^4.12.0": +"@azure/identity@npm:4.13.0": version: 4.13.0 resolution: "@azure/identity@npm:4.13.0" dependencies: @@ -794,7 +794,7 @@ __metadata: languageName: node linkType: hard -"@connectrpc/connect-fastify@npm:^1.4.0": +"@connectrpc/connect-fastify@npm:1.7.0": version: 1.7.0 resolution: "@connectrpc/connect-fastify@npm:1.7.0" peerDependencies: @@ -806,7 +806,7 @@ __metadata: languageName: node linkType: hard -"@connectrpc/connect-node@npm:^1.4.0": +"@connectrpc/connect-node@npm:1.7.0": version: 1.7.0 resolution: "@connectrpc/connect-node@npm:1.7.0" dependencies: @@ -818,7 +818,7 @@ __metadata: languageName: node linkType: hard -"@connectrpc/connect@npm:^1.4.0": +"@connectrpc/connect@npm:1.7.0": version: 1.7.0 resolution: "@connectrpc/connect@npm:1.7.0" peerDependencies: @@ -827,7 +827,7 @@ __metadata: languageName: node linkType: hard -"@connectrpc/protoc-gen-connect-es@npm:^1.4.0": +"@connectrpc/protoc-gen-connect-es@npm:1.7.0": version: 1.7.0 resolution: "@connectrpc/protoc-gen-connect-es@npm:1.7.0" dependencies: @@ -1565,7 +1565,7 @@ __metadata: resolution: "@lage-run/cache@workspace:packages/cache" dependencies: "@azure/core-auth": "npm:1.10.1" - "@azure/identity": "npm:^4.12.0" + "@azure/identity": "npm:4.13.0" "@lage-run/config": "workspace:^" "@lage-run/logger": "workspace:^" "@lage-run/monorepo-fixture": "workspace:^" @@ -1603,9 +1603,9 @@ __metadata: commander: "npm:9.5.0" execa: "npm:5.1.1" fast-glob: "npm:3.3.3" - is-interactive: "npm:^1.0.0" - proper-lockfile: "npm:^4.1.2" - shell-quote: "npm:^1.8.1" + is-interactive: "npm:1.0.0" + proper-lockfile: "npm:4.1.2" + shell-quote: "npm:1.8.3" workspace-tools: "npm:0.40.4" bin: lage: ./bin/lage.js @@ -1760,13 +1760,14 @@ __metadata: resolution: "@lage-run/rpc@workspace:packages/rpc" dependencies: "@bufbuild/buf": "npm:^1.39.0" - "@bufbuild/protobuf": "npm:^1.10.0" + "@bufbuild/protobuf": "npm:1.10.1" "@bufbuild/protoc-gen-es": "npm:^1.10.0" - "@connectrpc/connect": "npm:^1.4.0" - "@connectrpc/connect-fastify": "npm:^1.4.0" - "@connectrpc/connect-node": "npm:^1.4.0" - "@connectrpc/protoc-gen-connect-es": "npm:^1.4.0" - fastify: "npm:^4.28.1" + "@connectrpc/connect": "npm:1.7.0" + "@connectrpc/connect-fastify": "npm:1.7.0" + "@connectrpc/connect-node": "npm:1.7.0" + "@connectrpc/protoc-gen-connect-es": "npm:1.7.0" + "@lage-run/monorepo-scripts": "workspace:^" + fastify: "npm:4.29.1" languageName: unknown linkType: soft @@ -1810,8 +1811,8 @@ __metadata: dependencies: "@lage-run/monorepo-scripts": "workspace:^" jest-diff: "npm:^30.0.0" - mergician: "npm:^2.0.2" - p-limit: "npm:^3.1.0" + mergician: "npm:2.0.2" + p-limit: "npm:3.1.0" workspace-tools: "npm:0.40.4" languageName: unknown linkType: soft @@ -4369,7 +4370,7 @@ __metadata: languageName: node linkType: hard -"fastify@npm:^4.28.1": +"fastify@npm:4.29.1": version: 4.29.1 resolution: "fastify@npm:4.29.1" dependencies: @@ -5228,7 +5229,7 @@ __metadata: languageName: node linkType: hard -"is-interactive@npm:^1.0.0": +"is-interactive@npm:1.0.0": version: 1.0.0 resolution: "is-interactive@npm:1.0.0" checksum: 10c0/dd47904dbf286cd20aa58c5192161be1a67138485b9836d5a70433b21a45442e9611b8498b8ab1f839fc962c7620667a50535fdfb4a6bc7989b8858645c06b4d @@ -6365,7 +6366,7 @@ __metadata: languageName: node linkType: hard -"mergician@npm:^2.0.2": +"mergician@npm:2.0.2": version: 2.0.2 resolution: "mergician@npm:2.0.2" checksum: 10c0/ced960df3de520bef21caf219a6f7cc3efd8395719fb5cf5153361fd27913f9bbaf80ad301f2b6d43f7d0d4ff5d484fc223f3182cc12dc52b33b89063f83692e @@ -6783,6 +6784,15 @@ __metadata: languageName: node linkType: hard +"p-limit@npm:3.1.0, p-limit@npm:^3.0.0, p-limit@npm:^3.0.2, p-limit@npm:^3.1.0": + version: 3.1.0 + resolution: "p-limit@npm:3.1.0" + dependencies: + yocto-queue: "npm:^0.1.0" + checksum: 10c0/9db675949dbdc9c3763c89e748d0ef8bdad0afbb24d49ceaf4c46c02c77d30db4e0652ed36d0a0a7a95154335fab810d95c86153105bb73b3a90448e2bb14e1a + languageName: node + linkType: hard + "p-limit@npm:^2.2.0": version: 2.3.0 resolution: "p-limit@npm:2.3.0" @@ -6792,15 +6802,6 @@ __metadata: languageName: node linkType: hard -"p-limit@npm:^3.0.0, p-limit@npm:^3.0.2, p-limit@npm:^3.1.0": - version: 3.1.0 - resolution: "p-limit@npm:3.1.0" - dependencies: - yocto-queue: "npm:^0.1.0" - checksum: 10c0/9db675949dbdc9c3763c89e748d0ef8bdad0afbb24d49ceaf4c46c02c77d30db4e0652ed36d0a0a7a95154335fab810d95c86153105bb73b3a90448e2bb14e1a - languageName: node - linkType: hard - "p-locate@npm:^4.1.0": version: 4.1.0 resolution: "p-locate@npm:4.1.0" @@ -7134,7 +7135,7 @@ __metadata: languageName: node linkType: hard -"proper-lockfile@npm:^4.1.2": +"proper-lockfile@npm:4.1.2": version: 4.1.2 resolution: "proper-lockfile@npm:4.1.2" dependencies: @@ -7514,7 +7515,7 @@ __metadata: languageName: node linkType: hard -"shell-quote@npm:^1.8.1": +"shell-quote@npm:1.8.3": version: 1.8.3 resolution: "shell-quote@npm:1.8.3" checksum: 10c0/bee87c34e1e986cfb4c30846b8e6327d18874f10b535699866f368ade11ea4ee45433d97bf5eada22c4320c27df79c3a6a7eb1bf3ecfc47f2c997d9e5e2672fd From 5ebb3cd8a5924606d18bfba7b20e330c22fb85c9 Mon Sep 17 00:00:00 2001 From: Elizabeth Craig Date: Sat, 24 Jan 2026 00:11:51 -0800 Subject: [PATCH 2/2] renovate --- renovate.json5 | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/renovate.json5 b/renovate.json5 index dba8f2407..29486d626 100644 --- a/renovate.json5 +++ b/renovate.json5 @@ -1,12 +1,12 @@ { "$schema": "https://docs.renovatebot.com/renovate-schema.json", "extends": [ + "github>microsoft/m365-renovate-config", "github>microsoft/m365-renovate-config:beachball", "github>microsoft/m365-renovate-config:disableEsmVersions", "github>microsoft/m365-renovate-config:groupMore", "github>microsoft/m365-renovate-config:groupTypes", "github>microsoft/m365-renovate-config:keepFresh", - "github>microsoft/m365-renovate-config:pinActions", "github>microsoft/m365-renovate-config:restrictNode(14)" ], "labels": ["renovate"], @@ -40,21 +40,12 @@ "dependencyDashboardApproval": false }, { - // Don't try to pin or otherwise modify in-repo deps - "matchPackageNames": ["@lage-run/*"], + // There's a dev dep "lage-npm": "npm:lage@<..." intended to pull in a slightly older + // pre-built lage version for repo build orchestration (since local lage isn't built yet). + // Disable updating this with Renovate because it will choose a range that includes the + // latest version, which yarn will dedupe to the in-repo version. + "matchPackageNames": ["lage-npm"], "enabled": false - }, - { - // See syncpack.config.js for the rationale of pinning dependencies - "rangeStrategy": "pin", - "matchFileNames": ["packages/**"], // ignore this for docs, scripts, root - "matchDepTypes": ["dependencies"], - "matchPackageNames": ["!@lage-run/*", "!glob-hasher"] // see syncpack config - }, - { - "matchManagers": ["github-actions"], - "groupName": "GitHub actions", - "dependencyDashboardApproval": false } ] }