Skip to content

Commit and release process

Bob Nystrom edited this page Apr 24, 2025 · 2 revisions

This page describes how changes to dart_style are committed to the repo and how new versions are released and rolled out. If you don't contribute to the formatter, it's probably not useful to you.

Language versioning style changes

Most changes to the formatter are changes to the style rules it applies. Unless the change is a fix for obviously broken output (like reordering code or dropping code on the floor), the change should be language versioned.

This means that the style change should only apply to Dart code whose language version is the next upcoming Dart SDK version number of later. This way, users don't have their formatting spontaneously change on them when they upgrade to a new Dart SDK. Instead, it's only when they edit their SDK constraint and opt in to the latest language version that they also see the style changes.

For example, let's say the most recent release of the Dart SDK is 3.9.0. You make a style change in the formatter. That change should only apply to files whose language version is 3.10 or later. If the language version is 3.9 or earlier, the previous style should be applied.

This means that making style changes almost always increases the complexity of the formatter because it needs to support the old and new style. Eventually, we hope to drop support for particularly old styles.

Landing a change

The dart_style repo mostly follows a typical GitHub workflow. Changes are uploaded to branches and the author sends a pull request. Code review happens on the pull request and, once approved, the author squashes and merges the PR onto the main branch.

The main branch should always be in a shippable state. Any change that requires multiple commits before the formatter is back into a usable state should be done either on a branch or behind some sort of flag so that users aren't impacted until the work is completely done.

We also need to keep track of what changes have landed and whether a given change has been shipped. To do that:

Update the CHANGELOG

Every PR that affects something user-visible should also add an entry to the CHANGELOG file describing the change.

Update the package version

When a PR lands a change, it should update the dart_style version number in the pubspec.yaml file to indicate that there are unpublished changes. If the dart_style version does not end in -wip, then the repo should be exactly equal to the published dart_style package at that version.

Otherwise, the version ends in -wip, and the number preceding that is what the next published version number will be. If we see that the version ends in -wip, we know there are unpublished changes. The way that number is bumped from the previously published version depends on what kinds of changes have landed. For example, let's say the last published version was 1.2.3:

  • If main has some unpublished minor non-breaking changes, then the pubspec's version would be 1.2.4-wip.

  • If main has significant new features but no breaking changes, then the pubspec's version would be 1.3.0-wip.

  • If main has a breaking change, then the version would be 2.0.0-wip.

We don't consider style changes to be "breaking" (otherwise, almost every change would be a breaking change), so most revisions to dart_style are patch releases. Breaking changes usually mean a breaking change to the library API or CLI options. More impactful style changes may necessitate a minor or major version increment.

When you land a change on main, make sure that you update the pubspec's version number as needed to reflect your change. Note that you'll only need to bump the -wip version the first time a change of a certain kind is landed. So if, for example, there are already minor changes and the pubspec's version is 1.2.4-wip, then you can land more minor changes without touching the version. You may need to change the number of an already--wip version if you are landing a change of greater caliber than previous ones. So if the version is 1.2.4-wip and you land the first significant new feature, you'd bump it to 1.3.0-wip.

Update the internal version number

If you changed the pubspec version number, then you also need to change the corresponding number in lib/src/cli/formatter_options.dart. This is what's printed when you run dart format --version.

Shipping the formatter

The formatter reaches users in two main ways:

  • From the Dart SDK, by running dart format. This uses a version of dart_style that is copied into the Dart SDK.

  • As a library, by depending on the dart_style package and using the public API it exposes. (The package also exposes a command line tool you can invoke using dart run or dart pub global activate, but that's rarely used).

Most users run the formatter through the Dart or Flutter SDKs, so "shipping" the formatter is usually synchronized with a Dart SDK release. In particular, since the formatter internally encodes the language versions it supports, we need to update the formatter whenever a new Dart SDK is going to be released.

Shipping a new version of the formatter means both rolling it into the Dart SDK and publishing a new version of the package onto pub.dev, in that order. It is usually synchronized to an upcoming Dart SDK release.

Support the latest language version

When a new Dart SDK is about to ship, it means the highest supported language version is about to increase. The formatter shipped with that SDK obviously needs to support that language version.

  1. In lib/src/dart_formatter.dart, increment latestLanguageVersion to the upcoming Dart language version.

  2. If there are any language changes in that version, ensure that the formatter supports them and has tests.

  3. Make sure that the lower bound of dart_style's dependency on analyzer is high enough to ensure that it picks a version of analyzer that supports the new language. In practice, this usually means bumping the dependency to require the latest analyzer version.

Roll dart_style into the Dart SDK

Once a package is published to pub, it's very hard to unpublish it. We want to make sure it doesn't have any regressions or unanticipated style changes. We validate that by testing inside Google before publishing on pub. The Dart SDK is automatically rolled into Google's monorepo on a frequent basis, so by rolling a commit of dart_style into the SDK, we get internal testing on a very large corpus of Dart code.

When we have a commit of the formatter that we are ready to ship, the first step is rolling it into the Dart SDK:

  1. In the Dart SDK repo, update the "dart_style_rev" line in the DEPS file to point to the new commit hash.

TODO: If there are style changes that necessitate updating the pre-built SDK, describe how to do that.

  1. If there are user-visible changes, then update the SDK's CHANGELOG with those changes. You can mostly just copy the relevant entries from dart_style's CHANGELOG over.

  2. Upload that change and send it out for review (rnystrom@, kallentu@, athom@, or anyone on Dart eng-prod can review it).

    If the formatter change includes support for some new language feature, this may cause CFE golden file tests to fail on the trybots. That's because those tests run the formatter if it succeeds or fall back to using the unformatted output if it fails. CFE folks often commit tests for new language features before the formatter supports them, so those golden files will be unformatted. When rolling a new formatter in, those tests can now be successfully formatted, so the (now formatted) expected output doesn't match the (unformatted) golden file.

    If you see test failures like this, the output will have a command to run to update the golden files. Run that, verify that it's just formatting changes, and upload those fixes with the change.

  3. Once you get approval and the bots are green, land the change.

Wait a few days for the SDK to get rolled into Google's monorepo and see if there are any problems. If so, fix them. Otherwise, this commit is now ready to be published...

Publish a new version of dart_style

When a version of the formatter is rolled into the Dart SDK, we should also publish it. That way, there is always a version of the dart_style library available for tools like code generators that matches the formatting behavior of dart format.

  1. From a local clone of dart_style on the main branch, run:

    $ dart run grinder bump

    That runs the tests and some other sanity checks to make sure the release looks good. It then updates the version number in the pubspec to remove the -wip part.

  2. Assuming everything passes and looks good, create and upload a new branch with the changes and send out a PR for review (@munificent, @kallentu, or @natebosch). At this point, the changes should literally just be tweaking the version number in two places. You may also want to clean up the CHANGELOG a bit.

  3. Once the PR is approved, squash and merge it to main. A GitHub action will leave a comment on the PR telling you how to publish a version. That comment will contain the new version number. Click that number to open a page to create a new GitHub release for the version.

  4. Copy the relevant entries from the CHANGELOG into the release notes and then click "Publish release". That creates a new GitHub release for the version which in turn kicks off another Github action that automatically publishes that version to pub.dev.

  5. Go to pub.dev, search for "dart_style" and bask in the glory of having shipped a new version to the world.