Repository: atlassian/changesets
Pull request #358 (2020-05-08 07:41:45)

Make interlink dependency auto bump logic configurable
Relates to #117 and #189.

We'd like the ability to opt-out of the auto dependency bumping that occurs to min version ranges when two or more packages are released together, specifically in the case where the dependency has only been patch bumped.

This PR includes one way of achieving this via a `interLinkAutoBump` config option (I couldn't think of a good name, ideas welcome) which specifies the minimum bump type that the auto bumping of inter linked dependencies will apply to. By default it's set to 'patch' which should not change current behaviour of changesets.
I was originally going to add 'major' as an option as well but then decided against it since there isn't a way to specify bumping dependents (#117) and this configuration option doesn't change that.

It's unfortunate that adding a new feature behind a configuration option has resulted in two major releases. In my opinion, the package structure, function signaturres and/or versioning may need a rethink to avoid this going forward.


changeset-bot[bot]: ###  🦋  Changeset is good to go

Latest commit: 95ba6a83d9f887b28a0ea3c64a4c1533b9b83f98

**We got this.**

<details><summary>This PR includes changesets to release 15 packages</summary>

  | Name                              | Type  |
| --------------------------------- | ----- |
| @changesets/apply-release-plan    | Major |
| @changesets/types                 | Major |
| @changesets/config                | Minor |
| @changesets/cli                   | Patch |
| @changesets/assemble-release-plan | Patch |
| @changesets/changelog-git         | Patch |
| @changesets/changelog-github      | Patch |
| @changesets/get-dependents-graph  | Patch |
| @changesets/get-release-plan      | Patch |
| get-workspaces                    | Patch |
| @changesets/git                   | Patch |
| @changesets/parse                 | Patch |
| @changesets/pre                   | Patch |
| @changesets/read                  | Patch |
| @changesets/write                 | Patch |

</details>

Not sure what this means? [Click here  to learn what changesets are](https://github.com/changesets/changesets/blob/master/docs/adding-a-changeset.md).

[Click here if you're a maintainer who wants to add another changeset to this PR](https://github.com/atlassian/changesets/new/configurable-interlinked-autobump?filename=.changeset/grumpy-parents-film.md&value=---%0A%22%40changesets%2Fapply-release-plan%22%3A%20patch%0A%22%40changesets%2Fconfig%22%3A%20patch%0A%22%40changesets%2Ftypes%22%3A%20patch%0A---%0A%0AMake%20interlink%20dependency%20auto%20bump%20logic%20configurable%0A)


Noviny: Some quick thoughts:

I'd add a changeset for the CLI so this becomes available, and tests failed, but otherwise happy with this.

This does up the need for #117
---

Re: two major versions. This makes sense for those packages, and I don't really mind doing it.
codecov[bot]: # [Codecov](https://codecov.io/gh/atlassian/changesets/pull/358?src=pr&el=h1) Report
> Merging [#358](https://codecov.io/gh/atlassian/changesets/pull/358?src=pr&el=desc) into [master](https://codecov.io/gh/atlassian/changesets/commit/09f62f9c822f31899a48cbd93c7801d72a80b97e&el=desc) will **increase** coverage by `0.11%`.
> The diff coverage is `92.85%`.

[![Impacted file tree graph](https://codecov.io/gh/atlassian/changesets/pull/358/graphs/tree.svg?width=650&height=150&src=pr&token=j0W25zrXm2)](https://codecov.io/gh/atlassian/changesets/pull/358?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master     #358      +/-   ##
==========================================
+ Coverage   79.85%   79.96%   +0.11%     
==========================================
  Files          40       40              
  Lines        1072     1083      +11     
  Branches      246      247       +1     
==========================================
+ Hits          856      866      +10     
- Misses        206      207       +1     
  Partials       10       10              
```


| [Impacted Files](https://codecov.io/gh/atlassian/changesets/pull/358?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [packages/apply-release-plan/src/version-package.ts](https://codecov.io/gh/atlassian/changesets/pull/358/diff?src=pr&el=tree#diff-cGFja2FnZXMvYXBwbHktcmVsZWFzZS1wbGFuL3NyYy92ZXJzaW9uLXBhY2thZ2UudHM=) | `96.29% <87.50%> (-3.71%)` | :arrow_down: |
| [packages/apply-release-plan/src/index.ts](https://codecov.io/gh/atlassian/changesets/pull/358/diff?src=pr&el=tree#diff-cGFja2FnZXMvYXBwbHktcmVsZWFzZS1wbGFuL3NyYy9pbmRleC50cw==) | `92.47% <100.00%> (ø)` | |
| [packages/config/src/index.ts](https://codecov.io/gh/atlassian/changesets/pull/358/diff?src=pr&el=tree#diff-cGFja2FnZXMvY29uZmlnL3NyYy9pbmRleC50cw==) | `98.36% <100.00%> (+0.11%)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/atlassian/changesets/pull/358?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/atlassian/changesets/pull/358?src=pr&el=footer). Last update [09f62f9...94f718d](https://codecov.io/gh/atlassian/changesets/pull/358?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/.changeset/heavy-bananas-jog.md b/.changeset/heavy-bananas-jog.md
new file mode 100644
index 0000000..1b026e0
--- /dev/null
+++ b/.changeset/heavy-bananas-jog.md
@@ -0,0 +1,7 @@
+---
+"@changesets/apply-release-plan": major
+"@changesets/types": major
+"@changesets/config": minor
+---
+
+Add new updateInternalDependencies config option to disable auto bumping of internal dependencies in the same release if the dependency was only patch bumped
diff --git a/.changeset/pink-shoes-retire.md b/.changeset/pink-shoes-retire.md
new file mode 100644
index 0000000..d11f25b
--- /dev/null
+++ b/.changeset/pink-shoes-retire.md
@@ -0,0 +1,5 @@
+---
+"@changesets/cli": minor
+---
+
+Add new config option 'updateInternalDependencies' that can be set to 'minor' to only update internal dependencies in the same release if the dependency was minor released or above. Defaults to 'patch' which is the existing behaviour.
diff --git a/packages/apply-release-plan/src/index.ts b/packages/apply-release-plan/src/index.ts
index 7e871ca..2686105 100644
--- a/packages/apply-release-plan/src/index.ts
+++ b/packages/apply-release-plan/src/index.ts
@@ -84,14 +84,19 @@ export default async function applyReleasePlan(
     }
   }
 
-  let versionsToUpdate = releases.map(({ name, newVersion }) => ({
+  let versionsToUpdate = releases.map(({ name, newVersion, type }) => ({
     name,
-    version: newVersion
+    version: newVersion,
+    type
   }));
 
   // iterate over releases updating packages
   let finalisedRelease = releaseWithChangelogs.map(release => {
-    return versionPackage(release, versionsToUpdate);
+    return versionPackage(
+      release,
+      versionsToUpdate,
+      config.updateInternalDependencies
+    );
   });
 
   let prettierConfig = await prettier.resolveConfig(cwd);
diff --git a/packages/apply-release-plan/src/version-package.ts b/packages/apply-release-plan/src/version-package.ts
index bbac604..279ea5c 100644
--- a/packages/apply-release-plan/src/version-package.ts
+++ b/packages/apply-release-plan/src/version-package.ts
@@ -1,4 +1,8 @@
-import { ComprehensiveRelease, PackageJSON } from "@changesets/types";
+import {
+  ComprehensiveRelease,
+  PackageJSON,
+  VersionType
+} from "@changesets/types";
 import getVersionRangeType from "@changesets/get-version-range-type";
 import { Range } from "semver";
 
@@ -9,27 +13,42 @@ const DEPENDENCY_TYPES = [
   "optionalDependencies"
 ] as const;
 
+const bumpTypes = ["none", "patch", "minor", "major"];
+
+/* Converts a bump type into a numeric level to indicate order */
+function getBumpLevel(type: VersionType) {
+  const level = bumpTypes.indexOf(type);
+  if (level < 0) {
+    throw new Error(`Unrecognised bump type ${type}`);
+  }
+  return level;
+}
+
 export default function versionPackage(
   release: ComprehensiveRelease & {
     changelog: string | null;
     packageJson: PackageJSON;
     dir: string;
   },
-  versionsToUpdate: Array<{ name: string; version: string }>
+  versionsToUpdate: Array<{ name: string; version: string; type: VersionType }>,
+  updateInternalDependencies: "patch" | "minor"
 ) {
   let { newVersion, packageJson } = release;
 
+  const minLevel = getBumpLevel(updateInternalDependencies);
   packageJson.version = newVersion;
 
   for (let type of DEPENDENCY_TYPES) {
     let deps = packageJson[type];
     if (deps) {
-      for (let { name, version } of versionsToUpdate) {
+      for (let { name, version, type } of versionsToUpdate) {
         let depCurrentVersion = deps[name];
+        const bumpLevel = getBumpLevel(type);
         if (
           !depCurrentVersion ||
           depCurrentVersion.startsWith("file:") ||
-          depCurrentVersion.startsWith("link:")
+          depCurrentVersion.startsWith("link:") ||
+          bumpLevel < minLevel
         )
           continue;
         const usesWorkspaceRange = depCurrentVersion.startsWith("workspace:");
diff --git a/packages/config/src/index.ts b/packages/config/src/index.ts
index a27141f..d4cd767 100644
--- a/packages/config/src/index.ts
+++ b/packages/config/src/index.ts
@@ -12,7 +12,8 @@ export let defaultWrittenConfig = {
   commit: false,
   linked: [] as ReadonlyArray<ReadonlyArray<string>>,
   access: "restricted",
-  baseBranch: "master"
+  baseBranch: "master",
+  updateInternalDependencies: "patch"
 } as const;
 
 function getNormalisedChangelogOption(
@@ -138,6 +139,18 @@ export let parse = (json: WrittenConfig, packages: Packages): Config => {
       }
     }
   }
+  if (
+    json.updateInternalDependencies !== undefined &&
+    !["patch", "minor"].includes(json.updateInternalDependencies)
+  ) {
+    messages.push(
+      `The \`updateInternalDependencies\` option is set as ${JSON.stringify(
+        json.updateInternalDependencies,
+        null,
+        2
+      )} but can only be 'patch' or 'minor'`
+    );
+  }
   if (messages.length) {
     throw new ValidationError(
       `Some errors occurred when validating the changesets config:\n` +
@@ -161,7 +174,12 @@ export let parse = (json: WrittenConfig, packages: Packages): Config => {
     baseBranch:
       json.baseBranch === undefined
         ? defaultWrittenConfig.baseBranch
-        : json.baseBranch
+        : json.baseBranch,
+
+    updateInternalDependencies:
+      json.updateInternalDependencies === undefined
+        ? defaultWrittenConfig.updateInternalDependencies
+        : json.updateInternalDependencies
   };
   return config;
 };
diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts
index c3b5362..625bc9c 100644
--- a/packages/types/src/index.ts
+++ b/packages/types/src/index.ts
@@ -61,6 +61,8 @@ export type Config = {
   linked: Linked;
   access: AccessType;
   baseBranch: string;
+  /** The minimum bump type to trigger automatic update of internal dependencies that are part of the same release */
+  updateInternalDependencies: "patch" | "minor";
 };
 
 export type WrittenConfig = {
@@ -69,6 +71,8 @@ export type WrittenConfig = {
   linked?: Linked;
   access?: AccessType;
   baseBranch?: string;
+  /** The minimum bump type to trigger automatic update of internal dependencies that are part of the same release */
+  updateInternalDependencies?: "patch" | "minor";
 };
 
 export type NewChangesetWithCommit = NewChangeset & { commit?: string };


Test patch:
diff --git a/__fixtures__/interlinked-dependencies/.changeset/README.md b/__fixtures__/internal-dependencies/.changeset/README.md
similarity index 100%
rename from __fixtures__/interlinked-dependencies/.changeset/README.md
rename to __fixtures__/internal-dependencies/.changeset/README.md
diff --git a/__fixtures__/interlinked-dependencies/package.json b/__fixtures__/internal-dependencies/package.json
similarity index 100%
rename from __fixtures__/interlinked-dependencies/package.json
rename to __fixtures__/internal-dependencies/package.json
diff --git a/__fixtures__/interlinked-dependencies/packages/pkg-a/package.json b/__fixtures__/internal-dependencies/packages/pkg-a/package.json
similarity index 100%
rename from __fixtures__/interlinked-dependencies/packages/pkg-a/package.json
rename to __fixtures__/internal-dependencies/packages/pkg-a/package.json
diff --git a/__fixtures__/interlinked-dependencies/packages/pkg-b/package.json b/__fixtures__/internal-dependencies/packages/pkg-b/package.json
similarity index 100%
rename from __fixtures__/interlinked-dependencies/packages/pkg-b/package.json
rename to __fixtures__/internal-dependencies/packages/pkg-b/package.json
diff --git a/__fixtures__/interlinked-dependencies/packages/pkg-c/package.json b/__fixtures__/internal-dependencies/packages/pkg-c/package.json
similarity index 100%
rename from __fixtures__/interlinked-dependencies/packages/pkg-c/package.json
rename to __fixtures__/internal-dependencies/packages/pkg-c/package.json
diff --git a/packages/apply-release-plan/src/index.test.ts b/packages/apply-release-plan/src/index.test.ts
index 10ab996..4e84403 100644
--- a/packages/apply-release-plan/src/index.test.ts
+++ b/packages/apply-release-plan/src/index.test.ts
@@ -42,7 +42,8 @@ class FakeReleasePlan {
       commit: false,
       linked: [],
       access: "restricted",
-      baseBranch: "master"
+      baseBranch: "master",
+      updateInternalDependencies: "patch"
     };
 
     this.changesets = [baseChangeset, ...changesets];
@@ -70,7 +71,8 @@ async function testSetup(
       commit: false,
       linked: [],
       access: "restricted",
-      baseBranch: "master"
+      baseBranch: "master",
+      updateInternalDependencies: "patch"
     };
   }
   let tempDir = await f.copy(fixtureName);
@@ -271,7 +273,8 @@ describe("apply release plan", () => {
           commit: false,
           linked: [],
           access: "restricted",
-          baseBranch: "master"
+          baseBranch: "master",
+          updateInternalDependencies: "patch"
         }
       );
       let pkgPathA = changedFiles.find(a =>
@@ -290,7 +293,6 @@ describe("apply release plan", () => {
       expect(pkgJSONA).toMatchObject({
         name: "pkg-a",
         version: "1.0.0",
-
         devDependencies: {
           "pkg-b": "1.1.0"
         }
@@ -327,7 +329,8 @@ describe("apply release plan", () => {
           commit: false,
           linked: [],
           access: "restricted",
-          baseBranch: "master"
+          baseBranch: "master",
+          updateInternalDependencies: "patch"
         }
       );
       let pkgPath = changedFiles.find(a =>
@@ -345,6 +348,417 @@ describe("apply release plan", () => {
         }
       });
     });
+
+    describe("internal dependency bumping", () => {
+      describe("updateInternalDependencies set to patch", () => {
+        const updateInternalDependencies = "patch";
+        it("should update min version ranges of patch bumped internal dependencies", async () => {
+          let { changedFiles } = await testSetup(
+            "internal-dependencies",
+            {
+              changesets: [
+                {
+                  id: "quick-lions-devour",
+                  summary: "Hey, let's have fun with testing!",
+                  releases: [{ name: "pkg-a", type: "patch" }]
+                }
+              ],
+              releases: [
+                {
+                  name: "pkg-a",
+                  type: "patch",
+                  oldVersion: "1.0.3",
+                  newVersion: "1.0.4",
+                  changesets: ["quick-lions-devour"]
+                },
+                {
+                  name: "pkg-b",
+                  type: "patch",
+                  oldVersion: "1.2.0",
+                  newVersion: "1.2.1",
+                  changesets: ["quick-lions-devour"]
+                }
+              ],
+              preState: undefined
+            },
+            {
+              changelog: false,
+              commit: false,
+              linked: [],
+              access: "restricted",
+              baseBranch: "master",
+              updateInternalDependencies
+            }
+          );
+          let pkgPathA = changedFiles.find(a =>
+            a.endsWith(`pkg-a${path.sep}package.json`)
+          );
+          let pkgPathB = changedFiles.find(b =>
+            b.endsWith(`pkg-b${path.sep}package.json`)
+          );
+
+          if (!pkgPathA || !pkgPathB) {
+            throw new Error(`could not find an updated package json`);
+          }
+          let pkgJSONA = await fs.readJSON(pkgPathA);
+          let pkgJSONB = await fs.readJSON(pkgPathB);
+
+          expect(pkgJSONA).toMatchObject({
+            name: "pkg-a",
+            version: "1.0.4",
+            dependencies: {
+              "pkg-b": "~1.2.1"
+            }
+          });
+          expect(pkgJSONB).toMatchObject({
+            name: "pkg-b",
+            version: "1.2.1",
+            dependencies: {
+              "pkg-c": "2.0.0",
+              "pkg-a": "^1.0.4"
+            }
+          });
+        });
+        it("should update min version ranges of minor bumped internal dependencies", async () => {
+          let { changedFiles } = await testSetup(
+            "internal-dependencies",
+            {
+              changesets: [
+                {
+                  id: "quick-lions-devour",
+                  summary: "Hey, let's have fun with testing!",
+                  releases: [{ name: "pkg-a", type: "patch" }]
+                }
+              ],
+              releases: [
+                {
+                  name: "pkg-a",
+                  type: "minor",
+                  oldVersion: "1.0.3",
+                  newVersion: "1.1.0",
+                  changesets: ["quick-lions-devour"]
+                },
+                {
+                  name: "pkg-b",
+                  type: "patch",
+                  oldVersion: "1.2.0",
+                  newVersion: "1.2.1",
+                  changesets: ["quick-lions-devour"]
+                }
+              ],
+              preState: undefined
+            },
+            {
+              changelog: false,
+              commit: false,
+              linked: [],
+              access: "restricted",
+              baseBranch: "master",
+              updateInternalDependencies
+            }
+          );
+          let pkgPathA = changedFiles.find(a =>
+            a.endsWith(`pkg-a${path.sep}package.json`)
+          );
+          let pkgPathB = changedFiles.find(b =>
+            b.endsWith(`pkg-b${path.sep}package.json`)
+          );
+
+          if (!pkgPathA || !pkgPathB) {
+            throw new Error(`could not find an updated package json`);
+          }
+          let pkgJSONA = await fs.readJSON(pkgPathA);
+          let pkgJSONB = await fs.readJSON(pkgPathB);
+
+          expect(pkgJSONA).toMatchObject({
+            name: "pkg-a",
+            version: "1.1.0",
+            dependencies: {
+              "pkg-b": "~1.2.1"
+            }
+          });
+          expect(pkgJSONB).toMatchObject({
+            name: "pkg-b",
+            version: "1.2.1",
+            dependencies: {
+              "pkg-c": "2.0.0",
+              "pkg-a": "^1.1.0"
+            }
+          });
+        });
+        it("should update min version ranges of major bumped internal dependencies", async () => {
+          let { changedFiles } = await testSetup(
+            "internal-dependencies",
+            {
+              changesets: [
+                {
+                  id: "quick-lions-devour",
+                  summary: "Hey, let's have fun with testing!",
+                  releases: [{ name: "pkg-a", type: "patch" }]
+                }
+              ],
+              releases: [
+                {
+                  name: "pkg-a",
+                  type: "major",
+                  oldVersion: "1.0.3",
+                  newVersion: "2.0.0",
+                  changesets: ["quick-lions-devour"]
+                },
+                {
+                  name: "pkg-b",
+                  type: "patch",
+                  oldVersion: "1.2.0",
+                  newVersion: "1.2.1",
+                  changesets: ["quick-lions-devour"]
+                }
+              ],
+              preState: undefined
+            },
+            {
+              changelog: false,
+              commit: false,
+              linked: [],
+              access: "restricted",
+              baseBranch: "master",
+              updateInternalDependencies
+            }
+          );
+          let pkgPathA = changedFiles.find(a =>
+            a.endsWith(`pkg-a${path.sep}package.json`)
+          );
+          let pkgPathB = changedFiles.find(b =>
+            b.endsWith(`pkg-b${path.sep}package.json`)
+          );
+
+          if (!pkgPathA || !pkgPathB) {
+            throw new Error(`could not find an updated package json`);
+          }
+          let pkgJSONA = await fs.readJSON(pkgPathA);
+          let pkgJSONB = await fs.readJSON(pkgPathB);
+
+          expect(pkgJSONA).toMatchObject({
+            name: "pkg-a",
+            version: "2.0.0",
+            dependencies: {
+              "pkg-b": "~1.2.1"
+            }
+          });
+          expect(pkgJSONB).toMatchObject({
+            name: "pkg-b",
+            version: "1.2.1",
+            dependencies: {
+              "pkg-c": "2.0.0",
+              "pkg-a": "^2.0.0"
+            }
+          });
+        });
+      });
+      describe("updateInternalDependencies set to minor", () => {
+        const updateInternalDependencies = "minor";
+        it("should NOT update min version ranges of patch bumped internal dependencies", async () => {
+          let { changedFiles } = await testSetup(
+            "internal-dependencies",
+            {
+              changesets: [
+                {
+                  id: "quick-lions-devour",
+                  summary: "Hey, let's have fun with testing!",
+                  releases: [{ name: "pkg-a", type: "patch" }]
+                }
+              ],
+              releases: [
+                {
+                  name: "pkg-a",
+                  type: "patch",
+                  oldVersion: "1.0.3",
+                  newVersion: "1.0.4",
+                  changesets: ["quick-lions-devour"]
+                },
+                {
+                  name: "pkg-b",
+                  type: "patch",
+                  oldVersion: "1.2.0",
+                  newVersion: "1.2.1",
+                  changesets: ["quick-lions-devour"]
+                }
+              ],
+              preState: undefined
+            },
+            {
+              changelog: false,
+              commit: false,
+              linked: [],
+              access: "restricted",
+              baseBranch: "master",
+              updateInternalDependencies
+            }
+          );
+          let pkgPathA = changedFiles.find(a =>
+            a.endsWith(`pkg-a${path.sep}package.json`)
+          );
+          let pkgPathB = changedFiles.find(b =>
+            b.endsWith(`pkg-b${path.sep}package.json`)
+          );
+
+          if (!pkgPathA || !pkgPathB) {
+            throw new Error(`could not find an updated package json`);
+          }
+          let pkgJSONA = await fs.readJSON(pkgPathA);
+          let pkgJSONB = await fs.readJSON(pkgPathB);
+
+          expect(pkgJSONA).toMatchObject({
+            name: "pkg-a",
+            version: "1.0.4",
+            dependencies: {
+              "pkg-b": "~1.2.0"
+            }
+          });
+          expect(pkgJSONB).toMatchObject({
+            name: "pkg-b",
+            version: "1.2.1",
+            dependencies: {
+              "pkg-c": "2.0.0",
+              "pkg-a": "^1.0.3"
+            }
+          });
+        });
+        it("should update min version ranges of minor bumped internal dependencies", async () => {
+          let { changedFiles } = await testSetup(
+            "internal-dependencies",
+            {
+              changesets: [
+                {
+                  id: "quick-lions-devour",
+                  summary: "Hey, let's have fun with testing!",
+                  releases: [{ name: "pkg-a", type: "patch" }]
+                }
+              ],
+              releases: [
+                {
+                  name: "pkg-a",
+                  type: "minor",
+                  oldVersion: "1.0.3",
+                  newVersion: "1.1.0",
+                  changesets: ["quick-lions-devour"]
+                },
+                {
+                  name: "pkg-b",
+                  type: "patch",
+                  oldVersion: "1.2.0",
+                  newVersion: "1.2.1",
+                  changesets: ["quick-lions-devour"]
+                }
+              ],
+              preState: undefined
+            },
+            {
+              changelog: false,
+              commit: false,
+              linked: [],
+              access: "restricted",
+              baseBranch: "master",
+              updateInternalDependencies
+            }
+          );
+          let pkgPathA = changedFiles.find(a =>
+            a.endsWith(`pkg-a${path.sep}package.json`)
+          );
+          let pkgPathB = changedFiles.find(b =>
+            b.endsWith(`pkg-b${path.sep}package.json`)
+          );
+
+          if (!pkgPathA || !pkgPathB) {
+            throw new Error(`could not find an updated package json`);
+          }
+          let pkgJSONA = await fs.readJSON(pkgPathA);
+          let pkgJSONB = await fs.readJSON(pkgPathB);
+
+          expect(pkgJSONA).toMatchObject({
+            name: "pkg-a",
+            version: "1.1.0",
+            dependencies: {
+              "pkg-b": "~1.2.0"
+            }
+          });
+          expect(pkgJSONB).toMatchObject({
+            name: "pkg-b",
+            version: "1.2.1",
+            dependencies: {
+              "pkg-c": "2.0.0",
+              "pkg-a": "^1.1.0"
+            }
+          });
+        });
+        it("should update min version ranges of major bumped internal dependencies", async () => {
+          let { changedFiles } = await testSetup(
+            "internal-dependencies",
+            {
+              changesets: [
+                {
+                  id: "quick-lions-devour",
+                  summary: "Hey, let's have fun with testing!",
+                  releases: [{ name: "pkg-a", type: "patch" }]
+                }
+              ],
+              releases: [
+                {
+                  name: "pkg-a",
+                  type: "major",
+                  oldVersion: "1.0.3",
+                  newVersion: "2.0.0",
+                  changesets: ["quick-lions-devour"]
+                },
+                {
+                  name: "pkg-b",
+                  type: "patch",
+                  oldVersion: "1.2.0",
+                  newVersion: "1.2.1",
+                  changesets: ["quick-lions-devour"]
+                }
+              ],
+              preState: undefined
+            },
+            {
+              changelog: false,
+              commit: false,
+              linked: [],
+              access: "restricted",
+              baseBranch: "master",
+              updateInternalDependencies
+            }
+          );
+          let pkgPathA = changedFiles.find(a =>
+            a.endsWith(`pkg-a${path.sep}package.json`)
+          );
+          let pkgPathB = changedFiles.find(b =>
+            b.endsWith(`pkg-b${path.sep}package.json`)
+          );
+
+          if (!pkgPathA || !pkgPathB) {
+            throw new Error(`could not find an updated package json`);
+          }
+          let pkgJSONA = await fs.readJSON(pkgPathA);
+          let pkgJSONB = await fs.readJSON(pkgPathB);
+
+          expect(pkgJSONA).toMatchObject({
+            name: "pkg-a",
+            version: "2.0.0",
+            dependencies: {
+              "pkg-b": "~1.2.0"
+            }
+          });
+          expect(pkgJSONB).toMatchObject({
+            name: "pkg-b",
+            version: "1.2.1",
+            dependencies: {
+              "pkg-c": "2.0.0",
+              "pkg-a": "^2.0.0"
+            }
+          });
+        });
+      });
+    });
   });
   describe("changelogs", () => {
     it("should update a changelog for one package", async () => {
@@ -468,7 +882,8 @@ describe("apply release plan", () => {
           changelog: [
             path.resolve(__dirname, "test-utils/simple-get-changelog-entry"),
             null
-          ]
+          ],
+          updateInternalDependencies: "patch"
         }
       );
       let pkgAChangelogPath = changedFiles.find(a =>
diff --git a/packages/config/src/index.test.ts b/packages/config/src/index.test.ts
index 31e51c1..3b47321 100644
--- a/packages/config/src/index.test.ts
+++ b/packages/config/src/index.test.ts
@@ -25,7 +25,8 @@ test("read reads the config", async () => {
     changelog: false,
     commit: true,
     access: "restricted",
-    baseBranch: "master"
+    baseBranch: "master",
+    updateInternalDependencies: "patch"
   });
 });
 
@@ -34,7 +35,8 @@ let defaults = {
   changelog: ["@changesets/cli/changelog", null],
   commit: false,
   access: "restricted",
-  baseBranch: "master"
+  baseBranch: "master",
+  updateInternalDependencies: "patch"
 } as const;
 
 let correctCases = {
@@ -113,6 +115,24 @@ let correctCases = {
       ...defaults,
       linked: [["pkg-a", "pkg-b"]]
     }
+  },
+  "update internal dependencies minor": {
+    input: {
+      updateInternalDependencies: "minor"
+    },
+    output: {
+      ...defaults,
+      updateInternalDependencies: "minor"
+    }
+  },
+  "update internal dependencies patch": {
+    input: {
+      updateInternalDependencies: "patch"
+    },
+    output: {
+      ...defaults,
+      updateInternalDependencies: "patch"
+    }
   }
 } as const;
 
@@ -252,4 +272,12 @@ The package \\"pkg-a\\" is in multiple sets of linked packages. Packages can onl
       'The `access` option is set as "private", but this is actually not a valid value - the correct form is "restricted".'
     );
   });
+  test("updateInternalDependencies not patch or minor", () => {
+    expect(() => {
+      unsafeParse({ updateInternalDependencies: "major" });
+    }).toThrowErrorMatchingInlineSnapshot(`
+"Some errors occurred when validating the changesets config:
+The \`updateInternalDependencies\` option is set as \\"major\\" but can only be 'patch' or 'minor'"
+`);
+  });
 });