diff --git a/pkg/OWNERS b/pkg/OWNERS index eec08f0b971..de2d8ee31ff 100644 --- a/pkg/OWNERS +++ b/pkg/OWNERS @@ -1,3 +1,4 @@ +file:/tools/OWNERS_ECOSYSTEM #{LAST_RESORT_SUGGESTION} file:/tools/OWNERS_FOUNDATION #{LAST_RESORT_SUGGESTION} file:/tools/OWNERS_INFRA #{LAST_RESORT_SUGGESTION} diff --git a/pkg/dart2js_info/pubspec.yaml b/pkg/dart2js_info/pubspec.yaml index 441e3ae8581..fd83226eff2 100644 --- a/pkg/dart2js_info/pubspec.yaml +++ b/pkg/dart2js_info/pubspec.yaml @@ -18,7 +18,7 @@ dependencies: yaml: ^3.1.0 dev_dependencies: - lints: ^1.0.0 + lints: any test: ^1.2.0 executables: diff --git a/pkg/dds_service_extensions/pubspec.yaml b/pkg/dds_service_extensions/pubspec.yaml index ca19fe29650..06b514647e2 100644 --- a/pkg/dds_service_extensions/pubspec.yaml +++ b/pkg/dds_service_extensions/pubspec.yaml @@ -15,4 +15,4 @@ dependencies: vm_service: ^8.1.0 dev_dependencies: - lints: ^1.0.0 + lints: any diff --git a/pkg/native_stack_traces/pubspec.yaml b/pkg/native_stack_traces/pubspec.yaml index 0247ac9677e..a0d22e5e6d9 100644 --- a/pkg/native_stack_traces/pubspec.yaml +++ b/pkg/native_stack_traces/pubspec.yaml @@ -15,4 +15,4 @@ dependencies: path: ^1.8.0 dev_dependencies: - lints: ^1.0.0 + lints: any diff --git a/pkg/vm_service/pubspec.yaml b/pkg/vm_service/pubspec.yaml index 9d8c47ad71a..c6e4c3ac854 100644 --- a/pkg/vm_service/pubspec.yaml +++ b/pkg/vm_service/pubspec.yaml @@ -16,11 +16,11 @@ dev_dependencies: async: ^2.5.0 expect: any lints: any - markdown: ^4.0.0-nullsafety.0 - mockito: ^5.0.0-nullsafety.1 + markdown: ^4.0.0 + mockito: any path: ^1.8.0 process: ^4.0.0 - pub_semver: ^2.0.0-nullsafety.0 - test: ^1.16.0-nullsafety.13 + pub_semver: ^2.0.0 + test: ^1.16.0 test_package: path: 'test/test_package' diff --git a/tools/package_deps/OWNERS b/tools/package_deps/OWNERS new file mode 100644 index 00000000000..3ed85321e81 --- /dev/null +++ b/tools/package_deps/OWNERS @@ -0,0 +1,2 @@ +file:/tools/OWNERS_ECOSYSTEM +file:/tools/OWNERS_INFRA diff --git a/tools/package_deps/bin/package_deps.dart b/tools/package_deps/bin/package_deps.dart index 5fee3c1b257..43f2bc1ab01 100644 --- a/tools/package_deps/bin/package_deps.dart +++ b/tools/package_deps/bin/package_deps.dart @@ -3,13 +3,19 @@ import 'dart:io'; import 'package:cli_util/cli_logging.dart'; import 'package:collection/collection.dart'; import 'package:path/path.dart' as path; +import 'package:pub_semver/pub_semver.dart'; import 'package:yaml/yaml.dart' as yaml; const validateDEPS = false; +late final bool verbose; +late SdkDeps sdkDeps; + void main(List arguments) { Logger logger = Logger.standard(); + verbose = arguments.contains('-v') || arguments.contains('--verbose'); + // validate the cwd if (!FileSystemEntity.isFileSync('DEPS') || !FileSystemEntity.isDirectorySync('pkg')) { @@ -39,17 +45,12 @@ void main(List arguments) { List pkgPackages = packages.map((p) => p.packageName).toList(); - // Manually added directories (outside of pkg/). - List alsoValidate = [ - 'tools/package_deps', - ]; - - for (String p in alsoValidate) { - packages.add(Package(p)); - } - packages.sort(); + // Parse information about the SDK DEPS file and DEP'd in packages. + sdkDeps = SdkDeps(File('DEPS')); + sdkDeps.parse(); + var validateFailure = false; // For each, validate the pubspec contents. @@ -69,18 +70,12 @@ void main(List arguments) { print('SDK DEPS'); print(''); - var sdkDeps = SdkDeps(File('DEPS')); - sdkDeps.parse(); - List deps = [...sdkDeps.pkgs, ...sdkDeps.testedPkgs]..sort(); for (var pkg in deps) { final tested = sdkDeps.testedPkgs.contains(pkg); print('package:$pkg${tested ? ' [tested]' : ''}'); } - // TODO(devoncarew): Validate that published packages solve against the - // versions brought in from the DEPS file. - // TODO(devoncarew): Find unused entries in the DEPS file. } @@ -108,15 +103,15 @@ class Package implements Comparable { Package(this.dir) { var pubspec = File(path.join(dir, 'pubspec.yaml')); var doc = yaml.loadYamlDocument(pubspec.readAsStringSync()); - dynamic docContents = doc.contents.value; - _packageName = docContents['name']; - _publishToNone = docContents['publish_to'] == 'none'; + dynamic contents = doc.contents.value; + _packageName = contents['name']; + _publishToNone = contents['publish_to'] == 'none'; Set process(String section, List target) { - if (docContents[section] != null) { - final value = Set.from(docContents[section].keys); + if (contents[section] != null) { + final value = Set.from(contents[section].keys); - var deps = docContents[section]; + var deps = contents[section]; for (var package in deps.keys) { target.add(PubDep.parse(package, deps[package])); } @@ -162,8 +157,6 @@ class Package implements Comparable { _collectDartFiles(Directory(dir), files); for (var file in files) { - //print(' ${file.path}'); - var importedPackages = {}; for (var import in _collectImports(file)) { @@ -288,6 +281,42 @@ class Package implements Comparable { } } + // Validate that the version of any package dep'd in works with our declared + // version ranges. + for (PubDep dep in [..._declaredPubDeps, ..._declaredDevPubDeps]) { + if (dep is! SemverPubDep) { + continue; + } + + ResolvedDep resolvedDep = sdkDeps.resolve(dep.name)!; + if (resolvedDep.isMonoRepoPackage) { + continue; + } + + if (verbose) { + print(' ${dep.name} (${dep.value}) resolves ' + 'to ${resolvedDep.version}'); + } + + var declaredDep = VersionConstraint.parse(dep.value); + var resolvedVersion = resolvedDep.version; + if (resolvedVersion == null) { + // Depending on a package without a declared version is only legal if + // the package is not published (i.e., pkg/dartdev depends on + // package:pub, which is not a published and versioned package). + if (publishable) { + out(' Published packages must depend on packages with valid versions.'); + out(' dependency ${dep.name} does not declare a version'); + fail = true; + } + } else if (!declaredDep.allows(resolvedVersion)) { + out(' $packageName depends on ${dep.name} with a range of ' + '${dep.value}, but the version of ${resolvedDep.packageName} ' + 'in the repo is ${resolvedDep.version}.'); + fail = true; + } + } + // Validate that non-published packages use relative a (relative) path dep // for pkg/ packages. if (!publishable) { @@ -398,7 +427,7 @@ String _printSet(Set value) { var list = value.toList()..sort(); list = list.map((item) => 'package:$item').toList(); if (list.length > 1) { - return list.sublist(0, list.length - 1).join(', ') + ' and ' + list.last; + return '${list.sublist(0, list.length - 1).join(', ')} and ${list.last}'; } else { return list.join(', '); } @@ -410,9 +439,20 @@ class SdkDeps { List pkgs = []; List testedPkgs = []; + final Map _resolvedPackageVersions = {}; + SdkDeps(this.file); void parse() { + _parseDepsFile(); + _parseRepoPackageVersions(); + } + + ResolvedDep? resolve(String packageName) { + return _resolvedPackageVersions[packageName]; + } + + void _parseDepsFile() { // Var("dart_root") + "/third_party/pkg/dart2js_info": final pkgRegExp = RegExp(r'"/third_party/pkg/(\S+)"'); @@ -433,6 +473,43 @@ class SdkDeps { pkgs.sort(); testedPkgs.sort(); } + + void _parseRepoPackageVersions() { + _findPackages(Directory('pkg')); + _findPackages(Directory(path.join('third_party', 'devtools'))); + _findPackages(Directory(path.join('third_party', 'pkg'))); + _findPackages(Directory(path.join('third_party', 'pkg_tested'))); + + if (verbose) { + print('Package versions in the SDK:'); + for (var package in _resolvedPackageVersions.values) { + print(' ${package.packageName} at version ${package.version} ' + '[${package.relativePath}]'); + } + print(''); + } + } + + void _findPackages(Directory dir) { + var pubspec = File(path.join(dir.path, 'pubspec.yaml')); + if (pubspec.existsSync()) { + var doc = yaml.loadYamlDocument(pubspec.readAsStringSync()); + dynamic contents = doc.contents.value; + var name = contents['name']; + var version = contents['version']; + var dep = ResolvedDep( + packageName: name, + relativePath: path.relative(dir.path), + version: version == null ? null : Version.parse(version), + ); + _resolvedPackageVersions[name] = dep; + } else { + // Continue to recurse. + for (var subDir in dir.listSync().whereType()) { + _findPackages(subDir); + } + } + } } abstract class PubDep { @@ -479,3 +556,20 @@ class PathPubDep extends PubDep { class UnhandledPubDep extends PubDep { UnhandledPubDep(String name) : super(name); } + +class ResolvedDep { + final String packageName; + final String relativePath; + final Version? version; + + ResolvedDep({ + required this.packageName, + required this.relativePath, + this.version, + }); + + bool get isMonoRepoPackage => relativePath.startsWith('pkg'); + + @override + String toString() => '$packageName: $version'; +} diff --git a/tools/package_deps/pubspec.yaml b/tools/package_deps/pubspec.yaml index 184795eac6d..8573ac0b4ad 100644 --- a/tools/package_deps/pubspec.yaml +++ b/tools/package_deps/pubspec.yaml @@ -11,4 +11,5 @@ dependencies: cli_util: any collection: any path: any + pub_semver: any yaml: any