Skip to content

Manage CODEOWNERS programmatically (and regenerate it)#9529

Merged
mcmire merged 2 commits into
mainfrom
generate-codeowners
Jul 20, 2026
Merged

Manage CODEOWNERS programmatically (and regenerate it)#9529
mcmire merged 2 commits into
mainfrom
generate-codeowners

Conversation

@mcmire

@mcmire mcmire commented Jul 15, 2026

Copy link
Copy Markdown
Collaborator

Explanation

At the time of writing we have 97 (!!) packages in this monorepo, which are owned by various teams across MetaMask. However, managing codeownership via the CODEOWNERS file is tedious and error-prone.

We do use Yarn constraints to ensure that each package is configured in CODEOWNERS correctly, so that helps. However, in a future commit, we would like to update the set of per-package files that are owned by Core Platform to include more than just package.json and CHANGELOG. at the moment, we need to do that manually — Yarn constraints won't allow us to automatically apply those updates — and it would be painful.

To address this problem, this commit redefines CODEOWNERS as a TypeScript file (codeowners.ts in the root) and adds a script to generate CODEOWNERS from this file. The script also comes with a check command which is plugged into the lint pipeline to ensure that CODEOWNERS is always up to date.

The primary "feature" of codeowners.ts is that it contains a configuration object which maps of teams to packages that are owned by those teams (along with some other metadata). Ideally, when a new package is added, all a team needs to do is update this map and then the appropriate codeowner rules will be automatically added to support that package.

That said, to ensure minimal changes between the current version of CODEOWNERS and the new version in this PR, some compromises needed to be made, and as a result, codeowners.ts is not as dynamic or simple as it could be. In the future, we plan on making further changes to make codeowners.ts easier to maintain.

References

To merge #8384, I had to enable admin powers to bypass codeowner requirements. This is because tsconfig files are not co-owned by Core Platform. We could of course change CODEOWNERS to do this, but what if we want to add other kinds of files in the future? This file is already somewhat difficult to maintain and we don't want to make it worse. This PR came out of that observation.

Manual testing

  • Run yarn codeowners:check. It should not print any output, and should exit with 0.
  • Run yarn codeowners:generate. It should also not print any output, it should not change .github/CODEOWNERS, and should exit with 0.
  • Open codeowners.ts and add @MetaMask/core-platform to the list of teams for account-tree-controller.
  • Run yarn codeowners:check. It should print an error.
  • Run yarn codeowners:generate. .github/CODEOWNERS should change (the list of owners for account-tree-controller should include @MetaMask/core-platform`).
  • Undo the changes to .github/CODEOWNERS.
  • Open codeowners.ts and remove initializationPath from address-book-controller.
  • Run yarn codeowners:check. It should print an error.
  • Run yarn codeowners:generate. .github/CODEOWNERS should change (address-book-controller should be removed from the "Initialization" section).

Checklist

  • I've updated the test suite for new or updated code as appropriate
  • I've updated documentation (JSDoc, Markdown, etc.) for new or updated code as appropriate
  • I've communicated my changes to consumers by updating changelogs for packages I've changed
  • I've introduced breaking changes in this PR and have prepared draft pull requests for clients and consumer packages to resolve them

Note

Low Risk
Repo tooling and generated ownership metadata only; no runtime product or security logic changes.

Overview
CODEOWNERS is now generated from a root codeowners.ts config instead of being edited by hand. Teams update package ownership (and optional wallet initializationPath metadata) in one place; the generator builds team sections, joint ownership, initialization paths, and release package.json / CHANGELOG.md rules with aligned column padding.

New yarn codeowners:generate and yarn codeowners:check (wired into lint, lint:fix, and the CI lint matrix) keep .github/CODEOWNERS in sync. tsconfig.json includes codeowners.ts for typechecking.

Reviewed by Cursor Bugbot for commit 2426a9b. Bugbot is set up for automated code reviews on this repo. Configure here.

At the time of writing we have 97 (!!) packages in this monorepo, which
are owned by various teams across MetaMask. However, managing
codeownership via the CODEOWNERS file is tedious and error-prone. We do
use Yarn constraints to ensure that each package is configured in
CODEOWNERS correctly, so that helps. However, in a future commit, we
would like to update the set of per-package files that are owned by Core
Platform to include more than just `package.json` and `CHANGELOG`, and
at the moment, we need to do that manually; Yarn constraints won't allow
us to automatically apply those updates.

To address this problem, this commit redefines `CODEOWNERS` as a
TypeScript file (`codeowners.ts` in the root) and adds a script to
generate CODEOWNERS from this file. The script also comes with a `check`
command which is plugged into the lint pipeline to ensure that
CODEOWNERS is always up to date.

The primary "feature" of `codeowners.ts` is that it contains a
configuration object which maps of teams to packages that are owned by
those teams (along with some other metadata). Ideally, when a new
package is added, all a team needs to do is update this map and then the
appropriate codeowner rules will be automatically added to support that
package.

To ensure minimal changes between the current version of CODEOWNERS and
the new version in this PR, we needed to make some compromises, and
`codeowners.ts` is not as dynamic or simple as it could be. We plan on
making further changes in the future to make it more useful.
@mcmire
mcmire force-pushed the generate-codeowners branch from 83ad642 to a6936c4 Compare July 15, 2026 20:14
@mcmire mcmire changed the title Manage codeowners programmatically (and regenerate) Manage codeowners programmatically (and regenerate them) Jul 15, 2026
@mcmire mcmire changed the title Manage codeowners programmatically (and regenerate them) Manage CODEOWNERS programmatically (and regenerate it) Jul 15, 2026
Comment thread codeowners.ts
*
* @returns The team sections.
*/
function buildTeamSections(): CodeownersSection[] {

@mcmire mcmire Jul 15, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

As I alluded to in the PR description, this file is way longer than it should be, mainly because I wanted the diff for CODEOWNERS to be relatively easy to read. In the future we should be able to generate most of this purely from PACKAGES (with a few exceptions). For instance, this would be the bulk of the new version:

function buildPackageSections() {
  return PACKAGES_BY_GROUP.flatMap((group) => {
    return [
      [`### ${group.name}`],
      group.packages.flatMap((package) => {
        return [
          {
            pattern: package.workspacePath,
            owners: group.teams,
          },
          ...(
            package.isPartOfWalletLibrary
              ? {
                pattern: `/packages/wallet/src/initialization/instances/${package.directoryName}`,
                owners: group.teams,
              }
              : {},
          ),
          {
            pattern: `${package.workspacePath}/package.json`,
            owners: [CORE_PLATFORM_TEAM]
          },
          {
            pattern: `${package.workspacePath}/CHANGELOG.json`,
            owners: [CORE_PLATFORM_TEAM]
          }
        ]
      })
    ]
  })
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

mainly because I wanted the diff for CODEOWNERS to be relatively easy to read

Is this the reason why a lot of stuff here is duplicated? Fine with me to resolve in a follow-up if you think that's easier.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Yes, I plan on posting another PR that simplifies this file.

Comment thread scripts/manage-codeowners/main.ts Outdated
# Lines starting with '#' are comments.
# Each line is a file pattern followed by one or more owners.

# Note: Please keep this synchronized with the \`teams.json\` file in the repository root.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Using a TypeScript file to generate CODEOWNERS also lets us automatically check this in the future. That should allow us to remove this comment.

Comment thread .github/CODEOWNERS

@mcmire mcmire Jul 15, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

To read this diff, you'll probably want to turn off whitespace. (I could have coded whitespace settings into codeowners.ts but I didn't want to make that file worse than it already was.)

Comment thread .github/CODEOWNERS
/packages/account-tree-controller/package.json @MetaMask/accounts-engineers @MetaMask/core-platform
/packages/account-tree-controller/CHANGELOG.md @MetaMask/accounts-engineers @MetaMask/core-platform
/packages/accounts-controller/package.json @MetaMask/accounts-engineers @MetaMask/core-platform
/packages/accounts-controller/CHANGELOG.md @MetaMask/accounts-engineers @MetaMask/core-platform

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

For some reason accounts-controller/CHANGELOG.md was listed further down than it should have been.

Comment thread .github/CODEOWNERS
/packages/assets-controller/package.json @MetaMask/metamask-assets @MetaMask/core-platform
/packages/assets-controller/CHANGELOG.md @MetaMask/metamask-assets @MetaMask/core-platform
/packages/config-registry-controller/package.json @MetaMask/networks @MetaMask/core-platform
/packages/config-registry-controller/CHANGELOG.md @MetaMask/networks @MetaMask/core-platform

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

The files for config-registry-controller were listed in the wrong order.

@mcmire
mcmire marked this pull request as ready for review July 15, 2026 20:20
@mcmire
mcmire requested a review from a team as a code owner July 15, 2026 20:20
@mcmire
mcmire temporarily deployed to default-branch July 15, 2026 20:20 — with GitHub Actions Inactive
Comment thread .github/CODEOWNERS
Comment thread codeowners.ts
* of truth for which team(s) own a package, and is used to populate the
* sections in `codeownersSections` below.
*/
const PACKAGES: Record<string, PackageInfo> = {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Maybe this should be a separate JSON or YAML file in .github, like .github/CODEOWNERS.yml?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

It was my intent that codeowners.ts was the configuration file, just a programmatic version of it (similar to eslint.config.mjs). My concern with adding .github/CODEOWNERS.yml is that we'd essentially have two configuration files.

As mentioned before this file will be a lot simpler in the future, so it will be primarily the PACKAGES list along with some code to generate each line in CODEOWNERS. That said, do you think any of this code should be in manage-codeowners.ts?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think eventually this file should be just the PACKAGES list, with the code to generate the CODEOWNERS file in manage-codeowners.ts? But my thinking was at that point the PACKAGES could just as well be a JSON or YAML file (YAML for comment support and better readability). Don't think having two configuration files is a problem as long as the GitHub one is clearly marked as auto-generated, with the YAML one essentially becoming the real config file.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Gotcha. I did consider making a JSON/YAML file initially but thought that if any team needed to make an exception to the existing logic, they could do so easily if the file were a TypeScript file instead of JSON/YAML. But maybe I can allow for some exceptions. I'll play around with that in the followup PR.

@Mrtenz Mrtenz left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Looks good to me!

@mcmire
mcmire enabled auto-merge July 20, 2026 13:28
@mcmire
mcmire added this pull request to the merge queue Jul 20, 2026
Merged via the queue into main with commit 4a8e09d Jul 20, 2026
427 checks passed
@mcmire
mcmire deleted the generate-codeowners branch July 20, 2026 13:37
sirtimid added a commit that referenced this pull request Jul 20, 2026
Address review feedback on #9527:

- Make `instanceOptions.gasFeeController.clientId` required and drop the
  `'cli'` default, so every client (extension, mobile, wallet-cli) must
  identify itself to the gas API rather than silently inheriting a
  default. `gasFeeController` is now a required instance-options key
  (mirroring `networkController`); wallet-cli passes `clientId: 'cli'`
  explicitly.
- Throw a descriptive error naming the members of the cycle when
  `orderByDependencies` cannot satisfy the declared dependencies,
  instead of silently deferring to a later "handler not registered"
  messenger error.

Also re-add the `gas-fee-controller` instance rule via the new
programmatic `codeowners.ts` generator (#9529) picked up in the rebase
onto main.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants