Fix and update version checks (#3792)

Currently our version update checks aren't actually working; the script doesn't work correctly if no explicit --base-sha is passed, but that's always how CI is calling it.

Fixes https://github.com/flutter/flutter/issues/79823 (and version checks in general)

This makes a number of changes:
- Fixes it to work without --base-sha
- Adds tests that it works in that mode
  - And tightens existing tests to require ToolExit, not just any error, to reduce false-positive test success
- Adds verbose logging of the checks being done, to make it easier to debug this kind of issue in the future
- Tightens the exception handling for missing previous versions to just the line that's expected to fail in that case
- Only allows missing versions when "publish_to: none" is set
  - Adds that everywhere it's missing
  - Standardize the format in the repo to "none" (instead of also having "'none'").
- Allows the use of NEXT in CHANGELOG as a way of gathering changes that are worth noting, but not
  doing a publish cycle for. (Replaces the plan of using -dev versions, since that's actually harder to implement,
  and more confusing.)
  - Ensures that we don't forget to clean up NEXT entries when bumping versions
diff --git a/packages/android_alarm_manager/example/pubspec.yaml b/packages/android_alarm_manager/example/pubspec.yaml
index 7852534..e948ac2 100644
--- a/packages/android_alarm_manager/example/pubspec.yaml
+++ b/packages/android_alarm_manager/example/pubspec.yaml
@@ -1,5 +1,6 @@
 name: android_alarm_manager_example
 description: Demonstrates how to use the android_alarm_manager plugin.
+publish_to: none
 
 dependencies:
   flutter:
diff --git a/packages/android_intent/example/pubspec.yaml b/packages/android_intent/example/pubspec.yaml
index fc15451..0104749 100644
--- a/packages/android_intent/example/pubspec.yaml
+++ b/packages/android_intent/example/pubspec.yaml
@@ -1,5 +1,6 @@
 name: android_intent_example
 description: Demonstrates how to use the android_intent plugin.
+publish_to: none
 
 dependencies:
   flutter:
diff --git a/packages/battery/battery/example/pubspec.yaml b/packages/battery/battery/example/pubspec.yaml
index 648e9f5..a2a6527 100644
--- a/packages/battery/battery/example/pubspec.yaml
+++ b/packages/battery/battery/example/pubspec.yaml
@@ -1,5 +1,6 @@
 name: battery_example
 description: Demonstrates how to use the battery plugin.
+publish_to: none
 
 dependencies:
   flutter:
diff --git a/packages/camera/camera/example/pubspec.yaml b/packages/camera/camera/example/pubspec.yaml
index 5fdaf58..0c06de9 100644
--- a/packages/camera/camera/example/pubspec.yaml
+++ b/packages/camera/camera/example/pubspec.yaml
@@ -1,5 +1,6 @@
 name: camera_example
 description: Demonstrates how to use the camera plugin.
+publish_to: none
 
 dependencies:
   camera:
diff --git a/packages/connectivity/connectivity/example/pubspec.yaml b/packages/connectivity/connectivity/example/pubspec.yaml
index 58d6f0d..2eb6953 100644
--- a/packages/connectivity/connectivity/example/pubspec.yaml
+++ b/packages/connectivity/connectivity/example/pubspec.yaml
@@ -1,5 +1,6 @@
 name: connectivity_example
 description: Demonstrates how to use the connectivity plugin.
+publish_to: none
 
 dependencies:
   flutter:
diff --git a/packages/connectivity/connectivity_macos/example/pubspec.yaml b/packages/connectivity/connectivity_macos/example/pubspec.yaml
index d130158..27baef7 100644
--- a/packages/connectivity/connectivity_macos/example/pubspec.yaml
+++ b/packages/connectivity/connectivity_macos/example/pubspec.yaml
@@ -1,5 +1,6 @@
 name: connectivity_example
 description: Demonstrates how to use the connectivity plugin.
+publish_to: none
 
 dependencies:
   flutter:
diff --git a/packages/device_info/device_info/example/pubspec.yaml b/packages/device_info/device_info/example/pubspec.yaml
index 36b6d6e..5c158a3 100644
--- a/packages/device_info/device_info/example/pubspec.yaml
+++ b/packages/device_info/device_info/example/pubspec.yaml
@@ -1,5 +1,6 @@
 name: device_info_example
 description: Demonstrates how to use the device_info plugin.
+publish_to: none
 
 dependencies:
   flutter:
diff --git a/packages/device_info/device_info_platform_interface/lib/model/android_device_info.dart b/packages/device_info/device_info_platform_interface/lib/model/android_device_info.dart
index 961d373..b61dc14 100644
--- a/packages/device_info/device_info_platform_interface/lib/model/android_device_info.dart
+++ b/packages/device_info/device_info_platform_interface/lib/model/android_device_info.dart
@@ -29,7 +29,7 @@
     required this.isPhysicalDevice,
     required this.androidId,
     required List<String> systemFeatures,
-  })   : supported32BitAbis = List<String>.unmodifiable(supported32BitAbis),
+  })  : supported32BitAbis = List<String>.unmodifiable(supported32BitAbis),
         supported64BitAbis = List<String>.unmodifiable(supported64BitAbis),
         supportedAbis = List<String>.unmodifiable(supportedAbis),
         systemFeatures = List<String>.unmodifiable(systemFeatures);
diff --git a/packages/espresso/example/pubspec.yaml b/packages/espresso/example/pubspec.yaml
index 6e824ac..fe40421 100644
--- a/packages/espresso/example/pubspec.yaml
+++ b/packages/espresso/example/pubspec.yaml
@@ -1,6 +1,6 @@
 name: espresso_example
 description: Demonstrates how to use the espresso plugin.
-publish_to: 'none'
+publish_to: none
 
 environment:
   sdk: ">=2.12.0-259.9.beta <3.0.0"
diff --git a/packages/file_selector/file_selector/example/pubspec.yaml b/packages/file_selector/file_selector/example/pubspec.yaml
index 580237c..b2bbbb0 100644
--- a/packages/file_selector/file_selector/example/pubspec.yaml
+++ b/packages/file_selector/file_selector/example/pubspec.yaml
@@ -1,7 +1,6 @@
 name: example
 description: A new Flutter project.
-
-publish_to: 'none' # Remove this line if you wish to publish to pub.dev
+publish_to: none
 
 version: 1.0.0+1
 
diff --git a/packages/flutter_plugin_android_lifecycle/example/pubspec.yaml b/packages/flutter_plugin_android_lifecycle/example/pubspec.yaml
index 4506437..e0d225c 100644
--- a/packages/flutter_plugin_android_lifecycle/example/pubspec.yaml
+++ b/packages/flutter_plugin_android_lifecycle/example/pubspec.yaml
@@ -1,5 +1,6 @@
 name: flutter_plugin_android_lifecycle_example
 description: Demonstrates how to use the flutter_plugin_android_lifecycle plugin.
+publish_to: none
 
 dependencies:
   flutter:
diff --git a/packages/google_maps_flutter/google_maps_flutter/example/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter/example/pubspec.yaml
index eb06e09..0035c96 100644
--- a/packages/google_maps_flutter/google_maps_flutter/example/pubspec.yaml
+++ b/packages/google_maps_flutter/google_maps_flutter/example/pubspec.yaml
@@ -1,5 +1,6 @@
 name: google_maps_flutter_example
 description: Demonstrates how to use the google_maps_flutter plugin.
+publish_to: none
 
 environment:
   sdk: '>=2.12.0-259.9.beta <3.0.0'
diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/circles.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/circles.dart
index 2a19d87..ae8faa0 100644
--- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/circles.dart
+++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/circles.dart
@@ -15,7 +15,7 @@
   /// Initialize the cache. The [StreamController] comes from the [GoogleMapController], and is shared with other controllers.
   CirclesController({
     required StreamController<MapEvent> stream,
-  })   : _streamController = stream,
+  })  : _streamController = stream,
         _circleIdToController = Map<CircleId, CircleController>();
 
   /// Returns the cache of [CircleController]s. Test only.
diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/markers.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/markers.dart
index 704577b..b650b9b 100644
--- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/markers.dart
+++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/markers.dart
@@ -15,7 +15,7 @@
   /// Initialize the cache. The [StreamController] comes from the [GoogleMapController], and is shared with other controllers.
   MarkersController({
     required StreamController<MapEvent> stream,
-  })   : _streamController = stream,
+  })  : _streamController = stream,
         _markerIdToController = Map<MarkerId, MarkerController>();
 
   /// Returns the cache of [MarkerController]s. Test only.
diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polygons.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polygons.dart
index ef51bd6..8a964315 100644
--- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polygons.dart
+++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polygons.dart
@@ -15,7 +15,7 @@
   /// Initializes the cache. The [StreamController] comes from the [GoogleMapController], and is shared with other controllers.
   PolygonsController({
     required StreamController<MapEvent> stream,
-  })   : _streamController = stream,
+  })  : _streamController = stream,
         _polygonIdToController = Map<PolygonId, PolygonController>();
 
   /// Returns the cache of [PolygonController]s. Test only.
diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polylines.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polylines.dart
index 184c0d9..695b295 100644
--- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polylines.dart
+++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polylines.dart
@@ -15,7 +15,7 @@
   /// Initializes the cache. The [StreamController] comes from the [GoogleMapController], and is shared with other controllers.
   PolylinesController({
     required StreamController<MapEvent> stream,
-  })   : _streamController = stream,
+  })  : _streamController = stream,
         _polylineIdToController = Map<PolylineId, PolylineController>();
 
   /// Returns the cache of [PolylineContrller]s. Test only.
diff --git a/packages/google_sign_in/google_sign_in/example/pubspec.yaml b/packages/google_sign_in/google_sign_in/example/pubspec.yaml
index f08a131..721e77e 100755
--- a/packages/google_sign_in/google_sign_in/example/pubspec.yaml
+++ b/packages/google_sign_in/google_sign_in/example/pubspec.yaml
@@ -1,5 +1,6 @@
 name: google_sign_in_example
 description: Example of Google Sign-In plugin.
+publish_to: none
 
 dependencies:
   flutter:
diff --git a/packages/image_picker/image_picker/example/pubspec.yaml b/packages/image_picker/image_picker/example/pubspec.yaml
index b2e5b63..a52b0f5 100755
--- a/packages/image_picker/image_picker/example/pubspec.yaml
+++ b/packages/image_picker/image_picker/example/pubspec.yaml
@@ -1,6 +1,6 @@
 name: image_picker_example
 description: Demonstrates how to use the image_picker plugin.
-author: Flutter Team <flutter-dev@googlegroups.com>
+publish_to: none
 
 dependencies:
   video_player: ^2.0.0-nullsafety.7
diff --git a/packages/in_app_purchase/in_app_purchase/example/pubspec.yaml b/packages/in_app_purchase/in_app_purchase/example/pubspec.yaml
index 8d193de..6625cb9 100644
--- a/packages/in_app_purchase/in_app_purchase/example/pubspec.yaml
+++ b/packages/in_app_purchase/in_app_purchase/example/pubspec.yaml
@@ -1,6 +1,6 @@
 name: in_app_purchase_example
 description: Demonstrates how to use the in_app_purchase plugin.
-author: Flutter Team <flutter-dev@googlegroups.com>
+publish_to: none
 
 dependencies:
   flutter:
diff --git a/packages/integration_test/example/pubspec.yaml b/packages/integration_test/example/pubspec.yaml
index 558d5f7..e39f115 100644
--- a/packages/integration_test/example/pubspec.yaml
+++ b/packages/integration_test/example/pubspec.yaml
@@ -1,6 +1,6 @@
 name: integration_test_example
 description: Demonstrates how to use the integration_test plugin.
-publish_to: 'none'
+publish_to: none
 
 environment:
   sdk: ">=2.1.0 <3.0.0"
diff --git a/packages/ios_platform_images/example/pubspec.yaml b/packages/ios_platform_images/example/pubspec.yaml
index 552790e..e87dd59 100644
--- a/packages/ios_platform_images/example/pubspec.yaml
+++ b/packages/ios_platform_images/example/pubspec.yaml
@@ -1,6 +1,6 @@
 name: ios_platform_images_example
 description: Demonstrates how to use the ios_platform_images plugin.
-publish_to: 'none'
+publish_to: none
 homepage: https://github.com/flutter/plugins/tree/master/packages/ios_platform_images/ios_platform_images
 
 environment:
diff --git a/packages/local_auth/example/pubspec.yaml b/packages/local_auth/example/pubspec.yaml
index aad68c5..7cc6b39 100644
--- a/packages/local_auth/example/pubspec.yaml
+++ b/packages/local_auth/example/pubspec.yaml
@@ -1,5 +1,6 @@
 name: local_auth_example
 description: Demonstrates how to use the local_auth plugin.
+publish_to: none
 
 dependencies:
   flutter:
diff --git a/packages/package_info/example/pubspec.yaml b/packages/package_info/example/pubspec.yaml
index 0e47ad2..139b9e2 100644
--- a/packages/package_info/example/pubspec.yaml
+++ b/packages/package_info/example/pubspec.yaml
@@ -1,5 +1,6 @@
 name: package_info_example
 description: Demonstrates how to use the package_info plugin.
+publish_to: none
 
 dependencies:
   flutter:
diff --git a/packages/path_provider/path_provider/example/pubspec.yaml b/packages/path_provider/path_provider/example/pubspec.yaml
index 326c68e..6ebde43 100644
--- a/packages/path_provider/path_provider/example/pubspec.yaml
+++ b/packages/path_provider/path_provider/example/pubspec.yaml
@@ -1,5 +1,6 @@
 name: path_provider_example
 description: Demonstrates how to use the path_provider plugin.
+publish_to: none
 
 dependencies:
   flutter:
diff --git a/packages/path_provider/path_provider_macos/example/pubspec.yaml b/packages/path_provider/path_provider_macos/example/pubspec.yaml
index 1451dea..f91678c 100644
--- a/packages/path_provider/path_provider_macos/example/pubspec.yaml
+++ b/packages/path_provider/path_provider_macos/example/pubspec.yaml
@@ -1,5 +1,6 @@
 name: path_provider_example
 description: Demonstrates how to use the path_provider plugin.
+publish_to: none
 
 dependencies:
   flutter:
diff --git a/packages/path_provider/path_provider_windows/example/pubspec.yaml b/packages/path_provider/path_provider_windows/example/pubspec.yaml
index 29e228d..8843e2d 100644
--- a/packages/path_provider/path_provider_windows/example/pubspec.yaml
+++ b/packages/path_provider/path_provider_windows/example/pubspec.yaml
@@ -1,5 +1,6 @@
 name: path_provider_example
 description: Demonstrates how to use the path_provider plugin.
+publish_to: none
 
 dependencies:
   flutter:
diff --git a/packages/quick_actions/quick_actions/example/pubspec.yaml b/packages/quick_actions/quick_actions/example/pubspec.yaml
index b26f843..391b4c3 100644
--- a/packages/quick_actions/quick_actions/example/pubspec.yaml
+++ b/packages/quick_actions/quick_actions/example/pubspec.yaml
@@ -1,5 +1,6 @@
 name: quick_actions_example
 description: Demonstrates how to use the quick_actions plugin.
+publish_to: none
 
 dependencies:
   flutter:
diff --git a/packages/sensors/example/lib/snake.dart b/packages/sensors/example/lib/snake.dart
index 4717768..afa12e3 100644
--- a/packages/sensors/example/lib/snake.dart
+++ b/packages/sensors/example/lib/snake.dart
@@ -99,10 +99,10 @@
     final math.Point<int>? newDirection = currentAcceleration == null
         ? null
         : currentAcceleration.x.abs() < 1.0 && currentAcceleration.y.abs() < 1.0
-            ? null
-            : (currentAcceleration.x.abs() < currentAcceleration.y.abs())
-                ? math.Point<int>(0, currentAcceleration.y.sign.toInt())
-                : math.Point<int>(-currentAcceleration.x.sign.toInt(), 0);
+        ? null
+        : (currentAcceleration.x.abs() < currentAcceleration.y.abs())
+        ? math.Point<int>(0, currentAcceleration.y.sign.toInt())
+        : math.Point<int>(-currentAcceleration.x.sign.toInt(), 0);
     state.step(newDirection);
   }
 }
diff --git a/packages/sensors/example/pubspec.yaml b/packages/sensors/example/pubspec.yaml
index 7d8effb..884ebbb 100644
--- a/packages/sensors/example/pubspec.yaml
+++ b/packages/sensors/example/pubspec.yaml
@@ -1,5 +1,6 @@
 name: sensors_example
 description: Demonstrates how to use the sensors plugin.
+publish_to: none
 
 dependencies:
   flutter:
diff --git a/packages/share/example/pubspec.yaml b/packages/share/example/pubspec.yaml
index 378e25b..31deb1f 100644
--- a/packages/share/example/pubspec.yaml
+++ b/packages/share/example/pubspec.yaml
@@ -1,5 +1,6 @@
 name: share_example
 description: Demonstrates how to use the share plugin.
+publish_to: none
 
 dependencies:
   flutter:
diff --git a/packages/shared_preferences/shared_preferences/example/pubspec.yaml b/packages/shared_preferences/shared_preferences/example/pubspec.yaml
index 7a7b1ac..c3454a5 100644
--- a/packages/shared_preferences/shared_preferences/example/pubspec.yaml
+++ b/packages/shared_preferences/shared_preferences/example/pubspec.yaml
@@ -1,5 +1,6 @@
 name: shared_preferences_example
 description: Demonstrates how to use the shared_preferences plugin.
+publish_to: none
 
 dependencies:
   flutter:
diff --git a/packages/shared_preferences/shared_preferences_linux/example/pubspec.yaml b/packages/shared_preferences/shared_preferences_linux/example/pubspec.yaml
index a3a3cbc..8b2c393 100644
--- a/packages/shared_preferences/shared_preferences_linux/example/pubspec.yaml
+++ b/packages/shared_preferences/shared_preferences_linux/example/pubspec.yaml
@@ -1,5 +1,6 @@
 name: shared_preferences_linux_example
 description: Demonstrates how to use the shared_preferences_linux plugin.
+publish_to: none
 
 dependencies:
   flutter:
diff --git a/packages/shared_preferences/shared_preferences_macos/example/pubspec.yaml b/packages/shared_preferences/shared_preferences_macos/example/pubspec.yaml
index 8ad710c..c64fda7 100644
--- a/packages/shared_preferences/shared_preferences_macos/example/pubspec.yaml
+++ b/packages/shared_preferences/shared_preferences_macos/example/pubspec.yaml
@@ -1,5 +1,6 @@
 name: shared_preferences_example
 description: Demonstrates how to use the shared_preferences plugin.
+publish_to: none
 
 dependencies:
   flutter:
diff --git a/packages/shared_preferences/shared_preferences_windows/example/pubspec.yaml b/packages/shared_preferences/shared_preferences_windows/example/pubspec.yaml
index e835987..db6401c 100644
--- a/packages/shared_preferences/shared_preferences_windows/example/pubspec.yaml
+++ b/packages/shared_preferences/shared_preferences_windows/example/pubspec.yaml
@@ -1,5 +1,6 @@
 name: shared_preferences_windows_example
 description: Demonstrates how to use the shared_preferences_windows plugin.
+publish_to: none
 
 environment:
   sdk: ">=2.12.0-259.9.beta <3.0.0"
diff --git a/packages/url_launcher/url_launcher/example/pubspec.yaml b/packages/url_launcher/url_launcher/example/pubspec.yaml
index 4ab2353..e56f76f 100644
--- a/packages/url_launcher/url_launcher/example/pubspec.yaml
+++ b/packages/url_launcher/url_launcher/example/pubspec.yaml
@@ -1,5 +1,6 @@
 name: url_launcher_example
 description: Demonstrates how to use the url_launcher plugin.
+publish_to: none
 
 dependencies:
   flutter:
diff --git a/packages/url_launcher/url_launcher_linux/example/pubspec.yaml b/packages/url_launcher/url_launcher_linux/example/pubspec.yaml
index e0c59fb..389f3fd 100644
--- a/packages/url_launcher/url_launcher_linux/example/pubspec.yaml
+++ b/packages/url_launcher/url_launcher_linux/example/pubspec.yaml
@@ -1,5 +1,6 @@
 name: url_launcher_example
 description: Demonstrates how to use the url_launcher plugin.
+publish_to: none
 
 dependencies:
   flutter:
diff --git a/packages/url_launcher/url_launcher_macos/example/pubspec.yaml b/packages/url_launcher/url_launcher_macos/example/pubspec.yaml
index 595d1bc..2e221dc 100644
--- a/packages/url_launcher/url_launcher_macos/example/pubspec.yaml
+++ b/packages/url_launcher/url_launcher_macos/example/pubspec.yaml
@@ -1,5 +1,6 @@
 name: url_launcher_example
 description: Demonstrates how to use the url_launcher plugin.
+publish_to: none
 
 dependencies:
   flutter:
diff --git a/packages/url_launcher/url_launcher_windows/example/pubspec.yaml b/packages/url_launcher/url_launcher_windows/example/pubspec.yaml
index 86ceeec..a0fecd5 100644
--- a/packages/url_launcher/url_launcher_windows/example/pubspec.yaml
+++ b/packages/url_launcher/url_launcher_windows/example/pubspec.yaml
@@ -1,5 +1,6 @@
 name: url_launcher_example
 description: Demonstrates the Windows implementation of the url_launcher plugin.
+publish_to: none
 
 dependencies:
   flutter:
diff --git a/packages/video_player/video_player/example/pubspec.yaml b/packages/video_player/video_player/example/pubspec.yaml
index 354f3d1..6386a2b 100644
--- a/packages/video_player/video_player/example/pubspec.yaml
+++ b/packages/video_player/video_player/example/pubspec.yaml
@@ -1,6 +1,5 @@
 name: video_player_example
 description: Demonstrates how to use the video_player plugin.
-version: 0.0.1
 publish_to: none
 
 dependencies:
diff --git a/packages/webview_flutter/example/pubspec.yaml b/packages/webview_flutter/example/pubspec.yaml
index b706741..84e9163 100644
--- a/packages/webview_flutter/example/pubspec.yaml
+++ b/packages/webview_flutter/example/pubspec.yaml
@@ -1,5 +1,6 @@
 name: webview_flutter_example
 description: Demonstrates how to use the webview_flutter plugin.
+publish_to: none
 
 environment:
   sdk: ">=2.12.0-259.9.beta <3.0.0"
diff --git a/packages/webview_flutter/lib/webview_flutter.dart b/packages/webview_flutter/lib/webview_flutter.dart
index 92ff8e0..74d8af8 100644
--- a/packages/webview_flutter/lib/webview_flutter.dart
+++ b/packages/webview_flutter/lib/webview_flutter.dart
@@ -180,7 +180,7 @@
   JavascriptChannel({
     required this.name,
     required this.onMessageReceived,
-  })   : assert(name != null),
+  })  : assert(name != null),
         assert(onMessageReceived != null),
         assert(_validChannelNames.hasMatch(name));
 
diff --git a/packages/wifi_info_flutter/wifi_info_flutter/example/pubspec.yaml b/packages/wifi_info_flutter/wifi_info_flutter/example/pubspec.yaml
index 57edbe8..108bd39 100644
--- a/packages/wifi_info_flutter/wifi_info_flutter/example/pubspec.yaml
+++ b/packages/wifi_info_flutter/wifi_info_flutter/example/pubspec.yaml
@@ -1,6 +1,6 @@
 name: wifi_info_flutter_example
 description: Demonstrates how to use the wifi_info_flutter plugin.
-publish_to: 'none'
+publish_to: none
 
 environment:
   sdk: ">=2.12.0-259.9.beta <3.0.0"
diff --git a/script/tool/lib/src/common.dart b/script/tool/lib/src/common.dart
index fc1fdca..d8826c7 100644
--- a/script/tool/lib/src/common.dart
+++ b/script/tool/lib/src/common.dart
@@ -596,10 +596,18 @@
     return changedFiles.toList();
   }
 
-  /// Get the package version specified in the pubspec file in `pubspecPath` and at the revision of `gitRef`.
-  Future<Version> getPackageVersion(String pubspecPath, String gitRef) async {
-    final io.ProcessResult gitShow =
-        await baseGitDir.runCommand(<String>['show', '$gitRef:$pubspecPath']);
+  /// Get the package version specified in the pubspec file in `pubspecPath` and
+  /// at the revision of `gitRef` (defaulting to the base if not provided).
+  Future<Version> getPackageVersion(String pubspecPath, {String gitRef}) async {
+    final String ref = gitRef ?? (await _getBaseSha());
+
+    io.ProcessResult gitShow;
+    try {
+      gitShow =
+          await baseGitDir.runCommand(<String>['show', '$ref:$pubspecPath']);
+    } on io.ProcessException {
+      return null;
+    }
     final String fileContent = gitShow.stdout as String;
     final String versionString = loadYaml(fileContent)['version'] as String;
     return versionString == null ? null : Version.parse(versionString);
diff --git a/script/tool/lib/src/version_check_command.dart b/script/tool/lib/src/version_check_command.dart
index 086fa54..14dc8fb 100644
--- a/script/tool/lib/src/version_check_command.dart
+++ b/script/tool/lib/src/version_check_command.dart
@@ -13,8 +13,6 @@
 
 import 'common.dart';
 
-const String _kBaseSha = 'base-sha';
-
 /// Categories of version change types.
 enum NextVersionType {
   /// A breaking change.
@@ -96,48 +94,59 @@
     final List<String> changedPubspecs =
         await gitVersionFinder.getChangedPubSpecs();
 
-    final String baseSha = argResults[_kBaseSha] as String;
+    const String indentation = '  ';
     for (final String pubspecPath in changedPubspecs) {
-      try {
-        final File pubspecFile = fileSystem.file(pubspecPath);
-        if (!pubspecFile.existsSync()) {
-          continue;
-        }
-        final Pubspec pubspec = Pubspec.parse(pubspecFile.readAsStringSync());
-        if (pubspec.publishTo == 'none') {
-          continue;
-        }
+      print('Checking versions for $pubspecPath...');
+      final File pubspecFile = fileSystem.file(pubspecPath);
+      if (!pubspecFile.existsSync()) {
+        print('${indentation}Deleted; skipping.');
+        continue;
+      }
+      final Pubspec pubspec = Pubspec.parse(pubspecFile.readAsStringSync());
+      if (pubspec.publishTo == 'none') {
+        print('${indentation}Found "publish_to: none"; skipping.');
+        continue;
+      }
 
-        final Version masterVersion =
-            await gitVersionFinder.getPackageVersion(pubspecPath, baseSha);
-        final Version headVersion =
-            await gitVersionFinder.getPackageVersion(pubspecPath, 'HEAD');
-        if (headVersion == null) {
-          continue; // Example apps don't have versions
-        }
+      final Version headVersion =
+          await gitVersionFinder.getPackageVersion(pubspecPath, gitRef: 'HEAD');
+      if (headVersion == null) {
+        printErrorAndExit(
+            errorMessage: '${indentation}No version found. A package that '
+                'intentionally has no version should be marked '
+                '"publish_to: none".');
+      }
+      final Version masterVersion =
+          await gitVersionFinder.getPackageVersion(pubspecPath);
+      if (masterVersion == null) {
+        print('${indentation}Unable to find pubspec in master. '
+            'Safe to ignore if the project is new.');
+      }
 
-        final Map<Version, NextVersionType> allowedNextVersions =
-            getAllowedNextVersions(masterVersion, headVersion);
+      if (masterVersion == headVersion) {
+        print('${indentation}No version change.');
+        continue;
+      }
 
-        if (!allowedNextVersions.containsKey(headVersion)) {
-          final String error = '$pubspecPath incorrectly updated version.\n'
-              'HEAD: $headVersion, master: $masterVersion.\n'
-              'Allowed versions: $allowedNextVersions';
-          printErrorAndExit(errorMessage: error);
-        }
+      final Map<Version, NextVersionType> allowedNextVersions =
+          getAllowedNextVersions(masterVersion, headVersion);
 
-        final bool isPlatformInterface =
-            pubspec.name.endsWith('_platform_interface');
-        if (isPlatformInterface &&
-            allowedNextVersions[headVersion] ==
-                NextVersionType.BREAKING_MAJOR) {
-          final String error = '$pubspecPath breaking change detected.\n'
-              'Breaking changes to platform interfaces are strongly discouraged.\n';
-          printErrorAndExit(errorMessage: error);
-        }
-      } on io.ProcessException {
-        print('Unable to find pubspec in master for $pubspecPath.'
-            ' Safe to ignore if the project is new.');
+      if (!allowedNextVersions.containsKey(headVersion)) {
+        final String error = '${indentation}Incorrectly updated version.\n'
+            '${indentation}HEAD: $headVersion, master: $masterVersion.\n'
+            '${indentation}Allowed versions: $allowedNextVersions';
+        printErrorAndExit(errorMessage: error);
+      } else {
+        print('$indentation$headVersion -> $masterVersion');
+      }
+
+      final bool isPlatformInterface =
+          pubspec.name.endsWith('_platform_interface');
+      if (isPlatformInterface &&
+          allowedNextVersions[headVersion] == NextVersionType.BREAKING_MAJOR) {
+        final String error = '$pubspecPath breaking change detected.\n'
+            'Breaking changes to platform interfaces are strongly discouraged.\n';
+        printErrorAndExit(errorMessage: error);
       }
     }
 
@@ -153,7 +162,7 @@
     final String packageName = plugin.basename;
     print('-----------------------------------------');
     print(
-        'Checking the first version listed in CHANGELOG.MD matches the version in pubspec.yaml for $packageName.');
+        'Checking the first version listed in CHANGELOG.md matches the version in pubspec.yaml for $packageName.');
 
     final Pubspec pubspec = _tryParsePubspec(plugin);
     if (pubspec == null) {
@@ -169,12 +178,29 @@
     final Iterator<String> iterator = lines.iterator;
     while (iterator.moveNext()) {
       if (iterator.current.trim().isNotEmpty) {
-        firstLineWithText = iterator.current;
+        firstLineWithText = iterator.current.trim();
         break;
       }
     }
     // Remove all leading mark down syntax from the version line.
-    final String versionString = firstLineWithText.split(' ').last;
+    String versionString = firstLineWithText.split(' ').last;
+
+    // Skip validation for the special NEXT version that's used to accumulate
+    // changes that don't warrant publishing on their own.
+    bool hasNextSection = versionString == 'NEXT';
+    if (hasNextSection) {
+      print('Found NEXT; validating next version in the CHANGELOG.');
+      // Ensure that the version in pubspec hasn't changed without updating
+      // CHANGELOG. That means the next version entry in the CHANGELOG pass the
+      // normal validation.
+      while (iterator.moveNext()) {
+        if (iterator.current.trim().startsWith('## ')) {
+          versionString = iterator.current.trim().split(' ').last;
+          break;
+        }
+      }
+    }
+
     final Version fromChangeLog = Version.parse(versionString);
     if (fromChangeLog == null) {
       final String error =
@@ -190,6 +216,18 @@
 ''';
       printErrorAndExit(errorMessage: error);
     }
+
+    // If NEXT wasn't the first section, it should not exist at all.
+    if (!hasNextSection) {
+      final RegExp nextRegex = RegExp(r'^#+\s*NEXT\s*$');
+      if (lines.any((String line) => nextRegex.hasMatch(line))) {
+        printErrorAndExit(errorMessage: '''
+When bumping the version for release, the NEXT section should be incorporated
+into the new version's release notes.
+      ''');
+      }
+    }
+
     print('$packageName passed version check');
   }
 
diff --git a/script/tool/pubspec.yaml b/script/tool/pubspec.yaml
index e471239..9260e8f 100644
--- a/script/tool/pubspec.yaml
+++ b/script/tool/pubspec.yaml
@@ -1,6 +1,6 @@
 name: flutter_plugin_tools
 description: Productivity utils for hosting multiple plugins within one repository.
-publish_to: 'none'
+publish_to: none
 
 dependencies:
   args: "^1.4.3"
diff --git a/script/tool/test/version_check_test.dart b/script/tool/test/version_check_test.dart
index 96a460d..ed953e0 100644
--- a/script/tool/test/version_check_test.dart
+++ b/script/tool/test/version_check_test.dart
@@ -62,6 +62,8 @@
           final String response =
               gitShowResponses[invocation.positionalArguments[0][1]];
           when<String>(mockProcessResult.stdout as String).thenReturn(response);
+        } else if (invocation.positionalArguments[0][0] == 'merge-base') {
+          when<String>(mockProcessResult.stdout as String).thenReturn('abc123');
         }
         return Future<io.ProcessResult>.value(mockProcessResult);
       });
@@ -98,11 +100,12 @@
       );
       expect(gitDirCommands.length, equals(3));
       expect(
-          gitDirCommands[0].join(' '), equals('diff --name-only master HEAD'));
-      expect(gitDirCommands[1].join(' '),
-          equals('show master:packages/plugin/pubspec.yaml'));
-      expect(gitDirCommands[2].join(' '),
-          equals('show HEAD:packages/plugin/pubspec.yaml'));
+          gitDirCommands,
+          containsAll(<Matcher>[
+            equals(<String>['diff', '--name-only', 'master', 'HEAD']),
+            equals(<String>['show', 'master:packages/plugin/pubspec.yaml']),
+            equals(<String>['show', 'HEAD:packages/plugin/pubspec.yaml']),
+          ]));
     });
 
     test('denies invalid version', () async {
@@ -117,15 +120,50 @@
 
       await expectLater(
         result,
-        throwsA(const TypeMatcher<Error>()),
+        throwsA(const TypeMatcher<ToolExit>()),
       );
       expect(gitDirCommands.length, equals(3));
       expect(
-          gitDirCommands[0].join(' '), equals('diff --name-only master HEAD'));
-      expect(gitDirCommands[1].join(' '),
-          equals('show master:packages/plugin/pubspec.yaml'));
-      expect(gitDirCommands[2].join(' '),
-          equals('show HEAD:packages/plugin/pubspec.yaml'));
+          gitDirCommands,
+          containsAll(<Matcher>[
+            equals(<String>['diff', '--name-only', 'master', 'HEAD']),
+            equals(<String>['show', 'master:packages/plugin/pubspec.yaml']),
+            equals(<String>['show', 'HEAD:packages/plugin/pubspec.yaml']),
+          ]));
+    });
+
+    test('allows valid version without explicit base-sha', () async {
+      createFakePlugin('plugin', includeChangeLog: true, includeVersion: true);
+      gitDiffResponse = 'packages/plugin/pubspec.yaml';
+      gitShowResponses = <String, String>{
+        'abc123:packages/plugin/pubspec.yaml': 'version: 1.0.0',
+        'HEAD:packages/plugin/pubspec.yaml': 'version: 2.0.0',
+      };
+      final List<String> output =
+          await runCapturingPrint(runner, <String>['version-check']);
+
+      expect(
+        output,
+        containsAllInOrder(<String>[
+          'No version check errors found!',
+        ]),
+      );
+    });
+
+    test('denies invalid version without explicit base-sha', () async {
+      createFakePlugin('plugin', includeChangeLog: true, includeVersion: true);
+      gitDiffResponse = 'packages/plugin/pubspec.yaml';
+      gitShowResponses = <String, String>{
+        'abc123:packages/plugin/pubspec.yaml': 'version: 0.0.1',
+        'HEAD:packages/plugin/pubspec.yaml': 'version: 0.2.0',
+      };
+      final Future<List<String>> result =
+          runCapturingPrint(runner, <String>['version-check']);
+
+      await expectLater(
+        result,
+        throwsA(const TypeMatcher<ToolExit>()),
+      );
     });
 
     test('gracefully handles missing pubspec.yaml', () async {
@@ -143,6 +181,8 @@
         output,
         orderedEquals(<String>[
           'Determine diff with base sha: master',
+          'Checking versions for packages/plugin/pubspec.yaml...',
+          '  Deleted; skipping.',
           'No version check errors found!',
         ]),
       );
@@ -171,13 +211,18 @@
       );
       expect(gitDirCommands.length, equals(3));
       expect(
-          gitDirCommands[0].join(' '), equals('diff --name-only master HEAD'));
-      expect(
-          gitDirCommands[1].join(' '),
-          equals(
-              'show master:packages/plugin_platform_interface/pubspec.yaml'));
-      expect(gitDirCommands[2].join(' '),
-          equals('show HEAD:packages/plugin_platform_interface/pubspec.yaml'));
+          gitDirCommands,
+          containsAll(<Matcher>[
+            equals(<String>['diff', '--name-only', 'master', 'HEAD']),
+            equals(<String>[
+              'show',
+              'master:packages/plugin_platform_interface/pubspec.yaml'
+            ]),
+            equals(<String>[
+              'show',
+              'HEAD:packages/plugin_platform_interface/pubspec.yaml'
+            ]),
+          ]));
     });
 
     test('disallows breaking changes to platform interfaces', () async {
@@ -194,17 +239,22 @@
           runner, <String>['version-check', '--base-sha=master']);
       await expectLater(
         output,
-        throwsA(const TypeMatcher<Error>()),
+        throwsA(const TypeMatcher<ToolExit>()),
       );
       expect(gitDirCommands.length, equals(3));
       expect(
-          gitDirCommands[0].join(' '), equals('diff --name-only master HEAD'));
-      expect(
-          gitDirCommands[1].join(' '),
-          equals(
-              'show master:packages/plugin_platform_interface/pubspec.yaml'));
-      expect(gitDirCommands[2].join(' '),
-          equals('show HEAD:packages/plugin_platform_interface/pubspec.yaml'));
+          gitDirCommands,
+          containsAll(<Matcher>[
+            equals(<String>['diff', '--name-only', 'master', 'HEAD']),
+            equals(<String>[
+              'show',
+              'master:packages/plugin_platform_interface/pubspec.yaml'
+            ]),
+            equals(<String>[
+              'show',
+              'HEAD:packages/plugin_platform_interface/pubspec.yaml'
+            ]),
+          ]));
     });
 
     test('Allow empty lines in front of the first version in CHANGELOG',
@@ -230,7 +280,7 @@
       expect(
         output,
         containsAllInOrder(<String>[
-          'Checking the first version listed in CHANGELOG.MD matches the version in pubspec.yaml for plugin.',
+          'Checking the first version listed in CHANGELOG.md matches the version in pubspec.yaml for plugin.',
           'plugin passed version check',
           'No version check errors found!'
         ]),
@@ -255,7 +305,7 @@
           runner, <String>['version-check', '--base-sha=master']);
       await expectLater(
         output,
-        throwsA(const TypeMatcher<Error>()),
+        throwsA(const TypeMatcher<ToolExit>()),
       );
       try {
         final List<String> outputValue = await output;
@@ -291,7 +341,7 @@
       expect(
         output,
         containsAllInOrder(<String>[
-          'Checking the first version listed in CHANGELOG.MD matches the version in pubspec.yaml for plugin.',
+          'Checking the first version listed in CHANGELOG.md matches the version in pubspec.yaml for plugin.',
           'plugin passed version check',
           'No version check errors found!'
         ]),
@@ -322,7 +372,121 @@
           runner, <String>['version-check', '--base-sha=master']);
       await expectLater(
         output,
-        throwsA(const TypeMatcher<Error>()),
+        throwsA(const TypeMatcher<ToolExit>()),
+      );
+      try {
+        final List<String> outputValue = await output;
+        await expectLater(
+          outputValue,
+          containsAllInOrder(<String>[
+            '''
+  versions for plugin in CHANGELOG.md and pubspec.yaml do not match.
+  The version in pubspec.yaml is 1.0.0.
+  The first version listed in CHANGELOG.md is 1.0.1.
+  ''',
+          ]),
+        );
+      } on ToolExit catch (_) {}
+    });
+
+    test('Allow NEXT as a placeholder for gathering CHANGELOG entries',
+        () async {
+      createFakePlugin('plugin', includeChangeLog: true, includeVersion: true);
+
+      final Directory pluginDirectory =
+          mockPackagesDir.childDirectory('plugin');
+
+      createFakePubspec(pluginDirectory,
+          isFlutter: true, includeVersion: true, version: '1.0.0');
+      const String changelog = '''
+## NEXT
+
+* Some changes that won't be published until the next time there's a release.
+
+## 1.0.0
+
+* Some other changes.
+''';
+      createFakeCHANGELOG(pluginDirectory, changelog);
+      final List<String> output = await runCapturingPrint(
+          runner, <String>['version-check', '--base-sha=master']);
+      await expectLater(
+        output,
+        containsAllInOrder(<String>[
+          'Found NEXT; validating next version in the CHANGELOG.',
+          'plugin passed version check',
+          'No version check errors found!',
+        ]),
+      );
+    });
+
+    test('Fail if NEXT is left in the CHANGELOG when adding a version bump',
+        () async {
+      createFakePlugin('plugin', includeChangeLog: true, includeVersion: true);
+
+      final Directory pluginDirectory =
+          mockPackagesDir.childDirectory('plugin');
+
+      createFakePubspec(pluginDirectory,
+          isFlutter: true, includeVersion: true, version: '1.0.1');
+      const String changelog = '''
+## 1.0.1
+
+* Some changes.
+
+## NEXT
+
+* Some changes that should have been folded in 1.0.1.
+
+## 1.0.0
+
+* Some other changes.
+''';
+      createFakeCHANGELOG(pluginDirectory, changelog);
+      final Future<List<String>> output = runCapturingPrint(
+          runner, <String>['version-check', '--base-sha=master']);
+      await expectLater(
+        output,
+        throwsA(const TypeMatcher<ToolExit>()),
+      );
+      try {
+        final List<String> outputValue = await output;
+        await expectLater(
+          outputValue,
+          containsAllInOrder(<String>[
+            '''
+  versions for plugin in CHANGELOG.md and pubspec.yaml do not match.
+  The version in pubspec.yaml is 1.0.0.
+  The first version listed in CHANGELOG.md is 1.0.1.
+  ''',
+          ]),
+        );
+      } on ToolExit catch (_) {}
+    });
+
+    test('Fail if the version changes without replacing NEXT', () async {
+      createFakePlugin('plugin', includeChangeLog: true, includeVersion: true);
+
+      final Directory pluginDirectory =
+          mockPackagesDir.childDirectory('plugin');
+
+      createFakePubspec(pluginDirectory,
+          isFlutter: true, includeVersion: true, version: '1.0.1');
+      const String changelog = '''
+## NEXT
+
+* Some changes that should be listed as part of 1.0.1.
+
+## 1.0.0
+
+* Some other changes.
+''';
+      createFakeCHANGELOG(pluginDirectory, changelog);
+      final Future<List<String>> output = runCapturingPrint(
+          runner, <String>['version-check', '--base-sha=master']);
+      await expectLater(
+        output,
+        throwsA(const TypeMatcher<ToolExit>()),
       );
       try {
         final List<String> outputValue = await output;