| // Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| import 'dart:io'; |
| |
| import 'package:args/args.dart'; |
| |
| const _packageOption = 'package'; |
| const _versionOption = 'version'; |
| const _resetFlag = 'reset'; |
| const _skipStableCheckFlag = 'skipStableCheck'; |
| |
| /// Note: Must be run from the /tool directory. |
| /// |
| /// To prepare DWDS for release: |
| /// `dart run release.dart -p dwds` |
| /// |
| /// To prepare WebDev for release: |
| /// `dart run release.dart -p webdev` |
| /// |
| /// To reset DWDS after a release: |
| /// `dart run release.dart --reset -p dwds -v [[wip version]]` |
| /// |
| /// To reset WebDev after a release: |
| /// `dart run release.dart --reset -p webdev -v [[wip version]]` |
| |
| void main(List<String> arguments) async { |
| final parser = ArgParser() |
| ..addOption( |
| _packageOption, |
| abbr: 'p', |
| allowed: [ |
| 'webdev', |
| 'dwds', |
| ], |
| ) |
| ..addOption(_versionOption, abbr: 'v') |
| ..addFlag(_resetFlag, abbr: 'r') |
| ..addFlag(_skipStableCheckFlag, abbr: 's'); |
| |
| final argResults = parser.parse(arguments); |
| final package = argResults[_packageOption] as String?; |
| if (package == null) { |
| _logWarning('Please specify package with either --p=dwds or --p=webdev'); |
| return; |
| } |
| |
| final isReset = argResults[_resetFlag] as bool?; |
| final newVersion = argResults[_versionOption] as String?; |
| final skipStableCheck = argResults[_skipStableCheckFlag] as bool?; |
| |
| int exitCode; |
| if (isReset == true) { |
| exitCode = await runReset( |
| package: package, |
| newVersion: newVersion, |
| ); |
| } else { |
| exitCode = await runRelease( |
| package: package, |
| newVersion: newVersion, |
| skipStableCheck: skipStableCheck, |
| ); |
| } |
| if (exitCode != 0) { |
| _logWarning('Run terminated unexpectedly with exit code: $exitCode'); |
| } |
| } |
| |
| Future<int> runReset({ |
| required String package, |
| String? newVersion, |
| }) { |
| // Check that a new wip version has been provided. |
| final currentVersion = _readVersionFile(package); |
| if (newVersion == null || !newVersion.contains('wip')) { |
| _logInfo( |
| ''' |
| Please provide the next wip version for $package, e.g. -v 3.0.1-wip |
| Current version is $currentVersion. |
| ''', |
| ); |
| return Future.value(1); |
| } |
| |
| // Reset the dependency overrides for the package: |
| _updateOverrides(package, includeOverrides: true); |
| |
| // If updating webdev, also reset the dwds override for test_common to prevent |
| // conflicts: |
| if (package == 'webdev') { |
| _updateOverrides('test_common', includeOverrides: true); |
| } |
| |
| // Update the version strings in CHANGELOG and pubspec.yaml. |
| _updateVersionStrings( |
| package, |
| currentVersion: currentVersion, |
| nextVersion: newVersion, |
| isReset: true, |
| ); |
| |
| // Build the package. |
| final exitCode = _buildPackage(package); |
| return exitCode; |
| } |
| |
| Future<int> runRelease({ |
| required String package, |
| String? newVersion, |
| bool? skipStableCheck, |
| }) async { |
| // Check that we are on a stable version of Dart. |
| if (skipStableCheck != true) { |
| final checkVersionProcess = await Process.run('dart', ['--version']); |
| final versionInfo = checkVersionProcess.stdout as String; |
| if (!versionInfo.contains('stable')) { |
| _logWarning( |
| ''' |
| Expected to be on stable version of Dart, instead on: |
| $versionInfo |
| To skip this check, re-run with --skipStableCheck |
| ''', |
| ); |
| return checkVersionProcess.exitCode; |
| } |
| } |
| |
| // Update the pinned version of DWDS for webdev releases. |
| if (package == 'webdev') { |
| _logInfo('Updating pinned version of DWDS.'); |
| await _updateDwdsPin('test_common'); |
| final newVersion = await _updateDwdsPin('webdev'); |
| _logInfo('Add pinned DWDS info to CHANGELOG.'); |
| final changelog = File('../webdev/CHANGELOG.md'); |
| _addNewLine(changelog, |
| newLine: '- Update `dwds` constraint to `${newVersion ?? 'TODO'}`.'); |
| } |
| |
| // Remove any dependency overrides for the package: |
| _logInfo('Removing dependency overrides for $package.'); |
| _updateOverrides(package, includeOverrides: false); |
| |
| // If updating webdev, also remove the dwds override for test_common to |
| // prevent conflicts: |
| if (package == 'webdev') { |
| _updateOverrides('test_common', includeOverrides: false); |
| } |
| |
| // Run dart pub upgrade. |
| for (final packagePath in [ |
| '../dwds', |
| '../webdev', |
| '../frontend_server_common', |
| '../frontend_server_client', |
| '../test_common', |
| ]) { |
| _logInfo('Upgrading pub packages for $packagePath'); |
| final pubUpgradeProcess = await Process.run( |
| 'dart', |
| [ |
| 'pub', |
| 'upgrade', |
| ], |
| workingDirectory: packagePath, |
| ); |
| final upgradeErrors = pubUpgradeProcess.stderr as String; |
| if (upgradeErrors.isNotEmpty) { |
| _logWarning(upgradeErrors); |
| return pubUpgradeProcess.exitCode; |
| } |
| } |
| |
| // Update the version strings in CHANGELOG and pubspec.yaml. |
| final currentVersion = _readVersionFile(package); |
| final nextVersion = newVersion ?? _removeWip(currentVersion); |
| _updateVersionStrings( |
| package, |
| currentVersion: currentVersion, |
| nextVersion: nextVersion, |
| ); |
| |
| // Build the package. |
| final exitCode = _buildPackage(package); |
| return exitCode; |
| } |
| |
| Future<int> _buildPackage(String package) async { |
| _logInfo('Building $package'); |
| final buildProcess = await Process.run( |
| 'dart', |
| ['run', 'build_runner', 'build'], |
| workingDirectory: '../$package', |
| ); |
| |
| final buildErrors = buildProcess.stderr as String; |
| if (buildErrors.isNotEmpty) { |
| _logWarning(buildErrors); |
| } |
| return buildProcess.exitCode; |
| } |
| |
| void _updateOverrides( |
| String package, { |
| required bool includeOverrides, |
| }) { |
| final overridesFilePath = '../$package/pubspec_overrides.yaml'; |
| final noOverridesFilePath = '../$package/ignore_pubspec_overrides.yaml'; |
| if (includeOverrides) { |
| _renameFile(currentName: noOverridesFilePath, newName: overridesFilePath); |
| } else { |
| _renameFile(currentName: overridesFilePath, newName: noOverridesFilePath); |
| } |
| } |
| |
| void _renameFile({required String currentName, required String newName}) { |
| final currentFile = File(currentName); |
| if (!currentFile.existsSync()) { |
| _logInfo('Skip renaming $currentName to $newName, file does not exist.'); |
| return; |
| } |
| currentFile.rename(newName); |
| } |
| |
| void _updateVersionStrings( |
| String package, { |
| required String nextVersion, |
| required String currentVersion, |
| bool isReset = false, |
| }) { |
| _logInfo('Updating $package from $currentVersion to $nextVersion'); |
| final pubspec = File('../$package/pubspec.yaml'); |
| final changelog = File('../$package/CHANGELOG.md'); |
| if (isReset) { |
| _addNewLine(changelog, newLine: '## $nextVersion'); |
| _replaceInFile(pubspec, query: currentVersion, replaceWith: nextVersion); |
| } else { |
| for (final file in [pubspec, changelog]) { |
| _replaceInFile(file, query: currentVersion, replaceWith: nextVersion); |
| } |
| } |
| } |
| |
| void _addNewLine( |
| File file, { |
| required String newLine, |
| }) { |
| final newLines = [newLine, '', ...file.readAsLinesSync()]; |
| final content = newLines.joinWithNewLine(); |
| return file.writeAsStringSync(content); |
| } |
| |
| bool _replaceInFile( |
| File file, { |
| required String query, |
| required String replaceWith, |
| }) { |
| final newLines = <String>[]; |
| var replaced = false; |
| for (final line in file.readAsLinesSync()) { |
| if (line.contains(query)) { |
| newLines.add(line.replaceAll(query, replaceWith)); |
| replaced = true; |
| } else { |
| newLines.add(line); |
| } |
| } |
| final content = newLines.joinWithNewLine(); |
| file.writeAsStringSync(content); |
| return replaced; |
| } |
| |
| String _readVersionFile(String package) { |
| final versionFile = File('../$package/lib/src/version.dart'); |
| final lines = versionFile.readAsLinesSync(); |
| for (final line in lines) { |
| if (line.startsWith('const packageVersion =')) { |
| final version = line |
| .split('=') |
| .last |
| .split('') |
| .where((char) => char != ';' && char != "'" && char != '"') |
| .join(''); |
| return version.trim(); |
| } |
| } |
| throw Exception('Could not read version in $package/lib/src/version.dart'); |
| } |
| |
| String _removeWip(String wipVersion) { |
| if (!wipVersion.contains('wip')) { |
| throw Exception('$wipVersion is not a wip version.'); |
| } |
| return wipVersion.split('-wip').first; |
| } |
| |
| /// Returns the new pinned DWDS version on success. |
| Future<String?> _updateDwdsPin(String package) async { |
| final pubOutdatedProcess = await Process.run( |
| 'dart', |
| [ |
| 'pub', |
| 'outdated', |
| '--no-dependency-overrides', |
| ], |
| workingDirectory: '../$package', |
| ); |
| final lines = pubOutdatedProcess.stdout.split('\n') as List<String>; |
| String? nextDwdsVersion; |
| String? currentDwdsVersion; |
| for (final line in lines) { |
| if (line.trim().startsWith('dwds')) { |
| final segments = |
| line.trim().split(' ').where((segment) => segment != ' '); |
| nextDwdsVersion = segments.last; |
| currentDwdsVersion = |
| segments.lastWhere((segment) => segment.startsWith('*')).substring(1); |
| break; |
| } |
| } |
| final next = nextDwdsVersion ?? ''; |
| final current = currentDwdsVersion ?? ''; |
| if (next.isNotEmpty && current.isNotEmpty) { |
| _logInfo('Changing DWDS pin from $current to $next'); |
| _replaceInFile( |
| File('../$package/pubspec.yaml'), |
| query: current, |
| replaceWith: next, |
| ); |
| return nextDwdsVersion; |
| } |
| _logWarning('Unable to determine DWDS version to pin.'); |
| return null; |
| } |
| |
| void _logInfo(String message) { |
| stdout.writeln(message); |
| } |
| |
| void _logWarning(String warning) { |
| stderr.writeln(warning); |
| } |
| |
| extension JoinExtension on List<String> { |
| String joinWithNewLine() { |
| return '${join('\n')}\n'; |
| } |
| } |