-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Adds CI utils with check for lower bounds (#194)
* Adds CI utils with check for lower bounds * dart format * fix CI * Improve error message * dart format * adapt lower bounds of dependencies
- Loading branch information
Showing
9 changed files
with
245 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
# https://dart.dev/guides/libraries/private-files | ||
# Created by `dart pub` | ||
.dart_tool/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
## 1.0.0 | ||
|
||
- Initial version. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
CI Utils for dart_apitool |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
# This file configures the static analysis results for your project (errors, | ||
# warnings, and lints). | ||
# | ||
# This enables the 'recommended' set of lints from `package:lints`. | ||
# This set helps identify many issues that may lead to problems when running | ||
# or consuming Dart code, and enforces writing Dart using a single, idiomatic | ||
# style and format. | ||
# | ||
# If you want a smaller set of lints you can change this to specify | ||
# 'package:lints/core.yaml'. These are just the most critical lints | ||
# (the recommended set includes the core lints). | ||
# The core lints are also what is used by pub.dev for scoring packages. | ||
|
||
include: package:lints/recommended.yaml | ||
|
||
# Uncomment the following section to specify additional rules. | ||
|
||
# linter: | ||
# rules: | ||
# - camel_case_types | ||
|
||
# analyzer: | ||
# exclude: | ||
# - path/to/excluded/files/** | ||
|
||
# For more information about the core and recommended set of lints, see | ||
# https://dart.dev/go/core-lints | ||
|
||
# For additional information about configuring this file, see | ||
# https://dart.dev/guides/language/analysis-options |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import 'package:args/command_runner.dart'; | ||
import 'package:ci_util/src/check_lower_bound_dependencies_command.dart'; | ||
|
||
CommandRunner buildCommandRunner() { | ||
return CommandRunner('ci_util', 'CI utilities for dart_apitool') | ||
..addCommand(CheckLowerBoundDependenciesCommand()); | ||
} | ||
|
||
void main(List<String> arguments) async { | ||
final runner = buildCommandRunner(); | ||
await runner.run(arguments); | ||
} |
168 changes: 168 additions & 0 deletions
168
scripts/ci_util/lib/src/check_lower_bound_dependencies_command.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,168 @@ | ||
import 'dart:io'; | ||
|
||
import 'package:args/command_runner.dart'; | ||
import 'package:path/path.dart' as path; | ||
import 'package:pubspec_manager/pubspec_manager.dart'; | ||
|
||
class CheckLowerBoundDependenciesCommand extends Command { | ||
@override | ||
String get description => | ||
'checks if the package can compile with only the lower bound constraints of its dependencies.'; | ||
|
||
@override | ||
String get name => 'check-lower-bound-dependencies'; | ||
|
||
@override | ||
Future run() async { | ||
// copy sources to temporary directory | ||
final String apiToolRootPath = _getApiToolRootPath(); | ||
|
||
// get all dependencies | ||
final pubspec = | ||
PubSpec.loadFromPath(path.join(apiToolRootPath, 'pubspec.yaml')); | ||
|
||
final testFutures = pubspec.dependencies.list | ||
.whereType<DependencyVersioned>() | ||
.map((d) async { | ||
try { | ||
await _testWithFixedDependency((d as Dependency).name); | ||
} catch (e) { | ||
return LowerBoundCheckResult( | ||
dependencyName: (d as Dependency).name, error: e.toString()); | ||
} | ||
return LowerBoundCheckResult(dependencyName: (d as Dependency).name); | ||
}); | ||
final failedDependencies = (await Future.wait(testFutures)) | ||
.where((element) => element.error != null) | ||
.toList(); | ||
if (failedDependencies.isNotEmpty) { | ||
final errorMessage = StringBuffer(); | ||
errorMessage.writeln( | ||
'Following dependencies failed when locked to their lower bound:'); | ||
errorMessage | ||
.writeln(failedDependencies.map((e) => e.dependencyName).join('\n')); | ||
errorMessage.writeln(); | ||
errorMessage.writeln('See error messages for details:'); | ||
errorMessage.writeln( | ||
failedDependencies.map((e) => e.error!).join('\n-----------\n')); | ||
throw Exception(errorMessage.toString()); | ||
} | ||
} | ||
|
||
Future _testWithFixedDependency(String dependencyName) async { | ||
final String apiToolRootPath = _getApiToolRootPath(); | ||
final tempDir = await Directory.systemTemp.createTemp(); | ||
try { | ||
await _copyPath(apiToolRootPath, tempDir.path); | ||
await _fixDependency( | ||
path.join(tempDir.path, 'pubspec.yaml'), dependencyName); | ||
await _executePubGet(tempDir.path); | ||
await _executeBuild(tempDir.path); | ||
} finally { | ||
await tempDir.delete(recursive: true); | ||
} | ||
} | ||
|
||
String _getApiToolRootPath() { | ||
return path.normalize( | ||
path.join( | ||
path.dirname(path.absolute(Platform.script.path)), | ||
'..', | ||
'..', | ||
'..', | ||
), | ||
); | ||
} | ||
|
||
Future _executePubGet(String path) async { | ||
await _executeDart(path, ['pub', 'get']); | ||
} | ||
|
||
Future _executeBuild(String path) async { | ||
await _executeDart(path, ['compile', 'exe', 'bin/main.dart']); | ||
} | ||
|
||
Future _executeDart(String path, List<String> arguments) async { | ||
// check if we are in a fvm environment | ||
final fvmPath = await Process.run('which', ['fvm']); | ||
String executable = 'fvm'; | ||
List<String> additionalArguments = ['dart']; | ||
if (fvmPath.exitCode != 0) { | ||
print('fvm not found, using default dart.'); | ||
executable = 'dart'; | ||
additionalArguments = []; | ||
} | ||
final result = await Process.run( | ||
executable, | ||
[...additionalArguments, ...arguments], | ||
workingDirectory: path, | ||
); | ||
print(result.stdout); | ||
print(result.stderr); | ||
if (result.exitCode != 0) { | ||
throw Exception('Error executing dart: ${result.stderr}'); | ||
} | ||
} | ||
|
||
Future _fixDependency(String pubspecPath, String dependencyName) async { | ||
// load pubspec yaml | ||
final pubspec = PubSpec.loadFromPath(pubspecPath); | ||
// adapt dependency | ||
bool adaptedOne = false; | ||
for (final dependency in pubspec.dependencies.list) { | ||
if (dependency.name == dependencyName && | ||
dependency is DependencyVersioned) { | ||
final castedDependency = dependency as DependencyVersioned; | ||
castedDependency.versionConstraint = | ||
castedDependency.versionConstraint.replaceFirst('^', ''); | ||
adaptedOne = true; | ||
} | ||
} | ||
if (!adaptedOne) { | ||
throw Exception('Dependency $dependencyName not found in pubspec.yaml.'); | ||
} | ||
// write pubspec yaml | ||
pubspec.saveTo(pubspecPath); | ||
} | ||
|
||
Future<void> _copyPath(String from, String to) async { | ||
if (_doNothing(from, to)) { | ||
return; | ||
} | ||
if (await Directory(to).exists()) { | ||
await Directory(to).delete(); | ||
} | ||
await Directory(to).create(recursive: true); | ||
await for (final file in Directory(from).list(recursive: true)) { | ||
// ignore .git directory and its content | ||
if (path.split(file.path).contains('.git')) { | ||
continue; | ||
} | ||
final copyTo = path.join(to, path.relative(file.path, from: from)); | ||
if (file is Directory) { | ||
await Directory(copyTo).create(recursive: true); | ||
} else if (file is File) { | ||
await File(file.path).copy(copyTo); | ||
} else if (file is Link) { | ||
await Link(copyTo).create(await file.target(), recursive: true); | ||
} | ||
} | ||
} | ||
|
||
bool _doNothing(String from, String to) { | ||
if (path.canonicalize(from) == path.canonicalize(to)) { | ||
return true; | ||
} | ||
if (path.isWithin(from, to)) { | ||
throw ArgumentError('Cannot copy from $from to $to'); | ||
} | ||
return false; | ||
} | ||
} | ||
|
||
class LowerBoundCheckResult { | ||
final String dependencyName; | ||
final String? error; | ||
|
||
LowerBoundCheckResult({required this.dependencyName, this.error}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
name: ci_util | ||
description: A sample command-line application with basic argument parsing. | ||
version: 0.0.1 | ||
# repository: https://github.com/my_org/my_repo | ||
|
||
environment: | ||
sdk: ^3.3.3 | ||
|
||
# Add regular dependencies here. | ||
dependencies: | ||
args: ^2.4.2 | ||
path: ^1.9.0 | ||
pubspec_manager: ^1.0.0 | ||
|
||
dev_dependencies: | ||
lints: ^3.0.0 | ||
test: ^1.24.0 |