Skip to content

Conversation

@cryptodev-2s
Copy link
Contributor

@cryptodev-2s cryptodev-2s commented Nov 5, 2025

Description

Adds a new check-deps command to automatically detect, validate, and update dependency bump entries in CHANGELOGs.

Key Features

  • Detects dependency bumps from git diffs in package.json files
  • Validates exact versions in changelog entries (catches stale entries)
  • Auto-updates changelogs with --fix flag
  • Preserves PR history when bumping same dependency multiple times
  • Release-aware - adds entries to ## [X.Y.Z] section when package version changes, or [Unreleased] otherwise
  • Repository agnostic - reads repo URL from package.json
  • Handles renamed packages - automatically detects package rename info from package.json scripts to correctly parse changelogs with old package name tags

Example:

# Before (PR #7007):
- Bump `@metamask/transaction-controller` from `^61.0.0` to `^61.1.0` ([#7007](...))

# After fix (PR #1234):
- Bump `@metamask/transaction-controller` from `^61.0.0` to `^62.0.0` ([#7007](...), [#1234](...))

Implementation

New files:

  • src/check-dependency-bumps.ts + tests (24 tests)
  • src/changelog-validator.ts + tests (27 tests)

Modified:

  • src/command-line-arguments.ts - Added check-deps command
  • src/main.ts - Command routing
  • Updated test files for command structure

Coverage: 100% (statements, branches, functions, lines) - 340 passing tests

Testing in MetaMask/core

# Build tool
cd /path/to/create-release-branch && yarn build

# From core
cd /path/to/core
git checkout -b test-dep-bumps

# In one or more packages, modify package.json to:
# - Bump some dependencies
# - Bump some peerDependencies  
# - Bump some devDependencies (to verify they're correctly excluded)
# - Change the package version (to test release detection)

git add . && git commit -m "Test: bump dependencies"

# Validate
node /path/to/create-release-branch/dist/cli.js check-deps

# Fix without PR number
node /path/to/create-release-branch/dist/cli.js check-deps --fix

# Fix with PR number
node /path/to/create-release-branch/dist/cli.js check-deps --fix --pr 4532

# Validate with github-tools (https://github.com/MetaMask/github-tools)
cd /path/to/github-tools
yarn run changelog:check "/path/to/core" "main" "4532"

Note

Introduces check-deps to detect dependency bumps from git diffs and validate/update package changelogs (incl. releases, peerDeps BREAKING, and package renames).

  • CLI:
    • Add check-deps command via yargs in src/command-line-arguments.ts; route in src/main.ts.
  • Core logic:
    • Implement src/check-dependency-bumps.ts to parse git diff of **/package.json, detect dependencies/peerDependencies version bumps (skip dev/optional), dedupe, and capture package version changes.
    • Implement src/changelog-validator.ts to validate and update changelog entries:
      • Exact version matching; distinguishes deps vs peerDeps (**BREAKING:**).
      • Adds/updates entries with PR concatenation (uses #XXXXX when missing).
      • Release-aware: updates [Unreleased] or a specific [X.Y.Z] section.
      • Supports renamed packages via scripts flags (--tag-prefix-before-package-rename, --version-before-package-rename).
  • Shared types: Add src/types.ts for DependencyChange, PackageInfo, PackageChanges.
  • Tests:
    • Extensive unit and functional tests for detection, validation, updating, routing, and edge cases.
  • Docs/Changelog:
    • Update CHANGELOG.md with new command and usage.

Written by Cursor Bugbot for commit 703e1c3. This will update automatically on new commits. Configure here.

@cryptodev-2s cryptodev-2s force-pushed the feat/add-dependency-bump-checker branch 4 times, most recently from a88a703 to e9b5b6c Compare November 6, 2025 14:11
@cryptodev-2s cryptodev-2s marked this pull request as ready for review November 6, 2025 14:11
@cryptodev-2s cryptodev-2s requested a review from a team as a code owner November 6, 2025 14:11
@cryptodev-2s cryptodev-2s force-pushed the feat/add-dependency-bump-checker branch from e9b5b6c to abcda3f Compare November 6, 2025 15:28
@mcmire
Copy link
Contributor

mcmire commented Nov 12, 2025

@cryptodev-2s I haven't had time to review this yet, but I have one initial thought:

Should we rename check-deps to validate? My thought is that we will want to include some more validation steps in the future (e.g. #176), and if we group everything under validate it will create room for that work.

@cryptodev-2s
Copy link
Contributor Author

@cryptodev-2s I haven't had time to review this yet, but I have one initial thought:

Should we rename check-deps to validate? My thought is that we will want to include some more validation steps in the future (e.g. #176), and if we group everything under validate it will create room for that work.

Good point about future validation commands! However, I think check-deps should remain separate from release validation (#176) since:

  1. Different scope: check-deps works on any branch (feature branches included), not just release branches
  2. Independent use case: Validating dependency changelog entries is useful outside the release process
  3. Clear separation: Release-specific validation (Add command for validating release branch #176) deserves its own command

Suggestion:

Side note: Given we're adding more commands beyond release creation, we could consider renaming the package to something like @metamask/monorepo-tools in a future major version. But that's a separate discussion.

Introduces a new tool to automatically detect dependency version changes
and validate/update changelog entries accordingly.

Features:
- Detects dependency bumps from git diffs in package.json files
- Validates changelog entries with exact version matching
- Automatically updates changelogs with missing or outdated entries
- Smart PR reference concatenation when updating existing entries
- Dynamically reads repository URLs and package names
- Validates by default with optional --fix flag for updates

Usage:
  yarn check-dependency-bumps           # Validate changelogs
  yarn check-dependency-bumps --fix     # Auto-update changelogs
  yarn check-dependency-bumps --fix --pr 1234  # With PR number
Optimizes package name resolution by reading package.json inline during
git diff parsing instead of in a separate enrichment pass.

Changes:
- Make parseDiff async to read package names inline
- Remove enrichWithPackageNames function (no longer needed)
- Read packageName immediately when first encountering a package
- Simplify validateChangelogs and updateChangelogs signatures
- Remove packageNames parameter (now part of PackageInfo)

Benefits:
- Single-pass processing (parse + enrich in one step)
- Simpler code flow (24 lines removed)
- Better data locality (package info complete at creation)
- Cleaner API (functions receive unified PackageChanges structure)

Test coverage maintained: 100% (339 passing tests)
@cryptodev-2s cryptodev-2s force-pushed the feat/add-dependency-bump-checker branch from 9b796b6 to 88d116a Compare November 18, 2025 15:52
@cryptodev-2s cryptodev-2s requested a review from a team as a code owner November 18, 2025 15:52
@cryptodev-2s cryptodev-2s removed the request for review from a team November 18, 2025 15:54
@cryptodev-2s
Copy link
Contributor Author

@metamaskbot publish-preview

@github-actions
Copy link
Contributor

A preview build for this branch has been published.

You can configure your project to use the preview build with this identifier:

npm:@metamask-previews/[email protected]

See these instructions for more information about preview builds.

1 similar comment
@github-actions
Copy link
Contributor

A preview build for this branch has been published.

You can configure your project to use the preview build with this identifier:

npm:@metamask-previews/[email protected]

See these instructions for more information about preview builds.

@mcmire
Copy link
Contributor

mcmire commented Nov 18, 2025

check-deps works on any branch (feature branches included), not just release branches

Hmm. Currently this tool is centered around release management, so adding code that doesn't strictly relate to releases feels wrong. I did see your note about renaming this tool to monorepo-tools, but I'm not 100% convinced that's the right direction either. I will have to think about this some more.

BREAKING entries (peerDependencies) now appear before regular dependencies,
both alphabetically ordered in final changelog output.
@cryptodev-2s
Copy link
Contributor Author

@metamaskbot publish-preview

@github-actions
Copy link
Contributor

github-actions bot commented Dec 2, 2025

A preview build for this branch has been published.

You can configure your project to use the preview build with this identifier:

npm:@metamask-previews/[email protected]

See these instructions for more information about preview builds.

hasChangelogEntry now checks for **BREAKING:** prefix when matching
peerDependencies entries, preventing same dependency in both sections
from matching the wrong entry. This fixes the bug where updating
both entries would fail because both matched the first entry found.
When validating changelogs for a release version, the error message
now correctly shows the version section (e.g., [1.2.3]) instead of
always showing [Unreleased].
Automatically detect package rename info from package.json scripts
and pass it to parseChangelog to correctly handle changelogs with
old package name tags.
@cryptodev-2s cryptodev-2s requested a review from mcmire December 2, 2025 19:32
When updating existing entries and adding new ones for renamed packages,
the second parseChangelog call was missing the packageRename parameter.
This ensures both calls include packageRename for consistency.
cryptodev-2s and others added 2 commits December 2, 2025 23:03
Remove restrictive '@' filter that was silently ignoring non-scoped
packages like lodash, react, and typescript. The regex pattern already
handles both scoped and non-scoped packages correctly.
@cryptodev-2s cryptodev-2s force-pushed the feat/add-dependency-bump-checker branch from a40520c to 0d1de51 Compare December 3, 2025 19:46
@cryptodev-2s cryptodev-2s force-pushed the feat/add-dependency-bump-checker branch from fcf0548 to 23d29bf Compare December 3, 2025 22:47
@cryptodev-2s cryptodev-2s force-pushed the feat/add-dependency-bump-checker branch from 23d29bf to 259d980 Compare December 3, 2025 22:59
CHANGELOG.md Outdated
- Auto-updates changelogs with `--fix` flag, preserving PR history
- Detects package releases and validates/updates in correct changelog section (Unreleased vs specific version)
- Smart PR concatenation when same dependency bumped multiple times
- Usage: `yarn check-dependency-bumps --fix --pr <number>`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is how we'd call the script from other repos? How is that possible when this isn't registered in package.json as a bin script?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The command was incorrect, this step is actually handled by the create-release-branch CLI. I’ve just fixed the changelog accordingly.

By the way, I’m in the process of moving this over to auto-changelog instead. Here’s the draft PR: MetaMask/auto-changelog#267, I’ll mark it ready shortly.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh I see, didn't see this comment until now. Maybe this should be moved back into draft then?

Copy link
Contributor Author

@cryptodev-2s cryptodev-2s Dec 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No worries, I will move it back to draft and apply all the suggestions you mentioned here. They are also relevant for auto-changelog, since much of the logic is moved.
Thank you for the review!


// Track current file
if (line.startsWith('diff --git')) {
const match = line.match(/b\/(.+)/u);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: File path regex fails for paths containing /b/ directory

The regex /b\/(.+)/u used to extract file paths from git diff headers matches the first occurrence of b/ in the line. For a diff line like diff --git a/packages/b/package.json b/packages/b/package.json, the regex incorrectly matches the b/ inside packages/b/ and captures package.json b/packages/b/package.json instead of the intended packages/b/package.json. The regex could be changed to b\/(.+)$/u to match the space-prefixed b/ that marks the destination path at the end of the line.

Fix in Cursor Fix in Web


export type CommandLineArguments = {
export type ReleaseCommandArguments = {
_: string[];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was is this for? Do we need to return this from readCommandLineArguments? It doesn't seem to be used anywhere.

defaultBranch: args.defaultBranch,
interactive: args.interactive,
port: args.port,
} as ReleaseCommandArguments;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see that this type assertion can be removed if you undo the tempDirectory type change (back to string | undefined). Is there a reason that type change was made? Ideally we'd avoid type assertions if we can.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see that the CheckDepsCommandArguments type assertion is there for the same reason.

* Represents a single dependency version change
*/
export type DependencyChange = {
package: string;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Descriptions would be helpful for these properties

* @param options - Configuration options.
* @param options.fromRef - The starting git reference (optional).
* @param options.toRef - The ending git reference (defaults to HEAD).
* @param options.defaultBranch - The default branch to compare against (defaults to main).
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Should we call this baseBranch instead? The term defaultBranch suggests that this command is primarily meant for feature branches directed at main. But there is nothing about this workflow that seems to care whether the base branch is the default branch for the repo or not.

// Auto-detect branch changes if fromRef not provided
if (!actualFromRef) {
const currentBranch = await getCurrentBranchName(projectRoot);
stdout.write(`\n📌 Current branch: ${currentBranch}\n`);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Some of these messages could be pretty confusing if one of the parameters gets set to an empty string; it might return a warning message that doesn't tip us off to the problem. For example, if currentBranch was an empty string, this would suggest that the next line is the branch name, which it isn't. And with some of the later messages, inputting an empty string as the default branch name would be similarly confusing.

I have a habit of wrapping embedded parameters like this in single-quotes, so it's clear that there is something here in case it gets set to an unexpected value like an empty string, e.g.:

Suggested change
stdout.write(`\n📌 Current branch: ${currentBranch}\n`);
stdout.write(`\n📌 Current branch: '${currentBranch}'\n`);

Perhaps that would be helpful for these messages too

{ cwd: projectRoot },
);
} catch {
// If local branch doesn't exist, try remote
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: This would end up being misleading a lot of the time, e.g. if main was out-of-date locally, the first attempt would succeed and give the wrong answer.

It also feels unexpected for this function to make guesses about what the default branch is. main and origin/main are different branches. If we're going to auto-prefix a remote name, at the very least we should document that the function will do that.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would suggest that this function accept the default branch as-is, but make the default ${remote}/main instead of main.

And speaking of which, we should also allow customizing the remote name. Using something other than origin is not uncommon.

try {
return await getStdoutFromCommand(
'git',
['merge-base', 'HEAD', defaultBranch],
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: I see we're using merge-base in a similar manner in the restoreFiles function in src/repo.ts. Maybe we can consolidate the two implementations by using a utility function?

'git',
[
'diff',
'-U9999', // Show maximum context to ensure section headers are visible
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: 😅 sorry it took me way too long to understand what a "section header" was in this context. Here's a suggestion to maybe make it clearer

Suggested change
'-U9999', // Show maximum context to ensure section headers are visible
'-U9999', // Show maximum context to ensure full dependency lists are visible

* @param projectRoot - The project root directory.
* @returns The raw git diff output.
*/
async function getGitDiff(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: We should perhaps call this something less misleading, like getManifestGitDiff?

{ cwd: projectRoot },
);
} catch (error: any) {
// Git diff returns exit code 1 when there are no changes
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it? It doesn't seem to when I've tried it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like it only does this if you pass the --exit-code flag, which we haven't done here.

@cryptodev-2s cryptodev-2s marked this pull request as draft December 5, 2025 22:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants