fix(repo_manage): use pub_semver for changelog -wip transitions (#475)
diff --git a/pkgs/repo_manage/lib/changelog_updater.dart b/pkgs/repo_manage/lib/changelog_updater.dart
index 8756389..18332e5 100644
--- a/pkgs/repo_manage/lib/changelog_updater.dart
+++ b/pkgs/repo_manage/lib/changelog_updater.dart
@@ -6,20 +6,31 @@
 import 'dart:io';
 
 import 'package:path/path.dart' as path;
+import 'package:pub_semver/pub_semver.dart';
 
 import 'src/common.dart';
 
-String _incrementPatch(String version) {
-  final parts = version.split('.');
-  if (parts.length >= 3) {
-    final patchStr = parts[2].split('-').first.split('+').first;
-    if (int.tryParse(patchStr) case final patchNum?) {
-      final newPatchStr = (patchNum + 1).toString();
-      parts[2] = parts[2].replaceFirst(patchStr, newPatchStr);
-      return parts.join('.');
-    }
+extension on Version {
+  bool get isWip =>
+      isPreRelease &&
+      switch (preRelease.last) {
+        final String s => s == 'wip' || s.endsWith('-wip'),
+        _ => false,
+      };
+}
+
+Version _nextWipVersion(Version? current) {
+  if (current == null) return Version(0, 0, 1, pre: 'wip');
+  if (current.isPreRelease) {
+    return Version(
+      current.major,
+      current.minor,
+      current.patch,
+      pre: '${current.preRelease.join('.')}-wip',
+    );
   }
-  return version;
+  final next = current.nextPatch;
+  return Version(next.major, next.minor, next.patch, pre: 'wip');
 }
 
 void updatePubspecVersion(File pubspecFile, String newVersion) {
@@ -45,26 +56,25 @@
 }) {
   final changelogContent = changelogFile.readAsStringSync();
   final lines = LineSplitter.split(changelogContent).toList();
-  var currentVersion = '0.0.1';
+  Version? currentVersion;
   var currentVersionLine = 0;
 
   for (var i = 0; i < lines.length; i++) {
     final line = lines[i].trim();
     if (line.startsWith('## ')) {
-      final content = line.substring(3).trim();
-      final version = content.split(' ').first;
-      if (version.startsWith(RegExp(r'\d'))) {
-        currentVersion = version;
+      final token = line.substring(3).trim().split(' ').first;
+      try {
+        currentVersion = Version.parse(token);
         currentVersionLine = i;
         break;
+      } on FormatException {
+        // Ignore non-SemVer level-2 headings.
       }
     }
   }
 
-  final isWip = currentVersion.endsWith('-wip');
-
   final output = <String>[];
-  if (isWip) {
+  if (currentVersion != null && currentVersion.isWip) {
     var sectionEnd = lines.indexWhere(
             (line) => line.startsWith('## '), currentVersionLine + 1) -
         1;
@@ -76,25 +86,23 @@
     output.add('- $message');
     output.addAll(lines.skip(sectionEnd));
   } else {
+    final newVersion = _nextWipVersion(currentVersion).toString();
     output.addAll([
-      '## ${_incrementPatch(currentVersion)}-wip',
+      '## $newVersion',
       '',
       '- $message',
       '',
       ...lines,
     ]);
-  }
 
-  changelogFile.writeAsStringSync('${output.join('\n')}\n');
-
-  if (!isWip) {
-    final newVersion = '${_incrementPatch(currentVersion)}-wip';
     final changelogDir = path.dirname(changelogFile.path);
     final pubspecFile = File(path.join(changelogDir, 'pubspec.yaml'));
     if (pubspecFile.existsSync()) {
       updatePubspecVersion(pubspecFile, newVersion);
     }
   }
+
+  changelogFile.writeAsStringSync('${output.join('\n')}\n');
 }
 
 class ChangelogUpdaterCommand extends ReportCommand {
diff --git a/pkgs/repo_manage/pubspec.yaml b/pkgs/repo_manage/pubspec.yaml
index 9129dd1..3ba82f3 100644
--- a/pkgs/repo_manage/pubspec.yaml
+++ b/pkgs/repo_manage/pubspec.yaml
@@ -16,6 +16,7 @@
   github: ^9.11.0
   graphql: ^5.0.0
   path: ^1.8.0
+  pub_semver: ^2.1.0
 
 dev_dependencies:
   dart_flutter_team_lints: ^3.0.0
diff --git a/pkgs/repo_manage/test/changelog_updater_test.dart b/pkgs/repo_manage/test/changelog_updater_test.dart
index 3091570..3a90e1f 100644
--- a/pkgs/repo_manage/test/changelog_updater_test.dart
+++ b/pkgs/repo_manage/test/changelog_updater_test.dart
@@ -151,6 +151,77 @@
 version: 1.0.1-wip
 ''');
     });
+
+    test('strips build metadata when incrementing patch to WIP', () {
+      final tempDir = Directory.systemTemp.createTempSync();
+      addTearDown(() => tempDir.deleteSync(recursive: true));
+      final changelogFile = File('${tempDir.path}/CHANGELOG.md');
+      final pubspecFile = File('${tempDir.path}/pubspec.yaml');
+
+      changelogFile.writeAsStringSync('''
+## 1.2.3+build.4
+
+- Released version with build metadata
+''');
+      pubspecFile.writeAsStringSync('''
+name: my_package
+version: 1.2.3+build.4
+''');
+
+      updateChangelog(changelogFile: changelogFile, message: 'Next change');
+
+      expect(changelogFile.readAsStringSync(), '''
+## 1.2.4-wip
+
+- Next change
+
+## 1.2.3+build.4
+
+- Released version with build metadata
+''');
+
+      expect(pubspecFile.readAsStringSync(), '''
+name: my_package
+version: 1.2.4-wip
+''');
+    });
+
+    test('appends -wip to a published pre-release and coalesces follow-ups',
+        () {
+      final tempDir = Directory.systemTemp.createTempSync();
+      addTearDown(() => tempDir.deleteSync(recursive: true));
+      final changelogFile = File('${tempDir.path}/CHANGELOG.md');
+      final pubspecFile = File('${tempDir.path}/pubspec.yaml');
+
+      changelogFile.writeAsStringSync('''
+## 1.2.3-alpha.3
+
+- Published alpha
+''');
+      pubspecFile.writeAsStringSync('''
+name: my_package
+version: 1.2.3-alpha.3
+''');
+
+      updateChangelog(changelogFile: changelogFile, message: 'First fix');
+      updateChangelog(changelogFile: changelogFile, message: 'Second fix');
+
+      expect(changelogFile.readAsStringSync(), '''
+## 1.2.3-alpha.3-wip
+
+- First fix
+- Second fix
+
+## 1.2.3-alpha.3
+
+- Published alpha
+''');
+
+      expect(pubspecFile.readAsStringSync(), '''
+name: my_package
+version: 1.2.3-alpha.3-wip
+''');
+    });
   });
 }
 
diff --git a/pkgs/repo_manage/test/data/golden/changelog_empty.golden b/pkgs/repo_manage/test/data/golden/changelog_empty.golden
index 395e1bf..a515a4f 100644
--- a/pkgs/repo_manage/test/data/golden/changelog_empty.golden
+++ b/pkgs/repo_manage/test/data/golden/changelog_empty.golden
@@ -1,4 +1,4 @@
-## 0.0.2-wip
+## 0.0.1-wip
 
 - Initial entry