Skip to content

Change management

In a typical monorepo, we want to build up changes for publishable Workspaces over time while we work on features and major changes. Because we are using a monorepo with source dependencies, we should not need to publish often, because our local Workspace’s are benefitting from code changes immediately.

We still may have certain modules that we publish to a public or private registry. Developer workflows will have a few additional steps.

One of the few opinionated pieces of oneRepo is in how publishable modules log & track changes, update versions, and publish to their registry. All of this is handled by the change command set.

Unlike other change entry systems, oneRepo compiles change entry files into a .changes folder within each workspace. These files use incremental IDs to help developers review potential order of changes as well as a unique string to prevent collisions across branches. By default, the change entries will be named with a hash of their contents:

  • Directorymodules
    • Directoryburritos
      • Directory.changes
        • 001-978af1g.md
        • 002-3ah36g1.md
      • package.json
    • Directorytacos
      • Directory.changes
        • 001-978af1g.md
      • package.json

If you prefer something a little more fun, add the human-id package to your Root Workspace and set changes.filenames to 'human' in your Root configuration file.

  1. Add the human-id dependency

    Use the dependencies command to make things easy.

    Terminal window
    one dependencies add -w <root> --dev human-id
  2. Update the config

    Use the dependencies command to make things easy.

    onerepo.config.js
    import type { Config } from 'onerepo';
    export default {
    changes: {
    filenames: 'human',
    },
    } satisfies Config;
  3. Enjoy your funny filenames.

    • Directorymodules
      • Directoryburritos
        • Directory.changes
          • 001-late-lions-accept.md
          • 002-pretty-jars-dig.md
        • package.json
      • Directorytacos
        • Directory.changes
          • 001-swift-seals-walk.md
        • package.json
  1. Modify code and add change files

    For each Workspaces that publishes a module, whenever we commit notable changes that should appear in a changelog, we add change entry files using one change add command. This command will help us create individual changelog entries in the form of Markdown formatted files with YAML frontmatter. The files are stored away in each related Workspace until needed later.

    Terminal window
    one change add
    📦 What Workspace(s) would you like to
    add a change entry to?
    ↗ Modified Workspaces
    ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯
    ❯◯ @onerepo/graph
    ◯ onerepo
    ◯ @onerepo/plugin-changesets
    → Affected Workspaces
    ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯
    ◯ @onerepo/builders
    ◯ @onerepo/file
  2. Commit files

    Add change entries will create Markdown files with the change entry content in each appropriate Workspace. You will see these files in <workspace>/.changes/*.md. It is important to track and commit these files to the repository, as we will use them later on down the line when we’re ready to publish a new version.

    Terminal window
    git status
    On branch add-change-entries
    Changes to be committed:
    new file: modules/graph/.changes/3-d1eb2419.md
    git commit -m "add change entries"
  3. Pull request & merge” noTermina

    Create a pull request with your commits to review. Since change entry files are tracked, you can always edit them later if there’s a mistake or you would like to add more information for the change.

    Once ready, merge your pull request to your main branch and move on.

Eventually, we will need to publish one or more Workspaces to our registry for use in another repository. To do this, we follow another short workflow:

  1. Update versions

    First, we increment the version automatically using the command one change version. This command will prompt for the Workspace or Workspaces that we would like to publish and automatically ensure that any dependencies with changes are also incremented appropriately.

    This extra increment is necessary to ensure that each Workspace is published with the latest changes. Without this step, it would be possible that critical features would end up missing and causing errors in your published modules.

    Terminal window
    one change version
    📦 What Workspace(s) should be
    versioned for publishing?
    ↗ Workspaces with change entries
    ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯
    ❯◯ @onerepo/graph (1.0.0)
    ◯ onerepo (1.0.0)
    ◯ @onerepo/plugin-changesets (1.0.0)
    → Workspaces modified & missing change entries
    ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯
    ◯ @onerepo/builders (1.0.0)
    ◯ @onerepo/file (1.0.0)
  2. Verify new versions

    Next, you will be asked to verify new versions. This step will list new versions for dependencies of the Workspace(s) that you requested to version that also have changes in them.

    You cannot version and publish the requested Workspaces without including these Workspaces as well.

    Terminal window
    one change version
    (…continued)
    ┌ Verify Workspaces
    Workspace Current Type New
    ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯
    │ @onerepo/graph 0.10.0 minor 0.11.0
    The following dependencies also require versioning
    ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯
    │ @onerepo/logger 0.6.0 patch 0.6.1
    │ @onerepo/package-manager 0.5.0 patch 0.5.1
    │ @onerepo/subprocess 0.7.0 patch 0.7.1
    └ ✔ 2ms
    ? Is it okay to proceed? (Y/n)
  3. Pull request & merge” noTermina

    The change entries related to the versioned Workspaces will be removed and their contents recreated into each Workspace’s CHANGELOG.md file.

    Add and commit all changes, then create a pull request with your commits to review.

    Once ready, merge your pull request to your main branch.

  4. Publish

    Your updated versions are now ready to publish. Either automate this step in your CI pipline after pull requests are merged or run the command manually.

    Terminal window
    one change publish

Aliases: one changes, one changesets

Manage changes and changesets for publishable Workspaces.

Terminal window
one change <command>

Aliases: one change

Add changesets for modified Workspaces.

Terminal window
one change add [options...]

This command will prompt for appropriate Workspace(s), prompt for the release type, and request a descriptive change entry. Once all information has been entered, a change entry file will be created in the appropriate Workspace(s). Be sure to commit these files – they will be used later when you are ready to version and publish Workspaces to the registry for use outside of the Monorepo.

OptionTypeDescription
--addboolean, default: trueAdd the modified package.json files to the git stage for committing.
--affectedbooleanSelect all affected Workspaces. If no other inputs are chosen, this will default to true.
--all, -abooleanRun across all Workspaces
--type"major", "minor", "patch"Provide a semantic version bump type. If not given, a prompt will guide you through selecting the appropriate type.
--workspaces, -warrayList of Workspace names to run against
Advanced options
OptionTypeDescription
--filenames"hash", "human", default: "hash"Filename generation strategy for change files. If 'human', ensure you have the human-id package installed.
--from-refstringGit ref to start looking for affected files or Workspaces
--prompts"guided", "semver", default: "guided"Change the prompt question & answer style when adding change entries.
--stagedbooleanUse files on the git stage to calculate affected files or Workspaces. When unset or --no-staged, changes will be calculated from the entire branch, since its fork point.
--through-refstringGit ref to start looking for affected files or Workspaces

Migrate from Changesets to oneRepo changes.

Terminal window
one change migrate
Advanced options
OptionTypeDescription
--filenames"hash", "human", default: "hash"Filename generation strategy for change files. If 'human', ensure you have the human-id package installed.

Aliases: one change release

Publish all Workspaces with versions not available in the registry.

Terminal window
one change publish [options...]

This command is safe to run any time. By default, only Workspaces that have previously gone through the one change version process will end up being published. Use --all for all Workspaces or --workspaces <workspace-name> to specify individual Workspaces to try publishing.

For each Workspace, the registry will be queried first to ensure the current version in the Workspace does not yet exist in the registry. If a version does exist, the Workspace will be skipped.

The pre-publish and post-publish task lifecycles will be run during this command for each Workspace published. These Task lifecycles are roughly equivalent to the prepack and postpack provided by npm, Yarn, and pnpm.

OptionTypeDescription
--all, -abooleanRun across all Workspaces
--otpbooleanSet to true if your publishes require an OTP for NPM.
--skip-authbooleanSkip auth checks. This may be necessary for some internal registries using PATs or tokens.
--workspaces, -warrayList of Workspace names to run against
Advanced options
OptionTypeDescription
--affectedbooleanSelect all affected Workspaces. If no other inputs are chosen, this will default to true.
--allow-dirtybooleanBypass checks to ensure there are no un-committed changes.
--from-refstringGit ref to start looking for affected files or Workspaces
--stagedbooleanUse files on the git stage to calculate affected files or Workspaces. When unset or --no-staged, changes will be calculated from the entire branch, since its fork point.
--through-refstringGit ref to start looking for affected files or Workspaces

Publish the api Workspace and its dependencies, if necessary.

Terminal window
one change publish -w api

Attempt to publish all non-private Workspaces.

Terminal window
one change publish --all

Preview the next versions and changelogs for Workspaces.

Terminal window
one change show
OptionTypeDescription
--all, -abooleanRun across all Workspaces
--format"json", "plain", default: "plain"Choose how the results will be returned.
--workspaces, -warrayList of Workspace names to run against

Aliases: one change snap

Publish a snapshot pre-release.

Terminal window
one change snapshot

Periodically you may want to publish a snapshot release – something that needs testing from the registry, but should not update the current version nor be made widely available. For these cases, use --snapshot and versions will be published in the format 0.0.0-{tag}-{hash}, where {hash} is a short version of the current git sha. By appending a prerelease to version 0.0.0, discoverability is greatly reduced.

OptionTypeDescription
--all, -abooleanRun across all Workspaces
--allow-dirtybooleanBypass checks to ensure there are no un-committed changes.
--otpbooleanPrompt for one-time password before publishing to the registry
--resetboolean, default: trueReset package.json changes before exiting. Use --no-reset to disable.
--tagstring, default: "prerelease"Distribution tag to apply when publishing the pre-release.
--workspaces, -warrayList of Workspace names to run against

Aliases: one change required

Ensure there are change entries for every modified public Workspace.

Terminal window
one change verify

Add this to your pre-commit or pre-merge task lifecycles to ensure that changes to public & publishabled Workspaces are always accompanied with change entries.


Update version numbers for publishable Workspaces

Terminal window
one change version
OptionTypeDescription
--all, -abooleanRun across all Workspaces
--allow-dirtybooleanBypass checks to ensure there are no un-committed changes.
--prerelease, --pre, --pre-releasestringCreate a pre-release using the specified identifier.
--workspaces, -warrayList of Workspace names to run against

Create a prerelease for the next version the form of 1.2.3-alpha.0.

Terminal window
one change version --prerelease=alpha

Can I edit change files? Yes. This is one of the main benefits of the approach we use. All change files are tracked in your git repository until they’re ready to be consumed by a the change version command. Until then, it’s safe to add, edit, or delete change files as needed.

Got a typo? Want to add more context? No problem! Just modify the appropriate change files, commit them, and move on.

Versioning deleted my change files. Is that okay? This is expected! Change files are temporary placeholders that inform the change version command how to pick the appropriate next version. Once they are consumed, they get compiled back to each Workspace’s CHANGELOG.md file.

Can I edit my changelog after it’s written? Because change entries are written in isolation, it can be really helpful to edit your CHANGELOG.md file after versioning and before publishing to add context and make instructions flow better. You can safely modify your CHANGELOG.md files any time.

Why is the human-id package not used by default? oneRepo does its best to limit the number of dependencies it requires, especially those of significant weight. We really like human-id, but it is a large dataset and English-only, so it has been made optional.

oneRepo changes are inspired by 🦋 Changesets. While they provide a lot of the same functionality, there are various issues conflicting with oneRepo:

  • Changelogs are written to private packages (Issue #313)
  • Building and applying release plans is cumbersome with changesets when not using the @changesets/cli package directly. The oneRepo Graph does a fantastic job of handling Workspace linking and versioning.
  • Source Dependencies are one of the main reasons oneRepo was created, as other monorepo tooling doesn’t enforce or help with setting up and managing correct linking. Keeping changesets as a plugin makes this too optional and confusing to explain and implement.
  • It’s easy to forget to write changesets, which can prevent versioning & publishing. Changesets do not also take into account git changes in Workspaces and dependent Workspaces.
  • Changesets is haphazardly documented and difficult to onboard large teams. Many potentially helpful docs were stubbed in and forgotten for many years (example: Problems publishing in monorepos)

Conventional Commits is a standard specification for writing effective change entries via git commit messages. The commits are then parsed in order to determine version release type, breaking changes, and compiling changelogs.

While Conventional Commits is a wonderfully sound approach for single module repositories, it quickly degrades in Monorepos when developers work across a number of Workspaces or have changes that are less deserving of mention in changelogs.

There is also little room for error with Conventional Commits, because changelog entries are set in stone once commits are written – unless you allow rewriting history, which you likely do not.